1 /*
   2  * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.javafx.iio.ios;
  27 
  28 import com.sun.glass.utils.NativeLibLoader;
  29 import com.sun.javafx.iio.common.*;
  30 import com.sun.javafx.iio.ImageFrame;
  31 import com.sun.javafx.iio.ImageMetadata;
  32 import com.sun.javafx.iio.ImageStorage.ImageType;
  33 import com.sun.javafx.iio.common.ImageLoaderImpl;
  34 import com.sun.javafx.iio.common.ImageTools;
  35 
  36 import java.io.IOException;
  37 import java.io.InputStream;
  38 import java.nio.ByteBuffer;
  39 import java.security.AccessController;
  40 import java.security.PrivilegedAction;
  41 
  42 import java.util.Map;
  43 import java.util.HashMap;
  44 
  45 import java.net.URL;
  46 import java.net.MalformedURLException;
  47 
  48 /**
  49  * A loader for images on iOS platform.
  50  */
  51 public class IosImageLoader extends ImageLoaderImpl {
  52 
  53     /** These constants must match with those in native */
  54     public static final int GRAY = 0;
  55     public static final int GRAY_ALPHA = 1;
  56     public static final int GRAY_ALPHA_PRE = 2;
  57     public static final int PALETTE = 3;
  58     public static final int PALETTE_ALPHA = 4;
  59     public static final int PALETTE_ALPHA_PRE = 5;
  60     public static final int PALETTE_TRANS = 6;
  61     public static final int RGB = 7;
  62     public static final int RGBA = 8;
  63     public static final int RGBA_PRE = 9;
  64 
  65     private static final Map<Integer, ImageType> colorSpaceMapping = new HashMap<Integer, ImageType>();
  66 
  67     /** Pointer to the native loader */
  68     private long structPointer;
  69 
  70     /** Set by native code */
  71     private int inWidth;
  72     private int inHeight;
  73     private int nImages;
  74 
  75     private boolean isDisposed = false;
  76 
  77     private int delayTime; // applicable to animated images only
  78     private int loopCount; // applicable to animated images only
  79 
  80     /***************************** Native Loader methods ******************************************/
  81 
  82     /** Set up static method IDs for calls back to Java. */
  83     private static native void initNativeLoading();
  84 
  85     /** Create a loader and buffer data from the InputStream. Report progress if requested. */
  86     private native long loadImage(final InputStream stream, boolean reportProgress) throws IOException;
  87 
  88     /** Create a loader for the given URL. Report progress if requested. */
  89     private native long loadImageFromURL(final String url, boolean reportProgress) throws IOException;
  90 
  91     /** Set native image size */
  92     private native void resizeImage(long structPointer, int width, int height);
  93 
  94     /** Return a buffer with decompressed image data */
  95     private native byte[] getImageBuffer(long structPointer, int imageIndex);
  96 
  97     /** Return the number of color components */
  98     private native int getNumberOfComponents(long structPointer);
  99 
 100     /** Return image color space model code */
 101     private native int getColorSpaceCode(long structPointer);
 102 
 103     /** Return image duration for animated images */
 104     private native int getDelayTime(long structPointer);
 105 
 106     /** Destroy a loader. */
 107     private static native void disposeLoader(long structPointer);
 108 
 109     /*************************** End of Native Loader methods ***************************************/
 110 
 111 
 112     static {
 113         AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
 114             NativeLibLoader.loadLibrary("nativeiio");
 115             return null;
 116         });
 117 
 118         colorSpaceMapping.put(GRAY, ImageType.GRAY);
 119         colorSpaceMapping.put(GRAY_ALPHA, ImageType.GRAY_ALPHA);
 120         colorSpaceMapping.put(GRAY_ALPHA_PRE, ImageType.GRAY_ALPHA_PRE);
 121         colorSpaceMapping.put(PALETTE, ImageType.PALETTE);
 122         colorSpaceMapping.put(PALETTE_ALPHA, ImageType.PALETTE_ALPHA);
 123         colorSpaceMapping.put(PALETTE_ALPHA_PRE, ImageType.PALETTE_ALPHA_PRE);
 124         colorSpaceMapping.put(PALETTE_TRANS, ImageType.PALETTE_TRANS);
 125         colorSpaceMapping.put(RGB, ImageType.RGB);
 126         colorSpaceMapping.put(RGBA, ImageType.RGBA);
 127         colorSpaceMapping.put(RGBA_PRE, ImageType.RGBA_PRE);
 128 
 129         initNativeLoading();
 130     }
 131 
 132     /** Called by the native code when input parameters are known. */
 133     private void setInputParameters(
 134             int width,
 135             int height,
 136             int imageCount,
 137             int loopCount) {
 138 
 139         inWidth = width;
 140         inHeight = height;
 141         nImages = imageCount;
 142         this.loopCount = loopCount;
 143     }
 144 
 145     private void updateProgress(float progressPercentage) {
 146         updateImageProgress(progressPercentage);
 147     }
 148 
 149     private boolean shouldReportProgress() {
 150         return listeners != null && !listeners.isEmpty();
 151     }
 152 
 153     private void checkNativePointer() throws IOException {
 154         if (structPointer == 0L) {
 155             throw new IOException("Unable to initialize image native loader!");
 156         }
 157     }
 158 
 159     private void retrieveDelayTime() {
 160         if (nImages > 1) {
 161             delayTime = getDelayTime(structPointer);
 162         }
 163     }
 164 
 165     public IosImageLoader(final String urlString, final ImageDescriptor desc) throws IOException {
 166         super(desc);
 167 
 168         // see if the given URL is valid
 169         try {
 170             final URL url = new URL(urlString);
 171         }
 172         catch (MalformedURLException mue) {
 173             throw new IllegalArgumentException("Image loader: Malformed URL!");
 174         }
 175 
 176         try {
 177             structPointer = loadImageFromURL(urlString, shouldReportProgress());
 178         } catch (IOException e) {
 179             dispose();
 180             throw e;
 181         }
 182 
 183         checkNativePointer();
 184         retrieveDelayTime();
 185     }
 186 
 187     public IosImageLoader(final InputStream inputStream, final ImageDescriptor desc) throws IOException {
 188         super(desc);
 189         if (inputStream == null) {
 190             throw new IllegalArgumentException("Image loader: input stream == null");
 191         }
 192 
 193         try {
 194             structPointer = loadImage(inputStream, shouldReportProgress());
 195         } catch (IOException e) {
 196             dispose();
 197             throw e;
 198         }
 199 
 200         checkNativePointer();
 201         retrieveDelayTime();
 202     }
 203 
 204     /**
 205      * @inheritDoc
 206      */
 207     public synchronized void dispose() {
 208         if (!isDisposed && structPointer != 0L) {
 209             isDisposed = true;
 210             IosImageLoader.disposeLoader(structPointer);
 211             structPointer = 0L;
 212         }
 213     }
 214 
 215     protected void finalize() {
 216         dispose();
 217     }
 218 
 219    /**
 220     * @inheritDoc
 221     */
 222     public ImageFrame load(int imageIndex, int width, int height, boolean preserveAspectRatio, boolean smooth)
 223             throws IOException {
 224 
 225         if (imageIndex >= nImages) {
 226             dispose();
 227             return null;
 228         }
 229 
 230         // Determine output image dimensions.
 231         int[] widthHeight = ImageTools.computeDimensions(inWidth, inHeight, width, height, preserveAspectRatio);
 232         width = widthHeight[0];
 233         height = widthHeight[1];
 234 
 235         final ImageMetadata md = new ImageMetadata(
 236                 null, // gamma
 237                 true, // whether smaller values represent darker shades
 238                 null, // a palette index to use as background
 239                 null, // background color
 240                 null, // a palette index to be used as transparency
 241                 delayTime == 0 ? null : delayTime, // the amount of time to pause at the current image (milliseconds).
 242                 nImages > 1 ? loopCount : null, // number of loops
 243                 width, // image width
 244                 height, // image height
 245                 null, // image left offset
 246                 null, // image top offset
 247                 null); // disposal method
 248 
 249         updateImageMetadata(md);
 250 
 251         resizeImage(structPointer, width, height);
 252 
 253         // the color model and the number of components can change when resizing
 254         final int nComponents = getNumberOfComponents(structPointer);
 255         final int colorSpaceCode = getColorSpaceCode(structPointer);
 256         final ImageType imageType = colorSpaceMapping.get(colorSpaceCode);
 257 
 258         final byte[] pixels = getImageBuffer(structPointer, imageIndex);
 259 
 260         return new ImageFrame(imageType,
 261                 ByteBuffer.wrap(pixels),
 262                 width,
 263                 height,
 264                 width * nComponents,
 265                 null,
 266                 md);
 267     }
 268 }