@@ -666,9 +666,6 @@ - (void)test32ThatISOHDRDecodeWorks {
666
666
expect (SDRBPC).beLessThanOrEqualTo (8 );
667
667
expect ([SDRImage sd_colorAtPoint: CGPointMake (1 , 1 )]).notTo .beNil ();
668
668
#endif
669
-
670
- // FIXME: Encoding need iOS 18+/macOS 15+
671
- // And need test both GainMap HDR or ISO HDR, TODO
672
669
}
673
670
}
674
671
#endif
@@ -706,6 +703,63 @@ - (void)test33ThatGainMapHDRDecodeWorks {
706
703
#endif
707
704
}
708
705
706
+ - (void )test34ThatHDREncodeWorks {
707
+ // FIXME: Encoding need iOS 18+/macOS 15+
708
+ // GitHub Action virtualization framework contains issue for Gain Map HDR convert:
709
+ if (SDTestCase.isCI ) {
710
+ return ;
711
+ }
712
+ // Actually we test 4 cases, because decoded CGImage can contains gain map or not
713
+ // heic -> heic / heic -> jpeg / jpeg(gain map) -> heic / jpeg(gain map) -> jpeg
714
+ if (@available (macOS 15 , iOS 18 , tvOS 18 , watchOS 11 , *)) {
715
+ NSArray *decodeFormats = @[@" heic" , @" jpeg" ];
716
+ for (NSString *decodeFormat in decodeFormats) {
717
+ NSURL *url = [[NSBundle bundleForClass: [self class ]] URLForResource: @" TestHDR" withExtension: decodeFormat];
718
+ NSData *data = [NSData dataWithContentsOfURL: url];
719
+ // Decoding
720
+ UIImage *HDRImage = [SDImageIOCoder.sharedCoder decodedImageWithData: data options: @{SDImageCoderDecodeToHDR : @(YES )}];
721
+ float headroom = CGImageGetContentHeadroom (HDRImage.CGImage );
722
+ expect (headroom).beGreaterThan (1 );
723
+
724
+ NSArray *encodeFormats = @[@" heic" , @" jpeg" ];
725
+ for (NSString *encodeFormat in encodeFormats) {
726
+ NSLog (@" Testing HDR encodde from original : %@ to %@ " , decodeFormat, encodeFormat);
727
+ // HEIC with ISO Gain Map
728
+ SDImageFormat format = SDImageFormatHEIC;
729
+ if ([encodeFormat isEqualToString: @" jpeg" ]) {
730
+ // JPEG with XMP Gain Map
731
+ format = SDImageFormatJPEG;
732
+ }
733
+ NSData *SDRData = [SDImageIOCoder.sharedCoder encodedDataWithImage: HDRImage format: format options: @{SDImageCoderEncodeToHDR : @(0 )}];
734
+ NSData *HDRData = [SDImageIOCoder.sharedCoder encodedDataWithImage: HDRImage format: format options: @{SDImageCoderEncodeToHDR : @(1 )}];
735
+ NSData *HDRGainMapData = [SDImageIOCoder.sharedCoder encodedDataWithImage: HDRImage format: format options: @{SDImageCoderEncodeToHDR : @(2 )}];
736
+ expect (SDRData).notTo .beNil ();
737
+ expect (HDRData).notTo .beNil ();
738
+ expect (HDRGainMapData).notTo .beNil ();
739
+ // JPEG has no built-in support Gain Map, so it stored in XMP and be larger
740
+ if ([encodeFormat isEqualToString: @" jpeg" ]) {
741
+ expect (HDRGainMapData.length ).beGreaterThan (HDRData.length );
742
+ }
743
+
744
+ // Check gain map information
745
+ CGImageSourceRef source = CGImageSourceCreateWithData ((__bridge CFDataRef)HDRGainMapData, NULL );
746
+ NSDictionary *gainMap = [self gainMapFromImageSource: source];
747
+ expect (gainMap.count ).beGreaterThan (0 );
748
+ // At least gain map contains `kCGImageAuxiliaryDataInfoMetadata`
749
+ CGImageMetadataRef meta = (__bridge CGImageMetadataRef)(gainMap[(__bridge NSString *)kCGImageAuxiliaryDataInfoMetadata ]);
750
+ expect (meta).notTo .beNil ();
751
+
752
+ // A check for redecoded CGImage
753
+ CGImageRef redecodeSDRImage = CGImageSourceCreateImageAtIndex (source, 0 , nil );
754
+ expect (redecodeSDRImage).notTo .beNil ();
755
+ headroom = CGImageGetContentHeadroom (redecodeSDRImage);
756
+ expect (headroom).equal (1 );
757
+ CFRelease (source);
758
+ }
759
+ }
760
+ }
761
+ }
762
+
709
763
#pragma mark - Utils
710
764
711
765
- (void )verifyCoder : (id <SDImageCoder>)coder
@@ -835,6 +889,17 @@ - (NSArray *)thumbnailImagesFromImageSource:(CGImageSourceRef)source API_AVAILAB
835
889
return thumbnailImages;
836
890
}
837
891
892
+ - (NSDictionary *)gainMapFromImageSource : (CGImageSourceRef)source {
893
+ if (@available (macOS 15 , iOS 18 , tvOS 18 , watchOS 11 , *)) {
894
+ CFDictionaryRef HDRGainMap = CGImageSourceCopyAuxiliaryDataInfoAtIndex (source, 0 , kCGImageAuxiliaryDataTypeHDRGainMap );
895
+ CFDictionaryRef ISOGainMap = CGImageSourceCopyAuxiliaryDataInfoAtIndex (source, 0 , kCGImageAuxiliaryDataTypeISOGainMap );
896
+ NSDictionary *result = ISOGainMap ? (__bridge_transfer NSDictionary *)ISOGainMap : (__bridge_transfer NSDictionary *)HDRGainMap;
897
+ return result;
898
+ } else {
899
+ return nil ;
900
+ }
901
+ }
902
+
838
903
#pragma mark - Utils
839
904
- (CGRect)boxRectFromPDFData : (nonnull NSData *)data {
840
905
CGDataProviderRef provider = CGDataProviderCreateWithCFData ((__bridge CFDataRef)data);
0 commit comments