@@ -536,38 +536,180 @@ - (nullable UIImage *)sd_flippedImageWithHorizontal:(BOOL)horizontal vertical:(B
536
536
537
537
#pragma mark - Image Blending
538
538
539
+ static NSString * _Nullable SDGetCIFilterNameFromBlendMode (CGBlendMode blendMode) {
540
+ // CGBlendMode: https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_images/dq_images.html#//apple_ref/doc/uid/TP30001066-CH212-CJBIJEFG
541
+ // CIFilter: https://developer.apple.com/library/archive/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html#//apple_ref/doc/uid/TP30000136-SW71
542
+ NSString *filterName;
543
+ switch (blendMode) {
544
+ case kCGBlendModeMultiply :
545
+ filterName = @" CIMultiplyBlendMode" ;
546
+ break ;
547
+ case kCGBlendModeScreen :
548
+ filterName = @" CIScreenBlendMode" ;
549
+ break ;
550
+ case kCGBlendModeOverlay :
551
+ filterName = @" CIOverlayBlendMode" ;
552
+ break ;
553
+ case kCGBlendModeDarken :
554
+ filterName = @" CIDarkenBlendMode" ;
555
+ break ;
556
+ case kCGBlendModeLighten :
557
+ filterName = @" CILightenBlendMode" ;
558
+ break ;
559
+ case kCGBlendModeColorDodge :
560
+ filterName = @" CIColorDodgeBlendMode" ;
561
+ break ;
562
+ case kCGBlendModeColorBurn :
563
+ filterName = @" CIColorBurnBlendMode" ;
564
+ break ;
565
+ case kCGBlendModeSoftLight :
566
+ filterName = @" CISoftLightBlendMode" ;
567
+ break ;
568
+ case kCGBlendModeHardLight :
569
+ filterName = @" CIHardLightBlendMode" ;
570
+ break ;
571
+ case kCGBlendModeDifference :
572
+ filterName = @" CIDifferenceBlendMode" ;
573
+ break ;
574
+ case kCGBlendModeExclusion :
575
+ filterName = @" CIExclusionBlendMode" ;
576
+ break ;
577
+ case kCGBlendModeHue :
578
+ filterName = @" CIHueBlendMode" ;
579
+ break ;
580
+ case kCGBlendModeSaturation :
581
+ filterName = @" CISaturationBlendMode" ;
582
+ break ;
583
+ case kCGBlendModeColor :
584
+ // Color blend mode uses the luminance values of the background with the hue and saturation values of the source image.
585
+ filterName = @" CIColorBlendMode" ;
586
+ break ;
587
+ case kCGBlendModeLuminosity :
588
+ filterName = @" CILuminosityBlendMode" ;
589
+ break ;
590
+
591
+ // macOS 10.5+
592
+ case kCGBlendModeSourceAtop :
593
+ case kCGBlendModeDestinationAtop :
594
+ filterName = @" CISourceAtopCompositing" ;
595
+ break ;
596
+ case kCGBlendModeSourceIn :
597
+ case kCGBlendModeDestinationIn :
598
+ filterName = @" CISourceInCompositing" ;
599
+ break ;
600
+ case kCGBlendModeSourceOut :
601
+ case kCGBlendModeDestinationOut :
602
+ filterName = @" CISourceOutCompositing" ;
603
+ break ;
604
+ case kCGBlendModeNormal : // SourceOver
605
+ case kCGBlendModeDestinationOver :
606
+ filterName = @" CISourceOverCompositing" ;
607
+ break ;
608
+
609
+ // need special handling
610
+ case kCGBlendModeClear :
611
+ // use clear color instead
612
+ break ;
613
+ case kCGBlendModeCopy :
614
+ // use input color instead
615
+ break ;
616
+ case kCGBlendModeXOR :
617
+ // unsupported
618
+ break ;
619
+ case kCGBlendModePlusDarker :
620
+ // chain filters
621
+ break ;
622
+ case kCGBlendModePlusLighter :
623
+ // chain filters
624
+ break ;
625
+ }
626
+ return filterName;
627
+ }
628
+
539
629
- (nullable UIImage *)sd_tintedImageWithColor : (nonnull UIColor *)tintColor {
630
+ return [self sd_tintedImageWithColor: tintColor blendMode: kCGBlendModeSourceAtop ];
631
+ }
632
+
633
+ - (nullable UIImage *)sd_tintedImageWithColor : (nonnull UIColor *)tintColor blendMode : (CGBlendMode)blendMode {
540
634
BOOL hasTint = CGColorGetAlpha (tintColor.CGColor ) > __FLT_EPSILON__;
541
635
if (!hasTint) {
542
636
return self;
543
637
}
544
638
639
+ // blend mode, see https://en.wikipedia.org/wiki/Alpha_compositing
545
640
#if SD_UIKIT || SD_MAC
546
641
// CIImage shortcut
547
- if ( self.CIImage ) {
548
- CIImage * ciImage = self. CIImage ;
642
+ CIImage *ciImage = self.CIImage ;
643
+ if ( ciImage) {
549
644
CIImage *colorImage = [CIImage imageWithColor: [[CIColor alloc ] initWithColor: tintColor]];
550
645
colorImage = [colorImage imageByCroppingToRect: ciImage.extent];
551
- CIFilter *filter = [CIFilter filterWithName: @" CISourceAtopCompositing" ];
552
- [filter setValue: colorImage forKey: kCIInputImageKey ];
553
- [filter setValue: ciImage forKey: kCIInputBackgroundImageKey ];
554
- ciImage = filter.outputImage ;
646
+ NSString *filterName = SDGetCIFilterNameFromBlendMode (blendMode);
647
+ // Some blend mode is not nativelly supported
648
+ if (filterName) {
649
+ CIFilter *filter = [CIFilter filterWithName: filterName];
650
+ [filter setValue: colorImage forKey: kCIInputImageKey ];
651
+ [filter setValue: ciImage forKey: kCIInputBackgroundImageKey ];
652
+ ciImage = filter.outputImage ;
653
+ } else {
654
+ if (blendMode == kCGBlendModeClear ) {
655
+ // R = 0
656
+ CIColor *clearColor;
657
+ if (@available (iOS 10.0 , macOS 10.12 , tvOS 10.0 , *)) {
658
+ clearColor = CIColor .clearColor ;
659
+ } else {
660
+ clearColor = [[CIColor alloc ] initWithColor: UIColor.clearColor];
661
+ }
662
+ ciImage = [CIImage imageWithColor: clearColor];
663
+ } else if (blendMode == kCGBlendModeCopy ) {
664
+ // R = S
665
+ ciImage = colorImage;
666
+ } else if (blendMode == kCGBlendModePlusLighter ) {
667
+ // R = MIN(1, S + D)
668
+ // S + D
669
+ CIFilter *filter = [CIFilter filterWithName: @" CIAdditionCompositing" ];
670
+ [filter setValue: colorImage forKey: kCIInputImageKey ];
671
+ [filter setValue: ciImage forKey: kCIInputBackgroundImageKey ];
672
+ ciImage = filter.outputImage ;
673
+ // MIN
674
+ ciImage = [ciImage imageByApplyingFilter: @" CIColorClamp" withInputParameters: nil ];
675
+ } else if (blendMode == kCGBlendModePlusDarker ) {
676
+ // R = MAX(0, (1 - D) + (1 - S))
677
+ // (1 - D)
678
+ CIFilter *filter1 = [CIFilter filterWithName: @" CIColorInvert" ];
679
+ [filter1 setValue: ciImage forKey: kCIInputImageKey ];
680
+ ciImage = filter1.outputImage ;
681
+ // (1 - S)
682
+ CIFilter *filter2 = [CIFilter filterWithName: @" CIColorInvert" ];
683
+ [filter2 setValue: colorImage forKey: kCIInputImageKey ];
684
+ colorImage = filter2.outputImage ;
685
+ // +
686
+ CIFilter *filter = [CIFilter filterWithName: @" CIAdditionCompositing" ];
687
+ [filter setValue: colorImage forKey: kCIInputImageKey ];
688
+ [filter setValue: ciImage forKey: kCIInputBackgroundImageKey ];
689
+ ciImage = filter.outputImage ;
690
+ // MAX
691
+ ciImage = [ciImage imageByApplyingFilter: @" CIColorClamp" withInputParameters: nil ];
692
+ } else {
693
+ SD_LOG (" UIImage+Transform error: Unsupported blend mode: %zu " , blendMode);
694
+ ciImage = nil ;
695
+ }
696
+ }
697
+
698
+ if (ciImage) {
555
699
#if SD_UIKIT
556
700
UIImage *image = [UIImage imageWithCIImage: ciImage scale: self .scale orientation: self .imageOrientation];
557
701
#else
558
702
UIImage *image = [[UIImage alloc ] initWithCIImage: ciImage scale: self .scale orientation: kCGImagePropertyOrientationUp ];
559
703
#endif
560
704
return image;
705
+ }
561
706
}
562
707
#endif
563
708
564
709
CGSize size = self.size ;
565
710
CGRect rect = { CGPointZero, size };
566
711
CGFloat scale = self.scale ;
567
712
568
- // blend mode, see https://en.wikipedia.org/wiki/Alpha_compositing
569
- CGBlendMode blendMode = kCGBlendModeSourceAtop ;
570
-
571
713
SDGraphicsImageRendererFormat *format = [[SDGraphicsImageRendererFormat alloc ] init ];
572
714
format.scale = scale;
573
715
SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc ] initWithSize: size format: format];
0 commit comments