diff --git a/src/java.desktop/share/classes/java/awt/color/ICC_ColorSpace.java b/src/java.desktop/share/classes/java/awt/color/ICC_ColorSpace.java
index acb31fbca9c..071506ca460 100644
--- a/src/java.desktop/share/classes/java/awt/color/ICC_ColorSpace.java
+++ b/src/java.desktop/share/classes/java/awt/color/ICC_ColorSpace.java
@@ -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
@@ -140,10 +140,11 @@ public ICC_ColorSpace(ICC_Profile profile) {
if (profileClass != ICC_Profile.CLASS_INPUT
&& profileClass != ICC_Profile.CLASS_DISPLAY
&& profileClass != ICC_Profile.CLASS_OUTPUT
+ && profileClass != ICC_Profile.CLASS_DEVICELINK
&& profileClass != ICC_Profile.CLASS_COLORSPACECONVERSION
&& profileClass != ICC_Profile.CLASS_NAMEDCOLOR
&& profileClass != ICC_Profile.CLASS_ABSTRACT) {
- throw new IllegalArgumentException("Invalid profile type");
+ throw new IllegalArgumentException("Invalid profile class");
}
thisProfile = profile;
diff --git a/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java b/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java
index 0d31d25b119..0edf7c1c8c0 100644
--- a/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java
+++ b/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2023, 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
@@ -110,6 +110,14 @@ public sealed class ICC_Profile implements Serializable
*/
private transient volatile ProfileDeferralInfo deferralInfo;
+
+ /**
+ * Set to {@code true} for {@code BuiltInProfile}, {@code false} otherwise.
+ * This flag is used in {@link #setData(int, byte[])} to prevent modifying
+ * built-in profiles.
+ */
+ private final transient boolean builtIn;
+
/**
* The lazy registry of singleton profile objects for specific built-in
* color spaces defined in the ColorSpace class (e.g. CS_sRGB),
@@ -117,8 +125,8 @@ public sealed class ICC_Profile implements Serializable
*/
private interface BuiltInProfile {
/*
- * Deferral is only used for standard profiles. Enabling the appropriate
- * access privileges is handled at a lower level.
+ * ProfileDeferralInfo is used for built-in profile creation only,
+ * and all built-in profiles should be constructed using it.
*/
ICC_Profile SRGB = new ICC_ProfileRGB(new ProfileDeferralInfo(
"sRGB.pf", ColorSpace.TYPE_RGB, 3, CLASS_DISPLAY));
@@ -759,20 +767,27 @@ private interface BuiltInProfile {
*/
public static final int icXYZNumberX = 8;
+ private static final int HEADER_SIZE = 128;
/**
* Constructs an {@code ICC_Profile} object with a given ID.
*/
ICC_Profile(Profile p) {
cmmProfile = p;
+ builtIn = false;
}
/**
* Constructs an {@code ICC_Profile} object whose loading will be deferred.
* The ID will be 0 until the profile is loaded.
+ *
+ *
+ * Note: {@code ProfileDeferralInfo} is used for built-in profile
+ * creation only, and all built-in profiles should be constructed using it.
*/
ICC_Profile(ProfileDeferralInfo pdi) {
deferralInfo = pdi;
+ builtIn = true;
}
/**
@@ -789,10 +804,15 @@ public static ICC_Profile getInstance(byte[] data) {
ProfileDataVerifier.verify(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(p) == ColorSpace.TYPE_GRAY
&& getData(p, icSigMediaWhitePointTag) != null
@@ -978,6 +998,10 @@ public int getProfileClass() {
return info.profileClass;
}
byte[] theHeader = getData(icSigHead);
+ return getProfileClass(theHeader);
+ }
+
+ private static int getProfileClass(byte[] theHeader) {
int theClassSig = intFromBigEndian(theHeader, icHdrDeviceClass);
return switch (theClassSig) {
case icSigInputClass -> CLASS_INPUT;
@@ -1019,6 +1043,11 @@ private static int getColorSpaceType(Profile p) {
return iccCStoJCS(theColorSpaceSig);
}
+ private static int getColorSpaceType(byte[] theHeader) {
+ int theColorSpaceSig = intFromBigEndian(theHeader, icHdrColorSpace);
+ return iccCStoJCS(theColorSpaceSig);
+ }
+
/**
* Returns the color space type of the Profile Connection Space (PCS).
* Returns one of the color space type constants defined by the ColorSpace
@@ -1038,6 +1067,21 @@ public int getPCSType() {
return iccCStoJCS(thePCSSig);
}
+ private static int getPCSType(byte[] theHeader) {
+ int thePCSSig = intFromBigEndian(theHeader, icHdrPcs);
+ int theDeviceClass = intFromBigEndian(theHeader, icHdrDeviceClass);
+
+ if (theDeviceClass == icSigLinkClass) {
+ return iccCStoJCS(thePCSSig);
+ } else {
+ return switch (thePCSSig) {
+ case icSigXYZData -> ColorSpace.TYPE_XYZ;
+ case icSigLabData -> ColorSpace.TYPE_Lab;
+ default -> throw new IllegalArgumentException("Unexpected PCS type");
+ };
+ }
+ }
+
/**
* Write this {@code ICC_Profile} to a file.
*
@@ -1107,20 +1151,70 @@ private static byte[] getData(Profile p, int tagSignature) {
* This method is useful for advanced applications which need to access
* profile data directly.
*
+ *
+ * Note: JDK built-in ICC Profiles cannot be updated using this method
+ * as it will result in {@code IllegalArgumentException}. JDK built-in
+ * profiles are those obtained by {@code ICC_Profile.getInstance(int colorSpaceID)}
+ * where {@code colorSpaceID} is one of the following:
+ * {@link ColorSpace#CS_sRGB}, {@link ColorSpace#CS_LINEAR_RGB},
+ * {@link ColorSpace#CS_PYCC}, {@link ColorSpace#CS_GRAY} or
+ * {@link ColorSpace#CS_CIEXYZ}.
+ *
* @param tagSignature the ICC tag signature for the data element you want
* to set
* @param tagData the data to set for the specified tag signature
* @throws IllegalArgumentException if {@code tagSignature} is not a
* signature as defined in the ICC specification.
- * @throws IllegalArgumentException if a content of the {@code tagData}
+ * @throws IllegalArgumentException if the content of the {@code tagData}
* array can not be interpreted as valid tag data, corresponding to
* the {@code tagSignature}
+ * @throws IllegalArgumentException if this is a built-in profile for one
+ * of the pre-defined color spaces, that is those which can be obtained
+ * by calling {@code ICC_Profile.getInstance(int colorSpaceID)}
* @see #getData
+ * @see ColorSpace
*/
public void setData(int tagSignature, byte[] tagData) {
+ if (builtIn) {
+ throw new IllegalArgumentException("Built-in profile cannot be modified");
+ }
+
+ if (tagSignature == ICC_Profile.icSigHead) {
+ verifyHeader(tagData);
+ }
CMSManager.getModule().setTagData(cmmProfile(), tagSignature, tagData);
}
+ private static void verifyHeader(byte[] data) {
+ if (data == null || data.length < HEADER_SIZE) {
+ throw new IllegalArgumentException("Invalid header data");
+ }
+ getProfileClass(data);
+ getColorSpaceType(data);
+ getPCSType(data);
+ checkRenderingIntent(data);
+ }
+
+ private static boolean checkRenderingIntent(byte[] header) {
+ int index = ICC_Profile.icHdrRenderingIntent;
+
+ /* According to ICC spec, only the least-significant 16 bits shall be
+ * used to encode the rendering intent. The most significant 16 bits
+ * shall be set to zero. Thus, we are ignoring two most significant
+ * bytes here. Please refer ICC Spec Document for more details.
+ */
+ int renderingIntent = ((header[index+2] & 0xff) << 8) |
+ (header[index+3] & 0xff);
+
+ switch (renderingIntent) {
+ case icPerceptual, icMediaRelativeColorimetric,
+ icSaturation, icAbsoluteColorimetric -> {
+ return true;
+ }
+ default -> throw new IllegalArgumentException("Unknown Rendering Intent");
+ }
+ }
+
/**
* Returns the number of color components in the "input" color space of this
* profile. For example if the color space type of this profile is
diff --git a/test/jdk/java/awt/color/ICC_Profile/BuiltInProfileCheck/BuiltInProfileCheck.java b/test/jdk/java/awt/color/ICC_Profile/BuiltInProfileCheck/BuiltInProfileCheck.java
new file mode 100644
index 00000000000..8f00ab10747
--- /dev/null
+++ b/test/jdk/java/awt/color/ICC_Profile/BuiltInProfileCheck/BuiltInProfileCheck.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 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
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8346465
+ * @summary Tests if setData() throws IAE for BuiltIn profiles
+ */
+
+import java.awt.color.ColorSpace;
+import java.awt.color.ICC_Profile;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Map;
+
+public class BuiltInProfileCheck {
+ private static final int HEADER_TAG = ICC_Profile.icSigHead;
+ private static final int INDEX = ICC_Profile.icHdrDeviceClass;
+ private static final String EXCEPTION_MSG = "Built-in profile cannot be modified";
+ /**
+ * {@link #prepareTestProfile(String, boolean, int)}
+ * stores the profile to test in testProfile.
+ */
+ private static ICC_Profile testProfile;
+
+ private static final Map colorSpace = Map.of(
+ ColorSpace.CS_sRGB, "CS_sRGB",
+ ColorSpace.CS_PYCC, "CS_PYCC",
+ ColorSpace.CS_GRAY, "CS_GRAY",
+ ColorSpace.CS_CIEXYZ, "CS_CIEXYZ",
+ ColorSpace.CS_LINEAR_RGB, "CS_LINEAR_RGB"
+ );
+
+ public static void main(String[] args) throws Exception {
+ System.out.println("CASE 1: Testing BuiltIn Profile");
+ for (int cs : colorSpace.keySet()) {
+ prepareTestProfile("Default", true, cs);
+ testProfile(true, cs);
+ }
+ System.out.println("Passed\n");
+
+ System.out.println("CASE 2: Testing Custom Profile");
+ prepareTestProfile("Default", false, ColorSpace.CS_sRGB);
+ testProfile(false, ColorSpace.CS_sRGB);
+ System.out.println("Passed\n");
+
+ System.out.println("CASE 3: Testing Built-In Profile"
+ + " Serialization & Deserialization");
+ for (int cs : colorSpace.keySet()) {
+ prepareTestProfile("Serialize", true, cs);
+ testProfile(true, cs);
+ }
+ System.out.println("Passed\n");
+
+ System.out.println("CASE 4: Testing Custom Profile"
+ + " Serialization & Deserialization");
+ prepareTestProfile("Serialize", false, ColorSpace.CS_sRGB);
+ testProfile(false, ColorSpace.CS_sRGB);
+ System.out.println("Passed\n");
+
+ System.out.println("CASE 5: Test reading Built-In profile from .icc file");
+ prepareTestProfile("ReadFromFile", true, ColorSpace.CS_sRGB);
+ testProfile(true, ColorSpace.CS_sRGB);
+ System.out.println("Passed\n");
+
+ System.out.println("CASE 6: Test reading Custom profile from .icc file");
+ prepareTestProfile("ReadFromFile", false, ColorSpace.CS_sRGB);
+ testProfile(false, ColorSpace.CS_sRGB);
+ System.out.println("Passed\n");
+ }
+
+ private static void prepareTestProfile(String testCase,
+ boolean isBuiltIn, int cs) {
+ ICC_Profile builtInProfile = ICC_Profile.getInstance(cs);
+ // if isBuiltIn=true use builtInProfile else create a copy
+ testProfile = isBuiltIn
+ ? builtInProfile
+ : ICC_Profile.getInstance(builtInProfile.getData());
+
+ switch (testCase) {
+ case "Default" -> {
+ // empty case block
+ // no further processing of testProfile required for default case
+ }
+ case "Serialize" -> {
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos)) {
+ oos.writeObject(testProfile);
+
+ byte[] array = baos.toByteArray();
+ try (ObjectInputStream ois =
+ new ObjectInputStream(new ByteArrayInputStream(array))) {
+ testProfile = (ICC_Profile) ois.readObject();
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Test Failed ! Serial-Deserialization"
+ + " case failed", e);
+ }
+ }
+ case "ReadFromFile" -> {
+ // .icc files serialized on older JDK version
+ String filename = isBuiltIn ? "builtIn.icc" : "custom.icc";
+ String testDir = System.getProperty("test.src")
+ + System.getProperty("file.separator");
+ filename = testDir + filename;
+
+ try (FileInputStream fileIn = new FileInputStream(filename);
+ ObjectInputStream ois = new ObjectInputStream(fileIn)) {
+ testProfile = (ICC_Profile) ois.readObject();
+ } catch (Exception e) {
+ throw new RuntimeException("Test Failed ! Unable to fetch"
+ + " .icc files", e);
+ }
+ }
+ }
+ }
+
+ private static void testProfile(boolean isBuiltIn, int cs) {
+ byte[] headerData = testProfile.getData(HEADER_TAG);
+ // Set profile class to valid icSigInputClass = 0x73636E72
+ headerData[INDEX] = 0x73;
+ headerData[INDEX + 1] = 0x63;
+ headerData[INDEX + 2] = 0x6E;
+ headerData[INDEX + 3] = 0x72;
+
+ if (isBuiltIn) {
+ System.out.println("Testing: " + colorSpace.get(cs));
+ try {
+ // Try updating a built-in profile, IAE is expected
+ testProfile.setData(HEADER_TAG, headerData);
+ throw new RuntimeException("Test Failed! IAE NOT thrown for profile "
+ + colorSpace.get(cs));
+ } catch (IllegalArgumentException iae) {
+ if (!iae.getMessage().equals(EXCEPTION_MSG)) {
+ throw new RuntimeException("Test Failed! IAE with exception msg \""
+ + EXCEPTION_MSG + "\" NOT thrown for profile "
+ + colorSpace.get(cs));
+ }
+ }
+ } else {
+ // Modifying custom profile should NOT throw IAE
+ testProfile.setData(HEADER_TAG, headerData);
+ }
+ }
+}
diff --git a/test/jdk/java/awt/color/ICC_Profile/BuiltInProfileCheck/builtIn.icc b/test/jdk/java/awt/color/ICC_Profile/BuiltInProfileCheck/builtIn.icc
new file mode 100644
index 00000000000..dbe11f2c6d6
Binary files /dev/null and b/test/jdk/java/awt/color/ICC_Profile/BuiltInProfileCheck/builtIn.icc differ
diff --git a/test/jdk/java/awt/color/ICC_Profile/BuiltInProfileCheck/custom.icc b/test/jdk/java/awt/color/ICC_Profile/BuiltInProfileCheck/custom.icc
new file mode 100644
index 00000000000..b32c838c80a
Binary files /dev/null and b/test/jdk/java/awt/color/ICC_Profile/BuiltInProfileCheck/custom.icc differ
diff --git a/test/jdk/java/awt/color/ICC_Profile/SetHeaderInfo.java b/test/jdk/java/awt/color/ICC_Profile/SetHeaderInfo.java
index 89e06c216ef..9c6ff2ae6bf 100644
--- a/test/jdk/java/awt/color/ICC_Profile/SetHeaderInfo.java
+++ b/test/jdk/java/awt/color/ICC_Profile/SetHeaderInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 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
@@ -37,7 +37,9 @@ public static void main(String[] args) {
ColorSpace.CS_CIEXYZ, ColorSpace.CS_PYCC,
ColorSpace.CS_GRAY};
for (int cspace : cspaces) {
- ICC_Profile icc = ICC_Profile.getInstance(cspace);
+ ICC_Profile builtInProfile = ICC_Profile.getInstance(cspace);
+ ICC_Profile icc = ICC_Profile.getInstance(builtInProfile.getData());
+
testSame(icc);
testCustom(icc);
// some corner cases
diff --git a/test/jdk/java/awt/color/ICC_Profile/ValidateICCHeaderData/ValidateICCHeaderData.java b/test/jdk/java/awt/color/ICC_Profile/ValidateICCHeaderData/ValidateICCHeaderData.java
new file mode 100644
index 00000000000..28831a422b0
--- /dev/null
+++ b/test/jdk/java/awt/color/ICC_Profile/ValidateICCHeaderData/ValidateICCHeaderData.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 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
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8337703
+ * @summary To verify if ICC_Profile's setData() and getInstance() methods
+ * validate header data and throw IAE for invalid values.
+ * @run main ValidateICCHeaderData
+ */
+
+import java.awt.color.ColorSpace;
+import java.awt.color.ICC_Profile;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+
+public class ValidateICCHeaderData {
+ private static ICC_Profile profile;
+
+ private static final boolean DEBUG = false;
+ private static final int VALID_HEADER_SIZE = 128;
+ private static final int HEADER_TAG = ICC_Profile.icSigHead;
+ private static final int PROFILE_CLASS_START_INDEX = ICC_Profile.icHdrDeviceClass;
+ private static final int COLOR_SPACE_START_INDEX = ICC_Profile.icHdrColorSpace;
+ private static final int RENDER_INTENT_START_INDEX = ICC_Profile.icHdrRenderingIntent;
+ private static final int PCS_START_INDEX = ICC_Profile.icHdrPcs;
+
+ private static final int[] VALID_PROFILE_CLASS = new int[] {
+ ICC_Profile.icSigInputClass, ICC_Profile.icSigDisplayClass,
+ ICC_Profile.icSigOutputClass, ICC_Profile.icSigLinkClass,
+ ICC_Profile.icSigAbstractClass, ICC_Profile.icSigColorSpaceClass,
+ ICC_Profile.icSigNamedColorClass
+ };
+
+ private static final int[] VALID_COLOR_SPACE = new int[] {
+ ICC_Profile.icSigXYZData, ICC_Profile.icSigLabData,
+ ICC_Profile.icSigLuvData, ICC_Profile.icSigYCbCrData,
+ ICC_Profile.icSigYxyData, ICC_Profile.icSigRgbData,
+ ICC_Profile.icSigGrayData, ICC_Profile.icSigHsvData,
+ ICC_Profile.icSigHlsData, ICC_Profile.icSigCmykData,
+ ICC_Profile.icSigSpace2CLR, ICC_Profile.icSigSpace3CLR,
+ ICC_Profile.icSigSpace4CLR, ICC_Profile.icSigSpace5CLR,
+ ICC_Profile.icSigSpace6CLR, ICC_Profile.icSigSpace7CLR,
+ ICC_Profile.icSigSpace8CLR, ICC_Profile.icSigSpace9CLR,
+ ICC_Profile.icSigSpaceACLR, ICC_Profile.icSigSpaceBCLR,
+ ICC_Profile.icSigSpaceCCLR, ICC_Profile.icSigSpaceDCLR,
+ ICC_Profile.icSigSpaceECLR, ICC_Profile.icSigSpaceFCLR,
+ ICC_Profile.icSigCmyData
+ };
+
+ private static final int[] VALID_RENDER_INTENT = new int[] {
+ ICC_Profile.icPerceptual, ICC_Profile.icMediaRelativeColorimetric,
+ ICC_Profile.icSaturation, ICC_Profile.icAbsoluteColorimetric
+ };
+
+ private static void createCopyOfBuiltInProfile() {
+ ICC_Profile builtInProfile = ICC_Profile.getInstance(ColorSpace.CS_sRGB);
+ //copy of SRGB BuiltIn Profile that can be modified
+ //using ICC_Profile.setData()
+ profile = ICC_Profile.getInstance(builtInProfile.getData());
+ }
+
+ public static void main(String[] args) throws Exception {
+ createCopyOfBuiltInProfile();
+
+ System.out.println("CASE 1: Testing VALID Profile Classes ...");
+ testValidHeaderData(VALID_PROFILE_CLASS, PROFILE_CLASS_START_INDEX, 4);
+ System.out.println("CASE 1: Passed \n");
+
+ // PCS field validation for Profile class != DEVICE_LINK
+ System.out.println("CASE 2: Testing VALID PCS Type"
+ + " for Profile class != DEVICE_LINK ...");
+ testValidHeaderData(new int[] {ICC_Profile.icSigXYZData, ICC_Profile.icSigLabData},
+ PCS_START_INDEX, 4);
+ System.out.println("CASE 2: Passed \n");
+
+ System.out.println("CASE 3: Testing INVALID PCS Type"
+ + " for Profile class != DEVICE_LINK ...");
+ testInvalidHeaderData(ICC_Profile.icSigCmykData, PCS_START_INDEX, 4);
+ System.out.println("CASE 3: Passed \n");
+
+ System.out.println("CASE 4: Testing DEVICE LINK PROFILE CLASS ...");
+ testValidHeaderData(new int[] {ICC_Profile.icSigLinkClass},
+ PROFILE_CLASS_START_INDEX, 4);
+ //to check if instantiating BufferedImage with
+ //ICC_Profile device class = CLASS_DEVICELINK does not throw IAE.
+ BufferedImage img = new BufferedImage(100, 100,
+ BufferedImage.TYPE_3BYTE_BGR);
+ System.out.println("CASE 4: Passed \n");
+
+ // PCS field validation for Profile class == DEVICE_LINK
+ System.out.println("CASE 5: Testing VALID PCS Type"
+ + " for Profile class == DEVICE_LINK ...");
+ testValidHeaderData(VALID_COLOR_SPACE, PCS_START_INDEX, 4);
+ System.out.println("CASE 5: Passed \n");
+
+ System.out.println("CASE 6: Testing INVALID PCS Type"
+ + " for Profile class == DEVICE_LINK ...");
+ //original icSigLabData = 0x4C616220
+ int invalidSigLabData = 0x4C616221;
+ testInvalidHeaderData(invalidSigLabData, PCS_START_INDEX, 4);
+ System.out.println("CASE 6: Passed \n");
+
+ System.out.println("CASE 7: Testing VALID Color Spaces ...");
+ testValidHeaderData(VALID_COLOR_SPACE, COLOR_SPACE_START_INDEX, 4);
+ System.out.println("CASE 7: Passed \n");
+
+ System.out.println("CASE 8: Testing VALID Rendering Intent ...");
+ testValidHeaderData(VALID_RENDER_INTENT, RENDER_INTENT_START_INDEX, 4);
+ System.out.println("CASE 8: Passed \n");
+
+ System.out.println("CASE 9: Testing INVALID Profile Class ...");
+ //original icSigInputClass = 0x73636E72
+ int invalidSigInputClass = 0x73636E70;
+ testInvalidHeaderData(invalidSigInputClass, PROFILE_CLASS_START_INDEX, 4);
+ System.out.println("CASE 9: Passed \n");
+
+ System.out.println("CASE 10: Testing INVALID Color Space ...");
+ //original icSigXYZData = 0x58595A20
+ int invalidSigXYZData = 0x58595A21;
+ testInvalidHeaderData(invalidSigXYZData, COLOR_SPACE_START_INDEX, 4);
+ System.out.println("CASE 10: Passed \n");
+
+ System.out.println("CASE 11: Testing INVALID Rendering Intent ...");
+ //valid rendering intent values are 0-3
+ int invalidRenderIntent = 5;
+ testInvalidHeaderData(invalidRenderIntent, RENDER_INTENT_START_INDEX, 4);
+ System.out.println("CASE 11: Passed \n");
+
+ System.out.println("CASE 12: Testing INVALID Header Size ...");
+ testInvalidHeaderSize();
+ System.out.println("CASE 12: Passed \n");
+
+ System.out.println("CASE 13: Testing ICC_Profile.getInstance(..)"
+ + " with VALID profile data ...");
+ testProfileCreation(true);
+ System.out.println("CASE 13: Passed \n");
+
+ System.out.println("CASE 14: Testing ICC_Profile.getInstance(..)"
+ + " with INVALID profile data ...");
+ testProfileCreation(false);
+ System.out.println("CASE 14: Passed \n");
+
+ System.out.println("CASE 15: Testing Deserialization of ICC_Profile ...");
+ testDeserialization();
+ System.out.println("CASE 15: Passed \n");
+
+ System.out.println("Successfully completed testing all 15 cases. Test Passed !!");
+ }
+
+ private static void testValidHeaderData(int[] validData, int startIndex,
+ int fieldLength) {
+ for (int value : validData) {
+ setTag(value, startIndex, fieldLength);
+ }
+ }
+
+ private static void testInvalidHeaderData(int invalidData, int startIndex,
+ int fieldLength) {
+ try {
+ setTag(invalidData, startIndex, fieldLength);
+ throw new RuntimeException("Test Failed ! Expected IAE NOT thrown");
+ } catch (IllegalArgumentException iae) {
+ System.out.println("Expected IAE thrown: " + iae.getMessage());
+ }
+ }
+
+ private static void setTag(int value, int startIndex, int fieldLength) {
+ byte[] byteArray;
+ if (startIndex == RENDER_INTENT_START_INDEX) {
+ byteArray = ByteBuffer.allocate(4).putInt(value).array();
+ } else {
+ BigInteger big = BigInteger.valueOf(value);
+ byteArray = (big.toByteArray());
+ }
+
+ if (DEBUG) {
+ System.out.print("Byte Array : ");
+ for (int i = 0; i < byteArray.length; i++) {
+ System.out.print(byteArray[i] + " ");
+ }
+ System.out.println("\n");
+ }
+
+ byte[] iccProfileHeaderData = profile.getData(HEADER_TAG);
+ System.arraycopy(byteArray, 0, iccProfileHeaderData, startIndex, fieldLength);
+ profile.setData(HEADER_TAG, iccProfileHeaderData);
+ }
+
+ private static void testProfileCreation(boolean validCase) {
+ ICC_Profile builtInProfile = ICC_Profile.getInstance(ColorSpace.CS_GRAY);
+ byte[] profileData = builtInProfile.getData();
+
+ int validDeviceClass = ICC_Profile.icSigInputClass;
+ BigInteger big = BigInteger.valueOf(validDeviceClass);
+ //valid case set device class to 0x73636E72 (icSigInputClass)
+ //invalid case set device class to 0x00000000
+ byte[] field = validCase ? big.toByteArray()
+ : ByteBuffer.allocate(4).putInt(0).array();
+ System.arraycopy(field, 0, profileData, PROFILE_CLASS_START_INDEX, 4);
+
+ try {
+ ICC_Profile.getInstance(profileData);
+ if (!validCase) {
+ throw new RuntimeException("Test Failed ! Expected IAE NOT thrown");
+ }
+ } catch (IllegalArgumentException iae) {
+ if (!validCase) {
+ System.out.println("Expected IAE thrown: " + iae.getMessage());
+ } else {
+ throw new RuntimeException("Unexpected IAE thrown");
+ }
+ }
+ }
+
+ private static void testInvalidHeaderSize() {
+ byte[] iccProfileHeaderData = profile.getData(HEADER_TAG);
+ byte[] invalidHeaderSize = new byte[VALID_HEADER_SIZE - 1];
+ System.arraycopy(iccProfileHeaderData, 0,
+ invalidHeaderSize, 0, invalidHeaderSize.length);
+ try {
+ profile.setData(HEADER_TAG, invalidHeaderSize);
+ throw new RuntimeException("Test Failed ! Expected IAE NOT thrown");
+ } catch (IllegalArgumentException iae) {
+ System.out.println("Expected IAE thrown: " + iae.getMessage());
+ }
+ }
+
+ private static void testDeserialization() throws IOException {
+ //invalidSRGB.icc is serialized on older version of JDK
+ //Upon deserialization, the invalid profile is expected to throw IAE
+ try {
+ ICC_Profile.getInstance("./invalidSRGB.icc");
+ throw new RuntimeException("Test Failed ! Expected IAE NOT thrown");
+ } catch (IllegalArgumentException iae) {
+ System.out.println("Expected IAE thrown: " + iae.getMessage());
+ }
+ }
+}
diff --git a/test/jdk/java/awt/color/ICC_Profile/ValidateICCHeaderData/invalidSRGB.icc b/test/jdk/java/awt/color/ICC_Profile/ValidateICCHeaderData/invalidSRGB.icc
new file mode 100644
index 00000000000..1520dac1f1a
Binary files /dev/null and b/test/jdk/java/awt/color/ICC_Profile/ValidateICCHeaderData/invalidSRGB.icc differ
diff --git a/test/jdk/java/awt/color/ICC_ProfileSetNullDataTest.java b/test/jdk/java/awt/color/ICC_ProfileSetNullDataTest.java
index a327fa791fb..3e9b545d576 100644
--- a/test/jdk/java/awt/color/ICC_ProfileSetNullDataTest.java
+++ b/test/jdk/java/awt/color/ICC_ProfileSetNullDataTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 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
@@ -23,6 +23,7 @@
import java.awt.color.ColorSpace;
import java.awt.color.ICC_Profile;
+import java.util.Map;
/**
* @test
@@ -30,22 +31,25 @@
* @summary Test checks behavior of the ICC_Profile.setData(int, byte[])
*/
public final class ICC_ProfileSetNullDataTest {
+ private static final Map colorSpace = Map.of(
+ ColorSpace.CS_sRGB, "CS_sRGB",
+ ColorSpace.CS_PYCC, "CS_PYCC",
+ ColorSpace.CS_GRAY, "CS_GRAY",
+ ColorSpace.CS_CIEXYZ, "CS_CIEXYZ",
+ ColorSpace.CS_LINEAR_RGB, "CS_LINEAR_RGB"
+ );
public static void main(String[] args) {
- test(ICC_Profile.getInstance(ColorSpace.CS_sRGB));
- test(ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB));
- test(ICC_Profile.getInstance(ColorSpace.CS_CIEXYZ));
- test(ICC_Profile.getInstance(ColorSpace.CS_PYCC));
- test(ICC_Profile.getInstance(ColorSpace.CS_GRAY));
- }
-
- private static void test(ICC_Profile profile) {
- byte[] tagData = null;
- try {
- profile.setData(ICC_Profile.icSigCmykData, tagData);
- } catch (IllegalArgumentException e) {
- return;
+ for (int cs : colorSpace.keySet()) {
+ ICC_Profile builtInProfile = ICC_Profile.getInstance(cs);
+ ICC_Profile profile = ICC_Profile.getInstance(builtInProfile.getData());
+ try {
+ profile.setData(ICC_Profile.icSigCmykData, null);
+ throw new RuntimeException("IAE expected, but not thrown for "
+ + "ColorSpace: " + colorSpace.get(cs));
+ } catch (IllegalArgumentException e) {
+ // IAE expected
+ }
}
- throw new RuntimeException("IllegalArgumentException expected");
}
}
diff --git a/test/jdk/sun/java2d/cmm/ProfileOp/SetDataTest.java b/test/jdk/sun/java2d/cmm/ProfileOp/SetDataTest.java
index dc86de98bfa..6850061b225 100644
--- a/test/jdk/sun/java2d/cmm/ProfileOp/SetDataTest.java
+++ b/test/jdk/sun/java2d/cmm/ProfileOp/SetDataTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
@@ -29,7 +29,6 @@
* @run main SetDataTest
*/
-
import java.util.ArrayList;
import java.util.List;
import java.awt.color.ICC_Profile;
@@ -47,7 +46,8 @@ static class TestCase {
static byte[] invalidTRCData;
static {
- profile = ICC_Profile.getInstance(CS_GRAY);
+ ICC_Profile builtInProfile = ICC_Profile.getInstance(CS_GRAY);
+ profile = ICC_Profile.getInstance(builtInProfile.getData());
validTRCdata = profile.getData(icSigGrayTRCTag);
invalidTRCData = new byte[]{0x42, 0x42, 0x42, 0x42, 1, 3, 4, 6,};
}