Как потрогать прозрачное изображение 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;
}
Вот пример, который затемняет несколько изображений в оттенках серого с прозрачностью:
:
- В первой строке отображается панель инструментов 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. Альфа в цвете может делать то, что вам нужно, не придумывая что-то новое.
Ответ 10
Не моя работа, но я успешно использовал этот подход:
http://coffeeshopped.com/2010/09/iphone-how-to-dynamically-color-a-uiimage
Ответ 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);