Bug 48407 - OutOfMemoryError, when zooming in svg document
Summary: OutOfMemoryError, when zooming in svg document
Status: NEW
Alias: None
Product: Batik - Now in Jira
Classification: Unclassified
Component: GVT (show other bugs)
Version: 1.7
Hardware: PC Windows XP
: P2 normal
Target Milestone: ---
Assignee: Batik Developer's Mailing list
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-12-17 07:11 UTC by Georg Abfalter
Modified: 2010-06-22 07:36 UTC (History)
1 user (show)



Attachments
SVG document, having a grafic with color gradient in background for more realisitc appearance (95.50 KB, image/svg+xml)
2009-12-17 07:11 UTC, Georg Abfalter
Details
Image embedded in SVG file (40.01 KB, image/jpeg)
2009-12-19 12:10 UTC, Helder Magalhães
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Georg Abfalter 2009-12-17 07:11:51 UTC
Created attachment 24726 [details]
SVG document, having a grafic with color gradient in background for more realisitc appearance

We are using batik for displaying human bodies for locating their body parts. For realistic displaying we colorize the svg model with a graphic, which is embedded in svg document. The png-graphic is having a certain color gradient, for more realistic appearance. 

Problem:
If we are zoom in serveral times (shift-right mouse pressed and move up, -Xmx256M) with this svg document [see attachment], we do gain a java.lang.OutOfMemoryError.

TestProgram:
public class JSVGCanvasDemo
{
    public static void main(String[] P_args)
    {
        JFrame F_f = new JFrame("Batik");
        JSVGCanvasDemo F_canvas = new JSVGCanvasDemo();

        F_f.getContentPane().add(F_canvas.createSVGComponent());
        F_f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        F_f.setSize(400, 400);
        F_f.setVisible(true);
    }

    protected JSVGCanvas svgCanvas = new JSVGCanvas();

    public JSVGCanvasDemo()
    {
        // noop
    }

    public JComponent createSVGComponent()
    {
        final JPanel F_panel = new JPanel(new BorderLayout());
        F_panel.add("Center", svgCanvas);
        svgCanvas.setURI("file:/D:/male_front.svg");
        return F_panel;
    }
}

Stack:
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid1764.hprof ...
Heap dump file created [19697489 bytes in 3.415 secs]
-E-2009-12-17 15:45:46.887: java.lang.OutOfMemoryError: Java heap space
	at java.awt.image.DataBufferByte.<init>(DataBufferByte.java:42)
	at java.awt.image.Raster.createInterleavedRaster(Raster.java:253)
	at java.awt.image.Raster.createInterleavedRaster(Raster.java:194)
	at java.awt.image.ComponentColorModel.createCompatibleWritableRaster(ComponentColorModel.java:2808)
	at java.awt.image.BufferedImage.<init>(BufferedImage.java:409)
	at org.apache.batik.ext.awt.image.renderable.ClipRable8Bit.createRendering(ClipRable8Bit.java:158)
	at org.apache.batik.ext.awt.image.GraphicsUtil.drawImage(GraphicsUtil.java:448)
	at org.apache.batik.gvt.AbstractGraphicsNode.paint(AbstractGraphicsNode.java:549)
	at org.apache.batik.gvt.CompositeGraphicsNode.primitivePaint(CompositeGraphicsNode.java:165)
	at org.apache.batik.gvt.AbstractGraphicsNode.paint(AbstractGraphicsNode.java:509)
	at org.apache.batik.gvt.CompositeGraphicsNode.primitivePaint(CompositeGraphicsNode.java:165)
	at org.apache.batik.gvt.CanvasGraphicsNode.primitivePaint(CanvasGraphicsNode.java:159)
	at org.apache.batik.gvt.AbstractGraphicsNode.paint(AbstractGraphicsNode.java:509)
	at org.apache.batik.gvt.CompositeGraphicsNode.primitivePaint(CompositeGraphicsNode.java:165)
	at org.apache.batik.gvt.filter.GraphicsNodeRed8Bit.genRect(GraphicsNodeRed8Bit.java:140)
	at org.apache.batik.gvt.filter.GraphicsNodeRed8Bit.copyData(GraphicsNodeRed8Bit.java:116)
	at org.apache.batik.ext.awt.image.rendered.TileCacheRed.genRect(TileCacheRed.java:63)
	at org.apache.batik.ext.awt.image.rendered.AbstractTiledRed.drawBlockInPlace(AbstractTiledRed.java:629)
	at org.apache.batik.ext.awt.image.rendered.AbstractTiledRed.drawBlock(AbstractTiledRed.java:544)
	at org.apache.batik.ext.awt.image.rendered.AbstractTiledRed.copyToRasterByBlocks(AbstractTiledRed.java:430)
	at org.apache.batik.ext.awt.image.rendered.AbstractTiledRed.copyData(AbstractTiledRed.java:297)
	at org.apache.batik.ext.awt.image.rendered.TranslateRed.copyData(TranslateRed.java:105)
	at org.apache.batik.gvt.renderer.StaticRenderer.repaint(StaticRenderer.java:394)
	at org.apache.batik.gvt.renderer.StaticRenderer.repaint(StaticRenderer.java:344)
	at org.apache.batik.swing.gvt.GVTTreeRenderer.run(GVTTreeRenderer.java:123)
