Как потрогать прозрачное изображение PNG в iPhone?

Я знаю, что можно подкрасить прямоугольное изображение, вычеркнув над ним CGContextFillRect и установив режим смешивания. Однако я не могу понять, как сделать оттенок на прозрачном изображении, таком как значок. Это должно быть возможно, так как SDK делает это сам по табуляции в таких. Кто-нибудь сможет предоставить фрагмент?

UPDATE:

Множество замечательных предложений было дано для этой проблемы, поскольку я изначально спросил. Обязательно прочитайте все ответы, чтобы выяснить, что лучше всего подходит.

ОБНОВЛЕНИЕ (30 апреля 2015 г.):

С iOS 7.0 теперь я могу просто сделать следующее, которое удовлетворит потребности моего первоначального вопроса. Но если у вас более сложные случаи, проверьте все ответы.

UIImage *iconImage = [[UIImage imageNamed:@"myImageName"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];    
UIImageView *icon = [[UIImageView alloc] initWithImage:iconImage];
icon.tintColor = [UIColor redColor];

Ответ 1

Обновление: Вот Gist для расширения Swift UIColor, используя код ниже.


Если у вас есть изображение в оттенках серого, и вы хотите, чтобы белый стал цветовой тонировкой, kCGBlendModeMultiply. С помощью этого метода вы не можете сделать светлые участки ярче, чем ваш цвет.

Напротив, если у вас есть изображение не в градациях серого, ИЛИ у вас есть блики и тени, которые должны быть сохранены, режим смешивания kCGBlendModeColor является kCGBlendModeColor вариантом. Белый останется белым, а черный останется черным, так как яркость изображения сохранится. Этот режим предназначен только для тонирования - он такой же, как и режим наложения Color слоя Photoshop (отказ от ответственности: возможны незначительные отличия).

Обратите внимание, что тонировка альфа-пикселей не работает должным образом ни в iOS, ни в Photoshop - полупрозрачные черные пиксели не останутся черными. Я обновил ответ ниже, чтобы обойти эту проблему, потребовалось довольно много времени, чтобы выяснить.

Вы также можете использовать один из режимов kCGBlendModeSourceIn/DestinationIn вместо CGContextClipToMask.

Если вы хотите создать UIImage, каждый из следующих разделов кода может быть окружен следующим кодом:

UIGraphicsBeginImageContextWithOptions (myIconImage.size, NO, myIconImage.scale); // for correct resolution on retina, thanks @MobileVet
CGContextRef context = UIGraphicsGetCurrentContext();

CGContextTranslateCTM(context, 0, myIconImage.size.height);
CGContextScaleCTM(context, 1.0, -1.0);

CGRect rect = CGRectMake(0, 0, myIconImage.size.width, myIconImage.size.height);

// image drawing code here

UIImage *coloredImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Итак, вот код для тонирования прозрачного изображения с помощью kCGBlendModeColor:

// draw black background to preserve color of transparent pixels
CGContextSetBlendMode(context, kCGBlendModeNormal);
[[UIColor blackColor] setFill];
CGContextFillRect(context, rect);

// draw original image
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, rect, myIconImage.CGImage);

// tint image (loosing alpha) - the luminosity of the original image is preserved
CGContextSetBlendMode(context, kCGBlendModeColor);
[tintColor setFill];
CGContextFillRect(context, rect);

// mask by alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawImage(context, rect, myIconImage.CGImage);

Если ваше изображение не имеет полупрозрачных пикселей, вы также можете сделать это с помощью kCGBlendModeLuminosity:

// draw tint color
CGContextSetBlendMode(context, kCGBlendModeNormal);
[tintColor setFill];
CGContextFillRect(context, rect);

// replace luminosity of background (ignoring alpha)
CGContextSetBlendMode(context, kCGBlendModeLuminosity);
CGContextDrawImage(context, rect, myIconImage.CGImage);

// mask by alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawImage(context, rect, myIconImage.CGImage);

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

// draw tint color
CGContextSetBlendMode(context, kCGBlendModeNormal);
[tintColor setFill];
CGContextFillRect(context, rect);

// mask by alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawImage(context, rect, myIconImage.CGImage);

или наоборот:

// draw alpha-mask
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, rect, myIconImage.CGImage);

// draw tint color, preserving alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeSourceIn);
[tintColor setFill];
CGContextFillRect(context, rect);

Повеселись!

Ответ 2

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

