2011-01-13 3 views
3

백그라운드 스레드에서 이미지의 색상을 변경하려고합니다.
애플 문서는 UIGraphicsBeginImageContext 만 주 스레드에서 호출 할 수 있다고, 나는 CGBitmapContextCreate 사용하려고 해요 : (bitmapData와, pixelsWide, pixelsHigh, 8, // 비트 구성 요소 당UIGraphicsBeginImageContext vs CGBitmapContextCreate

컨텍스트 = CGBitmapContextCreate을

       bitmapBytesPerRow, 
          colorSpace, 
          kCGImageAlphaPremultipliedFirst); 

나는 CGBitmapContext를 사용하여 두 번째를 UIGraphisBeginImageContext를 사용하여 첫 번째 "changeColor"의 두 가지 버전이 몹시 떠들어 대다.

첫 번째 것은 올바르게 색상을 변경하지만 두 번째 색상은 올바르게 변경되지 않습니다.
왜 그런가요?



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

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

실제로 iOS 4.0에서 UIKit 그리기 기능은 이제 스레드 안전 : http://www.cocoabuilder.com/archive/cocoa/296299-drawing-thread-safety-in-ios.html. 이것은'UIGraphicsBeginImageContext()'를 포함하는 것으로 보입니다. 4.0 이상의 코어 그래픽 구현은 필요하지 않을 수도 있습니다. –

+0

은 신뢰할만한 견적입니까? http://stackoverflow.com/questions/4451855/uigraphicsbeginimagecontext-vs-cgbitmapcontextcreate-on-ios 거기에 대한 논쟁이 있습니다. 내 테스트 코드는 별도의 스레드에서 UIGraphicsBeginImageContext()로 사망했습니다. 비록 그것이 진짜 이유인지 100 % 확신 할 수는 없지만. – eugene

+0

이것은 또 다른 질문입니다. 여러분이 iOS 4.0을 말할 때, iOS <4.0을 대상으로하지만 iOS> = 4.0으로 컴파일 할 때 적용됩니까? 또는 코드가 iOS> = 4.0 인 컴퓨터에서만 실행되어야합니까? – eugene

답변

3

두 번째 방법은 첫 번째는 결코하지 않았다 작업을하고있다. I 만든

- (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()를 사용하여이 변환으며, I는 비트 맵의 ​​지원 데이터의 위험한 잘못된 free() 제거 : 여기에 더 가깝게 일치하는 두 번째 방법의 조정이 첫 번째의 문맥. 이 코드 스 니펫이 첫 번째 스 니펫과 동일하게 동작하지 않으면 CreateARGBBitmapContext()의 구현이 올바른지 확인해야합니다.

물론 Brad Larson이 의견에서 언급했듯이 iOS 4.0 이상을 타겟팅하는 경우 UIKit 그래픽 메서드는 스레드로부터 안전하며 스레드로부터 안전하므로 첫 번째 메서드를 사용할 수 있어야합니다. 잘 됐네.

+0

아 고마워! .. 'Free'가 없을 때 메모리 풋 프린트가 올라간다. 아마도 CreateARGBBitmapContext에서 뭔가 잘못하고있는 것일까? 질문을 함수로 업데이트했습니다. 제발 좀 봐 주시겠습니까? – eugene

+2

아, 무슨 일이 일어나는지 봅니다. 당신은 자신의 데이터를 mallocing하고, 그것에 대한 참조를 삭제합니다. 따라서 데이터는 나중에 실제로 공개해야합니다. 컨텍스트에서 데이터를 꺼내서'free() '를 호출하는 것은 다소 불쾌감을 느낍니다.'malloc()'대신에'NSMutableData'를 만들면'-mutableBytes'를 얻을 수 있습니다. 그렇게하면 동일한 버퍼가 생기지 만 자동 완성됩니다. 물론, autorelease 풀이 튀어 나오기 전에 컨텍스트가 해제되어야합니다 (그러나 어쨌든 여기에서 수행 중입니다). –

+0

한 가지 더, CGImageRelease로 imageRef를 릴리스해야합니까? – eugene