コード例 #1
0
void computeMD5HashStringForContext(CGContextRef bitmapContext, char hashString[33])
{
    ASSERT(CGBitmapContextGetBitsPerPixel(bitmapContext) == 32); // ImageDiff assumes 32 bit RGBA, we must as well.
    size_t pixelsHigh = CGBitmapContextGetHeight(bitmapContext);
    size_t pixelsWide = CGBitmapContextGetWidth(bitmapContext);
    size_t bytesPerRow = CGBitmapContextGetBytesPerRow(bitmapContext);
    
    // We need to swap the bytes to ensure consistent hashes independently of endianness
    MD5 md5;
    unsigned char* bitmapData = static_cast<unsigned char*>(CGBitmapContextGetData(bitmapContext));
#if PLATFORM(MAC)
    if ((CGBitmapContextGetBitmapInfo(bitmapContext) & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Big) {
        for (unsigned row = 0; row < pixelsHigh; row++) {
            Vector<uint8_t> buffer(4 * pixelsWide);
            for (unsigned column = 0; column < pixelsWide; column++)
                buffer[column] = OSReadLittleInt32(bitmapData, 4 * column);
            md5.addBytes(buffer);
            bitmapData += bytesPerRow;
        }
    } else
#endif
    {
        for (unsigned row = 0; row < pixelsHigh; row++) {
            md5.addBytes(bitmapData, 4 * pixelsWide);
            bitmapData += bytesPerRow;
        }
    }

    Vector<uint8_t, 16> hash;
    md5.checksum(hash);
    
    hashString[0] = '\0';
    for (int i = 0; i < 16; i++)
        snprintf(hashString, 33, "%s%02x", hashString, hash[i]);
}
コード例 #2
0
void
SourceSurfaceCGBitmapContext::DrawTargetWillChange()
{
  if (mDrawTarget) {
    // This will break the weak reference we hold to mCg
    size_t stride = CGBitmapContextGetBytesPerRow(mCg);
    size_t height = CGBitmapContextGetHeight(mCg);

    size_t bufLen = BufferSizeFromStrideAndHeight(stride, height);
    if (bufLen == 0) {
      mDataHolder.Dealloc();
      mData = nullptr;
    } else {
      static_assert(sizeof(decltype(mDataHolder[0])) == 1,
                    "mDataHolder.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen");
      mDataHolder.Realloc(/* actually an object count */ bufLen);
      mData = mDataHolder;

      // copy out the data from the CGBitmapContext
      // we'll maintain ownership of mData until
      // we transfer it to mImage
      memcpy(mData, CGBitmapContextGetData(mCg), bufLen);
    }

    // drop the current image for the data associated with the CGBitmapContext
    if (mImage)
      CGImageRelease(mImage);
    mImage = nullptr;

    mCg = nullptr;
    mDrawTarget = nullptr;
  }
}
コード例 #3
0
DataSourceSurfaceCG::DataSourceSurfaceCG(CGImageRef aImage)
{
  mFormat = SurfaceFormat::B8G8R8A8;
  mImage = aImage;
  mCg = CreateBitmapContextForImage(aImage);
  if (mCg == nullptr) {
    // error creating context
    return;
  }

  // Get image width, height. We'll use the entire image.
  CGFloat w = CGImageGetWidth(aImage);
  CGFloat h = CGImageGetHeight(aImage);
  CGRect rect = {{0,0},{w,h}};

  // Draw the image to the bitmap context. Once we draw, the memory
  // allocated for the context for rendering will then contain the
  // raw image data in the specified color space.
  CGContextDrawImage(mCg, rect, aImage);

  // Now we can get a pointer to the image data associated with the bitmap
  // context.
  mData = CGBitmapContextGetData(mCg);
  assert(mData);
}
コード例 #4
0
static void getMD5HashStringForBitmap(CGContextRef bitmap, char string[33])
{
    MD5_CTX md5Context;
    unsigned char hash[16];

    size_t bitsPerPixel = CGBitmapContextGetBitsPerPixel(bitmap);
    ASSERT(bitsPerPixel == 32); // ImageDiff assumes 32 bit RGBA, we must as well.
    size_t bytesPerPixel = bitsPerPixel / 8;
    size_t pixelsHigh = CGBitmapContextGetHeight(bitmap);
    size_t pixelsWide = CGBitmapContextGetWidth(bitmap);
    size_t bytesPerRow = CGBitmapContextGetBytesPerRow(bitmap);
    ASSERT(bytesPerRow >= (pixelsWide * bytesPerPixel));

    MD5_Init(&md5Context);
    unsigned char* bitmapData = static_cast<unsigned char*>(CGBitmapContextGetData(bitmap));
    for (unsigned row = 0; row < pixelsHigh; row++) {
        MD5_Update(&md5Context, bitmapData, static_cast<unsigned>(pixelsWide * bytesPerPixel));
        bitmapData += bytesPerRow;
    }
    MD5_Final(hash, &md5Context);

    string[0] = '\0';
    for (int i = 0; i < 16; i++)
        snprintf(string, 33, "%s%02x", string, hash[i]);
}
コード例 #5
0
void
SourceSurfaceCGBitmapContext::DrawTargetWillChange()
{
  if (mDrawTarget) {
    // This will break the weak reference we hold to mCg
    size_t stride = CGBitmapContextGetBytesPerRow(mCg);
    size_t height = CGBitmapContextGetHeight(mCg);

    //XXX: infalliable malloc?
    mData = malloc(stride * height);

    // copy out the data from the CGBitmapContext
    // we'll maintain ownership of mData until
    // we transfer it to mImage
    memcpy(mData, CGBitmapContextGetData(mCg), stride*height);

    // drop the current image for the data associated with the CGBitmapContext
    if (mImage)
      CGImageRelease(mImage);
    mImage = nullptr;

    mCg = nullptr;
    mDrawTarget = nullptr;
  }
}
コード例 #6
0
DRAW_TEST_F(CGContextFlush, FillFlushMultipleDrawingCounters, WhiteBackgroundTest<>) {
    CGContextRef context = GetDrawingContext();
    CGRect bounds = GetDrawingBounds();
    static int sDrawCount = 5;

    for (int i = 0; i < sDrawCount; ++i) {
        _CGContextPushBeginDraw(context);
    }

    CGContextSetRGBFillColor(context, 1, 0, 0, 1);
    CGContextFillRect(context, bounds);

    for (int i = 0; i < sDrawCount; ++i) {
        // Multiple flushes should work.
        CGContextFlush(context);
    }

    // Add some extra drawings
    CGContextClearRect(context, bounds);

    // We should still have red & clear should not of been executed.
    unsigned char* dataPtr = static_cast<unsigned char*>(CGBitmapContextGetData(context));
    ASSERT_NE(dataPtr, nullptr);

    // Validate only the red fill rect is executed.
    EXPECT_EQ(dataPtr[0], 0x00);
    EXPECT_EQ(dataPtr[1], 0x00);
    EXPECT_EQ(dataPtr[2], 0xff);
    EXPECT_EQ(dataPtr[3], 0xff);

    for (int i = 0; i < 3; ++i) {
        // Multiple flushes should work.
        CGContextFlush(context);
    }

    // validate clear
    EXPECT_EQ(dataPtr[0], 0x00);
    EXPECT_EQ(dataPtr[1], 0x00);
    EXPECT_EQ(dataPtr[2], 0x00);
    EXPECT_EQ(dataPtr[3], 0x00);

    CGContextSetRGBStrokeColor(context, 0, 1, 0, 1);
    CGContextStrokeRect(context, CGRectMake(100, 100, 200, 300));

    // Still should be clear.
    EXPECT_EQ(dataPtr[0], 0x00);
    EXPECT_EQ(dataPtr[1], 0x00);
    EXPECT_EQ(dataPtr[2], 0x00);
    EXPECT_EQ(dataPtr[3], 0x00);

    for (int i = 0; i < sDrawCount; ++i) {
        _CGContextPopEndDraw(context);
    }
}
コード例 #7
0
DRAW_TEST_F(CGContextFlush, FillFlush, WhiteBackgroundTest<>) {
    CGContextRef context = GetDrawingContext();
    CGRect bounds = GetDrawingBounds();
    _CGContextPushBeginDraw(context);

    CGContextSetRGBFillColor(context, 1, 0, 0, 1);
    CGContextFillRect(context, bounds);

    // Flush the red fill rect
    CGContextFlush(context);

    CGContextSetRGBFillColor(context, 0, 0, 1, 1);
    CGContextFillRect(context, CGRectMake(0, 0, 300, 300));

    // We should still have red & blue rectangle should not show up.
    unsigned char* dataPtr = static_cast<unsigned char*>(CGBitmapContextGetData(context));
    ASSERT_NE(dataPtr, nullptr);

    // Validate only the red fill rect is executed.
    EXPECT_EQ(dataPtr[0], 0x00);
    EXPECT_EQ(dataPtr[1], 0x00);
    EXPECT_EQ(dataPtr[2], 0xff);
    EXPECT_EQ(dataPtr[3], 0xff);

    CGContextFlush(context);

    // We should now see the blue fill rect
    EXPECT_EQ(dataPtr[0], 0xff);
    EXPECT_EQ(dataPtr[1], 0x00);
    EXPECT_EQ(dataPtr[2], 0x00);
    EXPECT_EQ(dataPtr[3], 0xff);

    // Add some extra drawings
    CGContextClearRect(context, CGRectMake(100, 100, 200, 300));

    CGPoint center = _CGRectGetCenter(bounds);

    CGMutablePathRef concentricCirclesPath = CGPathCreateMutable();

    CGPathAddEllipseInRect(concentricCirclesPath, nullptr, _CGRectCenteredOnPoint({ 50, 50 }, center));
    CGPathAddEllipseInRect(concentricCirclesPath, nullptr, _CGRectCenteredOnPoint({ 100, 100 }, center));
    CGPathAddEllipseInRect(concentricCirclesPath, nullptr, _CGRectCenteredOnPoint({ 150, 150 }, center));
    CGPathAddEllipseInRect(concentricCirclesPath, nullptr, _CGRectCenteredOnPoint({ 200, 200 }, center));

    CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 0.5);
    CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);

    CGContextAddPath(context, concentricCirclesPath);
    CGContextDrawPath(context, kCGPathFillStroke);

    CGPathRelease(concentricCirclesPath);

    _CGContextPopEndDraw(context);
}
コード例 #8
0
ファイル: gvrender_quartz.c プロジェクト: nyue/graphviz-cmake
static void quartzgen_end_job(GVJ_t *job)
{
	CGContextRef context = (CGContextRef)job->context;
	if (!job->external_context) {
		switch (job->device.id) {
		
		case FORMAT_PDF:
			/* save the PDF */
			CGPDFContextClose(context);
			break;
				
		case FORMAT_CGIMAGE:
			break;
			
#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1040
		default:	/* bitmap formats */
			{
				/* create an image destination */
				CGDataConsumerRef data_consumer = CGDataConsumerCreate(job, &device_data_consumer_callbacks);
				CGImageDestinationRef image_destination = CGImageDestinationCreateWithDataConsumer(data_consumer, format_uti[job->device.id], 1, NULL);
				
				/* add the bitmap image to the destination and save it */
				CGImageRef image = CGBitmapContextCreateImage(context);
				CGImageDestinationAddImage(image_destination, image, NULL);
				CGImageDestinationFinalize(image_destination);
				
				/* clean up */
				if (image_destination)
					CFRelease(image_destination);
				CGImageRelease(image);
				CGDataConsumerRelease(data_consumer);
			}
			break;
#endif
		}
		CGContextRelease(context);
	}
	else if (job->device.id == FORMAT_CGIMAGE)
	{
		/* create an image and save it where the window field is, which was set to the passed-in context at begin job */
		*((CGImageRef*)job->window) = CGBitmapContextCreateImage(context);
#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 20000
		void* context_data = CGBitmapContextGetData(context);
		size_t context_datalen = CGBitmapContextGetBytesPerRow(context) * CGBitmapContextGetHeight(context);
#endif
		CGContextRelease(context);
#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 20000
		munmap(context_data, context_datalen);
#endif
	}
}
コード例 #9
0
 bool allocHandle(const SkImageInfo& info, Rec* rec) override {
     // let CG allocate the pixels
     CGContextRef cg = SkCreateCGContext(SkPixmap(info, nullptr, 0));
     if (!cg) {
         return false;
     }
     rec->fReleaseProc = [](void* pixels, void* ctx){ CGContextRelease((CGContextRef)ctx); };
     rec->fReleaseCtx = cg;
     rec->fPixels = CGBitmapContextGetData(cg);
     rec->fRowBytes = CGBitmapContextGetBytesPerRow(cg);
     rec->fHandle = cg;
     CGContextSaveGState(cg);    // balanced each time updateContext is called
     return true;
 }
