Domanda

Sulla mia app per iPhone ho un'istanza di UIImage. Voglio ottenere un UIImage derivato che è il risultato del primo UIImage in cui uno dei suoi colori (ad es. Magenta) è reso trasparente. Come posso farlo?

È stato utile?

Soluzione

-(void)changeColor
{
    UIImage *temp23=[UIImage imageNamed:@"leaf.png"];
    CGImageRef ref1=[self createMask:temp23];
    const float colorMasking[6] = {1.0, 2.0, 1.0, 1.0, 1.0, 1.0};
    CGImageRef New=CGImageCreateWithMaskingColors(ref1, colorMasking);
    UIImage *resultedimage=[UIImage imageWithCGImage:New];
}

-(CGImageRef)createMask:(UIImage*)temp
{
    CGImageRef ref=temp.CGImage;
    int mWidth=CGImageGetWidth(ref);
    int mHeight=CGImageGetHeight(ref);
    int count=mWidth*mHeight*4;
    void *bufferdata=malloc(count);

    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;

    CGContextRef cgctx = CGBitmapContextCreate (bufferdata,mWidth,mHeight, 8,mWidth*4, colorSpaceRef, kCGImageAlphaPremultipliedFirst); 

    CGRect rect = {0,0,mWidth,mHeight};
    CGContextDrawImage(cgctx, rect, ref); 
    bufferdata = CGBitmapContextGetData (cgctx);

    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, bufferdata, mWidth*mHeight*4, NULL);
    CGImageRef savedimageref = CGImageCreate(mWidth,mHeight, 8, 32, mWidth*4, colorSpaceRef, bitmapInfo,provider , NULL, NO, renderingIntent);
    CFRelease(colorSpaceRef);
    return savedimageref;
}   

Il codice sopra è testato e ho cambiato il colore verde in rosso usando la maschera

Altri suggerimenti

OK. Avendo provato non so quante versioni di queste soluzioni, ho la mia versione personalizzata. Quello che ho scoperto è che la soluzione di @yubenyi funziona abbastanza bene, ma se vuoi prendere l'output dalla sua funzione changeWhiteColorTransparent () e passarlo indietro, non funziona.

Il mio primo passo è stato cambiare la sua funzione per accettare un colore e una tolleranza specifici in modo che il chiamante potesse specificare una gamma di colori per renderlo trasparente. Funzionava bene, quasi invariato, ma ho scoperto che l'output non era un'immagine valida per passare attraverso lo stesso codice con una seconda gamma di colori.

Dopo molte prove ed errori, ho funzionato facendo la sostituzione del colore da solo. Ho resistito perché sembrava troppo duro quando ci sono API per fare queste cose, ma non sempre si comportano come vuoi. In particolare, l'output di CGImageCreateWithMaskingColors () non può essere utilizzato come input in un'altra chiamata alla stessa funzione. Non sono stato in grado di capire perché, ma penso che abbia a che fare con il canale alfa.

In ogni caso, la mia soluzione è:

- (UIImage*) replaceColor:(UIColor*)color inImage:(UIImage*)image withTolerance:(float)tolerance {
    CGImageRef imageRef = [image CGImage];

    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetHeight(imageRef);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    NSUInteger bytesPerPixel = 4;
    NSUInteger bytesPerRow = bytesPerPixel * width;
    NSUInteger bitsPerComponent = 8;
    NSUInteger bitmapByteCount = bytesPerRow * height;

    unsigned char *rawData = (unsigned char*) calloc(bitmapByteCount, sizeof(unsigned char));

    CGContextRef context = CGBitmapContextCreate(rawData, width, height,
                                                 bitsPerComponent, bytesPerRow, colorSpace,
                                                 kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(colorSpace);

    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);

    CGColorRef cgColor = [color CGColor];
    const CGFloat *components = CGColorGetComponents(cgColor);
    float r = components[0];
    float g = components[1];
    float b = components[2];
    //float a = components[3]; // not needed

    r = r * 255.0;
    g = g * 255.0;
    b = b * 255.0;

    const float redRange[2] = {
        MAX(r - (tolerance / 2.0), 0.0),
        MIN(r + (tolerance / 2.0), 255.0)
    };

    const float greenRange[2] = {
        MAX(g - (tolerance / 2.0), 0.0),
        MIN(g + (tolerance / 2.0), 255.0)
    };

    const float blueRange[2] = {
        MAX(b - (tolerance / 2.0), 0.0),
        MIN(b + (tolerance / 2.0), 255.0)
    };

    int byteIndex = 0;

    while (byteIndex < bitmapByteCount) {
        unsigned char red   = rawData[byteIndex];
        unsigned char green = rawData[byteIndex + 1];
        unsigned char blue  = rawData[byteIndex + 2];

        if (((red >= redRange[0]) && (red <= redRange[1])) &&
            ((green >= greenRange[0]) && (green <= greenRange[1])) &&
            ((blue >= blueRange[0]) && (blue <= blueRange[1]))) {
            // make the pixel transparent
            //
            rawData[byteIndex] = 0;
            rawData[byteIndex + 1] = 0;
            rawData[byteIndex + 2] = 0;
            rawData[byteIndex + 3] = 0;
        }

        byteIndex += 4;
    }

    CGImageRef imgref = CGBitmapContextCreateImage(context);
    UIImage *result = [UIImage imageWithCGImage:imgref];

    CGImageRelease(imgref);
    CGContextRelease(context);
    free(rawData);

    return result;
}

Questa è una modifica del codice di yubenyi che funzionerà con più passaggi. Elimina il canale alfa prima dell'elaborazione convertendo l'immagine in un jpeg non compresso. Aggiunti anche alcuni commenti su come funziona la selezione della gamma di colori.

