rdppm.c: Fix CMYK upconversion/downconversion

If the data precision of the PBMPLUS file does not match the target data
precision, then the grayscale or RGB samples are rescaled to the target
data precision.  Thus, we need to pass (1 << cinfo->data_precision) - 1
to rgb_to_cmyk() instead of maxval.  This commit also modifies
TJUnitTest so that it validates the correctness of upconversion and
downconversion in the PPM reader.

Fixes #841
This commit is contained in:
DRC
2025-12-09 15:15:07 -05:00
parent fc4b417a90
commit 3cea8ccd51
4 changed files with 341 additions and 215 deletions

View File

@@ -23,6 +23,11 @@ the JNI wrapper's buffer size checks that rendered those checks ineffective.
invocation of `TJCompressor.loadSourceImage()` if the target data precision was
changed before the most recent invocation.
5. Fixed an issue in the PPM reader that caused incorrect pixels to be
generated when using `tj3LoadImage*()` or `TJCompressor.loadSourceImage()` to
load a PBMPLUS (PPM/PGM) file into a CMYK buffer with a different data
precision than that of the file.
3.1.2
=====

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C)2011-2018, 2022-2024 D. R. Commander. All Rights Reserved.
* Copyright (C)2011-2018, 2022-2025 D. R. Commander. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -320,14 +320,14 @@ final class TJUnitTest {
}
}
static int getVal(Object buf, int index) {
static int getVal(Object buf, int index, int targetPrecision) {
int v;
if (precision <= 8)
if (targetPrecision <= 8)
v = (int)(((byte[])buf)[index]);
else
v = (int)(((short[])buf)[index]);
if (v < 0)
v += maxSample + 1;
v += (1 << targetPrecision);
return v;
}
@@ -356,10 +356,10 @@ final class TJUnitTest {
index = (h - row - 1) * w + col;
else
index = row * w + col;
int c = getVal(buf, index * ps);
int m = getVal(buf, index * ps + 1);
int y = getVal(buf, index * ps + 2);
int k = getVal(buf, index * ps + 3);
int c = getVal(buf, index * ps, precision);
int m = getVal(buf, index * ps + 1, precision);
int y = getVal(buf, index * ps + 2, precision);
int k = getVal(buf, index * ps + 3, precision);
checkValMax(row, col, c, "C");
if (((row / blockSize) + (col / blockSize)) % 2 == 0) {
checkValMax(row, col, m, "M");
@@ -387,10 +387,11 @@ final class TJUnitTest {
index = pitch * (h - row - 1) + col * ps;
else
index = pitch * row + col * ps;
int r = getVal(buf, index + roffset);
int g = getVal(buf, index + goffset);
int b = getVal(buf, index + boffset);
int a = aoffset >= 0 ? getVal(buf, index + aoffset) : maxSample;
int r = getVal(buf, index + roffset, precision);
int g = getVal(buf, index + goffset, precision);
int b = getVal(buf, index + boffset, precision);
int a = aoffset >= 0 ? getVal(buf, index + aoffset, precision) :
maxSample;
if (((row / blockSize) + (col / blockSize)) % 2 == 0) {
if (row < halfway) {
checkValMax(row, col, r, "R");
@@ -434,15 +435,15 @@ final class TJUnitTest {
for (row = 0; row < h; row++) {
for (col = 0; col < w; col++) {
if (pf == TJ.PF_CMYK) {
int c = getVal(buf, pitch * row + col * ps);
int m = getVal(buf, pitch * row + col * ps + 1);
int y = getVal(buf, pitch * row + col * ps + 2);
int k = getVal(buf, pitch * row + col * ps + 3);
int c = getVal(buf, pitch * row + col * ps, precision);
int m = getVal(buf, pitch * row + col * ps + 1, precision);
int y = getVal(buf, pitch * row + col * ps + 2, precision);
int k = getVal(buf, pitch * row + col * ps + 3, precision);
System.out.format("%3d/%3d/%3d/%3d ", c, m, y, k);
} else {
int r = getVal(buf, pitch * row + col * ps + roffset);
int g = getVal(buf, pitch * row + col * ps + goffset);
int b = getVal(buf, pitch * row + col * ps + boffset);
int r = getVal(buf, pitch * row + col * ps + roffset, precision);
int g = getVal(buf, pitch * row + col * ps + goffset, precision);
int b = getVal(buf, pitch * row + col * ps + boffset, precision);
System.out.format("%3d/%3d/%3d ", r, g, b);
}
}
@@ -1080,20 +1081,22 @@ final class TJUnitTest {
}
static void cmykToRGB(int c, int m, int y, int k, int[] r, int[] g,
int[] b) {
r[0] = (int)((double)c * (double)k / (double)maxSample + 0.5);
g[0] = (int)((double)m * (double)k / (double)maxSample + 0.5);
b[0] = (int)((double)y * (double)k / (double)maxSample + 0.5);
int[] b, int targetMaxSample) {
r[0] = (int)((double)c * (double)k / (double)targetMaxSample + 0.5);
g[0] = (int)((double)m * (double)k / (double)targetMaxSample + 0.5);
b[0] = (int)((double)y * (double)k / (double)targetMaxSample + 0.5);
}
static boolean cmpBitmap(Object buf, int width, int pitch, int height,
int pf, boolean bottomUp, boolean gray2rgb) {
int pf, boolean bottomUp, boolean gray2rgb,
int targetPrecision) {
int roffset = TJ.getRedOffset(pf);
int goffset = TJ.getGreenOffset(pf);
int boffset = TJ.getBlueOffset(pf);
int aoffset = TJ.getAlphaOffset(pf);
int ps = TJ.getPixelSize(pf);
int i, j;
int targetMaxSample = (1 << targetPrecision) - 1;
for (j = 0; j < height; j++) {
int row = bottomUp ? height - j - 1 : j;
@@ -1104,16 +1107,28 @@ final class TJUnitTest {
int b = (j * (maxSample + 1) / height +
i * (maxSample + 1) / width) % (maxSample + 1);
if (precision != targetPrecision) {
long halfMaxSample = maxSample / 2;
r = (int)((r * ((1 << targetPrecision) - 1) + halfMaxSample) /
maxSample);
g = (int)((g * ((1 << targetPrecision) - 1) + halfMaxSample) /
maxSample);
b = (int)((b * ((1 << targetPrecision) - 1) + halfMaxSample) /
maxSample);
}
if (pf == TJ.PF_GRAY) {
if (getVal(buf, row * pitch + i * ps) != b)
if (getVal(buf, row * pitch + i * ps, targetPrecision) != b)
return false;
} else if (pf == TJ.PF_CMYK) {
int[] rf = new int[1], gf = new int[1], bf = new int[1];
cmykToRGB(getVal(buf, row * pitch + i * ps + 0),
getVal(buf, row * pitch + i * ps + 1),
getVal(buf, row * pitch + i * ps + 2),
getVal(buf, row * pitch + i * ps + 3), rf, gf, bf);
cmykToRGB(getVal(buf, row * pitch + i * ps + 0, targetPrecision),
getVal(buf, row * pitch + i * ps + 1, targetPrecision),
getVal(buf, row * pitch + i * ps + 2, targetPrecision),
getVal(buf, row * pitch + i * ps + 3, targetPrecision),
rf, gf, bf, targetMaxSample);
if (gray2rgb) {
if (rf[0] != b || gf[0] != b || bf[0] != b)
return false;
@@ -1121,16 +1136,23 @@ final class TJUnitTest {
return false;
} else {
if (gray2rgb) {
if (getVal(buf, row * pitch + i * ps + roffset) != b ||
getVal(buf, row * pitch + i * ps + goffset) != b ||
getVal(buf, row * pitch + i * ps + boffset) != b)
if (getVal(buf, row * pitch + i * ps + roffset,
targetPrecision) != b ||
getVal(buf, row * pitch + i * ps + goffset,
targetPrecision) != b ||
getVal(buf, row * pitch + i * ps + boffset,
targetPrecision) != b)
return false;
} else if (getVal(buf, row * pitch + i * ps + roffset) != r ||
getVal(buf, row * pitch + i * ps + goffset) != g ||
getVal(buf, row * pitch + i * ps + boffset) != b)
} else if (getVal(buf, row * pitch + i * ps + roffset,
targetPrecision) != r ||
getVal(buf, row * pitch + i * ps + goffset,
targetPrecision) != g ||
getVal(buf, row * pitch + i * ps + boffset,
targetPrecision) != b)
return false;
if (aoffset >= 0 &&
getVal(buf, row * pitch + i * ps + aoffset) != maxSample)
getVal(buf, row * pitch + i * ps + aoffset,
targetPrecision) != targetMaxSample)
return false;
}
}
@@ -1174,19 +1196,20 @@ final class TJUnitTest {
"fc2803bca103ff75785ea0dca992aa", "d8c91fac522c16b029e514d331a22bc4",
"e50cff0b3562ed7e64dbfc093440e333", "64f3320b226ea37fb58080713b4df1b2"
};
int maxTargetPrecision = 16;
try {
tjc = new TJCompressor();
tjc.set(TJ.PARAM_BOTTOMUP, bottomUp ? 1 : 0);
tjc.set(TJ.PARAM_PRECISION, precision);
tjd = new TJDecompressor();
tjd.set(TJ.PARAM_BOTTOMUP, bottomUp ? 1 : 0);
tjd.set(TJ.PARAM_PRECISION, precision);
if (precision == 8 && ext.equalsIgnoreCase("bmp"))
if (precision == 8 && ext.equalsIgnoreCase("bmp")) {
md5ref = (pf == TJ.PF_GRAY ? "51976530acf75f02beddf5d21149101d" :
"6d659071b9bfcdee2def22cb58ddadca");
else
maxTargetPrecision = 8;
} else
md5ref = (pf == TJ.PF_GRAY ? grayPPMRefs[precision] :
colorPPMRefs[precision]);
@@ -1207,32 +1230,11 @@ final class TJUnitTest {
throw new Exception(filename + " has an MD5 sum of " + md5sum +
". Should be " + md5ref);
tjc.loadSourceImage(filename, align, pf);
loadWidth = tjc.getWidth();
loadPitch = tjc.getPitch();
loadHeight = tjc.getHeight();
pf = tjc.getPixelFormat();
buf = tjc.getSourceBuf();
if (width != loadWidth || pitch != loadPitch || height != loadHeight)
throw new Exception("Image dimensions of " + filename + " are bogus");
if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, false))
throw new Exception("Pixel data in " + filename + " is bogus");
if (pf == TJ.PF_GRAY) {
pf = TJ.PF_XBGR;
tjc.loadSourceImage(filename, align, pf);
loadWidth = tjc.getWidth();
loadPitch = tjc.getPitch();
loadHeight = tjc.getHeight();
pf = tjc.getPixelFormat();
buf = tjc.getSourceBuf();
pitch = pad(width * TJ.getPixelSize(pf), align);
if (width != loadWidth || pitch != loadPitch || height != loadHeight)
throw new Exception("Image dimensions of " + filename +
" are bogus");
if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, true))
throw new Exception("Converting " + filename + " to RGB failed");
for (int targetPrecision = 2; targetPrecision <= maxTargetPrecision;
targetPrecision++) {
tjc.set(TJ.PARAM_PRECISION, targetPrecision);
pf = pixelFormat;
pf = TJ.PF_CMYK;
tjc.loadSourceImage(filename, align, pf);
loadWidth = tjc.getWidth();
loadPitch = tjc.getPitch();
@@ -1243,22 +1245,62 @@ final class TJUnitTest {
if (width != loadWidth || pitch != loadPitch || height != loadHeight)
throw new Exception("Image dimensions of " + filename +
" are bogus");
if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, true))
throw new Exception("Converting " + filename + " to CMYK failed");
if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, false,
ext.equalsIgnoreCase("bmp") ? 8 : targetPrecision))
throw new Exception("Pixel data in " + filename + " is bogus " +
"(target data precision = " + targetPrecision +
")");
if (pf == TJ.PF_GRAY) {
pf = TJ.PF_XBGR;
tjc.loadSourceImage(filename, align, pf);
loadWidth = tjc.getWidth();
loadPitch = tjc.getPitch();
loadHeight = tjc.getHeight();
pf = tjc.getPixelFormat();
buf = tjc.getSourceBuf();
pitch = pad(width * TJ.getPixelSize(pf), align);
if (width != loadWidth || pitch != loadPitch || height != loadHeight)
throw new Exception("Image dimensions of " + filename +
" are bogus");
if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, true,
ext.equalsIgnoreCase("bmp") ? 8 : targetPrecision))
throw new Exception("Converting " + filename + " to RGB failed " +
"(target data precision = " + targetPrecision +
")");
pf = TJ.PF_CMYK;
tjc.loadSourceImage(filename, align, pf);
loadWidth = tjc.getWidth();
loadPitch = tjc.getPitch();
loadHeight = tjc.getHeight();
pf = tjc.getPixelFormat();
buf = tjc.getSourceBuf();
pitch = pad(width * TJ.getPixelSize(pf), align);
if (width != loadWidth || pitch != loadPitch || height != loadHeight)
throw new Exception("Image dimensions of " + filename +
" are bogus");
if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, true,
ext.equalsIgnoreCase("bmp") ? 8 : targetPrecision))
throw new Exception("Converting " + filename +
" to CMYK failed (target data precision = " +
targetPrecision + ")");
}
/* Verify that TJCompressor.loadSourceImage() returns the proper
"preferred" pixel format for the file type. */
pf = pixelFormat;
pixelFormat = TJ.PF_UNKNOWN;
tjc.loadSourceImage(filename, align, pixelFormat);
pixelFormat = tjc.getPixelFormat();
if ((pf == TJ.PF_GRAY && pixelFormat != TJ.PF_GRAY) ||
(pf != TJ.PF_GRAY && ext.equalsIgnoreCase("bmp") &&
pixelFormat != TJ.PF_BGR) ||
(pf != TJ.PF_GRAY && ext.equalsIgnoreCase("ppm") &&
pixelFormat != TJ.PF_RGB))
throw new Exception("TJCompressor.loadImage() returned unexpected " +
"pixel format: " + PIXFORMATSTR[pixelFormat]);
}
/* Verify that TJCompressor.loadSourceImage() returns the proper
"preferred" pixel format for the file type. */
pf = pixelFormat;
pixelFormat = TJ.PF_UNKNOWN;
tjc.loadSourceImage(filename, align, pixelFormat);
pixelFormat = tjc.getPixelFormat();
if ((pf == TJ.PF_GRAY && pixelFormat != TJ.PF_GRAY) ||
(pf != TJ.PF_GRAY && ext.equalsIgnoreCase("bmp") &&
pixelFormat != TJ.PF_BGR) ||
(pf != TJ.PF_GRAY && ext.equalsIgnoreCase("ppm") &&
pixelFormat != TJ.PF_RGB))
throw new Exception("TJCompressor.loadImage() returned unexpected " +
"pixel format: " + PIXFORMATSTR[pixelFormat]);
File file = new File(filename);
file.delete();
} catch (Exception e) {

View File

@@ -5,7 +5,7 @@
* Copyright (C) 1991-1997, Thomas G. Lane.
* Modified 2009 by Bill Allombert, Guido Vollbeding.
* libjpeg-turbo Modifications:
* Copyright (C) 2015-2017, 2020-2024, D. R. Commander.
* Copyright (C) 2015-2017, 2020-2025, D. R. Commander.
* For conditions of distribution and use, see the accompanying README.ijg
* file.
*
@@ -217,7 +217,8 @@ get_text_gray_cmyk_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
} else {
for (col = cinfo->image_width; col > 0; col--) {
_JSAMPLE gray = rescale[read_pbm_integer(cinfo, infile, maxval)];
rgb_to_cmyk(maxval, gray, gray, gray, ptr, ptr + 1, ptr + 2, ptr + 3);
rgb_to_cmyk((1 << cinfo->data_precision) - 1, gray, gray, gray, ptr,
ptr + 1, ptr + 2, ptr + 3);
ptr += 4;
}
}
@@ -295,7 +296,8 @@ get_text_rgb_cmyk_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
_JSAMPLE r = rescale[read_pbm_integer(cinfo, infile, maxval)];
_JSAMPLE g = rescale[read_pbm_integer(cinfo, infile, maxval)];
_JSAMPLE b = rescale[read_pbm_integer(cinfo, infile, maxval)];
rgb_to_cmyk(maxval, r, g, b, ptr, ptr + 1, ptr + 2, ptr + 3);
rgb_to_cmyk((1 << cinfo->data_precision) - 1, r, g, b, ptr, ptr + 1,
ptr + 2, ptr + 3);
ptr += 4;
}
}
@@ -386,7 +388,8 @@ get_gray_cmyk_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
} else {
for (col = cinfo->image_width; col > 0; col--) {
_JSAMPLE gray = rescale[UCH(*bufferptr++)];
rgb_to_cmyk(maxval, gray, gray, gray, ptr, ptr + 1, ptr + 2, ptr + 3);
rgb_to_cmyk((1 << cinfo->data_precision) - 1, gray, gray, gray, ptr,
ptr + 1, ptr + 2, ptr + 3);
ptr += 4;
}
}
@@ -459,7 +462,8 @@ get_rgb_cmyk_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
_JSAMPLE r = rescale[UCH(*bufferptr++)];
_JSAMPLE g = rescale[UCH(*bufferptr++)];
_JSAMPLE b = rescale[UCH(*bufferptr++)];
rgb_to_cmyk(maxval, r, g, b, ptr, ptr + 1, ptr + 2, ptr + 3);
rgb_to_cmyk((1 << cinfo->data_precision) - 1, r, g, b, ptr, ptr + 1,
ptr + 2, ptr + 3);
ptr += 4;
}
}
@@ -560,15 +564,29 @@ get_word_gray_cmyk_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
ERREXIT(cinfo, JERR_INPUT_EOF);
ptr = source->pub._buffer[0];
bufferptr = source->iobuffer;
for (col = cinfo->image_width; col > 0; col--) {
register unsigned int gray;
gray = UCH(*bufferptr++) << 8;
gray |= UCH(*bufferptr++);
if (gray > maxval)
ERREXIT(cinfo, JERR_PPM_OUTOFRANGE);
rgb_to_cmyk(maxval, rescale[gray], rescale[gray], rescale[gray], ptr,
ptr + 1, ptr + 2, ptr + 3);
ptr += 4;
if (maxval == (1U << cinfo->data_precision) - 1U) {
for (col = cinfo->image_width; col > 0; col--) {
register unsigned int gray;
gray = UCH(*bufferptr++) << 8;
gray |= UCH(*bufferptr++);
if (gray > maxval)
ERREXIT(cinfo, JERR_PPM_OUTOFRANGE);
rgb_to_cmyk(maxval, gray, gray, gray, ptr, ptr + 1, ptr + 2, ptr + 3);
ptr += 4;
}
} else {
for (col = cinfo->image_width; col > 0; col--) {
register unsigned int temp;
_JSAMPLE gray;
temp = UCH(*bufferptr++) << 8;
temp |= UCH(*bufferptr++);
if (temp > maxval)
ERREXIT(cinfo, JERR_PPM_OUTOFRANGE);
gray = rescale[temp];
rgb_to_cmyk((1 << cinfo->data_precision) - 1, gray, gray, gray, ptr,
ptr + 1, ptr + 2, ptr + 3);
ptr += 4;
}
}
return 1;
}
@@ -634,23 +652,43 @@ get_word_rgb_cmyk_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
ERREXIT(cinfo, JERR_INPUT_EOF);
ptr = source->pub._buffer[0];
bufferptr = source->iobuffer;
for (col = cinfo->image_width; col > 0; col--) {
register unsigned int r, g, b;
r = UCH(*bufferptr++) << 8;
r |= UCH(*bufferptr++);
if (r > maxval)
ERREXIT(cinfo, JERR_PPM_OUTOFRANGE);
g = UCH(*bufferptr++) << 8;
g |= UCH(*bufferptr++);
if (g > maxval)
ERREXIT(cinfo, JERR_PPM_OUTOFRANGE);
b = UCH(*bufferptr++) << 8;
b |= UCH(*bufferptr++);
if (b > maxval)
ERREXIT(cinfo, JERR_PPM_OUTOFRANGE);
rgb_to_cmyk(maxval, rescale[r], rescale[g], rescale[b], ptr, ptr + 1,
ptr + 2, ptr + 3);
ptr += 4;
if (maxval == (1U << cinfo->data_precision) - 1U) {
for (col = cinfo->image_width; col > 0; col--) {
register unsigned int r, g, b;
r = UCH(*bufferptr++) << 8;
r |= UCH(*bufferptr++);
if (r > maxval)
ERREXIT(cinfo, JERR_PPM_OUTOFRANGE);
g = UCH(*bufferptr++) << 8;
g |= UCH(*bufferptr++);
if (g > maxval)
ERREXIT(cinfo, JERR_PPM_OUTOFRANGE);
b = UCH(*bufferptr++) << 8;
b |= UCH(*bufferptr++);
if (b > maxval)
ERREXIT(cinfo, JERR_PPM_OUTOFRANGE);
rgb_to_cmyk(maxval, r, g, b, ptr, ptr + 1, ptr + 2, ptr + 3);
ptr += 4;
}
} else {
for (col = cinfo->image_width; col > 0; col--) {
register unsigned int r, g, b;
r = UCH(*bufferptr++) << 8;
r |= UCH(*bufferptr++);
if (r > maxval)
ERREXIT(cinfo, JERR_PPM_OUTOFRANGE);
g = UCH(*bufferptr++) << 8;
g |= UCH(*bufferptr++);
if (g > maxval)
ERREXIT(cinfo, JERR_PPM_OUTOFRANGE);
b = UCH(*bufferptr++) << 8;
b |= UCH(*bufferptr++);
if (b > maxval)
ERREXIT(cinfo, JERR_PPM_OUTOFRANGE);
rgb_to_cmyk((1 << cinfo->data_precision) - 1, rescale[r], rescale[g],
rescale[b], ptr, ptr + 1, ptr + 2, ptr + 3);
ptr += 4;
}
}
return 1;
}

