简体   繁体   English

如何在 UIImage 上使一种颜色透明?

[英]How to make one color transparent on a UIImage?

On my iPhone app I have a UIImage instance.在我的 iPhone 应用程序上,我有一个 UIImage 实例。 I want to get a derived a UIImage that is the result of the first UIImage where one of its colors (eg magenta) is made transparent.我想得到一个派生的 UIImage,它是第一个 UIImage 的结果,其中一个 colors(例如洋红色)是透明的。 How can I do this?我怎样才能做到这一点?

OK.好的。 Having tried I don't know how many versions of these solutions, I have my own customised version.尝试过不知道这些解决方案有多少个版本,我有自己的定制版本。 What I've found is that the solution from @yubenyi works quite well, but if you want to take the output from his changeWhiteColorTransparent() function and pass it back in, it doesn't work.我发现@yubenyi 的解决方案效果很好,但是如果您想从他的 changeWhiteColorTransparent() function 中获取 output 并将其传回,则它不起作用。

My first step was to change his function to accept a specific colour and tolerance so that the caller could specify a range of colours to make transparent.我的第一步是更改他的 function 以接受特定的颜色和容差,以便调用者可以指定一系列颜色以使其透明。 This worked fine, almost unchanged, but I found that the output was not a valid image for passing through the same code with a second colour range.这工作正常,几乎没有变化,但我发现 output 不是通过具有第二个颜色范围的相同代码的有效图像。

After a lot of trial and error, I got this working by doing the colour replacement myself.经过大量的试验和错误,我通过自己进行颜色替换来完成这项工作。 I resisted this because it seemed like too much hard work when there are API's to do this stuff, but they don't always behave the way you want.我拒绝了这一点,因为当有 API 来做这些事情时,这似乎太辛苦了,但它们并不总是按照你想要的方式运行。 Specifically, the output from CGImageCreateWithMaskingColors() can't be used as an input into another call to the same function.具体来说,来自 CGImageCreateWithMaskingColors() 的 output 不能用作对同一 function 的另一个调用的输入。 I haven't been able to work out why, but I think it's something to do with the alpha channel.我无法弄清楚原因,但我认为这与 alpha 通道有关。

In any case, my solution is:无论如何,我的解决方案是:

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

This is a tweak of yubenyi's code that will work with multiple passes.这是对 yubenyi 代码的调整,可用于多次传递。 It strips the alpha channel before processing by converting the image to an uncompressed jpeg.它通过将图像转换为未压缩的 jpeg 在处理之前剥离 alpha 通道。 Also added some comments on how the color range selection works.还添加了一些关于颜色范围选择如何工作的评论。

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

After use of your functions, I found more simpler way of making transparent background for UIImage:)使用您的功能后,我发现为 UIImage 制作透明背景的更简单方法:)

For example, you have PNG image with black background and you want make this background transparent on screen.例如,您有黑色背景的 PNG 图像,并且您希望此背景在屏幕上透明。

You can try this:你可以试试这个:

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

In Swift 5 :Swift 5

let colorMasking: [CGFloat] = [0, 0, 0, 0, 0, 0]
guard let cgImage = cgImage?.copy(maskingColorComponents: colorMasking) else { return }
image = UIImage(cgImage: cgImage)

You have got image with transparent background.你有透明背景的图像。

That's all:)就这样:)

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

The above code is tested and I changed the green color to red color by using mask上面的代码经过测试,我通过使用掩码将绿色更改为红色

this function can work!这个 function 可以工作!

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

Swift 4: (for white masking) Swift 4:(用于白色遮罩)

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

    }
}

Based on @ PKCLsoft 's superb answer , I made an edit where it only makes white color transparent, but it makes alpha based on how "white" the pixel is.基于@PKCLsoft的出色回答,我做了一个编辑,它只使白色透明,但它根据像素的“白色”程度来制作阿尔法。 This could come handy, when you try to make a mask from a black and white image, so it will give smooth edges on texts or shapes.当您尝试从黑白图像制作蒙版时,这可能会派上用场,因此它将在文本或形状上提供平滑的边缘。

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

I hope it helps someone some day:)我希望有一天它对某人有所帮助:)

swift version for White background color to transparent color swift 版本为白色背景色到透明色

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

        let image = UIImage(data: self.jpegData(compressionQuality: 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

    }

}

cleaned up version of answer above removing removing some unnecessary清理上面的答案版本删除删除一些不必要的

 extension UIImage {
  func remove(color: UIColor, tolerance: CGFloat = 4) -> UIImage {
    let ciColor = CIColor(color: color)

    let maskComponents: [CGFloat] = [ciColor.red, ciColor.green, ciColor.blue].flatMap { value in
      [(value * 255) - tolerance, (value * 255) + tolerance]
    }

    guard let masked = cgImage?.copy(maskingColorComponents: maskComponents) else { return self }
    return UIImage(cgImage: masked)
  }
}

I've never done anything like this myself, but it looks like CGImageCreateWithMaskingColors may be of use here.我自己从来没有做过这样的事情,但看起来CGImageCreateWithMaskingColors可能在这里有用。 Using Core Graphics is beyond the scope of what I can explain in an answer on this site, though;不过,使用 Core Graphics 超出了我可以在本网站的答案中解释的 scope 范围; take a look at your documentation and search for tutorials on the Web.查看您的文档并搜索有关 Web 的教程。

UIImage extension in swift 5 with possibility to change any color to transparent. swift 5 中的 UIImage 扩展,可以将任何颜色更改为透明。 Based on @fegoulart answer.基于@fegoulart 的回答。

extension UIImage {
func remove(color: UIColor) -> UIImage? {
    
    let cgColor = color.cgColor
    let components = cgColor.components
    var r = components?[0] ?? 0.0
    var g = components?[1] ?? 0.0
    var b = components?[2] ?? 0.0
    
    r = r * 255.0
    g = g * 255.0
    b = b * 255.0
    
    let colorMasking: [CGFloat] = [r, r, g, g, b, b]
    
    let image = UIImage(data: self.jpegData(compressionQuality: 1.0)!)!
    let rawImageRef: CGImage = image.cgImage!
    
    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
}
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM