0001: /*
0002:
0003: Licensed to the Apache Software Foundation (ASF) under one or more
0004: contributor license agreements. See the NOTICE file distributed with
0005: this work for additional information regarding copyright ownership.
0006: The ASF licenses this file to You under the Apache License, Version 2.0
0007: (the "License"); you may not use this file except in compliance with
0008: the License. You may obtain a copy of the License at
0009:
0010: http://www.apache.org/licenses/LICENSE-2.0
0011:
0012: Unless required by applicable law or agreed to in writing, software
0013: distributed under the License is distributed on an "AS IS" BASIS,
0014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0015: See the License for the specific language governing permissions and
0016: limitations under the License.
0017:
0018: */
0019: package org.apache.batik.ext.awt.image;
0020:
0021: import java.awt.Composite;
0022: import java.awt.Graphics2D;
0023: import java.awt.GraphicsConfiguration;
0024: import java.awt.GraphicsDevice;
0025: import java.awt.Point;
0026: import java.awt.Rectangle;
0027: import java.awt.RenderingHints;
0028: import java.awt.Shape;
0029: import java.awt.color.ColorSpace;
0030: import java.awt.geom.AffineTransform;
0031: import java.awt.geom.Rectangle2D;
0032: import java.awt.image.BufferedImage;
0033: import java.awt.image.ColorModel;
0034: import java.awt.image.ComponentSampleModel;
0035: import java.awt.image.DataBuffer;
0036: import java.awt.image.DataBufferByte;
0037: import java.awt.image.DataBufferInt;
0038: import java.awt.image.DataBufferShort;
0039: import java.awt.image.DataBufferUShort;
0040: import java.awt.image.DirectColorModel;
0041: import java.awt.image.Raster;
0042: import java.awt.image.RenderedImage;
0043: import java.awt.image.SampleModel;
0044: import java.awt.image.SinglePixelPackedSampleModel;
0045: import java.awt.image.WritableRaster;
0046: import java.awt.image.renderable.RenderContext;
0047: import java.awt.image.renderable.RenderableImage;
0048: import java.lang.ref.Reference;
0049: import java.lang.ref.WeakReference;
0050:
0051: import org.apache.batik.ext.awt.RenderingHintsKeyExt;
0052: import org.apache.batik.ext.awt.image.renderable.PaintRable;
0053: import org.apache.batik.ext.awt.image.rendered.AffineRed;
0054: import org.apache.batik.ext.awt.image.rendered.Any2LsRGBRed;
0055: import org.apache.batik.ext.awt.image.rendered.Any2sRGBRed;
0056: import org.apache.batik.ext.awt.image.rendered.BufferedImageCachableRed;
0057: import org.apache.batik.ext.awt.image.rendered.CachableRed;
0058: import org.apache.batik.ext.awt.image.rendered.FormatRed;
0059: import org.apache.batik.ext.awt.image.rendered.RenderedImageCachableRed;
0060: import org.apache.batik.ext.awt.image.rendered.TranslateRed;
0061:
0062: /**
0063: * Set of utility methods for Graphics.
0064: * These generally bypass broken methods in Java2D or provide tweaked
0065: * implementations.
0066: *
0067: * @author <a href="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a>
0068: * @version $Id: GraphicsUtil.java 498740 2007-01-22 18:35:57Z dvholten $
0069: */
0070: public class GraphicsUtil {
0071:
0072: public static AffineTransform IDENTITY = new AffineTransform();
0073:
0074: /**
0075: * Draws <tt>ri</tt> into <tt>g2d</tt>. It does this be
0076: * requesting tiles from <tt>ri</tt> and drawing them individually
0077: * in <tt>g2d</tt> it also takes care of some colorspace and alpha
0078: * issues.
0079: * @param g2d The Graphics2D to draw into.
0080: * @param ri The image to be drawn.
0081: */
0082: public static void drawImage(Graphics2D g2d, RenderedImage ri) {
0083: drawImage(g2d, wrap(ri));
0084: }
0085:
0086: /**
0087: * Draws <tt>cr</tt> into <tt>g2d</tt>. It does this be
0088: * requesting tiles from <tt>ri</tt> and drawing them individually
0089: * in <tt>g2d</tt> it also takes care of some colorspace and alpha
0090: * issues.
0091: * @param g2d The Graphics2D to draw into.
0092: * @param cr The image to be drawn.
0093: */
0094: public static void drawImage(Graphics2D g2d, CachableRed cr) {
0095:
0096: // System.out.println("DrawImage G: " + g2d);
0097:
0098: AffineTransform at = null;
0099: while (true) {
0100: if (cr instanceof AffineRed) {
0101: AffineRed ar = (AffineRed) cr;
0102: if (at == null)
0103: at = ar.getTransform();
0104: else
0105: at.concatenate(ar.getTransform());
0106: cr = ar.getSource();
0107: continue;
0108: } else if (cr instanceof TranslateRed) {
0109: TranslateRed tr = (TranslateRed) cr;
0110: // System.out.println("testing Translate");
0111: int dx = tr.getDeltaX();
0112: int dy = tr.getDeltaY();
0113: if (at == null)
0114: at = AffineTransform.getTranslateInstance(dx, dy);
0115: else
0116: at.translate(dx, dy);
0117: cr = tr.getSource();
0118: continue;
0119: }
0120: break;
0121: }
0122: AffineTransform g2dAt = g2d.getTransform();
0123: if ((at == null) || (at.isIdentity()))
0124: at = g2dAt;
0125: else
0126: at.preConcatenate(g2dAt);
0127:
0128: ColorModel srcCM = cr.getColorModel();
0129: ColorModel g2dCM = getDestinationColorModel(g2d);
0130: ColorSpace g2dCS = null;
0131: if (g2dCM != null)
0132: g2dCS = g2dCM.getColorSpace();
0133: if (g2dCS == null)
0134: // Assume device is sRGB
0135: g2dCS = ColorSpace.getInstance(ColorSpace.CS_sRGB);
0136:
0137: ColorModel drawCM = g2dCM;
0138: if ((g2dCM == null) || !g2dCM.hasAlpha()) {
0139: // If we can't find out about our device or the device
0140: // does not support alpha just use SRGB unpremultiplied
0141: // (Just because this seems to work for us).
0142: drawCM = sRGB_Unpre;
0143: }
0144:
0145: if (cr instanceof BufferedImageCachableRed) {
0146: // There is a huge win if we can use the BI directly here.
0147: // This results in something like a 10x performance gain
0148: // for images, the best thing is this is the common case.
0149: if (g2dCS.equals(srcCM.getColorSpace())
0150: && drawCM.equals(srcCM)) {
0151: // System.err.println("Fast Case");
0152: g2d.setTransform(at);
0153: BufferedImageCachableRed bicr;
0154: bicr = (BufferedImageCachableRed) cr;
0155: g2d.drawImage(bicr.getBufferedImage(), bicr.getMinX(),
0156: bicr.getMinY(), null);
0157: g2d.setTransform(g2dAt);
0158: return;
0159: }
0160: }
0161:
0162: // Scaling down so do it before color conversion.
0163: double determinant = at.getDeterminant();
0164: if (!at.isIdentity() && (determinant <= 1.0)) {
0165: if (at.getType() != AffineTransform.TYPE_TRANSLATION)
0166: cr = new AffineRed(cr, at, g2d.getRenderingHints());
0167: else {
0168: int xloc = cr.getMinX() + (int) at.getTranslateX();
0169: int yloc = cr.getMinY() + (int) at.getTranslateY();
0170: cr = new TranslateRed(cr, xloc, yloc);
0171: }
0172: }
0173:
0174: if (g2dCS != srcCM.getColorSpace()) {
0175: // System.out.println("srcCS: " + srcCM.getColorSpace());
0176: // System.out.println("g2dCS: " + g2dCS);
0177: // System.out.println("sRGB: " +
0178: // ColorSpace.getInstance(ColorSpace.CS_sRGB));
0179: // System.out.println("LsRGB: " +
0180: // ColorSpace.getInstance
0181: // (ColorSpace.CS_LINEAR_RGB));
0182: if (g2dCS == ColorSpace.getInstance(ColorSpace.CS_sRGB))
0183: cr = convertTosRGB(cr);
0184: else if (g2dCS == ColorSpace
0185: .getInstance(ColorSpace.CS_LINEAR_RGB))
0186: cr = convertToLsRGB(cr);
0187: }
0188: srcCM = cr.getColorModel();
0189: if (!drawCM.equals(srcCM))
0190: cr = FormatRed.construct(cr, drawCM);
0191:
0192: // Scaling up so do it after color conversion.
0193: if (!at.isIdentity() && (determinant > 1.0))
0194: cr = new AffineRed(cr, at, g2d.getRenderingHints());
0195:
0196: // Now CR is in device space, so clear the g2d transform.
0197: g2d.setTransform(IDENTITY);
0198:
0199: // Ugly Hack alert. This Makes it use our SrcOver implementation
0200: // Which doesn't seem to have as many bugs as the JDK one when
0201: // going between different src's and destinations (of course it's
0202: // also a lot slower).
0203: Composite g2dComposite = g2d.getComposite();
0204: if (g2d.getRenderingHint(RenderingHintsKeyExt.KEY_TRANSCODING) == RenderingHintsKeyExt.VALUE_TRANSCODING_PRINTING) {
0205: if (SVGComposite.OVER.equals(g2dComposite)) {
0206: g2d.setComposite(SVGComposite.OVER);
0207: }
0208: }
0209: Rectangle crR = cr.getBounds();
0210: Shape clip = g2d.getClip();
0211:
0212: try {
0213: Rectangle clipR;
0214: if (clip == null) {
0215: clip = crR;
0216: clipR = crR;
0217: } else {
0218: clipR = clip.getBounds();
0219:
0220: if (!clipR.intersects(crR))
0221: return; // Nothing to draw...
0222: clipR = clipR.intersection(crR);
0223: }
0224:
0225: Rectangle gcR = getDestinationBounds(g2d);
0226: // System.out.println("ClipRects: " + clipR + " -> " + gcR);
0227: if (gcR != null) {
0228: if (!clipR.intersects(gcR))
0229: return; // Nothing to draw...
0230: clipR = clipR.intersection(gcR);
0231: }
0232:
0233: // System.out.println("Starting Draw: " + cr);
0234: // long startTime = System.currentTimeMillis();
0235:
0236: boolean useDrawRenderedImage = false;
0237:
0238: srcCM = cr.getColorModel();
0239: SampleModel srcSM = cr.getSampleModel();
0240: if ((srcSM.getWidth() * srcSM.getHeight()) >= (clipR.width * clipR.height))
0241: // if srcSM tiles are around the clip size
0242: // then just draw the renderedImage
0243: useDrawRenderedImage = true;
0244:
0245: Object atpHint = g2d
0246: .getRenderingHint(RenderingHintsKeyExt.KEY_AVOID_TILE_PAINTING);
0247:
0248: if (atpHint == RenderingHintsKeyExt.VALUE_AVOID_TILE_PAINTING_ON)
0249: useDrawRenderedImage = true; //for PDF and PS transcoders
0250:
0251: if (atpHint == RenderingHintsKeyExt.VALUE_AVOID_TILE_PAINTING_OFF)
0252: useDrawRenderedImage = false;
0253:
0254: WritableRaster wr;
0255: if (useDrawRenderedImage) {
0256: // This can be significantly faster but can also
0257: // require much more memory, so we only use it when
0258: // the clip size is smaller than the tile size.
0259: Raster r = cr.getData(clipR);
0260: wr = ((WritableRaster) r).createWritableChild(clipR.x,
0261: clipR.y, clipR.width, clipR.height, 0, 0, null);
0262:
0263: BufferedImage bi = new BufferedImage(srcCM, wr, srcCM
0264: .isAlphaPremultiplied(), null);
0265:
0266: // Any of the drawImage calls that take an
0267: // Affine are prone to the 'CGGStackRestore: gstack
0268: // underflow' bug on Mac OS X. This should work
0269: // around that problem.
0270: g2d.drawImage(bi, clipR.x, clipR.y, null);
0271: } else {
0272: // Use tiles to draw image...
0273: wr = Raster
0274: .createWritableRaster(srcSM, new Point(0, 0));
0275: BufferedImage bi = new BufferedImage(srcCM, wr, srcCM
0276: .isAlphaPremultiplied(), null);
0277:
0278: int xt0 = cr.getMinTileX();
0279: int xt1 = xt0 + cr.getNumXTiles();
0280: int yt0 = cr.getMinTileY();
0281: int yt1 = yt0 + cr.getNumYTiles();
0282: int tw = srcSM.getWidth();
0283: int th = srcSM.getHeight();
0284:
0285: Rectangle tR = new Rectangle(0, 0, tw, th);
0286: Rectangle iR = new Rectangle(0, 0, 0, 0);
0287:
0288: if (false) {
0289: System.err.println("SrcCM: " + srcCM);
0290: System.err.println("CR: " + cr);
0291: System.err.println("CRR: " + crR + " TG: [" + xt0
0292: + ',' + yt0 + ',' + xt1 + ',' + yt1
0293: + "] Off: " + cr.getTileGridXOffset() + ','
0294: + cr.getTileGridYOffset());
0295: }
0296:
0297: int yloc = yt0 * th + cr.getTileGridYOffset();
0298: int skip = (clipR.y - yloc) / th;
0299: if (skip < 0)
0300: skip = 0;
0301: yt0 += skip;
0302:
0303: int xloc = xt0 * tw + cr.getTileGridXOffset();
0304: skip = (clipR.x - xloc) / tw;
0305: if (skip < 0)
0306: skip = 0;
0307: xt0 += skip;
0308:
0309: int endX = clipR.x + clipR.width - 1;
0310: int endY = clipR.y + clipR.height - 1;
0311:
0312: if (false) {
0313: System.out.println("clipR: " + clipR + " TG: ["
0314: + xt0 + ',' + yt0 + ',' + xt1 + ',' + yt1
0315: + "] Off: " + cr.getTileGridXOffset() + ','
0316: + cr.getTileGridYOffset());
0317: }
0318:
0319: yloc = yt0 * th + cr.getTileGridYOffset();
0320: int minX = xt0 * tw + cr.getTileGridXOffset();
0321: int xStep = tw;
0322: xloc = minX;
0323: for (int y = yt0; y < yt1; y++, yloc += th) {
0324: if (yloc > endY)
0325: break;
0326: for (int x = xt0; x < xt1; x++, xloc += xStep) {
0327: if ((xloc < minX) || (xloc > endX))
0328: break;
0329: tR.x = xloc;
0330: tR.y = yloc;
0331: Rectangle2D.intersect(crR, tR, iR);
0332:
0333: WritableRaster twr;
0334: twr = wr.createWritableChild(0, 0, iR.width,
0335: iR.height, iR.x, iR.y, null);
0336:
0337: // System.out.println("Generating tile: " + twr);
0338: cr.copyData(twr);
0339:
0340: // Make sure we only draw the region that was written.
0341: BufferedImage subBI;
0342: subBI = bi.getSubimage(0, 0, iR.width,
0343: iR.height);
0344:
0345: if (false) {
0346: System.out.println("Drawing: " + tR);
0347: System.out.println("IR: " + iR);
0348: }
0349:
0350: // For some reason using the transform version
0351: // causes a gStackUnderflow error but if I just
0352: // use the drawImage with an x & y it works.
0353: g2d.drawImage(subBI, iR.x, iR.y, null);
0354: // AffineTransform trans
0355: // = AffineTransform.getTranslateInstance(iR.x, iR.y);
0356: // g2d.drawImage(subBI, trans, null);
0357:
0358: // String label = "sub [" + x + ", " + y + "]: ";
0359: // org.ImageDisplay.showImage
0360: // (label, subBI);
0361: }
0362: xStep = -xStep; // Reverse directions.
0363: xloc += xStep; // Get back in bounds.
0364: }
0365: }
0366: // long endTime = System.currentTimeMillis();
0367: // System.out.println("Time: " + (endTime-startTime));
0368:
0369: } finally {
0370: g2d.setTransform(g2dAt);
0371: g2d.setComposite(g2dComposite);
0372: }
0373:
0374: // System.out.println("Finished Draw");
0375: }
0376:
0377: /**
0378: * Draws a <tt>Filter</tt> (<tt>RenderableImage</tt>) into a
0379: * Graphics 2D after taking into account a particular
0380: * <tt>RenderContext</tt>.<p>
0381: *
0382: * This method also attempts to unwind the rendering chain a bit.
0383: * So it knows about certain operations (like affine, pad,
0384: * composite), rather than applying each of these operations in
0385: * turn it accounts for their affects through modifications to the
0386: * Graphics2D. This avoids generating lots of intermediate images.
0387: *
0388: * @param g2d The Graphics to draw into.
0389: * @param filter The filter to draw
0390: * @param rc The render context that controls the drawing operation.
0391: */
0392: public static void drawImage(Graphics2D g2d,
0393: RenderableImage filter, RenderContext rc) {
0394:
0395: AffineTransform origDev = g2d.getTransform();
0396: Shape origClip = g2d.getClip();
0397: RenderingHints origRH = g2d.getRenderingHints();
0398:
0399: Shape clip = rc.getAreaOfInterest();
0400: if (clip != null)
0401: g2d.clip(clip);
0402: g2d.transform(rc.getTransform());
0403: g2d.setRenderingHints(rc.getRenderingHints());
0404:
0405: drawImage(g2d, filter);
0406:
0407: g2d.setTransform(origDev);
0408: g2d.setClip(origClip);
0409: g2d.setRenderingHints(origRH);
0410: }
0411:
0412: /**
0413: * Draws a <tt>Filter</tt> (<tt>RenderableImage</tt>) into a
0414: * Graphics 2D.<p>
0415: *
0416: * This method also attempts to unwind the rendering chain a bit.
0417: * So it knows about certain operations (like affine, pad,
0418: * composite), rather than applying each of these operations in
0419: * turn it accounts for their affects through modifications to the
0420: * Graphics2D. This avoids generating lots of intermediate images.
0421: *
0422: * @param g2d The Graphics to draw into.
0423: * @param filter The filter to draw
0424: */
0425: public static void drawImage(Graphics2D g2d, RenderableImage filter) {
0426: if (filter instanceof PaintRable) {
0427: PaintRable pr = (PaintRable) filter;
0428: if (pr.paintRable(g2d))
0429: // paintRable succeeded so we are done...
0430: return;
0431: }
0432:
0433: // Get our sources image...
0434: // System.out.println("UnOpt: " + filter);
0435: AffineTransform at = g2d.getTransform();
0436: RenderedImage ri = filter.createRendering(new RenderContext(at,
0437: g2d.getClip(), g2d.getRenderingHints()));
0438:
0439: if (ri == null)
0440: return;
0441:
0442: g2d.setTransform(IDENTITY);
0443: drawImage(g2d, GraphicsUtil.wrap(ri));
0444: g2d.setTransform(at);
0445: }
0446:
0447: /**
0448: * This is a wrapper around the system's
0449: * BufferedImage.createGraphics that arranges for bi to be stored
0450: * in a Rendering hint in the returned Graphics2D.
0451: * This allows for accurate determination of the 'devices' size,
0452: * and colorspace.
0453: * @param bi The BufferedImage that the returned Graphics should
0454: * draw into.
0455: * @return A Graphics2D that draws into BufferedImage with <tt>bi</tt>
0456: * stored in a rendering hint.
0457: */
0458: public static Graphics2D createGraphics(BufferedImage bi,
0459: RenderingHints hints) {
0460: Graphics2D g2d = bi.createGraphics();
0461: if (hints != null)
0462: g2d.addRenderingHints(hints);
0463: g2d.setRenderingHint(RenderingHintsKeyExt.KEY_BUFFERED_IMAGE,
0464: new WeakReference(bi));
0465: g2d.clip(new Rectangle(0, 0, bi.getWidth(), bi.getHeight()));
0466: return g2d;
0467: }
0468:
0469: public static Graphics2D createGraphics(BufferedImage bi) {
0470: Graphics2D g2d = bi.createGraphics();
0471: g2d.setRenderingHint(RenderingHintsKeyExt.KEY_BUFFERED_IMAGE,
0472: new WeakReference(bi));
0473: g2d.clip(new Rectangle(0, 0, bi.getWidth(), bi.getHeight()));
0474: return g2d;
0475: }
0476:
0477: public static final boolean WARN_DESTINATION;
0478:
0479: static {
0480: boolean warn = true;
0481: try {
0482: String s = System.getProperty(
0483: "org.apache.batik.warn_destination", "true");
0484: warn = Boolean.valueOf(s).booleanValue();
0485: } catch (SecurityException se) {
0486: } catch (NumberFormatException nfe) {
0487: } finally {
0488: WARN_DESTINATION = warn;
0489: }
0490: }
0491:
0492: public static BufferedImage getDestination(Graphics2D g2d) {
0493: Object o = g2d
0494: .getRenderingHint(RenderingHintsKeyExt.KEY_BUFFERED_IMAGE);
0495: if (o != null)
0496: return (BufferedImage) (((Reference) o).get());
0497:
0498: // Check if this is a BufferedImage G2d if so throw an error...
0499: GraphicsConfiguration gc = g2d.getDeviceConfiguration();
0500: GraphicsDevice gd = gc.getDevice();
0501: if (WARN_DESTINATION
0502: && (gd.getType() == GraphicsDevice.TYPE_IMAGE_BUFFER)
0503: && (g2d
0504: .getRenderingHint(RenderingHintsKeyExt.KEY_TRANSCODING) != RenderingHintsKeyExt.VALUE_TRANSCODING_PRINTING))
0505: // throw new IllegalArgumentException
0506: System.err
0507: .println("Graphics2D from BufferedImage lacks BUFFERED_IMAGE hint");
0508:
0509: return null;
0510: }
0511:
0512: public static ColorModel getDestinationColorModel(Graphics2D g2d) {
0513: BufferedImage bi = getDestination(g2d);
0514: if (bi != null)
0515: return bi.getColorModel();
0516:
0517: GraphicsConfiguration gc = g2d.getDeviceConfiguration();
0518: if (gc == null)
0519: return null; // Can't tell
0520:
0521: // We are going to a BufferedImage but no hint was provided
0522: // so we can't determine the destination Color Model.
0523: if (gc.getDevice().getType() == GraphicsDevice.TYPE_IMAGE_BUFFER) {
0524: if (g2d
0525: .getRenderingHint(RenderingHintsKeyExt.KEY_TRANSCODING) == RenderingHintsKeyExt.VALUE_TRANSCODING_PRINTING)
0526: return sRGB_Unpre;
0527:
0528: // System.out.println("CM: " + gc.getColorModel());
0529: // System.out.println("CS: " + gc.getColorModel().getColorSpace());
0530: return null;
0531: }
0532:
0533: return gc.getColorModel();
0534: }
0535:
0536: public static ColorSpace getDestinationColorSpace(Graphics2D g2d) {
0537: ColorModel cm = getDestinationColorModel(g2d);
0538: if (cm != null)
0539: return cm.getColorSpace();
0540:
0541: return null;
0542: }
0543:
0544: public static Rectangle getDestinationBounds(Graphics2D g2d) {
0545: BufferedImage bi = getDestination(g2d);
0546: if (bi != null)
0547: return new Rectangle(0, 0, bi.getWidth(), bi.getHeight());
0548:
0549: GraphicsConfiguration gc = g2d.getDeviceConfiguration();
0550:
0551: // We are going to a BufferedImage but no hint was provided
0552: // so we can't determine the destination bounds.
0553: if (gc.getDevice().getType() == GraphicsDevice.TYPE_IMAGE_BUFFER)
0554: return null;
0555:
0556: // This is a JDK 1.3ism, so we will just return null...
0557: // return gc.getBounds();
0558: return null;
0559: }
0560:
0561: /**
0562: * Standard prebuilt Linear_sRGB color model with no alpha */
0563: public static final ColorModel Linear_sRGB = new DirectColorModel(
0564: ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), 24,
0565: 0x00FF0000, 0x0000FF00, 0x000000FF, 0x0, false,
0566: DataBuffer.TYPE_INT);
0567: /**
0568: * Standard prebuilt Linear_sRGB color model with premultiplied alpha.
0569: */
0570: public static final ColorModel Linear_sRGB_Pre = new DirectColorModel(
0571: ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), 32,
0572: 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, true,
0573: DataBuffer.TYPE_INT);
0574: /**
0575: * Standard prebuilt Linear_sRGB color model with unpremultiplied alpha.
0576: */
0577: public static final ColorModel Linear_sRGB_Unpre = new DirectColorModel(
0578: ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), 32,
0579: 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, false,
0580: DataBuffer.TYPE_INT);
0581:
0582: /**
0583: * Standard prebuilt sRGB color model with no alpha.
0584: */
0585: public static final ColorModel sRGB = new DirectColorModel(
0586: ColorSpace.getInstance(ColorSpace.CS_sRGB), 24, 0x00FF0000,
0587: 0x0000FF00, 0x000000FF, 0x0, false, DataBuffer.TYPE_INT);
0588: /**
0589: * Standard prebuilt sRGB color model with premultiplied alpha.
0590: */
0591: public static final ColorModel sRGB_Pre = new DirectColorModel(
0592: ColorSpace.getInstance(ColorSpace.CS_sRGB), 32, 0x00FF0000,
0593: 0x0000FF00, 0x000000FF, 0xFF000000, true,
0594: DataBuffer.TYPE_INT);
0595: /**
0596: * Standard prebuilt sRGB color model with unpremultiplied alpha.
0597: */
0598: public static final ColorModel sRGB_Unpre = new DirectColorModel(
0599: ColorSpace.getInstance(ColorSpace.CS_sRGB), 32, 0x00FF0000,
0600: 0x0000FF00, 0x000000FF, 0xFF000000, false,
0601: DataBuffer.TYPE_INT);
0602:
0603: /**
0604: * Method that returns either Linear_sRGB_Pre or Linear_sRGB_UnPre
0605: * based on premult flag.
0606: * @param premult True if the ColorModel should have premultiplied alpha.
0607: * @return a ColorMdoel with Linear sRGB colorSpace and
0608: * the alpha channel set in accordance with
0609: * <tt>premult</tt>
0610: */
0611: public static ColorModel makeLinear_sRGBCM(boolean premult) {
0612:
0613: return premult ? Linear_sRGB_Pre : Linear_sRGB_Unpre;
0614: }
0615:
0616: /**
0617: * Constructs a BufferedImage with a linear sRGB colorModel, and alpha.
0618: * @param width The desired width of the BufferedImage
0619: * @param height The desired height of the BufferedImage
0620: * @param premult The desired state of alpha premultiplied
0621: * @return The requested BufferedImage.
0622: */
0623: public static BufferedImage makeLinearBufferedImage(int width,
0624: int height, boolean premult) {
0625: ColorModel cm = makeLinear_sRGBCM(premult);
0626: WritableRaster wr = cm.createCompatibleWritableRaster(width,
0627: height);
0628: return new BufferedImage(cm, wr, premult, null);
0629: }
0630:
0631: /**
0632: * This method will return a CacheableRed that has it's data in
0633: * the linear sRGB colorspace. If <tt>src</tt> is already in
0634: * linear sRGB then this method does nothing and returns <tt>src</tt>.
0635: * Otherwise it creates a transform that will convert
0636: * <tt>src</tt>'s output to linear sRGB and returns that CacheableRed.
0637: *
0638: * @param src The image to convert to linear sRGB.
0639: * @return An equivilant image to <tt>src</tt> who's data is in
0640: * linear sRGB.
0641: */
0642: public static CachableRed convertToLsRGB(CachableRed src) {
0643: ColorModel cm = src.getColorModel();
0644: ColorSpace cs = cm.getColorSpace();
0645: if (cs == ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB))
0646: return src;
0647:
0648: return new Any2LsRGBRed(src);
0649: }
0650:
0651: /**
0652: * This method will return a CacheableRed that has it's data in
0653: * the sRGB colorspace. If <tt>src</tt> is already in
0654: * sRGB then this method does nothing and returns <tt>src</tt>.
0655: * Otherwise it creates a transform that will convert
0656: * <tt>src</tt>'s output to sRGB and returns that CacheableRed.
0657: *
0658: * @param src The image to convert to sRGB.
0659: * @return An equivilant image to <tt>src</tt> who's data is in sRGB.
0660: */
0661: public static CachableRed convertTosRGB(CachableRed src) {
0662: ColorModel cm = src.getColorModel();
0663: ColorSpace cs = cm.getColorSpace();
0664: if (cs == ColorSpace.getInstance(ColorSpace.CS_sRGB))
0665: return src;
0666:
0667: return new Any2sRGBRed(src);
0668: }
0669:
0670: /**
0671: * Convertes any RenderedImage to a CacheableRed. <p>
0672: * If <tt>ri</tt> is already a CacheableRed it casts it down and
0673: * returns it.<p>
0674: *
0675: * In cases where <tt>ri</tt> is not already a CacheableRed it
0676: * wraps <tt>ri</tt> with a helper class. The wrapped
0677: * CacheableRed "Pretends" that it has no sources since it has no
0678: * way of inteligently handling the dependency/dirty region calls
0679: * if it exposed the source.
0680: * @param ri The RenderedImage to convert.
0681: * @return a CacheableRed that contains the same data as ri.
0682: */
0683: public static CachableRed wrap(RenderedImage ri) {
0684: if (ri instanceof CachableRed)
0685: return (CachableRed) ri;
0686: if (ri instanceof BufferedImage)
0687: return new BufferedImageCachableRed((BufferedImage) ri);
0688: return new RenderedImageCachableRed(ri);
0689: }
0690:
0691: /**
0692: * An internal optimized version of copyData designed to work on
0693: * Integer packed data with a SinglePixelPackedSampleModel. Only
0694: * the region of overlap between src and dst is copied.
0695: *
0696: * Calls to this should be preflighted with is_INT_PACK_Data
0697: * on both src and dest (requireAlpha can be false).
0698: *
0699: * @param src The source of the data
0700: * @param dst The destination for the data.
0701: */
0702: public static void copyData_INT_PACK(Raster src, WritableRaster dst) {
0703: // System.out.println("Fast copyData");
0704: int x0 = dst.getMinX();
0705: if (x0 < src.getMinX())
0706: x0 = src.getMinX();
0707:
0708: int y0 = dst.getMinY();
0709: if (y0 < src.getMinY())
0710: y0 = src.getMinY();
0711:
0712: int x1 = dst.getMinX() + dst.getWidth() - 1;
0713: if (x1 > src.getMinX() + src.getWidth() - 1)
0714: x1 = src.getMinX() + src.getWidth() - 1;
0715:
0716: int y1 = dst.getMinY() + dst.getHeight() - 1;
0717: if (y1 > src.getMinY() + src.getHeight() - 1)
0718: y1 = src.getMinY() + src.getHeight() - 1;
0719:
0720: int width = x1 - x0 + 1;
0721: int height = y1 - y0 + 1;
0722:
0723: SinglePixelPackedSampleModel srcSPPSM;
0724: srcSPPSM = (SinglePixelPackedSampleModel) src.getSampleModel();
0725:
0726: final int srcScanStride = srcSPPSM.getScanlineStride();
0727: DataBufferInt srcDB = (DataBufferInt) src.getDataBuffer();
0728: final int[] srcPixels = srcDB.getBankData()[0];
0729: final int srcBase = (srcDB.getOffset() + srcSPPSM.getOffset(x0
0730: - src.getSampleModelTranslateX(), y0
0731: - src.getSampleModelTranslateY()));
0732:
0733: SinglePixelPackedSampleModel dstSPPSM;
0734: dstSPPSM = (SinglePixelPackedSampleModel) dst.getSampleModel();
0735:
0736: final int dstScanStride = dstSPPSM.getScanlineStride();
0737: DataBufferInt dstDB = (DataBufferInt) dst.getDataBuffer();
0738: final int[] dstPixels = dstDB.getBankData()[0];
0739: final int dstBase = (dstDB.getOffset() + dstSPPSM.getOffset(x0
0740: - dst.getSampleModelTranslateX(), y0
0741: - dst.getSampleModelTranslateY()));
0742:
0743: if ((srcScanStride == dstScanStride)
0744: && (srcScanStride == width)) {
0745: // System.out.println("VERY Fast copyData");
0746:
0747: System.arraycopy(srcPixels, srcBase, dstPixels, dstBase,
0748: width * height);
0749: } else if (width > 128) {
0750: int srcSP = srcBase;
0751: int dstSP = dstBase;
0752: for (int y = 0; y < height; y++) {
0753: System.arraycopy(srcPixels, srcSP, dstPixels, dstSP,
0754: width);
0755: srcSP += srcScanStride;
0756: dstSP += dstScanStride;
0757: }
0758: } else {
0759: for (int y = 0; y < height; y++) {
0760: int srcSP = srcBase + y * srcScanStride;
0761: int dstSP = dstBase + y * dstScanStride;
0762: for (int x = 0; x < width; x++)
0763: dstPixels[dstSP++] = srcPixels[srcSP++];
0764: }
0765: }
0766: }
0767:
0768: public static void copyData_FALLBACK(Raster src, WritableRaster dst) {
0769: // System.out.println("Fallback copyData");
0770:
0771: int x0 = dst.getMinX();
0772: if (x0 < src.getMinX())
0773: x0 = src.getMinX();
0774:
0775: int y0 = dst.getMinY();
0776: if (y0 < src.getMinY())
0777: y0 = src.getMinY();
0778:
0779: int x1 = dst.getMinX() + dst.getWidth() - 1;
0780: if (x1 > src.getMinX() + src.getWidth() - 1)
0781: x1 = src.getMinX() + src.getWidth() - 1;
0782:
0783: int y1 = dst.getMinY() + dst.getHeight() - 1;
0784: if (y1 > src.getMinY() + src.getHeight() - 1)
0785: y1 = src.getMinY() + src.getHeight() - 1;
0786:
0787: int width = x1 - x0 + 1;
0788: int[] data = null;
0789:
0790: for (int y = y0; y <= y1; y++) {
0791: data = src.getPixels(x0, y, width, 1, data);
0792: dst.setPixels(x0, y, width, 1, data);
0793: }
0794: }
0795:
0796: /**
0797: * Copies data from one raster to another. Only the region of
0798: * overlap between src and dst is copied. <tt>Src</tt> and
0799: * <tt>Dst</tt> must have compatible SampleModels.
0800: *
0801: * @param src The source of the data
0802: * @param dst The destination for the data.
0803: */
0804: public static void copyData(Raster src, WritableRaster dst) {
0805: if (is_INT_PACK_Data(src.getSampleModel(), false)
0806: && is_INT_PACK_Data(dst.getSampleModel(), false)) {
0807: copyData_INT_PACK(src, dst);
0808: return;
0809: }
0810:
0811: copyData_FALLBACK(src, dst);
0812: }
0813:
0814: /**
0815: * Creates a new raster that has a <b>copy</b> of the data in
0816: * <tt>ras</tt>. This is highly optimized for speed. There is
0817: * no provision for changing any aspect of the SampleModel.
0818: *
0819: * This method should be used when you need to change the contents
0820: * of a Raster that you do not "own" (ie the result of a
0821: * <tt>getData</tt> call).
0822: * @param ras The Raster to copy.
0823: * @return A writable copy of <tt>ras</tt>
0824: */
0825: public static WritableRaster copyRaster(Raster ras) {
0826: return copyRaster(ras, ras.getMinX(), ras.getMinY());
0827: }
0828:
0829: /**
0830: * Creates a new raster that has a <b>copy</b> of the data in
0831: * <tt>ras</tt>. This is highly optimized for speed. There is
0832: * no provision for changing any aspect of the SampleModel.
0833: * However you can specify a new location for the returned raster.
0834: *
0835: * This method should be used when you need to change the contents
0836: * of a Raster that you do not "own" (ie the result of a
0837: * <tt>getData</tt> call).
0838: *
0839: * @param ras The Raster to copy.
0840: *
0841: * @param minX The x location for the upper left corner of the
0842: * returned WritableRaster.
0843: *
0844: * @param minY The y location for the upper left corner of the
0845: * returned WritableRaster.
0846: *
0847: * @return A writable copy of <tt>ras</tt>
0848: */
0849: public static WritableRaster copyRaster(Raster ras, int minX,
0850: int minY) {
0851: WritableRaster ret = Raster.createWritableRaster(ras
0852: .getSampleModel(), new Point(0, 0));
0853: ret = ret.createWritableChild(ras.getMinX()
0854: - ras.getSampleModelTranslateX(), ras.getMinY()
0855: - ras.getSampleModelTranslateY(), ras.getWidth(), ras
0856: .getHeight(), minX, minY, null);
0857:
0858: // Use System.arraycopy to copy the data between the two...
0859: DataBuffer srcDB = ras.getDataBuffer();
0860: DataBuffer retDB = ret.getDataBuffer();
0861: if (srcDB.getDataType() != retDB.getDataType()) {
0862: throw new IllegalArgumentException(
0863: "New DataBuffer doesn't match original");
0864: }
0865: int len = srcDB.getSize();
0866: int banks = srcDB.getNumBanks();
0867: int[] offsets = srcDB.getOffsets();
0868: for (int b = 0; b < banks; b++) {
0869: switch (srcDB.getDataType()) {
0870: case DataBuffer.TYPE_BYTE: {
0871: DataBufferByte srcDBT = (DataBufferByte) srcDB;
0872: DataBufferByte retDBT = (DataBufferByte) retDB;
0873: System.arraycopy(srcDBT.getData(b), offsets[b], retDBT
0874: .getData(b), offsets[b], len);
0875: break;
0876: }
0877: case DataBuffer.TYPE_INT: {
0878: DataBufferInt srcDBT = (DataBufferInt) srcDB;
0879: DataBufferInt retDBT = (DataBufferInt) retDB;
0880: System.arraycopy(srcDBT.getData(b), offsets[b], retDBT
0881: .getData(b), offsets[b], len);
0882: break;
0883: }
0884: case DataBuffer.TYPE_SHORT: {
0885: DataBufferShort srcDBT = (DataBufferShort) srcDB;
0886: DataBufferShort retDBT = (DataBufferShort) retDB;
0887: System.arraycopy(srcDBT.getData(b), offsets[b], retDBT
0888: .getData(b), offsets[b], len);
0889: break;
0890: }
0891: case DataBuffer.TYPE_USHORT: {
0892: DataBufferUShort srcDBT = (DataBufferUShort) srcDB;
0893: DataBufferUShort retDBT = (DataBufferUShort) retDB;
0894: System.arraycopy(srcDBT.getData(b), offsets[b], retDBT
0895: .getData(b), offsets[b], len);
0896: break;
0897: }
0898: }
0899: }
0900:
0901: return ret;
0902: }
0903:
0904: /**
0905: * Coerces <tt>ras</tt> to be writable. The returned Raster continues to
0906: * reference the DataBuffer from ras, so modifications to the returned
0907: * WritableRaster will be seen in ras.<p>
0908: *
0909: * This method should only be used if you need a WritableRaster due to
0910: * an interface (such as to construct a BufferedImage), but have no
0911: * intention of modifying the contents of the returned Raster. If
0912: * you have any doubt about other users of the data in <tt>ras</tt>,
0913: * use copyRaster (above).
0914: * @param ras The raster to make writable.
0915: * @return A Writable version of ras (shares DataBuffer with
0916: * <tt>ras</tt>).
0917: */
0918: public static WritableRaster makeRasterWritable(Raster ras) {
0919: return makeRasterWritable(ras, ras.getMinX(), ras.getMinY());
0920: }
0921:
0922: /**
0923: * Coerces <tt>ras</tt> to be writable. The returned Raster continues to
0924: * reference the DataBuffer from ras, so modifications to the returned
0925: * WritableRaster will be seen in ras.<p>
0926: *
0927: * You can specify a new location for the returned WritableRaster, this
0928: * is especially useful for constructing BufferedImages which require
0929: * the Raster to be at (0,0).
0930: *
0931: * This method should only be used if you need a WritableRaster due to
0932: * an interface (such as to construct a BufferedImage), but have no
0933: * intention of modifying the contents of the returned Raster. If
0934: * you have any doubt about other users of the data in <tt>ras</tt>,
0935: * use copyRaster (above).
0936: *
0937: * @param ras The raster to make writable.
0938: *
0939: * @param minX The x location for the upper left corner of the
0940: * returned WritableRaster.
0941: *
0942: * @param minY The y location for the upper left corner of the
0943: * returned WritableRaster.
0944: *
0945: * @return A Writable version of <tT>ras</tt> with it's upper left
0946: * hand coordinate set to minX, minY (shares it's DataBuffer
0947: * with <tt>ras</tt>).
0948: */
0949: public static WritableRaster makeRasterWritable(Raster ras,
0950: int minX, int minY) {
0951: WritableRaster ret = Raster
0952: .createWritableRaster(ras.getSampleModel(), ras
0953: .getDataBuffer(), new Point(0, 0));
0954: ret = ret.createWritableChild(ras.getMinX()
0955: - ras.getSampleModelTranslateX(), ras.getMinY()
0956: - ras.getSampleModelTranslateY(), ras.getWidth(), ras
0957: .getHeight(), minX, minY, null);
0958: return ret;
0959: }
0960:
0961: /**
0962: * Create a new ColorModel with it's alpha premultiplied state matching
0963: * newAlphaPreMult.
0964: * @param cm The ColorModel to change the alpha premult state of.
0965: * @param newAlphaPreMult The new state of alpha premult.
0966: * @return A new colorModel that has isAlphaPremultiplied()
0967: * equal to newAlphaPreMult.
0968: */
0969: public static ColorModel coerceColorModel(ColorModel cm,
0970: boolean newAlphaPreMult) {
0971: if (cm.isAlphaPremultiplied() == newAlphaPreMult)
0972: return cm;
0973:
0974: // Easiest way to build proper colormodel for new Alpha state...
0975: // Eventually this should switch on known ColorModel types and
0976: // only fall back on this hack when the CM type is unknown.
0977: WritableRaster wr = cm.createCompatibleWritableRaster(1, 1);
0978: return cm.coerceData(wr, newAlphaPreMult);
0979: }
0980:
0981: /**
0982: * Coerces data within a bufferedImage to match newAlphaPreMult,
0983: * Note that this can not change the colormodel of bi so you
0984: *
0985: * @param wr The raster to change the state of.
0986: * @param cm The colormodel currently associated with data in wr.
0987: * @param newAlphaPreMult The desired state of alpha Premult for raster.
0988: * @return A new colormodel that matches newAlphaPreMult.
0989: */
0990: public static ColorModel coerceData(WritableRaster wr,
0991: ColorModel cm, boolean newAlphaPreMult) {
0992:
0993: // System.out.println("CoerceData: " + cm.isAlphaPremultiplied() +
0994: // " Out: " + newAlphaPreMult);
0995: if (!cm.hasAlpha())
0996: // Nothing to do no alpha channel
0997: return cm;
0998:
0999: if (cm.isAlphaPremultiplied() == newAlphaPreMult)
1000: // nothing to do alpha state matches...
1001: return cm;
1002:
1003: // System.out.println("CoerceData: " + wr.getSampleModel());
1004:
1005: if (newAlphaPreMult) {
1006: multiplyAlpha(wr);
1007: } else {
1008: divideAlpha(wr);
1009: }
1010:
1011: return coerceColorModel(cm, newAlphaPreMult);
1012: }
1013:
1014: public static void multiplyAlpha(WritableRaster wr) {
1015: if (is_BYTE_COMP_Data(wr.getSampleModel()))
1016: mult_BYTE_COMP_Data(wr);
1017: else if (is_INT_PACK_Data(wr.getSampleModel(), true))
1018: mult_INT_PACK_Data(wr);
1019: else {
1020: int[] pixel = null;
1021: int bands = wr.getNumBands();
1022: float norm = 1.0f / 255f;
1023: int x0, x1, y0, y1, a, b;
1024: float alpha;
1025: x0 = wr.getMinX();
1026: x1 = x0 + wr.getWidth();
1027: y0 = wr.getMinY();
1028: y1 = y0 + wr.getHeight();
1029: for (int y = y0; y < y1; y++)
1030: for (int x = x0; x < x1; x++) {
1031: pixel = wr.getPixel(x, y, pixel);
1032: a = pixel[bands - 1];
1033: if ((a >= 0) && (a < 255)) {
1034: alpha = a * norm;
1035: for (b = 0; b < bands - 1; b++)
1036: pixel[b] = (int) (pixel[b] * alpha + 0.5f);
1037: wr.setPixel(x, y, pixel);
1038: }
1039: }
1040: }
1041: }
1042:
1043: public static void divideAlpha(WritableRaster wr) {
1044: if (is_BYTE_COMP_Data(wr.getSampleModel()))
1045: divide_BYTE_COMP_Data(wr);
1046: else if (is_INT_PACK_Data(wr.getSampleModel(), true))
1047: divide_INT_PACK_Data(wr);
1048: else {
1049: int x0, x1, y0, y1, a, b;
1050: float ialpha;
1051: int bands = wr.getNumBands();
1052: int[] pixel = null;
1053:
1054: x0 = wr.getMinX();
1055: x1 = x0 + wr.getWidth();
1056: y0 = wr.getMinY();
1057: y1 = y0 + wr.getHeight();
1058: for (int y = y0; y < y1; y++)
1059: for (int x = x0; x < x1; x++) {
1060: pixel = wr.getPixel(x, y, pixel);
1061: a = pixel[bands - 1];
1062: if ((a > 0) && (a < 255)) {
1063: ialpha = 255 / (float) a;
1064: for (b = 0; b < bands - 1; b++)
1065: pixel[b] = (int) (pixel[b] * ialpha + 0.5f);
1066: wr.setPixel(x, y, pixel);
1067: }
1068: }
1069: }
1070: }
1071:
1072: /**
1073: * Copies data from one bufferedImage to another paying attention
1074: * to the state of AlphaPreMultiplied.
1075: *
1076: * @param src The source
1077: * @param dst The destination
1078: */
1079: public static void copyData(BufferedImage src, BufferedImage dst) {
1080: Rectangle srcRect = new Rectangle(0, 0, src.getWidth(), src
1081: .getHeight());
1082: copyData(src, srcRect, dst, new Point(0, 0));
1083: }
1084:
1085: /**
1086: * Copies data from one bufferedImage to another paying attention
1087: * to the state of AlphaPreMultiplied.
1088: *
1089: * @param src The source
1090: * @param srcRect The Rectangle of source data to be copied
1091: * @param dst The destination
1092: * @param destP The Place for the upper left corner of srcRect in dst.
1093: */
1094: public static void copyData(BufferedImage src, Rectangle srcRect,
1095: BufferedImage dst, Point destP) {
1096:
1097: /*
1098: if (srcCS != dstCS)
1099: throw new IllegalArgumentException
1100: ("Images must be in the same ColorSpace in order "+
1101: "to copy Data between them");
1102: */
1103: boolean srcAlpha = src.getColorModel().hasAlpha();
1104: boolean dstAlpha = dst.getColorModel().hasAlpha();
1105:
1106: // System.out.println("Src has: " + srcAlpha +
1107: // " is: " + src.isAlphaPremultiplied());
1108: //
1109: // System.out.println("Dst has: " + dstAlpha +
1110: // " is: " + dst.isAlphaPremultiplied());
1111:
1112: if (srcAlpha == dstAlpha)
1113: if ((!srcAlpha)
1114: || (src.isAlphaPremultiplied() == dst
1115: .isAlphaPremultiplied())) {
1116: // They match one another so just copy everything...
1117: copyData(src.getRaster(), dst.getRaster());
1118: return;
1119: }
1120:
1121: // System.out.println("Using Slow CopyData");
1122:
1123: int[] pixel = null;
1124: Raster srcR = src.getRaster();
1125: WritableRaster dstR = dst.getRaster();
1126: int bands = dstR.getNumBands();
1127:
1128: int dx = destP.x - srcRect.x;
1129: int dy = destP.y - srcRect.y;
1130:
1131: int w = srcRect.width;
1132: int x0 = srcRect.x;
1133: int y0 = srcRect.y;
1134: int y1 = y0 + srcRect.height - 1;
1135:
1136: if (!srcAlpha) {
1137: // Src has no alpha dest does so set alpha to 1.0 everywhere.
1138: // System.out.println("Add Alpha");
1139: int[] oPix = new int[bands * w];
1140: int out = (w * bands) - 1; // The 2 skips alpha channel
1141: while (out >= 0) {
1142: // Fill alpha channel with 255's
1143: oPix[out] = 255;
1144: out -= bands;
1145: }
1146:
1147: int b, in;
1148: for (int y = y0; y <= y1; y++) {
1149: pixel = srcR.getPixels(x0, y, w, 1, pixel);
1150: in = w * (bands - 1) - 1;
1151: out = (w * bands) - 2; // The 2 skips alpha channel on last pix
1152: switch (bands) {
1153: case 4:
1154: while (in >= 0) {
1155: oPix[out--] = pixel[in--];
1156: oPix[out--] = pixel[in--];
1157: oPix[out--] = pixel[in--];
1158: out--;
1159: }
1160: break;
1161: default:
1162: while (in >= 0) {
1163: for (b = 0; b < bands - 1; b++)
1164: oPix[out--] = pixel[in--];
1165: out--;
1166: }
1167: }
1168: dstR.setPixels(x0 + dx, y + dy, w, 1, oPix);
1169: }
1170: } else if (dstAlpha && dst.isAlphaPremultiplied()) {
1171: // Src and dest have Alpha but we need to multiply it for dst.
1172: // System.out.println("Mult Case");
1173: int a, b, alpha, in, fpNorm = (1 << 24) / 255, pt5 = 1 << 23;
1174: for (int y = y0; y <= y1; y++) {
1175: pixel = srcR.getPixels(x0, y, w, 1, pixel);
1176: in = bands * w - 1;
1177: switch (bands) {
1178: case 4:
1179: while (in >= 0) {
1180: a = pixel[in];
1181: if (a == 255)
1182: in -= 4;
1183: else {
1184: in--;
1185: alpha = fpNorm * a;
1186: pixel[in] = (pixel[in] * alpha + pt5) >>> 24;
1187: in--;
1188: pixel[in] = (pixel[in] * alpha + pt5) >>> 24;
1189: in--;
1190: pixel[in] = (pixel[in] * alpha + pt5) >>> 24;
1191: in--;
1192: }
1193: }
1194: break;
1195: default:
1196: while (in >= 0) {
1197: a = pixel[in];
1198: if (a == 255)
1199: in -= bands;
1200: else {
1201: in--;
1202: alpha = fpNorm * a;
1203: for (b = 0; b < bands - 1; b++) {
1204: pixel[in] = (pixel[in] * alpha + pt5) >>> 24;
1205: in--;
1206: }
1207: }
1208: }
1209: }
1210: dstR.setPixels(x0 + dx, y + dy, w, 1, pixel);
1211: }
1212: } else if (dstAlpha && !dst.isAlphaPremultiplied()) {
1213: // Src and dest have Alpha but we need to divide it out for dst.
1214: // System.out.println("Div Case");
1215: int a, b, ialpha, in, fpNorm = 0x00FF0000, pt5 = 1 << 15;
1216: for (int y = y0; y <= y1; y++) {
1217: pixel = srcR.getPixels(x0, y, w, 1, pixel);
1218: in = (bands * w) - 1;
1219: switch (bands) {
1220: case 4:
1221: while (in >= 0) {
1222: a = pixel[in];
1223: if ((a <= 0) || (a >= 255))
1224: in -= 4;
1225: else {
1226: in--;
1227: ialpha = fpNorm / a;
1228: pixel[in] = (pixel[in] * ialpha + pt5) >>> 16;
1229: in--;
1230: pixel[in] = (pixel[in] * ialpha + pt5) >>> 16;
1231: in--;
1232: pixel[in] = (pixel[in] * ialpha + pt5) >>> 16;
1233: in--;
1234: }
1235: }
1236: break;
1237: default:
1238: while (in >= 0) {
1239: a = pixel[in];
1240: if ((a <= 0) || (a >= 255))
1241: in -= bands;
1242: else {
1243: in--;
1244: ialpha = fpNorm / a;
1245: for (b = 0; b < bands - 1; b++) {
1246: pixel[in] = (pixel[in] * ialpha + pt5) >>> 16;
1247: in--;
1248: }
1249: }
1250: }
1251: }
1252: dstR.setPixels(x0 + dx, y + dy, w, 1, pixel);
1253: }
1254: } else if (src.isAlphaPremultiplied()) {
1255: int[] oPix = new int[bands * w];
1256: // Src has alpha dest does not so unpremult and store...
1257: // System.out.println("Remove Alpha, Div Case");
1258: int a, b, ialpha, in, out, fpNorm = 0x00FF0000, pt5 = 1 << 15;
1259: for (int y = y0; y <= y1; y++) {
1260: pixel = srcR.getPixels(x0, y, w, 1, pixel);
1261: in = (bands + 1) * w - 1;
1262: out = (bands * w) - 1;
1263: while (in >= 0) {
1264: a = pixel[in];
1265: in--;
1266: if (a > 0) {
1267: if (a < 255) {
1268: ialpha = fpNorm / a;
1269: for (b = 0; b < bands; b++)
1270: oPix[out--] = (pixel[in--] * ialpha + pt5) >>> 16;
1271: } else
1272: for (b = 0; b < bands; b++)
1273: oPix[out--] = pixel[in--];
1274: } else {
1275: in -= bands;
1276: for (b = 0; b < bands; b++)
1277: oPix[out--] = 255;
1278: }
1279: }
1280: dstR.setPixels(x0 + dx, y + dy, w, 1, oPix);
1281: }
1282: } else {
1283: // Src has unpremult alpha, dest does not have alpha,
1284: // just copy the color channels over.
1285: Rectangle dstRect = new Rectangle(destP.x, destP.y,
1286: srcRect.width, srcRect.height);
1287: for (int b = 0; b < bands; b++)
1288: copyBand(srcR, srcRect, b, dstR, dstRect, b);
1289: }
1290: }
1291:
1292: public static void copyBand(Raster src, int srcBand,
1293: WritableRaster dst, int dstBand) {
1294:
1295: Rectangle sR = src.getBounds();
1296: Rectangle dR = dst.getBounds();
1297: Rectangle cpR = sR.intersection(dR);
1298:
1299: copyBand(src, cpR, srcBand, dst, cpR, dstBand);
1300: }
1301:
1302: public static void copyBand(Raster src, Rectangle sR, int sBand,
1303: WritableRaster dst, Rectangle dR, int dBand) {
1304: int dy = dR.y - sR.y;
1305: int dx = dR.x - sR.x;
1306: sR = sR.intersection(src.getBounds());
1307: dR = dR.intersection(dst.getBounds());
1308: int width, height;
1309: if (dR.width < sR.width)
1310: width = dR.width;
1311: else
1312: width = sR.width;
1313: if (dR.height < sR.height)
1314: height = dR.height;
1315: else
1316: height = sR.height;
1317:
1318: int x = sR.x + dx;
1319: int[] samples = null;
1320: for (int y = sR.y; y < sR.y + height; y++) {
1321: samples = src.getSamples(sR.x, y, width, 1, sBand, samples);
1322: dst.setSamples(x, y + dy, width, 1, dBand, samples);
1323: }
1324: }
1325:
1326: public static boolean is_INT_PACK_Data(SampleModel sm,
1327: boolean requireAlpha) {
1328: // Check ColorModel is of type DirectColorModel
1329: if (!(sm instanceof SinglePixelPackedSampleModel))
1330: return false;
1331:
1332: // Check transfer type
1333: if (sm.getDataType() != DataBuffer.TYPE_INT)
1334: return false;
1335:
1336: SinglePixelPackedSampleModel sppsm;
1337: sppsm = (SinglePixelPackedSampleModel) sm;
1338:
1339: int[] masks = sppsm.getBitMasks();
1340: if (masks.length == 3) {
1341: if (requireAlpha)
1342: return false;
1343: } else if (masks.length != 4)
1344: return false;
1345:
1346: if (masks[0] != 0x00ff0000)
1347: return false;
1348: if (masks[1] != 0x0000ff00)
1349: return false;
1350: if (masks[2] != 0x000000ff)
1351: return false;
1352: if ((masks.length == 4) && (masks[3] != 0xff000000))
1353: return false;
1354:
1355: return true;
1356: }
1357:
1358: public static boolean is_BYTE_COMP_Data(SampleModel sm) {
1359: // Check ColorModel is of type DirectColorModel
1360: if (!(sm instanceof ComponentSampleModel))
1361: return false;
1362:
1363: // Check transfer type
1364: if (sm.getDataType() != DataBuffer.TYPE_BYTE)
1365: return false;
1366:
1367: return true;
1368: }
1369:
1370: protected static void divide_INT_PACK_Data(WritableRaster wr) {
1371: // System.out.println("Divide Int");
1372:
1373: SinglePixelPackedSampleModel sppsm;
1374: sppsm = (SinglePixelPackedSampleModel) wr.getSampleModel();
1375:
1376: final int width = wr.getWidth();
1377:
1378: final int scanStride = sppsm.getScanlineStride();
1379: DataBufferInt db = (DataBufferInt) wr.getDataBuffer();
1380: final int base = (db.getOffset() + sppsm.getOffset(wr.getMinX()
1381: - wr.getSampleModelTranslateX(), wr.getMinY()
1382: - wr.getSampleModelTranslateY()));
1383:
1384: // Access the pixel data array
1385: final int[] pixels = db.getBankData()[0];
1386: for (int y = 0; y < wr.getHeight(); y++) {
1387: int sp = base + y * scanStride;
1388: final int end = sp + width;
1389: while (sp < end) {
1390: int pixel = pixels[sp];
1391: int a = pixel >>> 24;
1392: if (a <= 0) {
1393: pixels[sp] = 0x00FFFFFF;
1394: } else if (a < 255) {
1395: int aFP = (0x00FF0000 / a);
1396: pixels[sp] = ((a << 24)
1397: | (((((pixel & 0xFF0000) >> 16) * aFP) & 0xFF0000))
1398: | (((((pixel & 0x00FF00) >> 8) * aFP) & 0xFF0000) >> 8) | (((((pixel & 0x0000FF)) * aFP) & 0xFF0000) >> 16));
1399: }
1400: sp++;
1401: }
1402: }
1403: }
1404:
1405: protected static void mult_INT_PACK_Data(WritableRaster wr) {
1406: // System.out.println("Multiply Int: " + wr);
1407:
1408: SinglePixelPackedSampleModel sppsm;
1409: sppsm = (SinglePixelPackedSampleModel) wr.getSampleModel();
1410:
1411: final int width = wr.getWidth();
1412:
1413: final int scanStride = sppsm.getScanlineStride();
1414: DataBufferInt db = (DataBufferInt) wr.getDataBuffer();
1415: final int base = (db.getOffset() + sppsm.getOffset(wr.getMinX()
1416: - wr.getSampleModelTranslateX(), wr.getMinY()
1417: - wr.getSampleModelTranslateY()));
1418: // Access the pixel data array
1419: final int[] pixels = db.getBankData()[0];
1420: for (int y = 0; y < wr.getHeight(); y++) {
1421: int sp = base + y * scanStride;
1422: final int end = sp + width;
1423: while (sp < end) {
1424: int pixel = pixels[sp];
1425: int a = pixel >>> 24;
1426: if ((a >= 0) && (a < 255)) { // this does NOT include a == 255 (0xff) !
1427: pixels[sp] = ((a << 24)
1428: | ((((pixel & 0xFF0000) * a) >> 8) & 0xFF0000)
1429: | ((((pixel & 0x00FF00) * a) >> 8) & 0x00FF00) | ((((pixel & 0x0000FF) * a) >> 8) & 0x0000FF));
1430: }
1431: sp++;
1432: }
1433: }
1434: }
1435:
1436: protected static void divide_BYTE_COMP_Data(WritableRaster wr) {
1437: // System.out.println("Multiply Int: " + wr);
1438:
1439: ComponentSampleModel csm;
1440: csm = (ComponentSampleModel) wr.getSampleModel();
1441:
1442: final int width = wr.getWidth();
1443:
1444: final int scanStride = csm.getScanlineStride();
1445: final int pixStride = csm.getPixelStride();
1446: final int[] bandOff = csm.getBandOffsets();
1447:
1448: DataBufferByte db = (DataBufferByte) wr.getDataBuffer();
1449: final int base = (db.getOffset() + csm.getOffset(wr.getMinX()
1450: - wr.getSampleModelTranslateX(), wr.getMinY()
1451: - wr.getSampleModelTranslateY()));
1452:
1453: int aOff = bandOff[bandOff.length - 1];
1454: int bands = bandOff.length - 1;
1455:
1456: // Access the pixel data array
1457: final byte[] pixels = db.getBankData()[0];
1458: for (int y = 0; y < wr.getHeight(); y++) {
1459: int sp = base + y * scanStride;
1460: final int end = sp + width * pixStride;
1461: while (sp < end) {
1462: int a = pixels[sp + aOff] & 0xFF;
1463: if (a == 0) {
1464: for (int b = 0; b < bands; b++)
1465: pixels[sp + bandOff[b]] = (byte) 0xFF;
1466: } else if (a < 255) { // this does NOT include a == 255 (0xff) !
1467: int aFP = (0x00FF0000 / a);
1468: for (int b = 0; b < bands; b++) {
1469: int i = sp + bandOff[b];
1470: pixels[i] = (byte) (((pixels[i] & 0xFF) * aFP) >>> 16);
1471: }
1472: }
1473: sp += pixStride;
1474: }
1475: }
1476: }
1477:
1478: protected static void mult_BYTE_COMP_Data(WritableRaster wr) {
1479: // System.out.println("Multiply Int: " + wr);
1480:
1481: ComponentSampleModel csm;
1482: csm = (ComponentSampleModel) wr.getSampleModel();
1483:
1484: final int width = wr.getWidth();
1485:
1486: final int scanStride = csm.getScanlineStride();
1487: final int pixStride = csm.getPixelStride();
1488: final int[] bandOff = csm.getBandOffsets();
1489:
1490: DataBufferByte db = (DataBufferByte) wr.getDataBuffer();
1491: final int base = (db.getOffset() + csm.getOffset(wr.getMinX()
1492: - wr.getSampleModelTranslateX(), wr.getMinY()
1493: - wr.getSampleModelTranslateY()));
1494:
1495: int aOff = bandOff[bandOff.length - 1];
1496: int bands = bandOff.length - 1;
1497:
1498: // Access the pixel data array
1499: final byte[] pixels = db.getBankData()[0];
1500: for (int y = 0; y < wr.getHeight(); y++) {
1501: int sp = base + y * scanStride;
1502: final int end = sp + width * pixStride;
1503: while (sp < end) {
1504: int a = pixels[sp + aOff] & 0xFF;
1505: if (a != 0xFF)
1506: for (int b = 0; b < bands; b++) {
1507: int i = sp + bandOff[b];
1508: pixels[i] = (byte) (((pixels[i] & 0xFF) * a) >> 8);
1509: }
1510: sp += pixStride;
1511: }
1512: }
1513: }
1514:
1515: /*
1516: This is skanky debugging code that might be useful in the future:
1517:
1518: if (count == 33) {
1519: String label = "sub [" + x + ", " + y + "]: ";
1520: org.ImageDisplay.showImage
1521: (label, subBI);
1522: org.ImageDisplay.printImage
1523: (label, subBI,
1524: new Rectangle(75-iR.x, 90-iR.y, 32, 32));
1525:
1526: }
1527:
1528:
1529: // if ((count++ % 50) == 10)
1530: // org.ImageDisplay.showImage("foo: ", subBI);
1531:
1532:
1533: Graphics2D realG2D = g2d;
1534: while (realG2D instanceof sun.java2d.ProxyGraphics2D) {
1535: realG2D = ((sun.java2d.ProxyGraphics2D)realG2D).getDelegate();
1536: }
1537: if (realG2D instanceof sun.awt.image.BufferedImageGraphics2D) {
1538: count++;
1539: if (count == 34) {
1540: RenderedImage ri;
1541: ri = ((sun.awt.image.BufferedImageGraphics2D)realG2D).bufImg;
1542: // g2d.setComposite(SVGComposite.OVER);
1543: // org.ImageDisplay.showImage("Bar: " + count, cr);
1544: org.ImageDisplay.printImage("Bar: " + count, cr,
1545: new Rectangle(75, 90, 32, 32));
1546:
1547: org.ImageDisplay.showImage ("Foo: " + count, ri);
1548: org.ImageDisplay.printImage("Foo: " + count, ri,
1549: new Rectangle(75, 90, 32, 32));
1550:
1551: System.out.println("BI: " + ri);
1552: System.out.println("BISM: " + ri.getSampleModel());
1553: System.out.println("BICM: " + ri.getColorModel());
1554: System.out.println("BICM class: " + ri.getColorModel().getClass());
1555: System.out.println("BICS: " + ri.getColorModel().getColorSpace());
1556: System.out.println
1557: ("sRGB CS: " +
1558: ColorSpace.getInstance(ColorSpace.CS_sRGB));
1559: System.out.println("G2D info");
1560: System.out.println("\tComposite: " + g2d.getComposite());
1561: System.out.println("\tTransform" + g2d.getTransform());
1562: java.awt.RenderingHints rh = g2d.getRenderingHints();
1563: java.util.Set keys = rh.keySet();
1564: java.util.Iterator iter = keys.iterator();
1565: while (iter.hasNext()) {
1566: Object o = iter.next();
1567:
1568: System.out.println("\t" + o.toString() + " -> " +
1569: rh.get(o).toString());
1570: }
1571:
1572: ri = cr;
1573: System.out.println("RI: " + ri);
1574: System.out.println("RISM: " + ri.getSampleModel());
1575: System.out.println("RICM: " + ri.getColorModel());
1576: System.out.println("RICM class: " + ri.getColorModel().getClass());
1577: System.out.println("RICS: " + ri.getColorModel().getColorSpace());
1578: }
1579: }
1580: */
1581:
1582: }
|