+ (UIImage *) imageNamed:(NSString *) name withTintColor: (UIColor *) tintColor {

    UIImage *baseImage = [UIImage imageNamed:name];

    CGRect drawRect = CGRectMake(0, 0, baseImage.size.width, baseImage.size.height);

    UIGraphicsBeginImageContextWithOptions(baseImage.size, NO, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextTranslateCTM(context, 0, baseImage.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    // draw original image
    CGContextSetBlendMode(context, kCGBlendModeNormal);
    CGContextDrawImage(context, drawRect, baseImage.CGImage);

    // draw color atop
    CGContextSetFillColorWithColor(context, tintColor.CGColor);
    CGContextSetBlendMode(context, kCGBlendModeSourceAtop);
    CGContextFillRect(context, drawRect);

    UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return tintedImage;
}

Ответ 3

После поиска, лучшим решением, к которому я пришел до сих пор, является использование комбинации режима наложения и обтравочной маски для достижения раскраски/тонировки прозрачного PNG:

CGContextSetBlendMode (context, kCGBlendModeMultiply);

CGContextDrawImage(context, rect, myIconImage.CGImage);

CGContextClipToMask(context, rect, myIconImage.CGImage);

CGContextSetFillColorWithColor(context, tintColor);

CGContextFillRect(context, rect);

Ответ 4

Я могу получить результаты очень близко к оттенку в панели навигации Apple, используя kCGBlendModeOverlay. Принимая excelent @fabb ответ и комбинируя подход @omz в этом сообщении fooobar.com/questions/78081/... Я пришел с этим решением, которое содержало результаты, которые я ожидал:

- (UIImage *)tintedImageUsingColor:(UIColor *)tintColor;
{
    UIGraphicsBeginImageContextWithOptions (self.size, NO, [[UIScreen mainScreen] scale]);

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);

    // draw original image
    [self drawInRect:rect blendMode:kCGBlendModeNormal alpha:1.0f];

    // tint image (loosing alpha). 
    // kCGBlendModeOverlay is the closest I was able to match the 
    // actual process used by apple in navigation bar 
    CGContextSetBlendMode(context, kCGBlendModeOverlay);
    [tintColor setFill];
    CGContextFillRect(context, rect);

    // mask by alpha values of original image
    [self drawInRect:rect blendMode:kCGBlendModeDestinationIn alpha:1.0f];

    UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return tintedImage;
}

Вот пример, который затемняет несколько изображений в оттенках серого с прозрачностью:

Rendered Example:

  • В первой строке отображается панель инструментов Apple [UIColor orangeColor].
  • Вторая строка - это тот же градиент, что и в нескольких цветах, начиная с четкого цвета (= фактического градиента) и заканчивая тем же оранжевым.
  • Третий - это простой круг с прозрачностью (белье - это цвет фона)
  • Четвертая строка представляет собой сложную темную шумную текстуру.

Ответ 5

Вы можете создать категорию UIImage и сделать это следующим образом:

- (instancetype)tintedImageWithColor:(UIColor *)tintColor {
    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGRect rect = (CGRect){ CGPointZero, self.size };
    CGContextSetBlendMode(context, kCGBlendModeNormal);
    [self drawInRect:rect];

    CGContextSetBlendMode(context, kCGBlendModeSourceIn);
    [tintColor setFill];
    CGContextFillRect(context, rect);

    UIImage *image  = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

Ответ 6

В iOS7 они ввели свойство tintColor в UIImageView и renderMode в UIImage. См. Мой пример в fooobar.com/questions/38105/...

Ответ 7

Обратите внимание, что в принятом ответе от fabb "окружающий" код для создания UIImage дал мне неправильное разрешение изображений на экране сетчатки. Чтобы исправить, измените:

UIGraphicsBeginImageContext(myIconImage.size);

чтобы:

UIGraphicsBeginImageContextWithOptions(myIconImage.size, NO, 0.0);

Последний параметр, который установлен в 0.0, является масштабируемым, и согласно документации Apple:

"Если вы укажете значение 0,0, коэффициент масштабирования будет установлен на коэффициент масштабирования главного экрана устройства".

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

Ответ 8

С iOS 7.0 вы также можете просто сделать это, чтобы подчеркнуть базовый UIImageView:

UIImage *iconImage = [[UIImage imageNamed:@"myImageName"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];    
UIImageView *icon = [[UIImageView alloc] initWithImage:iconImage];
icon.tintColor = [UIColor redColor];

Ответ 9

UIImageView (или любой вид в этом случае) имеет цвет фона, который является RGBA. Альфа в цвете может делать то, что вам нужно, не придумывая что-то новое.

Ответ 11

Я хотел затенять мои изображения в моем пользовательском подклассе UIButton, а другие решения не сделали того, что я хотел. Мне нужно было затемнить "оттенок" цвета изображения. Здесь, как изменить яркость с помощью CoreImage.

Custom buttons with shaded images on button press

  • Обязательно добавьте CoreImage.framework в библиотеки проектов. (Link Binary с библиотеками)

  • Метод оттенков UIImage

    - (UIImage *)shadeImage:(UIImage *)image {
     CIImage *inputImage = [CIImage imageWithCGImage:image.CGImage];
    
     CIContext *context = [CIContext contextWithOptions:nil];
    
     CIFilter *filter = [CIFilter filterWithName:@"CIColorControls"
                                  keysAndValues:kCIInputImageKey, inputImage,
                        @"inputBrightness", [NSNumber numberWithFloat:-.5], nil];
     CIImage *outputImage = [filter outputImage];
    
     CGImageRef cgImage = [context createCGImage:outputImage fromRect:[outputImage extent]];
     UIImage *newImage = [UIImage imageWithCGImage:cgImage scale:image.scale orientation:image.imageOrientation];
     CGImageRelease(cgImage);
    
     return newImage;
    }
    
  • Вы хотите сохранить копию контекста как ivar, а не воссоздать его.

Ответ 12

Никакие ответы не помогут мне в переполнении стека: наши дизайнеры рисуют элементы пользовательского интерфейса с различными формами, различными значениями альфа (и "альфа-дыры" ). В большинстве случаев это 32-битный PNG файл с альфа-каналом, который содержит черно-белые пиксели (всех возможных интенсивностей). После тонировки такой картины мне пришлось получить этот тонированный результат: белые пиксели - тонированные больше, а темные пиксели - меньше. И все это с учетом альфа-канала. И я написал этот метод для моей категории UIImage. Может быть, он не очень эффективен, но работает как часы:) Вот он:

- (UIImage *)imageTintedWithColor:(UIColor *)color {
    UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);

    CGContextSetBlendMode(context, kCGBlendModeCopy);
    [color setFill];
    CGContextFillRect(context, rect);

    [self drawInRect:rect blendMode:kCGBlendModeXOR alpha:1.0];

    CGContextSetBlendMode(context, kCGBlendModeXOR);
    CGContextFillRect(context, rect);

    [self drawInRect:rect blendMode:kCGBlendModeMultiply alpha:1.0];

    UIImage *coloredImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return coloredImage;
}

Ответ 13

Сначала я хочу поблагодарить fabb за его исключительное решение, которое помогло мне выполнить мою задачу, чтобы отточить наполовину прозрачные значки. Поскольку мне нужно решение для С# (Monotouch), мне пришлось перевести его код. Вот что я придумал. Просто скопируйте это в свой код и добавьте полупрозрачное изображение и сделанное.

Итак, снова все кредиты идут на fabb. Это только для того, чтобы начать работу с пользователями С#:)

//TINT COLOR IMAGE
UIImageView iImage = new UIImageView(new RectangleF(12, 14, 24,24));
iImage.ContentMode = UIViewContentMode.ScaleAspectFit;
iImage.Image = _dataItem.Image[0] as UIImage;

UIGraphics.BeginImageContextWithOptions(iImage.Bounds.Size, false, UIScreen.MainScreen.Scale);
CGContext context = UIGraphics.GetCurrentContext();

context.TranslateCTM(0, iImage.Bounds.Size.Height);
context.ScaleCTM(1.0f, -1.0f);

RectangleF rect = new RectangleF(0,0, iImage.Bounds.Width, iImage.Bounds.Height);

// draw black background to preserve color of transparent pixels
context.SetBlendMode(CGBlendMode.Normal);
UIColor.Black.SetFill();
context.FillRect(rect);

// draw original image
context.SetBlendMode(CGBlendMode.Normal);
context.DrawImage(rect, iImage.Image.CGImage);

// tint image (loosing alpha) - the luminosity of the original image is preserved
context.SetBlendMode(CGBlendMode.Color);
UIColor.Orange.SetFill();
context.FillRect(rect);

// mask by alpha values of original image
context.SetBlendMode(CGBlendMode.DestinationIn);
context.DrawImage(rect, iImage.Image.CGImage);

UIImage coloredImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();

iImage = new UIImageView(coloredImage);
iImage.Frame = new RectangleF(12, 14, 24,24);
//END TINT COLOR IMAGE

cell.Add(iImage);