Skip to content

Commit

Permalink
update to correctly handle ICC profiles and transfer functions
Browse files Browse the repository at this point in the history
  • Loading branch information
awxkee committed Sep 3, 2023
1 parent acd590b commit 62417e8
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 115 deletions.
40 changes: 3 additions & 37 deletions Sources/avifc/AVIFAnimatedDecoder.mm
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#import <Accelerate/Accelerate.h>
#import "AVIFRGBAMultiplier.h"
#import "PlatformImage.h"
#import "AVIFImageXForm.h"

@implementation AVIFAnimatedDecoder {
avifDecoder *_idec;
Expand Down Expand Up @@ -49,43 +50,8 @@ -(nullable CGImageRef)get:(int)frame {
if (nextImageResult != AVIF_RESULT_OK) {
return nil;
}

avifRGBImage rgbImage;
avifRGBImageSetDefaults(&rgbImage, _idec->image);
rgbImage.format = AVIF_RGB_FORMAT_RGBA;
rgbImage.depth = 8;
rgbImage.alphaPremultiplied = true;
avifRGBImageAllocatePixels(&rgbImage);
avifResult rgbResult = avifImageYUVToRGB(_idec->image, &rgbImage);
if (rgbResult != AVIF_RESULT_OK) {
avifRGBImageFreePixels(&rgbImage);
return nil;
}

int newWidth = rgbImage.width;
int newHeight = rgbImage.height;
int newRowBytes = rgbImage.rowBytes;
int depth = rgbImage.depth;
auto storedImage = malloc(rgbImage.height * rgbImage.rowBytes);

if (!storedImage) {
avifRGBImageFreePixels(&rgbImage);
return nil;
}
memcpy(storedImage, rgbImage.pixels, rgbImage.height * rgbImage.rowBytes);
avifRGBImageFreePixels(&rgbImage);

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
int flags = (int)kCGBitmapByteOrder32Big | (int)kCGImageAlphaPremultipliedLast;

CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, storedImage, rgbImage.width*rgbImage.height*4, AV1CGDataProviderReleaseDataCallback);
if (!provider) {
free(storedImage);
return NULL;
}

CGImageRef image = CGImageCreate(newWidth, newHeight, depth, 32, newRowBytes, colorSpace, flags, provider, NULL, false, kCGRenderingIntentDefault);
return image;
auto xForm = [[AVIFImageXForm alloc] init];
return [xForm formCGImage:_idec scale:1];
}

