Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Substance] OutOfMemory on JSlider with large model range #188

Closed
berlinsaint opened this issue Aug 7, 2019 · 3 comments

Comments

@berlinsaint
Copy link

commented Aug 7, 2019

Version of Radiance (current development is 2.5-SNAPSHOT)

radiance substance 2.0.1 substance 7.0+

Sub-project (Neon, Trident, Substance, Flamingo, ...)

Substance

Version of Java (current minimum is 9)

java9 and java9

Version of OS

windows10

The issue you're experiencing (expected vs actual, screenshot, stack trace etc)

here is my code in java9;

package com.itesttech.skin;

import com.itesttech.gui.util.ReflectionAction;
import com.itesttech.util.FileUtils;
import org.pushingpixels.substance.api.skin.SubstanceTwilightLookAndFeel;

import java.awt.*;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.image.*;
import java.util.Hashtable;

public class Walkthrough extends JFrame {
    public static class SegmentationSizeSlider {
        private final double scale;
        private final JSlider slider;

        public SegmentationSizeSlider(double scale, int orientation) {
            super();
            slider = new JSlider(orientation);
            this.scale = scale;
        }

        private int getScaledValue(long val) {
            long scaledVal = (long) (val / scale);
            if (scaledVal > Integer.MAX_VALUE)
                throw new AssertionError();
            return (int) scaledVal;
        }

        private long getActualValue(int val) {
            return (long) ((long) val * scale);
        }

        public long getValue() {
            return getActualValue(slider.getValue());
        }

        public void setValue(long n) {
            slider.setValue(getScaledValue(n));
        }

        public long getMinimum() {
            return getActualValue(slider.getMinimum());
        }

        public void setMinimum(long minimum) {
            slider.setMinimum(getScaledValue(minimum));
        }

        public long getMaximum() {
            return getActualValue(slider.getMaximum());
        }

        public void setMaximum(long maximum) {
            slider.setMaximum(getScaledValue(maximum));
        }

        public void setExtent(int extent) {
            slider.setExtent(Math.max(getScaledValue(extent) / extent, 1));
        }

        public void setMajorTickSpacing(long n) {
            slider.setMajorTickSpacing(getScaledValue(n));
        }

        public void setPaintTicks(boolean b) {
            slider.setPaintTicks(b);
        }

        public JComponent getComponent() {
            return slider;
        }

        public void addChangeListener(ChangeListener changeListener) {
            slider.addChangeListener(changeListener);
        }

        public void showLabel(boolean b, boolean si) {
            if (b) {
                Hashtable<Integer, JLabel> labelTable = new Hashtable<Integer, JLabel>();
                labelTable.put(new Integer(slider.getMinimum()), new JLabel(FileUtils.humanReadableByteCount(this.getMinimum(), si)));
                int scaledRange = Math.abs(slider.getMaximum() - slider.getMinimum());
                long range = Math.abs(this.getMaximum() - this.getMinimum());
                labelTable.put(new Integer(scaledRange / 4), new JLabel(FileUtils.humanReadableByteCount(range / 4, si)));
                labelTable.put(new Integer(scaledRange / 2), new JLabel(FileUtils.humanReadableByteCount(range / 2, si)));
                labelTable.put(new Integer(scaledRange / 4 * 3), new JLabel(FileUtils.humanReadableByteCount(range / 4 * 3, si)));
                labelTable.put(new Integer(slider.getMaximum()), new JLabel(FileUtils.humanReadableByteCount(this.getMaximum(), si)));
                slider.setLabelTable(labelTable);
                slider.setPaintLabels(true);
            }
            slider.setPaintLabels(b);
        }

        public boolean getValueIsAdjusting() {
            return slider.getValueIsAdjusting();
        }
    }

    private SegmentationSizeSlider segmentationSizeSlider;
    private final JTextField preferredRegionTf = new JTextField();
    private Action okAction = null;
    private Action cancelAction = null;
    private final JTextField segmentationSizeTf = new JTextField();
    private JCheckBox hideSegmentsContainer;
    private JButton okButton = null;
    private JButton cancelButton = null;
    private boolean si = false;

    private final int unit = 10485760; // 10MB

