Swiftgram/submodules/Display/Display/ImageCorners.swift
Peter Iakovlev e9a4a9347a Revert "Rename directories [skip ci]"
This reverts commit 789438a27450dcbdee6065ebf096198ed3b90fec
2020-03-01 10:06:51 +00:00

217 lines
8.3 KiB
Swift

import Foundation
import UIKit
import CoreGraphics
import SwiftSignalKit
private enum Corner: Hashable {
case TopLeft(Int), TopRight(Int), BottomLeft(Int), BottomRight(Int)
var hashValue: Int {
switch self {
case let .TopLeft(radius):
return radius | (1 << 24)
case let .TopRight(radius):
return radius | (2 << 24)
case let .BottomLeft(radius):
return radius | (3 << 24)
case let .BottomRight(radius):
return radius | (4 << 24)
}
}
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 func ==(lhs: Corner, rhs: Corner) -> Bool {
switch lhs {
case let .TopLeft(lhsRadius):
switch rhs {
case let .TopLeft(rhsRadius) where rhsRadius == lhsRadius:
return true
default:
return false
}
case let .TopRight(lhsRadius):
switch rhs {
case let .TopRight(rhsRadius) where rhsRadius == lhsRadius:
return true
default:
return false
}
case let .BottomLeft(lhsRadius):
switch rhs {
case let .BottomLeft(rhsRadius) where rhsRadius == lhsRadius:
return true
default:
return false
}
case let .BottomRight(lhsRadius):
switch rhs {
case let .BottomRight(rhsRadius) where rhsRadius == lhsRadius:
return true
default:
return false
}
}
}
private enum Tail: Hashable {
case BottomLeft(Int)
case BottomRight(Int)
var hashValue: Int {
switch self {
case let .BottomLeft(radius):
return radius | (1 << 24)
case let .BottomRight(radius):
return radius | (2 << 24)
}
}
var radius: Int {
switch self {
case let .BottomLeft(radius):
return radius
case let .BottomRight(radius):
return radius
}
}
}
private func ==(lhs: Tail, rhs: Tail) -> Bool {
switch lhs {
case let .BottomLeft(lhsRadius):
switch rhs {
case let .BottomLeft(rhsRadius) where rhsRadius == lhsRadius:
return true
default:
return false
}
case let .BottomRight(lhsRadius):
switch rhs {
case let .BottomRight(rhsRadius) where rhsRadius == lhsRadius:
return true
default:
return false
}
}
}
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)
}
}
}
}