Оттенок инвертирует цвета в Swift

Я пробую этот код, чтобы подкрасить цвет UIImage, но кажется, что он инвертирует цвета - окрашивая фон и превращая черный штрих в белый.

Как сделать так, чтобы он закрасил черные линии обводки изображения и оставил белый фон в покое? Вот пример детской площадки, где я пробую одну технику, а также другую, предложенную первым ответом.

Код детской площадки

 extension UIImage {

    func tint(color: UIColor, blendMode: CGBlendMode = CGBlendMode.Normal) -> UIImage
    {
        UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale);
        let context = UIGraphicsGetCurrentContext();
        CGContextTranslateCTM(context, 0, self.size.height);
        CGContextScaleCTM(context, 1.0, -1.0);
        CGContextSetBlendMode(context, blendMode);
        let rect = CGRectMake(0, 0, self.size.width, self.size.height);
        CGContextClipToMask(context, rect, self.CGImage);
        color.setFill()
        CGContextFillRect(context, rect);
        let newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage;
    }

    func tint2(color: UIColor, blendMode: CGBlendMode) -> UIImage
    {
        let drawRect = CGRectMake(0.0, 0.0, size.width, size.height)
        UIGraphicsBeginImageContextWithOptions(size, false, scale)
        let context = UIGraphicsGetCurrentContext()
        CGContextClipToMask(context, drawRect, CGImage)
        color.setFill()
        UIRectFill(drawRect)
        drawInRect(drawRect, blendMode: blendMode, alpha: 1.0)
        let tintedImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return tintedImage
    }

}

let stringUrl = "https://s3.amazonaws.com/neighbor-chat-assets/icons/avatar1000.png"

let url = NSURL(string: stringUrl)

let data = NSData(contentsOfURL: url!)

var originalimage = UIImage(data: data!)

var image = originalimage!.imageWithRenderingMode(.AlwaysTemplate).tint(UIColor.blueColor(), blendMode:.Normal)
var image2 = originalimage!.imageWithRenderingMode(.AlwaysTemplate).tint2(UIColor.blueColor(), blendMode:.Normal)

1 ответ

Решение

Это должно работать, я только что проверил с png и прозрачным фоном

func tint(color: UIColor, blendMode: CGBlendMode = CGBlendMode.Normal) -> UIImage
{
    UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale);
    let context = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(context, 0, self.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGContextSetBlendMode(context, blendMode);
    let rect = CGRectMake(0, 0, self.size.width, self.size.height);
    CGContextClipToMask(context, rect, self.CGImage);
    color.setFill()
    CGContextFillRect(context, rect);
    let newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

Также см. Этот пост для получения дополнительной информации. В качестве альтернативы можно заключить ваше изображение в UIImageView в режиме рендеринга UIImageRenderingMode.AlwaysTemplate, а затем установить свойство tintColor.

РЕДАКТИРОВАТЬ

Так что это функция, которую вы можете использовать, чтобы сначала замаскировать определенные цвета, если у вас нет альфа-канала в изображении

func tint(color: UIColor, blendMode: CGBlendMode = CGBlendMode.Normal, colorToMask: UIColor = UIColor.blackColor()) -> UIImage
{
    var fRed : CGFloat = 0
    var fGreen : CGFloat = 0
    var fBlue : CGFloat = 0
    var fAlpha: CGFloat = 0
    colorToMask.getRed(&fRed, green: &fGreen, blue: &fBlue, alpha: &fAlpha)

    let rect = CGRectMake(0, 0, self.size.width, self.size.height);
    var colorMasking : [CGFloat] = [fRed,fRed,fGreen,fGreen,fBlue,fBlue]
    let newImageRef = CGImageCreateWithMaskingColors(self.CGImage!, &colorMasking)

    UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale);
    let context = UIGraphicsGetCurrentContext();

    CGContextTranslateCTM(context, 0, self.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGContextSetBlendMode(context, blendMode);
    CGContextClipToMask(context, rect, newImageRef!);
    color.setFill()
    CGContextFillRect(context, rect);

    let newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}

Эта функция сначала создает маску в зависимости от указанного цвета и обрезает контекст этой маски.

РЕДАКТИРОВАТЬ № 2

Как указано в комментариях, мне также не удалось замаскировать белый цвет, я пробовал значения UInt8 (т.е. 255) и значения с плавающей запятой (то есть 1,0), но это все равно не работало. Поэтому единственное решение, которое я мог придумать, - это инвертировать изображение. Вот функция:

func negativeImage() -> UIImage {

    let coreImage = MYImage(CGImage: self.CGImage!)
    let filter = CIFilter(name: "CIColorInvert", withInputParameters: ["inputImage" : coreImage])!
    let result = filter.outputImage!
    let context = CIContext(options:nil)
    let cgimg : CGImageRef = context.createCGImage(result, fromRect: result.extent)

    let bmpcontext = CGBitmapContextCreate(nil, Int(self.size.width), Int(self.size.height), 8, Int(self.size.width)*4, CGColorSpaceCreateDeviceRGB(),CGImageAlphaInfo.NoneSkipLast.rawValue)
    CGContextDrawImage(bmpcontext, CGRectMake(0, 0, self.size.width, self.size.height), cgimg)
    let newImgRef = CGBitmapContextCreateImage(bmpcontext)!
    return UIImage(CGImage: newImgRef)
}

Вы можете использовать это так

var image : UIImage = ...
image = image.negativeImage()
image = image.tint(UIColor.redColor())

Вот некоторые общие замечания

  • CGImageCreateWithMaskingColors требует CGImage без альфа-канала, поэтому я использую флаг CGImageAlphaInfo.NoneSkipLast в функции absoluteImage, чтобы снова удалить альфа-канал
  • В этом нет абсолютно никакой проверки ошибок, вы должны по крайней мере проверить, есть ли альфа-канал, включенный в функцию оттенка, используя CGImageGetAlphaInfo
  • Вы можете либо сделать замаскированные части (в нашем примере белым) прозрачными (установив false в UIGraphicsBeginImageContextWithOptions команда в функции оттенка), или определенный цвет, установив значение true, которое по умолчанию является черным (вам придется рисовать перед обрезкой по маске).
Другие вопросы по тегам