skip to Main Content

I am developing an iPad app which presents pictures from a photographer. Those photos are uploaded on a webserver, and served directly through the app, where they are downloaded and displayed using the method below :

if([[NSFileManager defaultManager] fileExistsAtPath:[url path]]){
    CGImageSourceRef source = CGImageSourceCreateWithURL((CFURLRef)url, NULL);
    CGImageRef cgImage = nil;
    if(source){
        cgImage = CGImageSourceCreateImageAtIndex(source, 0, (CFDictionaryRef)dict);
    }

    UIImage *retImage = [UIImage imageWithCGImage:cgImage];

    if(cgImage){
        CGImageRelease(cgImage);
    }
    if(source){
        CFRelease(source);
    }

    return retImage;
}

I can observe a serious change in the display of the photos’ colours between the original picture (which is the same when displayed from the disk or from the web on my Mac and the photographer’s Mac) and on the iPad (the result is wrong in the app and even in Safari).

After some search I found some posts explaining how iDevices do not use embedded color profile, so I found I was going this way. The photos are saved using the following info :

colorspace: RGB, colorprofile: sRGB iec...

I found out on some articles (for example this link from imageoptim or analogsenses) that I should save the picture for device export by converting it to sRGB, without embedding the color profile, but I cant find out how could I do that ? Each time I tried (I don’t have photoshop, so I used command line ImageMagick), the resulting picture has the following information, and is still not displayed correctly on my iPad (and any other iPads I’ve tested) :

colorspace: RGB

Here is a example of a picture that do not display correctly on the iPhone or iPad, but does on the web

I would like to transform it to display it correctly, any Idea would be really welcomed 🙂

[EDIT] I have succeeded to obtain a correct image using “save for web” options of photoshop using the following parameters :
ok photoshop export parameters
But I’m still unable to apply those settings automatically to all my pictures.

3

