diff --git a/ChangeLog b/ChangeLog index 8386a20..dc7dea4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2015-12-13 Fred Kiefer + + * Tests/GNUmakefile + * Tests/doublebuffer.m + New test file for double buffering as used by back. + * Source/OpalGraphics/CGBitmapContext.m + * Source/OpalGraphics/CGColor.m + * Source/OpalGraphics/CGImage.m + * Source/OpalGraphics/OPColorTransformLCMS.m + * Source/OpalGraphics/OPImageConversion.h + * Source/OpalGraphics/OPImageConversion.m + * Source/OpalGraphics/OPPremultiplyAlpha.m + Correct and speed up image conversion. + 2013-10-01 Eric Wasylishen * Headers/CoreGraphics/CGContext.h: diff --git a/Source/OpalGraphics/CGBitmapContext.m b/Source/OpalGraphics/CGBitmapContext.m index a2b3e80..b557109 100644 --- a/Source/OpalGraphics/CGBitmapContext.m +++ b/Source/OpalGraphics/CGBitmapContext.m @@ -60,6 +60,12 @@ isCairoDrawingIntoUserBuffer: (BOOL)isCairoDrawingIntoUserBuffer @end @implementation CGBitmapContext +/* + ARGB => Alpha First & Big Endian + BGRA => Alpha First & Little Endian + RGBA => Alpha Last & Big Endian + ABGR => Alpha Last & Little Endian +*/ static BOOL isFormatNativelySupportedByCairo( CGBitmapInfo info, @@ -103,7 +109,7 @@ static BOOL isFormatNativelySupportedByCairo( else if (bitsPerComponent == 8 && numComps == 3 && model == kCGColorSpaceModelRGB - && (alpha != kCGImageAlphaNone)) + && (alpha == kCGImageAlphaPremultipliedFirst)) { format = CAIRO_FORMAT_ARGB32; } @@ -430,6 +436,7 @@ CGImageRef CGBitmapContextCreateImage(CGContextRef ctx) kCGRenderingIntentDefault ); + //img->surf = cairo_get_target(ctx->ct); CGDataProviderRelease(dp); OPRESTORELOGGING() return img; diff --git a/Source/OpalGraphics/CGColor.m b/Source/OpalGraphics/CGColor.m index 5c1347c..6902381 100644 --- a/Source/OpalGraphics/CGColor.m +++ b/Source/OpalGraphics/CGColor.m @@ -106,6 +106,7 @@ static CGColorRef _clearColor; sourceFormat.hasAlpha = true; sourceFormat.isAlphaPremultiplied = false; sourceFormat.isAlphaLast = true; + sourceFormat.needs32Swap = false; OPImageFormat destFormat; destFormat.compFormat = kOPComponentFormatFloat32bpc; @@ -113,6 +114,7 @@ static CGColorRef _clearColor; destFormat.hasAlpha = true; destFormat.isAlphaPremultiplied = false; destFormat.isAlphaLast = true; + destFormat.needs32Swap = false; id xform = [sourceSpace colorTransformTo: destSpace sourceFormat: sourceFormat diff --git a/Source/OpalGraphics/CGImage.m b/Source/OpalGraphics/CGImage.m index 9fe5df9..8e7eda5 100644 --- a/Source/OpalGraphics/CGImage.m +++ b/Source/OpalGraphics/CGImage.m @@ -296,6 +296,7 @@ CGImageRef CGImageCreateWithImageInRect( // TODO: Implement data provider to crop the data from the source image // TODO: Share underlying cairo surface! + new->surf = image->surf; return new; } @@ -493,16 +494,7 @@ cairo_surface_t *opal_CGImageGetSurfaceForImage(CGImageRef img, cairo_surface_t const size_t dstBitsPerComponent = 8; const size_t dstBitsPerPixel = 32; const size_t dstBytesPerRow = cairo_image_surface_get_stride(memSurf); - - CGBitmapInfo dstBitmapInfo = kCGImageAlphaPremultipliedFirst; - if (NSHostByteOrder() == NS_LittleEndian) - { - dstBitmapInfo |= kCGBitmapByteOrder32Little; - } - else if (NSHostByteOrder() == NS_BigEndian) - { - dstBitmapInfo |= kCGBitmapByteOrder32Big; - } + CGBitmapInfo dstBitmapInfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst; const CGColorSpaceRef dstColorSpace = srcColorSpace; OPImageConvert( @@ -515,8 +507,8 @@ cairo_surface_t *opal_CGImageGetSurfaceForImage(CGImageRef img, cairo_surface_t dstColorSpace, srcColorSpace, srcIntent); - DumpPixel(srcData, @"OPImageConvert src (expecting R G B A)"); - DumpPixel(dstData, @"OPImageConvert dst (expecting A R G B premult)"); + DumpPixel(srcData, @"OPImageConvert src (expecting R G B A)"); + DumpPixel(dstData, @"OPImageConvert dst (expecting A R G B premult)"); OPDataProviderReleaseBytePointer(img->dp, srcData); @@ -530,10 +522,10 @@ cairo_surface_t *opal_CGImageGetSurfaceForImage(CGImageRef img, cairo_surface_t CGImageGetWidth(img), CGImageGetHeight(img)); cairo_t *ctx = cairo_create(img->surf); - cairo_set_source_surface(ctx, memSurf, 0, 0); + cairo_set_source_surface(ctx, memSurf, 0, 0); cairo_paint(ctx); cairo_destroy(ctx); - cairo_surface_destroy(memSurf); + cairo_surface_destroy(memSurf); } return img->surf; diff --git a/Source/OpalGraphics/OPColorTransformLCMS.m b/Source/OpalGraphics/OPColorTransformLCMS.m index 12c7e65..18ace4c 100644 --- a/Source/OpalGraphics/OPColorTransformLCMS.m +++ b/Source/OpalGraphics/OPColorTransformLCMS.m @@ -97,12 +97,16 @@ static DWORD LcmsFormatForOPImageFormat(OPImageFormat opalFormat, CGColorSpaceRe if (opalFormat.hasAlpha) { cmsFormat |= EXTRA_SH(1); + if (!opalFormat.isAlphaLast) + { + cmsFormat |= SWAPFIRST_SH(1); + } } - if (!opalFormat.isAlphaLast && opalFormat.hasAlpha) + if (opalFormat.needs32Swap) { - cmsFormat |= SWAPFIRST_SH(1); + cmsFormat |= DOSWAP_SH(1); } - + cmsFormat |= COLORSPACE_SH( LcmsPixelTypeForCGColorSpaceModel( CGColorSpaceGetModel(colorSpace) @@ -112,6 +116,80 @@ static DWORD LcmsFormatForOPImageFormat(OPImageFormat opalFormat, CGColorSpaceRe return cmsFormat; } +static bool isAlphaLast(OPImageFormat opalFormat) +{ + if (opalFormat.needs32Swap) + { + return !opalFormat.isAlphaLast; + } + else + { + return opalFormat.isAlphaLast; + } +} + +typedef struct { + cmsHTRANSFORM xform; + OPColorSpaceLCMS *aSourceSpace; + OPColorSpaceLCMS *aDestSpace; + int lcmsSrcFormat; + int lcmsDstFormat; + CGColorRenderingIntent anIntent; +} cacheEntry; + +#define MAX_CACHE 10 +static cacheEntry transformCache[MAX_CACHE]; +static int usedTransforms = 0; + ++ (cmsHTRANSFORM) getTransformForSourceSpace: (OPColorSpaceLCMS *)aSourceSpace + destinationSpace: (OPColorSpaceLCMS *)aDestSpace + sourceFormat: (OPImageFormat)aSourceFormat + destinationFormat: (OPImageFormat)aDestFormat + renderingIntent: (CGColorRenderingIntent)anIntent +{ + const int lcmsIntent = LcmsIntentForCGColorRenderingIntent(anIntent); + int lcmsSrcFormat = LcmsFormatForOPImageFormat(aSourceFormat, aSourceSpace); + int lcmsDstFormat = LcmsFormatForOPImageFormat(aDestFormat, aDestSpace); + int i; + cmsHTRANSFORM xform; + cacheEntry entry; + + for (i = 0; i < usedTransforms; i++) + { + entry = transformCache[i]; + + if ((entry.anIntent == anIntent) + && (entry.lcmsSrcFormat == lcmsSrcFormat) + && (entry.lcmsDstFormat == lcmsDstFormat) + && [entry.aDestSpace isEqual: aDestSpace] + && [entry.aSourceSpace isEqual: aSourceSpace]) + { + return entry.xform; + } + } + + if (usedTransforms == MAX_CACHE) + { + cmsDeleteTransform(transformCache[usedTransforms].xform); + memcpy(transformCache, transformCache + 1, sizeof(cacheEntry) * (MAX_CACHE - 1)); + usedTransforms--; + } + + xform = cmsCreateTransform(aSourceSpace->profile, lcmsSrcFormat, + aDestSpace->profile, lcmsDstFormat, + lcmsIntent, 0); + // FIXME: check for success + transformCache[usedTransforms].xform = xform; + transformCache[usedTransforms].aSourceSpace = aSourceSpace; + transformCache[usedTransforms].aDestSpace = aDestSpace; + transformCache[usedTransforms].lcmsSrcFormat = lcmsSrcFormat; + transformCache[usedTransforms].lcmsDstFormat = lcmsDstFormat; + transformCache[usedTransforms].anIntent = anIntent; + usedTransforms++; + + return xform; +} + - (id) initWithSourceSpace: (OPColorSpaceLCMS *)aSourceSpace destinationSpace: (OPColorSpaceLCMS *)aDestSpace sourceFormat: (OPImageFormat)aSourceFormat @@ -120,17 +198,18 @@ static DWORD LcmsFormatForOPImageFormat(OPImageFormat opalFormat, CGColorSpaceRe pixelCount: (size_t)aPixelCount { self = [super init]; + if (!self) + { + return nil; + } ASSIGN(source, aSourceSpace); ASSIGN(dest, aDestSpace); - const int lcmsIntent = LcmsIntentForCGColorRenderingIntent(anIntent); - int lcmsSrcFormat = LcmsFormatForOPImageFormat(aSourceFormat, aSourceSpace); - int lcmsDstFormat = LcmsFormatForOPImageFormat(aDestFormat, aDestSpace); - - self->xform = cmsCreateTransform(aSourceSpace->profile, lcmsSrcFormat, - aDestSpace->profile, lcmsDstFormat, - lcmsIntent, 0); - // FIXME: check for success + self->xform = [OPColorTransformLCMS getTransformForSourceSpace: aSourceSpace + destinationSpace: aDestSpace + sourceFormat: aSourceFormat + destinationFormat: aDestFormat + renderingIntent: anIntent]; self->renderingIntent = anIntent; self->sourceFormat = aSourceFormat; @@ -159,7 +238,12 @@ static DWORD LcmsFormatForOPImageFormat(OPImageFormat opalFormat, CGColorSpaceRe - (void) dealloc { - cmsDeleteTransform(self->xform); + /* + if (self->xform != NULL) + { + cmsDeleteTransform(self->xform); + } + */ [source release]; [dest release]; if (tempBuffer1) @@ -182,52 +266,52 @@ static DWORD LcmsFormatForOPImageFormat(OPImageFormat opalFormat, CGColorSpaceRe const size_t totalComponentsOut = destFormat.colorComponents + (destFormat.hasAlpha ? 1 : 0); const bool destIntermediateIs16bpc = (destFormat.compFormat == kOPComponentFormatFloat32bpc - || destFormat.compFormat == kOPComponentFormat32bpc - || destFormat.compFormat == kOPComponentFormat16bpc); + || destFormat.compFormat == kOPComponentFormat32bpc + || destFormat.compFormat == kOPComponentFormat16bpc); - const bool sourceIntermediateIs16bpc = (sourceFormat.compFormat == kOPComponentFormatFloat32bpc - || sourceFormat.compFormat == kOPComponentFormat32bpc - || sourceFormat.compFormat == kOPComponentFormat16bpc); + const bool sourceIntermediateIs16bpc = (sourceFormat.compFormat == kOPComponentFormatFloat32bpc + || sourceFormat.compFormat == kOPComponentFormat32bpc + || sourceFormat.compFormat == kOPComponentFormat16bpc); //NSLog(@"Transform %d pixels %d comps in %d out", pixelCount, totalComponentsIn, totalComponentsOut); // Special case for kOPComponentFormatFloat32bpc, which LCMS 1 doesn't support directly - // Copy to temp input buffer + // Copy to temp input buffer if (sourceFormat.compFormat == kOPComponentFormatFloat32bpc) - { - for (size_t i=0; i %d", (float)((float*)input)[i*totalComponentsIn + j],(int)((uint16_t*)tempBuffer1)[i*totalComponentsIn + j]); - if ( (float)((float*)input)[i*totalComponentsIn + j] > 1) - { - NSLog(@"%s: overflow", __PRETTY_FUNCTION__); - } - } + for (size_t i = 0; i < pixelCount; i++) + { + for (size_t j = 0; j < totalComponentsIn; j++) + { + ((uint16_t*)tempBuffer1)[i*totalComponentsIn + j] = UINT16_MAX * ((float*)input)[i*totalComponentsIn + j]; + //NSLog(@"Input comp: %f => %d", (float)((float*)input)[i*totalComponentsIn + j],(int)((uint16_t*)tempBuffer1)[i*totalComponentsIn + j]); + if ((float)((float*)input)[i*totalComponentsIn + j] > 1) + { + NSLog(@"%s: overflow", __PRETTY_FUNCTION__); + } + } + } + input = (const unsigned char *)tempBuffer1; } - input = (const unsigned char *)tempBuffer1; - } else if (sourceFormat.compFormat == kOPComponentFormat32bpc) - { - for (size_t i=0; i> 16; - } - } - input = tempBuffer1; + for (size_t i = 0; i < pixelCount; i++) + { + for (size_t j = 0; j < totalComponentsIn; j++) + { + ((uint16_t*)tempBuffer1)[i*totalComponentsIn + j] = ((uint32_t*)input)[i*totalComponentsIn + j] >> 16; + } + } + input = tempBuffer1; } // Special case: if outputting kOPComponentFormatFloat32bpc, we get LCMS // to convert to uint32, then manually convert that to float if (destFormat.compFormat == kOPComponentFormatFloat32bpc || destFormat.compFormat == kOPComponentFormat32bpc) - { - tempOutput = tempBuffer2; - } + { + tempOutput = tempBuffer2; + } // Unpremultiply alpha in input if (sourceFormat.isAlphaPremultiplied) @@ -251,23 +335,33 @@ static DWORD LcmsFormatForOPImageFormat(OPImageFormat opalFormat, CGColorSpaceRe // generate a output alpha channel of alpha=100% if necessary if (!sourceFormat.hasAlpha && destFormat.hasAlpha) - { - size_t destIntermediateBytesPerComponent = (destIntermediateIs16bpc ? 2 : 1); - size_t destIntermediateTotalComponentsPerPixel = (destFormat.colorComponents + (destFormat.hasAlpha ? 1 : 0)); - size_t destIntermediateBytesPerRow = pixelCount * destIntermediateTotalComponentsPerPixel * destIntermediateBytesPerComponent; - memset(tempOutput, 0xff, destIntermediateBytesPerRow); - } + { + size_t destIntermediateBytesPerComponent = (destIntermediateIs16bpc ? 2 : 1); + size_t destIntermediateTotalComponentsPerPixel = (destFormat.colorComponents + (destFormat.hasAlpha ? 1 : 0)); + size_t destIntermediateBytesPerRow = pixelCount * destIntermediateTotalComponentsPerPixel * destIntermediateBytesPerComponent; + memset(tempOutput, 0xff, destIntermediateBytesPerRow); + } // get LCMS to do the main conversion of the color channels - - cmsDoTransform(xform, (void*)input, tempOutput, pixelCount); + + if (xform != NULL) + { + cmsDoTransform(xform, (void*)input, tempOutput, pixelCount); + } + else + { + size_t destIntermediateBytesPerComponent = (destIntermediateIs16bpc ? 2 : 1); + size_t destIntermediateTotalComponentsPerPixel = (destFormat.colorComponents + (destFormat.hasAlpha ? 1 : 0)); + size_t destIntermediateBytesPerRow = pixelCount * destIntermediateTotalComponentsPerPixel * destIntermediateBytesPerComponent; + memcpy(tempOutput, input, destIntermediateBytesPerRow); + } // copy alpha from source to dest if necessary if (sourceFormat.hasAlpha && destFormat.hasAlpha) { - const size_t sourceAlphaCompIndex = (sourceFormat.isAlphaLast ? sourceFormat.colorComponents : 0); - const size_t destAlphaCompIndex = (destFormat.isAlphaLast ? destFormat.colorComponents : 0); + const size_t sourceAlphaCompIndex = (isAlphaLast(sourceFormat) ? sourceFormat.colorComponents : 0); + const size_t destAlphaCompIndex = (isAlphaLast(destFormat) ? destFormat.colorComponents : 0); if (sourceIntermediateIs16bpc && destIntermediateIs16bpc) /* 16 bit -> 16 bit */ for (size_t i=0; i", msg, compFormatString, fmt.colorComponents, fmt.hasAlpha, fmt.isAlphaPremultiplied, fmt.isAlphaLast); @@ -196,6 +196,17 @@ static void OPRoundUp(size_t bitsPerComponentIn, size_t bitsPerPixelIn, size_t * *bitsPerPixelOut = (bitsPerPixelIn / bitsPerComponentIn) * (*bitsPerComponentOut); } +static bool needsByteSwap(CGBitmapInfo bitmapInfo) +{ + int order = bitmapInfo & kCGBitmapByteOrderMask; + + if (order == kCGBitmapByteOrderDefault) + { + order = kCGBitmapByteOrder32Big; + } + return (kCGBitmapByteOrder32Host != order); +} + static bool OPImageFormatForCGFormat( size_t bitsPerComponent, size_t bitsPerPixel, @@ -214,11 +225,11 @@ static bool OPImageFormatForCGFormat( case 32: if (bitmapInfo & kCGBitmapFloatComponents) { - out->compFormat = kOPComponentFormat32bpc; + out->compFormat = kOPComponentFormatFloat32bpc; } else { - out->compFormat = kOPComponentFormatFloat32bpc; + out->compFormat = kOPComponentFormat32bpc; } break; default: @@ -235,6 +246,9 @@ static bool OPImageFormatForCGFormat( alpha == kCGImageAlphaPremultipliedLast); out->isAlphaLast = (alpha == kCGImageAlphaPremultipliedLast || alpha == kCGImageAlphaLast); + // LCMS use big endian + out->needs32Swap = bitmapInfo & kCGBitmapByteOrder32Little; + return true; } @@ -267,43 +281,59 @@ void OPImageConvert( NSLog(@"Output format not supported"); } + if ((dstBitsPerComponent == srcBitsPerComponent) && + (dstBitsPerPixel == srcBitsPerPixel) && + (dstBytesPerRow == srcBytesPerRow) && + (dstBitmapInfo == srcBitmapInfo) && + [dstColorSpace isEqual: srcColorSpace]) + { + // No conversion needed, copy over the data + memcpy(dstData, srcData, height * srcBytesPerRow); + + return; + } + OPImageFormatLog(srcFormat, @"OPImageConversion source"); OPImageFormatLog(dstFormat, @"OPImageConversion dest"); id xform = [srcColorSpace colorTransformTo: dstColorSpace - sourceFormat: srcFormat - destinationFormat: dstFormat - renderingIntent: intent - pixelCount: width]; + sourceFormat: srcFormat + destinationFormat: dstFormat + renderingIntent: intent + pixelCount: width]; unsigned char *tempInput = malloc(srcBytesPerRow); - for (size_t row=0; row compMax) \ + { \ + pixelPtr[i] = compMax; \ + } \ + else \ + { \ + pixelPtr[i] = val; \ + } \ } \ } @@ -45,26 +67,60 @@ void OPPremultiplyAlpha( bool unPremultiply) { if (!format.hasAlpha) - { - NSLog(@"Warning, unnecessary call to OPPremultiplyAlpha"); - return; - } + { + NSLog(@"Warning, unnecessary call to OPPremultiplyAlpha"); + return; + } const size_t comps = (format.colorComponents + 1); + bool isAlphaLast = format.isAlphaLast; + + if (format.needs32Swap) + { + isAlphaLast = !isAlphaLast; + } + switch (format.compFormat) { case kOPComponentFormat8bpc: - _OPPremultiplyAlpha(format, uint8_t, UINT8_MAX, row, comps, pixels, unPremultiply); + if (unPremultiply) + { + _OPUnPremultiplyAlpha(isAlphaLast, uint8_t, UINT8_MAX, row, comps, pixels); + } + else + { + _OPPremultiplyAlpha(isAlphaLast, uint8_t, UINT8_MAX, row, comps, pixels); + } break; case kOPComponentFormat16bpc: - _OPPremultiplyAlpha(format, uint16_t, UINT16_MAX, row, comps, pixels, unPremultiply); + if (unPremultiply) + { + _OPUnPremultiplyAlpha(isAlphaLast, uint16_t, UINT16_MAX, row, comps, pixels); + } + else + { + _OPPremultiplyAlpha(isAlphaLast, uint16_t, UINT16_MAX, row, comps, pixels); + } break; case kOPComponentFormat32bpc: - _OPPremultiplyAlpha(format, uint32_t, UINT32_MAX, row, comps, pixels, unPremultiply); + if (unPremultiply) + { + _OPUnPremultiplyAlpha(isAlphaLast, uint32_t, UINT32_MAX, row, comps, pixels); + } + else + { + _OPPremultiplyAlpha(isAlphaLast, uint32_t, UINT32_MAX, row, comps, pixels); + } break; case kOPComponentFormatFloat32bpc: - _OPPremultiplyAlpha(format, float, 1.0f, row, comps, pixels, unPremultiply); + if (unPremultiply) + { + _OPUnPremultiplyAlpha(isAlphaLast, float, 1.0f, row, comps, pixels); + } + else + { + _OPPremultiplyAlpha(isAlphaLast, float, 1.0f, row, comps, pixels); + } break; } } - diff --git a/Tests/GNUmakefile b/Tests/GNUmakefile index 76779b3..854daa3 100644 --- a/Tests/GNUmakefile +++ b/Tests/GNUmakefile @@ -3,7 +3,7 @@ include $(GNUSTEP_MAKEFILES)/common.make ifdef NO_OPALTEXT TOOL_NAME = qeb2 shadows shapes layers pdf images gradients colorspace paths bitmaps gstate else -TOOL_NAME = qeb2 shadows shapes layers texttest textlayout pdf images gradients colorspace paths bitmaps gstate +TOOL_NAME = qeb2 shadows shapes layers texttest textlayout pdf images gradients colorspace paths bitmaps gstate doublebuffer endif ifeq ($(GNUSTEP_TARGET_OS), mingw32) @@ -26,6 +26,7 @@ gradients_OBJC_FILES = $(main_file) gradients.m colorspace_OBJC_FILES = $(main_file) colorspace.m bitmaps_OBJC_FILES = $(main_file) bitmaps.m gstate_OBJC_FILES = $(main_file) gstate.m +doublebuffer_OBJC_FILES = $(main_file) doublebuffer.m ifneq ($(GNUSTEP_TARGET_OS), mingw32) ADDITIONAL_OBJC_LIBS += -lm -lX11 diff --git a/Tests/doublebuffer.m b/Tests/doublebuffer.m new file mode 100644 index 0000000..b0d5381 --- /dev/null +++ b/Tests/doublebuffer.m @@ -0,0 +1,68 @@ +#ifdef __APPLE__ +#include +#else +#include +#endif + +#include + +/* Taken from GSQuartzCore's CABackingStore */ +static CGContextRef createCGBitmapContext(int pixelsWide, + int pixelsHigh) +{ + CGContextRef context = NULL; + CGColorSpaceRef colorSpace; + int bitmapBytesPerRow; + + bitmapBytesPerRow = (pixelsWide * 4); + + colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + + // Let CGBitmapContextCreate() allocate the memory. + // This should be good under Cocoa too. + context = CGBitmapContextCreate(NULL, + pixelsWide, + pixelsHigh, + 8, // bits per component + bitmapBytesPerRow, + colorSpace, + kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst); + + // Note: our use of premultiplied alpha means that we need to + // do alpha blending using: + // GL_SRC_ALPHA, GL_ONE + + CGColorSpaceRelease(colorSpace); + if (context == NULL) + { + NSLog(@"Context not created!"); + return NULL; + } + return context; +} + +void draw_red(CGContextRef ctx, CGRect rect) +{ + const CGFloat r = 0.9; + const CGFloat g = 0.0; + const CGFloat b = 0.0; + const CGFloat a = 0.7; + + + CGContextSetRGBFillColor(ctx, r, g, b, a); + CGContextFillRect(ctx, rect); + +} + +void draw(CGContextRef ctx, CGRect rect) +{ + CGContextRef ctx1 = createCGBitmapContext(rect.size.width, + rect.size.height); + draw_red(ctx1, rect); + + CGImageRef backingImage = CGBitmapContextCreateImage(ctx1); + + CGContextDrawImage(ctx, rect, backingImage); +} + +