@@ -536,38 +536,184 @@ - (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: kCGBlendModeSourceIn ];
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
+ colorImage = [CIImage imageWithColor: clearColor];
663
+ colorImage = [colorImage imageByCroppingToRect: ciImage.extent];
664
+ ciImage = colorImage;
665
+ } else if (blendMode == kCGBlendModeCopy ) {
666
+ // R = S
667
+ ciImage = colorImage;
668
+ } else if (blendMode == kCGBlendModePlusLighter ) {
669
+ // R = MIN(1, S + D)
670
+ // S + D
671
+ CIFilter *filter = [CIFilter filterWithName: @" CIAdditionCompositing" ];
672
+ [filter setValue: colorImage forKey: kCIInputImageKey ];
673
+ [filter setValue: ciImage forKey: kCIInputBackgroundImageKey ];
674
+ ciImage = filter.outputImage ;
675
+ // MIN
676
+ ciImage = [ciImage imageByApplyingFilter: @" CIColorClamp" withInputParameters: nil ];
677
+ } else if (blendMode == kCGBlendModePlusDarker ) {
678
+ // R = MAX(0, (1 - D) + (1 - S))
679
+ // (1 - D)
680
+ CIFilter *filter1 = [CIFilter filterWithName: @" CIColorControls" ];
681
+ [filter1 setValue: ciImage forKey: kCIInputImageKey ];
682
+ [filter1 setValue: @(-0.5 ) forKey: kCIInputBrightnessKey ];
683
+ ciImage = filter1.outputImage ;
684
+ // (1 - S)
685
+ CIFilter *filter2 = [CIFilter filterWithName: @" CIColorControls" ];
686
+ [filter2 setValue: colorImage forKey: kCIInputImageKey ];
687
+ [filter2 setValue: @(-0.5 ) forKey: kCIInputBrightnessKey ];
688
+ colorImage = filter2.outputImage ;
689
+ // +
690
+ CIFilter *filter = [CIFilter filterWithName: @" CIAdditionCompositing" ];
691
+ [filter setValue: colorImage forKey: kCIInputImageKey ];
692
+ [filter setValue: ciImage forKey: kCIInputBackgroundImageKey ];
693
+ ciImage = filter.outputImage ;
694
+ // MAX
695
+ ciImage = [ciImage imageByApplyingFilter: @" CIColorClamp" withInputParameters: nil ];
696
+ } else {
697
+ SD_LOG (" UIImage+Transform error: Unsupported blend mode: %d " , blendMode);
698
+ ciImage = nil ;
699
+ }
700
+ }
701
+
702
+ if (ciImage) {
555
703
#if SD_UIKIT
556
704
UIImage *image = [UIImage imageWithCIImage: ciImage scale: self .scale orientation: self .imageOrientation];
557
705
#else
558
706
UIImage *image = [[UIImage alloc ] initWithCIImage: ciImage scale: self .scale orientation: kCGImagePropertyOrientationUp ];
559
707
#endif
560
708
return image;
709
+ }
561
710
}
562
711
#endif
563
712
564
713
CGSize size = self.size ;
565
714
CGRect rect = { CGPointZero, size };
566
715
CGFloat scale = self.scale ;
567
716
568
- // blend mode, see https://en.wikipedia.org/wiki/Alpha_compositing
569
- CGBlendMode blendMode = kCGBlendModeSourceAtop ;
570
-
571
717
SDGraphicsImageRendererFormat *format = [[SDGraphicsImageRendererFormat alloc ] init ];
572
718
format.scale = scale;
573
719
SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc ] initWithSize: size format: format];
0 commit comments