예제 #1
0
  /**
   * 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;
    }
  }
예제 #2
0
 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();
 }
예제 #3
0
 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();
     }
   }
 }
예제 #4
0
    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
      }
    }
예제 #5
0
  /**
   * 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;
  }
예제 #6
0
  /**
   * 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;
  }
예제 #7
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);
  }
예제 #8
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;
    }
  }
예제 #9
0
  /**
   * 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);
  }