2009-09-11 13 views
26

Tengo una imagen en escala de grises que quiero usar para dibujar los controles Cocoa. La imagen tiene varios niveles de gris. Donde sea más oscuro, quiero que dibuje un color de tinte específico más oscuro. Quiero que sea transparente donde la imagen de origen es blanca.Tinte de un NSImage (o CIImage) en escala de grises

Básicamente, quiero reproducir el comportamiento de tintColor visto en UINavigationBar en el iPhone.

varias opciones Hasta ahora, he explorado:

  • dibujar el color de la tinta sobre la imagen en escala de grises utilizando la composición SourceOver -> Esto requiere un color de tinte no opaca -> El resultado sale mucho más oscuro de lo deseado

  • Utilice un CIMultiplyCompositing CIFilter para teñir la imagen -> no puedo [CIImage drawAtPoint: fromRect: operación: fracción:] para extraer sólo una parte de la imagen. Lo mismo funciona bien con NSImage -> Obtengo caídas ocasionales que no tienen sentido

  • Transforma la imagen en escala de grises en una máscara. Es decir. El negro debe ser opaco. Blanco debe ser transparente. Gray debe tener valores alfa intermedios. -> Esta parece ser la mejor solución -> Por más que lo intente, no puedo lograrlo.

+0

He decidido hacer el tinte y el recorte en dos pasos sucesivos. CIMultiplyCompositing funciona bien siempre que dibuje toda la imagen. Dibujo un NSImage, que a su vez dibujo parcialmente en una imagen más pequeña. –

Respuesta

8
- (NSImage *)imageTintedWithColor:(NSColor *)tint 
{ 
    if (tint != nil) { 
     NSSize size = [self size]; 
     NSRect bounds = { NSZeroPoint, size }; 
     NSImage *tintedImage = [[NSImage alloc] initWithSize:size]; 

     [tintedImage lockFocus]; 

     CIFilter *colorGenerator = [CIFilter filterWithName:@"CIConstantColorGenerator"]; 
     CIColor *color = [[[CIColor alloc] initWithColor:tint] autorelease]; 

     [colorGenerator setValue:color forKey:@"inputColor"]; 

     CIFilter *monochromeFilter = [CIFilter filterWithName:@"CIColorMonochrome"]; 
     CIImage *baseImage = [CIImage imageWithData:[self TIFFRepresentation]]; 

     [monochromeFilter setValue:baseImage forKey:@"inputImage"];  
     [monochromeFilter setValue:[CIColor colorWithRed:0.75 green:0.75 blue:0.75] forKey:@"inputColor"]; 
     [monochromeFilter setValue:[NSNumber numberWithFloat:1.0] forKey:@"inputIntensity"]; 

     CIFilter *compositingFilter = [CIFilter filterWithName:@"CIMultiplyCompositing"]; 

     [compositingFilter setValue:[colorGenerator valueForKey:@"outputImage"] forKey:@"inputImage"]; 
     [compositingFilter setValue:[monochromeFilter valueForKey:@"outputImage"] forKey:@"inputBackgroundImage"]; 

     CIImage *outputImage = [compositingFilter valueForKey:@"outputImage"]; 

     [outputImage drawAtPoint:NSZeroPoint 
         fromRect:bounds 
         operation:NSCompositeCopy 
         fraction:1.0]; 

     [tintedImage unlockFocus]; 

     return [tintedImage autorelease]; 
    } 
    else { 
     return [[self copy] autorelease]; 
    } 
} 

- (NSImage*)imageCroppedToRect:(NSRect)rect 
{ 
    NSPoint point = { -rect.origin.x, -rect.origin.y }; 
    NSImage *croppedImage = [[NSImage alloc] initWithSize:rect.size]; 

    [croppedImage lockFocus]; 
    { 
     [self compositeToPoint:point operation:NSCompositeCopy]; 
    } 
    [croppedImage unlockFocus]; 

    return [croppedImage autorelease]; 
} 
+2

Realmente hace falta tiempo innecesario para que los n00bs como yo averigüemos cómo usar este código: 1- Enlace QuartzCore 2- importarlo 3- Agregue una categoría NSImage con este impresionante código dentro. – Mazyod

+2

Sí, estoy de acuerdo ... Este código me parece ... pero no decir implícitamente que "es una categoría" va a dejar estupefacta a mucha gente. para todos los tontos ... Si no sabes qué es una categoría ... guarda este código, averigua qué es una categoría, luego regresa y úsala. ∀Ⓛ∃✖ –

+0

aunque entiendo el código, parece que no puedo obtener ningún resultado. Le doy una imagen tiff con un ícono negro y fondo transparente e intento matizarlo con blanco o negro. pero se mantiene negro. – glasz

3

El filtro CIMultiplyCompositing es definitivamente la manera de hacerlo. Si está fallando, ¿puedes publicar un rastro de pila? Uso CIFilters en gran medida y no tengo problemas.

//assume inputImage is the greyscale CIImage you want to tint 

CIImage* outputImage = nil; 

