1 /* 2 * Copyright (c) 2007, 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 26 package sun.java2d.cmm.lcms; 27 28 import java.awt.image.BufferedImage; 29 import java.awt.image.ComponentColorModel; 30 import java.awt.image.ComponentSampleModel; 31 import java.awt.image.DataBuffer; 32 import java.awt.image.ColorModel; 33 import java.awt.image.SampleModel; 34 import sun.awt.image.ByteComponentRaster; 35 import sun.awt.image.ShortComponentRaster; 36 import sun.awt.image.IntegerComponentRaster; 37 38 39 class LCMSImageLayout { 40 41 public static int BYTES_SH(int x) { 42 return x; 43 } 44 45 public static int EXTRA_SH(int x) { 46 return x<<7; 47 } 48 49 public static int CHANNELS_SH(int x) { 50 return x<<3; 51 } 52 53 public static final int SWAPFIRST = 1<<14; 54 55 public static final int DOSWAP = 1<<10; 56 57 public static final int PT_RGB_8 = 58 CHANNELS_SH(3) | BYTES_SH(1); 59 60 public static final int PT_GRAY_8 = 61 CHANNELS_SH(1) | BYTES_SH(1); 62 63 public static final int PT_GRAY_16 = 64 CHANNELS_SH(1) | BYTES_SH(2); 65 66 public static final int PT_RGBA_8 = 67 EXTRA_SH(1) | CHANNELS_SH(3) | BYTES_SH(1); 68 69 public static final int PT_ARGB_8 = 70 EXTRA_SH(1) | CHANNELS_SH(3) | BYTES_SH(1) | SWAPFIRST; 71 72 public static final int PT_BGR_8 = 73 DOSWAP | CHANNELS_SH(3) | BYTES_SH(1); 74 75 public static final int PT_ABGR_8 = 76 DOSWAP | EXTRA_SH(1) | CHANNELS_SH(3) | BYTES_SH(1); 77 78 public static final int PT_BGRA_8 = EXTRA_SH(1) | CHANNELS_SH(3) | 79 BYTES_SH(1) | DOSWAP | SWAPFIRST; 80 81 public static final int DT_BYTE = 0; 82 public static final int DT_SHORT = 1; 83 public static final int DT_INT = 2; 84 public static final int DT_DOUBLE = 3; 85 86 87 boolean isIntPacked = false; 88 int pixelType; 89 int dataType; 90 int width; 91 int height; 92 int nextRowOffset; 93 int offset; 94 95 /* This flag indicates whether the image can be processed 96 * at once by doTransfrom() native call. Otherwise, the 97 * image is processed scan by scan. 98 */ 99 private boolean imageAtOnce = false; 100 101 Object dataArray; 102 private LCMSImageLayout(int np, int pixelType, int pixelSize) { 103 this.pixelType = pixelType; 104 width = np; 105 height = 1; 106 nextRowOffset = np*pixelSize; 107 offset = 0; 108 } 109 110 private LCMSImageLayout(int width, int height, int pixelType, 111 int pixelSize) { 112 this.pixelType = pixelType; 113 this.width = width; 114 this.height = height; 115 nextRowOffset = width*pixelSize; 116 offset = 0; 117 } 118 119 120 public LCMSImageLayout(byte[] data, int np, int pixelType, int pixelSize) { 121 this(np, pixelType, pixelSize); 122 dataType = DT_BYTE; 123 dataArray = data; 124 } 125 126 public LCMSImageLayout(short[] data, int np, int pixelType, int pixelSize) { 127 this(np, pixelType, pixelSize); 128 dataType = DT_SHORT; 129 dataArray = data; 130 } 131 132 public LCMSImageLayout(int[] data, int np, int pixelType, int pixelSize) { 133 this(np, pixelType, pixelSize); 134 dataType = DT_INT; 135 dataArray = data; 136 } 137 138 public LCMSImageLayout(double[] data, int np, int pixelType, int pixelSize) 139 { 140 this(np, pixelType, pixelSize); 141 dataType = DT_DOUBLE; 142 dataArray = data; 143 } 144 145 private LCMSImageLayout() { 146 } 147 148 /* This method creates a layout object for given image. 149 * Returns null if the image is not supported by current implementation. 150 */ 151 public static LCMSImageLayout createImageLayout(BufferedImage image) { 152 LCMSImageLayout l = new LCMSImageLayout(); 153 154 ShortComponentRaster shortRaster; 155 IntegerComponentRaster intRaster; 156 ByteComponentRaster byteRaster; 157 switch (image.getType()) { 158 case BufferedImage.TYPE_INT_RGB: 159 l.pixelType = PT_ARGB_8; 160 l.isIntPacked = true; 161 break; 162 case BufferedImage.TYPE_INT_ARGB: 163 l.pixelType = PT_ARGB_8; 164 l.isIntPacked = true; 165 break; 166 case BufferedImage.TYPE_INT_BGR: 167 l.pixelType = PT_ABGR_8; 168 l.isIntPacked = true; 169 break; 170 case BufferedImage.TYPE_3BYTE_BGR: 171 l.pixelType = PT_BGR_8; 172 break; 173 case BufferedImage.TYPE_4BYTE_ABGR: 174 l.pixelType = PT_ABGR_8; 175 break; 176 case BufferedImage.TYPE_BYTE_GRAY: 177 l.pixelType = PT_GRAY_8; 178 break; 179 case BufferedImage.TYPE_USHORT_GRAY: 180 l.pixelType = PT_GRAY_16; 181 break; 182 default: 183 /* ColorConvertOp creates component images as 184 * default destination, so this kind of images 185 * has to be supported. 186 */ 187 ColorModel cm = image.getColorModel(); 188 if (cm instanceof ComponentColorModel) { 189 ComponentColorModel ccm = (ComponentColorModel) cm; 190 191 // verify whether the component size is fine 192 int[] cs = ccm.getComponentSize(); 193 for (int s : cs) { 194 if (s != 8) { 195 return null; 196 } 197 } 198 199 /* Make sure that band order in the raster 200 * is the natural color order for image's color space. 201 */ 202 SampleModel sm = image.getSampleModel(); 203 if (sm instanceof ComponentSampleModel) { 204 ComponentSampleModel csm = (ComponentSampleModel) sm; 205 206 if (csm.getTransferType() != DataBuffer.TYPE_BYTE) { 207 // We may consider to support other data types later. 208 return null; 209 } 210 int[] bandOffsets = csm.getBandOffsets(); 211 for (int i = 0; i < bandOffsets.length; i++) { 212 /* Now we accept only natural order of color bands, 213 * but the reversed order also can be handled with 214 * SWAP flag in the pixel format descriptor. 215 */ 216 if (bandOffsets[i] != i) { 217 return null; 218 } 219 } 220 } 221 l.pixelType = CHANNELS_SH(cs.length) | BYTES_SH(1); 222 byteRaster = (ByteComponentRaster) image.getRaster(); 223 l.nextRowOffset = byteRaster.getScanlineStride(); 224 l.offset = byteRaster.getDataOffset(0); 225 l.dataArray = byteRaster.getDataStorage(); 226 l.dataType = DT_BYTE; 227 228 l.width = image.getWidth(); 229 l.height = image.getHeight(); 230 231 if (l.nextRowOffset == l.width * byteRaster.getPixelStride()) { 232 l.imageAtOnce = true; 233 } 234 235 return l; 236 237 } else { 238 return null; 239 } 240 241 } 242 243 l.width = image.getWidth(); 244 l.height = image.getHeight(); 245 246 switch (image.getType()) { 247 case BufferedImage.TYPE_INT_RGB: 248 case BufferedImage.TYPE_INT_ARGB: 249 case BufferedImage.TYPE_INT_BGR: 250 intRaster = (IntegerComponentRaster)image.getRaster(); 251 l.nextRowOffset = intRaster.getScanlineStride()*4; 252 l.offset = intRaster.getDataOffset(0)*4; 253 l.dataArray = intRaster.getDataStorage(); 254 l.dataType = DT_INT; 255 256 if (l.nextRowOffset == l.width * 4 * intRaster.getPixelStride()) { 257 l.imageAtOnce = true; 258 } 259 break; 260 261 case BufferedImage.TYPE_3BYTE_BGR: 262 case BufferedImage.TYPE_4BYTE_ABGR: 263 byteRaster = (ByteComponentRaster) image.getRaster(); 264 l.nextRowOffset = byteRaster.getScanlineStride(); 265 int firstBand = image.getSampleModel().getNumBands() - 1; 266 l.offset = byteRaster.getDataOffset(firstBand); 267 l.dataArray = byteRaster.getDataStorage(); 268 l.dataType = DT_BYTE; 269 if (l.nextRowOffset == l.width * byteRaster.getPixelStride()) { 270 l.imageAtOnce = true; 271 } 272 break; 273 274 case BufferedImage.TYPE_BYTE_GRAY: 275 byteRaster = (ByteComponentRaster)image.getRaster(); 276 l.nextRowOffset = byteRaster.getScanlineStride(); 277 l.offset = byteRaster.getDataOffset(0); 278 l.dataArray = byteRaster.getDataStorage(); 279 l.dataType = DT_BYTE; 280 281 if (l.nextRowOffset == l.width * byteRaster.getPixelStride()) { 282 l.imageAtOnce = true; 283 } 284 break; 285 286 case BufferedImage.TYPE_USHORT_GRAY: 287 shortRaster = (ShortComponentRaster)image.getRaster(); 288 l.nextRowOffset = shortRaster.getScanlineStride()*2; 289 l.offset = shortRaster.getDataOffset(0) * 2; 290 l.dataArray = shortRaster.getDataStorage(); 291 l.dataType = DT_SHORT; 292 293 if (l.nextRowOffset == l.width * 2 *shortRaster.getPixelStride()) { 294 l.imageAtOnce = true; 295 } 296 break; 297 default: 298 return null; 299 } 300 return l; 301 } 302 }