diff options
Diffstat (limited to 'source/fitz/color-lcms.c')
-rw-r--r-- | source/fitz/color-lcms.c | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/source/fitz/color-lcms.c b/source/fitz/color-lcms.c new file mode 100644 index 00000000..4e75850f --- /dev/null +++ b/source/fitz/color-lcms.c @@ -0,0 +1,345 @@ +#include "mupdf/fitz.h" +#include "lcms2.h" +#include "lcms2_plugin.h" +#include "colorspace-imp.h" + +#define LCMS_BYTES_MASK 0x7 +/* #define DEBUG_LCMS_MEM(A) do { printf A; fflush(stdout); } while (0) */ +#define DEBUG_LCMS_MEM(A) do { } while (0) + +static void +fz_lcms_log_error(cmsContext id, cmsUInt32Number error_code, const char *error_text) +{ + fz_context *ctx = (fz_context *)cmsGetContextUserData(id); + fz_warn(ctx, "lcms error: %s", error_text); +} + +static void +*fz_lcms_malloc(cmsContext id, unsigned int size) +{ + void *result; + fz_context *ctx = (fz_context *)cmsGetContextUserData(id); + result = fz_malloc_no_throw(ctx, size); + DEBUG_LCMS_MEM(("Allocation:: mupdf ctx = %p lcms ctx = %p allocation = %p \n", (void*) ctx, (void*) id, (void*) result)); + return result; +} + +static void +fz_lcms_free(cmsContext id, void *ptr) +{ + fz_context *ctx = (fz_context *)cmsGetContextUserData(id); + DEBUG_LCMS_MEM(("Free:: mupdf ctx = %p lcms ctx = %p allocation = %p \n", (void*) ctx, (void*) id, (void*) ptr)); + fz_free(ctx, ptr); +} + +static void* +fz_lcms_realloc(cmsContext id, void *ptr, unsigned int size) +{ + fz_context *ctx = (fz_context *)cmsGetContextUserData(id); + DEBUG_LCMS_MEM(("Realloc:: mupdf ctx = %p lcms ctx = %p allocation = %p \n", (void*) ctx, (void*) id, (void*) ptr)); + if (ptr == 0) + return fz_lcms_malloc(id, size); + if (size == 0) + { + fz_lcms_free(id, ptr); + return NULL; + } + return fz_resize_array_no_throw(ctx, ptr, size, 1); +} + +static cmsPluginMemHandler fz_lcms_memhandler = +{ + { + cmsPluginMagicNumber, + 2000, + cmsPluginMemHandlerSig, + NULL + }, + fz_lcms_malloc, + fz_lcms_free, + fz_lcms_realloc, + NULL, + NULL, + NULL, +}; + +static int +fz_lcms_num_devcomps(cmsContext cmm_ctx, fz_iccprofile *profile) +{ + return cmsChannelsOf(cmm_ctx, cmsGetColorSpace(cmm_ctx, profile->cmm_handle)); +} + +static void +fz_lcms_premultiply_row(fz_context *ctx, int n, int w, unsigned char *s) +{ + unsigned char a; + int k; + int n1 = n-1; + + for (; w > 0; w--) + { + a = s[n1]; + for (k = 0; k < n1; k++) + s[k] = fz_mul255(s[k], a); + s += n; + } +} + +static void +fz_lcms_unmultiply_row(fz_context *ctx, int n, int w, unsigned char *s, const unsigned char *in) +{ + int a, inva; + int k; + int n1 = n-1; + + for (; w > 0; w--) + { + a = in[n1]; + inva = a ? 255 * 256 / a : 0; + for (k = 0; k < n1; k++) + s[k] = (in[k] * inva) >> 8; + s += n; + in += n; + } +} + +/* Transform pixmap */ +void +fz_lcms_transform_pixmap(fz_cmm_instance *instance, fz_icclink *link, fz_pixmap *dst, fz_pixmap *src) +{ + cmsContext cmm_ctx = (cmsContext)instance; + fz_context *ctx = (fz_context *)cmsGetContextUserData(cmm_ctx); + cmsHTRANSFORM hTransform = (cmsHTRANSFORM)link->cmm_handle; + int cmm_num_src, cmm_num_des; + unsigned char *inputpos, *outputpos, *buffer; + int ss = src->stride; + int ds = dst->stride; + int sw = src->w; + int dw = dst->w; + int sn = src->n; + int dn = dst->n; + int sa = src->alpha; + int da = dst->alpha; + int h = src->h; + DEBUG_LCMS_MEM(("@@@@@@@ Transform Pixmap Start:: mupdf ctx = %p lcms ctx = %p link = %p \n", (void*)ctx, (void*)cmm_ctx, (void*)link->cmm_handle)); + + /* check the channels. */ + cmm_num_src = T_CHANNELS(cmsGetTransformInputFormat(cmm_ctx, hTransform)); + cmm_num_des = T_CHANNELS(cmsGetTransformOutputFormat(cmm_ctx, hTransform)); + if (cmm_num_src != sn - sa || cmm_num_des != dn - da || sa != da) + fz_throw(ctx, FZ_ERROR_GENERIC, "Mismatching color setup in cmm pixmap transformation: src: %d vs %d+%d, dst: %d vs %d+%d", cmm_num_src, sn-sa, sa, cmm_num_des, dn-da, da); + + /* Transform */ + inputpos = src->samples; + outputpos = dst->samples; + if (src->alpha) + { + /* Allow for premultiplied alpha */ + buffer = fz_malloc(ctx, ss); + for (; h > 0; h--) + { + fz_lcms_unmultiply_row(ctx, sn, sw, buffer, inputpos); + cmsDoTransform(cmm_ctx, hTransform, inputpos, outputpos, sw); + fz_lcms_premultiply_row(ctx, dn, dw, outputpos); + inputpos += ss; + outputpos += ds; + } + fz_free(ctx, buffer); + } + else + { + for (; h > 0; h--) + { + cmsDoTransform(cmm_ctx, hTransform, inputpos, outputpos, sw); + inputpos += ss; + outputpos += ds; + } + } + DEBUG_LCMS_MEM(("@@@@@@@ Transform Pixmap End:: mupdf ctx = %p lcms ctx = %p link = %p \n", (void*)ctx, (void*)cmm_ctx, (void*)link->cmm_handle)); +} + +/* Transform a single color. */ +void +fz_lcms_transform_color(fz_cmm_instance *instance, fz_icclink *link, unsigned short *dst, const unsigned short *src) +{ + cmsContext cmm_ctx = (cmsContext)instance; + cmsHTRANSFORM hTransform = (cmsHTRANSFORM) link->cmm_handle; + + cmsDoTransform(cmm_ctx, hTransform, src, dst, 1); +} + +void +fz_lcms_init_link(fz_cmm_instance *instance, fz_icclink *link, const fz_color_params *rend, int cmm_flags, int num_bytes, int alpha, const fz_iccprofile *src, const fz_iccprofile *prf, const fz_iccprofile *dst) +{ + cmsContext cmm_ctx = (cmsContext)instance; + fz_context *ctx = (fz_context *)cmsGetContextUserData(cmm_ctx); + + cmsUInt32Number src_data_type, des_data_type; + cmsColorSpaceSignature src_cs, des_cs; + int src_num_chan, des_num_chan; + int lcms_src_cs, lcms_des_cs; + unsigned int flag = cmsFLAGS_LOWRESPRECALC | cmm_flags; + + DEBUG_LCMS_MEM(("@@@@@@@ Create Link Start:: mupdf ctx = %p lcms ctx = %p src = %p des = %p \n", (void*)ctx, (void*)cmm_ctx, (void*)src->cmm_handle, (void*)dst->cmm_handle)); + + /* src */ + src_cs = cmsGetColorSpace(cmm_ctx, src->cmm_handle); + lcms_src_cs = _cmsLCMScolorSpace(cmm_ctx, src_cs); + if (lcms_src_cs < 0) + lcms_src_cs = 0; + src_num_chan = cmsChannelsOf(cmm_ctx, src_cs); + src_data_type = (COLORSPACE_SH(lcms_src_cs) | CHANNELS_SH(src_num_chan) | DOSWAP_SH(src->bgr) | BYTES_SH(num_bytes) | EXTRA_SH(alpha)); + + /* dst */ + des_cs = cmsGetColorSpace(cmm_ctx, dst->cmm_handle); + lcms_des_cs = _cmsLCMScolorSpace(cmm_ctx, des_cs); + if (lcms_des_cs < 0) + lcms_des_cs = 0; + des_num_chan = cmsChannelsOf(cmm_ctx, des_cs); + des_data_type = (COLORSPACE_SH(lcms_des_cs) | CHANNELS_SH(des_num_chan) | DOSWAP_SH(dst->bgr) | BYTES_SH(num_bytes) | EXTRA_SH(alpha)); + + /* flags */ + if (rend->bp) + flag |= cmsFLAGS_BLACKPOINTCOMPENSATION; + + if (alpha) + flag |= cmsFLAGS_COPY_ALPHA; + + link->depth = num_bytes; + link->alpha = alpha; + + if (prf == NULL) + { + link->cmm_handle = cmsCreateTransformTHR(cmm_ctx, src->cmm_handle, src_data_type, dst->cmm_handle, des_data_type, rend->ri, flag); + if (!link->cmm_handle) + fz_throw(ctx, FZ_ERROR_GENERIC, "cmsCreateTransform failed"); + } + else + { + /* littleCMS proof creation links don't work properly with the Ghent + * test files. Handle this in a brutish manner. + */ + if (src == prf) + { + link->cmm_handle = cmsCreateTransformTHR(cmm_ctx, src->cmm_handle, src_data_type, dst->cmm_handle, des_data_type, INTENT_RELATIVE_COLORIMETRIC, flag); + if (!link->cmm_handle) + fz_throw(ctx, FZ_ERROR_GENERIC, "cmsCreateTransform failed"); + } + else if (prf == dst) + { + link->cmm_handle = cmsCreateTransformTHR(cmm_ctx, src->cmm_handle, src_data_type, prf->cmm_handle, des_data_type, rend->ri, flag); + if (!link->cmm_handle) + fz_throw(ctx, FZ_ERROR_GENERIC, "cmsCreateTransform failed"); + } + else + { + cmsHPROFILE src_to_prf_profile; + cmsHTRANSFORM src_to_prf_link; + cmsColorSpaceSignature prf_cs; + int prf_num_chan; + int lcms_prf_cs; + cmsUInt32Number prf_data_type; + cmsHPROFILE hProfiles[3]; + + prf_cs = cmsGetColorSpace(cmm_ctx, prf->cmm_handle); + lcms_prf_cs = _cmsLCMScolorSpace(cmm_ctx, prf_cs); + if (lcms_prf_cs < 0) + lcms_prf_cs = 0; + prf_num_chan = cmsChannelsOf(cmm_ctx, prf_cs); + prf_data_type = (COLORSPACE_SH(lcms_prf_cs) | CHANNELS_SH(prf_num_chan) | BYTES_SH(num_bytes)); + src_to_prf_link = cmsCreateTransformTHR(cmm_ctx, src->cmm_handle, src_data_type, prf->cmm_handle, prf_data_type, rend->ri, flag); + if (!src_to_prf_link) + fz_throw(ctx, FZ_ERROR_GENERIC, "cmsCreateTransform failed"); + src_to_prf_profile = cmsTransform2DeviceLink(cmm_ctx, src_to_prf_link, 3.4, flag); + cmsDeleteTransform(cmm_ctx, src_to_prf_link); + if (!src_to_prf_profile) + fz_throw(ctx, FZ_ERROR_GENERIC, "cmsTransform2DeviceLink failed"); + + hProfiles[0] = src_to_prf_profile; + hProfiles[1] = prf->cmm_handle; + hProfiles[2] = dst->cmm_handle; + link->cmm_handle = cmsCreateMultiprofileTransformTHR(cmm_ctx, hProfiles, 3, src_data_type, des_data_type, INTENT_RELATIVE_COLORIMETRIC, flag); + cmsCloseProfile(cmm_ctx, src_to_prf_profile); + if (!link->cmm_handle) + fz_throw(ctx, FZ_ERROR_GENERIC, "cmsCreateMultiprofileTransform failed"); + } + } + + DEBUG_LCMS_MEM(("@@@@@@@ Create Link End:: mupdf ctx = %p lcms ctx = %p link = %p link_cmm = %p src = %p des = %p \n", (void*)ctx, (void*)cmm_ctx, (void*)link, (void*)link->cmm_handle, (void*)src->cmm_handle, (void*)dst->cmm_handle)); +} + +void +fz_lcms_fin_link(fz_cmm_instance *instance, fz_icclink *link) +{ + cmsContext cmm_ctx = (cmsContext)instance; + DEBUG_LCMS_MEM(("Free Link:: link = %p \n", (void*)link->cmm_handle)); + if (link->cmm_handle != NULL) + cmsDeleteTransform(cmm_ctx, link->cmm_handle); + link->cmm_handle = NULL; +} + +static fz_cmm_instance * +fz_lcms_new_instance(fz_context *ctx) +{ + cmsContext cmm_ctx; + + cmm_ctx = cmsCreateContext(&fz_lcms_memhandler, ctx); + DEBUG_LCMS_MEM(("Context Creation:: mupdf ctx = %p lcms ctx = %p \n", (void*) ctx, (void*) cmm_ctx)); + if (cmm_ctx == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "cmsCreateContext failed"); + cmsSetLogErrorHandlerTHR(cmm_ctx, fz_lcms_log_error); + return (fz_cmm_instance *)cmm_ctx; +} + +static void +fz_lcms_drop_instance(fz_cmm_instance *instance) +{ + DEBUG_LCMS_MEM(("Context Destruction:: lcms ctx = %p \n", (void*)instance)); + if (instance == NULL) + return; + cmsDeleteContext((cmsContext)instance); +} + +static void +fz_lcms_init_profile(fz_cmm_instance *instance, fz_iccprofile *profile) +{ + cmsContext cmm_ctx = (cmsContext)instance; + fz_context *ctx = (fz_context *)cmsGetContextUserData(cmm_ctx); + size_t size; + unsigned char *data; + + DEBUG_LCMS_MEM(("@@@@@@@ Create Profile Start:: mupdf ctx = %p lcms ctx = %p \n", (void*)ctx, (void*)cmm_ctx)); + + size = fz_buffer_storage(ctx, profile->buffer, &data); + profile->cmm_handle = cmsOpenProfileFromMemTHR(cmm_ctx, data, size); + if (profile->cmm_handle == NULL) + { + profile->num_devcomp = 0; + fz_throw(ctx, FZ_ERROR_GENERIC, "cmsOpenProfileFromMem failed"); + } + profile->num_devcomp = fz_lcms_num_devcomps(cmm_ctx, profile); + + DEBUG_LCMS_MEM(("@@@@@@@ Create Profile End:: mupdf ctx = %p lcms ctx = %p profile = %p profile_cmm = %p \n", (void*)ctx, (void*)cmm_ctx, (void*)profile, (void*)profile->cmm_handle)); +} + +static void +fz_lcms_fin_profile(fz_cmm_instance *instance, fz_iccprofile *profile) +{ + cmsContext cmm_ctx = (cmsContext)instance; + DEBUG_LCMS_MEM(("Free Profile:: profile = %p \n", (void*) profile->cmm_handle)); + if (profile->cmm_handle != NULL) + cmsCloseProfile(cmm_ctx, profile->cmm_handle); + profile->cmm_handle = NULL; +} + +fz_cmm_engine fz_cmm_engine_lcms = { + fz_lcms_new_instance, + fz_lcms_drop_instance, + fz_lcms_transform_pixmap, + fz_lcms_transform_color, + fz_lcms_init_link, + fz_lcms_fin_link, + fz_lcms_init_profile, + fz_lcms_fin_profile, + cmsFLAGS_NOWHITEONWHITEFIXUP +}; |