Pillow/src/libImaging/Convert.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1800 lines
46 KiB
C
Raw Normal View History

/*
2010-07-31 06:52:47 +04:00
* The Python Imaging Library
* $Id$
*
2010-07-31 06:52:47 +04:00
* convert images
*
* history:
* 1995-06-15 fl created
* 1995-11-28 fl added some "RGBA" and "CMYK" conversions
* 1996-04-22 fl added "1" conversions (same as "L")
* 1996-05-05 fl added palette conversions (hack)
* 1996-07-23 fl fixed "1" conversions to zero/non-zero convention
* 1996-11-01 fl fixed "P" to "L" and "RGB" to "1" conversions
* 1996-12-29 fl set alpha byte in RGB converters
* 1997-05-12 fl added ImagingConvert2
* 1997-05-30 fl added floating point support
* 1997-08-27 fl added "P" to "1" and "P" to "F" conversions
* 1998-01-11 fl added integer support
* 1998-07-01 fl added "YCbCr" support
* 1998-07-02 fl added "RGBX" conversions (sort of)
* 1998-07-04 fl added floyd-steinberg dithering
* 1998-07-12 fl changed "YCrCb" to "YCbCr" (!)
* 1998-12-29 fl added basic "I;16" and "I;16B" conversions
* 1999-02-03 fl added "RGBa", and "BGR" conversions (experimental)
* 2003-09-26 fl added "LA" and "PA" conversions (experimental)
* 2005-05-05 fl fixed "P" to "1" threshold
* 2005-12-08 fl fixed palette memory leak in topalette
*
* Copyright (c) 1997-2005 by Secret Labs AB.
* Copyright (c) 1995-1997 by Fredrik Lundh.
*
* See the README file for details on usage and redistribution.
*/
#include "Imaging.h"
2014-07-22 09:36:40 +04:00
#define MAX(a, b) (a) > (b) ? (a) : (b)
#define MIN(a, b) (a) < (b) ? (a) : (b)
2022-03-05 05:42:39 +03:00
#define CLIP16(v) ((v) <= 0 ? 0 : (v) >= 65535 ? 65535 : (v))
2010-07-31 06:52:47 +04:00
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
#define L(rgb) ((INT32)(rgb)[0] * 299 + (INT32)(rgb)[1] * 587 + (INT32)(rgb)[2] * 114)
#define L24(rgb) ((rgb)[0] * 19595 + (rgb)[1] * 38470 + (rgb)[2] * 7471 + 0x8000)
2010-07-31 06:52:47 +04:00
/* ------------------- */
/* 1 (bit) conversions */
/* ------------------- */
static void
bit2l(UINT8 *out, const UINT8 *in, int xsize) {
int x;
2013-07-09 09:39:16 +04:00
for (x = 0; x < xsize; x++) *out++ = (*in++ != 0) ? 255 : 0;
2010-07-31 06:52:47 +04:00
}
static void
bit2rgb(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++) {
UINT8 v = (*in++ != 0) ? 255 : 0;
2013-07-09 09:39:16 +04:00
*out++ = v;
*out++ = v;
*out++ = v;
*out++ = 255;
2010-07-31 06:52:47 +04:00
}
}
static void
bit2cmyk(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++) {
2013-07-09 09:39:16 +04:00
*out++ = 0;
*out++ = 0;
*out++ = 0;
*out++ = (*in++ != 0) ? 0 : 255;
2010-07-31 06:52:47 +04:00
}
}
static void
bit2ycbcr(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++) {
2013-07-09 09:39:16 +04:00
*out++ = (*in++ != 0) ? 255 : 0;
2010-07-31 06:52:47 +04:00
*out++ = 128;
*out++ = 128;
*out++ = 255;
}
}
2019-08-02 14:35:17 +03:00
static void
bit2hsv(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, out += 4) {
UINT8 v = (*in++ != 0) ? 255 : 0;
out[0] = 0;
out[1] = 0;
out[2] = v;
out[3] = 255;
}
}
2010-07-31 06:52:47 +04:00
/* ----------------- */
/* RGB/L conversions */
/* ----------------- */
static void
l2bit(UINT8 *out, const UINT8 *in, int xsize) {
int x;
2020-05-10 12:56:36 +03:00
for (x = 0; x < xsize; x++) {
2013-07-09 09:39:16 +04:00
*out++ = (*in++ >= 128) ? 255 : 0;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
2016-05-10 21:46:40 +03:00
static void
lA2la(UINT8 *out, const UINT8 *in, int xsize) {
int x;
unsigned int alpha, pixel, tmp;
for (x = 0; x < xsize; x++, in += 4) {
alpha = in[3];
pixel = MULDIV255(in[0], alpha, tmp);
*out++ = (UINT8)pixel;
*out++ = (UINT8)pixel;
*out++ = (UINT8)pixel;
*out++ = (UINT8)alpha;
}
}
/* RGBa -> RGBA conversion to remove premultiplication
Needed for correct transforms/resizing on RGBA images */
static void
la2lA(UINT8 *out, const UINT8 *in, int xsize) {
int x;
unsigned int alpha, pixel;
for (x = 0; x < xsize; x++, in += 4) {
alpha = in[3];
if (alpha == 255 || alpha == 0) {
2016-05-10 21:46:40 +03:00
pixel = in[0];
} else {
2018-04-21 11:14:05 +03:00
pixel = CLIP8((255 * in[0]) / alpha);
2016-06-17 13:03:21 +03:00
}
2016-05-10 21:46:40 +03:00
*out++ = (UINT8)pixel;
*out++ = (UINT8)pixel;
*out++ = (UINT8)pixel;
*out++ = (UINT8)alpha;
}
}
2010-07-31 06:52:47 +04:00
static void
l2la(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++) {
UINT8 v = *in++;
2013-07-09 09:39:16 +04:00
*out++ = v;
*out++ = v;
*out++ = v;
*out++ = 255;
2010-07-31 06:52:47 +04:00
}
}
static void
l2rgb(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++) {
UINT8 v = *in++;
2013-07-09 09:39:16 +04:00
*out++ = v;
*out++ = v;
*out++ = v;
*out++ = 255;
2010-07-31 06:52:47 +04:00
}
}
2019-08-02 14:35:17 +03:00
static void
l2hsv(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, out += 4) {
UINT8 v = *in++;
out[0] = 0;
out[1] = 0;
out[2] = v;
out[3] = 255;
}
}
2010-07-31 06:52:47 +04:00
static void
la2l(UINT8 *out, const UINT8 *in, int xsize) {
int x;
2020-05-10 12:56:36 +03:00
for (x = 0; x < xsize; x++, in += 4) {
2013-07-09 09:39:16 +04:00
*out++ = in[0];
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
static void
la2rgb(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 4) {
UINT8 v = in[0];
2013-07-09 09:39:16 +04:00
*out++ = v;
*out++ = v;
*out++ = v;
*out++ = in[3];
2010-07-31 06:52:47 +04:00
}
}
2019-08-02 14:35:17 +03:00
static void
la2hsv(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 4, out += 4) {
UINT8 v = in[0];
out[0] = 0;
out[1] = 0;
out[2] = v;
out[3] = in[3];
}
}
2010-07-31 06:52:47 +04:00
static void
rgb2bit(UINT8 *out, const UINT8 *in, int xsize) {
int x;
2020-05-10 12:56:36 +03:00
for (x = 0; x < xsize; x++, in += 4) {
2013-07-09 09:39:16 +04:00
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
*out++ = (L(in) >= 128000) ? 255 : 0;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
static void
rgb2l(UINT8 *out, const UINT8 *in, int xsize) {
int x;
2020-05-10 12:56:36 +03:00
for (x = 0; x < xsize; x++, in += 4) {
2013-07-09 09:39:16 +04:00
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
2017-09-10 01:14:53 +03:00
*out++ = L24(in) >> 16;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
static void
rgb2la(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 4, out += 4) {
2013-07-09 09:39:16 +04:00
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
2017-09-10 01:14:53 +03:00
out[0] = out[1] = out[2] = L24(in) >> 16;
2010-07-31 06:52:47 +04:00
out[3] = 255;
}
}
static void
rgb2i(UINT8 *out_, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 4, out_ += 4) {
INT32 v = L24(in) >> 16;
memcpy(out_, &v, sizeof(v));
}
2010-07-31 06:52:47 +04:00
}
static void
rgb2i16l(UINT8 *out_, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 4) {
UINT8 v = CLIP16(L24(in) >> 16);
*out_++ = v;
*out_++ = v >> 8;
}
}
static void
rgb2i16b(UINT8 *out_, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 4) {
UINT8 v = CLIP16(L24(in) >> 16);
*out_++ = v >> 8;
*out_++ = v;
}
}
2010-07-31 06:52:47 +04:00
static void
rgb2f(UINT8 *out_, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 4, out_ += 4) {
FLOAT32 v = (float)L(in) / 1000.0F;
memcpy(out_, &v, sizeof(v));
}
2010-07-31 06:52:47 +04:00
}
static void
rgb2bgr15(UINT8 *out_, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 4, out_ += 2) {
2010-07-31 06:52:47 +04:00
UINT16 v = ((((UINT16)in[0]) << 7) & 0x7c00) +
((((UINT16)in[1]) << 2) & 0x03e0) +
((((UINT16)in[2]) >> 3) & 0x001f);
memcpy(out_, &v, sizeof(v));
}
2010-07-31 06:52:47 +04:00
}
static void
rgb2bgr16(UINT8 *out_, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 4, out_ += 2) {
2010-07-31 06:52:47 +04:00
UINT16 v = ((((UINT16)in[0]) << 8) & 0xf800) +
((((UINT16)in[1]) << 3) & 0x07e0) +
((((UINT16)in[2]) >> 3) & 0x001f);
memcpy(out_, &v, sizeof(v));
}
2010-07-31 06:52:47 +04:00
}
static void
rgb2bgr24(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 4) {
2013-07-09 09:39:16 +04:00
*out++ = in[2];
*out++ = in[1];
*out++ = in[0];
2010-07-31 06:52:47 +04:00
}
}
2014-07-22 09:36:40 +04:00
static void
2019-08-02 14:35:17 +03:00
rgb2hsv_row(UINT8 *out, const UINT8 *in) { // following colorsys.py
2014-07-22 09:36:40 +04:00
float h, s, rc, gc, bc, cr;
UINT8 maxc, minc;
UINT8 r, g, b;
UINT8 uh, us, uv;
2019-08-02 14:35:17 +03:00
r = in[0];
g = in[1];
b = in[2];
maxc = MAX(r, MAX(g, b));
minc = MIN(r, MIN(g, b));
uv = maxc;
if (minc == maxc) {
uh = 0;
us = 0;
} else {
cr = (float)(maxc - minc);
s = cr / (float)maxc;
rc = ((float)(maxc - r)) / cr;
gc = ((float)(maxc - g)) / cr;
bc = ((float)(maxc - b)) / cr;
if (r == maxc) {
h = bc - gc;
} else if (g == maxc) {
h = 2.0 + rc - bc;
2015-10-11 13:24:35 +03:00
} else {
2019-08-02 14:35:17 +03:00
h = 4.0 + gc - rc;
}
// incorrect hue happens if h/6 is negative.
h = fmod((h / 6.0 + 1.0), 1.0);
2014-07-22 09:36:40 +04:00
2019-08-02 14:35:17 +03:00
uh = (UINT8)CLIP8((int)(h * 255.0));
us = (UINT8)CLIP8((int)(s * 255.0));
}
out[0] = uh;
out[1] = us;
out[2] = uv;
}
2016-09-03 05:23:42 +03:00
2019-08-02 14:35:17 +03:00
static void
rgb2hsv(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 4, out += 4) {
rgb2hsv_row(out, in);
out[3] = in[3];
2014-07-22 09:36:40 +04:00
}
}
static void
hsv2rgb(UINT8 *out, const UINT8 *in, int xsize) { // following colorsys.py
2016-09-03 05:23:42 +03:00
2014-07-22 09:36:40 +04:00
int p, q, t;
2014-07-25 21:32:55 +04:00
UINT8 up, uq, ut;
2014-07-22 09:36:40 +04:00
int i, x;
float f, fs;
2014-07-25 21:32:55 +04:00
UINT8 h, s, v;
2016-09-03 05:23:42 +03:00
2014-07-22 09:36:40 +04:00
for (x = 0; x < xsize; x++, in += 4) {
h = in[0];
s = in[1];
v = in[2];
2016-09-03 05:23:42 +03:00
2014-07-22 09:36:40 +04:00
if (s == 0) {
*out++ = v;
*out++ = v;
*out++ = v;
2015-10-11 13:24:35 +03:00
} else {
2014-07-22 09:36:40 +04:00
i = floor((float)h * 6.0 / 255.0); // 0 - 6
2015-10-11 13:24:35 +03:00
f = (float)h * 6.0 / 255.0 - (float)i; // 0-1 : remainder.
fs = ((float)s) / 255.0;
2014-07-22 09:36:40 +04:00
p = round((float)v * (1.0 - fs));
q = round((float)v * (1.0 - fs * f));
t = round((float)v * (1.0 - fs * (1.0 - f)));
2018-04-21 11:14:05 +03:00
up = (UINT8)CLIP8(p);
uq = (UINT8)CLIP8(q);
ut = (UINT8)CLIP8(t);
2016-09-03 05:23:42 +03:00
2014-07-22 09:36:40 +04:00
switch (i % 6) {
2015-10-11 13:24:35 +03:00
case 0:
2014-07-22 09:36:40 +04:00
*out++ = v;
*out++ = ut;
*out++ = up;
break;
2015-10-11 13:24:35 +03:00
case 1:
2014-07-22 09:36:40 +04:00
*out++ = uq;
*out++ = v;
*out++ = up;
break;
2015-10-11 13:24:35 +03:00
case 2:
2014-07-22 09:36:40 +04:00
*out++ = up;
*out++ = v;
*out++ = ut;
break;
2015-10-11 13:24:35 +03:00
case 3:
2014-07-22 09:36:40 +04:00
*out++ = up;
*out++ = uq;
*out++ = v;
break;
2015-10-11 13:24:35 +03:00
case 4:
2014-07-22 09:36:40 +04:00
*out++ = ut;
*out++ = up;
*out++ = v;
break;
2015-10-11 13:24:35 +03:00
case 5:
2014-07-22 09:36:40 +04:00
*out++ = v;
*out++ = up;
*out++ = uq;
break;
}
}
*out++ = in[3];
}
}
2010-07-31 06:52:47 +04:00
/* ---------------- */
/* RGBA conversions */
/* ---------------- */
static void
rgb2rgba(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++) {
*out++ = *in++;
*out++ = *in++;
*out++ = *in++;
*out++ = 255;
in++;
}
}
static void
rgba2la(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 4, out += 4) {
2013-07-09 09:39:16 +04:00
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
2017-09-10 01:14:53 +03:00
out[0] = out[1] = out[2] = L24(in) >> 16;
2010-07-31 06:52:47 +04:00
out[3] = in[3];
}
}
static void
rgba2rgb(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++) {
*out++ = *in++;
*out++ = *in++;
*out++ = *in++;
*out++ = 255;
in++;
}
}
static void
2016-05-10 21:46:40 +03:00
rgbA2rgba(UINT8 *out, const UINT8 *in, int xsize) {
2010-07-31 06:52:47 +04:00
int x;
unsigned int alpha, tmp;
for (x = 0; x < xsize; x++) {
alpha = in[3];
*out++ = MULDIV255(*in++, alpha, tmp);
*out++ = MULDIV255(*in++, alpha, tmp);
*out++ = MULDIV255(*in++, alpha, tmp);
*out++ = *in++;
}
}
2015-10-11 13:24:35 +03:00
/* RGBa -> RGBA conversion to remove premultiplication
2013-10-05 00:24:47 +04:00
Needed for correct transforms/resizing on RGBA images */
static void
rgba2rgbA(UINT8 *out, const UINT8 *in, int xsize) {
int x;
unsigned int alpha;
for (x = 0; x < xsize; x++, in += 4) {
alpha = in[3];
if (alpha == 255 || alpha == 0) {
2013-10-05 00:24:47 +04:00
*out++ = in[0];
*out++ = in[1];
*out++ = in[2];
} else {
2018-04-21 11:14:05 +03:00
*out++ = CLIP8((255 * in[0]) / alpha);
*out++ = CLIP8((255 * in[1]) / alpha);
*out++ = CLIP8((255 * in[2]) / alpha);
2013-10-05 00:24:47 +04:00
}
*out++ = in[3];
}
}
2022-11-01 00:39:02 +03:00
static void
rgba2rgb_(UINT8 *out, const UINT8 *in, int xsize) {
int x;
unsigned int alpha;
for (x = 0; x < xsize; x++, in += 4) {
alpha = in[3];
if (alpha == 255 || alpha == 0) {
*out++ = in[0];
*out++ = in[1];
*out++ = in[2];
} else {
*out++ = CLIP8((255 * in[0]) / alpha);
*out++ = CLIP8((255 * in[1]) / alpha);
*out++ = CLIP8((255 * in[2]) / alpha);
}
*out++ = 255;
}
}
/*
2024-03-21 08:25:40 +03:00
* Conversion of RGB + single transparent color either to
* RGBA or LA, where any pixel matching the color will have the alpha channel set to 0, or
2024-03-26 11:57:17 +03:00
* RGBa or La, where any pixel matching the color will have all channels set to 0
*/
2016-09-03 05:23:42 +03:00
static void
rgbT2a(UINT8 *out, UINT8 *in, int xsize, int r, int g, int b, int premultiplied) {
#ifdef WORDS_BIGENDIAN
UINT32 trns = ((r & 0xff) << 24) | ((g & 0xff) << 16) | ((b & 0xff) << 8) | 0xff;
2024-03-21 08:25:40 +03:00
UINT32 repl = premultiplied ? 0 : (trns & 0xffffff00);
#else
UINT32 trns = (0xffU << 24) | ((b & 0xff) << 16) | ((g & 0xff) << 8) | (r & 0xff);
2024-03-21 08:25:40 +03:00
UINT32 repl = premultiplied ? 0 : (trns & 0x00ffffff);
#endif
2013-10-05 00:24:47 +04:00
int i;
UINT8 *ref = in != NULL ? in : out;
for (i = 0; i < xsize; i++, ref += sizeof(trns), out += sizeof(trns)) {
UINT32 v;
memcpy(&v, ref, sizeof(v));
if (v == trns) {
memcpy(out, &repl, sizeof(repl));
}
}
}
2016-09-03 05:23:42 +03:00
2010-07-31 06:52:47 +04:00
/* ---------------- */
/* CMYK conversions */
/* ---------------- */
static void
l2cmyk(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++) {
*out++ = 0;
*out++ = 0;
*out++ = 0;
*out++ = ~(*in++);
}
}
2019-03-28 12:59:39 +03:00
static void
la2cmyk(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 4) {
*out++ = 0;
*out++ = 0;
*out++ = 0;
*out++ = ~(in[0]);
}
}
2010-07-31 06:52:47 +04:00
static void
rgb2cmyk(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++) {
2013-07-09 09:39:16 +04:00
/* Note: no undercolour removal */
2010-07-31 06:52:47 +04:00
*out++ = ~(*in++);
*out++ = ~(*in++);
*out++ = ~(*in++);
*out++ = 0;
in++;
}
}
void
2010-07-31 06:52:47 +04:00
cmyk2rgb(UINT8 *out, const UINT8 *in, int xsize) {
2017-08-28 17:11:08 +03:00
int x, nk, tmp;
for (x = 0; x < xsize; x++) {
nk = 255 - in[3];
2018-04-21 11:14:05 +03:00
out[0] = CLIP8(nk - MULDIV255(in[0], nk, tmp));
out[1] = CLIP8(nk - MULDIV255(in[1], nk, tmp));
out[2] = CLIP8(nk - MULDIV255(in[2], nk, tmp));
2017-08-28 17:11:08 +03:00
out[3] = 255;
out += 4;
in += 4;
2010-07-31 06:52:47 +04:00
}
}
2019-08-02 14:35:17 +03:00
static void
cmyk2hsv(UINT8 *out, const UINT8 *in, int xsize) {
int x, nk, tmp;
for (x = 0; x < xsize; x++) {
nk = 255 - in[3];
out[0] = CLIP8(nk - MULDIV255(in[0], nk, tmp));
out[1] = CLIP8(nk - MULDIV255(in[1], nk, tmp));
out[2] = CLIP8(nk - MULDIV255(in[2], nk, tmp));
rgb2hsv_row(out, out);
out[3] = 255;
out += 4;
in += 4;
}
}
2010-07-31 06:52:47 +04:00
/* ------------- */
/* I conversions */
/* ------------- */
static void
bit2i(UINT8 *out_, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, out_ += 4) {
INT32 v = (*in++ != 0) ? 255 : 0;
memcpy(out_, &v, sizeof(v));
}
2010-07-31 06:52:47 +04:00
}
static void
l2i(UINT8 *out_, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, out_ += 4) {
INT32 v = *in++;
memcpy(out_, &v, sizeof(v));
}
2010-07-31 06:52:47 +04:00
}
static void
i2l(UINT8 *out, const UINT8 *in_, int xsize) {
int x;
for (x = 0; x < xsize; x++, out++, in_ += 4) {
INT32 v;
memcpy(&v, in_, sizeof(v));
2020-05-10 12:56:36 +03:00
if (v <= 0) {
*out = 0;
2020-05-10 12:56:36 +03:00
} else if (v >= 255) {
*out = 255;
2020-05-10 12:56:36 +03:00
} else {
*out = (UINT8)v;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
}
static void
i2f(UINT8 *out_, const UINT8 *in_, int xsize) {
int x;
for (x = 0; x < xsize; x++, in_ += 4, out_ += 4) {
INT32 i;
FLOAT32 f;
memcpy(&i, in_, sizeof(i));
f = i;
memcpy(out_, &f, sizeof(f));
}
2010-07-31 06:52:47 +04:00
}
static void
i2rgb(UINT8 *out, const UINT8 *in_, int xsize) {
int x;
INT32 *in = (INT32 *)in_;
for (x = 0; x < xsize; x++, in++, out += 4) {
2020-05-10 12:56:36 +03:00
if (*in <= 0) {
out[0] = out[1] = out[2] = 0;
2020-05-10 12:56:36 +03:00
} else if (*in >= 255) {
out[0] = out[1] = out[2] = 255;
2020-05-10 12:56:36 +03:00
} else {
out[0] = out[1] = out[2] = (UINT8)*in;
2020-05-10 12:56:36 +03:00
}
out[3] = 255;
}
}
2019-08-02 14:35:17 +03:00
static void
i2hsv(UINT8 *out, const UINT8 *in_, int xsize) {
int x;
INT32 *in = (INT32 *)in_;
for (x = 0; x < xsize; x++, in++, out += 4) {
out[0] = 0;
out[1] = 0;
2019-09-06 13:07:23 +03:00
if (*in <= 0) {
2019-08-02 14:35:17 +03:00
out[2] = 0;
2019-09-06 13:07:23 +03:00
} else if (*in >= 255) {
2019-08-02 14:35:17 +03:00
out[2] = 255;
2019-09-06 13:07:23 +03:00
} else {
2019-08-02 14:35:17 +03:00
out[2] = (UINT8)*in;
2019-09-06 13:07:23 +03:00
}
2019-08-02 14:35:17 +03:00
out[3] = 255;
}
}
2010-07-31 06:52:47 +04:00
/* ------------- */
/* F conversions */
/* ------------- */
static void
bit2f(UINT8 *out_, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, out_ += 4) {
FLOAT32 f = (*in++ != 0) ? 255.0F : 0.0F;
memcpy(out_, &f, sizeof(f));
}
2010-07-31 06:52:47 +04:00
}
static void
l2f(UINT8 *out_, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, out_ += 4) {
FLOAT32 f = (FLOAT32)*in++;
memcpy(out_, &f, sizeof(f));
}
2010-07-31 06:52:47 +04:00
}
static void
f2l(UINT8 *out, const UINT8 *in_, int xsize) {
int x;
for (x = 0; x < xsize; x++, out++, in_ += 4) {
FLOAT32 v;
memcpy(&v, in_, sizeof(v));
2020-05-10 12:56:36 +03:00
if (v <= 0.0) {
2010-07-31 06:52:47 +04:00
*out = 0;
2020-05-10 12:56:36 +03:00
} else if (v >= 255.0) {
2010-07-31 06:52:47 +04:00
*out = 255;
2020-05-10 12:56:36 +03:00
} else {
*out = (UINT8)v;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
}
static void
f2i(UINT8 *out_, const UINT8 *in_, int xsize) {
int x;
for (x = 0; x < xsize; x++, in_ += 4, out_ += 4) {
FLOAT32 f;
INT32 i;
memcpy(&f, in_, sizeof(f));
i = f;
memcpy(out_, &i, sizeof(i));
}
2010-07-31 06:52:47 +04:00
}
/* ----------------- */
/* YCbCr conversions */
/* ----------------- */
/* See ConvertYCbCr.c for RGB/YCbCr tables */
static void
l2ycbcr(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++) {
2013-07-09 09:39:16 +04:00
*out++ = *in++;
*out++ = 128;
*out++ = 128;
*out++ = 255;
2010-07-31 06:52:47 +04:00
}
}
2019-03-28 12:59:39 +03:00
static void
la2ycbcr(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 4) {
*out++ = in[0];
*out++ = 128;
*out++ = 128;
*out++ = 255;
}
}
2010-07-31 06:52:47 +04:00
static void
ycbcr2l(UINT8 *out, const UINT8 *in, int xsize) {
int x;
2020-05-10 12:56:36 +03:00
for (x = 0; x < xsize; x++, in += 4) {
2013-07-09 09:39:16 +04:00
*out++ = in[0];
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
2019-03-28 12:59:39 +03:00
static void
ycbcr2la(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 4, out += 4) {
out[0] = out[1] = out[2] = in[0];
out[3] = 255;
}
}
2010-07-31 06:52:47 +04:00
/* ------------------------- */
/* I;16 (16-bit) conversions */
/* ------------------------- */
static void
I_I16L(UINT8 *out, const UINT8 *in_, int xsize) {
int x, v;
for (x = 0; x < xsize; x++, in_ += 4) {
INT32 i;
memcpy(&i, in_, sizeof(i));
v = CLIP16(i);
*out++ = (UINT8)v;
*out++ = (UINT8)(v >> 8);
2010-07-31 06:52:47 +04:00
}
}
static void
I_I16B(UINT8 *out, const UINT8 *in_, int xsize) {
int x, v;
for (x = 0; x < xsize; x++, in_ += 4) {
INT32 i;
memcpy(&i, in_, sizeof(i));
v = CLIP16(i);
*out++ = (UINT8)(v >> 8);
*out++ = (UINT8)v;
2010-07-31 06:52:47 +04:00
}
}
static void
I16L_I(UINT8 *out_, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 2, out_ += 4) {
INT32 v = in[0] + ((int)in[1] << 8);
memcpy(out_, &v, sizeof(v));
}
2010-07-31 06:52:47 +04:00
}
static void
I16B_I(UINT8 *out_, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 2, out_ += 4) {
INT32 v = in[1] + ((int)in[0] << 8);
memcpy(out_, &v, sizeof(v));
}
2010-07-31 06:52:47 +04:00
}
static void
I16L_F(UINT8 *out_, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 2, out_ += 4) {
FLOAT32 v = in[0] + ((int)in[1] << 8);
memcpy(out_, &v, sizeof(v));
}
}
static void
I16B_F(UINT8 *out_, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 2, out_ += 4) {
FLOAT32 v = in[1] + ((int)in[0] << 8);
memcpy(out_, &v, sizeof(v));
}
}
2010-07-31 06:52:47 +04:00
static void
L_I16L(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in++) {
*out++ = *in;
*out++ = 0;
2010-07-31 06:52:47 +04:00
}
}
static void
L_I16B(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in++) {
*out++ = 0;
2010-07-31 06:52:47 +04:00
*out++ = *in;
}
}
static void
I16L_L(UINT8 *out, const UINT8 *in, int xsize) {
int x;
2020-05-10 12:56:36 +03:00
for (x = 0; x < xsize; x++, in += 2) {
if (in[1] != 0) {
*out++ = 255;
2020-05-10 12:56:36 +03:00
} else {
*out++ = in[0];
2020-05-10 12:56:36 +03:00
}
}
2010-07-31 06:52:47 +04:00
}
static void
I16B_L(UINT8 *out, const UINT8 *in, int xsize) {
int x;
2020-05-10 12:56:36 +03:00
for (x = 0; x < xsize; x++, in += 2) {
if (in[0] != 0) {
*out++ = 255;
2020-05-10 12:56:36 +03:00
} else {
*out++ = in[1];
2020-05-10 12:56:36 +03:00
}
}
2010-07-31 06:52:47 +04:00
}
2024-03-02 07:39:43 +03:00
static void
I16_RGB(UINT8 *out, const UINT8 *in, int xsize) {
int x;
for (x = 0; x < xsize; x++, in += 2) {
UINT8 v = in[1] == 0 ? in[0] : 255;
*out++ = v;
*out++ = v;
*out++ = v;
*out++ = 255;
}
}
2010-07-31 06:52:47 +04:00
static struct {
const char *from;
const char *to;
ImagingShuffler convert;
} converters[] = {
{"1", "L", bit2l},
{"1", "I", bit2i},
{"1", "F", bit2f},
{"1", "RGB", bit2rgb},
{"1", "RGBA", bit2rgb},
{"1", "RGBX", bit2rgb},
{"1", "CMYK", bit2cmyk},
{"1", "YCbCr", bit2ycbcr},
2019-08-02 14:35:17 +03:00
{"1", "HSV", bit2hsv},
2021-01-03 06:17:51 +03:00
2010-07-31 06:52:47 +04:00
{"L", "1", l2bit},
{"L", "LA", l2la},
{"L", "I", l2i},
{"L", "F", l2f},
{"L", "RGB", l2rgb},
{"L", "RGBA", l2rgb},
{"L", "RGBX", l2rgb},
{"L", "CMYK", l2cmyk},
{"L", "YCbCr", l2ycbcr},
2019-08-02 14:35:17 +03:00
{"L", "HSV", l2hsv},
2021-01-03 06:17:51 +03:00
2010-07-31 06:52:47 +04:00
{"LA", "L", la2l},
2016-05-10 21:46:40 +03:00
{"LA", "La", lA2la},
2010-07-31 06:52:47 +04:00
{"LA", "RGB", la2rgb},
{"LA", "RGBA", la2rgb},
2019-03-28 12:59:39 +03:00
{"LA", "RGBX", la2rgb},
{"LA", "CMYK", la2cmyk},
{"LA", "YCbCr", la2ycbcr},
2019-08-02 14:35:17 +03:00
{"LA", "HSV", la2hsv},
2021-01-03 06:17:51 +03:00
2016-05-10 21:46:40 +03:00
{"La", "LA", la2lA},
2021-01-03 06:17:51 +03:00
{"I", "L", i2l},
{"I", "F", i2f},
{"I", "RGB", i2rgb},
{"I", "RGBA", i2rgb},
{"I", "RGBX", i2rgb},
2019-08-02 14:35:17 +03:00
{"I", "HSV", i2hsv},
2021-01-03 06:17:51 +03:00
{"F", "L", f2l},
{"F", "I", f2i},
2021-01-03 06:17:51 +03:00
2010-07-31 06:52:47 +04:00
{"RGB", "1", rgb2bit},
{"RGB", "L", rgb2l},
{"RGB", "LA", rgb2la},
2024-03-26 11:57:17 +03:00
{"RGB", "La", rgb2la},
2010-07-31 06:52:47 +04:00
{"RGB", "I", rgb2i},
{"RGB", "I;16", rgb2i16l},
{"RGB", "I;16L", rgb2i16l},
{"RGB", "I;16B", rgb2i16b},
2010-07-31 06:52:47 +04:00
{"RGB", "F", rgb2f},
{"RGB", "BGR;15", rgb2bgr15},
{"RGB", "BGR;16", rgb2bgr16},
{"RGB", "BGR;24", rgb2bgr24},
{"RGB", "RGBA", rgb2rgba},
2024-03-21 08:25:40 +03:00
{"RGB", "RGBa", rgb2rgba},
2010-07-31 06:52:47 +04:00
{"RGB", "RGBX", rgb2rgba},
{"RGB", "CMYK", rgb2cmyk},
{"RGB", "YCbCr", ImagingConvertRGB2YCbCr},
2014-07-22 09:36:40 +04:00
{"RGB", "HSV", rgb2hsv},
2021-01-03 06:17:51 +03:00
2010-07-31 06:52:47 +04:00
{"RGBA", "1", rgb2bit},
{"RGBA", "L", rgb2l},
{"RGBA", "LA", rgba2la},
{"RGBA", "I", rgb2i},
{"RGBA", "F", rgb2f},
{"RGBA", "RGB", rgba2rgb},
2016-05-10 21:46:40 +03:00
{"RGBA", "RGBa", rgbA2rgba},
2010-07-31 06:52:47 +04:00
{"RGBA", "RGBX", rgb2rgba},
{"RGBA", "CMYK", rgb2cmyk},
{"RGBA", "YCbCr", ImagingConvertRGB2YCbCr},
2019-08-02 14:35:17 +03:00
{"RGBA", "HSV", rgb2hsv},
2021-01-03 06:17:51 +03:00
2013-10-05 00:24:47 +04:00
{"RGBa", "RGBA", rgba2rgbA},
2022-11-01 00:39:02 +03:00
{"RGBa", "RGB", rgba2rgb_},
2021-01-03 06:17:51 +03:00
2010-07-31 06:52:47 +04:00
{"RGBX", "1", rgb2bit},
{"RGBX", "L", rgb2l},
2019-03-28 12:59:39 +03:00
{"RGBX", "LA", rgb2la},
{"RGBX", "I", rgb2i},
{"RGBX", "F", rgb2f},
2010-07-31 06:52:47 +04:00
{"RGBX", "RGB", rgba2rgb},
{"RGBX", "CMYK", rgb2cmyk},
{"RGBX", "YCbCr", ImagingConvertRGB2YCbCr},
2019-08-02 14:35:17 +03:00
{"RGBX", "HSV", rgb2hsv},
2021-01-03 06:17:51 +03:00
2010-07-31 06:52:47 +04:00
{"CMYK", "RGB", cmyk2rgb},
{"CMYK", "RGBA", cmyk2rgb},
{"CMYK", "RGBX", cmyk2rgb},
2019-08-02 14:35:17 +03:00
{"CMYK", "HSV", cmyk2hsv},
2021-01-03 06:17:51 +03:00
2010-07-31 06:52:47 +04:00
{"YCbCr", "L", ycbcr2l},
2019-03-28 12:59:39 +03:00
{"YCbCr", "LA", ycbcr2la},
2010-07-31 06:52:47 +04:00
{"YCbCr", "RGB", ImagingConvertYCbCr2RGB},
2021-01-03 06:17:51 +03:00
2014-07-22 09:36:40 +04:00
{"HSV", "RGB", hsv2rgb},
2021-01-03 06:17:51 +03:00
2010-07-31 06:52:47 +04:00
{"I", "I;16", I_I16L},
{"I;16", "I", I16L_I},
2024-03-02 07:39:43 +03:00
{"I;16", "RGB", I16_RGB},
2010-07-31 06:52:47 +04:00
{"L", "I;16", L_I16L},
{"I;16", "L", I16L_L},
2021-01-03 06:17:51 +03:00
2010-07-31 06:52:47 +04:00
{"I", "I;16L", I_I16L},
{"I;16L", "I", I16L_I},
{"I", "I;16B", I_I16B},
{"I;16B", "I", I16B_I},
2021-01-03 06:17:51 +03:00
2010-07-31 06:52:47 +04:00
{"L", "I;16L", L_I16L},
{"I;16L", "L", I16L_L},
{"L", "I;16B", L_I16B},
{"I;16B", "L", I16B_L},
2022-12-28 09:16:44 +03:00
#ifdef WORDS_BIGENDIAN
{"L", "I;16N", L_I16B},
{"I;16N", "L", I16B_L},
#else
{"L", "I;16N", L_I16L},
{"I;16N", "L", I16L_L},
#endif
2021-01-03 06:17:51 +03:00
{"I;16", "F", I16L_F},
{"I;16L", "F", I16L_F},
{"I;16B", "F", I16B_F},
2021-01-03 06:17:51 +03:00
2010-07-31 06:52:47 +04:00
{NULL}};
/* FIXME: translate indexed versions to pointer versions below this line */
/* ------------------- */
/* Palette conversions */
/* ------------------- */
static void
p2bit(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
2010-07-31 06:52:47 +04:00
int x;
2023-10-19 11:12:01 +03:00
/* FIXME: precalculate grayscale palette? */
2020-05-10 12:56:36 +03:00
for (x = 0; x < xsize; x++) {
*out++ = (L(&palette->palette[in[x] * 4]) >= 128000) ? 255 : 0;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
2019-03-29 15:13:07 +03:00
static void
pa2bit(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
2019-03-29 15:13:07 +03:00
int x;
2023-10-19 11:12:01 +03:00
/* FIXME: precalculate grayscale palette? */
2020-05-10 12:56:36 +03:00
for (x = 0; x < xsize; x++, in += 4) {
*out++ = (L(&palette->palette[in[0] * 4]) >= 128000) ? 255 : 0;
2020-05-10 12:56:36 +03:00
}
2019-03-29 15:13:07 +03:00
}
2010-07-31 06:52:47 +04:00
static void
p2l(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
2010-07-31 06:52:47 +04:00
int x;
2023-10-19 11:12:01 +03:00
/* FIXME: precalculate grayscale palette? */
2020-05-10 12:56:36 +03:00
for (x = 0; x < xsize; x++) {
*out++ = L24(&palette->palette[in[x] * 4]) >> 16;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
2019-03-29 15:13:07 +03:00
static void
pa2l(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
2019-03-29 15:13:07 +03:00
int x;
2023-10-19 11:12:01 +03:00
/* FIXME: precalculate grayscale palette? */
2020-05-10 12:56:36 +03:00
for (x = 0; x < xsize; x++, in += 4) {
*out++ = L24(&palette->palette[in[0] * 4]) >> 16;
2020-05-10 12:56:36 +03:00
}
2019-03-29 15:13:07 +03:00
}
2022-08-16 15:45:55 +03:00
static void
pa2p(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
int x;
for (x = 0; x < xsize; x++, in += 4) {
*out++ = in[0];
}
}
2019-03-29 15:13:07 +03:00
static void
p2pa(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
2019-03-29 15:13:07 +03:00
int x;
2022-02-14 13:50:19 +03:00
int rgb = strcmp(palette->mode, "RGB");
2019-04-02 01:25:27 +03:00
for (x = 0; x < xsize; x++, in++) {
2022-05-27 13:24:19 +03:00
const UINT8 *rgba = &palette->palette[in[0] * 4];
2019-03-29 15:13:07 +03:00
*out++ = in[0];
*out++ = in[0];
*out++ = in[0];
2022-02-14 13:50:19 +03:00
*out++ = rgb == 0 ? 255 : rgba[3];
2019-03-29 15:13:07 +03:00
}
}
static void
p2la(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
int x;
2023-10-19 11:12:01 +03:00
/* FIXME: precalculate grayscale palette? */
for (x = 0; x < xsize; x++, out += 4) {
const UINT8 *rgba = &palette->palette[*in++ * 4];
out[0] = out[1] = out[2] = L24(rgba) >> 16;
out[3] = rgba[3];
}
}
2010-07-31 06:52:47 +04:00
static void
pa2la(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
2010-07-31 06:52:47 +04:00
int x;
2023-10-19 11:12:01 +03:00
/* FIXME: precalculate grayscale palette? */
2019-03-29 15:13:07 +03:00
for (x = 0; x < xsize; x++, in += 4, out += 4) {
out[0] = out[1] = out[2] = L24(&palette->palette[in[0] * 4]) >> 16;
2019-03-29 15:13:07 +03:00
out[3] = in[3];
2010-07-31 06:52:47 +04:00
}
}
static void
p2i(UINT8 *out_, const UINT8 *in, int xsize, ImagingPalette palette) {
2010-07-31 06:52:47 +04:00
int x;
for (x = 0; x < xsize; x++, out_ += 4) {
INT32 v = L24(&palette->palette[in[x] * 4]) >> 16;
memcpy(out_, &v, sizeof(v));
}
2010-07-31 06:52:47 +04:00
}
2019-03-29 15:13:07 +03:00
static void
pa2i(UINT8 *out_, const UINT8 *in, int xsize, ImagingPalette palette) {
2019-03-29 15:13:07 +03:00
int x;
INT32 *out = (INT32 *)out_;
2020-05-10 12:56:36 +03:00
for (x = 0; x < xsize; x++, in += 4) {
*out++ = L24(&palette->palette[in[0] * 4]) >> 16;
2020-05-10 12:56:36 +03:00
}
2019-03-29 15:13:07 +03:00
}
2010-07-31 06:52:47 +04:00
static void
p2f(UINT8 *out_, const UINT8 *in, int xsize, ImagingPalette palette) {
2010-07-31 06:52:47 +04:00
int x;
for (x = 0; x < xsize; x++, out_ += 4) {
FLOAT32 v = L(&palette->palette[in[x] * 4]) / 1000.0F;
memcpy(out_, &v, sizeof(v));
}
2010-07-31 06:52:47 +04:00
}
2019-03-29 15:13:07 +03:00
static void
pa2f(UINT8 *out_, const UINT8 *in, int xsize, ImagingPalette palette) {
2019-03-29 15:13:07 +03:00
int x;
FLOAT32 *out = (FLOAT32 *)out_;
2020-05-10 12:56:36 +03:00
for (x = 0; x < xsize; x++, in += 4) {
*out++ = (float)L(&palette->palette[in[0] * 4]) / 1000.0F;
2020-05-10 12:56:36 +03:00
}
2019-03-29 15:13:07 +03:00
}
2010-07-31 06:52:47 +04:00
static void
p2rgb(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
2010-07-31 06:52:47 +04:00
int x;
for (x = 0; x < xsize; x++) {
const UINT8 *rgb = &palette->palette[*in++ * 4];
2013-07-09 09:39:16 +04:00
*out++ = rgb[0];
*out++ = rgb[1];
*out++ = rgb[2];
*out++ = 255;
2010-07-31 06:52:47 +04:00
}
}
2019-03-29 15:13:07 +03:00
static void
pa2rgb(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
2019-03-29 15:13:07 +03:00
int x;
for (x = 0; x < xsize; x++, in += 4) {
const UINT8 *rgb = &palette->palette[in[0] * 4];
2019-03-29 15:13:07 +03:00
*out++ = rgb[0];
*out++ = rgb[1];
*out++ = rgb[2];
*out++ = 255;
}
}
2019-08-02 14:35:17 +03:00
static void
p2hsv(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
2019-08-02 14:35:17 +03:00
int x;
for (x = 0; x < xsize; x++, out += 4) {
const UINT8 *rgb = &palette->palette[*in++ * 4];
2019-08-02 14:35:17 +03:00
rgb2hsv_row(out, rgb);
out[3] = 255;
}
}
static void
pa2hsv(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
2019-08-02 14:35:17 +03:00
int x;
for (x = 0; x < xsize; x++, in += 4, out += 4) {
const UINT8 *rgb = &palette->palette[in[0] * 4];
2019-08-02 14:35:17 +03:00
rgb2hsv_row(out, rgb);
out[3] = 255;
}
}
2010-07-31 06:52:47 +04:00
static void
p2rgba(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
2010-07-31 06:52:47 +04:00
int x;
for (x = 0; x < xsize; x++) {
const UINT8 *rgba = &palette->palette[*in++ * 4];
2013-07-09 09:39:16 +04:00
*out++ = rgba[0];
*out++ = rgba[1];
*out++ = rgba[2];
*out++ = rgba[3];
2010-07-31 06:52:47 +04:00
}
}
static void
pa2rgba(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
2010-07-31 06:52:47 +04:00
int x;
for (x = 0; x < xsize; x++, in += 4) {
const UINT8 *rgb = &palette->palette[in[0] * 4];
2013-07-09 09:39:16 +04:00
*out++ = rgb[0];
*out++ = rgb[1];
*out++ = rgb[2];
*out++ = in[3];
2010-07-31 06:52:47 +04:00
}
}
static void
p2cmyk(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
2010-07-31 06:52:47 +04:00
p2rgb(out, in, xsize, palette);
rgb2cmyk(out, out, xsize);
}
2019-03-29 15:13:07 +03:00
static void
pa2cmyk(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
2019-03-29 15:13:07 +03:00
pa2rgb(out, in, xsize, palette);
rgb2cmyk(out, out, xsize);
}
2010-07-31 06:52:47 +04:00
static void
p2ycbcr(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
2010-07-31 06:52:47 +04:00
p2rgb(out, in, xsize, palette);
ImagingConvertRGB2YCbCr(out, out, xsize);
}
2019-03-29 15:13:07 +03:00
static void
pa2ycbcr(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) {
2019-03-29 15:13:07 +03:00
pa2rgb(out, in, xsize, palette);
ImagingConvertRGB2YCbCr(out, out, xsize);
}
2010-07-31 06:52:47 +04:00
static Imaging
frompalette(Imaging imOut, Imaging imIn, const char *mode) {
ImagingSectionCookie cookie;
int alpha;
int y;
void (*convert)(UINT8 *, const UINT8 *, int, ImagingPalette);
2010-07-31 06:52:47 +04:00
/* Map palette image to L, RGB, RGBA, or CMYK */
2020-05-10 12:56:36 +03:00
if (!imIn->palette) {
2013-07-09 09:39:16 +04:00
return (Imaging)ImagingError_ValueError("no palette");
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
alpha = !strcmp(imIn->mode, "PA");
2020-05-10 12:56:36 +03:00
if (strcmp(mode, "1") == 0) {
2019-03-29 15:13:07 +03:00
convert = alpha ? pa2bit : p2bit;
2020-05-10 12:56:36 +03:00
} else if (strcmp(mode, "L") == 0) {
2019-03-29 15:13:07 +03:00
convert = alpha ? pa2l : p2l;
2020-05-10 12:56:36 +03:00
} else if (strcmp(mode, "LA") == 0) {
2019-03-29 15:13:07 +03:00
convert = alpha ? pa2la : p2la;
2022-08-16 15:45:55 +03:00
} else if (strcmp(mode, "P") == 0) {
convert = pa2p;
2020-05-10 12:56:36 +03:00
} else if (strcmp(mode, "PA") == 0) {
2019-03-29 15:13:07 +03:00
convert = p2pa;
2020-05-10 12:56:36 +03:00
} else if (strcmp(mode, "I") == 0) {
2019-03-29 15:13:07 +03:00
convert = alpha ? pa2i : p2i;
2020-05-10 12:56:36 +03:00
} else if (strcmp(mode, "F") == 0) {
2019-03-29 15:13:07 +03:00
convert = alpha ? pa2f : p2f;
2020-05-10 12:56:36 +03:00
} else if (strcmp(mode, "RGB") == 0) {
2019-03-29 15:13:07 +03:00
convert = alpha ? pa2rgb : p2rgb;
2022-02-14 11:24:47 +03:00
} else if (strcmp(mode, "RGBA") == 0 || strcmp(mode, "RGBX") == 0) {
2019-03-29 15:13:07 +03:00
convert = alpha ? pa2rgba : p2rgba;
2020-05-10 12:56:36 +03:00
} else if (strcmp(mode, "CMYK") == 0) {
2019-03-29 15:13:07 +03:00
convert = alpha ? pa2cmyk : p2cmyk;
2020-05-10 12:56:36 +03:00
} else if (strcmp(mode, "YCbCr") == 0) {
2019-03-29 15:13:07 +03:00
convert = alpha ? pa2ycbcr : p2ycbcr;
2020-05-10 12:56:36 +03:00
} else if (strcmp(mode, "HSV") == 0) {
2019-08-02 14:35:17 +03:00
convert = alpha ? pa2hsv : p2hsv;
2020-05-10 12:56:36 +03:00
} else {
2013-07-09 09:39:16 +04:00
return (Imaging)ImagingError_ValueError("conversion not supported");
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2017-08-06 20:08:07 +03:00
imOut = ImagingNew2Dirty(mode, imOut, imIn);
2020-05-10 12:56:36 +03:00
if (!imOut) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
if (strcmp(mode, "P") == 0 || strcmp(mode, "PA") == 0) {
2022-08-16 15:45:55 +03:00
ImagingPaletteDelete(imOut->palette);
imOut->palette = ImagingPaletteDuplicate(imIn->palette);
}
2010-07-31 06:52:47 +04:00
ImagingSectionEnter(&cookie);
2020-05-10 12:56:36 +03:00
for (y = 0; y < imIn->ysize; y++) {
2013-07-09 09:39:16 +04:00
(*convert)(
(UINT8 *)imOut->image[y],
(UINT8 *)imIn->image[y],
imIn->xsize,
imIn->palette);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
ImagingSectionLeave(&cookie);
return imOut;
}
#if defined(_MSC_VER)
#pragma optimize("", off)
#endif
2010-07-31 06:52:47 +04:00
static Imaging
2019-03-29 15:13:07 +03:00
topalette(
Imaging imOut,
Imaging imIn,
const char *mode,
ImagingPalette inpalette,
int dither) {
2010-07-31 06:52:47 +04:00
ImagingSectionCookie cookie;
2019-03-29 15:13:07 +03:00
int alpha;
2010-07-31 06:52:47 +04:00
int x, y;
ImagingPalette palette = inpalette;
/* Map L or RGB/RGBX/RGBA to palette image */
2020-05-10 12:56:36 +03:00
if (strcmp(imIn->mode, "L") != 0 && strncmp(imIn->mode, "RGB", 3) != 0) {
2013-07-09 09:39:16 +04:00
return (Imaging)ImagingError_ValueError("conversion not supported");
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2019-03-29 15:13:07 +03:00
alpha = !strcmp(mode, "PA");
2010-07-31 06:52:47 +04:00
if (palette == NULL) {
/* FIXME: make user configurable */
2020-05-10 12:56:36 +03:00
if (imIn->bands == 1) {
2023-07-17 16:04:43 +03:00
palette = ImagingPaletteNew("RGB");
palette->size = 256;
int i;
for (i = 0; i < 256; i++) {
palette->palette[i * 4] = palette->palette[i * 4 + 1] =
palette->palette[i * 4 + 2] = (UINT8)i;
}
2020-05-10 12:56:36 +03:00
} else {
2013-07-09 09:39:16 +04:00
palette = ImagingPaletteNewBrowser(); /* Standard colour cube */
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
2020-05-10 12:56:36 +03:00
if (!palette) {
2013-07-09 09:39:16 +04:00
return (Imaging)ImagingError_ValueError("no palette");
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2019-03-29 15:13:07 +03:00
imOut = ImagingNew2Dirty(mode, imOut, imIn);
2010-07-31 06:52:47 +04:00
if (!imOut) {
2020-05-10 12:56:36 +03:00
if (palette != inpalette) {
2013-07-09 09:39:16 +04:00
ImagingPaletteDelete(palette);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
return NULL;
}
ImagingPaletteDelete(imOut->palette);
imOut->palette = ImagingPaletteDuplicate(palette);
if (imIn->bands == 1) {
2023-10-19 11:12:01 +03:00
/* grayscale image */
2010-07-31 06:52:47 +04:00
2023-10-19 11:12:01 +03:00
/* Grayscale palette: copy data as is */
2010-07-31 06:52:47 +04:00
ImagingSectionEnter(&cookie);
2019-03-29 15:13:07 +03:00
for (y = 0; y < imIn->ysize; y++) {
if (alpha) {
l2la((UINT8 *)imOut->image[y], (UINT8 *)imIn->image[y], imIn->xsize);
} else {
memcpy(imOut->image[y], imIn->image[y], imIn->linesize);
}
}
2010-07-31 06:52:47 +04:00
ImagingSectionLeave(&cookie);
} else {
2013-07-09 09:39:16 +04:00
/* colour image */
/* Create mapping cache */
if (ImagingPaletteCachePrepare(palette) < 0) {
ImagingDelete(imOut);
2020-05-10 12:56:36 +03:00
if (palette != inpalette) {
2013-07-09 09:39:16 +04:00
ImagingPaletteDelete(palette);
2020-05-10 12:56:36 +03:00
}
2013-07-09 09:39:16 +04:00
return NULL;
}
2010-07-31 06:52:47 +04:00
if (dither) {
/* floyd-steinberg dither */
int *errors;
errors = calloc(imIn->xsize + 1, sizeof(int) * 3);
if (!errors) {
ImagingDelete(imOut);
return ImagingError_MemoryError();
}
/* Map each pixel to the nearest palette entry */
ImagingSectionEnter(&cookie);
for (y = 0; y < imIn->ysize; y++) {
int r, r0, r1, r2;
int g, g0, g1, g2;
int b, b0, b1, b2;
UINT8 *in = (UINT8 *)imIn->image[y];
2019-03-29 15:13:07 +03:00
UINT8 *out = alpha ? (UINT8 *)imOut->image32[y] : imOut->image8[y];
2010-07-31 06:52:47 +04:00
int *e = errors;
r = r0 = r1 = 0;
g = g0 = g1 = 0;
b = b0 = b1 = b2 = 0;
for (x = 0; x < imIn->xsize; x++, in += 4) {
int d2;
INT16 *cache;
2018-04-21 11:14:05 +03:00
r = CLIP8(in[0] + (r + e[3 + 0]) / 16);
g = CLIP8(in[1] + (g + e[3 + 1]) / 16);
b = CLIP8(in[2] + (b + e[3 + 2]) / 16);
2010-07-31 06:52:47 +04:00
/* get closest colour */
cache = &ImagingPaletteCache(palette, r, g, b);
2020-05-10 12:56:36 +03:00
if (cache[0] == 0x100) {
2010-07-31 06:52:47 +04:00
ImagingPaletteCacheUpdate(palette, r, g, b);
2020-05-10 12:56:36 +03:00
}
2019-03-29 15:13:07 +03:00
if (alpha) {
out[x * 4] = out[x * 4 + 1] = out[x * 4 + 2] = (UINT8)cache[0];
out[x * 4 + 3] = 255;
} else {
out[x] = (UINT8)cache[0];
}
2010-07-31 06:52:47 +04:00
r -= (int)palette->palette[cache[0] * 4];
g -= (int)palette->palette[cache[0] * 4 + 1];
b -= (int)palette->palette[cache[0] * 4 + 2];
/* propagate errors (don't ask ;-) */
r2 = r;
d2 = r + r;
r += d2;
e[0] = r + r0;
r += d2;
r0 = r + r1;
r1 = r2;
r += d2;
g2 = g;
d2 = g + g;
g += d2;
e[1] = g + g0;
g += d2;
g0 = g + g1;
g1 = g2;
g += d2;
b2 = b;
d2 = b + b;
b += d2;
e[2] = b + b0;
b += d2;
b0 = b + b1;
b1 = b2;
b += d2;
e += 3;
}
e[0] = b0;
e[1] = b1;
e[2] = b2;
}
ImagingSectionLeave(&cookie);
free(errors);
} else {
/* closest colour */
ImagingSectionEnter(&cookie);
for (y = 0; y < imIn->ysize; y++) {
int r, g, b;
UINT8 *in = (UINT8 *)imIn->image[y];
2019-03-29 15:13:07 +03:00
UINT8 *out = alpha ? (UINT8 *)imOut->image32[y] : imOut->image8[y];
2010-07-31 06:52:47 +04:00
for (x = 0; x < imIn->xsize; x++, in += 4) {
INT16 *cache;
r = in[0];
g = in[1];
b = in[2];
/* get closest colour */
cache = &ImagingPaletteCache(palette, r, g, b);
2020-05-10 12:56:36 +03:00
if (cache[0] == 0x100) {
2010-07-31 06:52:47 +04:00
ImagingPaletteCacheUpdate(palette, r, g, b);
2020-05-10 12:56:36 +03:00
}
2019-03-29 15:13:07 +03:00
if (alpha) {
out[x * 4] = out[x * 4 + 1] = out[x * 4 + 2] = (UINT8)cache[0];
out[x * 4 + 3] = 255;
} else {
out[x] = (UINT8)cache[0];
}
2010-07-31 06:52:47 +04:00
}
}
ImagingSectionLeave(&cookie);
}
2020-05-10 12:56:36 +03:00
if (inpalette != palette) {
2013-07-09 09:39:16 +04:00
ImagingPaletteCacheDelete(palette);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
2020-05-10 12:56:36 +03:00
if (inpalette != palette) {
2010-07-31 06:52:47 +04:00
ImagingPaletteDelete(palette);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
return imOut;
}
static Imaging
2022-02-19 02:50:07 +03:00
tobilevel(Imaging imOut, Imaging imIn) {
2010-07-31 06:52:47 +04:00
ImagingSectionCookie cookie;
int x, y;
int *errors;
/* Map L or RGB to dithered 1 image */
2020-05-10 12:56:36 +03:00
if (strcmp(imIn->mode, "L") != 0 && strcmp(imIn->mode, "RGB") != 0) {
2013-07-09 09:39:16 +04:00
return (Imaging)ImagingError_ValueError("conversion not supported");
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2017-08-06 20:08:07 +03:00
imOut = ImagingNew2Dirty("1", imOut, imIn);
2020-05-10 12:56:36 +03:00
if (!imOut) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
errors = calloc(imIn->xsize + 1, sizeof(int));
if (!errors) {
ImagingDelete(imOut);
return ImagingError_MemoryError();
}
if (imIn->bands == 1) {
/* map each pixel to black or white, using error diffusion */
ImagingSectionEnter(&cookie);
for (y = 0; y < imIn->ysize; y++) {
int l, l0, l1, l2, d2;
UINT8 *in = (UINT8 *)imIn->image[y];
UINT8 *out = imOut->image8[y];
l = l0 = l1 = 0;
for (x = 0; x < imIn->xsize; x++) {
/* pick closest colour */
2018-04-21 11:14:05 +03:00
l = CLIP8(in[x] + (l + errors[x + 1]) / 16);
2010-07-31 06:52:47 +04:00
out[x] = (l > 128) ? 255 : 0;
/* propagate errors */
l -= (int)out[x];
l2 = l;
d2 = l + l;
l += d2;
errors[x] = l + l0;
l += d2;
l0 = l + l1;
l1 = l2;
l += d2;
}
errors[x] = l0;
}
ImagingSectionLeave(&cookie);
} else {
/* map each pixel to black or white, using error diffusion */
ImagingSectionEnter(&cookie);
for (y = 0; y < imIn->ysize; y++) {
int l, l0, l1, l2, d2;
UINT8 *in = (UINT8 *)imIn->image[y];
UINT8 *out = imOut->image8[y];
l = l0 = l1 = 0;
for (x = 0; x < imIn->xsize; x++, in += 4) {
/* pick closest colour */
2018-04-21 11:14:05 +03:00
l = CLIP8(L(in) / 1000 + (l + errors[x + 1]) / 16);
2010-07-31 06:52:47 +04:00
out[x] = (l > 128) ? 255 : 0;
/* propagate errors */
l -= (int)out[x];
l2 = l;
d2 = l + l;
l += d2;
errors[x] = l + l0;
l += d2;
l0 = l + l1;
l1 = l2;
l += d2;
}
errors[x] = l0;
}
ImagingSectionLeave(&cookie);
}
free(errors);
return imOut;
}
#if defined(_MSC_VER)
#pragma optimize("", on)
#endif
2010-07-31 06:52:47 +04:00
static Imaging
convert(
Imaging imOut, Imaging imIn, const char *mode, ImagingPalette palette, int dither) {
ImagingSectionCookie cookie;
ImagingShuffler convert;
int y;
2020-05-10 12:56:36 +03:00
if (!imIn) {
2013-07-09 09:39:16 +04:00
return (Imaging)ImagingError_ModeError();
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
if (!mode) {
2013-07-09 09:39:16 +04:00
/* Map palette image to full depth */
2020-05-10 12:56:36 +03:00
if (!imIn->palette) {
2013-07-09 09:39:16 +04:00
return (Imaging)ImagingError_ModeError();
2020-05-10 12:56:36 +03:00
}
2013-07-09 09:39:16 +04:00
mode = imIn->palette->mode;
2020-05-10 12:56:36 +03:00
} else {
2013-07-09 09:39:16 +04:00
/* Same mode? */
2020-05-10 12:56:36 +03:00
if (!strcmp(imIn->mode, mode)) {
2013-07-09 09:39:16 +04:00
return ImagingCopy2(imOut, imIn);
2020-05-10 12:56:36 +03:00
}
}
2010-07-31 06:52:47 +04:00
/* test for special conversions */
2020-05-10 12:56:36 +03:00
if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "PA") == 0) {
2013-07-09 09:39:16 +04:00
return frompalette(imOut, imIn, mode);
2020-05-10 12:56:36 +03:00
}
2020-05-10 12:56:36 +03:00
if (strcmp(mode, "P") == 0 || strcmp(mode, "PA") == 0) {
2019-03-29 15:13:07 +03:00
return topalette(imOut, imIn, mode, palette, dither);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2020-05-10 12:56:36 +03:00
if (dither && strcmp(mode, "1") == 0) {
2022-02-19 02:50:07 +03:00
return tobilevel(imOut, imIn);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
/* standard conversion machinery */
convert = NULL;
2020-05-10 12:56:36 +03:00
for (y = 0; converters[y].from; y++) {
2013-07-09 09:39:16 +04:00
if (!strcmp(imIn->mode, converters[y].from) &&
!strcmp(mode, converters[y].to)) {
convert = converters[y].convert;
break;
}
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2020-05-11 00:46:12 +03:00
if (!convert) {
2010-07-31 06:52:47 +04:00
#ifdef notdef
2013-07-09 09:39:16 +04:00
return (Imaging)ImagingError_ValueError("conversion not supported");
2010-07-31 06:52:47 +04:00
#else
2021-06-15 08:14:26 +03:00
static char buf[100];
2021-06-30 16:47:10 +03:00
snprintf(buf, 100, "conversion from %.10s to %.10s not supported", imIn->mode, mode);
2020-05-11 00:46:12 +03:00
return (Imaging)ImagingError_ValueError(buf);
2010-07-31 06:52:47 +04:00
#endif
2020-05-11 00:46:12 +03:00
}
2010-07-31 06:52:47 +04:00
2017-08-06 20:08:07 +03:00
imOut = ImagingNew2Dirty(mode, imOut, imIn);
2020-05-10 12:56:36 +03:00
if (!imOut) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
ImagingSectionEnter(&cookie);
2020-05-10 12:56:36 +03:00
for (y = 0; y < imIn->ysize; y++) {
2013-07-09 09:39:16 +04:00
(*convert)((UINT8 *)imOut->image[y], (UINT8 *)imIn->image[y], imIn->xsize);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
ImagingSectionLeave(&cookie);
return imOut;
}
Imaging
ImagingConvert(Imaging imIn, const char *mode, ImagingPalette palette, int dither) {
return convert(NULL, imIn, mode, palette, dither);
}
Imaging
ImagingConvert2(Imaging imOut, Imaging imIn) {
return convert(imOut, imIn, imOut->mode, NULL, 0);
}
Imaging
ImagingConvertTransparent(Imaging imIn, const char *mode, int r, int g, int b) {
ImagingSectionCookie cookie;
ImagingShuffler convert;
Imaging imOut = NULL;
2024-03-21 08:25:40 +03:00
int premultiplied = 0;
// If the transparency matches pixels in the source image, not the converted image
UINT8 *source;
int source_transparency = 0;
int y;
if (!imIn) {
return (Imaging)ImagingError_ModeError();
}
2016-09-03 05:23:42 +03:00
2024-03-21 08:25:40 +03:00
if (strcmp(imIn->mode, "RGB") == 0 && (strcmp(mode, "RGBA") == 0 || strcmp(mode, "RGBa") == 0)) {
convert = rgb2rgba;
2024-03-21 08:25:40 +03:00
if (strcmp(mode, "RGBa") == 0) {
premultiplied = 1;
}
2024-03-26 11:57:17 +03:00
} else if (strcmp(imIn->mode, "RGB") == 0 && (strcmp(mode, "LA") == 0 || strcmp(mode, "La") == 0)) {
convert = rgb2la;
source_transparency = 1;
2024-03-26 11:57:17 +03:00
if (strcmp(mode, "La") == 0) {
premultiplied = 1;
}
} else if ((strcmp(imIn->mode, "1") == 0 ||
strcmp(imIn->mode, "I") == 0 ||
2024-03-02 07:39:43 +03:00
strcmp(imIn->mode, "I;16") == 0 ||
strcmp(imIn->mode, "L") == 0
) && (
strcmp(mode, "RGBA") == 0 ||
strcmp(mode, "LA") == 0
)) {
if (strcmp(imIn->mode, "1") == 0) {
convert = bit2rgb;
} else if (strcmp(imIn->mode, "I") == 0) {
convert = i2rgb;
2024-03-02 07:39:43 +03:00
} else if (strcmp(imIn->mode, "I;16") == 0) {
convert = I16_RGB;
} else {
convert = l2rgb;
}
g = b = r;
} else {
static char buf[100];
snprintf(
buf,
100,
"conversion from %.10s to %.10s not supported in convert_transparent",
imIn->mode,
mode);
return (Imaging)ImagingError_ValueError(buf);
}
2017-08-06 20:08:07 +03:00
imOut = ImagingNew2Dirty(mode, imOut, imIn);
if (!imOut) {
return NULL;
}
ImagingSectionEnter(&cookie);
for (y = 0; y < imIn->ysize; y++) {
(*convert)((UINT8 *)imOut->image[y], (UINT8 *)imIn->image[y], imIn->xsize);
source = source_transparency ? (UINT8 *)imIn->image[y] : NULL;
rgbT2a((UINT8 *)imOut->image[y], source, imIn->xsize, r, g, b, premultiplied);
}
ImagingSectionLeave(&cookie);
2015-10-11 13:24:35 +03:00
return imOut;
}
2010-07-31 06:52:47 +04:00
Imaging
ImagingConvertInPlace(Imaging imIn, const char *mode) {
ImagingSectionCookie cookie;
ImagingShuffler convert;
int y;
/* limited support for inplace conversion */
2020-05-10 12:56:36 +03:00
if (strcmp(imIn->mode, "L") == 0 && strcmp(mode, "1") == 0) {
2010-07-31 06:52:47 +04:00
convert = l2bit;
2020-05-10 12:56:36 +03:00
} else if (strcmp(imIn->mode, "1") == 0 && strcmp(mode, "L") == 0) {
2010-07-31 06:52:47 +04:00
convert = bit2l;
2020-05-10 12:56:36 +03:00
} else {
2010-07-31 06:52:47 +04:00
return ImagingError_ModeError();
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
ImagingSectionEnter(&cookie);
2020-05-10 12:56:36 +03:00
for (y = 0; y < imIn->ysize; y++) {
2013-07-09 09:39:16 +04:00
(*convert)((UINT8 *)imIn->image[y], (UINT8 *)imIn->image[y], imIn->xsize);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
ImagingSectionLeave(&cookie);
return imIn;
}