mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-03 03:25:28 -04:00
Added caching of AdobeRGB1998 and Generic CMYK profiles for use in getInstance methods.
Added debug info. Refactored profile reading slightly.
This commit is contained in:
parent
ba4ff3dc45
commit
6cc97e3721
@ -39,6 +39,7 @@ import java.awt.color.ICC_ColorSpace;
|
|||||||
import java.awt.color.ICC_Profile;
|
import java.awt.color.ICC_Profile;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
@ -68,6 +69,8 @@ import java.util.Properties;
|
|||||||
* @version $Id: ColorSpaces.java,v 1.0 24.01.11 17.51 haraldk Exp$
|
* @version $Id: ColorSpaces.java,v 1.0 24.01.11 17.51 haraldk Exp$
|
||||||
*/
|
*/
|
||||||
public final class ColorSpaces {
|
public final class ColorSpaces {
|
||||||
|
private final static boolean DEBUG = "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.color.debug"));
|
||||||
|
|
||||||
// NOTE: java.awt.color.ColorSpace.CS_* uses 1000-1004, we'll use 5000+ to not interfere with future additions
|
// NOTE: java.awt.color.ColorSpace.CS_* uses 1000-1004, we'll use 5000+ to not interfere with future additions
|
||||||
|
|
||||||
/** The Adobe RGB 1998 (or compatible) color space. Either read from disk or built-in. */
|
/** The Adobe RGB 1998 (or compatible) color space. Either read from disk or built-in. */
|
||||||
@ -76,6 +79,11 @@ public final class ColorSpaces {
|
|||||||
/** A best-effort "generic" CMYK color space. Either read from disk or built-in. */
|
/** A best-effort "generic" CMYK color space. Either read from disk or built-in. */
|
||||||
public static final int CS_GENERIC_CMYK = 5001;
|
public static final int CS_GENERIC_CMYK = 5001;
|
||||||
|
|
||||||
|
// Weak references to hold the color spaces while cached
|
||||||
|
private static WeakReference<ICC_Profile> adobeRGB1998 = new WeakReference<ICC_Profile>(null);
|
||||||
|
private static WeakReference<ICC_Profile> genericCMYK = new WeakReference<ICC_Profile>(null);
|
||||||
|
|
||||||
|
// Cache for the latest used color spaces
|
||||||
private static final Map<Key, ICC_ColorSpace> cache = new LRUHashMap<Key, ICC_ColorSpace>(10);
|
private static final Map<Key, ICC_ColorSpace> cache = new LRUHashMap<Key, ICC_ColorSpace>(10);
|
||||||
|
|
||||||
private ColorSpaces() {}
|
private ColorSpaces() {}
|
||||||
@ -122,16 +130,16 @@ public final class ColorSpaces {
|
|||||||
if (profile.getColorSpaceType() == ColorSpace.TYPE_RGB && Arrays.equals(profileHeader, sRGB.header)) {
|
if (profile.getColorSpaceType() == ColorSpace.TYPE_RGB && Arrays.equals(profileHeader, sRGB.header)) {
|
||||||
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_sRGB);
|
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_sRGB);
|
||||||
}
|
}
|
||||||
if (profile.getColorSpaceType() == ColorSpace.TYPE_GRAY && Arrays.equals(profileHeader, GRAY.header)) {
|
else if (profile.getColorSpaceType() == ColorSpace.TYPE_GRAY && Arrays.equals(profileHeader, GRAY.header)) {
|
||||||
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_GRAY);
|
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_GRAY);
|
||||||
}
|
}
|
||||||
if (profile.getColorSpaceType() == ColorSpace.TYPE_3CLR && Arrays.equals(profileHeader, PYCC.header)) {
|
else if (profile.getColorSpaceType() == ColorSpace.TYPE_3CLR && Arrays.equals(profileHeader, PYCC.header)) {
|
||||||
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_PYCC);
|
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_PYCC);
|
||||||
}
|
}
|
||||||
if (profile.getColorSpaceType() == ColorSpace.TYPE_RGB && Arrays.equals(profileHeader, LINEAR_RGB.header)) {
|
else if (profile.getColorSpaceType() == ColorSpace.TYPE_RGB && Arrays.equals(profileHeader, LINEAR_RGB.header)) {
|
||||||
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
|
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
|
||||||
}
|
}
|
||||||
if (profile.getColorSpaceType() == ColorSpace.TYPE_XYZ && Arrays.equals(profileHeader, CIEXYZ.header)) {
|
else if (profile.getColorSpaceType() == ColorSpace.TYPE_XYZ && Arrays.equals(profileHeader, CIEXYZ.header)) {
|
||||||
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);
|
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,6 +186,7 @@ public final class ColorSpaces {
|
|||||||
// This is particularly annoying, as the byte copying isn't really necessary,
|
// This is particularly annoying, as the byte copying isn't really necessary,
|
||||||
// except the getRenderingIntent method is package protected in java.awt.color
|
// except the getRenderingIntent method is package protected in java.awt.color
|
||||||
byte[] data = profile.getData(ICC_Profile.icSigHead);
|
byte[] data = profile.getData(ICC_Profile.icSigHead);
|
||||||
|
|
||||||
return data[ICC_Profile.icHdrRenderingIntent] != 0;
|
return data[ICC_Profile.icHdrRenderingIntent] != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,7 +204,7 @@ public final class ColorSpaces {
|
|||||||
* @see ColorSpaces#CS_GENERIC_CMYK
|
* @see ColorSpaces#CS_GENERIC_CMYK
|
||||||
*/
|
*/
|
||||||
public static ColorSpace getColorSpace(int colorSpace) {
|
public static ColorSpace getColorSpace(int colorSpace) {
|
||||||
// TODO: Use internal cache for AdobeRGB and CMYK! (needs mapping between ID and Key...)
|
ICC_Profile profile;
|
||||||
|
|
||||||
switch (colorSpace) {
|
switch (colorSpace) {
|
||||||
// Default cases for convenience
|
// Default cases for convenience
|
||||||
@ -207,38 +216,49 @@ public final class ColorSpaces {
|
|||||||
return ColorSpace.getInstance(colorSpace);
|
return ColorSpace.getInstance(colorSpace);
|
||||||
|
|
||||||
case CS_ADOBE_RGB_1998:
|
case CS_ADOBE_RGB_1998:
|
||||||
try {
|
profile = adobeRGB1998.get();
|
||||||
String profile = Profiles.MAP.getProperty("ADOBE_RGB_1998");
|
|
||||||
return createColorSpace(ICC_Profile.getInstance(profile));
|
if (profile == null) {
|
||||||
}
|
// Try to get system default or user-defined profile
|
||||||
catch (IOException ignore) {
|
profile = readProfileFromPath(Profiles.getPath("ADOBE_RGB_1998"));
|
||||||
|
|
||||||
|
if (profile == null) {
|
||||||
|
// Fall back to the bundled ClayRGB1998 public domain Adobe RGB 1998 compatible profile,
|
||||||
|
// identical for all practical purposes
|
||||||
|
profile = readProfileFromClasspathResource("/profiles/ClayRGB1998.icc");
|
||||||
|
|
||||||
|
if (profile == null) {
|
||||||
|
// Should never happen given we now bundle fallback profile...
|
||||||
|
throw new IllegalStateException("Could not read AdobeRGB1998 profile");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
adobeRGB1998 = new WeakReference<ICC_Profile>(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to the bundled ClayRGB1998 public domain Adobe RGB 1998 compatible profile,
|
return createColorSpace(profile);
|
||||||
// identical for all practical purposes
|
|
||||||
InputStream stream = ColorSpaces.class.getResourceAsStream("/profiles/ClayRGB1998.icc");
|
|
||||||
try {
|
|
||||||
return createColorSpace(ICC_Profile.getInstance(stream));
|
|
||||||
}
|
|
||||||
catch (IOException ignore) {
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
FileUtil.close(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should never happen given we now bundle the profile...
|
|
||||||
throw new RuntimeException("Could not read AdobeRGB1998 profile");
|
|
||||||
|
|
||||||
case CS_GENERIC_CMYK:
|
case CS_GENERIC_CMYK:
|
||||||
try {
|
profile = genericCMYK.get();
|
||||||
String profile = Profiles.MAP.getProperty("GENERIC_CMYK");
|
|
||||||
return createColorSpace(ICC_Profile.getInstance(profile));
|
if (profile == null) {
|
||||||
}
|
// Try to get system default or user-defined profile
|
||||||
catch (IOException ignore) {
|
profile = readProfileFromPath(Profiles.getPath("GENERIC_CMYK"));
|
||||||
|
|
||||||
|
if (profile == null) {
|
||||||
|
if (DEBUG) {
|
||||||
|
System.out.println("Using fallback profile");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to generic CMYK ColorSpace, which is *insanely slow* using ColorConvertOp... :-P
|
||||||
|
return CMYKColorSpace.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
genericCMYK = new WeakReference<ICC_Profile>(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to generic CMYK ColorSpace, which is *insanely slow* using ColorConvertOp... :-P
|
return createColorSpace(profile);
|
||||||
return CMYKColorSpace.getInstance();
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
// TODO: Allow more customizable models based on the config file?
|
// TODO: Allow more customizable models based on the config file?
|
||||||
@ -247,6 +267,50 @@ public final class ColorSpaces {
|
|||||||
throw new IllegalArgumentException(String.format("Unsupported color space: %s", colorSpace));
|
throw new IllegalArgumentException(String.format("Unsupported color space: %s", colorSpace));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ICC_Profile readProfileFromClasspathResource(final String profilePath) {
|
||||||
|
InputStream stream = ColorSpaces.class.getResourceAsStream(profilePath);
|
||||||
|
|
||||||
|
if (stream != null) {
|
||||||
|
if (DEBUG) {
|
||||||
|
System.out.println("Loading profile from classpath resource: " + profilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
return ICC_Profile.getInstance(stream);
|
||||||
|
}
|
||||||
|
catch (IOException ignore) {
|
||||||
|
if (DEBUG) {
|
||||||
|
ignore.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
FileUtil.close(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ICC_Profile readProfileFromPath(final String profilePath) {
|
||||||
|
if (profilePath != null) {
|
||||||
|
if (DEBUG) {
|
||||||
|
System.out.println("Loading profilePath from: " + profilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return ICC_Profile.getInstance(profilePath);
|
||||||
|
}
|
||||||
|
catch (IOException ignore) {
|
||||||
|
if (DEBUG) {
|
||||||
|
ignore.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private static final class Key {
|
private static final class Key {
|
||||||
private final byte[] data;
|
private final byte[] data;
|
||||||
|
|
||||||
@ -283,9 +347,9 @@ public final class ColorSpaces {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class Profiles {
|
private static class Profiles {
|
||||||
static final Properties MAP = loadProfiles(Platform.os());
|
private static final Properties PROFILES = loadProfiles(Platform.os());
|
||||||
|
|
||||||
private static Properties loadProfiles(Platform.OperatingSystem os) {
|
private static Properties loadProfiles(final Platform.OperatingSystem os) {
|
||||||
Properties systemDefaults;
|
Properties systemDefaults;
|
||||||
try {
|
try {
|
||||||
systemDefaults = SystemUtil.loadProperties(ColorSpaces.class, "com/twelvemonkeys/imageio/color/icc_profiles_" + os);
|
systemDefaults = SystemUtil.loadProperties(ColorSpaces.class, "com/twelvemonkeys/imageio/color/icc_profiles_" + os);
|
||||||
@ -305,7 +369,16 @@ public final class ColorSpaces {
|
|||||||
catch (IOException ignore) {
|
catch (IOException ignore) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
System.out.println("User ICC profiles: " + profiles);
|
||||||
|
System.out.println("System ICC profiles : " + systemDefaults);
|
||||||
|
}
|
||||||
|
|
||||||
return profiles;
|
return profiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getPath(final String profileName) {
|
||||||
|
return PROFILES.getProperty(profileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user