#include "mupdf/fitz.h" #include "fitz-imp.h" #include #include #if FZ_ENABLE_JPX typedef struct fz_jpxd_s fz_jpxd; typedef struct stream_block_s stream_block; static void jpx_ycc_to_rgb(fz_context *ctx, fz_pixmap *pix, int cbsign, int crsign) { int w = pix->w; int h = pix->h; int stride = pix->stride; int x, y; for (y = 0; y < h; y++) { unsigned char * row = &pix->samples[stride * y]; for (x = 0; x < w; x++) { int ycc[3]; ycc[0] = row[x * 3 + 0]; ycc[1] = row[x * 3 + 1]; ycc[2] = row[x * 3 + 2]; /* consciously skip Y */ if (cbsign) ycc[1] -= 128; if (crsign) ycc[2] -= 128; row[x * 3 + 0] = fz_clampi(ycc[0] + 1.402f * ycc[2], 0, 255); row[x * 3 + 1] = fz_clampi(ycc[0] - 0.34413f * ycc[1] - 0.71414f * ycc[2], 0, 255); row[x * 3 + 2] = fz_clampi(ycc[0] + 1.772f * ycc[1], 0, 255); } } } #ifdef HAVE_LURATECH #include #define MAX_COLORS 4 #define MAX_ALPHAS 1 #define MAX_COMPONENTS (MAX_COLORS + MAX_ALPHAS) #define HAS_PALETTE(cs) ( \ (cs) == cJP2_Colorspace_Palette_Gray || \ (cs) == cJP2_Colorspace_Palette_RGBa || \ (cs) == cJP2_Colorspace_Palette_RGB_YCCa || \ (cs) == cJP2_Colorspace_Palette_CIE_LABa || \ (cs) == cJP2_Colorspace_Palette_ICCa || \ (cs) == cJP2_Colorspace_Palette_CMYKa) struct fz_jpxd_s { fz_pixmap *pix; JP2_Palette_Params *palette; JP2_Property_Value width; JP2_Property_Value height; fz_colorspace *cs; int expand_indexed; unsigned long xres; unsigned long yres; JP2_Property_Value hstep[MAX_COMPONENTS]; JP2_Property_Value vstep[MAX_COMPONENTS]; JP2_Property_Value bpss[MAX_COMPONENTS]; JP2_Property_Value signs[MAX_COMPONENTS]; }; struct stream_block_s { const unsigned char *data; size_t size; }; static void * JP2_Callback_Conv jpx_alloc(long size, JP2_Callback_Param param) { fz_context *ctx = (fz_context *) param; return fz_malloc(ctx, size); } static JP2_Error JP2_Callback_Conv jpx_free(void *ptr, JP2_Callback_Param param) { fz_context *ctx = (fz_context *) param; fz_free(ctx, ptr); return cJP2_Error_OK; } static unsigned long JP2_Callback_Conv jpx_read(unsigned char *pucData, unsigned long ulPos, unsigned long ulSize, JP2_Callback_Param param) { stream_block *sb = (stream_block *) param; if (ulPos >= sb->size) return 0; ulSize = (unsigned long)fz_minz(ulSize, sb->size - ulPos); memcpy(pucData, &sb->data[ulPos], ulSize); return ulSize; } static JP2_Error JP2_Callback_Conv jpx_write(unsigned char * pucData, short sComponent, unsigned long ulRow, unsigned long ulStart, unsigned long ulNum, JP2_Callback_Param param) { fz_jpxd *state = (fz_jpxd *) param; JP2_Property_Value hstep, vstep; unsigned char *row; int w, h, n, k, entries, expand; JP2_Property_Value x, y, i, bps, sign; unsigned long **palette; w = state->pix->w; h = state->pix->h; n = state->pix->n; if (ulRow >= (unsigned long)h || ulStart >= (unsigned long)w || sComponent >= n) return cJP2_Error_OK; ulNum = fz_mini(ulNum, w - ulStart); hstep = state->hstep[sComponent]; vstep = state->vstep[sComponent]; bps = state->bpss[sComponent]; sign = state->signs[sComponent]; palette = state->palette ? state->palette->ppulPalette : NULL; entries = state->palette ? state->palette->ulEntries : 1; expand = state->expand_indexed; row = state->pix->samples + state->pix->stride * ulRow * vstep + n * ulStart * hstep + sComponent; for (y = 0; ulRow * vstep + y < (JP2_Property_Value)h && y < vstep; y++) { unsigned char *p = row; for (i = 0; i < ulNum; i++) { for (x = 0; (ulStart + i) * hstep + x < (JP2_Property_Value)w && x < hstep; x++) { if (palette) { unsigned char v = fz_clampi(pucData[i], 0, entries - 1); if (expand) { for (k = 0; k < n; k++) p[k] = palette[k][v]; } else *p = v; } else { if (bps > 8) { unsigned int v = (pucData[2 * i + 1] << 8) | pucData[2 * i + 0]; v &= (1 << bps) - 1; v -= sign; *p = v >> (bps - 8); } else if (bps == 8) { unsigned int v = pucData[i]; v &= (1 << bps) - 1; v -= sign; *p = v; } else { unsigned int v = pucData[i]; v &= (1 << bps) - 1; v -= sign; *p = v << (8 - bps); } } p += n; } } row += state->pix->stride; } return cJP2_Error_OK; } static fz_pixmap * jpx_read_image(fz_context *ctx, fz_jpxd *state, const unsigned char *data, size_t size, fz_colorspace *defcs, int onlymeta) { JP2_Decomp_Handle doc; JP2_Channel_Def_Params *chans = NULL; JP2_Error err; int colors, alphas, prealphas; JP2_Property_Value k; JP2_Colorspace colorspace; JP2_Property_Value nchans; JP2_Property_Value widths[MAX_COMPONENTS]; JP2_Property_Value heights[MAX_COMPONENTS]; stream_block sb; memset(state, 0x00, sizeof (fz_jpxd)); sb.data = data; sb.size = size; fz_try(ctx) { err = JP2_Decompress_Start(&doc, jpx_alloc, (JP2_Callback_Param) ctx, jpx_free, (JP2_Callback_Param) ctx, jpx_read, (JP2_Callback_Param) &sb); if (err != cJP2_Error_OK) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot open image: %d", (int) err); #if defined(JP2_LICENSE_NUM_1) && defined(JP2_LICENSE_NUM_2) err = JP2_Document_SetLicense(doc, JP2_LICENSE_NUM_1, JP2_LICENSE_NUM_2); if (err != cJP2_Error_OK) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot set license: %d", (int) err); #endif err = JP2_Decompress_GetProp(doc, cJP2_Prop_Extern_Colorspace, (unsigned long *) &colorspace, -1, -1); if (err != cJP2_Error_OK) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot get colorspace: %d", (int) err); err = JP2_Decompress_GetChannelDefs(doc, &chans, &nchans); if (err != cJP2_Error_OK) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot get channel definitions: %d", (int) err); colors = 0; alphas = 0; prealphas = 0; for (k = 0; k < nchans; k++) { switch (chans[k].ulType) { case cJP2_Channel_Type_Color: colors++; break; case cJP2_Channel_Type_Opacity: alphas++; break; case cJP2_Channel_Type_Opacity_Pre: prealphas++; break; } } if (prealphas> 0) alphas = prealphas; colors = fz_clampi(colors, 0, MAX_COLORS); alphas = fz_clampi(alphas, 0, MAX_ALPHAS); nchans = colors + alphas; if (HAS_PALETTE(colorspace)) { err = JP2_Decompress_GetProp(doc, cJP2_Prop_Width, &state->width, -1, 0); if (err != cJP2_Error_OK) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot get width for palette indicies: %d", (int) err); err = JP2_Decompress_GetProp(doc, cJP2_Prop_Height, &state->height, -1, 0); if (err != cJP2_Error_OK) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot get height for palette indicies: %d", (int) err); for (k = 0; k < nchans; k++) { widths[k] = state->width; heights[k] = state->height; } } else { for (k = 0; k < nchans; k++) { err = JP2_Decompress_GetProp(doc, cJP2_Prop_Width, &widths[k], -1, k); if (err != cJP2_Error_OK) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot get width for component %d: %d", (int) k, (int) err); err = JP2_Decompress_GetProp(doc, cJP2_Prop_Height, &heights[k], -1, k); if (err != cJP2_Error_OK) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot get height for component %d: %d", (int) k, (int) err); state->width = fz_maxi(state->width, widths[k]); state->height = fz_maxi(state->height, heights[k]); } } err = JP2_Decompress_GetResolution(doc, &state->yres, &state->xres, NULL, cJP2_Resolution_Dots_Per_Inch, cJP2_Resolution_Capture); if (err != cJP2_Error_OK) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot get resolution: %d", (int) err); if (state->xres == 0 || state->yres == 0) state->xres = state->yres = 72; state->cs = NULL; if (defcs) { if ((JP2_Property_Value)defcs->n == nchans) state->cs = fz_keep_colorspace(ctx, defcs); else fz_warn(ctx, "jpx file (%lu) and dict colorspace (%d, %s) do not match", nchans, defcs->n, defcs->name); } #if FZ_ENABLE_ICC if (!state->cs && colorspace == cJP2_Colorspace_Palette_ICCa) { unsigned char *iccprofile = NULL; unsigned long size = 0; fz_buffer *cbuf = NULL; fz_var(cbuf); err = JP2_Decompress_GetICC(doc, &iccprofile, &size); if (err != cJP2_Error_OK) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot get ICC color profile: %d", (int) err); fz_try(ctx) { cbuf = fz_new_buffer_from_copied_data(ctx, iccprofile, size); state->cs = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_NONE, 0, NULL, cbuf); } fz_always(ctx) fz_drop_buffer(ctx, cbuf); fz_catch(ctx) fz_warn(ctx, "ignoring embedded ICC profile in JPX"); if (state->cs && (JP2_Property_Value)state->cs->n != nchans) { fz_warn(ctx, "invalid number of components in ICC profile, ignoring ICC profile in JPX"); fz_drop_colorspace(ctx, state->cs); state->cs = NULL; } } #endif if (!state->cs) { switch (colors) { case 4: state->cs = fz_keep_colorspace(ctx, fz_device_cmyk(ctx)); break; case 3: if (colorspace == cJP2_Colorspace_CIE_LABa) state->cs = fz_keep_colorspace(ctx, fz_device_lab(ctx)); else state->cs = fz_keep_colorspace(ctx, fz_device_rgb(ctx)); break; case 1: state->cs = fz_keep_colorspace(ctx, fz_device_gray(ctx)); break; case 0: if (alphas == 1) { /* alpha only images are rendered as grayscale */ state->cs = fz_keep_colorspace(ctx, fz_device_gray(ctx)); colors = 1; alphas = 0; break; } /* fallthrough */ default: fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported number of components: %lu", nchans); } } } fz_catch(ctx) { fz_drop_colorspace(ctx, state->cs); JP2_Decompress_End(doc); fz_rethrow(ctx); } if (onlymeta) { JP2_Decompress_End(doc); return NULL; } fz_try(ctx) { state->pix = fz_new_pixmap(ctx, state->cs, state->width, state->height, NULL, alphas); fz_clear_pixmap_with_value(ctx, state->pix, 0); if (HAS_PALETTE(colorspace)) { if (!fz_colorspace_is_indexed(ctx, state->cs)) state->expand_indexed = 1; err = JP2_Decompress_GetPalette(doc, &state->palette); if (err != cJP2_Error_OK) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot get indexed palette: %d", (int) err); /* no available sample file */ for (k = 0; k < state->palette->ulChannels; k++) if (state->palette->pucSignedSample[k]) fz_throw(ctx, FZ_ERROR_GENERIC, "signed palette components not yet supported"); } for (k = 0; k < nchans; k++) { state->hstep[k] = (state->width + (widths[k] - 1)) / widths[k]; state->vstep[k] = (state->height + (heights[k] - 1)) / heights[k]; if (HAS_PALETTE(colorspace)) { state->bpss[k] = state->palette->pucBitsPerSample[k]; state->signs[k] = state->palette->pucSignedSample[k]; } else { err = JP2_Decompress_GetProp(doc, cJP2_Prop_Bits_Per_Sample, &state->bpss[k], -1, k); if (err != cJP2_Error_OK) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot get bits per sample for component %d: %d", (int) k, (int) err); err = JP2_Decompress_GetProp(doc, cJP2_Prop_Signed_Samples, &state->signs[k], -1, k); if (err != cJP2_Error_OK) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot get signed for component %d: %d", (int) k, (int) err); } if (state->signs[k]) state->signs[k] = 1 << (state->bpss[k] - 1); } err = JP2_Decompress_SetProp(doc, cJP2_Prop_Output_Parameter, (JP2_Property_Value) state); if (err != cJP2_Error_OK) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot set write callback userdata: %d", (int) err); err = JP2_Decompress_SetProp(doc, cJP2_Prop_Output_Function, (JP2_Property_Value) jpx_write); if (err != cJP2_Error_OK) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot set write callback: %d", (int) err); err = JP2_Decompress_Image(doc); if (err != cJP2_Error_OK) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot decode image: %d", (int) err); if (colorspace == cJP2_Colorspace_RGB_YCCa) jpx_ycc_to_rgb(ctx, state->pix, !state->signs[1], !state->signs[2]); if (state->pix->alpha && ! (HAS_PALETTE(colorspace) && !state->expand_indexed)) { if (alphas > 0 && prealphas == 0) fz_premultiply_pixmap(ctx, state->pix); } } fz_always(ctx) { fz_drop_colorspace(ctx, state->cs); JP2_Decompress_End(doc); } fz_catch(ctx) { fz_drop_pixmap(ctx, state->pix); fz_rethrow(ctx); } return state->pix; } fz_pixmap * fz_load_jpx(fz_context *ctx, const unsigned char *data, size_t size, fz_colorspace *defcs) { fz_jpxd state = { 0 }; return jpx_read_image(ctx, &state, data, size, defcs, 0); } void fz_load_jpx_info(fz_context *ctx, const unsigned char *data, size_t size, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep) { fz_jpxd state = { 0 }; jpx_read_image(ctx, &state, data, size, NULL, 1); *cspacep = state.cs; *wp = state.width; *hp = state.height; *xresp = state.xres; *yresp = state.yres; } #else /* HAVE_LURATECH */ #include struct fz_jpxd_s { int width; int height; fz_colorspace *cs; int xres; int yres; }; struct stream_block_s { const unsigned char *data; OPJ_SIZE_T size; OPJ_SIZE_T pos; }; /* OpenJPEG does not provide a safe mechanism to intercept * allocations. In the latest version all allocations go * though opj_malloc etc, but no context is passed around. * * In order to ensure that allocations throughout mupdf * are done consistently, we implement opj_malloc etc as * functions that call down to fz_malloc etc. These * require context variables, so we lock and unlock around * calls to openjpeg. Any attempt to call through * without setting these will be detected. * * It is therefore vital that any fz_lock/fz_unlock * handlers are shared between all the fz_contexts in * use at a time. */ /* Potentially we can write different versions * of get_context and set_context for different * threading systems. */ static fz_context *opj_secret = NULL; static void set_opj_context(fz_context *ctx) { opj_secret = ctx; } static fz_context *get_opj_context(void) { return opj_secret; } void opj_lock(fz_context *ctx) { fz_lock(ctx, FZ_LOCK_FREETYPE); set_opj_context(ctx); } void opj_unlock(fz_context *ctx) { set_opj_context(NULL); fz_unlock(ctx, FZ_LOCK_FREETYPE); } void *opj_malloc(size_t size) { fz_context *ctx = get_opj_context(); assert(ctx != NULL); return fz_malloc_no_throw(ctx, size); } void *opj_calloc(size_t n, size_t size) { fz_context *ctx = get_opj_context(); assert(ctx != NULL); return fz_calloc_no_throw(ctx, n, size); } void *opj_realloc(void *ptr, size_t size) { fz_context *ctx = get_opj_context(); assert(ctx != NULL); return fz_realloc_no_throw(ctx, ptr, size); } void opj_free(void *ptr) { fz_context *ctx = get_opj_context(); assert(ctx != NULL); fz_free(ctx, ptr); } static void * opj_aligned_malloc_n(size_t alignment, size_t size) { uint8_t *ptr; int off; if (size == 0) return NULL; size += alignment + sizeof(uint8_t); ptr = opj_malloc(size); if (ptr == NULL) return NULL; off = alignment-(((int)(intptr_t)ptr) & (alignment - 1)); ptr[off-1] = off; return ptr + off; } void * opj_aligned_malloc(size_t size) { return opj_aligned_malloc_n(16, size); } void * opj_aligned_32_malloc(size_t size) { return opj_aligned_malloc_n(32, size); } void opj_aligned_free(void* ptr_) { uint8_t *ptr = (uint8_t *)ptr_; uint8_t off; if (ptr == NULL) return; off = ptr[-1]; opj_free((void *)(((unsigned char *)ptr) - off)); } #if 0 /* UNUSED currently, and moderately tricky, so deferred until required */ void * opj_aligned_realloc(void *ptr, size_t size) { return opj_realloc(ptr, size); } #endif static void fz_opj_error_callback(const char *msg, void *client_data) { fz_context *ctx = (fz_context *)client_data; char buf[200]; int n; fz_strlcpy(buf, msg, sizeof buf); n = strlen(buf); if (buf[n-1] == '\n') buf[n-1] = 0; fz_warn(ctx, "openjpeg error: %s", buf); } static void fz_opj_warning_callback(const char *msg, void *client_data) { fz_context *ctx = (fz_context *)client_data; char buf[200]; int n; fz_strlcpy(buf, msg, sizeof buf); n = strlen(buf); if (buf[n-1] == '\n') buf[n-1] = 0; fz_warn(ctx, "openjpeg warning: %s", buf); } static void fz_opj_info_callback(const char *msg, void *client_data) { /* fz_warn("openjpeg info: %s", msg); */ } static OPJ_SIZE_T fz_opj_stream_read(void * p_buffer, OPJ_SIZE_T p_nb_bytes, void * p_user_data) { stream_block *sb = (stream_block *)p_user_data; OPJ_SIZE_T len; len = sb->size - sb->pos; if (len == 0) return (OPJ_SIZE_T)-1; /* End of file! */ if (len > p_nb_bytes) len = p_nb_bytes; memcpy(p_buffer, sb->data + sb->pos, len); sb->pos += len; return len; } static OPJ_OFF_T fz_opj_stream_skip(OPJ_OFF_T skip, void * p_user_data) { stream_block *sb = (stream_block *)p_user_data; if (skip > (OPJ_OFF_T)(sb->size - sb->pos)) skip = (OPJ_OFF_T)(sb->size - sb->pos); sb->pos += skip; return sb->pos; } static OPJ_BOOL fz_opj_stream_seek(OPJ_OFF_T seek_pos, void * p_user_data) { stream_block *sb = (stream_block *)p_user_data; if (seek_pos > (OPJ_OFF_T)sb->size) return OPJ_FALSE; sb->pos = seek_pos; return OPJ_TRUE; } static fz_pixmap * jpx_read_image(fz_context *ctx, fz_jpxd *state, const unsigned char *data, size_t size, fz_colorspace *defcs, int onlymeta) { fz_pixmap *img = NULL; opj_dparameters_t params; opj_codec_t *codec; opj_image_t *jpx; opj_stream_t *stream; OPJ_CODEC_FORMAT format; int a, n, k; OPJ_UINT32 w, h; OPJ_UINT32 x, y; stream_block sb; OPJ_UINT32 i; fz_var(img); if (size < 2) fz_throw(ctx, FZ_ERROR_GENERIC, "not enough data to determine image format"); /* Check for SOC marker -- if found we have a bare J2K stream */ if (data[0] == 0xFF && data[1] == 0x4F) format = OPJ_CODEC_J2K; else format = OPJ_CODEC_JP2; opj_set_default_decoder_parameters(¶ms); if (fz_colorspace_is_indexed(ctx, defcs)) params.flags |= OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG; codec = opj_create_decompress(format); opj_set_info_handler(codec, fz_opj_info_callback, ctx); opj_set_warning_handler(codec, fz_opj_warning_callback, ctx); opj_set_error_handler(codec, fz_opj_error_callback, ctx); if (!opj_setup_decoder(codec, ¶ms)) { opj_destroy_codec(codec); fz_throw(ctx, FZ_ERROR_GENERIC, "j2k decode failed"); } stream = opj_stream_default_create(OPJ_TRUE); sb.data = data; sb.pos = 0; sb.size = size; opj_stream_set_read_function(stream, fz_opj_stream_read); opj_stream_set_skip_function(stream, fz_opj_stream_skip); opj_stream_set_seek_function(stream, fz_opj_stream_seek); opj_stream_set_user_data(stream, &sb, NULL); /* Set the length to avoid an assert */ opj_stream_set_user_data_length(stream, size); if (!opj_read_header(stream, codec, &jpx)) { opj_stream_destroy(stream); opj_destroy_codec(codec); fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to read JPX header"); } if (!opj_decode(codec, stream, jpx)) { opj_stream_destroy(stream); opj_destroy_codec(codec); opj_image_destroy(jpx); fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to decode JPX image"); } opj_stream_destroy(stream); opj_destroy_codec(codec); /* jpx should never be NULL here, but check anyway */ if (!jpx) fz_throw(ctx, FZ_ERROR_GENERIC, "opj_decode failed"); /* Count number of alpha and color channels */ n = a = 0; for (i = 0; i < jpx->numcomps; ++i) { if (jpx->comps[i].alpha) ++a; else ++n; } for (k = 1; k < n + a; k++) { if (!jpx->comps[k].data) { opj_image_destroy(jpx); fz_throw(ctx, FZ_ERROR_GENERIC, "image components are missing data"); } } state->width = w = jpx->x1 - jpx->x0; state->height = h = jpx->y1 - jpx->y0; state->xres = 72; /* openjpeg does not read the JPEG 2000 resc box */ state->yres = 72; /* openjpeg does not read the JPEG 2000 resc box */ state->cs = NULL; if (defcs) { if (defcs->n == n) state->cs = fz_keep_colorspace(ctx, defcs); else fz_warn(ctx, "jpx file and dict colorspace do not match"); } #if FZ_ENABLE_ICC if (!state->cs && jpx->icc_profile_buf) { fz_buffer *cbuf = NULL; fz_var(cbuf); fz_try(ctx) { cbuf = fz_new_buffer_from_copied_data(ctx, jpx->icc_profile_buf, jpx->icc_profile_len); state->cs = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_NONE, 0, NULL, cbuf); } fz_always(ctx) fz_drop_buffer(ctx, cbuf); fz_catch(ctx) fz_warn(ctx, "ignoring embedded ICC profile in JPX"); if (state->cs && state->cs->n != n) { fz_warn(ctx, "invalid number of components in ICC profile, ignoring ICC profile in JPX"); fz_drop_colorspace(ctx, state->cs); state->cs = NULL; } } #endif if (!state->cs) { switch (n) { case 1: state->cs = fz_keep_colorspace(ctx, fz_device_gray(ctx)); break; case 3: state->cs = fz_keep_colorspace(ctx, fz_device_rgb(ctx)); break; case 4: state->cs = fz_keep_colorspace(ctx, fz_device_cmyk(ctx)); break; default: { opj_image_destroy(jpx); fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported number of components: %d", n); } } } if (onlymeta) { opj_image_destroy(jpx); return NULL; } fz_try(ctx) { unsigned char *samples; int stride, comps; a = !!a; /* ignore any superfluous alpha channels */ img = fz_new_pixmap(ctx, state->cs, w, h, NULL, a); stride = fz_pixmap_stride(ctx, img); comps = fz_pixmap_components(ctx, img); samples = fz_pixmap_samples(ctx, img); fz_clear_pixmap_with_value(ctx, img, 0); for (k = 0; k < comps; k++) { opj_image_comp_t *comp = &(jpx->comps[k]); int oy = comp->y0 * comp->dy - jpx->y0; int ox = comp->x0 * comp->dx - jpx->x0; if (comp->data == NULL) fz_throw(ctx, FZ_ERROR_GENERIC, "No data for JP2 image component %d", k); for (y = 0; y < comp->h; y++) { for (x = 0; x < comp->w; x++) { OPJ_INT32 v; OPJ_UINT32 dx, dy; v = comp->data[y * comp->w + x]; if (comp->sgnd) v = v + (1 << (comp->prec - 1)); if (comp->prec > 8) v = v >> (comp->prec - 8); else if (comp->prec < 8) v = v << (8 - comp->prec); for (dy = 0; dy < comp->dy; dy++) { for (dx = 0; dx < comp->dx; dx++) { OPJ_UINT32 xx = ox + x * comp->dx + dx; OPJ_UINT32 yy = oy + y * comp->dy + dy; if (xx < w && yy < h) samples[yy * stride + xx * comps + k] = v; } } } } } if (jpx->color_space == OPJ_CLRSPC_SYCC && n == 3 && a == 0) jpx_ycc_to_rgb(ctx, img, 1, 1); if (a) fz_premultiply_pixmap(ctx, img); } fz_always(ctx) { fz_drop_colorspace(ctx, state->cs); opj_image_destroy(jpx); } fz_catch(ctx) { fz_drop_pixmap(ctx, img); fz_rethrow(ctx); } return img; } fz_pixmap * fz_load_jpx(fz_context *ctx, const unsigned char *data, size_t size, fz_colorspace *defcs) { fz_jpxd state = { 0 }; fz_pixmap *pix = NULL; fz_try(ctx) { opj_lock(ctx); pix = jpx_read_image(ctx, &state, data, size, defcs, 0); } fz_always(ctx) opj_unlock(ctx); fz_catch(ctx) fz_rethrow(ctx); return pix; } void fz_load_jpx_info(fz_context *ctx, const unsigned char *data, size_t size, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep) { fz_jpxd state = { 0 }; fz_try(ctx) { opj_lock(ctx); jpx_read_image(ctx, &state, data, size, NULL, 1); } fz_always(ctx) opj_unlock(ctx); fz_catch(ctx) fz_rethrow(ctx); *cspacep = state.cs; *wp = state.width; *hp = state.height; *xresp = state.xres; *yresp = state.yres; } #endif /* HAVE_LURATECH */ #else /* FZ_ENABLE_JPX */ fz_pixmap * fz_load_jpx(fz_context *ctx, const unsigned char *data, size_t size, fz_colorspace *defcs) { fz_throw(ctx, FZ_ERROR_GENERIC, "JPX support disabled"); } void fz_load_jpx_info(fz_context *ctx, const unsigned char *data, size_t size, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep) { fz_throw(ctx, FZ_ERROR_GENERIC, "JPX support disabled"); } #endif