Skip to content

Commit 16acfaf

Browse files
committed
8012229: [lcms] Improve performance of color conversion for images with alpha channel
Reviewed-by: azvegint
1 parent cb70ab0 commit 16acfaf

File tree

5 files changed

+206
-13
lines changed

5 files changed

+206
-13
lines changed

src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSImageLayout.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -22,19 +22,20 @@
2222
* or visit www.oracle.com if you need additional information or have any
2323
* questions.
2424
*/
25+
2526
package sun.java2d.cmm.lcms;
2627

2728
import java.awt.image.BufferedImage;
29+
import java.awt.image.ColorModel;
2830
import java.awt.image.ComponentColorModel;
2931
import java.awt.image.ComponentSampleModel;
30-
import java.awt.image.ColorModel;
3132
import java.awt.image.Raster;
32-
import java.awt.image.SampleModel;
33+
3334
import sun.awt.image.ByteComponentRaster;
34-
import sun.awt.image.ShortComponentRaster;
3535
import sun.awt.image.IntegerComponentRaster;
36+
import sun.awt.image.ShortComponentRaster;
3637

37-
class LCMSImageLayout {
38+
final class LCMSImageLayout {
3839

3940
public static int BYTES_SH(int x) {
4041
return x;
@@ -195,7 +196,12 @@ public static LCMSImageLayout createImageLayout(BufferedImage image) throws Imag
195196
* has to be supported.
196197
*/
197198
ColorModel cm = image.getColorModel();
198-
if (cm instanceof ComponentColorModel) {
199+
/* todo
200+
* Our generic code for rasters does not support alpha channels,
201+
* but it would be good to improve it when it is used from here.
202+
* See "createImageLayout(image.getRaster())" below.
203+
*/
204+
if (!cm.hasAlpha() && cm instanceof ComponentColorModel) {
199205
ComponentColorModel ccm = (ComponentColorModel) cm;
200206

201207
// verify whether the component size is fine

src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949

5050
import static sun.java2d.cmm.lcms.LCMSImageLayout.ImageLayoutException;
5151

52-
public class LCMSTransform implements ColorTransform {
52+
final class LCMSTransform implements ColorTransform {
5353
long ID;
5454
private int inFormatter = 0;
5555
private boolean isInIntPacked = false;
@@ -149,10 +149,26 @@ private synchronized void doTransform(LCMSImageLayout in,
149149
LCMS.colorConvert(this, in, out);
150150
}
151151

152+
/**
153+
* Returns {@code true} if lcms may supports this format directly.
154+
*/
155+
private static boolean isLCMSSupport(BufferedImage src, BufferedImage dst) {
156+
if (!dst.getColorModel().hasAlpha()) {
157+
return true;
158+
}
159+
// lcms as of now does not support pre-alpha
160+
if (src.isAlphaPremultiplied() || dst.isAlphaPremultiplied()) {
161+
return false;
162+
}
163+
// lcms does not set correct alpha for transparent dst if src is opaque
164+
// is it feature or bug?
165+
return dst.getColorModel().hasAlpha() == src.getColorModel().hasAlpha();
166+
}
167+
152168
public void colorConvert(BufferedImage src, BufferedImage dst) {
153169
LCMSImageLayout srcIL, dstIL;
154170
try {
155-
if (!dst.getColorModel().hasAlpha()) {
171+
if (isLCMSSupport(src, dst)) {
156172
dstIL = LCMSImageLayout.createImageLayout(dst);
157173

158174
if (dstIL != null) {

src/java.desktop/share/native/liblcms/LCMS.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_createNativeTransform
190190
}
191191

192192
sTrans = cmsCreateMultiprofileTransform(iccArray, j,
193-
inFormatter, outFormatter, renderType, 0);
193+
inFormatter, outFormatter, renderType, cmsFLAGS_COPY_ALPHA);
194194

195195
(*env)->ReleaseLongArrayElements(env, profileIDs, ids, 0);
196196

test/jdk/sun/java2d/cmm/ColorConvertOp/ColCvtAlpha.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2001, 2007, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -23,7 +23,7 @@
2323

2424
/*
2525
* @test
26-
* @bug 4405224
26+
* @bug 4405224 8012229
2727
* @summary Test color conversion for images with alpha
2828
*/
2929

@@ -76,9 +76,10 @@ public static void main(String args[]) {
7676
op.filter(src, dst);
7777

7878
for (int i = 0; i < 10; i++) {
79-
if (((dst.getRGB(0, i) >> 24) & 0xff) != 128) {
79+
int rgb = (dst.getRGB(0, i) >> 24) & 0xff;
80+
if (rgb != 128) {
8081
throw new RuntimeException(
81-
"Incorrect destination alpha value.");
82+
"Incorrect destination alpha value: " + rgb);
8283
}
8384
}
8485

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
* Copyright (c) 2021, 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.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import java.awt.AlphaComposite;
25+
import java.awt.Graphics2D;
26+
import java.awt.color.ColorSpace;
27+
import java.awt.image.BufferedImage;
28+
import java.awt.image.ColorConvertOp;
29+
30+
import static java.awt.image.BufferedImage.TYPE_3BYTE_BGR;
31+
import static java.awt.image.BufferedImage.TYPE_4BYTE_ABGR;
32+
import static java.awt.image.BufferedImage.TYPE_4BYTE_ABGR_PRE;
33+
import static java.awt.image.BufferedImage.TYPE_BYTE_BINARY;
34+
import static java.awt.image.BufferedImage.TYPE_BYTE_GRAY;
35+
import static java.awt.image.BufferedImage.TYPE_BYTE_INDEXED;
36+
import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
37+
import static java.awt.image.BufferedImage.TYPE_INT_ARGB_PRE;
38+
import static java.awt.image.BufferedImage.TYPE_INT_BGR;
39+
import static java.awt.image.BufferedImage.TYPE_INT_RGB;
40+
import static java.awt.image.BufferedImage.TYPE_USHORT_555_RGB;
41+
import static java.awt.image.BufferedImage.TYPE_USHORT_565_RGB;
42+
import static java.awt.image.BufferedImage.TYPE_USHORT_GRAY;
43+
44+
/*
45+
* @test
46+
* @bug 8012229
47+
* @summary one more test to check the alpha channel
48+
*/
49+
public final class ColCvtAlphaDifferentSrcDst {
50+
51+
private static final int WIDTH = 256;
52+
private static final int HEIGHT = 256;
53+
54+
public static void main(String[] args) throws Exception {
55+
differentToOpaqueDst();
56+
differentToTransparentDst(TYPE_INT_ARGB);
57+
differentToTransparentDst(TYPE_4BYTE_ABGR);
58+
differentToTransparentDst(TYPE_INT_ARGB_PRE);
59+
}
60+
61+
/**
62+
* Various types of source images transform to the opaque destination, the
63+
* result should be the same.
64+
*/
65+
private static void differentToOpaqueDst() {
66+
opaqueDst(TYPE_INT_ARGB, TYPE_INT_RGB);
67+
opaqueDst(TYPE_INT_ARGB, TYPE_INT_BGR);
68+
opaqueDst(TYPE_4BYTE_ABGR, TYPE_INT_BGR);
69+
70+
// It is unclear how to hangle pre colors in the opaque DST
71+
//opaqueDst(TYPE_INT_ARGB_PRE, TYPE_4BYTE_ABGR_PRE);
72+
//opaqueDst(TYPE_4BYTE_ABGR_PRE, TYPE_INT_BGR);
73+
}
74+
75+
/**
76+
* Transparent types of source images transform to the transparent
77+
* destination, the alpha channel should be the same in src/dst.
78+
*/
79+
private static void differentToTransparentDst(int typeDst) {
80+
transparentDst(TYPE_INT_RGB, typeDst);
81+
transparentDst(TYPE_INT_ARGB, typeDst);
82+
transparentDst(TYPE_INT_ARGB_PRE, typeDst);
83+
transparentDst(TYPE_INT_BGR, typeDst);
84+
transparentDst(TYPE_3BYTE_BGR, typeDst);
85+
transparentDst(TYPE_4BYTE_ABGR, typeDst);
86+
transparentDst(TYPE_4BYTE_ABGR_PRE, typeDst);
87+
transparentDst(TYPE_USHORT_565_RGB, typeDst);
88+
transparentDst(TYPE_USHORT_555_RGB, typeDst);
89+
transparentDst(TYPE_BYTE_GRAY, typeDst);
90+
transparentDst(TYPE_USHORT_GRAY, typeDst);
91+
transparentDst(TYPE_BYTE_BINARY, typeDst);
92+
transparentDst(TYPE_BYTE_INDEXED, typeDst);
93+
}
94+
95+
private static void opaqueDst(int transparent, int opaque) {
96+
ColorSpace to = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
97+
ColorSpace from = ColorSpace.getInstance(ColorSpace.CS_sRGB);
98+
ColorConvertOp op = new ColorConvertOp(from, to, null);
99+
// Source data
100+
BufferedImage timgSrc = createSrc(transparent);
101+
BufferedImage oimgSrc = createSrc(opaque);
102+
103+
// Destination data
104+
BufferedImage timgDst = createDst(TYPE_INT_RGB);
105+
BufferedImage oimgDst = createDst(TYPE_INT_RGB);
106+
107+
op.filter(timgSrc, timgDst);
108+
op.filter(oimgSrc, oimgDst);
109+
110+
validate(timgDst, oimgDst, false);
111+
}
112+
113+
private static void transparentDst(int typeSrc, int typeDst) {
114+
ColorSpace to = ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);
115+
ColorSpace from = ColorSpace.getInstance(ColorSpace.CS_sRGB);
116+
ColorConvertOp op = new ColorConvertOp(from, to, null);
117+
118+
BufferedImage src = createSrc(typeSrc);
119+
BufferedImage dst = createDst(typeDst);
120+
121+
op.filter(src, dst);
122+
123+
validate(src, dst, true);
124+
}
125+
126+
private static void validate(BufferedImage img1, BufferedImage img2,
127+
boolean alphaOnly) {
128+
for (int i = 0; i < WIDTH; i++) {
129+
for (int j = 0; j < HEIGHT; j++) {
130+
int rgb1 = img1.getRGB(i, j);
131+
int rgb2 = img2.getRGB(i, j);
132+
if (alphaOnly) {
133+
rgb1 |= 0x00FFFFFF;
134+
rgb2 |= 0x00FFFFFF;
135+
}
136+
if (rgb1 != rgb2) {
137+
System.out.println("rgb1 = " + Integer.toHexString(rgb1));
138+
System.out.println("rgb2 = " + Integer.toHexString(rgb2));
139+
throw new RuntimeException();
140+
}
141+
}
142+
}
143+
}
144+
145+
private static BufferedImage createSrc(int type) {
146+
BufferedImage img = new BufferedImage(WIDTH, HEIGHT, type);
147+
fill(img);
148+
return img;
149+
}
150+
151+
private static BufferedImage createDst(int type) {
152+
BufferedImage img = new BufferedImage(WIDTH, HEIGHT, type);
153+
154+
Graphics2D g = img.createGraphics();
155+
g.setComposite(AlphaComposite.Clear);
156+
g.fillRect(0, 0, WIDTH, HEIGHT);
157+
g.dispose();
158+
159+
return img;
160+
}
161+
162+
private static void fill(BufferedImage image) {
163+
for (int i = 0; i < WIDTH; i++) {
164+
for (int j = 0; j < HEIGHT; j++) {
165+
image.setRGB(i, j,
166+
(i << 24) | (i << 16) | (j << 8) | ((i + j) >> 1));
167+
}
168+
}
169+
}
170+
}

0 commit comments

Comments
 (0)