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 }