Cannot export a 6kx6k matrix
USE CASE: WHAT DO YOU WANT TO DO?
Export the largest supported file size
STEPS TO REPRODUCE AN ISSUE (OR TRIGGER A NEW FEATURE)
- Open the large_6kx6k.txt test data file
- 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)
-
repo owner -
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.
-
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.
-
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.
-
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...
-
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)
-
reporter Looks like PNGJ was written for this purpose: http://stackoverflow.com/questions/9089675/creating-huge-bufferedimage
...
-
reporter I've added the ability to disable elements of the export interface when export will lead to an exception:
- disables export region radio buttons when too big upon initial load and after a different format type is selected.
I need yet to:
- Disable aspect ratio radio buttons based on selected region & if it would be too big
- Disable to export format items that will lead to exceptions for all active regions
- Disable aspect ratios that would be too big for currently selected region
-
reporter -
assigned issue to
-
assigned issue to
-
reporter - changed status to resolved
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.
-
reporter - changed status to closed
Merged to master.
-
reporter - changed component to Import/export
-
reporter - removed milestone
Removing milestone: Import/export data (automated comment)
- Log in to comment
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?