UIGraphicsBeginImageContext против CGBitmapContextCreate

Я пытаюсь изменить цвет изображения в фоновом потоке.
В Apple doc говорится, что UIGraphicsBeginImageContext может быть вызван только из основного потока, и я пытаюсь использовать CGBitmapContextCreate:

context = CGBitmapContextCreate (bitmapData, PixWide, PixelsHigh, 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;

}

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() вспомогательных данных растрового контекста. Если этот фрагмент кода не ведет себя идентично первому, то вам придется взглянуть на свою реализацию CreateARGBBitmapContext() чтобы убедиться, что это правильно.

Конечно, как отметил Брэд Ларсон в комментариях, если вы ориентируетесь на iOS 4.0 и выше, графические методы UIKit (согласно примечаниям к выпуску) поточно-ориентированы, и вы должны уметь использовать первый метод просто отлично.

Другие вопросы по тегам