8358623: Avoid unnecessary data copying in ICC_Profile

Reviewed-by: honkar, prr
This commit is contained in:
Sergey Bylokhov 2025-07-07 19:32:17 +00:00
parent 39c9de2ace
commit ec3bb93d79
4 changed files with 115 additions and 44 deletions

View File

@ -799,31 +799,19 @@ public sealed class ICC_Profile implements Serializable
*/
public static ICC_Profile getInstance(byte[] data) {
ProfileDataVerifier.verify(data);
verifyHeader(data);
Profile p;
try {
byte[] theHeader = new byte[HEADER_SIZE];
System.arraycopy(data, 0, theHeader, 0, HEADER_SIZE);
verifyHeader(theHeader);
p = CMSManager.getModule().loadProfile(data);
} catch (CMMException c) {
throw new IllegalArgumentException("Invalid ICC Profile Data");
}
try {
if (getColorSpaceType(data) == ColorSpace.TYPE_GRAY
&& getData(p, icSigMediaWhitePointTag) != null
&& getData(p, icSigGrayTRCTag) != null) {
int type = getColorSpaceType(data);
if (type == ColorSpace.TYPE_GRAY) {
return new ICC_ProfileGray(p);
}
if (getColorSpaceType(data) == ColorSpace.TYPE_RGB
&& getData(p, icSigMediaWhitePointTag) != null
&& getData(p, icSigRedColorantTag) != null
&& getData(p, icSigGreenColorantTag) != null
&& getData(p, icSigBlueColorantTag) != null
&& getData(p, icSigRedTRCTag) != null
&& getData(p, icSigGreenTRCTag) != null
&& getData(p, icSigBlueTRCTag) != null) {
} else if (type == ColorSpace.TYPE_RGB) {
return new ICC_ProfileRGB(p);
}
} catch (CMMException c) {
@ -969,7 +957,7 @@ public sealed class ICC_Profile implements Serializable
* @return the major version of the profile
*/
public int getMajorVersion() {
return getData(icSigHead)[8];
return getData(cmmProfile(), icSigHead)[8];
}
/**
@ -978,7 +966,7 @@ public sealed class ICC_Profile implements Serializable
* @return the minor version of the profile
*/
public int getMinorVersion() {
return getData(icSigHead)[9];
return getData(cmmProfile(), icSigHead)[9];
}
/**
@ -991,12 +979,12 @@ public sealed class ICC_Profile implements Serializable
if (info != null) {
return info.profileClass;
}
byte[] theHeader = getData(icSigHead);
byte[] theHeader = getData(cmmProfile(), icSigHead);
return getProfileClass(theHeader);
}
private static int getProfileClass(byte[] theHeader) {
int theClassSig = intFromBigEndian(theHeader, icHdrDeviceClass);
private static int getProfileClass(byte[] data) {
int theClassSig = intFromBigEndian(data, icHdrDeviceClass);
return switch (theClassSig) {
case icSigInputClass -> CLASS_INPUT;
case icSigDisplayClass -> CLASS_DISPLAY;
@ -1032,8 +1020,8 @@ public sealed class ICC_Profile implements Serializable
return getColorSpaceType(theHeader);
}
private static int getColorSpaceType(byte[] theHeader) {
int theColorSpaceSig = intFromBigEndian(theHeader, icHdrColorSpace);
private static int getColorSpaceType(byte[] data) {
int theColorSpaceSig = intFromBigEndian(data, icHdrColorSpace);
return iccCStoJCS(theColorSpaceSig);
}
@ -1051,13 +1039,13 @@ public sealed class ICC_Profile implements Serializable
* {@code ColorSpace} class
*/
public int getPCSType() {
byte[] theHeader = getData(icSigHead);
byte[] theHeader = getData(cmmProfile(), icSigHead);
return getPCSType(theHeader);
}
private static int getPCSType(byte[] theHeader) {
int thePCSSig = intFromBigEndian(theHeader, icHdrPcs);
int theDeviceClass = intFromBigEndian(theHeader, icHdrDeviceClass);
private static int getPCSType(byte[] data) {
int thePCSSig = intFromBigEndian(data, icHdrPcs);
int theDeviceClass = intFromBigEndian(data, icHdrDeviceClass);
if (theDeviceClass == icSigLinkClass) {
return iccCStoJCS(thePCSSig);
@ -1120,18 +1108,27 @@ public sealed class ICC_Profile implements Serializable
* @see #setData(int, byte[])
*/
public byte[] getData(int tagSignature) {
byte[] t = getData(cmmProfile(), tagSignature);
return t != null ? t.clone() : null;
}
private static byte[] getData(Profile p, int tagSignature) {
try {
return CMSManager.getModule().getTagData(p, tagSignature);
return getData(cmmProfile(), tagSignature).clone();
} catch (CMMException c) {
return null;
}
}
/**
* Returns a particular tagged data element from the profile as a non-null
* byte array. The returned byte array is not cloned. It must not be exposed
* to or used by public APIs. It is intended strictly for internal use only.
*
* @param p the CMM profile from which to retrieve the tag data
* @param tagSignature the ICC tag signature for the data to retrieve
* @return a non-null byte array containing the tag data
* @throws CMMException if the specified tag doesn't exist
*/
static byte[] getData(Profile p, int tagSignature) {
return CMSManager.getModule().getTagData(p, tagSignature);
}
/**
* Sets a particular tagged data element in the profile from a byte array.
* The array should contain data in a format, corresponded to the
@ -1183,7 +1180,7 @@ public sealed class ICC_Profile implements Serializable
checkRenderingIntent(data);
}
private static void checkRenderingIntent(byte[] header) {
private static void checkRenderingIntent(byte[] data) {
int index = ICC_Profile.icHdrRenderingIntent;
/*
* ICC spec: only the least-significant 16 bits encode the rendering
@ -1191,7 +1188,7 @@ public sealed class ICC_Profile implements Serializable
* https://www.color.org/specification/ICC.1-2022-05.pdf, section 7.2.15
*/
// Extract 16-bit unsigned rendering intent (065535)
int intent = (header[index + 2] & 0xff) << 8 | header[index + 3] & 0xff;
int intent = (data[index + 2] & 0xff) << 8 | data[index + 3] & 0xff;
// Only check upper bound since intent can't be negative
if (intent > icICCAbsoluteColorimetric) {
throw new IllegalArgumentException(
@ -1212,7 +1209,7 @@ public sealed class ICC_Profile implements Serializable
if (info != null) {
return info.numComponents;
}
byte[] theHeader = getData(icSigHead);
byte[] theHeader = getData(cmmProfile(), icSigHead);
int theColorSpaceSig = intFromBigEndian(theHeader, icHdrColorSpace);
return switch (theColorSpaceSig) {
case icSigGrayData -> 1;
@ -1251,7 +1248,7 @@ public sealed class ICC_Profile implements Serializable
* encoded in an XYZType tag.
*/
final float[] getXYZTag(int tagSignature) {
byte[] theData = getData(tagSignature);
byte[] theData = getData(cmmProfile(), tagSignature);
float[] theXYZNumber = new float[3]; /* array to return */
/* convert s15Fixed16Number to float */
@ -1275,7 +1272,7 @@ public sealed class ICC_Profile implements Serializable
* single gamma value
*/
float getGamma(int tagSignature) {
byte[] theTRCData = getData(tagSignature);
byte[] theTRCData = getData(cmmProfile(), tagSignature);
if (intFromBigEndian(theTRCData, icCurveCount) != 1) {
throw new ProfileDataException("TRC is not a gamma");
}
@ -1306,7 +1303,7 @@ public sealed class ICC_Profile implements Serializable
* table
*/
short[] getTRC(int tagSignature) {
byte[] theTRCData = getData(tagSignature);
byte[] theTRCData = getData(cmmProfile(), tagSignature);
int nElements = intFromBigEndian(theTRCData, icCurveCount);
if (nElements == 1) {
throw new ProfileDataException("TRC is not a table");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2025, 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
@ -74,10 +74,15 @@ public final class ICC_ProfileGray extends ICC_Profile {
private static final long serialVersionUID = -1124721290732002649L;
/**
* Constructs a new {@code ICC_ProfileGray} from a CMM ID.
* Constructs a new {@code ICC_ProfileGray} from the specified CMM profile.
*
* @param p the CMM profile used to create this ICC profile
* @throws CMMException if the required tags are missing
*/
ICC_ProfileGray(Profile p) {
super(p);
getData(p, icSigMediaWhitePointTag);
getData(p, icSigGrayTRCTag);
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2025, 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
@ -104,12 +104,20 @@ public final class ICC_ProfileRGB extends ICC_Profile {
public static final int BLUECOMPONENT = 2;
/**
* Constructs an new {@code ICC_ProfileRGB} from a CMM ID.
* Constructs a new {@code ICC_ProfileRGB} from the specified CMM profile.
*
* @param p the CMM ID for the profile.
* @param p the CMM profile used to create this ICC profile
* @throws CMMException if the required tags are missing
*/
ICC_ProfileRGB(Profile p) {
super(p);
getData(p, icSigMediaWhitePointTag);
getData(p, icSigRedColorantTag);
getData(p, icSigGreenColorantTag);
getData(p, icSigBlueColorantTag);
getData(p, icSigRedTRCTag);
getData(p, icSigGreenTRCTag);
getData(p, icSigBlueTRCTag);
}
/**

View File

@ -0,0 +1,61 @@
/*
* Copyright Amazon.com Inc. 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.color.ColorSpace;
import java.awt.color.ICC_Profile;
/**
* @test
* @bug 8358623
* @summary Verifies ICC profile version of built-in color spaces
*/
public final class CheckVersions {
public static void main(String[] args) {
test(ColorSpace.CS_CIEXYZ, 2, 3, 0);
test(ColorSpace.CS_GRAY, 2, 3, 0);
test(ColorSpace.CS_LINEAR_RGB, 2, 3, 0);
test(ColorSpace.CS_PYCC, 4, 0, 0);
test(ColorSpace.CS_sRGB, 2, 3, 0);
}
private static void test(int cs, int expMajor, int expMinor, int expPatch) {
ICC_Profile profile = ICC_Profile.getInstance(cs);
int major = profile.getMajorVersion();
int minorRaw = profile.getMinorVersion();
int minor = (minorRaw >> 4) & 0x0F;
int patch = minorRaw & 0x0F;
if (major != expMajor || minor != expMinor || patch != expPatch) {
System.err.println("Expected major: " + expMajor);
System.err.println("Expected minor: " + expMinor);
System.err.println("Expected patch: " + expPatch);
System.err.println("Actual major: " + major);
System.err.println("Actual minor: " + minor);
System.err.println("Actual patch: " + patch);
throw new RuntimeException("Test failed for ColorSpace: " + cs);
}
}
}