mirror of
https://github.com/libjpeg-turbo/libjpeg-turbo.git
synced 2026-01-12 00:04:17 +08:00
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:
@@ -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
|
||||
=====
|
||||
|
||||
@@ -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,16 +1230,27 @@ final class TJUnitTest {
|
||||
throw new Exception(filename + " has an MD5 sum of " + md5sum +
|
||||
". Should be " + md5ref);
|
||||
|
||||
for (int targetPrecision = 2; targetPrecision <= maxTargetPrecision;
|
||||
targetPrecision++) {
|
||||
tjc.set(TJ.PARAM_PRECISION, targetPrecision);
|
||||
pf = pixelFormat;
|
||||
|
||||
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, false))
|
||||
throw new Exception("Pixel data in " + filename + " is bogus");
|
||||
throw new Exception("Image dimensions of " + filename +
|
||||
" are bogus");
|
||||
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);
|
||||
@@ -1229,8 +1263,11 @@ 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 RGB failed");
|
||||
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);
|
||||
@@ -1243,9 +1280,13 @@ 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, 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;
|
||||
@@ -1259,6 +1300,7 @@ final class TJUnitTest {
|
||||
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) {
|
||||
|
||||
54
src/rdppm.c
54
src/rdppm.c
@@ -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,16 +564,30 @@ 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;
|
||||
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, rescale[gray], rescale[gray], rescale[gray], ptr,
|
||||
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,6 +652,7 @@ 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;
|
||||
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;
|
||||
@@ -648,10 +667,29 @@ get_word_rgb_cmyk_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
203
src/tjunittest.c
203
src/tjunittest.c
@@ -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,44 @@ 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)
|
||||
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) {
|
||||
if ((buf = tj3LoadImage12(handle, filename, &loadWidth, align, &loadHeight,
|
||||
&pf)) == NULL)
|
||||
} 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)
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
if (pf == TJPF_GRAY) {
|
||||
tj3Free(buf); buf = NULL;
|
||||
|
||||
if (pf == TJPF_GRAY) {
|
||||
pf = TJPF_XBGR;
|
||||
if (precision <= 8) {
|
||||
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);
|
||||
@@ -1074,50 +1108,56 @@ static int doBmpTest(const char *ext, int width, int align, int height, int pf,
|
||||
THROW_TJ(handle);
|
||||
}
|
||||
pitch = PAD(width * tjPixelSize[pf], align);
|
||||
if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, 1)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
/* 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) ||
|
||||
@@ -1127,6 +1167,7 @@ static int doBmpTest(const char *ext, int width, int align, int height, int pf,
|
||||
pixFormatStr[pixelFormat]);
|
||||
retval = -1;
|
||||
}
|
||||
}
|
||||
unlink(filename);
|
||||
|
||||
bailout:
|
||||
|
||||
Reference in New Issue
Block a user