1 /* 2 * Copyright (C)2011-2017 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 public 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:"); 48 System.out.println("-yuv = test YUV encoding/decoding support"); 49 System.out.println("-noyuvpad = do not pad each line of each Y, U, and V plane to the nearest"); 50 System.out.println(" 4-byte boundary"); 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", "4:1:1" 57 }; 58 private static final String[] subName = { 59 "444", "422", "420", "GRAY", "440", "411" 60 }; 61 62 private static final String[] pixFormatStr = { 63 "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "Grayscale", 64 "RGBA", "BGRA", "ABGR", "ARGB", "CMYK" 65 }; 66 67 private static final int[] alphaOffset = { 68 -1, -1, -1, -1, -1, -1, -1, 3, 3, 0, 0, -1 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, TJ.PF_CMYK 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 boolean doYUV = false; 96 private static int pad = 4; 97 private static boolean bi = false; 98 99 private static int exitStatus = 0; 100 101 private static int biTypePF(int biType) { 102 ByteOrder byteOrder = ByteOrder.nativeOrder(); 103 switch(biType) { 104 case BufferedImage.TYPE_3BYTE_BGR: 105 return TJ.PF_BGR; 106 case BufferedImage.TYPE_4BYTE_ABGR: 107 case BufferedImage.TYPE_4BYTE_ABGR_PRE: 108 return TJ.PF_ABGR; 109 case BufferedImage.TYPE_BYTE_GRAY: 110 return TJ.PF_GRAY; 111 case BufferedImage.TYPE_INT_BGR: 112 return TJ.PF_RGBX; 113 case BufferedImage.TYPE_INT_RGB: 114 return TJ.PF_BGRX; 115 case BufferedImage.TYPE_INT_ARGB: 116 case BufferedImage.TYPE_INT_ARGB_PRE: 117 return TJ.PF_BGRA; 118 } 119 return 0; 120 } 121 122 private 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 } 141 return "Unknown"; 142 } 143 144 private static void initBuf(byte[] buf, int w, int pitch, int h, int pf, 145 int flags) throws Exception { 146 int roffset = TJ.getRedOffset(pf); 147 int goffset = TJ.getGreenOffset(pf); 148 int boffset = TJ.getBlueOffset(pf); 149 int aoffset = alphaOffset[pf]; 150 int ps = TJ.getPixelSize(pf); 151 int index, row, col, halfway = 16; 152 153 if (pf == TJ.PF_GRAY) { 154 Arrays.fill(buf, (byte)0); 155 for (row = 0; row < h; row++) { 156 for (col = 0; col < w; col++) { 157 if ((flags & TJ.FLAG_BOTTOMUP) != 0) 158 index = pitch * (h - row - 1) + col; 159 else 160 index = pitch * row + col; 161 if (((row / 8) + (col / 8)) % 2 == 0) 162 buf[index] = (row < halfway) ? (byte)255 : 0; 163 else 164 buf[index] = (row < halfway) ? 76 : (byte)226; 165 } 166 } 167 return; 168 } 169 if (pf == TJ.PF_CMYK) { 170 Arrays.fill(buf, (byte)255); 171 for (row = 0; row < h; row++) { 172 for (col = 0; col < w; col++) { 173 if ((flags & TJ.FLAG_BOTTOMUP) != 0) 174 index = (h - row - 1) * w + col; 175 else 176 index = row * w + col; 177 if (((row / 8) + (col / 8)) % 2 == 0) { 178 if (row >= halfway) buf[index * ps + 3] = 0; 179 } else { 180 buf[index * ps + 2] = 0; 181 if (row < halfway) 182 buf[index * ps + 1] = 0; 183 } 184 } 185 } 186 return; 187 } 188 189 Arrays.fill(buf, (byte)0); 190 for (row = 0; row < h; row++) { 191 for (col = 0; col < w; col++) { 192 if ((flags & TJ.FLAG_BOTTOMUP) != 0) 193 index = pitch * (h - row - 1) + col * ps; 194 else 195 index = pitch * row + col * ps; 196 if (((row / 8) + (col / 8)) % 2 == 0) { 197 if (row < halfway) { 198 buf[index + roffset] = (byte)255; 199 buf[index + goffset] = (byte)255; 200 buf[index + boffset] = (byte)255; 201 } 202 } else { 203 buf[index + roffset] = (byte)255; 204 if (row >= halfway) 205 buf[index + goffset] = (byte)255; 206 } 207 if (aoffset >= 0) 208 buf[index + aoffset] = (byte)255; 209 } 210 } 211 } 212 213 private static void initIntBuf(int[] buf, int w, int pitch, int h, int pf, 214 int flags) throws Exception { 215 int rshift = TJ.getRedOffset(pf) * 8; 216 int gshift = TJ.getGreenOffset(pf) * 8; 217 int bshift = TJ.getBlueOffset(pf) * 8; 218 int ashift = alphaOffset[pf] * 8; 219 int index, row, col, halfway = 16; 220 221 Arrays.fill(buf, 0); 222 for (row = 0; row < h; row++) { 223 for (col = 0; col < w; col++) { 224 if ((flags & TJ.FLAG_BOTTOMUP) != 0) 225 index = pitch * (h - row - 1) + col; 226 else 227 index = pitch * row + col; 228 if (((row / 8) + (col / 8)) % 2 == 0) { 229 if (row < halfway) { 230 buf[index] |= (255 << rshift); 231 buf[index] |= (255 << gshift); 232 buf[index] |= (255 << bshift); 233 } 234 } else { 235 buf[index] |= (255 << rshift); 236 if (row >= halfway) 237 buf[index] |= (255 << gshift); 238 } 239 if (ashift >= 0) 240 buf[index] |= (255 << ashift); 241 } 242 } 243 } 244 245 private static void initImg(BufferedImage img, int pf, int flags) 246 throws Exception { 247 WritableRaster wr = img.getRaster(); 248 int imgType = img.getType(); 249 if (imgType == BufferedImage.TYPE_INT_RGB || 250 imgType == BufferedImage.TYPE_INT_BGR || 251 imgType == BufferedImage.TYPE_INT_ARGB || 252 imgType == BufferedImage.TYPE_INT_ARGB_PRE) { 253 SinglePixelPackedSampleModel sm = 254 (SinglePixelPackedSampleModel)img.getSampleModel(); 255 int pitch = sm.getScanlineStride(); 256 DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); 257 int[] buf = db.getData(); 258 initIntBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, flags); 259 } else { 260 ComponentSampleModel sm = (ComponentSampleModel)img.getSampleModel(); 261 int pitch = sm.getScanlineStride(); 262 DataBufferByte db = (DataBufferByte)wr.getDataBuffer(); 263 byte[] buf = db.getData(); 264 initBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, flags); 265 } 266 } 267 268 private static void checkVal(int row, int col, int v, String vname, int cv) 269 throws Exception { 270 v = (v < 0) ? v + 256 : v; 271 if (v < cv - 1 || v > cv + 1) { 272 throw new Exception("Comp. " + vname + " at " + row + "," + col + 273 " should be " + cv + ", not " + v); 274 } 275 } 276 277 private static void checkVal0(int row, int col, int v, String vname) 278 throws Exception { 279 v = (v < 0) ? v + 256 : v; 280 if (v > 1) { 281 throw new Exception("Comp. " + vname + " at " + row + "," + col + 282 " should be 0, not " + v); 283 } 284 } 285 286 private static void checkVal255(int row, int col, int v, String vname) 287 throws Exception { 288 v = (v < 0) ? v + 256 : v; 289 if (v < 254) { 290 throw new Exception("Comp. " + vname + " at " + row + "," + col + 291 " should be 255, not " + v); 292 } 293 } 294 295 private static int checkBuf(byte[] buf, int w, int pitch, int h, int pf, 296 int subsamp, TJScalingFactor sf, int flags) 297 throws Exception { 298 int roffset = TJ.getRedOffset(pf); 299 int goffset = TJ.getGreenOffset(pf); 300 int boffset = TJ.getBlueOffset(pf); 301 int aoffset = alphaOffset[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_CMYK) { 310 for (row = 0; row < h; row++) { 311 for (col = 0; col < w; col++) { 312 if ((flags & TJ.FLAG_BOTTOMUP) != 0) 313 index = (h - row - 1) * w + col; 314 else 315 index = row * w + col; 316 byte c = buf[index * ps]; 317 byte m = buf[index * ps + 1]; 318 byte y = buf[index * ps + 2]; 319 byte k = buf[index * ps + 3]; 320 checkVal255(row, col, c, "C"); 321 if (((row / blockSize) + (col / blockSize)) % 2 == 0) { 322 checkVal255(row, col, m, "M"); 323 checkVal255(row, col, y, "Y"); 324 if (row < halfway) 325 checkVal255(row, col, k, "K"); 326 else 327 checkVal0(row, col, k, "K"); 328 } else { 329 checkVal0(row, col, y, "Y"); 330 checkVal255(row, col, k, "K"); 331 if (row < halfway) 332 checkVal0(row, col, m, "M"); 333 else 334 checkVal255(row, col, m, "M"); 335 } 336 } 337 } 338 return 1; 339 } 340 341 for (row = 0; row < halfway; row++) { 342 for (col = 0; col < w; col++) { 343 if ((flags & TJ.FLAG_BOTTOMUP) != 0) 344 index = pitch * (h - row - 1) + col * ps; 345 else 346 index = pitch * row + col * ps; 347 byte r = buf[index + roffset]; 348 byte g = buf[index + goffset]; 349 byte b = buf[index + boffset]; 350 byte a = aoffset >= 0 ? buf[index + aoffset] : (byte)255; 351 if (((row / blockSize) + (col / blockSize)) % 2 == 0) { 352 if (row < halfway) { 353 checkVal255(row, col, r, "R"); 354 checkVal255(row, col, g, "G"); 355 checkVal255(row, col, b, "B"); 356 } else { 357 checkVal0(row, col, r, "R"); 358 checkVal0(row, col, g, "G"); 359 checkVal0(row, col, b, "B"); 360 } 361 } else { 362 if (subsamp == TJ.SAMP_GRAY) { 363 if (row < halfway) { 364 checkVal(row, col, r, "R", 76); 365 checkVal(row, col, g, "G", 76); 366 checkVal(row, col, b, "B", 76); 367 } else { 368 checkVal(row, col, r, "R", 226); 369 checkVal(row, col, g, "G", 226); 370 checkVal(row, col, b, "B", 226); 371 } 372 } else { 373 checkVal255(row, col, r, "R"); 374 if (row < halfway) { 375 checkVal0(row, col, g, "G"); 376 } else { 377 checkVal255(row, col, g, "G"); 378 } 379 checkVal0(row, col, b, "B"); 380 } 381 } 382 checkVal255(row, col, a, "A"); 383 } 384 } 385 } catch(Exception e) { 386 System.out.println("\n" + e.getMessage()); 387 retval = 0; 388 } 389 390 if (retval == 0) { 391 for (row = 0; row < h; row++) { 392 for (col = 0; col < w; col++) { 393 if (pf == TJ.PF_CMYK) { 394 int c = buf[pitch * row + col * ps]; 395 int m = buf[pitch * row + col * ps + 1]; 396 int y = buf[pitch * row + col * ps + 2]; 397 int k = buf[pitch * row + col * ps + 3]; 398 if (c < 0) c += 256; 399 if (m < 0) m += 256; 400 if (y < 0) y += 256; 401 if (k < 0) k += 256; 402 System.out.format("%3d/%3d/%3d/%3d ", c, m, y, k); 403 } else { 404 int r = buf[pitch * row + col * ps + roffset]; 405 int g = buf[pitch * row + col * ps + goffset]; 406 int b = buf[pitch * row + col * ps + boffset]; 407 if (r < 0) r += 256; 408 if (g < 0) g += 256; 409 if (b < 0) b += 256; 410 System.out.format("%3d/%3d/%3d ", r, g, b); 411 } 412 } 413 System.out.print("\n"); 414 } 415 } 416 return retval; 417 } 418 419 private static int checkIntBuf(int[] buf, int w, int pitch, int h, int pf, 420 int subsamp, TJScalingFactor sf, int flags) 421 throws Exception { 422 int rshift = TJ.getRedOffset(pf) * 8; 423 int gshift = TJ.getGreenOffset(pf) * 8; 424 int bshift = TJ.getBlueOffset(pf) * 8; 425 int ashift = alphaOffset[pf] * 8; 426 int index, row, col, retval = 1; 427 int halfway = 16 * sf.getNum() / sf.getDenom(); 428 int blockSize = 8 * sf.getNum() / sf.getDenom(); 429 430 try { 431 for (row = 0; row < halfway; row++) { 432 for (col = 0; col < w; col++) { 433 if ((flags & TJ.FLAG_BOTTOMUP) != 0) 434 index = pitch * (h - row - 1) + col; 435 else 436 index = pitch * row + col; 437 int r = (buf[index] >> rshift) & 0xFF; 438 int g = (buf[index] >> gshift) & 0xFF; 439 int b = (buf[index] >> bshift) & 0xFF; 440 int a = ashift >= 0 ? (buf[index] >> ashift) & 0xFF : 255; 441 if (((row / blockSize) + (col / blockSize)) % 2 == 0) { 442 if (row < halfway) { 443 checkVal255(row, col, r, "R"); 444 checkVal255(row, col, g, "G"); 445 checkVal255(row, col, b, "B"); 446 } else { 447 checkVal0(row, col, r, "R"); 448 checkVal0(row, col, g, "G"); 449 checkVal0(row, col, b, "B"); 450 } 451 } else { 452 if (subsamp == TJ.SAMP_GRAY) { 453 if (row < halfway) { 454 checkVal(row, col, r, "R", 76); 455 checkVal(row, col, g, "G", 76); 456 checkVal(row, col, b, "B", 76); 457 } else { 458 checkVal(row, col, r, "R", 226); 459 checkVal(row, col, g, "G", 226); 460 checkVal(row, col, b, "B", 226); 461 } 462 } else { 463 checkVal255(row, col, r, "R"); 464 if (row < halfway) { 465 checkVal0(row, col, g, "G"); 466 } else { 467 checkVal255(row, col, g, "G"); 468 } 469 checkVal0(row, col, b, "B"); 470 } 471 } 472 checkVal255(row, col, a, "A"); 473 } 474 } 475 } catch(Exception e) { 476 System.out.println("\n" + e.getMessage()); 477 retval = 0; 478 } 479 480 if (retval == 0) { 481 for (row = 0; row < h; row++) { 482 for (col = 0; col < w; col++) { 483 int r = (buf[pitch * row + col] >> rshift) & 0xFF; 484 int g = (buf[pitch * row + col] >> gshift) & 0xFF; 485 int b = (buf[pitch * row + col] >> bshift) & 0xFF; 486 if (r < 0) r += 256; 487 if (g < 0) g += 256; 488 if (b < 0) b += 256; 489 System.out.format("%3d/%3d/%3d ", r, g, b); 490 } 491 System.out.print("\n"); 492 } 493 } 494 return retval; 495 } 496 497 private static int checkImg(BufferedImage img, int pf, int subsamp, 498 TJScalingFactor sf, int flags) throws Exception { 499 WritableRaster wr = img.getRaster(); 500 int imgType = img.getType(); 501 if (imgType == BufferedImage.TYPE_INT_RGB || 502 imgType == BufferedImage.TYPE_INT_BGR || 503 imgType == BufferedImage.TYPE_INT_ARGB || 504 imgType == BufferedImage.TYPE_INT_ARGB_PRE) { 505 SinglePixelPackedSampleModel sm = 506 (SinglePixelPackedSampleModel)img.getSampleModel(); 507 int pitch = sm.getScanlineStride(); 508 DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); 509 int[] buf = db.getData(); 510 return checkIntBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, 511 subsamp, sf, flags); 512 } else { 513 ComponentSampleModel sm = (ComponentSampleModel)img.getSampleModel(); 514 int pitch = sm.getScanlineStride(); 515 DataBufferByte db = (DataBufferByte)wr.getDataBuffer(); 516 byte[] buf = db.getData(); 517 return checkBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, subsamp, 518 sf, flags); 519 } 520 } 521 522 private static int PAD(int v, int p) { 523 return ((v + (p) - 1) & (~((p) - 1))); 524 } 525 526 private static int checkBufYUV(byte[] buf, int size, int w, int h, 527 int subsamp, TJScalingFactor sf) 528 throws Exception { 529 int row, col; 530 int hsf = TJ.getMCUWidth(subsamp) / 8, vsf = TJ.getMCUHeight(subsamp) / 8; 531 int pw = PAD(w, hsf), ph = PAD(h, vsf); 532 int cw = pw / hsf, ch = ph / vsf; 533 int ypitch = PAD(pw, pad), uvpitch = PAD(cw, pad); 534 int retval = 1; 535 int correctsize = ypitch * ph + 536 (subsamp == TJ.SAMP_GRAY ? 0 : uvpitch * ch * 2); 537 int halfway = 16 * sf.getNum() / sf.getDenom(); 538 int blockSize = 8 * sf.getNum() / sf.getDenom(); 539 540 try { 541 if (size != correctsize) 542 throw new Exception("Incorrect size " + size + ". Should be " + 543 correctsize); 544 545 for (row = 0; row < ph; row++) { 546 for (col = 0; col < pw; col++) { 547 byte y = buf[ypitch * row + col]; 548 if (((row / blockSize) + (col / blockSize)) % 2 == 0) { 549 if (row < halfway) 550 checkVal255(row, col, y, "Y"); 551 else 552 checkVal0(row, col, y, "Y"); 553 } else { 554 if (row < halfway) 555 checkVal(row, col, y, "Y", 76); 556 else 557 checkVal(row, col, y, "Y", 226); 558 } 559 } 560 } 561 if (subsamp != TJ.SAMP_GRAY) { 562 halfway = 16 / vsf * sf.getNum() / sf.getDenom(); 563 for (row = 0; row < ch; row++) { 564 for (col = 0; col < cw; col++) { 565 byte u = buf[ypitch * ph + (uvpitch * row + col)], 566 v = buf[ypitch * ph + uvpitch * ch + (uvpitch * row + col)]; 567 if (((row * vsf / blockSize) + (col * hsf / blockSize)) % 2 == 0) { 568 checkVal(row, col, u, "U", 128); 569 checkVal(row, col, v, "V", 128); 570 } else { 571 if (row < halfway) { 572 checkVal(row, col, u, "U", 85); 573 checkVal255(row, col, v, "V"); 574 } else { 575 checkVal0(row, col, u, "U"); 576 checkVal(row, col, v, "V", 149); 577 } 578 } 579 } 580 } 581 } 582 } catch(Exception e) { 583 System.out.println("\n" + e.getMessage()); 584 retval = 0; 585 } 586 587 if (retval == 0) { 588 for (row = 0; row < ph; row++) { 589 for (col = 0; col < pw; col++) { 590 int y = buf[ypitch * row + col]; 591 if (y < 0) y += 256; 592 System.out.format("%3d ", y); 593 } 594 System.out.print("\n"); 595 } 596 System.out.print("\n"); 597 for (row = 0; row < ch; row++) { 598 for (col = 0; col < cw; col++) { 599 int u = buf[ypitch * ph + (uvpitch * row + col)]; 600 if (u < 0) u += 256; 601 System.out.format("%3d ", u); 602 } 603 System.out.print("\n"); 604 } 605 System.out.print("\n"); 606 for (row = 0; row < ch; row++) { 607 for (col = 0; col < cw; col++) { 608 int v = buf[ypitch * ph + uvpitch * ch + (uvpitch * row + col)]; 609 if (v < 0) v += 256; 610 System.out.format("%3d ", v); 611 } 612 System.out.print("\n"); 613 } 614 } 615 616 return retval; 617 } 618 619 private static void writeJPEG(byte[] jpegBuf, int jpegBufSize, 620 String filename) throws Exception { 621 File file = new File(filename); 622 FileOutputStream fos = new FileOutputStream(file); 623 fos.write(jpegBuf, 0, jpegBufSize); 624 fos.close(); 625 } 626 627 private static int compTest(TJCompressor tjc, byte[] dstBuf, int w, 628 int h, int pf, String baseName, int subsamp, 629 int jpegQual, int flags) throws Exception { 630 String tempStr; 631 byte[] srcBuf = null; 632 BufferedImage img = null; 633 String pfStr, pfStrLong; 634 String buStr = (flags & TJ.FLAG_BOTTOMUP) != 0 ? "BU" : "TD"; 635 String buStrLong = (flags & TJ.FLAG_BOTTOMUP) != 0 ? 636 "Bottom-Up" : "Top-Down "; 637 int size = 0, ps, imgType = pf; 638 639 if (bi) { 640 pf = biTypePF(imgType); 641 pfStr = biTypeStr(imgType); 642 pfStrLong = pfStr + " (" + pixFormatStr[pf] + ")"; 643 } else { 644 pfStr = pixFormatStr[pf]; 645 pfStrLong = pfStr; 646 } 647 ps = TJ.getPixelSize(pf); 648 649 if (bi) { 650 img = new BufferedImage(w, h, imgType); 651 initImg(img, pf, flags); 652 tempStr = baseName + "_enc_" + pfStr + "_" + buStr + "_" + 653 subName[subsamp] + "_Q" + jpegQual + ".png"; 654 File file = new File(tempStr); 655 ImageIO.write(img, "png", file); 656 tjc.setSourceImage(img, 0, 0, 0, 0); 657 } else { 658 srcBuf = new byte[w * h * ps + 1]; 659 initBuf(srcBuf, w, w * ps, h, pf, flags); 660 tjc.setSourceImage(srcBuf, 0, 0, w, 0, h, pf); 661 } 662 Arrays.fill(dstBuf, (byte)0); 663 664 tjc.setSubsamp(subsamp); 665 tjc.setJPEGQuality(jpegQual); 666 if (doYUV) { 667 System.out.format("%s %s -> YUV %s ... ", pfStrLong, buStrLong, 668 subNameLong[subsamp]); 669 YUVImage yuvImage = tjc.encodeYUV(pad, flags); 670 if (checkBufYUV(yuvImage.getBuf(), yuvImage.getSize(), w, h, subsamp, 671 new TJScalingFactor(1, 1)) == 1) 672 System.out.print("Passed.\n"); 673 else { 674 System.out.print("FAILED!\n"); 675 exitStatus = -1; 676 } 677 678 System.out.format("YUV %s %s -> JPEG Q%d ... ", subNameLong[subsamp], 679 buStrLong, jpegQual); 680 tjc.setSourceImage(yuvImage); 681 } else { 682 System.out.format("%s %s -> %s Q%d ... ", pfStrLong, buStrLong, 683 subNameLong[subsamp], jpegQual); 684 } 685 tjc.compress(dstBuf, flags); 686 size = tjc.getCompressedSize(); 687 688 tempStr = baseName + "_enc_" + pfStr + "_" + buStr + "_" + 689 subName[subsamp] + "_Q" + jpegQual + ".jpg"; 690 writeJPEG(dstBuf, size, tempStr); 691 System.out.println("Done.\n Result in " + tempStr); 692 693 return size; 694 } 695 696 private static void decompTest(TJDecompressor tjd, byte[] jpegBuf, 697 int jpegSize, int w, int h, int pf, 698 String baseName, int subsamp, int flags, 699 TJScalingFactor sf) throws Exception { 700 String pfStr, pfStrLong, tempStr; 701 String buStrLong = (flags & TJ.FLAG_BOTTOMUP) != 0 ? 702 "Bottom-Up" : "Top-Down "; 703 int scaledWidth = sf.getScaled(w); 704 int scaledHeight = sf.getScaled(h); 705 int temp1, temp2, imgType = pf; 706 BufferedImage img = null; 707 byte[] dstBuf = null; 708 709 if (bi) { 710 pf = biTypePF(imgType); 711 pfStr = biTypeStr(imgType); 712 pfStrLong = pfStr + " (" + pixFormatStr[pf] + ")"; 713 } else { 714 pfStr = pixFormatStr[pf]; 715 pfStrLong = pfStr; 716 } 717 718 tjd.setSourceImage(jpegBuf, jpegSize); 719 if (tjd.getWidth() != w || tjd.getHeight() != h || 720 tjd.getSubsamp() != subsamp) 721 throw new Exception("Incorrect JPEG header"); 722 723 temp1 = scaledWidth; 724 temp2 = scaledHeight; 725 temp1 = tjd.getScaledWidth(temp1, temp2); 726 temp2 = tjd.getScaledHeight(temp1, temp2); 727 if (temp1 != scaledWidth || temp2 != scaledHeight) 728 throw new Exception("Scaled size mismatch"); 729 730 if (doYUV) { 731 System.out.format("JPEG -> YUV %s ", subNameLong[subsamp]); 732 if(!sf.isOne()) 733 System.out.format("%d/%d ... ", sf.getNum(), sf.getDenom()); 734 else System.out.print("... "); 735 YUVImage yuvImage = tjd.decompressToYUV(scaledWidth, pad, scaledHeight, 736 flags); 737 if (checkBufYUV(yuvImage.getBuf(), yuvImage.getSize(), scaledWidth, 738 scaledHeight, subsamp, sf) == 1) 739 System.out.print("Passed.\n"); 740 else { 741 System.out.print("FAILED!\n"); exitStatus = -1; 742 } 743 744 System.out.format("YUV %s -> %s %s ... ", subNameLong[subsamp], 745 pfStrLong, buStrLong); 746 tjd.setSourceImage(yuvImage); 747 } else { 748 System.out.format("JPEG -> %s %s ", pfStrLong, buStrLong); 749 if(!sf.isOne()) 750 System.out.format("%d/%d ... ", sf.getNum(), sf.getDenom()); 751 else System.out.print("... "); 752 } 753 if (bi) 754 img = tjd.decompress(scaledWidth, scaledHeight, imgType, flags); 755 else 756 dstBuf = tjd.decompress(scaledWidth, 0, scaledHeight, pf, flags); 757 758 if (bi) { 759 tempStr = baseName + "_dec_" + pfStr + "_" + 760 (((flags & TJ.FLAG_BOTTOMUP) != 0) ? "BU" : "TD") + "_" + 761 subName[subsamp] + "_" + 762 (double)sf.getNum() / (double)sf.getDenom() + "x" + ".png"; 763 File file = new File(tempStr); 764 ImageIO.write(img, "png", file); 765 } 766 767 if ((bi && checkImg(img, pf, subsamp, sf, flags) == 1) || 768 (!bi && checkBuf(dstBuf, scaledWidth, 769 scaledWidth * TJ.getPixelSize(pf), scaledHeight, pf, 770 subsamp, sf, flags) == 1)) 771 System.out.print("Passed.\n"); 772 else { 773 System.out.print("FAILED!\n"); 774 exitStatus = -1; 775 } 776 } 777 778 private static void decompTest(TJDecompressor tjd, byte[] jpegBuf, 779 int jpegSize, int w, int h, int pf, 780 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 797 private static void doTest(int w, int h, int[] formats, int subsamp, 798 String baseName) 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 840 private 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 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 _4byteFormats[4] = -1; 915 doTest(35, 39, bi ? _3byteFormatsBI : _3byteFormats, TJ.SAMP_444, 916 testName); 917 doTest(39, 41, bi ? _4byteFormatsBI : _4byteFormats, TJ.SAMP_444, 918 testName); 919 doTest(41, 35, bi ? _3byteFormatsBI : _3byteFormats, TJ.SAMP_422, 920 testName); 921 doTest(35, 39, bi ? _4byteFormatsBI : _4byteFormats, TJ.SAMP_422, 922 testName); 923 doTest(39, 41, bi ? _3byteFormatsBI : _3byteFormats, TJ.SAMP_420, 924 testName); 925 doTest(41, 35, bi ? _4byteFormatsBI : _4byteFormats, TJ.SAMP_420, 926 testName); 927 doTest(35, 39, bi ? _3byteFormatsBI : _3byteFormats, TJ.SAMP_440, 928 testName); 929 doTest(39, 41, bi ? _4byteFormatsBI : _4byteFormats, TJ.SAMP_440, 930 testName); 931 doTest(41, 35, bi ? _3byteFormatsBI : _3byteFormats, TJ.SAMP_411, 932 testName); 933 doTest(35, 39, bi ? _4byteFormatsBI : _4byteFormats, TJ.SAMP_411, 934 testName); 935 doTest(39, 41, bi ? onlyGrayBI : onlyGray, TJ.SAMP_GRAY, testName); 936 doTest(41, 35, bi ? _3byteFormatsBI : _3byteFormats, TJ.SAMP_GRAY, 937 testName); 938 _4byteFormats[4] = -1; 939 doTest(35, 39, bi ? _4byteFormatsBI : _4byteFormats, TJ.SAMP_GRAY, 940 testName); 941 if (!bi) 942 bufSizeTest(); 943 if (doYUV && !bi) { 944 System.out.print("\n--------------------\n\n"); 945 doTest(48, 48, onlyRGB, TJ.SAMP_444, "javatest_yuv0"); 946 doTest(48, 48, onlyRGB, TJ.SAMP_422, "javatest_yuv0"); 947 doTest(48, 48, onlyRGB, TJ.SAMP_420, "javatest_yuv0"); 948 doTest(48, 48, onlyRGB, TJ.SAMP_440, "javatest_yuv0"); 949 doTest(48, 48, onlyRGB, TJ.SAMP_411, "javatest_yuv0"); 950 doTest(48, 48, onlyRGB, TJ.SAMP_GRAY, "javatest_yuv0"); 951 doTest(48, 48, onlyGray, TJ.SAMP_GRAY, "javatest_yuv0"); 952 } 953 } catch(Exception e) { 954 e.printStackTrace(); 955 exitStatus = -1; 956 } 957 System.exit(exitStatus); 958 } 959 } 960