Opal: work in progress on color management.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/opal/trunk@31052 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
ericwa 2010-07-29 19:00:46 +00:00
parent b8237df007
commit 843c77be59
19 changed files with 1811 additions and 413 deletions

2
Resources/LICENSE Normal file
View File

@ -0,0 +1,2 @@
coated_FOGRA39L_argl.icc:
Copyright (c) 2008 Kai-Uwe Behrmann - http://www.opensource.org/licenses/zlib-license.php

Binary file not shown.

40
Source/CGColor-private.h Normal file
View File

@ -0,0 +1,40 @@
/** <title>CGColor</title>
<abstract>C Interface to graphics drawing library</abstract>
Copyright <copy>(C) 2006 Free Software Foundation, Inc.</copy>
Author: BALATON Zoltan <balaton@eik.bme.hu>
Date: 2006
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "CoreGraphics/CGColor.h"
@interface CGColor : NSObject
{
@public
CGColorSpaceRef cspace;
CGFloat *comps;
CGPatternRef pattern;
}
- (CGColor*) transformToColorSpace: (CGColorSpaceRef)space withRenderingIntent: (CGColorRenderingIntent)intent;
@end
CGColorRef OPColorGetTransformedToSpace(CGColorRef clr, CGColorSpaceRef space, CGColorRenderingIntent intent);

View File

@ -25,6 +25,10 @@
#include "CoreGraphics/CGContext.h"
#include "CoreGraphics/CGColor.h"
#import "CGColor-private.h"
#import "CGColorSpace-private.h"
#import "OPImageConversion.h"
const CFStringRef kCGColorWhite = @"kCGColorWhite";
const CFStringRef kCGColorBlack = @"kCGColorBlack";
const CFStringRef kCGColorClear = @"kCGColorClear";
@ -34,15 +38,6 @@ static CGColorRef _blackColor;
static CGColorRef _clearColor;
@interface CGColor : NSObject
{
@public
CGColorSpaceRef cspace;
CGFloat *comps;
CGPatternRef pattern;
}
@end
@implementation CGColor
- (id) initWithColorSpace: (CGColorSpaceRef)cs components: (const CGFloat*)components
@ -52,6 +47,7 @@ static CGColorRef _clearColor;
size_t nc, i;
nc = CGColorSpaceGetNumberOfComponents(cs);
NSLog(@"Create color with %d comps", nc);
self->comps = malloc((nc+1)*sizeof(CGFloat));
if (NULL == self->comps) {
NSLog(@"malloc failed");
@ -90,6 +86,57 @@ static CGColorRef _clearColor;
return YES;
}
- (CGColor*) transformToColorSpace: (CGColorSpaceRef)destSpace withRenderingIntent: (CGColorRenderingIntent)intent
{
CGColorSpaceRef sourceSpace = CGColorGetColorSpace(self);
// FIXME: this is ugly because CGColor uses CGFloats, but OPColorTransform only accepts
// 32-bit float components.
float originalComps[CGColorSpaceGetNumberOfComponents(sourceSpace) + 1];
float tranformedComps[CGColorSpaceGetNumberOfComponents(destSpace) + 1];
for (size_t i=0; i < CGColorSpaceGetNumberOfComponents(sourceSpace) + 1; i++)
{
originalComps[i] = comps[i];
}
OPImageFormat sourceFormat;
sourceFormat.compFormat = kOPComponentFormatFloat32bpc;
sourceFormat.colorComponents = CGColorSpaceGetNumberOfComponents(sourceSpace);
sourceFormat.hasAlpha = true;
sourceFormat.isAlphaPremultiplied = false;
sourceFormat.isAlphaLast = true;
OPImageFormat destFormat;
destFormat.compFormat = kOPComponentFormatFloat32bpc;
destFormat.colorComponents = CGColorSpaceGetNumberOfComponents(destSpace);
destFormat.hasAlpha = true;
destFormat.isAlphaPremultiplied = false;
destFormat.isAlphaLast = true;
OPColorTransform *xform = [sourceSpace colorTransformTo: destSpace
sourceFormat: sourceFormat
destinationFormat: destFormat
renderingIntent: intent
pixelCount: 1];
[xform transformPixelData: originalComps
output: tranformedComps];
// FIXME: hack, OPColorTransform doesn't yet copy the alpha
tranformedComps[CGColorSpaceGetNumberOfComponents(destSpace)] = CGColorGetAlpha(self);
CGFloat cgfloatTransformedComps[CGColorSpaceGetNumberOfComponents(destSpace) + 1];
for (size_t i=0; i < CGColorSpaceGetNumberOfComponents(destSpace) + 1; i++)
{
cgfloatTransformedComps[i] = tranformedComps[i];
}
// FIXME: release xform?
return [[[CGColor alloc] initWithColorSpace: destSpace components: cgfloatTransformedComps] autorelease];
}
@end
@ -213,7 +260,7 @@ CGColorRef CGColorGetConstantColor(CFStringRef name)
{
_blackColor = CGColorCreateGenericGray(0, 1);
}
return _whiteColor;
return _blackColor;
}
else if ([name isEqual: kCGColorClear])
{
@ -235,3 +282,8 @@ CGPatternRef CGColorGetPattern(CGColorRef clr)
{
return clr->pattern;
}
CGColorRef OPColorGetTransformedToSpace(CGColorRef clr, CGColorSpaceRef space, CGColorRenderingIntent intent)
{
return [clr transformToColorSpace: space withRenderingIntent: intent];
}

View File

@ -0,0 +1,95 @@
/** <title>CGColorSpace</title>
<abstract>C Interface to graphics drawing library</abstract>
Copyright <copy>(C) 2010 Free Software Foundation, Inc.</copy>
Author: Eric Wasylishen <ewasylishen@gmail.com>
Date: July, 2010
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import <Foundation/NSObject.h>
#include "CoreGraphics/CGColorSpace.h"
#import "OPImageConversion.h"
@interface OPColorTransform : NSObject
{
}
- (void) transformPixelData: (const unsigned char *)input
output: (char *)output;
@end
/**
* Abstract superclass for color spaces.
*/
@interface CGColorSpace : NSObject
{
}
+ (CGColorSpace *)colorSpaceGenericGray;
+ (CGColorSpace *)colorSpaceGenericRGB;
+ (CGColorSpace *)colorSpaceGenericCMYK;
+ (CGColorSpace *)colorSpaceGenericRGBLinear;
+ (CGColorSpace *)colorSpaceAdobeRGB1998;
+ (CGColorSpace *)colorSpaceSRGB;
+ (CGColorSpace *)colorSpaceGenericGrayGamma2_2;
- (NSData*)ICCProfile;
- (NSString*)name;
- (id)initWithCalibratedGrayWithWhitePoint: (const CGFloat*)whitePoint
blackPoint: (const CGFloat*)blackPoint
gamma: (CGFloat)gamma;
- (id)initWithCalibratedRGBWithWhitePoint: (const CGFloat*)whitePoint
blackPoint: (const CGFloat*)blackPoint
gamma: (const CGFloat *)gamma
matrix: (const CGFloat *)matrix;
- (id)initDeviceCMYK;
- (id)initDeviceGray;
- (id)initDeviceRGB;
- (id)initICCBasedWithComponents: (size_t)nComponents
range: (const CGFloat*)range
profile: (CGDataProviderRef)profile
alternateSpace: (CGColorSpaceRef)alternateSpace;
- (CGColorSpaceRef) initIndexedWithBaseSpace: (CGColorSpaceRef)baseSpace
lastIndex: (size_t)lastIndex
colorTable: (const unsigned char *)colorTable;
- (CGColorSpaceRef) initLabWithWhitePoint: (const CGFloat*)whitePoint
blackPoint: (const CGFloat*)blackPoint
range: (const CGFloat*)range;
- (CGColorSpaceRef) initPatternWithBaseSpace: (CGColorSpaceRef)baseSpace;
- (CGColorSpaceRef) initWithICCProfile: (CFDataRef)data;
- (CGColorSpaceRef) initWithName: (NSString *)name;
- (CGColorSpaceRef) initWithPlatformColorSpace: (void *)platformColorSpace;
- (CGColorSpaceRef) baseColorSpace;
- (void) getColorTable: (uint8_t*)table;
- (size_t) colorTableCount;
- (CGColorSpaceModel) model;
- (size_t) numberOfComponents;
- (OPColorTransform*) colorTransformTo: (CGColorSpace *)aColorSpace
sourceFormat: (OPImageFormat)aSourceFormat
destinationFormat: (OPImageFormat)aDestFormat
renderingIntent: (CGColorRenderingIntent)anIntent
pixelCount: (size_t)aPixelCount;
@end

View File

