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 }