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 }