0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: /* $Id: GraphicsUtil.java 499611 2007-01-24 23:17:50Z cam $ */
0019:
0020: package org.apache.xmlgraphics.image;
0021:
0022: import java.awt.Point;
0023: import java.awt.Rectangle;
0024: import java.awt.color.ColorSpace;
0025: import java.awt.geom.AffineTransform;
0026: import java.awt.image.BufferedImage;
0027: import java.awt.image.ColorModel;
0028: import java.awt.image.ComponentSampleModel;
0029: import java.awt.image.DataBuffer;
0030: import java.awt.image.DataBufferByte;
0031: import java.awt.image.DataBufferInt;
0032: import java.awt.image.DataBufferShort;
0033: import java.awt.image.DataBufferUShort;
0034: import java.awt.image.DirectColorModel;
0035: import java.awt.image.Raster;
0036: import java.awt.image.RenderedImage;
0037: import java.awt.image.SampleModel;
0038: import java.awt.image.SinglePixelPackedSampleModel;
0039: import java.awt.image.WritableRaster;
0040:
0041: import org.apache.xmlgraphics.image.rendered.Any2LsRGBRed;
0042: import org.apache.xmlgraphics.image.rendered.Any2sRGBRed;
0043: import org.apache.xmlgraphics.image.rendered.BufferedImageCachableRed;
0044: import org.apache.xmlgraphics.image.rendered.CachableRed;
0045: import org.apache.xmlgraphics.image.rendered.RenderedImageCachableRed;
0046:
0047: /**
0048: * Set of utility methods for Graphics.
0049: * These generally bypass broken methods in Java2D or provide tweaked
0050: * implementations.
0051: *
0052: * @author <a href="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a>
0053: * @version $Id: GraphicsUtil.java 499611 2007-01-24 23:17:50Z cam $
0054: */
0055: public class GraphicsUtil {
0056:
0057: public static AffineTransform IDENTITY = new AffineTransform();
0058:
0059: /**
0060: * Standard prebuilt Linear_sRGB color model with no alpha */
0061: public static final ColorModel Linear_sRGB = new DirectColorModel(
0062: ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), 24,
0063: 0x00FF0000, 0x0000FF00, 0x000000FF, 0x0, false,
0064: DataBuffer.TYPE_INT);
0065: /**
0066: * Standard prebuilt Linear_sRGB color model with premultiplied alpha.
0067: */
0068: public static final ColorModel Linear_sRGB_Pre = new DirectColorModel(
0069: ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), 32,
0070: 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, true,
0071: DataBuffer.TYPE_INT);
0072: /**
0073: * Standard prebuilt Linear_sRGB color model with unpremultiplied alpha.
0074: */
0075: public static final ColorModel Linear_sRGB_Unpre = new DirectColorModel(
0076: ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), 32,
0077: 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, false,
0078: DataBuffer.TYPE_INT);
0079:
0080: /**
0081: * Standard prebuilt sRGB color model with no alpha.
0082: */
0083: public static final ColorModel sRGB = new DirectColorModel(
0084: ColorSpace.getInstance(ColorSpace.CS_sRGB), 24, 0x00FF0000,
0085: 0x0000FF00, 0x000000FF, 0x0, false, DataBuffer.TYPE_INT);
0086: /**
0087: * Standard prebuilt sRGB color model with premultiplied alpha.
0088: */
0089: public static final ColorModel sRGB_Pre = new DirectColorModel(
0090: ColorSpace.getInstance(ColorSpace.CS_sRGB), 32, 0x00FF0000,
0091: 0x0000FF00, 0x000000FF, 0xFF000000, true,
0092: DataBuffer.TYPE_INT);
0093: /**
0094: * Standard prebuilt sRGB color model with unpremultiplied alpha.
0095: */
0096: public static final ColorModel sRGB_Unpre = new DirectColorModel(
0097: ColorSpace.getInstance(ColorSpace.CS_sRGB), 32, 0x00FF0000,
0098: 0x0000FF00, 0x000000FF, 0xFF000000, false,
0099: DataBuffer.TYPE_INT);
0100:
0101: /**
0102: * Method that returns either Linear_sRGB_Pre or Linear_sRGB_UnPre
0103: * based on premult flag.
0104: * @param premult True if the ColorModel should have premultiplied alpha.
0105: * @return a ColorMdoel with Linear sRGB colorSpace and
0106: * the alpha channel set in accordance with
0107: * <tt>premult</tt>
0108: */
0109: public static ColorModel makeLinear_sRGBCM(boolean premult) {
0110: return premult ? Linear_sRGB_Pre : Linear_sRGB_Unpre;
0111: }
0112:
0113: /**
0114: * Constructs a BufferedImage with a linear sRGB colorModel, and alpha.
0115: * @param width The desired width of the BufferedImage
0116: * @param height The desired height of the BufferedImage
0117: * @param premult The desired state of alpha premultiplied
0118: * @return The requested BufferedImage.
0119: */
0120: public static BufferedImage makeLinearBufferedImage(int width,
0121: int height, boolean premult) {
0122: ColorModel cm = makeLinear_sRGBCM(premult);
0123: WritableRaster wr = cm.createCompatibleWritableRaster(width,
0124: height);
0125: return new BufferedImage(cm, wr, premult, null);
0126: }
0127:
0128: /**
0129: * This method will return a CacheableRed that has it's data in
0130: * the linear sRGB colorspace. If <tt>src</tt> is already in
0131: * linear sRGB then this method does nothing and returns <tt>src</tt>.
0132: * Otherwise it creates a transform that will convert
0133: * <tt>src</tt>'s output to linear sRGB and returns that CacheableRed.
0134: *
0135: * @param src The image to convert to linear sRGB.
0136: * @return An equivilant image to <tt>src</tt> who's data is in
0137: * linear sRGB.
0138: */
0139: public static CachableRed convertToLsRGB(CachableRed src) {
0140: ColorModel cm = src.getColorModel();
0141: ColorSpace cs = cm.getColorSpace();
0142: if (cs == ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB))
0143: return src;
0144:
0145: return new Any2LsRGBRed(src);
0146: }
0147:
0148: /**
0149: * This method will return a CacheableRed that has it's data in
0150: * the sRGB colorspace. If <tt>src</tt> is already in
0151: * sRGB then this method does nothing and returns <tt>src</tt>.
0152: * Otherwise it creates a transform that will convert
0153: * <tt>src</tt>'s output to sRGB and returns that CacheableRed.
0154: *
0155: * @param src The image to convert to sRGB.
0156: * @return An equivilant image to <tt>src</tt> who's data is in sRGB.
0157: */
0158: public static CachableRed convertTosRGB(CachableRed src) {
0159: ColorModel cm = src.getColorModel();
0160: ColorSpace cs = cm.getColorSpace();
0161: if (cs == ColorSpace.getInstance(ColorSpace.CS_sRGB))
0162: return src;
0163:
0164: return new Any2sRGBRed(src);
0165: }
0166:
0167: /**
0168: * Convertes any RenderedImage to a CacheableRed. <p>
0169: * If <tt>ri</tt> is already a CacheableRed it casts it down and
0170: * returns it.<p>
0171: *
0172: * In cases where <tt>ri</tt> is not already a CacheableRed it
0173: * wraps <tt>ri</tt> with a helper class. The wrapped
0174: * CacheableRed "Pretends" that it has no sources since it has no
0175: * way of inteligently handling the dependency/dirty region calls
0176: * if it exposed the source.
0177: * @param ri The RenderedImage to convert.
0178: * @return a CacheableRed that contains the same data as ri.
0179: */
0180: public static CachableRed wrap(RenderedImage ri) {
0181: if (ri instanceof CachableRed)
0182: return (CachableRed) ri;
0183: if (ri instanceof BufferedImage)
0184: return new BufferedImageCachableRed((BufferedImage) ri);
0185: return new RenderedImageCachableRed(ri);
0186: }
0187:
0188: /**
0189: * An internal optimized version of copyData designed to work on
0190: * Integer packed data with a SinglePixelPackedSampleModel. Only
0191: * the region of overlap between src and dst is copied.
0192: *
0193: * Calls to this should be preflighted with is_INT_PACK_Data
0194: * on both src and dest (requireAlpha can be false).
0195: *
0196: * @param src The source of the data
0197: * @param dst The destination for the data.
0198: */
0199: public static void copyData_INT_PACK(Raster src, WritableRaster dst) {
0200: // System.out.println("Fast copyData");
0201: int x0 = dst.getMinX();
0202: if (x0 < src.getMinX())
0203: x0 = src.getMinX();
0204:
0205: int y0 = dst.getMinY();
0206: if (y0 < src.getMinY())
0207: y0 = src.getMinY();
0208:
0209: int x1 = dst.getMinX() + dst.getWidth() - 1;
0210: if (x1 > src.getMinX() + src.getWidth() - 1)
0211: x1 = src.getMinX() + src.getWidth() - 1;
0212:
0213: int y1 = dst.getMinY() + dst.getHeight() - 1;
0214: if (y1 > src.getMinY() + src.getHeight() - 1)
0215: y1 = src.getMinY() + src.getHeight() - 1;
0216:
0217: int width = x1 - x0 + 1;
0218: int height = y1 - y0 + 1;
0219:
0220: SinglePixelPackedSampleModel srcSPPSM;
0221: srcSPPSM = (SinglePixelPackedSampleModel) src.getSampleModel();
0222:
0223: final int srcScanStride = srcSPPSM.getScanlineStride();
0224: DataBufferInt srcDB = (DataBufferInt) src.getDataBuffer();
0225: final int[] srcPixels = srcDB.getBankData()[0];
0226: final int srcBase = (srcDB.getOffset() + srcSPPSM.getOffset(x0
0227: - src.getSampleModelTranslateX(), y0
0228: - src.getSampleModelTranslateY()));
0229:
0230: SinglePixelPackedSampleModel dstSPPSM;
0231: dstSPPSM = (SinglePixelPackedSampleModel) dst.getSampleModel();
0232:
0233: final int dstScanStride = dstSPPSM.getScanlineStride();
0234: DataBufferInt dstDB = (DataBufferInt) dst.getDataBuffer();
0235: final int[] dstPixels = dstDB.getBankData()[0];
0236: final int dstBase = (dstDB.getOffset() + dstSPPSM.getOffset(x0
0237: - dst.getSampleModelTranslateX(), y0
0238: - dst.getSampleModelTranslateY()));
0239:
0240: if ((srcScanStride == dstScanStride)
0241: && (srcScanStride == width)) {
0242: // System.out.println("VERY Fast copyData");
0243:
0244: System.arraycopy(srcPixels, srcBase, dstPixels, dstBase,
0245: width * height);
0246: } else if (width > 128) {
0247: int srcSP = srcBase;
0248: int dstSP = dstBase;
0249: for (int y = 0; y < height; y++) {
0250: System.arraycopy(srcPixels, srcSP, dstPixels, dstSP,
0251: width);
0252: srcSP += srcScanStride;
0253: dstSP += dstScanStride;
0254: }
0255: } else {
0256: for (int y = 0; y < height; y++) {
0257: int srcSP = srcBase + y * srcScanStride;
0258: int dstSP = dstBase + y * dstScanStride;
0259: for (int x = 0; x < width; x++)
0260: dstPixels[dstSP++] = srcPixels[srcSP++];
0261: }
0262: }
0263: }
0264:
0265: public static void copyData_FALLBACK(Raster src, WritableRaster dst) {
0266: // System.out.println("Fallback copyData");
0267:
0268: int x0 = dst.getMinX();
0269: if (x0 < src.getMinX())
0270: x0 = src.getMinX();
0271:
0272: int y0 = dst.getMinY();
0273: if (y0 < src.getMinY())
0274: y0 = src.getMinY();
0275:
0276: int x1 = dst.getMinX() + dst.getWidth() - 1;
0277: if (x1 > src.getMinX() + src.getWidth() - 1)
0278: x1 = src.getMinX() + src.getWidth() - 1;
0279:
0280: int y1 = dst.getMinY() + dst.getHeight() - 1;
0281: if (y1 > src.getMinY() + src.getHeight() - 1)
0282: y1 = src.getMinY() + src.getHeight() - 1;
0283:
0284: int width = x1 - x0 + 1;
0285: int[] data = null;
0286:
0287: for (int y = y0; y <= y1; y++) {
0288: data = src.getPixels(x0, y, width, 1, data);
0289: dst.setPixels(x0, y, width, 1, data);
0290: }
0291: }
0292:
0293: /**
0294: * Copies data from one raster to another. Only the region of
0295: * overlap between src and dst is copied. <tt>Src</tt> and
0296: * <tt>Dst</tt> must have compatible SampleModels.
0297: *
0298: * @param src The source of the data
0299: * @param dst The destination for the data.
0300: */
0301: public static void copyData(Raster src, WritableRaster dst) {
0302: if (is_INT_PACK_Data(src.getSampleModel(), false)
0303: && is_INT_PACK_Data(dst.getSampleModel(), false)) {
0304: copyData_INT_PACK(src, dst);
0305: return;
0306: }
0307:
0308: copyData_FALLBACK(src, dst);
0309: }
0310:
0311: /**
0312: * Creates a new raster that has a <b>copy</b> of the data in
0313: * <tt>ras</tt>. This is highly optimized for speed. There is
0314: * no provision for changing any aspect of the SampleModel.
0315: *
0316: * This method should be used when you need to change the contents
0317: * of a Raster that you do not "own" (ie the result of a
0318: * <tt>getData</tt> call).
0319: * @param ras The Raster to copy.
0320: * @return A writable copy of <tt>ras</tt>
0321: */
0322: public static WritableRaster copyRaster(Raster ras) {
0323: return copyRaster(ras, ras.getMinX(), ras.getMinY());
0324: }
0325:
0326: /**
0327: * Creates a new raster that has a <b>copy</b> of the data in
0328: * <tt>ras</tt>. This is highly optimized for speed. There is
0329: * no provision for changing any aspect of the SampleModel.
0330: * However you can specify a new location for the returned raster.
0331: *
0332: * This method should be used when you need to change the contents
0333: * of a Raster that you do not "own" (ie the result of a
0334: * <tt>getData</tt> call).
0335: *
0336: * @param ras The Raster to copy.
0337: *
0338: * @param minX The x location for the upper left corner of the
0339: * returned WritableRaster.
0340: *
0341: * @param minY The y location for the upper left corner of the
0342: * returned WritableRaster.
0343: *
0344: * @return A writable copy of <tt>ras</tt>
0345: */
0346: public static WritableRaster copyRaster(Raster ras, int minX,
0347: int minY) {
0348: WritableRaster ret = Raster.createWritableRaster(ras
0349: .getSampleModel(), new Point(0, 0));
0350: ret = ret.createWritableChild(ras.getMinX()
0351: - ras.getSampleModelTranslateX(), ras.getMinY()
0352: - ras.getSampleModelTranslateY(), ras.getWidth(), ras
0353: .getHeight(), minX, minY, null);
0354:
0355: // Use System.arraycopy to copy the data between the two...
0356: DataBuffer srcDB = ras.getDataBuffer();
0357: DataBuffer retDB = ret.getDataBuffer();
0358: if (srcDB.getDataType() != retDB.getDataType()) {
0359: throw new IllegalArgumentException(
0360: "New DataBuffer doesn't match original");
0361: }
0362: int len = srcDB.getSize();
0363: int banks = srcDB.getNumBanks();
0364: int[] offsets = srcDB.getOffsets();
0365: for (int b = 0; b < banks; b++) {
0366: switch (srcDB.getDataType()) {
0367: case DataBuffer.TYPE_BYTE: {
0368: DataBufferByte srcDBT = (DataBufferByte) srcDB;
0369: DataBufferByte retDBT = (DataBufferByte) retDB;
0370: System.arraycopy(srcDBT.getData(b), offsets[b], retDBT
0371: .getData(b), offsets[b], len);
0372: break;
0373: }
0374: case DataBuffer.TYPE_INT: {
0375: DataBufferInt srcDBT = (DataBufferInt) srcDB;
0376: DataBufferInt retDBT = (DataBufferInt) retDB;
0377: System.arraycopy(srcDBT.getData(b), offsets[b], retDBT
0378: .getData(b), offsets[b], len);
0379: break;
0380: }
0381: case DataBuffer.TYPE_SHORT: {
0382: DataBufferShort srcDBT = (DataBufferShort) srcDB;
0383: DataBufferShort retDBT = (DataBufferShort) retDB;
0384: System.arraycopy(srcDBT.getData(b), offsets[b], retDBT
0385: .getData(b), offsets[b], len);
0386: break;
0387: }
0388: case DataBuffer.TYPE_USHORT: {
0389: DataBufferUShort srcDBT = (DataBufferUShort) srcDB;
0390: DataBufferUShort retDBT = (DataBufferUShort) retDB;
0391: System.arraycopy(srcDBT.getData(b), offsets[b], retDBT
0392: .getData(b), offsets[b], len);
0393: break;
0394: }
0395: }
0396: }
0397:
0398: return ret;
0399: }
0400:
0401: /**
0402: * Coerces <tt>ras</tt> to be writable. The returned Raster continues to
0403: * reference the DataBuffer from ras, so modifications to the returned
0404: * WritableRaster will be seen in ras.<p>
0405: *
0406: * This method should only be used if you need a WritableRaster due to
0407: * an interface (such as to construct a BufferedImage), but have no
0408: * intention of modifying the contents of the returned Raster. If
0409: * you have any doubt about other users of the data in <tt>ras</tt>,
0410: * use copyRaster (above).
0411: * @param ras The raster to make writable.
0412: * @return A Writable version of ras (shares DataBuffer with
0413: * <tt>ras</tt>).
0414: */
0415: public static WritableRaster makeRasterWritable(Raster ras) {
0416: return makeRasterWritable(ras, ras.getMinX(), ras.getMinY());
0417: }
0418:
0419: /**
0420: * Coerces <tt>ras</tt> to be writable. The returned Raster continues to
0421: * reference the DataBuffer from ras, so modifications to the returned
0422: * WritableRaster will be seen in ras.<p>
0423: *
0424: * You can specify a new location for the returned WritableRaster, this
0425: * is especially useful for constructing BufferedImages which require
0426: * the Raster to be at (0,0).
0427: *
0428: * This method should only be used if you need a WritableRaster due to
0429: * an interface (such as to construct a BufferedImage), but have no
0430: * intention of modifying the contents of the returned Raster. If
0431: * you have any doubt about other users of the data in <tt>ras</tt>,
0432: * use copyRaster (above).
0433: *
0434: * @param ras The raster to make writable.
0435: *
0436: * @param minX The x location for the upper left corner of the
0437: * returned WritableRaster.
0438: *
0439: * @param minY The y location for the upper left corner of the
0440: * returned WritableRaster.
0441: *
0442: * @return A Writable version of <tT>ras</tt> with it's upper left
0443: * hand coordinate set to minX, minY (shares it's DataBuffer
0444: * with <tt>ras</tt>).
0445: */
0446: public static WritableRaster makeRasterWritable(Raster ras,
0447: int minX, int minY) {
0448: WritableRaster ret = Raster
0449: .createWritableRaster(ras.getSampleModel(), ras
0450: .getDataBuffer(), new Point(0, 0));
0451: ret = ret.createWritableChild(ras.getMinX()
0452: - ras.getSampleModelTranslateX(), ras.getMinY()
0453: - ras.getSampleModelTranslateY(), ras.getWidth(), ras
0454: .getHeight(), minX, minY, null);
0455: return ret;
0456: }
0457:
0458: /**
0459: * Create a new ColorModel with it's alpha premultiplied state matching
0460: * newAlphaPreMult.
0461: * @param cm The ColorModel to change the alpha premult state of.
0462: * @param newAlphaPreMult The new state of alpha premult.
0463: * @return A new colorModel that has isAlphaPremultiplied()
0464: * equal to newAlphaPreMult.
0465: */
0466: public static ColorModel coerceColorModel(ColorModel cm,
0467: boolean newAlphaPreMult) {
0468: if (cm.isAlphaPremultiplied() == newAlphaPreMult)
0469: return cm;
0470:
0471: // Easiest way to build proper colormodel for new Alpha state...
0472: // Eventually this should switch on known ColorModel types and
0473: // only fall back on this hack when the CM type is unknown.
0474: WritableRaster wr = cm.createCompatibleWritableRaster(1, 1);
0475: return cm.coerceData(wr, newAlphaPreMult);
0476: }
0477:
0478: /**
0479: * Coerces data within a bufferedImage to match newAlphaPreMult,
0480: * Note that this can not change the colormodel of bi so you
0481: *
0482: * @param wr The raster to change the state of.
0483: * @param cm The colormodel currently associated with data in wr.
0484: * @param newAlphaPreMult The desired state of alpha Premult for raster.
0485: * @return A new colormodel that matches newAlphaPreMult.
0486: */
0487: public static ColorModel coerceData(WritableRaster wr,
0488: ColorModel cm, boolean newAlphaPreMult) {
0489:
0490: // System.out.println("CoerceData: " + cm.isAlphaPremultiplied() +
0491: // " Out: " + newAlphaPreMult);
0492: if (!cm.hasAlpha())
0493: // Nothing to do no alpha channel
0494: return cm;
0495:
0496: if (cm.isAlphaPremultiplied() == newAlphaPreMult)
0497: // nothing to do alpha state matches...
0498: return cm;
0499:
0500: // System.out.println("CoerceData: " + wr.getSampleModel());
0501:
0502: if (newAlphaPreMult) {
0503: multiplyAlpha(wr);
0504: } else {
0505: divideAlpha(wr);
0506: }
0507:
0508: return coerceColorModel(cm, newAlphaPreMult);
0509: }
0510:
0511: public static void multiplyAlpha(WritableRaster wr) {
0512: if (is_BYTE_COMP_Data(wr.getSampleModel()))
0513: mult_BYTE_COMP_Data(wr);
0514: else if (is_INT_PACK_Data(wr.getSampleModel(), true))
0515: mult_INT_PACK_Data(wr);
0516: else {
0517: int[] pixel = null;
0518: int bands = wr.getNumBands();
0519: float norm = 1f / 255f;
0520: int x0, x1, y0, y1, a, b;
0521: float alpha;
0522: x0 = wr.getMinX();
0523: x1 = x0 + wr.getWidth();
0524: y0 = wr.getMinY();
0525: y1 = y0 + wr.getHeight();
0526: for (int y = y0; y < y1; y++)
0527: for (int x = x0; x < x1; x++) {
0528: pixel = wr.getPixel(x, y, pixel);
0529: a = pixel[bands - 1];
0530: if ((a >= 0) && (a < 255)) {
0531: alpha = a * norm;
0532: for (b = 0; b < bands - 1; b++)
0533: pixel[b] = (int) (pixel[b] * alpha + 0.5f);
0534: wr.setPixel(x, y, pixel);
0535: }
0536: }
0537: }
0538: }
0539:
0540: public static void divideAlpha(WritableRaster wr) {
0541: if (is_BYTE_COMP_Data(wr.getSampleModel()))
0542: divide_BYTE_COMP_Data(wr);
0543: else if (is_INT_PACK_Data(wr.getSampleModel(), true))
0544: divide_INT_PACK_Data(wr);
0545: else {
0546: int x0, x1, y0, y1, a, b;
0547: float ialpha;
0548: int bands = wr.getNumBands();
0549: int[] pixel = null;
0550:
0551: x0 = wr.getMinX();
0552: x1 = x0 + wr.getWidth();
0553: y0 = wr.getMinY();
0554: y1 = y0 + wr.getHeight();
0555: for (int y = y0; y < y1; y++)
0556: for (int x = x0; x < x1; x++) {
0557: pixel = wr.getPixel(x, y, pixel);
0558: a = pixel[bands - 1];
0559: if ((a > 0) && (a < 255)) {
0560: ialpha = 255 / (float) a;
0561: for (b = 0; b < bands - 1; b++)
0562: pixel[b] = (int) (pixel[b] * ialpha + 0.5f);
0563: wr.setPixel(x, y, pixel);
0564: }
0565: }
0566: }
0567: }
0568:
0569: /**
0570: * Copies data from one bufferedImage to another paying attention
0571: * to the state of AlphaPreMultiplied.
0572: *
0573: * @param src The source
0574: * @param dst The destination
0575: */
0576: public static void copyData(BufferedImage src, BufferedImage dst) {
0577: Rectangle srcRect = new Rectangle(0, 0, src.getWidth(), src
0578: .getHeight());
0579: copyData(src, srcRect, dst, new Point(0, 0));
0580: }
0581:
0582: /**
0583: * Copies data from one bufferedImage to another paying attention
0584: * to the state of AlphaPreMultiplied.
0585: *
0586: * @param src The source
0587: * @param srcRect The Rectangle of source data to be copied
0588: * @param dst The destination
0589: * @param destP The Place for the upper left corner of srcRect in dst.
0590: */
0591: public static void copyData(BufferedImage src, Rectangle srcRect,
0592: BufferedImage dst, Point destP) {
0593:
0594: /*
0595: if (srcCS != dstCS)
0596: throw new IllegalArgumentException
0597: ("Images must be in the same ColorSpace in order "+
0598: "to copy Data between them");
0599: */
0600: boolean srcAlpha = src.getColorModel().hasAlpha();
0601: boolean dstAlpha = dst.getColorModel().hasAlpha();
0602:
0603: // System.out.println("Src has: " + srcAlpha +
0604: // " is: " + src.isAlphaPremultiplied());
0605: //
0606: // System.out.println("Dst has: " + dstAlpha +
0607: // " is: " + dst.isAlphaPremultiplied());
0608:
0609: if (srcAlpha == dstAlpha)
0610: if (!srcAlpha
0611: || src.isAlphaPremultiplied() == dst
0612: .isAlphaPremultiplied()) {
0613: // They match one another so just copy everything...
0614: copyData(src.getRaster(), dst.getRaster());
0615: return;
0616: }
0617:
0618: // System.out.println("Using Slow CopyData");
0619:
0620: int[] pixel = null;
0621: Raster srcR = src.getRaster();
0622: WritableRaster dstR = dst.getRaster();
0623: int bands = dstR.getNumBands();
0624:
0625: int dx = destP.x - srcRect.x;
0626: int dy = destP.y - srcRect.y;
0627:
0628: int w = srcRect.width;
0629: int x0 = srcRect.x;
0630: int y0 = srcRect.y;
0631: int y1 = y0 + srcRect.height - 1;
0632:
0633: if (!srcAlpha) {
0634: // Src has no alpha dest does so set alpha to 1.0 everywhere.
0635: // System.out.println("Add Alpha");
0636: int[] oPix = new int[bands * w];
0637: int out = (w * bands) - 1; // The 2 skips alpha channel
0638: while (out >= 0) {
0639: // Fill alpha channel with 255's
0640: oPix[out] = 255;
0641: out -= bands;
0642: }
0643:
0644: int b, in;
0645: for (int y = y0; y <= y1; y++) {
0646: pixel = srcR.getPixels(x0, y, w, 1, pixel);
0647: in = w * (bands - 1) - 1;
0648: out = (w * bands) - 2; // The 2 skips alpha channel on last pix
0649: switch (bands) {
0650: case 4:
0651: while (in >= 0) {
0652: oPix[out--] = pixel[in--];
0653: oPix[out--] = pixel[in--];
0654: oPix[out--] = pixel[in--];
0655: out--;
0656: }
0657: break;
0658: default:
0659: while (in >= 0) {
0660: for (b = 0; b < bands - 1; b++)
0661: oPix[out--] = pixel[in--];
0662: out--;
0663: }
0664: }
0665: dstR.setPixels(x0 + dx, y + dy, w, 1, oPix);
0666: }
0667: } else if (dstAlpha && dst.isAlphaPremultiplied()) {
0668: // Src and dest have Alpha but we need to multiply it for dst.
0669: // System.out.println("Mult Case");
0670: int a, b, alpha, in, fpNorm = (1 << 24) / 255, pt5 = 1 << 23;
0671: for (int y = y0; y <= y1; y++) {
0672: pixel = srcR.getPixels(x0, y, w, 1, pixel);
0673: in = bands * w - 1;
0674: switch (bands) {
0675: case 4:
0676: while (in >= 0) {
0677: a = pixel[in];
0678: if (a == 255)
0679: in -= 4;
0680: else {
0681: in--;
0682: alpha = fpNorm * a;
0683: pixel[in] = (pixel[in] * alpha + pt5) >>> 24;
0684: in--;
0685: pixel[in] = (pixel[in] * alpha + pt5) >>> 24;
0686: in--;
0687: pixel[in] = (pixel[in] * alpha + pt5) >>> 24;
0688: in--;
0689: }
0690: }
0691: break;
0692: default:
0693: while (in >= 0) {
0694: a = pixel[in];
0695: if (a == 255)
0696: in -= bands;
0697: else {
0698: in--;
0699: alpha = fpNorm * a;
0700: for (b = 0; b < bands - 1; b++) {
0701: pixel[in] = (pixel[in] * alpha + pt5) >>> 24;
0702: in--;
0703: }
0704: }
0705: }
0706: }
0707: dstR.setPixels(x0 + dx, y + dy, w, 1, pixel);
0708: }
0709: } else if (dstAlpha && !dst.isAlphaPremultiplied()) {
0710: // Src and dest have Alpha but we need to divide it out for dst.
0711: // System.out.println("Div Case");
0712: int a, b, ialpha, in, fpNorm = 0x00FF0000, pt5 = 1 << 15;
0713: for (int y = y0; y <= y1; y++) {
0714: pixel = srcR.getPixels(x0, y, w, 1, pixel);
0715: in = (bands * w) - 1;
0716: switch (bands) {
0717: case 4:
0718: while (in >= 0) {
0719: a = pixel[in];
0720: if ((a <= 0) || (a >= 255))
0721: in -= 4;
0722: else {
0723: in--;
0724: ialpha = fpNorm / a;
0725: pixel[in] = (pixel[in] * ialpha + pt5) >>> 16;
0726: in--;
0727: pixel[in] = (pixel[in] * ialpha + pt5) >>> 16;
0728: in--;
0729: pixel[in] = (pixel[in] * ialpha + pt5) >>> 16;
0730: in--;
0731: }
0732: }
0733: break;
0734: default:
0735: while (in >= 0) {
0736: a = pixel[in];
0737: if ((a <= 0) || (a >= 255))
0738: in -= bands;
0739: else {
0740: in--;
0741: ialpha = fpNorm / a;
0742: for (b = 0; b < bands - 1; b++) {
0743: pixel[in] = (pixel[in] * ialpha + pt5) >>> 16;
0744: in--;
0745: }
0746: }
0747: }
0748: }
0749: dstR.setPixels(x0 + dx, y + dy, w, 1, pixel);
0750: }
0751: } else if (src.isAlphaPremultiplied()) {
0752: int[] oPix = new int[bands * w];
0753: // Src has alpha dest does not so unpremult and store...
0754: // System.out.println("Remove Alpha, Div Case");
0755: int a, b, ialpha, in, out, fpNorm = 0x00FF0000, pt5 = 1 << 15;
0756: for (int y = y0; y <= y1; y++) {
0757: pixel = srcR.getPixels(x0, y, w, 1, pixel);
0758: in = (bands + 1) * w - 1;
0759: out = (bands * w) - 1;
0760: while (in >= 0) {
0761: a = pixel[in];
0762: in--;
0763: if (a > 0) {
0764: if (a < 255) {
0765: ialpha = fpNorm / a;
0766: for (b = 0; b < bands; b++)
0767: oPix[out--] = (pixel[in--] * ialpha + pt5) >>> 16;
0768: } else
0769: for (b = 0; b < bands; b++)
0770: oPix[out--] = pixel[in--];
0771: } else {
0772: in -= bands;
0773: for (b = 0; b < bands; b++)
0774: oPix[out--] = 255;
0775: }
0776: }
0777: dstR.setPixels(x0 + dx, y + dy, w, 1, oPix);
0778: }
0779: } else {
0780: // Src has unpremult alpha, dest does not have alpha,
0781: // just copy the color channels over.
0782: Rectangle dstRect = new Rectangle(destP.x, destP.y,
0783: srcRect.width, srcRect.height);
0784: for (int b = 0; b < bands; b++)
0785: copyBand(srcR, srcRect, b, dstR, dstRect, b);
0786: }
0787: }
0788:
0789: public static void copyBand(Raster src, int srcBand,
0790: WritableRaster dst, int dstBand) {
0791:
0792: Rectangle sR = src.getBounds();
0793: Rectangle dR = dst.getBounds();
0794: Rectangle cpR = sR.intersection(dR);
0795:
0796: copyBand(src, cpR, srcBand, dst, cpR, dstBand);
0797: }
0798:
0799: public static void copyBand(Raster src, Rectangle sR, int sBand,
0800: WritableRaster dst, Rectangle dR, int dBand) {
0801: int dy = dR.y - sR.y;
0802: int dx = dR.x - sR.x;
0803: sR = sR.intersection(src.getBounds());
0804: dR = dR.intersection(dst.getBounds());
0805: int width, height;
0806: if (dR.width < sR.width)
0807: width = dR.width;
0808: else
0809: width = sR.width;
0810: if (dR.height < sR.height)
0811: height = dR.height;
0812: else
0813: height = sR.height;
0814:
0815: int x = sR.x + dx;
0816: int[] samples = null;
0817: for (int y = sR.y; y < sR.y + height; y++) {
0818: samples = src.getSamples(sR.x, y, width, 1, sBand, samples);
0819: dst.setSamples(x, y + dy, width, 1, dBand, samples);
0820: }
0821: }
0822:
0823: public static boolean is_INT_PACK_Data(SampleModel sm,
0824: boolean requireAlpha) {
0825: // Check ColorModel is of type DirectColorModel
0826: if (!(sm instanceof SinglePixelPackedSampleModel))
0827: return false;
0828:
0829: // Check transfer type
0830: if (sm.getDataType() != DataBuffer.TYPE_INT)
0831: return false;
0832:
0833: SinglePixelPackedSampleModel sppsm;
0834: sppsm = (SinglePixelPackedSampleModel) sm;
0835:
0836: int[] masks = sppsm.getBitMasks();
0837: if (masks.length == 3) {
0838: if (requireAlpha)
0839: return false;
0840: } else if (masks.length != 4)
0841: return false;
0842:
0843: if (masks[0] != 0x00ff0000)
0844: return false;
0845: if (masks[1] != 0x0000ff00)
0846: return false;
0847: if (masks[2] != 0x000000ff)
0848: return false;
0849: if ((masks.length == 4) && (masks[3] != 0xff000000))
0850: return false;
0851:
0852: return true;
0853: }
0854:
0855: public static boolean is_BYTE_COMP_Data(SampleModel sm) {
0856: // Check ColorModel is of type DirectColorModel
0857: if (!(sm instanceof ComponentSampleModel))
0858: return false;
0859:
0860: // Check transfer type
0861: if (sm.getDataType() != DataBuffer.TYPE_BYTE)
0862: return false;
0863:
0864: return true;
0865: }
0866:
0867: protected static void divide_INT_PACK_Data(WritableRaster wr) {
0868: // System.out.println("Divide Int");
0869:
0870: SinglePixelPackedSampleModel sppsm;
0871: sppsm = (SinglePixelPackedSampleModel) wr.getSampleModel();
0872:
0873: final int width = wr.getWidth();
0874:
0875: final int scanStride = sppsm.getScanlineStride();
0876: DataBufferInt db = (DataBufferInt) wr.getDataBuffer();
0877: final int base = (db.getOffset() + sppsm.getOffset(wr.getMinX()
0878: - wr.getSampleModelTranslateX(), wr.getMinY()
0879: - wr.getSampleModelTranslateY()));
0880:
0881: // Access the pixel data array
0882: final int[] pixels = db.getBankData()[0];
0883: for (int y = 0; y < wr.getHeight(); y++) {
0884: int sp = base + y * scanStride;
0885: final int end = sp + width;
0886: while (sp < end) {
0887: int pixel = pixels[sp];
0888: int a = pixel >>> 24;
0889: if (a <= 0) {
0890: pixels[sp] = 0x00FFFFFF;
0891: } else if (a < 255) {
0892: int aFP = (0x00FF0000 / a);
0893: pixels[sp] = ((a << 24)
0894: | (((((pixel & 0xFF0000) >> 16) * aFP) & 0xFF0000))
0895: | (((((pixel & 0x00FF00) >> 8) * aFP) & 0xFF0000) >> 8) | (((((pixel & 0x0000FF)) * aFP) & 0xFF0000) >> 16));
0896: }
0897: sp++;
0898: }
0899: }
0900: }
0901:
0902: protected static void mult_INT_PACK_Data(WritableRaster wr) {
0903: // System.out.println("Multiply Int: " + wr);
0904:
0905: SinglePixelPackedSampleModel sppsm;
0906: sppsm = (SinglePixelPackedSampleModel) wr.getSampleModel();
0907:
0908: final int width = wr.getWidth();
0909:
0910: final int scanStride = sppsm.getScanlineStride();
0911: DataBufferInt db = (DataBufferInt) wr.getDataBuffer();
0912: final int base = (db.getOffset() + sppsm.getOffset(wr.getMinX()
0913: - wr.getSampleModelTranslateX(), wr.getMinY()
0914: - wr.getSampleModelTranslateY()));
0915: // Access the pixel data array
0916: final int[] pixels = db.getBankData()[0];
0917: for (int y = 0; y < wr.getHeight(); y++) {
0918: int sp = base + y * scanStride;
0919: final int end = sp + width;
0920: while (sp < end) {
0921: int pixel = pixels[sp];
0922: int a = pixel >>> 24;
0923: if ((a >= 0) && (a < 255)) { // this does NOT include a == 255 (0xff) !
0924: pixels[sp] = ((a << 24)
0925: | ((((pixel & 0xFF0000) * a) >> 8) & 0xFF0000)
0926: | ((((pixel & 0x00FF00) * a) >> 8) & 0x00FF00) | ((((pixel & 0x0000FF) * a) >> 8) & 0x0000FF));
0927: }
0928: sp++;
0929: }
0930: }
0931: }
0932:
0933: protected static void divide_BYTE_COMP_Data(WritableRaster wr) {
0934: // System.out.println("Multiply Int: " + wr);
0935:
0936: ComponentSampleModel csm;
0937: csm = (ComponentSampleModel) wr.getSampleModel();
0938:
0939: final int width = wr.getWidth();
0940:
0941: final int scanStride = csm.getScanlineStride();
0942: final int pixStride = csm.getPixelStride();
0943: final int[] bandOff = csm.getBandOffsets();
0944:
0945: DataBufferByte db = (DataBufferByte) wr.getDataBuffer();
0946: final int base = (db.getOffset() + csm.getOffset(wr.getMinX()
0947: - wr.getSampleModelTranslateX(), wr.getMinY()
0948: - wr.getSampleModelTranslateY()));
0949:
0950: int aOff = bandOff[bandOff.length - 1];
0951: int bands = bandOff.length - 1;
0952:
0953: // Access the pixel data array
0954: final byte[] pixels = db.getBankData()[0];
0955: for (int y = 0; y < wr.getHeight(); y++) {
0956: int sp = base + y * scanStride;
0957: final int end = sp + width * pixStride;
0958: while (sp < end) {
0959: int a = pixels[sp + aOff] & 0xFF;
0960: if (a == 0) {
0961: for (int b = 0; b < bands; b++)
0962: pixels[sp + bandOff[b]] = (byte) 0xFF;
0963: } else if (a < 255) { // this does NOT include a == 255 (0xff) !
0964: int aFP = (0x00FF0000 / a);
0965: for (int b = 0; b < bands; b++) {
0966: int i = sp + bandOff[b];
0967: pixels[i] = (byte) (((pixels[i] & 0xFF) * aFP) >>> 16);
0968: }
0969: }
0970: sp += pixStride;
0971: }
0972: }
0973: }
0974:
0975: protected static void mult_BYTE_COMP_Data(WritableRaster wr) {
0976: // System.out.println("Multiply Int: " + wr);
0977:
0978: ComponentSampleModel csm;
0979: csm = (ComponentSampleModel) wr.getSampleModel();
0980:
0981: final int width = wr.getWidth();
0982:
0983: final int scanStride = csm.getScanlineStride();
0984: final int pixStride = csm.getPixelStride();
0985: final int[] bandOff = csm.getBandOffsets();
0986:
0987: DataBufferByte db = (DataBufferByte) wr.getDataBuffer();
0988: final int base = (db.getOffset() + csm.getOffset(wr.getMinX()
0989: - wr.getSampleModelTranslateX(), wr.getMinY()
0990: - wr.getSampleModelTranslateY()));
0991:
0992: int aOff = bandOff[bandOff.length - 1];
0993: int bands = bandOff.length - 1;
0994:
0995: // Access the pixel data array
0996: final byte[] pixels = db.getBankData()[0];
0997: for (int y = 0; y < wr.getHeight(); y++) {
0998: int sp = base + y * scanStride;
0999: final int end = sp + width * pixStride;
1000: while (sp < end) {
1001: int a = pixels[sp + aOff] & 0xFF;
1002: if (a != 0xFF)
1003: for (int b = 0; b < bands; b++) {
1004: int i = sp + bandOff[b];
1005: pixels[i] = (byte) (((pixels[i] & 0xFF) * a) >> 8);
1006: }
1007: sp += pixStride;
1008: }
1009: }
1010: }
1011:
1012: /*
1013: This is skanky debugging code that might be useful in the future:
1014:
1015: if (count == 33) {
1016: String label = "sub [" + x + ", " + y + "]: ";
1017: org.ImageDisplay.showImage
1018: (label, subBI);
1019: org.ImageDisplay.printImage
1020: (label, subBI,
1021: new Rectangle(75-iR.x, 90-iR.y, 32, 32));
1022:
1023: }
1024:
1025:
1026: // if ((count++ % 50) == 10)
1027: // org.ImageDisplay.showImage("foo: ", subBI);
1028:
1029:
1030: Graphics2D realG2D = g2d;
1031: while (realG2D instanceof sun.java2d.ProxyGraphics2D) {
1032: realG2D = ((sun.java2d.ProxyGraphics2D)realG2D).getDelegate();
1033: }
1034: if (realG2D instanceof sun.awt.image.BufferedImageGraphics2D) {
1035: count++;
1036: if (count == 34) {
1037: RenderedImage ri;
1038: ri = ((sun.awt.image.BufferedImageGraphics2D)realG2D).bufImg;
1039: // g2d.setComposite(SVGComposite.OVER);
1040: // org.ImageDisplay.showImage("Bar: " + count, cr);
1041: org.ImageDisplay.printImage("Bar: " + count, cr,
1042: new Rectangle(75, 90, 32, 32));
1043:
1044: org.ImageDisplay.showImage ("Foo: " + count, ri);
1045: org.ImageDisplay.printImage("Foo: " + count, ri,
1046: new Rectangle(75, 90, 32, 32));
1047:
1048: System.out.println("BI: " + ri);
1049: System.out.println("BISM: " + ri.getSampleModel());
1050: System.out.println("BICM: " + ri.getColorModel());
1051: System.out.println("BICM class: " + ri.getColorModel().getClass());
1052: System.out.println("BICS: " + ri.getColorModel().getColorSpace());
1053: System.out.println
1054: ("sRGB CS: " +
1055: ColorSpace.getInstance(ColorSpace.CS_sRGB));
1056: System.out.println("G2D info");
1057: System.out.println("\tComposite: " + g2d.getComposite());
1058: System.out.println("\tTransform" + g2d.getTransform());
1059: java.awt.RenderingHints rh = g2d.getRenderingHints();
1060: java.util.Set keys = rh.keySet();
1061: java.util.Iterator iter = keys.iterator();
1062: while (iter.hasNext()) {
1063: Object o = iter.next();
1064:
1065: System.out.println("\t" + o.toString() + " -> " +
1066: rh.get(o).toString());
1067: }
1068:
1069: ri = cr;
1070: System.out.println("RI: " + ri);
1071: System.out.println("RISM: " + ri.getSampleModel());
1072: System.out.println("RICM: " + ri.getColorModel());
1073: System.out.println("RICM class: " + ri.getColorModel().getClass());
1074: System.out.println("RICS: " + ri.getColorModel().getColorSpace());
1075: }
1076: }
1077: */
1078:
1079: }
|