-E-2009-12-17 15:45:47.09: java.lang.OutOfMemoryError: Java heap space
	at java.awt.image.DataBufferByte.<init>(DataBufferByte.java:42)
	at java.awt.image.Raster.createInterleavedRaster(Raster.java:253)
	at java.awt.image.Raster.createInterleavedRaster(Raster.java:194)
	at java.awt.image.ComponentColorModel.createCompatibleWritableRaster(ComponentColorModel.java:2808)
	at java.awt.image.BufferedImage.<init>(BufferedImage.java:409)
	at org.apache.batik.ext.awt.image.renderable.ClipRable8Bit.createRendering(ClipRable8Bit.java:158)
	at org.apache.batik.ext.awt.image.GraphicsUtil.drawImage(GraphicsUtil.java:448)
	at org.apache.batik.gvt.AbstractGraphicsNode.paint(AbstractGraphicsNode.java:549)
	at org.apache.batik.gvt.CompositeGraphicsNode.primitivePaint(CompositeGraphicsNode.java:165)
	at org.apache.batik.gvt.AbstractGraphicsNode.paint(AbstractGraphicsNode.java:509)
	at org.apache.batik.gvt.CompositeGraphicsNode.primitivePaint(CompositeGraphicsNode.java:165)
	at org.apache.batik.gvt.CanvasGraphicsNode.primitivePaint(CanvasGraphicsNode.java:159)
	at org.apache.batik.gvt.AbstractGraphicsNode.paint(AbstractGraphicsNode.java:509)
	at org.apache.batik.gvt.CompositeGraphicsNode.primitivePaint(CompositeGraphicsNode.java:165)
	at org.apache.batik.gvt.filter.GraphicsNodeRed8Bit.genRect(GraphicsNodeRed8Bit.java:140)
	at org.apache.batik.gvt.filter.GraphicsNodeRed8Bit.copyData(GraphicsNodeRed8Bit.java:116)
	at org.apache.batik.ext.awt.image.rendered.TileCacheRed.genRect(TileCacheRed.java:63)
	at org.apache.batik.ext.awt.image.rendered.AbstractTiledRed.drawBlockInPlace(AbstractTiledRed.java:629)
	at org.apache.batik.ext.awt.image.rendered.AbstractTiledRed.drawBlock(AbstractTiledRed.java:544)
	at org.apache.batik.ext.awt.image.rendered.AbstractTiledRed.copyToRasterByBlocks(AbstractTiledRed.java:430)
	at org.apache.batik.ext.awt.image.rendered.AbstractTiledRed.copyData(AbstractTiledRed.java:297)
	at org.apache.batik.ext.awt.image.rendered.TranslateRed.copyData(TranslateRed.java:105)
	at org.apache.batik.gvt.renderer.StaticRenderer.repaint(StaticRenderer.java:394)
	at org.apache.batik.gvt.renderer.StaticRenderer.repaint(StaticRenderer.java:344)
	at org.apache.batik.swing.gvt.GVTTreeRenderer.run(GVTTreeRenderer.java:123)
Comment 1 Helder Magalhães 2009-12-19 12:05:50 UTC
(In reply to comment #0)
> For realistic displaying we colorize the svg model with a graphic, which is
> embedded in svg document. The png-graphic is having a certain color gradient,
> for more realistic appearance. 

I just took a look at the document and the image format isn't PNG but JPEG. ;-)


Nevertheless, I recall a recent, tightly related commit (see revision 793536). Could you please download a nightly build [1] and try reproducing the issue again? (Although the latest nightly available, as of this writing, is already a bit outdated, it already includes the change which probably fixes the issue.)

Please report back! :-)


[1] http://mcc.id.au/batik-nightly/
Comment 2 Helder Magalhães 2009-12-19 12:10:10 UTC
Created attachment 24736 [details]
Image embedded in SVG file

