1 /*
   2  * Copyright (c) 2010, 2013, 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 package com.sun.glass.ui;
  26 
  27 import java.nio.Buffer;
  28 import java.nio.ByteBuffer;
  29 import java.nio.IntBuffer;
  30 import java.nio.ByteOrder;
  31 
  32 /**
  33  * The object wraps the given raw pixels data.
  34  *
  35  * Pixels class is NOT thread safe.
  36  */
  37 public abstract class Pixels {
  38     /**
  39      * The Format specifies the native byte order of the
  40      * underlying chunk of image data.
  41      * The data may be either INTs or BYTEs depending on
  42      * the constructor used.
  43      * The format ABCD implies the following byte order:
  44      * BYTE[0] = A
  45      * BYTE[1] = B
  46      * BYTE[2] = C
  47      * BYTE[3] = D
  48      * BYTE[4] = A
  49      * ...
  50      * Calling code should take care of endianness of the platform
  51      * when passing image data as ints.
  52      */
  53     public static class Format {
  54         public static final int BYTE_BGRA_PRE = 1;
  55         public static final int BYTE_ARGB = 2;
  56     }
  57 
  58     public static int getNativeFormat() {
  59         Application.checkEventThread();
  60         return Application.GetApplication().staticPixels_getNativeFormat();
  61     }
  62 
  63     // Need:
  64     // Clipboard:
  65     //    public Pixels(final int width, final int height, final byte[] data)
  66     //
  67     // Robot:
  68     //    public Pixels(final int width, final int height, final int[] data)
  69     //
  70     // PixelUtils == Prism == GlassToolkit :
  71     //    public Pixels(final int width, final int height, final ByteBuffer)
  72     //    public Pixels(final int width, final int height, final IntBuffer)
  73 
  74     // The following fields are safe to be protected, since they are final
  75     protected final int width;
  76     protected final int height;
  77     protected final int bytesPerComponent;
  78 
  79     // The following fields are safe to be protected, since they are final
  80     protected final ByteBuffer bytes;
  81     protected final IntBuffer ints;
  82 
  83     private final float scale;
  84 
  85     protected Pixels(final int width, final int height, final ByteBuffer pixels) {
  86         this.width = width;
  87         this.height = height;
  88         this.bytesPerComponent = 1;
  89         this.bytes = pixels.slice();
  90         if ((this.width <= 0) || (this.height <= 0) || ((this.width * this.height * 4) > this.bytes.capacity())) {
  91             throw new IllegalArgumentException("Too small byte buffer size "+this.width+"x"+this.height+" ["+(this.width*this.height*4)+"] > "+this.bytes.capacity());
  92         }
  93 
  94         this.ints = null;
  95         this.scale = 1.0f;
  96     }
  97 
  98     protected Pixels(final int width, final int height, IntBuffer pixels) {
  99         this.width = width;
 100         this.height = height;
 101         this.bytesPerComponent = 4;
 102         this.ints = pixels.slice();
 103         if ((this.width <= 0) || (this.height <= 0) || ((this.width * this.height) > this.ints.capacity())) {
 104             throw new IllegalArgumentException("Too small int buffer size "+this.width+"x"+this.height+" ["+(this.width*this.height)+"] > "+this.ints.capacity());
 105         }
 106 
 107         this.bytes = null;
 108         this.scale = 1.0f;
 109     }
 110 
 111     protected Pixels(final int width, final int height, IntBuffer pixels, float scale) {
 112         this.width = width;
 113         this.height = height;
 114         this.bytesPerComponent = 4;
 115         this.ints = pixels.slice();
 116         if ((this.width <= 0) || (this.height <= 0) || ((this.width * this.height) > this.ints.capacity())) {
 117             throw new IllegalArgumentException("Too small int buffer size "+this.width+"x"+this.height+" ["+(this.width*this.height)+"] > "+this.ints.capacity());
 118         }
 119 
 120         this.bytes = null;
 121         this.scale = scale;
 122     }
 123 
 124     public final float getScale() {
 125         Application.checkEventThread();
 126         return this.scale;
 127     }
 128 
 129     public final float getScaleUnsafe() {
 130         return this.scale;
 131     }
 132 
 133     public final int getWidth() {
 134         Application.checkEventThread();
 135         return this.width;
 136     }
 137 
 138     public final int getWidthUnsafe() {
 139         return this.width;
 140     }
 141 
 142     public final int getHeight() {
 143         Application.checkEventThread();
 144         return this.height;
 145     }
 146 
 147     public final int getHeightUnsafe() {
 148         return this.height;
 149     }
 150 
 151     public final int getBytesPerComponent() {
 152         Application.checkEventThread();
 153         return this.bytesPerComponent;
 154     }
 155 
 156     /*
 157      * Return the original pixels buffer.
 158      */
 159     public final Buffer getPixels() {
 160         if (this.bytes != null) {
 161             this.bytes.rewind();
 162             return this.bytes;
 163         } else if (this.ints != null) {
 164             this.ints.rewind();
 165             return this.ints;
 166         } else {
 167             throw new RuntimeException("Unexpected Pixels state.");
 168         }
 169     }
 170 
 171     /*
 172      * Return a copy of pixels as bytes.
 173      */
 174     public final ByteBuffer asByteBuffer() {
 175         Application.checkEventThread();
 176         ByteBuffer bb = ByteBuffer.allocateDirect(getWidth()*getHeight()*4);
 177         bb.order(ByteOrder.nativeOrder());
 178         bb.rewind();
 179         asByteBuffer(bb);
 180         return bb;
 181     }
 182 
 183     /*
 184      * Copy pixels into provided byte buffer.
 185      * The ByteBuffer must be direct.
 186      */
 187     public final void asByteBuffer(ByteBuffer bb) {
 188         Application.checkEventThread();
 189         if (!bb.isDirect()) {
 190             throw new RuntimeException("Expected direct buffer.");
 191         } else if (bb.remaining() < (getWidth()*getHeight()*4)) {
 192             throw new RuntimeException("Too small buffer.");
 193         }
 194         _fillDirectByteBuffer(bb);
 195     }
 196 
 197     // This method is called from the native code to reduce the number of JNI up-calls.
 198     private void attachData(long ptr) {
 199         if (this.ints != null) {
 200             int[] array = !this.ints.isDirect() ? this.ints.array() : null;
 201             _attachInt(ptr, this.width, this.height, this.ints, array, array != null ? this.ints.arrayOffset() : 0);
 202         }
 203         if (this.bytes != null) {
 204             byte[] array = !this.bytes.isDirect() ? this.bytes.array() : null;
 205             _attachByte(ptr, this.width, this.height, this.bytes, array, array != null ? this.bytes.arrayOffset() : 0);
 206         }
 207     }
 208 
 209     protected abstract void _fillDirectByteBuffer(ByteBuffer bb);
 210     protected abstract void _attachInt(long ptr, int w, int h, IntBuffer ints, int[] array, int offset);
 211     protected abstract void _attachByte(long ptr, int w, int h, ByteBuffer bytes, byte[] array, int offset);
 212 
 213     @Override public final boolean equals(Object object) {
 214         Application.checkEventThread();
 215         boolean equals = ((object != null) && (getClass().equals(object.getClass())));
 216         if (equals) {
 217             Pixels pixels = (Pixels)object;
 218             equals = ((getWidth() == pixels.getWidth()) && (getHeight() == pixels.getHeight()));
 219             if (equals) {
 220                 ByteBuffer b1 = asByteBuffer();
 221                 ByteBuffer b2 = pixels.asByteBuffer();
 222                 equals = (b1.compareTo(b2) == 0);
 223             }
 224         }
 225         return equals;
 226     }
 227 
 228     @Override public final int hashCode() {
 229         Application.checkEventThread();
 230         int val = getWidth();
 231         val = 31*val + getHeight();
 232         val = 17*val + asByteBuffer().hashCode();
 233         return val;
 234     }
 235 }