/*
* 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;
}
}
|