Beispiel #1
0
 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()));
   }
 }
Beispiel #2
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();
     }
   }
 }
Beispiel #3
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;
    }
  }
Beispiel #4
0
 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;
  }
Beispiel #8
0
  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);
  }
Beispiel #10
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
      }
    }
Beispiel #11
0
 /**
  * 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;
 }
Beispiel #12
0
  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();
    }
  }
Beispiel #13
0
  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;
  }
Beispiel #14
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;
  }
  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;
    }
  }
Beispiel #16
0
  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;
  }
Beispiel #17
0
  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));
    }
  }
Beispiel #19
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);
  }
Beispiel #20
0
 private static class GRAY {
   private static final byte[] header =
       ICC_Profile.getInstance(ColorSpace.CS_GRAY).getData(ICC_Profile.icSigHead);
 }
Beispiel #21
0
 private static class LINEAR_RGB {
   private static final byte[] header =
       ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB).getData(ICC_Profile.icSigHead);
 }
Beispiel #22
0
  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();
  }