@ -2,8 +2,10 @@
<abstract>C Interface to graphics drawing library</abstract>
Copyright <copy>(C) 2006 Free Software Foundation, Inc.</copy>
Copyright <copy>(C) 2010 Free Software Foundation, Inc.</copy>
Author: Eric Wasylishen <ewasylishen@gmail.com>
Date: July, 2010
Author: BALATON Zoltan <balaton@eik.bme.hu>
Date: 2006
@ -22,17 +24,13 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* FIXME: Color Management is not implemented yet. Consequently, color spaces
* are usually ignored and assumed to be deviceRGB. With most current equipment
* supporting the sRGB color space, this may be OK in most cases, though.
*
* We should properly implement this once Cairo supports color management.
*/
#import <Foundation/Foundation.h>
#import <Foundation/NSString.h>
#include "CoreGraphics/CGColorSpace.h"
#import "CGColorSpace-private.h"
#import "OPColorSpaceIndexed.h"
const CFStringRef kCGColorSpaceGenericGray = @"kCGColorSpaceGenericGray";
const CFStringRef kCGColorSpaceGenericRGB = @"kCGColorSpaceGenericRGB";
const CFStringRef kCGColorSpaceGenericCMYK = @"kCGColorSpaceGenericCMYK";
@ -41,53 +39,13 @@ const CFStringRef kCGColorSpaceAdobeRGB1998 = @"kCGColorSpaceAdobeRGB1998";
const CFStringRef kCGColorSpaceSRGB = @"kCGColorSpaceSRGB";
const CFStringRef kCGColorSpaceGenericGrayGamma2_2 = @"kCGColorSpaceGenericGrayGamma2_2";
static void opal_todev_rgb(CGFloat *dest, const CGFloat comps[]);
static void opal_todev_gray(CGFloat *dest, const CGFloat comps[]);
static void opal_todev_cmyk(CGFloat *dest, const CGFloat comps[]);
static CGColorSpaceRef _deviceRGB;
static CGColorSpaceRef _deviceGray;
static CGColorSpaceRef _deviceCMYK;
@interface CGColorSpace : NSObject
{
@public
CGColorSpaceRef cspace;
int numcomps;
CGColorSpaceModel model;
void (*todevice)(CGFloat *dest, const CGFloat comps[]);
}
@end
@implementation CGColorSpace
+ (void) load
{
_deviceRGB = [[CGColorSpace alloc] init];
_deviceRGB->numcomps = 3;
_deviceRGB->model = kCGColorSpaceModelRGB;
_deviceRGB->todevice = opal_todev_rgb;
_deviceGray = [[CGColorSpace alloc] init];
_deviceGray->numcomps = 1;
_deviceGray->model = kCGColorSpaceModelMonochrome;
_deviceGray->todevice = opal_todev_gray;
_deviceCMYK = [[CGColorSpace alloc] init];
_deviceCMYK->numcomps = 4;
_deviceCMYK->model = kCGColorSpaceModelCMYK;
_deviceCMYK->todevice = opal_todev_cmyk;
}
- (id) retain
{
return self;
}
- (void) release
+ (Class) colorSpaceClass
{
// FIXME:
return NSClassFromString(@"OPColorSpaceLCMS");
}
- (void) dealloc
@ -95,169 +53,61 @@ static CGColorSpaceRef _deviceCMYK;
[super dealloc];
}
- (BOOL) isEqual: (id)other
{
if (![other isKindOfClass: [CGColorSpace class]])
{
return NO;
}
CGColorSpace *otherCS = (CGColorSpace *)other;
return (otherCS->numcomps == self->numcomps
&& otherCS->model == self->model
&& otherCS->cspace == self->cspace
&& otherCS->todevice == self->todevice); // FIXME: will not accomodate all colorspace types
}
@end
CFDataRef CGColorSpaceCopyICCProfile(CGColorSpaceRef cs)
{
return nil;
}
CFStringRef CGColorSpaceCopyName(CGColorSpaceRef cs)
{
return nil;
}
CGColorSpaceRef CGColorSpaceCreateCalibratedGray(
const CGFloat *whitePoint,
const CGFloat *blackPoint,
CGFloat gamma)
{
return _deviceGray;
}
CGColorSpaceRef CGColorSpaceCreateCalibratedRGB(
const CGFloat *whitePoint,
const CGFloat *blackPoint,
const CGFloat *gamma,
const CGFloat *matrix)
{
return _deviceRGB;
}
CGColorSpaceRef CGColorSpaceCreateDeviceCMYK()
{
return _deviceCMYK;
}
CGColorSpaceRef CGColorSpaceCreateDeviceGray()
{
return _deviceGray;
}
CGColorSpaceRef CGColorSpaceCreateDeviceRGB()
{
return _deviceRGB;
}
CGColorSpaceRef CGColorSpaceCreateICCBased(
size_t nComponents,
const CGFloat *range,
CGDataProviderRef profile,
CGColorSpaceRef alternateSpace)
{
return _deviceRGB;
}
CGColorSpaceRef CGColorSpaceCreateIndexed(
CGColorSpaceRef baseSpace,
size_t lastIndex,
const unsigned char *colorTable)
{
return _deviceRGB;
}
CGColorSpaceRef CGColorSpaceCreateLab(
const CGFloat *whitePoint,
const CGFloat *blackPoint,
const CGFloat *range)
{
return _deviceRGB;
}
CGColorSpaceRef CGColorSpaceCreatePattern(CGColorSpaceRef baseSpace)
{
return baseSpace;
}
CGColorSpaceRef CGColorSpaceCreateWithICCProfile(CFDataRef data)
{
return _deviceRGB;
}
CGColorSpaceRef CGColorSpaceCreateWithName(CFStringRef name)
{
if ([name isEqualToString: kCGColorSpaceGenericGray])
{
return CGColorSpaceCreateDeviceGray();
}
else if ([name isEqualToString: kCGColorSpaceGenericRGB])
{
return CGColorSpaceCreateDeviceRGB();
}
else if ([name isEqualToString: kCGColorSpaceGenericCMYK])
{
return CGColorSpaceCreateDeviceCMYK();
}
else
{
NSLog(@"Unknown colorspace name");
}
return nil;
}
CGColorSpaceRef CGColorSpaceCreateWithPlatformColorSpace(
void *platformColorSpace)
{
return _deviceRGB;
}
CGColorSpaceRef CGColorSpaceGetBaseColorSpace(CGColorSpaceRef cs)
{
return cs;
}
void CGColorSpaceGetColorTable(CGColorSpaceRef cs, unsigned char *table)
- (void) getColorTable: (uint8_t*)table
{
}
size_t CGColorSpaceGetColorTableCount(CGColorSpaceRef cs)
- (size_t) colorTableCount
{
return 0;
}
CGColorSpaceModel CGColorSpaceGetModel(CGColorSpaceRef cs)
+ (CGColorSpaceRef) createWithName: (NSString *)name
{
return cs->model;
if ([name isEqualToString: kCGColorSpaceGenericGray])
{
return [[[self class] colorSpaceGenericGray] retain];
}
else if ([name isEqualToString: kCGColorSpaceGenericRGB])
{
return [[[self class] colorSpaceGenericRGB] retain];
}
else if ([name isEqualToString: kCGColorSpaceGenericCMYK])
{
return [[[self class] colorSpaceGenericCMYK] retain];
}
else if ([name isEqualToString: kCGColorSpaceGenericRGBLinear])
{
return [[[self class] colorSpaceGenericRGBLinear] retain];
}
else if ([name isEqualToString: kCGColorSpaceAdobeRGB1998])
{
return [[[self class] colorSpaceAdobeRGB1998] retain];
}
else if ([name isEqualToString: kCGColorSpaceSRGB])
{
return [[[self class] colorSpaceSRGB] retain];
}
else if ([name isEqualToString: kCGColorSpaceGenericGrayGamma2_2])
{
return [[[self class] colorSpaceGenericGrayGamma2_2] retain];
}
else
{
return nil;
}
}
size_t CGColorSpaceGetNumberOfComponents(CGColorSpaceRef cs)
{
return cs->numcomps;
}
@end
CFTypeID CGColorSpaceGetTypeID()
{
return (CFTypeID)[CGColorSpace class];
}
CGColorSpaceRef CGColorSpaceRetain(CGColorSpaceRef cs)
{
return [cs retain];
}
void CGColorSpaceRelease(CGColorSpaceRef cs)
{
[cs release];
}
@implementation OPColorTransform
@end
/**
* This is a fallback only used when building Opal without LittleCMS.
* Note that it doesn't do any color management.
*/
#if 0
static void opal_todev_rgb(CGFloat *dest, const CGFloat comps[])
{
dest[0] = comps[0];
@ -284,9 +134,149 @@ static void opal_todev_cmyk(CGFloat *dest, const CGFloat comps[])
dest[3] = comps[4];
}
/* FIXME: This sould really convert to the color space of the device,
* but Cairo only knows about RGBA, so we convert to that */
void opal_cspace_todev(CGColorSpaceRef cs, CGFloat *dest, const CGFloat comps[])
#endif
CFDataRef CGColorSpaceCopyICCProfile(CGColorSpaceRef cs)
{
cs->todevice(dest, comps);
return [cs IICProfile];
}
CFStringRef CGColorSpaceCopyName(CGColorSpaceRef cs)
{
return [cs name];
}
CGColorSpaceRef CGColorSpaceCreateCalibratedGray(
const CGFloat *whitePoint,
const CGFloat *blackPoint,
CGFloat gamma)
{
return [[[CGColorSpace colorSpaceClass] alloc]
initWithCalibratedGrayWithWhitePoint: whitePoint
blackPoint: blackPoint
gamma: gamma];
}
CGColorSpaceRef CGColorSpaceCreateCalibratedRGB(
const CGFloat *whitePoint,
const CGFloat *blackPoint,
const CGFloat *gamma,
const CGFloat *matrix)
{
return [[[CGColorSpace colorSpaceClass] alloc]
initWithCalibratedRGBWithWhitePoint: whitePoint
blackPoint: blackPoint
gamma: gamma
matrix: matrix];
}
CGColorSpaceRef CGColorSpaceCreateDeviceCMYK()
{
return [[[CGColorSpace colorSpaceClass] colorSpaceGenericCMYK] retain];
}
CGColorSpaceRef CGColorSpaceCreateDeviceGray()
{
return [[[CGColorSpace colorSpaceClass] colorSpaceGenericGray] retain];
}
CGColorSpaceRef CGColorSpaceCreateDeviceRGB()
{
return [[[CGColorSpace colorSpaceClass] colorSpaceSRGB] retain];
}
CGColorSpaceRef CGColorSpaceCreateICCBased(
size_t nComponents,
const CGFloat *range,
CGDataProviderRef profile,
CGColorSpaceRef alternateSpace)
{
return [[[CGColorSpace colorSpaceClass] alloc] initICCBasedWithComponents: nComponents
range: range
profile: profile
alternateSpace: alternateSpace];
}
CGColorSpaceRef CGColorSpaceCreateIndexed(
CGColorSpaceRef baseSpace,
size_t lastIndex,
const unsigned char *colorTable)
{
return [[OPColorSpaceIndexed alloc] initWithBaseSpace: baseSpace
lastIndex: lastIndex
colorTable: colorTable];
}
CGColorSpaceRef CGColorSpaceCreateLab(
const CGFloat *whitePoint,
const CGFloat *blackPoint,
const CGFloat *range)
{
return [[[CGColorSpace colorSpaceClass] alloc] initLabWithWhitePoint: whitePoint
blackPoint: blackPoint
range: range];
}
CGColorSpaceRef CGColorSpaceCreatePattern(CGColorSpaceRef baseSpace)
{
return [[[CGColorSpace colorSpaceClass] alloc] initPatternWithBaseSpace: baseSpace];
}
CGColorSpaceRef CGColorSpaceCreateWithICCProfile(CFDataRef data)
{
return [[[CGColorSpace colorSpaceClass] alloc] initWithICCProfile: data];
}
CGColorSpaceRef CGColorSpaceCreateWithName(CFStringRef name)
{
return [[CGColorSpace colorSpaceClass] createWithName: name];
}
CGColorSpaceRef CGColorSpaceCreateWithPlatformColorSpace(
void *platformColorSpace)
{
return [[[CGColorSpace colorSpaceClass] alloc] initWithPlatformColorSpace: platformColorSpace];
}
CGColorSpaceRef CGColorSpaceGetBaseColorSpace(CGColorSpaceRef cs)
{
return [cs baseColorSpace];
}
void CGColorSpaceGetColorTable(CGColorSpaceRef cs, unsigned char *table)
{
[cs getColorTable: table];
}
size_t CGColorSpaceGetColorTableCount(CGColorSpaceRef cs)
{
return [cs colorTableCount];
}
CGColorSpaceModel CGColorSpaceGetModel(CGColorSpaceRef cs)
{
return [cs model];
}
size_t CGColorSpaceGetNumberOfComponents(CGColorSpaceRef cs)
{
return [cs numberOfComponents];
}
CFTypeID CGColorSpaceGetTypeID()
{
return (CFTypeID)[CGColorSpace class];
}
CGColorSpaceRef CGColorSpaceRetain(CGColorSpaceRef cs)
{
return [cs retain];
}
void CGColorSpaceRelease(CGColorSpaceRef cs)
{
[cs release];
}

