0

I am porting a card-melding-game from Android to iOS (see https://play.google.com/store/apps/details?id=com.pipperpublishing.fivekings). I want to tint the wild cards in yellow (as an aid to game play for novices).

In Android this is brainlessly easy, using cards with black on white.

    static final int TINT_COLOR = Color.parseColor("#FFFF66"); //light yellow
    static final PorterDuff.Mode PORTER_DUFF_MODE = PorterDuff.Mode.DARKEN;
...

        if (highlightWildCard != null) {
            //mutate so this setting will not carry through to other rounds or to the Computer cards when shown
            this.getDrawable().mutate();
            this.getDrawable().setColorFilter(TINT_COLOR, PORTER_DUFF_MODE);
        }

I can't find an equivalent way to do this in iOS. I have tried this:

class CardView : UIImageView {
        static let TINT_COLOR = UIColor.yellowColor() //FIXME: UIColorFromRGB(0xFFFF66); //light yellow
...
        if (highlightWildCard != nil) {
            //for tintColor to be applied, have to replace with an image template - note this creates a new image
            self.image = self.image!.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate)
            self.backgroundColor = CardView.TINT_COLOR
        }

but this just paints the whole card yellow.

And I have tried using CGContextSetBlendMode, but just got a bunch of errors that I didn't understand.

Hopefully you iOS experts can point me in the right direction (and hopefully not one involving creating a separate set of tinted cards ;)

UPDATE: I have tried using this excellent extension from this post: How to tint a transparent PNG image in iPhone? but this seems to leave the card untinted.

Community
  • 1
  • 1
Opus1217
  • 723
  • 4
  • 17

2 Answers2

1

You can use the CoreImage framework for that.

I can show you how to do it in Objective-C, so I hope this helps. You should be able to translate it to swift, or you can just use it directly using a bridging header.

First you'll need a to create a CoreImage context. It is best to initialize it once - so save a reference to it with a property:

@property (strong, nonatomic) CIContext *ciContext;
...
self.ciContext = [CIContext contextWithOptions:@{kCIContextUseSoftwareRenderer : @NO}];

Now the interesting part - using a CIColorMatrix filter to shift the white. Here's a method which does just that (notice it uses the ciContext we've previously created):

-(UIImage*)filteredImageFromImage:(UIImage*)sourceImage withColor:(UIColor*)color
{
    size_t componentCount = CGColorGetNumberOfComponents(color.CGColor);
    if (componentCount < 4)
    {
        return sourceImage;
    }

    CIImage *inputCIImage = [CIImage imageWithCGImage:sourceImage.CGImage];
    CGRect extent = [inputCIImage extent];

    CGFloat r, g, b, a;
    [color getRed:&r green:&g blue:&b alpha:&a];

    CIFilter *colorMatrixFilter = [CIFilter filterWithName:@"CIColorMatrix"];
    [colorMatrixFilter setDefaults];
    [colorMatrixFilter setValue:inputCIImage forKey:kCIInputImageKey];
    [colorMatrixFilter setValue:[CIVector vectorWithX:r Y:0 Z:0 W:0] forKey:@"inputRVector"];
    [colorMatrixFilter setValue:[CIVector vectorWithX:0 Y:g Z:0 W:0] forKey:@"inputGVector"];
    [colorMatrixFilter setValue:[CIVector vectorWithX:0 Y:0 Z:b W:0] forKey:@"inputBVector"];

    CGImageRef cgiimage = [self.ciContext createCGImage:colorMatrixFilter.outputImage fromRect:extent];
    UIImage *newImage = [UIImage imageWithCGImage:cgiimage scale:[[UIScreen mainScreen] scale] orientation:sourceImage.imageOrientation];
    CGImageRelease(cgiimage);

    return newImage;
}

Finally, to tint your card to yellow:

UIImage *cardImage = [self filteredImageFromImage:[UIImage imageNamed:@"card"] withColor:[UIColor yellowColor]];

Edit:

Here's how to use it in swift with a bridging header:

  1. Add a new Objective-C class. Let's call it ImageFilter.

The header file:

