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