Answers


  1. To read an image, just use:

    UIImage *image = [UIImage imageWithContentsOfFile:path];
    

    As for the color profile issue, try the sips command-line tool to fix the image files. Something like:

    mkdir converted
    sips -m "/System/Library/ColorSync/Profiles/sRGB Profile.icc" *.JPG --out converted
    
    Login or Signup to reply.
  2. You can get first the color space through CGImage.

    @property(nonatomic, readonly) CGImageRef CGImage
    
    CGColorSpaceRef CGImageGetColorSpace (
       CGImageRef image
    );
    

    And depping of the colorSpace apply a format conversion. So to get the color space of an image, you’d do:

    CGColorSpaceRef colorspace = CGImageGetColorSpace([myUIImage CGImage]);
    

    Note: make sure to follow the get/create/copy rules for CG objects.

    Color conversion to RGB8 (also can be applied to RGB16 or RGB32, changing the bits per component in the method newBitmapRGBA8ContextFromImage):

    // Create a bitmap
    unsigned char *bitmap = [ImageHelper convertUIImageToBitmapRGBA8:image];
    
        // Create a UIImage using the bitmap
    UIImage *imageCopy = [ImageHelper convertBitmapRGBA8ToUIImage:bitmap withWidth:width withHeight:height];
    
        // Display the image copy on the GUI
    UIImageView *imageView = [[UIImageView alloc] initWithImage:imageCopy];
    

    ImageHelper.h

    #import <Foundation/Foundation.h>
    
    
    @interface ImageHelper : NSObject {
    
    }
    
    /** Converts a UIImage to RGBA8 bitmap.
     @param image - a UIImage to be converted
     @return a RGBA8 bitmap, or NULL if any memory allocation issues. Cleanup memory with free() when done.
     */
    + (unsigned char *) convertUIImageToBitmapRGBA8:(UIImage *)image;
    
    /** A helper routine used to convert a RGBA8 to UIImage
     @return a new context that is owned by the caller
     */
    + (CGContextRef) newBitmapRGBA8ContextFromImage:(CGImageRef)image;
    
    
    /** Converts a RGBA8 bitmap to a UIImage. 
     @param buffer - the RGBA8 unsigned char * bitmap
     @param width - the number of pixels wide
     @param height - the number of pixels tall
     @return a UIImage that is autoreleased or nil if memory allocation issues
     */
    + (UIImage *) convertBitmapRGBA8ToUIImage:(unsigned char *)buffer 
        withWidth:(int)width
        withHeight:(int)height;
    
    @end
    

    ImageHelper.m

    #import "ImageHelper.h"
    
    
    @implementation ImageHelper
    
    
    + (unsigned char *) convertUIImageToBitmapRGBA8:(UIImage *) image {
    
        CGImageRef imageRef = image.CGImage;
    
        // Create a bitmap context to draw the uiimage into
        CGContextRef context = [self newBitmapRGBA8ContextFromImage:imageRef];
    
        if(!context) {
            return NULL;
        }
    
        size_t width = CGImageGetWidth(imageRef);
        size_t height = CGImageGetHeight(imageRef);
    
        CGRect rect = CGRectMake(0, 0, width, height);
    
        // Draw image into the context to get the raw image data
        CGContextDrawImage(context, rect, imageRef);
    
        // Get a pointer to the data    
        unsigned char *bitmapData = (unsigned char *)CGBitmapContextGetData(context);
    
        // Copy the data and release the memory (return memory allocated with new)
        size_t bytesPerRow = CGBitmapContextGetBytesPerRow(context);
        size_t bufferLength = bytesPerRow * height;
    
        unsigned char *newBitmap = NULL;
    
        if(bitmapData) {
            newBitmap = (unsigned char *)malloc(sizeof(unsigned char) * bytesPerRow * height);
    
            if(newBitmap) { // Copy the data
                for(int i = 0; i < bufferLength; ++i) {
                    newBitmap[i] = bitmapData[i];
                }
            }
    
            free(bitmapData);
    
        } else {
            NSLog(@"Error getting bitmap pixel datan");
        }
    
        CGContextRelease(context);
    
        return newBitmap;   
    }
    
    + (CGContextRef) newBitmapRGBA8ContextFromImage:(CGImageRef) image {
        CGContextRef context = NULL;
        CGColorSpaceRef colorSpace;
        uint32_t *bitmapData;
    
        size_t bitsPerPixel = 32;
        size_t bitsPerComponent = 8;
        size_t bytesPerPixel = bitsPerPixel / bitsPerComponent;
    
        size_t width = CGImageGetWidth(image);
        size_t height = CGImageGetHeight(image);
    
        size_t bytesPerRow = width * bytesPerPixel;
        size_t bufferLength = bytesPerRow * height;
    
        colorSpace = CGColorSpaceCreateDeviceRGB();
    
        if(!colorSpace) {
            NSLog(@"Error allocating color space RGBn");
            return NULL;
        }
    
        // Allocate memory for image data
        bitmapData = (uint32_t *)malloc(bufferLength);
    
        if(!bitmapData) {
            NSLog(@"Error allocating memory for bitmapn");
            CGColorSpaceRelease(colorSpace);
            return NULL;
        }
    
        //Create bitmap context
    
        context = CGBitmapContextCreate(bitmapData, 
                width, 
                height, 
                bitsPerComponent, 
                bytesPerRow, 
                colorSpace, 
                kCGImageAlphaPremultipliedLast);    // RGBA
        if(!context) {
            free(bitmapData);
            NSLog(@"Bitmap context not created");
        }
    
        CGColorSpaceRelease(colorSpace);
    
        return context; 
    }
    
    + (UIImage *) convertBitmapRGBA8ToUIImage:(unsigned char *) buffer 
            withWidth:(int) width
           withHeight:(int) height {
    
    
        size_t bufferLength = width * height * 4;
        CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer, bufferLength, NULL);
        size_t bitsPerComponent = 8;
        size_t bitsPerPixel = 32;
        size_t bytesPerRow = 4 * width;
    
        CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
        if(colorSpaceRef == NULL) {
            NSLog(@"Error allocating color space");
            CGDataProviderRelease(provider);
            return nil;
        }
    
        CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast;
        CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
    
        CGImageRef iref = CGImageCreate(width, 
                    height, 
                    bitsPerComponent, 
                    bitsPerPixel, 
                    bytesPerRow, 
                    colorSpaceRef, 
                    bitmapInfo, 
                    provider,   // data provider
                    NULL,       // decode
                    YES,            // should interpolate
                    renderingIntent);
    
        uint32_t* pixels = (uint32_t*)malloc(bufferLength);
    
        if(pixels == NULL) {
            NSLog(@"Error: Memory not allocated for bitmap");
            CGDataProviderRelease(provider);
            CGColorSpaceRelease(colorSpaceRef);
            CGImageRelease(iref);       
            return nil;
        }
    
        CGContextRef context = CGBitmapContextCreate(pixels, 
                     width, 
                     height, 
                     bitsPerComponent, 
                     bytesPerRow, 
                     colorSpaceRef, 
                     bitmapInfo); 
    
        if(context == NULL) {
            NSLog(@"Error context not created");
            free(pixels);
        }
    
        UIImage *image = nil;
        if(context) {
    
            CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), iref);
    
            CGImageRef imageRef = CGBitmapContextCreateImage(context);
    
            // Support both iPad 3.2 and iPhone 4 Retina displays with the correct scale
            if([UIImage respondsToSelector:@selector(imageWithCGImage:scale:orientation:)]) {
                float scale = [[UIScreen mainScreen] scale];
                image = [UIImage imageWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp];
            } else {
                image = [UIImage imageWithCGImage:imageRef];
            }
    
            CGImageRelease(imageRef);   
            CGContextRelease(context);  
        }
    
        CGColorSpaceRelease(colorSpaceRef);
        CGImageRelease(iref);
        CGDataProviderRelease(provider);
    
        if(pixels) {
            free(pixels);
        }   
        return image;
    }
    
    @end
    
    Login or Signup to reply.
  3. @PhilippeAuriach
    I think you might have problem with [UIImage imageWithCGImage:cgImage], My suggestion is use [UIImage imageWithContentsOfFile:path] instead of above.

    Below code might be help you.

    if([[NSFileManager defaultManager] fileExistsAtPath:[url path]]){
        //Provide image path here...
        UIImage *image = [UIImage imageWithContentsOfFile:path];
    
        if(image){
            return image;
        }else{
           //Return default image
           return image;
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search