コード例 #10
0
SourceSurfaceCGBitmapContext::SourceSurfaceCGBitmapContext(DrawTargetCG *aDrawTarget)
{
  mDrawTarget = aDrawTarget;
  mFormat = aDrawTarget->GetFormat();
  mCg = (CGContextRef)aDrawTarget->GetNativeSurface(NativeSurfaceType::CGCONTEXT);
  if (!mCg)
    abort();

  mSize.width = CGBitmapContextGetWidth(mCg);
  mSize.height = CGBitmapContextGetHeight(mCg);
  mStride = CGBitmapContextGetBytesPerRow(mCg);
  mData = CGBitmapContextGetData(mCg);

  mImage = nullptr;
}
コード例 #11
0
/*	createImageFromBitmapContext creates a CGImageRef
	from a bitmap context. Calling this routine
	transfers 'ownership' of the raster data
	in the bitmap context, to the image. If the
	image can't be created, this routine frees
	the memory associated with the raster. 
*/
CGImageRef createImageFromBitmapContext(CGContextRef c)
{
    CGImageRef image;
	unsigned char *rasterData = CGBitmapContextGetData(c);
    size_t imageDataSize = 
		CGBitmapContextGetBytesPerRow(c)*CGBitmapContextGetHeight(c);
	
	if(rasterData == NULL){
		fprintf(stderr, "Context is not a bitmap context!\n");
		return NULL;
	}
	
    // Create the data provider from the image data, using
	// the image releaser function releaseBitmapContextImageData.
    CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL,
					    rasterData,
					    imageDataSize,
					    releaseBitmapContextImageData);
    if(dataProvider == NULL){
		// Since this routine owns the raster memory, it must
		// free it if it can't create the data provider.
		free(rasterData);
		fprintf(stderr, "Couldn't create data provider!\n");
		return NULL;
    }
	// Now create the image. The parameters for the image closely match
	// the parameters of the bitmap context. This code uses a NULL
	// decode array and shouldInterpolate is true.
    image = CGImageCreate(CGBitmapContextGetWidth(c), 
			  CGBitmapContextGetHeight(c), 
			  CGBitmapContextGetBitsPerComponent(c), 
			  CGBitmapContextGetBitsPerPixel(c), 
			  CGBitmapContextGetBytesPerRow(c), 
			  CGBitmapContextGetColorSpace(c),
			  myCGContextGetBitmapInfo(c),
			  dataProvider,
			  NULL,
			  true,
			  kCGRenderingIntentDefault);
    // Release the data provider since the image retains it.
    CGDataProviderRelease(dataProvider);
    if(image == NULL){
		fprintf(stderr, "Couldn't create image!\n");
		return NULL;
    }
    return image;
}
コード例 #12
0
/*	createMaskFromAlphaOnlyContext creates a CGImageRef
	from an alpha-only bitmap context. Calling this routine
	transfers 'ownership' of the raster data in the bitmap 
	context, to the image. If the image can't be created, this 
	routine frees the memory associated with the raster. 
*/
static CGImageRef createMaskFromAlphaOnlyContext(CGContextRef alphaContext)
{
    CGImageRef mask;
	unsigned char *rasterData = CGBitmapContextGetData(alphaContext);
    size_t imageDataSize = CGBitmapContextGetBytesPerRow(alphaContext)*
								CGBitmapContextGetHeight(alphaContext);
    float invertDecode[] = { 1. , 0. };
        
	if(rasterData == NULL){
		fprintf(stderr, "Context is not a bitmap context!\n");
		return NULL;
	}

    // Create the data provider from the image data.
    CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, 
					    rasterData,
					    imageDataSize,
					    releaseBitmapContextImageData);

    if(dataProvider == NULL){
		// Must free the memory if the data provider couldn't be created
		// since this routine now owns it.
		free(rasterData);
		fprintf(stderr, "Couldn't create data provider!\n");
		return NULL;
    }
    mask = CGImageMaskCreate(CGBitmapContextGetWidth(alphaContext), 
			  CGBitmapContextGetHeight(alphaContext), 
			  CGBitmapContextGetBitsPerComponent(alphaContext), 
			  CGBitmapContextGetBitsPerPixel(alphaContext), 
			  CGBitmapContextGetBytesPerRow(alphaContext), 
			  dataProvider,
	// The decode is an inverted decode since a mask has the opposite
	// sense than alpha, i.e. 0 in a mask paints 100% and 1 in a mask
	// paints nothing.
			  invertDecode,
			  true);

    // Release the data provider since the mask retains it.
    CGDataProviderRelease(dataProvider);

    if(mask == NULL){
		fprintf(stderr, "Couldn't create image mask!\n");
		return NULL;
    }
    return mask;
}
コード例 #13
0
void
SourceSurfaceCGBitmapContext::DrawTargetWillGoAway()
{
  if (mDrawTarget) {
    if (mDrawTarget->mData != CGBitmapContextGetData(mCg)) {
      DrawTargetWillChange();
      return;
    }

    // Instead of copying the data over, we can just swap it.
    mDataHolder.Swap(mDrawTarget->mData);
    mData = mDataHolder;
    mCg = nullptr;
    mDrawTarget = nullptr;
    // mImage is still valid because it still points to the same data.
  }
}
コード例 #14
0
ファイル: gvrender_quartz.c プロジェクト: nyue/graphviz-cmake
static void quartzgen_begin_page(GVJ_t *job)
{
	CGRect bounds = CGRectMake(0.0, 0.0, job->width, job->height);
	
	if (!job->context) {
		
		switch (job->device.id) {
		
		case FORMAT_PDF:
			{
				/* create the auxiliary info for PDF content, author and title */
				CFStringRef auxiliaryKeys[] = {
					kCGPDFContextCreator,
					kCGPDFContextTitle
				};
				CFStringRef auxiliaryValues[] = {
					CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s %s"), job->common->info[0], job->common->info[1]),
					job->obj->type == ROOTGRAPH_OBJTYPE ?
						CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)job->obj->u.g->name, strlen(job->obj->u.g->name), kCFStringEncodingUTF8, false, kCFAllocatorNull)
						: CFSTR("")
				};
				CFDictionaryRef auxiliaryInfo = CFDictionaryCreate(
					kCFAllocatorDefault,
					(const void **)&auxiliaryKeys,
					(const void **)&auxiliaryValues,
					sizeof(auxiliaryValues)/sizeof(auxiliaryValues[0]),
					&kCFTypeDictionaryKeyCallBacks,
					&kCFTypeDictionaryValueCallBacks
				);
				
				/* create a PDF for drawing into */
				CGDataConsumerRef data_consumer = CGDataConsumerCreate(job, &device_data_consumer_callbacks);
				job->context = CGPDFContextCreate(data_consumer, &bounds, auxiliaryInfo);
				
				/* clean up */
				CGDataConsumerRelease(data_consumer);
				CFRelease(auxiliaryInfo);
				int i;
				for (i = 0; i < sizeof(auxiliaryValues)/sizeof(auxiliaryValues[0]); ++i)
					CFRelease(auxiliaryValues[i]);
			}
			break;
		
		default: /* bitmap formats */
			{	
				size_t bytes_per_row = (job->width * BYTES_PER_PIXEL + BYTE_ALIGN) & ~BYTE_ALIGN;
				
				void* buffer = NULL;
				
#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 20000
				
				/* iPhoneOS has no swap files for memory, so if we're short of memory we need to make our own temp scratch file to back it */
				
				size_t buffer_size = job->height * bytes_per_row;
				mach_msg_type_number_t vm_info_size = HOST_VM_INFO_COUNT;
				vm_statistics_data_t vm_info;
				
				if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vm_info, &vm_info_size) != KERN_SUCCESS
					|| buffer_size * 2 > vm_info.free_count * vm_page_size)
				{
					FILE* temp_file = tmpfile();
					if (temp_file)
					{
						int temp_file_descriptor = fileno(temp_file);
						if (temp_file_descriptor >= 0 && ftruncate(temp_file_descriptor, buffer_size) == 0)
						{
							buffer = mmap(
								NULL,
								buffer_size,
								PROT_READ | PROT_WRITE,
								MAP_FILE | MAP_SHARED,
								temp_file_descriptor,
								0);
							if (buffer == (void*)-1)
								buffer = NULL;
						}
						fclose(temp_file);
					}
				}
				if (!buffer)
					buffer = mmap(
						NULL,
						buffer_size,
						PROT_READ | PROT_WRITE,
						MAP_ANON| MAP_SHARED,
						-1,
						0);				
