Core Graphics — what is it, how it works, and the framework for 2D graphics

Author: IT Sectr Published: 2026-07-21 Reading time: 10 min

Core Graphics is Apple's 2D rendering framework, part of Core Foundation, available on iOS, macOS, tvOS, and watchOS. It provides a low-level C API for working with vector and raster graphics, including drawing paths, gradients, shadows, images, and text. Core Graphics serves as the foundation for UIKit, SwiftUI, and AppKit. According to Apple Developer Documentation, 2026, the framework is used in more than 90% of iOS apps, because every visible element passes through Core Graphics or its hardware equivalent.

Key Takeaways

  • Core Graphics is Apple's fundamental 2D framework for vector and raster rendering.
  • CGContext is the central object of the framework through which all drawing operations are performed.
  • CGPath is an immutable vector path for reuse across different contexts.
  • CGGradient is an object for creating linear and radial gradients with color space support.
  • CGImage is a raster image that can be drawn, transformed, and masked.

What is Core Graphics?

Core Graphics (also known as Quartz 2D) is a framework implementing Apple's 2D graphics engine. It is based on the Quartz portable graphics library and uses a page-based drawing model, where each action is a command in a sequence resembling drawing on paper. Core Graphics supports antialiasing, transparency (alpha compositing), and ICC color spaces.

The Core Graphics architecture is built around a graphics context (CGContext) that encapsulates the target output device — screen, printer, PDF document, or raster buffer. The context manages current settings: color, line width, transformation, clipping area, and fonts. This approach provides a uniform API for different output devices without changing the drawing code.

Core Graphics primarily runs on the CPU, using a vector model (resolution-independent). This means lines and curves are rasterized at the current device resolution when drawn. Unlike Metal, Core Graphics does not require explicit GPU resource management — the system handles all optimization. For most 2D tasks (graphics, charts, custom UI elements), Core Graphics performance is sufficient to maintain 60 FPS.

Architecture and Core Types

Core Graphics includes about 50 base types (opaque types), each responsible for specific functionality. All types follow Core Foundation memory management rules: they are created through functions with Create in the name, require a call to CGRelease, and are not ARC-compatible in Objective-C (however Swift automatically manages memory through CF_RETURNS_RETAINED annotations).

Core Graphics Key Types

CGContext — the drawing context, the framework's central type. CGPath — an immutable vector path. CGColor — a color with a specified color space. CGGradient — a gradient fill. CGImage — a raster image. CGLayer — an optimized offline buffer for repeated drawing. CGDataProvider — a data source for images. CGColorSpace — a color space (sRGB, Display P3, Gray).

Each type is designed for minimal overhead: CGPath can be cached and reused across different contexts, CGColor is converted to GPU hardware representation when passed, CGLayer stores pre-rasterized content. This architecture allows Core Graphics to work efficiently both in simple UI tasks and in professional graphics editors.

TypePurposeMemory Management
CGContextGraphics contextCFRelease or automatic in Swift
CGPathVector pathCFRelease (immutable)
CGColorColor in color spaceCFRelease or CGColorCreate
CGImageRaster imageCFRelease (after creation)
CGLayerOffline bufferCFRelease or CGLayerCreate

CGPath: Vector Paths and Their Optimization

CGPath is an immutable object describing a sequence of geometric segments: straight lines, Bezier curves, arcs, and closures. Unlike UIBezierPath, CGPath contains no drawing methods — it only stores geometry. To draw, CGPath is added to the context (CGContextAddPath) and then rendered via strokePath or fillPath. Immutability guarantees thread safety and cacheability.

Creating and Optimizing Paths

CGPathCreateMutable creates a mutable path (CGMutablePathRef) for step-by-step construction. After final assembly, it is converted to an immutable CGPathRef. Apple recommends minimizing the number of points in CGPath — overly complex paths (more than 1000 segments) slow down rasterization. For optimization, use CGPathCreateCopyBySimplifyingPath, which removes redundant collinear points.

