objective-c cocoa core-image

objective c - Tinte de un NSImage(o CIImage) en escala de grises



objective-c cocoa (7)

El filtro CIMultiplyCompositing es definitivamente la manera de hacer esto. 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];

Tengo una imagen en escala de grises que quiero usar para dibujar 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.

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

Hasta ahora, he explorado varias opciones:

  • Dibuja el color del tinte sobre la imagen en escala de grises utilizando la composición de SourceOver -> Esto requiere un color de tinte no opaco -> El resultado sale mucho más oscuro que el deseado

  • Use un CIMultiplyCompositing CIFilter para colorear la imagen -> No puedo [CIImage drawAtPoint: fromRect: operation: fraction:] dibujar solo una parte de la imagen. Lo mismo funciona bien con NSImage -> Obten ocasionalmente cuelgues de los que no puedo entender

  • 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 lograr esto.


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


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


Swift versión 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 } }


Una implementación Swift 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 }



Quería teñir una imagen con un color de tinte que tuviera alfa sin ver los colores originales de la imagen. Así es como puedes hacer eso:

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