Path: blob/master/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSImageLayout.java
41161 views
/*1* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.java2d.cmm.lcms;2627import java.awt.image.BufferedImage;28import java.awt.image.ColorModel;29import java.awt.image.ComponentColorModel;30import java.awt.image.ComponentSampleModel;31import java.awt.image.Raster;3233import sun.awt.image.ByteComponentRaster;34import sun.awt.image.IntegerComponentRaster;35import sun.awt.image.ShortComponentRaster;3637final class LCMSImageLayout {3839public static int BYTES_SH(int x) {40return x;41}4243public static int EXTRA_SH(int x) {44return x << 7;45}4647public static int CHANNELS_SH(int x) {48return x << 3;49}50public static final int SWAPFIRST = 1 << 14;51public static final int DOSWAP = 1 << 10;52public static final int PT_RGB_8 =53CHANNELS_SH(3) | BYTES_SH(1);54public static final int PT_GRAY_8 =55CHANNELS_SH(1) | BYTES_SH(1);56public static final int PT_GRAY_16 =57CHANNELS_SH(1) | BYTES_SH(2);58public static final int PT_RGBA_8 =59EXTRA_SH(1) | CHANNELS_SH(3) | BYTES_SH(1);60public static final int PT_ARGB_8 =61EXTRA_SH(1) | CHANNELS_SH(3) | BYTES_SH(1) | SWAPFIRST;62public static final int PT_BGR_8 =63DOSWAP | CHANNELS_SH(3) | BYTES_SH(1);64public static final int PT_ABGR_8 =65DOSWAP | EXTRA_SH(1) | CHANNELS_SH(3) | BYTES_SH(1);66public static final int PT_BGRA_8 = EXTRA_SH(1) | CHANNELS_SH(3)67| BYTES_SH(1) | DOSWAP | SWAPFIRST;68public static final int DT_BYTE = 0;69public static final int DT_SHORT = 1;70public static final int DT_INT = 2;71public static final int DT_DOUBLE = 3;72boolean isIntPacked = false;73int pixelType;74int dataType;75int width;76int height;77int nextRowOffset;78private int nextPixelOffset;79int offset;8081/* This flag indicates whether the image can be processed82* at once by doTransfrom() native call. Otherwise, the83* image is processed scan by scan.84*/85private boolean imageAtOnce = false;86Object dataArray;8788private int dataArrayLength; /* in bytes */8990private LCMSImageLayout(int np, int pixelType, int pixelSize)91throws ImageLayoutException92{93this.pixelType = pixelType;94width = np;95height = 1;96nextPixelOffset = pixelSize;97nextRowOffset = safeMult(pixelSize, np);98offset = 0;99}100101private LCMSImageLayout(int width, int height, int pixelType,102int pixelSize)103throws ImageLayoutException104{105this.pixelType = pixelType;106this.width = width;107this.height = height;108nextPixelOffset = pixelSize;109nextRowOffset = safeMult(pixelSize, width);110offset = 0;111}112113114public LCMSImageLayout(byte[] data, int np, int pixelType, int pixelSize)115throws ImageLayoutException116{117this(np, pixelType, pixelSize);118dataType = DT_BYTE;119dataArray = data;120dataArrayLength = data.length;121122verify();123}124125public LCMSImageLayout(short[] data, int np, int pixelType, int pixelSize)126throws ImageLayoutException127{128this(np, pixelType, pixelSize);129dataType = DT_SHORT;130dataArray = data;131dataArrayLength = 2 * data.length;132133verify();134}135136public LCMSImageLayout(int[] data, int np, int pixelType, int pixelSize)137throws ImageLayoutException138{139this(np, pixelType, pixelSize);140dataType = DT_INT;141dataArray = data;142dataArrayLength = 4 * data.length;143144verify();145}146147public LCMSImageLayout(double[] data, int np, int pixelType, int pixelSize)148throws ImageLayoutException149{150this(np, pixelType, pixelSize);151dataType = DT_DOUBLE;152dataArray = data;153dataArrayLength = 8 * data.length;154155verify();156}157158private LCMSImageLayout() {159}160161/* This method creates a layout object for given image.162* Returns null if the image is not supported by current implementation.163*/164public static LCMSImageLayout createImageLayout(BufferedImage image) throws ImageLayoutException {165LCMSImageLayout l = new LCMSImageLayout();166167switch (image.getType()) {168case BufferedImage.TYPE_INT_RGB:169l.pixelType = PT_ARGB_8;170l.isIntPacked = true;171break;172case BufferedImage.TYPE_INT_ARGB:173l.pixelType = PT_ARGB_8;174l.isIntPacked = true;175break;176case BufferedImage.TYPE_INT_BGR:177l.pixelType = PT_ABGR_8;178l.isIntPacked = true;179break;180case BufferedImage.TYPE_3BYTE_BGR:181l.pixelType = PT_BGR_8;182break;183case BufferedImage.TYPE_4BYTE_ABGR:184l.pixelType = PT_ABGR_8;185break;186case BufferedImage.TYPE_BYTE_GRAY:187l.pixelType = PT_GRAY_8;188break;189case BufferedImage.TYPE_USHORT_GRAY:190l.pixelType = PT_GRAY_16;191break;192default:193/* ColorConvertOp creates component images as194* default destination, so this kind of images195* has to be supported.196*/197ColorModel cm = image.getColorModel();198/* todo199* Our generic code for rasters does not support alpha channels,200* but it would be good to improve it when it is used from here.201* See "createImageLayout(image.getRaster())" below.202*/203if (!cm.hasAlpha() && cm instanceof ComponentColorModel) {204ComponentColorModel ccm = (ComponentColorModel) cm;205206// verify whether the component size is fine207int[] cs = ccm.getComponentSize();208for (int s : cs) {209if (s != 8) {210return null;211}212}213214return createImageLayout(image.getRaster());215216}217return null;218}219220l.width = image.getWidth();221l.height = image.getHeight();222223switch (image.getType()) {224case BufferedImage.TYPE_INT_RGB:225case BufferedImage.TYPE_INT_ARGB:226case BufferedImage.TYPE_INT_BGR:227do {228IntegerComponentRaster intRaster = (IntegerComponentRaster)229image.getRaster();230l.nextRowOffset = safeMult(4, intRaster.getScanlineStride());231l.nextPixelOffset = safeMult(4, intRaster.getPixelStride());232l.offset = safeMult(4, intRaster.getDataOffset(0));233l.dataArray = intRaster.getDataStorage();234l.dataArrayLength = 4 * intRaster.getDataStorage().length;235l.dataType = DT_INT;236237if (l.nextRowOffset == l.width * 4 * intRaster.getPixelStride()) {238l.imageAtOnce = true;239}240} while (false);241break;242243case BufferedImage.TYPE_3BYTE_BGR:244case BufferedImage.TYPE_4BYTE_ABGR:245do {246ByteComponentRaster byteRaster = (ByteComponentRaster)247image.getRaster();248l.nextRowOffset = byteRaster.getScanlineStride();249l.nextPixelOffset = byteRaster.getPixelStride();250251int firstBand = image.getSampleModel().getNumBands() - 1;252l.offset = byteRaster.getDataOffset(firstBand);253l.dataArray = byteRaster.getDataStorage();254l.dataArrayLength = byteRaster.getDataStorage().length;255l.dataType = DT_BYTE;256if (l.nextRowOffset == l.width * byteRaster.getPixelStride()) {257l.imageAtOnce = true;258}259} while (false);260break;261262case BufferedImage.TYPE_BYTE_GRAY:263do {264ByteComponentRaster byteRaster = (ByteComponentRaster)265image.getRaster();266l.nextRowOffset = byteRaster.getScanlineStride();267l.nextPixelOffset = byteRaster.getPixelStride();268269l.dataArrayLength = byteRaster.getDataStorage().length;270l.offset = byteRaster.getDataOffset(0);271l.dataArray = byteRaster.getDataStorage();272l.dataType = DT_BYTE;273274if (l.nextRowOffset == l.width * byteRaster.getPixelStride()) {275l.imageAtOnce = true;276}277} while (false);278break;279280case BufferedImage.TYPE_USHORT_GRAY:281do {282ShortComponentRaster shortRaster = (ShortComponentRaster)283image.getRaster();284l.nextRowOffset = safeMult(2, shortRaster.getScanlineStride());285l.nextPixelOffset = safeMult(2, shortRaster.getPixelStride());286287l.offset = safeMult(2, shortRaster.getDataOffset(0));288l.dataArray = shortRaster.getDataStorage();289l.dataArrayLength = 2 * shortRaster.getDataStorage().length;290l.dataType = DT_SHORT;291292if (l.nextRowOffset == l.width * 2 * shortRaster.getPixelStride()) {293l.imageAtOnce = true;294}295} while (false);296break;297default:298return null;299}300l.verify();301return l;302}303304private static enum BandOrder {305DIRECT,306INVERTED,307ARBITRARY,308UNKNOWN;309310public static BandOrder getBandOrder(int[] bandOffsets) {311BandOrder order = UNKNOWN;312313int numBands = bandOffsets.length;314315for (int i = 0; (order != ARBITRARY) && (i < bandOffsets.length); i++) {316switch (order) {317case UNKNOWN:318if (bandOffsets[i] == i) {319order = DIRECT;320} else if (bandOffsets[i] == (numBands - 1 - i)) {321order = INVERTED;322} else {323order = ARBITRARY;324}325break;326case DIRECT:327if (bandOffsets[i] != i) {328order = ARBITRARY;329}330break;331case INVERTED:332if (bandOffsets[i] != (numBands - 1 - i)) {333order = ARBITRARY;334}335break;336}337}338return order;339}340}341342private void verify() throws ImageLayoutException {343344if (offset < 0 || offset >= dataArrayLength) {345throw new ImageLayoutException("Invalid image layout");346}347348if (nextPixelOffset != getBytesPerPixel(pixelType)) {349throw new ImageLayoutException("Invalid image layout");350}351352int lastScanOffset = safeMult(nextRowOffset, (height - 1));353354int lastPixelOffset = safeMult(nextPixelOffset, (width -1 ));355356lastPixelOffset = safeAdd(lastPixelOffset, lastScanOffset);357358int off = safeAdd(offset, lastPixelOffset);359360if (off < 0 || off >= dataArrayLength) {361throw new ImageLayoutException("Invalid image layout");362}363}364365static int safeAdd(int a, int b) throws ImageLayoutException {366long res = a;367res += b;368if (res < Integer.MIN_VALUE || res > Integer.MAX_VALUE) {369throw new ImageLayoutException("Invalid image layout");370}371return (int)res;372}373374static int safeMult(int a, int b) throws ImageLayoutException {375long res = a;376res *= b;377if (res < Integer.MIN_VALUE || res > Integer.MAX_VALUE) {378throw new ImageLayoutException("Invalid image layout");379}380return (int)res;381}382383@SuppressWarnings("serial") // JDK-implementation class384public static class ImageLayoutException extends Exception {385public ImageLayoutException(String message) {386super(message);387}388}389public static LCMSImageLayout createImageLayout(Raster r) {390LCMSImageLayout l = new LCMSImageLayout();391if (r instanceof ByteComponentRaster &&392r.getSampleModel() instanceof ComponentSampleModel) {393ByteComponentRaster br = (ByteComponentRaster)r;394395ComponentSampleModel csm = (ComponentSampleModel)r.getSampleModel();396397l.pixelType = CHANNELS_SH(br.getNumBands()) | BYTES_SH(1);398399int[] bandOffsets = csm.getBandOffsets();400BandOrder order = BandOrder.getBandOrder(bandOffsets);401402int firstBand = 0;403switch (order) {404case INVERTED:405l.pixelType |= DOSWAP;406firstBand = csm.getNumBands() - 1;407break;408case DIRECT:409// do nothing410break;411default:412// unable to create the image layout;413return null;414}415416l.nextRowOffset = br.getScanlineStride();417l.nextPixelOffset = br.getPixelStride();418419l.offset = br.getDataOffset(firstBand);420l.dataArray = br.getDataStorage();421l.dataType = DT_BYTE;422423l.width = br.getWidth();424l.height = br.getHeight();425426if (l.nextRowOffset == l.width * br.getPixelStride()) {427l.imageAtOnce = true;428}429return l;430}431return null;432}433434/**435* Derives number of bytes per pixel from the pixel format.436* Following bit fields are used here:437* [0..2] - bytes per sample438* [3..6] - number of color samples per pixel439* [7..9] - number of non-color samples per pixel440*441* A complete description of the pixel format can be found442* here: lcms2.h, lines 651 - 667.443*444* @param pixelType pixel format in lcms2 notation.445* @return number of bytes per pixel for given pixel format.446*/447private static int getBytesPerPixel(int pixelType) {448int bytesPerSample = (0x7 & pixelType);449int colorSamplesPerPixel = 0xF & (pixelType >> 3);450int extraSamplesPerPixel = 0x7 & (pixelType >> 7);451452return bytesPerSample * (colorSamplesPerPixel + extraSamplesPerPixel);453}454}455456457