View File

@ -24,12 +24,13 @@
#include "CoreGraphics/CGGeometry.h"
#include "CGContext-private.h"
#include "CGGradient-private.h"
#include <math.h>
#include <cairo.h>
#import "CGContext-private.h"
#import "CGGradient-private.h"
#import "CGColor-private.h"
#import "cairo/CairoFont.h"
/* The default (opaque black) color in a Cairo context,
@ -37,7 +38,6 @@
static cairo_pattern_t *default_cp;
extern void opal_surface_flush(cairo_surface_t *target);
extern void opal_cspace_todev(CGColorSpaceRef cs, CGFloat *dest, const CGFloat comps[]);
extern cairo_surface_t *opal_CGImageGetSurfaceForImage(CGImageRef img);
extern CGRect opal_CGImageGetSourceRect(CGImageRef image);
@ -784,14 +784,17 @@ CGRect CGContextGetClipBoundingBox(CGContextRef ctx)
static inline void set_color(cairo_pattern_t **cp, CGColorRef clr, double alpha)
{
CGFloat cc[4];
// FIXME: check why this might be called with a NULL clr
if (!clr) return;
cairo_pattern_t *newcp;
cairo_status_t cret;
opal_cspace_todev(CGColorGetColorSpace(clr), cc, CGColorGetComponents(clr));
CGColorSpaceRef srgb = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
CGColorRef srgbClr = OPColorGetTransformedToSpace(clr, srgb, kCGRenderingIntentRelativeColorimetric);
CGColorSpaceRelease(srgb);
CGFloat *cc = CGColorGetComponents(srgbClr);
NSLog(@"Set color with %f %f %f %f", (float)cc[0], (float)cc[1], (float)cc[2], (float)cc[3]*alpha);
newcp = cairo_pattern_create_rgba(cc[0], cc[1], cc[2], cc[3]*alpha);
cret = cairo_pattern_status(newcp);
if (cret) {
@ -799,7 +802,11 @@ static inline void set_color(cairo_pattern_t **cp, CGColorRef clr, double alpha)
cairo_status_to_string(cret));
return;
}
cairo_pattern_destroy(*cp);
if (*cp != NULL)
{
cairo_pattern_destroy(*cp);
}
*cp = newcp;
}

View File

@ -29,7 +29,8 @@
#import <Foundation/NSDictionary.h>
#include <stdlib.h>
#include <cairo.h>
#include "image/OPImageConversion.h"
#import "OPImageConversion.h"
@interface CGImage : NSObject
@ -95,14 +96,22 @@
const size_t numComponentsIncludingAlpha = numComponents + (hasAlpha ? 1 : 0);
if (aBitsPerComponent < 1)
if (aBitsPerComponent < 1 || aBitsPerComponent > 32)
{
NSLog(@"Unsupported bitsPerComponent: %d", aBitsPerComponent);
[self release];
return nil;
}
if ((aBitmapInfo & kCGBitmapFloatComponents) != 0 && aBitsPerComponent != 32)
{
NSLog(@"Only 32 bitsPerComponents supported for float components");
[self release];
return nil;
}
if (aBitsPerPixel < aBitsPerComponent * numComponentsIncludingAlpha)
{
// Note if an alpha channel is requrested, we require it to be the same size
// as the other components
NSLog(@"Too few bitsPerPixel for bitsPerComponent");
[self release];
return nil;
@ -469,7 +478,7 @@ cairo_surface_t *opal_CGImageGetSurfaceForImage(CGImageRef img)
const CGBitmapInfo dstBitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst;
const CGColorSpaceRef dstColorSpace = srcColorSpace;
bool ok = OPImageConvert(
OPImageConvert(
dstData, srcData,
srcWidth, srcHeight,
dstBitsPerComponent, srcBitsPerComponent,
@ -482,13 +491,6 @@ cairo_surface_t *opal_CGImageGetSurfaceForImage(CGImageRef img)
OPDataProviderReleaseBytePointer(img->dp, srcData);
cairo_surface_mark_dirty(img->surf); // done modifying the surface outside of cairo
if (!ok)
{
cairo_surface_destroy(img->surf);
img->surf = NULL;
NSLog(@"Image conversion to cairo surface failed.");
}
}
return img->surf;

View File

@ -0,0 +1,61 @@
/** <title>OPColorSpaceIndexed</title>
<abstract>C Interface to graphics drawing library</abstract>
Copyright <copy>(C) 2010 Free Software Foundation, Inc.</copy>
Author: Eric Wasylishen <ewasylishen@gmail.com>
Date: July, 2010
Author: BALATON Zoltan <balaton@eik.bme.hu>
Date: 2006
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "CoreGraphics/CGColorSpace.h"
#import "CGColorSpace-private.h"
@interface OPColorSpaceIndexed : CGColorSpace
{
@public
CGColorSpaceRef base;
size_t lastIndex;
const unsigned char *table;
}
- (id)initWithBaseSpace: (CGColorSpaceRef)aBaseSpace
lastIndex: (size_t)aLastIndex
colorTable: (const unsigned char *)aColorTable;
- (size_t) tableSize;
- (void) getColorTable: (uint8_t*)table;
- (size_t) colorTableCount;
- (OPColorTransform*) colorTransformTo: (CGColorSpace *)otherColor
sourceFormat: (OPImageFormat)sourceFormat
destinationFormat: (OPImageFormat)destFormat;
@end
@interface OPColorTransformIndexed : OPColorTransform
{
OPColorTransform *baseTransform;
}
- (void) transformPixelData: (const unsigned char *)input
output: (char *)output;
@end

View File

@ -0,0 +1,120 @@
/** <title>OPColorSpaceIndexed</title>
<abstract>C Interface to graphics drawing library</abstract>
Copyright <copy>(C) 2010 Free Software Foundation, Inc.</copy>
Author: Eric Wasylishen <ewasylishen@gmail.com>
Date: July, 2010
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import <Foundation/Foundation.h>
#import "OPColorSpaceIndexed.h"
@implementation OPColorSpaceIndexed
- (id)initWithBaseSpace: (CGColorSpaceRef)aBaseSpace
lastIndex: (size_t)aLastIndex
colorTable: (const unsigned char *)aColorTable
{
self = [super init];
ASSIGN(base, aBaseSpace);
lastIndex = aLastIndex;
table = malloc([self tableSize]);
if (NULL == table)
{
[self release];
return nil;
}
memmove(table, aColorTable, [self tableSize]);
return self;
}
- (size_t) tableSize
{
return (lastIndex + 1) * CGColorSpaceGetNumberOfComponents(base);
}
- (void) dealloc
{
free(table);
CGColorSpaceRelease(base);
[super dealloc];
}
- (NSData*)ICCProfile
{
return [base IICProfile]; // FIXME: ???
}
- (NSString*)name
{
return [base name]; // FIXME: ???
}
- (CGColorSpaceRef) baseColorSpace
{
return base;
}
- (size_t) numberOfComponents
{
return [base numberOfComponents];
}
- (void) getColorTable: (uint8_t*)table
{
memmove(table, self->table, [self tableSize]);
}
- (size_t) colorTableCount
{
return self->lastIndex + 1;
}
- (CGColorSpaceModel) model
{
return [base model];
}
- (BOOL) isEqual: (id)other
{
if ([other isKindOfClass: [OPColorSpaceIndexed class]])
{
OPColorSpaceIndexed *otherIndexed = (OPColorSpaceIndexed*)other;
return [self->base isEqual: otherIndexed->base]
&& self->lastIndex == otherIndexed->lastIndex
&& (0 == memcmp(otherIndexed->table,
self->table,
[self tableSize]));
}
return NO;
}
- (OPColorTransform*) colorTransformTo: (CGColorSpace *)otherColor
sourceFormat: (OPImageFormat)sourceFormat
destinationFormat: (OPImageFormat)destFormat
{
return [[[OPColorTransformIndexed alloc] init] autorelease];
}
@end
@implementation OPColorTransformIndexed
- (void) transformPixelData: (const unsigned char *)input
output: (char *)output
pixelCount: (size_t)count
{
// FIXME:
}
@end

131
Source/OPColorSpaceLCMS.h Normal file
View File

@ -0,0 +1,131 @@
/** <title>OPColorSpaceLCMS</title>
<abstract>C Interface to graphics drawing library</abstract>
Copyright <copy>(C) 2010 Free Software Foundation, Inc.</copy>
Author: Eric Wasylishen <ewasylishen@gmail.com>
Date: July, 2010
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <lcms.h>
#include "CoreGraphics/CGColorSpace.h"
#import "CGColorSpace-private.h"
@interface OPColorSpaceLCMS : CGColorSpace
{
@public
cmsHPROFILE profile;
NSData *data;
}
/**
* Returns a generic RGB color space
*/
+ (CGColorSpace *)colorSpaceGenericRGB;
/**
* Returns a generic RGB color space with a gamma of 1.0
*/
+ (CGColorSpace *)colorSpaceGenericRGBLinear;
/**
* Returns a CMYK colorspace following the FOGRA39L specification.
*/
+ (CGColorSpace *)colorSpaceGenericCMYK;
/**
* Returns an Adobe RGB compatible color space
*/
+ (CGColorSpace *)colorSpaceAdobeRGB1998;
/**
* Returns an sRGB compatible color space
*/
+ (CGColorSpace *)colorSpaceSRGB;
/**
* Returns a grayscale color space with a D65 white point
*/
+ (CGColorSpace *)colorSpaceGenericGray;
/**
* Returns a grayscale color space with gamma 2.2 and a D65 white point
*/
+ (CGColorSpace *)colorSpaceGenericGrayGamma2_2;
// Initializers
/**
* Creates a color space with the given LCMS profile. Takes ownership of the profile
* (releases it when done.)
*/
- (id)initWithProfile: (cmsHPROFILE)profile;
/**
* Returns ICC profile data for color spaces created from ICC profiles, otherwise nil
*/
- (NSData*)ICCProfile;
- (NSString*)name;
- (id)initWithCalibratedGrayWithWhitePoint: (const CGFloat*)whiteCIEXYZ
blackPoint: (const CGFloat*)blackCIEXYZ
gamma: (CGFloat)gamma;
- (id)initWithCalibratedRGBWithWhitePointCIExy: (const CGFloat*)white
redCIExy: (const CGFloat*)red
greenCIExy: (const CGFloat*)green
blueCIExy: (const CGFloat*)blue
gamma: (CGFloat)gamma;
- (id)initWithCalibratedRGBWithWhitePoint: (const CGFloat*)whitePoint
blackPoint: (const CGFloat*)blackPoint
gamma: (const CGFloat *)gamma
matrix: (const CGFloat *)matrix;
- (id)initICCBasedWithComponents: (size_t)nComponents
range: (const CGFloat*)range
profile: (CGDataProviderRef)profile
alternateSpace: (CGColorSpaceRef)alternateSpace;
- (CGColorSpaceRef) initIndexedWithBaseSpace: (CGColorSpaceRef)baseSpace
lastIndex: (size_t)lastIndex
colorTable: (const unsigned char *)colorTable;
- (CGColorSpaceRef) initLabWithWhitePoint: (const CGFloat*)whitePoint
blackPoint: (const CGFloat*)blackPoint
range: (const CGFloat*)range;
- (CGColorSpaceRef) initWithICCProfile: (CFDataRef)profileData;
- (CGColorSpaceRef) initWithPlatformColorSpace: (void *)platformColorSpace;
- (CGColorSpaceModel) model;
- (size_t) numberOfComponents;
- (OPColorTransform*) colorTransformTo: (CGColorSpace *)aColorSpace
sourceFormat: (OPImageFormat)aSourceFormat
destinationFormat: (OPImageFormat)aDestFormat
renderingIntent: (CGColorRenderingIntent)anIntent
pixelCount: (size_t)aPixelCount;
@end

