Comment pourrais-je teinter une image par programmation sur l’iPhone?

Je voudrais teinter une image avec une référence de couleur. Les résultats devraient ressembler au mode de fusion Multiply dans Photoshop, où les blancs seraient remplacés par une teinte :

texte alt

Je vais changer la valeur de couleur en continu.

Suivi: Je mettrais le code pour le faire dans la méthode drawRect: de mon ImageView, non?

Comme toujours, un extrait de code faciliterait grandement ma compréhension, contrairement à un lien.

Mise à jour: Sous-classement d’un UIImageView avec le code suggéré par Ramin .

Je mets ça dans viewDidLoad: de mon contrôleur de vue:

[self.lena setImage:[UIImage imageNamed:kImageName]]; [self.lena setOverlayColor:[UIColor blueColor]]; [super viewDidLoad]; 

Je vois l’image, mais elle n’est pas teintée. J’ai également essayé de charger d’autres images, de définir l’image dans IB et d’appeler setNeedsDisplay: dans mon contrôleur de vue.

Update : drawRect: n’est pas appelé.

Mise à jour finale: J’ai trouvé un ancien projet qui avait une imageView configurée correctement pour que je puisse tester le code de Ramin et cela fonctionne comme un charme!

Dernière mise à jour finale:

Pour ceux d’entre vous qui ne font que découvrir Core Graphics, voici la chose la plus simple qui puisse fonctionner.

Dans votre UIView sous-classé:

 - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetFillColor(context, CGColorGetComponents([UIColor colorWithRed:0.5 green:0.5 blue:0 alpha:1].CGColor)); // don't make color too saturated CGContextFillRect(context, rect); // draw base [[UIImage imageNamed:@"someImage.png"] drawInRect: rect blendMode:kCGBlendModeOverlay alpha:1.0]; // draw image } 

