public static void debug(String message, ICC_Profile value) { debug("ICC_Profile " + message + ": " + ((value == null) ? "null" : value.toString())); if (value != null) { debug("\t getProfileClass: " + byteQuadToString(value.getProfileClass())); debug("\t getPCSType: " + byteQuadToString(value.getPCSType())); debug("\t getColorSpaceType() : " + byteQuadToString(value.getColorSpaceType())); } }
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(); } } }
/** * 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; } }
IIOMetadataNode getNativeNode() { IIOMetadataNode node = new IIOMetadataNode("app2ICC"); if (profile != null) { node.setUserObject(ICC_Profile.getInstance(profile)); } return node; }
private ICC_Profile buildICCProfile( final ImageInfo info, final ColorSpace colorSpace, final ByteArrayOutputStream iccStream) throws IOException { if (iccStream != null && iccStream.size() > 0) { if (log.isDebugEnabled()) { log.debug("Effective ICC profile size: " + iccStream.size()); } final int alignment = 4; final int padding = (alignment - iccStream.size() % alignment) % alignment; if (padding != 0) { try { iccStream.write(new byte[padding]); } catch (final IOException ioe) { throw new IOException("Error while aligning ICC stream: " + ioe.getMessage()); } } ICC_Profile iccProfile = null; try { iccProfile = ColorProfileUtil.getICC_Profile(iccStream.toByteArray()); if (log.isDebugEnabled()) { log.debug("JPEG has an ICC profile: " + iccProfile.toString()); } } catch (final IllegalArgumentException iae) { log.warn( "An ICC profile is present in the JPEG file but it is invalid (" + iae.getMessage() + "). The color profile will be ignored. (" + info.getOriginalURI() + ")"); return null; } if (iccProfile.getNumComponents() != colorSpace.getNumComponents()) { log.warn( "The number of components of the ICC profile (" + iccProfile.getNumComponents() + ") doesn't match the image (" + colorSpace.getNumComponents() + "). Ignoring the ICC color profile."); return null; } else { return iccProfile; } } else { return null; // no ICC profile available } }
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(); }
/** * 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; }
public static String getDebug(String message, ICC_Profile value) { StringBuffer result = new StringBuffer(); result.append( getDebug("ICC_Profile " + message + ": " + ((value == null) ? "null" : value.toString())) + newline); if (value != null) { result.append( getDebug("\t getProfileClass: " + byteQuadToString(value.getProfileClass())) + newline); result.append(getDebug("\t getPCSType: " + byteQuadToString(value.getPCSType())) + newline); result.append( getDebug("\t getColorSpaceType() : " + byteQuadToString(value.getColorSpaceType())) + newline); } return result.toString(); }
/** * 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 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 } }
/** * Create a Java colorspace for this colorspace. * * @return A color space that can be used for Java AWT operations. * @throws IOException If there is an error creating the color space. */ public ColorSpace createColorSpace() throws IOException { InputStream profile = null; ColorSpace cSpace = null; try { profile = stream.getUnfilteredStream(); ICC_Profile iccProfile = ICC_Profile.getInstance(profile); cSpace = new ICC_ColorSpace(iccProfile); } finally { if (profile != null) { profile.close(); } } return cSpace; }
public static void main(String args[]) { try { ICC_Profile inProfile = ICC_Profile.getInstance("/System/Library/ColorSync/Profiles/AdobeRGB1998.icc"); ICC_Profile outProfile = ICC_Profile.getInstance("/Library/ColorSync/Profiles/CIE 1931 D50 Gamma 1.icm"); Profile cmsOutProfile = new Profile(outProfile); Profile cmsInProfile = new Profile(inProfile); BufferedImage inputImage = ImageIO.read(new File("/Stuff/Reference/small-q60-adobergb.TIF")); ShortInterleavedRaster inputRaster = (ShortInterleavedRaster) inputImage.getTile(0, 0); ColorSpace outCS = new ICC_ColorSpace(outProfile); ColorModel outCM = new ComponentColorModel(outCS, false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT); ShortInterleavedRaster outputRaster = (ShortInterleavedRaster) outCM.createCompatibleWritableRaster(inputImage.getWidth(), inputImage.getHeight()); BufferedImage outputImage = new BufferedImage(outCM, outputRaster, false, null); Transform cmsTransform = new Transform( cmsInProfile, TYPE_RGB_16, cmsOutProfile, TYPE_RGB_16, INTENT_PERCEPTUAL, 0); cmsTransform.doTransform(inputRaster, outputRaster); ImageIO.write(outputImage, "TIF", new File("/Stuff/small-q60-CIED65.TIF")); cmsTransform.dispose(); cmsOutProfile.dispose(); cmsInProfile.dispose(); // System.out.println("Profile: " + hProfile + ", " + ); } catch (IOException e) { e.printStackTrace(); } }
private static ICC_Profile readProfileFromPath(final String profilePath) { if (profilePath != null) { if (DEBUG) { System.out.println("Loading profile from: " + profilePath); } try { return ICC_Profile.getInstance(profilePath); } catch (IOException ignore) { if (DEBUG) { ignore.printStackTrace(); } } } return null; }
/** * 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; }
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; } }
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; }
public synchronized void init() { if (inited) { return; } byte[] in; try { stream.init(); in = stream.getDecodedStreamBytes(0); if (logger.isLoggable(Level.FINEST)) { String content = Utils.convertByteArrayToByteString(in); logger.finest("Content = " + content); } if (in != null) { ICC_Profile profile = ICC_Profile.getInstance(in); colorSpace = new ICC_ColorSpace(profile); } } catch (Exception e) { logger.log(Level.FINE, "Error Processing ICCBased Colour Profile", e); } inited = true; }
/** * Method called by the processAllColorSpace if the ColorSpace to check is a ICCBased color space. * Because this kind of ColorSpace can have alternate color space, the processAllColorSpace is * called to check this alternate color space. (Pattern is forbidden as Alternate Color Space) * * @param colorSpace the color space object to check. */ protected void processICCBasedColorSpace(PDColorSpace colorSpace) { PDICCBased iccBased = (PDICCBased) colorSpace; try { ICC_Profile.getInstance(iccBased.getPDStream().createInputStream()); PDColorSpace altpdcs = iccBased.getAlternateColorSpace(); if (altpdcs != null) { ColorSpaces altCsId = ColorSpaces.valueOf(altpdcs.getName()); if (altCsId == ColorSpaces.Pattern) { context.addValidationError( new ValidationError( ERROR_GRAPHIC_INVALID_PATTERN_COLOR_SPACE_FORBIDDEN, "Pattern is forbidden as AlternateColorSpace of a ICCBased")); } /* * According to the ISO-19005-1:2005 * * A conforming reader shall render ICCBased colour spaces as specified by the ICC specification, * and shall not use the Alternate colour space specified in an ICC profile stream dictionary * * We don't check the alternate ColorSpaces */ } } catch (IllegalArgumentException e) { // this is not a ICC_Profile context.addValidationError( new ValidationError( ERROR_GRAPHIC_INVALID_COLOR_SPACE_ICCBASED, "ICCBased color space is invalid: " + e.getMessage(), e)); } catch (IOException e) { context.addValidationError( new ValidationError( ERROR_GRAPHIC_INVALID_COLOR_SPACE, "Unable to read ICCBase color space: " + e.getMessage(), e)); } }
/** * 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); }
private static class GRAY { private static final byte[] header = ICC_Profile.getInstance(ColorSpace.CS_GRAY).getData(ICC_Profile.icSigHead); }
private static class LINEAR_RGB { private static final byte[] header = ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB).getData(ICC_Profile.icSigHead); }
public Iterator getImageTypes(int imageIndex) throws IIOException { List l; // List of ImageTypeSpecifiers Integer imageIndexInteger = new Integer(imageIndex); if (imageTypeMap.containsKey(imageIndexInteger)) { // Return the cached ITS List. l = (List) imageTypeMap.get(imageIndexInteger); } else { // Create a new ITS List. l = new ArrayList(1); // Create the ITS and cache if for later use so that this method // always returns an Iterator containing the same ITS objects. seekToImage(imageIndex); ImageTypeSpecifier itsRaw = TIFFDecompressor.getRawImageTypeSpecifier( photometricInterpretation, compression, samplesPerPixel, bitsPerSample, sampleFormat, extraSamples, colorMap); // Check for an ICCProfile field. TIFFField iccProfileField = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_ICC_PROFILE); // If an ICCProfile field is present change the ImageTypeSpecifier // to use it if the data layout is component type. if (iccProfileField != null && itsRaw.getColorModel() instanceof ComponentColorModel) { // Create a ColorSpace from the profile. byte[] iccProfileValue = iccProfileField.getAsBytes(); ICC_Profile iccProfile = ICC_Profile.getInstance(iccProfileValue); ICC_ColorSpace iccColorSpace = new ICC_ColorSpace(iccProfile); // Get the raw sample and color information. ColorModel cmRaw = itsRaw.getColorModel(); ColorSpace csRaw = cmRaw.getColorSpace(); SampleModel smRaw = itsRaw.getSampleModel(); // Get the number of samples per pixel and the number // of color components. int numBands = smRaw.getNumBands(); int numComponents = iccColorSpace.getNumComponents(); // Replace the ColorModel with the ICC ColorModel if the // numbers of samples and color components are amenable. if (numBands == numComponents || numBands == numComponents + 1) { // Set alpha flags. boolean hasAlpha = numComponents != numBands; boolean isAlphaPre = hasAlpha && cmRaw.isAlphaPremultiplied(); // Create a ColorModel of the same class and with // the same transfer type. ColorModel iccColorModel = new ComponentColorModel( iccColorSpace, cmRaw.getComponentSize(), hasAlpha, isAlphaPre, cmRaw.getTransparency(), cmRaw.getTransferType()); // Prepend the ICC profile-based ITS to the List. The // ColorModel and SampleModel are guaranteed to be // compatible as the old and new ColorModels are both // ComponentColorModels with the same transfer type // and the same number of components. l.add(new ImageTypeSpecifier(iccColorModel, smRaw)); // Append the raw ITS to the List if and only if its // ColorSpace has the same type and number of components // as the ICC ColorSpace. if (csRaw.getType() == iccColorSpace.getType() && csRaw.getNumComponents() == iccColorSpace.getNumComponents()) { l.add(itsRaw); } } else { // ICCProfile not compatible with SampleModel. // Append the raw ITS to the List. l.add(itsRaw); } } else { // No ICCProfile field or raw ColorModel not component. // Append the raw ITS to the List. l.add(itsRaw); } // Cache the ITS List. imageTypeMap.put(imageIndexInteger, l); } return l.iterator(); }