//create some green 
CIFilter* greenGenerator = [CIFilter filterWithName:@"CIConstantColorGenerator"]; 
CIColor* green = [CIColor colorWithRed:0.30 green:0.596 blue:0.172]; 
[greenGenerator setValue:green forKey:@"inputColor"]; 
CIImage* greenImage = [greenGenerator valueForKey:@"outputImage"]; 

//apply a multiply filter 
CIFilter* filter = [CIFilter filterWithName:@"CIMultiplyCompositing"]; 
[filter setValue:greenImage forKey:@"inputImage"]; 
[filter setValue:inputImage forKey:@"inputBackgroundImage"]; 
outputImage = [filter valueForKey:@"outputImage"]; 

[outputImage drawAtPoint:NSZeroPoint fromRect:NSRectFromCGRect([outputImage extent]) operation:NSCompositeCopy fraction:1.0]; 
+1

¿Realmente necesita utilizar el "CIConstantColorGenerator"? ¿Por qué no CIImage * greenImage = [CIImage imageWithColor: green]; ? – cocoafan

+0

Sí, podrías. Ese método solo se introdujo en 10.5 y en 2009, cuando escribí esta respuesta, Tiger todavía era de uso común. –

+0

¿Cuál es el propósito de la última línea del código? ¿no es la imagen de salida ya producida en la línea antes de eso? – SpaceDog

39

La solución anterior no funcionó para mí. Pero esta solución mucho más fácil funciona muy bien para mí

- (NSImage *)imageTintedWithColor:(NSColor *)tint 
{ 
    NSImage *image = [self copy]; 
    if (tint) { 
     [image lockFocus]; 
     [tint set]; 
     NSRect imageRect = {NSZeroPoint, [image size]}; 
     NSRectFillUsingOperation(imageRect, NSCompositeSourceAtop); 
     [image unlockFocus]; 
    } 
    return image; 
} 
+0

Bien, esto obtiene lo siguiente [result.png] (http://i.stack.imgur.com/lzk9m.png) – Besi

+2

Esperemos que esto sea útil para alguien en el futuro: si tiene problemas para hacer que funcione este código, configure la imagen copiada para que NO sea una plantilla, es decir, antes de devolver la imagen, [image setTemplate: NO]. De forma un tanto contra-intuitiva, si la imagen se establece como una plantilla, ignorará por completo el código que colorea la imagen, aunque la vista previa del depurador le mostrará que la imagen está coloreada correctamente. Personalmente encontré este problema porque estoy compartiendo un catálogo de activos con un proyecto de iOS donde los conjuntos de imágenes están todos configurados para ser plantillas. – user3062913

+0

Todo estaba funcionando, pero tuve que desactivar Template Image en el catálogo de activos para que esto funcione. Punto muy importante, gracias! –

12

La rápida aplicación de la respuesta de bluebamboo:

func tintedImage(_ image: NSImage, tint: NSColor) -> NSImage { 
    guard let tinted = image.copy() as? NSImage else { return image } 
    tinted.lockFocus() 
    tint.set() 

    let imageRect = NSRect(origin: NSZeroPoint, size: image.size) 
    NSRectFillUsingOperation(imageRect, .sourceAtop) 

    tinted.unlockFocus() 
    return tinted 
} 
+2

Gracias por publicar la traducción de Swift. Lo hice sobre la marcha, así que traduje la primera respuesta. La próxima vez puedo desplazarme hacia abajo aquí y obtenerlo en Swift. ¡Increíble! – Farini

+0

Gracias esto funcionó como un encanto para mí. Solo una nota rápida (al menos en macOS) no funciona si la imagen que utiliza está configurada para representarse como "Plantilla", elija renderizar como "Predeterminado". –

4

versión Swift en forma de Extensión:

extension NSImage { 
    func tintedImageWithColor(color:NSColor) -> NSImage { 
     let size  = self.size 
     let imageBounds = NSMakeRect(0, 0, size.width, size.height) 
     let copiedImage = self.copy() as! NSImage 

     copiedImage.lockFocus() 
     color.set() 
     NSRectFillUsingOperation(imageBounds, .CompositeSourceAtop) 
     copiedImage.unlockFocus() 

     return copiedImage 
    } 
} 
+0

Siguiendo las nuevas pautas Swift API, puede cambiar la definición a: 'func tinted (color: NSColor) -> NSImage' – Juul

+0

gracias, verifico si el código compila bajo swift 3.0 y actualiza la respuesta. – CryingHippo

2

Quería teñir una imagen con un color de tinte que tuviera alfa sin ver los colores originales de la imagen. He aquí cómo hacerlo:

extension NSImage { 
    func tinting(with tintColor: NSColor) -> NSImage { 
     guard let cgImage = self.cgImage(forProposedRect: nil, context: nil, hints: nil) else { return self } 

     return NSImage(size: size, flipped: false) { bounds in 
      guard let context = NSGraphicsContext.current()?.cgContext else { return false } 

      tintColor.set() 
      context.clip(to: bounds, mask: cgImage) 
      context.fill(bounds) 

      return true 
     } 
    } 
} 
Cuestiones relacionadas