8012229: [lcms] Improve performance of color conversion for images with alpha channel

Reviewed-by: azvegint
This commit is contained in:
Sergey Bylokhov 2021-03-31 23:02:55 +00:00
parent cb70ab0cb9
commit 16acfafb6b
5 changed files with 208 additions and 15 deletions
src/java.desktop/share
classes/sun/java2d/cmm/lcms
native/liblcms
test/jdk/sun/java2d/cmm/ColorConvertOp

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -22,19 +22,20 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.cmm.lcms;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.ComponentSampleModel;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import sun.awt.image.ByteComponentRaster;
import sun.awt.image.ShortComponentRaster;
import sun.awt.image.IntegerComponentRaster;
class LCMSImageLayout {
import sun.awt.image.ByteComponentRaster;
import sun.awt.image.IntegerComponentRaster;
import sun.awt.image.ShortComponentRaster;
final class LCMSImageLayout {
public static int BYTES_SH(int x) {
return x;
@ -195,7 +196,12 @@ class LCMSImageLayout {
* has to be supported.
*/
ColorModel cm = image.getColorModel();
if (cm instanceof ComponentColorModel) {
/* todo
* Our generic code for rasters does not support alpha channels,
* but it would be good to improve it when it is used from here.
* See "createImageLayout(image.getRaster())" below.
*/
if (!cm.hasAlpha() && cm instanceof ComponentColorModel) {
ComponentColorModel ccm = (ComponentColorModel) cm;
// verify whether the component size is fine

@ -49,7 +49,7 @@ import sun.java2d.cmm.ColorTransform;
import static sun.java2d.cmm.lcms.LCMSImageLayout.ImageLayoutException;
public class LCMSTransform implements ColorTransform {
final class LCMSTransform implements ColorTransform {
long ID;
private int inFormatter = 0;
private boolean isInIntPacked = false;
@ -149,10 +149,26 @@ public class LCMSTransform implements ColorTransform {
LCMS.colorConvert(this, in, out);
}
/**
* Returns {@code true} if lcms may supports this format directly.
*/
private static boolean isLCMSSupport(BufferedImage src, BufferedImage dst) {
if (!dst.getColorModel().hasAlpha()) {
return true;
}
// lcms as of now does not support pre-alpha
if (src.isAlphaPremultiplied() || dst.isAlphaPremultiplied()) {
return false;
}
// lcms does not set correct alpha for transparent dst if src is opaque
// is it feature or bug?
return dst.getColorModel().hasAlpha() == src.getColorModel().hasAlpha();
}
public void colorConvert(BufferedImage src, BufferedImage dst) {
LCMSImageLayout srcIL, dstIL;
try {
if (!dst.getColorModel().hasAlpha()) {
if (isLCMSSupport(src, dst)) {
dstIL = LCMSImageLayout.createImageLayout(dst);
if (dstIL != null) {

@ -190,7 +190,7 @@ JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_createNativeTransform
}
sTrans = cmsCreateMultiprofileTransform(iccArray, j,
inFormatter, outFormatter, renderType, 0);
inFormatter, outFormatter, renderType, cmsFLAGS_COPY_ALPHA);
(*env)->ReleaseLongArrayElements(env, profileIDs, ids, 0);

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2007, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -23,7 +23,7 @@
/*
* @test
* @bug 4405224
* @bug 4405224 8012229
* @summary Test color conversion for images with alpha
*/
@ -76,9 +76,10 @@ public class ColCvtAlpha {
op.filter(src, dst);
for (int i = 0; i < 10; i++) {
if (((dst.getRGB(0, i) >> 24) & 0xff) != 128) {
int rgb = (dst.getRGB(0, i) >> 24) & 0xff;
if (rgb != 128) {
throw new RuntimeException(
"Incorrect destination alpha value.");
"Incorrect destination alpha value: " + rgb);
}
}

@ -0,0 +1,170 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import static java.awt.image.BufferedImage.TYPE_3BYTE_BGR;
import static java.awt.image.BufferedImage.TYPE_4BYTE_ABGR;
import static java.awt.image.BufferedImage.TYPE_4BYTE_ABGR_PRE;
import static java.awt.image.BufferedImage.TYPE_BYTE_BINARY;
import static java.awt.image.BufferedImage.TYPE_BYTE_GRAY;
import static java.awt.image.BufferedImage.TYPE_BYTE_INDEXED;
import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
import static java.awt.image.BufferedImage.TYPE_INT_ARGB_PRE;
import static java.awt.image.BufferedImage.TYPE_INT_BGR;
import static java.awt.image.BufferedImage.TYPE_INT_RGB;
import static java.awt.image.BufferedImage.TYPE_USHORT_555_RGB;
import static java.awt.image.BufferedImage.TYPE_USHORT_565_RGB;
import static java.awt.image.BufferedImage.TYPE_USHORT_GRAY;
/*
* @test
* @bug 8012229
* @summary one more test to check the alpha channel
*/
public final class ColCvtAlphaDifferentSrcDst {
private static final int WIDTH = 256;
private static final int HEIGHT = 256;
public static void main(String[] args) throws Exception {
differentToOpaqueDst();
differentToTransparentDst(TYPE_INT_ARGB);
differentToTransparentDst(TYPE_4BYTE_ABGR);
differentToTransparentDst(TYPE_INT_ARGB_PRE);
}
/**
* Various types of source images transform to the opaque destination, the
* result should be the same.
*/
private static void differentToOpaqueDst() {
opaqueDst(TYPE_INT_ARGB, TYPE_INT_RGB);
opaqueDst(TYPE_INT_ARGB, TYPE_INT_BGR);
opaqueDst(TYPE_4BYTE_ABGR, TYPE_INT_BGR);
// It is unclear how to hangle pre colors in the opaque DST
//opaqueDst(TYPE_INT_ARGB_PRE, TYPE_4BYTE_ABGR_PRE);
//opaqueDst(TYPE_4BYTE_ABGR_PRE, TYPE_INT_BGR);
}
/**
* Transparent types of source images transform to the transparent
* destination, the alpha channel should be the same in src/dst.
*/
private static void differentToTransparentDst(int typeDst) {
transparentDst(TYPE_INT_RGB, typeDst);
transparentDst(TYPE_INT_ARGB, typeDst);
transparentDst(TYPE_INT_ARGB_PRE, typeDst);
transparentDst(TYPE_INT_BGR, typeDst);
transparentDst(TYPE_3BYTE_BGR, typeDst);
transparentDst(TYPE_4BYTE_ABGR, typeDst);
transparentDst(TYPE_4BYTE_ABGR_PRE, typeDst);
transparentDst(TYPE_USHORT_565_RGB, typeDst);
transparentDst(TYPE_USHORT_555_RGB, typeDst);
transparentDst(TYPE_BYTE_GRAY, typeDst);
transparentDst(TYPE_USHORT_GRAY, typeDst);
transparentDst(TYPE_BYTE_BINARY, typeDst);
transparentDst(TYPE_BYTE_INDEXED, typeDst);
}
private static void opaqueDst(int transparent, int opaque) {
ColorSpace to = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
ColorSpace from = ColorSpace.getInstance(ColorSpace.CS_sRGB);
ColorConvertOp op = new ColorConvertOp(from, to, null);
// Source data
BufferedImage timgSrc = createSrc(transparent);
BufferedImage oimgSrc = createSrc(opaque);
// Destination data
BufferedImage timgDst = createDst(TYPE_INT_RGB);
BufferedImage oimgDst = createDst(TYPE_INT_RGB);
op.filter(timgSrc, timgDst);
op.filter(oimgSrc, oimgDst);
validate(timgDst, oimgDst, false);
}
private static void transparentDst(int typeSrc, int typeDst) {
ColorSpace to = ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);
ColorSpace from = ColorSpace.getInstance(ColorSpace.CS_sRGB);
ColorConvertOp op = new ColorConvertOp(from, to, null);
BufferedImage src = createSrc(typeSrc);
BufferedImage dst = createDst(typeDst);
op.filter(src, dst);
validate(src, dst, true);
}
private static void validate(BufferedImage img1, BufferedImage img2,
boolean alphaOnly) {
for (int i = 0; i < WIDTH; i++) {
for (int j = 0; j < HEIGHT; j++) {
int rgb1 = img1.getRGB(i, j);
int rgb2 = img2.getRGB(i, j);
if (alphaOnly) {
rgb1 |= 0x00FFFFFF;
rgb2 |= 0x00FFFFFF;
}
if (rgb1 != rgb2) {
System.out.println("rgb1 = " + Integer.toHexString(rgb1));
System.out.println("rgb2 = " + Integer.toHexString(rgb2));
throw new RuntimeException();
}
}
}
}
private static BufferedImage createSrc(int type) {
BufferedImage img = new BufferedImage(WIDTH, HEIGHT, type);
fill(img);
return img;
}
private static BufferedImage createDst(int type) {
BufferedImage img = new BufferedImage(WIDTH, HEIGHT, type);
Graphics2D g = img.createGraphics();
g.setComposite(AlphaComposite.Clear);
g.fillRect(0, 0, WIDTH, HEIGHT);
g.dispose();
return img;
}
private static void fill(BufferedImage image) {
for (int i = 0; i < WIDTH; i++) {
for (int j = 0; j < HEIGHT; j++) {
image.setRGB(i, j,
(i << 24) | (i << 16) | (j << 8) | ((i + j) >> 1));
}
}
}
}