summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormio <stigma@disroot.org>2024-09-19 16:33:29 +1000
committermio <stigma@disroot.org>2024-09-19 16:33:29 +1000
commit308395a3f603ac2b72875b019a2050fe21e38573 (patch)
tree2251abf0f23c9f77a0abe0512b89cd4750b1aedc
parent984447ca7423cf0845f9e7935eb9a00e6ce2a352 (diff)
downloadtdelibs-issue/51/jasper-to-openjpeg.tar.gz
tdelibs-issue/51/jasper-to-openjpeg.zip
Add support for YCbCr imagesissue/51/jasper-to-openjpeg
The code has been adopted from the OpenJPEG project: https://github.com/uclouvain/openjpeg/blob/master/src/bin/common/color.c Full credits to the authors. Signed-off-by: mio <stigma@disroot.org>
-rw-r--r--kimgio/jp2.cpp467
1 files changed, 466 insertions, 1 deletions
diff --git a/kimgio/jp2.cpp b/kimgio/jp2.cpp
index 5f087e228..b7b86165b 100644
--- a/kimgio/jp2.cpp
+++ b/kimgio/jp2.cpp
@@ -32,11 +32,11 @@
* JPEG-2000 Plugin for KImageIO.
*
* Current limitations:
- * - Only reads sRGB/Grayscale images (doesn't convert colorspace).
* - Doesn't support writing images.
* - Doesn't support OPJ_CODEC_J2K.
* - Doesn't support subsampling.
* - Doesn't read ICC profiles.
+ * - Doesn't support esycc or cymk colorspaces.
*
* The API documentation is rather poor, so good references on how to use OpenJPEG
* are the tools provided by OpenJPEG, such as 'opj_decompress':
@@ -72,6 +72,460 @@ struct KIMGJP2Wrapper
}
};
+/*
+ * The following sycc* functions come from OpenJPEG
+ * https://github.com/uclouvain/openjpeg/blob/master/src/bin/common/color.c
+ *
+ * It has beens slightly adjusted to better fit the code style of this file.
+ *
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+static void sycc_to_rgb(int offset, int upb, int y, int cb, int cr,
+ int *out_r, int *out_g, int *out_b)
+{
+ int r, g, b;
+
+ cb -= offset;
+ cr -= offset;
+ r = y + (int)(1.402 * (float)cr);
+ if (r < 0)
+ {
+ r = 0;
+ }
+ else if (r > upb)
+ {
+ r = upb;
+ }
+ *out_r = r;
+
+ g = y - (int)(0.344 * (float)cb + 0.714 * (float)cr);
+ if (g < 0)
+ {
+ g = 0;
+ }
+ else if (g > upb)
+ {
+ g = upb;
+ }
+ *out_g = g;
+
+ b = y + (int)(1.772 * (float)cb);
+ if (b < 0)
+ {
+ b = 0;
+ }
+ else if (b > upb)
+ {
+ b = upb;
+ }
+ *out_b = b;
+}
+
+static bool sycc444_to_rgb(opj_image_t *img)
+{
+ int *d0, *d1, *d2, *r, *g, *b;
+ const int *y, *cb, *cr;
+ size_t maxw, maxh, max, i;
+ int offset, upb;
+
+ upb = (int)img->comps[0].prec;
+ offset = 1 << (upb - 1);
+ upb = (1 << upb) - 1;
+
+ maxw = (size_t)img->comps[0].w;
+ maxh = (size_t)img->comps[0].h;
+ max = maxw * maxh;
+
+ y = img->comps[0].data;
+ cb = img->comps[1].data;
+ cr = img->comps[2].data;
+
+ d0 = r = (int*)opj_image_data_alloc(sizeof(int) * max);
+ d1 = g = (int*)opj_image_data_alloc(sizeof(int) * max);
+ d2 = b = (int*)opj_image_data_alloc(sizeof(int) * max);
+
+ if (r == nullptr || g == nullptr || b == nullptr)
+ {
+ goto fails;
+ }
+
+ for (i = 0U; i < max; ++i)
+ {
+ sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
+ ++y;
+ ++cb;
+ ++cr;
+ ++r;
+ ++g;
+ ++b;
+ }
+ opj_image_data_free(img->comps[0].data);
+ img->comps[0].data = d0;
+ opj_image_data_free(img->comps[1].data);
+ img->comps[1].data = d1;
+ opj_image_data_free(img->comps[2].data);
+ img->comps[2].data = d2;
+ img->color_space = OPJ_CLRSPC_SRGB;
+ return true;
+
+fails:
+ opj_image_data_free(r);
+ opj_image_data_free(g);
+ opj_image_data_free(b);
+ return false;
+}
+
+static bool sycc422_to_rgb(opj_image_t *img)
+{
+ int *d0, *d1, *d2, *r, *g, *b;
+ const int *y, *cb, *cr;
+ size_t maxw, maxh, max, offx, loopmaxw;
+ int offset, upb;
+ size_t i;
+
+ upb = (int)img->comps[0].prec;
+ offset = 1 << (upb - 1);
+ upb = (1 << upb) - 1;
+
+ maxw = (size_t)img->comps[0].w;
+ maxh = (size_t)img->comps[0].h;
+ max = maxw * maxh;
+
+ y = img->comps[0].data;
+ cb = img->comps[1].data;
+ cr = img->comps[2].data;
+
+ d0 = r = (int*)opj_image_data_alloc(sizeof(int) * max);
+ d1 = g = (int*)opj_image_data_alloc(sizeof(int) * max);
+ d2 = b = (int*)opj_image_data_alloc(sizeof(int) * max);
+
+ if (r == nullptr || g == nullptr || b == nullptr)
+ {
+ goto fails;
+ }
+
+ /* if img->x0 is odd, then first column shall use Cb/Cr = 0 */
+ offx = img->x0 & 1U;
+ loopmaxw = maxw - offx;
+
+ for (i = 0U; i < maxh; ++i)
+ {
+ size_t j;
+
+ if (offx > 0U)
+ {
+ sycc_to_rgb(offset, upb, *y, 0, 0, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+ }
+
+ for (j = 0U; j < (loopmaxw & ~(size_t)1U); j += 2U)
+ {
+ sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+ sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+ ++cb;
+ ++cr;
+ }
+ if (j < loopmaxw)
+ {
+ sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+ ++cb;
+ ++cr;
+ }
+ }
+
+ opj_image_data_free(img->comps[0].data);
+ img->comps[0].data = d0;
+ opj_image_data_free(img->comps[1].data);
+ img->comps[1].data = d1;
+ opj_image_data_free(img->comps[2].data);
+ img->comps[2].data = d2;
+
+ img->comps[1].w = img->comps[2].w = img->comps[0].w;
+ img->comps[1].h = img->comps[2].h = img->comps[0].h;
+ img->comps[1].dx = img->comps[2].dx = img->comps[0].dx;
+ img->comps[1].dy = img->comps[2].dy = img->comps[0].dy;
+ img->color_space = OPJ_CLRSPC_SRGB;
+ return true;
+
+fails:
+ opj_image_data_free(r);
+ opj_image_data_free(g);
+ opj_image_data_free(b);
+ return false;
+}
+
+static bool sycc420_to_rgb(opj_image_t *img)
+{
+ int *d0, *d1, *d2, *r, *g, *b, *nr, *ng, *nb;
+ const int *y, *cb, *cr, *ny;
+ size_t maxw, maxh, max, offx, loopmaxw, offy, loopmaxh;
+ int offset, upb;
+ size_t i;
+
+ upb = (int)img->comps[0].prec;
+ offset = 1 << (upb - 1);
+ upb = (1 << upb) - 1;
+
+ maxw = (size_t)img->comps[0].w;
+ maxh = (size_t)img->comps[0].h;
+ max = maxw * maxh;
+
+ y = img->comps[0].data;
+ cb = img->comps[1].data;
+ cr = img->comps[2].data;
+
+ d0 = r = (int*)opj_image_data_alloc(sizeof(int) * max);
+ d1 = g = (int*)opj_image_data_alloc(sizeof(int) * max);
+ d2 = b = (int*)opj_image_data_alloc(sizeof(int) * max);
+
+ if (r == nullptr || g == nullptr || b == nullptr)
+ {
+ goto fails;
+ }
+
+ /* if img->x0 is odd, then first column shall use Cb/Cr = 0 */
+ offx = img->x0 & 1U;
+ loopmaxw = maxw - offx;
+ /* if img->y0 is odd, then first line shall use Cb/Cr = 0 */
+ offy = img->y0 & 1U;
+ loopmaxh = maxh - offy;
+
+ if (offy > 0U)
+ {
+ size_t j;
+
+ for (j = 0; j < maxw; ++j)
+ {
+ sycc_to_rgb(offset, upb, *y, 0, 0, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+ }
+ }
+
+ for (i = 0U; i < (loopmaxh & ~(size_t)1U); i += 2U)
+ {
+ size_t j;
+
+ ny = y + maxw;
+ nr = r + maxw;
+ ng = g + maxw;
+ nb = b + maxw;
+
+ if (offx > 0U)
+ {
+ sycc_to_rgb(offset, upb, *y, 0, 0, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+ sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb);
+ ++ny;
+ ++nr;
+ ++ng;
+ ++nb;
+ }
+
+ for (j = 0; j < (loopmaxw & ~(size_t)1U); j += 2U)
+ {
+ sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+ sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+
+ sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb);
+ ++ny;
+ ++nr;
+ ++ng;
+ ++nb;
+ sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb);
+ ++ny;
+ ++nr;
+ ++ng;
+ ++nb;
+ ++cb;
+ ++cr;
+ }
+ if (j < loopmaxw)
+ {
+ sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+
+ sycc_to_rgb(offset, upb, *ny, *cb, *cr, nr, ng, nb);
+ ++ny;
+ ++nr;
+ ++ng;
+ ++nb;
+ ++cb;
+ ++cr;
+ }
+ y += maxw;
+ r += maxw;
+ g += maxw;
+ b += maxw;
+ }
+ if (i < loopmaxh)
+ {
+ size_t j;
+
+ if (offx > 0U)
+ {
+ sycc_to_rgb(offset, upb, *y, 0, 0, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+ }
+
+ for (j = 0U; j < (loopmaxw & ~(size_t)1U); j += 2U)
+ {
+ sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
+
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+
+ sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
+
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+ ++cb;
+ ++cr;
+ }
+ if (j < loopmaxw)
+ {
+ sycc_to_rgb(offset, upb, *y, *cb, *cr, r, g, b);
+ }
+ }
+
+ opj_image_data_free(img->comps[0].data);
+ img->comps[0].data = d0;
+ opj_image_data_free(img->comps[1].data);
+ img->comps[1].data = d1;
+ opj_image_data_free(img->comps[2].data);
+ img->comps[2].data = d2;
+
+ img->comps[1].w = img->comps[2].w = img->comps[0].w;
+ img->comps[1].h = img->comps[2].h = img->comps[0].h;
+ img->comps[1].dx = img->comps[2].dx = img->comps[0].dx;
+ img->comps[1].dy = img->comps[2].dy = img->comps[0].dy;
+ img->color_space = OPJ_CLRSPC_SRGB;
+ return true;
+
+fails:
+ opj_image_data_free(r);
+ opj_image_data_free(g);
+ opj_image_data_free(b);
+ return false;
+}
+
+static bool color_sycc_to_rgb(opj_image_t *img)
+{
+ if (img->numcomps < 3)
+ {
+ img->color_space = OPJ_CLRSPC_GRAY;
+ return true;
+ }
+
+ if ((img->comps[0].dx == 1) &&
+ (img->comps[1].dx == 2) &&
+ (img->comps[2].dx == 2) &&
+ (img->comps[0].dy == 1) &&
+ (img->comps[1].dy == 2) &&
+ (img->comps[2].dy == 2))
+ {
+ /* horizontal and vertical sub-sample */
+ return sycc420_to_rgb(img);
+ }
+ else if ((img->comps[0].dx == 1) &&
+ (img->comps[1].dx == 2) &&
+ (img->comps[2].dx == 2) &&
+ (img->comps[0].dy == 1) &&
+ (img->comps[1].dy == 1) &&
+ (img->comps[2].dy == 1))
+ {
+ /* horizontal sub-sample only */
+ return sycc422_to_rgb(img);
+ }
+ else if ((img->comps[0].dx == 1) &&
+ (img->comps[1].dx == 1) &&
+ (img->comps[2].dx == 1) &&
+ (img->comps[0].dy == 1) &&
+ (img->comps[1].dy == 1) &&
+ (img->comps[2].dy == 1))
+ {
+ /* no sub-sample */
+ return sycc444_to_rgb(img);
+ }
+ else
+ {
+ kdWarning(kCategory) << "Can not convert in color_sycc_to_rgb" << endl;
+ return false;
+ }
+}
+
static void kimgio_jp2_err_handler(const char *message, void *data)
{
kdError(kCategory) << "Error decoding JP2 image: " << message;
@@ -301,6 +755,17 @@ TDE_EXPORT void kimgio_jp2_read(TQImageIO* io)
kimgio_jp2_read_image(image, jp2);
break;
}
+ case OPJ_CLRSPC_SYCC:
+ {
+ if (false == color_sycc_to_rgb(jp2.image))
+ {
+ kdError(kCategory) << "Could not convert YCbCr JP2 encoded image to sRGB." << endl;
+ io->setStatus(IO_UnspecifiedError);
+ return;
+ }
+ kimgio_jp2_read_image(image, jp2);
+ break;
+ }
default:
{
kdError(kCategory) << "Unsupported colorspace detected: "