diff options
Diffstat (limited to 'source')
55 files changed, 4225 insertions, 577 deletions
diff --git a/source/cbz/mucbz.c b/source/cbz/mucbz.c index 7000dc8f..2d357b6e 100644 --- a/source/cbz/mucbz.c +++ b/source/cbz/mucbz.c @@ -162,7 +162,7 @@ cbz_run_page(fz_context *ctx, fz_page *page_, fz_device *dev, const fz_matrix *c w = image->w * DPI / xres; h = image->h * DPI / yres; fz_pre_scale(&local_ctm, w, h); - fz_fill_image(ctx, dev, image, &local_ctm, 1); + fz_fill_image(ctx, dev, image, &local_ctm, 1, NULL); } static void diff --git a/source/cbz/muimg.c b/source/cbz/muimg.c index 6fe0687d..4cb8cc4a 100644 --- a/source/cbz/muimg.c +++ b/source/cbz/muimg.c @@ -57,7 +57,7 @@ img_run_page(fz_context *ctx, fz_page *page_, fz_device *dev, const fz_matrix *c w = image->w * DPI / xres; h = image->h * DPI / yres; fz_pre_scale(&local_ctm, w, h); - fz_fill_image(ctx, dev, image, &local_ctm, 1); + fz_fill_image(ctx, dev, image, &local_ctm, 1, NULL); } static void diff --git a/source/cbz/mutiff.c b/source/cbz/mutiff.c index 9cd306f2..b6500159 100644 --- a/source/cbz/mutiff.c +++ b/source/cbz/mutiff.c @@ -47,7 +47,7 @@ tiff_run_page(fz_context *ctx, fz_page *page_, fz_device *dev, const fz_matrix * w = image->w * DPI / xres; h = image->h * DPI / yres; fz_pre_scale(&local_ctm, w, h); - fz_fill_image(ctx, dev, image, &local_ctm, 1); + fz_fill_image(ctx, dev, image, &local_ctm, 1, NULL); } static void diff --git a/source/fitz/bbox-device.c b/source/fitz/bbox-device.c index 99c46953..a288ec45 100644 --- a/source/fitz/bbox-device.c +++ b/source/fitz/bbox-device.c @@ -37,7 +37,7 @@ fz_bbox_add_rect(fz_context *ctx, fz_device *dev, const fz_rect *rect, int clip) static void fz_bbox_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_rect r; fz_bbox_add_rect(ctx, dev, fz_bound_path(ctx, path, NULL, ctm, &r), 0); @@ -45,7 +45,7 @@ fz_bbox_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even static void fz_bbox_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, - const fz_matrix *ctm, fz_colorspace *colorspace, const float *color, float alpha) + const fz_matrix *ctm, fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_rect r; fz_bbox_add_rect(ctx, dev, fz_bound_path(ctx, path, stroke, ctm, &r), 0); @@ -53,7 +53,7 @@ fz_bbox_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const static void fz_bbox_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_rect r; fz_bbox_add_rect(ctx, dev, fz_bound_text(ctx, text, NULL, ctm, &r), 0); @@ -61,21 +61,21 @@ fz_bbox_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz static void fz_bbox_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, - const fz_matrix *ctm, fz_colorspace *colorspace, const float *color, float alpha) + const fz_matrix *ctm, fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_rect r; fz_bbox_add_rect(ctx, dev, fz_bound_text(ctx, text, stroke, ctm, &r), 0); } static void -fz_bbox_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha) +fz_bbox_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha, const fz_color_params *color_params) { fz_rect r; fz_bbox_add_rect(ctx, dev, fz_bound_shade(ctx, shade, ctm, &r), 0); } static void -fz_bbox_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha) +fz_bbox_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha, const fz_color_params *color_params) { fz_rect r = fz_unit_rect; fz_bbox_add_rect(ctx, dev, fz_transform_rect(&r, ctm), 0); @@ -83,7 +83,7 @@ fz_bbox_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_ma static void fz_bbox_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_rect r = fz_unit_rect; fz_bbox_add_rect(ctx, dev, fz_transform_rect(&r, ctm), 0); @@ -135,7 +135,7 @@ fz_bbox_pop_clip(fz_context *ctx, fz_device *dev) } static void -fz_bbox_begin_mask(fz_context *ctx, fz_device *dev, const fz_rect *rect, int luminosity, fz_colorspace *colorspace, const float *color) +fz_bbox_begin_mask(fz_context *ctx, fz_device *dev, const fz_rect *rect, int luminosity, fz_colorspace *colorspace, const float *color, const fz_color_params *color_params) { fz_bbox_device *bdev = (fz_bbox_device*)dev; fz_bbox_add_rect(ctx, dev, rect, 1); diff --git a/source/fitz/bitmap.c b/source/fitz/bitmap.c index eefaebd7..37aabe7b 100644 --- a/source/fitz/bitmap.c +++ b/source/fitz/bitmap.c @@ -306,7 +306,7 @@ fz_clear_bitmap(fz_context *ctx, fz_bitmap *bit) } static void -pbm_write_header(fz_context *ctx, fz_band_writer *writer) +pbm_write_header(fz_context *ctx, fz_band_writer *writer, const fz_colorspace *cs) { fz_output *out = writer->out; int w = writer->w; @@ -316,7 +316,7 @@ pbm_write_header(fz_context *ctx, fz_band_writer *writer) } static void -pkm_write_header(fz_context *ctx, fz_band_writer *writer) +pkm_write_header(fz_context *ctx, fz_band_writer *writer, const fz_colorspace *cs) { fz_output *out = writer->out; int w = writer->w; @@ -336,7 +336,7 @@ fz_write_bitmap_as_pbm(fz_context *ctx, fz_output *out, fz_bitmap *bitmap) writer = fz_new_pbm_band_writer(ctx, out); fz_try(ctx) { - fz_write_header(ctx, writer, bitmap->w, bitmap->h, 1, 0, 0, 0, 0); + fz_write_header(ctx, writer, bitmap->w, bitmap->h, 1, 0, 0, 0, 0, NULL); fz_write_band(ctx, writer, bitmap->stride, bitmap->h, bitmap->samples); } fz_always(ctx) @@ -356,7 +356,7 @@ fz_write_bitmap_as_pkm(fz_context *ctx, fz_output *out, fz_bitmap *bitmap) writer = fz_new_pkm_band_writer(ctx, out); fz_try(ctx) { - fz_write_header(ctx, writer, bitmap->w, bitmap->h, 4, 0, 0, 0, 0); + fz_write_header(ctx, writer, bitmap->w, bitmap->h, 4, 0, 0, 0, 0, NULL); fz_write_band(ctx, writer, bitmap->stride, bitmap->h, bitmap->samples); } fz_always(ctx) diff --git a/source/fitz/buffer.c b/source/fitz/buffer.c index 8212f782..96aa36a6 100644 --- a/source/fitz/buffer.c +++ b/source/fitz/buffer.c @@ -251,6 +251,22 @@ fz_append_rune(fz_context *ctx, fz_buffer *buf, int c) } void +fz_append_int32_be(fz_context *ctx, fz_buffer *buf, int x) +{ + fz_append_byte(ctx, buf, (x >> 24) & 0xFF); + fz_append_byte(ctx, buf, (x >> 16) & 0xFF); + fz_append_byte(ctx, buf, (x >> 8) & 0xFF); + fz_append_byte(ctx, buf, (x) & 0xFF); +} + +void +fz_append_int16_be(fz_context *ctx, fz_buffer *buf, int x) +{ + fz_append_byte(ctx, buf, (x >> 8) & 0xFF); + fz_append_byte(ctx, buf, (x) & 0xFF); +} + +void fz_append_int32_le(fz_context *ctx, fz_buffer *buf, int x) { fz_append_byte(ctx, buf, (x)&0xFF); @@ -435,7 +451,8 @@ fz_md5_buffer(fz_context *ctx, fz_buffer *buffer, unsigned char digest[16]) { fz_md5 state; fz_md5_init(&state); - fz_md5_update(&state, buffer->data, buffer->len); + if (buffer) + fz_md5_update(&state, buffer->data, buffer->len); fz_md5_final(&state, digest); } diff --git a/source/fitz/color-icc-create.c b/source/fitz/color-icc-create.c new file mode 100644 index 00000000..58acedf5 --- /dev/null +++ b/source/fitz/color-icc-create.c @@ -0,0 +1,448 @@ +#include "mupdf/fitz.h" +#include "icc34.h" + +#define SAVEICCPROFILE 0 +#define ICC_HEADER_SIZE 128 +#define ICC_TAG_SIZE 12 +#define ICC_NUMBER_COMMON_TAGS 2 +#define ICC_XYZPT_SIZE 12 +#define ICC_DATATYPE_SIZE 8 +#define D50_X 0.9642f +#define D50_Y 1.0f +#define D50_Z 0.8249f +static const char desc_name[] = "MuPDF Internal Profile"; +static const char copy_right[] = "Copyright Artifex Software 2017"; +#if SAVEICCPROFILE +unsigned int icc_debug_index = 0; +#endif + +typedef struct fz_icc_tag_s fz_icc_tag; + +struct fz_icc_tag_s +{ + icTagSignature sig; + icUInt32Number offset; + icUInt32Number size; + unsigned char byte_padding; +}; + +#if SAVEICCPROFILE +static void +save_profile(fz_context *ctx, fz_buffer *buf, const char *name) +{ + char full_file_name[50]; + fz_snprintf(full_file_name, sizeof full_file_name, "profile%d-%s.icc", icc_debug_index, name); + fz_save_buffer(ctx, buf, full_file_name); + icc_debug_index++; +} +#endif + +static void +fz_append_byte_n(fz_context *ctx, fz_buffer *buf, int c, int n) +{ + int k; + + for (k = 0; k < n; k++) + fz_append_byte(ctx, buf, c); +} + +static int +get_padding(int x) +{ + return (4 - x % 4) % 4; +} + +static void +setdatetime(fz_context *ctx, icDateTimeNumber *datetime) +{ + datetime->day = 0; + datetime->hours = 0; + datetime->minutes = 0; + datetime->month = 0; + datetime->seconds = 0; + datetime->year = 0; +} + +static void +add_gammadata(fz_context *ctx, fz_buffer *buf, unsigned short gamma, icTagTypeSignature curveType) +{ + fz_append_int32_be(ctx, buf, curveType); + fz_append_byte_n(ctx, buf, 0, 4); + + /* one entry for gamma */ + fz_append_int32_be(ctx, buf, 1); + + /* The encode (8frac8) gamma, with padding */ + fz_append_int16_be(ctx, buf, gamma); + + /* pad two bytes */ + fz_append_byte_n(ctx, buf, 0, 2); +} + +static unsigned short +float2u8Fixed8(fz_context *ctx, float number_in) +{ + return (unsigned short)(number_in * 256); +} + +static void +add_xyzdata(fz_context *ctx, fz_buffer *buf, icS15Fixed16Number temp_XYZ[]) +{ + int j; + + fz_append_int32_be(ctx, buf, icSigXYZType); + fz_append_byte_n(ctx, buf, 0, 4); + + for (j = 0; j < 3; j++) + fz_append_int32_be(ctx, buf, temp_XYZ[j]); +} + +static icS15Fixed16Number +double2XYZtype(fz_context *ctx, float number_in) +{ + short s; + unsigned short m; + + if (number_in < 0) + number_in = 0; + s = (short)number_in; + m = (unsigned short)((number_in - s) * 65536); + return (icS15Fixed16Number) ((s << 16) | m); +} + +static void +get_D50(fz_context *ctx, icS15Fixed16Number XYZ[]) +{ + XYZ[0] = double2XYZtype(ctx, D50_X); + XYZ[1] = double2XYZtype(ctx, D50_Y); + XYZ[2] = double2XYZtype(ctx, D50_Z); +} + +static void +get_XYZ_doubletr(fz_context *ctx, icS15Fixed16Number XYZ[], float vector[]) +{ + XYZ[0] = double2XYZtype(ctx, vector[0]); + XYZ[1] = double2XYZtype(ctx, vector[1]); + XYZ[2] = double2XYZtype(ctx, vector[2]); +} + +static void +add_desc_tag(fz_context *ctx, fz_buffer *buf, const char text[], fz_icc_tag tag_list[], int curr_tag) +{ + int len = strlen(text); + + fz_append_int32_be(ctx, buf, icSigTextDescriptionType); + fz_append_byte_n(ctx, buf, 0, 4); + fz_append_int32_be(ctx, buf, len + 1); + fz_append_string(ctx, buf, text); + /* 1 + 4 + 4 + 2 + 1 + 67 */ + fz_append_byte_n(ctx, buf, 0, 79); + fz_append_byte_n(ctx, buf, 0, tag_list[curr_tag].byte_padding); +} + +static void +add_text_tag(fz_context *ctx, fz_buffer *buf, const char text[], fz_icc_tag tag_list[], int curr_tag) +{ + fz_append_int32_be(ctx, buf, icSigTextType); + fz_append_byte_n(ctx, buf, 0, 4); + fz_append_string(ctx, buf, text); + fz_append_byte(ctx, buf, 0); + fz_append_byte_n(ctx, buf, 0, tag_list[curr_tag].byte_padding); +} + +static void +add_common_tag_data(fz_context *ctx, fz_buffer *buf, fz_icc_tag tag_list[]) +{ + add_desc_tag(ctx, buf, desc_name, tag_list, 0); + add_text_tag(ctx, buf, copy_right, tag_list, 1); +} + +static void +init_common_tags(fz_context *ctx, fz_icc_tag tag_list[], int num_tags, int *last_tag) +{ + int curr_tag, temp_size; + + if (*last_tag < 0) + curr_tag = 0; + else + curr_tag = (*last_tag) + 1; + + tag_list[curr_tag].offset = ICC_HEADER_SIZE + num_tags * ICC_TAG_SIZE + 4; + tag_list[curr_tag].sig = icSigProfileDescriptionTag; + + /* temp_size = DATATYPE_SIZE + 4 (zeros) + 4 (len) + strlen(desc_name) + 1 (null) + 4 + 4 + 2 + 1 + 67 + bytepad; */ + temp_size = strlen(desc_name) + 91; + + tag_list[curr_tag].byte_padding = get_padding(temp_size); + tag_list[curr_tag].size = temp_size + tag_list[curr_tag].byte_padding; + curr_tag++; + tag_list[curr_tag].offset = tag_list[curr_tag - 1].offset + tag_list[curr_tag - 1].size; + tag_list[curr_tag].sig = icSigCopyrightTag; + + /* temp_size = DATATYPE_SIZE + 4 (zeros) + strlen(copy_right) + 1 (null); */ + temp_size = strlen(copy_right) + 9; + tag_list[curr_tag].byte_padding = get_padding(temp_size); + tag_list[curr_tag].size = temp_size + tag_list[curr_tag].byte_padding; + *last_tag = curr_tag; +} + +static void +copy_header(fz_context *ctx, fz_buffer *buffer, icHeader *header) +{ + fz_append_int32_be(ctx, buffer, header->size); + fz_append_byte_n(ctx, buffer, 0, 4); + fz_append_int32_be(ctx, buffer, header->version); + fz_append_int32_be(ctx, buffer, header->deviceClass); + fz_append_int32_be(ctx, buffer, header->colorSpace); + fz_append_int32_be(ctx, buffer, header->pcs); + fz_append_byte_n(ctx, buffer, 0, 12); + fz_append_int32_be(ctx, buffer, header->magic); + fz_append_int32_be(ctx, buffer, header->platform); + fz_append_byte_n(ctx, buffer, 0, 24); + fz_append_int32_be(ctx, buffer, header->illuminant.X); + fz_append_int32_be(ctx, buffer, header->illuminant.Y); + fz_append_int32_be(ctx, buffer, header->illuminant.Z); + fz_append_byte_n(ctx, buffer, 0, 48); +} + +static void +setheader_common(fz_context *ctx, icHeader *header) +{ + header->cmmId = 0; + header->version = 0x02200000; + setdatetime(ctx, &(header->date)); + header->magic = icMagicNumber; + header->platform = icSigMacintosh; + header->flags = 0; + header->manufacturer = 0; + header->model = 0; + header->attributes[0] = 0; + header->attributes[1] = 0; + header->renderingIntent = 3; + header->illuminant.X = double2XYZtype(ctx, (float) 0.9642); + header->illuminant.Y = double2XYZtype(ctx, (float) 1.0); + header->illuminant.Z = double2XYZtype(ctx, (float) 0.8249); + header->creator = 0; + memset(header->reserved, 0, 44); +} + +static void +copy_tagtable(fz_context *ctx, fz_buffer *buf, fz_icc_tag *tag_list, int num_tags) +{ + int k; + + fz_append_int32_be(ctx, buf, num_tags); + for (k = 0; k < num_tags; k++) + { + fz_append_int32_be(ctx, buf, tag_list[k].sig); + fz_append_int32_be(ctx, buf, tag_list[k].offset); + fz_append_int32_be(ctx, buf, tag_list[k].size); + } +} + +static void +init_tag(fz_context *ctx, fz_icc_tag tag_list[], int *last_tag, icTagSignature tagsig, int datasize) +{ + int curr_tag = (*last_tag) + 1; + + tag_list[curr_tag].offset = tag_list[curr_tag - 1].offset + tag_list[curr_tag - 1].size; + tag_list[curr_tag].sig = tagsig; + tag_list[curr_tag].byte_padding = get_padding(ICC_DATATYPE_SIZE + datasize); + tag_list[curr_tag].size = ICC_DATATYPE_SIZE + datasize + tag_list[curr_tag].byte_padding; + *last_tag = curr_tag; +} + +static void +matrixmult(fz_context *ctx, float leftmatrix[], int nlrow, int nlcol, float rightmatrix[], int nrrow, int nrcol, float result[]) +{ + float *curr_row; + int k, l, j, ncols, nrows; + float sum; + + nrows = nlrow; + ncols = nrcol; + if (nlcol == nrrow) + { + for (k = 0; k < nrows; k++) + { + curr_row = &(leftmatrix[k*nlcol]); + for (l = 0; l < ncols; l++) + { + sum = 0.0; + for (j = 0; j < nlcol; j++) + sum = sum + curr_row[j] * rightmatrix[j*nrcol + l]; + result[k*ncols + l] = sum; + } + } + } +} + +static void +apply_adaption(fz_context *ctx, float matrix[], float in[], float out[]) +{ + out[0] = matrix[0] * in[0] + matrix[1] * in[1] + matrix[2] * in[2]; + out[1] = matrix[3] * in[0] + matrix[4] * in[1] + matrix[5] * in[2]; + out[2] = matrix[6] * in[0] + matrix[7] * in[1] + matrix[8] * in[2]; +} + +/* + Compute the CAT02 transformation to get us from the Cal White point to the + D50 white point +*/ +static void +gsicc_create_compute_cam(fz_context *ctx, float white_src[], float *cam) +{ + float cat02matrix[] = { 0.7328f, 0.4296f, -0.1624f, -0.7036f, 1.6975f, 0.0061f, 0.003f, 0.0136f, 0.9834f }; + float cat02matrixinv[] = { 1.0961f, -0.2789f, 0.1827f, 0.4544f, 0.4735f, 0.0721f, -0.0096f, -0.0057f, 1.0153f }; + float vonkries_diag[9]; + float temp_matrix[9]; + float lms_wp_src[3], lms_wp_des[3]; + int k; + float d50[3] = { D50_X, D50_Y, D50_Z }; + + matrixmult(ctx, cat02matrix, 3, 3, white_src, 3, 1, lms_wp_src); + matrixmult(ctx, cat02matrix, 3, 3, d50, 3, 1, lms_wp_des); + memset(&(vonkries_diag[0]), 0, sizeof(float) * 9); + + for (k = 0; k < 3; k++) + { + if (lms_wp_src[k] > 0) + vonkries_diag[k * 3 + k] = lms_wp_des[k] / lms_wp_src[k]; + else + vonkries_diag[k * 3 + k] = 1; + } + matrixmult(ctx, &(vonkries_diag[0]), 3, 3, cat02matrix, 3, 3, temp_matrix); + matrixmult(ctx, &(cat02matrixinv[0]), 3, 3, temp_matrix, 3, 3, cam); +} + +/* Create ICC profile from PDF calGray and calRGB definitions */ +fz_buffer * +fz_new_icc_data_from_cal_colorspace(fz_context *ctx, fz_cal_colorspace *cal) +{ + fz_icc_tag *tag_list; + icProfile iccprofile; + icHeader *header = &(iccprofile.header); + fz_buffer *profile; + size_t profile_size; + int k; + int num_tags; + unsigned short encode_gamma; + int last_tag; + icS15Fixed16Number temp_XYZ[3]; + int tag_location; + icTagSignature TRC_Tags[3] = { icSigRedTRCTag, icSigGreenTRCTag, icSigBlueTRCTag }; + int trc_tag_size; + float cat02[9]; + float black_adapt[3]; + int n = cal->n; + + /* common */ + setheader_common(ctx, header); + header->pcs = icSigXYZData; + profile_size = ICC_HEADER_SIZE; + header->deviceClass = icSigInputClass; + + if (n == 3) + { + header->colorSpace = icSigRgbData; + num_tags = 10; /* common (2) + rXYZ, gXYZ, bXYZ, rTRC, gTRC, bTRC, bkpt, wtpt */ + } + else + { + header->colorSpace = icSigGrayData; + num_tags = 5; /* common (2) + GrayTRC, bkpt, wtpt */ + TRC_Tags[0] = icSigGrayTRCTag; + } + + tag_list = fz_malloc(ctx, sizeof(fz_icc_tag) * num_tags); + + /* precompute sizes and offsets */ + profile_size += ICC_TAG_SIZE * num_tags; + profile_size += 4; /* number of tags.... */ + last_tag = -1; + init_common_tags(ctx, tag_list, num_tags, &last_tag); + if (n == 3) + { + init_tag(ctx, tag_list, &last_tag, icSigRedColorantTag, ICC_XYZPT_SIZE); + init_tag(ctx, tag_list, &last_tag, icSigGreenColorantTag, ICC_XYZPT_SIZE); + init_tag(ctx, tag_list, &last_tag, icSigBlueColorantTag, ICC_XYZPT_SIZE); + } + init_tag(ctx, tag_list, &last_tag, icSigMediaWhitePointTag, ICC_XYZPT_SIZE); + init_tag(ctx, tag_list, &last_tag, icSigMediaBlackPointTag, ICC_XYZPT_SIZE); + + /* 4 for count, 2 for gamma, Extra 2 bytes for 4 byte alignment requirement */ + trc_tag_size = 8; + for (k = 0; k < n; k++) + init_tag(ctx, tag_list, &last_tag, TRC_Tags[k], trc_tag_size); + for (k = 0; k < num_tags; k++) + profile_size += tag_list[k].size; + + /* Allocate buffer */ + fz_try(ctx) + { + profile = fz_new_buffer(ctx, profile_size); + } + fz_catch(ctx) + { + fz_free(ctx, tag_list); + fz_rethrow(ctx); + } + + /* Header */ + header->size = (icUInt32Number)profile_size; + copy_header(ctx, profile, header); + + /* Tag table */ + copy_tagtable(ctx, profile, tag_list, num_tags); + + /* Common tags */ + add_common_tag_data(ctx, profile, tag_list); + tag_location = ICC_NUMBER_COMMON_TAGS; + + /* Get the cat02 matrix */ + gsicc_create_compute_cam(ctx, cal->wp, cat02); + + /* The matrix */ + if (n == 3) + { + float primary[3]; + + for (k = 0; k < 3; k++) + { + /* Apply the cat02 matrix to the primaries */ + apply_adaption(ctx, cat02, &(cal->matrix[k * 3]), &(primary[0])); + get_XYZ_doubletr(ctx, temp_XYZ, &(primary[0])); + add_xyzdata(ctx, profile, temp_XYZ); + tag_location++; + } + } + + /* White and black points. WP is D50 */ + get_D50(ctx, temp_XYZ); + add_xyzdata(ctx, profile, temp_XYZ); + tag_location++; + + /* Black point. Apply cat02*/ + apply_adaption(ctx, cat02, cal->bp, &(black_adapt[0])); + get_XYZ_doubletr(ctx, temp_XYZ, &(black_adapt[0])); + add_xyzdata(ctx, profile, temp_XYZ); + tag_location++; + + /* Gamma */ + for (k = 0; k < n; k++) + { + encode_gamma = float2u8Fixed8(ctx, cal->gamma[k]); + add_gammadata(ctx, profile, encode_gamma, icSigCurveType); + tag_location++; + } + + fz_free(ctx, tag_list); + +#if SAVEICCPROFILE + if (n == 3) + save_profile(ctx, profile, "calRGB"); + else + save_profile(ctx, profile, "calGray"); +#endif + return profile; +} 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 +}; diff --git a/source/fitz/colorspace-imp.h b/source/fitz/colorspace-imp.h index bf8f3fef..9c909a4d 100644 --- a/source/fitz/colorspace-imp.h +++ b/source/fitz/colorspace-imp.h @@ -1,6 +1,21 @@ #ifndef MUPDF_FITZ_COLORSPACE_IMP_H #define MUPDF_FITZ_COLORSPACE_IMP_H +#include "mupdf/fitz/context.h" +#include "mupdf/fitz/colorspace.h" +#include "mupdf/fitz/color-management.h" +#include "mupdf/fitz/pixmap.h" + +int fz_cmm_avoid_white_fix_flag(fz_context *ctx); +void fz_cmm_transform_pixmap(fz_context *ctx, fz_icclink *link, fz_pixmap *dst, fz_pixmap *src); +void fz_cmm_transform_color(fz_context *ctx, fz_icclink *link, unsigned short *dst, const unsigned short *src); +void fz_cmm_init_link(fz_context *ctx, 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 *des); +void fz_cmm_fin_link(fz_context *ctx, fz_icclink *link); +fz_cmm_instance *fz_cmm_new_instance(fz_context *ctx); +void fz_cmm_drop_instance(fz_context *ctx); +void fz_cmm_init_profile(fz_context *ctx, fz_iccprofile *profile); +void fz_cmm_fin_profile(fz_context *ctx, fz_iccprofile *profile); + struct fz_colorspace_s { fz_storable storable; @@ -8,10 +23,48 @@ struct fz_colorspace_s char name[16]; int n; int is_subtractive; - fz_colorspace_convert_fn *to_rgb; - fz_colorspace_convert_fn *from_rgb; + fz_colorspace_convert_fn *to_ccs; + fz_colorspace_convert_fn *from_ccs; + fz_colorspace_clamp_fn *clamp; + fz_colorspace_base_fn *get_base; fz_colorspace_destruct_fn *free_data; void *data; }; +struct fz_iccprofile_s +{ + int num_devcomp; + int bgr; + fz_buffer *buffer; + unsigned char md5[16]; + void *cmm_handle; +}; + +struct fz_icclink_s +{ + fz_storable storable; + int num_in; + int num_out; + int depth; + int alpha; + int is_identity; + void *cmm_handle; +}; + +struct fz_default_colorspaces_s +{ + int refs; + fz_colorspace *gray; + fz_colorspace *rgb; + fz_colorspace *cmyk; + fz_colorspace *oi; +}; + +struct fz_colorspace_context_s +{ + int ctx_refs; + const fz_cmm_engine *cmm; + fz_colorspace *gray, *rgb, *bgr, *cmyk, *lab; +}; + #endif diff --git a/source/fitz/colorspace.c b/source/fitz/colorspace.c index 6113e496..d040b076 100644 --- a/source/fitz/colorspace.c +++ b/source/fitz/colorspace.c @@ -1,13 +1,139 @@ #include "mupdf/fitz.h" #include "colorspace-imp.h" +#include "fitz-imp.h" #include <assert.h> #include <math.h> #include <string.h> +/* CMM module */ + +int +fz_cmm_avoid_white_fix_flag(fz_context *ctx) +{ + if (ctx && ctx->colorspace && ctx->colorspace->cmm && ctx->cmm_instance) + return ctx->colorspace->cmm->avoid_white_fix_flag; + return 0; +} + +void +fz_cmm_transform_pixmap(fz_context *ctx, fz_icclink *link, fz_pixmap *dst, fz_pixmap *src) +{ + if (ctx && ctx->colorspace && ctx->colorspace->cmm && ctx->cmm_instance) + ctx->colorspace->cmm->transform_pixmap(ctx->cmm_instance, link, dst, src); +} + +void +fz_cmm_transform_color(fz_context *ctx, fz_icclink *link, unsigned short *dst, const unsigned short *src) +{ + if (ctx && ctx->colorspace && ctx->colorspace->cmm && ctx->cmm_instance) + ctx->colorspace->cmm->transform_color(ctx->cmm_instance, link, dst, src); +} + +void +fz_cmm_init_link(fz_context *ctx, 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 *des) +{ + if (ctx && ctx->colorspace && ctx->colorspace->cmm && ctx->cmm_instance) + ctx->colorspace->cmm->init_link(ctx->cmm_instance, link, rend, cmm_flags, num_bytes, alpha, src, prf, des); +} + +void +fz_cmm_fin_link(fz_context *ctx, fz_icclink *link) +{ + if (ctx && ctx->colorspace && ctx->colorspace->cmm && ctx->cmm_instance) + ctx->colorspace->cmm->fin_link(ctx->cmm_instance, link); +} + +fz_cmm_instance *fz_cmm_new_instance(fz_context *ctx) +{ + if (ctx && ctx->colorspace && ctx->colorspace->cmm) + return ctx->colorspace->cmm->new_instance(ctx); + return NULL; +} + +void fz_cmm_drop_instance(fz_context *ctx) +{ + if (ctx && ctx->colorspace && ctx->colorspace->cmm && ctx->cmm_instance) + ctx->colorspace->cmm->drop_instance(ctx->cmm_instance); +} + +void fz_cmm_init_profile(fz_context *ctx, fz_iccprofile *profile) +{ + if (ctx && ctx->colorspace && ctx->colorspace->cmm && ctx->cmm_instance) + ctx->colorspace->cmm->init_profile(ctx->cmm_instance, profile); +} + +void fz_cmm_fin_profile(fz_context *ctx, fz_iccprofile *profile) +{ + if (ctx && ctx->colorspace && ctx->colorspace->cmm && ctx->cmm_instance) + if (profile && profile->cmm_handle != NULL) + ctx->colorspace->cmm->fin_profile(ctx->cmm_instance, profile); +} + #define SLOWCMYK +const unsigned char * +fz_lookup_icc(fz_context *ctx, const char *name, size_t *size) +{ +#ifndef NO_ICC + if (fz_get_cmm_engine(ctx) == NULL) + return *size = 0, NULL; + if (!strcmp(name, "gray-icc")) { + extern const int fz_resources_icc_gray_icc_size; + extern const unsigned char fz_resources_icc_gray_icc[]; + *size = fz_resources_icc_gray_icc_size; + return fz_resources_icc_gray_icc; + } + if (!strcmp(name, "rgb-icc") || !strcmp(name, "bgr-icc")) { + extern const int fz_resources_icc_rgb_icc_size; + extern const unsigned char fz_resources_icc_rgb_icc[]; + *size = fz_resources_icc_rgb_icc_size; + return fz_resources_icc_rgb_icc; + } + if (!strcmp(name, "cmyk-icc")) { + extern const int fz_resources_icc_cmyk_icc_size; + extern const unsigned char fz_resources_icc_cmyk_icc[]; + *size = fz_resources_icc_cmyk_icc_size; + return fz_resources_icc_cmyk_icc; + } + if (!strcmp(name, "lab-icc")) { + extern const int fz_resources_icc_lab_icc_size; + extern const unsigned char fz_resources_icc_lab_icc[]; + *size = fz_resources_icc_lab_icc_size; + return fz_resources_icc_lab_icc; + } +#endif + return *size = 0, NULL; +} + +/* Same order as needed by lcms */ +static const char *fz_intent_names[] = +{ + "Perceptual", + "RelativeColorimetric", + "Saturation", + "AbsoluteColorimetric", +}; + +int +fz_lookup_rendering_intent(const char *name) +{ + int i; + for (i = 0; i < nelem(fz_intent_names); i++) + if (!strcmp(name, fz_intent_names[i])) + return i; + return FZ_RI_RELATIVE_COLORIMETRIC; +} + +char * +fz_rendering_intent_name(int ri) +{ + if (ri >= 0 && ri < nelem(fz_intent_names)) + return (char*)fz_intent_names[ri]; + return "RelativeColorimetric"; +} + void fz_drop_colorspace_imp(fz_context *ctx, fz_storable *cs_) { @@ -18,17 +144,33 @@ fz_drop_colorspace_imp(fz_context *ctx, fz_storable *cs_) fz_free(ctx, cs); } +static void +clamp_default(const fz_colorspace *cs, const float *src, float *dst) +{ + int i; + + for (i = 0; i < cs->n; i++) + dst[i] = fz_clamp(src[i], 0, 1); +} + fz_colorspace * -fz_new_colorspace(fz_context *ctx, char *name, int n, int is_subtractive, fz_colorspace_convert_fn *to_rgb, fz_colorspace_convert_fn *from_rgb, fz_colorspace_destruct_fn *destruct, void *data, size_t size) +fz_new_colorspace(fz_context *ctx, char *name, int is_static, int n, int is_subtractive, fz_colorspace_convert_fn *to_ccs, fz_colorspace_convert_fn *from_ccs, fz_colorspace_base_fn *base, fz_colorspace_clamp_fn *clamp, fz_colorspace_destruct_fn *destruct, void *data, size_t size) { fz_colorspace *cs = fz_malloc_struct(ctx, fz_colorspace); - FZ_INIT_STORABLE(cs, 1, fz_drop_colorspace_imp); + FZ_INIT_STORABLE(cs, is_static ? -1 : 1, fz_drop_colorspace_imp); cs->size = sizeof(fz_colorspace) + size; fz_strlcpy(cs->name, name, sizeof cs->name); cs->n = n; cs->is_subtractive = is_subtractive; - cs->to_rgb = to_rgb; - cs->from_rgb = from_rgb; + cs->to_ccs = to_ccs; + cs->from_ccs = from_ccs; + cs->get_base = base; + + if (clamp != NULL) + cs->clamp = clamp; + else + cs->clamp = clamp_default; + cs->free_data = destruct; cs->data = data; return cs; @@ -46,8 +188,296 @@ fz_drop_colorspace(fz_context *ctx, fz_colorspace *cs) fz_drop_storable(ctx, &cs->storable); } -/* Device colorspace definitions */ +/* icc links */ + +typedef struct fz_link_key_s fz_link_key; + +struct fz_link_key_s { + int refs; + unsigned char src_md5[16]; + unsigned char dst_md5[16]; + fz_color_params rend; + int alpha; + int depth; + int proof; +}; + +static void * +fz_keep_link_key(fz_context *ctx, void *key_) +{ + fz_link_key *key = (fz_link_key *)key_; + return fz_keep_imp(ctx, key, &key->refs); +} + +static void +fz_drop_link_key(fz_context *ctx, void *key_) +{ + fz_link_key *key = (fz_link_key *)key_; + if (fz_drop_imp(ctx, key, &key->refs)) + fz_free(ctx, key); +} + +static int +fz_cmp_link_key(fz_context *ctx, void *k0_, void *k1_) +{ + fz_link_key *k0 = (fz_link_key *)k0_; + fz_link_key *k1 = (fz_link_key *)k1_; + return k0->proof == k1->proof && + k0->alpha == k1->alpha && + k0->depth == k1->depth && + k0->rend.bp == k1->rend.bp && + k0->rend.ri == k1->rend.ri && + memcmp(k0->dst_md5, k1->dst_md5, 16) == 0 && + memcmp(k0->src_md5, k1->src_md5, 16); +} + +static void +fz_format_link_key(fz_context *ctx, char *s, int n, void *key_) +{ + static const char *hex = "0123456789abcdef"; + fz_link_key *key = (fz_link_key *)key_; + char sm[33], dm[33]; + int i; + for (i = 0; i < 16; ++i) + { + sm[i*2+0] = hex[key->src_md5[i]>>4]; + sm[i*2+1] = hex[key->src_md5[i]&15]; + dm[i*2+0] = hex[key->dst_md5[i]>>4]; + dm[i*2+1] = hex[key->dst_md5[i]&15]; + } + sm[32] = 0; + dm[32] = 0; + fz_snprintf(s, n, "(link src_md5=%s dst_md5=%s)", sm, dm); +} + +static int +fz_make_hash_link_key(fz_context *ctx, fz_store_hash *hash, void *key_) +{ + fz_link_key *key = (fz_link_key *)key_; + memcpy(hash->u.link.dst_md5, key->dst_md5, 16); + memcpy(hash->u.link.src_md5, key->src_md5, 16); + hash->u.link.ri = key->rend.ri; + hash->u.link.bp = key->rend.bp; + hash->u.link.alpha = key->alpha; + hash->u.link.depth = key->depth; + hash->u.link.proof = key->proof; + return 1; +} + +static fz_store_type fz_link_store_type = +{ + fz_make_hash_link_key, + fz_keep_link_key, + fz_drop_link_key, + fz_cmp_link_key, + fz_format_link_key, + NULL +}; + +static void +fz_drop_link_imp(fz_context *ctx, fz_storable *storable) +{ + fz_icclink *link = (fz_icclink *)storable; + fz_cmm_fin_link(ctx, link); + fz_free(ctx, link); +} + +static void +fz_drop_icclink(fz_context *ctx, fz_icclink *link) +{ + fz_drop_storable(ctx, &link->storable); +} + +static fz_iccprofile * +get_base_icc_profile(fz_context *ctx, fz_colorspace *cs) +{ + fz_colorspace *base; + fz_cal_colorspace *cal; + fz_iccprofile *cal_icc; + + if (!cs || !cs->get_base) + return NULL; + + base = cs->get_base(cs); + if (base == NULL) + return NULL; + + if (fz_colorspace_is_icc(ctx, base)) + return base->data; + if (!fz_colorspace_is_cal(ctx, base)) + return get_base_icc_profile(ctx, base); + + cal = base->data; + cal_icc = cal->profile; + if (cal_icc && cal_icc->cmm_handle == NULL) + fz_cmm_init_profile(ctx, cal_icc); + + return cal_icc; +} + +static fz_icclink * +fz_new_icc_link(fz_context *ctx, fz_iccprofile *src, fz_iccprofile *prf, fz_iccprofile *dst, const fz_color_params *rend, int num_bytes, int alpha) +{ + fz_icclink *link = fz_malloc_struct(ctx, fz_icclink); + FZ_INIT_STORABLE(link, 1, fz_drop_link_imp); + + link->num_in = src->num_devcomp; + link->num_out = dst->num_devcomp; + + if (memcmp(src->md5, dst->md5, 16) == 0 && rend->ri == FZ_RI_RELATIVE_COLORIMETRIC && prf == NULL) + { + link->is_identity = 1; + return link; + } + + fz_try(ctx) + fz_cmm_init_link(ctx, link, rend, 0, num_bytes, alpha, src, prf, dst); + fz_catch(ctx) + { + fz_free(ctx, link); + fz_rethrow(ctx); + } + + return link; +} + +static void +fz_md5_icc(fz_context *ctx, fz_iccprofile *profile) +{ + if (profile) + fz_md5_buffer(ctx, profile->buffer, profile->md5); +} +/* Create icc profile from calrgb, calgray values */ +static fz_iccprofile * +fz_icc_from_cal(fz_context *ctx, fz_colorspace *cs) +{ + fz_cal_colorspace *cal_data = cs->data; + fz_iccprofile *profile; + + if (cal_data->profile != NULL) + return cal_data->profile; + profile = fz_malloc_struct(ctx, fz_iccprofile); + + fz_try(ctx) + { + profile->buffer = fz_new_icc_data_from_cal_colorspace(ctx, cal_data); + fz_md5_icc(ctx, profile); + cal_data->profile = profile; + } + fz_catch(ctx) + { + fz_free(ctx, profile); + fz_rethrow(ctx); + } + + return profile; +} + +static fz_icclink * +fz_get_icc_link(fz_context *ctx, fz_colorspace *src, fz_colorspace *prf, fz_colorspace *dst, const fz_color_params *rend, int num_bytes, int alpha, int *src_n) +{ + fz_icclink *link = NULL; + fz_iccprofile *src_icc = NULL; + fz_iccprofile *dst_icc = dst->data; + fz_iccprofile *prf_icc = NULL; + fz_link_key *key; + fz_icclink *new_link; + + if (prf != NULL) + prf_icc = prf->data; + + if (fz_colorspace_is_icc(ctx, src)) + src_icc = src->data; + else if (fz_colorspace_is_cal(ctx, src)) + { + fz_cal_colorspace *cal; + + cal = src->data; + src_icc = cal->profile; + /* Check if we have any work to do. */ + if (src_icc == NULL) + src_icc = fz_icc_from_cal(ctx, src); + if (src_icc->cmm_handle == NULL) + { + fz_cmm_init_profile(ctx, src_icc); + + /* The CMM failed to make a profile. Use the default. */ + if (src_icc->cmm_handle == NULL) + { + switch (src->n) + { + case 1: + src_icc = fz_device_gray(ctx)->data; + break; + case 3: + src_icc = fz_device_rgb(ctx)->data; + break; + case 4: + src_icc = fz_device_cmyk(ctx)->data; + break; + default: + fz_throw(ctx, FZ_ERROR_GENERIC, "Poorly formed Cal color space"); + } + /* To avoid repeated failures building the pdf-cal color space, + * assign the default profile. */ + fz_cmm_fin_profile(ctx, src_icc); + cal->profile = src_icc; + } + } + } + else + src_icc = get_base_icc_profile(ctx, src); + + if (src_icc == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "Profile missing during link creation"); + + *src_n = src_icc->num_devcomp; + + fz_var(link); + fz_var(key); + + fz_try(ctx) + { + /* Check the storable to see if we have a copy. */ + key = fz_malloc_struct(ctx, fz_link_key); + key->refs = 1; + memcpy(&key->dst_md5, dst_icc->md5, 16); + memcpy(&key->src_md5, src_icc->md5, 16); + key->rend.ri = rend->ri; + key->rend.bp = rend->bp; + key->alpha = alpha; + key->depth = num_bytes; + key->proof = (prf_icc != NULL); + link = fz_find_item(ctx, fz_drop_link_imp, key, &fz_link_store_type); + + /* Not found. Make new one add to store. */ + if (link == NULL) + { + link = fz_new_icc_link(ctx, src_icc, prf_icc, dst_icc, rend, num_bytes, alpha); + new_link = fz_store_item(ctx, key, link, sizeof(fz_icclink), &fz_link_store_type); + if (new_link != NULL) + { + /* Found one while adding! Perhaps from another thread? */ + fz_drop_icclink(ctx, link); + link = new_link; + } + } + } + fz_always(ctx) + { + fz_drop_link_key(ctx, key); + } + fz_catch(ctx) + { + /* Ignore any error that came just from the enstoring. */ + if (link == NULL) + fz_rethrow(ctx); + } + return link; +} + +/* Device colorspace definitions */ static void gray_to_rgb(fz_context *ctx, fz_colorspace *cs, const float *gray, float *rgb) { rgb[0] = gray[0]; @@ -214,10 +644,19 @@ rgb_to_lab(fz_context *ctx, fz_colorspace *cs, const float *rgb, float *lab) lab[2] = rgb[2]; } -int -fz_colorspace_is_lab(fz_context *ctx, fz_colorspace *cs) +/* This could be different for a, b */ +static void +clamp_lab(const fz_colorspace *cs, const float *src, float *dst) { - return (cs && cs->to_rgb == lab_to_rgb); + int i; + + for (i = 0; i < 3; i++) + dst[i] = fz_clamp(src[i], i ? -128 : 0, i ? 127 : 100); +} + +static int fz_colorspace_is_lab(fz_context *ctx, const fz_colorspace *cs) +{ + return cs && cs->to_ccs == lab_to_rgb; } int @@ -226,33 +665,101 @@ fz_colorspace_is_subtractive(fz_context *ctx, fz_colorspace *cs) return (cs && cs->is_subtractive); } -static fz_colorspace k_default_gray = { {-1, fz_drop_colorspace_imp}, 0, "DeviceGray", 1, 0, gray_to_rgb, rgb_to_gray }; -static fz_colorspace k_default_rgb = { {-1, fz_drop_colorspace_imp}, 0, "DeviceRGB", 3, 0, rgb_to_rgb, rgb_to_rgb }; -static fz_colorspace k_default_bgr = { {-1, fz_drop_colorspace_imp}, 0, "DeviceBGR", 3, 0, bgr_to_rgb, rgb_to_bgr }; -static fz_colorspace k_default_cmyk = { {-1, fz_drop_colorspace_imp}, 0, "DeviceCMYK", 4, 1, cmyk_to_rgb, rgb_to_cmyk }; -static fz_colorspace k_default_lab = { {-1, fz_drop_colorspace_imp}, 0, "Lab", 3, 0, lab_to_rgb, rgb_to_lab }; +static fz_colorspace k_default_gray = { {-1, fz_drop_colorspace_imp}, 0, "DeviceGray", 1, 0, gray_to_rgb, rgb_to_gray, clamp_default, NULL, NULL, NULL }; +static fz_colorspace k_default_rgb = { {-1, fz_drop_colorspace_imp}, 0, "DeviceRGB", 3, 0, rgb_to_rgb, rgb_to_rgb, clamp_default, NULL, NULL, NULL }; +static fz_colorspace k_default_bgr = { {-1, fz_drop_colorspace_imp}, 0, "DeviceBGR", 3, 0, bgr_to_rgb, rgb_to_bgr, clamp_default, NULL, NULL, NULL }; +static fz_colorspace k_default_cmyk = { {-1, fz_drop_colorspace_imp}, 0, "DeviceCMYK", 4, 1, cmyk_to_rgb, rgb_to_cmyk, clamp_default, NULL, NULL, NULL }; +static fz_colorspace k_default_lab = { {-1, fz_drop_colorspace_imp}, 0, "Lab", 3, 0, lab_to_rgb, rgb_to_lab, clamp_lab, NULL, NULL, NULL}; +static fz_color_params k_default_color_params = { FZ_RI_RELATIVE_COLORIMETRIC, 1, 0, 0 }; -static fz_colorspace *fz_default_gray = &k_default_gray; -static fz_colorspace *fz_default_rgb = &k_default_rgb; -static fz_colorspace *fz_default_bgr = &k_default_bgr; -static fz_colorspace *fz_default_cmyk = &k_default_cmyk; -static fz_colorspace *fz_default_lab = &k_default_lab; +static fz_colorspace *default_gray = &k_default_gray; +static fz_colorspace *default_rgb = &k_default_rgb; +static fz_colorspace *default_bgr = &k_default_bgr; +static fz_colorspace *default_cmyk = &k_default_cmyk; +static fz_colorspace *default_lab = &k_default_lab; +static fz_color_params *default_color_params = &k_default_color_params; -struct fz_colorspace_context_s +const fz_cmm_engine *fz_get_cmm_engine(fz_context *ctx) { - int ctx_refs; - fz_colorspace *gray, *rgb, *bgr, *cmyk, *lab; -}; + return ctx->colorspace ? ctx->colorspace->cmm : NULL; +} + +static void +set_no_icc(fz_colorspace_context *cct) +{ + cct->gray = default_gray; + cct->rgb = default_rgb; + cct->bgr = default_bgr; + cct->cmyk = default_cmyk; + cct->lab = default_lab; +} + +void fz_set_cmm_engine(fz_context *ctx, const fz_cmm_engine *engine) +{ + fz_colorspace_context *cct; + + if (!ctx) + return; + cct = ctx->colorspace; + if (!cct) + return; + +#ifdef NO_ICC + if (engine) + fz_throw(ctx, FZ_ERROR_GENERIC, "ICC workflow not supported in NO_ICC build"); +#else + if (cct->cmm == engine) + return; + + fz_drop_cmm_context(ctx); + fz_drop_colorspace(ctx, cct->gray); + fz_drop_colorspace(ctx, cct->rgb); + fz_drop_colorspace(ctx, cct->bgr); + fz_drop_colorspace(ctx, cct->cmyk); + fz_drop_colorspace(ctx, cct->lab); + cct->gray = NULL; + cct->rgb = NULL; + cct->bgr = NULL; + cct->cmyk = NULL; + cct->lab = NULL; + cct->cmm = engine; + fz_new_cmm_context(ctx); + if (engine) + { + cct->gray = fz_new_icc_colorspace(ctx, 1, 1, NULL, "gray-icc"); + cct->rgb = fz_new_icc_colorspace(ctx, 1, 3, NULL, "rgb-icc"); + cct->bgr = fz_new_icc_colorspace(ctx, 1, 3, NULL, "bgr-icc"); + cct->cmyk = fz_new_icc_colorspace(ctx, 1, 4, NULL, "cmyk-icc"); + cct->lab = fz_new_icc_colorspace(ctx, 1, 3, NULL, "lab-icc"); + } + else + set_no_icc(cct); +#endif +} void fz_new_colorspace_context(fz_context *ctx) { ctx->colorspace = fz_malloc_struct(ctx, fz_colorspace_context); ctx->colorspace->ctx_refs = 1; - ctx->colorspace->gray = fz_default_gray; - ctx->colorspace->rgb = fz_default_rgb; - ctx->colorspace->bgr = fz_default_bgr; - ctx->colorspace->cmyk = fz_default_cmyk; - ctx->colorspace->lab = fz_default_lab; + set_no_icc(ctx->colorspace); +#ifdef NO_ICC + fz_set_cmm_engine(ctx, NULL); +#else + fz_set_cmm_engine(ctx, &fz_cmm_engine_lcms); +#endif +} + +void +fz_new_cmm_context(fz_context *ctx) +{ + ctx->cmm_instance = fz_cmm_new_instance(ctx); +} + +void +fz_drop_cmm_context(fz_context *ctx) +{ + fz_cmm_drop_instance(ctx); + ctx->cmm_instance = NULL; } fz_colorspace_context * @@ -269,6 +776,12 @@ void fz_drop_colorspace_context(fz_context *ctx) return; if (fz_drop_imp(ctx, ctx->colorspace, &ctx->colorspace->ctx_refs)) { + fz_drop_colorspace(ctx, ctx->colorspace->gray); + fz_drop_colorspace(ctx, ctx->colorspace->rgb); + /* FIXME: bgr */ + fz_drop_colorspace(ctx, ctx->colorspace->cmyk); + fz_drop_colorspace(ctx, ctx->colorspace->lab); + fz_drop_cmm_context(ctx); fz_free(ctx, ctx->colorspace); ctx->colorspace = NULL; } @@ -304,54 +817,15 @@ fz_device_lab(fz_context *ctx) return ctx->colorspace->lab; } -void -fz_set_device_gray(fz_context *ctx, fz_colorspace *cs) -{ - fz_lock(ctx, FZ_LOCK_ALLOC); - fz_drop_colorspace(ctx, ctx->colorspace->gray); - ctx->colorspace->gray = fz_keep_colorspace(ctx, cs); - fz_unlock(ctx, FZ_LOCK_ALLOC); -} - -void -fz_set_device_rgb(fz_context *ctx, fz_colorspace *cs) -{ - fz_lock(ctx, FZ_LOCK_ALLOC); - fz_drop_colorspace(ctx, ctx->colorspace->rgb); - ctx->colorspace->rgb = fz_keep_colorspace(ctx, cs); - fz_unlock(ctx, FZ_LOCK_ALLOC); -} - -void -fz_set_device_bgr(fz_context *ctx, fz_colorspace *cs) -{ - fz_lock(ctx, FZ_LOCK_ALLOC); - fz_drop_colorspace(ctx, ctx->colorspace->bgr); - ctx->colorspace->bgr = fz_keep_colorspace(ctx, cs); - fz_unlock(ctx, FZ_LOCK_ALLOC); -} - -void -fz_set_device_cmyk(fz_context *ctx, fz_colorspace *cs) -{ - fz_lock(ctx, FZ_LOCK_ALLOC); - fz_drop_colorspace(ctx, ctx->colorspace->cmyk); - ctx->colorspace->cmyk = fz_keep_colorspace(ctx, cs); - fz_unlock(ctx, FZ_LOCK_ALLOC); -} - -void -fz_set_device_lab(fz_context *ctx, fz_colorspace *cs) +const fz_color_params * +fz_default_color_params(fz_context *ctx) { - fz_lock(ctx, FZ_LOCK_ALLOC); - fz_drop_colorspace(ctx, ctx->colorspace->lab); - ctx->colorspace->lab = fz_keep_colorspace(ctx, cs); - fz_unlock(ctx, FZ_LOCK_ALLOC); + return default_color_params; } /* Fast pixmap color conversions */ -static void fast_gray_to_rgb(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) +static void fast_gray_to_rgb(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src, fz_colorspace *prf, fz_default_colorspaces *default_cs, const fz_color_params *color_params) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -429,7 +903,7 @@ static void fast_gray_to_rgb(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) } } -static void fast_gray_to_cmyk(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) +static void fast_gray_to_cmyk(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src, fz_colorspace *prf, fz_default_colorspaces *default_cs, const fz_color_params *color_params) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -510,7 +984,7 @@ static void fast_gray_to_cmyk(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) } } -static void fast_rgb_to_gray(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) +static void fast_rgb_to_gray(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src, fz_colorspace *prf, fz_default_colorspaces *default_cs, const fz_color_params *color_params) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -582,7 +1056,7 @@ static void fast_rgb_to_gray(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) } } -static void fast_bgr_to_gray(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) +static void fast_bgr_to_gray(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src, fz_colorspace *prf, fz_default_colorspaces *default_cs, const fz_color_params *color_params) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -654,7 +1128,7 @@ static void fast_bgr_to_gray(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) } } -static void fast_rgb_to_cmyk(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) +static void fast_rgb_to_cmyk(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src, fz_colorspace *prf, fz_default_colorspaces *default_cs, const fz_color_params *color_params) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -747,7 +1221,7 @@ static void fast_rgb_to_cmyk(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) } } -static void fast_bgr_to_cmyk(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) +static void fast_bgr_to_cmyk(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src, fz_colorspace *prf, fz_default_colorspaces *default_cs, const fz_color_params *color_params) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -840,7 +1314,7 @@ static void fast_bgr_to_cmyk(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) } } -static void fast_cmyk_to_gray(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) +static void fast_cmyk_to_gray(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src, fz_colorspace *prf, fz_default_colorspaces *default_cs, const fz_color_params *color_params) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -1247,7 +1721,7 @@ static inline void cached_cmyk_conv(unsigned char *restrict const pr, unsigned c #endif } -static void fast_cmyk_to_rgb(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) +static void fast_cmyk_to_rgb(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src, fz_colorspace *prf, fz_default_colorspaces *default_cs, const fz_color_params *color_params) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -1346,7 +1820,7 @@ static void fast_cmyk_to_rgb(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) } } -static void fast_cmyk_to_bgr(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) +static void fast_cmyk_to_bgr(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src, fz_colorspace *prf, fz_default_colorspaces *default_cs, const fz_color_params *color_params) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -1438,7 +1912,7 @@ static void fast_cmyk_to_bgr(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) } } -static void fast_rgb_to_bgr(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) +static void fast_rgb_to_bgr(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src, fz_colorspace *prf, fz_default_colorspaces *default_cs, const fz_color_params *color_params) { unsigned char *s = src->samples; unsigned char *d = dst->samples; @@ -1512,7 +1986,148 @@ static void fast_rgb_to_bgr(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) } static void -fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) +icc_conv_pixmap(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src, fz_colorspace *prf, fz_default_colorspaces *default_cs, const fz_color_params *color_params) +{ + fz_colorspace *srcs = src->colorspace; + fz_colorspace *dsts = dst->colorspace; + fz_icclink *link; + int i; + unsigned char *inputpos, *outputpos; + int src_n; + + /* Check if we have to do a color space default substitution */ + if (default_cs) + { + switch (fz_colorspace_n(ctx, src->colorspace)) + { + case 1: + if (src->colorspace == fz_device_gray(ctx)) + srcs = fz_default_gray(ctx, default_cs); + break; + case 3: + if (src->colorspace == fz_device_rgb(ctx)) + srcs = fz_default_rgb(ctx, default_cs); + break; + case 4: + if (src->colorspace == fz_device_cmyk(ctx)) + srcs = fz_default_cmyk(ctx, default_cs); + break; + } + } + + inputpos = src->samples; + outputpos = dst->samples; + + link = fz_get_icc_link(ctx, srcs, prf, dsts, color_params, 1, dst->alpha, &src_n); + + if (link->is_identity) + { + for (i = 0; i < src->h; i++) + { + memcpy(outputpos, inputpos, src->stride); + inputpos = inputpos + src->stride; + outputpos = outputpos + dst->stride; + } + } + else + fz_cmm_transform_pixmap(ctx, link, dst, src); + + fz_drop_icclink(ctx, link); +} + +/* Drill down through the base spaces until we get the either a pdf-cal or + * an ICC base space. This is where we want our pixmap to be decoded prior + * to application of the link transform */ +static fz_colorspace* +get_base_icc_space(fz_context *ctx, fz_colorspace *srcs) +{ + while (1) + { + srcs = srcs->get_base(srcs); + if (srcs == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "Final color space should be icc or pdf-cal or lab"); + + if (fz_colorspace_is_icc(ctx, srcs) || fz_colorspace_is_cal(ctx, srcs) || fz_colorspace_is_lab(ctx, srcs)) + return srcs; + } +} + +/* Cope with cases where we have to convert through multiple base spaces before + * getting to the final cm color space */ +static void +convert_to_icc_base(fz_context *ctx, fz_colorspace *srcs, float *src_f, float *des_f) +{ + float temp_f[FZ_MAX_COLORS]; + fz_colorspace *base_cs = srcs->get_base(srcs); + + if (fz_colorspace_is_icc(ctx, base_cs) || fz_colorspace_is_cal(ctx, base_cs) || fz_colorspace_is_lab(ctx, base_cs)) + srcs->to_ccs(ctx, srcs, src_f, des_f); + else + { + srcs->to_ccs(ctx, srcs, src_f, temp_f); + convert_to_icc_base(ctx, base_cs, temp_f, des_f); + } +} + +/* For DeviceN and Separation CS, where we require an alternate tint tranform + * prior to the application of an icc profile. Also, indexed images have to + * be handled. Realize those can map from index->devn->pdf-cal->icc for + * example. */ +static void +icc_base_conv_pixmap(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src, fz_colorspace *prf, fz_default_colorspaces *default_cs, const fz_color_params *color_params) +{ + fz_colorspace *srcs = src->colorspace; + fz_colorspace *base_cs = get_base_icc_space(ctx, srcs); + int i; + unsigned char *inputpos, *outputpos; + fz_pixmap *base; + fz_irect bbox; + int h, len; + float src_f[FZ_MAX_COLORS], des_f[FZ_MAX_COLORS]; + int sn = src->n; + int stride_src = src->stride - src->w * sn; + int stride_base; + int bn; + + base = fz_new_pixmap_with_bbox(ctx, base_cs, fz_pixmap_bbox(ctx, src, &bbox), src->alpha); + bn = base->n; + stride_base = base->stride - base->w * bn; + + inputpos = src->samples; + outputpos = base->samples; + + h = src->h; + while (h--) + { + len = src->w; + while (len--) + { + for (i = 0; i < sn; i++) + src_f[i] = (float) inputpos[i] / 255.0; + + convert_to_icc_base(ctx, srcs, src_f, des_f); + base_cs->clamp(base_cs, des_f, des_f); + + for (i = 0; i < bn; i++) + outputpos[i] = des_f[i] * 255.0; + + outputpos += bn; + inputpos += sn; + } + outputpos += stride_base; + inputpos += stride_src; + } + + fz_try(ctx) + icc_conv_pixmap(ctx, dst, base, prf, default_cs, color_params); + fz_always(ctx) + fz_drop_pixmap(ctx, base); + fz_catch(ctx) + fz_rethrow(ctx); +} + +static void +std_conv_pixmap(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src, fz_colorspace *prf, fz_default_colorspaces *default_cs, const fz_color_params *color_params) { float srcv[FZ_MAX_COLORS]; float dstv[FZ_MAX_COLORS]; @@ -1534,6 +2149,9 @@ fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) if ((int)w < 0 || h < 0) return; + if (color_params == NULL) + color_params = fz_default_color_params(ctx); + srcn = ss->n; dstn = ds->n; @@ -1548,11 +2166,11 @@ fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) } /* Special case for Lab colorspace (scaling of components to float) */ - if (!strcmp(ss->name, "Lab") && srcn == 3) + if ((fz_colorspace_is_lab(ctx, ss) || fz_colorspace_is_lab_icc(ctx, ss)) && srcn == 3) { fz_color_converter cc; - fz_lookup_color_converter(ctx, &cc, ds, ss); + fz_find_color_converter(ctx, &cc, NULL, ds, ss, color_params); while (h--) { size_t ww = w; @@ -1573,6 +2191,7 @@ fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) d += d_line_inc; s += s_line_inc; } + fz_drop_color_converter(ctx, &cc); } /* Brute-force for small images */ @@ -1580,7 +2199,7 @@ fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) { fz_color_converter cc; - fz_lookup_color_converter(ctx, &cc, ds, ss); + fz_find_color_converter(ctx, &cc, NULL, ds, ss, color_params); while (h--) { size_t ww = w; @@ -1600,6 +2219,7 @@ fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) d += d_line_inc; s += s_line_inc; } + fz_drop_color_converter(ctx, &cc); } /* 1-d lookup table for separation and similar colorspaces */ @@ -1608,7 +2228,7 @@ fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) unsigned char lookup[FZ_MAX_COLORS * 256]; fz_color_converter cc; - fz_lookup_color_converter(ctx, &cc, ds, ss); + fz_find_color_converter(ctx, &cc, NULL, ds, ss, color_params); for (i = 0; i < 256; i++) { srcv[0] = i / 255.0f; @@ -1616,6 +2236,7 @@ fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) for (k = 0; k < dstn; k++) lookup[i * dstn + k] = dstv[k] * 255; } + fz_drop_color_converter(ctx, &cc); while (h--) { @@ -1644,63 +2265,70 @@ fz_std_conv_pixmap(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) unsigned char *dold; fz_color_converter cc; - fz_lookup_color_converter(ctx, &cc, ds, ss); lookup = fz_new_hash_table(ctx, 509, srcn, -1, NULL); + fz_find_color_converter(ctx, &cc, NULL, ds, ss, color_params); - while (h--) + fz_try(ctx) { - size_t ww = w; - while (ww--) + while (h--) { - if (*s == *sold && memcmp(sold,s,srcn) == 0) - { - sold = s; - memcpy(d, dold, dstn); - d += dstn; - s += srcn; - if (da) - *d++ = (sa ? *s : 255); - s += sa; - } - else + size_t ww = w; + while (ww--) { - sold = s; - dold = d; - color = fz_hash_find(ctx, lookup, s); - if (color) + if (*s == *sold && memcmp(sold,s,srcn) == 0) { - memcpy(d, color, dstn); - s += srcn; + sold = s; + memcpy(d, dold, dstn); d += dstn; - if (dst->alpha) + s += srcn; + if (da) *d++ = (sa ? *s : 255); s += sa; } else { - for (k = 0; k < srcn; k++) - srcv[k] = *s++ / 255.0f; - cc.convert(ctx, &cc, dstv, srcv); - for (k = 0; k < dstn; k++) - *d++ = dstv[k] * 255; - - fz_hash_insert(ctx, lookup, s - srcn, d - dstn); - - if (dst->alpha) - *d++ = (sa ? *s : 255); - s += sa; + sold = s; + dold = d; + color = fz_hash_find(ctx, lookup, s); + if (color) + { + memcpy(d, color, dstn); + s += srcn; + d += dstn; + if (dst->alpha) + *d++ = (sa ? *s : 255); + s += sa; + } + else + { + for (k = 0; k < srcn; k++) + srcv[k] = *s++ / 255.0f; + cc.convert(ctx, &cc, dstv, srcv); + for (k = 0; k < dstn; k++) + *d++ = dstv[k] * 255; + + fz_hash_insert(ctx, lookup, s - srcn, d - dstn); + + if (dst->alpha) + *d++ = (sa ? *s : 255); + s += sa; + } } } + d += d_line_inc; + s += s_line_inc; } - d += d_line_inc; - s += s_line_inc; } + fz_always(ctx) + fz_drop_color_converter(ctx, &cc); + fz_catch(ctx) + fz_rethrow(ctx); fz_drop_hash_table(ctx, lookup); } } -static void fast_any_to_alpha(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) +static void fast_any_to_alpha(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src, fz_colorspace *prf, fz_default_colorspaces *default_cs, const fz_color_params *color_params) { if (!src->alpha) fz_clear_pixmap_with_value(ctx, dst, 255); @@ -1740,48 +2368,128 @@ static void fast_any_to_alpha(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src) } } +/* Used for testing all color managed source color spaces. If it is icc, cal or + * has a base space that is managed */ +static fz_colorspace * +fz_source_colorspace_cm(fz_context *ctx, fz_colorspace *cs) +{ + while (cs) + { + if (fz_colorspace_is_icc(ctx, cs)) + return cs; + if (fz_colorspace_is_cal(ctx, cs)) + return cs; + cs = fz_colorspace_base(ctx, cs); + } + return NULL; +} + fz_pixmap_converter *fz_lookup_pixmap_converter(fz_context *ctx, fz_colorspace *ds, fz_colorspace *ss) { if (ds == NULL) return fast_any_to_alpha; - if (ss == fz_default_gray) + if (ss == default_gray) { - if (ds == fz_default_rgb) return fast_gray_to_rgb; - else if (ds == fz_default_bgr) return fast_gray_to_rgb; /* bgr == rgb here */ - else if (ds == fz_default_cmyk) return fast_gray_to_cmyk; - else return fz_std_conv_pixmap; + if (ds == default_rgb) return fast_gray_to_rgb; + else if (ds == default_bgr) return fast_gray_to_rgb; /* bgr == rgb here */ + else if (ds == default_cmyk) return fast_gray_to_cmyk; + else return std_conv_pixmap; } - else if (ss == fz_default_rgb) + else if (ss == default_rgb) { - if (ds == fz_default_gray) return fast_rgb_to_gray; - else if (ds == fz_default_bgr) return fast_rgb_to_bgr; - else if (ds == fz_default_cmyk) return fast_rgb_to_cmyk; - else return fz_std_conv_pixmap; + if (ds == default_gray) return fast_rgb_to_gray; + else if (ds == default_bgr) return fast_rgb_to_bgr; + else if (ds == default_cmyk) return fast_rgb_to_cmyk; + else return std_conv_pixmap; } - else if (ss == fz_default_bgr) + else if (ss == default_bgr) { - if (ds == fz_default_gray) return fast_bgr_to_gray; - else if (ds == fz_default_rgb) return fast_rgb_to_bgr; /* bgr = rgb here */ - else if (ds == fz_default_cmyk) return fast_bgr_to_cmyk; - else return fz_std_conv_pixmap; + if (ds == default_gray) return fast_bgr_to_gray; + else if (ds == default_rgb) return fast_rgb_to_bgr; /* bgr = rgb here */ + else if (ds == default_cmyk) return fast_bgr_to_cmyk; + else return std_conv_pixmap; } - else if (ss == fz_default_cmyk) + else if (ss == default_cmyk) { - if (ds == fz_default_gray) return fast_cmyk_to_gray; - else if (ds == fz_default_bgr) return fast_cmyk_to_bgr; - else if (ds == fz_default_rgb) return fast_cmyk_to_rgb; - else return fz_std_conv_pixmap; + if (ds == default_gray) return fast_cmyk_to_gray; + else if (ds == default_bgr) return fast_cmyk_to_bgr; + else if (ds == default_rgb) return fast_cmyk_to_rgb; + else return std_conv_pixmap; } - else return fz_std_conv_pixmap; + else + { + fz_colorspace *ss_base = fz_source_colorspace_cm(ctx, ss); + if (ss_base != NULL && fz_colorspace_is_icc(ctx, ds)) + { + if (ss_base == ss) + return icc_conv_pixmap; + else + return icc_base_conv_pixmap; + } + else return std_conv_pixmap; + } } -/* Convert a single color */ +/* Single color conversion with ICC profiles. ToDo: Check if it makes sense + * to use lcms float link here or to do the conversion to short and back. + */ +static void +icc_conv_color(fz_context *ctx, fz_color_converter *cc, float *dstv, const float *srcv) +{ + fz_colorspace *dsts = cc->ds; + int src_n = cc->n; + int dst_n = dsts->n; + + fz_icclink *link = (fz_icclink *)cc->link; + int i; + unsigned short dstv_s[FZ_MAX_COLORS]; + unsigned short srcv_s[FZ_MAX_COLORS]; + + if (link->is_identity) + { + for (i = 0; i < src_n; i++) + dstv[i] = srcv[i]; + } + else + { + for (i = 0; i < src_n; i++) + srcv_s[i] = srcv[i] * 65535; + fz_cmm_transform_color(ctx, link, dstv_s, srcv_s); + for (i = 0; i < dst_n; i++) + dstv[i] = fz_clamp((float) dstv_s[i] / 65535.0, 0, 1); + } +} +/* Single ICC color conversion but for DeviceN, Sep and Indexed spaces. + * Does premapping to get to ICC */ +static void +icc_base_conv_color(fz_context *ctx, fz_color_converter *cc, float *dstv, const float *srcv) +{ + fz_colorspace *srcs = cc->ss; + + float local_src_map[FZ_MAX_COLORS]; + float local_src_map2[FZ_MAX_COLORS]; + float *src_map = local_src_map; + + do + { + srcs->to_ccs(ctx, srcs, srcv, src_map); + srcs = srcs->get_base(srcs); + srcs->clamp(srcs, src_map, src_map); + srcv = src_map; + src_map = (src_map == local_src_map ? local_src_map2 : local_src_map); + } + while (!fz_colorspace_is_icc(ctx, srcs) && !fz_colorspace_is_cal(ctx, srcs)); + + icc_conv_color(ctx, cc, dstv, srcv); +} + +/* Convert a single color */ static void std_conv_color(fz_context *ctx, fz_color_converter *cc, float *dstv, const float *srcv) { @@ -1797,9 +2505,9 @@ std_conv_color(fz_context *ctx, fz_color_converter *cc, float *dstv, const float if (srcs != dsts) { - assert(srcs->to_rgb && dsts->from_rgb); - srcs->to_rgb(ctx, srcs, srcv, rgb); - dsts->from_rgb(ctx, dsts, rgb, dstv); + assert(srcs->to_ccs && dsts->from_ccs); + srcs->to_ccs(ctx, srcs, srcv, rgb); + dsts->from_ccs(ctx, dsts, rgb, dstv); for (i = 0; i < dsts->n; i++) dstv[i] = fz_clamp(dstv[i], 0, 1); } @@ -1910,66 +2618,90 @@ cmyk2bgr(fz_context *ctx, fz_color_converter *cc, float *dv, const float *sv) #endif } -void fz_lookup_color_converter(fz_context *ctx, fz_color_converter *cc, fz_colorspace *ds, fz_colorspace *ss) +void fz_find_color_converter(fz_context *ctx, fz_color_converter *cc, fz_colorspace *is, fz_colorspace *ds, fz_colorspace *ss, const fz_color_params *params) { cc->ds = ds; cc->ss = ss; - if (ss == fz_default_gray) + cc->is = is; + cc->link = NULL; + if (ss == default_gray) { - if ((ds == fz_default_rgb) || (ds == fz_default_bgr)) + if ((ds == default_rgb) || (ds == default_bgr)) cc->convert = g2rgb; - else if (ds == fz_default_cmyk) + else if (ds == default_cmyk) cc->convert = g2cmyk; else cc->convert = std_conv_color; } - else if (ss == fz_default_rgb) + else if (ss == default_rgb) { - if (ds == fz_default_gray) + if (ds == default_gray) cc->convert = rgb2g; - else if (ds == fz_default_bgr) + else if (ds == default_bgr) cc->convert = rgb2bgr; - else if (ds == fz_default_cmyk) + else if (ds == default_cmyk) cc->convert = rgb2cmyk; else cc->convert = std_conv_color; } - else if (ss == fz_default_bgr) + else if (ss == default_bgr) { - if (ds == fz_default_gray) + if (ds == default_gray) cc->convert = bgr2g; - else if (ds == fz_default_rgb) + else if (ds == default_rgb) cc->convert = rgb2bgr; - else if (ds == fz_default_cmyk) + else if (ds == default_cmyk) cc->convert = bgr2cmyk; else cc->convert = std_conv_color; } - else if (ss == fz_default_cmyk) + else if (ss == default_cmyk) { - if (ds == fz_default_gray) + if (ds == default_gray) cc->convert = cmyk2g; - else if (ds == fz_default_rgb) + else if (ds == default_rgb) cc->convert = cmyk2rgb; - else if (ds == fz_default_bgr) + else if (ds == default_bgr) cc->convert = cmyk2bgr; else cc->convert = std_conv_color; } - else - cc->convert = std_conv_color; + { + fz_colorspace *ss_base = fz_source_colorspace_cm(ctx, ss); + if (ss_base != NULL && fz_colorspace_is_icc(ctx, ds)) + { + if (ss_base == ss) + cc->convert = icc_conv_color; + else + cc->convert = icc_base_conv_color; + cc->link = fz_get_icc_link(ctx, ss_base, is, ds, params, 2, 0, &cc->n); + } + else + cc->convert = std_conv_color; + } } void -fz_convert_color(fz_context *ctx, fz_colorspace *ds, float *dv, fz_colorspace *ss, const float *sv) +fz_drop_color_converter(fz_context *ctx, fz_color_converter *cc) +{ + fz_icclink *link = (fz_icclink *)cc->link; + if (link) + fz_drop_icclink(ctx, link); + cc->link = NULL; + +} + +void +fz_convert_color(fz_context *ctx, const fz_color_params *params, fz_colorspace *is, fz_colorspace *ds, float *dv, fz_colorspace *ss, const float *sv) { fz_color_converter cc; - fz_lookup_color_converter(ctx, &cc, ds, ss); + fz_find_color_converter(ctx, &cc, is, ds, ss, params); cc.convert(ctx, &cc, dv, sv); + fz_drop_color_converter(ctx, &cc); } /* Indexed */ @@ -1982,16 +2714,26 @@ struct indexed }; static void -indexed_to_rgb(fz_context *ctx, fz_colorspace *cs, const float *color, float *rgb) +indexed_to_alt(fz_context *ctx, fz_colorspace *cs, const float *color, float *alt) { struct indexed *idx = cs->data; - float alt[FZ_MAX_COLORS]; int i, k; + int n = idx->base->n; + i = color[0] * 255; i = fz_clampi(i, 0, idx->high); - for (k = 0; k < idx->base->n; k++) - alt[k] = idx->lookup[i * idx->base->n + k] / 255.0f; - idx->base->to_rgb(ctx, idx->base, alt, rgb); + for (k = 0; k < n; k++) + alt[k] = idx->lookup[i * n + k] / 255.0f; +} + +static void +indexed_to_rgb(fz_context *ctx, fz_colorspace *cs, const float *color, float *rgb) +{ + float alt[FZ_MAX_COLORS]; + struct indexed *idx = cs->data; + + indexed_to_alt(ctx, cs, color, alt); + idx->base->to_ccs(ctx, idx->base, alt, rgb); } static void @@ -2003,10 +2745,25 @@ free_indexed(fz_context *ctx, fz_colorspace *cs) fz_free(ctx, idx); } -int -fz_colorspace_is_indexed(fz_context *ctx, fz_colorspace *cs) +static fz_colorspace * +base_indexed(const fz_colorspace *cs) { - return (cs && cs->to_rgb == indexed_to_rgb); + struct indexed *idx = cs->data; + + return idx->base; +} + +static void +clamp_indexed(const fz_colorspace *cs, const float *in, float *out) +{ + struct indexed *idx = cs->data; + + *out = fz_clamp(*in, 0, idx->high) / 255.0; /* To do, avoid 255 divide */ +} + +int fz_colorspace_is_indexed(fz_context *ctx, const fz_colorspace *cs) +{ + return cs && cs->clamp == clamp_indexed; } fz_colorspace * @@ -2021,9 +2778,7 @@ fz_new_indexed_colorspace(fz_context *ctx, fz_colorspace *base, int high, unsign idx->high = high; fz_try(ctx) - { - cs = fz_new_colorspace(ctx, "Indexed", 1, 0, indexed_to_rgb, NULL, free_indexed, idx, sizeof(*idx) + (base->n * (idx->high + 1)) + base->size); - } + cs = fz_new_colorspace(ctx, "Indexed", 0, 1, 0, fz_colorspace_is_icc(ctx, fz_device_rgb(ctx)) ? indexed_to_alt : indexed_to_rgb, NULL, base_indexed, clamp_indexed, free_indexed, idx, sizeof(*idx) + (base->n * (idx->high + 1)) + base->size); fz_catch(ctx) { fz_free(ctx, idx); @@ -2044,7 +2799,7 @@ fz_expand_indexed_pixmap(fz_context *ctx, const fz_pixmap *src, int alpha) fz_irect bbox; int s_line_inc, d_line_inc; - assert(src->colorspace->to_rgb == indexed_to_rgb); + assert(src->colorspace->to_ccs == indexed_to_rgb || src->colorspace->to_ccs == indexed_to_alt); assert(src->n == 1 + alpha); idx = src->colorspace->data; @@ -2129,23 +2884,26 @@ static void fz_cached_color_convert(fz_context *ctx, fz_color_converter *cc_, fl } } -void fz_init_cached_color_converter(fz_context *ctx, fz_color_converter *cc, fz_colorspace *ds, fz_colorspace *ss) +void fz_init_cached_color_converter(fz_context *ctx, fz_color_converter *cc, fz_colorspace *is, fz_colorspace *ds, fz_colorspace *ss, const fz_color_params *params) { int n = ss->n; fz_cached_color_converter *cached = fz_malloc_struct(ctx, fz_cached_color_converter); fz_try(ctx) { - fz_lookup_color_converter(ctx, &cached->base, ds, ss); + fz_find_color_converter(ctx, &cached->base, is, ds, ss, params); cached->hash = fz_new_hash_table(ctx, 256, n * sizeof(float), -1, fz_free); cc->convert = fz_cached_color_convert; cc->ds = ds; cc->ss = ss; + cc->is = is; cc->opaque = cached; } fz_catch(ctx) { + fz_drop_color_converter(ctx, &cached->base); fz_drop_hash_table(ctx, cached->hash); + fz_free(ctx, cached); fz_rethrow(ctx); } } @@ -2160,12 +2918,13 @@ void fz_fin_cached_color_converter(fz_context *ctx, fz_color_converter *cc_) return; cc_->opaque = NULL; fz_drop_hash_table(ctx, cc->hash); + fz_drop_color_converter(ctx, &cc->base); fz_free(ctx, cc); } -int fz_colorspace_is(fz_context *ctx, const fz_colorspace *cs, fz_colorspace_convert_fn *to_rgb) +fz_colorspace *fz_colorspace_base(fz_context *ctx, const fz_colorspace *cs) { - return cs && cs->to_rgb == to_rgb; + return cs && cs->get_base ? cs->get_base(cs) : NULL; } int fz_colorspace_n(fz_context *ctx, const fz_colorspace *cs) @@ -2177,3 +2936,278 @@ const char *fz_colorspace_name(fz_context *ctx, const fz_colorspace *cs) { return cs ? cs->name : ""; } + +static void +free_icc(fz_context *ctx, fz_colorspace *cs) +{ + fz_iccprofile *profile = cs->data; + fz_drop_buffer(ctx, profile->buffer); + fz_cmm_fin_profile(ctx, profile); + fz_free(ctx, profile); +} + +/* This could be different for a* b* */ +static void +clamp_lab_icc(const fz_colorspace *cs, const float *src, float *dst) +{ + dst[0] = (fz_clamp(src[0], 0, 100)) / 100.0; + dst[1] = (fz_clamp(src[1], -128, 127) + 128.0) / 256; + dst[2] = (fz_clamp(src[2], -128, 127) + 128.0) / 256; +} + +/* Embedded icc profiles could have different range */ +static void +clamp_default_icc(const fz_colorspace *cs, const float *src, float *dst) +{ + int i; + fz_iccprofile *profile = cs->data; + + for (i = 0; i < profile->num_devcomp; i++) + dst[i] = fz_clamp(src[i], 0, 1); +} + +int fz_colorspace_is_icc(fz_context *ctx, const fz_colorspace *cs) +{ + return cs && cs->free_data == free_icc; +} + +int fz_colorspace_is_lab_icc(fz_context *ctx, const fz_colorspace *cs) +{ + return cs && cs->clamp == clamp_lab_icc; +} + +fz_colorspace * +fz_new_icc_colorspace(fz_context *ctx, int is_static, int num, fz_buffer *buf, const char *name) +{ + fz_colorspace *cs = NULL; + fz_iccprofile *profile; + int is_lab = 0; + + profile = fz_malloc_struct(ctx, fz_iccprofile); + fz_try(ctx) + { + profile->buffer = buf; + if (name != NULL) + { + size_t size; + const unsigned char *data; + data = fz_lookup_icc(ctx, name, &size); + profile->buffer = fz_new_buffer_from_shared_data(ctx, (const char *)data, size); + is_lab = (strcmp(name, "lab-icc") == 0); + profile->bgr = (strcmp(name, "bgr-icc") == 0); + } + + fz_cmm_init_profile(ctx, profile); + + /* Check if correct type */ + if (num != profile->num_devcomp) + { + if (name != NULL) + fz_drop_buffer(ctx, profile->buffer); + fz_cmm_fin_profile(ctx, profile); + fz_free(ctx, profile); + } + else + { + fz_keep_buffer(ctx, buf); + fz_md5_icc(ctx, profile); + cs = fz_new_colorspace(ctx, "icc", is_static, num, 0, NULL, NULL, NULL, is_lab ? clamp_lab_icc : clamp_default_icc, free_icc, profile, sizeof(profile)); + } + } + fz_catch(ctx) + { + fz_drop_buffer(ctx, profile->buffer); + fz_cmm_fin_profile(ctx, profile); + fz_free(ctx, profile); + } + return cs; +} + +/* Gets the icc data from a color space. Used in the writing out of the icc + * data for output formats. + */ +fz_buffer * +fz_icc_data_from_icc_colorspace(fz_context *ctx, const fz_colorspace *cs) +{ + fz_iccprofile *profile; + + if (cs == NULL || !fz_colorspace_is_icc(ctx, cs)) + return NULL; + profile = cs->data; + if (!profile) + return NULL; + return profile->buffer; +} + +static void +free_cal(fz_context *ctx, fz_colorspace *cs) +{ + fz_cal_colorspace *cal_data = cs->data; + if (cal_data->profile != NULL) + { + fz_drop_buffer(ctx, cal_data->profile->buffer); + fz_cmm_fin_profile(ctx, cal_data->profile); + fz_free(ctx, cal_data->profile); + } + fz_free(ctx, cal_data); +} + +int fz_colorspace_is_cal(fz_context *ctx, const fz_colorspace *cs) +{ + return cs && cs->free_data == free_cal; +} + +/* Profile created if needed during draw command. */ +fz_colorspace * +fz_new_cal_colorspace(fz_context *ctx, float *wp, float *bp, float *gamma, float *matrix) +{ + fz_colorspace *cs = NULL; + int num = (matrix == NULL ? 1 : 3); + fz_cal_colorspace *cal_data = fz_malloc_struct(ctx, fz_cal_colorspace); + + memcpy(&cal_data->bp, bp, sizeof(float) * 3); + memcpy(&cal_data->wp, wp, sizeof(float) * 3); + memcpy(&cal_data->gamma, gamma, sizeof(float) * num); + if (matrix != NULL) + memcpy(&cal_data->matrix, matrix, sizeof(float) * 9); + cal_data->n = num; + + fz_try(ctx) + cs = fz_new_colorspace(ctx, "pdf-cal", 0, num, 0, NULL, NULL, NULL, NULL, free_cal, cal_data, sizeof(cal_data)); + fz_catch(ctx) + { + fz_free(ctx, cal_data); + fz_rethrow(ctx); + } + return cs; +} + +void +fz_clamp_color(fz_context *ctx, const fz_colorspace *cs, const float *in, float *out) +{ + cs->clamp(cs, in, out); +} + +/* Default CS. To handle the page specific default settings that PDF can do in + * its page resource dictionary as well as the output intent. Both need to + * to be accessible by the device on the other side of the display list. + * Same with the output intent. */ +void +fz_set_default_gray(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs) +{ + if (cs->n == 1) + { + fz_drop_colorspace(ctx, default_cs->gray); + default_cs->gray = fz_keep_colorspace(ctx, cs); + } +} + +void +fz_set_default_rgb(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs) +{ + if (cs->n == 3) + { + fz_drop_colorspace(ctx, default_cs->rgb); + default_cs->rgb = fz_keep_colorspace(ctx, cs); + } +} + +void +fz_set_default_cmyk(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs) +{ + if (cs->n == 4) + { + fz_drop_colorspace(ctx, default_cs->cmyk); + default_cs->cmyk = fz_keep_colorspace(ctx, cs); + } +} + +void +fz_set_default_output_intent(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs) +{ + fz_drop_colorspace(ctx, default_cs->oi); + default_cs->oi = fz_keep_colorspace(ctx, cs); + + switch (cs->n) + { + case 1: + fz_drop_colorspace(ctx, default_cs->gray); + default_cs->gray = fz_keep_colorspace(ctx, cs); + break; + case 3: + fz_drop_colorspace(ctx, default_cs->rgb); + default_cs->rgb = fz_keep_colorspace(ctx, cs); + break; + case 4: + fz_drop_colorspace(ctx, default_cs->cmyk); + default_cs->cmyk = fz_keep_colorspace(ctx, cs); + break; + } +} + +fz_colorspace * +fz_default_gray(fz_context *ctx, fz_default_colorspaces *default_cs) +{ + if (default_cs) + return default_cs->gray; + else + return fz_device_gray(ctx); +} + +fz_colorspace * +fz_default_rgb(fz_context *ctx, fz_default_colorspaces *default_cs) +{ + if (default_cs) + return default_cs->rgb; + else + return fz_device_rgb(ctx); +} + +fz_colorspace * +fz_default_cmyk(fz_context *ctx, fz_default_colorspaces *default_cs) +{ + if (default_cs) + return default_cs->cmyk; + else + return fz_device_cmyk(ctx); +} + +fz_colorspace * +fz_default_output_intent(fz_context *ctx, fz_default_colorspaces *default_cs) +{ + if (default_cs) + return default_cs->oi; + else + return NULL; +} + +fz_default_colorspaces * +fz_new_default_colorspaces(fz_context *ctx) +{ + fz_default_colorspaces *default_cs = fz_malloc_struct(ctx, fz_default_colorspaces); + default_cs->refs = 1; + default_cs->gray = fz_keep_colorspace(ctx, fz_device_gray(ctx)); + default_cs->rgb = fz_keep_colorspace(ctx, fz_device_rgb(ctx)); + default_cs->cmyk = fz_keep_colorspace(ctx, fz_device_cmyk(ctx)); + default_cs->oi = NULL; + return default_cs; +} + +fz_default_colorspaces * +fz_keep_default_colorspaces(fz_context *ctx, fz_default_colorspaces *default_cs) +{ + return fz_keep_imp(ctx, default_cs, &default_cs->refs); +} + +void +fz_drop_default_colorspaces(fz_context *ctx, fz_default_colorspaces *default_cs) +{ + if (fz_drop_imp(ctx, default_cs, &default_cs->refs)) + { + fz_drop_colorspace(ctx, default_cs->gray); + fz_drop_colorspace(ctx, default_cs->rgb); + fz_drop_colorspace(ctx, default_cs->cmyk); + fz_drop_colorspace(ctx, default_cs->oi); + fz_free(ctx, default_cs); + } +} diff --git a/source/fitz/context.c b/source/fitz/context.c index 42806bc0..380af9a3 100644 --- a/source/fitz/context.c +++ b/source/fitz/context.c @@ -146,6 +146,7 @@ fz_drop_context(fz_context *ctx) fz_drop_style_context(ctx); fz_drop_tuning_context(ctx); fz_drop_colorspace_context(ctx); + fz_drop_cmm_context(ctx); fz_drop_font_context(ctx); fz_drop_id_context(ctx); fz_drop_output_context(ctx); @@ -242,6 +243,7 @@ fz_new_context_imp(const fz_alloc_context *alloc, const fz_locks_context *locks, fz_new_output_context(ctx); fz_new_store_context(ctx, max_store); fz_new_glyph_cache_context(ctx); + fz_new_cmm_context(ctx); fz_new_colorspace_context(ctx); fz_new_font_context(ctx); fz_new_id_context(ctx); @@ -291,6 +293,7 @@ fz_clone_context_internal(fz_context *ctx) new_ctx->store = fz_keep_store_context(new_ctx); new_ctx->glyph_cache = ctx->glyph_cache; new_ctx->glyph_cache = fz_keep_glyph_cache(new_ctx); + fz_new_cmm_context(new_ctx); new_ctx->colorspace = ctx->colorspace; new_ctx->colorspace = fz_keep_colorspace_context(new_ctx); new_ctx->font = ctx->font; diff --git a/source/fitz/device.c b/source/fitz/device.c index 21f74f65..fcf135d0 100644 --- a/source/fitz/device.c +++ b/source/fitz/device.c @@ -109,22 +109,22 @@ pop_clip_stack(fz_context *ctx, fz_device *dev) void fz_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { if (dev->error_depth) return; if (dev->fill_path) - dev->fill_path(ctx, dev, path, even_odd, ctm, colorspace, color, alpha); + dev->fill_path(ctx, dev, path, even_odd, ctm, colorspace, color, alpha, color_params); } void fz_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { if (dev->error_depth) return; if (dev->stroke_path) - dev->stroke_path(ctx, dev, path, stroke, ctm, colorspace, color, alpha); + dev->stroke_path(ctx, dev, path, stroke, ctm, colorspace, color, alpha, color_params); } void @@ -195,22 +195,22 @@ fz_clip_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const void fz_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { if (dev->error_depth) return; if (dev->fill_text) - dev->fill_text(ctx, dev, text, ctm, colorspace, color, alpha); + dev->fill_text(ctx, dev, text, ctm, colorspace, color, alpha, color_params); } void fz_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { if (dev->error_depth) return; if (dev->stroke_text) - dev->stroke_text(ctx, dev, text, stroke, ctm, colorspace, color, alpha); + dev->stroke_text(ctx, dev, text, stroke, ctm, colorspace, color, alpha, color_params); } void @@ -305,31 +305,31 @@ fz_pop_clip(fz_context *ctx, fz_device *dev) } void -fz_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha) +fz_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha, const fz_color_params *color_params) { if (dev->error_depth) return; if (dev->fill_shade) - dev->fill_shade(ctx, dev, shade, ctm, alpha); + dev->fill_shade(ctx, dev, shade, ctm, alpha, color_params); } void -fz_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha) +fz_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha, const fz_color_params *color_params) { if (dev->error_depth) return; if (dev->fill_image) - dev->fill_image(ctx, dev, image, ctm, alpha); + dev->fill_image(ctx, dev, image, ctm, alpha, color_params); } void fz_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { if (dev->error_depth) return; if (dev->fill_image_mask) - dev->fill_image_mask(ctx, dev, image, ctm, colorspace, color, alpha); + dev->fill_image_mask(ctx, dev, image, ctm, colorspace, color, alpha, color_params); } void @@ -366,7 +366,7 @@ fz_clip_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const fz_ma } void -fz_begin_mask(fz_context *ctx, fz_device *dev, const fz_rect *area, int luminosity, fz_colorspace *colorspace, const float *bc) +fz_begin_mask(fz_context *ctx, fz_device *dev, const fz_rect *area, int luminosity, fz_colorspace *colorspace, const float *bc, const fz_color_params *color_params) { if (dev->error_depth) { @@ -379,7 +379,7 @@ fz_begin_mask(fz_context *ctx, fz_device *dev, const fz_rect *area, int luminosi if (dev->hints & FZ_MAINTAIN_CONTAINER_STACK) push_clip_stack(ctx, dev, area, fz_device_container_stack_in_mask); if (dev->begin_mask) - dev->begin_mask(ctx, dev, area, luminosity, colorspace, bc); + dev->begin_mask(ctx, dev, area, luminosity, colorspace, bc, color_params); } fz_catch(ctx) { @@ -513,3 +513,10 @@ fz_render_flags(fz_context *ctx, fz_device *dev, int set, int clear) if (dev->render_flags) dev->render_flags(ctx, dev, set, clear); } + +void +fz_set_default_colorspaces(fz_context *ctx, fz_device *dev, fz_default_colorspaces *default_cs) +{ + if (dev->set_default_colorspaces) + dev->set_default_colorspaces(ctx, dev, default_cs); +} diff --git a/source/fitz/document.c b/source/fitz/document.c index 8f34026a..ceccd26d 100644 --- a/source/fitz/document.c +++ b/source/fitz/document.c @@ -296,6 +296,14 @@ fz_lookup_metadata(fz_context *ctx, fz_document *doc, const char *key, char *buf return -1; } +fz_colorspace * +fz_document_output_intent(fz_context *ctx, fz_document *doc) +{ + if (doc && doc->get_output_intent) + return doc->get_output_intent(ctx, doc); + return NULL; +} + fz_page * fz_load_page(fz_context *ctx, fz_document *doc, int number) { diff --git a/source/fitz/draw-device.c b/source/fitz/draw-device.c index af8e8384..b4cb88c2 100644 --- a/source/fitz/draw-device.c +++ b/source/fitz/draw-device.c @@ -44,6 +44,7 @@ struct fz_draw_device_s fz_device super; fz_matrix transform; fz_rasterizer *rast; + fz_default_colorspaces *default_cs; int flags; int top; fz_scale_cache *cache_x; @@ -107,6 +108,50 @@ static void stack_change(fz_context *ctx, fz_draw_device *dev, char *s) #define STACK_CONVERT(A) do {} while (0) #endif +/* Based upon the existence of a proof color space, and if we happen to be + * in a color space that is our target color space or a transparency group + * color space decide if we should be using the proof color space at this time */ +static fz_colorspace * +fz_proof_cs(fz_context *ctx, fz_device *devp) +{ + fz_draw_device *dev = (fz_draw_device*)devp; + fz_colorspace *prf = fz_default_output_intent(ctx, dev->default_cs); + fz_draw_state *state = &dev->stack[dev->top]; + fz_colorspace *model = state->dest->colorspace; + + if (prf == NULL || model == prf) + return NULL; + return prf; +} + +/* Logic below assumes that default cs is set to color context cs if there + * was not a default in the document for that particular cs + */ +static fz_colorspace *fz_default_colorspace(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs) +{ + if (cs == NULL) + return NULL; + if (default_cs == NULL) + return cs; + + switch (fz_colorspace_n(ctx, cs)) + { + case 1: + if (cs == fz_device_gray(ctx)) + return fz_default_gray(ctx, default_cs); + break; + case 3: + if (cs == fz_device_rgb(ctx)) + return fz_default_rgb(ctx, default_cs); + break; + case 4: + if (cs == fz_device_cmyk(ctx)) + return fz_default_cmyk(ctx, default_cs); + break; + } + return cs; +} + static void fz_grow_stack(fz_context *ctx, fz_draw_device *dev) { int max = dev->stack_cap * 2; @@ -281,12 +326,13 @@ static inline fz_matrix concat(const fz_matrix *one, const fz_matrix *two) static void fz_draw_fill_path(fz_context *ctx, fz_device *devp, const fz_path *path, int even_odd, const fz_matrix *in_ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace_in, const float *color, float alpha, const fz_color_params *color_params) { fz_draw_device *dev = (fz_draw_device*)devp; fz_matrix ctm = concat(in_ctm, &dev->transform); fz_rasterizer *rast = dev->rast; - + fz_colorspace *colorspace = fz_default_colorspace(ctx, dev->default_cs, colorspace_in); + fz_colorspace *prf = fz_proof_cs(ctx, devp); float expansion = fz_matrix_expansion(&ctm); float flatness = 0.3f / expansion; unsigned char colorbv[FZ_MAX_COLORS + 1]; @@ -299,6 +345,9 @@ fz_draw_fill_path(fz_context *ctx, fz_device *devp, const fz_path *path, int eve if (colorspace == NULL && model != NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "color destination requires source color"); + if (color_params == NULL) + color_params = fz_default_color_params(ctx); + if (flatness < 0.001f) flatness = 0.001f; @@ -312,7 +361,7 @@ fz_draw_fill_path(fz_context *ctx, fz_device *devp, const fz_path *path, int eve n = fz_colorspace_n(ctx, model); if (n > 0) { - fz_convert_color(ctx, model, colorfv, colorspace, color); + fz_convert_color(ctx, color_params, prf, model, colorfv, colorspace, color); for (i = 0; i < n; i++) colorbv[i] = colorfv[i] * 255; } @@ -336,12 +385,13 @@ fz_draw_fill_path(fz_context *ctx, fz_device *devp, const fz_path *path, int eve static void fz_draw_stroke_path(fz_context *ctx, fz_device *devp, const fz_path *path, const fz_stroke_state *stroke, const fz_matrix *in_ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace_in, const float *color, float alpha, const fz_color_params *color_params) { fz_draw_device *dev = (fz_draw_device*)devp; fz_matrix ctm = concat(in_ctm, &dev->transform); fz_rasterizer *rast = dev->rast; - + fz_colorspace *colorspace = fz_default_colorspace(ctx, dev->default_cs, colorspace_in); + fz_colorspace *prf = fz_proof_cs(ctx, devp); float expansion = fz_matrix_expansion(&ctm); float flatness = 0.3f / expansion; float linewidth = stroke->linewidth; @@ -354,9 +404,13 @@ fz_draw_stroke_path(fz_context *ctx, fz_device *devp, const fz_path *path, const fz_colorspace *model = state->dest->colorspace; float mlw = fz_graphics_min_line_width(ctx); + if (colorspace == NULL && model != NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "color destination requires source color"); + if (color_params == NULL) + color_params = fz_default_color_params(ctx); + if (mlw > aa_level) aa_level = mlw; if (linewidth * expansion < aa_level) @@ -374,7 +428,7 @@ fz_draw_stroke_path(fz_context *ctx, fz_device *devp, const fz_path *path, const n = fz_colorspace_n(ctx, model); if (n > 0) { - fz_convert_color(ctx, model, colorfv, colorspace, color); + fz_convert_color(ctx, color_params, prf, model, colorfv, colorspace, color); for (i = 0; i < n; i++) colorbv[i] = colorfv[i] * 255; } @@ -626,7 +680,7 @@ draw_glyph(unsigned char *colorbv, fz_pixmap *dst, fz_glyph *glyph, static void fz_draw_fill_text(fz_context *ctx, fz_device *devp, const fz_text *text, const fz_matrix *in_ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace_in, const float *color, float alpha, const fz_color_params *color_params) { fz_draw_device *dev = (fz_draw_device*)devp; fz_matrix ctm = concat(in_ctm, &dev->transform); @@ -637,17 +691,25 @@ fz_draw_fill_text(fz_context *ctx, fz_device *devp, const fz_text *text, const f float colorfv[FZ_MAX_COLORS]; fz_text_span *span; int i, n; + fz_colorspace *colorspace = NULL; + fz_colorspace *prf = fz_proof_cs(ctx, devp); + + if (colorspace_in) + colorspace = fz_default_colorspace(ctx, dev->default_cs, colorspace_in); if (colorspace == NULL && model != NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "color destination requires source color"); + if (color_params == NULL) + color_params = fz_default_color_params(ctx); + if (state->blendmode & FZ_BLEND_KNOCKOUT) state = fz_knockout_begin(ctx, dev); n = fz_colorspace_n(ctx, model); if (n > 0) { - fz_convert_color(ctx, model, colorfv, colorspace, color); + fz_convert_color(ctx, color_params, prf, model, colorfv, colorspace, color); for (i = 0; i < n; i++) colorbv[i] = colorfv[i] * 255; } @@ -700,7 +762,7 @@ fz_draw_fill_text(fz_context *ctx, fz_device *devp, const fz_text *text, const f fz_path *path = fz_outline_glyph(ctx, span->font, gid, &tm); if (path) { - fz_draw_fill_path(ctx, devp, path, 0, in_ctm, colorspace, color, alpha); + fz_draw_fill_path(ctx, devp, path, 0, in_ctm, colorspace, color, alpha, color_params); fz_drop_path(ctx, path); } else @@ -717,8 +779,7 @@ fz_draw_fill_text(fz_context *ctx, fz_device *devp, const fz_text *text, const f static void fz_draw_stroke_text(fz_context *ctx, fz_device *devp, const fz_text *text, const fz_stroke_state *stroke, - const fz_matrix *in_ctm, fz_colorspace *colorspace, - const float *color, float alpha) + const fz_matrix *in_ctm, fz_colorspace *colorspace_in, const float *color, float alpha, const fz_color_params *color_params) { fz_draw_device *dev = (fz_draw_device*)devp; fz_matrix ctm = concat(in_ctm, &dev->transform); @@ -728,17 +789,25 @@ fz_draw_stroke_text(fz_context *ctx, fz_device *devp, const fz_text *text, const float colorfv[FZ_MAX_COLORS]; fz_text_span *span; int i, n; + fz_colorspace *colorspace = NULL; + fz_colorspace *prf = fz_proof_cs(ctx, devp); + + if (colorspace_in) + colorspace = fz_default_colorspace(ctx, dev->default_cs, colorspace_in); if (colorspace == NULL && model != NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "color destination requires source color"); + if (color_params == NULL) + color_params = fz_default_color_params(ctx); + if (state->blendmode & FZ_BLEND_KNOCKOUT) state = fz_knockout_begin(ctx, dev); n = fz_colorspace_n(ctx, model); if (n > 0) { - fz_convert_color(ctx, model, colorfv, colorspace, color); + fz_convert_color(ctx, color_params, prf, model, colorfv, colorspace, color); for (i = 0; i < n; i++) colorbv[i] = colorfv[i] * 255; } @@ -779,7 +848,7 @@ fz_draw_stroke_text(fz_context *ctx, fz_device *devp, const fz_text *text, const fz_path *path = fz_outline_glyph(ctx, span->font, gid, &tm); if (path) { - fz_draw_stroke_path(ctx, devp, path, stroke, in_ctm, colorspace, color, alpha); + fz_draw_stroke_path(ctx, devp, path, stroke, in_ctm, colorspace, color, alpha, color_params); fz_drop_path(ctx, path); } else @@ -893,7 +962,7 @@ fz_draw_clip_text(fz_context *ctx, fz_device *devp, const fz_text *text, const f state[1].mask = NULL; fz_try(ctx) { - fz_draw_fill_path(ctx, devp, path, 0, in_ctm, fz_device_gray(ctx), &white, 1); + fz_draw_fill_path(ctx, devp, path, 0, in_ctm, fz_device_gray(ctx), &white, 1, NULL); } fz_always(ctx) { @@ -1016,7 +1085,7 @@ fz_draw_clip_stroke_text(fz_context *ctx, fz_device *devp, const fz_text *text, state[0].mask = NULL; fz_try(ctx) { - fz_draw_stroke_path(ctx, devp, path, stroke, in_ctm, fz_device_gray(ctx), &white, 1); + fz_draw_stroke_path(ctx, devp, path, stroke, in_ctm, fz_device_gray(ctx), &white, 1, NULL); } fz_always(ctx) { @@ -1050,7 +1119,7 @@ fz_draw_ignore_text(fz_context *ctx, fz_device *dev, const fz_text *text, const } static void -fz_draw_fill_shade(fz_context *ctx, fz_device *devp, fz_shade *shade, const fz_matrix *in_ctm, float alpha) +fz_draw_fill_shade(fz_context *ctx, fz_device *devp, fz_shade *shade, const fz_matrix *in_ctm, float alpha, const fz_color_params *color_params) { fz_draw_device *dev = (fz_draw_device*)devp; fz_matrix ctm = concat(in_ctm, &dev->transform); @@ -1061,6 +1130,7 @@ fz_draw_fill_shade(fz_context *ctx, fz_device *devp, fz_shade *shade, const fz_m unsigned char colorbv[FZ_MAX_COLORS + 1]; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model = state->dest->colorspace; + fz_colorspace *prf = fz_proof_cs(ctx, devp); fz_bound_shade(ctx, shade, &ctm, &bounds); scissor = state->scissor; @@ -1069,6 +1139,9 @@ fz_draw_fill_shade(fz_context *ctx, fz_device *devp, fz_shade *shade, const fz_m if (fz_is_empty_irect(&bbox)) return; + if (color_params == NULL) + color_params = fz_default_color_params(ctx); + if (state->blendmode & FZ_BLEND_KNOCKOUT) state = fz_knockout_begin(ctx, dev); @@ -1096,7 +1169,7 @@ fz_draw_fill_shade(fz_context *ctx, fz_device *devp, fz_shade *shade, const fz_m n = fz_colorspace_n(ctx, model); if (n > 0) { - fz_convert_color(ctx, model, colorfv, shade->colorspace, shade->background); + fz_convert_color(ctx, color_params, prf, model, colorfv, fz_default_colorspace(ctx, dev->default_cs, shade->colorspace), shade->background); for (i = 0; i < n; i++) colorbv[i] = colorfv[i] * 255; } @@ -1127,7 +1200,7 @@ fz_draw_fill_shade(fz_context *ctx, fz_device *devp, fz_shade *shade, const fz_m } } - fz_paint_shade(ctx, shade, &ctm, dest, &bbox); + fz_paint_shade(ctx, shade, &ctm, dest, prf, color_params, &bbox); if (shape) fz_clear_pixmap_rect_with_value(ctx, shape, 255, &bbox); @@ -1219,7 +1292,7 @@ fz_default_image_scale(void *arg, int dst_w, int dst_h, int src_w, int src_h) } static void -fz_draw_fill_image(fz_context *ctx, fz_device *devp, fz_image *image, const fz_matrix *in_ctm, float alpha) +fz_draw_fill_image(fz_context *ctx, fz_device *devp, fz_image *image, const fz_matrix *in_ctm, float alpha, const fz_color_params *color_params) { fz_draw_device *dev = (fz_draw_device*)devp; fz_matrix local_ctm = concat(in_ctm, &dev->transform); @@ -1231,6 +1304,8 @@ fz_draw_fill_image(fz_context *ctx, fz_device *devp, fz_image *image, const fz_m fz_irect clip; fz_matrix inverse; fz_irect src_area; + fz_colorspace *src_cs; + fz_colorspace *prf = fz_proof_cs(ctx, devp); fz_intersect_irect(fz_pixmap_bbox(ctx, state->dest, &clip), &state->scissor); @@ -1273,6 +1348,7 @@ fz_draw_fill_image(fz_context *ctx, fz_device *devp, fz_image *image, const fz_m } pixmap = fz_get_pixmap_from_image(ctx, image, &src_area, &local_ctm, &dx, &dy); + src_cs = fz_default_colorspace(ctx, dev->default_cs, pixmap->colorspace); /* convert images with more components (cmyk->rgb) before scaling */ /* convert images with fewer components (gray->rgb) after scaling */ @@ -1286,12 +1362,12 @@ fz_draw_fill_image(fz_context *ctx, fz_device *devp, fz_image *image, const fz_m state = fz_knockout_begin(ctx, dev); after = 0; - if (pixmap->colorspace == fz_device_gray(ctx)) + if (src_cs == fz_device_gray(ctx)) after = 1; - if (pixmap->colorspace != model && !after) + if (src_cs != model && !after) { - fz_pixmap *converted = fz_convert_pixmap(ctx, pixmap, model, 1); + fz_pixmap *converted = fz_convert_pixmap(ctx, pixmap, model, prf, dev->default_cs, color_params, 1); fz_drop_pixmap(ctx, pixmap); pixmap = converted; } @@ -1315,18 +1391,18 @@ fz_draw_fill_image(fz_context *ctx, fz_device *devp, fz_image *image, const fz_m } } - if (pixmap->colorspace != model) + if (src_cs != model && after) { #if FZ_PLOTTERS_RGB - if ((pixmap->colorspace == fz_device_gray(ctx) && model == fz_device_rgb(ctx)) || - (pixmap->colorspace == fz_device_gray(ctx) && model == fz_device_bgr(ctx))) + if ((src_cs == fz_device_gray(ctx) && model == fz_device_rgb(ctx)) || + (src_cs == fz_device_gray(ctx) && model == fz_device_bgr(ctx))) { /* We have special case rendering code for gray -> rgb/bgr */ } else #endif { - fz_pixmap *converted = fz_convert_pixmap(ctx, pixmap, model, 1); + fz_pixmap *converted = fz_convert_pixmap(ctx, pixmap, model, prf, dev->default_cs, color_params, 1); fz_drop_pixmap(ctx, pixmap); pixmap = converted; } @@ -1345,7 +1421,7 @@ fz_draw_fill_image(fz_context *ctx, fz_device *devp, fz_image *image, const fz_m static void fz_draw_fill_image_mask(fz_context *ctx, fz_device *devp, fz_image *image, const fz_matrix *in_ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace_in, const float *color, float alpha, const fz_color_params *color_params) { fz_draw_device *dev = (fz_draw_device*)devp; fz_matrix local_ctm = concat(in_ctm, &dev->transform); @@ -1360,6 +1436,14 @@ fz_draw_fill_image_mask(fz_context *ctx, fz_device *devp, fz_image *image, const fz_irect clip; fz_matrix inverse; fz_irect src_area; + fz_colorspace *colorspace = NULL; + fz_colorspace *prf = fz_proof_cs(ctx, devp); + + if (colorspace_in) + colorspace = fz_default_colorspace(ctx, dev->default_cs, colorspace_in); + + if (color_params == NULL) + color_params = fz_default_color_params(ctx); if (colorspace == NULL && model != NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "color destination requires source color"); @@ -1436,7 +1520,7 @@ fz_draw_fill_image_mask(fz_context *ctx, fz_device *devp, fz_image *image, const n = fz_colorspace_n(ctx, model); if (n > 0) { - fz_convert_color(ctx, model, colorfv, colorspace, color); + fz_convert_color(ctx, color_params, prf, model, colorfv, colorspace, color); for (i = 0; i < n; i++) colorbv[i] = colorfv[i] * 255; } @@ -1629,7 +1713,7 @@ fz_draw_pop_clip(fz_context *ctx, fz_device *devp) } static void -fz_draw_begin_mask(fz_context *ctx, fz_device *devp, const fz_rect *rect, int luminosity, fz_colorspace *colorspace, const float *colorfv) +fz_draw_begin_mask(fz_context *ctx, fz_device *devp, const fz_rect *rect, int luminosity, fz_colorspace *colorspace_in, const float *colorfv, const fz_color_params *color_params) { fz_draw_device *dev = (fz_draw_device*)devp; fz_pixmap *dest; @@ -1637,6 +1721,13 @@ fz_draw_begin_mask(fz_context *ctx, fz_device *devp, const fz_rect *rect, int lu fz_draw_state *state = push_stack(ctx, dev); fz_pixmap *shape = state->shape; fz_rect trect = *rect; + fz_colorspace *colorspace = NULL; + + if (colorspace_in) + colorspace = fz_default_colorspace(ctx, dev->default_cs, colorspace_in); + + if (color_params == NULL) + color_params = fz_default_color_params(ctx); STACK_PUSHED("mask"); fz_transform_rect(&trect, &dev->transform); @@ -1671,7 +1762,7 @@ fz_draw_begin_mask(fz_context *ctx, fz_device *devp, const fz_rect *rect, int lu float bc; if (!colorspace) colorspace = fz_device_gray(ctx); - fz_convert_color(ctx, fz_device_gray(ctx), &bc, colorspace, colorfv); + fz_convert_color(ctx, color_params, NULL, fz_device_gray(ctx), &bc, colorspace, colorfv); fz_clear_pixmap_with_value(ctx, dest, bc * 255); if (shape) fz_clear_pixmap_with_value(ctx, shape, 255); @@ -2247,11 +2338,28 @@ fz_draw_end_tile(fz_context *ctx, fz_device *devp) } static void +fz_draw_render_flags(fz_context *ctx, fz_device *devp, int set, int clear) +{ + fz_draw_device *dev = (fz_draw_device*)devp; + dev->flags = (dev->flags | set ) & ~clear; +} + +static void +fz_draw_set_default_colorspaces(fz_context *ctx, fz_device *devp, fz_default_colorspaces *default_cs) +{ + fz_draw_device *dev = (fz_draw_device*)devp; + fz_drop_default_colorspaces(ctx, dev->default_cs); + dev->default_cs = fz_keep_default_colorspaces(ctx, default_cs); +} + +static void fz_draw_drop_device(fz_context *ctx, fz_device *devp) { fz_draw_device *dev = (fz_draw_device*)devp; fz_rasterizer *rast = dev->rast; + fz_drop_default_colorspaces(ctx, dev->default_cs); + /* pop and free the stacks */ if (dev->top > 0) fz_warn(ctx, "items left on stack in draw device: %d", dev->top+1); @@ -2277,14 +2385,6 @@ fz_draw_drop_device(fz_context *ctx, fz_device *devp) fz_drop_rasterizer(ctx, rast); } -static void -fz_draw_render_flags(fz_context *ctx, fz_device *devp, int set, int clear) -{ - fz_draw_device *dev = (fz_draw_device*)devp; - - dev->flags = (dev->flags | set ) & ~clear; -} - fz_device * fz_new_draw_device(fz_context *ctx, const fz_matrix *transform, fz_pixmap *dest) { @@ -2319,6 +2419,7 @@ fz_new_draw_device(fz_context *ctx, const fz_matrix *transform, fz_pixmap *dest) dev->super.end_tile = fz_draw_end_tile; dev->super.render_flags = fz_draw_render_flags; + dev->super.set_default_colorspaces = fz_draw_set_default_colorspaces; dev->transform = transform ? *transform : fz_identity; dev->flags = 0; diff --git a/source/fitz/draw-mesh.c b/source/fitz/draw-mesh.c index a40cdd12..10091521 100644 --- a/source/fitz/draw-mesh.c +++ b/source/fitz/draw-mesh.c @@ -205,7 +205,7 @@ do_paint_tri(fz_context *ctx, void *arg, fz_vertex *av, fz_vertex *bv, fz_vertex } void -fz_paint_shade(fz_context *ctx, fz_shade *shade, const fz_matrix *ctm, fz_pixmap *dest, const fz_irect *bbox) +fz_paint_shade(fz_context *ctx, fz_shade *shade, const fz_matrix *ctm, fz_pixmap *dest, fz_colorspace *prf, const fz_color_params *color_params, const fz_irect *bbox) { unsigned char clut[256][FZ_MAX_COLORS]; fz_pixmap *temp = NULL; @@ -227,7 +227,7 @@ fz_paint_shade(fz_context *ctx, fz_shade *shade, const fz_matrix *ctm, fz_pixmap fz_color_converter cc; int cn = fz_colorspace_n(ctx, shade->colorspace); n = fz_colorspace_n(ctx, dest->colorspace); - fz_lookup_color_converter(ctx, &cc, dest->colorspace, shade->colorspace); + fz_find_color_converter(ctx, &cc, prf, dest->colorspace, shade->colorspace, color_params); for (i = 0; i < 256; i++) { cc.convert(ctx, &cc, color, shade->function[i]); @@ -235,6 +235,7 @@ fz_paint_shade(fz_context *ctx, fz_shade *shade, const fz_matrix *ctm, fz_pixmap clut[i][k] = color[k] * 255; clut[i][k] = shade->function[i][cn] * 255; } + fz_drop_color_converter(ctx, &cc); /* We need to use alpha = 1 here, because the shade might not fill * the bbox. */ conv = fz_new_pixmap_with_bbox(ctx, dest->colorspace, bbox, 1); @@ -250,7 +251,7 @@ fz_paint_shade(fz_context *ctx, fz_shade *shade, const fz_matrix *ctm, fz_pixmap ptd.shade = shade; ptd.bbox = bbox; - fz_init_cached_color_converter(ctx, &ptd.cc, temp->colorspace, shade->colorspace); + fz_init_cached_color_converter(ctx, &ptd.cc, NULL, temp->colorspace, shade->colorspace, color_params); fz_process_shade(ctx, shade, &local_ctm, prepare_mesh_vertex, &do_paint_tri, &ptd); if (shade->use_function) diff --git a/source/fitz/fitz-imp.h b/source/fitz/fitz-imp.h index d75e2242..23908a8d 100644 --- a/source/fitz/fitz-imp.h +++ b/source/fitz/fitz-imp.h @@ -13,6 +13,8 @@ struct fz_buffer_s }; void fz_new_colorspace_context(fz_context *ctx); +void fz_new_cmm_context(fz_context *ctx); +void fz_drop_cmm_context(fz_context *ctx); fz_colorspace_context *fz_keep_colorspace_context(fz_context *ctx); void fz_drop_colorspace_context(fz_context *ctx); diff --git a/source/fitz/icc34.h b/source/fitz/icc34.h new file mode 100644 index 00000000..3b63abcf --- /dev/null +++ b/source/fitz/icc34.h @@ -0,0 +1,1022 @@ +/* Header file guard bands */ +#ifndef ICC_H +#define ICC_H + +/***************************************************************** + 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. +******************************************************************/ + +/* + * This version of the header file corresponds to the profile + * specification version 3.4. + * + * All header file entries are pre-fixed with "ic" to help + * avoid name space collisions. Signatures are pre-fixed with + * icSig. + * + * The structures defined in this header file were created to + * represent a description of an ICC profile on disk. Rather + * than use pointers a technique is used where a single byte array + * was placed at the end of each structure. This allows us in "C" + * to extend the structure by allocating more data than is needed + * to account for variable length structures. + * + * This also ensures that data following is allocated + * contiguously and makes it easier to write and read data from + * the file. + * + * For example to allocate space for a 256 count length UCR + * and BG array, and fill the allocated data. Note strlen + 1 + * to remember NULL terminator. + * + icUcrBgCurve *ucrCurve, *bgCurve; + int ucr_nbytes, bg_nbytes, string_bytes; + icUcrBg *ucrBgWrite; + char ucr_string[100], *ucr_char; + + strcpy(ucr_string, "Example ucrBG curves"); + ucr_nbytes = sizeof(icUInt32Number) + + (UCR_CURVE_SIZE * sizeof(icUInt16Number)); + bg_nbytes = sizeof(icUInt32Number) + + (BG_CURVE_SIZE * sizeof(icUInt16Number)); + string_bytes = strlen(ucr_string) + 1; + + ucrBgWrite = (icUcrBg *)malloc( + (ucr_nbytes + bg_nbytes + string_bytes)); + + ucrCurve = (icUcrBgCurve *)ucrBgWrite->data; + ucrCurve->count = UCR_CURVE_SIZE; + for (i=0; i<ucrCurve->count; i++) + ucrCurve->curve[i] = (icUInt16Number)i; + + bgCurve = (icUcrBgCurve *)((char *)ucrCurve + ucr_nbytes); + bgCurve->count = BG_CURVE_SIZE; + for (i=0; i<bgCurve->count; i++) + bgCurve->curve[i] = 255 - (icUInt16Number)i; + + ucr_char = (char *)((char *)bgCurve + bg_nbytes); + memcpy(ucr_char, ucr_string, string_bytes); + * + */ + +/* + * Many of the structures contain variable length arrays. This + * is represented by the use of the convention. + * + * type data[icAny]; + */ + +/*------------------------------------------------------------------------*/ +/* + * Defines used in the specification + */ +#define icMagicNumber 0x61637370L /* 'acsp' */ +#define icVersionNumber 0x02100000L /* 2.1.0, BCD */ + +/* Screening Encodings */ +#define icPrtrDefaultScreensFalse 0x00000000L /* Bit pos 0 */ +#define icPrtrDefaultScreensTrue 0x00000001L /* Bit pos 0 */ +#define icLinesPerInch 0x00000002L /* Bit pos 1 */ +#define icLinesPerCm 0x00000000L /* Bit pos 1 */ + +/* + * Device attributes, currently defined values correspond + * to the low 4 bytes of the 8 byte attribute quantity, see + * the header for their location. + */ +#define icReflective 0x00000000L /* Bit pos 0 */ +#define icTransparency 0x00000001L /* Bit pos 0 */ +#define icGlossy 0x00000000L /* Bit pos 1 */ +#define icMatte 0x00000002L /* Bit pos 1 */ + +/* + * Profile header flags, the low 16 bits are reserved for consortium + * use. + */ +#define icEmbeddedProfileFalse 0x00000000L /* Bit pos 0 */ +#define icEmbeddedProfileTrue 0x00000001L /* Bit pos 0 */ +#define icUseAnywhere 0x00000000L /* Bit pos 1 */ +#define icUseWithEmbeddedDataOnly 0x00000002L /* Bit pos 1 */ + +/* Ascii or Binary data */ +#define icAsciiData 0x00000000L +#define icBinaryData 0x00000001L + +/* + * Define used to indicate that this is a variable length array + */ +#define icAny 1 + + +/*------------------------------------------------------------------------*/ +/* + * Use this area to translate platform definitions of long + * etc into icXXX form. The rest of the header uses the icXXX + * typedefs. Signatures are 4 byte quantities. + * + */ + + +#ifdef PACKAGE_NAME +/* + June 9, 2003, Adapted for use with configure by Bob Friesenhahn + Added the stupid check for autoconf by Marti Maria. + PACKAGE_NAME is defined if autoconf is being used +*/ + +typedef @UINT8_T@ icUInt8Number; +typedef @UINT16_T@ icUInt16Number; +typedef @UINT32_T@ icUInt32Number; +typedef @UINT32_T@ icUInt64Number[2]; + +typedef @INT8_T@ icInt8Number; +typedef @INT16_T@ icInt16Number; +typedef @INT32_T@ icInt32Number; +typedef @INT32_T@ icInt64Number[2]; + +#elif defined (__digital__) && defined (__unix__) + +/* + *Apr-17-2002: Modified by Marti Maria in order to provide wider portability. + */ + +/* Tru64 */ + +#include <inttypes.h> + +typedef uint8_t icUInt8Number; +typedef uint16_t icUInt16Number; +typedef uint32_t icUInt32Number; +typedef uint32_t icUInt64Number[2]; + +typedef int8_t icInt8Number; +typedef int16_t icInt16Number; +typedef int32_t icInt32Number; +typedef int32_t icInt64Number[2]; + +#elif defined(__sgi) +#include "sgidefs.h" + + +/* + * Number definitions + */ + +/* Unsigned integer numbers */ +typedef unsigned char icUInt8Number; +typedef unsigned short icUInt16Number; +typedef __uint32_t icUInt32Number; +typedef __uint32_t icUInt64Number[2]; + +/* Signed numbers */ +typedef char icInt8Number; +typedef short icInt16Number; +typedef __int32_t icInt32Number; +typedef __int32_t icInt64Number[2]; + + +#elif defined(__GNUC__) || defined(__unix__) || defined(__unix) + +#include <sys/types.h> + +#if defined(__sun) || defined(__hpux) || defined (__MINGW) || defined(__MINGW32__) || defined(HAVE_STDINT_H) + +#if defined (__MINGW) || defined(__MINGW32__) || defined(HAVE_STDINT_H) +#include <stdint.h> +#endif + + +typedef uint8_t icUInt8Number; +typedef uint16_t icUInt16Number; +typedef uint32_t icUInt32Number; +typedef uint32_t icUInt64Number[2]; + +#else + +/* Unsigned integer numbers */ +typedef u_int8_t icUInt8Number; +typedef u_int16_t icUInt16Number; +typedef u_int32_t icUInt32Number; +typedef u_int32_t icUInt64Number[2]; + +#endif /* defined(__sun) || defined(__hpux) || defined (__MINGW) || defined(__MINGW32__) || defined(HAVE_STDINT_H) */ + + +/* Signed numbers */ +typedef int8_t icInt8Number; +typedef int16_t icInt16Number; +typedef int32_t icInt32Number; +typedef int32_t icInt64Number[2]; + + +#else /* default definitions */ + +/* + * Number definitions + */ + +/* Unsigned integer numbers */ +typedef unsigned char icUInt8Number; +typedef unsigned short icUInt16Number; +typedef unsigned long icUInt32Number; +typedef unsigned long icUInt64Number[2]; + +/* Signed numbers */ +typedef char icInt8Number; +typedef short icInt16Number; +typedef long icInt32Number; +typedef long icInt64Number[2]; + + +#endif /* default defs */ + +/* Base types */ + +typedef icInt32Number icSignature; +typedef icInt32Number icS15Fixed16Number; +typedef icUInt32Number icU16Fixed16Number; + + +/*------------------------------------------------------------------------*/ +/* public tags and sizes */ +typedef enum { + icSigAToB0Tag = 0x41324230L, /* 'A2B0' */ + icSigAToB1Tag = 0x41324231L, /* 'A2B1' */ + icSigAToB2Tag = 0x41324232L, /* 'A2B2' */ + icSigBlueColorantTag = 0x6258595AL, /* 'bXYZ' */ + icSigBlueTRCTag = 0x62545243L, /* 'bTRC' */ + icSigBToA0Tag = 0x42324130L, /* 'B2A0' */ + icSigBToA1Tag = 0x42324131L, /* 'B2A1' */ + icSigBToA2Tag = 0x42324132L, /* 'B2A2' */ + icSigCalibrationDateTimeTag = 0x63616C74L, /* 'calt' */ + icSigCharTargetTag = 0x74617267L, /* 'targ' */ + icSigCopyrightTag = 0x63707274L, /* 'cprt' */ + icSigCrdInfoTag = 0x63726469L, /* 'crdi' */ + icSigDeviceMfgDescTag = 0x646D6E64L, /* 'dmnd' */ + icSigDeviceModelDescTag = 0x646D6464L, /* 'dmdd' */ + icSigGamutTag = 0x67616D74L, /* 'gamt ' */ + icSigGrayTRCTag = 0x6b545243L, /* 'kTRC' */ + icSigGreenColorantTag = 0x6758595AL, /* 'gXYZ' */ + icSigGreenTRCTag = 0x67545243L, /* 'gTRC' */ + icSigLuminanceTag = 0x6C756d69L, /* 'lumi' */ + icSigMeasurementTag = 0x6D656173L, /* 'meas' */ + icSigMediaBlackPointTag = 0x626B7074L, /* 'bkpt' */ + icSigMediaWhitePointTag = 0x77747074L, /* 'wtpt' */ + icSigNamedColorTag = 0x6E636f6CL, /* 'ncol' + * OBSOLETE, use ncl2 */ + icSigNamedColor2Tag = 0x6E636C32L, /* 'ncl2' */ + icSigPreview0Tag = 0x70726530L, /* 'pre0' */ + icSigPreview1Tag = 0x70726531L, /* 'pre1' */ + icSigPreview2Tag = 0x70726532L, /* 'pre2' */ + icSigProfileDescriptionTag = 0x64657363L, /* 'desc' */ + icSigProfileSequenceDescTag = 0x70736571L, /* 'pseq' */ + icSigPs2CRD0Tag = 0x70736430L, /* 'psd0' */ + icSigPs2CRD1Tag = 0x70736431L, /* 'psd1' */ + icSigPs2CRD2Tag = 0x70736432L, /* 'psd2' */ + icSigPs2CRD3Tag = 0x70736433L, /* 'psd3' */ + icSigPs2CSATag = 0x70733273L, /* 'ps2s' */ + icSigPs2RenderingIntentTag = 0x70733269L, /* 'ps2i' */ + icSigRedColorantTag = 0x7258595AL, /* 'rXYZ' */ + icSigRedTRCTag = 0x72545243L, /* 'rTRC' */ + icSigScreeningDescTag = 0x73637264L, /* 'scrd' */ + icSigScreeningTag = 0x7363726EL, /* 'scrn' */ + icSigTechnologyTag = 0x74656368L, /* 'tech' */ + icSigUcrBgTag = 0x62666420L, /* 'bfd ' */ + icSigViewingCondDescTag = 0x76756564L, /* 'vued' */ + icSigViewingConditionsTag = 0x76696577L, /* 'view' */ + icMaxEnumTag = 0xFFFFFFFFL +} icTagSignature; + +/* technology signature descriptions */ +typedef enum { + icSigDigitalCamera = 0x6463616DL, /* 'dcam' */ + icSigFilmScanner = 0x6673636EL, /* 'fscn' */ + icSigReflectiveScanner = 0x7273636EL, /* 'rscn' */ + icSigInkJetPrinter = 0x696A6574L, /* 'ijet' */ + icSigThermalWaxPrinter = 0x74776178L, /* 'twax' */ + icSigElectrophotographicPrinter = 0x6570686FL, /* 'epho' */ + icSigElectrostaticPrinter = 0x65737461L, /* 'esta' */ + icSigDyeSublimationPrinter = 0x64737562L, /* 'dsub' */ + icSigPhotographicPaperPrinter = 0x7270686FL, /* 'rpho' */ + icSigFilmWriter = 0x6670726EL, /* 'fprn' */ + icSigVideoMonitor = 0x7669646DL, /* 'vidm' */ + icSigVideoCamera = 0x76696463L, /* 'vidc' */ + icSigProjectionTelevision = 0x706A7476L, /* 'pjtv' */ + icSigCRTDisplay = 0x43525420L, /* 'CRT ' */ + icSigPMDisplay = 0x504D4420L, /* 'PMD ' */ + icSigAMDisplay = 0x414D4420L, /* 'AMD ' */ + icSigPhotoCD = 0x4B504344L, /* 'KPCD' */ + icSigPhotoImageSetter = 0x696D6773L, /* 'imgs' */ + icSigGravure = 0x67726176L, /* 'grav' */ + icSigOffsetLithography = 0x6F666673L, /* 'offs' */ + icSigSilkscreen = 0x73696C6BL, /* 'silk' */ + icSigFlexography = 0x666C6578L, /* 'flex' */ + icMaxEnumTechnology = 0xFFFFFFFFL +} icTechnologySignature; + +/* type signatures */ +typedef enum { + icSigCurveType = 0x63757276L, /* 'curv' */ + icSigDataType = 0x64617461L, /* 'data' */ + icSigDateTimeType = 0x6474696DL, /* 'dtim' */ + icSigLut16Type = 0x6d667432L, /* 'mft2' */ + icSigLut8Type = 0x6d667431L, /* 'mft1' */ + icSigMeasurementType = 0x6D656173L, /* 'meas' */ + icSigNamedColorType = 0x6E636f6CL, /* 'ncol' + * OBSOLETE, use ncl2 */ + icSigProfileSequenceDescType = 0x70736571L, /* 'pseq' */ + icSigS15Fixed16ArrayType = 0x73663332L, /* 'sf32' */ + icSigScreeningType = 0x7363726EL, /* 'scrn' */ + icSigSignatureType = 0x73696720L, /* 'sig ' */ + icSigTextType = 0x74657874L, /* 'text' */ + icSigTextDescriptionType = 0x64657363L, /* 'desc' */ + icSigU16Fixed16ArrayType = 0x75663332L, /* 'uf32' */ + icSigUcrBgType = 0x62666420L, /* 'bfd ' */ + icSigUInt16ArrayType = 0x75693136L, /* 'ui16' */ + icSigUInt32ArrayType = 0x75693332L, /* 'ui32' */ + icSigUInt64ArrayType = 0x75693634L, /* 'ui64' */ + icSigUInt8ArrayType = 0x75693038L, /* 'ui08' */ + icSigViewingConditionsType = 0x76696577L, /* 'view' */ + icSigXYZType = 0x58595A20L, /* 'XYZ ' */ + icSigXYZArrayType = 0x58595A20L, /* 'XYZ ' */ + icSigNamedColor2Type = 0x6E636C32L, /* 'ncl2' */ + icSigCrdInfoType = 0x63726469L, /* 'crdi' */ + icMaxEnumType = 0xFFFFFFFFL +} icTagTypeSignature; + +/* + * 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; + +/* profileClass enumerations */ +typedef enum { + icSigInputClass = 0x73636E72L, /* 'scnr' */ + icSigDisplayClass = 0x6D6E7472L, /* 'mntr' */ + icSigOutputClass = 0x70727472L, /* 'prtr' */ + icSigLinkClass = 0x6C696E6BL, /* 'link' */ + icSigAbstractClass = 0x61627374L, /* 'abst' */ + icSigColorSpaceClass = 0x73706163L, /* 'spac' */ + icSigNamedColorClass = 0x6e6d636cL, /* 'nmcl' */ + icMaxEnumClass = 0xFFFFFFFFL +} icProfileClassSignature; + +/* Platform Signatures */ +typedef enum { + icSigMacintosh = 0x4150504CL, /* 'APPL' */ + icSigMicrosoft = 0x4D534654L, /* 'MSFT' */ + icSigSolaris = 0x53554E57L, /* 'SUNW' */ + icSigSGI = 0x53474920L, /* 'SGI ' */ + icSigTaligent = 0x54474E54L, /* 'TGNT' */ + icMaxEnumPlatform = 0xFFFFFFFFL +} icPlatformSignature; + +/*------------------------------------------------------------------------*/ +/* + * Other enums + */ + +/* Measurement Flare, used in the measurmentType tag */ +typedef enum { + icFlare0 = 0x00000000L, /* 0% flare */ + icFlare100 = 0x00000001L, /* 100% flare */ + icMaxFlare = 0xFFFFFFFFL +} icMeasurementFlare; + +/* Measurement Geometry, used in the measurmentType tag */ +typedef enum { + icGeometryUnknown = 0x00000000L, /* Unknown */ + icGeometry045or450 = 0x00000001L, /* 0/45, 45/0 */ + icGeometry0dord0 = 0x00000002L, /* 0/d or d/0 */ + icMaxGeometry = 0xFFFFFFFFL +} icMeasurementGeometry; + +/* Rendering Intents, used in the profile header */ +typedef enum { + icPerceptual = 0, + icRelativeColorimetric = 1, + icSaturation = 2, + icAbsoluteColorimetric = 3, + icMaxEnumIntent = 0xFFFFFFFFL +} icRenderingIntent; + +/* Different Spot Shapes currently defined, used for screeningType */ +typedef enum { + icSpotShapeUnknown = 0, + icSpotShapePrinterDefault = 1, + icSpotShapeRound = 2, + icSpotShapeDiamond = 3, + icSpotShapeEllipse = 4, + icSpotShapeLine = 5, + icSpotShapeSquare = 6, + icSpotShapeCross = 7, + icMaxEnumSpot = 0xFFFFFFFFL +} icSpotShape; + +/* Standard Observer, used in the measurmentType tag */ +typedef enum { + icStdObsUnknown = 0x00000000L, /* Unknown */ + icStdObs1931TwoDegrees = 0x00000001L, /* 2 deg */ + icStdObs1964TenDegrees = 0x00000002L, /* 10 deg */ + icMaxStdObs = 0xFFFFFFFFL +} icStandardObserver; + +/* Pre-defined illuminants, used in measurement and viewing conditions type */ +typedef enum { + icIlluminantUnknown = 0x00000000L, + icIlluminantD50 = 0x00000001L, + icIlluminantD65 = 0x00000002L, + icIlluminantD93 = 0x00000003L, + icIlluminantF2 = 0x00000004L, + icIlluminantD55 = 0x00000005L, + icIlluminantA = 0x00000006L, + icIlluminantEquiPowerE = 0x00000007L, + icIlluminantF8 = 0x00000008L, + icMaxEnumIluminant = 0xFFFFFFFFL +} icIlluminant; + + +/*------------------------------------------------------------------------*/ +/* + * Arrays of numbers + */ + +/* Int8 Array */ +typedef struct { + icInt8Number data[icAny]; /* Variable array of values */ +} icInt8Array; + +/* UInt8 Array */ +typedef struct { + icUInt8Number data[icAny]; /* Variable array of values */ +} icUInt8Array; + +/* uInt16 Array */ +typedef struct { + icUInt16Number data[icAny]; /* Variable array of values */ +} icUInt16Array; + +/* Int16 Array */ +typedef struct { + icInt16Number data[icAny]; /* Variable array of values */ +} icInt16Array; + +/* uInt32 Array */ +typedef struct { + icUInt32Number data[icAny]; /* Variable array of values */ +} icUInt32Array; + +/* Int32 Array */ +typedef struct { + icInt32Number data[icAny]; /* Variable array of values */ +} icInt32Array; + +/* UInt64 Array */ +typedef struct { + icUInt64Number data[icAny]; /* Variable array of values */ +} icUInt64Array; + +/* Int64 Array */ +typedef struct { + icInt64Number data[icAny]; /* Variable array of values */ +} icInt64Array; + +/* u16Fixed16 Array */ +typedef struct { + icU16Fixed16Number data[icAny]; /* Variable array of values */ +} icU16Fixed16Array; + +/* s15Fixed16 Array */ +typedef struct { + icS15Fixed16Number data[icAny]; /* Variable array of values */ +} icS15Fixed16Array; + +/* The base date time number */ +typedef struct { + icUInt16Number year; + icUInt16Number month; + icUInt16Number day; + icUInt16Number hours; + icUInt16Number minutes; + icUInt16Number seconds; +} icDateTimeNumber; + +/* XYZ Number */ +typedef struct { + icS15Fixed16Number X; + icS15Fixed16Number Y; + icS15Fixed16Number Z; +} icXYZNumber; + +/* XYZ Array */ +typedef struct { + icXYZNumber data[icAny]; /* Variable array of XYZ numbers */ +} icXYZArray; + +/* Curve */ +typedef struct { + icUInt32Number count; /* Number of entries */ + icUInt16Number data[icAny]; /* The actual table data, real + * number is determined by count + * Interpretation depends on how + * data is used with a given tag + */ +} icCurve; + +/* Data */ +typedef struct { + icUInt32Number dataFlag; /* 0 = ascii, 1 = binary */ + icInt8Number data[icAny]; /* Data, size from tag */ +} icData; + +/* lut16 */ +typedef struct { + icUInt8Number inputChan; /* Number of input channels */ + icUInt8Number outputChan; /* Number of output channels */ + icUInt8Number clutPoints; /* Number of grid points */ + icInt8Number pad; /* Padding for byte alignment */ + icS15Fixed16Number e00; /* e00 in the 3 * 3 */ + icS15Fixed16Number e01; /* e01 in the 3 * 3 */ + icS15Fixed16Number e02; /* e02 in the 3 * 3 */ + icS15Fixed16Number e10; /* e10 in the 3 * 3 */ + icS15Fixed16Number e11; /* e11 in the 3 * 3 */ + icS15Fixed16Number e12; /* e12 in the 3 * 3 */ + icS15Fixed16Number e20; /* e20 in the 3 * 3 */ + icS15Fixed16Number e21; /* e21 in the 3 * 3 */ + icS15Fixed16Number e22; /* e22 in the 3 * 3 */ + icUInt16Number inputEnt; /* Num of in-table entries */ + icUInt16Number outputEnt; /* Num of out-table entries */ + icUInt16Number data[icAny]; /* Data follows see spec */ +/* + * Data that follows is of this form + * + * icUInt16Number inputTable[inputChan][icAny]; * The in-table + * icUInt16Number clutTable[icAny]; * The clut + * icUInt16Number outputTable[outputChan][icAny]; * The out-table + */ +} icLut16; + +/* lut8, input & output tables are always 256 bytes in length */ +typedef struct { + icUInt8Number inputChan; /* Num of input channels */ + icUInt8Number outputChan; /* Num of output channels */ + icUInt8Number clutPoints; /* Num of grid points */ + icInt8Number pad; + icS15Fixed16Number e00; /* e00 in the 3 * 3 */ + icS15Fixed16Number e01; /* e01 in the 3 * 3 */ + icS15Fixed16Number e02; /* e02 in the 3 * 3 */ + icS15Fixed16Number e10; /* e10 in the 3 * 3 */ + icS15Fixed16Number e11; /* e11 in the 3 * 3 */ + icS15Fixed16Number e12; /* e12 in the 3 * 3 */ + icS15Fixed16Number e20; /* e20 in the 3 * 3 */ + icS15Fixed16Number e21; /* e21 in the 3 * 3 */ + icS15Fixed16Number e22; /* e22 in the 3 * 3 */ + icUInt8Number data[icAny]; /* Data follows see spec */ +/* + * Data that follows is of this form + * + * icUInt8Number inputTable[inputChan][256]; * The in-table + * icUInt8Number clutTable[icAny]; * The clut + * icUInt8Number outputTable[outputChan][256]; * The out-table + */ +} icLut8; + +/* Measurement Data */ +typedef struct { + icStandardObserver stdObserver; /* Standard observer */ + icXYZNumber backing; /* XYZ for backing */ + icMeasurementGeometry geometry; /* Meas. geometry */ + icMeasurementFlare flare; /* Measurement flare */ + icIlluminant illuminant; /* Illuminant */ +} icMeasurement; + +/* Named color */ + +/* + * icNamedColor2 takes the place of icNamedColor + */ +typedef struct { + icUInt32Number vendorFlag; /* Bottom 16 bits for IC use */ + icUInt32Number count; /* Count of named colors */ + icUInt32Number nDeviceCoords; /* Num of device coordinates */ + icInt8Number prefix[32]; /* Prefix for each color name */ + icInt8Number suffix[32]; /* Suffix for each color name */ + icInt8Number data[icAny]; /* Named color data follows */ +/* + * Data that follows is of this form + * + * icInt8Number root1[32]; * Root name for 1st color + * icUInt16Number pcsCoords1[icAny]; * PCS coords of 1st color + * icUInt16Number deviceCoords1[icAny]; * Dev coords of 1st color + * icInt8Number root2[32]; * Root name for 2nd color + * icUInt16Number pcsCoords2[icAny]; * PCS coords of 2nd color + * icUInt16Number deviceCoords2[icAny]; * Dev coords of 2nd color + * : + * : + * Repeat for name and PCS and device color coordinates up to (count-1) + * + * NOTES: + * PCS and device space can be determined from the header. + * + * PCS coordinates are icUInt16 numbers and are described in Annex A of + * the ICC spec. Only 16 bit L*a*b* and XYZ are allowed. The number of + * coordinates is consistent with the headers PCS. + * + * Device coordinates are icUInt16 numbers where 0x0000 represents + * the minimum value and 0xFFFF represents the maximum value. + * If the nDeviceCoords value is 0 this field is not given. + */ +} icNamedColor2; + +/* Profile sequence structure */ +typedef struct { + icSignature deviceMfg; /* Dev Manufacturer */ + icSignature deviceModel; /* Dev Model */ + icUInt64Number attributes; /* Dev attributes */ + icTechnologySignature technology; /* Technology sig */ + icInt8Number data[icAny]; /* Desc text follows */ +/* + * Data that follows is of this form, this is an icInt8Number + * to avoid problems with a compiler generating bad code as + * these arrays are variable in length. + * + * icTextDescription deviceMfgDesc; * Manufacturer text + * icTextDescription modelDesc; * Model text + */ +} icDescStruct; + +/* Profile sequence description */ +typedef struct { + icUInt32Number count; /* Number of descriptions */ + icUInt8Number data[icAny]; /* Array of desc structs */ +} icProfileSequenceDesc; + +/* textDescription */ +typedef struct { + icUInt32Number count; /* Description length */ + icInt8Number data[icAny]; /* Descriptions follow */ +/* + * Data that follows is of this form + * + * icInt8Number desc[count] * NULL terminated ascii string + * icUInt32Number ucLangCode; * UniCode language code + * icUInt32Number ucCount; * UniCode description length + * icInt16Number ucDesc[ucCount];* The UniCode description + * icUInt16Number scCode; * ScriptCode code + * icUInt8Number scCount; * ScriptCode count + * icInt8Number scDesc[67]; * ScriptCode Description + */ +} icTextDescription; + +/* Screening Data */ +typedef struct { + icS15Fixed16Number frequency; /* Frequency */ + icS15Fixed16Number angle; /* Screen angle */ + icSpotShape spotShape; /* Spot Shape encodings below */ +} icScreeningData; + +typedef struct { + icUInt32Number screeningFlag; /* Screening flag */ + icUInt32Number channels; /* Number of channels */ + icScreeningData data[icAny]; /* Array of screening data */ +} icScreening; + +/* Text Data */ +typedef struct { + icInt8Number data[icAny]; /* Variable array of chars */ +} icText; + +/* Structure describing either a UCR or BG curve */ +typedef struct { + icUInt32Number count; /* Curve length */ + icUInt16Number curve[icAny]; /* The array of curve values */ +} icUcrBgCurve; + +/* Under color removal, black generation */ +typedef struct { + icInt8Number data[icAny]; /* The Ucr BG data */ +/* + * Data that follows is of this form, this is a icInt8Number + * to avoid problems with a compiler generating bad code as + * these arrays are variable in length. + * + * icUcrBgCurve ucr; * Ucr curve + * icUcrBgCurve bg; * Bg curve + * icInt8Number string; * UcrBg description + */ +} icUcrBg; + +/* viewingConditionsType */ +typedef struct { + icXYZNumber illuminant; /* In candelas per sq. meter */ + icXYZNumber surround; /* In candelas per sq. meter */ + icIlluminant stdIluminant; /* See icIlluminant defines */ +} icViewingCondition; + +/* CrdInfo type */ +typedef struct { + icUInt32Number count; /* Char count includes NULL */ + icInt8Number desc[icAny]; /* Null terminated string */ +} icCrdInfo; + +/*------------------------------------------------------------------------*/ +/* + * Tag Type definitions + */ + +/* + * Many of the structures contain variable length arrays. This + * is represented by the use of the convention. + * + * type data[icAny]; + */ + +/* The base part of each tag */ +typedef struct { + icTagTypeSignature sig; /* Signature */ + icInt8Number reserved[4]; /* Reserved, set to 0 */ +} icTagBase; + +/* curveType */ +typedef struct { + icTagBase base; /* Signature, "curv" */ + icCurve curve; /* The curve data */ +} icCurveType; + +/* dataType */ +typedef struct { + icTagBase base; /* Signature, "data" */ + icData data; /* The data structure */ +} icDataType; + +/* dateTimeType */ +typedef struct { + icTagBase base; /* Signature, "dtim" */ + icDateTimeNumber date; /* The date */ +} icDateTimeType; + +/* lut16Type */ +typedef struct { + icTagBase base; /* Signature, "mft2" */ + icLut16 lut; /* Lut16 data */ +} icLut16Type; + +/* lut8Type, input & output tables are always 256 bytes in length */ +typedef struct { + icTagBase base; /* Signature, "mft1" */ + icLut8 lut; /* Lut8 data */ +} icLut8Type; + +/* Measurement Type */ +typedef struct { + icTagBase base; /* Signature, "meas" */ + icMeasurement measurement; /* Measurement data */ +} icMeasurementType; + +/* Named color type */ +/* icNamedColor2Type, replaces icNamedColorType */ +typedef struct { + icTagBase base; /* Signature, "ncl2" */ + icNamedColor2 ncolor; /* Named color data */ +} icNamedColor2Type; + +/* Profile sequence description type */ +typedef struct { + icTagBase base; /* Signature, "pseq" */ + icProfileSequenceDesc desc; /* The seq description */ +} icProfileSequenceDescType; + +/* textDescriptionType */ +typedef struct { + icTagBase base; /* Signature, "desc" */ + icTextDescription desc; /* The description */ +} icTextDescriptionType; + +/* s15Fixed16Type */ +typedef struct { + icTagBase base; /* Signature, "sf32" */ + icS15Fixed16Array data; /* Array of values */ +} icS15Fixed16ArrayType; + +typedef struct { + icTagBase base; /* Signature, "scrn" */ + icScreening screen; /* Screening structure */ +} icScreeningType; + +/* sigType */ +typedef struct { + icTagBase base; /* Signature, "sig" */ + icSignature signature; /* The signature data */ +} icSignatureType; + +/* textType */ +typedef struct { + icTagBase base; /* Signature, "text" */ + icText data; /* Variable array of chars */ +} icTextType; + +/* u16Fixed16Type */ +typedef struct { + icTagBase base; /* Signature, "uf32" */ + icU16Fixed16Array data; /* Variable array of values */ +} icU16Fixed16ArrayType; + +/* Under color removal, black generation type */ +typedef struct { + icTagBase base; /* Signature, "bfd " */ + icUcrBg data; /* ucrBg structure */ +} icUcrBgType; + +/* uInt16Type */ +typedef struct { + icTagBase base; /* Signature, "ui16" */ + icUInt16Array data; /* Variable array of values */ +} icUInt16ArrayType; + +/* uInt32Type */ +typedef struct { + icTagBase base; /* Signature, "ui32" */ + icUInt32Array data; /* Variable array of values */ +} icUInt32ArrayType; + +/* uInt64Type */ +typedef struct { + icTagBase base; /* Signature, "ui64" */ + icUInt64Array data; /* Variable array of values */ +} icUInt64ArrayType; + +/* uInt8Type */ +typedef struct { + icTagBase base; /* Signature, "ui08" */ + icUInt8Array data; /* Variable array of values */ +} icUInt8ArrayType; + +/* viewingConditionsType */ +typedef struct { + icTagBase base; /* Signature, "view" */ + icViewingCondition view; /* Viewing conditions */ +} icViewingConditionType; + +/* XYZ Type */ +typedef struct { + icTagBase base; /* Signature, "XYZ" */ + icXYZArray data; /* Variable array of XYZ nums */ +} icXYZType; + +/* CRDInfoType where [0] is the CRD product name count and string and + * [1] -[5] are the rendering intents 0-4 counts and strings + */ +typedef struct { + icTagBase base; /* Signature, "crdi" */ + icCrdInfo info; /* 5 sets of counts & strings */ +}icCrdInfoType; + /* icCrdInfo productName; PS product count/string */ + /* icCrdInfo CRDName0; CRD name for intent 0 */ + /* icCrdInfo CRDName1; CRD name for intent 1 */ + /* icCrdInfo CRDName2; CRD name for intent 2 */ + /* icCrdInfo CRDName3; CRD name for intent 3 */ + +/*------------------------------------------------------------------------*/ + +/* + * Lists of tags, tags, profile header and profile structure + */ + +/* A tag */ +typedef struct { + icTagSignature sig; /* The tag signature */ + icUInt32Number offset; /* Start of tag relative to + * start of header, Spec + * Clause 5 */ + icUInt32Number size; /* Size in bytes */ +} icTag; + +/* A Structure that may be used independently for a list of tags */ +typedef struct { + icUInt32Number count; /* Num tags in the profile */ + icTag tags[icAny]; /* Variable array of tags */ +} icTagList; + +/* The Profile header */ +typedef struct { + icUInt32Number size; /* Prof size in bytes */ + icSignature cmmId; /* CMM for profile */ + icUInt32Number version; /* Format version */ + icProfileClassSignature deviceClass; /* Type of profile */ + icColorSpaceSignature colorSpace; /* Clr space of data */ + icColorSpaceSignature pcs; /* PCS, XYZ or Lab */ + icDateTimeNumber date; /* Creation Date */ + icSignature magic; /* icMagicNumber */ + icPlatformSignature platform; /* Primary Platform */ + icUInt32Number flags; /* Various bits */ + icSignature manufacturer; /* Dev manufacturer */ + icUInt32Number model; /* Dev model number */ + icUInt64Number attributes; /* Device attributes */ + icUInt32Number renderingIntent;/* Rendering intent */ + icXYZNumber illuminant; /* Profile illuminant */ + icSignature creator; /* Profile creator */ + icInt8Number reserved[44]; /* Reserved */ +} icHeader; + +/* + * A profile, + * we can't use icTagList here because its not at the end of the structure + */ +typedef struct { + icHeader header; /* The header */ + icUInt32Number count; /* Num tags in the profile */ + icInt8Number data[icAny]; /* The tagTable and tagData */ +/* + * Data that follows is of the form + * + * icTag tagTable[icAny]; * The tag table + * icInt8Number tagData[icAny]; * The tag data + */ +} icProfile; + +/*------------------------------------------------------------------------*/ +/* Obsolete entries */ + +/* icNamedColor was replaced with icNamedColor2 */ +typedef struct { + icUInt32Number vendorFlag; /* Bottom 16 bits for IC use */ + icUInt32Number count; /* Count of named colors */ + icInt8Number data[icAny]; /* Named color data follows */ +/* + * Data that follows is of this form + * + * icInt8Number prefix[icAny]; * Prefix + * icInt8Number suffix[icAny]; * Suffix + * icInt8Number root1[icAny]; * Root name + * icInt8Number coords1[icAny]; * Color coordinates + * icInt8Number root2[icAny]; * Root name + * icInt8Number coords2[icAny]; * Color coordinates + * : + * : + * Repeat for root name and color coordinates up to (count-1) + */ +} icNamedColor; + +/* icNamedColorType was replaced by icNamedColor2Type */ +typedef struct { + icTagBase base; /* Signature, "ncol" */ + icNamedColor ncolor; /* Named color data */ +} icNamedColorType; + +#endif /* ICC_H */ diff --git a/source/fitz/list-device.c b/source/fitz/list-device.c index fdd5ea18..a8f0bb09 100644 --- a/source/fitz/list-device.c +++ b/source/fitz/list-device.c @@ -30,7 +30,8 @@ typedef enum fz_display_command_e FZ_CMD_END_GROUP, FZ_CMD_BEGIN_TILE, FZ_CMD_END_TILE, - FZ_CMD_RENDER_FLAGS + FZ_CMD_RENDER_FLAGS, + FZ_CMD_DEFAULT_COLORSPACES } fz_display_command; /* The display list is a list of nodes. @@ -132,6 +133,7 @@ struct fz_list_device_s fz_matrix ctm; fz_stroke_state *stroke; fz_colorspace *colorspace; + fz_color_params *color_params; float color[FZ_MAX_COLORS]; fz_rect rect; @@ -144,6 +146,8 @@ struct fz_list_device_s }; enum { ISOLATED = 1, KNOCKOUT = 2 }; +enum { OPM = 1, OP = 2, BP = 3, RI = 4}; + #define SIZE_IN_NODES(t) \ ((t + sizeof(fz_display_node) - 1) / sizeof(fz_display_node)) @@ -530,38 +534,38 @@ fz_append_display_node( switch(node.cs) { case CS_GRAY_0: - writer->colorspace = fz_device_gray(ctx); + writer->colorspace = fz_keep_colorspace(ctx, fz_device_gray(ctx)); writer->color[0] = 0; break; case CS_GRAY_1: - writer->colorspace = fz_device_gray(ctx); + writer->colorspace = fz_keep_colorspace(ctx, fz_device_gray(ctx)); writer->color[0] = 1; break; case CS_RGB_0: writer->color[0] = 0; writer->color[1] = 0; writer->color[2] = 0; - writer->colorspace = fz_device_rgb(ctx); + writer->colorspace = fz_keep_colorspace(ctx, fz_device_rgb(ctx)); break; case CS_RGB_1: writer->color[0] = 1; writer->color[1] = 1; writer->color[2] = 1; - writer->colorspace = fz_device_rgb(ctx); + writer->colorspace = fz_keep_colorspace(ctx, fz_device_rgb(ctx)); break; case CS_CMYK_0: writer->color[0] = 0; writer->color[1] = 0; writer->color[2] = 0; writer->color[3] = 0; - writer->colorspace = fz_device_cmyk(ctx); + writer->colorspace = fz_keep_colorspace(ctx, fz_device_cmyk(ctx)); break; case CS_CMYK_1: writer->color[0] = 0; writer->color[1] = 0; writer->color[2] = 0; writer->color[3] = 1; - writer->colorspace = fz_device_cmyk(ctx); + writer->colorspace = fz_keep_colorspace(ctx, fz_device_cmyk(ctx)); break; default: { @@ -628,9 +632,35 @@ fz_append_display_node( list->len += size; } +/* Pack ri, op, opm, bp into flags upper bits, even/odd in lower bit */ +static int +fz_pack_color_params(const fz_color_params *color_params) +{ + int flags = 0; + + if (color_params == NULL) + return 0; + + flags = color_params->ri << RI; /* 2 bits */ + flags = flags | (color_params->bp << BP); + flags = flags | (color_params->op << OP); + flags = flags | (color_params->opm << OPM); + return flags; +} + +/* unpack ri, op, opm, bp from flags, even/odd in lower bit */ +static void +fz_unpack_color_params(fz_color_params *color_params, int flags) +{ + color_params->ri = (flags >> RI) & 3; + color_params->bp = (flags >> BP) & 1; + color_params->op = (flags >> OP) & 1; + color_params->opm = (flags >> OPM) & 1; +} + static void fz_list_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_rect rect; @@ -639,7 +669,7 @@ fz_list_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even ctx, dev, FZ_CMD_FILL_PATH, - even_odd, /* flags */ + even_odd | fz_pack_color_params(color_params), /* flags */ &rect, path, /* path */ color, @@ -653,7 +683,7 @@ fz_list_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even static void fz_list_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, - const fz_matrix *ctm, fz_colorspace *colorspace, const float *color, float alpha) + const fz_matrix *ctm, fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_rect rect; @@ -662,7 +692,7 @@ fz_list_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const ctx, dev, FZ_CMD_STROKE_PATH, - 0, /* flags */ + fz_pack_color_params(color_params), /* flags */ &rect, path, /* path */ color, @@ -724,7 +754,7 @@ fz_list_clip_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, c static void fz_list_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_rect rect; fz_text *cloned_text = fz_keep_text(ctx, text); @@ -736,7 +766,7 @@ fz_list_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz ctx, dev, FZ_CMD_FILL_TEXT, - 0, /* flags */ + fz_pack_color_params(color_params), /* flags */ &rect, NULL, /* path */ color, /* color */ @@ -756,7 +786,7 @@ fz_list_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz static void fz_list_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_rect rect; fz_text *cloned_text = fz_keep_text(ctx, text); @@ -768,7 +798,7 @@ fz_list_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const ctx, dev, FZ_CMD_STROKE_TEXT, - 0, /* flags */ + fz_pack_color_params(color_params), /* flags */ &rect, NULL, /* path */ color, /* color */ @@ -903,7 +933,7 @@ fz_list_pop_clip(fz_context *ctx, fz_device *dev) } static void -fz_list_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha) +fz_list_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha, const fz_color_params *color_params) { fz_shade *shade2 = fz_keep_shade(ctx, shade); fz_rect rect; @@ -915,7 +945,7 @@ fz_list_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_ma ctx, dev, FZ_CMD_FILL_SHADE, - 0, /* flags */ + fz_pack_color_params(color_params), /* flags */ &rect, NULL, /* path */ NULL, /* color */ @@ -934,7 +964,7 @@ fz_list_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_ma } static void -fz_list_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha) +fz_list_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha, const fz_color_params *color_params) { fz_image *image2 = fz_keep_image(ctx, image); fz_rect rect = fz_unit_rect; @@ -946,7 +976,7 @@ fz_list_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_ma ctx, dev, FZ_CMD_FILL_IMAGE, - 0, /* flags */ + fz_pack_color_params(color_params), /* flags */ &rect, NULL, /* path */ NULL, /* color */ @@ -966,7 +996,7 @@ fz_list_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_ma static void fz_list_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_image *image2 = fz_keep_image(ctx, image); fz_rect rect = fz_unit_rect; @@ -978,7 +1008,7 @@ fz_list_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const ctx, dev, FZ_CMD_FILL_IMAGE_MASK, - 0, /* flags */ + fz_pack_color_params(color_params), /* flags */ &rect, NULL, /* path */ color, @@ -1030,13 +1060,13 @@ fz_list_clip_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const } static void -fz_list_begin_mask(fz_context *ctx, fz_device *dev, const fz_rect *rect, int luminosity, fz_colorspace *colorspace, const float *color) +fz_list_begin_mask(fz_context *ctx, fz_device *dev, const fz_rect *rect, int luminosity, fz_colorspace *colorspace, const float *color, const fz_color_params *color_params) { fz_append_display_node( ctx, dev, FZ_CMD_BEGIN_MASK, - luminosity, /* flags */ + (!!luminosity) | fz_pack_color_params(color_params), /* flags */ rect, NULL, /* path */ color, @@ -1198,6 +1228,27 @@ fz_list_render_flags(fz_context *ctx, fz_device *dev, int set, int clear) } static void +fz_list_set_default_colorspaces(fz_context *ctx, fz_device *dev, fz_default_colorspaces *default_cs) +{ + fz_default_colorspaces *default_cs2 = fz_keep_default_colorspaces(ctx, default_cs); + + fz_append_display_node( + ctx, + dev, + FZ_CMD_DEFAULT_COLORSPACES, + 0, /* flags */ + NULL, + NULL, /* path */ + NULL, /* color */ + NULL, /* colorspace */ + NULL, /* alpha */ + NULL, /* ctm */ + NULL, /* stroke */ + &default_cs2, /* private_data */ + sizeof(default_cs2)); /* private_data_len */ +} + +static void fz_list_drop_device(fz_context *ctx, fz_device *dev) { fz_list_device *writer = (fz_list_device *)dev; @@ -1241,6 +1292,7 @@ fz_new_list_device(fz_context *ctx, fz_display_list *list) dev->super.end_tile = fz_list_end_tile; dev->super.render_flags = fz_list_render_flags; + dev->super.set_default_colorspaces = fz_list_set_default_colorspaces; dev->super.drop_device = fz_list_drop_device; @@ -1342,8 +1394,10 @@ fz_drop_display_list_imp(fz_context *ctx, fz_storable *list_) case FZ_CMD_CLIP_IMAGE_MASK: fz_drop_image(ctx, *(fz_image **)node); break; + case FZ_CMD_DEFAULT_COLORSPACES: + fz_drop_default_colorspaces(ctx, *(fz_default_colorspaces **)node); + break; } - node = next; } fz_free(ctx, list->list); @@ -1405,6 +1459,7 @@ fz_run_display_list(fz_context *ctx, fz_display_list *list, fz_device *dev, cons fz_stroke_state *stroke = NULL; float color[FZ_MAX_COLORS] = { 0 }; fz_colorspace *colorspace = fz_device_gray(ctx); + fz_color_params color_params; fz_rect rect = { 0 }; /* Transformed versions of graphic state entries */ @@ -1423,6 +1478,8 @@ fz_run_display_list(fz_context *ctx, fz_display_list *list, fz_device *dev, cons cookie->progress = 0; } + color_params = *fz_default_color_params(ctx); + node = list->list; node_end = &list->list[list->len]; for (; node != node_end ; node = next_node) @@ -1455,34 +1512,34 @@ fz_run_display_list(fz_context *ctx, fz_display_list *list, fz_device *dev, cons { default: case CS_GRAY_0: - colorspace = fz_device_gray(ctx); + colorspace = fz_keep_colorspace(ctx, fz_device_gray(ctx)); color[0] = 0.0f; break; case CS_GRAY_1: - colorspace = fz_device_gray(ctx); + colorspace = fz_keep_colorspace(ctx, fz_device_gray(ctx)); color[0] = 1.0f; break; case CS_RGB_0: - colorspace = fz_device_rgb(ctx); + colorspace = fz_keep_colorspace(ctx, fz_device_rgb(ctx)); color[0] = 0.0f; color[1] = 0.0f; color[2] = 0.0f; break; case CS_RGB_1: - colorspace = fz_device_rgb(ctx); + colorspace = fz_keep_colorspace(ctx, fz_device_rgb(ctx)); color[0] = 1.0f; color[1] = 1.0f; color[2] = 1.0f; break; case CS_CMYK_0: - colorspace = fz_device_cmyk(ctx); + colorspace = fz_keep_colorspace(ctx, fz_device_cmyk(ctx)); color[0] = 0.0f; color[1] = 0.0f; color[2] = 0.0f; color[3] = 0.0f; break; case CS_CMYK_1: - colorspace = fz_device_cmyk(ctx); + colorspace = fz_keep_colorspace(ctx, fz_device_cmyk(ctx)); color[0] = 0.0f; color[1] = 0.0f; color[2] = 0.0f; @@ -1572,7 +1629,7 @@ fz_run_display_list(fz_context *ctx, fz_display_list *list, fz_device *dev, cons if (tiled || n.cmd == FZ_CMD_BEGIN_TILE || n.cmd == FZ_CMD_END_TILE || - n.cmd == FZ_CMD_RENDER_FLAGS) + n.cmd == FZ_CMD_RENDER_FLAGS || n.cmd == FZ_CMD_DEFAULT_COLORSPACES) { empty = 0; } @@ -1619,10 +1676,12 @@ visible: switch (n.cmd) { case FZ_CMD_FILL_PATH: - fz_fill_path(ctx, dev, path, n.flags, &trans_ctm, colorspace, color, alpha); + fz_unpack_color_params(&color_params, n.flags); + fz_fill_path(ctx, dev, path, n.flags & 1, &trans_ctm, colorspace, color, alpha, &color_params); break; case FZ_CMD_STROKE_PATH: - fz_stroke_path(ctx, dev, path, stroke, &trans_ctm, colorspace, color, alpha); + fz_unpack_color_params(&color_params, n.flags); + fz_stroke_path(ctx, dev, path, stroke, &trans_ctm, colorspace, color, alpha, &color_params); break; case FZ_CMD_CLIP_PATH: fz_clip_path(ctx, dev, path, n.flags, &trans_ctm, &trans_rect); @@ -1631,10 +1690,12 @@ visible: fz_clip_stroke_path(ctx, dev, path, stroke, &trans_ctm, &trans_rect); break; case FZ_CMD_FILL_TEXT: - fz_fill_text(ctx, dev, *(fz_text **)node, &trans_ctm, colorspace, color, alpha); + fz_unpack_color_params(&color_params, n.flags); + fz_fill_text(ctx, dev, *(fz_text **)node, &trans_ctm, colorspace, color, alpha, &color_params); break; case FZ_CMD_STROKE_TEXT: - fz_stroke_text(ctx, dev, *(fz_text **)node, stroke, &trans_ctm, colorspace, color, alpha); + fz_unpack_color_params(&color_params, n.flags); + fz_stroke_text(ctx, dev, *(fz_text **)node, stroke, &trans_ctm, colorspace, color, alpha, &color_params); break; case FZ_CMD_CLIP_TEXT: fz_clip_text(ctx, dev, *(fz_text **)node, &trans_ctm, &trans_rect); @@ -1646,13 +1707,16 @@ visible: fz_ignore_text(ctx, dev, *(fz_text **)node, &trans_ctm); break; case FZ_CMD_FILL_SHADE: - fz_fill_shade(ctx, dev, *(fz_shade **)node, &trans_ctm, alpha); + fz_unpack_color_params(&color_params, n.flags); + fz_fill_shade(ctx, dev, *(fz_shade **)node, &trans_ctm, alpha, &color_params); break; case FZ_CMD_FILL_IMAGE: - fz_fill_image(ctx, dev, *(fz_image **)node, &trans_ctm, alpha); + fz_unpack_color_params(&color_params, n.flags); + fz_fill_image(ctx, dev, *(fz_image **)node, &trans_ctm, alpha, &color_params); break; case FZ_CMD_FILL_IMAGE_MASK: - fz_fill_image_mask(ctx, dev, *(fz_image **)node, &trans_ctm, colorspace, color, alpha); + fz_unpack_color_params(&color_params, n.flags); + fz_fill_image_mask(ctx, dev, *(fz_image **)node, &trans_ctm, colorspace, color, alpha, &color_params); break; case FZ_CMD_CLIP_IMAGE_MASK: fz_clip_image_mask(ctx, dev, *(fz_image **)node, &trans_ctm, &trans_rect); @@ -1661,7 +1725,8 @@ visible: fz_pop_clip(ctx, dev); break; case FZ_CMD_BEGIN_MASK: - fz_begin_mask(ctx, dev, &trans_rect, n.flags, colorspace, color); + fz_unpack_color_params(&color_params, n.flags); + fz_begin_mask(ctx, dev, &trans_rect, n.flags, colorspace, color, &color_params); break; case FZ_CMD_END_MASK: fz_end_mask(ctx, dev); @@ -1694,6 +1759,9 @@ visible: else if (n.flags == 1) fz_render_flags(ctx, dev, FZ_DEVFLAG_GRIDFIT_AS_TILED, 0); break; + case FZ_CMD_DEFAULT_COLORSPACES: + fz_set_default_colorspaces(ctx, dev, *(fz_default_colorspaces **)node); + break; } } fz_catch(ctx) diff --git a/source/fitz/output-cbz.c b/source/fitz/output-cbz.c index c7ab5805..55c28e77 100644 --- a/source/fitz/output-cbz.c +++ b/source/fitz/output-cbz.c @@ -40,7 +40,7 @@ cbz_end_page(fz_context *ctx, fz_document_writer *wri_, fz_device *dev) fz_snprintf(name, sizeof name, "p%04d.png", wri->count); - buffer = fz_new_buffer_from_pixmap_as_png(ctx, wri->pixmap); + buffer = fz_new_buffer_from_pixmap_as_png(ctx, wri->pixmap, NULL); fz_try(ctx) fz_write_zip_entry(ctx, wri->zip, name, buffer, 0); fz_always(ctx) diff --git a/source/fitz/output-pcl.c b/source/fitz/output-pcl.c index 2c680f8f..7f676cf5 100644 --- a/source/fitz/output-pcl.c +++ b/source/fitz/output-pcl.c @@ -691,7 +691,7 @@ fz_write_pixmap_as_pcl(fz_context *ctx, fz_output *out, const fz_pixmap *pixmap, writer = fz_new_color_pcl_band_writer(ctx, out, pcl); fz_try(ctx) { - fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0); + fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0, pixmap->colorspace); fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples); } fz_always(ctx) @@ -713,7 +713,7 @@ typedef struct color_pcl_band_writer_s } color_pcl_band_writer; static void -color_pcl_write_header(fz_context *ctx, fz_band_writer *writer_) +color_pcl_write_header(fz_context *ctx, fz_band_writer *writer_, const fz_colorspace *cs) { color_pcl_band_writer *writer = (color_pcl_band_writer *)writer_; fz_output *out = writer->super.out; @@ -1078,7 +1078,7 @@ fz_write_bitmap_as_pcl(fz_context *ctx, fz_output *out, const fz_bitmap *bitmap, writer = fz_new_mono_pcl_band_writer(ctx, out, pcl); fz_try(ctx) { - fz_write_header(ctx, writer, bitmap->w, bitmap->h, 1, 0, bitmap->xres, bitmap->yres, 0); + fz_write_header(ctx, writer, bitmap->w, bitmap->h, 1, 0, bitmap->xres, bitmap->yres, 0, NULL); fz_write_band(ctx, writer, bitmap->stride, bitmap->h, bitmap->samples); } fz_always(ctx) @@ -1099,7 +1099,7 @@ typedef struct mono_pcl_band_writer_s } mono_pcl_band_writer; static void -mono_pcl_write_header(fz_context *ctx, fz_band_writer *writer_) +mono_pcl_write_header(fz_context *ctx, fz_band_writer *writer_, const fz_colorspace *cs) { mono_pcl_band_writer *writer = (mono_pcl_band_writer *)writer_; fz_output *out = writer->super.out; diff --git a/source/fitz/output-png.c b/source/fitz/output-png.c index c8972b25..4a325959 100644 --- a/source/fitz/output-png.c +++ b/source/fitz/output-png.c @@ -33,7 +33,7 @@ fz_save_pixmap_as_png(fz_context *ctx, fz_pixmap *pixmap, const char *filename) fz_try(ctx) { writer = fz_new_png_band_writer(ctx, out); - fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0); + fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0, pixmap->colorspace); fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples); } fz_always(ctx) @@ -59,7 +59,7 @@ fz_write_pixmap_as_png(fz_context *ctx, fz_output *out, const fz_pixmap *pixmap) fz_try(ctx) { - fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0); + fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0, pixmap->colorspace); fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples); } fz_always(ctx) @@ -82,7 +82,59 @@ typedef struct png_band_writer_s } png_band_writer; static void -png_write_header(fz_context *ctx, fz_band_writer *writer_) +png_write_icc(fz_context *ctx, png_band_writer *writer, const fz_colorspace *cs) +{ + fz_output *out = writer->super.out; + int size; + fz_buffer *buffer = fz_icc_data_from_icc_colorspace(ctx, cs); + unsigned char *data; + unsigned char *chunk, *pos, *cdata; + uLong bound; + uLongf csize; + uLong long_size; + int t; + + long_size = (uLong)fz_buffer_storage(ctx, buffer, &data); + + if (!data) + return; + + /* Deflate the profile */ + bound = compressBound(long_size); + cdata = fz_malloc(ctx, bound); + csize = (uLongf)bound; + t = compress(cdata, &csize, data, long_size); + if (t != Z_OK) + { + fz_free(ctx, cdata); + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot deflate icc buffer"); + } + size = csize + strlen("MuPDF Profile") + 2; + + fz_var(cdata); + + fz_try(ctx) + { + chunk = fz_calloc(ctx, size, 1); + pos = chunk; + memcpy(chunk, "MuPDF Profile", strlen("MuPDF Profile")); + pos += strlen("MuPDF Profile") + 2; + memcpy(pos, cdata, csize); + putchunk(ctx, out, "iCCP", chunk, size); + } + fz_always(ctx) + { + fz_free(ctx, cdata); + fz_free(ctx, chunk); + } + fz_catch(ctx) + { + /* Nothing */ + } +} + +static void +png_write_header(fz_context *ctx, fz_band_writer *writer_, const fz_colorspace *cs) { png_band_writer *writer = (png_band_writer *)(void *)writer_; fz_output *out = writer->super.out; @@ -116,6 +168,8 @@ png_write_header(fz_context *ctx, fz_band_writer *writer_) fz_write_data(ctx, out, pngsig, 8); putchunk(ctx, out, "IHDR", head, 13); + + png_write_icc(ctx, writer, cs); } static void @@ -240,7 +294,7 @@ fz_band_writer *fz_new_png_band_writer(fz_context *ctx, fz_output *out) * drop pix early in the case where we have to convert, potentially saving * us having to have 2 copies of the pixmap and a buffer open at once. */ static fz_buffer * -png_from_pixmap(fz_context *ctx, fz_pixmap *pix, int drop) +png_from_pixmap(fz_context *ctx, fz_pixmap *pix, const fz_color_params *color_params, int drop) { fz_buffer *buf = NULL; fz_output *out; @@ -253,11 +307,14 @@ png_from_pixmap(fz_context *ctx, fz_pixmap *pix, int drop) if (pix->w == 0 || pix->h == 0) return NULL; + if (color_params == NULL) + color_params = fz_default_color_params(ctx); + fz_try(ctx) { if (pix->colorspace && pix->colorspace != fz_device_gray(ctx) && pix->colorspace != fz_device_rgb(ctx)) { - pix2 = fz_convert_pixmap(ctx, pix, fz_device_rgb(ctx), 1); + pix2 = fz_convert_pixmap(ctx, pix, fz_device_rgb(ctx), NULL, NULL, color_params, 1); if (drop) fz_drop_pixmap(ctx, pix); pix = pix2; @@ -280,22 +337,20 @@ png_from_pixmap(fz_context *ctx, fz_pixmap *pix, int drop) } fz_buffer * -fz_new_buffer_from_image_as_png(fz_context *ctx, fz_image *image) +fz_new_buffer_from_image_as_png(fz_context *ctx, fz_image *image, const fz_color_params *color_params) { fz_pixmap *pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, NULL, NULL); - fz_buffer *buf = NULL; - - fz_var(buf); + fz_buffer *buf; fz_try(ctx) - buf = png_from_pixmap(ctx, pix, 1); + buf = png_from_pixmap(ctx, pix, color_params, 1); fz_catch(ctx) fz_rethrow(ctx); return buf; } fz_buffer * -fz_new_buffer_from_pixmap_as_png(fz_context *ctx, fz_pixmap *pix) +fz_new_buffer_from_pixmap_as_png(fz_context *ctx, fz_pixmap *pix, const fz_color_params *color_params) { - return png_from_pixmap(ctx, pix, 0); + return png_from_pixmap(ctx, pix, color_params, 0); } diff --git a/source/fitz/output-pnm.c b/source/fitz/output-pnm.c index 455a5358..496d8ea0 100644 --- a/source/fitz/output-pnm.c +++ b/source/fitz/output-pnm.c @@ -4,7 +4,7 @@ * Write pixmap to PNM file (without alpha channel) */ static void -pnm_write_header(fz_context *ctx, fz_band_writer *writer) +pnm_write_header(fz_context *ctx, fz_band_writer *writer, const fz_colorspace *cs) { fz_output *out = writer->out; int w = writer->w; @@ -123,7 +123,7 @@ void fz_write_pixmap_as_pnm(fz_context *ctx, fz_output *out, fz_pixmap *pixmap) { fz_band_writer *writer = fz_new_pnm_band_writer(ctx, out); - fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0); + fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0, pixmap->colorspace); fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples); fz_drop_band_writer(ctx, writer); } @@ -139,7 +139,7 @@ fz_save_pixmap_as_pnm(fz_context *ctx, fz_pixmap *pixmap, const char *filename) fz_try(ctx) { writer = fz_new_pnm_band_writer(ctx, out); - fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0); + fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0, pixmap->colorspace); fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples); } fz_always(ctx) @@ -156,7 +156,7 @@ fz_save_pixmap_as_pnm(fz_context *ctx, fz_pixmap *pixmap, const char *filename) */ static void -pam_write_header(fz_context *ctx, fz_band_writer *writer) +pam_write_header(fz_context *ctx, fz_band_writer *writer, const fz_colorspace *cs) { fz_output *out = writer->out; int w = writer->w; @@ -220,7 +220,7 @@ void fz_write_pixmap_as_pam(fz_context *ctx, fz_output *out, fz_pixmap *pixmap) { fz_band_writer *writer = fz_new_pam_band_writer(ctx, out); - fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0); + fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0, pixmap->colorspace); fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples); fz_drop_band_writer(ctx, writer); } @@ -236,7 +236,7 @@ fz_save_pixmap_as_pam(fz_context *ctx, fz_pixmap *pixmap, const char *filename) fz_try(ctx) { writer = fz_new_pam_band_writer(ctx, out); - fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0); + fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0, pixmap->colorspace); fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples); } fz_always(ctx) diff --git a/source/fitz/output-ps.c b/source/fitz/output-ps.c index 90be4367..a3d354ad 100644 --- a/source/fitz/output-ps.c +++ b/source/fitz/output-ps.c @@ -41,7 +41,7 @@ void fz_write_ps_file_trailer(fz_context *ctx, fz_output *out, int pages) } static void -ps_write_header(fz_context *ctx, fz_band_writer *writer_) +ps_write_header(fz_context *ctx, fz_band_writer *writer_, const fz_colorspace *cs) { ps_band_writer *writer = (ps_band_writer *)writer_; fz_output *out = writer->super.out; @@ -147,7 +147,7 @@ void fz_write_pixmap_as_ps(fz_context *ctx, fz_output *out, const fz_pixmap *pix fz_try(ctx) { - fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0); + fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0, pixmap->colorspace); fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples); } fz_always(ctx) diff --git a/source/fitz/output-pwg.c b/source/fitz/output-pwg.c index 1b5f3812..f36c3a2b 100644 --- a/source/fitz/output-pwg.c +++ b/source/fitz/output-pwg.c @@ -102,7 +102,7 @@ fz_write_pixmap_as_pwg_page(fz_context *ctx, fz_output *out, const fz_pixmap *pi fz_try(ctx) { - fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0); + fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0, pixmap->colorspace); fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples); } fz_always(ctx) @@ -118,7 +118,7 @@ fz_write_bitmap_as_pwg_page(fz_context *ctx, fz_output *out, const fz_bitmap *bi fz_try(ctx) { - fz_write_header(ctx, writer, bitmap->w, bitmap->h, bitmap->n, 0, bitmap->xres, bitmap->yres, 0); + fz_write_header(ctx, writer, bitmap->w, bitmap->h, bitmap->n, 0, bitmap->xres, bitmap->yres, 0, NULL); fz_write_band(ctx, writer, bitmap->stride, bitmap->h, bitmap->samples); } fz_always(ctx) @@ -174,7 +174,7 @@ fz_save_bitmap_as_pwg(fz_context *ctx, fz_bitmap *bitmap, char *filename, int ap } static void -pwg_write_mono_header(fz_context *ctx, fz_band_writer *writer_) +pwg_write_mono_header(fz_context *ctx, fz_band_writer *writer_, const fz_colorspace *cs) { pwg_band_writer *writer = (pwg_band_writer *)writer_; @@ -272,7 +272,7 @@ fz_band_writer *fz_new_mono_pwg_band_writer(fz_context *ctx, fz_output *out, con } static void -pwg_write_header(fz_context *ctx, fz_band_writer *writer_) +pwg_write_header(fz_context *ctx, fz_band_writer *writer_, const fz_colorspace *cs) { pwg_band_writer *writer = (pwg_band_writer *)writer_; int n = writer->super.n; diff --git a/source/fitz/output-tga.c b/source/fitz/output-tga.c index 712f82fe..f576dd74 100644 --- a/source/fitz/output-tga.c +++ b/source/fitz/output-tga.c @@ -69,7 +69,7 @@ fz_write_pixmap_as_tga(fz_context *ctx, fz_output *out, fz_pixmap *pixmap) fz_try(ctx) { - fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0); + fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0, pixmap->colorspace); fz_write_band(ctx, writer, -pixmap->stride, pixmap->h, pixmap->samples + pixmap->stride * (pixmap->h-1)); } fz_always(ctx) @@ -79,7 +79,7 @@ fz_write_pixmap_as_tga(fz_context *ctx, fz_output *out, fz_pixmap *pixmap) } static void -tga_write_header(fz_context *ctx, fz_band_writer *writer_) +tga_write_header(fz_context *ctx, fz_band_writer *writer_, const fz_colorspace *cs) { tga_band_writer *writer = (tga_band_writer *)writer_; fz_output *out = writer->super.out; diff --git a/source/fitz/output.c b/source/fitz/output.c index cbcc4209..3240be12 100644 --- a/source/fitz/output.c +++ b/source/fitz/output.c @@ -392,7 +392,7 @@ fz_band_writer *fz_new_band_writer_of_size(fz_context *ctx, size_t size, fz_outp return writer; } -void fz_write_header(fz_context *ctx, fz_band_writer *writer, int w, int h, int n, int alpha, int xres, int yres, int pagenum) +void fz_write_header(fz_context *ctx, fz_band_writer *writer, int w, int h, int n, int alpha, int xres, int yres, int pagenum, const fz_colorspace *cs) { if (writer == NULL || writer->band == NULL) return; @@ -404,7 +404,7 @@ void fz_write_header(fz_context *ctx, fz_band_writer *writer, int w, int h, int writer->yres = yres; writer->pagenum = pagenum; writer->line = 0; - writer->header(ctx, writer); + writer->header(ctx, writer, cs); } void fz_write_band(fz_context *ctx, fz_band_writer *writer, int stride, int band_height, const unsigned char *samples) diff --git a/source/fitz/pixmap.c b/source/fitz/pixmap.c index ab7d068f..d89889fd 100644 --- a/source/fitz/pixmap.c +++ b/source/fitz/pixmap.c @@ -722,7 +722,7 @@ fz_ensure_pixmap_is_additive(fz_context *ctx, fz_pixmap *pix) { if (fz_colorspace_is_subtractive(ctx, pix->colorspace)) { - fz_pixmap *rgb = fz_convert_pixmap(ctx, pix, fz_device_rgb(ctx), 1); + fz_pixmap *rgb = fz_convert_pixmap(ctx, pix, fz_device_rgb(ctx), NULL, NULL, NULL/* FIXME */, 1); fz_drop_pixmap(ctx, pix); return rgb; } @@ -881,13 +881,16 @@ fz_pixmap_size(fz_context *ctx, fz_pixmap * pix) } fz_pixmap * -fz_convert_pixmap(fz_context *ctx, fz_pixmap *pix, fz_colorspace *ds, int keep_alpha) +fz_convert_pixmap(fz_context *ctx, fz_pixmap *pix, fz_colorspace *ds, fz_colorspace *prf, fz_default_colorspaces *default_cs, const fz_color_params *color_params, int keep_alpha) { fz_pixmap *cvt; if (!ds && !keep_alpha) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot both throw away and keep alpha"); + if (color_params == NULL) + color_params = fz_default_color_params(ctx); + cvt = fz_new_pixmap(ctx, ds, pix->w, pix->h, keep_alpha && pix->alpha); cvt->xres = pix->xres; @@ -899,7 +902,7 @@ fz_convert_pixmap(fz_context *ctx, fz_pixmap *pix, fz_colorspace *ds, int keep_a fz_try(ctx) { fz_pixmap_converter *pc = fz_lookup_pixmap_converter(ctx, ds, pix->colorspace); - pc(ctx, cvt, pix); + pc(ctx, cvt, pix, prf, default_cs, color_params); } fz_catch(ctx) { diff --git a/source/fitz/stext-device.c b/source/fitz/stext-device.c index 0213a945..f942babe 100644 --- a/source/fitz/stext-device.c +++ b/source/fitz/stext-device.c @@ -867,7 +867,7 @@ fz_stext_extract(fz_context *ctx, fz_stext_device *dev, fz_text_span *span, cons static void fz_stext_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_stext_device *tdev = (fz_stext_device*)dev; fz_stext_style *style; @@ -881,7 +881,7 @@ fz_stext_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const f static void fz_stext_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_stext_device *tdev = (fz_stext_device*)dev; fz_stext_style *style; @@ -934,7 +934,7 @@ fz_stext_ignore_text(fz_context *ctx, fz_device *dev, const fz_text *text, const static void fz_stext_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *img, const fz_matrix *ctm, - fz_colorspace *cspace, const float *color, float alpha) + fz_colorspace *cspace, const float *color, float alpha, const fz_color_params *color_params) { fz_stext_device *tdev = (fz_stext_device*)dev; fz_stext_page *page = tdev->page; @@ -969,9 +969,9 @@ fz_stext_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *img, const f } static void -fz_stext_fill_image(fz_context *ctx, fz_device *dev, fz_image *img, const fz_matrix *ctm, float alpha) +fz_stext_fill_image(fz_context *ctx, fz_device *dev, fz_image *img, const fz_matrix *ctm, float alpha, const fz_color_params *color_params) { - fz_stext_fill_image_mask(ctx, dev, img, ctm, NULL, NULL, alpha); + fz_stext_fill_image_mask(ctx, dev, img, ctm, NULL, NULL, alpha, color_params); } static int diff --git a/source/fitz/stext-output.c b/source/fitz/stext-output.c index 49ef2ea5..a247fc8d 100644 --- a/source/fitz/stext-output.c +++ b/source/fitz/stext-output.c @@ -240,7 +240,7 @@ fz_print_stext_page_html(fz_context *ctx, fz_output *out, fz_stext_page *page) break; default: { - fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, image->image); + fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, image->image, NULL); fz_write_printf(ctx, out, "image/png;base64,"); send_data_base64_stext(ctx, out, buf); fz_drop_buffer(ctx, buf); diff --git a/source/fitz/svg-device.c b/source/fitz/svg-device.c index 7c6c8826..1bd6987f 100644 --- a/source/fitz/svg-device.c +++ b/source/fitz/svg-device.c @@ -200,14 +200,14 @@ svg_dev_stroke_state(fz_context *ctx, svg_device *sdev, const fz_stroke_state *s } static unsigned int -svg_hex_color(fz_context *ctx, fz_colorspace *colorspace, const float *color) +svg_hex_color(fz_context *ctx, fz_colorspace *colorspace, const float *color, const fz_color_params *color_params) { float rgb[3]; int r, g, b; if (colorspace != fz_device_rgb(ctx)) { - fz_convert_color(ctx, fz_device_rgb(ctx), rgb, colorspace, color); + fz_convert_color(ctx, color_params, NULL, fz_device_rgb(ctx), rgb, colorspace, color); color = rgb; } @@ -219,12 +219,12 @@ svg_hex_color(fz_context *ctx, fz_colorspace *colorspace, const float *color) } static void -svg_dev_fill_color(fz_context *ctx, svg_device *sdev, fz_colorspace *colorspace, const float *color, float alpha) +svg_dev_fill_color(fz_context *ctx, svg_device *sdev, fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_output *out = sdev->out; if (colorspace) { - int rgb = svg_hex_color(ctx, colorspace, color); + int rgb = svg_hex_color(ctx, colorspace, color, color_params); if (rgb != 0) /* black is the default value */ fz_write_printf(ctx, out, " fill=\"#%06x\"", rgb); } @@ -235,11 +235,11 @@ svg_dev_fill_color(fz_context *ctx, svg_device *sdev, fz_colorspace *colorspace, } static void -svg_dev_stroke_color(fz_context *ctx, svg_device *sdev, fz_colorspace *colorspace, const float *color, float alpha) +svg_dev_stroke_color(fz_context *ctx, svg_device *sdev, fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_output *out = sdev->out; if (colorspace) - fz_write_printf(ctx, out, " fill=\"none\" stroke=\"#%06x\"", svg_hex_color(ctx, colorspace, color)); + fz_write_printf(ctx, out, " fill=\"none\" stroke=\"#%06x\"", svg_hex_color(ctx, colorspace, color, color_params)); else fz_write_printf(ctx, out, " fill=\"none\" stroke=\"none\""); if (alpha != 1) @@ -505,7 +505,7 @@ svg_dev_text_span_as_paths_defs(fz_context *ctx, fz_device *dev, fz_text_span *s static void svg_dev_text_span_as_paths_fill(fz_context *ctx, fz_device *dev, const fz_text_span *span, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha, font *fnt) + fz_colorspace *colorspace, const float *color, float alpha, font *fnt, const fz_color_params *color_params) { svg_device *sdev = (svg_device*)dev; fz_output *out = sdev->out; @@ -538,7 +538,7 @@ svg_dev_text_span_as_paths_fill(fz_context *ctx, fz_device *dev, const fz_text_s fz_concat(&local_trm2, &shift, &local_trm2); fz_write_printf(ctx, out, "<use xlink:href=\"#font_%x_%x\"", fnt->id, gid); svg_dev_ctm(ctx, sdev, &local_trm2); - svg_dev_fill_color(ctx, sdev, colorspace, color, alpha); + svg_dev_fill_color(ctx, sdev, colorspace, color, alpha, color_params); fz_write_printf(ctx, out, "/>\n"); } } @@ -546,7 +546,7 @@ svg_dev_text_span_as_paths_fill(fz_context *ctx, fz_device *dev, const fz_text_s static void svg_dev_text_span_as_paths_stroke(fz_context *ctx, fz_device *dev, const fz_text_span *span, const fz_stroke_state *stroke, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha, font *fnt) + fz_colorspace *colorspace, const float *color, float alpha, font *fnt, const fz_color_params *color_params) { svg_device *sdev = (svg_device*)dev; fz_output *out = sdev->out; @@ -579,7 +579,7 @@ svg_dev_text_span_as_paths_stroke(fz_context *ctx, fz_device *dev, const fz_text fz_write_printf(ctx, out, "<use xlink:href=\"#font_%x_%x\"", fnt->id, gid); svg_dev_stroke_state(ctx, sdev, stroke, &local_trm2); svg_dev_ctm(ctx, sdev, &local_trm2); - svg_dev_stroke_color(ctx, sdev, colorspace, color, alpha); + svg_dev_stroke_color(ctx, sdev, colorspace, color, alpha, color_params); fz_write_printf(ctx, out, "/>\n"); } } @@ -588,7 +588,7 @@ svg_dev_text_span_as_paths_stroke(fz_context *ctx, fz_device *dev, const fz_text static void svg_dev_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { svg_device *sdev = (svg_device*)dev; fz_output *out = sdev->out; @@ -596,7 +596,7 @@ svg_dev_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even fz_write_printf(ctx, out, "<path"); svg_dev_ctm(ctx, sdev, ctm); svg_dev_path(ctx, sdev, path); - svg_dev_fill_color(ctx, sdev, colorspace, color, alpha); + svg_dev_fill_color(ctx, sdev, colorspace, color, alpha, color_params); if (even_odd) fz_write_printf(ctx, out, " fill-rule=\"evenodd\""); fz_write_printf(ctx, out, "/>\n"); @@ -604,7 +604,7 @@ svg_dev_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even static void svg_dev_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { svg_device *sdev = (svg_device*)dev; fz_output *out = sdev->out; @@ -612,7 +612,7 @@ svg_dev_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_write_printf(ctx, out, "<path"); svg_dev_ctm(ctx, sdev, ctm); svg_dev_stroke_state(ctx, sdev, stroke, &fz_identity); - svg_dev_stroke_color(ctx, sdev, colorspace, color, alpha); + svg_dev_stroke_color(ctx, sdev, colorspace, color, alpha, color_params); svg_dev_path(ctx, sdev, path); fz_write_printf(ctx, out, "/>\n"); } @@ -655,7 +655,7 @@ svg_dev_clip_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, c fz_write_printf(ctx, out, "<path"); svg_dev_ctm(ctx, sdev, ctm); svg_dev_stroke_state(ctx, sdev, stroke, &fz_identity); - svg_dev_stroke_color(ctx, sdev, fz_device_rgb(ctx), white, 1); + svg_dev_stroke_color(ctx, sdev, fz_device_rgb(ctx), white, 1, NULL); svg_dev_path(ctx, sdev, path); fz_write_printf(ctx, out, "/>\n</mask>\n"); out = end_def(ctx, sdev); @@ -664,7 +664,7 @@ svg_dev_clip_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, c static void svg_dev_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { svg_device *sdev = (svg_device*)dev; fz_output *out = sdev->out; @@ -676,7 +676,7 @@ svg_dev_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz for (span = text->head; span; span = span->next) { fz_write_printf(ctx, out, "<text"); - svg_dev_fill_color(ctx, sdev, colorspace, color, alpha); + svg_dev_fill_color(ctx, sdev, colorspace, color, alpha, color_params); svg_dev_text_span(ctx, sdev, ctm, span); } } @@ -685,14 +685,14 @@ svg_dev_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz for (span = text->head; span; span = span->next) { fnt = svg_dev_text_span_as_paths_defs(ctx, dev, span, ctm); - svg_dev_text_span_as_paths_fill(ctx, dev, span, ctm, colorspace, color, alpha, fnt); + svg_dev_text_span_as_paths_fill(ctx, dev, span, ctm, colorspace, color, alpha, fnt, color_params); } } } static void svg_dev_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { svg_device *sdev = (svg_device*)dev; fz_output *out = sdev->out; @@ -704,7 +704,7 @@ svg_dev_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const for (span = text->head; span; span = span->next) { fz_write_printf(ctx, out, "<text"); - svg_dev_fill_color(ctx, sdev, colorspace, color, alpha); + svg_dev_fill_color(ctx, sdev, colorspace, color, alpha, color_params); svg_dev_text_span(ctx, sdev, ctm, span); } } @@ -713,7 +713,7 @@ svg_dev_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const for (span = text->head; span; span = span->next) { fnt = svg_dev_text_span_as_paths_defs(ctx, dev, span, ctm); - svg_dev_text_span_as_paths_stroke(ctx, dev, span, stroke, ctm, colorspace, color, alpha, fnt); + svg_dev_text_span_as_paths_stroke(ctx, dev, span, stroke, ctm, colorspace, color, alpha, fnt, color_params); } } } @@ -741,7 +741,7 @@ svg_dev_clip_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz for (span = text->head; span; span = span->next) { fz_write_printf(ctx, out, "<text"); - svg_dev_fill_color(ctx, sdev, fz_device_rgb(ctx), white, 1); + svg_dev_fill_color(ctx, sdev, fz_device_rgb(ctx), white, 1, NULL); svg_dev_text_span(ctx, sdev, ctm, span); } } @@ -750,7 +750,7 @@ svg_dev_clip_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz for (span = text->head; span; span = span->next) { fnt = svg_dev_text_span_as_paths_defs(ctx, dev, span, ctm); - svg_dev_text_span_as_paths_fill(ctx, dev, span, ctm, fz_device_rgb(ctx), white, 1.0f, fnt); + svg_dev_text_span_as_paths_fill(ctx, dev, span, ctm, fz_device_rgb(ctx), white, 1.0f, fnt, NULL); } } fz_write_printf(ctx, out, "</mask>\n"); @@ -782,7 +782,7 @@ svg_dev_clip_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, c { fz_write_printf(ctx, out, "<text"); svg_dev_stroke_state(ctx, sdev, stroke, &fz_identity); - svg_dev_stroke_color(ctx, sdev, fz_device_rgb(ctx), white, 1); + svg_dev_stroke_color(ctx, sdev, fz_device_rgb(ctx), white, 1, NULL); svg_dev_text_span(ctx, sdev, ctm, span); } } @@ -791,7 +791,7 @@ svg_dev_clip_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, c for (span = text->head; span; span = span->next) { fnt = svg_dev_text_span_as_paths_defs(ctx, dev, span, ctm); - svg_dev_text_span_as_paths_stroke(ctx, dev, span, stroke, ctm, fz_device_rgb(ctx), white, 1.0f, fnt); + svg_dev_text_span_as_paths_stroke(ctx, dev, span, stroke, ctm, fz_device_rgb(ctx), white, 1.0f, fnt, NULL); } } fz_write_printf(ctx, out, "</mask>\n"); @@ -813,7 +813,7 @@ svg_dev_ignore_text(fz_context *ctx, fz_device *dev, const fz_text *text, const for (span = text->head; span; span = span->next) { fz_write_printf(ctx, out, "<text"); - svg_dev_fill_color(ctx, sdev, fz_device_rgb(ctx), black, 0.0f); + svg_dev_fill_color(ctx, sdev, fz_device_rgb(ctx), black, 0.0f, NULL); svg_dev_text_span(ctx, sdev, ctm, span); } } @@ -863,7 +863,7 @@ send_data_base64(fz_context *ctx, fz_output *out, fz_buffer *buffer) * have conniptions. We therefore have an option that is * made to avoid this (reuse-images=no). */ static void -svg_send_image(fz_context *ctx, svg_device *sdev, fz_image *img) +svg_send_image(fz_context *ctx, svg_device *sdev, fz_image *img, const fz_color_params *color_params) { fz_output *out = sdev->out; fz_compressed_buffer *buffer; @@ -917,7 +917,7 @@ svg_send_image(fz_context *ctx, svg_device *sdev, fz_image *img) /*@fallthough@*/ default: { - fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, img); + fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, img, color_params); fz_write_printf(ctx, out, "image/png;base64,"); send_data_base64(ctx, out, buf); fz_drop_buffer(ctx, buf); @@ -941,7 +941,7 @@ svg_send_image(fz_context *ctx, svg_device *sdev, fz_image *img) } static void -svg_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha) +svg_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha, const fz_color_params *color_params) { svg_device *sdev = (svg_device*)dev; fz_output *out = sdev->out; @@ -958,12 +958,12 @@ svg_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_ma fz_write_printf(ctx, out, " opacity=\"%g\"", alpha); svg_dev_ctm(ctx, sdev, &local_ctm); fz_write_printf(ctx, out, ">\n"); - svg_send_image(ctx, sdev, image); + svg_send_image(ctx, sdev, image, color_params); fz_write_printf(ctx, out, "</g>\n"); } static void -svg_dev_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha) +svg_dev_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha, const fz_color_params *color_params) { svg_device *sdev = (svg_device*)dev; fz_output *out = sdev->out; @@ -985,8 +985,8 @@ svg_dev_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_ma fz_try(ctx) { - fz_paint_shade(ctx, shade, ctm, pix, &bbox); - buf = fz_new_buffer_from_pixmap_as_png(ctx, pix); + fz_paint_shade(ctx, shade, ctm, pix, NULL, color_params, &bbox); + buf = fz_new_buffer_from_pixmap_as_png(ctx, pix, color_params); if (alpha != 1.0f) fz_write_printf(ctx, out, "<g opacity=\"%g\">\n", alpha); fz_write_printf(ctx, out, "<image x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" xlink:href=\"data:image/png;base64,", pix->x, pix->y, pix->w, pix->h); @@ -1008,7 +1008,7 @@ svg_dev_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_ma static void svg_dev_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { svg_device *sdev = (svg_device*)dev; fz_output *out; @@ -1022,11 +1022,11 @@ svg_dev_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const fz_concat(&local_ctm, &scale, ctm); out = start_def(ctx, sdev); fz_write_printf(ctx, out, "<mask id=\"ma%d\">\n", mask); - svg_send_image(ctx, sdev, image); + svg_send_image(ctx, sdev, image, color_params); fz_write_printf(ctx, out, "</mask>\n"); out = end_def(ctx, sdev); fz_write_printf(ctx, out, "<rect x=\"0\" y=\"0\" width=\"%d\" height=\"%d\"", image->w, image->h); - svg_dev_fill_color(ctx, sdev, colorspace, color, alpha); + svg_dev_fill_color(ctx, sdev, colorspace, color, alpha, color_params); svg_dev_ctm(ctx, sdev, &local_ctm); fz_write_printf(ctx, out, " mask=\"url(#ma%d)\"/>\n", mask); } @@ -1048,7 +1048,7 @@ svg_dev_clip_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const fz_write_printf(ctx, out, "<mask id=\"ma%d\">\n<g", mask); svg_dev_ctm(ctx, sdev, &local_ctm); fz_write_printf(ctx, out, ">\n"); - svg_send_image(ctx, sdev, image); + svg_send_image(ctx, sdev, image, NULL/* FIXME */); fz_write_printf(ctx, out, "</g>\n</mask>\n"); out = end_def(ctx, sdev); fz_write_printf(ctx, out, "<g mask=\"url(#ma%d)\">\n", mask); @@ -1065,7 +1065,7 @@ svg_dev_pop_clip(fz_context *ctx, fz_device *dev) } static void -svg_dev_begin_mask(fz_context *ctx, fz_device *dev, const fz_rect *bbox, int luminosity, fz_colorspace *colorspace, const float *color) +svg_dev_begin_mask(fz_context *ctx, fz_device *dev, const fz_rect *bbox, int luminosity, fz_colorspace *colorspace, const float *color, const fz_color_params *color_params) { svg_device *sdev = (svg_device*)dev; fz_output *out; diff --git a/source/fitz/test-device.c b/source/fitz/test-device.c index 38dc331b..aa6631a8 100644 --- a/source/fitz/test-device.c +++ b/source/fitz/test-device.c @@ -29,7 +29,7 @@ is_rgb_color_u8(int threshold_u8, int r, int g, int b) } static void -fz_test_color(fz_context *ctx, fz_test_device *t, fz_colorspace *colorspace, const float *color) +fz_test_color(fz_context *ctx, fz_test_device *t, fz_colorspace *colorspace, const float *color, const fz_color_params *color_params) { if (!*t->is_color && colorspace && colorspace != fz_device_gray(ctx)) { @@ -46,7 +46,7 @@ fz_test_color(fz_context *ctx, fz_test_device *t, fz_colorspace *colorspace, con else { float rgb[3]; - fz_convert_color(ctx, fz_device_rgb(ctx), rgb, colorspace, color); + fz_convert_color(ctx, color_params, NULL, fz_device_rgb(ctx), rgb, colorspace, color); if (is_rgb_color(t->threshold, rgb[0], rgb[1], rgb[2])) { *t->is_color = 2; @@ -62,68 +62,73 @@ fz_test_color(fz_context *ctx, fz_test_device *t, fz_colorspace *colorspace, con static void fz_test_fill_path(fz_context *ctx, fz_device *dev_, const fz_path *path, int even_odd, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_test_device *dev = (fz_test_device*)dev_; - if (dev->resolved == 0) + if (dev->resolved == 0 && alpha != 0.0f) { - if (alpha != 0.0f) - fz_test_color(ctx, dev, colorspace, color); + if (color_params == NULL) + color_params = fz_default_color_params(ctx); + fz_test_color(ctx, dev, colorspace, color, color_params); } if (dev->passthrough) - fz_fill_path(ctx, dev->passthrough, path, even_odd, ctm, colorspace, color, alpha); + fz_fill_path(ctx, dev->passthrough, path, even_odd, ctm, colorspace, color, alpha, color_params); } static void fz_test_stroke_path(fz_context *ctx, fz_device *dev_, const fz_path *path, const fz_stroke_state *stroke, - const fz_matrix *ctm, fz_colorspace *colorspace, const float *color, float alpha) + const fz_matrix *ctm, fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_test_device *dev = (fz_test_device*)dev_; - if (dev->resolved == 0) + if (dev->resolved == 0 && alpha != 0.0f) { - if (alpha != 0.0f) - fz_test_color(ctx, dev, colorspace, color); + if (color_params == NULL) + color_params = fz_default_color_params(ctx); + fz_test_color(ctx, dev, colorspace, color, color_params); } if (dev->passthrough) - fz_stroke_path(ctx, dev->passthrough, path, stroke, ctm, colorspace, color, alpha); + fz_stroke_path(ctx, dev->passthrough, path, stroke, ctm, colorspace, color, alpha, color_params); } static void fz_test_fill_text(fz_context *ctx, fz_device *dev_, const fz_text *text, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_test_device *dev = (fz_test_device*)dev_; - if (dev->resolved == 0) + if (dev->resolved == 0 && alpha != 0.0f) { - if (alpha != 0.0f) - fz_test_color(ctx, dev, colorspace, color); + if (color_params == NULL) + color_params = fz_default_color_params(ctx); + fz_test_color(ctx, dev, colorspace, color, color_params); } if (dev->passthrough) - fz_fill_text(ctx, dev->passthrough, text, ctm, colorspace, color, alpha); + fz_fill_text(ctx, dev->passthrough, text, ctm, colorspace, color, alpha, color_params); } static void fz_test_stroke_text(fz_context *ctx, fz_device *dev_, const fz_text *text, const fz_stroke_state *stroke, - const fz_matrix *ctm, fz_colorspace *colorspace, const float *color, float alpha) + const fz_matrix *ctm, fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_test_device *dev = (fz_test_device*)dev_; - if (dev->resolved == 0) + if (dev->resolved == 0 && alpha != 0.0f) { - if (alpha != 0.0f) - fz_test_color(ctx, dev, colorspace, color); + if (color_params == NULL) + color_params = fz_default_color_params(ctx); + fz_test_color(ctx, dev, colorspace, color, color_params); } if (dev->passthrough) - fz_stroke_text(ctx, dev->passthrough, text, stroke, ctm, colorspace, color, alpha); + fz_stroke_text(ctx, dev->passthrough, text, stroke, ctm, colorspace, color, alpha, color_params); } struct shadearg { fz_test_device *dev; fz_shade *shade; + const fz_color_params *color_params; }; static void @@ -133,14 +138,17 @@ prepare_vertex(fz_context *ctx, void *arg_, fz_vertex *v, const float *color) fz_test_device *dev = arg->dev; fz_shade *shade = arg->shade; if (!shade->use_function) - fz_test_color(ctx, dev, shade->colorspace, color); + fz_test_color(ctx, dev, shade->colorspace, color, arg->color_params); } static void -fz_test_fill_shade(fz_context *ctx, fz_device *dev_, fz_shade *shade, const fz_matrix *ctm, float alpha) +fz_test_fill_shade(fz_context *ctx, fz_device *dev_, fz_shade *shade, const fz_matrix *ctm, float alpha, const fz_color_params *color_params) { fz_test_device *dev = (fz_test_device*)dev_; + if (color_params == NULL) + color_params = fz_default_color_params(ctx); + if (dev->resolved == 0) { if ((dev->options & FZ_TEST_OPT_SHADINGS) == 0) @@ -161,23 +169,24 @@ fz_test_fill_shade(fz_context *ctx, fz_device *dev_, fz_shade *shade, const fz_m { int i; for (i = 0; i < 256; i++) - fz_test_color(ctx, dev, shade->colorspace, shade->function[i]); + fz_test_color(ctx, dev, shade->colorspace, shade->function[i], color_params); } else { struct shadearg arg; arg.dev = dev; arg.shade = shade; + arg.color_params = color_params; fz_process_shade(ctx, shade, ctm, prepare_vertex, NULL, &arg); } } } if (dev->passthrough) - fz_fill_shade(ctx, dev->passthrough, shade, ctm, alpha); + fz_fill_shade(ctx, dev->passthrough, shade, ctm, alpha, color_params); } static void -fz_test_fill_image(fz_context *ctx, fz_device *dev_, fz_image *image, const fz_matrix *ctm, float alpha) +fz_test_fill_image(fz_context *ctx, fz_device *dev_, fz_image *image, const fz_matrix *ctm, float alpha, const fz_color_params *color_params) { fz_test_device *dev = (fz_test_device*)dev_; @@ -231,7 +240,7 @@ fz_test_fill_image(fz_context *ctx, fz_device *dev_, fz_image *image, const fz_m fz_color_converter cc; unsigned int n = (unsigned int)image->n; - fz_init_cached_color_converter(ctx, &cc, fz_device_rgb(ctx), image->colorspace); + fz_init_cached_color_converter(ctx, &cc, NULL, fz_device_rgb(ctx), image->colorspace, color_params); for (i = 0; i < count; i++) { float cs[FZ_MAX_COLORS]; @@ -299,7 +308,7 @@ fz_test_fill_image(fz_context *ctx, fz_device *dev_, fz_image *image, const fz_m fz_color_converter cc; unsigned int n = (unsigned int)pix->n-1; - fz_init_cached_color_converter(ctx, &cc, fz_device_rgb(ctx), pix->colorspace); + fz_init_cached_color_converter(ctx, &cc, NULL, fz_device_rgb(ctx), pix->colorspace, color_params); while (h--) { for (i = 0; i < count; i++) @@ -336,22 +345,25 @@ fz_test_fill_image(fz_context *ctx, fz_device *dev_, fz_image *image, const fz_m break; } if (dev->passthrough) - fz_fill_image(ctx, dev->passthrough, image, ctm, alpha); + fz_fill_image(ctx, dev->passthrough, image, ctm, alpha, color_params); } static void fz_test_fill_image_mask(fz_context *ctx, fz_device *dev_, fz_image *image, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_test_device *dev = (fz_test_device*)dev_; + if (color_params == NULL) + color_params = fz_default_color_params(ctx); + if (dev->resolved == 0) { /* We assume that at least some of the image pixels are non-zero */ - fz_test_color(ctx, dev, colorspace, color); + fz_test_color(ctx, dev, colorspace, color, color_params); } if (dev->passthrough) - fz_fill_image_mask(ctx, dev->passthrough, image, ctm, colorspace, color, alpha); + fz_fill_image_mask(ctx, dev->passthrough, image, ctm, colorspace, color, alpha, color_params); } static void @@ -411,11 +423,11 @@ fz_test_pop_clip(fz_context *ctx, fz_device *dev_) } static void -fz_test_begin_mask(fz_context *ctx, fz_device *dev_, const fz_rect *rect, int luminosity, fz_colorspace *cs, const float *bc) +fz_test_begin_mask(fz_context *ctx, fz_device *dev_, const fz_rect *rect, int luminosity, fz_colorspace *cs, const float *bc, const fz_color_params *color_params) { fz_test_device *dev = (fz_test_device*)dev_; - fz_begin_mask(ctx, dev->passthrough, rect, luminosity, cs, bc); + fz_begin_mask(ctx, dev->passthrough, rect, luminosity, cs, bc, color_params); } static void diff --git a/source/fitz/trace-device.c b/source/fitz/trace-device.c index c2f8b4c2..db08aa52 100644 --- a/source/fitz/trace-device.c +++ b/source/fitz/trace-device.c @@ -117,7 +117,7 @@ fz_trace_path(fz_context *ctx, fz_output *out, const fz_path *path) static void fz_trace_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_output *out = ((fz_trace_device*)dev)->out; fz_write_printf(ctx, out, "<fill_path"); @@ -134,7 +134,7 @@ fz_trace_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int eve static void fz_trace_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_output *out = ((fz_trace_device*)dev)->out; int i; @@ -190,7 +190,7 @@ fz_trace_clip_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, static void fz_trace_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_output *out = ((fz_trace_device*)dev)->out; fz_write_printf(ctx, out, "<fill_text"); @@ -203,7 +203,7 @@ fz_trace_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const f static void fz_trace_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_output *out = ((fz_trace_device*)dev)->out; fz_write_printf(ctx, out, "<stroke_text"); @@ -248,7 +248,7 @@ fz_trace_ignore_text(fz_context *ctx, fz_device *dev, const fz_text *text, const } static void -fz_trace_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha) +fz_trace_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha, const fz_color_params *color_params) { fz_output *out = ((fz_trace_device*)dev)->out; fz_write_printf(ctx, out, "<fill_image alpha=\"%g\"", alpha); @@ -258,7 +258,7 @@ fz_trace_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_m } static void -fz_trace_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha) +fz_trace_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha, const fz_color_params *color_params) { fz_output *out = ((fz_trace_device*)dev)->out; fz_write_printf(ctx, out, "<fill_shade alpha=\"%g\"", alpha); @@ -268,7 +268,7 @@ fz_trace_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_m static void fz_trace_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { fz_output *out = ((fz_trace_device*)dev)->out; fz_write_printf(ctx, out, "<fill_image_mask"); @@ -296,7 +296,7 @@ fz_trace_pop_clip(fz_context *ctx, fz_device *dev) } static void -fz_trace_begin_mask(fz_context *ctx, fz_device *dev, const fz_rect *bbox, int luminosity, fz_colorspace *colorspace, const float *color) +fz_trace_begin_mask(fz_context *ctx, fz_device *dev, const fz_rect *bbox, int luminosity, fz_colorspace *colorspace, const float *color, const fz_color_params *color_params) { fz_output *out = ((fz_trace_device*)dev)->out; fz_write_printf(ctx, out, "<mask bbox=\"%g %g %g %g\" s=\"%s\"", diff --git a/source/gprf/gprf-doc.c b/source/gprf/gprf-doc.c index b0111fd4..34a293bd 100644 --- a/source/gprf/gprf-doc.c +++ b/source/gprf/gprf-doc.c @@ -827,7 +827,7 @@ gprf_run_page(fz_context *ctx, fz_page *page_, fz_device *dev, const fz_matrix * local.e = x * scale; local.f = y * scale; fz_concat(&local, &local, ctm); - fz_fill_image(ctx, dev, page->tiles[i++], &local, 1.0f); + fz_fill_image(ctx, dev, page->tiles[i++], &local, 1.0f, NULL); } } fz_render_flags(ctx, dev, 0, FZ_DEVFLAG_GRIDFIT_AS_TILED); diff --git a/source/html/html-layout.c b/source/html/html-layout.c index 6c3600ca..1ad37f8f 100644 --- a/source/html/html-layout.c +++ b/source/html/html-layout.c @@ -1536,7 +1536,7 @@ static void draw_flow_box(fz_context *ctx, fz_html_box *box, float page_top, flo { if (text) { - fz_fill_text(ctx, dev, text, ctm, fz_device_rgb(ctx), prev_color, 1); + fz_fill_text(ctx, dev, text, ctm, fz_device_rgb(ctx), prev_color, 1, NULL); fz_drop_text(ctx, text); text = NULL; } @@ -1623,7 +1623,7 @@ static void draw_flow_box(fz_context *ctx, fz_html_box *box, float page_top, flo { if (text) { - fz_fill_text(ctx, dev, text, ctm, fz_device_rgb(ctx), color, 1); + fz_fill_text(ctx, dev, text, ctm, fz_device_rgb(ctx), color, 1, NULL); fz_drop_text(ctx, text); text = NULL; } @@ -1632,14 +1632,14 @@ static void draw_flow_box(fz_context *ctx, fz_html_box *box, float page_top, flo fz_matrix local_ctm = *ctm; fz_pre_translate(&local_ctm, node->x, node->y - page_top); fz_pre_scale(&local_ctm, node->w, node->h); - fz_fill_image(ctx, dev, node->content.image, &local_ctm, 1); + fz_fill_image(ctx, dev, node->content.image, &local_ctm, 1, NULL); } } } if (text) { - fz_fill_text(ctx, dev, text, ctm, fz_device_rgb(ctx), color, 1); + fz_fill_text(ctx, dev, text, ctm, fz_device_rgb(ctx), color, 1, NULL); fz_drop_text(ctx, text); text = NULL; } @@ -1663,7 +1663,7 @@ static void draw_rect(fz_context *ctx, fz_device *dev, const fz_matrix *ctm, flo rgb[1] = color.g / 255.0f; rgb[2] = color.b / 255.0f; - fz_fill_path(ctx, dev, path, 0, ctm, fz_device_rgb(ctx), rgb, color.a / 255.0f); + fz_fill_path(ctx, dev, path, 0, ctm, fz_device_rgb(ctx), rgb, color.a / 255.0f, NULL); fz_drop_path(ctx, path); } @@ -1821,7 +1821,7 @@ static void draw_list_mark(fz_context *ctx, fz_html_box *box, float page_top, fl color[1] = box->style.color.g / 255.0f; color[2] = box->style.color.b / 255.0f; - fz_fill_text(ctx, dev, text, ctm, fz_device_rgb(ctx), color, 1); + fz_fill_text(ctx, dev, text, ctm, fz_device_rgb(ctx), color, 1, NULL); } fz_always(ctx) fz_drop_text(ctx, text); diff --git a/source/pdf/pdf-appearance.c b/source/pdf/pdf-appearance.c index fbd50434..da351de9 100644 --- a/source/pdf/pdf-appearance.c +++ b/source/pdf/pdf-appearance.c @@ -1727,7 +1727,7 @@ void pdf_set_markup_appearance(fz_context *ctx, pdf_document *doc, pdf_annot *an if (stroke) { // assert(path) - fz_stroke_path(ctx, dev, path, stroke, &page_ctm, fz_device_rgb(ctx), color, alpha); + fz_stroke_path(ctx, dev, path, stroke, &page_ctm, fz_device_rgb(ctx), color, alpha, NULL); fz_drop_stroke_state(ctx, stroke); stroke = NULL; fz_drop_path(ctx, path); @@ -1745,7 +1745,7 @@ void pdf_set_markup_appearance(fz_context *ctx, pdf_document *doc, pdf_annot *an if (stroke) { - fz_stroke_path(ctx, dev, path, stroke, &page_ctm, fz_device_rgb(ctx), color, alpha); + fz_stroke_path(ctx, dev, path, stroke, &page_ctm, fz_device_rgb(ctx), color, alpha, NULL); } fz_close_device(ctx, dev); @@ -1860,7 +1860,7 @@ void pdf_update_ink_appearance(fz_context *ctx, pdf_document *doc, pdf_annot *an } cs = pdf_to_color(ctx, doc, pdf_dict_get(ctx, annot->obj, PDF_NAME_C), color); - fz_stroke_path(ctx, dev, path, stroke, &page_ctm, cs, color, 1.0f); + fz_stroke_path(ctx, dev, path, stroke, &page_ctm, cs, color, 1.0f, NULL); fz_expand_rect(&rect, width); /* @@ -2115,15 +2115,15 @@ void pdf_update_text_annot_appearance(fz_context *ctx, pdf_document *doc, pdf_an center_rect_within_rect(&bounds, &rect, &tm); fz_concat(&tm, &tm, &page_ctm); cs = fz_device_rgb(ctx); - fz_fill_path(ctx, dev, path, 0, &tm, cs, yellow, 1.0f); - fz_stroke_path(ctx, dev, path, stroke, &tm, cs, black, 1.0f); + fz_fill_path(ctx, dev, path, 0, &tm, cs, yellow, 1.0f, NULL); + fz_stroke_path(ctx, dev, path, stroke, &tm, cs, black, 1.0f, NULL); fz_drop_path(ctx, path); path = NULL; path = fz_new_path(ctx); draw_speech_bubble(ctx, path); - fz_fill_path(ctx, dev, path, 0, &tm, cs, white, 1.0f); - fz_stroke_path(ctx, dev, path, stroke, &tm, cs, black, 1.0f); + fz_fill_path(ctx, dev, path, 0, &tm, cs, white, 1.0f, NULL); + fz_stroke_path(ctx, dev, path, stroke, &tm, cs, black, 1.0f, NULL); fz_close_device(ctx, dev); @@ -2198,7 +2198,7 @@ void pdf_update_free_text_annot_appearance(fz_context *ctx, pdf_document *doc, p dlist = fz_new_display_list(ctx, NULL); dev = fz_new_list_device(ctx, dlist); - fz_fill_text(ctx, dev, text, &page_ctm, cs, font_rec.da_rec.col, 1.0f); + fz_fill_text(ctx, dev, text, &page_ctm, cs, font_rec.da_rec.col, 1.0f, NULL); fz_close_device(ctx, dev); fz_transform_rect(&rect, &page_ctm); @@ -2384,7 +2384,7 @@ void pdf_set_signature_appearance(fz_context *ctx, pdf_document *doc, pdf_annot center_rect_within_rect(&logo_bounds, &rect, &logo_tm); fz_concat(&logo_tm, &logo_tm, &page_ctm); cs = fz_device_rgb(ctx); - fz_fill_path(ctx, dev, path, 0, &logo_tm, cs, logo_color, 1.0f); + fz_fill_path(ctx, dev, path, 0, &logo_tm, cs, logo_color, 1.0f, NULL); fz_drop_colorspace(ctx, cs); cs = NULL; @@ -2400,7 +2400,7 @@ void pdf_set_signature_appearance(fz_context *ctx, pdf_document *doc, pdf_annot /* Display the name in the left-hand half of the form field */ rect.x1 = (rect.x0 + rect.x1)/2.0f; text = fit_text(ctx, &font_rec, name, &rect); - fz_fill_text(ctx, dev, text, &page_ctm, cs, font_rec.da_rec.col, 1.0f); + fz_fill_text(ctx, dev, text, &page_ctm, cs, font_rec.da_rec.col, 1.0f, NULL); fz_drop_text(ctx, text); text = NULL; @@ -2413,7 +2413,7 @@ void pdf_set_signature_appearance(fz_context *ctx, pdf_document *doc, pdf_annot rect = annot_rect; rect.x0 = (rect.x0 + rect.x1)/2.0f; text = fit_text(ctx, &font_rec, fz_string_from_buffer(ctx, fzbuf), &rect); - fz_fill_text(ctx, dev, text, &page_ctm, cs, font_rec.da_rec.col, 1.0f); + fz_fill_text(ctx, dev, text, &page_ctm, cs, font_rec.da_rec.col, 1.0f, NULL); fz_close_device(ctx, dev); diff --git a/source/pdf/pdf-colorspace.c b/source/pdf/pdf-colorspace.c index 2a862c65..9b840cc9 100644 --- a/source/pdf/pdf-colorspace.c +++ b/source/pdf/pdf-colorspace.c @@ -6,52 +6,119 @@ #include <string.h> /* ICCBased */ - static fz_colorspace * -load_icc_based(fz_context *ctx, pdf_obj *dict) +load_icc_based(fz_context *ctx, pdf_obj *dict, int alt) { int n; pdf_obj *obj; + fz_buffer *buffer = NULL; + fz_colorspace *cs = NULL; + fz_colorspace *cs_alt = NULL; + fz_colorspace_clamp_fn *alt_lab_clamping = NULL; + + fz_var(cs); + fz_var(cs_alt); + fz_var(buffer); + + /* + alt => "If ICC unreadable/unsupported, then return the + alternate instead". + + Regardless of whether alt is set or not, we DO read the + alternate space, because we need to know whether it's a + LAB space or not to affect our clamping. We just might + not return it. + */ + fz_try(ctx) + { + obj = pdf_dict_get(ctx, dict, PDF_NAME_Alternate); + if (obj) + { + cs_alt = pdf_load_colorspace(ctx, obj); + if (fz_colorspace_is_lab_icc(ctx, cs_alt)) + alt_lab_clamping = cs_alt->clamp; + } + } + fz_catch(ctx) + { + fz_drop_colorspace(ctx, cs_alt); + cs_alt = NULL; + } + + /* If we're not going to be allowed to return it, drop it! */ + if (!alt) + { + fz_drop_colorspace(ctx, cs_alt); + cs_alt = NULL; + } n = pdf_to_int(ctx, pdf_dict_get(ctx, dict, PDF_NAME_N)); - obj = pdf_dict_get(ctx, dict, PDF_NAME_Alternate); - if (obj) + fz_try(ctx) { - fz_colorspace *cs_alt = NULL; - - fz_try(ctx) + if (fz_get_cmm_engine(ctx)) { - cs_alt = pdf_load_colorspace(ctx, obj); - if (cs_alt->n != n) - { - fz_drop_colorspace(ctx, cs_alt); - fz_throw(ctx, FZ_ERROR_SYNTAX, "ICCBased /Alternate colorspace must have %d components", n); - } + buffer = pdf_load_stream(ctx, dict); + cs = fz_new_icc_colorspace(ctx, 0, n, buffer, NULL); } - fz_catch(ctx) + } + fz_always(ctx) + fz_drop_buffer(ctx, buffer); + fz_catch(ctx) + { + if (!alt) + fz_rethrow(ctx); + } + + if (cs) + { + if (n != 1 && n != 3 && n != 4) { - cs_alt = NULL; + fz_drop_colorspace(ctx, cs); + fz_throw(ctx, FZ_ERROR_GENERIC, "ICC Based must have 1, 3 or 4 components"); } - if (cs_alt) - return cs_alt; + /* Override the clamping if the alternate was LAB */ + if (alt_lab_clamping) + cs->clamp = alt_lab_clamping; + fz_drop_colorspace(ctx, cs_alt); + return cs; + } + + /* Failed to load the ICC profile - either because it was broken, + * or because we aren't in an ICC workflow. If we aren't allowed + * to return the alternate, then that's all she wrote. */ + if (!alt) + fz_throw(ctx, FZ_ERROR_GENERIC, "Unable to read ICC workflow"); + + /* If we have an alternate we are allowed to use, return that. */ + if (cs_alt) + { + if (n != 1 && n != 3 && n != 4) + { + fz_drop_colorspace(ctx, cs_alt); + fz_throw(ctx, FZ_ERROR_GENERIC, "ICC Based must have 1, 3 or 4 components"); + } + return cs_alt; } switch (n) { - case 1: return fz_device_gray(ctx); - case 3: return fz_device_rgb(ctx); - case 4: return fz_device_cmyk(ctx); + case 1: + cs = fz_device_gray(ctx); + break; + case 3: + cs = fz_device_rgb(ctx); + break; + case 4: + cs = fz_device_cmyk(ctx); + break; + default: fz_throw(ctx, FZ_ERROR_SYNTAX, "ICCBased must have 1, 3 or 4 components"); } - fz_throw(ctx, FZ_ERROR_SYNTAX, "ICCBased must have 1, 3 or 4 components"); + return cs; } -/* Lab */ - -/* Separation and DeviceN */ - struct separation { fz_colorspace *base; @@ -59,12 +126,19 @@ struct separation }; static void +separation_to_alt(fz_context *ctx, fz_colorspace *cs, const float *color, float *alt) +{ + struct separation *sep = cs->data; + pdf_eval_function(ctx, sep->tint, color, cs->n, alt, sep->base->n); +} + +static void separation_to_rgb(fz_context *ctx, fz_colorspace *cs, const float *color, float *rgb) { struct separation *sep = cs->data; float alt[FZ_MAX_COLORS]; pdf_eval_function(ctx, sep->tint, color, cs->n, alt, sep->base->n); - fz_convert_color(ctx, fz_device_rgb(ctx), rgb, sep->base, alt); + fz_convert_color(ctx, fz_default_color_params(ctx), NULL, fz_device_rgb(ctx), rgb, sep->base, alt); } static void @@ -77,6 +151,14 @@ free_separation(fz_context *ctx, fz_colorspace *cs) } static fz_colorspace * +base_separation(const fz_colorspace *cs) +{ + struct separation *sep = cs->data; + + return sep->base; +} + +static fz_colorspace * load_separation(fz_context *ctx, pdf_obj *array) { fz_colorspace *cs; @@ -108,10 +190,11 @@ load_separation(fz_context *ctx, pdf_obj *array) * "cannot load tint function (%d 0 R)", pdf_to_num(ctx, tintobj) */ sep = fz_malloc_struct(ctx, struct separation); - sep->base = base; + sep->base = fz_keep_colorspace(ctx, base); /* We drop it during the sep free... */ sep->tint = tint; - cs = fz_new_colorspace(ctx, n == 1 ? "Separation" : "DeviceN", n, 1, separation_to_rgb, NULL, free_separation, sep, + cs = fz_new_colorspace(ctx, n == 1 ? "Separation" : "DeviceN", 0, n, 1, + fz_colorspace_is_icc(ctx, fz_device_rgb(ctx)) ? separation_to_alt : separation_to_rgb, NULL, base_separation, NULL, free_separation, sep, sizeof(struct separation) + (base ? base->size : 0) + pdf_function_size(ctx, tint)); } fz_catch(ctx) @@ -128,7 +211,7 @@ load_separation(fz_context *ctx, pdf_obj *array) int pdf_is_tint_colorspace(fz_context *ctx, fz_colorspace *cs) { - return fz_colorspace_is(ctx, cs, separation_to_rgb); + return cs && cs->free_data == free_separation; } /* Indexed */ @@ -201,6 +284,104 @@ load_indexed(fz_context *ctx, pdf_obj *array) return cs; } +static void +pdf_load_cal_common(fz_context *ctx, pdf_obj *dict, float *wp, float *bp, float *gamma) +{ + pdf_obj *obj; + int i; + + obj = pdf_dict_get(ctx, dict, PDF_NAME_WhitePoint); + if (pdf_array_len(ctx, obj) != 3) + fz_throw(ctx, FZ_ERROR_SYNTAX, "WhitePoint must be a 3-element array"); + + for (i = 0; i < 3; i++) + { + wp[i] = pdf_to_real(ctx, pdf_array_get(ctx, obj, i)); + if (wp[i] < 0) + fz_throw(ctx, FZ_ERROR_SYNTAX, "WhitePoint numbers must be positive"); + } + if (wp[1] != 1) + fz_throw(ctx, FZ_ERROR_SYNTAX, "WhitePoint Yw must be 1.0"); + + obj = pdf_dict_get(ctx, dict, PDF_NAME_BlackPoint); + if (pdf_array_len(ctx, obj) == 3) + { + for (i = 0; i < 3; i++) + { + bp[i] = pdf_to_real(ctx, pdf_array_get(ctx, obj, i)); + if (bp[i] < 0) + fz_throw(ctx, FZ_ERROR_SYNTAX, "BlackPoint numbers must be positive"); + } + } + + obj = pdf_dict_get(ctx, dict, PDF_NAME_Gamma); + if (pdf_is_number(ctx, obj)) + { + gamma[0] = pdf_to_real(ctx, obj); + gamma[1] = gamma[2]; + if (gamma[0] <= 0) + fz_throw(ctx, FZ_ERROR_SYNTAX, "Gamma must be greater than zero"); + } + else if (pdf_array_len(ctx, obj) == 3) + { + for (i = 0; i < 3; i++) + { + gamma[i] = pdf_to_real(ctx, pdf_array_get(ctx, obj, i)); + if (gamma[i] <= 0) + fz_throw(ctx, FZ_ERROR_SYNTAX, "Gamma must be greater than zero"); + } + } +} + +static fz_colorspace * +pdf_load_cal_gray(fz_context *ctx, pdf_obj *dict) +{ + float wp[3]; + float bp[3] = { 0, 0, 0 }; + float gamma[3] = { 1, 1, 1 }; + + if (dict == NULL) + return fz_device_gray(ctx); + + fz_try(ctx) + { + pdf_load_cal_common(ctx, dict, wp, bp, gamma); + gamma[2] = gamma[1] = gamma[0]; + } + fz_catch(ctx) + return fz_device_gray(ctx); + return fz_new_cal_colorspace(ctx, wp, bp, gamma, NULL); +} + +static fz_colorspace * +pdf_load_cal_rgb(fz_context *ctx, pdf_obj *dict) +{ + pdf_obj *obj; + float matrix[9] = { 1, 0, 0, 0, 1, 0, 0, 0, 1 }; + float wp[3]; + float bp[3] = { 0, 0, 0 }; + float gamma[3] = { 1, 1, 1 }; + int i; + + if (dict == NULL) + return fz_device_rgb(ctx); + + fz_try(ctx) + { + pdf_load_cal_common(ctx, dict, wp, bp, gamma); + + obj = pdf_dict_get(ctx, dict, PDF_NAME_Matrix); + if (pdf_array_len(ctx, obj) == 9) + { + for (i = 0; i < 9; i++) + matrix[i] = pdf_to_real(ctx, pdf_array_get(ctx, obj, i)); + } + } + fz_catch(ctx) + return fz_device_rgb(ctx); + return fz_new_cal_colorspace(ctx, wp, bp, gamma, matrix); +} + /* Parse and create colorspace from PDF object */ static fz_colorspace * @@ -249,9 +430,19 @@ pdf_load_colorspace_imp(fz_context *ctx, pdf_obj *obj) else if (pdf_name_eq(ctx, name, PDF_NAME_DeviceCMYK)) return fz_device_cmyk(ctx); else if (pdf_name_eq(ctx, name, PDF_NAME_CalGray)) - return fz_device_gray(ctx); + { + if (fz_get_cmm_engine(ctx)) + return pdf_load_cal_gray(ctx, pdf_array_get(ctx, obj, 1)); + else + return fz_device_gray(ctx); + } else if (pdf_name_eq(ctx, name, PDF_NAME_CalRGB)) - return fz_device_rgb(ctx); + { + if (fz_get_cmm_engine(ctx)) + return pdf_load_cal_rgb(ctx, pdf_array_get(ctx, obj, 1)); + else + return fz_device_rgb(ctx); + } else if (pdf_name_eq(ctx, name, PDF_NAME_CalCMYK)) return fz_device_cmyk(ctx); else if (pdf_name_eq(ctx, name, PDF_NAME_Lab)) @@ -264,7 +455,7 @@ pdf_load_colorspace_imp(fz_context *ctx, pdf_obj *obj) if (pdf_mark_obj(ctx, obj)) fz_throw(ctx, FZ_ERROR_SYNTAX, "recursive colorspace"); if (pdf_name_eq(ctx, name, PDF_NAME_ICCBased)) - cs = load_icc_based(ctx, pdf_array_get(ctx, obj, 1)); + cs = load_icc_based(ctx, pdf_array_get(ctx, obj, 1), 1); else if (pdf_name_eq(ctx, name, PDF_NAME_Indexed)) cs = load_indexed(ctx, obj); @@ -305,6 +496,16 @@ pdf_load_colorspace_imp(fz_context *ctx, pdf_obj *obj) } } + /* We have seen files where /DefaultRGB is specified as 1 0 R, + * and 1 0 obj << /Length 3144 /Alternate /DeviceRGB /N 3 >> + * stream ...iccprofile... endstream endobj. + * This *should* be [ /ICCBased 1 0 R ], but Acrobat seems to + * handle it, so do our best. */ + else if (pdf_is_dict(ctx, obj)) + { + return load_icc_based(ctx, obj, 1); + } + fz_throw(ctx, FZ_ERROR_SYNTAX, "could not parse color space (%d 0 R)", pdf_to_num(ctx, obj)); } @@ -324,3 +525,48 @@ pdf_load_colorspace(fz_context *ctx, pdf_obj *obj) return cs; } + +static fz_colorspace * +pdf_load_output_intent(fz_context *ctx, pdf_document *doc) +{ + pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME_Root); + pdf_obj *intents = pdf_dict_get(ctx, root, PDF_NAME_OutputIntents); + pdf_obj *intent_dict; + pdf_obj *dest_profile; + fz_colorspace *cs = NULL; + + /* An array of intents */ + if (!intents) + return NULL; + + /* For now, always just use the first intent. I have never even seen a file + * with multiple intents but it could happen */ + intent_dict = pdf_array_get(ctx, intents, 0); + if (!intent_dict) + return NULL; + dest_profile = pdf_dict_get(ctx, intent_dict, PDF_NAME_DestOutputProfile); + if (!dest_profile) + return NULL; + + fz_var(cs); + + fz_try(ctx) + cs = load_icc_based(ctx, dest_profile, 0); + fz_catch(ctx) + { + /* Swallow the error */ + fz_warn(ctx, "Attempt to read Output Intent failed"); + } + + return cs; +} + +fz_colorspace * +pdf_document_output_intent(fz_context *ctx, pdf_document *doc) +{ +#ifndef NOICC + if (!doc->oi) + doc->oi = pdf_load_output_intent(ctx, doc); +#endif + return doc->oi; +} diff --git a/source/pdf/pdf-device.c b/source/pdf/pdf-device.c index 23fed0fd..ba51265a 100644 --- a/source/pdf/pdf-device.c +++ b/source/pdf/pdf-device.c @@ -214,7 +214,7 @@ pdf_dev_ctm(fz_context *ctx, pdf_device *pdev, const fz_matrix *ctm) } static void -pdf_dev_color(fz_context *ctx, pdf_device *pdev, fz_colorspace *colorspace, const float *color, int stroke) +pdf_dev_color(fz_context *ctx, pdf_device *pdev, fz_colorspace *colorspace, const float *color, int stroke, const fz_color_params *color_params) { int diff = 0; int i; @@ -232,7 +232,7 @@ pdf_dev_color(fz_context *ctx, pdf_device *pdev, fz_colorspace *colorspace, cons if (cspace == 0) { /* If it's an unknown colorspace, fallback to rgb */ - fz_convert_color(ctx, fz_device_rgb(ctx), rgb, colorspace, color); + fz_convert_color(ctx, color_params, NULL, fz_device_rgb(ctx), rgb, colorspace, color); color = rgb; colorspace = fz_device_rgb(ctx); cspace = 3; @@ -631,14 +631,14 @@ pdf_dev_new_form(fz_context *ctx, pdf_obj **form_ref, pdf_device *pdev, const fz static void pdf_dev_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { pdf_device *pdev = (pdf_device*)dev; gstate *gs = CURRENT_GSTATE(pdev); pdf_dev_end_text(ctx, pdev); pdf_dev_alpha(ctx, pdev, alpha, 0); - pdf_dev_color(ctx, pdev, colorspace, color, 0); + pdf_dev_color(ctx, pdev, colorspace, color, 0, color_params); pdf_dev_ctm(ctx, pdev, ctm); pdf_dev_path(ctx, pdev, path); fz_append_string(ctx, gs->buf, (even_odd ? "f*\n" : "f\n")); @@ -646,14 +646,14 @@ pdf_dev_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even static void pdf_dev_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { pdf_device *pdev = (pdf_device*)dev; gstate *gs = CURRENT_GSTATE(pdev); pdf_dev_end_text(ctx, pdev); pdf_dev_alpha(ctx, pdev, alpha, 1); - pdf_dev_color(ctx, pdev, colorspace, color, 1); + pdf_dev_color(ctx, pdev, colorspace, color, 1, color_params); pdf_dev_ctm(ctx, pdev, ctm); pdf_dev_stroke_state(ctx, pdev, stroke); pdf_dev_path(ctx, pdev, path); @@ -694,7 +694,7 @@ pdf_dev_clip_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, c static void pdf_dev_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { pdf_device *pdev = (pdf_device*)dev; fz_text_span *span; @@ -706,14 +706,14 @@ pdf_dev_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz pdf_dev_font(ctx, pdev, span->font); pdf_dev_ctm(ctx, pdev, ctm); pdf_dev_alpha(ctx, pdev, alpha, 0); - pdf_dev_color(ctx, pdev, colorspace, color, 0); + pdf_dev_color(ctx, pdev, colorspace, color, 0, color_params); pdf_dev_text_span(ctx, pdev, span); } } static void pdf_dev_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { pdf_device *pdev = (pdf_device*)dev; fz_text_span *span; @@ -724,7 +724,7 @@ pdf_dev_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const pdf_dev_font(ctx, pdev, span->font); pdf_dev_ctm(ctx, pdev, ctm); pdf_dev_alpha(ctx, pdev, alpha, 1); - pdf_dev_color(ctx, pdev, colorspace, color, 1); + pdf_dev_color(ctx, pdev, colorspace, color, 1, color_params); pdf_dev_text_span(ctx, pdev, span); } } @@ -804,7 +804,7 @@ pdf_dev_add_image_res(fz_context *ctx, fz_device *dev, pdf_obj *im_res) } static void -pdf_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha) +pdf_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha, const fz_color_params *color_params) { pdf_device *pdev = (pdf_device*)dev; pdf_obj *im_res; @@ -832,7 +832,7 @@ pdf_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_ma } static void -pdf_dev_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha) +pdf_dev_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha, const fz_color_params *color_params) { pdf_device *pdev = (pdf_device*)dev; @@ -842,7 +842,7 @@ pdf_dev_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_ma static void pdf_dev_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { pdf_device *pdev = (pdf_device*)dev; pdf_obj *im_res = NULL; @@ -858,7 +858,7 @@ pdf_dev_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const } fz_append_string(ctx, gs->buf, "q\n"); pdf_dev_alpha(ctx, pdev, alpha, 0); - pdf_dev_color(ctx, pdev, colorspace, color, 0); + pdf_dev_color(ctx, pdev, colorspace, color, 0, color_params); /* PDF images are upside down, so fiddle the ctm */ fz_pre_scale(&local_ctm, 1, -1); @@ -892,7 +892,7 @@ pdf_dev_pop_clip(fz_context *ctx, fz_device *dev) } static void -pdf_dev_begin_mask(fz_context *ctx, fz_device *dev, const fz_rect *bbox, int luminosity, fz_colorspace *colorspace, const float *color) +pdf_dev_begin_mask(fz_context *ctx, fz_device *dev, const fz_rect *bbox, int luminosity, fz_colorspace *colorspace, const float *color, const fz_color_params *color_params) { pdf_device *pdev = (pdf_device*)dev; pdf_document *doc = pdev->doc; diff --git a/source/pdf/pdf-image.c b/source/pdf/pdf-image.c index b19f1e39..cf82b540 100644 --- a/source/pdf/pdf-image.c +++ b/source/pdf/pdf-image.c @@ -18,7 +18,7 @@ pdf_load_jpx_imp(fz_context *ctx, pdf_document *doc, pdf_obj *rdb, pdf_obj *dict if (tile->n != 1) { - fz_pixmap *gray = fz_convert_pixmap(ctx, tile, fz_device_gray(ctx), 0); + fz_pixmap *gray = fz_convert_pixmap(ctx, tile, fz_device_gray(ctx), NULL, NULL, fz_default_color_params(ctx), 0); fz_drop_pixmap(ctx, tile); tile = gray; } diff --git a/source/pdf/pdf-interpret.c b/source/pdf/pdf-interpret.c index f98326e6..d9620212 100644 --- a/source/pdf/pdf-interpret.c +++ b/source/pdf/pdf-interpret.c @@ -200,6 +200,24 @@ pdf_process_extgstate(fz_context *ctx, pdf_processor *proc, pdf_csi *csi, pdf_ob fz_rethrow(ctx); } + /* overprint and color management */ + + obj = pdf_dict_get(ctx, dict, PDF_NAME_OP); + if (pdf_is_name(ctx, obj) && proc->op_gs_OP) + proc->op_gs_OP(ctx, proc, pdf_to_bool(ctx, obj)); + + obj = pdf_dict_get(ctx, dict, PDF_NAME_op); + if (pdf_is_name(ctx, obj) && proc->op_gs_op) + proc->op_gs_op(ctx, proc, pdf_to_bool(ctx, obj)); + + obj = pdf_dict_get(ctx, dict, PDF_NAME_OPM); + if (pdf_is_name(ctx, obj) && proc->op_gs_OPM) + proc->op_gs_OPM(ctx, proc, pdf_to_int(ctx, obj)); + + obj = pdf_dict_get(ctx, dict, PDF_NAME_UseBlackPtComp); + if (pdf_is_name(ctx, obj) && proc->op_gs_UseBlackPtComp) + proc->op_gs_UseBlackPtComp(ctx, proc, obj); + /* transfer functions */ obj = pdf_dict_get(ctx, dict, PDF_NAME_TR2); @@ -253,10 +271,7 @@ pdf_process_extgstate(fz_context *ctx, pdf_processor *proc, pdf_csi *csi, pdf_ob { colorspace = pdf_xobject_colorspace(ctx, xobj); if (colorspace) - { colorspace_n = fz_colorspace_n(ctx, colorspace); - fz_drop_colorspace(ctx, colorspace); - } /* Default background color is black. */ for (k = 0; k < colorspace_n; k++) @@ -266,6 +281,7 @@ pdf_process_extgstate(fz_context *ctx, pdf_processor *proc, pdf_csi *csi, pdf_ob * to do for now. */ if (colorspace == fz_device_cmyk(ctx)) softmask_bc[3] = 1.0f; + fz_drop_colorspace(ctx, colorspace); bc = pdf_dict_get(ctx, obj, PDF_NAME_BC); if (pdf_is_array(ctx, bc)) diff --git a/source/pdf/pdf-op-buffer.c b/source/pdf/pdf-op-buffer.c index d5585766..6974fb00 100644 --- a/source/pdf/pdf-op-buffer.c +++ b/source/pdf/pdf-op-buffer.c @@ -65,6 +65,26 @@ pdf_out_ri(fz_context *ctx, pdf_processor *proc, const char *intent) } static void +pdf_out_gs_OP(fz_context *ctx, pdf_processor *proc, int b) +{ +} + +static void +pdf_out_gs_op(fz_context *ctx, pdf_processor *proc, int b) +{ +} + +static void +pdf_out_gs_OPM(fz_context *ctx, pdf_processor *proc, int i) +{ +} + +static void +pdf_out_gs_UseBlackPtComp(fz_context *ctx, pdf_processor *proc, pdf_obj *name) +{ +} + +static void pdf_out_i(fz_context *ctx, pdf_processor *proc, float flatness) { fz_output *out = ((pdf_output_processor*)proc)->out; @@ -891,6 +911,12 @@ pdf_new_output_processor(fz_context *ctx, fz_output *out, int ahxencode) /* compatibility */ proc->super.op_BX = pdf_out_BX; proc->super.op_EX = pdf_out_EX; + + /* extgstate */ + proc->super.op_gs_OP = pdf_out_gs_OP; + proc->super.op_gs_op = pdf_out_gs_op; + proc->super.op_gs_OPM = pdf_out_gs_OPM; + proc->super.op_gs_UseBlackPtComp = pdf_out_gs_UseBlackPtComp; } proc->out = out; diff --git a/source/pdf/pdf-op-filter.c b/source/pdf/pdf-op-filter.c index ad80d3cd..a71f2fa7 100644 --- a/source/pdf/pdf-op-filter.c +++ b/source/pdf/pdf-op-filter.c @@ -389,6 +389,42 @@ pdf_filter_ri(fz_context *ctx, pdf_processor *proc, const char *intent) } static void +pdf_filter_gs_OP(fz_context *ctx, pdf_processor *proc, int b) +{ + pdf_filter_processor *p = (pdf_filter_processor*)proc; + filter_flush(ctx, p, 0); + if (p->chain->op_gs_OP) + p->chain->op_gs_OP(ctx, p->chain, b); +} + +static void +pdf_filter_gs_op(fz_context *ctx, pdf_processor *proc, int b) +{ + pdf_filter_processor *p = (pdf_filter_processor*)proc; + filter_flush(ctx, p, 0); + if (p->chain->op_gs_op) + p->chain->op_gs_op(ctx, p->chain, b); +} + +static void +pdf_filter_gs_OPM(fz_context *ctx, pdf_processor *proc, int i) +{ + pdf_filter_processor *p = (pdf_filter_processor*)proc; + filter_flush(ctx, p, 0); + if (p->chain->op_gs_OPM) + p->chain->op_gs_OPM(ctx, p->chain, i); +} + +static void +pdf_filter_gs_UseBlackPtComp(fz_context *ctx, pdf_processor *proc, pdf_obj *name) +{ + pdf_filter_processor *p = (pdf_filter_processor*)proc; + filter_flush(ctx, p, 0); + if (p->chain->op_gs_UseBlackPtComp) + p->chain->op_gs_UseBlackPtComp(ctx, p->chain, name); +} + +static void pdf_filter_i(fz_context *ctx, pdf_processor *proc, float flatness) { pdf_filter_processor *p = (pdf_filter_processor*)proc; @@ -1238,6 +1274,12 @@ pdf_new_filter_processor(fz_context *ctx, pdf_processor *chain, pdf_obj *old_rdb proc->super.op_BX = pdf_filter_BX; proc->super.op_EX = pdf_filter_EX; + /* extgstate */ + proc->super.op_gs_OP = pdf_filter_gs_OP; + proc->super.op_gs_op = pdf_filter_gs_op; + proc->super.op_gs_OPM = pdf_filter_gs_OPM; + proc->super.op_gs_UseBlackPtComp = pdf_filter_gs_UseBlackPtComp; + proc->super.op_END = pdf_filter_END; } diff --git a/source/pdf/pdf-op-run.c b/source/pdf/pdf-op-run.c index fddc9b91..f028732e 100644 --- a/source/pdf/pdf-op-run.c +++ b/source/pdf/pdf-op-run.c @@ -36,6 +36,7 @@ struct pdf_material_s pdf_pattern *pattern; fz_shade *shade; int gstate_num; + fz_color_params color_params; float alpha; float v[FZ_MAX_COLORS]; }; @@ -148,7 +149,7 @@ begin_softmask(fz_context *ctx, pdf_run_processor *pr, softmask_save *save) fz_try(ctx) { - fz_begin_mask(ctx, pr->dev, &mask_bbox, gstate->luminosity, mask_colorspace, gstate->softmask_bc); + fz_begin_mask(ctx, pr->dev, &mask_bbox, gstate->luminosity, mask_colorspace, gstate->softmask_bc, &gstate->fill.color_params); pdf_run_xobject(ctx, pr, softmask, save->page_resources, &fz_identity); } fz_always(ctx) @@ -224,7 +225,7 @@ pdf_show_shade(fz_context *ctx, pdf_run_processor *pr, fz_shade *shd) /* FIXME: The gstate->ctm in the next line may be wrong; maybe * it should be the parent gstates ctm? */ - fz_fill_shade(ctx, pr->dev, shd, &gstate->ctm, gstate->fill.alpha); + fz_fill_shade(ctx, pr->dev, shd, &gstate->ctm, gstate->fill.alpha, &gstate->fill.color_params); pdf_end_group(ctx, pr, &softmask); } @@ -540,7 +541,7 @@ pdf_show_image(fz_context *ctx, pdf_run_processor *pr, fz_image *image) break; case PDF_MAT_COLOR: fz_fill_image_mask(ctx, pr->dev, image, &image_ctm, - gstate->fill.colorspace, gstate->fill.v, gstate->fill.alpha); + gstate->fill.colorspace, gstate->fill.v, gstate->fill.alpha, &gstate->fill.color_params); break; case PDF_MAT_PATTERN: if (gstate->fill.pattern) @@ -554,7 +555,7 @@ pdf_show_image(fz_context *ctx, pdf_run_processor *pr, fz_image *image) if (gstate->fill.shade) { fz_clip_image_mask(ctx, pr->dev, image, &image_ctm, &bbox); - fz_fill_shade(ctx, pr->dev, gstate->fill.shade, &pr->gstate[gstate->fill.gstate_num].ctm, gstate->fill.alpha); + fz_fill_shade(ctx, pr->dev, gstate->fill.shade, &pr->gstate[gstate->fill.gstate_num].ctm, gstate->fill.alpha, &gstate->fill.color_params); fz_pop_clip(ctx, pr->dev); } break; @@ -562,7 +563,7 @@ pdf_show_image(fz_context *ctx, pdf_run_processor *pr, fz_image *image) } else { - fz_fill_image(ctx, pr->dev, image, &image_ctm, gstate->fill.alpha); + fz_fill_image(ctx, pr->dev, image, &image_ctm, gstate->fill.alpha, &gstate->fill.color_params); } if (image->mask) @@ -646,7 +647,7 @@ pdf_show_path(fz_context *ctx, pdf_run_processor *pr, int doclose, int dofill, i break; case PDF_MAT_COLOR: fz_fill_path(ctx, pr->dev, path, even_odd, &gstate->ctm, - gstate->fill.colorspace, gstate->fill.v, gstate->fill.alpha); + gstate->fill.colorspace, gstate->fill.v, gstate->fill.alpha, &gstate->fill.color_params); break; case PDF_MAT_PATTERN: if (gstate->fill.pattern) @@ -661,7 +662,7 @@ pdf_show_path(fz_context *ctx, pdf_run_processor *pr, int doclose, int dofill, i { fz_clip_path(ctx, pr->dev, path, even_odd, &gstate->ctm, &bbox); /* The cluster and page 2 of patterns.pdf shows that fz_fill_shade should NOT be called with gstate->ctm. */ - fz_fill_shade(ctx, pr->dev, gstate->fill.shade, &pr->gstate[gstate->fill.gstate_num].ctm, gstate->fill.alpha); + fz_fill_shade(ctx, pr->dev, gstate->fill.shade, &pr->gstate[gstate->fill.gstate_num].ctm, gstate->fill.alpha, &gstate->fill.color_params); fz_pop_clip(ctx, pr->dev); } break; @@ -676,7 +677,7 @@ pdf_show_path(fz_context *ctx, pdf_run_processor *pr, int doclose, int dofill, i break; case PDF_MAT_COLOR: fz_stroke_path(ctx, pr->dev, path, gstate->stroke_state, &gstate->ctm, - gstate->stroke.colorspace, gstate->stroke.v, gstate->stroke.alpha); + gstate->stroke.colorspace, gstate->stroke.v, gstate->stroke.alpha, &gstate->stroke.color_params); break; case PDF_MAT_PATTERN: if (gstate->stroke.pattern) @@ -690,7 +691,7 @@ pdf_show_path(fz_context *ctx, pdf_run_processor *pr, int doclose, int dofill, i if (gstate->stroke.shade) { fz_clip_stroke_path(ctx, pr->dev, path, gstate->stroke_state, &gstate->ctm, &bbox); - fz_fill_shade(ctx, pr->dev, gstate->stroke.shade, &pr->gstate[gstate->stroke.gstate_num].ctm, gstate->stroke.alpha); + fz_fill_shade(ctx, pr->dev, gstate->stroke.shade, &pr->gstate[gstate->stroke.gstate_num].ctm, gstate->stroke.alpha, &gstate->stroke.color_params); fz_pop_clip(ctx, pr->dev); } break; @@ -794,7 +795,7 @@ pdf_flush_text(fz_context *ctx, pdf_run_processor *pr) break; case PDF_MAT_COLOR: fz_fill_text(ctx, pr->dev, text, &gstate->ctm, - gstate->fill.colorspace, gstate->fill.v, gstate->fill.alpha); + gstate->fill.colorspace, gstate->fill.v, gstate->fill.alpha, &gstate->fill.color_params); break; case PDF_MAT_PATTERN: if (gstate->fill.pattern) @@ -809,7 +810,7 @@ pdf_flush_text(fz_context *ctx, pdf_run_processor *pr) { fz_clip_text(ctx, pr->dev, text, &gstate->ctm, &tb); /* Page 2 of patterns.pdf shows that fz_fill_shade should NOT be called with gstate->ctm */ - fz_fill_shade(ctx, pr->dev, gstate->fill.shade, &pr->gstate[gstate->fill.gstate_num].ctm, gstate->fill.alpha); + fz_fill_shade(ctx, pr->dev, gstate->fill.shade, &pr->gstate[gstate->fill.gstate_num].ctm, gstate->fill.alpha, &gstate->fill.color_params); fz_pop_clip(ctx, pr->dev); } break; @@ -824,7 +825,7 @@ pdf_flush_text(fz_context *ctx, pdf_run_processor *pr) break; case PDF_MAT_COLOR: fz_stroke_text(ctx, pr->dev, text, gstate->stroke_state, &gstate->ctm, - gstate->stroke.colorspace, gstate->stroke.v, gstate->stroke.alpha); + gstate->stroke.colorspace, gstate->stroke.v, gstate->stroke.alpha, &gstate->stroke.color_params); break; case PDF_MAT_PATTERN: if (gstate->stroke.pattern) @@ -838,7 +839,7 @@ pdf_flush_text(fz_context *ctx, pdf_run_processor *pr) if (gstate->stroke.shade) { fz_clip_stroke_text(ctx, pr->dev, text, gstate->stroke_state, &gstate->ctm, &tb); - fz_fill_shade(ctx, pr->dev, gstate->stroke.shade, &pr->gstate[gstate->stroke.gstate_num].ctm, gstate->stroke.alpha); + fz_fill_shade(ctx, pr->dev, gstate->stroke.shade, &pr->gstate[gstate->stroke.gstate_num].ctm, gstate->stroke.alpha, &gstate->stroke.color_params); fz_pop_clip(ctx, pr->dev); } break; @@ -1085,6 +1086,9 @@ pdf_init_gstate(fz_context *ctx, pdf_gstate *gs, const fz_matrix *ctm) gs->softmask_resources = NULL; gs->softmask_ctm = fz_identity; gs->luminosity = 0; + + gs->fill.color_params = *fz_default_color_params(ctx); + gs->stroke.color_params = *fz_default_color_params(ctx); } static void @@ -1133,7 +1137,6 @@ pdf_set_color(fz_context *ctx, pdf_run_processor *pr, int what, float *v) { pdf_gstate *gstate = pr->gstate + pr->gtop; pdf_material *mat; - int i, n; gstate = pdf_flush_text(ctx, pr); @@ -1143,25 +1146,7 @@ pdf_set_color(fz_context *ctx, pdf_run_processor *pr, int what, float *v) { case PDF_MAT_PATTERN: case PDF_MAT_COLOR: - /* ICC Colorspaces would be handled here too, if we handled them */ - if (fz_colorspace_is_indexed(ctx, mat->colorspace)) - { - mat->v[0] = fz_clamp(v[0], 0, 1) / 255; - break; - } - else if (fz_colorspace_is_lab(ctx, mat->colorspace)) - { - n = fz_colorspace_n(ctx, mat->colorspace); - /* input is in range (0..100, -128..127, -128..127) not (0..1, 0..1, 0..1) */ - for (i = 0; i < n; i++) - mat->v[i] = fz_clamp(v[i], i ? -128 : 0, i ? 127 : 100); - } - else - { - n = fz_colorspace_n(ctx, mat->colorspace); - for (i = 0; i < n; i++) - mat->v[i] = fz_clamp(v[i], 0, 1); - } + fz_clamp_color(ctx, mat->colorspace, v, mat->v); break; default: fz_warn(ctx, "color incompatible with material"); @@ -1435,6 +1420,44 @@ static void pdf_run_d(fz_context *ctx, pdf_processor *proc, pdf_obj *array, floa static void pdf_run_ri(fz_context *ctx, pdf_processor *proc, const char *intent) { + pdf_run_processor *pr = (pdf_run_processor *)proc; + pdf_gstate *gstate = pdf_flush_text(ctx, pr); + gstate->fill.color_params.ri = fz_lookup_rendering_intent(intent); + gstate->stroke.color_params.ri = gstate->fill.color_params.ri; +} + +static void pdf_run_gs_OP(fz_context *ctx, pdf_processor *proc, int b) +{ + pdf_run_processor *pr = (pdf_run_processor *)proc; + pdf_gstate *gstate = pdf_flush_text(ctx, pr); + gstate->stroke.color_params.op = b; + gstate->fill.color_params.op = b; +} + +static void pdf_run_gs_op(fz_context *ctx, pdf_processor *proc, int b) +{ + pdf_run_processor *pr = (pdf_run_processor *)proc; + pdf_gstate *gstate = pdf_flush_text(ctx, pr); + gstate->fill.color_params.op = b; +} + +static void pdf_run_gs_OPM(fz_context *ctx, pdf_processor *proc, int i) +{ + pdf_run_processor *pr = (pdf_run_processor *)proc; + pdf_gstate *gstate = pdf_flush_text(ctx, pr); + gstate->stroke.color_params.opm = i; + gstate->fill.color_params.opm = i; +} + +static void pdf_run_gs_UseBlackPtComp(fz_context *ctx, pdf_processor *proc, pdf_obj *obj) +{ + pdf_run_processor *pr = (pdf_run_processor *)proc; + pdf_gstate *gstate = pdf_flush_text(ctx, pr); + int on = pdf_name_eq(ctx, obj, PDF_NAME_ON); + /* The spec says that "ON" means on, "OFF" means "Off", and + * "Default" or anything else means "Meh, do what you want." */ + gstate->stroke.color_params.bp = on; + gstate->fill.color_params.bp = on; } static void pdf_run_i(fz_context *ctx, pdf_processor *proc, float flatness) @@ -1491,10 +1514,7 @@ static void pdf_run_gs_SMask(fz_context *ctx, pdf_processor *proc, pdf_xobject * fz_colorspace *cs = pdf_xobject_colorspace(ctx, smask); int cs_n = 1; if (cs) - { cs_n = fz_colorspace_n(ctx, cs); - fz_drop_colorspace(ctx, cs); - } gstate->softmask_ctm = gstate->ctm; gstate->softmask = pdf_keep_xobject(ctx, smask); gstate->softmask_resources = pdf_keep_obj(ctx, page_resources); @@ -2149,6 +2169,12 @@ pdf_new_run_processor(fz_context *ctx, fz_device *dev, const fz_matrix *ctm, con proc->super.op_BX = pdf_run_BX; proc->super.op_EX = pdf_run_EX; + /* extgstate */ + proc->super.op_gs_OP = pdf_run_gs_OP; + proc->super.op_gs_op = pdf_run_gs_op; + proc->super.op_gs_OPM = pdf_run_gs_OPM; + proc->super.op_gs_UseBlackPtComp = pdf_run_gs_UseBlackPtComp; + proc->super.op_END = pdf_run_END; } diff --git a/source/pdf/pdf-page.c b/source/pdf/pdf-page.c index 8f24ef91..f243bdcd 100644 --- a/source/pdf/pdf-page.c +++ b/source/pdf/pdf-page.c @@ -676,6 +676,61 @@ pdf_new_page(fz_context *ctx, pdf_document *doc) return page; } +fz_default_colorspaces * +pdf_load_default_colorspaces(fz_context *ctx, pdf_document *doc, pdf_page *page) +{ + pdf_obj *res; + pdf_obj *obj; + pdf_obj *cs_obj; + fz_default_colorspaces *default_cs; + fz_colorspace *oi; + + /* If we are doing color management check for internal default color spaces. */ + /* Photoshop is notorious for doing this in its PDF creation. */ + if (!fz_colorspace_is_icc(ctx, fz_device_rgb(ctx))) + return NULL; + + default_cs = fz_new_default_colorspaces(ctx); + res = pdf_page_resources(ctx, page); + obj = pdf_dict_get(ctx, res, PDF_NAME_ColorSpace); + if (obj) + { + /* The spec says to ignore any colors we can't understand */ + fz_try(ctx) + { + cs_obj = pdf_dict_get(ctx, obj, PDF_NAME_DefaultGray); + if (cs_obj) + fz_set_default_gray(ctx, default_cs, pdf_load_colorspace(ctx, cs_obj)); + } + fz_catch(ctx) + {} + + fz_try(ctx) + { + cs_obj = pdf_dict_get(ctx, obj, PDF_NAME_DefaultRGB); + if (cs_obj) + fz_set_default_rgb(ctx, default_cs, pdf_load_colorspace(ctx, cs_obj)); + } + fz_catch(ctx) + {} + + fz_try(ctx) + { + cs_obj = pdf_dict_get(ctx, obj, PDF_NAME_DefaultCMYK); + if (cs_obj) + fz_set_default_cmyk(ctx, default_cs, pdf_load_colorspace(ctx, cs_obj)); + } + fz_catch(ctx) + {} + } + + oi = pdf_document_output_intent(ctx, doc); + if (oi) + fz_set_default_output_intent(ctx, default_cs, oi); + + return default_cs; +} + pdf_page * pdf_load_page(fz_context *ctx, pdf_document *doc, int number) { diff --git a/source/pdf/pdf-run.c b/source/pdf/pdf-run.c index 4fa1071f..e4a773ba 100644 --- a/source/pdf/pdf-run.c +++ b/source/pdf/pdf-run.c @@ -7,6 +7,11 @@ pdf_run_annot_with_usage(fz_context *ctx, pdf_document *doc, pdf_page *page, pdf fz_matrix local_ctm, page_ctm; fz_rect mediabox; pdf_processor *proc; + fz_default_colorspaces *default_cs; + + default_cs = pdf_load_default_colorspaces(ctx, doc, page); + if (default_cs) + fz_set_default_colorspaces(ctx, dev, default_cs); pdf_page_transform(ctx, page, &mediabox, &page_ctm); fz_concat(&local_ctm, &page_ctm, ctm); @@ -31,6 +36,11 @@ pdf_run_page_contents_with_usage(fz_context *ctx, pdf_document *doc, pdf_page *p pdf_obj *contents; fz_rect mediabox; pdf_processor *proc; + fz_default_colorspaces *default_cs; + + default_cs = pdf_load_default_colorspaces(ctx, doc, page); + if (default_cs) + fz_set_default_colorspaces(ctx, dev, default_cs); pdf_page_transform(ctx, page, &mediabox, &page_ctm); fz_concat(&local_ctm, &page_ctm, ctm); diff --git a/source/pdf/pdf-xref.c b/source/pdf/pdf-xref.c index 256ebfec..ce62e6b7 100644 --- a/source/pdf/pdf-xref.c +++ b/source/pdf/pdf-xref.c @@ -1540,6 +1540,8 @@ pdf_drop_document_imp(fz_context *ctx, pdf_document *doc) pdf_drop_resource_tables(ctx, doc); + fz_drop_colorspace(ctx, doc->oi); + for (i = 0; i < doc->orphans_count; i++) pdf_drop_obj(ctx, doc->orphans[i]); @@ -2230,6 +2232,7 @@ pdf_new_document(fz_context *ctx, fz_stream *file) pdf_document *doc = fz_new_derived_document(ctx, pdf_document); doc->super.drop_document = (fz_document_drop_fn*)pdf_drop_document_imp; + doc->super.get_output_intent = (fz_document_output_intent_fn*)pdf_document_output_intent; doc->super.needs_password = (fz_document_needs_password_fn*)pdf_needs_password; doc->super.authenticate_password = (fz_document_authenticate_password_fn*)pdf_authenticate_password; doc->super.has_permission = (fz_document_has_permission_fn*)pdf_has_permission; diff --git a/source/svg/svg-run.c b/source/svg/svg-run.c index f02a8aca..f974c675 100644 --- a/source/svg/svg-run.c +++ b/source/svg/svg-run.c @@ -39,13 +39,13 @@ static void svg_run_element(fz_context *ctx, fz_device *dev, svg_document *doc, static void svg_fill(fz_context *ctx, fz_device *dev, svg_document *doc, fz_path *path, svg_state *state) { float opacity = state->opacity * state->fill_opacity; - fz_fill_path(ctx, dev, path, state->fill_rule, &state->transform, fz_device_rgb(ctx), state->fill_color, opacity); + fz_fill_path(ctx, dev, path, state->fill_rule, &state->transform, fz_device_rgb(ctx), state->fill_color, opacity, NULL); } static void svg_stroke(fz_context *ctx, fz_device *dev, svg_document *doc, fz_path *path, svg_state *state) { float opacity = state->opacity * state->stroke_opacity; - fz_stroke_path(ctx, dev, path, &state->stroke, &state->transform, fz_device_rgb(ctx), state->stroke_color, opacity); + fz_stroke_path(ctx, dev, path, &state->stroke, &state->transform, fz_device_rgb(ctx), state->stroke_color, opacity, NULL); } static void svg_draw_path(fz_context *ctx, fz_device *dev, svg_document *doc, fz_path *path, svg_state *state) diff --git a/source/tools/mudraw.c b/source/tools/mudraw.c index ef95f1d3..aebe441e 100644 --- a/source/tools/mudraw.c +++ b/source/tools/mudraw.c @@ -254,6 +254,12 @@ static int files = 0; static int num_workers = 0; static worker_t *workers; +#ifdef NO_ICC +static fz_cmm_engine *icc_engine = NULL; +#else +static fz_cmm_engine *icc_engine = &fz_cmm_engine_lcms; +#endif + static const char *layer_config = NULL; static struct { @@ -327,6 +333,7 @@ static void usage(void) "\t-i\tignore errors\n" "\t-L\tlow memory mode (avoid caching, clear objects after each page)\n" "\t-P\tparallel interpretation/rendering\n" + "\t-N\tdisable ICC workflow (\"N\"o color management)\n" "\n" "\t-y l\tList the layer configs to stderr\n" "\t-y -\tSelect layer config (by number)\n" @@ -797,7 +804,9 @@ static void dodrawpage(fz_context *ctx, fz_page *page, fz_display_list *list, in bander = fz_new_color_pcl_band_writer(ctx, out, NULL); } if (bander) - fz_write_header(ctx, bander, pix->w, totalheight, pix->n, pix->alpha, pix->xres, pix->yres, output_pagenum++); + { + fz_write_header(ctx, bander, pix->w, totalheight, pix->n, pix->alpha, pix->xres, pix->yres, output_pagenum++, pix->colorspace); + } } for (band = 0; band < bands; band++) @@ -1345,10 +1354,11 @@ int mudraw_main(int argc, char **argv) fz_context *ctx; fz_alloc_context alloc_ctx = { NULL, trace_malloc, trace_realloc, trace_free }; fz_locks_context *locks = NULL; + fz_colorspace *oi = NULL; fz_var(doc); - while ((c = fz_getopt(argc, argv, "p:o:F:R:r:w:h:fB:c:G:Is:A:DiW:H:S:T:U:XLvPl:y:")) != -1) + while ((c = fz_getopt(argc, argv, "p:o:F:R:r:w:h:fB:c:G:Is:A:DiW:H:S:T:U:XLvPl:y:N")) != -1) { switch (c) { @@ -1397,6 +1407,7 @@ int mudraw_main(int argc, char **argv) case 'D': uselist = 0; break; case 'l': min_line_width = fz_atof(fz_optarg); break; case 'i': ignore_errors = 1; break; + case 'N': icc_engine = NULL; break; case 'T': #ifndef DISABLE_MUTHREADS @@ -1459,6 +1470,7 @@ int mudraw_main(int argc, char **argv) fz_set_text_aa_level(ctx, alphabits_text); fz_set_graphics_aa_level(ctx, alphabits_graphics); fz_set_graphics_min_line_width(ctx, min_line_width); + fz_set_cmm_engine(ctx, icc_engine); #ifndef DISABLE_MUTHREADS if (bgprint.active) @@ -1659,6 +1671,17 @@ int mudraw_main(int argc, char **argv) doc = fz_open_document(ctx, filename); + /* Once document is open check for output intent colorspace */ + oi = fz_document_output_intent(ctx, doc); + if (oi) + { + if (fz_colorspace_n(ctx, oi) == fz_colorspace_n(ctx, colorspace)) + { + fz_drop_colorspace(ctx, colorspace); + colorspace = fz_keep_colorspace(ctx, oi); + } + } + if (fz_needs_password(ctx, doc)) { if (!fz_authenticate_password(ctx, doc, password)) diff --git a/source/tools/muraster.c b/source/tools/muraster.c index 41d5e3bf..77d60b8f 100644 --- a/source/tools/muraster.c +++ b/source/tools/muraster.c @@ -615,7 +615,7 @@ static int dodrawpage(fz_context *ctx, int pagenum, fz_cookie *cookie, render_de pix = fz_new_pixmap_with_bbox(ctx, colorspace, &ibounds, 0); fz_set_pixmap_resolution(ctx, pix, x_resolution, y_resolution); } - fz_write_header(ctx, render->bander, pix->w, total_height, pix->n, pix->alpha, pix->xres, pix->yres, pagenum); + fz_write_header(ctx, render->bander, pix->w, total_height, pix->n, pix->alpha, pix->xres, pix->yres, pagenum, pix->colorspace); for (band = 0; band < bands; band++) { @@ -718,7 +718,7 @@ static int try_render_page(fz_context *ctx, int pagenum, fz_cookie *cookie, int { int w = render->ibounds.x1 - render->ibounds.x0; int h = render->ibounds.y1 - render->ibounds.y0; - fz_write_header(ctx, render->bander, w, h, render->n, 0, 0, 0, 0); + fz_write_header(ctx, render->bander, w, h, render->n, 0, 0, 0, 0, NULL); } fz_catch(ctx) { diff --git a/source/tools/murun.c b/source/tools/murun.c index 205e8f91..47cc1884 100644 --- a/source/tools/murun.c +++ b/source/tools/murun.c @@ -538,6 +538,18 @@ static struct color ffi_tocolor(js_State *J, int idx) return c; } +static fz_color_params *ffi_tocolorparams(js_State *J, int idx) +{ + /* TODO */ + return NULL; +} + +static void ffi_pushcolorparams(js_State *J, const fz_color_params *color_params) +{ + /* TODO */ + js_pushnull(J); +} + static const char *string_from_cap(fz_linecap cap) { switch (cap) { @@ -771,7 +783,7 @@ typedef struct js_device_s static void js_dev_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { js_State *J = ((js_device*)dev)->J; if (js_try(J)) @@ -782,7 +794,8 @@ js_dev_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_ js_pushboolean(J, even_odd); ffi_pushmatrix(J, *ctm); ffi_pushcolor(J, colorspace, color, alpha); - js_call(J, 6); + ffi_pushcolorparams(J, color_params); + js_call(J, 7); js_pop(J, 1); } js_endtry(J); @@ -809,7 +822,7 @@ js_dev_clip_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_ static void js_dev_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { js_State *J = ((js_device*)dev)->J; if (js_try(J)) @@ -820,7 +833,8 @@ js_dev_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, ffi_pushstroke(J, stroke); ffi_pushmatrix(J, *ctm); ffi_pushcolor(J, colorspace, color, alpha); - js_call(J, 6); + ffi_pushcolorparams(J, color_params); + js_call(J, 7); js_pop(J, 1); } js_endtry(J); @@ -846,7 +860,7 @@ js_dev_clip_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, co static void js_dev_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { js_State *J = ((js_device*)dev)->J; if (js_try(J)) @@ -856,7 +870,8 @@ js_dev_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_ ffi_pushtext(J, text); ffi_pushmatrix(J, *ctm); ffi_pushcolor(J, colorspace, color, alpha); - js_call(J, 5); + ffi_pushcolorparams(J, color_params); + js_call(J, 6); js_pop(J, 1); } js_endtry(J); @@ -864,7 +879,7 @@ js_dev_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_ static void js_dev_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, - const fz_matrix *ctm, fz_colorspace *colorspace, const float *color, float alpha) + const fz_matrix *ctm, fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { js_State *J = ((js_device*)dev)->J; if (js_try(J)) @@ -875,7 +890,8 @@ js_dev_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const f ffi_pushstroke(J, stroke); ffi_pushmatrix(J, *ctm); ffi_pushcolor(J, colorspace, color, alpha); - js_call(J, 6); + ffi_pushcolorparams(J, color_params); + js_call(J, 7); js_pop(J, 1); } js_endtry(J); @@ -932,7 +948,7 @@ js_dev_ignore_text(fz_context *ctx, fz_device *dev, const fz_text *text, const f } static void -js_dev_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha) +js_dev_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha, const fz_color_params *color_params) { js_State *J = ((js_device*)dev)->J; if (js_try(J)) @@ -942,14 +958,15 @@ js_dev_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_mat ffi_pushshade(J, shade); ffi_pushmatrix(J, *ctm); js_pushnumber(J, alpha); - js_call(J, 3); + ffi_pushcolorparams(J, color_params); + js_call(J, 4); js_pop(J, 1); } js_endtry(J); } static void -js_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha) +js_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha, const fz_color_params *color_params) { js_State *J = ((js_device*)dev)->J; if (js_try(J)) @@ -959,7 +976,8 @@ js_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_mat ffi_pushimage(J, image); ffi_pushmatrix(J, *ctm); js_pushnumber(J, alpha); - js_call(J, 3); + ffi_pushcolorparams(J, color_params); + js_call(J, 4); js_pop(J, 1); } js_endtry(J); @@ -967,7 +985,7 @@ js_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_mat static void js_dev_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, - fz_colorspace *colorspace, const float *color, float alpha) + fz_colorspace *colorspace, const float *color, float alpha, const fz_color_params *color_params) { js_State *J = ((js_device*)dev)->J; if (js_try(J)) @@ -977,7 +995,8 @@ js_dev_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const f ffi_pushimage(J, image); ffi_pushmatrix(J, *ctm); ffi_pushcolor(J, colorspace, color, alpha); - js_call(J, 5); + ffi_pushcolorparams(J, color_params); + js_call(J, 6); js_pop(J, 1); } js_endtry(J); @@ -1015,7 +1034,7 @@ js_dev_pop_clip(fz_context *ctx, fz_device *dev) static void js_dev_begin_mask(fz_context *ctx, fz_device *dev, const fz_rect *bbox, int luminosity, - fz_colorspace *colorspace, const float *color) + fz_colorspace *colorspace, const float *color, const fz_color_params *color_params) { js_State *J = ((js_device*)dev)->J; if (js_try(J)) @@ -1024,14 +1043,9 @@ js_dev_begin_mask(fz_context *ctx, fz_device *dev, const fz_rect *bbox, int lumi js_copy(J, -2); ffi_pushrect(J, *bbox); js_pushboolean(J, luminosity); - if (colorspace) { - ffi_pushcolorspace(J, colorspace); - ffi_pusharray(J, color, fz_colorspace_n(ctx, colorspace)); - } else { - js_pushnull(J); - js_pushnull(J); - } - js_call(J, 4); + ffi_pushcolor(J, colorspace, color, 1); + ffi_pushcolorparams(J, color_params); + js_call(J, 6); js_pop(J, 1); } js_endtry(J); @@ -1178,8 +1192,9 @@ static void ffi_Device_fillPath(js_State *J) int even_odd = js_toboolean(J, 2); fz_matrix ctm = ffi_tomatrix(J, 3); struct color c = ffi_tocolor(J, 4); + fz_color_params *color_params = ffi_tocolorparams(J, 7); fz_try(ctx) - fz_fill_path(ctx, dev, path, even_odd, &ctm, c.colorspace, c.color, c.alpha); + fz_fill_path(ctx, dev, path, even_odd, &ctm, c.colorspace, c.color, c.alpha, color_params); fz_catch(ctx) rethrow(J); } @@ -1192,8 +1207,9 @@ static void ffi_Device_strokePath(js_State *J) fz_stroke_state stroke = ffi_tostroke(J, 2); fz_matrix ctm = ffi_tomatrix(J, 3); struct color c = ffi_tocolor(J, 4); + fz_color_params *color_params = ffi_tocolorparams(J, 7); fz_try(ctx) - fz_stroke_path(ctx, dev, path, &stroke, &ctm, c.colorspace, c.color, c.alpha); + fz_stroke_path(ctx, dev, path, &stroke, &ctm, c.colorspace, c.color, c.alpha, color_params); fz_catch(ctx) rethrow(J); } @@ -1231,8 +1247,9 @@ static void ffi_Device_fillText(js_State *J) fz_text *text = js_touserdata(J, 1, "fz_text"); fz_matrix ctm = ffi_tomatrix(J, 2); struct color c = ffi_tocolor(J, 3); + fz_color_params *color_params = ffi_tocolorparams(J, 6); fz_try(ctx) - fz_fill_text(ctx, dev, text, &ctm, c.colorspace, c.color, c.alpha); + fz_fill_text(ctx, dev, text, &ctm, c.colorspace, c.color, c.alpha, color_params); fz_catch(ctx) rethrow(J); } @@ -1245,8 +1262,9 @@ static void ffi_Device_strokeText(js_State *J) fz_stroke_state stroke = ffi_tostroke(J, 2); fz_matrix ctm = ffi_tomatrix(J, 3); struct color c = ffi_tocolor(J, 4); + fz_color_params *color_params = ffi_tocolorparams(J, 7); fz_try(ctx) - fz_stroke_text(ctx, dev, text, &stroke, &ctm, c.colorspace, c.color, c.alpha); + fz_stroke_text(ctx, dev, text, &stroke, &ctm, c.colorspace, c.color, c.alpha, color_params); fz_catch(ctx) rethrow(J); } @@ -1295,8 +1313,9 @@ static void ffi_Device_fillShade(js_State *J) fz_shade *shade = js_touserdata(J, 1, "fz_shade"); fz_matrix ctm = ffi_tomatrix(J, 2); float alpha = js_tonumber(J, 3); + fz_color_params *color_params = ffi_tocolorparams(J, 4); fz_try(ctx) - fz_fill_shade(ctx, dev, shade, &ctm, alpha); + fz_fill_shade(ctx, dev, shade, &ctm, alpha, color_params); fz_catch(ctx) rethrow(J); } @@ -1308,8 +1327,9 @@ static void ffi_Device_fillImage(js_State *J) fz_image *image = js_touserdata(J, 1, "fz_image"); fz_matrix ctm = ffi_tomatrix(J, 2); float alpha = js_tonumber(J, 3); + fz_color_params *color_params = ffi_tocolorparams(J, 4); fz_try(ctx) - fz_fill_image(ctx, dev, image, &ctm, alpha); + fz_fill_image(ctx, dev, image, &ctm, alpha, color_params); fz_catch(ctx) rethrow(J); } @@ -1321,8 +1341,9 @@ static void ffi_Device_fillImageMask(js_State *J) fz_image *image = js_touserdata(J, 1, "fz_image"); fz_matrix ctm = ffi_tomatrix(J, 2); struct color c = ffi_tocolor(J, 3); + fz_color_params *color_params = ffi_tocolorparams(J, 6); fz_try(ctx) - fz_fill_image_mask(ctx, dev, image, &ctm, c.colorspace, c.color, c.alpha); + fz_fill_image_mask(ctx, dev, image, &ctm, c.colorspace, c.color, c.alpha, color_params); fz_catch(ctx) rethrow(J); } @@ -1356,8 +1377,9 @@ static void ffi_Device_beginMask(js_State *J) fz_rect area = ffi_torect(J, 1); int luminosity = js_toboolean(J, 2); struct color c = ffi_tocolor(J, 3); + fz_color_params *color_params = ffi_tocolorparams(J, 6); fz_try(ctx) - fz_begin_mask(ctx, dev, &area, luminosity, c.colorspace, c.color); + fz_begin_mask(ctx, dev, &area, luminosity, c.colorspace, c.color, color_params); fz_catch(ctx) rethrow(J); } @@ -4312,25 +4334,25 @@ int murun_main(int argc, char **argv) { jsB_propfun(J, "Device.close", ffi_Device_close, 0); - jsB_propfun(J, "Device.fillPath", ffi_Device_fillPath, 6); - jsB_propfun(J, "Device.strokePath", ffi_Device_strokePath, 6); + jsB_propfun(J, "Device.fillPath", ffi_Device_fillPath, 7); + jsB_propfun(J, "Device.strokePath", ffi_Device_strokePath, 7); jsB_propfun(J, "Device.clipPath", ffi_Device_clipPath, 3); jsB_propfun(J, "Device.clipStrokePath", ffi_Device_clipStrokePath, 3); - jsB_propfun(J, "Device.fillText", ffi_Device_fillText, 5); - jsB_propfun(J, "Device.strokeText", ffi_Device_strokeText, 6); + jsB_propfun(J, "Device.fillText", ffi_Device_fillText, 6); + jsB_propfun(J, "Device.strokeText", ffi_Device_strokeText, 7); jsB_propfun(J, "Device.clipText", ffi_Device_clipText, 2); jsB_propfun(J, "Device.clipStrokeText", ffi_Device_clipStrokeText, 3); jsB_propfun(J, "Device.ignoreText", ffi_Device_ignoreText, 2); - jsB_propfun(J, "Device.fillShade", ffi_Device_fillShade, 3); - jsB_propfun(J, "Device.fillImage", ffi_Device_fillImage, 3); - jsB_propfun(J, "Device.fillImageMask", ffi_Device_fillImageMask, 5); + jsB_propfun(J, "Device.fillShade", ffi_Device_fillShade, 4); + jsB_propfun(J, "Device.fillImage", ffi_Device_fillImage, 4); + jsB_propfun(J, "Device.fillImageMask", ffi_Device_fillImageMask, 6); jsB_propfun(J, "Device.clipImageMask", ffi_Device_clipImageMask, 2); jsB_propfun(J, "Device.popClip", ffi_Device_popClip, 0); - jsB_propfun(J, "Device.beginMask", ffi_Device_beginMask, 5); /* should be 4 */ + jsB_propfun(J, "Device.beginMask", ffi_Device_beginMask, 6); jsB_propfun(J, "Device.endMask", ffi_Device_endMask, 0); jsB_propfun(J, "Device.beginGroup", ffi_Device_beginGroup, 5); jsB_propfun(J, "Device.endGroup", ffi_Device_endGroup, 0); diff --git a/source/tools/pdfextract.c b/source/tools/pdfextract.c index 49a2747e..984cda16 100644 --- a/source/tools/pdfextract.c +++ b/source/tools/pdfextract.c @@ -42,7 +42,7 @@ static void writepixmap(fz_context *ctx, fz_pixmap *pix, char *file, int dorgb) if (dorgb && pix->colorspace && pix->colorspace != fz_device_rgb(ctx)) { - rgb = fz_convert_pixmap(ctx, pix, fz_device_rgb(ctx), 1); + rgb = fz_convert_pixmap(ctx, pix, fz_device_rgb(ctx), NULL, NULL, NULL /* FIXME */, 1); pix = rgb; } diff --git a/source/xps/xps-common.c b/source/xps/xps-common.c index 9e590788..cc7fed94 100644 --- a/source/xps/xps-common.c +++ b/source/xps/xps-common.c @@ -109,7 +109,7 @@ xps_begin_opacity(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, cons if (opacity_mask_tag) { - fz_begin_mask(ctx, dev, area, 0, NULL, NULL); + fz_begin_mask(ctx, dev, area, 0, NULL, NULL, NULL); xps_parse_brush(ctx, doc, ctm, area, base_uri, dict, opacity_mask_tag); fz_end_mask(ctx, dev); } diff --git a/source/xps/xps-glyphs.c b/source/xps/xps-glyphs.c index bfe38432..55a8a362 100644 --- a/source/xps/xps-glyphs.c +++ b/source/xps/xps-glyphs.c @@ -610,7 +610,7 @@ xps_parse_glyphs(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, xps_set_color(ctx, doc, colorspace, samples); fz_fill_text(ctx, dev, text, &local_ctm, - doc->colorspace, doc->color, doc->alpha); + doc->colorspace, doc->color, doc->alpha, NULL); } /* If it's a complex brush, use the charpath as a clip mask */ diff --git a/source/xps/xps-gradient.c b/source/xps/xps-gradient.c index a7a7cf44..349132d5 100644 --- a/source/xps/xps-gradient.c +++ b/source/xps/xps-gradient.c @@ -68,7 +68,7 @@ xps_parse_gradient_stops(fz_context *ctx, xps_document *doc, char *base_uri, fz_ xps_parse_color(ctx, doc, base_uri, color, &colorspace, sample); - fz_convert_color(ctx, fz_device_rgb(ctx), rgb, colorspace, sample + 1); + fz_convert_color(ctx, fz_default_color_params(ctx), NULL, fz_device_rgb(ctx), rgb, colorspace, sample + 1); stops[count].r = rgb[0]; stops[count].g = rgb[1]; @@ -240,7 +240,7 @@ xps_draw_one_radial_gradient(fz_context *ctx, xps_document *doc, const fz_matrix shade->u.l_or_r.coords[1][1] = y1; shade->u.l_or_r.coords[1][2] = r1; - fz_fill_shade(ctx, dev, shade, ctm, 1); + fz_fill_shade(ctx, dev, shade, ctm, 1, fz_default_color_params(ctx)); fz_drop_shade(ctx, shade); } @@ -279,7 +279,7 @@ xps_draw_one_linear_gradient(fz_context *ctx, xps_document *doc, const fz_matrix shade->u.l_or_r.coords[1][1] = y1; shade->u.l_or_r.coords[1][2] = 0; - fz_fill_shade(ctx, dev, shade, ctm, doc->opacity[doc->opacity_top]); + fz_fill_shade(ctx, dev, shade, ctm, doc->opacity[doc->opacity_top], fz_default_color_params(ctx)); fz_drop_shade(ctx, shade); } diff --git a/source/xps/xps-image.c b/source/xps/xps-image.c index 2996b4ee..9408c528 100644 --- a/source/xps/xps-image.c +++ b/source/xps/xps-image.c @@ -23,7 +23,7 @@ xps_paint_image_brush(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, xs = image->w * 96 / image->xres; ys = image->h * 96 / image->yres; fz_pre_scale(&local_ctm, xs, ys); - fz_fill_image(ctx, doc->dev, image, &local_ctm, doc->opacity[doc->opacity_top]); + fz_fill_image(ctx, doc->dev, image, &local_ctm, doc->opacity[doc->opacity_top], fz_default_color_params(ctx)); } static void diff --git a/source/xps/xps-path.c b/source/xps/xps-path.c index c60934a2..c9bd64e0 100644 --- a/source/xps/xps-path.c +++ b/source/xps/xps-path.c @@ -1002,7 +1002,7 @@ xps_parse_path(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, char *b xps_set_color(ctx, doc, colorspace, samples); fz_fill_path(ctx, dev, path, fill_rule == 0, &local_ctm, - doc->colorspace, doc->color, doc->alpha); + doc->colorspace, doc->color, doc->alpha, NULL); } if (fill_tag) @@ -1020,7 +1020,7 @@ xps_parse_path(fz_context *ctx, xps_document *doc, const fz_matrix *ctm, char *b xps_set_color(ctx, doc, colorspace, samples); fz_stroke_path(ctx, dev, stroke_path, stroke, &local_ctm, - doc->colorspace, doc->color, doc->alpha); + doc->colorspace, doc->color, doc->alpha, NULL); } if (stroke_tag) |