// ImageFilter.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h> 

@interface ImageFilter : NSObject
-(UIImage*)filteredImageFromImage:(UIImage*)sourceImage withColor:(UIColor*)color;
@end

The implementation file:

// ImageFilter.m

#import "ImageFilter.h"
#import <CoreImage/CoreImage.h>


@interface ImageFilter()
@property (strong, nonatomic) CIContext *ciContext;
@end

@implementation ImageFilter

- (instancetype)init
{
    self = [super init];
    if (self != nil)
    {
        self.ciContext = [CIContext contextWithOptions:@{kCIContextUseSoftwareRenderer : @NO}];
    }
    return self;
}

-(UIImage*)filteredImageFromImage:(UIImage*)sourceImage withColor:(UIColor*)color
{
    size_t componentCount = CGColorGetNumberOfComponents(color.CGColor);
    if (componentCount < 4)
    {
        return sourceImage;
    }

    CIImage *inputCIImage = [CIImage imageWithCGImage:sourceImage.CGImage];
    CGRect extent = [inputCIImage extent];

    CGFloat r, g, b, a;
    [color getRed:&r green:&g blue:&b alpha:&a];

    CIFilter *colorMatrixFilter = [CIFilter filterWithName:@"CIColorMatrix"];
    [colorMatrixFilter setDefaults];
    [colorMatrixFilter setValue:inputCIImage forKey:kCIInputImageKey];
    [colorMatrixFilter setValue:[CIVector vectorWithX:r Y:0 Z:0 W:0] forKey:@"inputRVector"];
    [colorMatrixFilter setValue:[CIVector vectorWithX:0 Y:g Z:0 W:0] forKey:@"inputGVector"];
    [colorMatrixFilter setValue:[CIVector vectorWithX:0 Y:0 Z:b W:0] forKey:@"inputBVector"];

    CGImageRef cgiimage = [self.ciContext createCGImage:colorMatrixFilter.outputImage fromRect:extent];
    UIImage *newImage = [UIImage imageWithCGImage:cgiimage scale:[[UIScreen mainScreen] scale] orientation:sourceImage.imageOrientation];
    CGImageRelease(cgiimage);

    return newImage;
}

@end
  1. In your bridging file (usually YourProject-Bridging-Header.h which Xcode will ask and create automatically when you add a new Objective-C file to your project) import our new file:

    #import ImageEffect.h

Also, don't forget to make sure the "YourProject-Bridging-Header.h" is specified under "Objective-C Bridging Header in your target's build settings.

  1. Use it in your swift code:

    let imageFilter: ImageFilter = ImageFilter()

    let cardImage = imageFilter.filteredImageFromImage(UIImage(named: "card"), withColor: UIColor.yellowColor());
    

Note: the code formatting went a bit crazy on me, don't know why. It's causing the last bit of code in swift to not show as a code block properly...

Artal
  • 8,933
  • 2
  • 27
  • 30
  • Thanks! I'll give it a try! – Opus1217 Jan 15 '16 at 01:28
  • This crashed on me, although admittedly that might be because of my imperfect conversion to Swift. However, I was able to get the other solution working (see above) – Opus1217 Jan 15 '16 at 07:08
  • @Opus1217 yes, probably some conversion issues, this bit of code works just fine. At least you got another working solution.. – Artal Jan 15 '16 at 15:18
  • I'll give youe solution another try since the tinting below does wash out the other colors. – Opus1217 Jan 15 '16 at 21:53
  • Cool. Maybe this time try to create a bridging header and use it as objective-c code. – Artal Jan 15 '16 at 21:56
  • @Opus1217 see the Edit to my answer which explains how to use it as Objective C code in swift. – Artal Jan 15 '16 at 23:03
0

I tried using How to tint a transparent PNG image in iPhone? to do the tinting, but that just darkened the red and black on the cards and left the white untouched. However, I switched to CGBlendMode of .Darken (instead of .Color) and that achieved that I was looking for - the white turns to the yellow color and the red/blacl is acceptably darkened also.

Community
  • 1
  • 1
Opus1217
  • 723
  • 4
  • 17