326
Source/OPColorSpaceLCMS.m Normal file
View File

@ -0,0 +1,326 @@
/** <title>OPColorSpaceLCMS</title>
<abstract>C Interface to graphics drawing library</abstract>
Copyright <copy>(C) 2010 Free Software Foundation, Inc.</copy>
Author: Eric Wasylishen <ewasylishen@gmail.com>
Date: July, 2010
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <lcms.h>
#include "CoreGraphics/CGColorSpace.h"
#import "OPColorSpaceLCMS.h"
#import "CGColorSpace-private.h"
#import "OPColorTransformLCMS.h"
@implementation OPColorSpaceLCMS
static CGColorSpace *colorSpaceSRGB;
static CGColorSpace *colorSpaceGenericRGBLinear;
static CGColorSpace *colorSpaceGenericCMYK;
static CGColorSpace *colorSpaceAdobeRGB1998;
static CGColorSpace *colorSpaceGenericGrayGamma2_2;
/**
* Returns a generic RGB color space
*/
+ (CGColorSpace *)colorSpaceGenericRGB
{
return [self colorSpaceSRGB];
}
/**
* Returns a generic RGB color space with a gamma of 1.0
*/
+ (CGColorSpace *)colorSpaceGenericRGBLinear
{
if (nil == colorSpaceGenericRGBLinear)
{
// Use the sRGB white point and primaries with a gamma of 1.0
CGFloat whiteCIExy[2] = {0.3127, 0.3290};
CGFloat redCIExy[2] = {0.64, 0.33};
CGFloat greenCIExy[2] = {0.30, 0.60};
CGFloat blueCIExy[2] = {0.15, 0.06};
colorSpaceGenericRGBLinear = [[OPColorSpaceLCMS alloc]
initWithCalibratedRGBWithWhitePointCIExy: whiteCIExy
redCIExy: redCIExy
greenCIExy: greenCIExy
blueCIExy: blueCIExy
gamma: 1.0];
}
return colorSpaceGenericRGBLinear;
}
/**
* Returns a CMYK colorspace following the FOGRA39L specification.
*/
+ (CGColorSpace *)colorSpaceGenericCMYK
{
if (nil == colorSpaceGenericCMYK)
{
NSString *path = [[NSBundle bundleForClass: [self class]]
pathForResource: @"coated_FOGRA39L_argl"
ofType: @"icc"];
NSData *data = [NSData dataWithContentsOfFile: path];
colorSpaceGenericCMYK = [[OPColorSpaceLCMS alloc] initWithICCProfile: data];
}
return colorSpaceGenericCMYK;
}
/**
* Returns an Adobe RGB compatible color space
*/
+ (CGColorSpace *)colorSpaceAdobeRGB1998
{
if (nil == colorSpaceAdobeRGB1998)
{
CGFloat whiteCIExy[2] = {0.3127, 0.3290};
CGFloat redCIExy[2] = {0.6400, 0.3300};
CGFloat greenCIExy[2] = {0.2100, 0.7100};
CGFloat blueCIExy[2] = {0.1500, 0.0600};
colorSpaceAdobeRGB1998 = [[OPColorSpaceLCMS alloc]
initWithCalibratedRGBWithWhitePointCIExy: whiteCIExy
redCIExy: redCIExy
greenCIExy: greenCIExy
blueCIExy: blueCIExy
gamma: (563.0/256.0)];
}
return colorSpaceAdobeRGB1998;
}
/**
* Returns an sRGB compatible color space
*/
+ (CGColorSpace *)colorSpaceSRGB
{
if (nil == colorSpaceSRGB)
{
colorSpaceSRGB = [[OPColorSpaceLCMS alloc] initWithProfile: cmsCreate_sRGBProfile()];
}
return colorSpaceSRGB;
}
/**
* Returns a grayscale color space with a D65 white point
*/
+ (CGColorSpace *)colorSpaceGenericGray
{
return [self colorSpaceGenericGrayGamma2_2];
}
/**
* Returns a grayscale color space with gamma 2.2 and a D65 white point
*/
+ (CGColorSpace *)colorSpaceGenericGrayGamma2_2
{
if (nil == colorSpaceGenericGrayGamma2_2)
{
CGFloat whiteCIEXYZ[3] = {0.9504, 1.0000, 1.0888};
CGFloat blackCIEXYZ[3] = {0, 0, 0};
colorSpaceGenericGrayGamma2_2 = [[OPColorSpaceLCMS alloc] initWithCalibratedGrayWithWhitePoint: whiteCIEXYZ
blackPoint: blackCIEXYZ
gamma: 2.2];
}
return colorSpaceGenericGrayGamma2_2;
}
// Initializers
/**
* Creates a color space with the given LCMS profile. Takes ownership of the profile
* (releases it when done.)
*/
- (id)initWithProfile: (cmsHPROFILE)aProfile
{
self = [super init];
self->profile = aProfile;
return self;
}
/**
* Returns ICC profile data for color spaces created from ICC profiles, otherwise nil
*/
- (NSData*)ICCProfile
{
return data;
}
- (NSString*)name
{
return [NSString stringWithUTF8String: cmsTakeProductName(self->profile)];
}
static inline cmsCIExyY CIExyzToCIExyY(const CGFloat point[3])
{
// LittleCMS docs say Y is always 1
cmsCIExyY xyY = {point[0], point[1], 1.0};
return xyY;
}
static inline cmsCIExyY CIEXYZToCIExyY(const CGFloat point[3])
{
cmsCIEXYZ XYZ = {point[0], point[1], point[2]};
cmsCIExyY xyY;
cmsXYZ2xyY(&xyY, &XYZ);
return xyY;
}
- (id)initWithCalibratedGrayWithWhitePoint: (const CGFloat*)whiteCIEXYZ
blackPoint: (const CGFloat*)blackCIEXYZ
gamma: (CGFloat)gamma
{
self = [super init];
// NOTE: we ignore the black point; LCMS computes it on its own
LPGAMMATABLE table = cmsBuildGamma(256, gamma);
cmsCIExyY whiteCIExyY = CIEXYZToCIExyY(whiteCIEXYZ);
self->profile = cmsCreateGrayProfile(&whiteCIExyY, table);
cmsFreeGamma(table);
return self;
}
- (id)initWithCalibratedRGBWithWhitePointCIExy: (const CGFloat*)white
redCIExy: (const CGFloat*)red
greenCIExy: (const CGFloat*)green
blueCIExy: (const CGFloat*)blue
gamma: (CGFloat)gamma
{
self = [super init];
LPGAMMATABLE tables[3] = {cmsBuildGamma(256, gamma),
cmsBuildGamma(256, gamma),
cmsBuildGamma(256, gamma)};
cmsCIExyY whitePoint = {white[0], white[1], 1.0};
cmsCIExyYTRIPLE primaries = {
{red[0], red[1], 1.0},
{green[0], green[1], 1.0},
{blue[0], blue[1], 1.0}
};
self->profile = cmsCreateRGBProfile(&whitePoint, &primaries, tables);
cmsFreeGamma(tables[0]);
cmsFreeGamma(tables[1]);
cmsFreeGamma(tables[2]);
return self;
}
- (id)initWithCalibratedRGBWithWhitePoint: (const CGFloat*)whitePoint
blackPoint: (const CGFloat*)blackPoint
gamma: (const CGFloat *)gamma
matrix: (const CGFloat *)matrix
{
self = [super init];
// NOTE: we ignore the black point; LCMS computes it on its own
LPGAMMATABLE tables[3] = {cmsBuildGamma(256, gamma[0]),
cmsBuildGamma(256, gamma[1]),
cmsBuildGamma(256, gamma[2])};
// FIXME: I'm not 100% sure this is the correct interpretation of matrix
// We can test it by checking the results in pdf manual vs doing a trasformation
// with LCMS to XYZ.
cmsCIExyYTRIPLE primaries;
primaries.Red = CIEXYZToCIExyY(matrix);
primaries.Green = CIEXYZToCIExyY(matrix+3);
primaries.Blue = CIEXYZToCIExyY(matrix+6);
cmsCIExyY whitePointCIExyY = CIEXYZToCIExyY(whitePoint);
self->profile = cmsCreateRGBProfile(&whitePointCIExyY, &primaries, tables);
cmsFreeGamma(tables[0]);
cmsFreeGamma(tables[1]);
cmsFreeGamma(tables[2]);
return self;
}
- (id)initICCBasedWithComponents: (size_t)nComponents
range: (const CGFloat*)range
profile: (CGDataProviderRef)profile
alternateSpace: (CGColorSpaceRef)alternateSpace
{
return nil;
}
- (CGColorSpaceRef) initLabWithWhitePoint: (const CGFloat*)whitePoint
blackPoint: (const CGFloat*)blackPoint
range: (const CGFloat*)range
{
return nil;
}
- (CGColorSpaceRef) initWithICCProfile: (CFDataRef)profileData
{
self = [super init];
self->data = [profileData retain];
self->profile = cmsOpenProfileFromMem([profileData bytes], [profileData length]);
return self;
}
- (CGColorSpaceRef) initWithPlatformColorSpace: (void *)platformColorSpace
{
[self release];
return nil;
}
static CGColorSpaceModel CGColorSpaceModelForSignature(icColorSpaceSignature sig)
{
switch (sig)
{
case icSigGrayData:
return kCGColorSpaceModelMonochrome;
case icSigRgbData:
return kCGColorSpaceModelRGB;
case icSigCmykData:
return kCGColorSpaceModelCMYK;
case icSigLabData:
return kCGColorSpaceModelLab;
default:
return kCGColorSpaceModelUnknown;
}
}
- (CGColorSpaceModel) model
{
return CGColorSpaceModelForSignature(cmsGetColorSpace(profile));
}
- (size_t) numberOfComponents
{
return _cmsChannelsOf(cmsGetColorSpace(profile));
}
- (OPColorTransform*) colorTransformTo: (CGColorSpace *)aColorSpace
sourceFormat: (OPImageFormat)aSourceFormat
destinationFormat: (OPImageFormat)aDestFormat
renderingIntent: (CGColorRenderingIntent)anIntent
pixelCount: (size_t)aPixelCount
{
return [[OPColorTransformLCMS alloc]
initWithSourceSpace: self
destinationSpace: aColorSpace
sourceFormat: aSourceFormat
destinationFormat: aDestFormat
renderingIntent: anIntent
pixelCount: aPixelCount];
}
@end

