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 cmuelle8, 7 years ago)

josm-large-jpeg-scaled-loading-through-jni-turbojpeg-to-workaround-oom-experimental.patch (update with fix for ticket:15625)

  • src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java

     
    2222import java.awt.geom.AffineTransform;
    2323import java.awt.geom.Rectangle2D;
    2424import java.awt.image.BufferedImage;
     25import java.awt.image.ImageObserver;
    2526import java.io.File;
     27import java.io.FileInputStream;
    2628
    2729import javax.swing.JComponent;
    2830import javax.swing.SwingUtilities;
    2931
     32import org.libjpegturbo.turbojpeg.TJDecompressor;
     33import org.libjpegturbo.turbojpeg.TJException;
    3034import org.openstreetmap.josm.data.preferences.BooleanProperty;
    3135import org.openstreetmap.josm.data.preferences.DoubleProperty;
    3236import org.openstreetmap.josm.spi.preferences.Config;
     
    112116    public static class VisRect extends Rectangle {
    113117        private final Rectangle init;
    114118
     119        /** set when this {@code VisRect} is updated by a mouse drag operation and
     120         * unset on mouse release **/
     121        public boolean isDragUpdate;
     122
    115123        /**
    116124         * Constructs a new {@code VisRect}.
    117125         * @param     x the specified X coordinate
     
    124132            init = new Rectangle(this);
    125133        }
    126134
     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         */
    127143        public VisRect(int x, int y, int width, int height, VisRect peer) {
    128144            super(x, y, width, height);
    129145            init = peer.init;
     
    145161            this(0, 0, 0, 0);
    146162        }
    147163
     164        @SuppressWarnings("javadoc")
    148165        public boolean isFullView() {
    149166            return init.equals(this);
    150167        }
    151168
     169        @SuppressWarnings("javadoc")
    152170        public boolean isFullView1D() {
    153171            return (init.x == x && init.width == width)
    154172                || (init.y == y && init.height == height);
    155173        }
    156174
     175        @SuppressWarnings("javadoc")
    157176        public void reset() {
    158177            setBounds(init);
    159178        }
    160179
     180        @SuppressWarnings("javadoc")
    161181        public void checkRectPos() {
    162182            if (x < 0) {
    163183                x = 0;
     
    173193            }
    174194        }
    175195
     196        @SuppressWarnings("javadoc")
    176197        public void checkRectSize() {
    177198            if (width > init.width) {
    178199                width = init.width;
     
    182203            }
    183204        }
    184205
     206        @SuppressWarnings("javadoc")
    185207        public void checkPointInside(Point p) {
    186208            if (p.x < x) {
    187209                p.x = x;
     
    199221    }
    200222
    201223    /** The thread that reads the images. */
    202     private class LoadImageRunnable implements Runnable {
     224    private class LoadImageRunnable implements Runnable, ImageObserver {
    203225
    204226        private final File file;
    205227        private final int orientation;
     228        private int width;
     229        private int height;
    206230
    207231        LoadImageRunnable(File file, Integer orientation) {
    208232            this.file = file;
     
    210234        }
    211235
    212236        @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
    213251        public void run() {
    214252            Image img = Toolkit.getDefaultToolkit().createImage(file.getPath());
    215             tracker.addImage(img, 1);
    216253
    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                    }
    223294                }
     295                if (tracker.isErrorID(1)) {
     296                    img = null;
     297                    System.gc();
     298                }
     299            } else {
     300                img = null;
     301            }
     302
     303            if (img == null) {
    224304                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;
    229366                }
    230367            }
    231368
    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;
    235372            }
    236373
    237374            synchronized (ImageDisplay.this) {
     
    241378                    return;
    242379                }
    243380
    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;
    251383                    if (ExifReader.orientationNeedsCorrection(orientation)) {
    252                         final int hh, ww;
    253384                        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;
    259388                        }
    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));
    262392                        final Graphics2D g = rot.createGraphics();
    263                         g.drawImage(image, xform, null);
     393                        g.drawImage(img, xform, null);
    264394                        g.dispose();
    265 
    266                         visibleRect.setSize(ww, hh);
    267                         image.flush();
    268                         ImageDisplay.this.image = rot;
     395                        img.flush();
     396                        img = rot;
    269397                    }
     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);
    270407                }
    271408
    272409                selectedRect = null;
    273                 errorLoading = error;
     410                errorLoading = (img == null);
    274411            }
    275412            tracker.removeImage(img);
    276413            ImageDisplay.this.repaint();
     
    473610
    474611            if (mouseIsDragging(e)) {
    475612                Point p = comp2imgCoord(visibleRect, e.getX(), e.getY(), getSize());
     613                visibleRect.isDragUpdate = true;
    476614                visibleRect.x += mousePointInImg.x - p.x;
    477615                visibleRect.y += mousePointInImg.y - p.y;
    478616                visibleRect.checkRectPos();
     
    503641
    504642        @Override
    505643        public void mouseReleased(MouseEvent e) {
    506             if (!mouseIsZoomSelecting(e) || selectedRect == null)
    507                 return;
    508 
    509644            File file;
    510645            Image image;
    511646
     
    514649                image = ImageDisplay.this.image;
    515650            }
    516651
    517             if (image == null) {
     652            if (image == null)
    518653                return;
     654
     655            if (mouseIsDragging(e)) {
     656                visibleRect.isDragUpdate = false;
    519657            }
    520658
    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;
    523662
    524             // Check that the zoom doesn't exceed MAX_ZOOM:1
    525             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                }
    531670
    532             // Set the same ratio for the visible rectangle and the display area
    533             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                }
    540679
    541             // Keep the center of the selection
    542             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                }
    548687
    549             selectedRect.checkRectSize();
    550             selectedRect.checkRectPos();
     688                selectedRect.checkRectSize();
     689                selectedRect.checkRectPos();
     690            }
    551691
    552692            synchronized (ImageDisplay.this) {
    553693                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                    }
    555700                }
    556701            }
    557             selectedRect = null;
    558702            ImageDisplay.this.repaint();
    559703        }
    560704
     
    586730        preferenceChanged(null);
    587731    }
    588732
     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     */
    589738    public void setImage(File file, Integer orientation) {
    590739        synchronized (this) {
    591740            this.file = file;
     
    650799            Rectangle target = calculateDrawImageRectangle(visibleRect, size);
    651800            double scale = target.width / (double) r.width; // pixel ratio is 1:1
    652801
    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;
    656808
    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                }
    663823            } else {
    664824                // if target and r cause drawImage to scale image region to a tmp buffer exceeding
    665825                // its bounds, it will silently fail; crop with r first in such cases
     
    797957        return new VisRect(x + compRect.x, y + compRect.y, w, h, imgRect);
    798958    }
    799959
     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     */
    800965    public void zoomBestFitOrOne() {
    801966        File file;
    802967        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
     29package org.libjpegturbo.turbojpeg;
     30
     31import java.util.Arrays;
     32import java.util.List;
     33
     34import org.openstreetmap.josm.tools.Logging;
     35
     36final 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
     30package org.libjpegturbo.turbojpeg;
     31
     32/**
     33 * TurboJPEG utility class (cannot be instantiated)
     34 */
     35public 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
     30package org.libjpegturbo.turbojpeg;
     31
     32import java.awt.image.BufferedImage;
     33import java.awt.image.ComponentSampleModel;
     34import java.awt.image.DataBufferByte;
     35import java.awt.image.DataBufferInt;
     36import java.awt.image.SinglePixelPackedSampleModel;
     37import java.awt.image.WritableRaster;
     38import java.io.Closeable;
     39import java.nio.ByteOrder;
     40
     41/**
     42 * TurboJPEG decompressor
     43 */
     44public 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
     29package org.libjpegturbo.turbojpeg;
     30
     31import java.io.IOException;
     32
     33public 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
     30package org.libjpegturbo.turbojpeg;
     31
     32/**
     33 * Fractional scaling factor
     34 */
     35public 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}