#endif				
				
				/* create a true color bitmap for drawing into */
				CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
				job->context = CGBitmapContextCreate(
					buffer,							/* data: MacOSX lets system allocate, iPhoneOS use manual memory mapping */
					job->width,						/* width in pixels */
					job->height,					/* height in pixels */
					BITS_PER_COMPONENT,				/* bits per component */
					bytes_per_row,					/* bytes per row: align to 16 byte boundary */
					color_space,					/* color space: device RGB */
					kCGImageAlphaPremultipliedFirst	/* bitmap info: premul ARGB has best support in OS X */
				);
				job->imagedata = CGBitmapContextGetData((CGContextRef)job->context);
				
				/* clean up */
				CGColorSpaceRelease(color_space);
			}
			break;
		}
		
	}
	
	/* start the page (if this is a paged context) and graphics state */
	CGContextRef context = (CGContextRef)job->context;
	CGContextBeginPage(context, &bounds);
	CGContextSaveGState(context);
	CGContextSetMiterLimit(context, 1.0);
	CGContextSetLineJoin(context, kCGLineJoinRound);
	
	/* set up the context transformation */
	CGContextScaleCTM(context, job->scale.x, job->scale.y);
	CGContextRotateCTM(context, -job->rotation * M_PI / 180.0);
	CGContextTranslateCTM(context, job->translation.x, job->translation.y);
}
コード例 #15
0
ファイル: draw.c プロジェクト: carriercomm/gameshell-mac
void *draw__get_bitmap_data(draw__Bitmap bitmap) {
  return CGBitmapContextGetData(bitmap);
}
コード例 #16
0
DataSourceSurfaceCG::~DataSourceSurfaceCG()
{
  CGImageRelease(mImage);
  free(CGBitmapContextGetData(mCg));
  CGContextRelease(mCg);
}
コード例 #17
0
bool  ImageIODecoder::readData( Mat& img )
{
    uchar* data = img.data;
    int step = img.step;
    bool color = img.channels() > 1;
    int bpp; // Bytes per pixel
    int bit_depth = 8;

    // Get Height, Width, and color information
    if( !readHeader() )
        return false;

    CGContextRef     context = NULL; // The bitmap context
    CGColorSpaceRef  colorSpace = NULL;
    uchar*           bitmap = NULL;
    CGImageAlphaInfo alphaInfo;

    // CoreGraphics will take care of converting to grayscale and back as long as the
    // appropriate colorspace is set
    if( color == CV_LOAD_IMAGE_GRAYSCALE )
    {
        colorSpace = CGColorSpaceCreateDeviceGray();
        bpp = 1;
        alphaInfo = kCGImageAlphaNone;
    }
    else if( color == CV_LOAD_IMAGE_COLOR )
    {
        colorSpace = CGColorSpaceCreateWithName( kCGColorSpaceGenericRGBLinear );
        bpp = 4; /* CG only has 8 and 32 bit color spaces, so we waste a byte */
        alphaInfo = kCGImageAlphaNoneSkipLast;
    }
    if( !colorSpace )
        return false;

    bitmap = (uchar*)malloc( bpp * m_height * m_width );
    if( !bitmap )
    {
        CGColorSpaceRelease( colorSpace );
        return false;
    }

    context = CGBitmapContextCreate( (void *)bitmap,
                                     m_width,        /* width */
                                     m_height,       /* height */
                                     bit_depth,    /* bit depth */
                                     bpp * m_width,  /* bytes per row */
                                     colorSpace,     /* color space */
                                     alphaInfo);

    CGColorSpaceRelease( colorSpace );
    if( !context )
    {
        free( bitmap );
        return false;
    }

    // Copy the image data into the bitmap region
    CGRect rect = {{0,0},{m_width,m_height}};
    CGContextDrawImage( context, rect, imageRef );

    uchar* bitdata = (uchar*)CGBitmapContextGetData( context );
    if( !bitdata )
    {
        free( bitmap);
        CGContextRelease( context );
        return false;
    }

    // Move the bitmap (in RGB) into data (in BGR)
    int bitmapIndex = 0;

    if( color == CV_LOAD_IMAGE_COLOR )
	{
		uchar * base = data;

		for (int y = 0; y < m_height; y++)
		{
			uchar * line = base + y * step;

		    for (int x = 0; x < m_width; x++)
		    {
				// Blue channel
				line[0] = bitdata[bitmapIndex + 2];
				// Green channel
				line[1] = bitdata[bitmapIndex + 1];
				// Red channel
				line[2] = bitdata[bitmapIndex + 0];

				line        += 3;
				bitmapIndex += bpp;
			}
		}
    }
    else if( color == CV_LOAD_IMAGE_GRAYSCALE )
    {
		for (int y = 0; y < m_height; y++)
			memcpy (data + y * step, bitmap + y * m_width, m_width);
    }

    free( bitmap );
    CGContextRelease( context );
    return true;
}