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;
  }
}
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]);
}
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]);
}
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;
  }
}
/*	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;
}
/*	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;
}
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
	}
}
 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;
 }
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;
}