View File

@ -0,0 +1,52 @@
/** <title>OPColorTransformLCMS</title>
<abstract>C Interface to graphics drawing library</abstract>
Copyright <copy>(C) 2010 Free Software Foundation, Inc.</copy>
Author: Eric Wasylishen <ewasylishen@gmail.com>
Date: July, 2010
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <lcms.h>
#include "CoreGraphics/CGColorSpace.h"
#import "CGColorSpace-private.h"
#import "OPColorSpaceLCMS.h"
@interface OPColorTransformLCMS : OPColorTransform
{
cmsHTRANSFORM xform;
CGColorSpace *source, *dest;
OPImageFormat sourceFormat, destFormat;
CGColorRenderingIntent renderingIntent;
size_t pixelCount;
char *tempBuffer1, *tempBuffer2;
}
- (id) initWithSourceSpace: (OPColorSpaceLCMS *)aSourceSpace
destinationSpace: (OPColorSpaceLCMS *)aDestSpace
sourceFormat: (OPImageFormat)aSourceFormat
destinationFormat: (OPImageFormat)aDestFormat
renderingIntent: (CGColorRenderingIntent)anIntent
pixelCount: (size_t)aPixelCount;
- (void) transformPixelData: (const unsigned char *)input
output: (char *)output;
@end

View File

@ -0,0 +1,320 @@
/** <title>OPColorTransformLCMS</title>
<abstract>C Interface to graphics drawing library</abstract>
Copyright <copy>(C) 2010 Free Software Foundation, Inc.</copy>
Author: Eric Wasylishen <ewasylishen@gmail.com>
Date: July, 2010
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <lcms.h>
#include "CoreGraphics/CGColorSpace.h"
#import "CGColorSpace-private.h"
#import "OPColorTransformLCMS.h"
#import "OPPremultiplyAlpha.h"
@implementation OPColorTransformLCMS
static int LcmsIntentForCGColorRenderingIntent(CGColorRenderingIntent intent)
{
switch (intent)
{
default:
case kCGRenderingIntentDefault:
return INTENT_RELATIVE_COLORIMETRIC; // FIXME: Check a user default
case kCGRenderingIntentAbsoluteColorimetric:
return INTENT_ABSOLUTE_COLORIMETRIC;
case kCGRenderingIntentRelativeColorimetric:
return INTENT_RELATIVE_COLORIMETRIC;
case kCGRenderingIntentPerceptual:
return INTENT_PERCEPTUAL;
case kCGRenderingIntentSaturation:
return INTENT_SATURATION;
}
}
static int LcmsPixelTypeForCGColorSpaceModel(CGColorSpaceModel model)
{
switch (model)
{
case kCGColorSpaceModelMonochrome:
return PT_GRAY;
case kCGColorSpaceModelRGB:
return PT_RGB;
case kCGColorSpaceModelCMYK:
return PT_CMYK;
case kCGColorSpaceModelLab:
return PT_Lab;
case kCGColorSpaceModelUnknown:
case kCGColorSpaceModelDeviceN:
case kCGColorSpaceModelIndexed:
case kCGColorSpaceModelPattern:
default:
return PT_ANY;
}
}
static DWORD LcmsFormatForOPImageFormat(OPImageFormat opalFormat, CGColorSpaceRef colorSpace)
{
DWORD cmsFormat = 0;
NSLog(@"LcmsFormatForOPImageFormat pixel comp format %d, channels %d, has alpha %d, is alpha last %d",
opalFormat.compFormat, opalFormat.colorComponents, opalFormat.hasAlpha, opalFormat.isAlphaLast);
switch (opalFormat.compFormat)
{
case kOPComponentFormat8bpc:
cmsFormat |= BYTES_SH(1);
break;
case kOPComponentFormat16bpc:
cmsFormat |= BYTES_SH(2);
break;
case kOPComponentFormat32bpc:
cmsFormat |= BYTES_SH(2); // Convert to 16-bit before passing to LCMS
break;
case kOPComponentFormatFloat32bpc:
cmsFormat |= BYTES_SH(2); // Convert to 16-bit before passing to LCMS
break;
}
cmsFormat |= CHANNELS_SH((DWORD)opalFormat.colorComponents);
if (opalFormat.hasAlpha)
{
cmsFormat |= EXTRA_SH(1);
}
if (!opalFormat.isAlphaLast && opalFormat.hasAlpha)
{
cmsFormat |= SWAPFIRST_SH(1);
}
cmsFormat |= COLORSPACE_SH(
LcmsPixelTypeForCGColorSpaceModel(
CGColorSpaceGetModel(colorSpace)
)
);
return cmsFormat;
}
- (id) initWithSourceSpace: (OPColorSpaceLCMS *)aSourceSpace
destinationSpace: (OPColorSpaceLCMS *)aDestSpace
sourceFormat: (OPImageFormat)aSourceFormat
destinationFormat: (OPImageFormat)aDestFormat
renderingIntent: (CGColorRenderingIntent)anIntent
pixelCount: (size_t)aPixelCount
{
self = [super init];
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->renderingIntent = anIntent;
self->sourceFormat = aSourceFormat;
self->destFormat = aDestFormat;
self->pixelCount = aPixelCount;
if (sourceFormat.compFormat == kOPComponentFormatFloat32bpc
|| sourceFormat.compFormat == kOPComponentFormat32bpc)
{
tempBuffer1 = malloc(2 * pixelCount); // Convert to 16-bit
}
else if (sourceFormat.isAlphaPremultiplied)
{
tempBuffer1 = malloc(OPComponentNumberOfBytes(sourceFormat.compFormat) * pixelCount);
}
if (destFormat.compFormat == kOPComponentFormatFloat32bpc
|| destFormat.compFormat == kOPComponentFormat32bpc)
{
tempBuffer2 = malloc(OPComponentNumberOfBytes(destFormat.compFormat) * pixelCount);
}
return self;
}
- (void) dealloc
{
cmsDeleteTransform(self->xform);
[source release];
[dest release];
if (tempBuffer1)
{
free(tempBuffer1);
}
if (tempBuffer2)
{
free(tempBuffer2);
}
[super dealloc];
}
- (void) transformPixelData: (const unsigned char *)input
output: (char *)output
{
char *tempOutput = output;
const size_t totalComponentsIn = sourceFormat.colorComponents + (sourceFormat.hasAlpha ? 1 : 0);
const size_t totalComponentsOut = destFormat.colorComponents + (destFormat.hasAlpha ? 1 : 0);
//NSLog(@"Transform %d pixels %d comps in %d out", pixelCount, totalComponentsIn, totalComponentsOut);
// Special case for kOPComponentFormatFloat32bpc, which LCMS 1 doesn't support directly
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(@"oveflow");
}
}
}
input = 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;
}
// 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;
}
// Unpremultiply alpha in input
if (sourceFormat.isAlphaPremultiplied)
{
if (sourceFormat.compFormat == kOPComponentFormatFloat32bpc
|| sourceFormat.compFormat == kOPComponentFormat32bpc)
{
OPImageFormat fake = sourceFormat;
fake.compFormat = kOPComponentFormat16bpc;
OPPremultiplyAlpha(tempBuffer1, pixelCount, fake, true);
}
else
{
const size_t numBytes = OPComponentNumberOfBytes(sourceFormat.compFormat) * pixelCount;
memmove(tempBuffer1, input, numBytes);
OPPremultiplyAlpha(tempBuffer1, pixelCount, sourceFormat, true);
input = tempBuffer1;
}
}
cmsDoTransform(xform, (void*)input, tempOutput, pixelCount);
if (destFormat.compFormat == kOPComponentFormatFloat32bpc
|| destFormat.compFormat == kOPComponentFormat32bpc
|| destFormat.compFormat == kOPComponentFormat16bpc)
{
for (size_t j=0; j<totalComponentsIn; j++)
{
//NSLog(@"Transformed comp: %d => %d", ((uint16_t*)input[j]), (uint16_t*)tempOutput[j]);
}
}
// Now copy the alpha
#if 0
if (destFormat.compFormat == kOPComponentFormatFloat32bpc
|| destFormat.compFormat == kOPComponentFormat32bpc
|| destFormat.compFormat == kOPComponentFormat16bpc)
{
for (size_t i=0; i<pixelCount; i++)
{
for (size_t j=0; j<totalComponentsOut; j++)
{
((uint16_t*)tempOutput)[i*totalComponentsOut + j] = ((uint16_t*)tempBuffer2)[i*totalComponentsOut + j] / ((float)UINT16_MAX);
}
}
}
else
{
for (size_t i=0; i<pixelCount; i++)
{
for (size_t j=0; j<totalComponentsOut; j++)
{
((float*)output)[i*totalComponentsOut + j] = ((uint16_t*)tempBuffer2)[i*totalComponentsOut + j] / ((float)UINT16_MAX);
}
}
}
#endif
// Premultiply alpha in output
if (destFormat.isAlphaPremultiplied)
{
OPImageFormat fake = sourceFormat;
if (sourceFormat.compFormat == kOPComponentFormatFloat32bpc
|| sourceFormat.compFormat == kOPComponentFormat32bpc)
{
fake.compFormat = kOPComponentFormat16bpc;
}
OPPremultiplyAlpha(tempOutput, pixelCount, fake, false);
}
if (destFormat.compFormat == kOPComponentFormatFloat32bpc)
{
for (size_t i=0; i<pixelCount; i++)
{
for (size_t j=0; j<totalComponentsOut; j++)
{
((float*)output)[i*totalComponentsOut + j] = ((uint16_t*)tempBuffer2)[i*totalComponentsOut + j] / ((float)UINT16_MAX);
//NSLog(@"Output comp: %d => %f", (int)((uint16_t*)tempBuffer2)[i*totalComponentsOut + j], (float)((float*)output)[i*totalComponentsOut + j]);
}
}
}
else if (destFormat.compFormat == kOPComponentFormat32bpc)
{
for (size_t i=0; i<pixelCount; i++)
{
for (size_t j=0; j<totalComponentsOut; j++)
{
((uint32_t*)output)[i*totalComponentsOut + j] = ((uint16_t*)tempBuffer2)[i*totalComponentsOut + j] << 16;
}
}
}
}
@end

View File

@ -22,7 +22,33 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
bool OPImageConvert(
#include <CoreGraphics/CGColorSpace.h>
#include <CoreGraphics/CGImage.h>
/**
* To make the internals sane and fast, we only work with these four pixel components
* internally. They are all native-endian.
*/
typedef enum OPComponentFormat
{
kOPComponentFormat8bpc,
kOPComponentFormat16bpc,
kOPComponentFormat32bpc,
kOPComponentFormatFloat32bpc
} OPComponentFormat;
typedef struct OPImageFormat
{
OPComponentFormat compFormat;
size_t colorComponents;
bool hasAlpha;
bool isAlphaPremultiplied;
bool isAlphaLast;
} OPImageFormat;
size_t OPComponentNumberOfBytes(OPComponentFormat fmt);
void OPImageConvert(
unsigned char *dstData,
const unsigned char *srcData,
size_t width,
@ -37,4 +63,4 @@ bool OPImageConvert(
CGBitmapInfo srcBitmapInfo,
CGColorSpaceRef dstColorSpace,
CGColorSpaceRef srcColorSpace,
CGColorRenderingIntent intent);
CGColorRenderingIntent intent);

