summaryrefslogtreecommitdiff
path: root/source/fitz/load-jpx.c
diff options
context:
space:
mode:
authorSebastian Rasmussen <sebras@gmail.com>2016-06-21 10:32:11 +0200
committerSebastian Rasmussen <sebras@gmail.com>2016-07-06 11:52:22 +0800
commitb03381add14f1c22662402d581024308335ec4f9 (patch)
tree94f9b37c370b70829caaed872a4a0230ab893e01 /source/fitz/load-jpx.c
parent10c8945851bf76bd22601411d2708fe2ff1bbea6 (diff)
downloadmupdf-b03381add14f1c22662402d581024308335ec4f9.tar.xz
Add optional support for Luratech JPEG2000 decoder.
Diffstat (limited to 'source/fitz/load-jpx.c')
-rw-r--r--source/fitz/load-jpx.c480
1 files changed, 479 insertions, 1 deletions
diff --git a/source/fitz/load-jpx.c b/source/fitz/load-jpx.c
index c1ed831a..a79a268e 100644
--- a/source/fitz/load-jpx.c
+++ b/source/fitz/load-jpx.c
@@ -1,5 +1,480 @@
#include "mupdf/fitz.h"
+#ifdef HAVE_LURATECH
+
+#include <lwf_jp2.h>
+
+typedef struct fz_jpxd_s fz_jpxd;
+
+struct fz_jpxd_s
+{
+ JP2_Decomp_Handle doc;
+ fz_context *ctx;
+ fz_pixmap *pix;
+ JP2_Palette_Params *palette;
+ JP2_Colorspace colorspace;
+ unsigned char *data;
+ int size;
+ JP2_Property_Value width;
+ JP2_Property_Value height;
+ fz_colorspace *cs;
+ int expand_indexed;
+ unsigned long xres;
+ unsigned long yres;
+
+ JP2_Property_Value nchans;
+ JP2_Property_Value *widths;
+ JP2_Property_Value *heights;
+ JP2_Property_Value *hstep;
+ JP2_Property_Value *vstep;
+ JP2_Property_Value *bpss;
+ JP2_Property_Value *signs;
+};
+
+static void * JP2_Callback_Conv
+jpx_alloc(long size, JP2_Callback_Param param)
+{
+ fz_jpxd *state = (fz_jpxd *) param;
+ return fz_malloc(state->ctx, size);
+}
+
+static JP2_Error JP2_Callback_Conv
+jpx_free(void *ptr, JP2_Callback_Param param)
+{
+ fz_jpxd *state = (fz_jpxd *) param;
+ fz_free(state->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)
+{
+ fz_jpxd *state = (fz_jpxd *) param;
+
+ if (ulPos < 0 || ulPos >= state->size)
+ return 0;
+
+ ulSize = fz_mini(ulSize, state->size - ulPos);
+ memcpy(pucData, &state->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;
+ unsigned char *row;
+ int x, y, i;
+
+ if (ulRow >= state->pix->h || ulStart >= state->pix->w || sComponent >= state->pix->n)
+ return cJP2_Error_OK;
+
+ ulNum = fz_mini(ulNum, state->pix->w - ulStart);
+
+ if (state->palette)
+ {
+
+ row = &state->pix->samples[state->pix->stride * ulRow * state->vstep[sComponent] +
+ state->pix->n * ulStart * state->hstep[sComponent] +
+ sComponent];
+
+ for (y = 0; ulRow * state->vstep[sComponent] + y < state->pix->h && y < state->vstep[sComponent]; y++)
+ {
+ unsigned char *p = row;
+
+ for (i = 0; i < ulNum; i++)
+ {
+ for (x = 0; (ulStart + i) * state->hstep[sComponent] + x < state->pix->w && x < state->hstep[sComponent]; x++)
+ {
+ unsigned char v = fz_clampi(pucData[i], 0, state->palette->ulEntries);
+
+ if (state->expand_indexed)
+ {
+ int k;
+ for (k = 0; k < state->pix->n; k++)
+ p[k] = state->palette->ppulPalette[k][v];
+ p += state->pix->n;
+ }
+ else
+ {
+ *p= v;
+ p++;
+ }
+ }
+ }
+
+ row += state->pix->stride;
+ }
+ }
+ else
+ {
+ unsigned int signedoffset;
+
+ if (state->signs[sComponent])
+ signedoffset = 1 << (state->bpss[sComponent] - 1);
+ else
+ signedoffset = 0;
+
+ row = &state->pix->samples[state->pix->stride * ulRow * state->vstep[sComponent] +
+ state->pix->n * ulStart * state->hstep[sComponent] +
+ sComponent];
+
+ if (state->bpss[sComponent] > 8)
+ {
+ for (y = 0; ulRow * state->vstep[sComponent] + y < state->pix->h && y < state->vstep[sComponent]; y++)
+ {
+ unsigned char *p = row;
+
+ for (i = 0; i < ulNum; i++)
+ {
+ for (x = 0; (ulStart + i) * state->hstep[sComponent] + x < state->pix->w && x < state->hstep[sComponent]; x++)
+ {
+ unsigned int v = (pucData[2 * i + 1] << 8) | pucData[2 * i + 0];
+ v &= (1 << state->bpss[sComponent]) - 1;
+ v -= signedoffset;
+ *p = v >> (state->bpss[sComponent] - 8);
+ p += state->pix->n;
+ }
+ }
+
+ row += state->pix->stride;
+ }
+ }
+ else if (state->bpss[sComponent] == 8)
+ {
+ for (y = 0; ulRow * state->vstep[sComponent] + y < state->pix->h && y < state->vstep[sComponent]; y++)
+ {
+ unsigned char *p = row;
+
+ for (i = 0; i < ulNum; i++)
+ {
+ for (x = 0; (ulStart + i) * state->hstep[sComponent] + x < state->pix->w && x < state->hstep[sComponent]; x++)
+ {
+ unsigned int v = pucData[i];
+ v &= (1 << state->bpss[sComponent]) - 1;
+ v -= signedoffset;
+ *p = v;
+ p += state->pix->n;
+ }
+ }
+
+ row += state->pix->stride;
+ }
+ }
+ else
+ {
+ for (y = 0; ulRow * state->vstep[sComponent] + y < state->pix->h && y < state->vstep[sComponent]; y++)
+ {
+ unsigned char *p = row;
+
+ for (i = 0; i < ulNum; i++)
+ {
+ for (x = 0; (ulStart + i) * state->hstep[sComponent] + x < state->pix->w && x < state->hstep[sComponent]; x++)
+ {
+ unsigned int v = pucData[i];
+ v &= (1 << state->bpss[sComponent]) - 1;
+ v -= signedoffset;
+ *p = v << (8 - state->bpss[sComponent]);
+ p += state->pix->n;
+ }
+ }
+
+ row += state->pix->stride;
+ }
+ }
+ }
+
+ return cJP2_Error_OK;
+}
+
+static void
+jpx_ycc_to_rgb(fz_context *ctx, fz_jpxd *state)
+{
+ int x, y;
+
+ for (y = 0; y < state->height; y++)
+ {
+ unsigned char * row = &state->pix->samples[state->pix->stride * y];
+ for (x = 0; x < state->width; x++)
+ {
+ int ycc[3];
+ ycc[0] = row[x * 3 + 0];
+ ycc[1] = row[x * 3 + 1];
+ ycc[2] = row[x * 3 + 2];
+
+ /* conciously skip Y */
+ if (!state->signs[1])
+ ycc[1] -= 128;
+ if (!state->signs[2])
+ ycc[2] -= 128;
+
+ row[x * 3 + 0] = fz_clampi((double)ycc[0] + 1.402 * ycc[2], 0, 255);
+ row[x * 3 + 1] = fz_clampi((double)ycc[0] - 0.34413 * ycc[1] - 0.71414 * ycc[2], 0, 255);
+ row[x * 3 + 2] = fz_clampi((double)ycc[0] + 1.772 * ycc[1], 0, 255);
+ }
+ }
+
+}
+
+struct indexed
+{
+ fz_colorspace *base;
+ int high;
+ unsigned char *lookup;
+};
+
+static fz_pixmap *
+jpx_read_image(fz_context *ctx, fz_jpxd *state, unsigned char *data, size_t size, fz_colorspace *defcs, int indexed, int onlymeta)
+{
+ JP2_Channel_Def_Params *chans = NULL;
+ JP2_Error err;
+ int k, colors, alphas, prealphas;
+
+ memset(state, 0x00, sizeof (fz_jpxd));
+ state->ctx = ctx;
+ state->data = data;
+ state->size = size;
+
+ fz_try(ctx)
+ {
+ err = JP2_Decompress_Start(&state->doc,
+ jpx_alloc, (JP2_Callback_Param) state,
+ jpx_free, (JP2_Callback_Param) state,
+ jpx_read, (JP2_Callback_Param) state);
+ 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(state->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(state->doc, cJP2_Prop_Extern_Colorspace, (unsigned long *) &state->colorspace, -1, -1);
+ if (err != cJP2_Error_OK)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot get colorspace: %d", (int) err);
+
+ if (state->colorspace == cJP2_Colorspace_Palette_Gray ||
+ state->colorspace == cJP2_Colorspace_Palette_RGBa ||
+ state->colorspace == cJP2_Colorspace_Palette_RGB_YCCa ||
+ state->colorspace == cJP2_Colorspace_Palette_CIE_LABa ||
+ state->colorspace == cJP2_Colorspace_Palette_ICCa ||
+ state->colorspace == cJP2_Colorspace_Palette_CMYKa)
+ {
+ err = JP2_Decompress_GetPalette(state->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 compoments not yet supported");
+ }
+
+ err = JP2_Decompress_GetChannelDefs(state->doc, &chans, &state->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 < state->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, 4);
+ alphas = fz_clampi(alphas, 0, 1);
+
+ state->nchans = colors + alphas;
+
+ state->widths = fz_malloc(ctx, state->nchans * sizeof (JP2_Property_Value));
+ state->heights = fz_malloc(ctx, state->nchans * sizeof (JP2_Property_Value));
+ state->hstep = fz_malloc(ctx, state->nchans * sizeof (JP2_Property_Value));
+ state->vstep = fz_malloc(ctx, state->nchans * sizeof (JP2_Property_Value));
+ state->bpss = fz_malloc(ctx, state->nchans * sizeof (JP2_Property_Value));
+ state->signs = fz_malloc(ctx, state->nchans * sizeof (JP2_Property_Value));
+
+ if (state->palette)
+ {
+ err = JP2_Decompress_GetProp(state->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(state->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 < state->nchans; k++)
+ {
+ state->widths[k] = state->width;
+ state->heights[k] = state->height;
+ state->bpss[k] = state->palette->pucBitsPerSample[k];
+ state->signs[k] = state->palette->pucSignedSample[k];
+ }
+ }
+ else
+ {
+ for (k = 0; k < state->nchans; k++)
+ {
+ err = JP2_Decompress_GetProp(state->doc, cJP2_Prop_Width, &state->widths[k], -1, k);
+ if (err != cJP2_Error_OK)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot get width for compoment %d: %d", k, (int) err);
+ err = JP2_Decompress_GetProp(state->doc, cJP2_Prop_Height, &state->heights[k], -1, k);
+ if (err != cJP2_Error_OK)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot get height for compomment %d: %d", k, (int) err);
+ err = JP2_Decompress_GetProp(state->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 compomment %d: %d", k, (int) err);
+ err = JP2_Decompress_GetProp(state->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 compomment %d: %d", k, (int) err);
+
+ state->width = fz_maxi(state->width, state->widths[k]);
+ state->height = fz_maxi(state->height, state->heights[k]);
+ }
+ }
+
+ for (k = 0; k < state->nchans; k++)
+ {
+ state->hstep[k] = (state->width + (state->widths[k] - 1)) / state->widths[k];
+ state->vstep[k] = (state->height + (state->heights[k] - 1)) / state->heights[k];
+ }
+
+ err = JP2_Decompress_GetResolution(state->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;
+
+ if (defcs)
+ {
+ if (defcs->n == state->nchans)
+ {
+ state->cs = defcs;
+ }
+ else
+ {
+ fz_warn(ctx, "jpx file (%lu) and dict colorspace (%d, %s) do not match", state->nchans, defcs->n, defcs->name);
+ defcs = NULL;
+ }
+ }
+
+ if (!defcs)
+ {
+ switch (colors)
+ {
+ case 4: state->cs = fz_device_cmyk(ctx); break;
+ case 3: if (state->colorspace == cJP2_Colorspace_CIE_LABa)
+ state->cs = fz_device_lab(ctx);
+ else
+ state->cs = fz_device_rgb(ctx);
+ break;
+ case 1: state->cs = fz_device_gray(ctx); break;
+ case 0: if (alphas == 1)
+ {
+ /* alpha only images are rendered as grayscale */
+ state->cs = fz_device_gray(ctx);
+ colors = 1;
+ alphas = 0;
+ break;
+ }
+ /* fallthrough */
+ default: fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported number of components: %lu", state->nchans);
+ }
+ }
+
+ if (state->palette && !fz_colorspace_is_indexed(ctx, state->cs))
+ state->expand_indexed = 1;
+
+ if (!onlymeta)
+ {
+ state->pix = fz_new_pixmap(ctx, state->cs, state->width, state->height, alphas);
+ fz_clear_pixmap_with_value(ctx, state->pix, 0);
+
+ err = JP2_Decompress_SetProp(state->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(state->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(state->doc);
+ if (err != cJP2_Error_OK)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot decode image: %d", (int) err);
+
+ if (state->colorspace == cJP2_Colorspace_RGB_YCCa)
+ jpx_ycc_to_rgb(ctx, state);
+
+ if (state->pix->alpha && ! (state->palette && !state->expand_indexed))
+ {
+ if (state->pix->n == 5)
+ {
+ fz_pixmap *tmp = fz_new_pixmap(ctx, fz_device_rgb(ctx), state->pix->w, state->pix->h, 1);
+ fz_convert_pixmap(ctx, tmp, state->pix);
+ fz_drop_pixmap(ctx, state->pix);
+ state->pix = tmp;
+ }
+
+ if (alphas > 0 && prealphas == 0)
+ fz_premultiply_pixmap(ctx, state->pix);
+ }
+
+ }
+ }
+ fz_always(ctx)
+ {
+ JP2_Decompress_End(state->doc);
+ fz_free(ctx, state->signs);
+ fz_free(ctx, state->widths);
+ fz_free(ctx, state->heights);
+ fz_free(ctx, state->hstep);
+ fz_free(ctx, state->vstep);
+ fz_free(ctx, state->bpss);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+
+ return state->pix;
+}
+
+fz_pixmap *
+fz_load_jpx(fz_context *ctx, unsigned char *data, size_t size, fz_colorspace *defcs, int indexed)
+{
+ fz_jpxd state = { 0 };
+
+ return jpx_read_image(ctx, &state, data, size, defcs, indexed, 0);
+}
+
+void
+fz_load_jpx_info(fz_context *ctx, 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, 0, 1);
+
+ *cspacep = state.cs;
+ *wp = state.width;
+ *hp = state.height;
+ *xresp = state.xres;
+ *yresp = state.yres;
+}
+
+#else /* HAVE_LURATECH */
+
/* Without the definition of OPJ_STATIC, compilation fails on windows
* due to the use of __stdcall. We believe it is required on some
* linux toolchains too. */
@@ -185,7 +660,7 @@ jpx_read_image(fz_context *ctx, unsigned char *data, size_t size, fz_colorspace
}
else
{
- fz_warn(ctx, "jpx file and dict colorspaces do not match");
+ fz_warn(ctx, "jpx file and dict colorspace do not match");
defcs = NULL;
}
}
@@ -197,6 +672,7 @@ jpx_read_image(fz_context *ctx, unsigned char *data, size_t size, fz_colorspace
case 1: colorspace = fz_device_gray(ctx); break;
case 3: colorspace = fz_device_rgb(ctx); break;
case 4: colorspace = fz_device_cmyk(ctx); break;
+ default: fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported number of components: %d", n);
}
}
@@ -268,3 +744,5 @@ fz_load_jpx_info(fz_context *ctx, unsigned char *data, size_t size, int *wp, int
*xresp = 72; /* openjpeg does not read the JPEG 2000 resc box */
*yresp = 72; /* openjpeg does not read the JPEG 2000 resc box */
}
+
+#endif /* HAVE_LURATECH */