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 }