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