Cannot export a 6kx6k matrix

Issue #377 closed
Robert Leach created an issue

USE CASE: WHAT DO YOU WANT TO DO?

Export the largest supported file size

STEPS TO REPRODUCE AN ISSUE (OR TRIGGER A NEW FEATURE)

  1. Open the large_6kx6k.txt test data file
  2. Export the entire matrix

CURRENT BEHAVIOR

Java exception about the data being too large and no output file is created:

Dimensions (width=66000 height=66000) are too large
 - java.awt.image.SampleModel.<init>(SampleModel.java:130)
 - java.awt.image.SinglePixelPackedSampleModel.<init>(SinglePixelPackedSampleModel.java:144)
 - java.awt.image.Raster.createPackedRaster(Raster.java:773)
 - java.awt.image.Raster.createPackedRaster(Raster.java:475)
 - java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:1032)
 - java.awt.image.BufferedImage.<init>(BufferedImage.java:340)
 - Controllers.ExportHandler.exportImage(ExportHandler.java:456)
 - Controllers.ExportHandler.export(ExportHandler.java:426)
 - edu.stanford.genetics.treeview.ExportDialogController$ExportListener.actionPerformed(ExportDialogController.java:124)
 - javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
 - javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2346)
 - javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
 - javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
 - javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
 - java.awt.Component.processMouseEvent(Component.java:6527)
 - javax.swing.JComponent.processMouseEvent(JComponent.java:3321)
 - java.awt.Component.processEvent(Component.java:6292)
 - java.awt.Container.processEvent(Container.java:2234)
 - java.awt.Component.dispatchEventImpl(Component.java:4883)
 - java.awt.Container.dispatchEventImpl(Container.java:2292)
 - java.awt.Component.dispatchEvent(Component.java:4705)
 - java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4898)
 - java.awt.LightweightDispatcher.processMouseEvent(Container.java:4533)
 - java.awt.LightweightDispatcher.dispatchEvent(Container.java:4462)
 - java.awt.Container.dispatchEventImpl(Container.java:2278)
 - java.awt.Window.dispatchEventImpl(Window.java:2739)
 - java.awt.Component.dispatchEvent(Component.java:4705)
 - java.awt.EventQueue.dispatchEventImpl(EventQueue.java:746)
 - java.awt.EventQueue.access$400(EventQueue.java:97)
 - java.awt.EventQueue$3.run(EventQueue.java:697)
 - java.awt.EventQueue$3.run(EventQueue.java:691)
 - java.security.AccessController.doPrivileged(Native Method)
 - java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
 - java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:86)
 - java.awt.EventQueue$4.run(EventQueue.java:719)
 - java.awt.EventQueue$4.run(EventQueue.java:717)
 - java.security.AccessController.doPrivileged(Native Method)
 - java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
 - java.awt.EventQueue.dispatchEvent(EventQueue.java:716)
 - java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
 - java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
 - java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:109)
 - java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:184)
 - java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:229)
 - java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:227)
 - java.security.AccessController.doPrivileged(Native Method)
 - java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:227)
 - java.awt.Dialog.show(Dialog.java:1084)
 - java.awt.Component.show(Component.java:1656)
 - java.awt.Component.setVisible(Component.java:1608)
 - java.awt.Window.setVisible(Window.java:1014)
 - java.awt.Dialog.setVisible(Dialog.java:1005)
 - Utilities.CustomDialog.setVisible(CustomDialog.java:109)
 - Controllers.TVController.openExportMenu(TVController.java:1013)
 - Controllers.MenubarController.execute(MenubarController.java:75)
 - Controllers.TVController$StackMenuListener.actionPerformed(TVController.java:249)
 - javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
 - javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2346)
 - javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
 - javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
 - javax.swing.AbstractButton.doClick(AbstractButton.java:376)
 - javax.swing.AbstractButton.doClick(AbstractButton.java:356)
 - javax.swing.plaf.basic.BasicMenuItemUI$Actions.actionPerformed(BasicMenuItemUI.java:802)
 - javax.swing.SwingUtilities.notifyAction(SwingUtilities.java:1663)
 - javax.swing.JComponent.processKeyBinding(JComponent.java:2879)
 - javax.swing.JMenuBar.processBindingForKeyStrokeRecursive(JMenuBar.java:699)
 - javax.swing.JMenuBar.processBindingForKeyStrokeRecursive(JMenuBar.java:706)
 - javax.swing.JMenuBar.processBindingForKeyStrokeRecursive(JMenuBar.java:706)
 - javax.swing.JMenuBar.processKeyBinding(JMenuBar.java:677)
 - javax.swing.KeyboardManager.fireBinding(KeyboardManager.java:307)
 - javax.swing.KeyboardManager.fireKeyboardAction(KeyboardManager.java:293)
 - javax.swing.JComponent.processKeyBindingsForAllComponents(JComponent.java:2971)
 - javax.swing.SwingUtilities.processKeyBindings(SwingUtilities.java:1587)
 - javax.swing.UIManager$2.postProcessKeyEvent(UIManager.java:1483)
 - java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:817)
 - java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:1074)
 - java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:945)
 - java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:771)
 - java.awt.Component.dispatchEventImpl(Component.java:4754)
 - java.awt.Container.dispatchEventImpl(Container.java:2292)
 - java.awt.Window.dispatchEventImpl(Window.java:2739)
 - java.awt.Component.dispatchEvent(Component.java:4705)
 - java.awt.EventQueue.dispatchEventImpl(EventQueue.java:746)
 - java.awt.EventQueue.access$400(EventQueue.java:97)
 - java.awt.EventQueue$3.run(EventQueue.java:697)
 - java.awt.EventQueue$3.run(EventQueue.java:691)
 - java.security.AccessController.doPrivileged(Native Method)
 - java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
 - java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:86)
 - java.awt.EventQueue$4.run(EventQueue.java:719)
 - java.awt.EventQueue$4.run(EventQueue.java:717)
 - java.security.AccessController.doPrivileged(Native Method)
 - java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
 - java.awt.EventQueue.dispatchEvent(EventQueue.java:716)
 - java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
 - java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
 - java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
 - java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
 - java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
 - java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