-(UIImage *)changeWhiteColorTransparent: (UIImage *)image
{
    //convert to uncompressed jpg to remove any alpha channels
    //this is a necessary first step when processing images that already have transparency
    image = [UIImage imageWithData:UIImageJPEGRepresentation(image, 1.0)];
    CGImageRef rawImageRef=image.CGImage;
    //RGB color range to mask (make transparent)  R-Low, R-High, G-Low, G-High, B-Low, B-High
    const double colorMasking[6] = {222, 255, 222, 255, 222, 255};

    UIGraphicsBeginImageContext(image.size);
    CGImageRef maskedImageRef=CGImageCreateWithMaskingColors(rawImageRef, colorMasking);

    //iPhone translation
    CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0.0, image.size.height);
    CGContextScaleCTM(UIGraphicsGetCurrentContext(), 1.0, -1.0); 

    CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, image.size.width, image.size.height), maskedImageRef);
    UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
    CGImageRelease(maskedImageRef);
    UIGraphicsEndImageContext();    
    return result;
}

questa funzione può funzionare!

-(UIImage *)changeWhiteColorTransparent: (UIImage *)image
{
    CGImageRef rawImageRef=image.CGImage;

    const float colorMasking[6] = {222, 255, 222, 255, 222, 255};

    UIGraphicsBeginImageContext(image.size);
    CGImageRef maskedImageRef=CGImageCreateWithMaskingColors(rawImageRef, colorMasking);
    {
        //if in iphone
        CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0.0, image.size.height);
        CGContextScaleCTM(UIGraphicsGetCurrentContext(), 1.0, -1.0); 
    }

    CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, image.size.width, image.size.height), maskedImageRef);
    UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
    CGImageRelease(maskedImageRef);
    UIGraphicsEndImageContext();    
    return result;
}

Dopo aver utilizzato le tue funzioni, ho trovato un modo più semplice di creare uno sfondo trasparente per UIImage :)

Ad esempio, hai un'immagine PNG con sfondo nero e vuoi rendere questo sfondo trasparente sullo schermo.

Puoi provare questo:

UIImage * image = [UIImage imageNamed:@"image.png"];

const CGFloat colorMasking[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
image = [UIImage imageWithCGImage: CGImageCreateWithMaskingColors(image.CGImage, colorMasking)];

Hai un'immagine con sfondo trasparente.

Questo è tutto :)

Swift 4: (per il mascheramento bianco)

extension UIImage { 
func imageByMakingWhiteBackgroundTransparent() -> UIImage? {

    let image = UIImage(data: UIImageJPEGRepresentation(self, 1.0)!)!
    let rawImageRef: CGImage = image.cgImage!

    let colorMasking: [CGFloat] = [222, 255, 222, 255, 222, 255]
    UIGraphicsBeginImageContext(image.size);

    let maskedImageRef = rawImageRef.copy(maskingColorComponents: colorMasking)
    UIGraphicsGetCurrentContext()?.translateBy(x: 0.0,y: image.size.height)
    UIGraphicsGetCurrentContext()?.scaleBy(x: 1.0, y: -1.0)
    UIGraphicsGetCurrentContext()?.draw(maskedImageRef!, in: CGRect.init(x: 0, y: 0, width: image.size.width, height: image.size.height))
    let result = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return result

    }
}

Basato sulla superba risposta di @ PKCLsoft , ho apportato una modifica in cui rende trasparente solo il colore bianco, ma rende l'alfa basato su come "bianco" il pixel è. Questo potrebbe tornare utile quando provi a creare una maschera da un'immagine in bianco e nero, in modo da dare bordi lisci su testi o forme.

- (UIImage *)makeWhiteColorTransparentInImage:(UIImage *)image {
   CGImageRef imageRef = [image CGImage];

   NSUInteger width = CGImageGetWidth(imageRef);
   NSUInteger height = CGImageGetHeight(imageRef);
   CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

   NSUInteger bytesPerPixel = 4;
   NSUInteger bytesPerRow = bytesPerPixel * width;
   NSUInteger bitsPerComponent = 8;
   NSUInteger bitmapByteCount = bytesPerRow * height;

   unsigned char *rawData = (unsigned char*) calloc(bitmapByteCount, sizeof(unsigned char));

   CGContextRef context = CGBitmapContextCreate(rawData, width, height,
                                             bitsPerComponent, bytesPerRow, colorSpace,
                                             kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
   CGColorSpaceRelease(colorSpace);

   CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);

   int byteIndex = 0;

   while (byteIndex < bitmapByteCount) {
      unsigned char red   = rawData[byteIndex];
      unsigned char green = rawData[byteIndex + 1];
      unsigned char blue  = rawData[byteIndex + 2];
      unsigned char alpha  = rawData[byteIndex + 3];

      if (alpha == 0.0) {
          rawData[byteIndex + 3] = 0.0;
      } else {
          rawData[byteIndex + 3] = 255.0 - (red + green + blue) / 3.0;
      }

      byteIndex += 4;
    }

    CGImageRef imgref = CGBitmapContextCreateImage(context);
    UIImage *result = [UIImage imageWithCGImage:imgref];

    CGImageRelease(imgref);
    CGContextRelease(context);
    free(rawData);

    return result;
}

Spero che possa aiutare qualcuno un giorno :)

Non ho mai fatto nulla di simile da solo, ma sembra che CGImageCreateWithMaskingColors possa essere utile qui. L'uso di Core Graphics va oltre lo scopo di ciò che posso spiegare in una risposta su questo sito; dai un'occhiata alla tua documentazione e cerca tutorial sul Web.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top