31
31
32
32
// Should moved to SDWebImage Core
33
33
#include < sys/sysctl.h>
34
- static int computeHostNumPhysicalCores ( ) {
34
+ static int computeHostNumLogicalCores ( void ) {
35
35
uint32_t count;
36
36
size_t len = sizeof (count);
37
- sysctlbyname (" hw.physicalcpu " , &count, &len, NULL , 0 );
37
+ sysctlbyname (" hw.logicalcpu " , &count, &len, NULL , 0 );
38
38
if (count < 1 ) {
39
39
int nm[2 ];
40
40
nm[0 ] = CTL_HW;
@@ -373,20 +373,8 @@ - (NSData *)encodedDataWithFrames:(NSArray<SDImageFrame *> *)frames loopCount:(N
373
373
if (options[SDImageCoderEncodeCompressionQuality]) {
374
374
compressionQuality = [options[SDImageCoderEncodeCompressionQuality] doubleValue ];
375
375
}
376
- CGSize maxPixelSize = CGSizeZero;
377
- NSValue *maxPixelSizeValue = options[SDImageCoderEncodeMaxPixelSize];
378
- if (maxPixelSizeValue != nil ) {
379
- #if SD_MAC
380
- maxPixelSize = maxPixelSizeValue.sizeValue ;
381
- #else
382
- maxPixelSize = maxPixelSizeValue.CGSizeValue ;
383
- #endif
384
- }
385
- NSUInteger maxFileSize = 0 ;
386
- if (options[SDImageCoderEncodeMaxFileSize]) {
387
- maxFileSize = [options[SDImageCoderEncodeMaxFileSize] unsignedIntegerValue ];
388
- }
389
376
377
+ // TODO: Animated JPEG-XL Encoding support
390
378
// BOOL encodeFirstFrame = [options[SDImageCoderEncodeFirstFrameOnly] boolValue];
391
379
// if (encodeFirstFrame || frames.count <= 1) {
392
380
//
@@ -461,7 +449,7 @@ - (NSData *)encodedDataWithFrames:(NSArray<SDImageFrame *> *)frames loopCount:(N
461
449
#else
462
450
CGImagePropertyOrientation orientation = kCGImagePropertyOrientationUp ;
463
451
#endif
464
- data = [self sd_encodedJXLDataWithImage: imageRef orientation: orientation quality: compressionQuality maxPixelSize: maxPixelSize maxFileSize: maxFileSize options: nil ];
452
+ data = [self sd_encodedJXLDataWithImage: imageRef orientation: orientation quality: compressionQuality options: options ];
465
453
466
454
return data;
467
455
}
@@ -494,11 +482,9 @@ JxlEncoderStatus EncodeWithEncoder(JxlEncoder* enc, NSMutableData *compressed) {
494
482
}
495
483
496
484
- (nullable NSData *)sd_encodedJXLDataWithImage : (nullable CGImageRef)imageRef
497
- orientation : (CGImagePropertyOrientation)orientation
498
- quality : (double )quality
499
- maxPixelSize : (CGSize)maxPixelSize
500
- maxFileSize : (NSUInteger )maxFileSize
501
- options : (nullable SDImageCoderOptions *)options
485
+ orientation : (CGImagePropertyOrientation)orientation
486
+ quality : (double )quality
487
+ options : (NSDictionary *)options
502
488
{
503
489
if (!imageRef) {
504
490
return nil ;
@@ -586,7 +572,7 @@ - (nullable NSData *)sd_encodedJXLDataWithImage:(nullable CGImageRef)imageRef
586
572
JxlBasicInfo info;
587
573
JxlColorEncoding jxl_color;
588
574
JxlPixelFormat jxl_fmt;
589
- JxlEncoderStatus jret;
575
+ __block JxlEncoderStatus jret;
590
576
591
577
// encoder
592
578
JxlEncoder* enc = JxlEncoderCreate (NULL );
@@ -597,12 +583,28 @@ - (nullable NSData *)sd_encodedJXLDataWithImage:(nullable CGImageRef)imageRef
597
583
/* populate the basic info settings */
598
584
JxlEncoderInitBasicInfo (&info);
599
585
586
+ /* Parse the extra options */
587
+ NSDictionary *frameSetting = options[SDImageCoderEncodeJXLFrameSetting];
588
+ BOOL loseless = options[SDImageCoderEncodeJXLLoseless] ? [options[SDImageCoderEncodeJXLLoseless] boolValue ] : NO ;
589
+ int codeStreamLevel = options[SDImageCoderEncodeJXLCodeStreamLevel] ? [options[SDImageCoderEncodeJXLCodeStreamLevel] intValue ] : -1 ;
590
+
591
+ float distance;
592
+ if (options[SDImageCoderEncodeJXLDistance] != nil ) {
593
+ // prefer JXL distance
594
+ distance = [options[SDImageCoderEncodeJXLDistance] floatValue ];
595
+ } else {
596
+ // convert JPEG quality (0-100) to JXL distance
597
+ distance = JxlEncoderDistanceFromQuality (quality * 100.0 );
598
+ }
599
+ /* bitexact lossless requires there to be no XYB transform */
600
+ info.uses_original_profile = distance == 0.0 ;
601
+
600
602
jxl_fmt.num_channels = (uint32_t )components;
601
603
info.xsize = (uint32_t )width;
602
604
info.ysize = (uint32_t )height;
603
605
info.num_extra_channels = (jxl_fmt.num_channels + 1 ) % 2 ;
604
606
info.num_color_channels = jxl_fmt.num_channels - info.num_extra_channels ;
605
- info.bits_per_sample = bitsPerPixel / jxl_fmt.num_channels ;
607
+ info.bits_per_sample = ( uint32_t ) bitsPerPixel / jxl_fmt.num_channels ;
606
608
info.alpha_bits = (info.num_extra_channels > 0 ) * info.bits_per_sample ;
607
609
// floating point
608
610
if (SD_OPTIONS_CONTAINS (bitmapInfo, kCGBitmapFloatComponents )) {
@@ -618,10 +620,6 @@ - (nullable NSData *)sd_encodedJXLDataWithImage:(nullable CGImageRef)imageRef
618
620
info.orientation = (JxlOrientation)orientation;
619
621
// default endian (little)
620
622
jxl_fmt.endianness = JXL_NATIVE_ENDIAN;
621
- // convert JPEG quality (0-100) to JXL distance
622
- float distance = JxlEncoderDistanceFromQuality (quality * 100.0 );
623
- /* bitexact lossless requires there to be no XYB transform */
624
- info.uses_original_profile = distance == 0.0 ;
625
623
/* rendering intent doesn't matter here
626
624
* but libjxl will whine if we don't set it */
627
625
JxlRenderingIntent render_indent;
@@ -662,23 +660,47 @@ - (nullable NSData *)sd_encodedJXLDataWithImage:(nullable CGImageRef)imageRef
662
660
663
661
/* This needs to be set each time the decoder is reset */
664
662
JxlEncoderFrameSettings* frame_settings = JxlEncoderFrameSettingsCreate (enc, NULL );
665
- jret = JxlEncoderSetFrameDistance (frame_settings, distance);
666
- // JxlEncoderSetExtraChannelDistance(frame_settings, distance);
663
+ jret |= JxlEncoderSetFrameDistance (frame_settings, distance);
664
+ /* Set lossless */
665
+ jret |= JxlEncoderSetFrameLossless (frame_settings, loseless ? 1 : 0 );
666
+ /* Set code steram level */
667
+ jret |= JxlEncoderSetCodestreamLevel (enc, codeStreamLevel);
668
+
669
+ /* Set extra frame setting */
670
+ [frameSetting enumerateKeysAndObjectsUsingBlock: ^(NSNumber * _Nonnull key, NSNumber * _Nonnull value, BOOL * _Nonnull stop) {
671
+ JxlEncoderFrameSettingId frame_key = key.unsignedIntValue ;
672
+ // check the value is floating point or integer
673
+ if ([[value stringValue ] containsString: @" ." ]) {
674
+ // floating point value
675
+ double frame_value = value.doubleValue ;
676
+ jret |= JxlEncoderFrameSettingsSetFloatOption (frame_settings, frame_key, frame_value);
677
+ } else {
678
+ // integer value
679
+ int64_t frame_value = value.integerValue ;
680
+ jret |= JxlEncoderFrameSettingsSetOption (frame_settings, frame_key, frame_value);
681
+ }
682
+ }];
683
+
667
684
if (jret != JXL_ENC_SUCCESS) {
668
685
JxlEncoderDestroy (enc);
669
686
return nil ;
670
687
}
671
688
672
- /* This needs to be set each time the decoder is reset */
673
- size_t threadCount = computeHostNumPhysicalCores ();
674
- void * runner = JxlThreadParallelRunnerCreate (NULL , threadCount);
675
- jret = JxlEncoderSetParallelRunner (enc, JxlThreadParallelRunner, runner);
676
- if (jret != JXL_ENC_SUCCESS) {
677
- JxlEncoderDestroy (enc);
678
- return nil ;
689
+ /* This needs to be set each time the decoder is reset */
690
+ size_t threadCount = [options[SDImageCoderEncodeJXLThreadCount] unsignedIntValue ];
691
+ if (threadCount == 0 ) {
692
+ threadCount = computeHostNumLogicalCores ();
693
+ }
694
+ if (threadCount > 1 ) {
695
+ void * runner = JxlThreadParallelRunnerCreate (NULL , threadCount);
696
+ jret = JxlEncoderSetParallelRunner (enc, JxlThreadParallelRunner, runner);
697
+ if (jret != JXL_ENC_SUCCESS) {
698
+ JxlEncoderDestroy (enc);
699
+ return nil ;
700
+ }
679
701
}
680
702
681
- // Add bitmap buffer
703
+ /* Add frame bitmap buffer */
682
704
jret = JxlEncoderAddImageFrame (frame_settings, &jxl_fmt,
683
705
buffer.bytes ,
684
706
buffer.length );
@@ -688,7 +710,7 @@ - (nullable NSData *)sd_encodedJXLDataWithImage:(nullable CGImageRef)imageRef
688
710
}
689
711
JxlEncoderCloseInput (enc);
690
712
691
- // libjxp support incremental encoding, but we just wait it finished
713
+ /* libjxp support incremental encoding, but we just wait it until finished */
692
714
NSMutableData *output = [NSMutableData data ];
693
715
jret = EncodeWithEncoder (enc, output);
694
716
@@ -701,3 +723,10 @@ - (nullable NSData *)sd_encodedJXLDataWithImage:(nullable CGImageRef)imageRef
701
723
}
702
724
703
725
@end
726
+
727
+ #pragma mark - JXL Encode Options
728
+ SDImageCoderOption SDImageCoderEncodeJXLDistance = @" encodeJXLDistance" ;
729
+ SDImageCoderOption SDImageCoderEncodeJXLLoseless = @" encodeJXLLoseless" ;
730
+ SDImageCoderOption SDImageCoderEncodeJXLCodeStreamLevel = @" encodeJXLCodeStreamLevel" ;
731
+ SDImageCoderOption SDImageCoderEncodeJXLFrameSetting = @" encodeJXLFrameSetting" ;
732
+ SDImageCoderOption SDImageCoderEncodeJXLThreadCount = @" encodeJXLThreadCount" ;
0 commit comments