Follow up:
I created a custom Composite and, yes, it’s faster than creating a new “tricked” image.
Sadly, due to direct raster manipulation I don’t think it will never be hw accelerated.
THIS IS THE EXTENSIBLECOMPOSITE
package it.classx.util.java2d;
import java.awt.Color;
import java.awt.Composite;
import java.awt.CompositeContext;
import java.awt.RenderingHints;
import java.awt.color.ColorSpace;
import java.awt.image.ColorConvertOp;
import java.awt.image.ColorModel;
import java.awt.image.DataBufferInt;
import java.awt.image.Raster;
import java.awt.image.SinglePixelPackedSampleModel;
import java.awt.image.WritableRaster;
/**
* A flexible extensible composite
* Creation date: (19/07/2004 11.50.21)
* @author: Mik of ClassX
*/
public abstract class ExtensibleComposite implements java.awt.Composite, java.awt.CompositeContext
{
/** The source color model for this context. */
protected ColorModel srcColors = null;
/** The destination color model for this context. */
protected ColorModel dstColors = null;
/** The source color space for this context. */
protected ColorSpace srcColorSpace = null;
/** The destination color space for this context. */
protected ColorSpace dstColorSpace = null;
/**
* Composes the two source {@link Raster} objects and
* places the result in the destination
* {@link WritableRaster}. Note that the destination
* can be the same object as either the first or second
* source. Note that <code>dstIn</code> and
* <code>dstOut</code> must be compatible with the
* <code>dstColorModel</code> passed to the
* {@link Composite#createContext(java.awt.image.ColorModel, java.awt.image.ColorModel, java.awt.RenderingHints) createContext}
* method of the <code>Composite</code> interface.
* @param src the first source for the compositing operation
* @param dstIn the second source for the compositing operation
* @param dstOut the <code>WritableRaster</code> into which the
* result of the operation is stored
* @see Composite
*/
public void compose(java.awt.image.Raster src, java.awt.image.Raster dstIn, java.awt.image.WritableRaster dstOut)
{
//
// Sanity check: Raster sizes should be compatible
//
if (src.getWidth() != dstIn.getWidth()
|| src.getHeight() != dstIn.getHeight()
|| src.getMinX() != dstIn.getMinX()
|| src.getMinY() != dstIn.getMinY())
throw new Error("Incompatible sizes or origin for src and dstIn rasters");
//
// Raster data. For clarity
//
int x = src.getMinX();
int y = src.getMinY();
int w = src.getWidth();
int h = src.getHeight();
//
// First, convert to sRGB if necessary
//
ColorModel srgbCM = ColorModel.getRGBdefault();
boolean srcNeedsConvert = !srcColors.equals(srgbCM);
if (srcNeedsConvert)
{
WritableRaster newSrc = srgbCM.createCompatibleWritableRaster(w, h);
ColorConvertOp srcTosRGB = new ColorConvertOp(srcColorSpace, srgbCM.getColorSpace(), null);
srcTosRGB.filter(src, newSrc);
src = newSrc.createWritableTranslatedChild(x, y);
}
boolean dstNeedsConvert = !dstColors.equals(srgbCM);
WritableRaster dstOutSav = dstOut;
if (dstNeedsConvert)
{
WritableRaster newDstIn = srgbCM.createCompatibleWritableRaster(w, h);
ColorConvertOp dstTosRGB = new ColorConvertOp(dstColorSpace, srgbCM.getColorSpace(), null);
dstTosRGB.filter(dstIn, newDstIn);
dstIn = newDstIn.createWritableTranslatedChild(x, y);
WritableRaster newDstOut = srgbCM.createCompatibleWritableRaster(w, h);
dstTosRGB.filter(dstOut, newDstOut);
dstOut = newDstOut.createWritableTranslatedChild(x, y);
}
//
// Now, access the data banks for faster processing. Because we are sure
// that we are using the DirectColorModel, we know that the internal
// DataBuffers are DataBufferInt and have a single bank.
// Because the relevant buffers may not start at index 0, take the (x,y)
// starting offset into account. Furthermore, the scanline stride may be
// bigger than the rasters width (in case the raster is a child raster, for
// example). Therefore, we remember to use the scanline stride to adjust offsets.
//
DataBufferInt srcDB = (DataBufferInt) src.getDataBuffer();
DataBufferInt dstInDB = (DataBufferInt) dstIn.getDataBuffer();
DataBufferInt dstOutDB = (DataBufferInt) dstOut.getDataBuffer();
int srcRGB[] = srcDB.getBankData()[0];
int dstInRGB[] = dstInDB.getBankData()[0];
int dstOutRGB[] = dstOutDB.getBankData()[0];
int srcOffset = srcDB.getOffset();
int dstInOffset = dstInDB.getOffset();
int dstOutOffset = dstOutDB.getOffset();
int srcScanStride = ((SinglePixelPackedSampleModel) src.getSampleModel()).getScanlineStride();
int dstInScanStride = ((SinglePixelPackedSampleModel) dstIn.getSampleModel()).getScanlineStride();
int dstOutScanStride = ((SinglePixelPackedSampleModel) dstOut.getSampleModel()).getScanlineStride();
int srcAdjust = srcScanStride - w;
int dstInAdjust = dstInScanStride - w;
int dstOutAdjust = dstOutScanStride - w;
//
// Now, iterate through the buffer data.
//
int sr = 0, sg = 0, sb = 0, sa = 0; // src rgb pixel values
int dir = 0, dig = 0, dib = 0, dia = 0; // dstIn rgb pixel values
int dor = 0, dog = 0, dob = 0, doa = 0; // dstOut rgb pixel values
int sRGB = 0, diRGB = 0, doRGB = 0;
int si = srcOffset;
int dii = dstInOffset;
int doi = dstOutOffset;
// ScanLine iteration
for (int i = 0; i < h; i++)
{
// Column iteration
for (int j = 0; j < w; j++)
{
// compose pixels
dstOutRGB[doi] = composeRGB(srcRGB[si],dstInRGB[dii]);
// Move to next pixel
si++;
dii++;
doi++;
}
// Move to next scanline
si += srcAdjust;
dii += dstInAdjust;
doi += dstOutAdjust;
}
//
// Convert to dstOut ColorModel if necessary
//
if (dstNeedsConvert)
{
ColorConvertOp srgbToDst = new ColorConvertOp(srgbCM.getColorSpace(), dstColorSpace, null);
srgbToDst.filter(dstOut, dstOutSav);
}
}
// the implementor must override this method
// srcPix = the first source int ARGB pixel of the compositing operation
// dstInPix = the second source int ARGB pixel of the compositing operation
//
// returns the int ARGB result of the compositing operation
public abstract int composeRGB(int srcPix, int dstInPix);
/**
* Creates a context containing state that is used to perform
* the compositing operation. In a multi-threaded environment,
* several contexts can exist simultaneously for a single
* <code>Composite</code> object.
* @param srcColorModel the {@link ColorModel} of the source
* @param dstColorModel the <code>ColorModel</code> of the destination
* @param hints the hint that the context object uses to choose between
* rendering alternatives
* @return the <code>CompositeContext</code> object used to perform the
* compositing operation.
*/
public java.awt.CompositeContext createContext(
java.awt.image.ColorModel srcColorModel,
java.awt.image.ColorModel dstColorModel,
java.awt.RenderingHints hints)
{
// extract colormodel and colorspace
srcColors = srcColorModel;
dstColors = dstColorModel;
//
srcColorSpace = srcColors.getColorSpace();
dstColorSpace = dstColors.getColorSpace();
return this;
}
/**
* Releases resources allocated for a context.
*/
public void dispose()
{
}
}
AND THIS IS THE COMPOSITE CODE
package it.classx.util.java2d;
/**
* Writes the BLUE channel of the source to the ALPHA of the destination
* Creation date: (19/07/2004 12.35.47)
* @author: Mik of ClassX
*/
public class BlueToTransparenceComposite extends ExtensibleComposite
{
/**
* dst(ARGB) = srcBlue,dstInRed,dstInGreen,dstInBlue
*/
public int composeRGB(int srcPix, int dstInPix)
{
return ((srcPix << 24) | (dstInPix & 0x00FFFFFF));
}
}
some of the src code in the compose() method comes from the GLF (V.Hardy).
Cheers,
Mik