View File

@@ -199,11 +199,11 @@ static void initBuf(void *buf, int w, int h, int pf, int bottomUp)
}
static int getVal(void *buf, int index)
static int getVal(void *buf, int index, int targetPrecision)
{
if (precision <= 8)
if (targetPrecision <= 8)
return ((unsigned char *)buf)[index];
else if (precision <= 12)
else if (targetPrecision <= 12)
return ((short *)buf)[index];
else
return ((unsigned short *)buf)[index];
@@ -230,10 +230,10 @@ static int checkBuf(void *buf, int w, int h, int pf, int subsamp,
if (bottomUp) index = (h - row - 1) * w + col;
else index = row * w + col;
c = getVal(buf, index * ps);
m = getVal(buf, index * ps + 1);
y = getVal(buf, index * ps + 2);
k = getVal(buf, index * ps + 3);
c = getVal(buf, index * ps, precision);
m = getVal(buf, index * ps + 1, precision);
y = getVal(buf, index * ps + 2, precision);
k = getVal(buf, index * ps + 3, precision);
if (((row / blocksize) + (col / blocksize)) % 2 == 0) {
CHECKVALMAX(c); CHECKVALMAX(m); CHECKVALMAX(y);
if (row < halfway) CHECKVALMAX(k)
@@ -254,10 +254,11 @@ static int checkBuf(void *buf, int w, int h, int pf, int subsamp,
if (bottomUp) index = (h - row - 1) * w + col;
else index = row * w + col;
r = getVal(buf, index * ps + roffset);
g = getVal(buf, index * ps + goffset);
b = getVal(buf, index * ps + boffset);
a = aoffset >= 0 ? getVal(buf, index * ps + aoffset) : maxSample;
r = getVal(buf, index * ps + roffset, precision);
g = getVal(buf, index * ps + goffset, precision);
b = getVal(buf, index * ps + boffset, precision);
a = aoffset >= 0 ? getVal(buf, index * ps + aoffset, precision) :
maxSample;
if (((row / blocksize) + (col / blocksize)) % 2 == 0) {
if (row < halfway) {
CHECKVALMAX(r); CHECKVALMAX(g); CHECKVALMAX(b);
@@ -289,15 +290,16 @@ bailout:
for (row = 0; row < h; row++) {
for (col = 0; col < w; col++) {
if (pf == TJPF_CMYK)
printf("%.3d/%.3d/%.3d/%.3d ", getVal(buf, (row * w + col) * ps),
getVal(buf, (row * w + col) * ps + 1),
getVal(buf, (row * w + col) * ps + 2),
getVal(buf, (row * w + col) * ps + 3));
printf("%.3d/%.3d/%.3d/%.3d ",
getVal(buf, (row * w + col) * ps, precision),
getVal(buf, (row * w + col) * ps + 1, precision),
getVal(buf, (row * w + col) * ps + 2, precision),
getVal(buf, (row * w + col) * ps + 3, precision));
else
printf("%.3d/%.3d/%.3d ",
getVal(buf, (row * w + col) * ps + roffset),
getVal(buf, (row * w + col) * ps + goffset),
getVal(buf, (row * w + col) * ps + boffset));
getVal(buf, (row * w + col) * ps + roffset, precision),
getVal(buf, (row * w + col) * ps + goffset, precision),
getVal(buf, (row * w + col) * ps + boffset, precision));
}
printf("\n");
}
@@ -909,15 +911,16 @@ static void initBitmap(void *buf, int width, int pitch, int height, int pf,
}
static void cmyk_to_rgb(int c, int m, int y, int k, int *r, int *g, int *b)
static void cmyk_to_rgb(int c, int m, int y, int k, int *r, int *g, int *b,
int targetMaxSample)
{
*r = (int)((double)c * (double)k / (double)maxSample + 0.5);
*g = (int)((double)m * (double)k / (double)maxSample + 0.5);
*b = (int)((double)y * (double)k / (double)maxSample + 0.5);
*r = (int)((double)c * (double)k / (double)targetMaxSample + 0.5);
*g = (int)((double)m * (double)k / (double)targetMaxSample + 0.5);
*b = (int)((double)y * (double)k / (double)targetMaxSample + 0.5);
}
static int cmpBitmap(void *buf, int width, int pitch, int height, int pf,
int bottomUp, int gray2rgb)
int bottomUp, int gray2rgb, int targetPrecision)
{
int roffset = tjRedOffset[pf];
int goffset = tjGreenOffset[pf];
@@ -925,6 +928,7 @@ static int cmpBitmap(void *buf, int width, int pitch, int height, int pf,
int aoffset = tjAlphaOffset[pf];
int ps = tjPixelSize[pf];
int i, j;
int targetMaxSample = (1 << targetPrecision) - 1;
for (j = 0; j < height; j++) {
int row = bottomUp ? height - j - 1 : j;
@@ -935,32 +939,51 @@ static int cmpBitmap(void *buf, int width, int pitch, int height, int pf,
int b = (j * (maxSample + 1) / height +
i * (maxSample + 1) / width) % (maxSample + 1);
if (precision != targetPrecision) {
long halfMaxSample = maxSample / 2;
r = (int)((r * ((1 << targetPrecision) - 1) + halfMaxSample) /
maxSample);
g = (int)((g * ((1 << targetPrecision) - 1) + halfMaxSample) /
maxSample);
b = (int)((b * ((1 << targetPrecision) - 1) + halfMaxSample) /
maxSample);
}
if (pf == TJPF_GRAY) {
if (getVal(buf, row * pitch + i * ps) != b)
if (getVal(buf, row * pitch + i * ps, targetPrecision) != b)
return 0;
} else if (pf == TJPF_CMYK) {
int rf, gf, bf;
cmyk_to_rgb(getVal(buf, row * pitch + i * ps + 0),
getVal(buf, row * pitch + i * ps + 1),
getVal(buf, row * pitch + i * ps + 2),
getVal(buf, row * pitch + i * ps + 3), &rf, &gf, &bf);
cmyk_to_rgb(getVal(buf, row * pitch + i * ps + 0, targetPrecision),
getVal(buf, row * pitch + i * ps + 1, targetPrecision),
getVal(buf, row * pitch + i * ps + 2, targetPrecision),
getVal(buf, row * pitch + i * ps + 3, targetPrecision),
&rf, &gf, &bf, targetMaxSample);
if (gray2rgb) {
if (rf != b || gf != b || bf != b)
return 0;
} else if (rf != r || gf != g || bf != b) return 0;
} else {
if (gray2rgb) {
if (getVal(buf, row * pitch + i * ps + roffset) != b ||
getVal(buf, row * pitch + i * ps + goffset) != b ||
getVal(buf, row * pitch + i * ps + boffset) != b)
if (getVal(buf, row * pitch + i * ps + roffset,
targetPrecision) != b ||
getVal(buf, row * pitch + i * ps + goffset,
targetPrecision) != b ||
getVal(buf, row * pitch + i * ps + boffset,
targetPrecision) != b)
return 0;
} else if (getVal(buf, row * pitch + i * ps + roffset) != r ||
getVal(buf, row * pitch + i * ps + goffset) != g ||
getVal(buf, row * pitch + i * ps + boffset) != b)
} else if (getVal(buf, row * pitch + i * ps + roffset,
targetPrecision) != r ||
getVal(buf, row * pitch + i * ps + goffset,
targetPrecision) != g ||
getVal(buf, row * pitch + i * ps + boffset,
targetPrecision) != b)
return 0;
if (aoffset >= 0 &&
getVal(buf, row * pitch + i * ps + aoffset) != maxSample)
getVal(buf, row * pitch + i * ps + aoffset,
targetPrecision) != targetMaxSample)
return 0;
}
}
@@ -998,16 +1021,18 @@ static int doBmpTest(const char *ext, int width, int align, int height, int pf,
"00fc2803bca103ff75785ea0dca992aa", "d8c91fac522c16b029e514d331a22bc4",
"e50cff0b3562ed7e64dbfc093440e333", "64f3320b226ea37fb58080713b4df1b2"
};
int targetPrecision, maxTargetPrecision = 16;
if ((handle = tj3Init(TJINIT_TRANSFORM)) == NULL)
THROW_TJ(NULL);
TRY_TJ(handle, tj3Set(handle, TJPARAM_BOTTOMUP, bottomUp));
TRY_TJ(handle, tj3Set(handle, TJPARAM_PRECISION, precision));
if (precision == 8 && !strcasecmp(ext, "bmp"))
if (precision == 8 && !strcasecmp(ext, "bmp")) {
md5ref = (pf == TJPF_GRAY ? "51976530acf75f02beddf5d21149101d" :
"6d659071b9bfcdee2def22cb58ddadca");
else
maxTargetPrecision = 8;
} else
md5ref = (pf == TJPF_GRAY ? grayPPMRefs[precision] :
colorPPMRefs[precision]);
@@ -1027,6 +1052,7 @@ static int doBmpTest(const char *ext, int width, int align, int height, int pf,
TRY_TJ(handle, tj3SaveImage16(handle, filename, (unsigned short *)buf,
width, pitch, height, pf));
}
tj3Free(buf); buf = NULL;
md5sum = MD5File(filename, md5buf);
if (!md5sum) {
printf("\n Could not determine MD5 sum of %s\n", filename);
@@ -1035,36 +1061,16 @@ static int doBmpTest(const char *ext, int width, int align, int height, int pf,
if (strcasecmp(md5sum, md5ref))
THROW_MD5(filename, md5sum, md5ref);
tj3Free(buf); buf = NULL;
if (precision <= 8) {
if ((buf = tj3LoadImage8(handle, filename, &loadWidth, align, &loadHeight,
&pf)) == NULL)
THROW_TJ(handle);
} else if (precision <= 12) {
if ((buf = tj3LoadImage12(handle, filename, &loadWidth, align, &loadHeight,
&pf)) == NULL)
THROW_TJ(handle);
} else {
if ((buf = tj3LoadImage16(handle, filename, &loadWidth, align, &loadHeight,
&pf)) == NULL)
THROW_TJ(handle);
}
if (width != loadWidth || height != loadHeight) {
printf("\n Image dimensions of %s are bogus\n", filename);
retval = -1; goto bailout;
}
if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, 0)) {
printf("\n Pixel data in %s is bogus\n", filename);
retval = -1; goto bailout;
}
if (pf == TJPF_GRAY) {
tj3Free(buf); buf = NULL;
pf = TJPF_XBGR;
if (precision <= 8) {
for (targetPrecision = 2; targetPrecision <= maxTargetPrecision;
targetPrecision++) {
TRY_TJ(handle, tj3Set(handle, TJPARAM_PRECISION, targetPrecision));
pf = pixelFormat;
if (targetPrecision <= 8) {
if ((buf = tj3LoadImage8(handle, filename, &loadWidth, align,
&loadHeight, &pf)) == NULL)
THROW_TJ(handle);
} else if (precision <= 12) {
} else if (targetPrecision <= 12) {
if ((buf = tj3LoadImage12(handle, filename, &loadWidth, align,
&loadHeight, &pf)) == NULL)
THROW_TJ(handle);
@@ -1073,60 +1079,95 @@ static int doBmpTest(const char *ext, int width, int align, int height, int pf,
&loadHeight, &pf)) == NULL)
THROW_TJ(handle);
}
pitch = PAD(width * tjPixelSize[pf], align);
if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, 1)) {
printf("\n Converting %s to RGB failed\n", filename);
if (width != loadWidth || height != loadHeight) {
printf("\n Image dimensions of %s are bogus\n", filename);
retval = -1; goto bailout;
}
pitch = PAD(width * tjPixelSize[pf], align);
if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, 0,
!strcasecmp(ext, "bmp") ? 8 : targetPrecision)) {
printf("\n Pixel data in %s is bogus\n", filename);
printf(" (target data precision = %d)\n", targetPrecision);
retval = -1; goto bailout;
}
tj3Free(buf); buf = NULL;
if (pf == TJPF_GRAY) {
pf = TJPF_XBGR;
if (targetPrecision <= 8) {
if ((buf = tj3LoadImage8(handle, filename, &loadWidth, align,
&loadHeight, &pf)) == NULL)
THROW_TJ(handle);
} else if (targetPrecision <= 12) {
if ((buf = tj3LoadImage12(handle, filename, &loadWidth, align,
&loadHeight, &pf)) == NULL)
THROW_TJ(handle);
} else {
if ((buf = tj3LoadImage16(handle, filename, &loadWidth, align,
&loadHeight, &pf)) == NULL)
THROW_TJ(handle);
}
pitch = PAD(width * tjPixelSize[pf], align);
if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, 1,
!strcasecmp(ext, "bmp") ? 8 : targetPrecision)) {
printf("\n Converting %s to RGB failed\n", filename);
printf(" (target data precision = %d)\n", targetPrecision);
retval = -1; goto bailout;
}
tj3Free(buf); buf = NULL;
pf = TJPF_CMYK;
if (targetPrecision <= 8) {
if ((buf = tj3LoadImage8(handle, filename, &loadWidth, align,
&loadHeight, &pf)) == NULL)
THROW_TJ(handle);
} else if (targetPrecision <= 12) {
if ((buf = tj3LoadImage12(handle, filename, &loadWidth, align,
&loadHeight, &pf)) == NULL)
THROW_TJ(handle);
} else {
if ((buf = tj3LoadImage16(handle, filename, &loadWidth, align,
&loadHeight, &pf)) == NULL)
THROW_TJ(handle);
}
pitch = PAD(width * tjPixelSize[pf], align);
if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, 1,
!strcasecmp(ext, "bmp") ? 8 : targetPrecision)) {
printf("\n Converting %s to CMYK failed\n", filename);
printf(" (target data precision = %d)\n", targetPrecision);
retval = -1; goto bailout;
}
tj3Free(buf); buf = NULL;
}
tj3Free(buf); buf = NULL;
pf = TJPF_CMYK;
if (precision <= 8) {
/* Verify that tj3LoadImage*() returns the proper "preferred" pixel format
for the file type. */
pf = pixelFormat;
pixelFormat = TJPF_UNKNOWN;
if (targetPrecision <= 8) {
if ((buf = tj3LoadImage8(handle, filename, &loadWidth, align,
&loadHeight, &pf)) == NULL)
&loadHeight, &pixelFormat)) == NULL)
THROW_TJ(handle);
} else if (precision <= 12) {
} else if (targetPrecision <= 12) {
if ((buf = tj3LoadImage12(handle, filename, &loadWidth, align,
&loadHeight, &pf)) == NULL)
&loadHeight, &pixelFormat)) == NULL)
THROW_TJ(handle);
} else {
if ((buf = tj3LoadImage16(handle, filename, &loadWidth, align,
&loadHeight, &pf)) == NULL)
&loadHeight, &pixelFormat)) == NULL)
THROW_TJ(handle);
}
pitch = PAD(width * tjPixelSize[pf], align);
if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, 1)) {
printf("\n Converting %s to CMYK failed\n", filename);
retval = -1; goto bailout;
tj3Free(buf); buf = NULL;
if ((pf == TJPF_GRAY && pixelFormat != TJPF_GRAY) ||
(pf != TJPF_GRAY && !strcasecmp(ext, "bmp") &&
pixelFormat != TJPF_BGR) ||
(pf != TJPF_GRAY && !strcasecmp(ext, "ppm") &&
pixelFormat != TJPF_RGB)) {
printf("\n tj3LoadImage8() returned unexpected pixel format: %s\n",
pixFormatStr[pixelFormat]);
retval = -1;
}
}
/* Verify that tj3LoadImage*() returns the proper "preferred" pixel format
for the file type. */
tj3Free(buf); buf = NULL;
pf = pixelFormat;
pixelFormat = TJPF_UNKNOWN;
if (precision <= 8) {
if ((buf = tj3LoadImage8(handle, filename, &loadWidth, align, &loadHeight,
&pixelFormat)) == NULL)
THROW_TJ(handle);
} else if (precision <= 12) {
if ((buf = tj3LoadImage12(handle, filename, &loadWidth, align, &loadHeight,
&pixelFormat)) == NULL)
THROW_TJ(handle);
} else {
if ((buf = tj3LoadImage16(handle, filename, &loadWidth, align, &loadHeight,
&pixelFormat)) == NULL)
THROW_TJ(handle);
}
if ((pf == TJPF_GRAY && pixelFormat != TJPF_GRAY) ||
(pf != TJPF_GRAY && !strcasecmp(ext, "bmp") &&
pixelFormat != TJPF_BGR) ||
(pf != TJPF_GRAY && !strcasecmp(ext, "ppm") &&
pixelFormat != TJPF_RGB)) {
printf("\n tj3LoadImage8() returned unexpected pixel format: %s\n",
pixFormatStr[pixelFormat]);
retval = -1;
}
unlink(filename);
bailout: