From 8ee41078494cc082cfbcf02521ab86b30ea61612 Mon Sep 17 00:00:00 2001 From: David Joy Date: Tue, 15 Apr 2014 14:37:36 -0400 Subject: [PATCH 1/5] Add 16bit encoder --- libImaging/Jpeg2KEncode.c | 55 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/libImaging/Jpeg2KEncode.c b/libImaging/Jpeg2KEncode.c index c1e16e97f..4aaefd60a 100644 --- a/libImaging/Jpeg2KEncode.c +++ b/libImaging/Jpeg2KEncode.c @@ -88,6 +88,38 @@ j2k_pack_l(Imaging im, UINT8 *buf, } } +static void +j2k_pack_i16(Imaging im, UINT8 *buf, + unsigned x0, unsigned y0, unsigned w, unsigned h) +{ + UINT8 *ptr = buf; + unsigned x,y; + for (y = 0; y < h; ++y) { + UINT8 *data = (UINT8 *)(im->image[y + y0] + x0); + for (x = 0; x < w; ++x) { + *ptr++ = data[1]; + *ptr++ = data[0]; + data += 2; + } + } +} + +static void +j2k_pack_i16b(Imaging im, UINT8 *buf, + unsigned x0, unsigned y0, unsigned w, unsigned h) +{ + UINT8 *ptr = buf; + unsigned x,y; + for (y = 0; y < h; ++y) { + UINT8 *data = (UINT8 *)(im->image[y + y0] + x0); + for (x = 0; x < w; ++x) { + *ptr++ = data[0]; + *ptr++ = data[1]; + data += 2; + } + } +} + static void j2k_pack_la(Imaging im, UINT8 *buf, unsigned x0, unsigned y0, unsigned w, unsigned h) @@ -247,6 +279,9 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, j2k_pack_tile_t pack; int ret = -1; + unsigned prec = 8; + unsigned bpp = 8; + stream = opj_stream_default_create(OPJ_FALSE); if (!stream) { @@ -266,6 +301,18 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, components = 1; color_space = OPJ_CLRSPC_GRAY; pack = j2k_pack_l; + } else if (strcmp (im->mode, "I;16") == 0) { + components = 1; + color_space = OPJ_CLRSPC_GRAY; + pack = j2k_pack_i16; + prec = 16; + bpp = 16; + } else if (strcmp (im->mode, "I;16B") == 0) { + components = 1; + color_space = OPJ_CLRSPC_GRAY; + pack = j2k_pack_i16b; + prec = 16; + bpp = 16; } else if (strcmp (im->mode, "LA") == 0) { components = 2; color_space = OPJ_CLRSPC_GRAY; @@ -293,8 +340,8 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, image_params[n].w = im->xsize; image_params[n].h = im->ysize; image_params[n].x0 = image_params[n].y0 = 0; - image_params[n].prec = 8; - image_params[n].bpp = 8; + image_params[n].prec = prec; + image_params[n].bpp = bpp; image_params[n].sgnd = 0; } @@ -437,7 +484,7 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, num_tiles = tiles_x * tiles_y; - state->buffer = malloc (tile_width * tile_height * components); + state->buffer = malloc (tile_width * tile_height * components * prec / 8); tile_ndx = 0; for (y = 0; y < tiles_y; ++y) { @@ -469,7 +516,7 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, pack(im, state->buffer, pixx, pixy, pixw, pixh); - data_size = pixw * pixh * components; + data_size = pixw * pixh * components * prec / 8; if (!opj_write_tile(codec, tile_ndx++, state->buffer, data_size, stream)) { From 48ecc9eacb36649440b20d1c6ed7f0f81b94bda9 Mon Sep 17 00:00:00 2001 From: David Joy Date: Tue, 15 Apr 2014 16:42:15 -0400 Subject: [PATCH 2/5] Switch bits per pixel from 16 to 12 Makes GIMP happy... still not round tripping --- libImaging/Jpeg2KEncode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libImaging/Jpeg2KEncode.c b/libImaging/Jpeg2KEncode.c index 4aaefd60a..502a0de68 100644 --- a/libImaging/Jpeg2KEncode.c +++ b/libImaging/Jpeg2KEncode.c @@ -306,13 +306,13 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, color_space = OPJ_CLRSPC_GRAY; pack = j2k_pack_i16; prec = 16; - bpp = 16; + bpp = 12; } else if (strcmp (im->mode, "I;16B") == 0) { components = 1; color_space = OPJ_CLRSPC_GRAY; pack = j2k_pack_i16b; prec = 16; - bpp = 16; + bpp = 12; } else if (strcmp (im->mode, "LA") == 0) { components = 2; color_space = OPJ_CLRSPC_GRAY; From fd4a9963a8114a6918255eef65ce491039eeafee Mon Sep 17 00:00:00 2001 From: David Joy Date: Tue, 15 Apr 2014 14:37:36 -0400 Subject: [PATCH 3/5] Add 16bit encoder --- libImaging/Jpeg2KEncode.c | 55 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/libImaging/Jpeg2KEncode.c b/libImaging/Jpeg2KEncode.c index c1e16e97f..4aaefd60a 100644 --- a/libImaging/Jpeg2KEncode.c +++ b/libImaging/Jpeg2KEncode.c @@ -88,6 +88,38 @@ j2k_pack_l(Imaging im, UINT8 *buf, } } +static void +j2k_pack_i16(Imaging im, UINT8 *buf, + unsigned x0, unsigned y0, unsigned w, unsigned h) +{ + UINT8 *ptr = buf; + unsigned x,y; + for (y = 0; y < h; ++y) { + UINT8 *data = (UINT8 *)(im->image[y + y0] + x0); + for (x = 0; x < w; ++x) { + *ptr++ = data[1]; + *ptr++ = data[0]; + data += 2; + } + } +} + +static void +j2k_pack_i16b(Imaging im, UINT8 *buf, + unsigned x0, unsigned y0, unsigned w, unsigned h) +{ + UINT8 *ptr = buf; + unsigned x,y; + for (y = 0; y < h; ++y) { + UINT8 *data = (UINT8 *)(im->image[y + y0] + x0); + for (x = 0; x < w; ++x) { + *ptr++ = data[0]; + *ptr++ = data[1]; + data += 2; + } + } +} + static void j2k_pack_la(Imaging im, UINT8 *buf, unsigned x0, unsigned y0, unsigned w, unsigned h) @@ -247,6 +279,9 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, j2k_pack_tile_t pack; int ret = -1; + unsigned prec = 8; + unsigned bpp = 8; + stream = opj_stream_default_create(OPJ_FALSE); if (!stream) { @@ -266,6 +301,18 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, components = 1; color_space = OPJ_CLRSPC_GRAY; pack = j2k_pack_l; + } else if (strcmp (im->mode, "I;16") == 0) { + components = 1; + color_space = OPJ_CLRSPC_GRAY; + pack = j2k_pack_i16; + prec = 16; + bpp = 16; + } else if (strcmp (im->mode, "I;16B") == 0) { + components = 1; + color_space = OPJ_CLRSPC_GRAY; + pack = j2k_pack_i16b; + prec = 16; + bpp = 16; } else if (strcmp (im->mode, "LA") == 0) { components = 2; color_space = OPJ_CLRSPC_GRAY; @@ -293,8 +340,8 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, image_params[n].w = im->xsize; image_params[n].h = im->ysize; image_params[n].x0 = image_params[n].y0 = 0; - image_params[n].prec = 8; - image_params[n].bpp = 8; + image_params[n].prec = prec; + image_params[n].bpp = bpp; image_params[n].sgnd = 0; } @@ -437,7 +484,7 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, num_tiles = tiles_x * tiles_y; - state->buffer = malloc (tile_width * tile_height * components); + state->buffer = malloc (tile_width * tile_height * components * prec / 8); tile_ndx = 0; for (y = 0; y < tiles_y; ++y) { @@ -469,7 +516,7 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, pack(im, state->buffer, pixx, pixy, pixw, pixh); - data_size = pixw * pixh * components; + data_size = pixw * pixh * components * prec / 8; if (!opj_write_tile(codec, tile_ndx++, state->buffer, data_size, stream)) { From 9184d0f0965f82fcc8ef20965e295018a37de15f Mon Sep 17 00:00:00 2001 From: David Joy Date: Tue, 15 Apr 2014 16:42:15 -0400 Subject: [PATCH 4/5] Switch bits per pixel from 16 to 12 Makes GIMP happy... still not round tripping --- libImaging/Jpeg2KEncode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libImaging/Jpeg2KEncode.c b/libImaging/Jpeg2KEncode.c index 4aaefd60a..502a0de68 100644 --- a/libImaging/Jpeg2KEncode.c +++ b/libImaging/Jpeg2KEncode.c @@ -306,13 +306,13 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, color_space = OPJ_CLRSPC_GRAY; pack = j2k_pack_i16; prec = 16; - bpp = 16; + bpp = 12; } else if (strcmp (im->mode, "I;16B") == 0) { components = 1; color_space = OPJ_CLRSPC_GRAY; pack = j2k_pack_i16b; prec = 16; - bpp = 16; + bpp = 12; } else if (strcmp (im->mode, "LA") == 0) { components = 2; color_space = OPJ_CLRSPC_GRAY; From a955fc71b52614656cff0de4173d8babe749fefc Mon Sep 17 00:00:00 2001 From: David Joy Date: Tue, 27 May 2014 16:16:22 -0400 Subject: [PATCH 5/5] Add basic 16-bit J2K decode --- PIL/Jpeg2KImagePlugin.py | 11 ++++++--- libImaging/Jpeg2KDecode.c | 51 +++++++++++++++++++++++++++++++++++++++ libImaging/Jpeg2KEncode.c | 23 +++--------------- 3 files changed, 62 insertions(+), 23 deletions(-) diff --git a/PIL/Jpeg2KImagePlugin.py b/PIL/Jpeg2KImagePlugin.py index f57f4a784..048fe2af1 100644 --- a/PIL/Jpeg2KImagePlugin.py +++ b/PIL/Jpeg2KImagePlugin.py @@ -76,6 +76,7 @@ def _parse_jp2_header(fp): size = None mode = None + bpc = None hio = io.BytesIO(header) while True: @@ -93,7 +94,9 @@ def _parse_jp2_header(fp): = struct.unpack('>IIHBBBB', content) size = (width, height) if unkc: - if nc == 1: + if nc == 1 and bpc == 15: + mode = 'I' + elif nc == 1: mode = 'L' elif nc == 2: mode = 'LA' @@ -113,7 +116,9 @@ def _parse_jp2_header(fp): mode = 'RGBA' break elif cs == 17: # grayscale - if nc == 1: + if nc == 1 and bpc == 15: + mode = 'I' + elif nc == 1: mode = 'L' elif nc == 2: mode = 'LA' @@ -122,7 +127,7 @@ def _parse_jp2_header(fp): if nc == 3: mode = 'RGB' elif nc == 4: - mode == 'RGBA' + mode = 'RGBA' break return (size, mode) diff --git a/libImaging/Jpeg2KDecode.c b/libImaging/Jpeg2KDecode.c index 6b6176c78..89ad10136 100644 --- a/libImaging/Jpeg2KDecode.c +++ b/libImaging/Jpeg2KDecode.c @@ -135,6 +135,56 @@ j2ku_gray_l(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, } } + +static void +j2ku_gray_i(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, + const UINT8 *tiledata, Imaging im) +{ + unsigned x0 = tileinfo->x0 - in->x0, y0 = tileinfo->y0 - in->y0; + unsigned w = tileinfo->x1 - tileinfo->x0; + unsigned h = tileinfo->y1 - tileinfo->y0; + + int shift = 16 - in->comps[0].prec; + int offset = in->comps[0].sgnd ? 1 << (in->comps[0].prec - 1) : 0; + int csiz = (in->comps[0].prec + 7) >> 3; + + unsigned x, y; + + if (csiz == 3) + csiz = 4; + + if (shift < 0) + offset += 1 << (-shift - 1); + + switch (csiz) { + case 1: + for (y = 0; y < h; ++y) { + const UINT8 *data = &tiledata[y * w]; + UINT32 *row = (UINT32 *)im->image[y0 + y] + x0; + for (x = 0; x < w; ++x) + *row++ = j2ku_shift(offset + *data++, shift); + } + break; + case 2: + for (y = 0; y < h; ++y) { + const UINT16 *data = (const UINT16 *)&tiledata[2 * y * w]; + UINT32 *row = (UINT32 *)im->image[y0 + y] + x0; + for (x = 0; x < w; ++x) + *row++ = j2ku_shift(offset + *data++, shift); + } + break; + case 4: + for (y = 0; y < h; ++y) { + const UINT32 *data = (const UINT32 *)&tiledata[4 * y * w]; + UINT32 *row = (UINT32 *)im->image[y0 + y] + x0; + for (x = 0; x < w; ++x) + *row++ = j2ku_shift(offset + *data++, shift); + } + break; + } +} + + static void j2ku_gray_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, const UINT8 *tiledata, Imaging im) @@ -466,6 +516,7 @@ j2ku_sycca_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, static const struct j2k_decode_unpacker j2k_unpackers[] = { { "L", OPJ_CLRSPC_GRAY, 1, j2ku_gray_l }, + { "I", OPJ_CLRSPC_GRAY, 1, j2ku_gray_i }, { "LA", OPJ_CLRSPC_GRAY, 2, j2ku_graya_la }, { "RGB", OPJ_CLRSPC_GRAY, 1, j2ku_gray_rgb }, { "RGB", OPJ_CLRSPC_GRAY, 2, j2ku_gray_rgb }, diff --git a/libImaging/Jpeg2KEncode.c b/libImaging/Jpeg2KEncode.c index 502a0de68..d84ff4f8f 100644 --- a/libImaging/Jpeg2KEncode.c +++ b/libImaging/Jpeg2KEncode.c @@ -97,25 +97,8 @@ j2k_pack_i16(Imaging im, UINT8 *buf, for (y = 0; y < h; ++y) { UINT8 *data = (UINT8 *)(im->image[y + y0] + x0); for (x = 0; x < w; ++x) { - *ptr++ = data[1]; - *ptr++ = data[0]; - data += 2; - } - } -} - -static void -j2k_pack_i16b(Imaging im, UINT8 *buf, - unsigned x0, unsigned y0, unsigned w, unsigned h) -{ - UINT8 *ptr = buf; - unsigned x,y; - for (y = 0; y < h; ++y) { - UINT8 *data = (UINT8 *)(im->image[y + y0] + x0); - for (x = 0; x < w; ++x) { - *ptr++ = data[0]; - *ptr++ = data[1]; - data += 2; + *ptr++ = *data++; + *ptr++ = *data++; } } } @@ -310,7 +293,7 @@ j2k_encode_entry(Imaging im, ImagingCodecState state, } else if (strcmp (im->mode, "I;16B") == 0) { components = 1; color_space = OPJ_CLRSPC_GRAY; - pack = j2k_pack_i16b; + pack = j2k_pack_i16; prec = 16; bpp = 12; } else if (strcmp (im->mode, "LA") == 0) {