250
Source/OPImageConversion.m Normal file
View File

@ -0,0 +1,250 @@
/** <title>CGImage-conversion.m</title>
<abstract>C Interface to graphics drawing library</abstract>
Copyright <copy>(C) 2010 Free Software Foundation, Inc.</copy>
Author: Eric Wasylishen <ewasylishen@gmail.com>
Date: July, 2010
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <lcms.h>
#import <Foundation/NSString.h>
#include "CoreGraphics/CGColorSpace.h"
#include "CoreGraphics/CGImage.h"
#import "CGColorSpace-private.h"
size_t OPComponentNumberOfBytes(OPComponentFormat fmt)
{
switch (fmt)
{
case kOPComponentFormat8bpc:
return 1;
case kOPComponentFormat16bpc:
return 2;
case kOPComponentFormat32bpc:
return 4;
case kOPComponentFormatFloat32bpc:
return 4;
}
}
static inline uint64_t swap64(uint64_t val)
{
char out[8];
char *in = (char *)(&val);
for (unsigned int i = 0; i<8; i++)
{
out[i] = in[7-i];
}
return *((uint64_t*)&out);
}
static inline void
_set_bit_value(unsigned char *base, size_t msb_off, size_t bit_width,
uint32_t val)
{
if (msb_off % 32 == 0 && bit_width == 32)
{
((uint32_t*)base)[msb_off / 32] = val;
}
else if (msb_off % 16 == 0 && bit_width == 16)
{
((uint16_t*)base)[msb_off / 16] = val;
}
else if (msb_off % 8 == 0 && bit_width == 8)
{
((uint8_t*)base)[msb_off / 8] = val;
}
else if (bit_width <= 32)
{
int byte1 = msb_off / 8;
int shift = 64 - bit_width - (msb_off % 8);
uint64_t value = val;
value &= ((1 << bit_width) - 1);
value <<= shift;
value = swap64(value); // if little endian
uint64_t mask = ((1 << bit_width) - 1);
mask <<= shift;
mask = ~mask;
mask = swap64(mask); // if little endian
*((uint64_t*)(base + byte1)) &= mask;
*((uint64_t*)(base + byte1)) |= value;
}
else
{
abort();
}
}
static inline uint32_t
_get_bit_value(const unsigned char *base, size_t msb_off, size_t bit_width)
{
if (msb_off % 32 == 0 && bit_width == 32)
{
return ((uint32_t*)base)[msb_off / 32];
}
else if (msb_off % 16 == 0 && bit_width == 16)
{
return ((uint16_t*)base)[msb_off / 16];
}
else if (msb_off % 8 == 0 && bit_width == 8)
{
return ((uint8_t*)base)[msb_off / 8];
}
else
{
int byte1 = msb_off / 8;
int byte2 = ((msb_off + bit_width - 1) / 8);
int shift = 64 - bit_width - (msb_off % 8);
int bytes_needed = byte2 - byte1 + 1;
char chars[8];
switch (bytes_needed)
{
case 5: chars[4] = base[byte1+4];
case 4: chars[3] = base[byte1+3];
case 3: chars[2] = base[byte1+2];
case 2: chars[1] = base[byte1+1];
case 1: chars[0] = base[byte1+0];
break;
default:
abort();
}
uint64_t value = *((uint64_t*)chars);
value = swap64(value); // if little endian
value >>= shift;
value &= ((1<<bit_width)-1);
return (uint32_t)value;
}
}
/**
* Rounds up a format to a standard size.
*/
static void OPRoundUp(size_t bitsPerComponentIn, size_t bitsPerPixelIn, size_t *bitsPerComponentOut, size_t *bitsPerPixelOut)
{
if (bitsPerComponentIn < 8)
{
*bitsPerComponentOut = 8;
}
else if (bitsPerComponentIn < 16)
{
*bitsPerComponentOut = 16;
}
else if (bitsPerComponentIn < 32)
{
*bitsPerComponentOut = 32;
}
*bitsPerPixelOut = (bitsPerPixelIn / bitsPerComponentIn) * (*bitsPerComponentOut);
}
static bool OPImageFormatForCGFormat(
size_t bitsPerComponent,
size_t bitsPerPixel,
CGBitmapInfo bitmapInfo,
CGColorSpaceRef colorSpace,
OPImageFormat *out)
{
switch (bitsPerComponent)
{
case 8:
out->compFormat = kOPComponentFormat8bpc;
break;
case 16:
out->compFormat = kOPComponentFormat16bpc;
break;
case 32:
if (bitmapInfo & kCGBitmapFloatComponents)
{
out->compFormat = kOPComponentFormat32bpc;
}
else
{
out->compFormat = kOPComponentFormatFloat32bpc;
}
break;
default:
return false;
}
size_t colorComponents = CGColorSpaceGetNumberOfComponents(colorSpace);
size_t actualComponents = bitsPerPixel / bitsPerComponent;
CGImageAlphaInfo alpha = bitmapInfo & kCGBitmapAlphaInfoMask;
out->colorComponents = colorComponents;
out->hasAlpha = (alpha != kCGImageAlphaNone && actualComponents > colorComponents);
out->isAlphaPremultiplied = (alpha == kCGImageAlphaPremultipliedFirst ||
alpha == kCGImageAlphaPremultipliedLast);
out->isAlphaLast = (alpha == kCGImageAlphaPremultipliedLast ||
alpha == kCGImageAlphaLast);
return true;
}
void OPImageConvert(
unsigned char *dstData,
const unsigned char *srcData,
size_t width,
size_t height,
size_t dstBitsPerComponent,
size_t srcBitsPerComponent,
size_t dstBitsPerPixel,
size_t srcBitsPerPixel,
size_t dstBytesPerRow,
size_t srcBytesPerRow,
CGBitmapInfo dstBitmapInfo,
CGBitmapInfo srcBitmapInfo,
CGColorSpaceRef dstColorSpace,
CGColorSpaceRef srcColorSpace,
CGColorRenderingIntent intent)
{
// For now, just support conversions that OPColorTransform can docs
OPImageFormat srcFormat, dstFormat;
if (!OPImageFormatForCGFormat(srcBitsPerComponent, srcBitsPerPixel, srcBitmapInfo, srcColorSpace, &srcFormat))
{
NSLog(@"Input format not supported");
}
if (!OPImageFormatForCGFormat(srcBitsPerComponent, srcBitsPerPixel, srcBitmapInfo, srcColorSpace, &dstFormat))
{
NSLog(@"Output format not supported");
}
OPColorTransform *xform = [srcColorSpace colorTransformTo: dstColorSpace
sourceFormat: srcFormat
destinationFormat: dstFormat
renderingIntent: intent
pixelCount: width];
for (size_t row=0; row<height; row++)
{
// FIXME: alpha
[xform transformPixelData: srcData + (row * srcBytesPerRow)
output: dstData + (row * dstBytesPerRow)];
}
}

