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 scalex;
  84     private final float scaley;
  85 
  86     protected Pixels(final int width, final int height, final ByteBuffer pixels) {
  87         this.width = width;
  88         this.height = height;
  89         this.bytesPerComponent = 1;
  90         this.bytes = pixels.slice();
  91         if ((this.width <= 0) || (this.height <= 0) || ((this.width * this.height * 4) > this.bytes.capacity())) {
  92             throw new IllegalArgumentException("Too small byte buffer size "+this.width+"x"+this.height+" ["+(this.width*this.height*4)+"] > "+this.bytes.capacity());
  93         }
  94 
  95         this.ints = null;
  96         this.scalex = 1.0f;
  97         this.scaley = 1.0f;
  98     }
  99 
 100     protected Pixels(final int width, final int height, IntBuffer pixels) {
 101         this.width = width;
 102         this.height = height;
 103         this.bytesPerComponent = 4;
 104         this.ints = pixels.slice();
 105         if ((this.width <= 0) || (this.height <= 0) || ((this.width * this.height) > this.ints.capacity())) {
 106             throw new IllegalArgumentException("Too small int buffer size "+this.width+"x"+this.height+" ["+(this.width*this.height)+"] > "+this.ints.capacity());
 107         }
 108 
 109         this.bytes = null;
 110         this.scalex = 1.0f;
 111         this.scaley = 1.0f;
 112     }
 113 
 114     protected Pixels(final int width, final int height, IntBuffer pixels, float scalex, float scaley) {
 115         this.width = width;
 116         this.height = height;
 117         this.bytesPerComponent = 4;
 118         this.ints = pixels.slice();
 119         if ((this.width <= 0) || (this.height <= 0) || ((this.width * this.height) > this.ints.capacity())) {
 120             throw new IllegalArgumentException("Too small int buffer size "+this.width+"x"+this.height+" ["+(this.width*this.height)+"] > "+this.ints.capacity());
 121         }
 122 
 123         this.bytes = null;
 124         this.scalex = scalex;
 125         this.scaley = scaley;
 126     }
 127 
 128     public final float getScaleX() {
 129         Application.checkEventThread();
 130         return this.scalex;
 131     }
 132 
 133     public final float getScaleY() {
 134         Application.checkEventThread();
 135         return this.scaley;
 136     }
 137 
 138     public final float getScaleXUnsafe() {
 139         return this.scalex;
 140     }
 141 
 142     public final float getScaleYUnsafe() {
 143         return this.scaley;
 144     }
 145 
 146     public final int getWidth() {
 147         Application.checkEventThread();
 148         return this.width;
 149     }
 150 
 151     public final int getWidthUnsafe() {
 152         return this.width;
 153     }
 154 
 155     public final int getHeight() {
 156         Application.checkEventThread();
 157         return this.height;
 158     }
 159 
 160     public final int getHeightUnsafe() {
 161         return this.height;
 162     }
 163 
 164     public final int getBytesPerComponent() {
 165         Application.checkEventThread();
 166         return this.bytesPerComponent;
 167     }
 168 
 169     /*
 170      * Return the original pixels buffer.
 171      */
 172     public final Buffer getPixels() {
 173         if (this.bytes != null) {
 174             this.bytes.rewind();
 175             return this.bytes;
 176         } else if (this.ints != null) {
 177             this.ints.rewind();
 178             return this.ints;
 179         } else {
 180             throw new RuntimeException("Unexpected Pixels state.");
 181         }
 182     }
 183 
 184     /*
 185      * Return a copy of pixels as bytes.
 186      */
 187     public final ByteBuffer asByteBuffer() {
 188         Application.checkEventThread();
 189         ByteBuffer bb = ByteBuffer.allocateDirect(getWidth()*getHeight()*4);
 190         bb.order(ByteOrder.nativeOrder());
 191         bb.rewind();
 192         asByteBuffer(bb);
 193         return bb;
 194     }
 195 
 196     /*
 197      * Copy pixels into provided byte buffer.
 198      * The ByteBuffer must be direct.
 199      */
 200     public final void asByteBuffer(ByteBuffer bb) {
 201         Application.checkEventThread();
 202         if (!bb.isDirect()) {
 203             throw new RuntimeException("Expected direct buffer.");
 204         } else if (bb.remaining() < (getWidth()*getHeight()*4)) {
 205             throw new RuntimeException("Too small buffer.");
 206         }
 207         _fillDirectByteBuffer(bb);
 208     }
 209 
 210     // This method is called from the native code to reduce the number of JNI up-calls.
 211     private void attachData(long ptr) {
 212         if (this.ints != null) {
 213             int[] array = !this.ints.isDirect() ? this.ints.array() : null;
 214             _attachInt(ptr, this.width, this.height, this.ints, array, array != null ? this.ints.arrayOffset() : 0);
 215         }
 216         if (this.bytes != null) {
 217             byte[] array = !this.bytes.isDirect() ? this.bytes.array() : null;
 218             _attachByte(ptr, this.width, this.height, this.bytes, array, array != null ? this.bytes.arrayOffset() : 0);
 219         }
 220     }
 221 
 222     protected abstract void _fillDirectByteBuffer(ByteBuffer bb);
 223     protected abstract void _attachInt(long ptr, int w, int h, IntBuffer ints, int[] array, int offset);
 224     protected abstract void _attachByte(long ptr, int w, int h, ByteBuffer bytes, byte[] array, int offset);
 225 
 226     @Override public final boolean equals(Object object) {
 227         Application.checkEventThread();
 228         boolean equals = ((object != null) && (getClass().equals(object.getClass())));
 229         if (equals) {
 230             Pixels pixels = (Pixels)object;
 231             equals = ((getWidth() == pixels.getWidth()) && (getHeight() == pixels.getHeight()));
 232             if (equals) {
 233                 ByteBuffer b1 = asByteBuffer();
 234                 ByteBuffer b2 = pixels.asByteBuffer();
 235                 equals = (b1.compareTo(b2) == 0);
 236             }
 237         }
 238         return equals;
 239     }
 240 
 241     @Override public final int hashCode() {
 242         Application.checkEventThread();
 243         int val = getWidth();
 244         val = 31*val + getHeight();
 245         val = 17*val + asByteBuffer().hashCode();
 246         return val;
 247     }
 248 }