in batik-awt-util/src/main/java/org/apache/batik/ext/awt/image/GraphicsUtil.java [96:384]
public static void drawImage(Graphics2D g2d,
CachableRed cr) {
// System.out.println("DrawImage G: " + g2d);
AffineTransform at = null;
while (true) {
if (cr instanceof AffineRed) {
AffineRed ar = (AffineRed)cr;
if (at == null)
at = ar.getTransform();
else
at.concatenate(ar.getTransform());
cr = ar.getSource();
continue;
} else if (cr instanceof TranslateRed) {
TranslateRed tr = (TranslateRed)cr;
// System.out.println("testing Translate");
int dx = tr.getDeltaX();
int dy = tr.getDeltaY();
if (at == null)
at = AffineTransform.getTranslateInstance(dx, dy);
else
at.translate(dx, dy);
cr = tr.getSource();
continue;
}
break;
}
AffineTransform g2dAt = g2d.getTransform();
if ((at == null) || (at.isIdentity()))
at = g2dAt;
else
at.preConcatenate(g2dAt);
ColorModel srcCM = cr.getColorModel();
ColorModel g2dCM = getDestinationColorModel(g2d);
ColorSpace g2dCS = null;
if (g2dCM != null)
g2dCS = g2dCM.getColorSpace();
if (g2dCS == null)
// Assume device is sRGB
g2dCS = ColorSpace.getInstance(ColorSpace.CS_sRGB);
ColorModel drawCM = g2dCM;
if ((g2dCM == null) || !g2dCM.hasAlpha()) {
// If we can't find out about our device or the device
// does not support alpha just use SRGB unpremultiplied
// (Just because this seems to work for us).
drawCM = sRGB_Unpre;
}
if (cr instanceof BufferedImageCachableRed) {
// There is a huge win if we can use the BI directly here.
// This results in something like a 10x performance gain
// for images, the best thing is this is the common case.
if (g2dCS.equals(srcCM.getColorSpace()) &&
drawCM.equals(srcCM)) {
// System.err.println("Fast Case");
g2d.setTransform(at);
BufferedImageCachableRed bicr;
bicr = (BufferedImageCachableRed)cr;
g2d.drawImage(bicr.getBufferedImage(),
bicr.getMinX(), bicr.getMinY(), null);
g2d.setTransform(g2dAt);
return;
}
}
// Scaling down so do it before color conversion.
double determinant = at.getDeterminant();
if (!at.isIdentity() && (determinant <= 1.0)) {
if (at.getType() != AffineTransform.TYPE_TRANSLATION)
cr = new AffineRed(cr, at, g2d.getRenderingHints());
else {
int xloc = cr.getMinX() + (int)at.getTranslateX();
int yloc = cr.getMinY() + (int)at.getTranslateY();
cr = new TranslateRed(cr, xloc, yloc);
}
}
if (g2dCS != srcCM.getColorSpace()) {
// System.out.println("srcCS: " + srcCM.getColorSpace());
// System.out.println("g2dCS: " + g2dCS);
// System.out.println("sRGB: " +
// ColorSpace.getInstance(ColorSpace.CS_sRGB));
// System.out.println("LsRGB: " +
// ColorSpace.getInstance
// (ColorSpace.CS_LINEAR_RGB));
if (g2dCS == ColorSpace.getInstance(ColorSpace.CS_sRGB))
cr = convertTosRGB(cr);
else if (g2dCS == ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB))
cr = convertToLsRGB(cr);
}
srcCM = cr.getColorModel();
if (!drawCM.equals(srcCM))
cr = FormatRed.construct(cr, drawCM);
// Scaling up so do it after color conversion.
if (!at.isIdentity() && (determinant > 1.0))
cr = new AffineRed(cr, at, g2d.getRenderingHints());
// Now CR is in device space, so clear the g2d transform.
g2d.setTransform(IDENTITY);
// Ugly Hack alert. This Makes it use our SrcOver implementation
// Which doesn't seem to have as many bugs as the JDK one when
// going between different src's and destinations (of course it's
// also a lot slower).
Composite g2dComposite = g2d.getComposite();
if (g2d.getRenderingHint(RenderingHintsKeyExt.KEY_TRANSCODING) ==
RenderingHintsKeyExt.VALUE_TRANSCODING_PRINTING) {
if (SVGComposite.OVER.equals(g2dComposite)) {
g2d.setComposite(SVGComposite.OVER);
}
}
Rectangle crR = cr.getBounds();
Shape clip = g2d.getClip();
try {
Rectangle clipR;
if (clip == null) {
clip = crR;
clipR = crR;
} else {
clipR = clip.getBounds();
if ( ! clipR.intersects(crR) )
return; // Nothing to draw...
clipR = clipR.intersection(crR);
}
Rectangle gcR = getDestinationBounds(g2d);
// System.out.println("ClipRects: " + clipR + " -> " + gcR);
if (gcR != null) {
if ( ! clipR.intersects(gcR) )
return; // Nothing to draw...
clipR = clipR.intersection(gcR);
}
// System.out.println("Starting Draw: " + cr);
// long startTime = System.currentTimeMillis();
boolean useDrawRenderedImage = false;
srcCM = cr.getColorModel();
SampleModel srcSM = cr.getSampleModel();
if ((srcSM.getWidth()*srcSM.getHeight()) >=
(clipR.width*clipR.height))
// if srcSM tiles are around the clip size
// then just draw the renderedImage
useDrawRenderedImage = true;
Object atpHint = g2d.getRenderingHint
(RenderingHintsKeyExt.KEY_AVOID_TILE_PAINTING);
if (atpHint == RenderingHintsKeyExt.VALUE_AVOID_TILE_PAINTING_ON)
useDrawRenderedImage = true; //for PDF and PS transcoders
if (atpHint == RenderingHintsKeyExt.VALUE_AVOID_TILE_PAINTING_OFF)
useDrawRenderedImage = false;
WritableRaster wr;
if (useDrawRenderedImage) {
// This can be significantly faster but can also
// require much more memory, so we only use it when
// the clip size is smaller than the tile size.
Raster r = cr.getData(clipR);
wr = ((WritableRaster)r).createWritableChild
(clipR.x, clipR.y, clipR.width, clipR.height,
0, 0, null);
BufferedImage bi = new BufferedImage
(srcCM, wr, srcCM.isAlphaPremultiplied(), null);
// Any of the drawImage calls that take an
// Affine are prone to the 'CGGStackRestore: gstack
// underflow' bug on Mac OS X. This should work
// around that problem.
g2d.drawImage(bi, clipR.x, clipR.y, null);
} else {
// Use tiles to draw image...
wr = Raster.createWritableRaster(srcSM, new Point(0,0));
BufferedImage bi = new BufferedImage
(srcCM, wr, srcCM.isAlphaPremultiplied(), null);
int xt0 = cr.getMinTileX();
int xt1 = xt0+cr.getNumXTiles();
int yt0 = cr.getMinTileY();
int yt1 = yt0+cr.getNumYTiles();
int tw = srcSM.getWidth();
int th = srcSM.getHeight();
Rectangle tR = new Rectangle(0,0,tw,th);
Rectangle iR = new Rectangle(0,0,0,0);
if (false) {
System.err.println("SrcCM: " + srcCM);
System.err.println("CR: " + cr);
System.err.println("CRR: " + crR + " TG: [" +
xt0 + ',' +
yt0 + ',' +
xt1 + ',' +
yt1 +"] Off: " +
cr.getTileGridXOffset() + ',' +
cr.getTileGridYOffset());
}
int yloc = yt0*th+cr.getTileGridYOffset();
int skip = (clipR.y-yloc)/th;
if (skip <0) skip = 0;
yt0+=skip;
int xloc = xt0*tw+cr.getTileGridXOffset();
skip = (clipR.x-xloc)/tw;
if (skip <0) skip = 0;
xt0+=skip;
int endX = clipR.x+clipR.width-1;
int endY = clipR.y+clipR.height-1;
if (false) {
System.out.println("clipR: " + clipR + " TG: [" +
xt0 + ',' +
yt0 + ',' +
xt1 + ',' +
yt1 +"] Off: " +
cr.getTileGridXOffset() + ',' +
cr.getTileGridYOffset());
}
yloc = yt0*th+cr.getTileGridYOffset();
int minX = xt0*tw+cr.getTileGridXOffset();
int xStep = tw;
xloc = minX;
for (int y=yt0; y<yt1; y++, yloc += th) {
if (yloc > endY) break;
for (int x=xt0; x<xt1; x++, xloc+=xStep) {
if ((xloc<minX) || (xloc > endX)) break;
tR.x = xloc;
tR.y = yloc;
Rectangle2D.intersect(crR, tR, iR);
WritableRaster twr;
twr = wr.createWritableChild(0, 0,
iR.width, iR.height,
iR.x, iR.y, null);
// System.out.println("Generating tile: " + twr);
cr.copyData(twr);
// Make sure we only draw the region that was written.
BufferedImage subBI;
subBI = bi.getSubimage(0, 0, iR.width, iR.height);
if (false) {
System.out.println("Drawing: " + tR);
System.out.println("IR: " + iR);
}
// For some reason using the transform version
// causes a gStackUnderflow error but if I just
// use the drawImage with an x & y it works.
g2d.drawImage(subBI, iR.x, iR.y, null);
// AffineTransform trans
// = AffineTransform.getTranslateInstance(iR.x, iR.y);
// g2d.drawImage(subBI, trans, null);
// String label = "sub [" + x + ", " + y + "]: ";
// org.ImageDisplay.showImage
// (label, subBI);
}
xStep = -xStep; // Reverse directions.
xloc += xStep; // Get back in bounds.
}
}
// long endTime = System.currentTimeMillis();
// System.out.println("Time: " + (endTime-startTime));
} finally {
g2d.setTransform(g2dAt);
g2d.setComposite(g2dComposite);
}
// System.out.println("Finished Draw");
}