mirror of
https://github.com/darlinghq/darling-coregraphics.git
synced 2024-11-23 04:09:42 +00:00
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:
parent
8269adcc02
commit
86e0cba223
14
ChangeLog
14
ChangeLog
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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++)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
68
Tests/doublebuffer.m
Normal 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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user