For shape animation between two CGPath objects, you can use CABasicAnimation on the path property of CAShapeLayer. Limitation: both CGPath objects must have the same number of segments (control points). The CGPathApply function lets you traverse all path elements and compare their structure. If segments do not match, the animation will be jerky — in this case, use CADisplayLink for manual point interpolation.

swift
// create mutable path
let mutablePath = CGMutablePath()
mutablePath.moveTo(to: CGPoint(x: 0, y: 0))
mutablePath.addLine(to: CGPoint(x: 100, y: 0))
mutablePath.addLine(to: CGPoint(x: 100, y: 100))
mutablePath.closeSubpath()

// convert to immutable for safe caching
let immutablePath: CGPath = mutablePath
CATransaction.perform {
    shapeLayer.path = immutablePath
}

Gradients: CGGradient and CGShading

CGGradient is an object defining a smooth transition between two or more colors along a straight line (linear gradient) or from the center outward (radial gradient). CGGradient is created with a specified color space, an array of CGColorRef objects, and an array of stop positions from 0.0 to 1.0. After creation, the gradient can be reused multiple times in different contexts.

CGShading for Advanced Gradients

CGShading is a more powerful mechanism that allows defining a color function along a path. Unlike CGGradient, which works with a fixed set of colors, CGShading uses a callback function (CGFunctionRef) that computes the color at each point. This enables complex gradients with non-linear transitions and composite color schemes.

To draw a gradient inside a shape, you need a clip on the shape's path. The order: add the path to the context, call CGContextClip, then draw the gradient. Without a clip, the gradient fills the entire context. On iOS, CGGradient is recommended as the simpler and more performant option — CGShading is only justified for custom color functions.

swift
func drawGradient(ctx: CGContext, rect: CGRect) {
    let colors = [UIColor.red.cgColor, UIColor.blue.cgColor] as CFArray
    guard let gradient = CGGradient(
        colorsSpace: CGColorSpaceCreateDeviceRGB(),
        colors: colors,
        locations: [0.0, 1.0]
    ) else { return }

    ctx.saveGState()
    ctx.addEllipse(in: rect)
    ctx.clip()
    ctx.drawLinearGradient(
        gradient,
        start: CGPoint(x: rect.midX, y: rect.minY),
        end: CGPoint(x: rect.midX, y: rect.maxY),
        options: []
    )
    ctx.restoreGState()
}

Working with Images: CGImage and CGLayer

CGImage is a raster image in Core Graphics that can be loaded from a file, created programmatically, or obtained from UIImage. CGImage contains pixel data, color space information, size, bit depth, and data layout. Unlike UIImage, CGImage does not manage Retina scaling — this responsibility falls on the developer via the scale parameter.

CGLayer — Optimizing Repeated Drawing

CGLayer is an offline buffer for pre-rasterizing static content. If the same set of drawing commands is executed repeatedly (for example, a background pattern), CGLayer rasterizes it once and then simply copies it into the context. According to Apple, using CGLayer can speed up repeated drawing by 2–5× on CPU and up to 10× with hardware acceleration.

For image loading, use CGImageSource — a powerful API for reading metadata, thumbnails, and individual frames from GIF/APNG. CGImageSource allows progressive loading and retrieving only the necessary fragments. When working with large images (exceeding the screen size), it is recommended to create a CGImageSource with disk caching and use CGImageSourceCreateThumbnailAtIndex to display a reduced version.

swift
func createLayer(ctx: CGContext, size: CGSize) -> CGLayer? {
    guard let layer = CGLayer(ctx, size: size, auxiliaryInfo: nil) else { return nil }
    let layerCtx = layer.context !

    // draw once
    layerCtx.setFillColor(UIColor.lightGray.cgColor)
    layerCtx.addEllipse(in: CGRect(origin: .zero, size: size))
    layerCtx.fillPath()
    return layer
}

// usage in draw()
for i in 0..<20 {
    ctx.draw(layer, at: CGPoint(x: CGFloat(i) * 30, y: 0))
}

Core Graphics Code Example

Let's build a custom View for displaying a watermark on an image using Core Graphics. The watermark includes semi-transparent text and a vector logo overlaid on top of the original image. The code demonstrates working with CGContext, CGImage, text, and transparency in a single context.

