JohnYezub/CGTestApp

Learn CoreGraphics (Quartz 2D) by example — an iOS app with focused CGContext demos and an interactive drawing canvas (pencil, pinch-zoom, two-finger rotate).

★ 0Forks 0SwiftGitHub ↗Compare

README

CGTestApp

A small iOS app for learning 2D graphics on Apple platforms by example. It pairs a gallery of focused CoreGraphics (Quartz 2D) demos and a hands-on drawing screen — all rendered into a real CGContext — with a minimal Metal shader running on the GPU.

Built with SwiftUI + UIKit interop, targeting iOS.

CGTestApp main screen listing the drawing playground and CoreGraphics demos

What it demonstrates

Drawing playground

A practical screen that ties the concepts together:

  • Pencil brush — freehand drawing with one finger via touchesBegan/Moved/Ended, strokes stroked with round line caps/joins for a smooth line.
  • Pinch to zoom & two-finger rotate — applied to the view's transform through UIPinchGestureRecognizer + UIRotationGestureRecognizer (recognized simultaneously).
  • Clear — resets strokes and the transform.

Two ideas worth highlighting from the implementation:

  1. Strokes are stored in the view's local coordinates, which are independent of transform, so the drawing stays consistent under any zoom/rotation — no need to invert the transform for touch points.
  2. Single-finger drawing and two-finger gestures don't conflict: when a second finger appears, the recognizer takes over and the in-progress stroke is dropped via touchesCancelled.

CoreGraphics demo gallery

Each demo draws into a CGContext and isolates one topic:

  1. Path: fill & stroke — the core cycle (build path → set color → fill/stroke) and the painter's model.
  2. Bezier curves — cubic/quadratic curves and their control points.
  3. Gradients — linear and radial gradients inside a clip region.
  4. Transforms (CTM) — translate / rotate / scale of the coordinate system.
  5. Graphics state + shadows — saveGState / restoreGState, setShadow.
  6. Blend modes — multiply, screen.
  7. Text — NSAttributedString, outlined glyphs.
  8. Offscreen → UIImage — rendering into a bitmap context via UIGraphicsImageRenderer.

Plus a SwiftUI Canvas example, showing that GraphicsContext is a declarative facade over the same engine.

Metal shader (GPU)

A minimal "Hello, GPU" screen: a full-screen fragment shader that animates a plasma pattern, redrawn every frame by the GPU. It shows the whole render pipeline end to end:

  • MTLDevice → command queue → render pipeline state (compiled vertex + fragment shaders).
  • An MTKView drives draw(in:) ~60 times per second.
  • Each frame encodes a full-screen triangle plus the current time and presents it.

The point of contrast with CoreGraphics: the fragment shader is written for a single pixel, and the GPU runs it for every pixel in parallel — where CoreGraphics rasterizes on the CPU.

Project structure

File Responsibility
CGCanvasView.swift UIViewRepresentable bridge that exposes a raw CGContext to SwiftUI.
CGDemos.swift The eight CGContext drawing demos.
DrawingScreen.swift The interactive drawing screen (pencil, zoom, rotate, clear).
Shaders.metal Metal Shading Language: the full-screen vertex + plasma fragment shaders.
MetalScreen.swift The Metal renderer, MTKView bridge, and GPU screen.
ContentView.swift Navigation and the SwiftUI Canvas example.

Coordinate systems (a common gotcha)

Inside UIView.draw(_:) the origin is top-left with the Y axis pointing down (UIKit convention). In a "raw" CoreGraphics context (PDF, bitmap) the origin is bottom-left, Y up. This matters most when drawing text and images.

Running

Open CGTestApp.xcodeproj in Xcode and run on an iOS Simulator or device.

In the Simulator, pinch/rotate are emulated by holding ⌥ Option (two fingers); add ⇧ Shift to move the finger pair.

Building the Metal shader requires the Metal Toolchain. On Xcode 16+ it is a separate downloadable component; if the build complains it's missing, run:

xcodebuild -downloadComponent MetalToolchain

Contributors

EugeneYezub

Issues