/** * Write out the given profile to the stream, embedded in the necessary number of APP2 segments, * per the ICC spec. This is the only mechanism for writing an ICC profile to a stream. */ static void writeICC(ICC_Profile profile, ImageOutputStream ios) throws IOException { int LENGTH_LENGTH = 2; final String ID = "ICC_PROFILE"; int ID_LENGTH = ID.length() + 1; // spec says it's null-terminated int COUNTS_LENGTH = 2; int MAX_ICC_CHUNK_SIZE = 65535 - LENGTH_LENGTH - ID_LENGTH - COUNTS_LENGTH; byte[] data = profile.getData(); int numChunks = data.length / MAX_ICC_CHUNK_SIZE; if ((data.length % MAX_ICC_CHUNK_SIZE) != 0) { numChunks++; } int chunkNum = 1; int offset = 0; for (int i = 0; i < numChunks; i++) { int dataLength = Math.min(data.length - offset, MAX_ICC_CHUNK_SIZE); int segLength = dataLength + COUNTS_LENGTH + ID_LENGTH + LENGTH_LENGTH; ios.write(0xff); ios.write(JPEG.APP2); MarkerSegment.write2bytes(ios, segLength); byte[] id = ID.getBytes("US-ASCII"); ios.write(id); ios.write(0); // Null-terminate the string ios.write(chunkNum++); ios.write(numChunks); ios.write(data, offset, dataLength); offset += dataLength; } }
public LCMS_Profile(ICC_Profile iccProfile) { Long handle = (Long) profileCache.get(iccProfile); if (handle == null) { byte data[] = iccProfile.getData(); cmsProfileHandle = LCMS.cmsOpenProfileFromMem(data, data.length); profileCache.put(iccProfile, new Long(cmsProfileHandle)); } else cmsProfileHandle = handle.longValue(); }
ICCMarkerSegment(Node node) throws IIOInvalidTreeException { super(JPEG.APP2); if (node instanceof IIOMetadataNode) { IIOMetadataNode ourNode = (IIOMetadataNode) node; ICC_Profile prof = (ICC_Profile) ourNode.getUserObject(); if (prof != null) { // May be null profile = prof.getData(); } } }
public Profile(ICC_Profile iccProfile) { RCHandle handle = (RCHandle) profileCache.get(iccProfile); if (handle != null && handle.increment() > 1) cmsProfile = handle; else { byte data[] = iccProfile.getData(); cmsProfile = new RCHandle(cmsOpenProfileFromMem(data, data.length)); profileCache.put(iccProfile, cmsProfile); cmsProfile.increment(); // for the cache reference } }
/** * Fixes problematic 'XYZ ' tags in Corbis RGB profile. * * @return {@code true} if found and fixed, otherwise {@code false} for short-circuiting to avoid * unnecessary array copying. */ private static boolean fixProfileXYZTag(ICC_Profile profile, final int tagSignature) { byte[] data = profile.getData(tagSignature); // The CMM expects 0x64 65 73 63 ('XYZ ') but is 0x17 A5 05 B8..? if (data != null && Arrays.equals(Arrays.copyOfRange(data, 0, 4), CORBIS_RGB_ALTERNATE_XYZ)) { data[0] = 'X'; data[1] = 'Y'; data[2] = 'Z'; data[3] = ' '; profile.setData(tagSignature, data); return true; } return false; }
/** * Tests whether an ICC color profile is known to cause problems for {@link * java.awt.image.ColorConvertOp}. * * <p><em> Note that this method only tests if a color conversion using this profile is known to * fail. There's no guarantee that the color conversion will succeed even if this method returns * {@code false}. </em> * * @param profile the ICC color profile. May not be {@code null}. * @return {@code true} if known to be offending, {@code false} otherwise * @throws IllegalArgumentException if {@code profile} is {@code null} */ public static boolean isOffendingColorProfile(final ICC_Profile profile) { Validate.notNull(profile, "profile"); // NOTE: // Several embedded ICC color profiles are non-compliant with Java pre JDK7 and throws // CMMException // The problem with these embedded ICC profiles seems to be the rendering intent // being 1 (01000000) - "Media Relative Colormetric" in the offending profiles, // and 0 (00000000) - "Perceptual" in the good profiles // (that is 1 single bit of difference right there.. ;-) // See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7064516 // This is particularly annoying, as the byte copying isn't really necessary, // except the getRenderingIntent method is package protected in java.awt.color byte[] data = profile.getData(ICC_Profile.icSigHead); return data[ICC_Profile.icHdrRenderingIntent] != 0; }
/** * Creates an ICC color space from the given ICC color profile. * * <p>For standard Java color spaces, the built-in instance is returned. Otherwise, color spaces * are looked up from cache and created on demand. * * @param profile the ICC color profile. May not be {@code null}. * @return an ICC color space * @throws IllegalArgumentException if {@code profile} is {@code null} */ public static ICC_ColorSpace createColorSpace(final ICC_Profile profile) { Validate.notNull(profile, "profile"); byte[] profileHeader = profile.getData(ICC_Profile.icSigHead); ICC_ColorSpace cs = getInternalCS(profile.getColorSpaceType(), profileHeader); if (cs != null) { return cs; } // Special case for color profiles with rendering intent != 0, see isOffendingColorProfile // method // NOTE: Rendering intent is really a 4 byte value, but legal values are 0-3 // (ICC1v42_2006_05_1.pdf, 7.2.15, p. 19) if (profileHeader[ICC_Profile.icHdrRenderingIntent] != 0) { profileHeader[ICC_Profile.icHdrRenderingIntent] = 0; // Test again if this is an internal CS cs = getInternalCS(profile.getColorSpaceType(), profileHeader); if (cs != null) { return cs; } // NOTE: The intent change breaks JDK7: Seems to be a bug in ICC_Profile.getData/setData, // as calling it with unchanged header data, still breaks when creating new ICC_ColorSpace... // However, we simply skip that, as JDK7 handles the rendering intents already. if (!JDK_HANDLES_RENDERING_INTENTS) { // Fix profile before lookup/create profile.setData(ICC_Profile.icSigHead, profileHeader); } } // Special handling to detect problematic Corbis RGB ICC Profile. // This makes sure tags that are expected to be of type 'XYZ ' really have this expected type. // Should leave other ICC profiles unchanged. if (fixProfileXYZTag(profile, ICC_Profile.icSigMediaWhitePointTag)) { fixProfileXYZTag(profile, ICC_Profile.icSigRedColorantTag); fixProfileXYZTag(profile, ICC_Profile.icSigGreenColorantTag); fixProfileXYZTag(profile, ICC_Profile.icSigBlueColorantTag); } return getCachedOrCreateCS(profile, profileHeader); }
public ICCColorSpaceExt(ICC_Profile p, int intent) { super(p); this.intent = intent; switch (intent) { case AUTO: case RELATIVE_COLORIMETRIC: case ABSOLUTE_COLORIMETRIC: case SATURATION: case PERCEPTUAL: break; default: throw new IllegalArgumentException(); } /** Apply the requested intent into the profile */ if (intent != AUTO) { byte[] hdr = p.getData(ICC_Profile.icSigHead); hdr[ICC_Profile.icHdrRenderingIntent] = (byte) intent; } }
/** * Tests whether an ICC color profile is equal to the default sRGB profile. * * @param profile the ICC profile to test. May not be {@code null}. * @return {@code true} if {@code profile} is equal to the default sRGB profile. * @throws IllegalArgumentException if {@code profile} is {@code null} * @see java.awt.color.ColorSpace#isCS_sRGB() */ public static boolean isCS_sRGB(final ICC_Profile profile) { Validate.notNull(profile, "profile"); return profile.getColorSpaceType() == ColorSpace.TYPE_RGB && Arrays.equals(profile.getData(ICC_Profile.icSigHead), sRGB.header); }