import Foundation import UIKit import CoreGraphics import SwiftSignalKit private enum Corner: Hashable { case TopLeft(Int), TopRight(Int), BottomLeft(Int), BottomRight(Int) var radius: Int { switch self { case let .TopLeft(radius): return radius case let .TopRight(radius): return radius case let .BottomLeft(radius): return radius case let .BottomRight(radius): return radius } } } private enum Tail: Hashable { case BottomLeft(Int) case BottomRight(Int) var radius: Int { switch self { case let .BottomLeft(radius): return radius case let .BottomRight(radius): return radius } } } private var cachedCorners = Atomic<[Corner: DrawingContext]>(value: [:]) private func cornerContext(_ corner: Corner) -> DrawingContext { let cached: DrawingContext? = cachedCorners.with { return $0[corner] } if let cached = cached { return cached } else { let context = DrawingContext(size: CGSize(width: CGFloat(corner.radius), height: CGFloat(corner.radius)), clear: true)! context.withContext { c in c.clear(CGRect(origin: CGPoint(), size: CGSize(width: CGFloat(corner.radius), height: CGFloat(corner.radius)))) c.setFillColor(UIColor.black.cgColor) switch corner { case let .TopLeft(radius): let rect = CGRect(origin: CGPoint(), size: CGSize(width: CGFloat(radius * 2), height: CGFloat(radius * 2))) c.fillEllipse(in: rect) case let .TopRight(radius): let rect = CGRect(origin: CGPoint(x: -CGFloat(radius), y: 0.0), size: CGSize(width: CGFloat(radius * 2), height: CGFloat(radius * 2))) c.fillEllipse(in: rect) case let .BottomLeft(radius): let rect = CGRect(origin: CGPoint(x: 0.0, y: -CGFloat(radius)), size: CGSize(width: CGFloat(radius * 2), height: CGFloat(radius * 2))) c.fillEllipse(in: rect) case let .BottomRight(radius): let rect = CGRect(origin: CGPoint(x: -CGFloat(radius), y: -CGFloat(radius)), size: CGSize(width: CGFloat(radius * 2), height: CGFloat(radius * 2))) c.fillEllipse(in: rect) } } let _ = cachedCorners.modify { current in var current = current current[corner] = context return current } return context } } public func addCorners(_ context: DrawingContext, arguments: TransformImageArguments) { let corners = arguments.corners let drawingRect = arguments.drawingRect if case let .Corner(radius) = corners.topLeft, radius > CGFloat.ulpOfOne { let corner = cornerContext(.TopLeft(Int(radius))) context.blt(corner, at: CGPoint(x: drawingRect.minX, y: drawingRect.minY)) } if case let .Corner(radius) = corners.topRight, radius > CGFloat.ulpOfOne { let corner = cornerContext(.TopRight(Int(radius))) context.blt(corner, at: CGPoint(x: drawingRect.maxX - radius, y: drawingRect.minY)) } switch corners.bottomLeft { case let .Corner(radius): if radius > CGFloat.ulpOfOne { let corner = cornerContext(.BottomLeft(Int(radius))) context.blt(corner, at: CGPoint(x: drawingRect.minX, y: drawingRect.maxY - radius)) } case let .Tail(radius, image): if radius > CGFloat.ulpOfOne { let color = context.colorAt(CGPoint(x: drawingRect.minX, y: drawingRect.maxY - 1.0)) context.withContext { c in c.clear(CGRect(x: drawingRect.minX - 4.0, y: 0.0, width: 4.0, height: drawingRect.maxY - 6.0)) c.setFillColor(color.cgColor) c.fill(CGRect(x: 0.0, y: drawingRect.maxY - 7.0, width: 4.0, height: 7.0)) c.setBlendMode(.destinationIn) let cornerRect = CGRect(origin: CGPoint(x: drawingRect.minX - 6.0, y: drawingRect.maxY - image.size.height), size: image.size) c.translateBy(x: cornerRect.midX, y: cornerRect.midY) c.scaleBy(x: 1.0, y: -1.0) c.translateBy(x: -cornerRect.midX, y: -cornerRect.midY) c.draw(image.cgImage!, in: cornerRect) c.translateBy(x: cornerRect.midX, y: cornerRect.midY) c.scaleBy(x: 1.0, y: -1.0) c.translateBy(x: -cornerRect.midX, y: -cornerRect.midY) } } } switch corners.bottomRight { case let .Corner(radius): if radius > CGFloat.ulpOfOne { let corner = cornerContext(.BottomRight(Int(radius))) context.blt(corner, at: CGPoint(x: drawingRect.maxX - radius, y: drawingRect.maxY - radius)) } case let .Tail(radius, image): if radius > CGFloat.ulpOfOne { let color = context.colorAt(CGPoint(x: drawingRect.maxX - 1.0, y: drawingRect.maxY - 1.0)) context.withContext { c in c.clear(CGRect(x: drawingRect.maxX, y: 0.0, width: 4.0, height: drawingRect.maxY - image.size.height)) c.setFillColor(color.cgColor) c.fill(CGRect(x: drawingRect.maxX, y: drawingRect.maxY - 7.0, width: 5.0, height: 7.0)) c.setBlendMode(.destinationIn) let cornerRect = CGRect(origin: CGPoint(x: drawingRect.maxX - image.size.width + 6.0, y: drawingRect.maxY - image.size.height), size: image.size) c.translateBy(x: cornerRect.midX, y: cornerRect.midY) c.scaleBy(x: 1.0, y: -1.0) c.translateBy(x: -cornerRect.midX, y: -cornerRect.midY) c.draw(image.cgImage!, in: cornerRect) c.translateBy(x: cornerRect.midX, y: cornerRect.midY) c.scaleBy(x: 1.0, y: -1.0) c.translateBy(x: -cornerRect.midX, y: -cornerRect.midY) } } } }