public void render(GCModel model, String chartFilePath) throws IOException {
    GCPreferences gcPreferences = new GCPreferences();
    gcPreferences.load();

    final ModelChartImpl pane = new ModelChartImpl();
    pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

    pane.setModel(model, gcPreferences);
    pane.setFootprint(model.getFootprint());
    pane.setMaxPause(model.getPause().getMax());
    pane.setRunningTime(model.getRunningTime());

    Dimension d = new Dimension(gcPreferences.getWindowWidth(), gcPreferences.getWindowHeight());
    pane.setSize(d);
    pane.addNotify();
    pane.validate();

    pane.autoSetScaleFactor();

    final BufferedImage image = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_RGB);
    final Graphics2D graphics = image.createGraphics();
    graphics.setBackground(Color.WHITE);
    graphics.clearRect(0, 0, image.getWidth(), image.getHeight());

    pane.paint(graphics);

    ImageIO.write(image, "png", new File(chartFilePath));
  }
  public ModelChartImpl() {
    super();
    this.model = new GCModel(true);
    this.chart = new Chart();
    this.chart.setPreferredSize(new Dimension(0, 0));
    setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);

    // set scrolling speed
    horizontalScrollBar = getHorizontalScrollBar();
    horizontalScrollBar.setUnitIncrement(50);
    horizontalScrollBar.setBlockIncrement(500);

    // order of the renderers determines what is painted first and last
    // we start with what's painted last
    GridBagConstraints gridBagConstraints = new GridBagConstraints();
    gridBagConstraints.fill = GridBagConstraints.BOTH;
    gridBagConstraints.weightx = 2;
    gridBagConstraints.weighty = 2;
    gridBagConstraints.gridx = 1;
    gridBagConstraints.gridy = 1;

    initialMarkLevelRenderer = new InitialMarkLevelRenderer(this);
    chart.add(initialMarkLevelRenderer, gridBagConstraints);
    usedHeapRenderer = new UsedHeapRenderer(this);
    chart.add(usedHeapRenderer, gridBagConstraints);
    gcTimesRenderer = new GCTimesRenderer(this);
    chart.add(gcTimesRenderer, gridBagConstraints);
    fullGCLineRenderer = new FullGCLineRenderer(this);
    chart.add(fullGCLineRenderer, gridBagConstraints);
    gcRectanglesRenderer = new GCRectanglesRenderer(this);
    chart.add(gcRectanglesRenderer, gridBagConstraints);
    incLineRenderer = new IncLineRenderer(this);
    chart.add(incLineRenderer, gridBagConstraints);
    concurrentGcLineRenderer = new ConcurrentGcBegionEndRenderer(this);
    chart.add(concurrentGcLineRenderer, gridBagConstraints);
    totalTenuredRenderer = new TotalTenuredRenderer(this);
    chart.add(totalTenuredRenderer, gridBagConstraints);
    totalYoungRenderer = new TotalYoungRenderer(this);
    chart.add(totalYoungRenderer, gridBagConstraints);
    totalHeapRenderer = new TotalHeapRenderer(this);
    chart.add(totalHeapRenderer, gridBagConstraints);

    setViewportView(chart);
    // This would make scrolling slower, but eliminates flickering...
    // getViewport().setScrollMode(JViewport.BACKINGSTORE_SCROLL_MODE);

    JPanel rowHeaderPanel = new JPanel();
    GridBagLayout layout = new GridBagLayout();
    rowHeaderPanel.setLayout(layout);
    GridBagConstraints constraints = new GridBagConstraints();
    constraints.fill = GridBagConstraints.VERTICAL;
    constraints.weightx = 2;
    constraints.weighty = 1;
    constraints.gridheight = 2;
    constraints.gridx = 0;
    constraints.gridy = 1;
    this.memoryRuler = new Ruler(true, 0, model.getFootprint(), "K");
    this.pauseRuler = new Ruler(true, 0, model.getPause().getMax(), "s");
    layout.setConstraints(memoryRuler, constraints);
    rowHeaderPanel.add(memoryRuler);
    constraints.gridx = 1;
    layout.setConstraints(pauseRuler, constraints);
    rowHeaderPanel.add(pauseRuler);
    setRowHeaderView(rowHeaderPanel);
    setCorner(JScrollPane.UPPER_LEFT_CORNER, new JPanel());
    setCorner(JScrollPane.LOWER_LEFT_CORNER, new JPanel());

    DateFormat dateFormatter = new TimeFormat();
    this.timestampRuler = new Ruler(false, 0, model.getRunningTime(), "", dateFormatter);
    setColumnHeaderView(timestampRuler);

    getViewport()
        .addComponentListener(
            new ComponentListener() {
              public void componentResized(ComponentEvent e) {
                chart.setSize(chart.getPreferredSize());
                memoryRuler.setSize(
                    (int) memoryRuler.getPreferredSize().getWidth(), e.getComponent().getHeight());
                pauseRuler.setSize(
                    (int) pauseRuler.getPreferredSize().getWidth(), e.getComponent().getHeight());
                timestampRuler.setSize(
                    (int) chart.getPreferredSize().getWidth(),
                    (int) timestampRuler.getPreferredSize().getHeight());
              }

              public void componentMoved(ComponentEvent e) {}

              public void componentShown(ComponentEvent e) {}

              public void componentHidden(ComponentEvent e) {}
            });
    // timestamp menu
    final JPopupMenu popupMenu = new JPopupMenu();
    timeOffsetPanel = new TimeOffsetPanel(popupMenu);
    popupMenu.add(timeOffsetPanel);
    final JPopupMenu timestampRulerPopup = popupMenu;
    Action setOffsetAction =
        new AbstractAction() {
          public void actionPerformed(ActionEvent e) {
            if (timeOffsetPanel.isOffsetSet())
              timestampRuler.setOffset(timeOffsetPanel.getDate().getTime() / 1000);
            else timestampRuler.setOffset(0);
            timestampRuler.revalidate();
            timestampRuler.repaint();
          }
        };
    timeOffsetPanel.setOkAction(setOffsetAction);
    this.timestampRuler.addMouseListener(
        new MouseAdapter() {
          public void mousePressed(MouseEvent e) {
            maybePopup(e);
          }

          public void mouseReleased(MouseEvent e) {
            maybePopup(e);
          }

          public void maybePopup(MouseEvent e) {
            if (e.isPopupTrigger()) {
              if (timestampRuler.getOffset() != 0) {
                timeOffsetPanel.setDate(new Date((long) timestampRuler.getOffset() * 1000));
                timeOffsetPanel.setOffsetSet(true);
              } else {
                long suggestedStartDate = model.getLastModified();
                if (model.hasDateStamp()) {
                  suggestedStartDate = (long) (model.getFirstDateStamp().getTime());
                } else if (model.hasCorrectTimestamp()) {
                  suggestedStartDate -= (long) (model.getRunningTime() * 1000.0d);
                }
                timeOffsetPanel.setDate(new Date(suggestedStartDate));
                timeOffsetPanel.setOffsetSet(false);
              }
              timestampRulerPopup.show(e.getComponent(), e.getX(), e.getY());
              timeOffsetPanel.requestFocus();
            }
          }
        });
  }