TJExample.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. /*
  2. * Copyright (C)2011-2012, 2014-2015, 2017-2018 D. R. Commander.
  3. * All Rights Reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * - Redistributions of source code must retain the above copyright notice,
  9. * this list of conditions and the following disclaimer.
  10. * - Redistributions in binary form must reproduce the above copyright notice,
  11. * this list of conditions and the following disclaimer in the documentation
  12. * and/or other materials provided with the distribution.
  13. * - Neither the name of the libjpeg-turbo Project nor the names of its
  14. * contributors may be used to endorse or promote products derived from this
  15. * software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
  18. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
  21. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  22. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  23. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  24. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  25. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  26. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  27. * POSSIBILITY OF SUCH DAMAGE.
  28. */
  29. /*
  30. * This program demonstrates how to compress, decompress, and transform JPEG
  31. * images using the TurboJPEG Java API
  32. */
  33. import java.io.*;
  34. import java.awt.*;
  35. import java.awt.image.*;
  36. import java.nio.*;
  37. import javax.imageio.*;
  38. import javax.swing.*;
  39. import org.libjpegturbo.turbojpeg.*;
  40. @SuppressWarnings("checkstyle:JavadocType")
  41. class TJExample implements TJCustomFilter {
  42. static final String CLASS_NAME =
  43. new TJExample().getClass().getName();
  44. static final int DEFAULT_SUBSAMP = TJ.SAMP_444;
  45. static final int DEFAULT_QUALITY = 95;
  46. static final String[] SUBSAMP_NAME = {
  47. "4:4:4", "4:2:2", "4:2:0", "Grayscale", "4:4:0", "4:1:1"
  48. };
  49. static final String[] COLORSPACE_NAME = {
  50. "RGB", "YCbCr", "GRAY", "CMYK", "YCCK"
  51. };
  52. /* DCT filter example. This produces a negative of the image. */
  53. @SuppressWarnings("checkstyle:JavadocMethod")
  54. public void customFilter(ShortBuffer coeffBuffer, Rectangle bufferRegion,
  55. Rectangle planeRegion, int componentIndex,
  56. int transformIndex, TJTransform transform)
  57. throws TJException {
  58. for (int i = 0; i < bufferRegion.width * bufferRegion.height; i++) {
  59. coeffBuffer.put(i, (short)(-coeffBuffer.get(i)));
  60. }
  61. }
  62. static void usage() throws Exception {
  63. System.out.println("\nUSAGE: java [Java options] " + CLASS_NAME +
  64. " <Input image> <Output image> [options]\n");
  65. System.out.println("Input and output images can be in any image format that the Java Image I/O");
  66. System.out.println("extensions understand. If either filename ends in a .jpg extension, then");
  67. System.out.println("the TurboJPEG API will be used to compress or decompress the image.\n");
  68. System.out.println("Compression Options (used if the output image is a JPEG image)");
  69. System.out.println("--------------------------------------------------------------\n");
  70. System.out.println("-subsamp <444|422|420|gray> = Apply this level of chrominance subsampling when");
  71. System.out.println(" compressing the output image. The default is to use the same level of");
  72. System.out.println(" subsampling as in the input image, if the input image is also a JPEG");
  73. System.out.println(" image, or to use grayscale if the input image is a grayscale non-JPEG");
  74. System.out.println(" image, or to use " +
  75. SUBSAMP_NAME[DEFAULT_SUBSAMP] +
  76. " subsampling otherwise.\n");
  77. System.out.println("-q <1-100> = Compress the output image with this JPEG quality level");
  78. System.out.println(" (default = " + DEFAULT_QUALITY + ").\n");
  79. System.out.println("Decompression Options (used if the input image is a JPEG image)");
  80. System.out.println("---------------------------------------------------------------\n");
  81. System.out.println("-scale M/N = Scale the input image by a factor of M/N when decompressing it.");
  82. System.out.print("(M/N = ");
  83. for (int i = 0; i < SCALING_FACTORS.length; i++) {
  84. System.out.print(SCALING_FACTORS[i].getNum() + "/" +
  85. SCALING_FACTORS[i].getDenom());
  86. if (SCALING_FACTORS.length == 2 && i != SCALING_FACTORS.length - 1)
  87. System.out.print(" or ");
  88. else if (SCALING_FACTORS.length > 2) {
  89. if (i != SCALING_FACTORS.length - 1)
  90. System.out.print(", ");
  91. if (i == SCALING_FACTORS.length - 2)
  92. System.out.print("or ");
  93. }
  94. }
  95. System.out.println(")\n");
  96. System.out.println("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 =");
  97. System.out.println(" Perform one of these lossless transform operations on the input image");
  98. System.out.println(" prior to decompressing it (these options are mutually exclusive.)\n");
  99. System.out.println("-grayscale = Perform lossless grayscale conversion on the input image prior");
  100. System.out.println(" to decompressing it (can be combined with the other transform operations");
  101. System.out.println(" above.)\n");
  102. System.out.println("-crop WxH+X+Y = Perform lossless cropping on the input image prior to");
  103. System.out.println(" decompressing it. X and Y specify the upper left corner of the cropping");
  104. System.out.println(" region, and W and H specify the width and height of the cropping region.");
  105. System.out.println(" X and Y must be evenly divible by the MCU block size (8x8 if the input");
  106. System.out.println(" image was compressed using no subsampling or grayscale, 16x8 if it was");
  107. System.out.println(" compressed using 4:2:2 subsampling, or 16x16 if it was compressed using");
  108. System.out.println(" 4:2:0 subsampling.)\n");
  109. System.out.println("General Options");
  110. System.out.println("---------------\n");
  111. System.out.println("-display = Display output image (Output filename need not be specified in this");
  112. System.out.println(" case.)\n");
  113. System.out.println("-fastupsample = Use the fastest chrominance upsampling algorithm available in");
  114. System.out.println(" the underlying codec.\n");
  115. System.out.println("-fastdct = Use the fastest DCT/IDCT algorithms available in the underlying");
  116. System.out.println(" codec.\n");
  117. System.out.println("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the");
  118. System.out.println(" underlying codec.\n");
  119. System.exit(1);
  120. }
  121. public static void main(String[] argv) {
  122. try {
  123. TJScalingFactor scalingFactor = new TJScalingFactor(1, 1);
  124. int outSubsamp = -1, outQual = -1;
  125. TJTransform xform = new TJTransform();
  126. boolean display = false;
  127. int flags = 0;
  128. int width, height;
  129. String inFormat = "jpg", outFormat = "jpg";
  130. BufferedImage img = null;
  131. byte[] imgBuf = null;
  132. if (argv.length < 2)
  133. usage();
  134. if (argv[1].substring(0, 2).equalsIgnoreCase("-d"))
  135. display = true;
  136. /* Parse arguments. */
  137. for (int i = 2; i < argv.length; i++) {
  138. if (argv[i].length() < 2)
  139. continue;
  140. else if (argv[i].length() > 2 &&
  141. argv[i].substring(0, 3).equalsIgnoreCase("-sc") &&
  142. i < argv.length - 1) {
  143. int match = 0;
  144. String[] scaleArg = argv[++i].split("/");
  145. if (scaleArg.length == 2) {
  146. TJScalingFactor tempsf =
  147. new TJScalingFactor(Integer.parseInt(scaleArg[0]),
  148. Integer.parseInt(scaleArg[1]));
  149. for (int j = 0; j < SCALING_FACTORS.length; j++) {
  150. if (tempsf.equals(SCALING_FACTORS[j])) {
  151. scalingFactor = SCALING_FACTORS[j];
  152. match = 1;
  153. break;
  154. }
  155. }
  156. }
  157. if (match != 1)
  158. usage();
  159. } else if (argv[i].length() > 2 &&
  160. argv[i].substring(0, 3).equalsIgnoreCase("-su") &&
  161. i < argv.length - 1) {
  162. i++;
  163. if (argv[i].substring(0, 1).equalsIgnoreCase("g"))
  164. outSubsamp = TJ.SAMP_GRAY;
  165. else if (argv[i].equals("444"))
  166. outSubsamp = TJ.SAMP_444;
  167. else if (argv[i].equals("422"))
  168. outSubsamp = TJ.SAMP_422;
  169. else if (argv[i].equals("420"))
  170. outSubsamp = TJ.SAMP_420;
  171. else
  172. usage();
  173. } else if (argv[i].substring(0, 2).equalsIgnoreCase("-q") &&
  174. i < argv.length - 1) {
  175. outQual = Integer.parseInt(argv[++i]);
  176. if (outQual < 1 || outQual > 100)
  177. usage();
  178. } else if (argv[i].substring(0, 2).equalsIgnoreCase("-g"))
  179. xform.options |= TJTransform.OPT_GRAY;
  180. else if (argv[i].equalsIgnoreCase("-hflip"))
  181. xform.op = TJTransform.OP_HFLIP;
  182. else if (argv[i].equalsIgnoreCase("-vflip"))
  183. xform.op = TJTransform.OP_VFLIP;
  184. else if (argv[i].equalsIgnoreCase("-transpose"))
  185. xform.op = TJTransform.OP_TRANSPOSE;
  186. else if (argv[i].equalsIgnoreCase("-transverse"))
  187. xform.op = TJTransform.OP_TRANSVERSE;
  188. else if (argv[i].equalsIgnoreCase("-rot90"))
  189. xform.op = TJTransform.OP_ROT90;
  190. else if (argv[i].equalsIgnoreCase("-rot180"))
  191. xform.op = TJTransform.OP_ROT180;
  192. else if (argv[i].equalsIgnoreCase("-rot270"))
  193. xform.op = TJTransform.OP_ROT270;
  194. else if (argv[i].equalsIgnoreCase("-custom"))
  195. xform.cf = new TJExample();
  196. else if (argv[i].length() > 2 &&
  197. argv[i].substring(0, 2).equalsIgnoreCase("-c") &&
  198. i < argv.length - 1) {
  199. String[] cropArg = argv[++i].split("[x\\+]");
  200. if (cropArg.length != 4)
  201. usage();
  202. xform.width = Integer.parseInt(cropArg[0]);
  203. xform.height = Integer.parseInt(cropArg[1]);
  204. xform.x = Integer.parseInt(cropArg[2]);
  205. xform.y = Integer.parseInt(cropArg[3]);
  206. if (xform.x < 0 || xform.y < 0 || xform.width < 1 ||
  207. xform.height < 1)
  208. usage();
  209. xform.options |= TJTransform.OPT_CROP;
  210. } else if (argv[i].substring(0, 2).equalsIgnoreCase("-d"))
  211. display = true;
  212. else if (argv[i].equalsIgnoreCase("-fastupsample")) {
  213. System.out.println("Using fast upsampling code");
  214. flags |= TJ.FLAG_FASTUPSAMPLE;
  215. } else if (argv[i].equalsIgnoreCase("-fastdct")) {
  216. System.out.println("Using fastest DCT/IDCT algorithm");
  217. flags |= TJ.FLAG_FASTDCT;
  218. } else if (argv[i].equalsIgnoreCase("-accuratedct")) {
  219. System.out.println("Using most accurate DCT/IDCT algorithm");
  220. flags |= TJ.FLAG_ACCURATEDCT;
  221. } else usage();
  222. }
  223. /* Determine input and output image formats based on file extensions. */
  224. String[] inFileTokens = argv[0].split("\\.");
  225. if (inFileTokens.length > 1)
  226. inFormat = inFileTokens[inFileTokens.length - 1];
  227. String[] outFileTokens;
  228. if (display)
  229. outFormat = "bmp";
  230. else {
  231. outFileTokens = argv[1].split("\\.");
  232. if (outFileTokens.length > 1)
  233. outFormat = outFileTokens[outFileTokens.length - 1];
  234. }
  235. if (inFormat.equalsIgnoreCase("jpg")) {
  236. /* Input image is a JPEG image. Decompress and/or transform it. */
  237. boolean doTransform = (xform.op != TJTransform.OP_NONE ||
  238. xform.options != 0 || xform.cf != null);
  239. /* Read the JPEG file into memory. */
  240. File jpegFile = new File(argv[0]);
  241. FileInputStream fis = new FileInputStream(jpegFile);
  242. int jpegSize = fis.available();
  243. if (jpegSize < 1) {
  244. System.out.println("Input file contains no data");
  245. System.exit(1);
  246. }
  247. byte[] jpegBuf = new byte[jpegSize];
  248. fis.read(jpegBuf);
  249. fis.close();
  250. TJDecompressor tjd;
  251. if (doTransform) {
  252. /* Transform it. */
  253. TJTransformer tjt = new TJTransformer(jpegBuf);
  254. TJTransform[] xforms = new TJTransform[1];
  255. xforms[0] = xform;
  256. xforms[0].options |= TJTransform.OPT_TRIM;
  257. TJDecompressor[] tjds = tjt.transform(xforms, 0);
  258. tjd = tjds[0];
  259. tjt.close();
  260. } else
  261. tjd = new TJDecompressor(jpegBuf);
  262. width = tjd.getWidth();
  263. height = tjd.getHeight();
  264. int inSubsamp = tjd.getSubsamp();
  265. int inColorspace = tjd.getColorspace();
  266. System.out.println((doTransform ? "Transformed" : "Input") +
  267. " Image (jpg): " + width + " x " + height +
  268. " pixels, " + SUBSAMP_NAME[inSubsamp] +
  269. " subsampling, " + COLORSPACE_NAME[inColorspace]);
  270. if (outFormat.equalsIgnoreCase("jpg") && doTransform &&
  271. scalingFactor.isOne() && outSubsamp < 0 && outQual < 0) {
  272. /* Input image has been transformed, and no re-compression options
  273. have been selected. Write the transformed image to disk and
  274. exit. */
  275. File outFile = new File(argv[1]);
  276. FileOutputStream fos = new FileOutputStream(outFile);
  277. fos.write(tjd.getJPEGBuf(), 0, tjd.getJPEGSize());
  278. fos.close();
  279. System.exit(0);
  280. }
  281. /* Scaling and/or a non-JPEG output image format and/or compression
  282. options have been selected, so we need to decompress the
  283. input/transformed image. */
  284. width = scalingFactor.getScaled(width);
  285. height = scalingFactor.getScaled(height);
  286. if (outSubsamp < 0)
  287. outSubsamp = inSubsamp;
  288. if (!outFormat.equalsIgnoreCase("jpg"))
  289. img = tjd.decompress(width, height, BufferedImage.TYPE_INT_RGB,
  290. flags);
  291. else
  292. imgBuf = tjd.decompress(width, 0, height, TJ.PF_BGRX, flags);
  293. tjd.close();
  294. } else {
  295. /* Input image is not a JPEG image. Load it into memory. */
  296. img = ImageIO.read(new File(argv[0]));
  297. if (img == null)
  298. throw new Exception("Input image type not supported.");
  299. width = img.getWidth();
  300. height = img.getHeight();
  301. if (outSubsamp < 0) {
  302. if (img.getType() == BufferedImage.TYPE_BYTE_GRAY)
  303. outSubsamp = TJ.SAMP_GRAY;
  304. else
  305. outSubsamp = DEFAULT_SUBSAMP;
  306. }
  307. System.out.println("Input Image: " + width + " x " + height +
  308. " pixels");
  309. }
  310. System.gc();
  311. if (!display)
  312. System.out.print("Output Image (" + outFormat + "): " + width +
  313. " x " + height + " pixels");
  314. if (display) {
  315. /* Display the uncompressed image */
  316. ImageIcon icon = new ImageIcon(img);
  317. JLabel label = new JLabel(icon, JLabel.CENTER);
  318. JOptionPane.showMessageDialog(null, label, "Output Image",
  319. JOptionPane.PLAIN_MESSAGE);
  320. } else if (outFormat.equalsIgnoreCase("jpg")) {
  321. /* Output image format is JPEG. Compress the uncompressed image. */
  322. if (outQual < 0)
  323. outQual = DEFAULT_QUALITY;
  324. System.out.println(", " + SUBSAMP_NAME[outSubsamp] +
  325. " subsampling, quality = " + outQual);
  326. TJCompressor tjc = new TJCompressor();
  327. tjc.setSubsamp(outSubsamp);
  328. tjc.setJPEGQuality(outQual);
  329. if (img != null)
  330. tjc.setSourceImage(img, 0, 0, 0, 0);
  331. else
  332. tjc.setSourceImage(imgBuf, 0, 0, width, 0, height, TJ.PF_BGRX);
  333. byte[] jpegBuf = tjc.compress(flags);
  334. int jpegSize = tjc.getCompressedSize();
  335. tjc.close();
  336. /* Write the JPEG image to disk. */
  337. File outFile = new File(argv[1]);
  338. FileOutputStream fos = new FileOutputStream(outFile);
  339. fos.write(jpegBuf, 0, jpegSize);
  340. fos.close();
  341. } else {
  342. /* Output image format is not JPEG. Save the uncompressed image
  343. directly to disk. */
  344. System.out.print("\n");
  345. File outFile = new File(argv[1]);
  346. ImageIO.write(img, outFormat, outFile);
  347. }
  348. } catch (Exception e) {
  349. e.printStackTrace();
  350. System.exit(-1);
  351. }
  352. }
  353. static final TJScalingFactor[] SCALING_FACTORS =
  354. TJ.getScalingFactors();
  355. };