package org.icepdf.core.pobjects.graphics.RasterOps; import java.awt.*; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.DataBufferByte; import java.awt.image.Raster; import java.awt.image.RasterOp; import java.awt.image.WritableRaster; /** * The basic idea is that we do a fuzzy colour conversion from YCCK to * CMYK. The conversion is not perfect but when converted again from * CMYK to RGB the result is much better then going directly from YCCK to * RGB. * NOTE: no masking here, as it is done later in the call to * * @since 5.1 */ public class YCCKRasterOp implements RasterOp { private RenderingHints hints = null; public YCCKRasterOp(RenderingHints hints) { this.hints = hints; } public WritableRaster filter(Raster src, WritableRaster dest) { if (dest == null) dest = src.createCompatibleWritableRaster(); // may have to add some instance of checks byte[] srcPixels = ((DataBufferByte) src.getDataBuffer()).getData(); byte[] destPixels = ((DataBufferByte) dest.getDataBuffer()).getData(); double Y, Cb, Cr, K; double lastY = -1, lastCb = -1, lastCr = -1, lastK = -1; int c = 0, m = 0, y2 = 0, k = 0; int bands = src.getNumBands(); for (int pixel = 0; pixel < srcPixels.length; pixel += bands) { Y = (srcPixels[pixel] & 0xff); Cb = (srcPixels[pixel + 1] & 0xff); Cr = (srcPixels[pixel + 2] & 0xff); K = (srcPixels[pixel + 3] & 0xff); if (!(lastY == Y && lastCb == Cb && lastCr == Cr && lastK == K)) { // intel codecs, http://software.intel.com/sites/products/documentation/hpc/ipp/ippi/ippi_ch6/ch6_color_models.html // Intel IPP conversion for JPEG codec. c = 255 - (int) (Y + (1.402 * Cr) - 179.456); m = 255 - (int) (Y - (0.34414 * Cb) - (0.71413636 * Cr) + 135.45984); y2 = 255 - (int) (Y + (1.7718 * Cb) - 226.816); k = (int) K; c = clip(0, 255, c); m = clip(0, 255, m); y2 = clip(0, 255, y2); } lastY = Y; lastCb = Cb; lastCr = Cr; lastK = K; destPixels[pixel] = (byte) (c & 0xff); destPixels[pixel + 1] = (byte) (m & 0xff); destPixels[pixel + 2] = (byte) (y2 & 0xff); destPixels[pixel + 3] = (byte) (k & 0xff); } return dest; } public Rectangle2D getBounds2D(Raster src) { return null; } public WritableRaster createCompatibleDestRaster(Raster src) { return src.createCompatibleWritableRaster(); } public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) { if (dstPt == null) dstPt = (Point2D) srcPt.clone(); else dstPt.setLocation(srcPt); return dstPt; } public RenderingHints getRenderingHints() { return hints; } /** * Clips the value according to the specified floor and ceiling. * * @param floor floor value of clip * @param ceiling ceiling value of clip * @param value value to clip. * @return clipped value. */ private static int clip(int floor, int ceiling, int value) { if (value < floor) { value = floor; } if (value > ceiling) { value = ceiling; } return value; } /* // older method for conversion, keeping for historical context. protected static void alterRasterYCCK2BGRA(WritableRaster wr, BufferedImage smaskImage, BufferedImage maskImage, float[] decode, int bitsPerComponent) { Raster smaskRaster = null; int smaskWidth = 0; int smaskHeight = 0; if (smaskImage != null) { smaskRaster = smaskImage.getRaster(); smaskWidth = smaskRaster.getWidth(); smaskHeight = smaskRaster.getHeight(); } Raster maskRaster = null; int maskWidth = 0; int maskHeight = 0; if (maskImage != null) { maskRaster = maskImage.getRaster(); maskWidth = maskRaster.getWidth(); maskHeight = maskRaster.getHeight(); } byte[] dataValues = new byte[wr.getNumBands()]; float[] origValues = new float[wr.getNumBands()]; double[] rgbaValues = new double[4]; int width = wr.getWidth(); int height = wr.getHeight(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { // apply decode param. ImageUtility.getNormalizedComponents( (byte[]) wr.getDataElements(x, y, dataValues), decode, origValues); float Y = origValues[0] * 255; float Cb = origValues[1] * 255; float Cr = origValues[2] * 255; // float K = origValues[3] * 255; // removing alteration for now as some samples are too dark. // Y *= .95; // gives a darker image, as y approaches zero, // the image becomes darke float Cr_128 = Cr - 128; float Cb_128 = Cb - 128; // adobe conversion for CCIR Rec. 601-1 standard. // http://partners.adobe.com/public/developer/en/ps/sdk/5116.DCT_Filter.pdf // double rVal = Y + (1.4020 * Cr_128); // double gVal = Y - (.3441363 * Cb_128) - (.71413636 * Cr_128); // double bVal = Y + (1.772 * Cb_128); // intel codecs, http://software.intel.com/sites/products/documentation/hpc/ipp/ippi/ippi_ch6/ch6_color_models.html // Intel IPP conversion for JPEG codec. // double rVal = Y + (1.402 * Cr) - 179.456; // double gVal = Y - (0.34414 * Cb) - (.71413636 * Cr) + 135.45984; // double bVal = Y + (1.772 * Cb) - 226.816; // ICEsoft custom algorithm, results may vary, res are a little // off but over all a better conversion/ then the stoke algorithms. double rVal = Y + (1.4020 * Cr_128); double gVal = Y + (.14414 * Cb_128) + (.11413636 * Cr_128); double bVal = Y + (1.772 * Cb_128); // Intel IPP conversion for ITU-R BT.601 for video // default 16, higher more green and darker blacks, lower less // green hue and lighter blacks. // double kLight = (1.164 * (Y -16 )); // double rVal = kLight + (1.596 * Cr_128); // double gVal = kLight - (0.392 * Cb_128) - (0.813 * Cr_128); // double bVal = kLight + (1.017 * Cb_128); // intel PhotoYCC Color Model [0.1], not a likely candidate for jpegs. // double y1 = Y/255.0; // double c1 = Cb/255.0; // double c2 = Cr/255.0; // double rVal = ((0.981 * y1) + (1.315 * (c2 - 0.537))) *255.0; // double gVal = ((0.981 * y1) - (0.311 * (c1 - 0.612))- (0.669 * (c2 - 0.537))) *255.0; // double bVal = ((0.981 * y1) + (1.601 * (c1 - 0.612))) *255.0; // check the range an convert as needed. byte rByte = (rVal < 0) ? (byte) 0 : (rVal > 255) ? (byte) 0xFF : (byte) rVal; byte gByte = (gVal < 0) ? (byte) 0 : (gVal > 255) ? (byte) 0xFF : (byte) gVal; byte bByte = (bVal < 0) ? (byte) 0 : (bVal > 255) ? (byte) 0xFF : (byte) bVal; int alpha = 0xFF; if (y < smaskHeight && x < smaskWidth && smaskRaster != null) { alpha = (smaskRaster.getSample(x, y, 0) & 0xFF); } else if (y < maskHeight && x < maskWidth && maskRaster != null) { // When making an ImageMask, the alpha channel is setup so that // it both works correctly for the ImageMask being painted, // and also for when it's used here, to determine the alpha // of an image that it's masking alpha = (maskImage.getRGB(x, y) >>> 24) & 0xFF; // Extract Alpha from ARGB } rgbaValues[0] = bByte; rgbaValues[1] = gByte; rgbaValues[2] = rByte; rgbaValues[3] = alpha; wr.setPixel(x, y, rgbaValues); } } } */ }