From 560a20d5d127778d237c23c25bf41fbb1f9f10ad Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Tue, 7 Apr 2009 12:02:11 -0400 Subject: [PATCH] Bug 481926 - Rewrite color management component sr=vlad, r=ted, r=joedrew (\o/) Replaces lcms with qcms --- config/autoconf.mk.in | 6 +- config/static-config.mk | 1 - configure.in | 18 +- content/canvas/src/Makefile.in | 1 + gfx/Makefile.in | 2 +- gfx/qcms/Makefile.in | 24 + gfx/qcms/iccread.c | 789 +++++++++ gfx/qcms/qcms.h | 153 ++ gfx/qcms/qcmsint.h | 143 ++ gfx/qcms/qcmstypes.h | 49 + gfx/qcms/transform.c | 1535 +++++++++++++++++ gfx/src/thebes/Makefile.in | 2 + gfx/thebes/public/gfxPlatform.h | 18 +- gfx/thebes/public/gfxPlatformGtk.h | 2 +- gfx/thebes/public/gfxPlatformMac.h | 2 +- gfx/thebes/public/gfxQtPlatform.h | 2 +- gfx/thebes/public/gfxWindowsPlatform.h | 2 +- gfx/thebes/src/Makefile.in | 3 +- gfx/thebes/src/gfxContext.cpp | 1 - gfx/thebes/src/gfxPattern.cpp | 1 - gfx/thebes/src/gfxPlatform.cpp | 97 +- gfx/thebes/src/gfxPlatformGtk.cpp | 43 +- gfx/thebes/src/gfxPlatformMac.cpp | 10 +- gfx/thebes/src/gfxQtPlatform.cpp | 4 +- gfx/thebes/src/gfxWindowsPlatform.cpp | 8 +- gfx/thebes/test/Makefile.in | 5 +- layout/base/Makefile.in | 1 + layout/mathml/Makefile.in | 1 + layout/svg/base/src/Makefile.in | 1 + modules/libpr0n/build/Makefile.in | 4 +- modules/libpr0n/decoders/gif/Makefile.in | 2 +- .../libpr0n/decoders/gif/nsGIFDecoder2.cpp | 6 +- modules/libpr0n/decoders/jpeg/Makefile.in | 2 +- .../libpr0n/decoders/jpeg/nsJPEGDecoder.cpp | 49 +- modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h | 6 +- modules/libpr0n/decoders/png/Makefile.in | 2 +- modules/libpr0n/decoders/png/nsPNGDecoder.cpp | 110 +- modules/libpr0n/decoders/png/nsPNGDecoder.h | 6 +- .../reftest/pngsuite-ancillary/ccwn2c08.html | 606 +++---- .../reftest/pngsuite-ancillary/ccwn3p08.html | 570 +++--- .../{lcms-asm-check.js => qcms-asm-check.js} | 6 +- toolkit/library/libxul-rules.mk | 2 +- toolkit/toolkit-makefiles.sh | 7 +- toolkit/toolkit-tiers.mk | 2 +- widget/src/build/Makefile.in | 2 +- widget/src/cocoa/Makefile.in | 4 +- widget/src/cocoa/nsCocoaWindow.mm | 6 +- widget/src/gtk2/Makefile.in | 3 +- widget/src/os2/Makefile.in | 2 +- widget/src/qt/Makefile.in | 2 +- widget/src/windows/Makefile.in | 1 + widget/src/xpwidgets/Makefile.in | 2 +- widget/src/xpwidgets/nsXPLookAndFeel.cpp | 6 +- 53 files changed, 3492 insertions(+), 840 deletions(-) create mode 100644 gfx/qcms/Makefile.in create mode 100644 gfx/qcms/iccread.c create mode 100644 gfx/qcms/qcms.h create mode 100644 gfx/qcms/qcmsint.h create mode 100644 gfx/qcms/qcmstypes.h create mode 100644 gfx/qcms/transform.c rename modules/libpr0n/test/reftest/pngsuite-ancillary/{lcms-asm-check.js => qcms-asm-check.js} (81%) diff --git a/config/autoconf.mk.in b/config/autoconf.mk.in index 814a77479e7..ea482624a65 100644 --- a/config/autoconf.mk.in +++ b/config/autoconf.mk.in @@ -281,7 +281,7 @@ OS_LDFLAGS = @LDFLAGS@ OS_COMPILE_CFLAGS = $(OS_CPPFLAGS) @COMPILE_CFLAGS@ OS_COMPILE_CXXFLAGS = $(OS_CPPFLAGS) @COMPILE_CXXFLAGS@ -OS_INCLUDES = $(NSPR_CFLAGS) $(JPEG_CFLAGS) $(PNG_CFLAGS) $(ZLIB_CFLAGS) $(LCMS_CFLAGS) +OS_INCLUDES = $(NSPR_CFLAGS) $(JPEG_CFLAGS) $(PNG_CFLAGS) $(ZLIB_CFLAGS) OS_LIBS = @LIBS@ ACDEFINES = @MOZ_DEFINES@ @@ -448,9 +448,7 @@ PNG_LIBS = @MOZ_PNG_LIBS@ PNG_REQUIRES = png endif -LCMS_CFLAGS = @LCMS_CFLAGS@ -LCMS_LIBS = @LCMS_LIBS@ -LCMS_REQUIRES = lcms +QCMS_LIBS = @QCMS_LIBS@ MOZ_NATIVE_SQLITE = @MOZ_NATIVE_SQLITE@ SQLITE_CFLAGS = @SQLITE_CFLAGS@ diff --git a/config/static-config.mk b/config/static-config.mk index e54feabd832..d4331a779f7 100644 --- a/config/static-config.mk +++ b/config/static-config.mk @@ -69,7 +69,6 @@ STATIC_EXTRA_LIBS += \ $(PNG_LIBS) \ $(JPEG_LIBS) \ $(ZLIB_LIBS) \ - $(LCMS_LIBS) \ $(NULL) ifdef MOZ_PSM diff --git a/configure.in b/configure.in index 24d2c0dde23..c8d81962fdb 100644 --- a/configure.in +++ b/configure.in @@ -130,7 +130,6 @@ GCONF_VERSION=1.2.1 LIBGNOME_VERSION=2.0 STARTUP_NOTIFICATION_VERSION=0.8 DBUS_VERSION=0.60 -LCMS_VERSION=1.17 SQLITE_VERSION=3.6.10 LIBNOTIFY_VERSION=0.4 @@ -7683,22 +7682,11 @@ AC_SUBST(MOZ_TREE_CAIRO) AC_SUBST(MOZ_CAIRO_CFLAGS) AC_SUBST(MOZ_CAIRO_LIBS) -dnl ======================================================== -dnl Check for lcms +dnl qcms dnl ======================================================== -LCMS_CFLAGS= -if test "$_WIN32_MSVC"; then - if test -z "$BUILD_STATIC_LIBS" -a -z "$MOZ_ENABLE_LIBXUL"; then - LCMS_CFLAGS=-DLCMS_DLL - fi - LCMS_LIBS='$(LIBXUL_DIST)/lib/mozlcms.lib' -else - LCMS_LIBS='-L$(LIBXUL_DIST)/bin -lmozlcms' -fi - -AC_SUBST(LCMS_CFLAGS) -AC_SUBST(LCMS_LIBS) +QCMS_LIBS='$(DEPTH)/gfx/qcms/$(LIB_PREFIX)mozqcms.$(LIB_SUFFIX)' +AC_SUBST(QCMS_LIBS) dnl ======================================================== dnl disable xul diff --git a/content/canvas/src/Makefile.in b/content/canvas/src/Makefile.in index 9f57d37654a..b2581da1df9 100644 --- a/content/canvas/src/Makefile.in +++ b/content/canvas/src/Makefile.in @@ -65,6 +65,7 @@ REQUIRES = \ imglib2 \ thebes \ view \ + qcms \ $(NULL) # XXX some platforms can't handle building diff --git a/gfx/Makefile.in b/gfx/Makefile.in index d0eed84759f..870cabc03e4 100644 --- a/gfx/Makefile.in +++ b/gfx/Makefile.in @@ -48,7 +48,7 @@ ifdef MOZ_TREE_CAIRO DIRS = cairo endif -DIRS += thebes public idl src +DIRS += thebes public idl src qcms ifdef ENABLE_TESTS ifndef MOZ_ENABLE_LIBXUL diff --git a/gfx/qcms/Makefile.in b/gfx/qcms/Makefile.in new file mode 100644 index 00000000000..17fb66319a0 --- /dev/null +++ b/gfx/qcms/Makefile.in @@ -0,0 +1,24 @@ +DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = qcms +LIBRARY_NAME = mozqcms +LIBXUL_LIBRARY = 1 +GRE_MODULE = 1 +DIST_INSTALL = 1 + +EXPORTS = qcms.h qcmstypes.h + +CSRCS = iccread.c transform.c + +FORCE_STATIC_LIB = 1 +# This library is used by other shared libs +FORCE_USE_PIC = 1 + +include $(topsrcdir)/config/rules.mk + +CFLAGS += -DMOZ_QCMS diff --git a/gfx/qcms/iccread.c b/gfx/qcms/iccread.c new file mode 100644 index 00000000000..6d415bbd5b5 --- /dev/null +++ b/gfx/qcms/iccread.c @@ -0,0 +1,789 @@ +// qcms +// Copyright (C) 2009 Mozilla Foundation +// Copyright (C) 1998-2007 Marti Maria +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#include +#include +#include +#include "qcmsint.h" + +//XXX: use a better typename +typedef uint32_t __be32; +typedef uint16_t __be16; + +/* all of the platforms that we use _MSC_VER on are little endian + * so this is sufficient for now */ +#ifdef _MSC_VER +#define LITTLE_ENDIAN +#endif + +#if !defined(BIG_ENDIAN) && !defined(LITTLE_ENDIAN) +#error Unknown endianess +#endif + +#if 0 +not used yet +/* __builtin_bswap isn't available in older gccs + * so open code it for now */ +static __be32 cpu_to_be32(int32_t v) +{ +#ifdef LITTLE_ENDIAN + return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | ((v & 0xff000000) >> 24); + //return __builtin_bswap32(v); + return v; +#endif +} +#endif + +static uint32_t be32_to_cpu(__be32 v) +{ +#ifdef LITTLE_ENDIAN + return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | ((v & 0xff000000) >> 24); + //return __builtin_bswap32(v); +#else + return v; +#endif +} + +static uint32_t be16_to_cpu(__be16 v) +{ +#ifdef LITTLE_ENDIAN + return ((v & 0xff) << 8) | ((v & 0xff00) >> 8); +#else + return v; +#endif +} + +/* a wrapper around the memory that we are going to parse + * into a qcms_profile */ +struct mem_source +{ + const unsigned char *buf; + size_t size; + qcms_bool valid; + const char *invalid_reason; +}; + +static void invalid_source(struct mem_source *mem, const char *reason) +{ + mem->valid = false; + mem->invalid_reason = reason; +} + +static uint32_t read_u32(struct mem_source *mem, size_t offset) +{ + if (offset + 4 > mem->size) { + invalid_source(mem, "Invalid offset"); + return 0; + } else { + return be32_to_cpu(*(__be32*)(mem->buf + offset)); + } +} + +static uint16_t read_u16(struct mem_source *mem, size_t offset) +{ + if (offset + 2 > mem->size) { + invalid_source(mem, "Invalid offset"); + return 0; + } else { + return be16_to_cpu(*(__be16*)(mem->buf + offset)); + } +} + +static uint8_t read_u8(struct mem_source *mem, size_t offset) +{ + if (offset + 1 > mem->size) { + invalid_source(mem, "Invalid offset"); + return 0; + } else { + return *(uint8_t*)(mem->buf + offset); + } +} + +static s15Fixed16Number read_s15Fixed16Number(struct mem_source *mem, size_t offset) +{ + return read_u32(mem, offset); +} + +#if 0 +static uInt16Number read_uInt16Number(struct mem_source *mem, size_t offset) +{ + return read_u16(mem, offset); +} +#endif + +#define BAD_VALUE_PROFILE NULL +#define INVALID_PROFILE NULL +#define NO_MEM_PROFILE NULL + +/* An arbitrary 4MB limit on profile size */ +#define MAX_PROFILE_SIZE 1024*1024*4 +#define MAX_TAG_COUNT 1024 + +static void check_CMM_type_signature(struct mem_source *src) +{ + //uint32_t CMM_type_signature = read_u32(src, 4); + //TODO: do the check? + +} + +static void check_profile_version(struct mem_source *src) +{ + uint8_t major_revision = read_u8(src, 8 + 0); + uint8_t minor_revision = read_u8(src, 8 + 1); + uint8_t reserved1 = read_u8(src, 8 + 2); + uint8_t reserved2 = read_u8(src, 8 + 3); + if (major_revision > 0x2) + invalid_source(src, "Unsupported major revision"); + if (minor_revision > 0x40) + invalid_source(src, "Unsupported minor revision"); + if (reserved1 != 0 || reserved2 != 0) + invalid_source(src, "Invalid reserved bytes"); +} + +#define INPUT_DEVICE_PROFILE 0x73636e72 // 'scnr' +#define DISPLAY_DEVICE_PROFILE 0x6d6e7472 // 'mntr' +#define OUTPUT_DEVICE_PROFILE 0x70727472 // 'prtr' +#define DEVICE_LINK_PROFILE 0x6c696e6b // 'link' +#define COLOR_SPACE_PROFILE 0x73706163 // 'spac' +#define ABSTRACT_PROFILE 0x61627374 // 'abst' +#define NAMED_COLOR_PROFILE 0x6e6d636c // 'nmcl' + +static void read_class_signature(qcms_profile *profile, struct mem_source *mem) +{ + profile->class = read_u32(mem, 12); + switch (profile->class) { + case DISPLAY_DEVICE_PROFILE: + case INPUT_DEVICE_PROFILE: + break; + case OUTPUT_DEVICE_PROFILE: + default: + invalid_source(mem, "Invalid Profile/Device Class signature"); + } +} + +static void read_color_space(qcms_profile *profile, struct mem_source *mem) +{ + profile->color_space = read_u32(mem, 16); + switch (profile->color_space) { + case RGB_SIGNATURE: + case GRAY_SIGNATURE: + break; + default: + invalid_source(mem, "Unsupported colorspace"); + } +} + +struct tag +{ + uint32_t signature; + uint32_t offset; + uint32_t size; +}; + +struct tag_index { + uint32_t count; + struct tag *tags; +}; + +static struct tag_index read_tag_table(qcms_profile *profile, struct mem_source *mem) +{ + struct tag_index index = {0, NULL}; + int i; + + index.count = read_u32(mem, 128); + if (index.count > MAX_TAG_COUNT) { + invalid_source(mem, "max number of tags exceeded"); + return index; + } + + index.tags = malloc(sizeof(struct tag)*index.count); + if (index.tags) { + for (i = 0; i < index.count; i++) { + index.tags[i].signature = read_u32(mem, 128 + 4 + 4*i*3); + index.tags[i].offset = read_u32(mem, 128 + 4 + 4*i*3 + 4); + index.tags[i].size = read_u32(mem, 128 + 4 + 4*i*3 + 8); + } + } + + return index; +} + +// Checks a profile for obvious inconsistencies and returns +// true if the profile looks bogus and should probably be +// ignored. +qcms_bool qcms_profile_is_bogus(qcms_profile *profile) +{ + float sum[3], target[3], tolerance[3]; + unsigned i; + + // Sum the values + sum[0] = s15Fixed16Number_to_float(profile->redColorant.X) + + s15Fixed16Number_to_float(profile->greenColorant.X) + + s15Fixed16Number_to_float(profile->blueColorant.X); + sum[1] = s15Fixed16Number_to_float(profile->redColorant.Y) + + s15Fixed16Number_to_float(profile->greenColorant.Y) + + s15Fixed16Number_to_float(profile->blueColorant.Y); + sum[2] = s15Fixed16Number_to_float(profile->redColorant.Z) + + s15Fixed16Number_to_float(profile->greenColorant.Z) + + s15Fixed16Number_to_float(profile->blueColorant.Z); + + // Build our target vector (see mozilla bug 460629) + target[0] = 0.96420; + target[1] = 1.00000; + target[2] = 0.82491; + + // Our tolerance vector - Recommended by Chris Murphy based on + // conversion from the LAB space criterion of no more than 3 in any one + // channel. This is similar to, but slightly more tolerant than Adobe's + // criterion. + tolerance[0] = 0.02; + tolerance[1] = 0.02; + tolerance[2] = 0.04; + + // Compare with our tolerance + for (i = 0; i < 3; ++i) { + if (!(((sum[i] - tolerance[i]) <= target[i]) && + ((sum[i] + tolerance[i]) >= target[i]))) + return true; + } + + // All Good + return false; +} + +#define TAG_bXYZ 0x6258595a +#define TAG_gXYZ 0x6758595a +#define TAG_rXYZ 0x7258595a +#define TAG_rTRC 0x72545243 +#define TAG_bTRC 0x62545243 +#define TAG_gTRC 0x67545243 +#define TAG_kTRC 0x6b545243 +#define TAG_A2B0 0x41324230 + +static struct tag *find_tag(struct tag_index index, uint32_t tag_id) +{ + int i; + struct tag *tag = NULL; + for (i = 0; i < index.count; i++) { + if (index.tags[i].signature == tag_id) { + return &index.tags[i]; + } + } + return tag; +} + +#define XYZ_TYPE 0x58595a20 // 'XYZ ' +#define CURVE_TYPE 0x63757276 // 'curv' +#define LUT16_TYPE 0x6d667432 // 'mft2' +#define LUT8_TYPE 0x6d667431 // 'mft1' + +static struct XYZNumber read_tag_XYZType(struct mem_source *src, struct tag_index index, uint32_t tag_id) +{ + struct XYZNumber num = {0}; + struct tag *tag = find_tag(index, tag_id); + if (tag) { + uint32_t offset = tag->offset; + + uint32_t type = read_u32(src, offset); + if (type != XYZ_TYPE) + invalid_source(src, "unexpected type, expected XYZ"); + num.X = read_s15Fixed16Number(src, offset+8); + num.Y = read_s15Fixed16Number(src, offset+12); + num.Z = read_s15Fixed16Number(src, offset+16); + } else { + invalid_source(src, "missing xyztag"); + } + return num; +} + +static struct curveType *read_tag_curveType(struct mem_source *src, struct tag_index index, uint32_t tag_id) +{ + struct tag *tag = find_tag(index, tag_id); + struct curveType *curve = NULL; + if (tag) { + uint32_t offset = tag->offset; + uint32_t type = read_u32(src, offset); + uint32_t count = read_u32(src, offset+8); + int i; + + if (type != CURVE_TYPE) { + invalid_source(src, "unexpected type, expected CURV"); + return NULL; + } + +#define MAX_CURVE_ENTRIES 40000 //arbitrary + if (count > MAX_CURVE_ENTRIES) { + invalid_source(src, "curve size too large"); + return NULL; + } + curve = malloc(sizeof(struct curveType) + sizeof(uInt16Number)*count); + if (!curve) + return NULL; + + curve->count = count; + for (i=0; idata[i] = read_u16(src, offset + 12 + i *2); + } + } else { + invalid_source(src, "missing curvetag"); + } + + return curve; +} + +/* This function's not done yet */ +static struct lutType *read_tag_lutType(struct mem_source *src, struct tag_index index, uint32_t tag_id) +{ + struct tag *tag = find_tag(index, tag_id); + uint32_t offset = tag->offset; + uint32_t type = read_u32(src, offset); + uint16_t num_input_table_entries; + uint16_t num_output_table_entries; + uint8_t in_chan, grid_points, out_chan; + uint32_t clut_size; + struct lutType *lut; + int i; + + num_input_table_entries = read_u16(src, offset + 48); + num_output_table_entries = read_u16(src, offset + 50); + + in_chan = read_u8(src, offset + 8); + out_chan = read_u8(src, offset + 9); + grid_points = read_u8(src, offset + 10); + + if (!src->valid) + return NULL; + + clut_size = in_chan * grid_points * out_chan; +#define MAX_CLUT_SIZE 10000 // arbitrary + if (clut_size > MAX_CLUT_SIZE) { + return NULL; + } + + if (type != LUT16_TYPE && type != LUT8_TYPE) + return NULL; + + lut = malloc(sizeof(struct lutType) + (clut_size + num_input_table_entries + num_output_table_entries)*sizeof(uint8_t)); + if (!lut) + return NULL; + lut->num_input_channels = read_u8(src, offset + 8); + lut->num_output_channels = read_u8(src, offset + 9); + lut->num_clut_grid_points = read_u8(src, offset + 10); + lut->e00 = read_s15Fixed16Number(src, offset+12); + lut->e01 = read_s15Fixed16Number(src, offset+16); + lut->e02 = read_s15Fixed16Number(src, offset+20); + lut->e10 = read_s15Fixed16Number(src, offset+24); + lut->e11 = read_s15Fixed16Number(src, offset+28); + lut->e12 = read_s15Fixed16Number(src, offset+32); + lut->e20 = read_s15Fixed16Number(src, offset+36); + lut->e21 = read_s15Fixed16Number(src, offset+40); + lut->e22 = read_s15Fixed16Number(src, offset+44); + + //TODO: finish up + for (i = 0; i < lut->num_input_table_entries; i++) { + } + return lut; +} + +static void read_rendering_intent(qcms_profile *profile, struct mem_source *src) +{ + profile->rendering_intent = read_u32(src, 64); + switch (profile->rendering_intent) { + case QCMS_INTENT_PERCEPTUAL: + case QCMS_INTENT_SATURATION: + case QCMS_INTENT_RELATIVE_COLORIMETRIC: + case QCMS_INTENT_ABSOLUTE_COLORIMETRIC: + break; + default: + invalid_source(src, "unknown rendering intent"); + } +} + +qcms_profile *qcms_profile_create(void) +{ + return calloc(sizeof(qcms_profile), 1); +} + +/* build sRGB gamma table */ +/* based on cmsBuildParametricGamma() */ +static uint16_t *build_sRGB_gamma_table(int num_entries) +{ + int i; + /* taken from lcms: Build_sRGBGamma() */ + double gamma = 2.4; + double a = 1./1.055; + double b = 0.055/1.055; + double c = 1./12.92; + double d = 0.04045; + + uint16_t *table = malloc(sizeof(uint16_t) * num_entries); + if (!table) + return NULL; + + for (i=0; i= d + // Y = cX | X < d + if (x >= d) { + double e = (a*x + b); + if (e > 0) + y = pow(e, gamma); + else + y = 0; + } else { + y = c*x; + } + + // Saturate -- this could likely move to a separate function + output = y * 65535. + .5; + if (output > 65535.) + output = 65535; + if (output < 0) + output = 0; + table[i] = (uint16_t)floor(output); + } + return table; +} + +static struct curveType *curve_from_table(uint16_t *table, int num_entries) +{ + struct curveType *curve; + int i; + curve = malloc(sizeof(struct curveType) + sizeof(uInt16Number)*num_entries); + if (!curve) + return NULL; + curve->count = num_entries; + for (i = 0; i < num_entries; i++) { + curve->data[i] = table[i]; + } + return curve; +} + +static uint16_t float_to_u8Fixed8Number(float a) +{ + if (a > (255. + 255./256)) + return 0xffff; + else if (a < 0.) + return 0; + else + return floor(a*256. + .5); +} + +static struct curveType *curve_from_gamma(float gamma) +{ + struct curveType *curve; + int num_entries = 1; + curve = malloc(sizeof(struct curveType) + sizeof(uInt16Number)*num_entries); + if (!curve) + return NULL; + curve->count = num_entries; + curve->data[0] = float_to_u8Fixed8Number(gamma); + return curve; +} + +static void qcms_profile_fini(qcms_profile *profile) +{ + free(profile->redTRC); + free(profile->blueTRC); + free(profile->greenTRC); + free(profile->grayTRC); + free(profile); +} + +//XXX: it would be nice if we had a way of ensuring +// everything in a profile was initialized regardless of how it was created + +//XXX: should this also be taking a black_point? +/* similar to CGColorSpaceCreateCalibratedRGB */ +qcms_profile* qcms_profile_create_rgb_with_gamma( + qcms_CIE_xyY white_point, + qcms_CIE_xyYTRIPLE primaries, + float gamma) +{ + qcms_profile* profile = qcms_profile_create(); + if (!profile) + return NO_MEM_PROFILE; + + //XXX: should store the whitepoint + set_rgb_colorants(profile, white_point, primaries); + + profile->redTRC = curve_from_gamma(gamma); + profile->blueTRC = curve_from_gamma(gamma); + profile->greenTRC = curve_from_gamma(gamma); + + if (!profile->redTRC || !profile->blueTRC || !profile->greenTRC) { + qcms_profile_fini(profile); + return NO_MEM_PROFILE; + } + profile->class = DISPLAY_DEVICE_PROFILE; + profile->rendering_intent = QCMS_INTENT_PERCEPTUAL; + profile->color_space = RGB_SIGNATURE; + return profile; +} + +qcms_profile* qcms_profile_create_rgb_with_table( + qcms_CIE_xyY white_point, + qcms_CIE_xyYTRIPLE primaries, + uint16_t *table, int num_entries) +{ + qcms_profile* profile = qcms_profile_create(); + if (!profile) + return NO_MEM_PROFILE; + + //XXX: should store the whitepoint + set_rgb_colorants(profile, white_point, primaries); + + profile->redTRC = curve_from_table(table, num_entries); + profile->blueTRC = curve_from_table(table, num_entries); + profile->greenTRC = curve_from_table(table, num_entries); + + if (!profile->redTRC || !profile->blueTRC || !profile->greenTRC) { + qcms_profile_fini(profile); + return NO_MEM_PROFILE; + } + profile->class = DISPLAY_DEVICE_PROFILE; + profile->rendering_intent = QCMS_INTENT_PERCEPTUAL; + profile->color_space = RGB_SIGNATURE; + return profile; +} + +/* from lcms: cmsWhitePointFromTemp */ +/* tempK must be >= 4000. and <= 25000. + * similar to argyll: icx_DTEMP2XYZ() */ +static qcms_CIE_xyY white_point_from_temp(int temp_K) +{ + qcms_CIE_xyY white_point; + double x, y; + double T, T2, T3; + // double M1, M2; + + // No optimization provided. + T = temp_K; + T2 = T*T; // Square + T3 = T2*T; // Cube + + // For correlated color temperature (T) between 4000K and 7000K: + if (T >= 4000. && T <= 7000.) { + x = -4.6070*(1E9/T3) + 2.9678*(1E6/T2) + 0.09911*(1E3/T) + 0.244063; + } else { + // or for correlated color temperature (T) between 7000K and 25000K: + if (T > 7000.0 && T <= 25000.0) { + x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T) + 0.237040; + } else { + assert(0 && "invalid temp"); + } + } + + // Obtain y(x) + + y = -3.000*(x*x) + 2.870*x - 0.275; + + // wave factors (not used, but here for futures extensions) + + // M1 = (-1.3515 - 1.7703*x + 5.9114 *y)/(0.0241 + 0.2562*x - 0.7341*y); + // M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y); + + // Fill white_point struct + white_point.x = x; + white_point.y = y; + white_point.Y = 1.0; + + return white_point; +} + +qcms_profile* qcms_profile_sRGB(void) +{ + qcms_profile *profile; + uint16_t *table; + + qcms_CIE_xyYTRIPLE Rec709Primaries = { + {0.6400, 0.3300, 1.0}, + {0.3000, 0.6000, 1.0}, + {0.1500, 0.0600, 1.0} + }; + qcms_CIE_xyY D65; + + D65 = white_point_from_temp(6504); + + table = build_sRGB_gamma_table(1024); + + if (!table) + return NO_MEM_PROFILE; + + profile = qcms_profile_create_rgb_with_table(D65, Rec709Primaries, table, 1024); + free(table); + return profile; +} + + +/* qcms_profile_from_memory does not hold a reference to the memory passed in */ +qcms_profile* qcms_profile_from_memory(const void *mem, size_t size) +{ + uint32_t length; + struct mem_source source; + struct mem_source *src = &source; + struct tag_index index; + qcms_profile *profile; + + source.buf = mem; + source.size = size; + source.valid = true; + length = read_u32(src, 0); + if (length <= size) { + // shrink the area that we can read if appropriate + source.size = length; + } else { + return INVALID_PROFILE; + } + + profile = qcms_profile_create(); + if (!profile) + return NO_MEM_PROFILE; + + check_CMM_type_signature(src); + check_profile_version(src); + read_class_signature(profile, src); + read_rendering_intent(profile, src); + read_color_space(profile, src); + //TODO read rest of profile stuff + + if (!src->valid) + goto invalid_profile; + + index = read_tag_table(profile, src); + if (!src->valid || !index.tags) + goto invalid_tag_table; + + if (profile->class == DISPLAY_DEVICE_PROFILE || profile->class == INPUT_DEVICE_PROFILE) { + if (profile->color_space == RGB_SIGNATURE) { + + profile->redColorant = read_tag_XYZType(src, index, TAG_rXYZ); + profile->blueColorant = read_tag_XYZType(src, index, TAG_bXYZ); + profile->greenColorant = read_tag_XYZType(src, index, TAG_gXYZ); + + if (!src->valid) + goto invalid_tag_table; + + profile->redTRC = read_tag_curveType(src, index, TAG_rTRC); + profile->blueTRC = read_tag_curveType(src, index, TAG_bTRC); + profile->greenTRC = read_tag_curveType(src, index, TAG_gTRC); + + if (!profile->redTRC || !profile->blueTRC || !profile->greenTRC) + goto invalid_tag_table; + + } else if (profile->color_space == GRAY_SIGNATURE) { + + profile->grayTRC = read_tag_curveType(src, index, TAG_kTRC); + if (!profile->grayTRC) + goto invalid_tag_table; + + } else { + goto invalid_tag_table; + } + } else if (0 && profile->class == OUTPUT_DEVICE_PROFILE) { + profile->A2B0 = read_tag_lutType(src, index, TAG_A2B0); + } else { + goto invalid_tag_table; + } + + if (!src->valid) + goto invalid_tag_table; + + free(index.tags); + + return profile; + +invalid_tag_table: + free(index.tags); +invalid_profile: + qcms_profile_fini(profile); + return INVALID_PROFILE; +} + +qcms_intent qcms_profile_get_rendering_intent(qcms_profile *profile) +{ + return profile->rendering_intent; +} + +icColorSpaceSignature +qcms_profile_get_color_space(qcms_profile *profile) +{ + return profile->color_space; +} + +void qcms_profile_release(qcms_profile *profile) +{ + if (profile->output_table_r) + precache_release(profile->output_table_r); + if (profile->output_table_g) + precache_release(profile->output_table_g); + if (profile->output_table_b) + precache_release(profile->output_table_b); + + qcms_profile_fini(profile); +} + +#include +qcms_profile* qcms_profile_from_file(FILE *file) +{ + uint32_t length, remaining_length; + qcms_profile *profile; + size_t read_length; + __be32 length_be; + void *data; + + fread(&length_be, sizeof(length), 1, file); + length = be32_to_cpu(length_be); + if (length > MAX_PROFILE_SIZE) + return BAD_VALUE_PROFILE; + + /* allocate room for the entire profile */ + data = malloc(length); + if (!data) + return NO_MEM_PROFILE; + + /* copy in length to the front so that the buffer will contain the entire profile */ + *((__be32*)data) = length_be; + remaining_length = length - sizeof(length_be); + + /* read the rest profile */ + read_length = fread((unsigned char*)data + sizeof(length_be), 1, remaining_length, file); + if (read_length != remaining_length) + return INVALID_PROFILE; + + profile = qcms_profile_from_memory(data, length); + free(data); + return profile; +} + +qcms_profile* qcms_profile_from_path(const char *path) +{ + qcms_profile *profile = NULL; + FILE *file = fopen(path, "r"); + if (file) { + profile = qcms_profile_from_file(file); + fclose(file); + } + return profile; +} diff --git a/gfx/qcms/qcms.h b/gfx/qcms/qcms.h new file mode 100644 index 00000000000..d45e89c3495 --- /dev/null +++ b/gfx/qcms/qcms.h @@ -0,0 +1,153 @@ +#ifndef QCMS_H +#define QCMS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* if we've already got an ICC_H header we can ignore the following */ +#ifndef ICC_H +/* icc34 defines */ + +/***************************************************************** + Copyright (c) 1994-1996 SunSoft, Inc. + + Rights Reserved + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without restrict- +ion, including without limitation the rights to use, copy, modify, +merge, publish distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON- +INFRINGEMENT. IN NO EVENT SHALL SUNSOFT, INC. OR ITS PARENT +COMPANY BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of SunSoft, Inc. +shall not be used in advertising or otherwise to promote the +sale, use or other dealings in this Software without written +authorization from SunSoft Inc. +******************************************************************/ + +/* + * Color Space Signatures + * Note that only icSigXYZData and icSigLabData are valid + * Profile Connection Spaces (PCSs) + */ +typedef enum { + icSigXYZData = 0x58595A20L, /* 'XYZ ' */ + icSigLabData = 0x4C616220L, /* 'Lab ' */ + icSigLuvData = 0x4C757620L, /* 'Luv ' */ + icSigYCbCrData = 0x59436272L, /* 'YCbr' */ + icSigYxyData = 0x59787920L, /* 'Yxy ' */ + icSigRgbData = 0x52474220L, /* 'RGB ' */ + icSigGrayData = 0x47524159L, /* 'GRAY' */ + icSigHsvData = 0x48535620L, /* 'HSV ' */ + icSigHlsData = 0x484C5320L, /* 'HLS ' */ + icSigCmykData = 0x434D594BL, /* 'CMYK' */ + icSigCmyData = 0x434D5920L, /* 'CMY ' */ + icSig2colorData = 0x32434C52L, /* '2CLR' */ + icSig3colorData = 0x33434C52L, /* '3CLR' */ + icSig4colorData = 0x34434C52L, /* '4CLR' */ + icSig5colorData = 0x35434C52L, /* '5CLR' */ + icSig6colorData = 0x36434C52L, /* '6CLR' */ + icSig7colorData = 0x37434C52L, /* '7CLR' */ + icSig8colorData = 0x38434C52L, /* '8CLR' */ + icSig9colorData = 0x39434C52L, /* '9CLR' */ + icSig10colorData = 0x41434C52L, /* 'ACLR' */ + icSig11colorData = 0x42434C52L, /* 'BCLR' */ + icSig12colorData = 0x43434C52L, /* 'CCLR' */ + icSig13colorData = 0x44434C52L, /* 'DCLR' */ + icSig14colorData = 0x45434C52L, /* 'ECLR' */ + icSig15colorData = 0x46434C52L, /* 'FCLR' */ + icMaxEnumData = 0xFFFFFFFFL +} icColorSpaceSignature; +#endif + +#include + +typedef int qcms_bool; + +struct _qcms_transform; +typedef struct _qcms_transform qcms_transform; + +struct _qcms_profile; +typedef struct _qcms_profile qcms_profile; + +/* these values match the Rendering Intent values from the ICC spec */ +typedef enum { + QCMS_INTENT_DEFAULT = 0, + QCMS_INTENT_PERCEPTUAL = 0, + QCMS_INTENT_RELATIVE_COLORIMETRIC = 1, + QCMS_INTENT_SATURATION = 2, + QCMS_INTENT_ABSOLUTE_COLORIMETRIC = 3 +} qcms_intent; + +//XXX: I don't really like the _DATA_ prefix +typedef enum { + QCMS_DATA_RGB_8, + QCMS_DATA_RGBA_8, + QCMS_DATA_GRAY_8, + QCMS_DATA_GRAYA_8 +} qcms_data_type; + +/* the names for the following two types are sort of ugly */ +typedef struct +{ + double x; + double y; + double Y; +} qcms_CIE_xyY; + +typedef struct +{ + qcms_CIE_xyY red; + qcms_CIE_xyY green; + qcms_CIE_xyY blue; +} qcms_CIE_xyYTRIPLE; + +qcms_profile* qcms_profile_create_rgb_with_gamma( + qcms_CIE_xyY white_point, + qcms_CIE_xyYTRIPLE primaries, + float gamma); + +qcms_profile* qcms_profile_from_memory(const void *mem, size_t size); + +qcms_profile* qcms_profile_from_file(FILE *file); +qcms_profile* qcms_profile_from_path(const char *path); +qcms_profile* qcms_profile_sRGB(void); +void qcms_profile_release(qcms_profile *profile); + +qcms_bool qcms_profile_is_bogus(qcms_profile *profile); +qcms_intent qcms_profile_get_rendering_intent(qcms_profile *profile); +icColorSpaceSignature qcms_profile_get_color_space(qcms_profile *profile); + +void qcms_profile_precache_output_transform(qcms_profile *profile); + +qcms_transform* qcms_transform_create( + qcms_profile *in, qcms_data_type in_type, + qcms_profile* out, qcms_data_type out_type, + qcms_intent intent); + +void qcms_transform_release(qcms_transform *); + +void qcms_transform_data(qcms_transform *transform, void *src, void *dest, size_t length); + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gfx/qcms/qcmsint.h b/gfx/qcms/qcmsint.h new file mode 100644 index 00000000000..a85f278ed5a --- /dev/null +++ b/gfx/qcms/qcmsint.h @@ -0,0 +1,143 @@ +#include +#include "qcms.h" +#include "qcmstypes.h" + +/* used as a 16bit lookup table for the output transformation. + * we refcount them so we only need to have one around per output + * profile, instead of duplicating them per transform */ +struct precache_output +{ + int ref_count; + uint8_t data[65535]; +}; + +#ifdef _MSC_VER +#define ALIGN __declspec(align(16)) +#else +#define ALIGN __attribute__(( aligned (16) )) +#endif + +struct _qcms_transform { + float ALIGN matrix[3][4]; + float *input_gamma_table_r; + float *input_gamma_table_g; + float *input_gamma_table_b; + + float *input_gamma_table_gray; + + float out_gamma_r; + float out_gamma_g; + float out_gamma_b; + + float out_gamma_gray; + + uint16_t *output_gamma_lut_r; + uint16_t *output_gamma_lut_g; + uint16_t *output_gamma_lut_b; + + uint16_t *output_gamma_lut_gray; + + size_t output_gamma_lut_r_length; + size_t output_gamma_lut_g_length; + size_t output_gamma_lut_b_length; + + size_t output_gamma_lut_gray_length; + + struct precache_output *output_table_r; + struct precache_output *output_table_g; + struct precache_output *output_table_b; + + void (*transform_fn)(struct _qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length); +}; + +typedef int32_t s15Fixed16Number; +typedef uint16_t uInt16Number; + +struct XYZNumber { + s15Fixed16Number X; + s15Fixed16Number Y; + s15Fixed16Number Z; +}; + +struct curveType { + uint32_t count; + uInt16Number data[0]; +}; + +struct lutType { + uint8_t num_input_channels; + uint8_t num_output_channels; + uint8_t num_clut_grid_points; + + s15Fixed16Number e00; + s15Fixed16Number e01; + s15Fixed16Number e02; + s15Fixed16Number e10; + s15Fixed16Number e11; + s15Fixed16Number e12; + s15Fixed16Number e20; + s15Fixed16Number e21; + s15Fixed16Number e22; + + uint16_t num_input_table_entries; + uint16_t num_output_table_entries; + + uint16_t *input_table; + uint16_t *clut_table; + uint16_t *output_table; +}; +#if 0 +this is from an intial idea of having the struct correspond to the data in +the file. I decided that it wasn't a good idea. +struct tag_value { + uint32_t type; + union { + struct { + uint32_t reserved; + struct { + s15Fixed16Number X; + s15Fixed16Number Y; + s15Fixed16Number Z; + } XYZNumber; + } XYZType; + }; +}; // I guess we need to pack this? +#endif + +#define RGB_SIGNATURE 0x52474220 +#define GRAY_SIGNATURE 0x47524159 + +struct _qcms_profile { + uint32_t class; + uint32_t color_space; + qcms_intent rendering_intent; + struct XYZNumber redColorant; + struct XYZNumber blueColorant; + struct XYZNumber greenColorant; + struct curveType *redTRC; + struct curveType *blueTRC; + struct curveType *greenTRC; + struct curveType *grayTRC; + struct lutType *A2B0; + + struct precache_output *output_table_r; + struct precache_output *output_table_g; + struct precache_output *output_table_b; +}; + +#ifdef _MSC_VER +#define inline _inline +#endif + +static inline float s15Fixed16Number_to_float(s15Fixed16Number a) +{ + return ((int32_t)a)/65536.; +} + +static inline s15Fixed16Number double_to_s15Fixed16Number(double v) +{ + return (int32_t)(v*65536); +} + +void precache_release(struct precache_output *p); +void set_rgb_colorants(qcms_profile *profile, qcms_CIE_xyY white_point, qcms_CIE_xyYTRIPLE primaries); diff --git a/gfx/qcms/qcmstypes.h b/gfx/qcms/qcmstypes.h new file mode 100644 index 00000000000..65bc7dee904 --- /dev/null +++ b/gfx/qcms/qcmstypes.h @@ -0,0 +1,49 @@ +#ifndef QCMS_TYPES_H +#define QCMS_TYPES_H + +#ifdef MOZ_QCMS + +#include "prtypes.h" + +typedef PRInt8 int8_t; +typedef PRUint8 uint8_t; +typedef PRInt16 int16_t; +typedef PRUint16 uint16_t; +typedef PRInt32 int32_t; +typedef PRUint32 uint32_t; +typedef PRInt64 int64_t; +typedef PRUint64 uint64_t; +typedef PRUptrdiff uintptr_t; + +#else + +#if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || defined (_sgi) || defined (__sun) || defined (sun) || defined (__digital__) +# include +#elif defined (_MSC_VER) +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#ifdef _WIN64 +typedef unsigned __int64 uintptr_t; +#else +typedef unsigned long uintptr_t; +#endif + +#elif defined (_AIX) +# include +#else +# include +#endif + +#endif + +typedef qcms_bool bool; +#define true 1 +#define false 0 + +#endif diff --git a/gfx/qcms/transform.c b/gfx/qcms/transform.c new file mode 100644 index 00000000000..e079d67f95c --- /dev/null +++ b/gfx/qcms/transform.c @@ -0,0 +1,1535 @@ +// qcms +// Copyright (C) 2009 Mozilla Corporation +// Copyright (C) 1998-2007 Marti Maria +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#include +#include +#include +#include "qcmsint.h" + +#if defined(_M_IX86) || defined(__i386__) +#define X86 +#endif + +//XXX: could use a bettername +typedef uint16_t uint16_fract_t; + +/* value must be a value between 0 and 1 */ +//XXX: is the above a good restriction to have? +float lut_interp_linear(double value, uint16_t *table, int length) +{ + int upper, lower; + value = value * (length - 1); + upper = ceil(value); + lower = floor(value); + //XXX: can we be more performant here? + value = table[upper]*(1. - (upper - value)) + table[lower]*(upper - value); + /* scale the value */ + return value * (1./65535.); +} + +/* same as above but takes and returns a uint16_t value representing a range from 0..1 */ +uint16_t lut_interp_linear16(uint16_t input_value, uint16_t *table, int length) +{ + uint32_t value = (input_value * (length - 1)); + uint32_t upper = (value + 65534) / 65535; /* equivalent to ceil(value/65535) */ + uint32_t lower = value / 65535; /* equivalent to floor(value/65535) */ + uint32_t interp = value % 65535; + + value = (table[upper]*(interp) + table[lower]*(65535 - interp))/65535; + + return value; +} + +void compute_curve_gamma_table_type1(float gamma_table[256], double gamma) +{ + unsigned int i; + for (i = 0; i < 256; i++) { + gamma_table[i] = pow(i/255., gamma); + } +} + +void compute_curve_gamma_table_type2(float gamma_table[256], uint16_t *table, int length) +{ + unsigned int i; + for (i = 0; i < 256; i++) { + gamma_table[i] = lut_interp_linear(i/255., table, length); + } +} + +void compute_curve_gamma_table_type0(float gamma_table[256]) +{ + unsigned int i; + for (i = 0; i < 256; i++) { + gamma_table[i] = i; + } +} + +unsigned char clamp_u8(float v) +{ + if (v > 255.) + return 255; + else if (v < 0) + return 0; + else + return floor(v+.5); +} + +struct vector { + float v[3]; +}; + +struct matrix { + float m[3][3]; + bool invalid; +}; + +struct vector matrix_eval(struct matrix mat, struct vector v) +{ + struct vector result; + result.v[0] = mat.m[0][0]*v.v[0] + mat.m[0][1]*v.v[1] + mat.m[0][2]*v.v[2]; + result.v[1] = mat.m[1][0]*v.v[0] + mat.m[1][1]*v.v[1] + mat.m[1][2]*v.v[2]; + result.v[2] = mat.m[2][0]*v.v[0] + mat.m[2][1]*v.v[1] + mat.m[2][2]*v.v[2]; + return result; +} + +//XXX: should probably pass by reference and we could +//probably reuse this computation in matrix_invert +float matrix_det(struct matrix mat) +{ + float det; + det = mat.m[0][0]*mat.m[1][1]*mat.m[2][2] + + mat.m[0][1]*mat.m[1][2]*mat.m[2][0] + + mat.m[0][2]*mat.m[1][0]*mat.m[2][1] - + mat.m[0][0]*mat.m[1][2]*mat.m[2][1] - + mat.m[0][1]*mat.m[1][0]*mat.m[2][2] - + mat.m[0][2]*mat.m[1][1]*mat.m[2][0]; + return det; +} + +/* from pixman and cairo and Mathematics for Game Programmers */ +/* lcms uses gauss-jordan elimination with partial pivoting which is + * less efficient and not as numerically stable. See Mathematics for + * Game Programmers. */ +struct matrix matrix_invert(struct matrix mat) +{ + struct matrix dest_mat; + int i,j; + static int a[3] = { 2, 2, 1 }; + static int b[3] = { 1, 0, 0 }; + + /* inv (A) = 1/det (A) * adj (A) */ + float det = matrix_det(mat); + + if (det == 0) { + dest_mat.invalid = true; + } else { + dest_mat.invalid = false; + } + + det = 1/det; + + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) { + double p; + int ai = a[i]; + int aj = a[j]; + int bi = b[i]; + int bj = b[j]; + + p = mat.m[ai][aj] * mat.m[bi][bj] - + mat.m[ai][bj] * mat.m[bi][aj]; + if (((i + j) & 1) != 0) + p = -p; + + dest_mat.m[j][i] = det * p; + } + } + return dest_mat; +} + +struct matrix matrix_identity(void) +{ + struct matrix i; + i.m[0][0] = 1; + i.m[0][1] = 0; + i.m[0][2] = 0; + i.m[1][0] = 0; + i.m[1][1] = 1; + i.m[1][2] = 0; + i.m[2][0] = 0; + i.m[2][1] = 0; + i.m[2][2] = 1; + i.invalid = false; + return i; +} + +/* from pixman */ +/* MAT3per... */ +struct matrix matrix_multiply(struct matrix a, struct matrix b) +{ + struct matrix result; + int dx, dy; + int o; + for (dy = 0; dy < 3; dy++) { + for (dx = 0; dx < 3; dx++) { + double v = 0; + for (o = 0; o < 3; o++) { + v += a.m[dy][o] * b.m[o][dx]; + } + result.m[dy][dx] = v; + } + } + result.invalid = a.invalid || b.invalid; + return result; +} + +float u8Fixed8Number_to_float(uint16_t x) +{ + // 0x0000 = 0. + // 0x0100 = 1. + // 0xffff = 255 + 255/256 + return x/256.; +} + +float *build_input_gamma_table(struct curveType *TRC) +{ + float *gamma_table = malloc(sizeof(float)*256); + if (gamma_table) { + if (TRC->count == 0) { + compute_curve_gamma_table_type0(gamma_table); + } else if (TRC->count == 1) { + compute_curve_gamma_table_type1(gamma_table, u8Fixed8Number_to_float(TRC->data[0])); + } else { + compute_curve_gamma_table_type2(gamma_table, TRC->data, TRC->count); + } + } + return gamma_table; +} + +struct matrix build_colorant_matrix(qcms_profile *p) +{ + struct matrix result; + result.m[0][0] = s15Fixed16Number_to_float(p->redColorant.X); + result.m[0][1] = s15Fixed16Number_to_float(p->greenColorant.X); + result.m[0][2] = s15Fixed16Number_to_float(p->blueColorant.X); + result.m[1][0] = s15Fixed16Number_to_float(p->redColorant.Y); + result.m[1][1] = s15Fixed16Number_to_float(p->greenColorant.Y); + result.m[1][2] = s15Fixed16Number_to_float(p->blueColorant.Y); + result.m[2][0] = s15Fixed16Number_to_float(p->redColorant.Z); + result.m[2][1] = s15Fixed16Number_to_float(p->greenColorant.Z); + result.m[2][2] = s15Fixed16Number_to_float(p->blueColorant.Z); + result.invalid = false; + return result; +} + +/* The following code is copied nearly directly from lcms. + * I think it could be much better. For example, Argyll seems to have better code in + * icmTable_lookup_bwd and icmTable_setup_bwd. However, for now this is a quick way + * to a working solution and allows for easy comparing with lcms. */ +uint16_fract_t lut_inverse_interp16(uint16_t Value, uint16_t LutTable[], int length) +{ + int l = 1; + int r = 0x10000; + int x = 0, res; // 'int' Give spacing for negative values + int NumZeroes, NumPoles; + int cell0, cell1; + double val2; + double y0, y1, x0, x1; + double a, b, f; + + // July/27 2001 - Expanded to handle degenerated curves with an arbitrary + // number of elements containing 0 at the begining of the table (Zeroes) + // and another arbitrary number of poles (FFFFh) at the end. + // First the zero and pole extents are computed, then value is compared. + + NumZeroes = 0; + while (LutTable[NumZeroes] == 0 && NumZeroes < length-1) + NumZeroes++; + + // There are no zeros at the beginning and we are trying to find a zero, so + // return anything. It seems zero would be the less destructive choice + /* I'm not sure that this makes sense, but oh well... */ + if (NumZeroes == 0 && Value == 0) + return 0; + + NumPoles = 0; + while (LutTable[length-1- NumPoles] == 0xFFFF && NumPoles < length-1) + NumPoles++; + + // Does the curve belong to this case? + if (NumZeroes > 1 || NumPoles > 1) + { + int a, b; + + // Identify if value fall downto 0 or FFFF zone + if (Value == 0) return 0; + // if (Value == 0xFFFF) return 0xFFFF; + + // else restrict to valid zone + + a = ((NumZeroes-1) * 0xFFFF) / (length-1); + b = ((length-1 - NumPoles) * 0xFFFF) / (length-1); + + l = a - 1; + r = b + 1; + } + + + // Seems not a degenerated case... apply binary search + + while (r > l) { + + x = (l + r) / 2; + + res = (int) lut_interp_linear16((uint16_fract_t) (x-1), LutTable, length); + + if (res == Value) { + + // Found exact match. + + return (uint16_fract_t) (x - 1); + } + + if (res > Value) r = x - 1; + else l = x + 1; + } + + // Not found, should we interpolate? + + + // Get surrounding nodes + + val2 = (length-1) * ((double) (x - 1) / 65535.0); + + cell0 = (int) floor(val2); + cell1 = (int) ceil(val2); + + if (cell0 == cell1) return (uint16_fract_t) x; + + y0 = LutTable[cell0] ; + x0 = (65535.0 * cell0) / (length-1); + + y1 = LutTable[cell1] ; + x1 = (65535.0 * cell1) / (length-1); + + a = (y1 - y0) / (x1 - x0); + b = y0 - a * x0; + + if (fabs(a) < 0.01) return (uint16_fract_t) x; + + f = ((Value - b) / a); + + if (f < 0.0) return (uint16_fract_t) 0; + if (f >= 65535.0) return (uint16_fract_t) 0xFFFF; + + return (uint16_fract_t) floor(f + 0.5); + +} + +// Build a White point, primary chromas transfer matrix from RGB to CIE XYZ +// This is just an approximation, I am not handling all the non-linear +// aspects of the RGB to XYZ process, and assumming that the gamma correction +// has transitive property in the tranformation chain. +// +// the alghoritm: +// +// - First I build the absolute conversion matrix using +// primaries in XYZ. This matrix is next inverted +// - Then I eval the source white point across this matrix +// obtaining the coeficients of the transformation +// - Then, I apply these coeficients to the original matrix +static struct matrix build_RGB_to_XYZ_transfer_matrix(qcms_CIE_xyY white, qcms_CIE_xyYTRIPLE primrs) +{ + struct matrix primaries; + struct matrix primaries_invert; + struct matrix result; + struct vector white_point; + struct vector coefs; + + double xn, yn; + double xr, yr; + double xg, yg; + double xb, yb; + + xn = white.x; + yn = white.y; + + xr = primrs.red.x; + yr = primrs.red.y; + xg = primrs.green.x; + yg = primrs.green.y; + xb = primrs.blue.x; + yb = primrs.blue.y; + + primaries.m[0][0] = xr; + primaries.m[0][1] = xg; + primaries.m[0][2] = xb; + + primaries.m[1][0] = yr; + primaries.m[1][1] = yg; + primaries.m[1][2] = yb; + + primaries.m[2][0] = 1 - xr - yr; + primaries.m[2][1] = 1 - xg - yg; + primaries.m[2][2] = 1 - xb - yb; + primaries.invalid = false; + + white_point.v[0] = xn/yn; + white_point.v[1] = 1.; + white_point.v[2] = (1.0-xn-yn)/yn; + + primaries_invert = matrix_invert(primaries); + + coefs = matrix_eval(primaries_invert, white_point); + + result.m[0][0] = coefs.v[0]*xr; + result.m[0][1] = coefs.v[1]*xg; + result.m[0][2] = coefs.v[2]*xb; + + result.m[1][0] = coefs.v[0]*yr; + result.m[1][1] = coefs.v[1]*yg; + result.m[1][2] = coefs.v[2]*yb; + + result.m[2][0] = coefs.v[0]*(1.-xr-yr); + result.m[2][1] = coefs.v[1]*(1.-xg-yg); + result.m[2][2] = coefs.v[2]*(1.-xb-yb); + result.invalid = primaries_invert.invalid; + + return result; +} + +struct CIE_XYZ { + double X; + double Y; + double Z; +}; + +/* CIE Illuminant D50 */ +static const struct CIE_XYZ D50_XYZ = { + 0.9642, + 1.0000, + 0.8249 +}; + +/* from lcms: xyY2XYZ() + * corresponds to argyll: icmYxy2XYZ() */ +static struct CIE_XYZ xyY2XYZ(qcms_CIE_xyY source) +{ + struct CIE_XYZ dest; + dest.X = (source.x / source.y) * source.Y; + dest.Y = source.Y; + dest.Z = ((1 - source.x - source.y) / source.y) * source.Y; + return dest; +} + +/* from lcms: ComputeChromaticAdaption */ +// Compute chromatic adaption matrix using chad as cone matrix +static struct matrix +compute_chromatic_adaption(struct CIE_XYZ source_white_point, + struct CIE_XYZ dest_white_point, + struct matrix chad) +{ + struct matrix chad_inv; + struct vector cone_source_XYZ, cone_source_rgb; + struct vector cone_dest_XYZ, cone_dest_rgb; + struct matrix cone, tmp; + + tmp = chad; + chad_inv = matrix_invert(tmp); + + cone_source_XYZ.v[0] = source_white_point.X; + cone_source_XYZ.v[1] = source_white_point.Y; + cone_source_XYZ.v[2] = source_white_point.Z; + + cone_dest_XYZ.v[0] = dest_white_point.X; + cone_dest_XYZ.v[1] = dest_white_point.Y; + cone_dest_XYZ.v[2] = dest_white_point.Z; + + cone_source_rgb = matrix_eval(chad, cone_source_XYZ); + cone_dest_rgb = matrix_eval(chad, cone_dest_XYZ); + + cone.m[0][0] = cone_dest_rgb.v[0]/cone_source_rgb.v[0]; + cone.m[0][1] = 0; + cone.m[0][2] = 0; + cone.m[1][0] = 0; + cone.m[1][1] = cone_dest_rgb.v[1]/cone_source_rgb.v[1]; + cone.m[1][2] = 0; + cone.m[2][0] = 0; + cone.m[2][1] = 0; + cone.m[2][2] = cone_dest_rgb.v[2]/cone_source_rgb.v[2]; + cone.invalid = false; + + // Normalize + return matrix_multiply(chad_inv, matrix_multiply(cone, chad)); +} + +/* from lcms: cmsAdaptionMatrix */ +// Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll +// Bradford is assumed +static struct matrix +adaption_matrix(struct CIE_XYZ source_illumination, struct CIE_XYZ target_illumination) +{ + struct matrix lam_rigg = {{ // Bradford matrix + { 0.8951, 0.2664, -0.1614 }, + { -0.7502, 1.7135, 0.0367 }, + { 0.0389, -0.0685, 1.0296 } + }}; + return compute_chromatic_adaption(source_illumination, target_illumination, lam_rigg); +} + +/* from lcms: cmsAdaptMatrixToD50 */ +static struct matrix adapt_matrix_to_D50(struct matrix r, qcms_CIE_xyY source_white_pt) +{ + struct CIE_XYZ Dn; + struct matrix Bradford; + Dn = xyY2XYZ(source_white_pt); + + Bradford = adaption_matrix(Dn, D50_XYZ); + return matrix_multiply(Bradford, r); +} + +void set_rgb_colorants(qcms_profile *profile, qcms_CIE_xyY white_point, qcms_CIE_xyYTRIPLE primaries) +{ + struct matrix colorants; + colorants = build_RGB_to_XYZ_transfer_matrix(white_point, primaries); + colorants = adapt_matrix_to_D50(colorants, white_point); + + /* note: there's a transpose type of operation going on here */ + profile->redColorant.X = double_to_s15Fixed16Number(colorants.m[0][0]); + profile->redColorant.Y = double_to_s15Fixed16Number(colorants.m[1][0]); + profile->redColorant.Z = double_to_s15Fixed16Number(colorants.m[2][0]); + + profile->greenColorant.X = double_to_s15Fixed16Number(colorants.m[0][1]); + profile->greenColorant.Y = double_to_s15Fixed16Number(colorants.m[1][1]); + profile->greenColorant.Z = double_to_s15Fixed16Number(colorants.m[2][1]); + + profile->blueColorant.X = double_to_s15Fixed16Number(colorants.m[0][2]); + profile->blueColorant.Y = double_to_s15Fixed16Number(colorants.m[1][2]); + profile->blueColorant.Z = double_to_s15Fixed16Number(colorants.m[2][2]); +} + +static uint16_t *invert_lut(uint16_t *table, int length) +{ + int i; + /* for now we invert the lut by creating a lut of the same size + * and attempting to lookup a value for each entry using lut_inverse_interp16 */ + uint16_t *output = malloc(sizeof(uint16_t)*length); + if (!output) + return NULL; + + for (i = 0; i < length; i++) { + double x = ((double) i * 65535.) / (double) (length - 1); + uint16_fract_t input = floor(x + .5); + output[i] = lut_inverse_interp16(input, table, length); + } + return output; +} + +static uint16_t *build_linear_table(int length) +{ + int i; + uint16_t *output = malloc(sizeof(uint16_t)*length); + if (!output) + return NULL; + + for (i = 0; i < length; i++) { + double x = ((double) i * 65535.) / (double) (length - 1); + uint16_fract_t input = floor(x + .5); + output[i] = input; + } + return output; +} + +static uint16_t *build_pow_table(float gamma, int length) +{ + int i; + uint16_t *output = malloc(sizeof(uint16_t)*length); + if (!output) + return NULL; + + for (i = 0; i < length; i++) { + uint16_fract_t result; + double x = ((double) i) / (double) (length - 1); + x = pow(x, gamma); + //XXX turn this conversion into a function + result = floor(x*65535. + .5); + output[i] = result; + } + return output; +} + +static float clamp_float(float a) +{ + if (a > 1.) + return 1.; + else if (a < 0) + return 0; + else + return a; +} + +#if 0 +static void qcms_transform_data_rgb_out_pow(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) +{ + int i; + float (*mat)[4] = transform->matrix; + for (i=0; iinput_gamma_table_r[device_r]; + float linear_g = transform->input_gamma_table_g[device_g]; + float linear_b = transform->input_gamma_table_b[device_b]; + + float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b; + float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b; + float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b; + + float out_device_r = pow(out_linear_r, transform->out_gamma_r); + float out_device_g = pow(out_linear_g, transform->out_gamma_g); + float out_device_b = pow(out_linear_b, transform->out_gamma_b); + + *dest++ = clamp_u8(255*out_device_r); + *dest++ = clamp_u8(255*out_device_g); + *dest++ = clamp_u8(255*out_device_b); + } +} +#endif + +static void qcms_transform_data_gray_out_lut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) +{ + int i; + for (i = 0; i < length; i++) { + float out_device_r, out_device_g, out_device_b; + unsigned char device = *src++; + + float linear = transform->input_gamma_table_gray[device]; + + out_device_r = lut_interp_linear(linear, transform->output_gamma_lut_r, transform->output_gamma_lut_r_length); + out_device_g = lut_interp_linear(linear, transform->output_gamma_lut_g, transform->output_gamma_lut_g_length); + out_device_b = lut_interp_linear(linear, transform->output_gamma_lut_b, transform->output_gamma_lut_b_length); + + *dest++ = clamp_u8(out_device_r*255); + *dest++ = clamp_u8(out_device_g*255); + *dest++ = clamp_u8(out_device_b*255); + } +} + +static void qcms_transform_data_graya_out_lut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) +{ + int i; + for (i = 0; i < length; i++) { + float out_device_r, out_device_g, out_device_b; + unsigned char device = *src++; + unsigned char alpha = *src++; + + float linear = transform->input_gamma_table_gray[device]; + + out_device_r = lut_interp_linear(linear, transform->output_gamma_lut_r, transform->output_gamma_lut_r_length); + out_device_g = lut_interp_linear(linear, transform->output_gamma_lut_g, transform->output_gamma_lut_g_length); + out_device_b = lut_interp_linear(linear, transform->output_gamma_lut_b, transform->output_gamma_lut_b_length); + + *dest++ = clamp_u8(out_device_r*255); + *dest++ = clamp_u8(out_device_g*255); + *dest++ = clamp_u8(out_device_b*255); + *dest++ = alpha; + } +} + + +static void qcms_transform_data_gray_out_precache(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) +{ + int i; + for (i = 0; i < length; i++) { + unsigned char device = *src++; + uint16_t gray; + + float linear = transform->input_gamma_table_gray[device]; + + /* we could round here... */ + gray = linear * 65535.; + + *dest++ = transform->output_table_r->data[gray]; + *dest++ = transform->output_table_g->data[gray]; + *dest++ = transform->output_table_b->data[gray]; + } +} + +static void qcms_transform_data_graya_out_precache(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) +{ + int i; + for (i = 0; i < length; i++) { + unsigned char device = *src++; + unsigned char alpha = *src++; + uint16_t gray; + + float linear = transform->input_gamma_table_gray[device]; + + /* we could round here... */ + gray = linear * 65535.; + + *dest++ = transform->output_table_r->data[gray]; + *dest++ = transform->output_table_g->data[gray]; + *dest++ = transform->output_table_b->data[gray]; + *dest++ = alpha; + } +} + +static const ALIGN float floatScale = 65536.0f; +static const ALIGN float * const floatScaleAddr = &floatScale; // Win32 ASM doesn't know how to take addressOf inline + +static const ALIGN float clampMaxValue = ((float) (65536 - 1)) / 65536.0f; + +#ifdef X86 +#if 0 +#include +void qcms_transform_data_rgb_out_lut_sse_intrin(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) +{ + int i; + float (*mat)[4] = transform->matrix; + char input_back[32]; + /* Ensure we have a buffer that's 16 byte aligned regardless of the original + * stack alignment. We can't use __attribute__((aligned(16))) or __declspec(align(32)) + * because they don't work on stack variables. gcc 4.4 does do the right thing + * on x86 but that's too new for us right now. For more info: gcc bug #16660 */ + float *input = (float*)(((uintptr_t)&input_back[16]) & ~0xf); + /* share input and output locations to save having to keep the + * locations in separate registers */ + uint32_t* output = (uint32_t*)input; + for (i=0; iinput_gamma_table_r[device_r]); + vec_r = _mm_shuffle_ps(vec_r, vec_r, 0); + __m128 vec_g = _mm_load_ss(&transform->input_gamma_table_r[device_g]); + vec_g = _mm_shuffle_ps(vec_g, vec_g, 0); + __m128 vec_b = _mm_load_ss(&transform->input_gamma_table_r[device_b]); + vec_b = _mm_shuffle_ps(vec_b, vec_b, 0); + + vec_r = _mm_mul_ps(vec_r, xmm1); + vec_g = _mm_mul_ps(vec_g, xmm2); + vec_b = _mm_mul_ps(vec_b, xmm3); + + vec_r = _mm_add_ps(vec_r, _mm_add_ps(vec_g, vec_b)); + + __m128 max = _mm_load_ss(&clampMax); + max = _mm_shuffle_ps(max, max, 0); + __m128 min = _mm_setzero_ps(); + + vec_r = _mm_max_ps(min, vec_r); + vec_r = _mm_min_ps(max, vec_r); + + __m128 scale = _mm_load_ss(&floatScale); + scale = _mm_shuffle_ps(scale, scale, 0); + __m128 result = _mm_mul_ps(vec_r, scale); + + __m128i out = _mm_cvtps_epi32(result); + _mm_store_si128((__m128i*)input, out); + + *dest++ = transform->output_table_r->data[output[0]]; + *dest++ = transform->output_table_g->data[output[1]]; + *dest++ = transform->output_table_b->data[output[2]]; + } +} +#endif +static void qcms_transform_data_rgb_out_lut_sse(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) +{ + int i; + float (*mat)[4] = transform->matrix; + char input_back[32]; + /* Ensure we have a buffer that's 16 byte aligned regardless of the original + * stack alignment. We can't use __attribute__((aligned(16))) or __declspec(align(32)) + * because they don't work on stack variables. gcc 4.4 does do the right thing + * on x86 but that's too new for us right now. For more info: gcc bug #16660 */ + float *input = (float*)(((uintptr_t)&input_back[16]) & ~0xf); + /* share input and output locations to save having to keep the + * locations in separate registers */ + uint32_t* output = (uint32_t*)input; + for (i = 0; i < length; i++) { + const float *clampMax = &clampMaxValue; + + unsigned char device_r = *src++; + unsigned char device_g = *src++; + unsigned char device_b = *src++; + + input[0] = transform->input_gamma_table_r[device_r]; + input[1] = transform->input_gamma_table_g[device_g]; + input[2] = transform->input_gamma_table_b[device_b]; + +#ifdef __GNUC__ + __asm( + "movaps (%0), %%xmm1;\n\t" // Move the first matrix column to xmm1 + "movaps 16(%0), %%xmm2;\n\t" // Move the second matrix column to xmm2 + "movaps 32(%0), %%xmm3;\n\t" // move the third matrix column to xmm3 + "movaps (%3), %%xmm0;\n\t" // Move the vector to xmm0 + + // Note - We have to copy and then shuffle because of the weird + // semantics of shufps + // + "movaps %%xmm0, %%xmm4;\n\t" // Copy the vector to xmm4 + "shufps $0, %%xmm4, %%xmm4;\n\t" // Shuffle to repeat the first vector element repeated 4 times + "mulps %%xmm4, %%xmm1;\n\t" // Multiply the first vector element by the first matrix column + "movaps %%xmm0, %%xmm5; \n\t" // Copy the vector to xmm5 + "shufps $0x55, %%xmm5, %%xmm5;\n\t" // Shuffle to repeat the second vector element repeated 4 times + "mulps %%xmm5, %%xmm2;\n\t" // Multiply the second vector element by the seccond matrix column + "movaps %%xmm0, %%xmm6;\n\t" // Copy the vector to xmm6 + "shufps $0xAA, %%xmm6, %%xmm6;\n\t" // Shuffle to repeat the third vector element repeated 4 times + "mulps %%xmm6, %%xmm3;\n\t" // Multiply the third vector element by the third matrix column + + "addps %%xmm3, %%xmm2;\n\t" // Sum (second + third) columns + "addps %%xmm2, %%xmm1;\n\t" // Sum ((second + third) + first) columns + + "movss (%1), %%xmm7;\n\t" // load the floating point representation of 65535/65536 + "shufps $0, %%xmm7, %%xmm7;\n\t" // move it into all of the four slots + "minps %%xmm7, %%xmm1;\n\t" // clamp the vector to 1.0 max + "xorps %%xmm6, %%xmm6;\n\t" // get us cleared bitpatern, which is 0.0f + "maxps %%xmm6, %%xmm1;\n\t" // clamp the vector to 0.0 min + "movss (%2), %%xmm5;\n\t" // load the floating point scale factor + "shufps $0, %%xmm5, %%xmm5;\n\t" // put it in all four slots + "mulps %%xmm5, %%xmm1;\n\t" // multiply by the scale factor + "cvtps2dq %%xmm1, %%xmm1;\n\t" // convert to integers + "movdqa %%xmm1, (%3);\n\t" // store + + : + : "r" (mat), "r" (clampMax), "r" (&floatScale), "r" (input) + : "memory" +/* older versions of gcc don't know about these registers so only include them as constraints + if gcc knows about them */ +#ifdef __SSE2__ + , "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7" +#endif + ); +#else + __asm { + mov eax, mat + mov ecx, clampMax + mov edx, floatScaleAddr + mov ebx, input + + movaps xmm1, [eax] + movaps xmm2, [eax + 16] + movaps xmm3, [eax + 32] + movaps xmm0, [ebx] + + movaps xmm4, xmm0 + shufps xmm4, xmm4, 0 + mulps xmm1, xmm4 + movaps xmm5, xmm0 + shufps xmm5, xmm5, 0x55 + mulps xmm2, xmm5 + movaps xmm6, xmm0 + shufps xmm6, xmm6, 0xAA + mulps xmm3, xmm6 + + addps xmm2, xmm3 + addps xmm1, xmm2 + + movss xmm7, [ecx] + shufps xmm7, xmm7, 0 + minps xmm1, xmm7 + xorps xmm6, xmm6 + maxps xmm1, xmm6 + movss xmm5, [edx] + shufps xmm5, xmm5, 0 + mulps xmm1, xmm5 + cvtps2dq xmm1, xmm1 + movdqa [ebx], xmm1 + } +#endif + + *dest++ = transform->output_table_r->data[output[0]]; + *dest++ = transform->output_table_g->data[output[1]]; + *dest++ = transform->output_table_b->data[output[2]]; + } +} + +static void qcms_transform_data_rgba_out_lut_sse(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) +{ + int i; + float (*mat)[4] = transform->matrix; + char input_back[32]; + /* align input on 16 byte boundary */ + float *input = (float*)(((uintptr_t)&input_back[16]) & ~0xf); + /* share input and output locations to save having to keep the + * locations in separate registers */ + uint32_t* output = (uint32_t*)input; + for (i = 0; i < length; i++) { + const float *clampMax = &clampMaxValue; + + unsigned char device_r = *src++; + unsigned char device_g = *src++; + unsigned char device_b = *src++; + unsigned char alpha = *src++; + + input[0] = transform->input_gamma_table_r[device_r]; + input[1] = transform->input_gamma_table_g[device_g]; + input[2] = transform->input_gamma_table_b[device_b]; + +#ifdef __GNUC__ + __asm( + "movaps (%0), %%xmm1;\n\t" // Move the first matrix column to xmm1 + "movaps 16(%0), %%xmm2;\n\t" // Move the second matrix column to xmm2 + "movaps 32(%0), %%xmm3;\n\t" // move the third matrix column to xmm3 + "movaps (%3), %%xmm0;\n\t" // Move the vector to xmm0 + + // Note - We have to copy and then shuffle because of the weird + // semantics of shufps + // + "movaps %%xmm0, %%xmm4;\n\t" // Copy the vector to xmm4 + "shufps $0, %%xmm4, %%xmm4;\n\t" // Shuffle to repeat the first vector element repeated 4 times + "mulps %%xmm4, %%xmm1;\n\t" // Multiply the first vector element by the first matrix column + "movaps %%xmm0, %%xmm5; \n\t" // Copy the vector to xmm5 + "shufps $0x55, %%xmm5, %%xmm5;\n\t" // Shuffle to repeat the second vector element repeated 4 times + "mulps %%xmm5, %%xmm2;\n\t" // Multiply the second vector element by the seccond matrix column + "movaps %%xmm0, %%xmm6;\n\t" // Copy the vector to xmm6 + "shufps $0xAA, %%xmm6, %%xmm6;\n\t" // Shuffle to repeat the third vector element repeated 4 times + "mulps %%xmm6, %%xmm3;\n\t" // Multiply the third vector element by the third matrix column + + "addps %%xmm3, %%xmm2;\n\t" // Sum (second + third) columns + "addps %%xmm2, %%xmm1;\n\t" // Sum ((second + third) + first) columns + + "movss (%1), %%xmm7;\n\t" // load the floating point representation of 65535/65536 + "shufps $0, %%xmm7, %%xmm7;\n\t" // move it into all of the four slots + "minps %%xmm7, %%xmm1;\n\t" // clamp the vector to 1.0 max + "xorps %%xmm6, %%xmm6;\n\t" // get us cleared bitpatern, which is 0.0f + "maxps %%xmm6, %%xmm1;\n\t" // clamp the vector to 0.0 min + "movss (%2), %%xmm5;\n\t" // load the floating point scale factor + "shufps $0, %%xmm5, %%xmm5;\n\t" // put it in all four slots + "mulps %%xmm5, %%xmm1;\n\t" // multiply by the scale factor + "cvtps2dq %%xmm1, %%xmm1;\n\t" // convert to integers + "movdqa %%xmm1, (%3);\n\t" // store + + : + : "r" (mat), "r" (clampMax), "r" (&floatScale), "r" (input) + : "memory" +/* older versions of gcc don't know about these registers so only include them as constraints + if gcc knows about them */ +#ifdef __SSE2__ + , "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7" +#endif + ); +#else + __asm { + mov eax, mat + mov ecx, clampMax + mov edx, floatScaleAddr + mov ebx, input + + movaps xmm1, [eax] + movaps xmm2, [eax + 16] + movaps xmm3, [eax + 32] + movaps xmm0, [ebx] + + movaps xmm4, xmm0 + shufps xmm4, xmm4, 0 + mulps xmm1, xmm4 + movaps xmm5, xmm0 + shufps xmm5, xmm5, 0x55 + mulps xmm2, xmm5 + movaps xmm6, xmm0 + shufps xmm6, xmm6, 0xAA + mulps xmm3, xmm6 + + addps xmm2, xmm3 + addps xmm1, xmm2 + + movss xmm7, [ecx] + shufps xmm7, xmm7, 0 + minps xmm1, xmm7 + xorps xmm6, xmm6 + maxps xmm1, xmm6 + movss xmm5, [edx] + shufps xmm5, xmm5, 0 + mulps xmm1, xmm5 + cvtps2dq xmm1, xmm1 + movdqa [ebx], xmm1 + } +#endif + + *dest++ = transform->output_table_r->data[output[0]]; + *dest++ = transform->output_table_g->data[output[1]]; + *dest++ = transform->output_table_b->data[output[2]]; + *dest++ = alpha; + } +} +#endif + +static void qcms_transform_data_rgb_out_lut_precache(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) +{ + int i; + float (*mat)[4] = transform->matrix; + for (i = 0; i < length; i++) { + unsigned char device_r = *src++; + unsigned char device_g = *src++; + unsigned char device_b = *src++; + uint16_t r, g, b; + + float linear_r = transform->input_gamma_table_r[device_r]; + float linear_g = transform->input_gamma_table_g[device_g]; + float linear_b = transform->input_gamma_table_b[device_b]; + + float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b; + float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b; + float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b; + + out_linear_r = clamp_float(out_linear_r); + out_linear_g = clamp_float(out_linear_g); + out_linear_b = clamp_float(out_linear_b); + + /* we could round here... */ + r = out_linear_r * 65535.; + g = out_linear_g * 65535.; + b = out_linear_b * 65535.; + + *dest++ = transform->output_table_r->data[r]; + *dest++ = transform->output_table_g->data[g]; + *dest++ = transform->output_table_b->data[b]; + } +} + +static void qcms_transform_data_rgba_out_lut_precache(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) +{ + int i; + float (*mat)[4] = transform->matrix; + for (i = 0; i < length; i++) { + unsigned char device_r = *src++; + unsigned char device_g = *src++; + unsigned char device_b = *src++; + unsigned char alpha = *src++; + uint16_t r, g, b; + + float linear_r = transform->input_gamma_table_r[device_r]; + float linear_g = transform->input_gamma_table_g[device_g]; + float linear_b = transform->input_gamma_table_b[device_b]; + + float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b; + float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b; + float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b; + + out_linear_r = clamp_float(out_linear_r); + out_linear_g = clamp_float(out_linear_g); + out_linear_b = clamp_float(out_linear_b); + + /* we could round here... */ + r = out_linear_r * 65535.; + g = out_linear_g * 65535.; + b = out_linear_b * 65535.; + + *dest++ = transform->output_table_r->data[r]; + *dest++ = transform->output_table_g->data[g]; + *dest++ = transform->output_table_b->data[b]; + *dest++ = alpha; + } +} + +static void qcms_transform_data_rgb_out_lut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) +{ + int i; + float (*mat)[4] = transform->matrix; + for (i = 0; i < length; i++) { + unsigned char device_r = *src++; + unsigned char device_g = *src++; + unsigned char device_b = *src++; + float out_device_r, out_device_g, out_device_b; + + float linear_r = transform->input_gamma_table_r[device_r]; + float linear_g = transform->input_gamma_table_g[device_g]; + float linear_b = transform->input_gamma_table_b[device_b]; + + float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b; + float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b; + float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b; + + out_linear_r = clamp_float(out_linear_r); + out_linear_g = clamp_float(out_linear_g); + out_linear_b = clamp_float(out_linear_b); + + out_device_r = lut_interp_linear(out_linear_r, transform->output_gamma_lut_r, transform->output_gamma_lut_r_length); + out_device_g = lut_interp_linear(out_linear_g, transform->output_gamma_lut_g, transform->output_gamma_lut_g_length); + out_device_b = lut_interp_linear(out_linear_b, transform->output_gamma_lut_b, transform->output_gamma_lut_b_length); + + *dest++ = clamp_u8(out_device_r*255); + *dest++ = clamp_u8(out_device_g*255); + *dest++ = clamp_u8(out_device_b*255); + } +} + +static void qcms_transform_data_rgba_out_lut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) +{ + int i; + float (*mat)[4] = transform->matrix; + for (i = 0; i < length; i++) { + unsigned char device_r = *src++; + unsigned char device_g = *src++; + unsigned char device_b = *src++; + unsigned char alpha = *src++; + float out_device_r, out_device_g, out_device_b; + + float linear_r = transform->input_gamma_table_r[device_r]; + float linear_g = transform->input_gamma_table_g[device_g]; + float linear_b = transform->input_gamma_table_b[device_b]; + + float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b; + float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b; + float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b; + + out_linear_r = clamp_float(out_linear_r); + out_linear_g = clamp_float(out_linear_g); + out_linear_b = clamp_float(out_linear_b); + + out_device_r = lut_interp_linear(out_linear_r, transform->output_gamma_lut_r, transform->output_gamma_lut_r_length); + out_device_g = lut_interp_linear(out_linear_g, transform->output_gamma_lut_g, transform->output_gamma_lut_g_length); + out_device_b = lut_interp_linear(out_linear_b, transform->output_gamma_lut_b, transform->output_gamma_lut_b_length); + + *dest++ = clamp_u8(out_device_r*255); + *dest++ = clamp_u8(out_device_g*255); + *dest++ = clamp_u8(out_device_b*255); + *dest++ = alpha; + } +} + +#if 0 +static void qcms_transform_data_rgb_out_linear(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) +{ + int i; + float (*mat)[4] = transform->matrix; + for (i = 0; i < length; i++) { + unsigned char device_r = *src++; + unsigned char device_g = *src++; + unsigned char device_b = *src++; + + float linear_r = transform->input_gamma_table_r[device_r]; + float linear_g = transform->input_gamma_table_g[device_g]; + float linear_b = transform->input_gamma_table_b[device_b]; + + float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b; + float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b; + float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b; + + *dest++ = clamp_u8(out_linear_r*255); + *dest++ = clamp_u8(out_linear_g*255); + *dest++ = clamp_u8(out_linear_b*255); + } +} +#endif + +static struct precache_output *precache_reference(struct precache_output *p) +{ + p->ref_count++; + return p; +} + +static struct precache_output *precache_create() +{ + struct precache_output *p = malloc(sizeof(struct precache_output)); + if (p) + p->ref_count = 1; + return p; +} + +void precache_release(struct precache_output *p) +{ + if (--p->ref_count == 0) { + free(p); + } +} + +#ifdef HAS_POSIX_MEMALIGN +static qcms_transform *transform_alloc(void) +{ + qcms_transform *t; + if (!posix_memalign(&t, 16, sizeof(*t))) { + return t; + } else { + return NULL; + } +} +static void transform_free(qcms_transform *t) +{ + free(t); +} +#else +static qcms_transform *transform_alloc(void) +{ + /* transform needs to be aligned on a 16byte boundrary */ + char *original_block = calloc(sizeof(qcms_transform) + sizeof(void*) + 16, 1); + /* make room for a pointer to the block returned by calloc */ + void *transform_start = original_block + sizeof(void*); + /* align transform_start */ + qcms_transform *transform_aligned = (qcms_transform*)(((uintptr_t)transform_start + 15) & ~0xf); + + /* store a pointer to the block returned by calloc so that we can free it later */ + void **(original_block_ptr) = (void**)transform_aligned; + if (!original_block) + return NULL; + original_block_ptr--; + *original_block_ptr = original_block; + + return transform_aligned; +} +static void transform_free(qcms_transform *t) +{ + /* get at the pointer to the unaligned block returned by calloc */ + void **p = (void**)t; + p--; + free(*p); +} +#endif + +void qcms_transform_release(qcms_transform *t) +{ + /* ensure we only free the gamma tables once even if there are + * multiple references to the same data */ + + if (t->output_table_r) + precache_release(t->output_table_r); + if (t->output_table_g) + precache_release(t->output_table_g); + if (t->output_table_b) + precache_release(t->output_table_b); + + free(t->input_gamma_table_r); + if (t->input_gamma_table_g != t->input_gamma_table_r) + free(t->input_gamma_table_g); + if (t->input_gamma_table_g != t->input_gamma_table_r && + t->input_gamma_table_g != t->input_gamma_table_b) + free(t->input_gamma_table_b); + + free(t->input_gamma_table_gray); + + free(t->output_gamma_lut_r); + free(t->output_gamma_lut_g); + free(t->output_gamma_lut_b); + + transform_free(t); +} + +static void compute_precache_pow(uint8_t *output, float gamma) +{ + uint32_t v = 0; + for (v = 0; v <= 0xffff; v++) { + //XXX: don't do integer/float conversion... and round? + output[v] = 255. * pow(v/65535., gamma); + } +} + +void compute_precache_lut(uint8_t *output, uint16_t *table, int length) +{ + uint32_t v = 0; + for (v = 0; v <= 0xffff; v++) { + //XXX: don't do integer/float conversion... round? + output[v] = lut_interp_linear16(v, table, length) >> 8; + } +} + +void compute_precache_linear(uint8_t *output) +{ + uint32_t v = 0; + for (v = 0; v <= 0xffff; v++) { + //XXX: round? + output[v] = v >> 8; + } +} + +qcms_bool compute_precache(struct curveType *trc, uint8_t *output) +{ + if (trc->count == 0) { + compute_precache_linear(output); + } else if (trc->count == 1) { + compute_precache_pow(output, 1./u8Fixed8Number_to_float(trc->data[0])); + } else { + uint16_t *inverted = invert_lut(trc->data, trc->count); + if (!inverted) + return false; + compute_precache_lut(output, inverted, trc->count); + free(inverted); + } + return true; +} + + +// Determine if we can build with SSE2 (this was partly copied from jmorecfg.h in +// mozilla/jpeg) + // ------------------------------------------------------------------------- +#if defined(_M_IX86) && defined(_MSC_VER) +#define HAS_CPUID +/* Get us a CPUID function. Avoid clobbering EBX because sometimes it's the PIC + register - I'm not sure if that ever happens on windows, but cpuid isn't + on the critical path so we just preserve the register to be safe and to be + consistent with the non-windows version. */ +static void cpuid(uint32_t fxn, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) { + uint32_t a_, b_, c_, d_; + __asm { + xchg ebx, esi + mov eax, fxn + cpuid + mov a_, eax + mov b_, ebx + mov c_, ecx + mov d_, edx + xchg ebx, esi + } + *a = a_; + *b = b_; + *c = c_; + *d = d_; +} +#elif defined(__GNUC__) && defined(__i386__) +#define HAS_CPUID +/* Get us a CPUID function. We can't use ebx because it's the PIC register on + some platforms, so we use ESI instead and save ebx to avoid clobbering it. */ +static void cpuid(uint32_t fxn, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) { + + uint32_t a_, b_, c_, d_; + __asm__ __volatile__ ("xchgl %%ebx, %%esi; cpuid; xchgl %%ebx, %%esi;" + : "=a" (a_), "=S" (b_), "=c" (c_), "=d" (d_) : "a" (fxn)); + *a = a_; + *b = b_; + *c = c_; + *d = d_; +} +#endif + +// -------------------------Runtime SSE2 Detection----------------------------- + +#define SSE2_EDX_MASK (1UL << 26) +static qcms_bool sse2_available(void) +{ +#ifdef HAS_CPUID + static int has_sse2 = -1; + uint32_t a, b, c, d; + uint32_t function = 0x00000001; + + if (has_sse2 == -1) { + has_sse2 = 0; + cpuid(function, &a, &b, &c, &d); + if (d & SSE2_EDX_MASK) + has_sse2 = 1; + else + has_sse2 = 0; + } + + return has_sse2; +#endif + return false; +} + + +void build_output_lut(struct curveType *trc, + uint16_t **output_gamma_lut, size_t *output_gamma_lut_length) +{ + if (trc->count == 0) { + *output_gamma_lut = build_linear_table(4096); + *output_gamma_lut_length = 4096; + } else if (trc->count == 1) { + float gamma = 1./u8Fixed8Number_to_float(trc->data[0]); + *output_gamma_lut = build_pow_table(gamma, 4096); + *output_gamma_lut_length = 4096; + } else { + *output_gamma_lut = invert_lut(trc->data, trc->count); + *output_gamma_lut_length = trc->count; + } + +} + +void qcms_profile_precache_output_transform(qcms_profile *profile) +{ + /* we only support precaching on rgb profiles */ + if (profile->color_space != RGB_SIGNATURE) + return; + + if (!profile->output_table_r) { + profile->output_table_r = precache_create(); + if (profile->output_table_r && + !compute_precache(profile->redTRC, profile->output_table_r->data)) { + precache_release(profile->output_table_r); + profile->output_table_r = NULL; + } + } + if (!profile->output_table_g) { + profile->output_table_g = precache_create(); + if (profile->output_table_g && + !compute_precache(profile->greenTRC, profile->output_table_g->data)) { + precache_release(profile->output_table_g); + profile->output_table_g = NULL; + } + } + if (!profile->output_table_b) { + profile->output_table_b = precache_create(); + if (profile->output_table_b && + !compute_precache(profile->blueTRC, profile->output_table_b->data)) { + precache_release(profile->output_table_g); + profile->output_table_g = NULL; + } + } +} + +#define NO_MEM_TRANSFORM NULL + +qcms_transform* qcms_transform_create( + qcms_profile *in, qcms_data_type in_type, + qcms_profile* out, qcms_data_type out_type, + qcms_intent intent) +{ + bool precache = false; + + qcms_transform *transform = transform_alloc(); + if (!transform) { + return NULL; + } + if (out_type != QCMS_DATA_RGB_8 && + out_type != QCMS_DATA_RGBA_8) { + assert(0 && "output type"); + free(transform); + return NULL; + } + + if (out->output_table_r && + out->output_table_g && + out->output_table_b) { + precache = true; + } + + if (precache) { + transform->output_table_r = precache_reference(out->output_table_r); + transform->output_table_g = precache_reference(out->output_table_g); + transform->output_table_b = precache_reference(out->output_table_b); + } else { + build_output_lut(out->redTRC, &transform->output_gamma_lut_r, &transform->output_gamma_lut_r_length); + build_output_lut(out->greenTRC, &transform->output_gamma_lut_g, &transform->output_gamma_lut_g_length); + build_output_lut(out->blueTRC, &transform->output_gamma_lut_b, &transform->output_gamma_lut_b_length); + if (!transform->output_gamma_lut_r || !transform->output_gamma_lut_g || !transform->output_gamma_lut_b) { + qcms_transform_release(transform); + return NO_MEM_TRANSFORM; + } + } + + if (in->color_space == RGB_SIGNATURE) { + struct matrix in_matrix, out_matrix, result; + + if (in_type != QCMS_DATA_RGB_8 && + in_type != QCMS_DATA_RGBA_8){ + assert(0 && "input type"); + free(transform); + return NULL; + } + if (precache) { +#ifdef X86 + if (sse2_available()) { + if (in_type == QCMS_DATA_RGB_8) + transform->transform_fn = qcms_transform_data_rgb_out_lut_sse; + else + transform->transform_fn = qcms_transform_data_rgba_out_lut_sse; + + } else +#endif + { + if (in_type == QCMS_DATA_RGB_8) + transform->transform_fn = qcms_transform_data_rgb_out_lut_precache; + else + transform->transform_fn = qcms_transform_data_rgba_out_lut_precache; + } + } else { + if (in_type == QCMS_DATA_RGB_8) + transform->transform_fn = qcms_transform_data_rgb_out_lut; + else + transform->transform_fn = qcms_transform_data_rgba_out_lut; + } + + //XXX: avoid duplicating tables if we can + transform->input_gamma_table_r = build_input_gamma_table(in->redTRC); + transform->input_gamma_table_g = build_input_gamma_table(in->greenTRC); + transform->input_gamma_table_b = build_input_gamma_table(in->blueTRC); + + if (!transform->input_gamma_table_r || !transform->input_gamma_table_g || !transform->input_gamma_table_b) { + qcms_transform_release(transform); + return NO_MEM_TRANSFORM; + } + + /* build combined colorant matrix */ + in_matrix = build_colorant_matrix(in); + out_matrix = build_colorant_matrix(out); + out_matrix = matrix_invert(out_matrix); + if (out_matrix.invalid) { + qcms_transform_release(transform); + return NULL; + } + result = matrix_multiply(out_matrix, in_matrix); + + /* store the results in column major mode + * this makes doing the multiplication with sse easier */ + transform->matrix[0][0] = result.m[0][0]; + transform->matrix[1][0] = result.m[0][1]; + transform->matrix[2][0] = result.m[0][2]; + transform->matrix[0][1] = result.m[1][0]; + transform->matrix[1][1] = result.m[1][1]; + transform->matrix[2][1] = result.m[1][2]; + transform->matrix[0][2] = result.m[2][0]; + transform->matrix[1][2] = result.m[2][1]; + transform->matrix[2][2] = result.m[2][2]; + + } else if (in->color_space == GRAY_SIGNATURE) { + if (in_type != QCMS_DATA_GRAY_8 && + in_type != QCMS_DATA_GRAYA_8){ + assert(0 && "input type"); + free(transform); + return NULL; + } + + transform->input_gamma_table_gray = build_input_gamma_table(in->grayTRC); + if (!transform->input_gamma_table_gray) { + qcms_transform_release(transform); + return NO_MEM_TRANSFORM; + } + + if (precache) { + if (in_type == QCMS_DATA_GRAY_8) { + transform->transform_fn = qcms_transform_data_gray_out_precache; + } else { + transform->transform_fn = qcms_transform_data_graya_out_precache; + } + } else { + if (in_type == QCMS_DATA_GRAY_8) { + transform->transform_fn = qcms_transform_data_gray_out_lut; + } else { + transform->transform_fn = qcms_transform_data_graya_out_lut; + } + } + } else { + assert(0 && "unexpected colorspace"); + } + return transform; +} + +void qcms_transform_data(qcms_transform *transform, void *src, void *dest, size_t length) +{ + transform->transform_fn(transform, src, dest, length); +} diff --git a/gfx/src/thebes/Makefile.in b/gfx/src/thebes/Makefile.in index 62f41c90e5a..a29bca57526 100644 --- a/gfx/src/thebes/Makefile.in +++ b/gfx/src/thebes/Makefile.in @@ -62,6 +62,7 @@ REQUIRES = xpcom \ unicharutil \ imglib2 \ $(ZLIB_REQUIRES) \ + qcms \ $(NULL) CPPSRCS = \ @@ -92,6 +93,7 @@ CPPSRCS += nsSystemFontsWin.cpp \ REQUIRES += \ cairo \ + qcms \ $(NULL) _OS_LIBS = usp10 diff --git a/gfx/thebes/public/gfxPlatform.h b/gfx/thebes/public/gfxPlatform.h index 2f4eb0907b6..9351fbc3b60 100644 --- a/gfx/thebes/public/gfxPlatform.h +++ b/gfx/thebes/public/gfxPlatform.h @@ -48,13 +48,11 @@ #include "gfxASurface.h" #include "gfxColor.h" +#include "qcms.h" #ifdef XP_OS2 #undef OS2EMX_PLAIN_CHAR #endif -typedef void* cmsHPROFILE; -typedef void* cmsHTRANSFORM; - class gfxImageSurface; class gfxFont; class gfxFontGroup; @@ -269,39 +267,39 @@ public: * * Sets 'out' to 'in' if transform is NULL. */ - static void TransformPixel(const gfxRGBA& in, gfxRGBA& out, cmsHTRANSFORM transform); + static void TransformPixel(const gfxRGBA& in, gfxRGBA& out, qcms_transform *transform); /** * Return the output device ICC profile. */ - static cmsHPROFILE GetCMSOutputProfile(); + static qcms_profile* GetCMSOutputProfile(); /** * Return the sRGB ICC profile. */ - static cmsHPROFILE GetCMSsRGBProfile(); + static qcms_profile* GetCMSsRGBProfile(); /** * Return sRGB -> output device transform. */ - static cmsHTRANSFORM GetCMSRGBTransform(); + static qcms_transform* GetCMSRGBTransform(); /** * Return output -> sRGB device transform. */ - static cmsHTRANSFORM GetCMSInverseRGBTransform(); + static qcms_transform* GetCMSInverseRGBTransform(); /** * Return sRGBA -> output device transform. */ - static cmsHTRANSFORM GetCMSRGBATransform(); + static qcms_transform* GetCMSRGBATransform(); protected: gfxPlatform() { } virtual ~gfxPlatform(); private: - virtual cmsHPROFILE GetPlatformCMSOutputProfile(); + virtual qcms_profile* GetPlatformCMSOutputProfile(); nsCOMPtr overrideObserver; }; diff --git a/gfx/thebes/public/gfxPlatformGtk.h b/gfx/thebes/public/gfxPlatformGtk.h index eed910a5ccf..2b5b429b1ae 100644 --- a/gfx/thebes/public/gfxPlatformGtk.h +++ b/gfx/thebes/public/gfxPlatformGtk.h @@ -142,7 +142,7 @@ protected: static gfxFontconfigUtils *sFontconfigUtils; private: - virtual cmsHPROFILE GetPlatformCMSOutputProfile(); + virtual qcms_profile *GetPlatformCMSOutputProfile(); }; #endif /* GFX_PLATFORM_GTK_H */ diff --git a/gfx/thebes/public/gfxPlatformMac.h b/gfx/thebes/public/gfxPlatformMac.h index 77621b28b10..742db1d10a6 100644 --- a/gfx/thebes/public/gfxPlatformMac.h +++ b/gfx/thebes/public/gfxPlatformMac.h @@ -107,7 +107,7 @@ private: void AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], PRUint32 &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang); - virtual cmsHPROFILE GetPlatformCMSOutputProfile(); + virtual qcms_profile* GetPlatformCMSOutputProfile(); // read in the pref value for the lower threshold on font anti-aliasing static PRUint32 ReadAntiAliasingThreshold(); diff --git a/gfx/thebes/public/gfxQtPlatform.h b/gfx/thebes/public/gfxQtPlatform.h index e602336b08e..c12f9e2f354 100644 --- a/gfx/thebes/public/gfxQtPlatform.h +++ b/gfx/thebes/public/gfxQtPlatform.h @@ -97,7 +97,7 @@ protected: static gfxFontconfigUtils *sFontconfigUtils; private: - virtual cmsHPROFILE GetPlatformCMSOutputProfile(); + virtual qcms_profile *GetPlatformCMSOutputProfile(); }; #endif /* GFX_PLATFORM_QT_H */ diff --git a/gfx/thebes/public/gfxWindowsPlatform.h b/gfx/thebes/public/gfxWindowsPlatform.h index 1261effefd4..c642016cb48 100644 --- a/gfx/thebes/public/gfxWindowsPlatform.h +++ b/gfx/thebes/public/gfxWindowsPlatform.h @@ -165,7 +165,7 @@ private: nsRefPtr& aFontFamily, void* userArg); - virtual cmsHPROFILE GetPlatformCMSOutputProfile(); + virtual qcms_profile* GetPlatformCMSOutputProfile(); static int PrefChangedCallback(const char*, void*); diff --git a/gfx/thebes/src/Makefile.in b/gfx/thebes/src/Makefile.in index 54c9e46a929..0e46489f353 100644 --- a/gfx/thebes/src/Makefile.in +++ b/gfx/thebes/src/Makefile.in @@ -18,6 +18,7 @@ REQUIRES = \ xpcom \ unicharutil \ $(LCMS_REQUIRES) \ + qcms \ $(NULL) CPPSRCS = \ @@ -47,7 +48,7 @@ EXTRA_DSO_LDOPTS += \ $(XPCOM_LIBS) \ $(NSPR_LIBS) \ $(ZLIB_LIBS) \ - $(LCMS_LIBS) \ + $(QCMS_LIBS) \ $(NULL) diff --git a/gfx/thebes/src/gfxContext.cpp b/gfx/thebes/src/gfxContext.cpp index 466f5c6cf2f..75f6f8631c7 100644 --- a/gfx/thebes/src/gfxContext.cpp +++ b/gfx/thebes/src/gfxContext.cpp @@ -46,7 +46,6 @@ #endif #include "cairo.h" -#include "lcms.h" #include "gfxContext.h" diff --git a/gfx/thebes/src/gfxPattern.cpp b/gfx/thebes/src/gfxPattern.cpp index d9eada746ed..ca1e7ec1d91 100644 --- a/gfx/thebes/src/gfxPattern.cpp +++ b/gfx/thebes/src/gfxPattern.cpp @@ -41,7 +41,6 @@ #include "gfxPlatform.h" #include "cairo.h" -#include "lcms.h" gfxPattern::gfxPattern(cairo_pattern_t *aPattern) { diff --git a/gfx/thebes/src/gfxPlatform.cpp b/gfx/thebes/src/gfxPlatform.cpp index 7c52f53f056..c5b10025483 100644 --- a/gfx/thebes/src/gfxPlatform.cpp +++ b/gfx/thebes/src/gfxPlatform.cpp @@ -64,7 +64,7 @@ #include "nsWeakReference.h" #include "cairo.h" -#include "lcms.h" +#include "qcms.h" #include "plstr.h" #include "nsIPrefService.h" @@ -74,12 +74,12 @@ gfxPlatform *gPlatform = nsnull; // These two may point to the same profile -static cmsHPROFILE gCMSOutputProfile = nsnull; -static cmsHPROFILE gCMSsRGBProfile = nsnull; +static qcms_profile *gCMSOutputProfile = nsnull; +static qcms_profile *gCMSsRGBProfile = nsnull; -static cmsHTRANSFORM gCMSRGBTransform = nsnull; -static cmsHTRANSFORM gCMSInverseRGBTransform = nsnull; -static cmsHTRANSFORM gCMSRGBATransform = nsnull; +static qcms_transform *gCMSRGBTransform = nsnull; +static qcms_transform *gCMSInverseRGBTransform = nsnull; +static qcms_transform *gCMSRGBATransform = nsnull; static PRBool gCMSInitialized = PR_FALSE; static eCMSMode gCMSMode = eCMSMode_Off; @@ -223,16 +223,6 @@ gfxPlatform::Init() if (prefs) prefs->AddObserver(CMForceSRGBPrefName, gPlatform->overrideObserver, PR_TRUE); - /* By default, LCMS calls exit() on error, which isn't what we want. If - cms is enabled, change the error functionality. */ - if (GetCMSMode() != eCMSMode_Off) { -#ifdef DEBUG - cmsErrorAction(LCMS_ERROR_SHOW); -#else - cmsErrorAction(LCMS_ERROR_IGNORE); -#endif - } - return NS_OK; } @@ -508,7 +498,9 @@ gfxPlatform::GetCMSMode() /* Chris Murphy (CM consultant) suggests this as a default in the event that we cannot reproduce relative + Black Point Compensation. BPC brings an unacceptable performance overhead, so we go with perceptual. */ -#define INTENT_DEFAULT INTENT_PERCEPTUAL +#define INTENT_DEFAULT QCMS_INTENT_PERCEPTUAL +#define INTENT_MIN 0 +#define INTENT_MAX 3 PRBool gfxPlatform::GetRenderingIntent() @@ -540,20 +532,24 @@ gfxPlatform::GetRenderingIntent() } void -gfxPlatform::TransformPixel(const gfxRGBA& in, gfxRGBA& out, cmsHTRANSFORM transform) +gfxPlatform::TransformPixel(const gfxRGBA& in, gfxRGBA& out, qcms_transform *transform) { if (transform) { + /* we want the bytes in RGB order */ #ifdef IS_LITTLE_ENDIAN + /* ABGR puts the bytes in |RGBA| order on little endian */ PRUint32 packed = in.Packed(gfxRGBA::PACKED_ABGR); - cmsDoTransform(transform, + qcms_transform_data(transform, (PRUint8 *)&packed, (PRUint8 *)&packed, 1); out.~gfxRGBA(); new (&out) gfxRGBA(packed, gfxRGBA::PACKED_ABGR); #else + /* ARGB puts the bytes in |ARGB| order on big endian */ PRUint32 packed = in.Packed(gfxRGBA::PACKED_ARGB); - cmsDoTransform(transform, + /* add one to move past the alpha byte */ + qcms_transform_data(transform, (PRUint8 *)&packed + 1, (PRUint8 *)&packed + 1, 1); out.~gfxRGBA(); @@ -565,13 +561,13 @@ gfxPlatform::TransformPixel(const gfxRGBA& in, gfxRGBA& out, cmsHTRANSFORM trans out = in; } -cmsHPROFILE +qcms_profile * gfxPlatform::GetPlatformCMSOutputProfile() { return nsnull; } -cmsHPROFILE +qcms_profile * gfxPlatform::GetCMSOutputProfile() { if (!gCMSOutputProfile) { @@ -597,7 +593,7 @@ gfxPlatform::GetCMSOutputProfile() rv = prefs->GetCharPref(CMProfilePrefName, getter_Copies(fname)); if (NS_SUCCEEDED(rv) && !fname.IsEmpty()) { - gCMSOutputProfile = cmsOpenProfileFromFile(fname, "r"); + gCMSOutputProfile = qcms_profile_from_path(fname); } } } @@ -609,92 +605,87 @@ gfxPlatform::GetCMSOutputProfile() /* Determine if the profile looks bogus. If so, close the profile * and use sRGB instead. See bug 460629, */ - if (gCMSOutputProfile && cmsProfileIsBogus(gCMSOutputProfile)) { + if (gCMSOutputProfile && qcms_profile_is_bogus(gCMSOutputProfile)) { NS_ASSERTION(gCMSOutputProfile != GetCMSsRGBProfile(), "Builtin sRGB profile tagged as bogus!!!"); - cmsCloseProfile(gCMSOutputProfile); + qcms_profile_release(gCMSOutputProfile); gCMSOutputProfile = nsnull; } if (!gCMSOutputProfile) { gCMSOutputProfile = GetCMSsRGBProfile(); } - /* Precache the LUT16 Interpolations for the output profile. See bug 444661 for details. */ - cmsPrecacheProfile(gCMSOutputProfile, CMS_PRECACHE_LI168_REVERSE); + qcms_profile_precache_output_transform(gCMSOutputProfile); } return gCMSOutputProfile; } -cmsHPROFILE +qcms_profile * gfxPlatform::GetCMSsRGBProfile() { if (!gCMSsRGBProfile) { /* Create the profile using lcms. */ - gCMSsRGBProfile = cmsCreate_sRGBProfile(); - - /* Precache the Fixed-point Interpolations for sRGB as an input - profile. See bug 444661 for details. */ - cmsPrecacheProfile(gCMSsRGBProfile, CMS_PRECACHE_LI8F_FORWARD); + gCMSsRGBProfile = qcms_profile_sRGB(); } return gCMSsRGBProfile; } -cmsHTRANSFORM +qcms_transform * gfxPlatform::GetCMSRGBTransform() { if (!gCMSRGBTransform) { - cmsHPROFILE inProfile, outProfile; + qcms_profile *inProfile, *outProfile; outProfile = GetCMSOutputProfile(); inProfile = GetCMSsRGBProfile(); if (!inProfile || !outProfile) return nsnull; - gCMSRGBTransform = cmsCreateTransform(inProfile, TYPE_RGB_8, - outProfile, TYPE_RGB_8, - INTENT_PERCEPTUAL, cmsFLAGS_FLOATSHAPER); + gCMSRGBTransform = qcms_transform_create(inProfile, QCMS_DATA_RGB_8, + outProfile, QCMS_DATA_RGB_8, + QCMS_INTENT_PERCEPTUAL); } return gCMSRGBTransform; } -cmsHTRANSFORM +qcms_transform * gfxPlatform::GetCMSInverseRGBTransform() { if (!gCMSInverseRGBTransform) { - cmsHPROFILE inProfile, outProfile; + qcms_profile *inProfile, *outProfile; inProfile = GetCMSOutputProfile(); outProfile = GetCMSsRGBProfile(); if (!inProfile || !outProfile) return nsnull; - gCMSInverseRGBTransform = cmsCreateTransform(inProfile, TYPE_RGB_8, - outProfile, TYPE_RGB_8, - INTENT_PERCEPTUAL, cmsFLAGS_FLOATSHAPER); + gCMSInverseRGBTransform = qcms_transform_create(inProfile, QCMS_DATA_RGB_8, + outProfile, QCMS_DATA_RGB_8, + QCMS_INTENT_PERCEPTUAL); } return gCMSInverseRGBTransform; } -cmsHTRANSFORM +qcms_transform * gfxPlatform::GetCMSRGBATransform() { if (!gCMSRGBATransform) { - cmsHPROFILE inProfile, outProfile; + qcms_profile *inProfile, *outProfile; outProfile = GetCMSOutputProfile(); inProfile = GetCMSsRGBProfile(); if (!inProfile || !outProfile) return nsnull; - gCMSRGBATransform = cmsCreateTransform(inProfile, TYPE_RGBA_8, - outProfile, TYPE_RGBA_8, - INTENT_PERCEPTUAL, cmsFLAGS_FLOATSHAPER); + gCMSRGBATransform = qcms_transform_create(inProfile, QCMS_DATA_RGBA_8, + outProfile, QCMS_DATA_RGBA_8, + QCMS_INTENT_PERCEPTUAL); } return gCMSRGBATransform; @@ -705,19 +696,19 @@ static void ShutdownCMS() { if (gCMSRGBTransform) { - cmsDeleteTransform(gCMSRGBTransform); + qcms_transform_release(gCMSRGBTransform); gCMSRGBTransform = nsnull; } if (gCMSInverseRGBTransform) { - cmsDeleteTransform(gCMSInverseRGBTransform); + qcms_transform_release(gCMSInverseRGBTransform); gCMSInverseRGBTransform = nsnull; } if (gCMSRGBATransform) { - cmsDeleteTransform(gCMSRGBATransform); + qcms_transform_release(gCMSRGBATransform); gCMSRGBATransform = nsnull; } if (gCMSOutputProfile) { - cmsCloseProfile(gCMSOutputProfile); + qcms_profile_release(gCMSOutputProfile); // handle the aliased case if (gCMSsRGBProfile == gCMSOutputProfile) @@ -725,7 +716,7 @@ static void ShutdownCMS() gCMSOutputProfile = nsnull; } if (gCMSsRGBProfile) { - cmsCloseProfile(gCMSsRGBProfile); + qcms_profile_release(gCMSsRGBProfile); gCMSsRGBProfile = nsnull; } diff --git a/gfx/thebes/src/gfxPlatformGtk.cpp b/gfx/thebes/src/gfxPlatformGtk.cpp index 367aec1de4f..64076331dc7 100644 --- a/gfx/thebes/src/gfxPlatformGtk.cpp +++ b/gfx/thebes/src/gfxPlatformGtk.cpp @@ -77,8 +77,6 @@ #include "nsMathUtils.h" -#include "lcms.h" - #define GDK_PIXMAP_SIZE_MAX 32767 #ifndef MOZ_PANGO @@ -529,7 +527,7 @@ gfxPlatformGtk::InitDPI() } } -cmsHPROFILE +qcms_profile * gfxPlatformGtk::GetPlatformCMSOutputProfile() { #ifdef MOZ_X11 @@ -559,10 +557,10 @@ gfxPlatformGtk::GetPlatformCMSOutputProfile() &retAtom, &retFormat, &retLength, &retAfter, &retProperty); - cmsHPROFILE profile = NULL; + qcms_profile* profile = NULL; if (retLength > 0) - profile = cmsOpenProfileFromMem(retProperty, retLength); + profile = qcms_profile_from_memory(retProperty, retLength); XFree(retProperty); @@ -584,8 +582,8 @@ gfxPlatformGtk::GetPlatformCMSOutputProfile() &retAtom, &retFormat, &retLength, &retAfter, &retProperty)) { double gamma; - cmsCIExyY whitePoint; - cmsCIExyYTRIPLE primaries; + qcms_CIE_xyY whitePoint; + qcms_CIE_xyYTRIPLE primaries; if (retLength != 128) { #ifdef DEBUG_tor @@ -603,23 +601,23 @@ gfxPlatformGtk::GetPlatformCMSOutputProfile() (retProperty[0x1a] >> 0 & 3)) / 1024.0; whitePoint.Y = 1.0; - primaries.Red.x = ((retProperty[0x1b] << 2) | + primaries.red.x = ((retProperty[0x1b] << 2) | (retProperty[0x19] >> 6 & 3)) / 1024.0; - primaries.Red.y = ((retProperty[0x1c] << 2) | + primaries.red.y = ((retProperty[0x1c] << 2) | (retProperty[0x19] >> 4 & 3)) / 1024.0; - primaries.Red.Y = 1.0; + primaries.red.Y = 1.0; - primaries.Green.x = ((retProperty[0x1d] << 2) | + primaries.green.x = ((retProperty[0x1d] << 2) | (retProperty[0x19] >> 2 & 3)) / 1024.0; - primaries.Green.y = ((retProperty[0x1e] << 2) | + primaries.green.y = ((retProperty[0x1e] << 2) | (retProperty[0x19] >> 0 & 3)) / 1024.0; - primaries.Green.Y = 1.0; + primaries.green.Y = 1.0; - primaries.Blue.x = ((retProperty[0x1f] << 2) | + primaries.blue.x = ((retProperty[0x1f] << 2) | (retProperty[0x1a] >> 6 & 3)) / 1024.0; - primaries.Blue.y = ((retProperty[0x20] << 2) | + primaries.blue.y = ((retProperty[0x20] << 2) | (retProperty[0x1a] >> 4 & 3)) / 1024.0; - primaries.Blue.Y = 1.0; + primaries.blue.Y = 1.0; XFree(retProperty); @@ -633,17 +631,8 @@ gfxPlatformGtk::GetPlatformCMSOutputProfile() primaries.Blue.x, primaries.Blue.y, primaries.Blue.Y); #endif - LPGAMMATABLE gammaTable[3]; - gammaTable[0] = gammaTable[1] = gammaTable[2] = - cmsBuildGamma(256, gamma); - - if (!gammaTable[0]) - return nsnull; - - cmsHPROFILE profile = - cmsCreateRGBProfile(&whitePoint, &primaries, gammaTable); - - cmsFreeGamma(gammaTable[0]); + qcms_profile* profile = + qcms_profile_create_rgb_with_gamma(whitePoint, primaries, gamma); #ifdef DEBUG_tor if (profile) { diff --git a/gfx/thebes/src/gfxPlatformMac.cpp b/gfx/thebes/src/gfxPlatformMac.cpp index cbbc7bf9421..f1d28f690b7 100644 --- a/gfx/thebes/src/gfxPlatformMac.cpp +++ b/gfx/thebes/src/gfxPlatformMac.cpp @@ -58,7 +58,7 @@ #include "nsTArray.h" #include "nsUnicodeRange.h" -#include "lcms.h" +#include "qcms.h" gfxPlatformMac::gfxPlatformMac() { @@ -382,7 +382,7 @@ gfxPlatformMac::ReadAntiAliasingThreshold() return threshold; } -cmsHPROFILE +qcms_profile * gfxPlatformMac::GetPlatformCMSOutputProfile() { CMProfileLocation device; @@ -393,14 +393,14 @@ gfxPlatformMac::GetPlatformCMSOutputProfile() if (err != noErr) return nsnull; - cmsHPROFILE profile = nsnull; + qcms_profile *profile = nsnull; switch (device.locType) { case cmFileBasedProfile: { FSRef fsRef; if (!FSpMakeFSRef(&device.u.fileLoc.spec, &fsRef)) { char path[512]; if (!FSRefMakePath(&fsRef, (UInt8*)(path), sizeof(path))) { - profile = cmsOpenProfileFromFile(path, "r"); + profile = qcms_profile_from_path(path); #ifdef DEBUG_tor if (profile) fprintf(stderr, @@ -411,7 +411,7 @@ gfxPlatformMac::GetPlatformCMSOutputProfile() break; } case cmPathBasedProfile: - profile = cmsOpenProfileFromFile(device.u.pathLoc.path, "r"); + profile = qcms_profile_from_path(device.u.pathLoc.path); #ifdef DEBUG_tor if (profile) fprintf(stderr, diff --git a/gfx/thebes/src/gfxQtPlatform.cpp b/gfx/thebes/src/gfxQtPlatform.cpp index 92677b75221..4a4443b6f71 100644 --- a/gfx/thebes/src/gfxQtPlatform.cpp +++ b/gfx/thebes/src/gfxQtPlatform.cpp @@ -59,7 +59,7 @@ #include "nsMathUtils.h" #include "nsTArray.h" -#include "lcms.h" +#include "qcms.h" #include #include FT_FREETYPE_H @@ -354,7 +354,7 @@ gfxQtPlatform::InitDPI() } } -cmsHPROFILE +qcms_profile* gfxQtPlatform::GetPlatformCMSOutputProfile() { return nsnull; diff --git a/gfx/thebes/src/gfxWindowsPlatform.cpp b/gfx/thebes/src/gfxWindowsPlatform.cpp index 1b3e2d5dc7f..4798b9c106e 100644 --- a/gfx/thebes/src/gfxWindowsPlatform.cpp +++ b/gfx/thebes/src/gfxWindowsPlatform.cpp @@ -71,8 +71,6 @@ #include -#include "lcms.h" - #ifdef MOZ_FT2_FONTS static FT_Library gPlatformFTLibrary = NULL; #endif @@ -847,7 +845,7 @@ gfxWindowsPlatform::FindFontEntry(const nsAString& aName, const gfxFontStyle& aF return ff->FindFontEntry(aFontStyle); } -cmsHPROFILE +qcms_profile* gfxWindowsPlatform::GetPlatformCMSOutputProfile() { #ifndef MOZ_FT2_FONTS @@ -858,8 +856,8 @@ gfxWindowsPlatform::GetPlatformCMSOutputProfile() GetICMProfileW(dc, &size, (LPWSTR)&str); ReleaseDC(nsnull, dc); - cmsHPROFILE profile = - cmsOpenProfileFromFile(NS_ConvertUTF16toUTF8(str).get(), "r"); + qcms_profile* profile = + qcms_profile_from_path(NS_ConvertUTF16toUTF8(str).get()); #ifdef DEBUG_tor if (profile) fprintf(stderr, diff --git a/gfx/thebes/test/Makefile.in b/gfx/thebes/test/Makefile.in index 6e728c897b0..c46626724a7 100644 --- a/gfx/thebes/test/Makefile.in +++ b/gfx/thebes/test/Makefile.in @@ -51,10 +51,10 @@ REQUIRES = \ thebes \ cairo \ pref \ - lcms \ necko \ unicharutil \ nspr \ + qcms \ $(NULL) # All platforms @@ -63,8 +63,8 @@ CPPSRCS = \ gfxFontSelectionTest.cpp \ gfxTextRunPerfTest.cpp \ gfxWordCacheTest.cpp \ - gfxColorManagementTest.cpp \ $(NULL) +# gfxColorManagementTest.cpp \ ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa) CMMSRCS = gfxTestCocoaHelper.mm @@ -86,7 +86,6 @@ LIBS = $(HELPER_OBJS) \ $(NSPR_LIBS) \ $(TK_LIBS) \ $(ZLIB_LIBS) \ - $(LCMS_LIBS) \ $(NULL) ifeq ($(MOZ_WIDGET_TOOLKIT),windows) diff --git a/layout/base/Makefile.in b/layout/base/Makefile.in index e5d075ecbf7..cc840919275 100644 --- a/layout/base/Makefile.in +++ b/layout/base/Makefile.in @@ -78,6 +78,7 @@ REQUIRES = xpcom \ shistory \ caps \ thebes \ + qcms \ $(NULL) ifdef ACCESSIBILITY diff --git a/layout/mathml/Makefile.in b/layout/mathml/Makefile.in index 8ba80d05a78..18a7855faeb 100644 --- a/layout/mathml/Makefile.in +++ b/layout/mathml/Makefile.in @@ -64,6 +64,7 @@ REQUIRES = xpcom \ webbrwsr \ pref \ js \ + qcms \ $(NULL) LOCAL_INCLUDES = \ diff --git a/layout/svg/base/src/Makefile.in b/layout/svg/base/src/Makefile.in index cfb4723072e..716eb359368 100644 --- a/layout/svg/base/src/Makefile.in +++ b/layout/svg/base/src/Makefile.in @@ -63,6 +63,7 @@ REQUIRES = xpcom \ xpconnect \ docshell \ thebes \ + qcms \ $(NULL) CPPSRCS = \ diff --git a/modules/libpr0n/build/Makefile.in b/modules/libpr0n/build/Makefile.in index 799ebfc84a7..c1e851bcee6 100644 --- a/modules/libpr0n/build/Makefile.in +++ b/modules/libpr0n/build/Makefile.in @@ -60,7 +60,7 @@ REQUIRES = xpcom \ $(JPEG_REQUIRES) \ $(PNG_REQUIRES) \ $(ZLIB_REQUIRES) \ - $(LCMS_REQUIRES) \ + qcms \ $(NULL) CPPSRCS = \ @@ -93,7 +93,7 @@ EXTRA_DSO_LDOPTS = \ $(LIBS_DIR) \ $(JPEG_LIBS) \ $(PNG_LIBS) $(ZLIB_LIBS) \ - $(LCMS_LIBS) \ + $(QCMS_LIBS) \ $(EXTRA_DSO_LIBS) \ $(MOZ_COMPONENT_LIBS) \ $(NULL) diff --git a/modules/libpr0n/decoders/gif/Makefile.in b/modules/libpr0n/decoders/gif/Makefile.in index a7aadc9b019..5cc3f1a7a7e 100644 --- a/modules/libpr0n/decoders/gif/Makefile.in +++ b/modules/libpr0n/decoders/gif/Makefile.in @@ -53,7 +53,7 @@ REQUIRES = xpcom \ gfx \ thebes \ imglib2 \ - $(LCMS_REQUIRES) \ + qcms \ $(NULL) CPPSRCS = nsGIFDecoder2.cpp diff --git a/modules/libpr0n/decoders/gif/nsGIFDecoder2.cpp b/modules/libpr0n/decoders/gif/nsGIFDecoder2.cpp index 022db6cba5a..f7cfda4887f 100644 --- a/modules/libpr0n/decoders/gif/nsGIFDecoder2.cpp +++ b/modules/libpr0n/decoders/gif/nsGIFDecoder2.cpp @@ -86,7 +86,7 @@ mailing address. #include "gfxColor.h" #include "gfxPlatform.h" -#include "lcms.h" +#include "qcms.h" /* * GETN(n, s) requests at least 'n' bytes available from 'q', at start of state 's' @@ -681,9 +681,9 @@ static void ConvertColormap(PRUint32 *aColormap, PRUint32 aColors) { // Apply CMS transformation if enabled and available if (gfxPlatform::GetCMSMode() == eCMSMode_All) { - cmsHTRANSFORM transform = gfxPlatform::GetCMSRGBTransform(); + qcms_transform *transform = gfxPlatform::GetCMSRGBTransform(); if (transform) - cmsDoTransform(transform, aColormap, aColormap, aColors); + qcms_transform_data(transform, aColormap, aColormap, aColors); } // Convert from the GIF's RGB format to the Cairo format. // Work from end to begin, because of the in-place expansion diff --git a/modules/libpr0n/decoders/jpeg/Makefile.in b/modules/libpr0n/decoders/jpeg/Makefile.in index 76246da8e69..30c5ef054c4 100644 --- a/modules/libpr0n/decoders/jpeg/Makefile.in +++ b/modules/libpr0n/decoders/jpeg/Makefile.in @@ -54,7 +54,7 @@ REQUIRES = xpcom \ thebes \ imglib2 \ $(JPEG_REQUIRES) \ - $(LCMS_REQUIRES) \ + qcms \ $(NULL) CPPSRCS = nsJPEGDecoder.cpp diff --git a/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.cpp b/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.cpp index e10a3e5b07b..ecaa09a0973 100644 --- a/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.cpp +++ b/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.cpp @@ -125,9 +125,9 @@ nsJPEGDecoder::~nsJPEGDecoder() { PR_FREEIF(mBackBuffer); if (mTransform) - cmsDeleteTransform(mTransform); + qcms_transform_release(mTransform); if (mInProfile) - cmsCloseProfile(mInProfile); + qcms_profile_release(mInProfile); PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG, ("nsJPEGDecoder::~nsJPEGDecoder: Destroying JPEG decoder %p", @@ -338,10 +338,10 @@ nsresult nsJPEGDecoder::ProcessData(const char *data, PRUint32 count, PRUint32 * if ((cmsMode != eCMSMode_Off) && read_icc_profile(&mInfo, &profile, &profileLength) && - (mInProfile = cmsOpenProfileFromMem(profile, profileLength)) != NULL) { + (mInProfile = qcms_profile_from_memory(profile, profileLength)) != NULL) { free(profile); - PRUint32 profileSpace = cmsGetColorSpace(mInProfile); + PRUint32 profileSpace = qcms_profile_get_color_space(mInProfile); PRBool mismatch = PR_FALSE; #ifdef DEBUG_tor @@ -361,14 +361,13 @@ nsresult nsJPEGDecoder::ProcessData(const char *data, PRUint32 count, PRUint32 * case JCS_YCbCr: if (profileSpace == icSigRgbData) mInfo.out_color_space = JCS_RGB; - else if (profileSpace != icSigYCbCrData) + else + // qcms doesn't support ycbcr mismatch = PR_TRUE; break; case JCS_CMYK: case JCS_YCCK: - if (profileSpace == icSigCmykData) - mInfo.out_color_space = JCS_CMYK; - else + // qcms doesn't support cmyk mismatch = PR_TRUE; break; default: @@ -379,19 +378,13 @@ nsresult nsJPEGDecoder::ProcessData(const char *data, PRUint32 count, PRUint32 * } if (!mismatch) { - PRUint32 type; + qcms_data_type type; switch (mInfo.out_color_space) { case JCS_GRAYSCALE: - type = COLORSPACE_SH(PT_GRAY) | CHANNELS_SH(1) | BYTES_SH(1); + type = QCMS_DATA_GRAY_8; break; case JCS_RGB: - type = COLORSPACE_SH(PT_RGB) | CHANNELS_SH(3) | BYTES_SH(1); - break; - case JCS_YCbCr: - type = COLORSPACE_SH(PT_YCbCr) | CHANNELS_SH(3) | BYTES_SH(1); - break; - case JCS_CMYK: - type = COLORSPACE_SH(PT_CMYK) | CHANNELS_SH(4) | BYTES_SH(1); + type = QCMS_DATA_RGB_8; break; default: mState = JPEG_ERROR; @@ -399,26 +392,28 @@ nsresult nsJPEGDecoder::ProcessData(const char *data, PRUint32 count, PRUint32 * ("} (unknown colorpsace (2))")); return NS_ERROR_UNEXPECTED; } - +#if 0 + We don't currently support CMYK profiles. The following + code dealt with lcms types. Add something like this + back when we gain support for CMYK. /* Adobe Photoshop writes YCCK/CMYK files with inverted data */ if (mInfo.out_color_space == JCS_CMYK) type |= FLAVOR_SH(mInfo.saw_Adobe_marker ? 1 : 0); - +#endif if (gfxPlatform::GetCMSOutputProfile()) { /* Calculate rendering intent. */ int intent = gfxPlatform::GetRenderingIntent(); if (intent == -1) - intent = cmsTakeRenderingIntent(mInProfile); + intent = qcms_profile_get_rendering_intent(mInProfile); /* Create the color management transform. */ - mTransform = cmsCreateTransform(mInProfile, + mTransform = qcms_transform_create(mInProfile, type, gfxPlatform::GetCMSOutputProfile(), - TYPE_RGB_8, - intent, - cmsFLAGS_FLOATSHAPER); + QCMS_DATA_RGB_8, + (qcms_intent)intent); } } else { #ifdef DEBUG_tor @@ -743,7 +738,7 @@ nsJPEGDecoder::OutputScanlines(PRBool* suspend) to the 3byte RGB byte pixels at 'end' of row */ sampleRow += mInfo.output_width; } - cmsDoTransform(mTransform, source, sampleRow, mInfo.output_width); + qcms_transform_data(mTransform, source, sampleRow, mInfo.output_width); /* Move 3byte RGB data to end of row */ if (mInfo.out_color_space == JCS_CMYK) { memmove(sampleRow + mInfo.output_width, @@ -761,9 +756,9 @@ nsJPEGDecoder::OutputScanlines(PRBool* suspend) } if (gfxPlatform::GetCMSMode() == eCMSMode_All) { /* No embedded ICC profile - treat as sRGB */ - cmsHTRANSFORM transform = gfxPlatform::GetCMSRGBTransform(); + qcms_transform *transform = gfxPlatform::GetCMSRGBTransform(); if (transform) { - cmsDoTransform(transform, sampleRow, sampleRow, mInfo.output_width); + qcms_transform_data(transform, sampleRow, sampleRow, mInfo.output_width); } } } diff --git a/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h b/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h index 9ec00c27379..7b066ea49bb 100644 --- a/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h +++ b/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h @@ -50,7 +50,7 @@ #include "imgILoad.h" #include "nsIInputStream.h" #include "nsIPipe.h" -#include "lcms.h" +#include "qcms.h" extern "C" { #include "jpeglib.h" @@ -122,8 +122,8 @@ public: JOCTET *mProfile; PRUint32 mProfileLength; - cmsHPROFILE mInProfile; - cmsHTRANSFORM mTransform; + qcms_profile *mInProfile; + qcms_transform *mTransform; PRPackedBool mReading; }; diff --git a/modules/libpr0n/decoders/png/Makefile.in b/modules/libpr0n/decoders/png/Makefile.in index b0dd0ccbb9b..cbdd482b4b0 100644 --- a/modules/libpr0n/decoders/png/Makefile.in +++ b/modules/libpr0n/decoders/png/Makefile.in @@ -57,7 +57,7 @@ REQUIRES = xpcom \ imglib2 \ $(PNG_REQUIRES) \ $(ZLIB_REQUIRES) \ - $(LCMS_REQUIRES) \ + qcms \ $(NULL) CPPSRCS = nsPNGDecoder.cpp diff --git a/modules/libpr0n/decoders/png/nsPNGDecoder.cpp b/modules/libpr0n/decoders/png/nsPNGDecoder.cpp index 41fb26f57a7..9c49a148828 100644 --- a/modules/libpr0n/decoders/png/nsPNGDecoder.cpp +++ b/modules/libpr0n/decoders/png/nsPNGDecoder.cpp @@ -89,11 +89,11 @@ nsPNGDecoder::~nsPNGDecoder() if (interlacebuf) nsMemory::Free(interlacebuf); if (mInProfile) { - cmsCloseProfile(mInProfile); + qcms_profile_release(mInProfile); /* mTransform belongs to us only if mInProfile is non-null */ if (mTransform) - cmsDeleteTransform(mTransform); + qcms_transform_release(mTransform); } } @@ -397,12 +397,12 @@ PNGDoGammaCorrection(png_structp png_ptr, png_infop info_ptr) } // Adapted from http://www.littlecms.com/pngchrm.c example code -static cmsHPROFILE +static qcms_profile * PNGGetColorProfile(png_structp png_ptr, png_infop info_ptr, - int color_type, PRUint32 *inType, PRUint32 *intent) + int color_type, qcms_data_type *inType, PRUint32 *intent) { - cmsHPROFILE profile = nsnull; - *intent = INTENT_PERCEPTUAL; // Our default + qcms_profile *profile = nsnull; + *intent = QCMS_INTENT_PERCEPTUAL; // Our default // First try to see if iCCP chunk is present if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) { @@ -413,38 +413,40 @@ PNGGetColorProfile(png_structp png_ptr, png_infop info_ptr, png_get_iCCP(png_ptr, info_ptr, &profileName, &compression, &profileData, &profileLen); - profile = cmsOpenProfileFromMem(profileData, profileLen); - PRUint32 profileSpace = cmsGetColorSpace(profile); + profile = qcms_profile_from_memory(profileData, profileLen); + if (profile) { + PRUint32 profileSpace = qcms_profile_get_color_space(profile); - PRBool mismatch = PR_FALSE; - if (color_type & PNG_COLOR_MASK_COLOR) { - if (profileSpace != icSigRgbData) - mismatch = PR_TRUE; - } else { - if (profileSpace == icSigRgbData) - png_set_gray_to_rgb(png_ptr); - else if (profileSpace != icSigGrayData) - mismatch = PR_TRUE; - } + PRBool mismatch = PR_FALSE; + if (color_type & PNG_COLOR_MASK_COLOR) { + if (profileSpace != icSigRgbData) + mismatch = PR_TRUE; + } else { + if (profileSpace == icSigRgbData) + png_set_gray_to_rgb(png_ptr); + else if (profileSpace != icSigGrayData) + mismatch = PR_TRUE; + } - if (mismatch) { - cmsCloseProfile(profile); - profile = nsnull; - } else { - *intent = cmsTakeRenderingIntent(profile); + if (mismatch) { + qcms_profile_release(profile); + profile = nsnull; + } else { + *intent = qcms_profile_get_rendering_intent(profile); + } } } // Check sRGB chunk if (!profile && png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) { - profile = cmsCreate_sRGBProfile(); + profile = qcms_profile_sRGB(); if (profile) { int fileIntent; png_set_gray_to_rgb(png_ptr); png_get_sRGB(png_ptr, info_ptr, &fileIntent); - PRUint32 map[] = { INTENT_PERCEPTUAL, INTENT_RELATIVE_COLORIMETRIC, - INTENT_SATURATION, INTENT_ABSOLUTE_COLORIMETRIC }; + PRUint32 map[] = { QCMS_INTENT_PERCEPTUAL, QCMS_INTENT_RELATIVE_COLORIMETRIC, + QCMS_INTENT_SATURATION, QCMS_INTENT_ABSOLUTE_COLORIMETRIC }; *intent = map[fileIntent]; } } @@ -453,49 +455,40 @@ PNGGetColorProfile(png_structp png_ptr, png_infop info_ptr, if (!profile && png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA) && png_get_valid(png_ptr, info_ptr, PNG_INFO_cHRM)) { - cmsCIExyYTRIPLE primaries; - cmsCIExyY whitePoint; + qcms_CIE_xyYTRIPLE primaries; + qcms_CIE_xyY whitePoint; png_get_cHRM(png_ptr, info_ptr, &whitePoint.x, &whitePoint.y, - &primaries.Red.x, &primaries.Red.y, - &primaries.Green.x, &primaries.Green.y, - &primaries.Blue.x, &primaries.Blue.y); + &primaries.red.x, &primaries.red.y, + &primaries.green.x, &primaries.green.y, + &primaries.blue.x, &primaries.blue.y); whitePoint.Y = - primaries.Red.Y = primaries.Green.Y = primaries.Blue.Y = 1.0; + primaries.red.Y = primaries.green.Y = primaries.blue.Y = 1.0; double gammaOfFile; - LPGAMMATABLE gammaTable[3]; png_get_gAMA(png_ptr, info_ptr, &gammaOfFile); - gammaTable[0] = gammaTable[1] = gammaTable[2] = - cmsBuildGamma(256, 1/gammaOfFile); - - if (!gammaTable[0]) - return nsnull; - - profile = cmsCreateRGBProfile(&whitePoint, &primaries, gammaTable); + profile = qcms_profile_create_rgb_with_gamma(whitePoint, primaries, 1/gammaOfFile); if (profile) png_set_gray_to_rgb(png_ptr); - - cmsFreeGamma(gammaTable[0]); } if (profile) { - PRUint32 profileSpace = cmsGetColorSpace(profile); + PRUint32 profileSpace = qcms_profile_get_color_space(profile); if (profileSpace == icSigGrayData) { if (color_type & PNG_COLOR_MASK_ALPHA) - *inType = TYPE_GRAYA_8; + *inType = QCMS_DATA_GRAYA_8; else - *inType = TYPE_GRAY_8; + *inType = QCMS_DATA_GRAY_8; } else { if (color_type & PNG_COLOR_MASK_ALPHA || png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) - *inType = TYPE_RGBA_8; + *inType = QCMS_DATA_RGBA_8; else - *inType = TYPE_RGB_8; + *inType = QCMS_DATA_RGB_8; } } @@ -556,7 +549,8 @@ info_callback(png_structp png_ptr, png_infop info_ptr) if (bit_depth == 16) png_set_strip_16(png_ptr); - PRUint32 inType, intent, pIntent; + qcms_data_type inType; + PRUint32 intent, pIntent; if (gfxPlatform::GetCMSMode() != eCMSMode_Off) { intent = gfxPlatform::GetRenderingIntent(); decoder->mInProfile = PNGGetColorProfile(png_ptr, info_ptr, @@ -566,25 +560,19 @@ info_callback(png_structp png_ptr, png_infop info_ptr) intent = pIntent; } if (decoder->mInProfile && gfxPlatform::GetCMSOutputProfile()) { - PRUint32 outType; + qcms_data_type outType; PRUint32 dwFlags = 0; if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) - outType = TYPE_RGBA_8; + outType = QCMS_DATA_RGBA_8; else - outType = TYPE_RGB_8; + outType = QCMS_DATA_RGB_8; - /* Determine if we can use the optimized floating point path. */ - if ((inType == outType) && - ((inType == TYPE_RGB_8) || (inType == TYPE_RGBA_8))) - dwFlags |= cmsFLAGS_FLOATSHAPER; - - decoder->mTransform = cmsCreateTransform(decoder->mInProfile, + decoder->mTransform = qcms_transform_create(decoder->mInProfile, inType, gfxPlatform::GetCMSOutputProfile(), outType, - intent, - dwFlags); + (qcms_intent)intent); } else { png_set_gray_to_rgb(png_ptr); PNGDoGammaCorrection(png_ptr, info_ptr); @@ -749,7 +737,7 @@ row_callback(png_structp png_ptr, png_bytep new_row, if (decoder->mTransform) { if (decoder->mCMSLine) { - cmsDoTransform(decoder->mTransform, line, decoder->mCMSLine, iwidth); + qcms_transform_data(decoder->mTransform, line, decoder->mCMSLine, iwidth); /* copy alpha over */ PRUint32 channels = decoder->mChannels; if (channels == 2 || channels == 4) { @@ -758,7 +746,7 @@ row_callback(png_structp png_ptr, png_bytep new_row, } line = decoder->mCMSLine; } else { - cmsDoTransform(decoder->mTransform, line, line, iwidth); + qcms_transform_data(decoder->mTransform, line, line, iwidth); } } diff --git a/modules/libpr0n/decoders/png/nsPNGDecoder.h b/modules/libpr0n/decoders/png/nsPNGDecoder.h index 1a3268e0c52..e5b27433d88 100644 --- a/modules/libpr0n/decoders/png/nsPNGDecoder.h +++ b/modules/libpr0n/decoders/png/nsPNGDecoder.h @@ -52,7 +52,7 @@ #include "png.h" -#include "lcms.h" +#include "qcms.h" #define NS_PNGDECODER_CID \ { /* 36fa00c2-1dd2-11b2-be07-d16eeb4c50ed */ \ @@ -87,8 +87,8 @@ public: png_infop mInfo; PRUint8 *mCMSLine; PRUint8 *interlacebuf; - cmsHPROFILE mInProfile; - cmsHTRANSFORM mTransform; + qcms_profile *mInProfile; + qcms_transform *mTransform; gfx_format format; PRUint8 mChannels; diff --git a/modules/libpr0n/test/reftest/pngsuite-ancillary/ccwn2c08.html b/modules/libpr0n/test/reftest/pngsuite-ancillary/ccwn2c08.html index 31834e5074e..c3ec2c08aa3 100644 --- a/modules/libpr0n/test/reftest/pngsuite-ancillary/ccwn2c08.html +++ b/modules/libpr0n/test/reftest/pngsuite-ancillary/ccwn2c08.html @@ -1,6 +1,6 @@ - + @@ -82,10 +82,10 @@ - - + + - + @@ -115,13 +115,13 @@ - + - + - + @@ -150,14 +150,20 @@ - + - + - + - + @@ -180,15 +186,15 @@ - - + + - + - + - + @@ -216,24 +222,24 @@ - + - - + + - + - + @@ -253,22 +259,22 @@ if (lcms_has_assembly) { - - + + - + - + - - + + - + @@ -292,16 +298,16 @@ if (lcms_has_assembly) { - + - + - + @@ -323,22 +329,22 @@ if (lcms_has_assembly) { - + - - - - + + + + - + - + @@ -355,12 +361,12 @@ if (lcms_has_assembly) { - + - - + + @@ -368,11 +374,11 @@ if (lcms_has_assembly) { - + - - - + + + @@ -392,28 +398,22 @@ if (lcms_has_assembly) { - - - + + + - + - - + + - - - + + + @@ -427,34 +427,28 @@ if (lcms_has_assembly) { - - - + + + - - + + - - - + + + - - + + - + - - + + @@ -467,27 +461,27 @@ if (lcms_has_assembly) { - - - - - - + + + + + + - - + + - + - + - + @@ -505,26 +499,26 @@ if (lcms_has_assembly) { - + - - - + + + - + - - + + - - + + - - + + @@ -536,28 +530,28 @@ if (lcms_has_assembly) { - + - - + + - + - + - + - + @@ -571,19 +565,19 @@ if (lcms_has_assembly) { - + - + - - - - - - - - + + + + + + + + @@ -593,8 +587,8 @@ if (lcms_has_assembly) { - - + + @@ -602,59 +596,59 @@ if (lcms_has_assembly) { - - - - + + + + - - - - - - - + + + + + + + - - + + - + - - - + + + - + - + - - + + - + - + - - - - + + + + - - - - + + + + - + @@ -664,205 +658,229 @@ if (lcms_has_assembly) { - + - - - + + + - + - - + + - + - + - + - + - + - + - - - - + + + + - - - - - - - + + + + + + + - + - - - - + + + + - + - + - + - + - - + + - - + + - + - + - - - + + + - - + + - - + + - + - + - + - + - - + + - + - - + + - - + + - + - - + + - - + + - - - + + + - - - + + + - - - - - + + + + + - - + + - - + + - - - - - - + + + + + + - + - - + + - + - - + + - + - + - + - - - - - - + + + + + + @@ -873,7 +891,7 @@ if (lcms_has_assembly) { - + @@ -881,21 +899,21 @@ if (lcms_has_assembly) { - + - + - + - + - - - + + + @@ -907,25 +925,25 @@ if (lcms_has_assembly) { - + - + - - + + - + - + @@ -941,24 +959,24 @@ if (lcms_has_assembly) { - - + + - - - - + + + + - - + + @@ -975,20 +993,20 @@ if (lcms_has_assembly) { - + - + - - - + + + - + - + @@ -1010,18 +1028,18 @@ if (lcms_has_assembly) { - + - - - - + + + + - + - + @@ -1046,13 +1064,13 @@ if (lcms_has_assembly) { - - + + - + - - + + diff --git a/modules/libpr0n/test/reftest/pngsuite-ancillary/ccwn3p08.html b/modules/libpr0n/test/reftest/pngsuite-ancillary/ccwn3p08.html index fa7d9114115..7d3189f163c 100644 --- a/modules/libpr0n/test/reftest/pngsuite-ancillary/ccwn3p08.html +++ b/modules/libpr0n/test/reftest/pngsuite-ancillary/ccwn3p08.html @@ -1,6 +1,6 @@ - +
@@ -86,7 +86,7 @@ - + @@ -114,11 +114,11 @@ - + - - + + @@ -147,16 +147,16 @@ - - - - + + + + - + - + @@ -184,21 +184,15 @@ - + - + - + @@ -229,16 +223,9 @@ if (lcms_has_assembly) { - - + + + @@ -260,7 +247,7 @@ if (lcms_has_assembly) { - + @@ -270,15 +257,9 @@ if (lcms_has_assembly) { - - - + + + @@ -311,10 +292,10 @@ if (lcms_has_assembly) { - + - - + + @@ -338,8 +319,8 @@ if (lcms_has_assembly) { - - + + @@ -371,13 +352,13 @@ if (lcms_has_assembly) { - - + + - + @@ -401,26 +382,26 @@ if (lcms_has_assembly) { - - + + - - - + + + - - - + + + - - + + - + @@ -434,28 +415,28 @@ if (lcms_has_assembly) { - - - + + + - - - - + + + + - - + + - - + + - + @@ -468,28 +449,28 @@ if (lcms_has_assembly) { - - + + - + - + - + - - + + - + - + - - + + @@ -503,29 +484,29 @@ if (lcms_has_assembly) { - + - + - - - + + + - + - + - - + + - + @@ -536,31 +517,31 @@ if (lcms_has_assembly) { - + - + - - - + + + - - - + + + - - + + - + @@ -569,33 +550,33 @@ if (lcms_has_assembly) { - - + + - + - + - - + + - + - - - + + + - - - + + + - + @@ -604,28 +585,28 @@ if (lcms_has_assembly) { - - - + + + - - - - + + + + - + - - - - + + + + @@ -637,35 +618,35 @@ if (lcms_has_assembly) { - + - - + + - - - - + + + + - + - - + + - + @@ -673,91 +654,109 @@ if (lcms_has_assembly) { - - - + + + - - - - - - - - - + + + + + + + + + - + - - + + - + - - - + + + - - - - - + + + + + - - - + + + - - - - + + + + - - - - - - + + + + + + - + - + - + - - - - + + + + - - - + + + - + - + - - + + @@ -766,14 +765,14 @@ if (lcms_has_assembly) { - - - + + + - + @@ -782,23 +781,23 @@ if (lcms_has_assembly) { - - + + - - - - + + + + - + - - + + @@ -807,8 +806,8 @@ if (lcms_has_assembly) { - - + + @@ -816,24 +815,24 @@ if (lcms_has_assembly) { - - - - + + + + - + - + - - - - - + + + + + @@ -841,28 +840,28 @@ if (lcms_has_assembly) { - - + + - + - - - - + + + + - - - - - + + + + + @@ -875,35 +874,28 @@ if (lcms_has_assembly) { - + + - - - + + + - - - - - - - - - - + + + + + + + + + + @@ -919,22 +911,22 @@ if (lcms_has_assembly) { - + - - - - - - - - + + + + + + + + - + @@ -958,15 +950,15 @@ if (lcms_has_assembly) { - - + + - - - - - - + + + + + + @@ -983,7 +975,7 @@ if (lcms_has_assembly) { - + @@ -993,11 +985,11 @@ if (lcms_has_assembly) { - - - - - + + + + + @@ -1024,12 +1016,12 @@ if (lcms_has_assembly) { - - - - - - + + + + + + @@ -1053,14 +1045,14 @@ if (lcms_has_assembly) { - - - - - - - - + + + + + + + + diff --git a/modules/libpr0n/test/reftest/pngsuite-ancillary/lcms-asm-check.js b/modules/libpr0n/test/reftest/pngsuite-ancillary/qcms-asm-check.js similarity index 81% rename from modules/libpr0n/test/reftest/pngsuite-ancillary/lcms-asm-check.js rename to modules/libpr0n/test/reftest/pngsuite-ancillary/qcms-asm-check.js index 6637e0399fa..02495f6ee73 100644 --- a/modules/libpr0n/test/reftest/pngsuite-ancillary/lcms-asm-check.js +++ b/modules/libpr0n/test/reftest/pngsuite-ancillary/qcms-asm-check.js @@ -1,7 +1,7 @@ -// This is a workaround for bug 465088, that the lcms assembly doesn't +// This is a workaround for bug 465088, that the qcms assembly doesn't // quite match the non-assembly output. -function check_lcms_has_assembly() +function check_qcms_has_assembly() { // We have assembly code on x86 and x86_64 architectures. // Unfortunately, detecting that is a little complicated. @@ -25,4 +25,4 @@ function check_lcms_has_assembly() return false; } -var lcms_has_assembly = check_lcms_has_assembly(); +var qcms_has_assembly = check_qcms_has_assembly(); diff --git a/toolkit/library/libxul-rules.mk b/toolkit/library/libxul-rules.mk index 299c22c6136..5152478a36e 100644 --- a/toolkit/library/libxul-rules.mk +++ b/toolkit/library/libxul-rules.mk @@ -40,7 +40,7 @@ EXTRA_DSO_LDOPTS += \ $(LIBS_DIR) \ $(JPEG_LIBS) \ $(PNG_LIBS) \ - $(LCMS_LIBS) \ + $(QCMS_LIBS) \ $(MOZ_JS_LIBS) \ $(NSS_LIBS) \ $(MOZ_CAIRO_LIBS) \ diff --git a/toolkit/toolkit-makefiles.sh b/toolkit/toolkit-makefiles.sh index 7e7646c4ad6..5869cf2fc0a 100644 --- a/toolkit/toolkit-makefiles.sh +++ b/toolkit/toolkit-makefiles.sh @@ -755,10 +755,8 @@ MAKEFILES_libmar=" modules/libmar/tool/Makefile " -MAKEFILES_lcms=" - modules/lcms/Makefile - modules/lcms/include/Makefile - modules/lcms/src/Makefile +MAKEFILES_qcms=" + modules/qcms/Makefile " add_makefiles " @@ -804,6 +802,7 @@ add_makefiles " $MAKEFILES_zlib $MAKEFILES_libmar $MAKEFILES_lcms + $MAKEFILES_qcms " # diff --git a/toolkit/toolkit-tiers.mk b/toolkit/toolkit-tiers.mk index a4dd4037c4d..24fed742fa7 100644 --- a/toolkit/toolkit-tiers.mk +++ b/toolkit/toolkit-tiers.mk @@ -70,7 +70,7 @@ endif tier_external_dirs += modules/libmar endif -tier_external_dirs += modules/lcms +tier_external_dirs += gfx/qcms # # tier "gecko" - core components diff --git a/widget/src/build/Makefile.in b/widget/src/build/Makefile.in index 8fe7feb8d8f..7f42ddf5d72 100644 --- a/widget/src/build/Makefile.in +++ b/widget/src/build/Makefile.in @@ -90,7 +90,7 @@ EXTRA_DSO_LDOPTS = \ $(EXTRA_DSO_LIBS) \ $(MOZ_COMPONENT_LIBS) \ $(MOZ_UNICHARUTIL_LIBS) \ - $(LCMS_LIBS) \ + $(QCMS_LIBS) \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/widget/src/cocoa/Makefile.in b/widget/src/cocoa/Makefile.in index e38a871e948..da463aabbee 100644 --- a/widget/src/cocoa/Makefile.in +++ b/widget/src/cocoa/Makefile.in @@ -66,7 +66,7 @@ REQUIRES = xpcom \ intl \ exthandler \ appshell \ - lcms \ + qcms \ thebes \ js \ xpconnect \ @@ -125,7 +125,7 @@ EXTRA_DSO_LDOPTS += \ $(call EXPAND_LIBNAME_PATH,gkgfx,$(DEPTH)/gfx/src) \ $(MOZ_COMPONENT_LIBS) \ -lthebes \ - $(LCMS_LIBS) \ + $(QCMS_LIBS) \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/widget/src/cocoa/nsCocoaWindow.mm b/widget/src/cocoa/nsCocoaWindow.mm index 3be9c46c68f..0d7e151fd14 100644 --- a/widget/src/cocoa/nsCocoaWindow.mm +++ b/widget/src/cocoa/nsCocoaWindow.mm @@ -64,7 +64,7 @@ #include "nsNativeThemeColors.h" #include "gfxPlatform.h" -#include "lcms.h" +#include "qcms.h" // defined in nsAppShell.mm extern nsCocoaAppModalWindowList *gCocoaAppModalWindowList; @@ -1354,13 +1354,13 @@ NS_IMETHODIMP nsCocoaWindow::SetWindowTitlebarColor(nscolor aColor, PRBool aActi // to match the system appearance lame, so probably we just shouldn't color // correct chrome. if (gfxPlatform::GetCMSMode() == eCMSMode_All) { - cmsHTRANSFORM transform = gfxPlatform::GetCMSRGBATransform(); + qcms_transform *transform = gfxPlatform::GetCMSRGBATransform(); if (transform) { PRUint8 color[3]; color[0] = NS_GET_R(aColor); color[1] = NS_GET_G(aColor); color[2] = NS_GET_B(aColor); - cmsDoTransform(transform, color, color, 1); + qcms_transform_data(transform, color, color, 1); aColor = NS_RGB(color[0], color[1], color[2]); } } diff --git a/widget/src/gtk2/Makefile.in b/widget/src/gtk2/Makefile.in index 25d9f3744f8..3cd6f244f25 100644 --- a/widget/src/gtk2/Makefile.in +++ b/widget/src/gtk2/Makefile.in @@ -69,6 +69,7 @@ REQUIRES = xpcom \ locale \ thebes \ cairo \ + qcms \ $(NULL) ifdef MOZ_X11 @@ -142,7 +143,7 @@ EXTRA_DSO_LDOPTS += \ $(XCOMPOSITE_LIBS) \ $(MOZ_GTK2_LIBS) \ -lthebes \ - $(LCMS_LIBS) \ + $(QCMS_LIBS) \ $(NULL) ifdef MOZ_PLATFORM_HILDON diff --git a/widget/src/os2/Makefile.in b/widget/src/os2/Makefile.in index d1490e56851..8ecbf6e3d4c 100644 --- a/widget/src/os2/Makefile.in +++ b/widget/src/os2/Makefile.in @@ -107,7 +107,7 @@ EXTRA_DSO_LDOPTS = \ $(MOZ_UNICHARUTIL_LIBS) \ $(MOZ_COMPONENT_LIBS) \ -lthebes \ - $(LCMS_LIBS) \ + $(QCMS_LIBS) \ $(NULL) ifdef ENABLE_TESTS diff --git a/widget/src/qt/Makefile.in b/widget/src/qt/Makefile.in index 5e53f0c0a09..2a677e54081 100644 --- a/widget/src/qt/Makefile.in +++ b/widget/src/qt/Makefile.in @@ -106,10 +106,10 @@ EXTRA_DSO_LDOPTS = \ $(MOZ_COMPONENT_LIBS) \ -lgkgfx \ -lthebes \ - $(LCMS_LIBS) \ $(MOZ_JS_LIBS) \ $(MOZ_QT_LIBS) \ $(GLIB_LIBS) \ + $(QCMS_LIBS) \ $(NULL) diff --git a/widget/src/windows/Makefile.in b/widget/src/windows/Makefile.in index 533cf4e7c88..356f8f7959e 100644 --- a/widget/src/windows/Makefile.in +++ b/widget/src/windows/Makefile.in @@ -71,6 +71,7 @@ REQUIRES = xpcom \ unicharutil \ thebes \ cairo \ + qcms \ $(NULL) CPPSRCS = \ diff --git a/widget/src/xpwidgets/Makefile.in b/widget/src/xpwidgets/Makefile.in index 898a8f5fe6a..8b5bde643cd 100644 --- a/widget/src/xpwidgets/Makefile.in +++ b/widget/src/xpwidgets/Makefile.in @@ -61,7 +61,7 @@ REQUIRES = xpcom \ unicharutil \ view \ windowwatcher \ - lcms \ + qcms \ thebes \ $(NULL) diff --git a/widget/src/xpwidgets/nsXPLookAndFeel.cpp b/widget/src/xpwidgets/nsXPLookAndFeel.cpp index 1f8154b45fa..e0a0da80b22 100644 --- a/widget/src/xpwidgets/nsXPLookAndFeel.cpp +++ b/widget/src/xpwidgets/nsXPLookAndFeel.cpp @@ -47,7 +47,7 @@ #include "nsFont.h" #include "gfxPlatform.h" -#include "lcms.h" +#include "qcms.h" #ifdef DEBUG #include "nsSize.h" @@ -632,13 +632,13 @@ nsXPLookAndFeel::GetColor(const nsColorID aID, nscolor &aColor) if (sUseNativeColors && NS_SUCCEEDED(NativeGetColor(aID, aColor))) { if ((gfxPlatform::GetCMSMode() == eCMSMode_All) && !IsSpecialColor(aID, aColor)) { - cmsHTRANSFORM transform = gfxPlatform::GetCMSInverseRGBTransform(); + qcms_transform *transform = gfxPlatform::GetCMSInverseRGBTransform(); if (transform) { PRUint8 color[3]; color[0] = NS_GET_R(aColor); color[1] = NS_GET_G(aColor); color[2] = NS_GET_B(aColor); - cmsDoTransform(transform, color, color, 1); + qcms_transform_data(transform, color, color, 1); aColor = NS_RGB(color[0], color[1], color[2]); } }