Authored by DreamPiggy

Add support to animted WebP dispose method

dispose method is widely used in animated WebP images and should add support
@@ -53,12 +53,16 @@ static void FreeImageData(void *info, const void *data, size_t size) { @@ -53,12 +53,16 @@ static void FreeImageData(void *info, const void *data, size_t size) {
53 NSMutableArray *images = [NSMutableArray array]; 53 NSMutableArray *images = [NSMutableArray array];
54 NSTimeInterval duration = 0; 54 NSTimeInterval duration = 0;
55 55
  56 + int canvasWidth = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH);
  57 + int canvasHeight = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT);
  58 + CGContextRef canvas = CGBitmapContextCreate(NULL, canvasWidth, canvasHeight, 8, 0, sd_CGColorSpaceGetDeviceRGB(), kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast);
  59 +
56 do { 60 do {
57 UIImage *image; 61 UIImage *image;
58 if (iter.blend_method == WEBP_MUX_BLEND) { 62 if (iter.blend_method == WEBP_MUX_BLEND) {
59 - image = [self sd_blendWebpImageWithOriginImage:images.lastObject demuxer:demuxer iterator:iter]; 63 + image = [self sd_blendWebpImageWithCanvas:canvas iterator:iter];
60 } else { 64 } else {
61 - image = [self sd_nonblendWebpImageWithDemuxer:demuxer iterator:iter]; 65 + image = [self sd_nonblendWebpImageWithCanvas:canvas iterator:iter];
62 } 66 }
63 67
64 if (!image) { 68 if (!image) {
@@ -72,6 +76,7 @@ static void FreeImageData(void *info, const void *data, size_t size) { @@ -72,6 +76,7 @@ static void FreeImageData(void *info, const void *data, size_t size) {
72 76
73 WebPDemuxReleaseIterator(&iter); 77 WebPDemuxReleaseIterator(&iter);
74 WebPDemuxDelete(demuxer); 78 WebPDemuxDelete(demuxer);
  79 + CGContextRelease(canvas);
75 80
76 UIImage *finalImage = nil; 81 UIImage *finalImage = nil;
77 #if SD_UIKIT || SD_WATCH 82 #if SD_UIKIT || SD_WATCH
@@ -85,29 +90,21 @@ static void FreeImageData(void *info, const void *data, size_t size) { @@ -85,29 +90,21 @@ static void FreeImageData(void *info, const void *data, size_t size) {
85 } 90 }
86 91
87 92
88 -+ (nullable UIImage *)sd_blendWebpImageWithOriginImage:(nullable UIImage *)originImage demuxer:(WebPDemuxer *)demuxer iterator:(WebPIterator)iter {  
89 - if (!originImage) {  
90 - return nil;  
91 - }  
92 - 93 ++ (nullable UIImage *)sd_blendWebpImageWithCanvas:(CGContextRef)canvas iterator:(WebPIterator)iter {
93 UIImage *image = [self sd_rawWebpImageWithData:iter.fragment]; 94 UIImage *image = [self sd_rawWebpImageWithData:iter.fragment];
94 if (!image) { 95 if (!image) {
95 return nil; 96 return nil;
96 } 97 }
97 98
98 - int canvasWidth = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH);  
99 - int canvasHeight = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT); 99 + size_t canvasWidth = CGBitmapContextGetWidth(canvas);
  100 + size_t canvasHeight = CGBitmapContextGetHeight(canvas);
100 CGSize size = CGSizeMake(canvasWidth, canvasHeight); 101 CGSize size = CGSizeMake(canvasWidth, canvasHeight);
101 CGFloat tmpX = iter.x_offset; 102 CGFloat tmpX = iter.x_offset;
102 CGFloat tmpY = size.height - iter.height - iter.y_offset; 103 CGFloat tmpY = size.height - iter.height - iter.y_offset;
103 CGRect imageRect = CGRectMake(tmpX, tmpY, iter.width, iter.height); 104 CGRect imageRect = CGRectMake(tmpX, tmpY, iter.width, iter.height);
104 105
105 - CGColorSpaceRef colorSpaceRef = sd_CGColorSpaceGetDeviceRGB();  
106 - uint32_t bitmapInfo = iter.has_alpha ? kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast : kCGBitmapByteOrder32Big | kCGImageAlphaNoneSkipLast;  
107 - CGContextRef blendCanvas = CGBitmapContextCreate(NULL, size.width, size.height, 8, 0, colorSpaceRef, bitmapInfo);  
108 - CGContextDrawImage(blendCanvas, CGRectMake(0, 0, size.width, size.height), originImage.CGImage);  
109 - CGContextDrawImage(blendCanvas, imageRect, image.CGImage);  
110 - CGImageRef newImageRef = CGBitmapContextCreateImage(blendCanvas); 106 + CGContextDrawImage(canvas, imageRect, image.CGImage);
  107 + CGImageRef newImageRef = CGBitmapContextCreateImage(canvas);
111 108
112 #if SD_UIKIT || SD_WATCH 109 #if SD_UIKIT || SD_WATCH
113 image = [UIImage imageWithCGImage:newImageRef]; 110 image = [UIImage imageWithCGImage:newImageRef];
@@ -116,31 +113,30 @@ static void FreeImageData(void *info, const void *data, size_t size) { @@ -116,31 +113,30 @@ static void FreeImageData(void *info, const void *data, size_t size) {
116 #endif 113 #endif
117 114
118 CGImageRelease(newImageRef); 115 CGImageRelease(newImageRef);
119 - CGContextRelease(blendCanvas); 116 +
  117 + if (iter.dispose_method == WEBP_MUX_DISPOSE_NONE) {
  118 + // do not dispose
  119 + } else {
  120 + CGContextClearRect(canvas, imageRect);
  121 + }
120 122
121 return image; 123 return image;
122 } 124 }
123 125
124 -+ (nullable UIImage *)sd_nonblendWebpImageWithDemuxer:(WebPDemuxer *)demuxer iterator:(WebPIterator)iter { 126 ++ (nullable UIImage *)sd_nonblendWebpImageWithCanvas:(CGContextRef)canvas iterator:(WebPIterator)iter {
125 UIImage *image = [self sd_rawWebpImageWithData:iter.fragment]; 127 UIImage *image = [self sd_rawWebpImageWithData:iter.fragment];
126 if (!image) { 128 if (!image) {
127 return nil; 129 return nil;
128 } 130 }
129 131
130 - if (iter.x_offset == 0 && iter.y_offset == 0) {  
131 - return image;  
132 - }  
133 -  
134 - int canvasWidth = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH);  
135 - int canvasHeight = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT); 132 + size_t canvasWidth = CGBitmapContextGetWidth(canvas);
  133 + size_t canvasHeight = CGBitmapContextGetHeight(canvas);
136 CGSize size = CGSizeMake(canvasWidth, canvasHeight); 134 CGSize size = CGSizeMake(canvasWidth, canvasHeight);
137 CGFloat tmpX = iter.x_offset; 135 CGFloat tmpX = iter.x_offset;
138 CGFloat tmpY = size.height - iter.height - iter.y_offset; 136 CGFloat tmpY = size.height - iter.height - iter.y_offset;
139 CGRect imageRect = CGRectMake(tmpX, tmpY, iter.width, iter.height); 137 CGRect imageRect = CGRectMake(tmpX, tmpY, iter.width, iter.height);
140 138
141 - CGColorSpaceRef colorSpaceRef = sd_CGColorSpaceGetDeviceRGB();  
142 - uint32_t bitmapInfo = iter.has_alpha ? kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast : kCGBitmapByteOrder32Big | kCGImageAlphaNoneSkipLast;  
143 - CGContextRef canvas = CGBitmapContextCreate(NULL, size.width, size.height, 8, 0, colorSpaceRef, bitmapInfo); 139 + CGContextClearRect(canvas, imageRect);
144 CGContextDrawImage(canvas, imageRect, image.CGImage); 140 CGContextDrawImage(canvas, imageRect, image.CGImage);
145 CGImageRef newImageRef = CGBitmapContextCreateImage(canvas); 141 CGImageRef newImageRef = CGBitmapContextCreateImage(canvas);
146 142
@@ -151,7 +147,12 @@ static void FreeImageData(void *info, const void *data, size_t size) { @@ -151,7 +147,12 @@ static void FreeImageData(void *info, const void *data, size_t size) {
151 #endif 147 #endif
152 148
153 CGImageRelease(newImageRef); 149 CGImageRelease(newImageRef);
154 - CGContextRelease(canvas); 150 +
  151 + if (iter.dispose_method == WEBP_MUX_DISPOSE_NONE) {
  152 + // do not dispose
  153 + } else {
  154 + CGContextClearRect(canvas, imageRect);
  155 + }
155 156
156 return image; 157 return image;
157 } 158 }
@@ -202,7 +203,7 @@ static void FreeImageData(void *info, const void *data, size_t size) { @@ -202,7 +203,7 @@ static void FreeImageData(void *info, const void *data, size_t size) {
202 return image; 203 return image;
203 } 204 }
204 205
205 -CGColorSpaceRef sd_CGColorSpaceGetDeviceRGB() { 206 +static CGColorSpaceRef sd_CGColorSpaceGetDeviceRGB() {
206 static CGColorSpaceRef space; 207 static CGColorSpaceRef space;
207 static dispatch_once_t onceToken; 208 static dispatch_once_t onceToken;
208 dispatch_once(&onceToken, ^{ 209 dispatch_once(&onceToken, ^{