2011-01-13 2 views
3

Ich versuche, die Farbe eines Bildes in einem Hintergrund-Thread zu ändern.
Apple-doc sagt UIGraphicsBeginImageContext kann nur von Haupt-Thread aufgerufen werden, und ich versuche, CGBitmapContextCreate zu verwenden:UIGraphicsBeginImageContext vs CGBitmapContextCreate

context = CGBitmapContextCreate (bitmapdata, pixelsWide, pixelsHigh, 8 // Bits pro Komponente

       bitmapBytesPerRow, 
          colorSpace, 
          kCGImageAlphaPremultipliedFirst); 

ich habe zwei Versionen von "changeColor" ersten mit UIGraphisBeginImageContext, zweitem CGBitmapContext mit Erstellen.

Die erste ändert korrekt die Farbe, die zweite jedoch nicht.
Warum ist das?

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

Tatsächlich sind UIKit Zeichenfunktionen jetzt in iOS 4.0 threadsafe: http://www.cocoabuilder.com/archive/cocoa/296299-drawing-thread-safety-in-ios.html. Dies scheint 'UIGraphicsBeginImageContext()' zu enthalten. Möglicherweise benötigen Sie die reine Core Graphics-Implementierung in 4.0+ nicht. –

+0

ist das Zitat maßgebend? http://StackOverflow.com/Questions/4451855/uigraphicsbegeimagecontext-vs-cgbitmapcontextcreate-on-ios Es gibt Debatten darüber, denke ich ?, und mein Testcode starb mit UIGraphicsBeginImageContext() auf separaten Thread. obwohl ich nicht hundertprozentig sicher sein kann, dass das der wahre Grund ist. – eugene

+0

das ist eine andere Frage, wenn Sie sagen, iOS 4.0, gilt es, wenn ich iOS <4.0 Ziel, aber kompilieren mit iOS> = 4.0? oder der Code sollte nur auf einem Rechner mit iOS> = 4.0 laufen? – eugene

Antwort

3

Ihre zweite Methode tut Arbeit, die die erste nie. Hier ist eine Einstellung des zweiten Verfahrens, um genauer zu entsprechen die erste:

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

Die beiden Änderungen I vorgenommen wurden ich dies CGContextSetFillColorWithColor() zu verwenden umgewandelt und I entfernt, um die gefährliche und falsche free() der Trägerdaten der Bitmap Kontext. Wenn sich dieses Code-Snippet nicht identisch mit dem ersten verhält, müssen Sie Ihre Implementierung von CreateARGBBitmapContext() überprüfen, um zu verifizieren, dass es korrekt ist.

Natürlich, wie in den Kommentaren von Brad Larson erwähnt, sind die UIKit-Grafikmethoden (gemäß den Versionshinweisen) threadsicher, wenn Sie auf iOS 4.0 und höher abzielen, und Sie sollten die erste Methode verwenden können Alles gut.

+0

ah Danke! .. ich sehe Speicherabbildung, wenn ich nicht "frei" habe, vielleicht mache ich etwas falsch in CreateARGBBitmapContext? Ich habe die Frage mit der Funktion aktualisiert, können Sie es sich bitte anschauen? – eugene

+2

Ah, ich sehe was vor sich geht. Sie mallokieren Ihre eigenen Daten und legen dann Ihren Hinweis darauf hin. Diese Daten müssen also später veröffentlicht werden. Es fühlt sich einfach unangenehm an, die Daten aus dem Kontext herauszuziehen und 'free()' darauf zu nennen.Wenn Sie eine Alternative wünschen, können Sie statt 'malloc()' ein 'NSMutableData' der richtigen Größe erstellen und' -mutableBytes' verwenden. Das gibt Ihnen den gleichen Puffer, aber Autoreleased. Natürlich, wenn Sie das tun, muss der Kontext freigegeben werden, bevor der Autorelease-Pool auftaucht (aber Sie tun das hier trotzdem). –

+0

noch eine Sache, sollte ich die imageRef mit CGImageRelease veröffentlichen? – eugene

Verwandte Themen