Exported file: [/Users/rleach/PROJECT/TREEVIEW/TestData/large_6kx6k.txt.PNG].

EXPECTED BEHAVIOR

No error and a properly exported file is produced.

DEVELOPERS ONLY SECTION

SUGGESTED CHANGE (Pseudocode optional)

Unsure

FILES AFFECTED (where the changes will be implemented) - developers only

unknown

LEVEL OF EFFORT - developers only

medium

COMMENTS

Comments (13)

  1. Christopher Keil repo owner

    How does the export code even work for these large sizes? Remember the issues we had with the old drawing code (which you are using, right?) due to weird color averaging when multiple data points mapped to one pixel (before using BufferedImage's built-in image scaling methods). This exceptions seems to involve BufferedImage stuff as well - is this a limit of the class due to the gigantic pixel requirements?

  2. Robert Leach reporter

    I'm not sure if my implementation is optimally coded. I am just calling modified versions of the old methods that drew the matrix and trees and passing in a Graphics2D object. They basically call things like drawRect, fillRect, drayPolyLine, and DrawLine. I don't actually know where BufferedImage comes into play.

    The modifications I made to the code basically have a minimum size of the number of data rows & cols that exist, so there should be no averaging. It actually draws a multiple of those dimensions because the way it behaved by default was that however wide a column was (let's call it C points wide), the thickness of the tree line was 1/C (and it's a whole number). There might have been a better way to do it, but I basically made the thickness of the rows/cols to be wide enough to make the thickness of the tree lines to look right.

  3. Robert Leach reporter

    I was playing around with this (debugging it), and noticed that if I change the minimum tile dimension from 11 to 5, I get a different exception: out of memory. If I change it to 3, the image exports fine. The tree lines however look too thick because of this tile dimension.

    I followed the trace up and saw that the height x width must be smaller than Integer.MAX_VALUE, which for me is 2147483647. The matrix is 6000x6000 and each cell was 11 wide/tall, which means 66000x66000 = 4356000000, which is 2208516353 too big.

    I might be able to look deeper into this and find a way to make it 6000x6000 and set the line width or something, but that won't work for PNG which only draws pixels. I might be able to adjust the minimum tile dimension for larger data.

    Lance and I figured that 1st: catch the exception. 2nd: predict whether this will happen and either warn the user that the export might not work, gray-out options, or tell the user that they need to zoom in or something and that they can't export the entire matrix. The ideal solution would be to generate portions of the image and stitch them together.

  4. Robert Leach reporter

    OK, the old code I copied for exporting png, jpg, and ppm all use bufferedimage:

                BufferedImage im;
                //JPG is the only format that doesn't support an alpha channel, so
                //we must create a buffered image object without the ARGB type
                if(format == Format.JPG) {
                    im = new BufferedImage(
                        getXDim(region),getYDim(region),BufferedImage.TYPE_INT_RGB);
                } else {
                    im = new BufferedImage(getXDim(region),getYDim(region),
                        BufferedImage.TYPE_INT_ARGB);
                }
    
                Graphics2D g2d = (Graphics2D) im.getGraphics();
                createContent(g2d,region,showSelections);
    
                File exportFile = new File(fileName);
                if(format == Format.PNG) {
                    ImageIO.write(im,"png",exportFile);
                } else if(format == Format.JPG) {
                    ImageIO.write(im,"jpg",exportFile);
                } else if(format == Format.PPM) { //ppm = bitmat
                    final OutputStream os = new BufferedOutputStream(
                        new FileOutputStream(exportFile));
                    PpmWriter.writePpm(im,os);
                } else {
                    LogBuffer.println("Unrecognized export format: [" + format +
                        "].");
                    return;
                }
    

    The other formats (PDF, SVG, & PS; for which I use freehep) do not use buffered image.

  5. Robert Leach reporter

    PNG and JPG write methods take type "RenderedImage" and PPM takes type "Image", which I assume "BufferedImage", is derived from. I wonder if I can change the type to solve this issue...

  6. Robert Leach reporter

    Here's the exception when I use the minTileDim of 5:

    Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space
        at java.awt.image.DataBufferInt.<init>(DataBufferInt.java:75)
        at java.awt.image.Raster.createPackedRaster(Raster.java:467)
        at java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:1032)
        at java.awt.image.BufferedImage.<init>(BufferedImage.java:340)
        at Controllers.ExportHandler.exportImage(ExportHandler.java:458)
        at Controllers.ExportHandler.export(ExportHandler.java:428)
        at edu.stanford.genetics.treeview.ExportDialogController$ExportListener.actionPerformed(ExportDialogController.java:124)
        at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
        at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2346)
        at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
        at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
        at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
        at java.awt.Component.processMouseEvent(Component.java:6527)
        at javax.swing.JComponent.processMouseEvent(JComponent.java:3321)
        at java.awt.Component.processEvent(Component.java:6292)
        at java.awt.Container.processEvent(Container.java:2234)
        at java.awt.Component.dispatchEventImpl(Component.java:4883)
        at java.awt.Container.dispatchEventImpl(Container.java:2292)
        at java.awt.Component.dispatchEvent(Component.java:4705)
        at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4898)
        at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4533)
        at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4462)
        at java.awt.Container.dispatchEventImpl(Container.java:2278)
        at java.awt.Window.dispatchEventImpl(Window.java:2739)
        at java.awt.Component.dispatchEvent(Component.java:4705)
        at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:746)
        at java.awt.EventQueue.access$400(EventQueue.java:97)
        at java.awt.EventQueue$3.run(EventQueue.java:697)
        at java.awt.EventQueue$3.run(EventQueue.java:691)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
        at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:86)
    
  7. Robert Leach reporter

    I've added the ability to disable elements of the export interface when export will lead to an exception:

    1. disables export region radio buttons when too big upon initial load and after a different format type is selected.

    I need yet to:

    1. Disable aspect ratio radio buttons based on selected region & if it would be too big
    2. Disable to export format items that will lead to exceptions for all active regions
    3. Disable aspect ratios that would be too big for currently selected region
  8. Robert Leach reporter

    PR 58 does the minimum work to resolve this issue. By tweaking the settings, a 6kx6k matrix can now be exported. Some settings can lead to an out of memory error, but that issue is caught and presented to the user in a warning window.

    I will create a new issue for alpha04 which is intended to overcome the size limitation of PNG/JPG/PPM exports. There exists a package called PNGJ which addresses this issue by stitching multiple pieces of a large image together, but it only works for PNG exports, not the other image formats.

  9. Log in to comment