Hoe kan ik een transparante PNG-afbeelding in iPhone kleuren?

Ik weet dat het mogelijk is om een ​​rechthoekige afbeelding te kleuren door er een CGContextFillRect overheen te tekenen en de overvloeimodus in te stellen. Ik kan er echter niet achter komen hoe ik een tint op een transparante afbeelding, zoals een pictogram, moet aanbrengen. Het moet mogelijk zijn, aangezien de SDK het zelf op tabbalken doet. Zou iemand een fragment kunnen geven?

UPDATE:

Er zijn veel goede suggesties gegeven voor dit probleem sinds ik het oorspronkelijk vroeg. Zorg ervoor dat u alle antwoorden doorleest om erachter te komen wat het beste bij u past.

UPDATE (30 april 2015):

Met iOS 7.0 kan ik nu gewoon het volgende doen, wat zou voldoen aan de behoeften van mijn oorspronkelijke vraag. Maar als je meer gecompliceerde gevallen hebt, bekijk dan alle antwoorden.

UIImage *iconImage = [[UIImage imageNamed:@"myImageName"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];    
UIImageView *icon = [[UIImageView alloc] initWithImage:iconImage];
icon.tintColor = [UIColor redColor];

Antwoord 1, autoriteit 100%

Update:hier is een Samenvattingvoor een Swift UIColor extensie met behulp van de onderstaande code.


Als u een grijswaardenafbeeldingheeft en u wilt dat wit de tintkleurwordt, dan is kCGBlendModeMultiplyde juiste keuze. Met deze methode kun je geen highlights hebben die lichter zijn dan je kleur.

Integendeel, als je een afbeelding zonder grijswaardenhebt, OFheb je highlights enschaduwendie behouden moet blijven, de overvloeimodus kCGBlendModeColoris de weg te gaan. Wit blijft wit en zwart blijft zwart omdat de lichtheid van het beeld behouden blijft. Deze modus is alleen gemaakt voor het kleuren – het is hetzelfde als de Color-laagovervloeimodus van Photoshop (disclaimer: er kunnen enigszins afwijkende resultaten optreden).

Houd er rekening mee dat het kleuren van alfapixels niet correct werkt, noch in iOS, noch in Photoshop – halftransparante zwarte pixels zouden niet zwart blijven. Ik heb het antwoord hieronder geüpdatet om dat probleem te omzeilen, het duurde behoorlijk lang om erachter te komen.

U kunt ook een van de overvloeimodi kCGBlendModeSourceIn/DestinationIngebruiken in plaats van CGContextClipToMask.

Als u een UIImagewilt maken, kan elk van de volgende codesecties worden omgeven door de volgende code:

UIGraphicsBeginImageContextWithOptions (myIconImage.size, NO, myIconImage.scale); // for correct resolution on retina, thanks @MobileVet
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0, myIconImage.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGRect rect = CGRectMake(0, 0, myIconImage.size.width, myIconImage.size.height);
// image drawing code here
UIImage *coloredImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Dus hier is de code voor het kleuren van een transparante afbeelding met kCGBlendModeColor:

// draw black background to preserve color of transparent pixels
CGContextSetBlendMode(context, kCGBlendModeNormal);
[[UIColor blackColor] setFill];
CGContextFillRect(context, rect);
// draw original image
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, rect, myIconImage.CGImage);
// tint image (loosing alpha) - the luminosity of the original image is preserved
CGContextSetBlendMode(context, kCGBlendModeColor);
[tintColor setFill];
CGContextFillRect(context, rect);
// mask by alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawImage(context, rect, myIconImage.CGImage);

Als je afbeelding geen halftransparante pixels heeft, kun je het ook andersom doen met kCGBlendModeLuminosity:

// draw tint color
CGContextSetBlendMode(context, kCGBlendModeNormal);
[tintColor setFill];
CGContextFillRect(context, rect);
// replace luminosity of background (ignoring alpha)
CGContextSetBlendMode(context, kCGBlendModeLuminosity);
CGContextDrawImage(context, rect, myIconImage.CGImage);
// mask by alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawImage(context, rect, myIconImage.CGImage);

Als je niet om helderheid geeft, omdat je net een afbeelding hebt met een alfakanaal dat moet worden getint met een kleur, kun je dit op een efficiëntere manier doen:

// draw tint color
CGContextSetBlendMode(context, kCGBlendModeNormal);
[tintColor setFill];
CGContextFillRect(context, rect);
// mask by alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawImage(context, rect, myIconImage.CGImage);

of andersom:

// draw alpha-mask
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, rect, myIconImage.CGImage);
// draw tint color, preserving alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeSourceIn);
[tintColor setFill];
CGContextFillRect(context, rect);

Veel plezier!


Antwoord 2, autoriteit 13%

Ik had het meeste succes met deze methode, omdat de andere die ik probeerde vervormde kleuren veroorzaakten voor semi-transparante pixels voor bepaalde kleurcombinaties. Dit zou ook wat beter moeten zijn aan de prestatiekant.

