web-dev-qa-db-fra.com

Comment devrais-je teinter une image par programme sur l'iPhone?

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

alt text

Je changerai continuellement la valeur de la couleur.

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, par opposition à un lien.

Mise à jour: Sous-classer un UIImageView avec le code Ramin suggéré. 

Je mets ceci 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é.

Dernière mise à jour: J'ai trouvé un vieux projet avec une imageView correctement configurée pour pouvoir tester le code de Ramin et cela fonctionne à merveille!

Dernière mise à jour:

Pour ceux qui viennent d’apprendre à connaître Core Graphics, voici la chose la plus simple qui puisse fonctionner.

Dans votre sous-classe UIView:

- (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
}
85
willc2

Tout d'abord, vous voudrez sous-classer UIImageView et substituer 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 jeu de paramètres personnalisé qui force le rafraîchissement 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 voudrez d'abord dessiner l'image, puis la superposer avec un rectangle rempli de la couleur souhaitée ainsi que du mode de fusion approprié, comme ceci:

- (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 devez limiter le dessin réel à la seule zone transmise à drawRect. Toutefois, comme l'image d'arrière-plan doit être redessinée à chaque changement de couleur, il est probable que tout sera à rafraîchir.

Pour l'utiliser, créez une instance de l'objet, 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).

44
Ramin

Dans iOS7, ils ont introduit la propriété tintColor sur UIImageView et renderingMode sur UIImage. Pour teinter un UIImage sur iOS7, 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
75
Toland Hon

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

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

alors ne perdez même pas de temps à essayer de remplacer cette méthode dans une sous-classe UIImageView. Commencez par UIView à la place.

26
mattorb

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 cela.

J'ai nommé ma classe CSTintedImageView et elle hérite de UIView puisque UIImageView n'appelle pas la méthode drawRect:, comme indiqué dans les réponses précédentes . J'ai défini un initialiseur désigné similaire à celui de 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
25
user263865

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 sont familiers. Exemples: définition des couleurs à l'aide de RVB 0-255, application de filtres de fondu, de transformations ...

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

5
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
4
gngrwzrd

Voici un autre moyen d'implémenter la teinte des images, surtout si vous utilisez déjà QuartzCore pour autre chose. C'était ma réponse pour une question similaire

Importer QuartzCore:

#import <QuartzCore/QuartzCore.h>

Créez un CALayer transparent et ajoutez-le en tant que sous-couche à l'image que vous souhaitez teinter:

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 pas déjà fait), sinon vous obtiendrez des erreurs de compilation telles que

Undefined symbols for architecture i386: "_OBJC_CLASS_$_CALayer"
3
lekksi

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

2
pierre

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)
1
yazh

Vous pouvez également envisager de mettre en cache l'image composée pour la performance et de la restituer dans drawRect :, puis de la mettre à jour si un indicateur incorrect est effectivement endommagé. Même si vous le modifiez souvent, il peut arriver que des tirages arrivent et que vous ne soyez 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 :)

0
Steve Riggins

La seule chose à laquelle je puisse penser serait de créer une vue rectangulaire essentiellement transparente avec la couleur désirée et de la superposer à la vue de votre image en l’ajoutant en tant que sous-vue. Je ne sais pas si cela va vraiment ternir l'image de la façon dont vous l'imaginez, je ne sais pas comment vous pourriez pirater une image et remplacer sélectivement certaines couleurs par d'autres ... Cela me semble plutôt ambitieux.

Par exemple:

UIImageView *yourPicture = (however you grab the image);
UIView *colorBlock = [[UIView alloc] initWithFrame:yourPicture.frame];
//Replace R G B 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. 
0
Tim Bowen

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

0
esilver

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 l'utiliser, il suffit d'appeler:

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

Lorsque vous supprimez la teinte, appelez simplement:

removeTint(yourView);
0
Albert Renshaw