@@ -609,12 +609,16 @@ - (NSData *)encodedDataWithImage:(UIImage *)image format:(SDImageFormat)format o
609
609
if (options[SDImageCoderEncodeCompressionQuality]) {
610
610
compressionQuality = [options[SDImageCoderEncodeCompressionQuality] doubleValue ];
611
611
}
612
+ NSUInteger maxFileSize = 0 ;
613
+ if (options[SDImageCoderEncodeMaxFileSize]) {
614
+ maxFileSize = [options[SDImageCoderEncodeMaxFileSize] unsignedIntegerValue ];
615
+ }
612
616
NSArray <SDImageFrame *> *frames = [SDImageCoderHelper framesFromAnimatedImage: image];
613
617
614
618
BOOL encodeFirstFrame = [options[SDImageCoderEncodeFirstFrameOnly] boolValue ];
615
619
if (encodeFirstFrame || frames.count == 0 ) {
616
620
// for static single webp image
617
- data = [self sd_encodedWebpDataWithImage: image.CGImage quality: compressionQuality];
621
+ data = [self sd_encodedWebpDataWithImage: image.CGImage quality: compressionQuality fileSize: maxFileSize ];
618
622
} else {
619
623
// for animated webp image
620
624
WebPMux *mux = WebPMuxNew ();
@@ -623,7 +627,7 @@ - (NSData *)encodedDataWithImage:(UIImage *)image format:(SDImageFormat)format o
623
627
}
624
628
for (size_t i = 0 ; i < frames.count ; i++) {
625
629
SDImageFrame *currentFrame = frames[i];
626
- NSData *webpData = [self sd_encodedWebpDataWithImage: currentFrame.image.CGImage quality: compressionQuality];
630
+ NSData *webpData = [self sd_encodedWebpDataWithImage: currentFrame.image.CGImage quality: compressionQuality fileSize: maxFileSize ];
627
631
int duration = currentFrame.duration * 1000 ;
628
632
WebPMuxFrameInfo frame = { .bitstream .bytes = webpData.bytes ,
629
633
.bitstream .size = webpData.length ,
@@ -660,7 +664,7 @@ - (NSData *)encodedDataWithImage:(UIImage *)image format:(SDImageFormat)format o
660
664
return data;
661
665
}
662
666
663
- - (nullable NSData *)sd_encodedWebpDataWithImage : (nullable CGImageRef )imageRef quality : (double )quality {
667
+ - (nullable NSData *)sd_encodedWebpDataWithImage : (nullable CGImageRef )imageRef quality : (double )quality fileSize : ( NSUInteger ) fileSize {
664
668
NSData *webpData;
665
669
if (!imageRef) {
666
670
return nil ;
@@ -704,7 +708,7 @@ - (nullable NSData *)sd_encodedWebpDataWithImage:(nullable CGImageRef)imageRef q
704
708
return nil ;
705
709
}
706
710
707
- uint8_t *rgba = NULL ;
711
+ uint8_t *rgba = NULL ; // RGBA Buffer managed by CFData, don't call `free` on it, instead call `CFRelease` on `dataRef`
708
712
// We could not assume that input CGImage's color mode is always RGB888/RGBA8888. Convert all other cases to target color mode using vImage
709
713
if (byteOrderNormal && ((alphaInfo == kCGImageAlphaNone ) || (alphaInfo == kCGImageAlphaLast ))) {
710
714
// If the input CGImage is already RGB888/RGBA8888
@@ -758,34 +762,58 @@ - (nullable NSData *)sd_encodedWebpDataWithImage:(nullable CGImageRef)imageRef q
758
762
759
763
rgba = dest.data ; // Converted buffer
760
764
bytesPerRow = dest.rowBytes ; // Converted bytePerRow
761
- CFRelease (dataRef);
762
- dataRef = NULL ;
765
+ CFRelease (dataRef); // Use CFData to manage bytes for free, the same code path for error handling
766
+ dataRef = CFDataCreateWithBytesNoCopy ( kCFAllocatorDefault , rgba, bytesPerRow * height, kCFAllocatorDefault ) ;
763
767
}
764
768
765
- uint8_t *data = NULL ; // Output WebP data
766
769
float qualityFactor = quality * 100 ; // WebP quality is 0-100
767
770
// Encode RGB888/RGBA8888 buffer to WebP data
768
- size_t size;
771
+ // Using the libwebp advanced API: https://developers.google.com/speed/webp/docs/api#advanced_encoding_api
772
+ WebPConfig config;
773
+ WebPPicture picture;
774
+ WebPMemoryWriter writer;
775
+
776
+ if (!WebPConfigPreset (&config, WEBP_PRESET_DEFAULT, qualityFactor) ||
777
+ !WebPPictureInit (&picture)) {
778
+ // shouldn't happen, except if system installation is broken
779
+ CFRelease (dataRef);
780
+ return nil ;
781
+ }
782
+
783
+ config.target_size = (int )fileSize; // Max filesize for output, 0 means use quality instead
784
+ config.thread_level = 1 ; // Thread encoding for fast
785
+ config.lossless = 0 ; // Disable lossless encoding (If we need, can add new Encoding Options in future version)
786
+ picture.use_argb = config.lossless ; // Lossy encoding use YUV for internel bitstream
787
+ picture.width = (int )width;
788
+ picture.height = (int )height;
789
+ picture.writer = WebPMemoryWrite; // Output in memory data buffer
790
+ picture.custom_ptr = &writer;
791
+ WebPMemoryWriterInit (&writer);
792
+
793
+ int result;
769
794
if (hasAlpha) {
770
- size = WebPEncodeRGBA (rgba, ( int )width , (int )height, ( int ) bytesPerRow, qualityFactor, &data );
795
+ result = WebPPictureImportRGBA (&picture, rgba , (int )bytesPerRow);
771
796
} else {
772
- size = WebPEncodeRGB (rgba, ( int )width , (int )height, ( int ) bytesPerRow, qualityFactor, &data );
797
+ result = WebPPictureImportRGB (&picture, rgba , (int )bytesPerRow);
773
798
}
774
- if (dataRef) {
775
- CFRelease (dataRef); // free non-converted rgba buffer
776
- dataRef = NULL ;
777
- } else {
778
- free (rgba); // free converted rgba buffer
779
- rgba = NULL ;
799
+ if (!result) {
800
+ WebPMemoryWriterClear (&writer);
801
+ CFRelease (dataRef);
802
+ return nil ;
780
803
}
781
804
782
- if (size) {
805
+ result = WebPEncode (&config, &picture);
806
+ CFRelease (dataRef); // Free bitmap buffer
807
+ WebPPictureFree (&picture);
808
+
809
+ if (result) {
783
810
// success
784
- webpData = [NSData dataWithBytes: data length: size];
785
- }
786
- if (data) {
787
- WebPFree (data) ;
811
+ webpData = [NSData dataWithBytes: writer.mem length: writer. size];
812
+ } else {
813
+ // failed
814
+ webpData = nil ;
788
815
}
816
+ WebPMemoryWriterClear (&writer);
789
817
790
818
return webpData;
791
819
}
0 commit comments