2009-09-11 12 views
26

Ho un'immagine in scala di grigi che desidero utilizzare per disegnare i controlli Cocoa. L'immagine ha vari livelli di grigio. Dove è più scuro, voglio che disegni un colore di tinta specificato più scuro. Voglio che sia trasparente dove l'immagine di origine è bianca.Tinting a scala di grigi NSImage (o CIImage)

Fondamentalmente, voglio riprodurre il comportamento di tintColor visto in UINavigationBar su iPhone.

Finora, ho esplorato diverse opzioni:

  • disegnare il colore della tinta sull'immagine scala di grigi usando composizione SourceOver -> Questo richiede un colore tinta non opachi -> Il risultato viene fuori molto più scuro di quanto desiderato

  • Utilizzare un CIMultiplyCompositing CIFilter per colorare l'immagine -> non posso [CIImage drawAtPoint: fromRect: operazione: frazione:] per disegnare solo una parte dell'immagine. Lo stesso funziona bene con NSImage -> Ottengo crash occasionali che non riesco a dare un senso a

  • Trasforma l'immagine in scala di grigi in una maschera. Cioè Il nero dovrebbe essere opaco. Il bianco dovrebbe essere trasparente Il grigio dovrebbe avere valori alfa intermedi. -> Questa sembra essere la soluzione migliore -> Prova come potrei, non posso ottenere questo.

+0

Ho deciso di eseguire la colorazione e il ritaglio in due passaggi successivi. CIMultiplyCompositing funziona bene finché disegno l'intera immagine. Disegno su un NSImage, che a sua volta disegno parzialmente su un'immagine più piccola. –

risposta

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

Ci vuole davvero del tempo inutile per n00bs come me per capire come usare questo codice: 1- Link QuartzCore 2- importarlo 3- Aggiungi una categoria NSImage con questo fantastico codice all'interno. – Mazyod

+2

Sì, concordo .. Questo codice oscilla ... ma non implicitamente dicendo che "è una categoria" lascerà un sacco di gente stupita. per tutti voi stupidi .. Se non sapete cos'è una categoria .. salvate questo codice, cercate di capire che cos'è una categoria, quindi tornate indietro e usatela. ∀Ⓛ∃✖ –

+0

anche se capisco il codice non riesco a ottenere alcun risultato da esso. gli do un'immagine tiff con un'icona completamente nera e uno sfondo trasparente e cerco di tingersi di bianco o nero. ma rimane nero. – glasz

3

Il filtro CIMultiplyCompositing è sicuramente il modo per farlo. Se si blocca, puoi pubblicare una traccia dello stack? Uso pesantemente i CIFilters e non ho problemi di crash.

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

Hai veramente bisogno di usare "CIConstantColorGenerator"? Perché non CIImage * greenImage = [CIImage imageWithColor: green]; ? – cocoafan

+0

Sì, è possibile. Questo metodo è stato introdotto solo in 10.5 e nel 2009, quando ho scritto questa risposta, Tiger era ancora di uso comune. –

+0

qual è lo scopo dell'ultima riga del codice? non è l'outputImage già prodotto sulla linea prima di quello? – SpaceDog

39

La soluzione di cui sopra non ha funzionato per me. Ma questa soluzione molto più semplice grandi opere per me

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

Bello, questo ottiene il seguente [result.png] (http://i.stack.imgur.com/lzk9m.png) – Besi

+2

Speriamo che questo si riveli utile a qualcuno in futuro: se hai problemi a far funzionare questo codice, imposta l'immagine copiata su NON un modello, ovvero prima di restituire l'immagine, [image setTemplate: NO]. In qualche modo contro-intuitivamente, se l'immagine è impostata come modello, ignorerà completamente il codice che colora l'immagine, anche se l'anteprima del debugger mostrerà che l'immagine è colorata correttamente. Personalmente mi sono imbattuto in questo problema perché sto condividendo un catalogo di risorse con un progetto iOS in cui i set di immagini sono tutti impostati come modelli. – user3062913

+0

Tutto funzionava, ma dovevo disattivare l'immagine del modello nel catalogo delle risorse perché funzionasse. Punto molto importante, grazie! –

12

Un'implementazione Swift della risposta di 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

Grazie per aver pubblicato la traduzione di Swift. L'ho fatto al volo, quindi ho tradotto la prima risposta. La prossima volta potrò semplicemente scorrere qui e scaricarlo in Swift. Eccezionale! – Farini

+0

Grazie per aver funzionato come un incantesimo per me. Solo una nota rapida nota (almeno su macOS) non funziona se l'immagine che si utilizza è impostata per il rendering come "Modello", scegliere il rendering come "Predefinito". –

4

versione Swift in forma di estensione:

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

Seguendo le nuove linee guida dell'API Swift, è possibile modificare la definizione in: 'func tinted (colour: NSColor) -> NSImage' – Juul

+0

grazie, controllerò se il codice viene compilato sotto swift 3.0 e aggiorna la risposta. – CryingHippo

2

Volevo tingere un'immagine con un colore tinta che aveva alfa senza vedere i colori originali dell'immagine. Ecco come è possibile farlo:

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