Як підфарбувати прозоре зображення PNG у iPhone?


74

Я знаю, що можна тонувати прямокутне зображення, намалювавши над ним CGContextFillRect та встановивши режим змішування. Однак я не можу зрозуміти, як зробити відтінок на прозорому зображенні, такому як піктограма. Це повинно бути можливим, оскільки SDK робить це самостійно на вкладках у таких. Хтось зможе надати фрагмент?

ОНОВЛЕННЯ:

Багато чудових пропозицій було дано щодо цієї проблеми відтоді, як я спочатку запитав. Обов’язково прочитайте всі відповіді, щоб з’ясувати, що вам найбільше підходить.

ОНОВЛЕННЯ (30 квітня 2015 р.):

З iOS 7.0 я тепер можу просто зробити наступне, що задовольнило б потреби мого початкового запитання. Але якщо у вас складніші випадки, перевірте всі відповіді.

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

Відповіді:


165

Оновлення: Ось суть для розширення Swift UIColor, використовуючи код нижче.


Якщо у вас є зображення в сірих тонах і ви хочете, щоб білий колір став тонуючим кольором , kCGBlendModeMultiplyце шлях. За допомогою цього методу ви не можете мати світлі місця, світліші за кольоровий колір.

Навпаки, якщо у вас є зображення , що не має відтінків сірого , АБО у вас є відблиски та тіні, які слід зберегти, режим накладання kCGBlendModeColor- це шлях. Білий залишатиметься білим, а чорний залишатиметься чорним у міру збереження легкості зображення. Цей режим створений лише для тонування - він такий самий, як і Colorрежим накладання шарів Photoshop (застереження: можуть траплятися дещо інші результати).

Зауважте, що тонування альфа-пікселів не працює належним чином ні в iOS, ні в Photoshop - напівпрозорі чорні пікселі не залишаться чорними. Я оновив відповідь нижче, щоб обійти це питання, і це зайняло досить багато часу, щоб з’ясувати це.

Ви також можете використовувати один із режимів змішування kCGBlendModeSourceIn/DestinationInзамість CGContextClipToMask.

Якщо ви хочете створити a 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);

Веселіться!


Це фантастика і заощадило мені купу часу! Мені подобається техніка використання kCGBlendModeDestinationIn для відновлення вихідного альфа-зображення, яким ви маніпулюєте. Набагато простіше, ніж мати справу з відсічними масками.
Ziconic

8
Це блискуче і ретельно ... однак йому не вистачає одного крихітного варіанту, який забезпечує якісний вихід на дисплеї Retina ---- UIGraphicsBeginImageContextWithOptions (myIconImage.size, NO, [[UIScreen mainScreen] масштаб]);
MobileVet

Або встановіть масштаб сітківки таким чином UIGraphicsBeginImageContextWithOptions (self.size, 0, self.scale) у категорії
HotJard

21

Я мав найбільший успіх із цим методом, оскільки інші, які я спробував, спричинили спотворені кольори для напівпрозорих пікселів для певних комбінацій кольорів. Це також має бути трохи кращим з точки зору продуктивності.

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

Здається, це не працює з кольорами, альфа-компонент яких менше 1. Чи знаєте ви спосіб це виправити?
Зев Айзенберг,

Я працюю з цим кодом з github.com/vilanovi/UIImage-Additions/blob/master/… :CGContextSetBlendMode(context, kCGBlendModeNormal); CGContextDrawImage(context, rect, self.CGImage); CGContextSetBlendMode(context, kCGBlendModeSourceIn); [color setFill]; CGContextFillRect(context, rect);
Зев Айзенберг

18

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

CGContextSetBlendMode (context, kCGBlendModeMultiply);

CGContextDrawImage(context, rect, myIconImage.CGImage);

CGContextClipToMask(context, rect, myIconImage.CGImage);

CGContextSetFillColorWithColor(context, tintColor);

CGContextFillRect(context, rect);

Я спробував усе інше, що міг подумати, перш ніж знайти це, і це єдине, що спрацювало. Дякую, @anna!
Тодд Леман,

Це спрацювало, але перевернуло мій образ, тому я використав цю відповідь від Кліфа Вієгаса, щоб виправити це: stackoverflow.com/questions/506622/…
C. Lee

працював чудово. вищевказане рішення залишило для мене межу в 1 піксель з якоїсь дивної причини.
Готфрід

16

Я можу отримати результати дуже близькі до відтінку на панелі навігації Apple, використовуючи kCGBlendModeOverlay. Беручи чудову відповідь @fabb та поєднуючи підхід @omz у цій публікації https://stackoverflow.com/a/4684876/229019, я прийшов із цим рішенням, яке містить результати, яких я очікував:

- (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].
  • Другий рядок - це той самий градієнт, підфарбований у кілька кольорів, починаючи з чіткого кольору (= ​​фактичний градієнт) і закінчуючи тим же оранжевим.
  • Третій - це просте коло з прозорістю (білизна - колір фону)
  • Четвертий рядок - це складна темна галаслива текстура

4
Для мене це чудово працювало. Я хотів би лише додати, що зміна першого рядка на: UIGraphicsBeginImageContextWithOptions (self.size, NO, [[UIScreen mainScreen] масштаб]); дозволило зображенню зберегти форматування сітківки, якщо це можливо.
jwarrent

1
Це було б доречніше: UIGraphicsBeginImageContextWithOptions (self.size, NO, self.scale);
malhal

8

Ви можете створити категорію 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;
}


3

У iOS 7.0 ви також можете просто зробити це, щоб підфарбувати базовий UIImageView:

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

3

Зверніть увагу, що у прийнятій відповіді fabb "оточуючий" код для створення UIImage дав мені неправильну роздільну здатність зображень на екрані сітківки. Щоб виправити, змініть:

UIGraphicsBeginImageContext(myIconImage.size);

до:

UIGraphicsBeginImageContextWithOptions(myIconImage.size, NO, 0.0);

Останнім параметром, для якого встановлено 0,0, є масштаб, і згідно з документацією Apple:

"Якщо вказано значення 0,0, коефіцієнт масштабування встановлюється як коефіцієнт масштабування головного екрану пристрою".

Не маю дозволу коментувати, і редагування здається трохи грубим, тому я згадую це у відповіді. Про всяк випадок, коли хтось зіткнеться з цією ж проблемою.


2

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



1

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

Спеціальні кнопки із затіненими зображеннями при натисканні кнопки

  1. Обов’язково додайте CoreImage.framework до бібліотек свого проекту. (Пов’язати двійковий файл з бібліотеками)

  2. Метод відтінків 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;
    }
    
  3. Вам захочеться зберегти копію контексту як ivar, а не відтворювати його.


1

Жодні відповіді не допоможуть мені при переповненні стека: наші дизайнери малюють елементи інтерфейсу з різними формами, різними значеннями альфа (та "альфа-дірками"). У більшості випадків це 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;
}

0

Спочатку я хочу подякувати fabb за його виняткове рішення, яке допомогло мені виконати моє завдання - підфарбувати напівпрозорі іконки. Оскільки мені потрібно рішення для C # (Monotouch), мені довелося перекласти його код. Ось що я придумав. Просто скопіюйте вставте це у свій код і додайте своє напівпрозоре зображення і готово.

Тож знову всі кредити йдуть на fabb. Це просто для запуску користувачів C # :)

//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);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.