使用石英 2D 从透明 png 交换 UIImage 上的颜色通道

Swap color channels on UIImage from transparent png with quartz 2D

我有一个奇怪的问题。我想交换从 png 加载的 UIImage 的红色和绿色通道。我查找了示例并使用了以下代码,它适用于没有透明度的 png 图像:

- (UIImage *)redToGreen {

CGImageRef imgRef = [self CGImage];

size_t width = CGImageGetWidth(imgRef);
size_t height = CGImageGetHeight(imgRef);

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
size_t bitsPerComponent = 8;
size_t bytesPerPixel = 4;
size_t bytesPerRow = bytesPerPixel * width;
size_t totalBytes = bytesPerRow * height;


//Allocate Image space
uint8_t* rawData = malloc(totalBytes);

//Create Bitmap of same size
CGContextRef context = CGBitmapContextCreate(rawData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

//Draw our image to the context
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imgRef);

for ( int i = 0; i < totalBytes; i += 4 ) {

    uint8_t* red = rawData + i;
    uint8_t* green = rawData + (i + 1);
    uint8_t red_old = *red;

    *red = *green;
    *green = red_old;
}

//Create Image
CGImageRef newImg = CGBitmapContextCreateImage(context);

//Release Created Data Structs
CGColorSpaceRelease(colorSpace);
CGContextRelease(context);
free(rawData);

//Create UIImage struct around image
UIImage* newImage = [UIImage imageWithCGImage:newImg];

//Release our hold on the image
CGImageRelease(newImg);

//return new image!
return newImage;
}

但是,当我在透明 png 上尝试时,我在背景中看到了奇怪的人工制品。

例如这张透明图片:

在黑色背景上加载显示为:

我在代码中所做的是:

[btn setImage:[[UIImage imageNamed:@"trash.png"] redToGreen] forState:UIControlStateNormal];

我尝试更改 kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big 因为有不同的例子,但似乎没有任何效果。有什么想法吗?

更新: 我觉得很奇怪这段代码被复制粘贴而没有抱怨。除了已接受的答案所指示的未清除缓冲区的问题外,另一个缺陷是比例始终设置为 1,因此如果您打开 @2x 图像,结果将具有两倍的磅值。制作 UIImage 的正确调用是:

UIImage* newImage = [UIImage imageWithCGImage:newImg scale:self.scale orientation:self.imageOrientation];

创建位图上下文时,它不会清除缓冲区。 (例如,使用包含预先存在的图像的缓冲区创建位图上下文是非常明智的。)

有两种方法:

1) 在绘制原始图像之前明确清除它,使用CGContextClearRect()

2) 在绘制原始图像之前将上下文的混合模式设置为 "copy",以便它 "paints over" 缓冲区中可能存在的任何垃圾;你会用 CGContextSetBlendMode(context, kCGBlendModeCopy) 来做到这一点