+ (UIImage *) imageNamed:(NSString *) name withTintColor: (UIColor *) tintColor {
    UIImage *baseImage = [UIImage imageNamed:name];
    CGRect drawRect = CGRectMake(0, 0, baseImage.size.width, baseImage.size.height);
    UIGraphicsBeginImageContextWithOptions(baseImage.size, NO, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(context, 0, baseImage.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    // draw original image
    CGContextSetBlendMode(context, kCGBlendModeNormal);
    CGContextDrawImage(context, drawRect, baseImage.CGImage);
    // draw color atop
    CGContextSetFillColorWithColor(context, tintColor.CGColor);
    CGContextSetBlendMode(context, kCGBlendModeSourceAtop);
    CGContextFillRect(context, drawRect);
    UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return tintedImage;
}

Antwoord 3, autoriteit 11%

Na wat rond te hebben gezocht, is de beste oplossing die ik tot nu toe heb gevonden, het gebruik van een combinatie van de overvloeimodus en het uitknipmasker om een ​​transparante PNG in te kleuren/kleuren:

CGContextSetBlendMode (context, kCGBlendModeMultiply);
CGContextDrawImage(context, rect, myIconImage.CGImage);
CGContextClipToMask(context, rect, myIconImage.CGImage);
CGContextSetFillColorWithColor(context, tintColor);
CGContextFillRect(context, rect);

Antwoord 4, autoriteit 10%

Ik kan resultaten krijgen die heel dicht bij de tint in de Apple-navigatiebalk liggen door kCGBlendModeOverlay te gebruiken. Uitstekend @fabb-antwoord nemen en @omz-aanpak combineren in dit bericht https://stackoverflow.com/a/4684876/229019Ik kwam met deze oplossing met de resultaten die ik verwachtte:

- (UIImage *)tintedImageUsingColor:(UIColor *)tintColor;
{
    UIGraphicsBeginImageContextWithOptions (self.size, NO, [[UIScreen mainScreen] scale]);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
    // draw original image
    [self drawInRect:rect blendMode:kCGBlendModeNormal alpha:1.0f];
    // tint image (loosing alpha). 
    // kCGBlendModeOverlay is the closest I was able to match the 
    // actual process used by apple in navigation bar 
    CGContextSetBlendMode(context, kCGBlendModeOverlay);
    [tintColor setFill];
    CGContextFillRect(context, rect);
    // mask by alpha values of original image
    [self drawInRect:rect blendMode:kCGBlendModeDestinationIn alpha:1.0f];
    UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return tintedImage;
}

Hier is een voorbeeld waarin verschillende grijswaardenafbeeldingen transparant worden gekleurd:

Gerenderd voorbeeld:

  • De eerste regel is de Apple-werkbalk getint [UIColor orangeColor].
  • De tweede regel is hetzelfde verloop, getint in verschillende kleuren, beginnend met een heldere kleur (= het eigenlijke verloop) en eindigend met hetzelfde oranje.
  • De derde is een eenvoudige cirkel met transparantie (het linnen is de achtergrondkleur)
  • De vierde regel is een complexe donkere, luidruchtige textuur

Antwoord 5, autoriteit 5%

Je zou een UIImage-categorie kunnen maken en het als volgt doen:

- (instancetype)tintedImageWithColor:(UIColor *)tintColor {
    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGRect rect = (CGRect){ CGPointZero, self.size };
    CGContextSetBlendMode(context, kCGBlendModeNormal);
    [self drawInRect:rect];
    CGContextSetBlendMode(context, kCGBlendModeSourceIn);
    [tintColor setFill];
    CGContextFillRect(context, rect);
    UIImage *image  = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

Antwoord 6, autoriteit 5%

In iOS7 hebben ze de eigenschap tintColor in UIImageView en renderingMode in UIImage geïntroduceerd. Zie mijn voorbeeld op https://stackoverflow.com/a/19125120/1570970


Antwoord 7, autoriteit 2%

Met iOS 7.0 kun je dit ook gewoon doen om een ​​standaard UIImageView te kleuren:

UIImage *iconImage = [[UIImage imageNamed:@"myImageName"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];    
UIImageView *icon = [[UIImageView alloc] initWithImage:iconImage];
icon.tintColor = [UIColor redColor];

Antwoord 8, autoriteit 2%

Merk op dat in het geaccepteerde antwoord van fabb, de “omringende” code voor het maken van een UIImage me de verkeerde resolutie van afbeeldingen op het retina-scherm gaf. Wijzig:

. om dit op te lossen

UIGraphicsBeginImageContext(myIconImage.size);

naar:

UIGraphicsBeginImageContextWithOptions(myIconImage.size, NO, 0.0);

De laatste parameter die is ingesteld op 0.0 is scale, en volgens Apple-documentatie:

“Als u een waarde van 0,0 opgeeft, wordt de schaalfactor ingesteld op de schaal
factor van het hoofdscherm van het apparaat”.

Heb geen toestemming om commentaar te geven, en bewerken lijkt een beetje onbeleefd, dus ik vermeld dit in een antwoord.
Voor het geval iemand hetzelfde probleem tegenkomt.


Antwoord 9

UIImageView (of welke andere weergave dan ook) heeft een achtergrondkleur die RGBA is. De alfa in de kleur kan doen wat je nodig hebt zonder iets nieuws uit te vinden.


Antwoord 10

Niet mijn werk, maar ik heb deze aanpak met succes toegepast:

http://coffeeshopped.com/2010/ 09/iphone-hoe-dynamisch-kleuren-een-uiimage


Antwoord 11

Ik wilde mijn afbeeldingsweergaven schaduw geven in mijn aangepaste UIButton-subklasse en de andere oplossingen deden niet wat ik wilde. Ik moest de kleur van de afbeelding donkerder maken. Hier leest u hoe u de helderheid kunt wijzigen met CoreImage.

Aangepaste knoppen met gearceerde afbeeldingen bij het indrukken van de knop

  1. Zorg ervoor dat u CoreImage.framework toevoegt aan de bibliotheken van uw project. (Binary koppelen aan bibliotheken)

  2. UIImage-schaduwmethode

    - (UIImage *)shadeImage:(UIImage *)image {
     CIImage *inputImage = [CIImage imageWithCGImage:image.CGImage];
     CIContext *context = [CIContext contextWithOptions:nil];
     CIFilter *filter = [CIFilter filterWithName:@"CIColorControls"
                                  keysAndValues:kCIInputImageKey, inputImage,
                        @"inputBrightness", [NSNumber numberWithFloat:-.5], nil];
     CIImage *outputImage = [filter outputImage];
     CGImageRef cgImage = [context createCGImage:outputImage fromRect:[outputImage extent]];
     UIImage *newImage = [UIImage imageWithCGImage:cgImage scale:image.scale orientation:image.imageOrientation];
     CGImageRelease(cgImage);
     return newImage;
    }
    
  3. U wilt een kopie van de context opslaan als een ivar, in plaats van deze opnieuw te maken.


Antwoord 12

Geen antwoorden zullen me helpen bij het overlopen van de stapel: onze ontwerpers tekenen UI-elementen met verschillende vormen, verschillende alfawaarden (en “alpha-holes”). In de meeste gevallen is dit een 32-bits PNG-bestand met alfakanaal, dat zwart & witte pixels (van alle mogelijke intensiteiten). Na het kleuren van zo’n foto moest ik dit getinte resultaat krijgen: witte pixels – over meer getint, en donkere pixels – minder. En dit alles met het oog op alfakanaal. En ik schreef deze methode voor mijn UIImage-categorie. Misschien niet hoog efficiënt, maar het werkt als een klok 🙂 Hier is het:

- (UIImage *)imageTintedWithColor:(UIColor *)color {
    UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
    CGContextSetBlendMode(context, kCGBlendModeCopy);
    [color setFill];
    CGContextFillRect(context, rect);
    [self drawInRect:rect blendMode:kCGBlendModeXOR alpha:1.0];
    CGContextSetBlendMode(context, kCGBlendModeXOR);
    CGContextFillRect(context, rect);
    [self drawInRect:rect blendMode:kCGBlendModeMultiply alpha:1.0];
    UIImage *coloredImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return coloredImage;
}

Antwoord 13

Ten eerste wil ik fabb bedanken voor zijn uitzonderlijke oplossing die me heeft geholpen mijn taak te volbrengen om halftransparante pictogrammen te kleuren. Omdat ik een oplossing nodig had voor C# (Monotouch) moest ik zijn code vertalen. Hier is wat ik bedacht. Kopieer en plak dit in je code en voeg je halftransparante afbeelding toe en je bent klaar.

Dus nogmaals, alle credits gaan naar fabb. Dit is alleen om C#-gebruikers een kickstart te geven 🙂

//TINT COLOR IMAGE
UIImageView iImage = new UIImageView(new RectangleF(12, 14, 24,24));
iImage.ContentMode = UIViewContentMode.ScaleAspectFit;
iImage.Image = _dataItem.Image[0] as UIImage;
UIGraphics.BeginImageContextWithOptions(iImage.Bounds.Size, false, UIScreen.MainScreen.Scale);
CGContext context = UIGraphics.GetCurrentContext();
context.TranslateCTM(0, iImage.Bounds.Size.Height);
context.ScaleCTM(1.0f, -1.0f);
RectangleF rect = new RectangleF(0,0, iImage.Bounds.Width, iImage.Bounds.Height);
// draw black background to preserve color of transparent pixels
context.SetBlendMode(CGBlendMode.Normal);
UIColor.Black.SetFill();
context.FillRect(rect);
// draw original image
context.SetBlendMode(CGBlendMode.Normal);
context.DrawImage(rect, iImage.Image.CGImage);
// tint image (loosing alpha) - the luminosity of the original image is preserved
context.SetBlendMode(CGBlendMode.Color);
UIColor.Orange.SetFill();
context.FillRect(rect);
// mask by alpha values of original image
context.SetBlendMode(CGBlendMode.DestinationIn);
context.DrawImage(rect, iImage.Image.CGImage);
UIImage coloredImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
iImage = new UIImageView(coloredImage);
iImage.Frame = new RectangleF(12, 14, 24,24);
//END TINT COLOR IMAGE
cell.Add(iImage);

Other episodes