Optimize image drawing

This commit is contained in:
Ali 2021-06-16 22:35:34 +04:00
parent d0830b3c00
commit 0f9648fb6c
17 changed files with 137 additions and 148 deletions

View File

@ -614,7 +614,7 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
self.data = data
self.width = width
self.height = height
self.bytesPerRow = (4 * Int(width) + 15) & (~15)
self.bytesPerRow = DeviceGraphicsContextSettings.shared.bytesPerRow(forWidth: Int(width))
self.currentFrame = 0
let rawData = TGGUnzipData(data, 8 * 1024 * 1024) ?? data
let decompressedData = transformedWithFitzModifier(data: rawData, fitzModifier: fitzModifier)

View File

@ -13,7 +13,7 @@ final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
queue.async { [weak self] in
switch type {
case .argb:
let calculatedBytesPerRow = (4 * Int(width) + 15) & (~15)
let calculatedBytesPerRow = DeviceGraphicsContextSettings.shared.bytesPerRow(forWidth: Int(width))
assert(bytesPerRow == calculatedBytesPerRow)
case .yuva:
break

View File

@ -6,7 +6,7 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import "ASCGImageBuffer.h"
#import <AsyncDisplayKit/ASCGImageBuffer.h>
#import <sys/mman.h>
#import <mach/mach_init.h>

View File

@ -7,7 +7,7 @@
//
#import <AsyncDisplayKit/ASGraphicsContext.h>
#import "ASCGImageBuffer.h"
#import <AsyncDisplayKit/ASCGImageBuffer.h>
#import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASConfigurationInternal.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>

View File

@ -161,7 +161,7 @@ public func peerAvatarImage(account: Account, peerReference: PeerReference?, aut
}
if shouldBlur {
let imageContextSize = CGSize(width: 64.0, height: 64.0)
let imageContext = DrawingContext(size: imageContextSize, scale: 1.0, premultiplied: true, clear: true)
let imageContext = DrawingContext(size: imageContextSize, scale: 1.0, clear: true)
imageContext.withFlippedContext { c in
c.draw(dataImage, in: CGRect(origin: CGPoint(), size: imageContextSize))

View File

@ -1,6 +1,7 @@
import Foundation
import UIKit
import Accelerate
import AsyncDisplayKit
public let deviceColorSpace: CGColorSpace = {
if #available(iOSApplicationExtension 9.3, iOS 9.3, *) {
@ -19,38 +20,20 @@ private let grayscaleColorSpace = CGColorSpaceCreateDeviceGray()
let deviceScale = UIScreen.main.scale
public func generateImagePixel(_ size: CGSize, scale: CGFloat, pixelGenerator: (CGSize, UnsafeMutablePointer<UInt8>, Int) -> Void) -> UIImage? {
let scaledSize = CGSize(width: size.width * scale, height: size.height * scale)
let bytesPerRow = (4 * Int(scaledSize.width) + 15) & (~15)
let length = bytesPerRow * Int(scaledSize.height)
let bytes = malloc(length)!.assumingMemoryBound(to: UInt8.self)
guard let provider = CGDataProvider(dataInfo: bytes, data: bytes, size: length, releaseData: { bytes, _, _ in
free(bytes)
})
else {
return nil
}
pixelGenerator(scaledSize, bytes, bytesPerRow)
let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue)
guard let image = CGImage(width: Int(scaledSize.width), height: Int(scaledSize.height), bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: bytesPerRow, space: deviceColorSpace, bitmapInfo: bitmapInfo, provider: provider, decode: nil, shouldInterpolate: false, intent: .defaultIntent)
else {
return nil
}
return UIImage(cgImage: image, scale: scale, orientation: .up)
let context = DrawingContext(size: size, scale: scale, opaque: false, clear: false)
pixelGenerator(CGSize(width: size.width * scale, height: size.height * scale), context.bytes.assumingMemoryBound(to: UInt8.self), context.bytesPerRow)
return context.generateImage()
}
private func withImageBytes(image: UIImage, _ f: (UnsafePointer<UInt8>, Int, Int, Int) -> Void) {
let selectedScale = image.scale
let scaledSize = CGSize(width: image.size.width * selectedScale, height: image.size.height * selectedScale)
let bytesPerRow = (4 * Int(scaledSize.width) + 15) & (~15)
let bytesPerRow = DeviceGraphicsContextSettings.shared.bytesPerRow(forWidth: Int(scaledSize.width))
let length = bytesPerRow * Int(scaledSize.height)
let bytes = malloc(length)!.assumingMemoryBound(to: UInt8.self)
memset(bytes, 0, length)
let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue)
let bitmapInfo = DeviceGraphicsContextSettings.shared.transparentBitmapInfo
guard let context = CGContext(data: bytes, width: Int(scaledSize.width), height: Int(scaledSize.height), bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: deviceColorSpace, bitmapInfo: bitmapInfo.rawValue) else {
return
@ -65,7 +48,7 @@ private func withImageBytes(image: UIImage, _ f: (UnsafePointer<UInt8>, Int, Int
public func generateGrayscaleAlphaMaskImage(image: UIImage) -> UIImage? {
let selectedScale = image.scale
let scaledSize = CGSize(width: image.size.width * selectedScale, height: image.size.height * selectedScale)
let bytesPerRow = (1 * Int(scaledSize.width) + 15) & (~15)
let bytesPerRow = (1 * Int(scaledSize.width) + 31) & (~31)
let length = bytesPerRow * Int(scaledSize.height)
let bytes = malloc(length)!.assumingMemoryBound(to: UInt8.self)
memset(bytes, 0, length)
@ -111,69 +94,19 @@ public func generateGrayscaleAlphaMaskImage(image: UIImage) -> UIImage? {
}
public func generateImage(_ size: CGSize, contextGenerator: (CGSize, CGContext) -> Void, opaque: Bool = false, scale: CGFloat? = nil) -> UIImage? {
let selectedScale = scale ?? deviceScale
let scaledSize = CGSize(width: size.width * selectedScale, height: size.height * selectedScale)
let bytesPerRow = (4 * Int(scaledSize.width) + 15) & (~15)
let length = bytesPerRow * Int(scaledSize.height)
let bytes = malloc(length)!.assumingMemoryBound(to: Int8.self)
guard let provider = CGDataProvider(dataInfo: bytes, data: bytes, size: length, releaseData: { bytes, _, _ in
free(bytes)
})
else {
return nil
let context = DrawingContext(size: size, scale: scale ?? 0.0, opaque: opaque, clear: false)
context.withFlippedContext { c in
contextGenerator(context.size, c)
}
let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | (opaque ? CGImageAlphaInfo.noneSkipFirst.rawValue : CGImageAlphaInfo.premultipliedFirst.rawValue))
guard let context = CGContext(data: bytes, width: Int(scaledSize.width), height: Int(scaledSize.height), bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: deviceColorSpace, bitmapInfo: bitmapInfo.rawValue) else {
return nil
}
context.scaleBy(x: selectedScale, y: selectedScale)
contextGenerator(size, context)
guard let image = CGImage(width: Int(scaledSize.width), height: Int(scaledSize.height), bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: bytesPerRow, space: deviceColorSpace, bitmapInfo: bitmapInfo, provider: provider, decode: nil, shouldInterpolate: false, intent: .defaultIntent)
else {
return nil
}
return UIImage(cgImage: image, scale: selectedScale, orientation: .up)
return context.generateImage()
}
public func generateImage(_ size: CGSize, opaque: Bool = false, scale: CGFloat? = nil, rotatedContext: (CGSize, CGContext) -> Void) -> UIImage? {
let selectedScale = scale ?? deviceScale
let scaledSize = CGSize(width: size.width * selectedScale, height: size.height * selectedScale)
let bytesPerRow = (4 * Int(scaledSize.width) + 15) & (~15)
let length = bytesPerRow * Int(scaledSize.height)
let bytes = malloc(length)!.assumingMemoryBound(to: Int8.self)
guard let provider = CGDataProvider(dataInfo: bytes, data: bytes, size: length, releaseData: { bytes, _, _ in
free(bytes)
}) else {
return nil
let context = DrawingContext(size: size, scale: scale ?? 0.0, opaque: opaque, clear: false)
context.withContext { c in
rotatedContext(context.size, c)
}
let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | (opaque ? CGImageAlphaInfo.noneSkipFirst.rawValue : CGImageAlphaInfo.premultipliedFirst.rawValue))
guard let context = CGContext(data: bytes, width: Int(scaledSize.width), height: Int(scaledSize.height), bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: deviceColorSpace, bitmapInfo: bitmapInfo.rawValue) else {
return nil
}
context.scaleBy(x: selectedScale, y: selectedScale)
context.translateBy(x: size.width / 2.0, y: size.height / 2.0)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0)
rotatedContext(size, context)
guard let image = CGImage(width: Int(scaledSize.width), height: Int(scaledSize.height), bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: bytesPerRow, space: deviceColorSpace, bitmapInfo: bitmapInfo, provider: provider, decode: nil, shouldInterpolate: false, intent: .defaultIntent)
else {
return nil
}
return UIImage(cgImage: image, scale: selectedScale, orientation: .up)
return context.generateImage()
}
public func generateFilledCircleImage(diameter: CGFloat, color: UIColor?, strokeColor: UIColor? = nil, strokeWidth: CGFloat? = nil, backgroundColor: UIColor? = nil) -> UIImage? {
@ -433,6 +366,47 @@ public enum DrawingContextBltMode {
case Alpha
}
public struct DeviceGraphicsContextSettings {
public static let shared: DeviceGraphicsContextSettings = {
UIGraphicsBeginImageContextWithOptions(CGSize(width: 1.0, height: 1.0), true, 1.0)
let refContext = UIGraphicsGetCurrentContext()!
let bytesPerRow = refContext.bytesPerRow
let bitsPerPixel = refContext.bitsPerPixel
let bitsPerComponent = refContext.bitsPerComponent
let opaqueBitmapInfo = refContext.bitmapInfo
let image = UIGraphicsGetImageFromCurrentImageContext()!
let colorSpace = image.cgImage!.colorSpace!
assert(bytesPerRow == 32)
UIGraphicsEndImageContext()
UIGraphicsBeginImageContextWithOptions(CGSize(width: 1.0, height: 1.0), false, 1.0)
let refCtxTransparent = UIGraphicsGetCurrentContext()!
let transparentBitmapInfo = refCtxTransparent.bitmapInfo
UIGraphicsEndImageContext()
return DeviceGraphicsContextSettings(
rowAlignment: bytesPerRow,
bitsPerPixel: bitsPerPixel,
bitsPerComponent: bitsPerComponent,
opaqueBitmapInfo: opaqueBitmapInfo,
transparentBitmapInfo: transparentBitmapInfo,
colorSpace: colorSpace
)
}()
public let rowAlignment: Int
public let bitsPerPixel: Int
public let bitsPerComponent: Int
public let opaqueBitmapInfo: CGBitmapInfo
public let transparentBitmapInfo: CGBitmapInfo
public let colorSpace: CGColorSpace
public func bytesPerRow(forWidth width: Int) -> Int {
let baseValue = self.bitsPerPixel * width / 8
return (baseValue + 31) & ~0x1F
}
}
public class DrawingContext {
public let size: CGSize
public let scale: CGFloat
@ -440,46 +414,33 @@ public class DrawingContext {
public let bytesPerRow: Int
private let bitmapInfo: CGBitmapInfo
public let length: Int
public let bytes: UnsafeMutableRawPointer
let provider: CGDataProvider?
private var _context: CGContext?
private let imageBuffer: ASCGImageBuffer
public var bytes: UnsafeMutableRawPointer {
return self.imageBuffer.mutableBytes
}
private let context: CGContext
private var hasGeneratedImage = false
public func withContext(_ f: (CGContext) -> ()) {
if self._context == nil {
if let c = CGContext(data: bytes, width: Int(scaledSize.width), height: Int(scaledSize.height), bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: deviceColorSpace, bitmapInfo: self.bitmapInfo.rawValue) {
c.scaleBy(x: scale, y: scale)
self._context = c
}
}
if let _context = self._context {
_context.translateBy(x: self.size.width / 2.0, y: self.size.height / 2.0)
_context.scaleBy(x: 1.0, y: -1.0)
_context.translateBy(x: -self.size.width / 2.0, y: -self.size.height / 2.0)
f(_context)
_context.translateBy(x: self.size.width / 2.0, y: self.size.height / 2.0)
_context.scaleBy(x: 1.0, y: -1.0)
_context.translateBy(x: -self.size.width / 2.0, y: -self.size.height / 2.0)
}
let context = self.context
context.translateBy(x: self.size.width / 2.0, y: self.size.height / 2.0)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -self.size.width / 2.0, y: -self.size.height / 2.0)
f(context)
context.translateBy(x: self.size.width / 2.0, y: self.size.height / 2.0)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -self.size.width / 2.0, y: -self.size.height / 2.0)
}
public func withFlippedContext(_ f: (CGContext) -> ()) {
if self._context == nil {
if let c = CGContext(data: bytes, width: Int(scaledSize.width), height: Int(scaledSize.height), bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: deviceColorSpace, bitmapInfo: self.bitmapInfo.rawValue) {
c.scaleBy(x: scale, y: scale)
self._context = c
}
}
if let _context = self._context {
f(_context)
}
f(self.context)
}
public init(size: CGSize, scale: CGFloat = 0.0, premultiplied: Bool = true, opaque: Bool = false, clear: Bool = false) {
public init(size: CGSize, scale: CGFloat = 0.0, opaque: Bool = false, clear: Bool = false) {
let actualScale: CGFloat
if scale.isZero {
actualScale = deviceScale
@ -490,35 +451,63 @@ public class DrawingContext {
self.scale = actualScale
self.scaledSize = CGSize(width: size.width * actualScale, height: size.height * actualScale)
self.bytesPerRow = (4 * Int(scaledSize.width) + 15) & (~15)
self.bytesPerRow = DeviceGraphicsContextSettings.shared.bytesPerRow(forWidth: Int(scaledSize.width))
self.length = bytesPerRow * Int(scaledSize.height)
self.imageBuffer = ASCGImageBuffer(length: UInt(self.length))
if opaque {
self.bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.noneSkipFirst.rawValue)
} else if premultiplied {
self.bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue)
self.bitmapInfo = DeviceGraphicsContextSettings.shared.opaqueBitmapInfo
} else {
self.bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.first.rawValue)
self.bitmapInfo = DeviceGraphicsContextSettings.shared.transparentBitmapInfo
}
self.bytes = malloc(length)!
self.context = CGContext(
data: self.imageBuffer.mutableBytes,
width: Int(self.scaledSize.width),
height: Int(self.scaledSize.height),
bitsPerComponent: DeviceGraphicsContextSettings.shared.bitsPerComponent,
bytesPerRow: self.bytesPerRow,
space: DeviceGraphicsContextSettings.shared.colorSpace,
bitmapInfo: self.bitmapInfo.rawValue,
releaseCallback: nil,
releaseInfo: nil
)!
self.context.scaleBy(x: self.scale, y: self.scale)
if clear {
memset(self.bytes, 0, self.length)
}
self.provider = CGDataProvider(dataInfo: bytes, data: bytes, size: length, releaseData: { bytes, _, _ in
free(bytes)
})
assert(self.bytesPerRow % 16 == 0)
assert(Int64(Int(bitPattern: self.bytes)) % 16 == 0)
}
public func generateImage() -> UIImage? {
if self.scaledSize.width.isZero || self.scaledSize.height.isZero {
return nil
}
if let image = CGImage(width: Int(scaledSize.width), height: Int(scaledSize.height), bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: bytesPerRow, space: deviceColorSpace, bitmapInfo: bitmapInfo, provider: provider!, decode: nil, shouldInterpolate: false, intent: .defaultIntent) {
return UIImage(cgImage: image, scale: scale, orientation: .up)
if self.hasGeneratedImage {
#if DEBUG
assert(false)
#endif
return nil
}
self.hasGeneratedImage = true
let dataProvider = self.imageBuffer.createDataProviderAndInvalidate()
if let image = CGImage(
width: Int(self.scaledSize.width),
height: Int(self.scaledSize.height),
bitsPerComponent: self.context.bitsPerComponent,
bitsPerPixel: self.context.bitsPerPixel,
bytesPerRow: self.context.bytesPerRow,
space: DeviceGraphicsContextSettings.shared.colorSpace,
bitmapInfo: self.context.bitmapInfo,
provider: dataProvider,
decode: nil,
shouldInterpolate: true,
intent: .defaultIntent
) {
return UIImage(cgImage: image, scale: self.scale, orientation: .up)
} else {
return nil
}

View File

@ -56,7 +56,7 @@ var deviceScale: CGFloat {
func generateImage(_ size: CGSize, contextGenerator: (CGSize, CGContext) -> Void, opaque: Bool = false, scale: CGFloat? = nil) -> GImage? {
let selectedScale = scale ?? deviceScale
let scaledSize = CGSize(width: size.width * selectedScale, height: size.height * selectedScale)
let bytesPerRow = (4 * Int(scaledSize.width) + 15) & (~15)
let bytesPerRow = (4 * Int(scaledSize.width) + 31) & (~31)
let length = bytesPerRow * Int(scaledSize.height)
let bytes = malloc(length)!.assumingMemoryBound(to: Int8.self)

View File

@ -113,7 +113,7 @@
_offscreenContextWidth = offscreenWidth;
_offscreenContextHeight = offscreenHeight;
_offscreenContextStride = ((4 * _offscreenContextWidth + 15) & (~15));
_offscreenContextStride = ((4 * _offscreenContextWidth + 31) & (~31));
_offscreenMemory = malloc(_offscreenContextStride * _offscreenContextHeight);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

View File

@ -21,7 +21,7 @@ protocol LegacyPaintEntity {
}
private func render(width: Int, height: Int, bytesPerRow: Int, data: Data, type: AnimationRendererFrameType) -> CIImage? {
let calculatedBytesPerRow = (4 * Int(width) + 15) & (~15)
let calculatedBytesPerRow = (4 * Int(width) + 31) & (~31)
assert(bytesPerRow == calculatedBytesPerRow)
let image = generateImagePixel(CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, pixelGenerator: { _, pixelData, bytesPerRow in

View File

@ -44,7 +44,7 @@ public final class ManagedAnimationState {
return nil
}
resolvedInstance = instance
renderContext = DrawingContext(size: displaySize, scale: UIScreenScale, premultiplied: true, clear: true)
renderContext = DrawingContext(size: displaySize, scale: UIScreenScale, clear: true)
}
self.item = item

View File

@ -222,7 +222,7 @@ public final class FFMpegMediaVideoFrameDecoder: MediaTrackFrameDecoder {
var srcCb = vImage_Buffer(data: frame.data[1], height: vImagePixelCount(frame.height), width: vImagePixelCount(frame.width / 2), rowBytes: Int(frame.lineSize[1]))
var srcCr = vImage_Buffer(data: frame.data[2], height: vImagePixelCount(frame.height), width: vImagePixelCount(frame.width / 2), rowBytes: Int(frame.lineSize[2]))
let argbBytesPerRow = (4 * Int(frame.width) + 15) & (~15)
let argbBytesPerRow = (4 * Int(frame.width) + 31) & (~31)
let argbLength = argbBytesPerRow * Int(frame.height)
let argb = malloc(argbLength)!
guard let provider = CGDataProvider(dataInfo: argb, data: argb, size: argbLength, releaseData: { bytes, _, _ in

View File

@ -70,7 +70,7 @@ NSData * _Nullable compressJPEGData(UIImage * _Nonnull sourceImage) {
int width = (int)(sourceImage.size.width * sourceImage.scale);
int height = (int)(sourceImage.size.height * sourceImage.scale);
int targetBytesPerRow = ((4 * (int)width) + 15) & (~15);
int targetBytesPerRow = ((4 * (int)width) + 31) & (~31);
uint8_t *targetMemory = malloc((int)(targetBytesPerRow * height));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
@ -86,7 +86,7 @@ NSData * _Nullable compressJPEGData(UIImage * _Nonnull sourceImage) {
UIGraphicsPopContext();
int bufferBytesPerRow = ((3 * (int)width) + 15) & (~15);
int bufferBytesPerRow = ((3 * (int)width) + 31) & (~31);
uint8_t *buffer = malloc(bufferBytesPerRow * height);
for (int y = 0; y < height; y++) {
@ -158,7 +158,7 @@ NSData * _Nullable compressMiniThumbnail(UIImage * _Nonnull image, CGSize size)
int width = (int)fittedSize.width;
int height = (int)fittedSize.height;
int targetBytesPerRow = ((4 * (int)width) + 15) & (~15);
int targetBytesPerRow = ((4 * (int)width) + 31) & (~31);
uint8_t *targetMemory = malloc((int)(targetBytesPerRow * height));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
@ -174,7 +174,7 @@ NSData * _Nullable compressMiniThumbnail(UIImage * _Nonnull image, CGSize size)
UIGraphicsPopContext();
int bufferBytesPerRow = ((3 * (int)width) + 15) & (~15);
int bufferBytesPerRow = ((3 * (int)width) + 31) & (~31);
uint8_t *buffer = malloc(bufferBytesPerRow * height);
for (int y = 0; y < height; y++) {

View File

@ -46,7 +46,7 @@ public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = n
if let output = filter.outputImage {
let size = Int(output.extent.width)
let bytesPerRow = (4 * Int(size) + 15) & (~15)
let bytesPerRow = DeviceGraphicsContextSettings.shared.bytesPerRow(forWidth: Int(size))
let length = bytesPerRow * size
let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.noneSkipFirst.rawValue)

View File

@ -136,7 +136,7 @@ public func experimentalConvertCompressedLottieToCombinedMp4(data: Data, size: C
return
}
let bytesPerRow = (4 * Int(size.width) + 15) & (~15)
let bytesPerRow = DeviceGraphicsContextSettings.shared.bytesPerRow(forWidth: Int(size.width))
var currentFrame: Int32 = 0

View File

@ -16,7 +16,7 @@ private let motionAmount: CGFloat = 32.0
private func generateBlurredContents(image: UIImage) -> UIImage? {
let size = image.size.aspectFitted(CGSize(width: 64.0, height: 64.0))
let context = DrawingContext(size: size, scale: 1.0, premultiplied: true, opaque: true, clear: false)
let context = DrawingContext(size: size, scale: 1.0, opaque: true, clear: false)
context.withFlippedContext { c in
c.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size))
}

View File

@ -19,7 +19,7 @@
const struct { int width, height; } targetContextSize = { width, height};
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 31) & (~31);
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));