|
|
@@ -46,29 +46,15 @@ enum GraphicsContext {
|
|
|
|
|
|
static func current(size: CGSize, scale: CGFloat, inverting: Bool, cgImage: CGImage?) -> CGContext? {
|
|
|
#if os(macOS)
|
|
|
- guard let rep = NSBitmapImageRep(
|
|
|
- bitmapDataPlanes: nil,
|
|
|
- pixelsWide: Int(size.width),
|
|
|
- pixelsHigh: Int(size.height),
|
|
|
- bitsPerSample: cgImage?.bitsPerComponent ?? 8,
|
|
|
- samplesPerPixel: 4,
|
|
|
- hasAlpha: true,
|
|
|
- isPlanar: false,
|
|
|
- colorSpaceName: .calibratedRGB,
|
|
|
- bytesPerRow: 0,
|
|
|
- bitsPerPixel: 0) else
|
|
|
- {
|
|
|
- assertionFailure("[Kingfisher] Image representation cannot be created.")
|
|
|
- return nil
|
|
|
- }
|
|
|
- rep.size = size
|
|
|
- guard let context = NSGraphicsContext(bitmapImageRep: rep) else {
|
|
|
+ let descriptor = BitmapContextDescriptor(size: size, cgImage: cgImage)
|
|
|
+ guard let context = descriptor.makeContext() else {
|
|
|
assertionFailure("[Kingfisher] Image context cannot be created.")
|
|
|
return nil
|
|
|
}
|
|
|
-
|
|
|
- NSGraphicsContext.current = context
|
|
|
- return context.cgContext
|
|
|
+ let graphicsContext = NSGraphicsContext(cgContext: context, flipped: false)
|
|
|
+ graphicsContext.imageInterpolation = .high
|
|
|
+ NSGraphicsContext.current = graphicsContext
|
|
|
+ return graphicsContext.cgContext
|
|
|
#elseif os(watchOS)
|
|
|
guard let context = UIGraphicsGetCurrentContext() else {
|
|
|
return nil
|
|
|
@@ -96,3 +82,75 @@ enum GraphicsContext {
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
+
|
|
|
+#if os(macOS)
|
|
|
+private struct BitmapContextDescriptor {
|
|
|
+ let width: Int
|
|
|
+ let height: Int
|
|
|
+ let bitsPerComponent: Int
|
|
|
+ let bytesPerRow: Int
|
|
|
+ let colorSpace: CGColorSpace
|
|
|
+ let bitmapInfo: CGBitmapInfo
|
|
|
+
|
|
|
+ init(size: CGSize, cgImage: CGImage?) {
|
|
|
+ width = max(Int(size.width.rounded(.down)), 1)
|
|
|
+ height = max(Int(size.height.rounded(.down)), 1)
|
|
|
+ colorSpace = BitmapContextDescriptor.resolveColorSpace(from: cgImage)
|
|
|
+ bitsPerComponent = BitmapContextDescriptor.supportedBitsPerComponent(from: cgImage)
|
|
|
+ let hasAlpha = BitmapContextDescriptor.containsAlpha(from: cgImage)
|
|
|
+ bitmapInfo = BitmapContextDescriptor.bitmapInfo(hasAlpha: hasAlpha)
|
|
|
+ let componentsPerPixel = colorSpace.numberOfComponents + 1 // Reserve an alpha (or skip) slot.
|
|
|
+ let bitsPerPixel = componentsPerPixel * bitsPerComponent
|
|
|
+ bytesPerRow = BitmapContextDescriptor.alignedBytesPerRow(bitsPerPixel: bitsPerPixel, width: width)
|
|
|
+ }
|
|
|
+
|
|
|
+ func makeContext() -> CGContext? {
|
|
|
+ CGContext(
|
|
|
+ data: nil,
|
|
|
+ width: width,
|
|
|
+ height: height,
|
|
|
+ bitsPerComponent: bitsPerComponent,
|
|
|
+ bytesPerRow: bytesPerRow,
|
|
|
+ space: colorSpace,
|
|
|
+ bitmapInfo: bitmapInfo.rawValue
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ private static func supportedBitsPerComponent(from cgImage: CGImage?) -> Int {
|
|
|
+ guard let bits = cgImage?.bitsPerComponent, bits > 0 else { return 8 }
|
|
|
+ if bits <= 8 { return 8 }
|
|
|
+ return 16
|
|
|
+ }
|
|
|
+
|
|
|
+ private static func resolveColorSpace(from cgImage: CGImage?) -> CGColorSpace {
|
|
|
+ guard let cgColorSpace = cgImage?.colorSpace else {
|
|
|
+ return CGColorSpaceCreateDeviceRGB()
|
|
|
+ }
|
|
|
+ let components = cgColorSpace.numberOfComponents
|
|
|
+ if components == 1 || components == 3 {
|
|
|
+ return cgColorSpace
|
|
|
+ }
|
|
|
+ return CGColorSpaceCreateDeviceRGB()
|
|
|
+ }
|
|
|
+
|
|
|
+ private static func containsAlpha(from cgImage: CGImage?) -> Bool {
|
|
|
+ guard let alphaInfo = cgImage?.alphaInfo else { return true }
|
|
|
+ switch alphaInfo {
|
|
|
+ case .none, .noneSkipFirst, .noneSkipLast:
|
|
|
+ return false
|
|
|
+ default:
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static func bitmapInfo(hasAlpha: Bool) -> CGBitmapInfo {
|
|
|
+ let alphaInfo: CGImageAlphaInfo = hasAlpha ? .premultipliedLast : .noneSkipLast
|
|
|
+ return CGBitmapInfo(rawValue: alphaInfo.rawValue)
|
|
|
+ }
|
|
|
+
|
|
|
+ private static func alignedBytesPerRow(bitsPerPixel: Int, width: Int) -> Int {
|
|
|
+ let rawBytes = (bitsPerPixel * width + 7) / 8
|
|
|
+ return (rawBytes + 0x3F) & ~0x3F
|
|
|
+ }
|
|
|
+}
|
|
|
+#endif
|