【问题标题】:UIGraphicsBeginImageContext vs CGBitmapContextCreateUIGraphicsBeginImageContext vs CGBitmapContextCreate
【发布时间】:2011-01-13 18:01:06
【问题描述】:

我正在尝试在后台线程中更改图像的颜色。
苹果文档说 UIGraphicsBeginImageContext 只能从主线程调用,我正在尝试使用 CGBitmapContextCreate:

上下文 = CGBitmapContextCreate (位图数据, 像素宽, 像素高, 8, // 每个组件的位数

                             bitmapBytesPerRow,
                             colorSpace,
                             kCGImageAlphaPremultipliedFirst);

我有两个版本的“changeColor”,第一个使用 UIGraphisBeginImageContext,第二个使用 CGBitmapContextCreate。

第一个正确改变颜色,但第二个没有。
这是为什么?

- (UIImage*) changeColor: (UIColor*) aColor
{
    if(aColor == nil)
        return self;

    UIGraphicsBeginImageContext(self.size);

    CGRect bounds;
    bounds.origin = CGPointMake(0,0);
    bounds.size = self.size;
    [aColor set];

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(context, 0, self.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    CGContextClipToMask(context, bounds, [self CGImage]);
    CGContextFillRect(context, bounds);

    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return img;
}

- (UIImage*) changeColor: (UIColor*) aColor
{
    if(aColor == nil)
        return self;

    CGContextRef context = CreateARGBBitmapContext(self.size);

    CGRect bounds;
    bounds.origin = CGPointMake(0,0);
    bounds.size = self.size;

    CGColorRef colorRef = aColor.CGColor;
    const CGFloat *components = CGColorGetComponents(colorRef);
    float red = components[0];
    float green = components[1];
    float blue = components[2];

    CGContextSetRGBFillColor(context, red, green, blue, 1);


    CGContextClipToMask(context, bounds, [self CGImage]);
    CGContextFillRect(context, bounds);

    CGImageRef imageRef = CGBitmapContextCreateImage(context);
    UIImage* img = [UIImage imageWithCGImage: imageRef];
    unsigned char* data = (unsigned char*)CGBitmapContextGetData (context);  
    CGContextRelease(context);
    free(data);

    return img;
}

CGContextRef CreateARGBBitmapContext(CGSize size)
{
    CGContextRef    context = NULL;
    CGColorSpaceRef colorSpace;
    void *          bitmapData;
    int             bitmapByteCount;
    int             bitmapBytesPerRow;

    // Get image width, height. We'll use the entire image.                                                                                                                                                                                 
    size_t pixelsWide = size.width;
    size_t pixelsHigh = size.height;

    // Declare the number of bytes per row. Each pixel in the bitmap in this                                                                                                                                                                
    // example is represented by 4 bytes; 8 bits each of red, green, blue, and                                                                                                                                                              
    // alpha.                                                                                                                                                                                                                               
    bitmapBytesPerRow   = (pixelsWide * 4);
    bitmapByteCount     = (bitmapBytesPerRow * pixelsHigh);

    // Use the generic RGB color space.                                                                                                                                                                                                     
    colorSpace = CGColorSpaceCreateDeviceRGB();

    if (colorSpace == NULL)
    {
        fprintf(stderr, "Error allocating color space\n");
        return NULL;
    }

    // Allocate memory for image data. This is the destination in memory                                                                                                                                                                    
    // where any drawing to the bitmap context will be rendered.                                                                                                                                                                            
    bitmapData = malloc( bitmapByteCount );
    if (bitmapData == NULL)
    {
        fprintf (stderr, "Memory not allocated!");
        CGColorSpaceRelease( colorSpace );
        return NULL;
    }
    // Create the bitmap context. We want pre-multiplied ARGB, 8-bits                                                                                                                                                                       
    // per component. Regardless of what the source image format is                                                                                                                                                                         
    // (CMYK, Grayscale, and so on) it will be converted over to the format                                                                                                                                                                 
    // specified here by CGBitmapContextCreate.                                                                                                                                                                                             
    context = CGBitmapContextCreate (bitmapData,
                                     pixelsWide,
                                     pixelsHigh,
                                     8,      // bits per component                                                                                                                                                                          
                                     bitmapBytesPerRow,
                                     colorSpace,
                                     kCGImageAlphaPremultipliedFirst);
    if (context == NULL)
    {
        free (bitmapData);
        fprintf (stderr, "Context not created!");
    }

    // Make sure and release colorspace before returning                                                                                                                                                                                    
    CGColorSpaceRelease( colorSpace );

    return context;

}

【问题讨论】:

  • 实际上,在 iOS 4.0 中,UIKit 绘图函数现在是线程安全的:cocoabuilder.com/archive/cocoa/…。这似乎包括UIGraphicsBeginImageContext()。您可能不需要 4.0+ 中的纯 Core Graphics 实现。
  • 报价是否权威? stackoverflow.com/questions/4451855/… 我猜有争论吗?我的测试代码在单独的线程上因 UIGraphicsBeginImageContext() 而死。虽然我不能 100% 确定这是真正的原因。
  • 这是另一个问题,当您说 iOS 4.0 时,当我以 iOS = 4.0 编译时是否适用?或者代码应该只在 iOS >=4.0 的机器上运行?
  • David Duncan 是 Apple 的一名工程师,所以我认为他的声明以及发行说明是权威的。这仅在 iOS 4.0+ 设备上运行时是线程安全的,因为如果针对旧版本,针对 4.x SDK 构建仍然使用本地操作系统实现。如果您需要旧版操作系统支持,您仍然需要采用纯 Core Graphics 路线。

标签: ios colors cgbitmapcontextcreate


【解决方案1】:

您的第二种方法正在做第一种方法从未做过的工作。这是对第二种方法的调整,以更接近第一种方法:

- (UIImage*) changeColor: (UIColor*) aColor
{
    if(aColor == nil)
        return self;

    CGContextRef context = CreateARGBBitmapContext(self.size);

    CGRect bounds;
    bounds.origin = CGPointMake(0,0);
    bounds.size = self.size;

    CGContextSetFillColorWithColor(aColor.CGColor);

    CGContextClipToMask(context, bounds, [self CGImage]);
    CGContextFillRect(context, bounds);

    CGImageRef imageRef = CGBitmapContextCreateImage(context);
    UIImage* img = [UIImage imageWithCGImage: imageRef];
    CGContextRelease(context);

    return img;
}

我所做的两个更改是我将其转换为使用CGContextSetFillColorWithColor(),并删除了位图上下文的支持数据的危险和不正确的free()。如果这段代码 sn-p 的行为与第一个不同,那么您将不得不查看您的 CreateARGBBitmapContext() 实现以验证它是否正确。

当然,正如 Brad Larson 在 cmets 中提到的,如果您的目标是 iOS 4.0 及更高版本,UIKit 图形方法(根据发行说明)是线程安全的,您应该能够使用第一种方法很好。

【讨论】:

  • 啊谢谢!.. 当我没有“空闲”时,我看到内存占用增加了,也许我在 CreateARGBBitmapContext 中做错了什么?我已经用函数更新了问题,你能看一下吗?
  • 啊,我明白了。您正在分配自己的数据,然后删除对它的引用。所以这些数据确实需要稍后发布。从上下文中提取数据并为此调用free() 感觉相当讨厌。如果你想要一个替代方案,而不是malloc(),你可以创建一个合适大小的NSMutableData 并获取-mutableBytes。这将为您提供相同的缓冲区,但自动释放。当然,如果您这样做,则必须在自动释放池弹出之前释放上下文(但无论如何您都在这里这样做)。
  • 还有一件事,我应该用 CGImageRelease 释放 imageRef 吗?
  • 讨厌苹果的文档,没有说清楚我是否需​​要发布它:(
  • 哦,是的,您显然应该这样做。 CGBitmapContextCreateImage() 为您提供自有参考。 +[UIImage imageWithCGImage:] 将在 UIImage 对象的生命周期内保留 CGImageRef。但您仍然拥有自己的参考资料,需要使用 CGImageRelease() 发布。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多