eBookReaderSwitch/mupdf/source/fitz/load-jpx.c

981 lines
23 KiB
C
Raw Normal View History

#include "mupdf/fitz.h"
#include "fitz-imp.h"
#include <assert.h>
#include <string.h>
#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 <lwf_jp2.h>
#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 <openjpeg.h>
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(&params);
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, &params))
{
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