package org.icepdf.core.pobjects.graphics.RasterOps; import org.icepdf.core.util.Defs; import java.awt.*; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.*; /** * Raster operation for converting a CMYK colour to RGB using an a rough * algorithm which generally results in images that are darker then they should * be. The black value can be configured using the system property * -Dorg.icepdf.core.cmyk.image.black=255. * <br> * This colour conversion method should only be used if its not desirable to * use the more accurate ICC Color Profile for colour conversion. * * @since 5.1 */ public class CMYKRasterOp implements RasterOp { // default cmyk value, > 255 will lighten the image. private static float blackRatio; private RenderingHints hints = null; public CMYKRasterOp(RenderingHints hints) { this.hints = hints; blackRatio = Defs.intProperty("org.icepdf.core.cmyk.image.black", 255); } public static void setBlackRatio(float blackRatio) { CMYKRasterOp.blackRatio = blackRatio; } 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(); int[] destPixels = ((DataBufferInt) dest.getDataBuffer()).getData(); // this convoluted cymk->rgba method is from DeviceCMYK class. float inCyan, inMagenta, inYellow, inBlack; float lastCyan = -1, lastMagenta = -1, lastYellow = -1, lastBlack = -1; double c, m, y2, aw, ac, am, ay, ar, ag, ab; float outRed, outGreen, outBlue; int rValue = 0, gValue = 0, bValue = 0, alpha = 0; int bands = src.getNumBands(); for (int pixel = 0, intPixels = 0; pixel < srcPixels.length; pixel += bands, intPixels++) { inCyan = (srcPixels[pixel] & 0xff) / 255.0f; inMagenta = (srcPixels[pixel + 1] & 0xff) / 255.0f; inYellow = (srcPixels[pixel + 2] & 0xff) / 255.0f; // lessen the amount of black, standard 255 fraction is too dark // increasing the denominator has the same affect of lighting up // the image. inBlack = (srcPixels[pixel + 3] & 0xff) / blackRatio; if (!(inCyan == lastCyan && inMagenta == lastMagenta && inYellow == lastYellow && inBlack == lastBlack)) { c = clip(0, 1, inCyan + inBlack); m = clip(0, 1, inMagenta + inBlack); y2 = clip(0, 1, inYellow + inBlack); aw = (1 - c) * (1 - m) * (1 - y2); ac = c * (1 - m) * (1 - y2); am = (1 - c) * m * (1 - y2); ay = (1 - c) * (1 - m) * y2; ar = (1 - c) * m * y2; ag = c * (1 - m) * y2; ab = c * m * (1 - y2); outRed = (float) clip(0, 1, aw + 0.9137 * am + 0.9961 * ay + 0.9882 * ar); outGreen = (float) clip(0, 1, aw + 0.6196 * ac + ay + 0.5176 * ag); outBlue = (float) clip(0, 1, aw + 0.7804 * ac + 0.5412 * am + 0.0667 * ar + 0.2118 * ag + 0.4863 * ab); rValue = (int) (outRed * 255); gValue = (int) (outGreen * 255); bValue = (int) (outBlue * 255); alpha = 0xFF; } lastCyan = inCyan; lastMagenta = inMagenta; lastYellow = inYellow; lastBlack = inBlack; destPixels[intPixels] = ((alpha & 0xff) << 24) | ((rValue & 0xff) << 16) | ((gValue & 0xff) << 8) | (bValue & 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 double clip(double floor, double ceiling, double value) { if (value < floor) { value = floor; } if (value > ceiling) { value = ceiling; } return value; } }