Optimized version of copyData designed to work on Integer packed data with a SinglePixelPackedSampleModel
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* $Id: GraphicsUtil.java 603243 2007-12-11 13:49:04Z jeremias $ */ import java.awt.Point; import java.awt.Rectangle; import java.awt.color.ColorSpace; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferUShort; import java.awt.image.DirectColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.WritableRaster; /** * Set of utility methods for Graphics. * These generally bypass broken methods in Java2D or provide tweaked * implementations. * * @author <a href="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a> * @version $Id: GraphicsUtil.java 603243 2007-12-11 13:49:04Z jeremias $ */ public class GraphicsUtil { public static AffineTransform IDENTITY = new AffineTransform(); /** * Standard prebuilt Linear_sRGB color model with no alpha */ public static final ColorModel Linear_sRGB = new DirectColorModel(ColorSpace.getInstance (ColorSpace.CS_LINEAR_RGB), 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x0, false, DataBuffer.TYPE_INT); /** * Standard prebuilt Linear_sRGB color model with premultiplied alpha. */ public static final ColorModel Linear_sRGB_Pre = new DirectColorModel(ColorSpace.getInstance (ColorSpace.CS_LINEAR_RGB), 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, true, DataBuffer.TYPE_INT); /** * Standard prebuilt Linear_sRGB color model with unpremultiplied alpha. */ public static final ColorModel Linear_sRGB_Unpre = new DirectColorModel(ColorSpace.getInstance (ColorSpace.CS_LINEAR_RGB), 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, false, DataBuffer.TYPE_INT); /** * Standard prebuilt sRGB color model with no alpha. */ public static final ColorModel sRGB = new DirectColorModel(ColorSpace.getInstance (ColorSpace.CS_sRGB), 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x0, false, DataBuffer.TYPE_INT); /** * Standard prebuilt sRGB color model with premultiplied alpha. */ public static final ColorModel sRGB_Pre = new DirectColorModel(ColorSpace.getInstance (ColorSpace.CS_sRGB), 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, true, DataBuffer.TYPE_INT); /** * Standard prebuilt sRGB color model with unpremultiplied alpha. */ public static final ColorModel sRGB_Unpre = new DirectColorModel(ColorSpace.getInstance (ColorSpace.CS_sRGB), 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, false, DataBuffer.TYPE_INT); /** * Method that returns either Linear_sRGB_Pre or Linear_sRGB_UnPre * based on premult flag. * @param premult True if the ColorModel should have premultiplied alpha. * @return a ColorMdoel with Linear sRGB colorSpace and * the alpha channel set in accordance with * <tt>premult</tt> */ public static ColorModel makeLinear_sRGBCM(boolean premult) { return premult ? Linear_sRGB_Pre : Linear_sRGB_Unpre; } /** * Constructs a BufferedImage with a linear sRGB colorModel, and alpha. * @param width The desired width of the BufferedImage * @param height The desired height of the BufferedImage * @param premult The desired state of alpha premultiplied * @return The requested BufferedImage. */ public static BufferedImage makeLinearBufferedImage(int width, int height, boolean premult) { ColorModel cm = makeLinear_sRGBCM(premult); WritableRaster wr = cm.createCompatibleWritableRaster(width, height); return new BufferedImage(cm, wr, premult, null); } /** * An internal optimized version of copyData designed to work on * Integer packed data with a SinglePixelPackedSampleModel. Only * the region of overlap between src and dst is copied. * * Calls to this should be preflighted with is_INT_PACK_Data * on both src and dest (requireAlpha can be false). * * @param src The source of the data * @param dst The destination for the data. */ public static void copyData_INT_PACK(Raster src, WritableRaster dst) { // System.out.println("Fast copyData"); int x0 = dst.getMinX(); if (x0 < src.getMinX()) x0 = src.getMinX(); int y0 = dst.getMinY(); if (y0 < src.getMinY()) y0 = src.getMinY(); int x1 = dst.getMinX()+dst.getWidth()-1; if (x1 > src.getMinX()+src.getWidth()-1) x1 = src.getMinX()+src.getWidth()-1; int y1 = dst.getMinY()+dst.getHeight()-1; if (y1 > src.getMinY()+src.getHeight()-1) y1 = src.getMinY()+src.getHeight()-1; int width = x1-x0+1; int height = y1-y0+1; SinglePixelPackedSampleModel srcSPPSM; srcSPPSM = (SinglePixelPackedSampleModel)src.getSampleModel(); final int srcScanStride = srcSPPSM.getScanlineStride(); DataBufferInt srcDB = (DataBufferInt)src.getDataBuffer(); final int [] srcPixels = srcDB.getBankData()[0]; final int srcBase = (srcDB.getOffset() + srcSPPSM.getOffset(x0-src.getSampleModelTranslateX(), y0-src.getSampleModelTranslateY())); SinglePixelPackedSampleModel dstSPPSM; dstSPPSM = (SinglePixelPackedSampleModel)dst.getSampleModel(); final int dstScanStride = dstSPPSM.getScanlineStride(); DataBufferInt dstDB = (DataBufferInt)dst.getDataBuffer(); final int [] dstPixels = dstDB.getBankData()[0]; final int dstBase = (dstDB.getOffset() + dstSPPSM.getOffset(x0-dst.getSampleModelTranslateX(), y0-dst.getSampleModelTranslateY())); if ((srcScanStride == dstScanStride) && (srcScanStride == width)) { // System.out.println("VERY Fast copyData"); System.arraycopy(srcPixels, srcBase, dstPixels, dstBase, width*height); } else if (width > 128) { int srcSP = srcBase; int dstSP = dstBase; for (int y=0; y<height; y++) { System.arraycopy(srcPixels, srcSP, dstPixels, dstSP, width); srcSP += srcScanStride; dstSP += dstScanStride; } } else { for (int y=0; y<height; y++) { int srcSP = srcBase+y*srcScanStride; int dstSP = dstBase+y*dstScanStride; for (int x=0; x<width; x++) dstPixels[dstSP++] = srcPixels[srcSP++]; } } } public static void copyData_FALLBACK(Raster src, WritableRaster dst) { // System.out.println("Fallback copyData"); int x0 = dst.getMinX(); if (x0 < src.getMinX()) x0 = src.getMinX(); int y0 = dst.getMinY(); if (y0 < src.getMinY()) y0 = src.getMinY(); int x1 = dst.getMinX()+dst.getWidth()-1; if (x1 > src.getMinX()+src.getWidth()-1) x1 = src.getMinX()+src.getWidth()-1; int y1 = dst.getMinY()+dst.getHeight()-1; if (y1 > src.getMinY()+src.getHeight()-1) y1 = src.getMinY()+src.getHeight()-1; int width = x1-x0+1; int [] data = null; for (int y = y0; y <= y1 ; y++) { data = src.getPixels(x0,y,width,1,data); dst.setPixels (x0,y,width,1,data); } } /** * Copies data from one raster to another. Only the region of * overlap between src and dst is copied. <tt>Src</tt> and * <tt>Dst</tt> must have compatible SampleModels. * * @param src The source of the data * @param dst The destination for the data. */ public static void copyData(Raster src, WritableRaster dst) { if (is_INT_PACK_Data(src.getSampleModel(), false) && is_INT_PACK_Data(dst.getSampleModel(), false)) { copyData_INT_PACK(src, dst); return; } copyData_FALLBACK(src, dst); } /** * Creates a new raster that has a <b>copy</b> of the data in * <tt>ras</tt>. This is highly optimized for speed. There is * no provision for changing any aspect of the SampleModel. * * This method should be used when you need to change the contents * of a Raster that you do not "own" (ie the result of a * <tt>getData</tt> call). * @param ras The Raster to copy. * @return A writable copy of <tt>ras</tt> */ public static WritableRaster copyRaster(Raster ras) { return copyRaster(ras, ras.getMinX(), ras.getMinY()); } /** * Creates a new raster that has a <b>copy</b> of the data in * <tt>ras</tt>. This is highly optimized for speed. There is * no provision for changing any aspect of the SampleModel. * However you can specify a new location for the returned raster. * * This method should be used when you need to change the contents * of a Raster that you do not "own" (ie the result of a * <tt>getData</tt> call). * * @param ras The Raster to copy. * * @param minX The x location for the upper left corner of the * returned WritableRaster. * * @param minY The y location for the upper left corner of the * returned WritableRaster. * * @return A writable copy of <tt>ras</tt> */ public static WritableRaster copyRaster(Raster ras, int minX, int minY) { WritableRaster ret = Raster.createWritableRaster (ras.getSampleModel(), new Point(0,0)); ret = ret.createWritableChild (ras.getMinX()-ras.getSampleModelTranslateX(), ras.getMinY()-ras.getSampleModelTranslateY(), ras.getWidth(), ras.getHeight(), minX, minY, null); // Use System.arraycopy to copy the data between the two... DataBuffer srcDB = ras.getDataBuffer(); DataBuffer retDB = ret.getDataBuffer(); if (srcDB.getDataType() != retDB.getDataType()) { throw new IllegalArgumentException ("New DataBuffer doesn't match original"); } int len = srcDB.getSize(); int banks = srcDB.getNumBanks(); int [] offsets = srcDB.getOffsets(); for (int b=0; b< banks; b++) { switch (srcDB.getDataType()) { case DataBuffer.TYPE_BYTE: { DataBufferByte srcDBT = (DataBufferByte)srcDB; DataBufferByte retDBT = (DataBufferByte)retDB; System.arraycopy(srcDBT.getData(b), offsets[b], retDBT.getData(b), offsets[b], len); break; } case DataBuffer.TYPE_INT: { DataBufferInt srcDBT = (DataBufferInt)srcDB; DataBufferInt retDBT = (DataBufferInt)retDB; System.arraycopy(srcDBT.getData(b), offsets[b], retDBT.getData(b), offsets[b], len); break; } case DataBuffer.TYPE_SHORT: { DataBufferShort srcDBT = (DataBufferShort)srcDB; DataBufferShort retDBT = (DataBufferShort)retDB; System.arraycopy(srcDBT.getData(b), offsets[b], retDBT.getData(b), offsets[b], len); break; } case DataBuffer.TYPE_USHORT: { DataBufferUShort srcDBT = (DataBufferUShort)srcDB; DataBufferUShort retDBT = (DataBufferUShort)retDB; System.arraycopy(srcDBT.getData(b), offsets[b], retDBT.getData(b), offsets[b], len); break; } } } return ret; } /** * Coerces <tt>ras</tt> to be writable. The returned Raster continues to * reference the DataBuffer from ras, so modifications to the returned * WritableRaster will be seen in ras.<p> * * This method should only be used if you need a WritableRaster due to * an interface (such as to construct a BufferedImage), but have no * intention of modifying the contents of the returned Raster. If * you have any doubt about other users of the data in <tt>ras</tt>, * use copyRaster (above). * @param ras The raster to make writable. * @return A Writable version of ras (shares DataBuffer with * <tt>ras</tt>). */ public static WritableRaster makeRasterWritable(Raster ras) { return makeRasterWritable(ras, ras.getMinX(), ras.getMinY()); } /** * Coerces <tt>ras</tt> to be writable. The returned Raster continues to * reference the DataBuffer from ras, so modifications to the returned * WritableRaster will be seen in ras.<p> * * You can specify a new location for the returned WritableRaster, this * is especially useful for constructing BufferedImages which require * the Raster to be at (0,0). * * This method should only be used if you need a WritableRaster due to * an interface (such as to construct a BufferedImage), but have no * intention of modifying the contents of the returned Raster. If * you have any doubt about other users of the data in <tt>ras</tt>, * use copyRaster (above). * * @param ras The raster to make writable. * * @param minX The x location for the upper left corner of the * returned WritableRaster. * * @param minY The y location for the upper left corner of the * returned WritableRaster. * * @return A Writable version of <tT>ras</tt> with it's upper left * hand coordinate set to minX, minY (shares it's DataBuffer * with <tt>ras</tt>). */ public static WritableRaster makeRasterWritable(Raster ras, int minX, int minY) { WritableRaster ret = Raster.createWritableRaster (ras.getSampleModel(), ras.getDataBuffer(), new Point(0,0)); ret = ret.createWritableChild (ras.getMinX()-ras.getSampleModelTranslateX(), ras.getMinY()-ras.getSampleModelTranslateY(), ras.getWidth(), ras.getHeight(), minX, minY, null); return ret; } /** * Create a new ColorModel with it's alpha premultiplied state matching * newAlphaPreMult. * @param cm The ColorModel to change the alpha premult state of. * @param newAlphaPreMult The new state of alpha premult. * @return A new colorModel that has isAlphaPremultiplied() * equal to newAlphaPreMult. */ public static ColorModel coerceColorModel(ColorModel cm, boolean newAlphaPreMult) { if (cm.isAlphaPremultiplied() == newAlphaPreMult) return cm; // Easiest way to build proper colormodel for new Alpha state... // Eventually this should switch on known ColorModel types and // only fall back on this hack when the CM type is unknown. WritableRaster wr = cm.createCompatibleWritableRaster(1,1); return cm.coerceData(wr, newAlphaPreMult); } /** * Coerces data within a bufferedImage to match newAlphaPreMult, * Note that this can not change the colormodel of bi so you * * @param wr The raster to change the state of. * @param cm The colormodel currently associated with data in wr. * @param newAlphaPreMult The desired state of alpha Premult for raster. * @return A new colormodel that matches newAlphaPreMult. */ public static ColorModel coerceData(WritableRaster wr, ColorModel cm, boolean newAlphaPreMult) { // System.out.println("CoerceData: " + cm.isAlphaPremultiplied() + // " Out: " + newAlphaPreMult); if (!cm.hasAlpha()) // Nothing to do no alpha channel return cm; if (cm.isAlphaPremultiplied() == newAlphaPreMult) // nothing to do alpha state matches... return cm; // System.out.println("CoerceData: " + wr.getSampleModel()); if (newAlphaPreMult) { multiplyAlpha(wr); } else { divideAlpha(wr); } return coerceColorModel(cm, newAlphaPreMult); } public static void multiplyAlpha(WritableRaster wr) { if (is_BYTE_COMP_Data(wr.getSampleModel())) mult_BYTE_COMP_Data(wr); else if (is_INT_PACK_Data(wr.getSampleModel(), true)) mult_INT_PACK_Data(wr); else { int [] pixel = null; int bands = wr.getNumBands(); float norm = 1f/255f; int x0, x1, y0, y1, a, b; float alpha; x0 = wr.getMinX(); x1 = x0+wr.getWidth(); y0 = wr.getMinY(); y1 = y0+wr.getHeight(); for (int y=y0; y<y1; y++) for (int x=x0; x<x1; x++) { pixel = wr.getPixel(x,y,pixel); a = pixel[bands-1]; if ((a >= 0) && (a < 255)) { alpha = a*norm; for (b=0; b<bands-1; b++) pixel[b] = (int)(pixel[b]*alpha+0.5f); wr.setPixel(x,y,pixel); } } } } public static void divideAlpha(WritableRaster wr) { if (is_BYTE_COMP_Data(wr.getSampleModel())) divide_BYTE_COMP_Data(wr); else if (is_INT_PACK_Data(wr.getSampleModel(), true)) divide_INT_PACK_Data(wr); else { int x0, x1, y0, y1, a, b; float ialpha; int bands = wr.getNumBands(); int [] pixel = null; x0 = wr.getMinX(); x1 = x0+wr.getWidth(); y0 = wr.getMinY(); y1 = y0+wr.getHeight(); for (int y=y0; y<y1; y++) for (int x=x0; x<x1; x++) { pixel = wr.getPixel(x,y,pixel); a = pixel[bands-1]; if ((a > 0) && (a < 255)) { ialpha = 255/(float)a; for (b=0; b<bands-1; b++) pixel[b] = (int)(pixel[b]*ialpha+0.5f); wr.setPixel(x,y,pixel); } } } } /** * Copies data from one bufferedImage to another paying attention * to the state of AlphaPreMultiplied. * * @param src The source * @param dst The destination */ public static void copyData(BufferedImage src, BufferedImage dst) { Rectangle srcRect = new Rectangle(0, 0, src.getWidth(), src.getHeight()); copyData(src, srcRect, dst, new Point(0,0)); } /** * Copies data from one bufferedImage to another paying attention * to the state of AlphaPreMultiplied. * * @param src The source * @param srcRect The Rectangle of source data to be copied * @param dst The destination * @param destP The Place for the upper left corner of srcRect in dst. */ public static void copyData(BufferedImage src, Rectangle srcRect, BufferedImage dst, Point destP) { /* if (srcCS != dstCS) throw new IllegalArgumentException ("Images must be in the same ColorSpace in order "+ "to copy Data between them"); */ boolean srcAlpha = src.getColorModel().hasAlpha(); boolean dstAlpha = dst.getColorModel().hasAlpha(); // System.out.println("Src has: " + srcAlpha + // " is: " + src.isAlphaPremultiplied()); // // System.out.println("Dst has: " + dstAlpha + // " is: " + dst.isAlphaPremultiplied()); if (srcAlpha == dstAlpha) if (!srcAlpha || src.isAlphaPremultiplied() == dst.isAlphaPremultiplied()) { // They match one another so just copy everything... copyData(src.getRaster(), dst.getRaster()); return; } // System.out.println("Using Slow CopyData"); int [] pixel = null; Raster srcR = src.getRaster(); WritableRaster dstR = dst.getRaster(); int bands = dstR.getNumBands(); int dx = destP.x-srcRect.x; int dy = destP.y-srcRect.y; int w = srcRect.width; int x0 = srcRect.x; int y0 = srcRect.y; int y1 = y0+srcRect.height-1; if (!srcAlpha) { // Src has no alpha dest does so set alpha to 1.0 everywhere. // System.out.println("Add Alpha"); int [] oPix = new int[bands*w]; int out = (w*bands)-1; // The 2 skips alpha channel while(out >= 0) { // Fill alpha channel with 255's oPix[out] = 255; out -= bands; } int b, in; for (int y=y0; y<=y1; y++) { pixel = srcR.getPixels(x0,y,w,1,pixel); in = w*(bands-1)-1; out = (w*bands)-2; // The 2 skips alpha channel on last pix switch (bands) { case 4: while(in >= 0) { oPix[out--] = pixel[in--]; oPix[out--] = pixel[in--]; oPix[out--] = pixel[in--]; out--; } break; default: while(in >= 0) { for (b=0; b<bands-1; b++) oPix[out--] = pixel[in--]; out--; } } dstR.setPixels(x0+dx, y+dy, w, 1, oPix); } } else if (dstAlpha && dst.isAlphaPremultiplied()) { // Src and dest have Alpha but we need to multiply it for dst. // System.out.println("Mult Case"); int a, b, alpha, in, fpNorm = (1<<24)/255, pt5 = 1<<23; for (int y=y0; y<=y1; y++) { pixel = srcR.getPixels(x0,y,w,1,pixel); in=bands*w-1; switch (bands) { case 4: while(in >= 0) { a = pixel[in]; if (a == 255) in -= 4; else { in--; alpha = fpNorm*a; pixel[in] = (pixel[in]*alpha+pt5)>>>24; in--; pixel[in] = (pixel[in]*alpha+pt5)>>>24; in--; pixel[in] = (pixel[in]*alpha+pt5)>>>24; in--; } } break; default: while(in >= 0) { a = pixel[in]; if (a == 255) in -= bands; else { in--; alpha = fpNorm*a; for (b=0; b<bands-1; b++) { pixel[in] = (pixel[in]*alpha+pt5)>>>24; in--; } } } } dstR.setPixels(x0+dx, y+dy, w, 1, pixel); } } else if (dstAlpha && !dst.isAlphaPremultiplied()) { // Src and dest have Alpha but we need to divide it out for dst. // System.out.println("Div Case"); int a, b, ialpha, in, fpNorm = 0x00FF0000, pt5 = 1<<15; for (int y=y0; y<=y1; y++) { pixel = srcR.getPixels(x0,y,w,1,pixel); in=(bands*w)-1; switch(bands) { case 4: while(in >= 0) { a = pixel[in]; if ((a <= 0) || (a >= 255)) in -= 4; else { in--; ialpha = fpNorm/a; pixel[in] = (pixel[in]*ialpha+pt5)>>>16; in--; pixel[in] = (pixel[in]*ialpha+pt5)>>>16; in--; pixel[in] = (pixel[in]*ialpha+pt5)>>>16; in--; } } break; default: while(in >= 0) { a = pixel[in]; if ((a <= 0) || (a >= 255)) in -= bands; else { in--; ialpha = fpNorm/a; for (b=0; b<bands-1; b++) { pixel[in] = (pixel[in]*ialpha+pt5)>>>16; in--; } } } } dstR.setPixels(x0+dx, y+dy, w, 1, pixel); } } else if (src.isAlphaPremultiplied()) { int [] oPix = new int[bands*w]; // Src has alpha dest does not so unpremult and store... // System.out.println("Remove Alpha, Div Case"); int a, b, ialpha, in, out, fpNorm = 0x00FF0000, pt5 = 1<<15; for (int y=y0; y<=y1; y++) { pixel = srcR.getPixels(x0,y,w,1,pixel); in = (bands+1)*w -1; out = (bands*w)-1; while(in >= 0) { a = pixel[in]; in--; if (a > 0) { if (a < 255) { ialpha = fpNorm/a; for (b=0; b<bands; b++) oPix[out--] = (pixel[in--]*ialpha+pt5)>>>16; } else for (b=0; b<bands; b++) oPix[out--] = pixel[in--]; } else { in -= bands; for (b=0; b<bands; b++) oPix[out--] = 255; } } dstR.setPixels(x0+dx, y+dy, w, 1, oPix); } } else { // Src has unpremult alpha, dest does not have alpha, // just copy the color channels over. Rectangle dstRect = new Rectangle(destP.x, destP.y, srcRect.width, srcRect.height); for (int b=0; b<bands; b++) copyBand(srcR, srcRect, b, dstR, dstRect, b); } } public static void copyBand(Raster src, int srcBand, WritableRaster dst, int dstBand) { Rectangle sR = src.getBounds(); Rectangle dR = dst.getBounds(); Rectangle cpR = sR.intersection(dR); copyBand(src, cpR, srcBand, dst, cpR, dstBand); } public static void copyBand(Raster src, Rectangle sR, int sBand, WritableRaster dst, Rectangle dR, int dBand) { int dy = dR.y -sR.y; int dx = dR.x -sR.x; sR = sR.intersection(src.getBounds()); dR = dR.intersection(dst.getBounds()); int width, height; if (dR.width < sR.width) width = dR.width; else width = sR.width; if (dR.height < sR.height) height = dR.height; else height = sR.height; int x = sR.x+dx; int [] samples = null; for (int y=sR.y; y< sR.y+height; y++) { samples = src.getSamples(sR.x, y, width, 1, sBand, samples); dst.setSamples(x, y+dy, width, 1, dBand, samples); } } public static boolean is_INT_PACK_Data(SampleModel sm, boolean requireAlpha) { // Check ColorModel is of type DirectColorModel if(!(sm instanceof SinglePixelPackedSampleModel)) return false; // Check transfer type if(sm.getDataType() != DataBuffer.TYPE_INT) return false; SinglePixelPackedSampleModel sppsm; sppsm = (SinglePixelPackedSampleModel)sm; int [] masks = sppsm.getBitMasks(); if (masks.length == 3) { if (requireAlpha) return false; } else if (masks.length != 4) return false; if(masks[0] != 0x00ff0000) return false; if(masks[1] != 0x0000ff00) return false; if(masks[2] != 0x000000ff) return false; if ((masks.length == 4) && (masks[3] != 0xff000000)) return false; return true; } public static boolean is_BYTE_COMP_Data(SampleModel sm) { // Check ColorModel is of type DirectColorModel if(!(sm instanceof ComponentSampleModel)) return false; // Check transfer type if(sm.getDataType() != DataBuffer.TYPE_BYTE) return false; return true; } protected static void divide_INT_PACK_Data(WritableRaster wr) { // System.out.println("Divide Int"); SinglePixelPackedSampleModel sppsm; sppsm = (SinglePixelPackedSampleModel)wr.getSampleModel(); final int width = wr.getWidth(); final int scanStride = sppsm.getScanlineStride(); DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); final int base = (db.getOffset() + sppsm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(), wr.getMinY()-wr.getSampleModelTranslateY())); // Access the pixel data array final int[] pixels = db.getBankData()[0]; for (int y=0; y<wr.getHeight(); y++) { int sp = base + y*scanStride; final int end = sp + width; while (sp < end) { int pixel = pixels[sp]; int a = pixel>>>24; if (a<=0) { pixels[sp] = 0x00FFFFFF; } else if (a<255) { int aFP = (0x00FF0000/a); pixels[sp] = ((a << 24) | (((((pixel&0xFF0000)>>16)*aFP)&0xFF0000) ) | (((((pixel&0x00FF00)>>8) *aFP)&0xFF0000)>>8 ) | (((((pixel&0x0000FF)) *aFP)&0xFF0000)>>16)); } sp++; } } } protected static void mult_INT_PACK_Data(WritableRaster wr) { // System.out.println("Multiply Int: " + wr); SinglePixelPackedSampleModel sppsm; sppsm = (SinglePixelPackedSampleModel)wr.getSampleModel(); final int width = wr.getWidth(); final int scanStride = sppsm.getScanlineStride(); DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); final int base = (db.getOffset() + sppsm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(), wr.getMinY()-wr.getSampleModelTranslateY())); // Access the pixel data array final int[] pixels = db.getBankData()[0]; for (int y=0; y<wr.getHeight(); y++) { int sp = base + y*scanStride; final int end = sp + width; while (sp < end) { int pixel = pixels[sp]; int a = pixel>>>24; if ((a>=0) && (a<255)) { // this does NOT include a == 255 (0xff) ! pixels[sp] = ((a << 24) | ((((pixel&0xFF0000)*a)>>8)&0xFF0000) | ((((pixel&0x00FF00)*a)>>8)&0x00FF00) | ((((pixel&0x0000FF)*a)>>8)&0x0000FF)); } sp++; } } } protected static void divide_BYTE_COMP_Data(WritableRaster wr) { // System.out.println("Multiply Int: " + wr); ComponentSampleModel csm; csm = (ComponentSampleModel)wr.getSampleModel(); final int width = wr.getWidth(); final int scanStride = csm.getScanlineStride(); final int pixStride = csm.getPixelStride(); final int [] bandOff = csm.getBandOffsets(); DataBufferByte db = (DataBufferByte)wr.getDataBuffer(); final int base = (db.getOffset() + csm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(), wr.getMinY()-wr.getSampleModelTranslateY())); int aOff = bandOff[bandOff.length-1]; int bands = bandOff.length-1; // Access the pixel data array final byte[] pixels = db.getBankData()[0]; for (int y=0; y<wr.getHeight(); y++) { int sp = base + y*scanStride; final int end = sp + width*pixStride; while (sp < end) { int a = pixels[sp+aOff]&0xFF; if (a==0) { for (int b = 0; b < bands; b++) pixels[sp+bandOff[b]] = (byte)0xFF; } else if (a<255) { // this does NOT include a == 255 (0xff) ! int aFP = (0x00FF0000/a); for (int b = 0; b < bands; b++) { int i = sp+bandOff[b]; pixels[i] = (byte)(((pixels[i]&0xFF)*aFP)>>>16); } } sp+=pixStride; } } } protected static void mult_BYTE_COMP_Data(WritableRaster wr) { // System.out.println("Multiply Int: " + wr); ComponentSampleModel csm; csm = (ComponentSampleModel)wr.getSampleModel(); final int width = wr.getWidth(); final int scanStride = csm.getScanlineStride(); final int pixStride = csm.getPixelStride(); final int [] bandOff = csm.getBandOffsets(); DataBufferByte db = (DataBufferByte)wr.getDataBuffer(); final int base = (db.getOffset() + csm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(), wr.getMinY()-wr.getSampleModelTranslateY())); int aOff = bandOff[bandOff.length-1]; int bands = bandOff.length-1; // Access the pixel data array final byte[] pixels = db.getBankData()[0]; for (int y=0; y<wr.getHeight(); y++) { int sp = base + y*scanStride; final int end = sp + width*pixStride; while (sp < end) { int a = pixels[sp+aOff]&0xFF; if (a!=0xFF) for (int b = 0; b < bands; b++) { int i = sp+bandOff[b]; pixels[i] = (byte)(((pixels[i]&0xFF)*a)>>8); } sp+=pixStride; } } } /* This is skanky debugging code that might be useful in the future: if (count == 33) { String label = "sub [" + x + ", " + y + "]: "; org.ImageDisplay.showImage (label, subBI); org.ImageDisplay.printImage (label, subBI, new Rectangle(75-iR.x, 90-iR.y, 32, 32)); } // if ((count++ % 50) == 10) // org.ImageDisplay.showImage("foo: ", subBI); Graphics2D realG2D = g2d; while (realG2D instanceof sun.java2d.ProxyGraphics2D) { realG2D = ((sun.java2d.ProxyGraphics2D)realG2D).getDelegate(); } if (realG2D instanceof sun.awt.image.BufferedImageGraphics2D) { count++; if (count == 34) { RenderedImage ri; ri = ((sun.awt.image.BufferedImageGraphics2D)realG2D).bufImg; // g2d.setComposite(SVGComposite.OVER); // org.ImageDisplay.showImage("Bar: " + count, cr); org.ImageDisplay.printImage("Bar: " + count, cr, new Rectangle(75, 90, 32, 32)); org.ImageDisplay.showImage ("Foo: " + count, ri); org.ImageDisplay.printImage("Foo: " + count, ri, new Rectangle(75, 90, 32, 32)); System.out.println("BI: " + ri); System.out.println("BISM: " + ri.getSampleModel()); System.out.println("BICM: " + ri.getColorModel()); System.out.println("BICM class: " + ri.getColorModel().getClass()); System.out.println("BICS: " + ri.getColorModel().getColorSpace()); System.out.println ("sRGB CS: " + ColorSpace.getInstance(ColorSpace.CS_sRGB)); System.out.println("G2D info"); System.out.println("\tComposite: " + g2d.getComposite()); System.out.println("\tTransform" + g2d.getTransform()); java.awt.RenderingHints rh = g2d.getRenderingHints(); java.util.Set keys = rh.keySet(); java.util.Iterator iter = keys.iterator(); while (iter.hasNext()) { Object o = iter.next(); System.out.println("\t" + o.toString() + " -> " + rh.get(o).toString()); } ri = cr; System.out.println("RI: " + ri); System.out.println("RISM: " + ri.getSampleModel()); System.out.println("RICM: " + ri.getColorModel()); System.out.println("RICM class: " + ri.getColorModel().getClass()); System.out.println("RICS: " + ri.getColorModel().getColorSpace()); } } */ /** * Extracts an alpha raster from a RenderedImage. The method tries to avoid copying data * unnecessarily by checking if the RenderedImage is a BufferedImage which offers suitable * direct methods. * @param image the image * @return the alpha raster */ public static Raster getAlphaRaster(RenderedImage image) { ColorModel cm = image.getColorModel(); if (!cm.hasAlpha() || cm.getTransparency() != ColorModel.TRANSLUCENT) { throw new IllegalStateException("Image doesn't have an alpha channel"); } Raster alpha; if (image instanceof BufferedImage) { //Optimization possible with BufferedImage (No copying) alpha = ((BufferedImage)image).getAlphaRaster(); } else { WritableRaster wraster = GraphicsUtil.makeRasterWritable(image.getData()); alpha = image.getColorModel().getAlphaRaster(wraster); } return alpha; } }