Как раскрасить белый фон изображения в iOS

Я портирую карточную игру с Android на iOS (см. https://play.google.com/store/apps/details?id=com.pipperpublishing.fivekings). Я хочу закрасить дикие карты в желтый цвет (в помощь новичкам).

В Android это безумно просто, используя карты с черным по белому.

    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);
        }

Я не могу найти эквивалентного способа сделать это в iOS. Я пробовал это:

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
        }

но это просто окрашивает всю карточку в желтый цвет.

И я пробовал использовать CGContextSetBlendMode, но получил кучу ошибок, которых не понял.

Надеюсь, вы, эксперты по iOS, укажете мне верное направление (и, надеюсь, не то, что связано с созданием отдельного набора тонированных карточек;)

ОБНОВЛЕНИЕ: я пробовал использовать это отличное расширение из этого сообщения: Как подкрасить прозрачное изображение PNG в iPhone?, но при этом карта остается нетронутой.


person Opus1217    schedule 14.01.2016    source источник
comment
Вы пробовали self.tintColor = CardView.TINT_COLOR?   -  person kostek    schedule 14.01.2016
comment
Да, это окрашивает всю карточку в желтый цвет :)   -  person Opus1217    schedule 15.01.2016


Ответы (2)


Для этого вы можете использовать CoreImage фреймворк.

Я могу показать вам, как это сделать в Objective-C, поэтому надеюсь, что это поможет. У вас должна быть возможность перевести его на swift, или вы можете просто использовать его напрямую, используя заголовок моста.

Сначала вам понадобится для создания CoreImage контекста. Лучше всего один раз инициализировать его, поэтому сохраните ссылку на него со свойством:

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

Теперь самое интересное - использование фильтра CIColorMatrix для сдвига белого. Вот метод, который делает именно это (обратите внимание, что он использует ciContext, который мы создали ранее):

-(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;
}

Наконец, чтобы окрасить карту в желтый цвет:

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

Изменить:

Вот как быстро использовать его с заголовком моста:

  1. Добавьте новый класс Objective-C. Назовем его ImageFilter.

Заголовочный файл:

// ImageFilter.h

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

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

Файл реализации:

// 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. В вашем файле моста (обычно YourProject-Bridging-Header.h, который Xcode запросит и создаст автоматически, когда вы добавите новый файл Objective-C в свой проект) импортируйте наш новый файл:

    #import ImageEffect.h

Кроме того, не забудьте убедиться, что «YourProject-Bridging-Header.h» указан в «Заголовке моста Objective-C» в настройках вашей целевой сборки.

  1. Используйте его в своем быстром коде:

    пусть imageFilter: ImageFilter = ImageFilter ()

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

Примечание: форматирование кода меня немного помешало, не знаю почему. Это приводит к тому, что последний бит кода быстро не отображается как блок кода должным образом ...

person Artal    schedule 14.01.2016
comment
Спасибо! Я попробую! - person Opus1217; 15.01.2016
comment
Это разбило меня, хотя, по общему признанию, это могло быть из-за моего несовершенного перехода на Swift. Однако мне удалось заставить другое решение работать (см. Выше) - person Opus1217; 15.01.2016
comment
@ Opus1217 да, возможно, некоторые проблемы с преобразованием, этот фрагмент кода работает нормально. По крайней мере, у вас есть другое рабочее решение .. - person Artal; 15.01.2016
comment
Я дам вам решение еще раз, так как нижеприведенная тонировка размывает другие цвета. - person Opus1217; 16.01.2016
comment
Прохладный. Возможно, на этот раз попробуйте создать заголовок моста и использовать его как код objective-c. - person Artal; 16.01.2016
comment
@ Opus1217 см. Правка моего ответа, в котором объясняется, как быстро использовать его в качестве кода Objective C. - person Artal; 16.01.2016

Я пробовал использовать Как подкрасить прозрачное изображение PNG в iPhone?, чтобы сделать тонировку, но это только затемнило красный и черный на карточках и оставило белый нетронутым. Однако я переключился на CGBlendMode для .Darken (вместо .Color), и это достигло того, что я искал - белый цвет превращается в желтый цвет, а красный / черный также становится приемлемо затемненным.

person Opus1217    schedule 15.01.2016