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