swift
class WatermarkView: UIView {

    private let watermarkText = "IT Sectr"
    private let watermarkOpacity: CGFloat = 0.3

    override func draw(_ rect: CGRect) {
        guard let ctx = UIGraphicsGetCurrentContext() else { return }

        // draw background image
        UIImage(named: "background")?.draw(in: rect)

        // configure watermark transparency
        ctx.saveGState()
        ctx.translateBy(x: rect.midX, y: rect.midY)
        ctx.rotate(by: -.pi / 6)

        // semi-transparent text
        let attributes: NSAttributedString.Attributes = [
            .font: UIFont.boldSystemFont(ofSize: 48),
            .foregroundColor: UIColor.white.withAlphaComponent(watermarkOpacity)
        ]
        let textSize = watermarkText.size(withAttributes: attributes)
        watermarkText.draw(
            at: CGPoint(x: -textSize.width / 2, y: -textSize.height / 2),
            withAttributes: attributes
        )

        // vector icon (circle with cross) below text
        ctx.setStrokeColor(UIColor.white.withAlphaComponent(watermarkOpacity).cgColor)
        ctx.setLineWidth(3)
        ctx.addEllipse(in: CGRect(x: -25, y: -75, width: 50, height: 50))
        ctx.strokePath()
        ctx.moveTo(x: -15, y: -50)
        ctx.addLineTo(x: 15, y: -50)
        ctx.moveTo(x: 0, y: -65)
        ctx.addLineTo(x: 0, y: -35)
        ctx.strokePath()

        ctx.restoreGState()
    }
}

The code uses key Core Graphics features: saveGState/restoreGState to isolate watermark effects from the background, translateBy and rotate for positioning and rotation, semi-transparent text via withAlphaComponent, and a vector icon through addEllipse and addLineTo. The entire watermark is drawn as a single composition after the background image.

When creating watermarks for photos in production, it is important to consider performance. If the image is larger than the screen, reduce it using CGImageSourceCreateThumbnailAtIndex before applying the watermark. Core Graphics handles all operations on the CPU — for batch processing of dozens of images, use a background queue and CGBitmapContext to avoid blocking the UI.

Frequently Asked Questions

How does Core Graphics differ from UIKit?

UIKit is a high-level UI framework that uses Core Graphics internally for drawing. Core Graphics is a low-level C API for 2D graphics. UIKit is more convenient for standard elements, while Core Graphics gives full control over every pixel.

When should I use Core Graphics instead of Metal?

Core Graphics is suitable for static 2D graphics, charts, and custom UI elements. Metal is for 3D graphics, complex animations, and processing large pixel arrays with GPU acceleration. For 95% of UI tasks, Core Graphics is sufficient.

How can I save memory when working with CGImage?

Use CGImageSource to load thumbnails via createThumbnailAtIndex instead of full loading. For displaying large images, create CGImage with the downsample option. Release CGImage via CGImageRelease when it is no longer needed.

Does Core Graphics support PDF?

Yes, Core Graphics includes full PDF support through CGPDFDocument for reading and CGPDFContext for creating PDF documents. You can draw any CGContext content directly into a PDF page while preserving vectors.

How do I ensure Retina quality when drawing?

Core Graphics automatically uses the device resolution when drawing on screen. For offline contexts, use UIGraphicsBeginImageContextWithOptions with scale = 0 (system scale) or explicitly specify 2.0/3.0 for Retina/Retina HD.

Summary

  • Core Graphics is Apple's fundamental 2D framework for vector and raster rendering on CPU.
  • CGContext is the central object that manages the state stack and target output device.
  • CGPath provides immutable vector paths for safe caching and thread-safe reuse.
  • CGGradient and CGShading provide mechanisms for creating linear, radial, and functional gradients.
  • CGImage provides raster images with support for various color spaces and bit depths.
  • CGLayer is an offline buffer that accelerates repeated drawing by 2–5×.
  • Core Graphics serves as the foundation for UIKit, SwiftUI, and AppKit — every visible element in iOS passes through it.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also