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 a11e0bd293b..5574b3c01f8 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 @@ -107,6 +107,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), @@ -114,8 +122,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)); @@ -763,14 +771,20 @@ public sealed class ICC_Profile implements Serializable */ 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; } /** @@ -1131,17 +1145,34 @@ public sealed class ICC_Profile implements Serializable * 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); } 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 final class SetHeaderInfo { 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_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 @@ import java.awt.color.ICC_Profile; * @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 @@ public class SetDataTest { 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,}; }