-(int)frameDuration:(int)frame {
Expand Down
103 changes: 25 additions & 78 deletions Sources/avifc/AVIFDataDecoder.mm
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#import "AVIFDataDecoder.h"
#import "AVIFRGBAMultiplier.h"
#import <vector>
#import "AVIFImageXForm.h"

@implementation AVIFDataDecoder {
avifDecoder *_idec;
Expand Down Expand Up @@ -60,50 +61,21 @@ - (nullable Image *)incrementallyDecodeData:(NSData *)data {
// Static image
if (_idec->imageCount >= 1) {
avifResult nextImageResult = avifDecoderNextImage(_idec);
avifRGBImage rgbImage;
avifRGBImageSetDefaults(&rgbImage, _idec->image);
rgbImage.format = AVIF_RGB_FORMAT_RGBA;
rgbImage.alphaPremultiplied = true;
rgbImage.depth = 8;
avifRGBImageAllocatePixels(&rgbImage);
avifResult rgbResult = avifImageYUVToRGB(_idec->image, &rgbImage);
if (rgbResult != AVIF_RESULT_OK) {
NSLog(@"avifImageYUVToRGB %s", avifResultToString(nextImageResult));

avifRGBImageFreePixels(&rgbImage);
if (nextImageResult != AVIF_RESULT_OK) {
NSLog(@"avifImageYUVToRGB %s", avifResultToString(nextImageResult));
avifDecoderDestroy(_idec);
_idec = NULL;
return nil;
}

int newWidth = rgbImage.width;
int newHeight = rgbImage.height;
int newRowBytes = rgbImage.rowBytes;
int depth = rgbImage.depth;
int stride = rgbImage.rowBytes;
auto pixelsData = malloc(stride * newHeight);
memcpy(pixelsData, rgbImage.pixels, stride * newHeight);
avifRGBImageFreePixels(&rgbImage);

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
int flags = (int)kCGBitmapByteOrder32Big | (int)kCGImageAlphaPremultipliedLast;
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixelsData, stride*newHeight, AV1CGDataProviderReleaseDataCallback);
if (!provider) {
free(pixelsData);
CGColorSpaceRelease(colorSpace);
return NULL;

auto xForm = [[AVIFImageXForm alloc] init];
auto image = [xForm form:_idec scale:1];

if (!image) {
return nil;
}
CGImageRef imageRef = CGImageCreate(newWidth, newHeight, depth, 32, newRowBytes, colorSpace, flags, provider, NULL, false, kCGRenderingIntentDefault);
Image *image = nil;
#if AVIF_PLUGIN_MAC
image = [[NSImage alloc] initWithCGImage:imageRef size:CGSizeZero];
#else
image = [UIImage imageWithCGImage:imageRef scale:1 orientation: UIImageOrientationUp];
#endif

CGDataProviderRelease(provider);
CGImageRelease(imageRef);
CGColorSpaceRelease(colorSpace);
return image;
} else if (_idec->imageCount < 1) {
NSLog(@"AVIF Data decoder: image is not already allocated... continue decoding...");
Expand Down Expand Up @@ -205,14 +177,18 @@ - (nullable Image *)decode:(nonnull NSInputStream *)inputStream sampleSize:(CGSi
avifResult decodeResult = avifDecoderParse(decoder.get());
if (decodeResult != AVIF_RESULT_OK) {
NSLog(@"Failed to decode image: %s", avifResultToString(decodeResult));
*error = [[NSError alloc] initWithDomain:@"AVIF" code:500 userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Decoding AVIF failed with: %s", avifResultToString(decodeResult)] }];
*error = [[NSError alloc] initWithDomain:@"AVIF"
code:500
userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Decoding AVIF failed with: %s", avifResultToString(decodeResult)] }];
return nil;
}
// Static image
avifResult nextImageResult = avifDecoderNextImage(decoder.get());
if (nextImageResult != AVIF_RESULT_OK) {
NSLog(@"Failed to decode image: %s", avifResultToString(nextImageResult));
*error = [[NSError alloc] initWithDomain:@"AVIF" code:500 userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Decoding AVIF failed with: %s", avifResultToString(nextImageResult)] }];
*error = [[NSError alloc] initWithDomain:@"AVIF"
code:500
userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Decoding AVIF failed with: %s", avifResultToString(nextImageResult)] }];
return nil;
}

Expand All @@ -229,50 +205,21 @@ - (nullable Image *)decode:(nonnull NSInputStream *)inputStream sampleSize:(CGSi
resizeFactor = sampleSize.height / (float)decoder->image->width;
}

if (!avifImageScale(decoder->image, (float)decoder->image->width*resizeFactor, (float)decoder->image->height*resizeFactor, AVIF_DEFAULT_IMAGE_SIZE_LIMIT, (uint32_t) maxContentSize, &decoder->diag)) {
if (!avifImageScale(decoder->image, (float)decoder->image->width*resizeFactor,
(float)decoder->image->height*resizeFactor, AVIF_DEFAULT_IMAGE_SIZE_LIMIT,
(uint32_t) maxContentSize, &decoder->diag)) {
return nil;
}

}
avifRGBImage rgbImage;
avifRGBImageSetDefaults(&rgbImage, decoder->image);
rgbImage.format = AVIF_RGB_FORMAT_RGBA;
rgbImage.alphaPremultiplied = true;
rgbImage.depth = 8;
avifRGBImageAllocatePixels(&rgbImage);
avifResult rgbResult = avifImageYUVToRGB(decoder->image, &rgbImage);
if (rgbResult != AVIF_RESULT_OK) {
avifRGBImageFreePixels(&rgbImage);
*error = [[NSError alloc] initWithDomain:@"AVIF" code:500 userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Decoding AVIF failed with: %s", avifResultToString(rgbResult)] }];

auto xForm = [[AVIFImageXForm alloc] init];
auto image = [xForm form:decoder.get() scale:scale];

if (!image) {
*error = [[NSError alloc] initWithDomain:@"AVIF" code:500 userInfo:@{ NSLocalizedDescriptionKey: @"Decoding AVIF has failed" }];
return nil;
}

int newWidth = rgbImage.width;
int newHeight = rgbImage.height;
int newRowBytes = rgbImage.rowBytes;
int depth = rgbImage.depth;
int stride = rgbImage.rowBytes;
auto pixelsData = malloc(stride * newHeight);
memcpy(pixelsData, rgbImage.pixels, stride * newHeight);
avifRGBImageFreePixels(&rgbImage);
decoder.reset();

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
int flags = (int)kCGBitmapByteOrder32Big | (int)kCGImageAlphaPremultipliedLast;
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pixelsData, stride*newHeight, AV1CGDataProviderReleaseDataCallback);
if (!provider) {
free(pixelsData);
CGColorSpaceRelease(colorSpace);
return NULL;
}

CGImageRef imageRef = CGImageCreate(newWidth, newHeight, depth, 32, newRowBytes, colorSpace, flags, provider, NULL, false, kCGRenderingIntentDefault);
Image *image = nil;
#if AVIF_PLUGIN_MAC
image = [[NSImage alloc] initWithCGImage:imageRef size:CGSizeZero];
#else
image = [UIImage imageWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp];
#endif

return image;
}

Expand Down
24 changes: 24 additions & 0 deletions Sources/avifc/AVIFImageXForm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// AVIFImageXForm.h
//
//
// Created by Radzivon Bartoshyk on 02/09/2023.
//

#ifndef AVIFImageXForm_h
#define AVIFImageXForm_h

#import "PlatformImage.h"
#if __has_include(<libavif/avif.h>)
#import <libavif/avif.h>
#else
#import "avif/avif.h"
#endif

@interface AVIFImageXForm : NSObject
- (nullable Image*)form:(nonnull avifDecoder*)decoder scale:(CGFloat)scale;
- (_Nullable CGImageRef)formCGImage:(nonnull avifDecoder*)decoder scale:(CGFloat)scale;
@end


#endif /* Header_h */
Loading

0 comments on commit 62417e8

Please sign in to comment.