¿Cómo puedo teñir una imagen programáticamente en el iPhone?
cocoa-touch image-processing (12)
Me gustaría colorear una imagen con una referencia de color. Los resultados deben verse como el modo de fusión Multiplicar en Photoshop, donde los blancos serían reemplazados con tinte :
Voy a cambiar el valor del color continuamente.
Seguimiento: Pondría el código para hacer esto en el método drawRect: de mi ImageView, ¿verdad?
Como siempre, un fragmento de código sería de gran ayuda en mi comprensión, a diferencia de un enlace.
Actualización: Subclassing UIImageView con el código sugerido por Ramin .
Puse esto en viewDidLoad: of my view controller:
[self.lena setImage:[UIImage imageNamed:kImageName]];
[self.lena setOverlayColor:[UIColor blueColor]];
[super viewDidLoad];
Veo la imagen, pero no está teñida. También intenté cargar otras imágenes, configurar la imagen en IB y llamar a setNeedsDisplay: en mi controlador de vista.
Actualización : drawRect: no se está llamando.
Actualización final: ¡Encontré un proyecto antiguo que tenía una imageView configurada correctamente para poder probar el código de Ramin y funciona como un encanto!
Final, actualización final:
Para aquellos de ustedes que recién están aprendiendo sobre Core Graphics, aquí es lo más simple que podría funcionar.
En tu UIView subclasificado:
- (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
}
Aquí hay otra forma de implementar el tintado de imagen, especialmente si ya está usando QuartzCore para otra cosa. Esta fue mi respuesta para una pregunta similar .
Importar QuartzCore:
#import <QuartzCore/QuartzCore.h>
Cree CALayer transparente y agréguelo como una subcapa para la imagen que desea teñir:
CALayer *sublayer = [CALayer layer];
[sublayer setBackgroundColor:[UIColor whiteColor].CGColor];
[sublayer setOpacity:0.3];
[sublayer setFrame:toBeTintedImage.frame];
[toBeTintedImage.layer addSublayer:sublayer];
Agregue QuartzCore a su lista de marcos de proyectos (si no está ya allí), de lo contrario obtendrá errores de compilación como este:
Undefined symbols for architecture i386: "_OBJC_CLASS_$_CALayer"
En iOS7, han introducido la propiedad tintColor en UIImageView y renderingMode en UIImage. Para teñir un UIImage en iOS7, todo lo que tienes que hacer es:
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
Esto podría ser muy útil: PhotoshopFramework es una poderosa biblioteca para manipular imágenes en Objective-C. Esto fue desarrollado para brindar las mismas funcionalidades que los usuarios de Adobe Photoshop están familiarizados. Ejemplos: Establezca colores usando RGB 0-255, aplique archivos de mezcla, transformaciones ...
Es de código abierto, aquí está el enlace del proyecto: https://sourceforge.net/projects/photoshopframew/
Lo único que se me ocurre es crear una vista rectangular, en su mayoría transparente, con el color deseado y colocarla sobre la vista de la imagen añadiéndola como subvista. No estoy seguro si esto realmente teñirá la imagen de la forma en que te imaginas, aunque no estoy seguro de cómo harías para piratear una imagen y reemplazar selectivamente ciertos colores con otros ... suena bastante ambicioso para mí.
Por ejemplo:
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];
Documentación para 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.
Para 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)
Para aquellos de ustedes que intentan subclasificar una clase UIImageView y quedar atrapados en "drawRect: no se llama", tenga en cuenta que debe subclasificar una clase UIView en su lugar, porque para las clases UIImageView, no se llama al método "drawRect:". Lea más aquí: drawRect no se llama en mi subclase de UIImageView
Primero querrás subclasificar UIImageView y anular el método drawRect. Su clase necesita una propiedad UIColor (llamémosla overlayColor) para mantener el color de mezcla y un setter personalizado que obliga a volver a dibujar cuando cambia el color. Algo como esto:
- (void) setOverlayColor:(UIColor *)newColor {
if (overlayColor)
[overlayColor release];
overlayColor = [newColor retain];
[self setNeedsDisplay]; // fires off drawRect each time color changes
}
En el método drawRect, primero deberá dibujar la imagen y luego superponerla con un rectángulo relleno con el color que desee junto con el modo de fusión adecuado, algo como esto:
- (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);
}
Normalmente, para optimizar el dibujo, restringirías el dibujo real solo al área que se pasa a drawRect, pero como la imagen de fondo debe volver a dibujarse cada vez que cambia el color, es probable que todo deba actualizarse.
Para usarlo, crea una instancia del objeto y luego establece la propiedad de la image
(heredada de UIImageView) en la imagen y overlayColor
en un valor UIColor (los niveles de mezcla se pueden ajustar cambiando el valor alfa del color que pasas).
Quería teñir una imagen con alfa y creé la siguiente clase. Por favor, avíseme si encuentra algún problema con él.
He nombrado mi clase CSTintedImageView
y hereda de UIView
ya que UIImageView
no llama al método drawRect:
como se menciona en las respuestas anteriores. He configurado un inicializador designado similar al que se encuentra en la clase UIImageView
.
Uso:
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
Solo una aclaración rápida (después de algunas investigaciones sobre este tema). El documento de Apple here establece claramente que:
La clase
UIImageView
está optimizada para dibujar sus imágenes en la pantalla.UIImageView
no llama al métododrawRect:
de sus subclases. Si su subclase necesita incluir código de dibujo personalizado, debeUIView
subclase de la claseUIView
lugar.
así que no pierdas el tiempo tratando de anular ese método en una subclase UIImageView
. Comience con UIView
en UIView
lugar.
También es posible que desee considerar el almacenamiento en caché de la imagen compuesta para el rendimiento y simplemente representarla en drawRect :, y luego actualizarla si una bandera sucia está realmente sucia. Aunque es posible que lo cambie a menudo, es posible que haya casos en los que los dibujos estén llegando y que no esté sucio, por lo que puede actualizarlos desde la memoria caché. Si la memoria es más un problema que el rendimiento, puede ignorar esto :)
Tengo una biblioteca que abrí para esto: ios-image-filters
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