Skip to content

Commit 715cee4

Browse files
committed
Fix the Animated WebP render logic used by SDAnimatedImageView. The current code do extra cost. Refactor it with more simple logic.
1 parent 3f256df commit 715cee4

File tree

1 file changed

+25
-28
lines changed

1 file changed

+25
-28
lines changed

SDWebImageWebPCoder/Classes/SDImageWebPCoder.m

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -795,59 +795,56 @@ - (UIImage *)safeAnimatedImageFrameAtIndex:(NSUInteger)index {
795795
SDWebPCoderFrame *frame = _frames[index];
796796
UIImage *image;
797797
WebPIterator iter;
798+
799+
// Because Animated WebP supports dispose method, which means frames can based on previous canvas context. However, if we clear canvas and loop from the 0 index until the request index, it's harm for performance.
800+
// But when one frame's dispose method is `WEBP_MUX_DISPOSE_BACKGROUND`, the canvas is cleared after the frame decoded. And subsequent frames are not effected by that frame.
801+
// So, we calculate each frame's `blendFromIndex`. Then directly draw canvas from that index, instead of always from 0 index.
802+
798803
if (_currentBlendIndex + 1 == index) {
799-
// If current blend index is equal to request index, normal serial process
804+
// If the request index is subsequence of current blend index, it does not matter what dispose method is. The canvas is always ready.
800805
_currentBlendIndex = index;
806+
NSUInteger startIndex = index;
801807
// libwebp's index start with 1
802-
if (!WebPDemuxGetFrame(_demux, (int)(index + 1), &iter)) {
808+
if (!WebPDemuxGetFrame(_demux, (int)(startIndex + 1), &iter)) {
803809
WebPDemuxReleaseIterator(&iter);
804810
return nil;
805811
}
806-
CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas:_canvas iterator:iter colorSpace:_colorSpace];
807-
if (!imageRef) {
808-
return nil;
809-
}
810-
#if SD_UIKIT || SD_WATCH
811-
image = [[UIImage alloc] initWithCGImage:imageRef scale:_scale orientation:UIImageOrientationUp];
812-
#else
813-
image = [[UIImage alloc] initWithCGImage:imageRef scale:_scale orientation:kCGImagePropertyOrientationUp];
814-
#endif
815-
CGImageRelease(imageRef);
816812
} else {
817-
// Else, this can happen when one image set to different imageViews or one loop end. So we should clear the shared cavans.
813+
// Else, this can happen when one image set to different imageViews or one loop end. So we should clear the canvas. Then draw until the canvas is ready.
818814
if (_currentBlendIndex != NSNotFound) {
819815
CGContextClearRect(_canvas, CGRectMake(0, 0, _canvasWidth, _canvasHeight));
820816
}
821817
_currentBlendIndex = index;
822818

823819
// Then, loop from the blend from index, draw each of previous frames on the canvas.
824-
// We use do while loop to call `WebPDemuxNextFrame`(fast), only (startIndex == endIndex) need to create image instance
820+
// We use do while loop to call `WebPDemuxNextFrame`(fast), until the endIndex meet.
821+
// This also handle the 0 index case.
825822
size_t startIndex = frame.blendFromIndex;
826823
size_t endIndex = frame.index;
824+
// libwebp's index start with 1
827825
if (!WebPDemuxGetFrame(_demux, (int)(startIndex + 1), &iter)) {
828826
WebPDemuxReleaseIterator(&iter);
829827
return nil;
830828
}
831829
do {
832830
@autoreleasepool {
833-
if ((size_t)iter.frame_num == endIndex) {
834-
[self sd_blendWebpImageWithCanvas:_canvas iterator:iter colorSpace:_colorSpace];
835-
} else {
836-
CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas:_canvas iterator:iter colorSpace:_colorSpace];
837-
if (!imageRef) {
838-
return nil;
839-
}
840-
#if SD_UIKIT || SD_WATCH
841-
image = [[UIImage alloc] initWithCGImage:imageRef scale:_scale orientation:UIImageOrientationUp];
842-
#else
843-
image = [[UIImage alloc] initWithCGImage:imageRef scale:_scale orientation:kCGImagePropertyOrientationUp];
844-
#endif
845-
CGImageRelease(imageRef);
846-
}
831+
[self sd_blendWebpImageWithCanvas:_canvas iterator:iter colorSpace:_colorSpace];
847832
}
848833
} while ((size_t)iter.frame_num < (endIndex + 1) && WebPDemuxNextFrame(&iter));
849834
}
850835

836+
// Now the canvas is ready, which respects of dispose method behavior. Just do normal decoding and produce image.
837+
CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas:_canvas iterator:iter colorSpace:_colorSpace];
838+
if (!imageRef) {
839+
return nil;
840+
}
841+
#if SD_UIKIT || SD_WATCH
842+
image = [[UIImage alloc] initWithCGImage:imageRef scale:_scale orientation:UIImageOrientationUp];
843+
#else
844+
image = [[UIImage alloc] initWithCGImage:imageRef scale:_scale orientation:kCGImagePropertyOrientationUp];
845+
#endif
846+
CGImageRelease(imageRef);
847+
851848
WebPDemuxReleaseIterator(&iter);
852849
return image;
853850
}

0 commit comments

Comments
 (0)