Tout d’abord, vous voudrez sous-classer UIImageView et remplacer la méthode drawRect. Votre classe a besoin d’une propriété UIColor (appelons-la overlayColor) pour contenir la couleur de fusion et d’un setter personnalisé qui oblige à redessiner lorsque la couleur change. Quelque chose comme ça:

 - (void) setOverlayColor:(UIColor *)newColor { if (overlayColor) [overlayColor release]; overlayColor = [newColor retain]; [self setNeedsDisplay]; // fires off drawRect each time color changes } 

Dans la méthode drawRect, vous souhaiterez d’abord dessiner l’image puis la superposer avec un rectangle rempli de la couleur souhaitée avec le mode de fusion approprié, par exemple:

 - (void) drawRect:(CGRect)area { CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSaveGState(context); // Draw picture first // CGContextDrawImage(context, self.frame, self.image.CGImage); // Blend mode could be any of CGBlendMode values. Now draw filled rectangle // over top of image. // CGContextSetBlendMode (context, kCGBlendModeMultiply); CGContextSetFillColor(context, CGColorGetComponents(self.overlayColor.CGColor)); CGContextFillRect (context, self.bounds); CGContextRestoreGState(context); } 

Normalement, pour optimiser le dessin, vous restreindriez le dessin réel à la seule zone transmise à drawRect, mais comme l’image d’arrière-plan doit être redessinée chaque fois que la couleur change, il est probable que tout soit rafraîchissant.

Pour l’utiliser, créez une instance de l’object, puis définissez la propriété image (héritée de UIImageView) sur l’image et overlayColor sur une valeur UIColor (les niveaux de fusion peuvent être ajustés en modifiant la valeur alpha de la couleur transmise).

Dans iOS7, ils ont introduit la propriété tintColor sur UIImageView et renderingMode sur UIImage. Pour teinter UIImage sur iOS 7, il suffit de:

 UIImageView* imageView = … UIImage* originalImage = … UIImage* imageForRendering = [originalImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; imageView.image = imageForRendering; imageView.tintColor = [UIColor redColor]; // or any color you want to tint it with 

Juste une brève clarification (après quelques recherches sur ce sujet). Le document Apple indique clairement que:

La classe UIImageView est optimisée pour dessiner ses images sur l’écran. UIImageView n’appelle pas la méthode drawRect: de ses sous-classes. Si votre sous-classe doit inclure un code de dessin personnalisé, vous devez sous- UIView classe UIView la place.

ne perdez donc pas de temps à essayer de remplacer cette méthode dans une sous-classe UIImageView . Commencez avec UIView place.

Je voulais teinter une image avec alpha et j’ai créé la classe suivante. S’il vous plaît laissez-moi savoir si vous trouvez des problèmes avec elle.

J’ai nommé ma classe CSTintedImageView et elle hérite de UIView car UIImageView n’appelle pas la méthode drawRect: comme mentionné dans les réponses précédentes. J’ai défini un initialiseur désigné similaire à celui trouvé dans la classe UIImageView .

Usage:

 CSTintedImageView * imageView = [[CSTintedImageView alloc] initWithImage:[UIImage imageNamed:@"image"]]; imageView.tintColor = [UIColor redColor]; 

CSTintedImageView.h

 @interface CSTintedImageView : UIView @property (strong, nonatomic) UIImage * image; @property (strong, nonatomic) UIColor * tintColor; - (id)initWithImage:(UIImage *)image; @end 

CSTintedImageView.m

 #import "CSTintedImageView.h" @implementation CSTintedImageView @synthesize image=_image; @synthesize tintColor=_tintColor; - (id)initWithImage:(UIImage *)image { self = [super initWithFrame:CGRectMake(0, 0, image.size.width, image.size.height)]; if(self) { self.image = image; //set the view to opaque self.opaque = NO; } return self; } - (void)setTintColor:(UIColor *)color { _tintColor = color; //update every time the tint color is set [self setNeedsDisplay]; } - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); //resolve CG/iOS coordinate mismatch CGContextScaleCTM(context, 1, -1); CGContextTranslateCTM(context, 0, -rect.size.height); //set the clipping area to the image CGContextClipToMask(context, rect, _image.CGImage); //set the fill color CGContextSetFillColor(context, CGColorGetComponents(_tintColor.CGColor)); CGContextFillRect(context, rect); //blend mode overlay CGContextSetBlendMode(context, kCGBlendModeOverlay); //draw the image CGContextDrawImage(context, rect, _image.CGImage); } @end 

Cela pourrait être très utile: PhotoshopFramework est une bibliothèque puissante pour manipuler des images sur Objective-C. Cela a été développé pour apporter les mêmes fonctionnalités que les utilisateurs d’Adobe Photoshop. Exemples: définir des couleurs à l’aide de RVB 0-255, appliquer des filtres de fusion, des transformations …

Est open source, voici le lien du projet: https://sourceforge.net/projects/photoshopframew/

 UIImage * image = mySourceImage; UIColor * color = [UIColor yellowColor]; UIGraphicsBeginImageContext(image.size); [image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height) blendMode:kCGBlendModeNormal alpha:1]; UIBezierPath * path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, image.size.width, image.size.height)]; [color setFill]; [path fillWithBlendMode:kCGBlendModeMultiply alpha:1]; //look up blending modes for your needs UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); //use newImage for something 

Voici une autre façon d’implémenter la teinte de l’image, surtout si vous utilisez déjà QuartzCore pour autre chose. C’était ma réponse à une question similaire .

Import QuartzCore:

 #import  

Créez CALayer transparent et ajoutez-le en tant que sous-couche pour l’image à colorier:

 CALayer *sublayer = [CALayer layer]; [sublayer setBackgroundColor:[UIColor whiteColor].CGColor]; [sublayer setOpacity:0.3]; [sublayer setFrame:toBeTintedImage.frame]; [toBeTintedImage.layer addSublayer:sublayer]; 

Ajoutez QuartzCore à votre liste de projets (si ce n’est déjà fait), sinon vous obtiendrez des erreurs de compilation comme ceci:

 Undefined symbols for architecture i386: "_OBJC_CLASS_$_CALayer" 

