Comment teinter une image PNG transparente sur iPhone?

Je sais qu'il est possible de colorer une image rectangular en y dessinant un CGContextFillRect et en définissant le mode de fusion. Cependant, je n'arrive pas à comprendre comment faire une teinte sur une image transparente telle qu'une icône. Cela doit être possible puisque le SDK le fait lui-même sur des tabulations dans tel. Quelqu'un pourrait-il fournir un extrait?

METTRE À JOUR:

Beaucoup de bonnes suggestions ont été données pour ce problème depuis que j'ai demandé à l'origine. Assurez-vous de lire toutes les réponses pour déterminer ce qui vous convient le mieux.

MISE À JOUR (30 avril 2015):

Avec iOS 7.0, je peux maintenant faire ce qui suit, ce qui répondrait aux besoins de ma question initiale. Mais si vous avez des cas plus compliqués, consultez toutes les réponses.

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

Mise à jour: Voici un aperçu pour une extension Swift UIColor en utilisant le code ci-dessous.


Si vous avez une image en niveaux de gris et que vous voulez que le blanc devienne la couleur de teinte , kCGBlendModeMultiply est le path à parcourir. Avec cette méthode, vous ne pouvez pas avoir des reflets plus clairs que votre couleur de teinte.

Au contraire, si vous avez une image non en niveaux de gris , OU si vous avez des surlignages et des ombres qui doivent être conservés, le mode de fusion kCGBlendModeColor est le path à parcourir. Le blanc restra blanc et le noir restra noir car la légèreté de l'image est préservée. Ce mode est juste fait pour teinter – il est le même que le mode de mélange de couche de Color de Photoshop (avertissement: des résultats légèrement différents peuvent arriver).

Notez que la coloration des alpha-pixels ne fonctionne pas correctement ni dans iOS ni dans Photoshop – les pixels noirs semi-transparents ne restront pas noirs. J'ai mis à jour la réponse ci-dessous pour contourner ce problème, il a fallu beaucoup de time pour le savoir.

Vous pouvez également utiliser l'un des modes de fusion kCGBlendModeSourceIn/DestinationIn au lieu de CGContextClipToMask .

Si vous voulez créer un UIImage , chacune des sections de code suivantes peut être entourée du code suivant:

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

Voici donc le code pour teinter une image transparente avec 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); 

Si votre image n'a pas de pixels semi-transparents, vous pouvez également le faire dans l'autre sens avec 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); 

Si vous ne tenez pas count de la luminosité, comme vous venez d'get une image avec un canal alpha qui devrait être teinté d'une couleur, vous pouvez le faire de manière plus efficace:

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

ou l'inverse

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

S'amuser!

J'ai eu le plus de succès avec cette méthode, parce que les autres que j'ai essayés ont provoqué des colors déformées pour des pixels semi-transparents pour certaines combinaisons de colors. Cela devrait également être un peu mieux du côté des performances.

 + (UIImage *) imageNamed:(NSSsortingng *) 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; } 

Après avoir fait des searchs, la meilleure solution à laquelle je suis parvenu jusqu'ici est d'utiliser une combinaison de mode de fusion et de masque d'écrêtage pour coloriser / colorer un PNG transparent:

 CGContextSetBlendMode (context, kCGBlendModeMultiply); CGContextDrawImage(context, rect, myIconImage.CGImage); CGContextClipToMask(context, rect, myIconImage.CGImage); CGContextSetFillColorWithColor(context, tintColor); CGContextFillRect(context, rect); 

Je peux get des résultats très proches de la teinte dans la barre de navigation Apple en utilisant kCGBlendModeOverlay. Prenant excellente réponse @fabb et en combinant l'approche @omz dans ce post https://stackoverflow.com/a/4684876/229019 Je suis venu avec cette solution qui détient les résultats que je m'attendais:

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

Voici un exemple teintant plusieurs images en niveaux de gris avec transparence:

Exemple rendu :

  • La première ligne est la barre d'outils Apple teintée [UIColor orangeColor].
  • La deuxième ligne est le même dégradé teinté en plusieurs colors commençant par une couleur claire (= le dégradé actuel) et se terminant par le même orange.
  • Le troisième est un simple cercle avec transparence (le lin est la couleur de fond)
  • La quasortingème ligne est une texture sombre et complexe

Vous pouvez créer une catégorie UIImage et le faire comme ceci:

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

Dans iOS7, ils ont introduit la propriété tintColor sur UIImageView et renderingMode sur UIImage. Voir mon exemple à https://stackoverflow.com/a/19125120/1570970

Notez que dans la réponse acceptée par fabb, le code "environnant" pour faire un UIImage m'a donné la mauvaise résolution des images sur l'écran Retina. Pour corriger, changez:

UIGraphicsBeginImageContext (myIconImage.size);

à:

UIGraphicsBeginImageContextWithOptions (myIconImage.size, NO, 0.0);

Le dernier paramètre défini sur 0.0 est l'échelle et selon la documentation d'Apple: "Si vous spécifiez une valeur de 0.0, le facteur d'échelle est défini sur le facteur d'échelle de l'écran principal du périphérique".

N'ont pas la permission de commenter, et l'édition semble un peu impoli, ainsi je mentionne ceci dans une réponse. Juste au cas où quelqu'un rencontrerait ce même problème.

Avec iOS 7.0, vous pouvez également le faire pour colorer un UIImageView basique:

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

UIImageView (ou n'importe quelle vue d'ailleurs) a une couleur d'arrière-plan qui est RGBA. L'alpha dans la couleur peut faire ce dont vous avez besoin sans inventer quelque chose de nouveau.

Pas mon travail, mais j'ai réussi à utiliser cette approche:

http://coffeeshopped.com/2010/09/iphone-how-to-dynamically-color-a-uiimage

Je voulais ombrer mes vues d'images dans ma sous-class UIButton personnalisée et les autres solutions n'ont pas fait ce que je voulais. J'avais besoin d'assombrir "teinter" la couleur de l'image. Voici comment changer la luminosité en utilisant CoreImage.

Boutons personnalisés avec des images ombrées sur le bouton

  1. Assurez-vous d'append CoreImage.framework aux bibliothèques de votre projet. (Link Binary avec les bibliothèques)

  2. Méthode d'ombrage 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. Vous souhaiterez stocker une copy du context sous forme d'ivar, plutôt que de le recréer.

Je tiens d'abord à remercier fabb pour sa solution exceptionnelle qui m'a aidé à accomplir ma tâche de teinter des icons semi-transparentes. Parce que j'avais besoin d'une solution pour C # (Monotouch), je devais traduire son code. Voici ce que j'ai trouvé. Il suffit de copyr coller cela dans votre code et append votre image semi-transparente et votre fait.

Donc, encore une fois, tous les crédits vont à fabb. Ceci est juste pour lancer les users 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); 

Aucune réponse ne m'aidera en cas de dépassement de stack: nos concepteurs dessinent des éléments d'interface user avec des formulaires variés, des valeurs alpha variées (et des "trous alpha"). Dans la plupart des cas, il s'agit d'un file PNG 32 bits avec canal alpha, qui comprend des pixels noir et blanc (de toutes les intensités possibles). Après avoir teinté une telle image, j'ai dû get ce résultat teinté: des pixels blancs – teintés de plus en plus, et des pixels sombres – less. Et tout cela en vue du canal alpha. Et j'ai écrit cette méthode pour ma catégorie UIImage. Peut-être que ce n'est pas très efficace, mais ça fonctionne comme une horloge 🙂

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