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