View File

@ -0,0 +1,33 @@
/** <title>OPPremultiplyAlpha</title>
<abstract>C Interface to graphics drawing library</abstract>
Copyright <copy>(C) 2010 Free Software Foundation, Inc.</copy>
Author: Eric Wasylishen <ewasylishen@gmail.com>
Date: July, 2010
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import "OPImageConversion.h"
#import "CGColorSpace-private.h"
void OPPremultiplyAlpha(
unsigned char *row,
size_t pixels,
OPImageFormat format,
bool unPremultiply
);

View File

@ -0,0 +1,67 @@
/** <title>OPColorTransformLCMS</title>
<abstract>C Interface to graphics drawing library</abstract>
Copyright <copy>(C) 2010 Free Software Foundation, Inc.</copy>
Author: Eric Wasylishen <ewasylishen@gmail.com>
Date: July, 2010
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import "OPPremultiplyAlpha.h"
#define _OPPremultiplyAlpha(format, compType, compMax, rowStart, comps, pixels, unPremultiply) \
for (compType *comp = (compType*)rowStart; comp < ((compType*)rowStart)+(pixels * comps); comp += comps) \
{ \
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); \
const float alphaValue = comp[alphaComp] / (float)compMax; \
for (size_t i = firstComp; i<=lastComp; i++) \
{ \
comp[i] *= (unPremultiply ? (1.0/alphaValue) : alphaValue); \
} \
}
void OPPremultiplyAlpha(
unsigned char *row,
size_t pixels,
OPImageFormat format,
bool unPremultiply)
{
if (unPremultiply == !format.isAlphaPremultiplied || !format.hasAlpha)
return;
const size_t comps = (format.colorComponents + 1);
switch (format.compFormat)
{
case kOPComponentFormat8bpc:
_OPPremultiplyAlpha(format, uint8_t, UINT8_MAX, row, comps, pixels, unPremultiply);
break;
case kOPComponentFormat16bpc:
_OPPremultiplyAlpha(format, uint16_t, UINT16_MAX, row, comps, pixels, unPremultiply);
break;
case kOPComponentFormat32bpc:
_OPPremultiplyAlpha(format, uint32_t, UINT32_MAX, row, comps, pixels, unPremultiply);
break;
case kOPComponentFormatFloat32bpc:
_OPPremultiplyAlpha(format, float, 1.0f, row, comps, pixels, unPremultiply);
break;
}
}

View File

@ -1,176 +0,0 @@
/** <title>CGImage-conversion.m</title>
<abstract>C Interface to graphics drawing library</abstract>
Copyright <copy>(C) 2010 Free Software Foundation, Inc.</copy>
Author: Eric Wasylishen <ewasylishen@gmail.com>
Date: July, 2010
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <lcms.h>
#import <Foundation/NSString.h>
#include "CoreGraphics/CGColorSpace.h"
#include "CoreGraphics/CGImage.h"
static int LcmsIntentForCGColorRenderingIntent(CGColorRenderingIntent intent)
{
switch (intent)
{
default:
case kCGRenderingIntentDefault:
return INTENT_RELATIVE_COLORIMETRIC; // FIXME: what should we do here?
case kCGRenderingIntentAbsoluteColorimetric:
return INTENT_ABSOLUTE_COLORIMETRIC;
case kCGRenderingIntentRelativeColorimetric:
return INTENT_RELATIVE_COLORIMETRIC;
case kCGRenderingIntentPerceptual:
return INTENT_PERCEPTUAL;
case kCGRenderingIntentSaturation:
return INTENT_SATURATION;
}
}
static int LcmsPixelTypeForCGColorSpaceModel(CGColorSpaceModel model)
{
switch (model)
{
case kCGColorSpaceModelMonochrome:
return PT_GRAY;
case kCGColorSpaceModelRGB:
return PT_RGB;
case kCGColorSpaceModelCMYK:
return PT_CMYK;
case kCGColorSpaceModelLab:
return PT_Lab;
case kCGColorSpaceModelUnknown:
case kCGColorSpaceModelDeviceN:
case kCGColorSpaceModelIndexed:
case kCGColorSpaceModelPattern:
default:
return PT_ANY;
}
}
static bool LcmsFormatForCGFormat(
size_t bitsPerComponent,
size_t bitsPerPixel,
CGBitmapInfo info,
CGColorSpaceRef colorSpace,
DWORD *outFormat)
{
DWORD format = 0;
if (info & kCGBitmapFloatComponents)
{
// LCMS 2 supports floats
NSLog(@"Floats not supported");
return false;
}
if (bitsPerComponent % 8 != 0)
{
NSLog(@"Only multiples of 8 BPC supported");
return false;
}
if (bitsPerPixel % bitsPerComponent != 0)
{
NSLog(@"bitsPerPixel not a multiple of bitsPerComponent");
return false;
}
size_t colorComponents = CGColorSpaceGetNumberOfComponents(colorSpace);
size_t actualComponents = bitsPerPixel / bitsPerComponent;
CGImageAlphaInfo alpha = info & kCGBitmapAlphaInfoMask;
format |= BYTES_SH(bitsPerComponent / 8);
format |= CHANNELS_SH(colorComponents);
if (colorComponents == actualComponents - 1)
{
format |= EXTRA_SH(1);
if (alpha == kCGImageAlphaFirst ||
alpha == kCGImageAlphaNoneSkipFirst ||
alpha == kCGImageAlphaPremultipliedFirst)
{
format |= SWAPFIRST_SH(1);
}
}
else if (colorComponents != actualComponents)
{
NSLog(@"colorComponents don't make sense");
return false;
}
format |= COLORSPACE_SH(
LcmsPixelTypeForCGColorSpaceModel(
CGColorSpaceGetModel(colorSpace)
)
);
*outFormat = format;
return true;
}
bool OPImageConvert(
unsigned char *dstData,
const unsigned char *srcData,
size_t width,
size_t height,
size_t dstBitsPerComponent,
size_t srcBitsPerComponent,
size_t dstBitsPerPixel,
size_t srcBitsPerPixel,
size_t dstBytesPerRow,
size_t srcBytesPerRow,
CGBitmapInfo dstBitmapInfo,
CGBitmapInfo srcBitmapInfo,
CGColorSpaceRef dstColorSpace,
CGColorSpaceRef srcColorSpace,
CGColorRenderingIntent intent)
{
DWORD lcmsSrcFormat, lcmsDstFormat;
if (!LcmsFormatForCGFormat(srcBitsPerComponent, srcBitsPerPixel, srcBitmapInfo, srcColorSpace, &lcmsSrcFormat))
{
NSLog(@"Couldn't find LCMS format for source format");
return false;
}
if (!LcmsFormatForCGFormat(dstBitsPerComponent, dstBitsPerPixel, dstBitmapInfo, dstColorSpace, &lcmsDstFormat))
{
NSLog(@"Couldn't find LCMS format for dest format");
return false;
}
cmsHPROFILE srgb = cmsCreate_sRGBProfile();
cmsHTRANSFORM xform = cmsCreateTransform(srgb, lcmsSrcFormat, srgb, lcmsDstFormat,
LcmsIntentForCGColorRenderingIntent(intent), 0);
for (size_t row = 0; row < height; row++)
{
unsigned char *dstRow = dstData + (dstBytesPerRow * row);
const unsigned char *srcRow = srcData + (srcBytesPerRow * row);
cmsDoTransform(xform, (void*)srcRow, dstRow, width);
}
cmsDeleteTransform(xform);
return true;
}