diff --git a/Sources/avif/AVIFDepth.swift b/Sources/avif/AVIFDepth.swift deleted file mode 100644 index d17c70b..0000000 --- a/Sources/avif/AVIFDepth.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// AVIFDepth.swift -// -// -// Created by Radzivon Bartoshyk on 04/11/2022. -// - -import Foundation - -public enum AVIFDepth: UInt { - case eightBits = 8, tenBits = 10 -} diff --git a/Sources/avif/AVIFEncoder.swift b/Sources/avif/AVIFEncoder.swift index a03db27..d04ac94 100644 --- a/Sources/avif/AVIFEncoder.swift +++ b/Sources/avif/AVIFEncoder.swift @@ -14,8 +14,7 @@ import AppKit #endif public class AVIFEncoder { - public static func encode(image: PlatformImage, quality: Double = 1.0, speed: Int = -1, - depth: AVIFDepth = AVIFDepth.eightBits) throws -> Data { + public static func encode(image: PlatformImage, quality: Double = 1.0, speed: Int = -1) throws -> Data { var newImage = image if newImage.size.width.truncatingRemainder(dividingBy: 2) != 0 || newImage.size.height.truncatingRemainder(dividingBy: 2) != 0 { @@ -23,7 +22,7 @@ public class AVIFEncoder { height: CGFloat((Int(newImage.size.height) / 2) * 2)) newImage = imageScaled(toScale: image, to: newSize) } - return try AVIFEncoding().encode(newImage, speed: speed, depth: depth.rawValue, quality: quality) + return try AVIFEncoding().encode(newImage, speed: speed, quality: quality) } private static func imageScaled(toScale image: PlatformImage, to size: CGSize, scale: CGFloat? = nil) -> PlatformImage { diff --git a/Sources/avifc/AVIFAnimatedEncoder.m b/Sources/avifc/AVIFAnimatedEncoder.mm similarity index 98% rename from Sources/avifc/AVIFAnimatedEncoder.m rename to Sources/avifc/AVIFAnimatedEncoder.mm index 82503b2..c3fd805 100644 --- a/Sources/avifc/AVIFAnimatedEncoder.m +++ b/Sources/avifc/AVIFAnimatedEncoder.mm @@ -35,6 +35,7 @@ - (void* _Nullable)addImage:(Image * _Nonnull)platformImage duration:(NSUInteger avifImage * image = avifImageCreate(width, height, 8, AVIF_PIXEL_FORMAT_YUV420); avifRGBImageSetDefaults(&rgb, image); avifRGBImageAllocatePixels(&rgb); + rgb.alphaPremultiplied = true; memcpy(rgb.pixels, rgba, rgb.rowBytes * image->height); free(rgba); diff --git a/Sources/avifc/AVIFDataDecoder.mm b/Sources/avifc/AVIFDataDecoder.mm index 2f5d214..a60430b 100644 --- a/Sources/avifc/AVIFDataDecoder.mm +++ b/Sources/avifc/AVIFDataDecoder.mm @@ -101,7 +101,7 @@ - (nullable Image *)incrementallyDecodeData:(NSData *)data { image = [UIImage imageWithCGImage:imageRef scale:1 orientation: UIImageOrientationUp]; #endif - CFRelease(provider); + CGDataProviderRelease(provider); CGImageRelease(imageRef); CGColorSpaceRelease(colorSpace); return image; @@ -263,7 +263,7 @@ - (nullable Image *)decode:(nonnull NSInputStream *)inputStream sampleSize:(CGSi #endif CGColorSpaceRelease(colorSpace); - CFRelease(provider); + CGDataProviderRelease(provider); CGImageRelease(imageRef); return image; } diff --git a/Sources/avifc/AVIFEncoding.m b/Sources/avifc/AVIFEncoding.mm similarity index 85% rename from Sources/avifc/AVIFEncoding.m rename to Sources/avifc/AVIFEncoding.mm index abfa920..2aef665 100644 --- a/Sources/avifc/AVIFEncoding.m +++ b/Sources/avifc/AVIFEncoding.mm @@ -14,13 +14,17 @@ #import #include "AVIFEncoding.h" #include "PlatformImage.h" +#include + +static void releaseSharedEncoder(avifEncoder* encoder) { + avifEncoderDestroy(encoder); +} @implementation AVIFEncoding { } - (nullable NSData *)encodeImage:(nonnull Image *)platformImage speed:(NSInteger)speed - depth:(NSUInteger)depth quality:(double)quality error:(NSError * _Nullable *_Nullable)error { unsigned char * rgba = [platformImage rgbaPixels]; #if TARGET_OS_OSX @@ -31,9 +35,10 @@ - (nullable NSData *)encodeImage:(nonnull Image *)platformImage int height = [platformImage size].height * [platformImage scale]; #endif avifRGBImage rgb; - avifImage * image = avifImageCreate(width, height, (uint32_t)depth, AVIF_PIXEL_FORMAT_YUV420); + avifImage * image = avifImageCreate(width, height, (uint32_t)8, AVIF_PIXEL_FORMAT_YUV420); avifRGBImageSetDefaults(&rgb, image); avifRGBImageAllocatePixels(&rgb); + rgb.alphaPremultiplied = true; memcpy(rgb.pixels, rgba, rgb.rowBytes * image->height); free(rgba); @@ -45,7 +50,7 @@ - (nullable NSData *)encodeImage:(nonnull Image *)platformImage return nil; } - avifEncoder * encoder = avifEncoderCreate(); + std::shared_ptr encoder(avifEncoderCreate(), releaseSharedEncoder); encoder->maxThreads = 4; if (quality != 1.0) { int rescaledQuality = AVIF_QUANTIZER_WORST_QUALITY - (int)(quality * AVIF_QUANTIZER_WORST_QUALITY); @@ -55,21 +60,22 @@ - (nullable NSData *)encodeImage:(nonnull Image *)platformImage if (speed != -1) { encoder->speed = (int)MAX(MIN(speed, AVIF_SPEED_FASTEST), AVIF_SPEED_SLOWEST); } - avifResult addImageResult = avifEncoderAddImage(encoder, image, 1, AVIF_ADD_IMAGE_FLAG_SINGLE); + auto encoderPtr = encoder.get(); + avifResult addImageResult = avifEncoderAddImage(encoderPtr, image, 1, AVIF_ADD_IMAGE_FLAG_SINGLE); if (addImageResult != AVIF_RESULT_OK) { avifRGBImageFreePixels(&rgb); avifImageDestroy(image); - avifEncoderDestroy(encoder); + encoder.reset(); *error = [[NSError alloc] initWithDomain:@"AVIFEncoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat: @"add image failed with result: %s", avifResultToString(addImageResult)] }]; return nil; } avifRWData avifOutput = AVIF_DATA_EMPTY; - avifResult finishResult = avifEncoderFinish(encoder, &avifOutput); + avifResult finishResult = avifEncoderFinish(encoderPtr, &avifOutput); if (finishResult != AVIF_RESULT_OK) { avifRGBImageFreePixels(&rgb); avifImageDestroy(image); - avifEncoderDestroy(encoder); + encoder.reset(); *error = [[NSError alloc] initWithDomain:@"AVIFEncoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat: @"encoding failed with result: %s", avifResultToString(addImageResult)] }]; return nil; } @@ -79,7 +85,7 @@ - (nullable NSData *)encodeImage:(nonnull Image *)platformImage avifRWDataFree(&avifOutput); avifRGBImageFreePixels(&rgb); avifImageDestroy(image); - avifEncoderDestroy(encoder); + encoder.reset(); return result; } diff --git a/Sources/avifc/PlatformImage.m b/Sources/avifc/PlatformImage.m index 663c2e6..25fe694 100644 --- a/Sources/avifc/PlatformImage.m +++ b/Sources/avifc/PlatformImage.m @@ -21,13 +21,13 @@ -(nonnull uint8_t *) rgbaPixels { CGImageRef imageRef = [self makeCGImage]; NSUInteger width = CGImageGetWidth(imageRef); NSUInteger height = CGImageGetHeight(imageRef); - int targetBytesPerRow = ((4 * (int)width) + 31) & (~31); - uint8_t *targetMemory = malloc((int)(targetBytesPerRow * height)); + int stride = (int)4 * (int)width * sizeof(uint8_t); + uint8_t *targetMemory = malloc((int)(stride * height)); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - CGBitmapInfo bitmapInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; + CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big; - CGContextRef targetContext = CGBitmapContextCreate(targetMemory, width, height, 8, targetBytesPerRow, colorSpace, bitmapInfo); + CGContextRef targetContext = CGBitmapContextCreate(targetMemory, width, height, 8, stride, colorSpace, bitmapInfo); [NSGraphicsContext saveGraphicsState]; [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithCGContext:targetContext flipped:FALSE]]; @@ -40,28 +40,9 @@ -(nonnull uint8_t *) rgbaPixels { [NSGraphicsContext restoreGraphicsState]; - int bufferBytesPerRow = ((3 * (int)width) + 31) & (~31); - uint8_t *buffer = malloc(bufferBytesPerRow * height); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - uint32_t *color = ((uint32_t *)&targetMemory[y * targetBytesPerRow + x * 4]); - - uint32_t r = ((*color >> 16) & 0xff); - uint32_t g = ((*color >> 8) & 0xff); - uint32_t b = (*color & 0xff); - - buffer[y * bufferBytesPerRow + x * 3 + 0] = r; - buffer[y * bufferBytesPerRow + x * 3 + 1] = g; - buffer[y * bufferBytesPerRow + x * 3 + 2] = b; - } - } - CGContextRelease(targetContext); - free(targetMemory); - - return buffer; + return targetMemory; } #else - (unsigned char *)rgbaPixels { @@ -69,7 +50,7 @@ - (unsigned char *)rgbaPixels { NSUInteger width = CGImageGetWidth(imageRef); NSUInteger height = CGImageGetHeight(imageRef); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - unsigned char *rawData = (unsigned char*) calloc(height * width * 4, sizeof(unsigned char)); + unsigned char *rawData = (unsigned char*) malloc(height * width * 4 * sizeof(uint8_t)); NSUInteger bytesPerPixel = 4; NSUInteger bytesPerRow = bytesPerPixel * width; NSUInteger bitsPerComponent = 8; @@ -80,12 +61,7 @@ - (unsigned char *)rgbaPixels { CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); CGContextRelease(context); - - unsigned char* newBytes = [AVIFRGBAMultiplier unpremultiplyBytes:rawData width:width height:height depth:8]; - if (newBytes) { - free(rawData); - rawData = newBytes; - } + return rawData; } #endif diff --git a/Sources/avifc/include/AVIFEncoding.h b/Sources/avifc/include/AVIFEncoding.h index e646753..2a5aa86 100644 --- a/Sources/avifc/include/AVIFEncoding.h +++ b/Sources/avifc/include/AVIFEncoding.h @@ -17,6 +17,6 @@ @interface AVIFEncoding : NSObject -- (nullable NSData *)encodeImage:(nonnull Image *)platformImage speed:(NSInteger)speed depth:(NSUInteger)depth quality:(double)quality error:(NSError * _Nullable *_Nullable)error; +- (nullable NSData *)encodeImage:(nonnull Image *)platformImage speed:(NSInteger)speed quality:(double)quality error:(NSError * _Nullable *_Nullable)error; @end