Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
signalapp
GitHub Repository: signalapp/Signal-iOS
Path: blob/main/Signal/QRCodes/Bitmaps/CGContext+LineDrawing.swift
1 views
//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import CoreGraphics

extension CGContext {
    /// Returns a context painted with the given grid drawing, scaled by the
    /// given amount.
    ///
    /// The origin of the context is the bottom-left, matching the default
    /// behavior for CoreGraphics contexts.
    ///
    /// Properties such as the line width, join, cap, color will be set on the
    /// context.
    ///
    /// - Parameter scaledBy
    /// Represents how many painted pixels should be used to represent a single
    /// "pixel" in the grid drawing. Higher scale translates to smoother curves
    /// at high resolution.
    static func drawing(
        gridDrawing drawing: Bitmaps.GridDrawing,
        scaledBy scaleInt: Int,
        lineJoin: CGLineJoin = .round,
        lineCap: CGLineCap = .round,
        foregroundColor: CGColor,
        backgroundColor: CGColor,
    ) -> CGContext {
        let scaleFloat = CGFloat(scaleInt)

        let cgContext = CGContext(
            data: nil,
            width: drawing.width * scaleInt,
            height: drawing.height * scaleInt,
            bitsPerComponent: 8,
            bytesPerRow: 4 * drawing.width * scaleInt, // Four components per pixel
            space: CGColorSpaceCreateDeviceRGB(),
            bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue,
        )!

        cgContext.setFillColor(backgroundColor)
        cgContext.fill([cgContext.boundingRect])

        cgContext.setLineWidth(scaleFloat)
        cgContext.setLineJoin(lineJoin)
        cgContext.setLineCap(lineCap)
        cgContext.setStrokeColor(foregroundColor)

        let segmentsCGPointPairs = drawing.segments.flatMap { segment -> [CGPoint] in
            return [
                segment.start.cgPoint(scaledBy: scaleFloat),
                segment.end.cgPoint(scaledBy: scaleFloat),
            ]
        }

        cgContext.strokeLineSegments(between: segmentsCGPointPairs)

        return cgContext
    }

    private var boundingRect: CGRect {
        return CGRect(origin: .zero, size: CGSize(width: width, height: height))
    }
}