My final GNUstep commit.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/opal/trunk@39237 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
fredkiefer 2015-12-13 17:55:23 +00:00
parent 8269adcc02
commit 86e0cba223
10 changed files with 393 additions and 127 deletions

View File

@ -1,3 +1,17 @@
2015-12-13 Fred Kiefer <FredKiefer@gmx.de>
* 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 <ewasylishen@gmail.com>
* Headers/CoreGraphics/CGContext.h:

View File

@ -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;

View File

@ -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<OPColorTransform> xform = [sourceSpace colorTransformTo: destSpace
sourceFormat: sourceFormat

View File

@ -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;

View File

@ -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<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__);
}
}
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<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;
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<pixelCount; i++)

View File

@ -44,6 +44,8 @@ typedef struct OPImageFormat
bool hasAlpha;
bool isAlphaPremultiplied;
bool isAlphaLast;
bool needs32Swap;
// FIXME: More flags are needed
} OPImageFormat;
size_t OPComponentNumberOfBytes(OPComponentFormat fmt);

View File

@ -61,18 +61,18 @@ void OPImageFormatLog(OPImageFormat fmt, NSString *msg)
NSString *compFormatString = nil;
switch (fmt.compFormat)
{
case kOPComponentFormat8bpc:
compFormatString = @"kOPComponentFormat8bpc";
break;
case kOPComponentFormat16bpc:
compFormatString = @"kOPComponentFormat16bpc";
break;
case kOPComponentFormat8bpc:
compFormatString = @"kOPComponentFormat8bpc";
break;
case kOPComponentFormat16bpc:
compFormatString = @"kOPComponentFormat16bpc";
break;
case kOPComponentFormat32bpc:
compFormatString = @"kOPComponentFormat32bpc";
break;
compFormatString = @"kOPComponentFormat32bpc";
break;
case kOPComponentFormatFloat32bpc:
compFormatString = @"kOPComponentFormatFloat32bpc";
break;
compFormatString = @"kOPComponentFormatFloat32bpc";
break;
}
NSDebugLLog(@"Opal", @"%@: <%@, color components=%d, alpha?=%d, premul?=%d, alpha last?=%d>",
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<OPColorTransform> 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<height; row++)
{
const unsigned char *input = srcData + (row * srcBytesPerRow);
if (srcBitmapInfo & kCGBitmapByteOrder32Little)
{
for (size_t i=0; i<width; i++)
{
((uint32_t*)tempInput)[i] = GSSwapI32(((uint32_t*)(srcData + (row * srcBytesPerRow)))[i]);
}
input = tempInput;
}
for (size_t row = 0; row < height; row++)
{
const unsigned char *input = srcData + (row * srcBytesPerRow);
/*
if (needsByteSwap(srcBitmapInfo))
{
for (uint32_t *pixel = (uint32_t*)tempInput;
pixel < (uint32_t*)(tempInput + srcBytesPerRow);
pixel++)
{
*pixel = GSSwapI32(*pixel);
}
input = tempInput;
}
*/
[xform transformPixelData: input
output: dstData + (row * dstBytesPerRow)];
/*
if (needsByteSwap(dstBitmapInfo))
{
for (uint32_t *pixel = (uint32_t*) (dstData + (row * dstBytesPerRow));
pixel < (uint32_t*)(dstData + (row * dstBytesPerRow) + dstBytesPerRow);
pixel++)
{
*pixel = GSSwapI32(*pixel);
}
}
*/
}
[xform transformPixelData: input
output: dstData + (row * dstBytesPerRow)];
if (dstBitmapInfo & kCGBitmapByteOrder32Little)
{
for (uint32_t *pixel = (uint32_t*) (dstData + (row * dstBytesPerRow));
pixel < (uint32_t*) (dstData + (row * dstBytesPerRow) + dstBytesPerRow);
pixel++)
{
*pixel = GSSwapI32(*pixel);
}
}
}
free(tempInput);
}

View File

@ -24,17 +24,39 @@
#import "OPPremultiplyAlpha.h"
#define _OPPremultiplyAlpha(format, compType, compMax, rowStart, comps, pixels, unPremultiply) \
for (size_t pixel = 0; pixel<pixels; pixel++) \
#define _OPPremultiplyAlpha(isAlphaLast, compType, compMax, rowStart, comps, pixels) \
const size_t firstComp = (isAlphaLast ? 0 : 1); \
const size_t lastComp = (isAlphaLast ? (comps - 2) : (comps - 1)); \
const size_t alphaComp = (isAlphaLast ? (comps - 1) : 0); \
for (size_t pixel = 0; pixel < pixels; pixel++) \
{ \
compType *pixelPtr = (compType *)(rowStart + ((sizeof(compType)*comps) * pixel)); \
const size_t firstComp = (format.isAlphaLast ? 0 : 1); \
const size_t lastComp = (format.isAlphaLast ? (comps - 2) : (comps - 1)); \
const size_t alphaComp = (format.isAlphaLast ? (comps - 1) : 0); \
compType *pixelPtr = (compType *)(rowStart + ((sizeof(compType)*comps) * pixel)); \
const float alphaValue = pixelPtr[alphaComp] / (float)compMax; \
for (size_t i = firstComp; i<=lastComp; i++) \
for (size_t i = firstComp; i <= lastComp; i++) \
{ \
pixelPtr[i] *= (unPremultiply ? (1.0/alphaValue) : alphaValue); \
pixelPtr[i] *= alphaValue; \
} \
}
#define _OPUnPremultiplyAlpha(isAlphaLast, compType, compMax, rowStart, comps, pixels) \
const size_t firstComp = (isAlphaLast ? 0 : 1); \
const size_t lastComp = (isAlphaLast ? (comps - 2) : (comps - 1)); \
const size_t alphaComp = (isAlphaLast ? (comps - 1) : 0); \
for (size_t pixel = 0; pixel < pixels; pixel++) \
{ \
compType *pixelPtr = (compType *)(rowStart + ((sizeof(compType)*comps) * pixel)); \
const float alphaValue = pixelPtr[alphaComp] / (float)compMax; \
for (size_t i = firstComp; i <= lastComp; i++) \
{ \
const float val = (pixelPtr[i] / alphaValue); \
if (val > 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;
}
}

View File

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

68
Tests/doublebuffer.m Normal file
View File

@ -0,0 +1,68 @@
#ifdef __APPLE__
#include <ApplicationServices/ApplicationServices.h>
#else
#include <CoreGraphics/CoreGraphics.h>
#endif
#include <Foundation/Foundation.h>
/* 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);
}