Ticket #15574: josm-large-jpeg-scaled-loading-through-jni-turbojpeg-to-workaround-oom-experimental.patch
File josm-large-jpeg-scaled-loading-through-jni-turbojpeg-to-workaround-oom-experimental.patch, 77.1 KB (added by , 7 years ago) |
---|
-
src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java
22 22 import java.awt.geom.AffineTransform; 23 23 import java.awt.geom.Rectangle2D; 24 24 import java.awt.image.BufferedImage; 25 import java.awt.image.ImageObserver; 25 26 import java.io.File; 27 import java.io.FileInputStream; 26 28 27 29 import javax.swing.JComponent; 28 30 import javax.swing.SwingUtilities; 29 31 32 import org.libjpegturbo.turbojpeg.TJDecompressor; 33 import org.libjpegturbo.turbojpeg.TJException; 30 34 import org.openstreetmap.josm.data.preferences.BooleanProperty; 31 35 import org.openstreetmap.josm.data.preferences.DoubleProperty; 32 36 import org.openstreetmap.josm.spi.preferences.Config; … … 112 116 public static class VisRect extends Rectangle { 113 117 private final Rectangle init; 114 118 119 /** set when this {@code VisRect} is updated by a mouse drag operation and 120 * unset on mouse release **/ 121 public boolean isDragUpdate; 122 115 123 /** 116 124 * Constructs a new {@code VisRect}. 117 125 * @param x the specified X coordinate … … 124 132 init = new Rectangle(this); 125 133 } 126 134 135 /** 136 * Constructs a new {@code VisRect}. 137 * @param x the specified X coordinate 138 * @param y the specified Y coordinate 139 * @param width the width of the rectangle 140 * @param height the height of the rectangle 141 * @param peer share full bounds with this peer {@code VisRect} 142 */ 127 143 public VisRect(int x, int y, int width, int height, VisRect peer) { 128 144 super(x, y, width, height); 129 145 init = peer.init; … … 145 161 this(0, 0, 0, 0); 146 162 } 147 163 164 @SuppressWarnings("javadoc") 148 165 public boolean isFullView() { 149 166 return init.equals(this); 150 167 } 151 168 169 @SuppressWarnings("javadoc") 152 170 public boolean isFullView1D() { 153 171 return (init.x == x && init.width == width) 154 172 || (init.y == y && init.height == height); 155 173 } 156 174 175 @SuppressWarnings("javadoc") 157 176 public void reset() { 158 177 setBounds(init); 159 178 } 160 179 180 @SuppressWarnings("javadoc") 161 181 public void checkRectPos() { 162 182 if (x < 0) { 163 183 x = 0; … … 173 193 } 174 194 } 175 195 196 @SuppressWarnings("javadoc") 176 197 public void checkRectSize() { 177 198 if (width > init.width) { 178 199 width = init.width; … … 182 203 } 183 204 } 184 205 206 @SuppressWarnings("javadoc") 185 207 public void checkPointInside(Point p) { 186 208 if (p.x < x) { 187 209 p.x = x; … … 199 221 } 200 222 201 223 /** The thread that reads the images. */ 202 private class LoadImageRunnable implements Runnable {224 private class LoadImageRunnable implements Runnable, ImageObserver { 203 225 204 226 private final File file; 205 227 private final int orientation; 228 private int width; 229 private int height; 206 230 207 231 LoadImageRunnable(File file, Integer orientation) { 208 232 this.file = file; … … 210 234 } 211 235 212 236 @Override 237 public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { 238 if (((infoflags & ImageObserver.WIDTH) == ImageObserver.WIDTH) && 239 ((infoflags & ImageObserver.HEIGHT) == ImageObserver.HEIGHT)) { 240 this.width = width; 241 this.height = height; 242 synchronized (this) { 243 this.notify(); 244 return false; 245 } 246 } 247 return true; 248 } 249 250 @Override 213 251 public void run() { 214 252 Image img = Toolkit.getDefaultToolkit().createImage(file.getPath()); 215 tracker.addImage(img, 1);216 253 217 // Wait for the end of loading 218 while (!tracker.checkID(1, true)) { 219 if (this.file != ImageDisplay.this.file) { 220 // The file has changed 221 tracker.removeImage(img); 222 return; 254 synchronized (this) { 255 width = -1; 256 img.getWidth(this); 257 img.getHeight(this); 258 259 while (width < 0) { 260 try { 261 this.wait(); 262 if (width < 0) { 263 errorLoading = true; 264 return; 265 } 266 } catch (InterruptedException e) { 267 e.printStackTrace(); 268 } 269 } 270 } 271 272 long allocatedMem = Runtime.getRuntime().totalMemory() - 273 Runtime.getRuntime().freeMemory(); 274 long mem = Runtime.getRuntime().maxMemory()-allocatedMem; 275 276 if (mem > ((long)width*height*4)*2) { 277 Logging.info("Loading "+file.getPath()+" using default toolkit"); 278 tracker.addImage(img, 1); 279 280 // Wait for the end of loading 281 while (!tracker.checkID(1, true)) { 282 if (this.file != ImageDisplay.this.file) { 283 // The file has changed 284 tracker.removeImage(img); 285 return; 286 } 287 try { 288 Thread.sleep(5); 289 } catch (InterruptedException e) { 290 Logging.warn("InterruptedException in "+getClass().getSimpleName()+ 291 " while loading image "+file.getPath()); 292 Thread.currentThread().interrupt(); 293 } 223 294 } 295 if (tracker.isErrorID(1)) { 296 img = null; 297 System.gc(); 298 } 299 } else { 300 img = null; 301 } 302 303 if (img == null) { 224 304 try { 225 Thread.sleep(5); 226 } catch (InterruptedException e) { 227 Logging.warn("InterruptedException in "+getClass().getSimpleName()+" while loading image "+file.getPath()); 228 Thread.currentThread().interrupt(); 305 if (!file.getPath().matches(".*\\.[jJ][pP][eE]?[gG]$")) { 306 throw new TJException("file ending indicates non-jpeg data"); 307 } 308 309 // as of JDK8 javax.imageio.plugins.jpeg.JPEGImageReadParam.canSetSourceRenderSize() is 310 // always false, so retry loading a scaled version computed by turbojpeg system library 311 // if it was built with java support 312 TJDecompressor tjd; 313 try { 314 tjd = new TJDecompressor(); 315 } catch (java.lang.UnsatisfiedLinkError le) { 316 Logging.warn("turbojpeg not found in "+System.getProperty("java.library.path")); 317 throw new TJException("library not found"); 318 } 319 320 Logging.info("Loading "+file.getPath()+" ("+width+"x"+height+") using turbojpeg"); 321 FileInputStream fis = new FileInputStream(file); 322 if (fis.available()>0) { 323 byte[] buf = new byte[fis.available()]; 324 int l = fis.read(buf); 325 fis.close(); 326 tjd.setSourceImage(buf, l); 327 328 allocatedMem = Runtime.getRuntime().totalMemory() - 329 Runtime.getRuntime().freeMemory(); 330 mem = Runtime.getRuntime().maxMemory()-allocatedMem; 331 332 BufferedImage bi = null; 333 while (width>0 && height>0) { 334 if (mem > ((long)width*height*4)*2) { 335 try { 336 bi = new BufferedImage( 337 tjd.getScaledWidth(width, height), 338 tjd.getScaledHeight(width, height), 339 BufferedImage.TYPE_INT_RGB); 340 tjd.decompress(bi, 0); 341 // store final width and height as actual 342 // values used by TJ may have been smaller, 343 // store them not before decoding succeeded 344 width = tjd.getScaledWidth(width, height); 345 height = tjd.getScaledHeight(width, height); 346 break; 347 } catch (java.lang.OutOfMemoryError oom) { 348 bi = null; 349 System.gc(); 350 } catch (Exception e) { 351 e.printStackTrace(); 352 bi = null; 353 } 354 } 355 width = (width*4)/5; 356 height = (height*4)/5; 357 } 358 359 tjd.close(); 360 tjd = null; 361 img = bi; 362 } 363 } catch (Exception ex) { 364 ex.printStackTrace(); 365 img = null; 229 366 } 230 367 } 231 368 232 boolean error = tracker.isErrorID(1);233 if (img.getWidth(null) < 0 || img.getHeight(null) < 0) {234 error = true;369 if (img == null || width <= 0 || height <= 0) { 370 tracker.removeImage(img); 371 img = null; 235 372 } 236 373 237 374 synchronized (ImageDisplay.this) { … … 241 378 return; 242 379 } 243 380 244 if (!error) { 245 ImageDisplay.this.image = img; 246 visibleRect = new VisRect(0, 0, img.getWidth(null), img.getHeight(null)); 247 248 final int w = (int) visibleRect.getWidth(); 249 final int h = (int) visibleRect.getHeight(); 250 381 if (img != null) { 382 boolean switchedDim = false; 251 383 if (ExifReader.orientationNeedsCorrection(orientation)) { 252 final int hh, ww;253 384 if (ExifReader.orientationSwitchesDimensions(orientation)) { 254 ww = h; 255 hh = w; 256 } else { 257 ww = w; 258 hh = h; 385 width = img.getHeight(null); 386 height = img.getWidth(null); 387 switchedDim = true; 259 388 } 260 final BufferedImage rot = new BufferedImage(ww, hh, BufferedImage.TYPE_INT_RGB); 261 final AffineTransform xform = ExifReader.getRestoreOrientationTransform(orientation, w, h); 389 final BufferedImage rot = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 390 final AffineTransform xform = ExifReader.getRestoreOrientationTransform(orientation, 391 img.getWidth(null), img.getHeight(null)); 262 392 final Graphics2D g = rot.createGraphics(); 263 g.drawImage(im age, xform, null);393 g.drawImage(img, xform, null); 264 394 g.dispose(); 265 266 visibleRect.setSize(ww, hh); 267 image.flush(); 268 ImageDisplay.this.image = rot; 395 img.flush(); 396 img = rot; 269 397 } 398 399 ImageDisplay.this.image = img; 400 visibleRect = new VisRect(0, 0, width, height); 401 402 Logging.info("Loaded "+file.getPath()+ 403 " with dimensions "+width+"x"+height+ 404 " mem(prev-avail="+mem/1024/1024+"m,taken="+ 405 width*height*4/1024/1024+"m)"+ 406 " exifOrientationSwitchedDimension="+switchedDim); 270 407 } 271 408 272 409 selectedRect = null; 273 errorLoading = error;410 errorLoading = (img == null); 274 411 } 275 412 tracker.removeImage(img); 276 413 ImageDisplay.this.repaint(); … … 473 610 474 611 if (mouseIsDragging(e)) { 475 612 Point p = comp2imgCoord(visibleRect, e.getX(), e.getY(), getSize()); 613 visibleRect.isDragUpdate = true; 476 614 visibleRect.x += mousePointInImg.x - p.x; 477 615 visibleRect.y += mousePointInImg.y - p.y; 478 616 visibleRect.checkRectPos(); … … 503 641 504 642 @Override 505 643 public void mouseReleased(MouseEvent e) { 506 if (!mouseIsZoomSelecting(e) || selectedRect == null)507 return;508 509 644 File file; 510 645 Image image; 511 646 … … 514 649 image = ImageDisplay.this.image; 515 650 } 516 651 517 if (image == null) {652 if (image == null) 518 653 return; 654 655 if (mouseIsDragging(e)) { 656 visibleRect.isDragUpdate = false; 519 657 } 520 658 521 int oldWidth = selectedRect.width; 522 int oldHeight = selectedRect.height; 659 if (mouseIsZoomSelecting(e) && selectedRect != null) { 660 int oldWidth = selectedRect.width; 661 int oldHeight = selectedRect.height; 523 662 524 // Check that the zoom doesn't exceed MAX_ZOOM:1525 if (selectedRect.width < getSize().width / MAX_ZOOM.get()) {526 selectedRect.width = (int) (getSize().width / MAX_ZOOM.get());527 }528 if (selectedRect.height < getSize().height / MAX_ZOOM.get()) {529 selectedRect.height = (int) (getSize().height / MAX_ZOOM.get());530 }663 // Check that the zoom doesn't exceed MAX_ZOOM:1 664 if (selectedRect.width < getSize().width / MAX_ZOOM.get()) { 665 selectedRect.width = (int) (getSize().width / MAX_ZOOM.get()); 666 } 667 if (selectedRect.height < getSize().height / MAX_ZOOM.get()) { 668 selectedRect.height = (int) (getSize().height / MAX_ZOOM.get()); 669 } 531 670 532 // Set the same ratio for the visible rectangle and the display area533 int hFact = selectedRect.height * getSize().width;534 int wFact = selectedRect.width * getSize().height;535 if (hFact > wFact) {536 selectedRect.width = hFact / getSize().height;537 } else {538 selectedRect.height = wFact / getSize().width;539 }671 // Set the same ratio for the visible rectangle and the display area 672 int hFact = selectedRect.height * getSize().width; 673 int wFact = selectedRect.width * getSize().height; 674 if (hFact > wFact) { 675 selectedRect.width = hFact / getSize().height; 676 } else { 677 selectedRect.height = wFact / getSize().width; 678 } 540 679 541 // Keep the center of the selection542 if (selectedRect.width != oldWidth) {543 selectedRect.x -= (selectedRect.width - oldWidth) / 2;544 }545 if (selectedRect.height != oldHeight) {546 selectedRect.y -= (selectedRect.height - oldHeight) / 2;547 }680 // Keep the center of the selection 681 if (selectedRect.width != oldWidth) { 682 selectedRect.x -= (selectedRect.width - oldWidth) / 2; 683 } 684 if (selectedRect.height != oldHeight) { 685 selectedRect.y -= (selectedRect.height - oldHeight) / 2; 686 } 548 687 549 selectedRect.checkRectSize(); 550 selectedRect.checkRectPos(); 688 selectedRect.checkRectSize(); 689 selectedRect.checkRectPos(); 690 } 551 691 552 692 synchronized (ImageDisplay.this) { 553 693 if (file == ImageDisplay.this.file) { 554 ImageDisplay.this.visibleRect.setBounds(selectedRect); 694 if (selectedRect == null) { 695 ImageDisplay.this.visibleRect = visibleRect; 696 } else { 697 ImageDisplay.this.visibleRect.setBounds(selectedRect); 698 selectedRect = null; 699 } 555 700 } 556 701 } 557 selectedRect = null;558 702 ImageDisplay.this.repaint(); 559 703 } 560 704 … … 586 730 preferenceChanged(null); 587 731 } 588 732 733 /** 734 * Sets a new source image to be displayed by this {@code ImageDisplay}. 735 * @param file new source image 736 * @param orientation orientation of new source (landscape, portrait, upside-down, etc.) 737 */ 589 738 public void setImage(File file, Integer orientation) { 590 739 synchronized (this) { 591 740 this.file = file; … … 650 799 Rectangle target = calculateDrawImageRectangle(visibleRect, size); 651 800 double scale = target.width / (double) r.width; // pixel ratio is 1:1 652 801 653 if (selectedRect == null && bilinLower < scale && scale < bilinUpper) { 654 BufferedImage bi = ImageProvider.toBufferedImage(image, r); 655 r.x = r.y = 0; 802 if (selectedRect == null && !visibleRect.isDragUpdate && 803 bilinLower < scale && scale < bilinUpper) { 804 try { 805 BufferedImage bi = ImageProvider.toBufferedImage(image, r); 806 if (bi != null) { 807 r.x = r.y = 0; 656 808 657 // See https://community.oracle.com/docs/DOC-983611 - The Perils of Image.getScaledInstance() 658 // Pre-scale image when downscaling by more than two times to avoid aliasing from default algorithm 659 image = ImageProvider.createScaledImage(bi, target.width, target.height, 660 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 661 r.width = target.width; 662 r.height = target.height; 809 // See https://community.oracle.com/docs/DOC-983611 - The Perils of Image.getScaledInstance() 810 // Pre-scale image when downscaling by more than two times to avoid aliasing from default algorithm 811 bi = ImageProvider.createScaledImage(bi, target.width, target.height, 812 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 813 r.width = target.width; 814 r.height = target.height; 815 image = bi; 816 } 817 } catch (java.lang.OutOfMemoryError oom) { 818 // fall-back to the non-bilinear scaler 819 r.x = visibleRect.x; 820 r.y = visibleRect.y; 821 System.gc(); 822 } 663 823 } else { 664 824 // if target and r cause drawImage to scale image region to a tmp buffer exceeding 665 825 // its bounds, it will silently fail; crop with r first in such cases … … 797 957 return new VisRect(x + compRect.x, y + compRect.y, w, h, imgRect); 798 958 } 799 959 960 /** 961 * Make the current image either scale to fit inside this component, 962 * or show a portion of image (1:1), if the image size is larger than 963 * the component size. 964 */ 800 965 public void zoomBestFitOrOne() { 801 966 File file; 802 967 Image image; -
src/org/libjpegturbo/turbojpeg/TJLoader.java
1 /* 2 * Copyright (C)2011 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 package org.libjpegturbo.turbojpeg; 30 31 import java.util.Arrays; 32 import java.util.List; 33 34 import org.openstreetmap.josm.tools.Logging; 35 36 final class TJLoader { 37 static void load() { 38 List<String> libs = Arrays.asList("turbojpeg", "jpeg", "jpegturbo"); 39 List<String> sfxs = Arrays.asList("so"); 40 List<String> pfxs = Arrays.asList("/usr/lib", "/usr/lib64", "/usr/lib32", 41 "/opt/libjpeg-turbo/lib64", "/opt/libjpeg-turbo/lib32"); 42 String os = System.getProperty("os.name").toLowerCase(); 43 if (os.indexOf("mac") >= 0) 44 sfxs.add("jnilib"); 45 46 for (String lib : libs) { 47 try { 48 System.loadLibrary(lib); 49 return; 50 } catch (java.lang.UnsatisfiedLinkError e) { 51 } 52 for (String s : sfxs) { 53 for (String p : pfxs) { 54 try { 55 System.load(p + "/lib" + lib + "." + s); 56 return; 57 } catch (java.lang.UnsatisfiedLinkError e2) { 58 } 59 } 60 } 61 } 62 Logging.warn("turbojpeg jni library not found or not loaded"); 63 } 64 } -
src/org/libjpegturbo/turbojpeg/TJ.java
1 /* 2 * Copyright (C)2011-2013 D. R. Commander. All Rights Reserved. 3 * Copyright (C)2015 Viktor Szathmáry. 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 package org.libjpegturbo.turbojpeg; 31 32 /** 33 * TurboJPEG utility class (cannot be instantiated) 34 */ 35 public final class TJ { 36 37 38 /** 39 * The number of chrominance subsampling options 40 */ 41 public static final int NUMSAMP = 6; 42 /** 43 * 4:4:4 chrominance subsampling (no chrominance subsampling). The JPEG 44 * or YUV image will contain one chrominance component for every pixel in the 45 * source image. 46 */ 47 public static final int SAMP_444 = 0; 48 /** 49 * 4:2:2 chrominance subsampling. The JPEG or YUV image will contain one 50 * chrominance component for every 2x1 block of pixels in the source image. 51 */ 52 public static final int SAMP_422 = 1; 53 /** 54 * 4:2:0 chrominance subsampling. The JPEG or YUV image will contain one 55 * chrominance component for every 2x2 block of pixels in the source image. 56 */ 57 public static final int SAMP_420 = 2; 58 /** 59 * Grayscale. The JPEG or YUV image will contain no chrominance components. 60 */ 61 public static final int SAMP_GRAY = 3; 62 /** 63 * 4:4:0 chrominance subsampling. The JPEG or YUV image will contain one 64 * chrominance component for every 1x2 block of pixels in the source image. 65 * Note that 4:4:0 subsampling is not fully accelerated in libjpeg-turbo. 66 */ 67 public static final int SAMP_440 = 4; 68 /** 69 * 4:1:1 chrominance subsampling. The JPEG or YUV image will contain one 70 * chrominance component for every 4x1 block of pixels in the source image. 71 * JPEG images compressed with 4:1:1 subsampling will be almost exactly the 72 * same size as those compressed with 4:2:0 subsampling, and in the 73 * aggregate, both subsampling methods produce approximately the same 74 * perceptual quality. However, 4:1:1 is better able to reproduce sharp 75 * horizontal features. Note that 4:1:1 subsampling is not fully accelerated 76 * in libjpeg-turbo. 77 */ 78 public static final int SAMP_411 = 5; 79 80 81 /** 82 * Returns the MCU block width for the given level of chrominance 83 * subsampling. 84 * 85 * @param subsamp the level of chrominance subsampling (one of 86 * <code>SAMP_*</code>) 87 * 88 * @return the MCU block width for the given level of chrominance 89 * subsampling. 90 */ 91 public static int getMCUWidth(int subsamp) { 92 checkSubsampling(subsamp); 93 return mcuWidth[subsamp]; 94 } 95 96 private static final int[] mcuWidth = { 97 8, 16, 16, 8, 8, 32 98 }; 99 100 101 /** 102 * Returns the MCU block height for the given level of chrominance 103 * subsampling. 104 * 105 * @param subsamp the level of chrominance subsampling (one of 106 * <code>SAMP_*</code>) 107 * 108 * @return the MCU block height for the given level of chrominance 109 * subsampling. 110 */ 111 public static int getMCUHeight(int subsamp) { 112 checkSubsampling(subsamp); 113 return mcuHeight[subsamp]; 114 } 115 116 private static final int[] mcuHeight = { 117 8, 8, 16, 8, 16, 8 118 }; 119 120 121 /** 122 * The number of pixel formats 123 */ 124 public static final int NUMPF = 12; 125 /** 126 * RGB pixel format. The red, green, and blue components in the image are 127 * stored in 3-byte pixels in the order R, G, B from lowest to highest byte 128 * address within each pixel. 129 */ 130 public static final int PF_RGB = 0; 131 /** 132 * BGR pixel format. The red, green, and blue components in the image are 133 * stored in 3-byte pixels in the order B, G, R from lowest to highest byte 134 * address within each pixel. 135 */ 136 public static final int PF_BGR = 1; 137 /** 138 * RGBX pixel format. The red, green, and blue components in the image are 139 * stored in 4-byte pixels in the order R, G, B from lowest to highest byte 140 * address within each pixel. The X component is ignored when compressing 141 * and undefined when decompressing. 142 */ 143 public static final int PF_RGBX = 2; 144 /** 145 * BGRX pixel format. The red, green, and blue components in the image are 146 * stored in 4-byte pixels in the order B, G, R from lowest to highest byte 147 * address within each pixel. The X component is ignored when compressing 148 * and undefined when decompressing. 149 */ 150 public static final int PF_BGRX = 3; 151 /** 152 * XBGR pixel format. The red, green, and blue components in the image are 153 * stored in 4-byte pixels in the order R, G, B from highest to lowest byte 154 * address within each pixel. The X component is ignored when compressing 155 * and undefined when decompressing. 156 */ 157 public static final int PF_XBGR = 4; 158 /** 159 * XRGB pixel format. The red, green, and blue components in the image are 160 * stored in 4-byte pixels in the order B, G, R from highest to lowest byte 161 * address within each pixel. The X component is ignored when compressing 162 * and undefined when decompressing. 163 */ 164 public static final int PF_XRGB = 5; 165 /** 166 * Grayscale pixel format. Each 1-byte pixel represents a luminance 167 * (brightness) level from 0 to 255. 168 */ 169 public static final int PF_GRAY = 6; 170 /** 171 * RGBA pixel format. This is the same as {@link #PF_RGBX}, except that when 172 * decompressing, the X byte is guaranteed to be 0xFF, which can be 173 * interpreted as an opaque alpha channel. 174 */ 175 public static final int PF_RGBA = 7; 176 /** 177 * BGRA pixel format. This is the same as {@link #PF_BGRX}, except that when 178 * decompressing, the X byte is guaranteed to be 0xFF, which can be 179 * interpreted as an opaque alpha channel. 180 */ 181 public static final int PF_BGRA = 8; 182 /** 183 * ABGR pixel format. This is the same as {@link #PF_XBGR}, except that when 184 * decompressing, the X byte is guaranteed to be 0xFF, which can be 185 * interpreted as an opaque alpha channel. 186 */ 187 public static final int PF_ABGR = 9; 188 /** 189 * ARGB pixel format. This is the same as {@link #PF_XRGB}, except that when 190 * decompressing, the X byte is guaranteed to be 0xFF, which can be 191 * interpreted as an opaque alpha channel. 192 */ 193 public static final int PF_ARGB = 10; 194 /** 195 * CMYK pixel format. Unlike RGB, which is an additive color model used 196 * primarily for display, CMYK (Cyan/Magenta/Yellow/Key) is a subtractive 197 * color model used primarily for printing. In the CMYK color model, the 198 * value of each color component typically corresponds to an amount of cyan, 199 * magenta, yellow, or black ink that is applied to a white background. In 200 * order to convert between CMYK and RGB, it is necessary to use a color 201 * management system (CMS.) A CMS will attempt to map colors within the 202 * printer's gamut to perceptually similar colors in the display's gamut and 203 * vice versa, but the mapping is typically not 1:1 or reversible, nor can it 204 * be defined with a simple formula. Thus, such a conversion is out of scope 205 * for a codec library. However, the TurboJPEG API allows for compressing 206 * CMYK pixels into a YCCK JPEG image (see {@link #CS_YCCK}) and 207 * decompressing YCCK JPEG images into CMYK pixels. 208 */ 209 public static final int PF_CMYK = 11; 210 211 212 /** 213 * Returns the pixel size (in bytes) for the given pixel format. 214 * 215 * @param pixelFormat the pixel format (one of <code>PF_*</code>) 216 * 217 * @return the pixel size (in bytes) for the given pixel format. 218 */ 219 public static int getPixelSize(int pixelFormat) { 220 checkPixelFormat(pixelFormat); 221 return pixelSize[pixelFormat]; 222 } 223 224 private static final int[] pixelSize = { 225 3, 3, 4, 4, 4, 4, 1, 4, 4, 4, 4, 4 226 }; 227 228 229 /** 230 * For the given pixel format, returns the number of bytes that the red 231 * component is offset from the start of the pixel. For instance, if a pixel 232 * of format <code>TJ.PF_BGRX</code> is stored in <code>char pixel[]</code>, 233 * then the red component will be 234 * <code>pixel[TJ.getRedOffset(TJ.PF_BGRX)]</code>. 235 * 236 * @param pixelFormat the pixel format (one of <code>PF_*</code>) 237 * 238 * @return the red offset for the given pixel format. 239 */ 240 public static int getRedOffset(int pixelFormat) { 241 checkPixelFormat(pixelFormat); 242 return redOffset[pixelFormat]; 243 } 244 245 private static final int[] redOffset = { 246 0, 2, 0, 2, 3, 1, 0, 0, 2, 3, 1, -1 247 }; 248 249 250 /** 251 * For the given pixel format, returns the number of bytes that the green 252 * component is offset from the start of the pixel. For instance, if a pixel 253 * of format <code>TJ.PF_BGRX</code> is stored in <code>char pixel[]</code>, 254 * then the green component will be 255 * <code>pixel[TJ.getGreenOffset(TJ.PF_BGRX)]</code>. 256 * 257 * @param pixelFormat the pixel format (one of <code>PF_*</code>) 258 * 259 * @return the green offset for the given pixel format. 260 */ 261 public static int getGreenOffset(int pixelFormat) { 262 checkPixelFormat(pixelFormat); 263 return greenOffset[pixelFormat]; 264 } 265 266 private static final int[] greenOffset = { 267 1, 1, 1, 1, 2, 2, 0, 1, 1, 2, 2, -1 268 }; 269 270 271 /** 272 * For the given pixel format, returns the number of bytes that the blue 273 * component is offset from the start of the pixel. For instance, if a pixel 274 * of format <code>TJ.PF_BGRX</code> is stored in <code>char pixel[]</code>, 275 * then the blue component will be 276 * <code>pixel[TJ.getBlueOffset(TJ.PF_BGRX)]</code>. 277 * 278 * @param pixelFormat the pixel format (one of <code>PF_*</code>) 279 * 280 * @return the blue offset for the given pixel format. 281 */ 282 public static int getBlueOffset(int pixelFormat) { 283 checkPixelFormat(pixelFormat); 284 return blueOffset[pixelFormat]; 285 } 286 287 private static final int[] blueOffset = { 288 2, 0, 2, 0, 1, 3, 0, 2, 0, 1, 3, -1 289 }; 290 291 292 /** 293 * The number of JPEG colorspaces 294 */ 295 public static final int NUMCS = 5; 296 /** 297 * RGB colorspace. When compressing the JPEG image, the R, G, and B 298 * components in the source image are reordered into image planes, but no 299 * colorspace conversion or subsampling is performed. RGB JPEG images can be 300 * decompressed to any of the extended RGB pixel formats or grayscale, but 301 * they cannot be decompressed to YUV images. 302 */ 303 public static final int CS_RGB = 0; 304 /** 305 * YCbCr colorspace. YCbCr is not an absolute colorspace but rather a 306 * mathematical transformation of RGB designed solely for storage and 307 * transmission. YCbCr images must be converted to RGB before they can 308 * actually be displayed. In the YCbCr colorspace, the Y (luminance) 309 * component represents the black & white portion of the original image, and 310 * the Cb and Cr (chrominance) components represent the color portion of the 311 * original image. Originally, the analog equivalent of this transformation 312 * allowed the same signal to drive both black & white and color televisions, 313 * but JPEG images use YCbCr primarily because it allows the color data to be 314 * optionally subsampled for the purposes of reducing bandwidth or disk 315 * space. YCbCr is the most common JPEG colorspace, and YCbCr JPEG images 316 * can be compressed from and decompressed to any of the extended RGB pixel 317 * formats or grayscale, or they can be decompressed to YUV planar images. 318 */ 319 public static final int CS_YCbCr = 1; 320 /** 321 * Grayscale colorspace. The JPEG image retains only the luminance data (Y 322 * component), and any color data from the source image is discarded. 323 * Grayscale JPEG images can be compressed from and decompressed to any of 324 * the extended RGB pixel formats or grayscale, or they can be decompressed 325 * to YUV planar images. 326 */ 327 public static final int CS_GRAY = 2; 328 /** 329 * CMYK colorspace. When compressing the JPEG image, the C, M, Y, and K 330 * components in the source image are reordered into image planes, but no 331 * colorspace conversion or subsampling is performed. CMYK JPEG images can 332 * only be decompressed to CMYK pixels. 333 */ 334 public static final int CS_CMYK = 3; 335 /** 336 * YCCK colorspace. YCCK (AKA "YCbCrK") is not an absolute colorspace but 337 * rather a mathematical transformation of CMYK designed solely for storage 338 * and transmission. It is to CMYK as YCbCr is to RGB. CMYK pixels can be 339 * reversibly transformed into YCCK, and as with YCbCr, the chrominance 340 * components in the YCCK pixels can be subsampled without incurring major 341 * perceptual loss. YCCK JPEG images can only be compressed from and 342 * decompressed to CMYK pixels. 343 */ 344 public static final int CS_YCCK = 4; 345 346 347 /** 348 * The uncompressed source/destination image is stored in bottom-up (Windows, 349 * OpenGL) order, not top-down (X11) order. 350 */ 351 public static final int FLAG_BOTTOMUP = 2; 352 353 @Deprecated 354 public static final int FLAG_FORCEMMX = 8; 355 @Deprecated 356 public static final int FLAG_FORCESSE = 16; 357 @Deprecated 358 public static final int FLAG_FORCESSE2 = 32; 359 @Deprecated 360 public static final int FLAG_FORCESSE3 = 128; 361 362 /** 363 * When decompressing an image that was compressed using chrominance 364 * subsampling, use the fastest chrominance upsampling algorithm available in 365 * the underlying codec. The default is to use smooth upsampling, which 366 * creates a smooth transition between neighboring chrominance components in 367 * order to reduce upsampling artifacts in the decompressed image. 368 */ 369 public static final int FLAG_FASTUPSAMPLE = 256; 370 /** 371 * Use the fastest DCT/IDCT algorithm available in the underlying codec. The 372 * default if this flag is not specified is implementation-specific. For 373 * example, the implementation of TurboJPEG for libjpeg[-turbo] uses the fast 374 * algorithm by default when compressing, because this has been shown to have 375 * only a very slight effect on accuracy, but it uses the accurate algorithm 376 * when decompressing, because this has been shown to have a larger effect. 377 */ 378 public static final int FLAG_FASTDCT = 2048; 379 /** 380 * Use the most accurate DCT/IDCT algorithm available in the underlying 381 * codec. The default if this flag is not specified is 382 * implementation-specific. For example, the implementation of TurboJPEG for 383 * libjpeg[-turbo] uses the fast algorithm by default when compressing, 384 * because this has been shown to have only a very slight effect on accuracy, 385 * but it uses the accurate algorithm when decompressing, because this has 386 * been shown to have a larger effect. 387 */ 388 public static final int FLAG_ACCURATEDCT = 4096; 389 390 391 /** 392 * Returns the maximum size of the buffer (in bytes) required to hold a JPEG 393 * image with the given width, height, and level of chrominance subsampling. 394 * 395 * @param width the width (in pixels) of the JPEG image 396 * 397 * @param height the height (in pixels) of the JPEG image 398 * 399 * @param jpegSubsamp the level of chrominance subsampling to be used when 400 * generating the JPEG image (one of {@link TJ TJ.SAMP_*}) 401 * 402 * @return the maximum size of the buffer (in bytes) required to hold a JPEG 403 * image with the given width, height, and level of chrominance subsampling. 404 */ 405 public static native int bufSize(int width, int height, int jpegSubsamp); 406 407 /** 408 * Returns the size of the buffer (in bytes) required to hold a YUV planar 409 * image with the given width, height, and level of chrominance subsampling. 410 * 411 * @param width the width (in pixels) of the YUV image 412 * 413 * @param pad the width of each line in each plane of the image is padded to 414 * the nearest multiple of this number of bytes (must be a power of 2.) 415 * 416 * @param height the height (in pixels) of the YUV image 417 * 418 * @param subsamp the level of chrominance subsampling used in the YUV 419 * image (one of {@link TJ TJ.SAMP_*}) 420 * 421 * @return the size of the buffer (in bytes) required to hold a YUV planar 422 * image with the given width, height, and level of chrominance subsampling. 423 */ 424 public static native int bufSizeYUV(int width, int pad, int height, 425 int subsamp); 426 427 /** 428 * @deprecated Use {@link #bufSizeYUV(int, int, int, int)} instead. 429 */ 430 @Deprecated 431 public static native int bufSizeYUV(int width, int height, int subsamp); 432 433 /** 434 * Returns the size of the buffer (in bytes) required to hold a YUV image 435 * plane with the given parameters. 436 * 437 * @param componentID ID number of the image plane (0 = Y, 1 = U/Cb, 438 * 2 = V/Cr) 439 * 440 * @param width width (in pixels) of the YUV image. NOTE: this is the width 441 * of the whole image, not the plane width. 442 * 443 * @param stride bytes per line in the image plane. 444 * 445 * @param height height (in pixels) of the YUV image. NOTE: this is the 446 * height of the whole image, not the plane height. 447 * 448 * @param subsamp the level of chrominance subsampling used in the YUV 449 * image (one of {@link TJ TJ.SAMP_*}) 450 * 451 * @return the size of the buffer (in bytes) required to hold a YUV planar 452 * image with the given parameters. 453 */ 454 public static native int planeSizeYUV(int componentID, int width, int stride, 455 int height, int subsamp); 456 457 /** 458 * Returns the plane width of a YUV image plane with the given parameters. 459 * Refer to {@link YUVImage YUVImage} for a description of plane width. 460 * 461 * @param componentID ID number of the image plane (0 = Y, 1 = U/Cb, 462 * 2 = V/Cr) 463 * 464 * @param width width (in pixels) of the YUV image 465 * 466 * @param subsamp the level of chrominance subsampling used in the YUV image 467 * (one of {@link TJ TJ.SAMP_*}) 468 * 469 * @return the plane width of a YUV image plane with the given parameters. 470 */ 471 public static native int planeWidth(int componentID, int width, int subsamp); 472 473 /** 474 * Returns the plane height of a YUV image plane with the given parameters. 475 * Refer to {@link YUVImage YUVImage} for a description of plane height. 476 * 477 * @param componentID ID number of the image plane (0 = Y, 1 = U/Cb, 478 * 2 = V/Cr) 479 * 480 * @param height height (in pixels) of the YUV image 481 * 482 * @param subsamp the level of chrominance subsampling used in the YUV image 483 * (one of {@link TJ TJ.SAMP_*}) 484 * 485 * @return the plane height of a YUV image plane with the given parameters. 486 */ 487 public static native int planeHeight(int componentID, int height, 488 int subsamp); 489 490 /** 491 * Returns a list of fractional scaling factors that the JPEG decompressor in 492 * this implementation of TurboJPEG supports. 493 * 494 * @return a list of fractional scaling factors that the JPEG decompressor in 495 * this implementation of TurboJPEG supports. 496 */ 497 public static native TJScalingFactor[] getScalingFactors(); 498 499 static { 500 TJLoader.load(); 501 } 502 503 private static void checkPixelFormat(int pixelFormat) { 504 if (pixelFormat < 0 || pixelFormat >= NUMPF) 505 throw new IllegalArgumentException("Invalid pixel format"); 506 } 507 508 private static void checkSubsampling(int subsamp) { 509 if (subsamp < 0 || subsamp >= NUMSAMP) 510 throw new IllegalArgumentException("Invalid subsampling type"); 511 } 512 513 } -
src/org/libjpegturbo/turbojpeg/TJDecompressor.java
1 /* 2 * Copyright (C)2011-2015 D. R. Commander. All Rights Reserved. 3 * Copyright (C)2015 Viktor Szathmáry. 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 package org.libjpegturbo.turbojpeg; 31 32 import java.awt.image.BufferedImage; 33 import java.awt.image.ComponentSampleModel; 34 import java.awt.image.DataBufferByte; 35 import java.awt.image.DataBufferInt; 36 import java.awt.image.SinglePixelPackedSampleModel; 37 import java.awt.image.WritableRaster; 38 import java.io.Closeable; 39 import java.nio.ByteOrder; 40 41 /** 42 * TurboJPEG decompressor 43 */ 44 public class TJDecompressor implements Closeable { 45 46 private static final String NO_ASSOC_ERROR = 47 "No JPEG image is associated with this instance"; 48 49 /** 50 * Create a TurboJPEG decompresssor instance. 51 * @throws TJException 52 */ 53 public TJDecompressor() throws TJException { 54 init(); 55 } 56 57 /** 58 * Create a TurboJPEG decompressor instance and associate the JPEG source 59 * image stored in <code>jpegImage</code> with the newly created instance. 60 * 61 * @param jpegImage JPEG image buffer (size of the JPEG image is assumed to 62 * be the length of the array.) This buffer is not modified. 63 * @throws TJException 64 */ 65 public TJDecompressor(byte[] jpegImage) throws TJException { 66 init(); 67 setSourceImage(jpegImage, jpegImage.length); 68 } 69 70 /** 71 * Create a TurboJPEG decompressor instance and associate the JPEG source 72 * image of length <code>imageSize</code> bytes stored in 73 * <code>jpegImage</code> with the newly created instance. 74 * 75 * @param jpegImage JPEG image buffer. This buffer is not modified. 76 * @param imageSize size of the JPEG image (in bytes) 77 * @throws TJException 78 */ 79 public TJDecompressor(byte[] jpegImage, int imageSize) throws TJException { 80 init(); 81 setSourceImage(jpegImage, imageSize); 82 } 83 84 /** 85 * Associate the JPEG image of length <code>imageSize</code> bytes stored in 86 * <code>jpegImage</code> with this decompressor instance. This image will 87 * be used as the source image for subsequent decompress operations. 88 * 89 * @param jpegImage JPEG image buffer. This buffer is not modified. 90 * @param imageSize size of the JPEG image (in bytes) 91 * @throws TJException 92 */ 93 public void setSourceImage(byte[] jpegImage, int imageSize) 94 throws TJException { 95 if (jpegImage == null || imageSize < 1) 96 throw new IllegalArgumentException("Invalid argument in setSourceImage()"); 97 jpegBuf = jpegImage; 98 jpegBufSize = imageSize; 99 decompressHeader(jpegBuf, jpegBufSize); 100 } 101 102 /** 103 * Returns the width of the source image (JPEG or YUV) associated with this 104 * decompressor instance. 105 * 106 * @return the width of the source image (JPEG or YUV) associated with this 107 * decompressor instance. 108 */ 109 public int getWidth() { 110 if (jpegWidth < 1) 111 throw new IllegalStateException(NO_ASSOC_ERROR); 112 return jpegWidth; 113 } 114 115 /** 116 * Returns the height of the source image (JPEG or YUV) associated with this 117 * decompressor instance. 118 * 119 * @return the height of the source image (JPEG or YUV) associated with this 120 * decompressor instance. 121 */ 122 public int getHeight() { 123 if (jpegHeight < 1) 124 throw new IllegalStateException(NO_ASSOC_ERROR); 125 return jpegHeight; 126 } 127 128 /** 129 * Returns the level of chrominance subsampling used in the source image 130 * (JPEG or YUV) associated with this decompressor instance. See 131 * {@link TJ#SAMP_444 TJ.SAMP_*}. 132 * 133 * @return the level of chrominance subsampling used in the source image 134 * (JPEG or YUV) associated with this decompressor instance. 135 */ 136 public int getSubsamp() { 137 if (jpegSubsamp < 0) 138 throw new IllegalStateException(NO_ASSOC_ERROR); 139 if (jpegSubsamp >= TJ.NUMSAMP) 140 throw new IllegalStateException("JPEG header information is invalid"); 141 return jpegSubsamp; 142 } 143 144 /** 145 * Returns the colorspace used in the source image (JPEG or YUV) associated 146 * with this decompressor instance. See {@link TJ#CS_RGB TJ.CS_*}. If the 147 * source image is YUV, then this always returns {@link TJ#CS_YCbCr}. 148 * 149 * @return the colorspace used in the source image (JPEG or YUV) associated 150 * with this decompressor instance. 151 */ 152 public int getColorspace() { 153 if (jpegColorspace < 0) 154 throw new IllegalStateException(NO_ASSOC_ERROR); 155 if (jpegColorspace >= TJ.NUMCS) 156 throw new IllegalStateException("JPEG header information is invalid"); 157 return jpegColorspace; 158 } 159 160 /** 161 * Returns the JPEG image buffer associated with this decompressor instance. 162 * 163 * @return the JPEG image buffer associated with this decompressor instance. 164 */ 165 public byte[] getJPEGBuf() { 166 if (jpegBuf == null) 167 throw new IllegalStateException(NO_ASSOC_ERROR); 168 return jpegBuf; 169 } 170 171 /** 172 * Returns the size of the JPEG image (in bytes) associated with this 173 * decompressor instance. 174 * 175 * @return the size of the JPEG image (in bytes) associated with this 176 * decompressor instance. 177 */ 178 public int getJPEGSize() { 179 if (jpegBufSize < 1) 180 throw new IllegalStateException(NO_ASSOC_ERROR); 181 return jpegBufSize; 182 } 183 184 /** 185 * Returns the width of the largest scaled-down image that the TurboJPEG 186 * decompressor can generate without exceeding the desired image width and 187 * height. 188 * 189 * @param desiredWidth desired width (in pixels) of the decompressed image. 190 * Setting this to 0 is the same as setting it to the width of the JPEG image 191 * (in other words, the width will not be considered when determining the 192 * scaled image size.) 193 * 194 * @param desiredHeight desired height (in pixels) of the decompressed image. 195 * Setting this to 0 is the same as setting it to the height of the JPEG 196 * image (in other words, the height will not be considered when determining 197 * the scaled image size.) 198 * 199 * @return the width of the largest scaled-down image that the TurboJPEG 200 * decompressor can generate without exceeding the desired image width and 201 * height. 202 */ 203 public int getScaledWidth(int desiredWidth, int desiredHeight) { 204 if (jpegWidth < 1 || jpegHeight < 1) 205 throw new IllegalStateException(NO_ASSOC_ERROR); 206 if (desiredWidth < 0 || desiredHeight < 0) 207 throw new IllegalArgumentException("Invalid argument in getScaledWidth()"); 208 TJScalingFactor[] sf = TJ.getScalingFactors(); 209 if (desiredWidth == 0) 210 desiredWidth = jpegWidth; 211 if (desiredHeight == 0) 212 desiredHeight = jpegHeight; 213 int scaledWidth = jpegWidth, scaledHeight = jpegHeight; 214 for (int i = 0; i < sf.length; i++) { 215 scaledWidth = sf[i].getScaled(jpegWidth); 216 scaledHeight = sf[i].getScaled(jpegHeight); 217 if (scaledWidth <= desiredWidth && scaledHeight <= desiredHeight) 218 break; 219 } 220 if (scaledWidth > desiredWidth || scaledHeight > desiredHeight) 221 throw new IllegalArgumentException("Could not scale down to desired image dimensions"); 222 return scaledWidth; 223 } 224 225 /** 226 * Returns the height of the largest scaled-down image that the TurboJPEG 227 * decompressor can generate without exceeding the desired image width and 228 * height. 229 * 230 * @param desiredWidth desired width (in pixels) of the decompressed image. 231 * Setting this to 0 is the same as setting it to the width of the JPEG image 232 * (in other words, the width will not be considered when determining the 233 * scaled image size.) 234 * 235 * @param desiredHeight desired height (in pixels) of the decompressed image. 236 * Setting this to 0 is the same as setting it to the height of the JPEG 237 * image (in other words, the height will not be considered when determining 238 * the scaled image size.) 239 * 240 * @return the height of the largest scaled-down image that the TurboJPEG 241 * decompressor can generate without exceeding the desired image width and 242 * height. 243 */ 244 public int getScaledHeight(int desiredWidth, int desiredHeight) { 245 if (jpegWidth < 1 || jpegHeight < 1) 246 throw new IllegalStateException(NO_ASSOC_ERROR); 247 if (desiredWidth < 0 || desiredHeight < 0) 248 throw new IllegalArgumentException("Invalid argument in getScaledHeight()"); 249 TJScalingFactor[] sf = TJ.getScalingFactors(); 250 if (desiredWidth == 0) 251 desiredWidth = jpegWidth; 252 if (desiredHeight == 0) 253 desiredHeight = jpegHeight; 254 int scaledWidth = jpegWidth, scaledHeight = jpegHeight; 255 for (int i = 0; i < sf.length; i++) { 256 scaledWidth = sf[i].getScaled(jpegWidth); 257 scaledHeight = sf[i].getScaled(jpegHeight); 258 if (scaledWidth <= desiredWidth && scaledHeight <= desiredHeight) 259 break; 260 } 261 if (scaledWidth > desiredWidth || scaledHeight > desiredHeight) 262 throw new IllegalArgumentException("Could not scale down to desired image dimensions"); 263 return scaledHeight; 264 } 265 266 /** 267 * Decompress the JPEG source image or decode the YUV source image associated 268 * with this decompressor instance and output a grayscale, RGB, or CMYK image 269 * to the given destination buffer. 270 * 271 * @param dstBuf buffer that will receive the decompressed/decoded image. 272 * If the source image is a JPEG image, then this buffer should normally be 273 * <code>pitch * scaledHeight</code> bytes in size, where 274 * <code>scaledHeight</code> can be determined by calling <code> 275 * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegHeight) 276 * </code> with one of the scaling factors returned from {@link 277 * TJ#getScalingFactors} or by calling {@link #getScaledHeight}. If the 278 * source image is a YUV image, then this buffer should normally be 279 * <code>pitch * height</code> bytes in size, where <code>height</code> is 280 * the height of the YUV image. However, the buffer may also be larger than 281 * the dimensions of the source image, in which case the <code>x</code>, 282 * <code>y</code>, and <code>pitch</code> parameters can be used to specify 283 * the region into which the source image should be decompressed/decoded. 284 * 285 * @param x x offset (in pixels) of the region in the destination image into 286 * which the source image should be decompressed/decoded 287 * 288 * @param y y offset (in pixels) of the region in the destination image into 289 * which the source image should be decompressed/decoded 290 * 291 * @param desiredWidth If the source image is a JPEG image, then this 292 * specifies the desired width (in pixels) of the decompressed image (or 293 * image region.) If the desired destination image dimensions are different 294 * than the source image dimensions, then TurboJPEG will use scaling in the 295 * JPEG decompressor to generate the largest possible image that will fit 296 * within the desired dimensions. Setting this to 0 is the same as setting 297 * it to the width of the JPEG image (in other words, the width will not be 298 * considered when determining the scaled image size.) This parameter is 299 * ignored if the source image is a YUV image. 300 * 301 * @param pitch bytes per line of the destination image. Normally, this 302 * should be set to <code>scaledWidth * TJ.pixelSize(pixelFormat)</code> if 303 * the destination image is unpadded, but you can use this to, for instance, 304 * pad each line of the destination image to a 4-byte boundary or to 305 * decompress/decode the source image into a region of a larger image. NOTE: 306 * if the source image is a JPEG image, then <code>scaledWidth</code> can be 307 * determined by calling <code> 308 * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth) 309 * </code> or by calling {@link #getScaledWidth}. If the source image is a 310 * YUV image, then <code>scaledWidth</code> is the width of the YUV image. 311 * Setting this parameter to 0 is the equivalent of setting it to 312 * <code>scaledWidth * TJ.pixelSize(pixelFormat)</code>. 313 * 314 * @param desiredHeight If the source image is a JPEG image, then this 315 * specifies the desired height (in pixels) of the decompressed image (or 316 * image region.) If the desired destination image dimensions are different 317 * than the source image dimensions, then TurboJPEG will use scaling in the 318 * JPEG decompressor to generate the largest possible image that will fit 319 * within the desired dimensions. Setting this to 0 is the same as setting 320 * it to the height of the JPEG image (in other words, the height will not be 321 * considered when determining the scaled image size.) This parameter is 322 * ignored if the source image is a YUV image. 323 * 324 * @param pixelFormat pixel format of the decompressed/decoded image (one of 325 * {@link TJ#PF_RGB TJ.PF_*}) 326 * 327 * @param flags the bitwise OR of one or more of 328 * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} 329 * 330 * @throws TJException 331 */ 332 public void decompress(byte[] dstBuf, int x, int y, int desiredWidth, 333 int pitch, int desiredHeight, int pixelFormat, 334 int flags) throws TJException { 335 if (jpegBuf == null) 336 throw new IllegalStateException(NO_ASSOC_ERROR); 337 if (dstBuf == null || x < 0 || y < 0 || pitch < 0 || 338 pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0) 339 throw new IllegalArgumentException("Invalid argument in decompress()"); 340 if (x > 0 || y > 0) 341 decompress(jpegBuf, jpegBufSize, dstBuf, x, y, desiredWidth, pitch, 342 desiredHeight, pixelFormat, flags); 343 else 344 decompress(jpegBuf, jpegBufSize, dstBuf, 0, 0, desiredWidth, pitch, 345 desiredHeight, pixelFormat, flags); 346 } 347 348 /** 349 * Decompress the JPEG source image associated with this decompressor 350 * instance and return a buffer containing the decompressed image. 351 * 352 * @param desiredWidth see 353 * {@link #decompress(byte[], int, int, int, int, int, int, int)} 354 * for description 355 * 356 * @param pitch see 357 * {@link #decompress(byte[], int, int, int, int, int, int, int)} 358 * for description 359 * 360 * @param desiredHeight see 361 * {@link #decompress(byte[], int, int, int, int, int, int, int)} 362 * for description 363 * 364 * @param pixelFormat pixel format of the decompressed image (one of 365 * {@link TJ#PF_RGB TJ.PF_*}) 366 * 367 * @param flags the bitwise OR of one or more of 368 * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} 369 * 370 * @return a buffer containing the decompressed image. 371 * @throws TJException 372 */ 373 public byte[] decompress(int desiredWidth, int pitch, int desiredHeight, 374 int pixelFormat, int flags) throws TJException { 375 if (pitch < 0 || desiredWidth < 0 || desiredHeight < 0 || 376 pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0) 377 throw new IllegalArgumentException("Invalid argument in decompress()"); 378 int pixelSize = TJ.getPixelSize(pixelFormat); 379 int scaledWidth = getScaledWidth(desiredWidth, desiredHeight); 380 int scaledHeight = getScaledHeight(desiredWidth, desiredHeight); 381 if (pitch == 0) 382 pitch = scaledWidth * pixelSize; 383 byte[] buf = new byte[pitch * scaledHeight]; 384 decompress(buf, 0, 0, desiredWidth, pitch, desiredHeight, pixelFormat, flags); 385 return buf; 386 } 387 388 /** 389 * Decompress the JPEG source image associated with this decompressor 390 * instance and output a grayscale, RGB, or CMYK image to the given 391 * destination buffer. 392 * 393 * @param dstBuf buffer that will receive the decompressed/decoded image. 394 * If the source image is a JPEG image, then this buffer should normally be 395 * <code>stride * scaledHeight</code> pixels in size, where 396 * <code>scaledHeight</code> can be determined by calling <code> 397 * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegHeight) 398 * </code> with one of the scaling factors returned from {@link 399 * TJ#getScalingFactors} or by calling {@link #getScaledHeight}. If the 400 * source image is a YUV image, then this buffer should normally be 401 * <code>stride * height</code> pixels in size, where <code>height</code> is 402 * the height of the YUV image. However, the buffer may also be larger than 403 * the dimensions of the JPEG image, in which case the <code>x</code>, 404 * <code>y</code>, and <code>stride</code> parameters can be used to specify 405 * the region into which the source image should be decompressed. 406 * 407 * @param x x offset (in pixels) of the region in the destination image into 408 * which the source image should be decompressed/decoded 409 * 410 * @param y y offset (in pixels) of the region in the destination image into 411 * which the source image should be decompressed/decoded 412 * 413 * @param desiredWidth If the source image is a JPEG image, then this 414 * specifies the desired width (in pixels) of the decompressed image (or 415 * image region.) If the desired destination image dimensions are different 416 * than the source image dimensions, then TurboJPEG will use scaling in the 417 * JPEG decompressor to generate the largest possible image that will fit 418 * within the desired dimensions. Setting this to 0 is the same as setting 419 * it to the width of the JPEG image (in other words, the width will not be 420 * considered when determining the scaled image size.) This parameter is 421 * ignored if the source image is a YUV image. 422 * 423 * @param stride pixels per line of the destination image. Normally, this 424 * should be set to <code>scaledWidth</code>, but you can use this to, for 425 * instance, decompress the JPEG image into a region of a larger image. 426 * NOTE: if the source image is a JPEG image, then <code>scaledWidth</code> 427 * can be determined by calling <code> 428 * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth) 429 * </code> or by calling {@link #getScaledWidth}. If the source image is a 430 * YUV image, then <code>scaledWidth</code> is the width of the YUV image. 431 * Setting this parameter to 0 is the equivalent of setting it to 432 * <code>scaledWidth</code>. 433 * 434 * @param desiredHeight If the source image is a JPEG image, then this 435 * specifies the desired height (in pixels) of the decompressed image (or 436 * image region.) If the desired destination image dimensions are different 437 * than the source image dimensions, then TurboJPEG will use scaling in the 438 * JPEG decompressor to generate the largest possible image that will fit 439 * within the desired dimensions. Setting this to 0 is the same as setting 440 * it to the height of the JPEG image (in other words, the height will not be 441 * considered when determining the scaled image size.) This parameter is 442 * ignored if the source image is a YUV image. 443 * 444 * @param pixelFormat pixel format of the decompressed image (one of 445 * {@link TJ#PF_RGB TJ.PF_*}) 446 * 447 * @param flags the bitwise OR of one or more of 448 * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} 449 * 450 * @throws TJException 451 */ 452 public void decompress(int[] dstBuf, int x, int y, int desiredWidth, 453 int stride, int desiredHeight, int pixelFormat, 454 int flags) throws TJException { 455 if (jpegBuf == null) 456 throw new IllegalStateException(NO_ASSOC_ERROR); 457 if (dstBuf == null || x < 0 || y < 0 || stride < 0 || 458 pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0) 459 throw new IllegalArgumentException("Invalid argument in decompress()"); 460 decompress(jpegBuf, jpegBufSize, dstBuf, x, y, desiredWidth, stride, 461 desiredHeight, pixelFormat, flags); 462 } 463 464 /** 465 * Decompress the JPEG source image or decode the YUV source image associated 466 * with this decompressor instance and output a decompressed/decoded image to 467 * the given <code>BufferedImage</code> instance. 468 * 469 * @param dstImage a <code>BufferedImage</code> instance that will receive 470 * the decompressed/decoded image. If the source image is a JPEG image, then 471 * the width and height of the <code>BufferedImage</code> instance must match 472 * one of the scaled image sizes that TurboJPEG is capable of generating from 473 * the JPEG image. If the source image is a YUV image, then the width and 474 * height of the <code>BufferedImage</code> instance must match the width and 475 * height of the YUV image. 476 * 477 * @param flags the bitwise OR of one or more of 478 * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} 479 * 480 * @throws TJException 481 */ 482 public void decompress(BufferedImage dstImage, int flags) throws TJException { 483 if (dstImage == null || flags < 0) 484 throw new IllegalArgumentException("Invalid argument in decompress()"); 485 int desiredWidth = dstImage.getWidth(); 486 int desiredHeight = dstImage.getHeight(); 487 int scaledWidth, scaledHeight; 488 489 scaledWidth = getScaledWidth(desiredWidth, desiredHeight); 490 scaledHeight = getScaledHeight(desiredWidth, desiredHeight); 491 if (scaledWidth != desiredWidth || scaledHeight != desiredHeight) 492 throw new IllegalArgumentException("BufferedImage dimensions do not match one of the scaled image sizes that TurboJPEG is capable of generating."); 493 int pixelFormat; boolean intPixels = false; 494 if (byteOrder == null) 495 byteOrder = ByteOrder.nativeOrder(); 496 switch(dstImage.getType()) { 497 case BufferedImage.TYPE_3BYTE_BGR: 498 pixelFormat = TJ.PF_BGR; break; 499 case BufferedImage.TYPE_4BYTE_ABGR: 500 case BufferedImage.TYPE_4BYTE_ABGR_PRE: 501 pixelFormat = TJ.PF_XBGR; break; 502 case BufferedImage.TYPE_BYTE_GRAY: 503 pixelFormat = TJ.PF_GRAY; break; 504 case BufferedImage.TYPE_INT_BGR: 505 if (byteOrder == ByteOrder.BIG_ENDIAN) 506 pixelFormat = TJ.PF_XBGR; 507 else 508 pixelFormat = TJ.PF_RGBX; 509 intPixels = true; break; 510 case BufferedImage.TYPE_INT_RGB: 511 if (byteOrder == ByteOrder.BIG_ENDIAN) 512 pixelFormat = TJ.PF_XRGB; 513 else 514 pixelFormat = TJ.PF_BGRX; 515 intPixels = true; break; 516 case BufferedImage.TYPE_INT_ARGB: 517 case BufferedImage.TYPE_INT_ARGB_PRE: 518 if (byteOrder == ByteOrder.BIG_ENDIAN) 519 pixelFormat = TJ.PF_ARGB; 520 else 521 pixelFormat = TJ.PF_BGRA; 522 intPixels = true; break; 523 default: 524 throw new IllegalArgumentException("Unsupported BufferedImage format"); 525 } 526 WritableRaster wr = dstImage.getRaster(); 527 if (intPixels) { 528 SinglePixelPackedSampleModel sm = 529 (SinglePixelPackedSampleModel)dstImage.getSampleModel(); 530 int stride = sm.getScanlineStride(); 531 DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); 532 int[] buf = db.getData(); 533 if (jpegBuf == null) 534 throw new IllegalStateException(NO_ASSOC_ERROR); 535 decompress(jpegBuf, jpegBufSize, buf, 0, 0, scaledWidth, stride, 536 scaledHeight, pixelFormat, flags); 537 } else { 538 ComponentSampleModel sm = 539 (ComponentSampleModel)dstImage.getSampleModel(); 540 int pixelSize = sm.getPixelStride(); 541 if (pixelSize != TJ.getPixelSize(pixelFormat)) 542 throw new IllegalArgumentException("Inconsistency between pixel format and pixel size in BufferedImage"); 543 int pitch = sm.getScanlineStride(); 544 DataBufferByte db = (DataBufferByte)wr.getDataBuffer(); 545 byte[] buf = db.getData(); 546 decompress(buf, 0, 0, scaledWidth, pitch, scaledHeight, pixelFormat, 547 flags); 548 } 549 } 550 551 /** 552 * Decompress the JPEG source image or decode the YUV source image associated 553 * with this decompressor instance and return a <code>BufferedImage</code> 554 * instance containing the decompressed/decoded image. 555 * 556 * @param desiredWidth see 557 * {@link #decompress(byte[], int, int, int, int, int, int, int)} for 558 * description 559 * 560 * @param desiredHeight see 561 * {@link #decompress(byte[], int, int, int, int, int, int, int)} for 562 * description 563 * 564 * @param bufferedImageType the image type of the <code>BufferedImage</code> 565 * instance that will be created (for instance, 566 * <code>BufferedImage.TYPE_INT_RGB</code>) 567 * 568 * @param flags the bitwise OR of one or more of 569 * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} 570 * 571 * @return a <code>BufferedImage</code> instance containing the 572 * decompressed/decoded image. 573 * @throws TJException 574 */ 575 public BufferedImage decompress(int desiredWidth, int desiredHeight, 576 int bufferedImageType, int flags) 577 throws TJException { 578 if (desiredWidth < 0 || desiredHeight < 0 || flags < 0) 579 throw new IllegalArgumentException("Invalid argument in decompress()"); 580 int scaledWidth = getScaledWidth(desiredWidth, desiredHeight); 581 int scaledHeight = getScaledHeight(desiredWidth, desiredHeight); 582 BufferedImage img = new BufferedImage(scaledWidth, scaledHeight, 583 bufferedImageType); 584 decompress(img, flags); 585 return img; 586 } 587 588 /** 589 * Free the native structures associated with this decompressor instance. 590 */ 591 @Override 592 public void close() throws TJException { 593 if (handle != 0) 594 destroy(); 595 } 596 597 @Override 598 protected void finalize() throws Throwable { 599 try { 600 close(); 601 } catch(TJException e) { 602 } finally { 603 super.finalize(); 604 } 605 } 606 607 private native void init() throws TJException; 608 609 private native void destroy() throws TJException; 610 611 private native void decompressHeader(byte[] srcBuf, int size) 612 throws TJException; 613 614 private native void decompress(byte[] srcBuf, int size, byte[] dstBuf, int x, 615 int y, int desiredWidth, int pitch, int desiredHeight, int pixelFormat, 616 int flags) throws TJException; 617 618 private native void decompress(byte[] srcBuf, int size, int[] dstBuf, int x, 619 int y, int desiredWidth, int stride, int desiredHeight, int pixelFormat, 620 int flags) throws TJException; 621 622 static { 623 TJLoader.load(); 624 } 625 626 protected long handle = 0; 627 protected byte[] jpegBuf = null; 628 protected int jpegBufSize = 0; 629 protected int jpegWidth = 0; 630 protected int jpegHeight = 0; 631 protected int jpegSubsamp = -1; 632 protected int jpegColorspace = -1; 633 private ByteOrder byteOrder = null; 634 } -
src/org/libjpegturbo/turbojpeg/TJException.java
1 /* 2 * Copyright (C)2015 Viktor Szathmáry. 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 package org.libjpegturbo.turbojpeg; 30 31 import java.io.IOException; 32 33 public class TJException extends IOException { 34 35 private static final long serialVersionUID = 1L; 36 37 public TJException() { 38 super(); 39 } 40 41 public TJException(String message, Throwable cause) { 42 super(message, cause); 43 } 44 45 public TJException(String message) { 46 super(message); 47 } 48 49 public TJException(Throwable cause) { 50 super(cause); 51 } 52 53 } -
src/org/libjpegturbo/turbojpeg/TJScalingFactor.java
1 /* 2 * Copyright (C)2011 D. R. Commander. All Rights Reserved. 3 * Copyright (C)2015 Viktor Szathmáry. 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 package org.libjpegturbo.turbojpeg; 31 32 /** 33 * Fractional scaling factor 34 */ 35 public class TJScalingFactor { 36 37 public TJScalingFactor(int num, int denom) { 38 if (num < 1 || denom < 1) 39 throw new IllegalArgumentException("Numerator and denominator must be >= 1"); 40 this.num = num; 41 this.denom = denom; 42 } 43 44 /** 45 * Returns numerator 46 * 47 * @return numerator 48 */ 49 public int getNum() { 50 return num; 51 } 52 53 /** 54 * Returns denominator 55 * 56 * @return denominator 57 */ 58 public int getDenom() { 59 return denom; 60 } 61 62 /** 63 * Returns the scaled value of <code>dimension</code>. This function 64 * performs the integer equivalent of 65 * <code>ceil(dimension * scalingFactor)</code>. 66 * 67 * @return the scaled value of <code>dimension</code>. 68 */ 69 public int getScaled(int dimension) { 70 return (dimension * num + denom - 1) / denom; 71 } 72 73 /** 74 * Returns true or false, depending on whether this instance and 75 * <code>other</code> have the same numerator and denominator. 76 * 77 * @return true or false, depending on whether this instance and 78 * <code>other</code> have the same numerator and denominator. 79 */ 80 @Override 81 public boolean equals(Object other) { 82 return (other instanceof TJScalingFactor) && 83 this.num == ((TJScalingFactor)other).num && 84 this.denom == ((TJScalingFactor)other).denom; 85 } 86 87 @Override 88 public int hashCode() { 89 return super.hashCode(); 90 } 91 92 /** 93 * Returns true or false, depending on whether this instance is equal to 94 * 1/1. 95 * 96 * @return true or false, depending on whether this instance is equal to 97 * 1/1. 98 */ 99 public boolean isOne() { 100 return num == 1 && denom == 1; 101 } 102 103 /** 104 * Numerator 105 */ 106 private int num = 1; 107 108 /** 109 * Denominator 110 */ 111 private int denom = 1; 112 }