Repository: SDWebImage/SDWebImage
File: SDWebImage/Core/SDImageIOAnimatedCoder.m (Objective-C)

Code before:
static NSString * kSDCGImageSourceSkipMetadata = @"kCGImageSourceSkipMetadata";

// This strip the un-wanted CGImageProperty, like the internal CGImageSourceRef in iOS 15+
// However, CGImageCreateCopy still keep those CGImageProperty, not suit for our use case
static CGImageRef __nullable SDCGImageCreateCopy(CGImageRef cg_nullable image) {
    if (!image) return nil;
    size_t width = CGImageGetWidth(image);
    size_t height = CGImageGetHeight(image);
    size_t bitsPerComponent = CGImageGetBitsPerComponent(image);
    size_t bitsPerPixel = CGImageGetBitsPerPixel(image);
    size_t bytesPerRow = CGImageGetBytesPerRow(image);
    CGColorSpaceRef space = CGImageGetColorSpace(image);
    CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(image);
    CGDataProviderRef provider = CGImageGetDataProvider(image);
    const CGFloat *decode = CGImageGetDecode(image);
    bool shouldInterpolate = CGImageGetShouldInterpolate(image);
    CGColorRenderingIntent intent = CGImageGetRenderingIntent(image);
    CGImageRef newImage = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, space, bitmapInfo, provider, decode, shouldInterpolate, intent);
    return newImage;
}

@interface SDImageIOCoderFrame : NSObject

@property (nonatomic, assign) NSUInteger index; // Frame index (zero based)
@property (nonatomic, assign) NSTimeInterval duration; // Frame duration in seconds

@end

@implementation SDImageIOCoderFrame
@end

@implementation SDImageIOAnimatedCoder {
    size_t _width, _height;
    CGImageSourceRef _imageSource;
    BOOL _incremental;
    SD_LOCK_DECLARE(_lock); // Lock only apply for incremental animation decoding
    NSData *_imageData;
    CGFloat _scale;
    NSUInteger _loopCount;
    NSUInteger _frameCount;
    NSArray<SDImageIOCoderFrame *> *_frames;
    BOOL _finished;
    BOOL _preserveAspectRatio;
    CGSize _thumbnailSize;
    NSUInteger _limitBytes;
    BOOL _lazyDecode;
}

- (void)dealloc
{
    if (_imageSource) {

Review comment (refactor):
Is this useful for other thing ? May consider to expose to public API like `+[SDImageCoderHelper CGImageGetRGBA]`

Code after:
static NSString * kSDCGImageSourceSkipMetadata = @"kCGImageSourceSkipMetadata";

// This strip the un-wanted CGImageProperty, like the internal CGImageSourceRef in iOS 15+
// However, CGImageCreateCopy still keep those CGImageProperty, not suit for our use case
static CGImageRef __nullable SDCGImageCreateMutableCopy(CGImageRef cg_nullable image, CGBitmapInfo bitmapInfo) {
    if (!image) return nil;
    size_t width = CGImageGetWidth(image);
    size_t height = CGImageGetHeight(image);
    size_t bitsPerComponent = CGImageGetBitsPerComponent(image);
    size_t bitsPerPixel = CGImageGetBitsPerPixel(image);
    size_t bytesPerRow = CGImageGetBytesPerRow(image);
    CGColorSpaceRef space = CGImageGetColorSpace(image);
    CGDataProviderRef provider = CGImageGetDataProvider(image);
    const CGFloat *decode = CGImageGetDecode(image);
    bool shouldInterpolate = CGImageGetShouldInterpolate(image);
    CGColorRenderingIntent intent = CGImageGetRenderingIntent(image);
    CGImageRef newImage = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, space, bitmapInfo, provider, decode, shouldInterpolate, intent);
    return newImage;
}

static inline CGImageRef __nullable SDCGImageCreateCopy(CGImageRef cg_nullable image) {
    if (!image) return nil;
    return SDCGImageCreateMutableCopy(image, CGImageGetBitmapInfo(image));
}

static BOOL SDLoadOnePixelBitmapBuffer(CGImageRef imageRef, uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *a) {
    CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
    CGImageAlphaInfo alphaInfo = bitmapInfo & kCGBitmapAlphaInfoMask;
    CGBitmapInfo byteOrderInfo = bitmapInfo & kCGBitmapByteOrderMask;
    
    // Get pixels
    CGDataProviderRef provider = CGImageGetDataProvider(imageRef);
    if (!provider) {
        return NO;
    }
    CFDataRef data = CGDataProviderCopyData(provider);
    if (!data) {
        return NO;
    }
    
    CFRange range = CFRangeMake(0, 4); // one pixel
    if (CFDataGetLength(data) < range.location + range.length) {
        CFRelease(data);
        return NO;
    }
    uint8_t pixel[4] = {0};
    CFDataGetBytes(data, range, pixel);
    CFRelease(data);
    
    BOOL byteOrderNormal = NO;
    switch (byteOrderInfo) {