@@ -650,29 +650,110 @@ - (BOOL)sd_encodeFrameWithEnc:(JxlEncoder*)enc
650
650
if (!imageRef) {
651
651
return NO ;
652
652
}
653
+ // bitmap info from CGImage
654
+ __unused size_t width = CGImageGetWidth (imageRef);
655
+ size_t height = CGImageGetHeight (imageRef);
656
+ size_t bitsPerComponent = CGImageGetBitsPerComponent (imageRef);
657
+ size_t bitsPerPixel = CGImageGetBitsPerPixel (imageRef);
658
+ size_t components = bitsPerPixel / bitsPerComponent;
659
+ size_t bytesPerRow = CGImageGetBytesPerRow (imageRef);
660
+ CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo (imageRef);
661
+ CGImageAlphaInfo alphaInfo = bitmapInfo & kCGBitmapAlphaInfoMask ;
662
+ CGImageByteOrderInfo byteOrderInfo = bitmapInfo & kCGBitmapByteOrderMask ;
663
+ BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
664
+ alphaInfo == kCGImageAlphaNoneSkipFirst ||
665
+ alphaInfo == kCGImageAlphaNoneSkipLast );
666
+ BOOL byteOrderNormal = NO ;
667
+ switch (byteOrderInfo) {
668
+ case kCGBitmapByteOrderDefault : {
669
+ byteOrderNormal = YES ;
670
+ } break ;
671
+ case kCGBitmapByteOrder16Little :
672
+ case kCGBitmapByteOrder32Little : {
673
+ } break ;
674
+ case kCGBitmapByteOrder16Big :
675
+ case kCGBitmapByteOrder32Big : {
676
+ byteOrderNormal = YES ;
677
+ } break ;
678
+ default : break ;
679
+ }
680
+ // We must prefer the input CGImage's color space, which may contains ICC profile
681
+ CGColorSpaceRef colorSpace = CGImageGetColorSpace (imageRef);
682
+ // We only supports RGB colorspace, filter the un-supported one (like Monochrome, CMYK, etc)
683
+ if (CGColorSpaceGetModel (colorSpace) != kCGColorSpaceModelRGB ) {
684
+ // Ignore and convert, we don't know how to encode this colorspace directlly to WebP
685
+ // This may cause little visible difference because of colorpsace conversion
686
+ colorSpace = NULL ;
687
+ }
688
+ CGColorRenderingIntent renderingIntent = CGImageGetRenderingIntent (imageRef);
653
689
654
- // If we can not get bitmap buffer, early return
655
- CGDataProviderRef dataProvider = CGImageGetDataProvider (imageRef);
656
- if (!dataProvider) {
657
- return NO ;
690
+ // TODO: ugly code, libjxl supports RGBA order only, but input CGImage maybe BGRA, ARGB, etc
691
+ // see: encode.h JxlDataType
692
+ // * TODO(lode): support different channel orders if needed (RGB, BGR, ...)
693
+ // Begin here vImage <---
694
+ // RGB888/RGBA8888 (Non-premultiplied to works for libjxl)
695
+ CGImageAlphaInfo destAlphaInfo;
696
+ if (hasAlpha) {
697
+ if (components == 4 ) {
698
+ destAlphaInfo = kCGImageAlphaLast ;
699
+ } else if (components == 1 ) {
700
+ destAlphaInfo = kCGImageAlphaOnly ;
701
+ } else {
702
+ // Unsupported!
703
+ destAlphaInfo = alphaInfo;
704
+ }
705
+ } else {
706
+ if (components == 4 ) {
707
+ destAlphaInfo = kCGImageAlphaNoneSkipLast ;
708
+ } else if (components == 3 ) {
709
+ destAlphaInfo = kCGImageAlphaNone ;
710
+ } else {
711
+ // Unsupported!
712
+ destAlphaInfo = alphaInfo;
713
+ }
714
+ }
715
+ CGImageByteOrderInfo destByteOrderInfo = byteOrderInfo;
716
+ if (!byteOrderNormal) {
717
+ // not RGB order, need reverse...
718
+ if (byteOrderInfo == kCGImageByteOrder16Little ) {
719
+ destByteOrderInfo = kCGImageByteOrder16Big ;
720
+ } else if (byteOrderInfo == kCGImageByteOrder32Little ) {
721
+ destByteOrderInfo = kCGImageByteOrder32Big ;
722
+ }
723
+ }
724
+ CGBitmapInfo destBitmapInfo = (CGBitmapInfo)destAlphaInfo | (CGBitmapInfo)destByteOrderInfo;
725
+ if (SD_OPTIONS_CONTAINS (bitmapInfo, kCGBitmapFloatComponents )) {
726
+ destBitmapInfo |= kCGBitmapFloatComponents ;
658
727
}
659
728
660
- NSData *buffer = (__bridge_transfer NSData *) CGDataProviderCopyData (dataProvider);
661
- if (!buffer) {
729
+ uint8_t *rgba = NULL ; // RGBA Buffer managed by CFData, don't call `free` on it, instead call `CFRelease` on `dataRef`
730
+ vImage_CGImageFormat destFormat = {
731
+ .bitsPerComponent = (uint32_t )bitsPerComponent,
732
+ .bitsPerPixel = (uint32_t )bitsPerPixel,
733
+ .colorSpace = colorSpace,
734
+ .bitmapInfo = destBitmapInfo,
735
+ .renderingIntent = renderingIntent
736
+ };
737
+ vImage_Buffer dest;
738
+ // We could not assume that input CGImage's color mode is always RGB888/RGBA8888. Convert all other cases to target color mode using vImage
739
+ // But vImageBuffer_InitWithCGImage will do convert automatically (unless you use `kvImageNoAllocate`), so no need to call `vImageConvert` by ourselves
740
+ vImage_Error error = vImageBuffer_InitWithCGImage (&dest, &destFormat, NULL , imageRef, kvImageNoFlags);
741
+ if (error != kvImageNoError) {
662
742
return NO ;
663
743
}
664
- CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo (imageRef) ;
665
- size_t bitsPerComponent = CGImageGetBitsPerComponent (imageRef) ;
666
- size_t bitsPerPixel = CGImageGetBitsPerPixel (imageRef) ;
667
- size_t components = bitsPerPixel / bitsPerComponent;
744
+ rgba = dest. data ;
745
+ bytesPerRow = dest. rowBytes ;
746
+ size_t rgba_size = bytesPerRow * height ;
747
+ // End here vImage --->
668
748
669
749
JxlEncoderStatus jret = JXL_ENC_SUCCESS;
670
750
JxlPixelFormat jxl_fmt;
671
751
672
752
/* Set the current frame pixel format */
673
753
jxl_fmt.num_channels = (uint32_t )components;
674
754
// CGImage has its own alignment, since we don't use vImage to re-align the input buffer, don't apply here
675
- jxl_fmt.align = 0 ;
755
+ size_t alignment = (bitsPerComponent / 8 ) * components * 8 ;
756
+ jxl_fmt.align = alignment;
676
757
// default endian (little)
677
758
jxl_fmt.endianness = JXL_NATIVE_ENDIAN;
678
759
// floating point
@@ -702,8 +783,10 @@ - (BOOL)sd_encodeFrameWithEnc:(JxlEncoder*)enc
702
783
703
784
/* Add frame bitmap buffer */
704
785
jret = JxlEncoderAddImageFrame (frame_settings, &jxl_fmt,
705
- buffer.bytes ,
706
- buffer.length );
786
+ rgba,
787
+ rgba_size);
788
+ // free the vImage allocated buffer
789
+ free (rgba);
707
790
if (jret != JXL_ENC_SUCCESS) {
708
791
return NO ;
709
792
}
0 commit comments