Pour ceux d’entre vous qui tentent de sous-classer une classe UIImageView et d’être bloqués à “drawRect: n’est pas appelé”, notez que vous devez sous-classer une classe UIView, car pour les classes UIImageView, la méthode “drawRect:” n’est pas appelée. Lire plus ici: drawRect n’étant pas appelé dans ma sous-classe de UIImageView

Pour Swift 2.0,

  let image: UIImage! = UIGraphicsGetImageFromCurrentImageContext() imgView.image = imgView.image!.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate) imgView.tintColor = UIColor(red: 51/255.0, green: 51/255.0, blue: 51/255.0, alpha: 1.0) 

La seule chose à laquelle je pourrais penser serait de créer une vue rectangular, en grande partie transparente, avec la couleur souhaitée et de la placer sur votre vue en l’ajoutant en tant que sous-vue. Je ne suis pas sûr que cela teint vraiment l’image comme vous l’imaginez, je ne suis pas sûr de savoir comment vous pirater une image et remplacer sélectivement certaines couleurs par d’autres … ça me semble assez ambitieux.

Par exemple:

 UIImageView *yourPicture = (however you grab the image); UIView *colorBlock = [[UIView alloc] initWithFrame:yourPicture.frame]; //Replace RGB and A with values from 0 - 1 based on your color and transparency colorBlock.backgroundColor = [UIColor colorWithRed:R green:G blue:B alpha:A]; [yourPicture addSubView:colorBlock]; 

Documentation pour UIColor:

 colorWithRed:green:blue:alpha: Creates and returns a color object using the specified opacity and RGB component values. + (UIColor *)colorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha Parameters red - The red component of the color object, specified as a value from 0.0 to 1.0. green - The green component of the color object, specified as a value from 0.0 to 1.0. blue - The blue component of the color object, specified as a value from 0.0 to 1.0. alpha - The opacity value of the color object, specified as a value from 0.0 to 1.0. Return Value The color object. The color information represented by this object is in the device RGB colorspace. 

Vous pouvez également envisager de mettre en cache l’image composite pour la performance et la rendre simplement dans drawRect :, puis mise à jour si un indicateur sale est effectivement sale. Bien que vous puissiez le changer souvent, il peut y avoir des cas où des tirages arrivent et que vous n’êtes pas sale, vous pouvez donc simplement actualiser à partir du cache. Si la mémoire est plus un problème que la performance, vous pouvez l’ignorer 🙂

J’ai une bibliothèque que j’ai ouverte pour cela: ios-image-filters

J’ai fait des macros à cet effet:

 #define removeTint(view) \ if ([((NSNumber *)[view.layer valueForKey:@"__hasTint"]) boolValue]) {\ for (CALayer *layer in [view.layer sublayers]) {\ if ([((NSNumber *)[layer valueForKey:@"__isTintLayer"]) boolValue]) {\ [layer removeFromSuperlayer];\ break;\ }\ }\ } #define setTint(view, tintColor) \ {\ if ([((NSNumber *)[view.layer valueForKey:@"__hasTint"]) boolValue]) {\ removeTint(view);\ }\ [view.layer setValue:@(YES) forKey:@"__hasTint"];\ CALayer *tintLayer = [CALayer new];\ tintLayer.frame = view.bounds;\ tintLayer.backgroundColor = [tintColor CGColor];\ [tintLayer setValue:@(YES) forKey:@"__isTintLayer"];\ [view.layer addSublayer:tintLayer];\ } 

Pour utiliser, il suffit d’appeler:

 setTint(yourView, yourUIColor); //Note: include opacity of tint in your UIColor using the alpha channel (RGBA), eg [UIColor colorWithRed:0.5f green:0.0 blue:0.0 alpha:0.25f]; 

Lorsque vous supprimez la teinte, appelez simplement:

 removeTint(yourView);