1 /* 2 * Copyright (C)2011-2018 D. R. Commander. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * - Redistributions of source code must retain the above copyright notice, 8 * this list of conditions and the following disclaimer. 9 * - Redistributions in binary form must reproduce the above copyright notice, 10 * this list of conditions and the following disclaimer in the documentation 11 * and/or other materials provided with the distribution. 12 * - Neither the name of the libjpeg-turbo Project nor the names of its 13 * contributors may be used to endorse or promote products derived from this 14 * software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * This program tests the various code paths in the TurboJPEG JNI Wrapper 31 */ 32 33 import java.io.*; 34 import java.util.*; 35 import java.awt.image.*; 36 import javax.imageio.*; 37 import java.nio.*; 38 import org.libjpegturbo.turbojpeg.*; 39 40 @SuppressWarnings("checkstyle:JavadocType") 41 final class TJUnitTest { 42 TJUnitTest()43 private TJUnitTest() {} 44 45 static final String CLASS_NAME = 46 new TJUnitTest().getClass().getName(); 47 usage()48 static void usage() { 49 System.out.println("\nUSAGE: java " + CLASS_NAME + " [options]\n"); 50 System.out.println("Options:"); 51 System.out.println("-yuv = test YUV encoding/decoding support"); 52 System.out.println("-noyuvpad = do not pad each line of each Y, U, and V plane to the nearest"); 53 System.out.println(" 4-byte boundary"); 54 System.out.println("-bi = test BufferedImage support\n"); 55 System.exit(1); 56 } 57 58 static final String[] SUBNAME_LONG = { 59 "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1" 60 }; 61 static final String[] SUBNAME = { 62 "444", "422", "420", "GRAY", "440", "411" 63 }; 64 65 static final String[] PIXFORMATSTR = { 66 "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "Grayscale", 67 "RGBA", "BGRA", "ABGR", "ARGB", "CMYK" 68 }; 69 70 static final int[] FORMATS_3BYTE = { 71 TJ.PF_RGB, TJ.PF_BGR 72 }; 73 static final int[] FORMATS_3BYTEBI = { 74 BufferedImage.TYPE_3BYTE_BGR 75 }; 76 static final int[] FORMATS_4BYTE = { 77 TJ.PF_RGBX, TJ.PF_BGRX, TJ.PF_XBGR, TJ.PF_XRGB, TJ.PF_CMYK 78 }; 79 static final int[] FORMATS_4BYTEBI = { 80 BufferedImage.TYPE_INT_BGR, BufferedImage.TYPE_INT_RGB, 81 BufferedImage.TYPE_4BYTE_ABGR, BufferedImage.TYPE_4BYTE_ABGR_PRE, 82 BufferedImage.TYPE_INT_ARGB, BufferedImage.TYPE_INT_ARGB_PRE 83 }; 84 static final int[] FORMATS_GRAY = { 85 TJ.PF_GRAY 86 }; 87 static final int[] FORMATS_GRAYBI = { 88 BufferedImage.TYPE_BYTE_GRAY 89 }; 90 static final int[] FORMATS_RGB = { 91 TJ.PF_RGB 92 }; 93 94 private static boolean doYUV = false; 95 private static int pad = 4; 96 private static boolean bi = false; 97 98 private static int exitStatus = 0; 99 biTypePF(int biType)100 static int biTypePF(int biType) { 101 ByteOrder byteOrder = ByteOrder.nativeOrder(); 102 switch (biType) { 103 case BufferedImage.TYPE_3BYTE_BGR: 104 return TJ.PF_BGR; 105 case BufferedImage.TYPE_4BYTE_ABGR: 106 case BufferedImage.TYPE_4BYTE_ABGR_PRE: 107 return TJ.PF_ABGR; 108 case BufferedImage.TYPE_BYTE_GRAY: 109 return TJ.PF_GRAY; 110 case BufferedImage.TYPE_INT_BGR: 111 return TJ.PF_RGBX; 112 case BufferedImage.TYPE_INT_RGB: 113 return TJ.PF_BGRX; 114 case BufferedImage.TYPE_INT_ARGB: 115 case BufferedImage.TYPE_INT_ARGB_PRE: 116 return TJ.PF_BGRA; 117 default: 118 return 0; 119 } 120 } 121 biTypeStr(int biType)122 static String biTypeStr(int biType) { 123 switch (biType) { 124 case BufferedImage.TYPE_3BYTE_BGR: 125 return "3BYTE_BGR"; 126 case BufferedImage.TYPE_4BYTE_ABGR: 127 return "4BYTE_ABGR"; 128 case BufferedImage.TYPE_4BYTE_ABGR_PRE: 129 return "4BYTE_ABGR_PRE"; 130 case BufferedImage.TYPE_BYTE_GRAY: 131 return "BYTE_GRAY"; 132 case BufferedImage.TYPE_INT_BGR: 133 return "INT_BGR"; 134 case BufferedImage.TYPE_INT_RGB: 135 return "INT_RGB"; 136 case BufferedImage.TYPE_INT_ARGB: 137 return "INT_ARGB"; 138 case BufferedImage.TYPE_INT_ARGB_PRE: 139 return "INT_ARGB_PRE"; 140 default: 141 return "Unknown"; 142 } 143 } 144 initBuf(byte[] buf, int w, int pitch, int h, int pf, int flags)145 static void initBuf(byte[] buf, int w, int pitch, int h, int pf, int flags) 146 throws Exception { 147 int roffset = TJ.getRedOffset(pf); 148 int goffset = TJ.getGreenOffset(pf); 149 int boffset = TJ.getBlueOffset(pf); 150 int aoffset = TJ.getAlphaOffset(pf); 151 int ps = TJ.getPixelSize(pf); 152 int index, row, col, halfway = 16; 153 154 if (pf == TJ.PF_GRAY) { 155 Arrays.fill(buf, (byte)0); 156 for (row = 0; row < h; row++) { 157 for (col = 0; col < w; col++) { 158 if ((flags & TJ.FLAG_BOTTOMUP) != 0) 159 index = pitch * (h - row - 1) + col; 160 else 161 index = pitch * row + col; 162 if (((row / 8) + (col / 8)) % 2 == 0) 163 buf[index] = (row < halfway) ? (byte)255 : 0; 164 else 165 buf[index] = (row < halfway) ? 76 : (byte)226; 166 } 167 } 168 return; 169 } 170 if (pf == TJ.PF_CMYK) { 171 Arrays.fill(buf, (byte)255); 172 for (row = 0; row < h; row++) { 173 for (col = 0; col < w; col++) { 174 if ((flags & TJ.FLAG_BOTTOMUP) != 0) 175 index = (h - row - 1) * w + col; 176 else 177 index = row * w + col; 178 if (((row / 8) + (col / 8)) % 2 == 0) { 179 if (row >= halfway) buf[index * ps + 3] = 0; 180 } else { 181 buf[index * ps + 2] = 0; 182 if (row < halfway) 183 buf[index * ps + 1] = 0; 184 } 185 } 186 } 187 return; 188 } 189 190 Arrays.fill(buf, (byte)0); 191 for (row = 0; row < h; row++) { 192 for (col = 0; col < w; col++) { 193 if ((flags & TJ.FLAG_BOTTOMUP) != 0) 194 index = pitch * (h - row - 1) + col * ps; 195 else 196 index = pitch * row + col * ps; 197 if (((row / 8) + (col / 8)) % 2 == 0) { 198 if (row < halfway) { 199 buf[index + roffset] = (byte)255; 200 buf[index + goffset] = (byte)255; 201 buf[index + boffset] = (byte)255; 202 } 203 } else { 204 buf[index + roffset] = (byte)255; 205 if (row >= halfway) 206 buf[index + goffset] = (byte)255; 207 } 208 if (aoffset >= 0) 209 buf[index + aoffset] = (byte)255; 210 } 211 } 212 } 213 initIntBuf(int[] buf, int w, int pitch, int h, int pf, int flags)214 static void initIntBuf(int[] buf, int w, int pitch, int h, int pf, int flags) 215 throws Exception { 216 int rshift = TJ.getRedOffset(pf) * 8; 217 int gshift = TJ.getGreenOffset(pf) * 8; 218 int bshift = TJ.getBlueOffset(pf) * 8; 219 int ashift = TJ.getAlphaOffset(pf) * 8; 220 int index, row, col, halfway = 16; 221 222 Arrays.fill(buf, 0); 223 for (row = 0; row < h; row++) { 224 for (col = 0; col < w; col++) { 225 if ((flags & TJ.FLAG_BOTTOMUP) != 0) 226 index = pitch * (h - row - 1) + col; 227 else 228 index = pitch * row + col; 229 if (((row / 8) + (col / 8)) % 2 == 0) { 230 if (row < halfway) { 231 buf[index] |= (255 << rshift); 232 buf[index] |= (255 << gshift); 233 buf[index] |= (255 << bshift); 234 } 235 } else { 236 buf[index] |= (255 << rshift); 237 if (row >= halfway) 238 buf[index] |= (255 << gshift); 239 } 240 if (ashift >= 0) 241 buf[index] |= (255 << ashift); 242 } 243 } 244 } 245 initImg(BufferedImage img, int pf, int flags)246 static void initImg(BufferedImage img, int pf, int flags) throws Exception { 247 WritableRaster wr = img.getRaster(); 248 int imgType = img.getType(); 249 250 if (imgType == BufferedImage.TYPE_INT_RGB || 251 imgType == BufferedImage.TYPE_INT_BGR || 252 imgType == BufferedImage.TYPE_INT_ARGB || 253 imgType == BufferedImage.TYPE_INT_ARGB_PRE) { 254 SinglePixelPackedSampleModel sm = 255 (SinglePixelPackedSampleModel)img.getSampleModel(); 256 int pitch = sm.getScanlineStride(); 257 DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); 258 int[] buf = db.getData(); 259 initIntBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, flags); 260 } else { 261 ComponentSampleModel sm = (ComponentSampleModel)img.getSampleModel(); 262 int pitch = sm.getScanlineStride(); 263 DataBufferByte db = (DataBufferByte)wr.getDataBuffer(); 264 byte[] buf = db.getData(); 265 initBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, flags); 266 } 267 } 268 checkVal(int row, int col, int v, String vname, int cv)269 static void checkVal(int row, int col, int v, String vname, int cv) 270 throws Exception { 271 v = (v < 0) ? v + 256 : v; 272 if (v < cv - 1 || v > cv + 1) { 273 throw new Exception("Comp. " + vname + " at " + row + "," + col + 274 " should be " + cv + ", not " + v); 275 } 276 } 277 checkVal0(int row, int col, int v, String vname)278 static void checkVal0(int row, int col, int v, String vname) 279 throws Exception { 280 v = (v < 0) ? v + 256 : v; 281 if (v > 1) { 282 throw new Exception("Comp. " + vname + " at " + row + "," + col + 283 " should be 0, not " + v); 284 } 285 } 286 checkVal255(int row, int col, int v, String vname)287 static void checkVal255(int row, int col, int v, String vname) 288 throws Exception { 289 v = (v < 0) ? v + 256 : v; 290 if (v < 254) { 291 throw new Exception("Comp. " + vname + " at " + row + "," + col + 292 " should be 255, not " + v); 293 } 294 } 295 checkBuf(byte[] buf, int w, int pitch, int h, int pf, int subsamp, TJScalingFactor sf, int flags)296 static int checkBuf(byte[] buf, int w, int pitch, int h, int pf, int subsamp, 297 TJScalingFactor sf, int flags) throws Exception { 298 int roffset = TJ.getRedOffset(pf); 299 int goffset = TJ.getGreenOffset(pf); 300 int boffset = TJ.getBlueOffset(pf); 301 int aoffset = TJ.getAlphaOffset(pf); 302 int ps = TJ.getPixelSize(pf); 303 int index, row, col, retval = 1; 304 int halfway = 16 * sf.getNum() / sf.getDenom(); 305 int blockSize = 8 * sf.getNum() / sf.getDenom(); 306 307 try { 308 309 if (pf == TJ.PF_GRAY) 310 roffset = goffset = boffset = 0; 311 312 if (pf == TJ.PF_CMYK) { 313 for (row = 0; row < h; row++) { 314 for (col = 0; col < w; col++) { 315 if ((flags & TJ.FLAG_BOTTOMUP) != 0) 316 index = (h - row - 1) * w + col; 317 else 318 index = row * w + col; 319 byte c = buf[index * ps]; 320 byte m = buf[index * ps + 1]; 321 byte y = buf[index * ps + 2]; 322 byte k = buf[index * ps + 3]; 323 checkVal255(row, col, c, "C"); 324 if (((row / blockSize) + (col / blockSize)) % 2 == 0) { 325 checkVal255(row, col, m, "M"); 326 checkVal255(row, col, y, "Y"); 327 if (row < halfway) 328 checkVal255(row, col, k, "K"); 329 else 330 checkVal0(row, col, k, "K"); 331 } else { 332 checkVal0(row, col, y, "Y"); 333 checkVal255(row, col, k, "K"); 334 if (row < halfway) 335 checkVal0(row, col, m, "M"); 336 else 337 checkVal255(row, col, m, "M"); 338 } 339 } 340 } 341 return 1; 342 } 343 344 for (row = 0; row < halfway; row++) { 345 for (col = 0; col < w; col++) { 346 if ((flags & TJ.FLAG_BOTTOMUP) != 0) 347 index = pitch * (h - row - 1) + col * ps; 348 else 349 index = pitch * row + col * ps; 350 byte r = buf[index + roffset]; 351 byte g = buf[index + goffset]; 352 byte b = buf[index + boffset]; 353 byte a = aoffset >= 0 ? buf[index + aoffset] : (byte)255; 354 if (((row / blockSize) + (col / blockSize)) % 2 == 0) { 355 if (row < halfway) { 356 checkVal255(row, col, r, "R"); 357 checkVal255(row, col, g, "G"); 358 checkVal255(row, col, b, "B"); 359 } else { 360 checkVal0(row, col, r, "R"); 361 checkVal0(row, col, g, "G"); 362 checkVal0(row, col, b, "B"); 363 } 364 } else { 365 if (subsamp == TJ.SAMP_GRAY) { 366 if (row < halfway) { 367 checkVal(row, col, r, "R", 76); 368 checkVal(row, col, g, "G", 76); 369 checkVal(row, col, b, "B", 76); 370 } else { 371 checkVal(row, col, r, "R", 226); 372 checkVal(row, col, g, "G", 226); 373 checkVal(row, col, b, "B", 226); 374 } 375 } else { 376 checkVal255(row, col, r, "R"); 377 if (row < halfway) { 378 checkVal0(row, col, g, "G"); 379 } else { 380 checkVal255(row, col, g, "G"); 381 } 382 checkVal0(row, col, b, "B"); 383 } 384 } 385 checkVal255(row, col, a, "A"); 386 } 387 } 388 } catch (Exception e) { 389 System.out.println("\n" + e.getMessage()); 390 retval = 0; 391 } 392 393 if (retval == 0) { 394 for (row = 0; row < h; row++) { 395 for (col = 0; col < w; col++) { 396 if (pf == TJ.PF_CMYK) { 397 int c = buf[pitch * row + col * ps]; 398 int m = buf[pitch * row + col * ps + 1]; 399 int y = buf[pitch * row + col * ps + 2]; 400 int k = buf[pitch * row + col * ps + 3]; 401 if (c < 0) c += 256; 402 if (m < 0) m += 256; 403 if (y < 0) y += 256; 404 if (k < 0) k += 256; 405 System.out.format("%3d/%3d/%3d/%3d ", c, m, y, k); 406 } else { 407 int r = buf[pitch * row + col * ps + roffset]; 408 int g = buf[pitch * row + col * ps + goffset]; 409 int b = buf[pitch * row + col * ps + boffset]; 410 if (r < 0) r += 256; 411 if (g < 0) g += 256; 412 if (b < 0) b += 256; 413 System.out.format("%3d/%3d/%3d ", r, g, b); 414 } 415 } 416 System.out.print("\n"); 417 } 418 } 419 return retval; 420 } 421 checkIntBuf(int[] buf, int w, int pitch, int h, int pf, int subsamp, TJScalingFactor sf, int flags)422 static int checkIntBuf(int[] buf, int w, int pitch, int h, int pf, 423 int subsamp, TJScalingFactor sf, int flags) 424 throws Exception { 425 int rshift = TJ.getRedOffset(pf) * 8; 426 int gshift = TJ.getGreenOffset(pf) * 8; 427 int bshift = TJ.getBlueOffset(pf) * 8; 428 int ashift = TJ.getAlphaOffset(pf) * 8; 429 int index, row, col, retval = 1; 430 int halfway = 16 * sf.getNum() / sf.getDenom(); 431 int blockSize = 8 * sf.getNum() / sf.getDenom(); 432 433 try { 434 for (row = 0; row < halfway; row++) { 435 for (col = 0; col < w; col++) { 436 if ((flags & TJ.FLAG_BOTTOMUP) != 0) 437 index = pitch * (h - row - 1) + col; 438 else 439 index = pitch * row + col; 440 int r = (buf[index] >> rshift) & 0xFF; 441 int g = (buf[index] >> gshift) & 0xFF; 442 int b = (buf[index] >> bshift) & 0xFF; 443 int a = ashift >= 0 ? (buf[index] >> ashift) & 0xFF : 255; 444 if (((row / blockSize) + (col / blockSize)) % 2 == 0) { 445 if (row < halfway) { 446 checkVal255(row, col, r, "R"); 447 checkVal255(row, col, g, "G"); 448 checkVal255(row, col, b, "B"); 449 } else { 450 checkVal0(row, col, r, "R"); 451 checkVal0(row, col, g, "G"); 452 checkVal0(row, col, b, "B"); 453 } 454 } else { 455 if (subsamp == TJ.SAMP_GRAY) { 456 if (row < halfway) { 457 checkVal(row, col, r, "R", 76); 458 checkVal(row, col, g, "G", 76); 459 checkVal(row, col, b, "B", 76); 460 } else { 461 checkVal(row, col, r, "R", 226); 462 checkVal(row, col, g, "G", 226); 463 checkVal(row, col, b, "B", 226); 464 } 465 } else { 466 checkVal255(row, col, r, "R"); 467 if (row < halfway) { 468 checkVal0(row, col, g, "G"); 469 } else { 470 checkVal255(row, col, g, "G"); 471 } 472 checkVal0(row, col, b, "B"); 473 } 474 } 475 checkVal255(row, col, a, "A"); 476 } 477 } 478 } catch (Exception e) { 479 System.out.println("\n" + e.getMessage()); 480 retval = 0; 481 } 482 483 if (retval == 0) { 484 for (row = 0; row < h; row++) { 485 for (col = 0; col < w; col++) { 486 int r = (buf[pitch * row + col] >> rshift) & 0xFF; 487 int g = (buf[pitch * row + col] >> gshift) & 0xFF; 488 int b = (buf[pitch * row + col] >> bshift) & 0xFF; 489 if (r < 0) r += 256; 490 if (g < 0) g += 256; 491 if (b < 0) b += 256; 492 System.out.format("%3d/%3d/%3d ", r, g, b); 493 } 494 System.out.print("\n"); 495 } 496 } 497 return retval; 498 } 499 checkImg(BufferedImage img, int pf, int subsamp, TJScalingFactor sf, int flags)500 static int checkImg(BufferedImage img, int pf, int subsamp, 501 TJScalingFactor sf, int flags) throws Exception { 502 WritableRaster wr = img.getRaster(); 503 int imgType = img.getType(); 504 if (imgType == BufferedImage.TYPE_INT_RGB || 505 imgType == BufferedImage.TYPE_INT_BGR || 506 imgType == BufferedImage.TYPE_INT_ARGB || 507 imgType == BufferedImage.TYPE_INT_ARGB_PRE) { 508 SinglePixelPackedSampleModel sm = 509 (SinglePixelPackedSampleModel)img.getSampleModel(); 510 int pitch = sm.getScanlineStride(); 511 DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); 512 int[] buf = db.getData(); 513 return checkIntBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, 514 subsamp, sf, flags); 515 } else { 516 ComponentSampleModel sm = (ComponentSampleModel)img.getSampleModel(); 517 int pitch = sm.getScanlineStride(); 518 DataBufferByte db = (DataBufferByte)wr.getDataBuffer(); 519 byte[] buf = db.getData(); 520 return checkBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, subsamp, 521 sf, flags); 522 } 523 } 524 pad(int v, int p)525 static int pad(int v, int p) { 526 return ((v + (p) - 1) & (~((p) - 1))); 527 } 528 checkBufYUV(byte[] buf, int size, int w, int h, int subsamp, TJScalingFactor sf)529 static int checkBufYUV(byte[] buf, int size, int w, int h, int subsamp, 530 TJScalingFactor sf) throws Exception { 531 int row, col; 532 int hsf = TJ.getMCUWidth(subsamp) / 8, vsf = TJ.getMCUHeight(subsamp) / 8; 533 int pw = pad(w, hsf), ph = pad(h, vsf); 534 int cw = pw / hsf, ch = ph / vsf; 535 int ypitch = pad(pw, pad), uvpitch = pad(cw, pad); 536 int retval = 1; 537 int correctsize = ypitch * ph + 538 (subsamp == TJ.SAMP_GRAY ? 0 : uvpitch * ch * 2); 539 int halfway = 16 * sf.getNum() / sf.getDenom(); 540 int blockSize = 8 * sf.getNum() / sf.getDenom(); 541 542 try { 543 if (size != correctsize) 544 throw new Exception("Incorrect size " + size + ". Should be " + 545 correctsize); 546 547 for (row = 0; row < ph; row++) { 548 for (col = 0; col < pw; col++) { 549 byte y = buf[ypitch * row + col]; 550 if (((row / blockSize) + (col / blockSize)) % 2 == 0) { 551 if (row < halfway) 552 checkVal255(row, col, y, "Y"); 553 else 554 checkVal0(row, col, y, "Y"); 555 } else { 556 if (row < halfway) 557 checkVal(row, col, y, "Y", 76); 558 else 559 checkVal(row, col, y, "Y", 226); 560 } 561 } 562 } 563 if (subsamp != TJ.SAMP_GRAY) { 564 halfway = 16 / vsf * sf.getNum() / sf.getDenom(); 565 for (row = 0; row < ch; row++) { 566 for (col = 0; col < cw; col++) { 567 byte u = buf[ypitch * ph + (uvpitch * row + col)], 568 v = buf[ypitch * ph + uvpitch * ch + (uvpitch * row + col)]; 569 if (((row * vsf / blockSize) + (col * hsf / blockSize)) % 2 == 0) { 570 checkVal(row, col, u, "U", 128); 571 checkVal(row, col, v, "V", 128); 572 } else { 573 if (row < halfway) { 574 checkVal(row, col, u, "U", 85); 575 checkVal255(row, col, v, "V"); 576 } else { 577 checkVal0(row, col, u, "U"); 578 checkVal(row, col, v, "V", 149); 579 } 580 } 581 } 582 } 583 } 584 } catch (Exception e) { 585 System.out.println("\n" + e.getMessage()); 586 retval = 0; 587 } 588 589 if (retval == 0) { 590 for (row = 0; row < ph; row++) { 591 for (col = 0; col < pw; col++) { 592 int y = buf[ypitch * row + col]; 593 if (y < 0) y += 256; 594 System.out.format("%3d ", y); 595 } 596 System.out.print("\n"); 597 } 598 System.out.print("\n"); 599 for (row = 0; row < ch; row++) { 600 for (col = 0; col < cw; col++) { 601 int u = buf[ypitch * ph + (uvpitch * row + col)]; 602 if (u < 0) u += 256; 603 System.out.format("%3d ", u); 604 } 605 System.out.print("\n"); 606 } 607 System.out.print("\n"); 608 for (row = 0; row < ch; row++) { 609 for (col = 0; col < cw; col++) { 610 int v = buf[ypitch * ph + uvpitch * ch + (uvpitch * row + col)]; 611 if (v < 0) v += 256; 612 System.out.format("%3d ", v); 613 } 614 System.out.print("\n"); 615 } 616 } 617 618 return retval; 619 } 620 writeJPEG(byte[] jpegBuf, int jpegBufSize, String filename)621 static void writeJPEG(byte[] jpegBuf, int jpegBufSize, String filename) 622 throws Exception { 623 File file = new File(filename); 624 FileOutputStream fos = new FileOutputStream(file); 625 fos.write(jpegBuf, 0, jpegBufSize); 626 fos.close(); 627 } 628 compTest(TJCompressor tjc, byte[] dstBuf, int w, int h, int pf, String baseName, int subsamp, int jpegQual, int flags)629 static int compTest(TJCompressor tjc, byte[] dstBuf, int w, int h, int pf, 630 String baseName, int subsamp, int jpegQual, int flags) 631 throws Exception { 632 String tempStr; 633 byte[] srcBuf = null; 634 BufferedImage img = null; 635 String pfStr, pfStrLong; 636 String buStr = (flags & TJ.FLAG_BOTTOMUP) != 0 ? "BU" : "TD"; 637 String buStrLong = (flags & TJ.FLAG_BOTTOMUP) != 0 ? 638 "Bottom-Up" : "Top-Down "; 639 int size = 0, ps, imgType = pf; 640 641 if (bi) { 642 pf = biTypePF(imgType); 643 pfStr = biTypeStr(imgType); 644 pfStrLong = pfStr + " (" + PIXFORMATSTR[pf] + ")"; 645 } else { 646 pfStr = PIXFORMATSTR[pf]; 647 pfStrLong = pfStr; 648 } 649 ps = TJ.getPixelSize(pf); 650 651 if (bi) { 652 img = new BufferedImage(w, h, imgType); 653 initImg(img, pf, flags); 654 tempStr = baseName + "_enc_" + pfStr + "_" + buStr + "_" + 655 SUBNAME[subsamp] + "_Q" + jpegQual + ".png"; 656 File file = new File(tempStr); 657 ImageIO.write(img, "png", file); 658 tjc.setSourceImage(img, 0, 0, 0, 0); 659 } else { 660 srcBuf = new byte[w * h * ps + 1]; 661 initBuf(srcBuf, w, w * ps, h, pf, flags); 662 tjc.setSourceImage(srcBuf, 0, 0, w, 0, h, pf); 663 } 664 Arrays.fill(dstBuf, (byte)0); 665 666 tjc.setSubsamp(subsamp); 667 tjc.setJPEGQuality(jpegQual); 668 if (doYUV) { 669 System.out.format("%s %s -> YUV %s ... ", pfStrLong, buStrLong, 670 SUBNAME_LONG[subsamp]); 671 YUVImage yuvImage = tjc.encodeYUV(pad, flags); 672 if (checkBufYUV(yuvImage.getBuf(), yuvImage.getSize(), w, h, subsamp, 673 new TJScalingFactor(1, 1)) == 1) 674 System.out.print("Passed.\n"); 675 else { 676 System.out.print("FAILED!\n"); 677 exitStatus = -1; 678 } 679 680 System.out.format("YUV %s %s -> JPEG Q%d ... ", SUBNAME_LONG[subsamp], 681 buStrLong, jpegQual); 682 tjc.setSourceImage(yuvImage); 683 } else { 684 System.out.format("%s %s -> %s Q%d ... ", pfStrLong, buStrLong, 685 SUBNAME_LONG[subsamp], jpegQual); 686 } 687 tjc.compress(dstBuf, flags); 688 size = tjc.getCompressedSize(); 689 690 tempStr = baseName + "_enc_" + pfStr + "_" + buStr + "_" + 691 SUBNAME[subsamp] + "_Q" + jpegQual + ".jpg"; 692 writeJPEG(dstBuf, size, tempStr); 693 System.out.println("Done.\n Result in " + tempStr); 694 695 return size; 696 } 697 decompTest(TJDecompressor tjd, byte[] jpegBuf, int jpegSize, int w, int h, int pf, String baseName, int subsamp, int flags, TJScalingFactor sf)698 static void decompTest(TJDecompressor tjd, byte[] jpegBuf, int jpegSize, 699 int w, int h, int pf, String baseName, int subsamp, 700 int flags, TJScalingFactor sf) throws Exception { 701 String pfStr, pfStrLong, tempStr; 702 String buStrLong = (flags & TJ.FLAG_BOTTOMUP) != 0 ? 703 "Bottom-Up" : "Top-Down "; 704 int scaledWidth = sf.getScaled(w); 705 int scaledHeight = sf.getScaled(h); 706 int temp1, temp2, imgType = pf; 707 BufferedImage img = null; 708 byte[] dstBuf = null; 709 710 if (bi) { 711 pf = biTypePF(imgType); 712 pfStr = biTypeStr(imgType); 713 pfStrLong = pfStr + " (" + PIXFORMATSTR[pf] + ")"; 714 } else { 715 pfStr = PIXFORMATSTR[pf]; 716 pfStrLong = pfStr; 717 } 718 719 tjd.setSourceImage(jpegBuf, jpegSize); 720 if (tjd.getWidth() != w || tjd.getHeight() != h || 721 tjd.getSubsamp() != subsamp) 722 throw new Exception("Incorrect JPEG header"); 723 724 temp1 = scaledWidth; 725 temp2 = scaledHeight; 726 temp1 = tjd.getScaledWidth(temp1, temp2); 727 temp2 = tjd.getScaledHeight(temp1, temp2); 728 if (temp1 != scaledWidth || temp2 != scaledHeight) 729 throw new Exception("Scaled size mismatch"); 730 731 if (doYUV) { 732 System.out.format("JPEG -> YUV %s ", SUBNAME_LONG[subsamp]); 733 if (!sf.isOne()) 734 System.out.format("%d/%d ... ", sf.getNum(), sf.getDenom()); 735 else System.out.print("... "); 736 YUVImage yuvImage = tjd.decompressToYUV(scaledWidth, pad, scaledHeight, 737 flags); 738 if (checkBufYUV(yuvImage.getBuf(), yuvImage.getSize(), scaledWidth, 739 scaledHeight, subsamp, sf) == 1) 740 System.out.print("Passed.\n"); 741 else { 742 System.out.print("FAILED!\n"); exitStatus = -1; 743 } 744 745 System.out.format("YUV %s -> %s %s ... ", SUBNAME_LONG[subsamp], 746 pfStrLong, buStrLong); 747 tjd.setSourceImage(yuvImage); 748 } else { 749 System.out.format("JPEG -> %s %s ", pfStrLong, buStrLong); 750 if (!sf.isOne()) 751 System.out.format("%d/%d ... ", sf.getNum(), sf.getDenom()); 752 else System.out.print("... "); 753 } 754 if (bi) 755 img = tjd.decompress(scaledWidth, scaledHeight, imgType, flags); 756 else 757 dstBuf = tjd.decompress(scaledWidth, 0, scaledHeight, pf, flags); 758 759 if (bi) { 760 tempStr = baseName + "_dec_" + pfStr + "_" + 761 (((flags & TJ.FLAG_BOTTOMUP) != 0) ? "BU" : "TD") + "_" + 762 SUBNAME[subsamp] + "_" + 763 (double)sf.getNum() / (double)sf.getDenom() + "x" + ".png"; 764 File file = new File(tempStr); 765 ImageIO.write(img, "png", file); 766 } 767 768 if ((bi && checkImg(img, pf, subsamp, sf, flags) == 1) || 769 (!bi && checkBuf(dstBuf, scaledWidth, 770 scaledWidth * TJ.getPixelSize(pf), scaledHeight, pf, 771 subsamp, sf, flags) == 1)) 772 System.out.print("Passed.\n"); 773 else { 774 System.out.print("FAILED!\n"); 775 exitStatus = -1; 776 } 777 } 778 decompTest(TJDecompressor tjd, byte[] jpegBuf, int jpegSize, int w, int h, int pf, String baseName, int subsamp, int flags)779 static void decompTest(TJDecompressor tjd, byte[] jpegBuf, int jpegSize, 780 int w, int h, int pf, String baseName, int subsamp, 781 int flags) throws Exception { 782 int i; 783 TJScalingFactor[] sf = TJ.getScalingFactors(); 784 for (i = 0; i < sf.length; i++) { 785 int num = sf[i].getNum(); 786 int denom = sf[i].getDenom(); 787 if (subsamp == TJ.SAMP_444 || subsamp == TJ.SAMP_GRAY || 788 (subsamp == TJ.SAMP_411 && num == 1 && 789 (denom == 2 || denom == 1)) || 790 (subsamp != TJ.SAMP_411 && num == 1 && 791 (denom == 4 || denom == 2 || denom == 1))) 792 decompTest(tjd, jpegBuf, jpegSize, w, h, pf, baseName, subsamp, 793 flags, sf[i]); 794 } 795 } 796 doTest(int w, int h, int[] formats, int subsamp, String baseName)797 static void doTest(int w, int h, int[] formats, int subsamp, String baseName) 798 throws Exception { 799 TJCompressor tjc = null; 800 TJDecompressor tjd = null; 801 int size; 802 byte[] dstBuf; 803 804 dstBuf = new byte[TJ.bufSize(w, h, subsamp)]; 805 806 try { 807 tjc = new TJCompressor(); 808 tjd = new TJDecompressor(); 809 810 for (int pf : formats) { 811 if (pf < 0) continue; 812 for (int i = 0; i < 2; i++) { 813 int flags = 0; 814 if (subsamp == TJ.SAMP_422 || subsamp == TJ.SAMP_420 || 815 subsamp == TJ.SAMP_440 || subsamp == TJ.SAMP_411) 816 flags |= TJ.FLAG_FASTUPSAMPLE; 817 if (i == 1) 818 flags |= TJ.FLAG_BOTTOMUP; 819 size = compTest(tjc, dstBuf, w, h, pf, baseName, subsamp, 100, 820 flags); 821 decompTest(tjd, dstBuf, size, w, h, pf, baseName, subsamp, flags); 822 if (pf >= TJ.PF_RGBX && pf <= TJ.PF_XRGB && !bi) { 823 System.out.print("\n"); 824 decompTest(tjd, dstBuf, size, w, h, pf + (TJ.PF_RGBA - TJ.PF_RGBX), 825 baseName, subsamp, flags); 826 } 827 System.out.print("\n"); 828 } 829 } 830 System.out.print("--------------------\n\n"); 831 } catch (Exception e) { 832 if (tjc != null) tjc.close(); 833 if (tjd != null) tjd.close(); 834 throw e; 835 } 836 if (tjc != null) tjc.close(); 837 if (tjd != null) tjd.close(); 838 } 839 bufSizeTest()840 static void bufSizeTest() throws Exception { 841 int w, h, i, subsamp; 842 byte[] srcBuf, dstBuf = null; 843 YUVImage dstImage = null; 844 TJCompressor tjc = null; 845 Random r = new Random(); 846 847 try { 848 tjc = new TJCompressor(); 849 System.out.println("Buffer size regression test"); 850 for (subsamp = 0; subsamp < TJ.NUMSAMP; subsamp++) { 851 for (w = 1; w < 48; w++) { 852 int maxh = (w == 1) ? 2048 : 48; 853 for (h = 1; h < maxh; h++) { 854 if (h % 100 == 0) 855 System.out.format("%04d x %04d\b\b\b\b\b\b\b\b\b\b\b", w, h); 856 srcBuf = new byte[w * h * 4]; 857 if (doYUV) 858 dstImage = new YUVImage(w, pad, h, subsamp); 859 else 860 dstBuf = new byte[TJ.bufSize(w, h, subsamp)]; 861 for (i = 0; i < w * h * 4; i++) { 862 srcBuf[i] = (byte)(r.nextInt(2) * 255); 863 } 864 tjc.setSourceImage(srcBuf, 0, 0, w, 0, h, TJ.PF_BGRX); 865 tjc.setSubsamp(subsamp); 866 tjc.setJPEGQuality(100); 867 if (doYUV) 868 tjc.encodeYUV(dstImage, 0); 869 else 870 tjc.compress(dstBuf, 0); 871 872 srcBuf = new byte[h * w * 4]; 873 if (doYUV) 874 dstImage = new YUVImage(h, pad, w, subsamp); 875 else 876 dstBuf = new byte[TJ.bufSize(h, w, subsamp)]; 877 for (i = 0; i < h * w * 4; i++) { 878 srcBuf[i] = (byte)(r.nextInt(2) * 255); 879 } 880 tjc.setSourceImage(srcBuf, 0, 0, h, 0, w, TJ.PF_BGRX); 881 if (doYUV) 882 tjc.encodeYUV(dstImage, 0); 883 else 884 tjc.compress(dstBuf, 0); 885 } 886 dstImage = null; 887 dstBuf = null; 888 System.gc(); 889 } 890 } 891 System.out.println("Done. "); 892 } catch (Exception e) { 893 if (tjc != null) tjc.close(); 894 throw e; 895 } 896 if (tjc != null) tjc.close(); 897 } 898 main(String[] argv)899 public static void main(String[] argv) { 900 try { 901 String testName = "javatest"; 902 for (int i = 0; i < argv.length; i++) { 903 if (argv[i].equalsIgnoreCase("-yuv")) 904 doYUV = true; 905 else if (argv[i].equalsIgnoreCase("-noyuvpad")) 906 pad = 1; 907 else if (argv[i].equalsIgnoreCase("-bi")) { 908 bi = true; 909 testName = "javabitest"; 910 } else 911 usage(); 912 } 913 if (doYUV) 914 FORMATS_4BYTE[4] = -1; 915 doTest(35, 39, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_444, 916 testName); 917 doTest(39, 41, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_444, 918 testName); 919 doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_422, 920 testName); 921 doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_422, 922 testName); 923 doTest(39, 41, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_420, 924 testName); 925 doTest(41, 35, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_420, 926 testName); 927 doTest(35, 39, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_440, 928 testName); 929 doTest(39, 41, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_440, 930 testName); 931 doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_411, 932 testName); 933 doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_411, 934 testName); 935 doTest(39, 41, bi ? FORMATS_GRAYBI : FORMATS_GRAY, TJ.SAMP_GRAY, 936 testName); 937 doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_GRAY, 938 testName); 939 FORMATS_4BYTE[4] = -1; 940 doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_GRAY, 941 testName); 942 if (!bi) 943 bufSizeTest(); 944 if (doYUV && !bi) { 945 System.out.print("\n--------------------\n\n"); 946 doTest(48, 48, FORMATS_RGB, TJ.SAMP_444, "javatest_yuv0"); 947 doTest(48, 48, FORMATS_RGB, TJ.SAMP_422, "javatest_yuv0"); 948 doTest(48, 48, FORMATS_RGB, TJ.SAMP_420, "javatest_yuv0"); 949 doTest(48, 48, FORMATS_RGB, TJ.SAMP_440, "javatest_yuv0"); 950 doTest(48, 48, FORMATS_RGB, TJ.SAMP_411, "javatest_yuv0"); 951 doTest(48, 48, FORMATS_RGB, TJ.SAMP_GRAY, "javatest_yuv0"); 952 doTest(48, 48, FORMATS_GRAY, TJ.SAMP_GRAY, "javatest_yuv0"); 953 } 954 } catch (Exception e) { 955 e.printStackTrace(); 956 exitStatus = -1; 957 } 958 System.exit(exitStatus); 959 } 960 } 961