¿Cómo hacer que un color sea transparente en un UIImage?

En mi aplicación para iPhone tengo una instancia de UIImage. Quiero obtener un UIImage derivado que es el resultado del primer UIImage donde uno de sus colores (por ejemplo, magenta) se vuelve transparente. ¿Cómo puedo hacer esto?

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

El código anterior se prueba y cambié el color verde a rojo con máscara

DE ACUERDO. Después de haber intentado no sé cuántas versiones de estas soluciones, tengo mi propia versión personalizada. Lo que he encontrado es que la solución de @yubenyi funciona bastante bien, pero si desea tomar la salida de su función changeWhiteColorTransparent () y volver a ingresarla, no funciona.

Mi primer paso fue cambiar su función para que aceptara un color y tolerancia específicos, de modo que la persona que llama pudiera especificar un rango de colores para hacer transparente. Esto funcionó bien, casi sin cambios, pero encontré que la salida no era una imagen válida para pasar por el mismo código con un segundo rango de color.

Después de muchas pruebas y errores, hice que funcionara reemplazando el color yo mismo. Me resistí a esto porque parecía demasiado trabajo cuando hay API para hacer esto, pero no siempre se comportan de la manera que quieres. Específicamente, el resultado de CGImageCreateWithMaskingColors () no se puede utilizar como entrada en otra llamada a la misma función. No he podido averiguar por qué, pero creo que tiene algo que ver con el canal alfa.

En cualquier caso, mi solución es:

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

Este es un ajuste del código de yubenyi que funcionará con múltiples pases. Quita el canal alfa antes de procesar convirtiendo la imagen en un jpeg sin comprimir. También se agregaron algunos comentarios sobre cómo funciona la selección del rango de color.

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

esta función puede funcionar!

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

Después del uso de sus funciones, encontré una manera más simple de crear un fondo transparente para UIImage 🙂

Por ejemplo, tiene una imagen PNG con fondo negro y desea que este fondo sea transparente en la pantalla.

Puedes intentar esto:

 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)]; 

Tienes imagen con fondo transparente.

Eso es todo 🙂

Swift 4: (para enmascarar blanco)

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

Nunca he hecho algo así, pero parece que CGImageCreateWithMaskingColors puede ser útil aquí. Sin embargo, usar Core Graphics está más allá del scope de lo que puedo explicar en una respuesta en este sitio; Eche un vistazo a su documentación y busque tutoriales en la Web.

Basándome en la excelente respuesta de @ PKCLsoft , realicé una edición donde solo hace que el color blanco sea transparente, pero hace alfa en función de qué tan “blanco” sea el píxel. Esto podría ser útil, cuando intentes hacer una máscara a partir de una imagen en blanco y negro, para que tenga bordes suaves en textos o formas.

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

Espero que ayude a alguien algún día 🙂