    public Walkthrough() {
        super("Sample app");
        double scale = (5368709120L > Integer.MAX_VALUE) ? (5368709120L / (double) Integer.MAX_VALUE) : (1.0);
        hideSegmentsContainer = new JCheckBox("Hide_segments_container_check_box");
        hideSegmentsContainer.setSelected(false);
        initMenuActions();
        segmentationSizeSlider = new SegmentationSizeSlider(scale, JSlider.HORIZONTAL);
        segmentationSizeSlider.setMinimum(104857600);
        segmentationSizeSlider.setMaximum(5368709120L);
        segmentationSizeSlider.setValue(222);
        String preferredRegion = "xx";
        preferredRegionTf.setText((preferredRegion == null) ? ("") : (preferredRegion));

        initSegmentationSizeSelection();

        Box outer = Box.createVerticalBox();
        Box box = Box.createVerticalBox();
        Box boxSegmentationSize = Box.createVerticalBox();
        boxSegmentationSize.setBorder(BorderFactory.createEmptyBorder(10, 6, 10, 6));

        boxSegmentationSize.setBorder(BorderFactory.createTitledBorder("Segmentation_Size"));

        setMinPrefMaxWidth(segmentationSizeSlider.getComponent(), 400, Integer.MAX_VALUE, 100);
        boxSegmentationSize.add(segmentationSizeSlider.getComponent());
        boxSegmentationSize.add(segmentationSizeTf);


        Box boxHideSegmentsContainer = Box.createHorizontalBox();
        boxHideSegmentsContainer.setBorder(BorderFactory.createEmptyBorder(20, 6, 20, 6));
        boxHideSegmentsContainer.add(hideSegmentsContainer);

        JPanel buttons = new JPanel(new FlowLayout(FlowLayout.RIGHT));

        buttons.setBorder(BorderFactory.createEtchedBorder());
        buttons.add(okButton);
        buttons.add(cancelButton);

        box.add(boxSegmentationSize);
        box.add(boxHideSegmentsContainer);

        Box boxPreferredRegion = Box.createHorizontalBox();
        boxPreferredRegion.setBorder(BorderFactory.createTitledBorder("Preferred_Region"));
        boxPreferredRegion.add(new JLabel("Region"));
        boxPreferredRegion.add(Box.createHorizontalStrut(8));
        boxPreferredRegion.add(preferredRegionTf);


        box.add(Box.createVerticalStrut(15));
        box.add(boxPreferredRegion);

        outer.add(box);
        this.add(outer, BorderLayout.NORTH);
        this.add(buttons, BorderLayout.SOUTH);

        this.setIconImage(new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR));
        this.setSize(new Dimension(450, 200));
        this.setLocationRelativeTo(null);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void onOk() {


        String preferredRegion = preferredRegionTf.getText();
        if (preferredRegion == null || preferredRegion.trim().isEmpty()) {
            preferredRegion = null;
        }

    }

    public void onShow() {

    }


    public void onCancel() {
    }


    private JComponent setMinPrefMaxWidth(JComponent comp, int minPrefW, int maxW, int prefHeight) {
        if (comp == null)
            return null;
        comp.setMinimumSize(new Dimension(minPrefW, 0));
        comp.setPreferredSize(new Dimension(minPrefW, prefHeight));
        comp.setMaximumSize(new Dimension(maxW, Integer.MAX_VALUE));
        return comp;
    }

    private void initSegmentationSizeSelection() {
        if (segmentationSizeSlider == null || segmentationSizeTf == null)
            throw new AssertionError();

        segmentationSizeTf.setEditable(false);
        segmentationSizeTf.setAlignmentX(CENTER_ALIGNMENT);
        segmentationSizeTf.setBorder(null);
        if (segmentationSizeSlider.getComponent().getFont() != null)
            segmentationSizeTf.setFont(segmentationSizeSlider.getComponent().getFont());

        long range = 5368709120L - 104857600;
        segmentationSizeSlider.setExtent(unit);
        segmentationSizeSlider.setMajorTickSpacing(range / 8);
        segmentationSizeSlider.setPaintTicks(true);
        setMinPrefMaxWidth(segmentationSizeSlider.getComponent(), 400, Integer.MAX_VALUE, 100);
        // Labels
        segmentationSizeSlider.showLabel(true, si);

        // Events
        segmentationSizeSlider.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                if (!segmentationSizeSlider.getValueIsAdjusting()) {
                    long size = segmentationSizeSlider.getValue();
                    segmentationSizeTf.setText("dddd");
                }
            }
        });
    }

    private void initMenuActions() {
        okAction = new ReflectionAction<Walkthrough>("Ok", this, "onOk");
        cancelAction = new ReflectionAction<Walkthrough>("Cancel", this, "onCancel");

        okButton = new JButton(okAction);
        cancelButton = new JButton(cancelAction);
    }

    public static void main(String[] args) {
        JFrame.setDefaultLookAndFeelDecorated(true);

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                try {
                    //UIManager.setLookAndFeel(new SubstanceTwilightLookAndFeel());
                    Walkthrough w = new Walkthrough();
                    w.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

when not use the substance skin it will like this;
image

when uncomment the line 253, and run , the java will oom, and crash without any messages...

however i have met this error in older version substance in java8 ,
and in java9 radiance has the same problem. any help will be appreciated. thanks

@kirill-grouchnikov kirill-grouchnikov changed the title substance crash when use jslider [Substance] OutOfMemory on JSlider with large model range Aug 7, 2019

@kirill-grouchnikov

This comment has been minimized.

Copy link
Owner

commented Aug 7, 2019

This is in SubstanceSliderUI.paintTicks. It combines X/Ys of minor and major ticks to paint them in a single call. In this particular case there is an integer overflow which causes the boundary checks (value < model.max) to never be reached.

Will fix in 2.5-SNAPSHOT.

@berlinsaint

This comment has been minimized.

Copy link
Author

commented Aug 8, 2019

@kirill-grouchnikov thanks a lot。 BTW, can you fix it in the oldler version (7.x),or provide an java8 version of substance because our program wrote with java8~ 。。。

@kirill-grouchnikov

This comment has been minimized.

Copy link
Owner

commented Aug 8, 2019

The official fix will only be available in the 2.5-SNAPSHOT version. If your environment requires supporting Java 8, you'll need to maintain your own copy of Substance / Radiance with that fix applied locally.

kirill-grouchnikov added a commit that referenced this issue Aug 9, 2019
A pinchful of fixes
* Gradle versions plugin -> 0.22.0
* Update screenshots with the latest binaries
* Add Graphite Sunset and tweak Sunset light colors to remove yellow - #189
* Fix infinite loop on drawing ticks of JSliders with large model ranges - #188
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.