I just used an online base64 converter [1] to extract the image; I'm attaching it for posterity, although I'm somehow convinced that this issue was already fixed a few months ago (see my previous comment).

[1] http://www.opinionatedgeek.com/dotnet/tools/Base64Decode/
Comment 3 Georg Abfalter 2009-12-22 05:41:42 UTC
(In reply to comment #1)
> (In reply to comment #0)
> > For realistic displaying we colorize the svg model with a graphic, which is
> > embedded in svg document. The png-graphic is having a certain color gradient,
> > for more realistic appearance. 
> 
> I just took a look at the document and the image format isn't PNG but JPEG. ;-)
> 
> 
> Nevertheless, I recall a recent, tightly related commit (see revision 793536).
> Could you please download a nightly build [1] and try reproducing the issue
> again? (Although the latest nightly available, as of this writing, is already a
> bit outdated, it already includes the change which probably fixes the issue.)
> 
> Please report back! :-)
> 
> 
> [1] http://mcc.id.au/batik-nightly/

i have just retested the problem with the batik nightly build; i'm still gaining the OutOfMemmoryError :(
Comment 4 Christoph Bimminger 2010-06-22 07:36:51 UTC
The bug also occurs with up-to-date nightly build batik-src-10-05-21.

I analyzed the bug, and tried to fix it with a local hack. In org.apache.batik.ext.awt.image.renderable.ClipRable8Bit an alpha mask is created, where the BufferedImage is used for. If I see right, the Alpha Mask - which supports BYTE_GRAY, only contains 100% white area in the shape region. 

For my purpose, I removed the BufferedImage completely. The "hacked" createRendering method looks like posted below (see lines commented-out). I assume that there might occur rendering bugs when image shapes are not rectangular? But better such a rendering bug than the OutOfMemoryError.

A task would, in my humble opinion, be to eliminate the BufferedImage here and use kind of "ShapeImage" that delegates to the shape for the alpha mask. The shape can be used to determine the alpha value of each pixel when multiplying the image with the alpha mask, without needing a large BufferedImage. But it will be tricky to find a structure to multiply image value and alpha intensity, on something different to a BufferedImage. Can a BufferedImage in original image dimension be used, and be scaled "on the fly" when rendering, without requireing large memory for a scaled BufferedImage?

public RenderedImage createRendering(RenderContext rc) {

        AffineTransform usr2dev = rc.getTransform();

        // Just copy over the rendering hints.
        RenderingHints rh = rc.getRenderingHints();
        if (rh == null)  rh = new RenderingHints(null);

        Shape aoi = rc.getAreaOfInterest();
        if (aoi == null) aoi = getBounds2D();

        Rectangle2D rect     = getBounds2D();
        Rectangle2D clipRect = clipPath.getBounds2D();
        Rectangle2D aoiRect  = aoi.getBounds2D();

        if ( ! rect.intersects(clipRect) )
            return null;
        Rectangle2D.intersect(rect, clipRect, rect);


        if ( ! rect.intersects(aoiRect) )
            return null;
        Rectangle2D.intersect(rect, aoi.getBounds2D(), rect);

        Rectangle devR = usr2dev.createTransformedShape(rect).getBounds();

        if ((devR.width == 0) || (devR.height == 0) || false)
            return null;

//        BufferedImage bi = new BufferedImage(devR.width, devR.height,
//                                             BufferedImage.TYPE_BYTE_GRAY);

        Shape devShape = usr2dev.createTransformedShape(getClipPath());
        Rectangle devAOIR;
        devAOIR = usr2dev.createTransformedShape(aoi).getBounds();

//        Graphics2D g2d = GraphicsUtil.createGraphics(bi, rh);

        if (false) {
            java.util.Set s = rh.keySet();
            java.util.Iterator i = s.iterator();
            while (i.hasNext()) {
                Object o = i.next();
                System.out.println("XXX: " + o + " -> " + rh.get(o));
            }
        }
//        g2d.translate(-devR.x, -devR.y);
//        g2d.setPaint(Color.white);
//        g2d.fill(devShape);
//        g2d.dispose();

        RenderedImage ri;
        ri = getSource().createRendering(new RenderContext(usr2dev, rect, rh));

        CachableRed cr, clipCr;
        cr = RenderedImageCachableRed.wrap(ri);
//        clipCr = new BufferedImageCachableRed(bi, devR.x, devR.y);
        CachableRed ret = cr;
//        CachableRed ret = new MultiplyAlphaRed(cr, clipCr);

          // Pad back out to the proper size...
        ret = new PadRed(ret, devAOIR, PadMode.ZERO_PAD, rh);

        return ret;
    }