Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8017247

[macosx] Bi-cubic and Bi-linear rendering are slow and BiCubic renders twice.

    Details

    • Subcomponent:
      2d
    • OS:
      os_x

      Description

      FULL PRODUCT VERSION :
      java version " 1.7.0_15 "
      Java(TM) SE Runtime Environment (build 1.7.0_15-b03)
      Java HotSpot(TM) 64-Bit Server VM (build 23.7-b01, mixed mode)


      ADDITIONAL OS VERSION INFORMATION :
      Darwin slytherin 11.4.2 Darwin Kernel Version 11.4.2: Thu Aug 23 16:25:48 PDT 2012; root:xnu-1699.32.7~1/RELEASE_X86_64 x86_64

      (Mac OSX 10.7.5)


      EXTRA RELEVANT SYSTEM CONFIGURATION :
      Mac Pro 2.66 GHz Quad-Core Intel Xeon with 3 GB 1066 Mhz DDR3 memory. (I don't know if this is relevant.)

      A DESCRIPTION OF THE PROBLEM :
      Rendering an image with RenderingHints.KEY_INTERPOLATION set to
      VALUE_INTERPOLATION_BICUBIC or VALUE_INTERPOLATION_BILINEAR causes
      rendering to run very slowly, and renders twice when using
      VALUE_INTERPOLATION_BICUBIC.


      REGRESSION. Last worked in version 6u45

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        To maximize the effect, run this on the largest screen you can feasibly
      use. Run the enclosed test case, which displays a test image in a
      window. The repaint button lets you repaint the test image, and the
      three radio buttons let you choose which type of interpolation to use
      when repainting. Hit the repaint button to repaint using the default
      nearest-neighbor algorithm. Then choose the Bi-linear interpolation
      and hit repaint again. Observe the repainting speed. (The quality of
      the black circle improves when you switch to Bi-linear painting, which
      is how you can tell the painting is complete.) Then try this again
      after choosing BiCubic interpolation.
      Do all of this under JRE 1.6 and 1.7, and compare the results


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      For all three interpolation modes, painting of the image should be fast, and should not flicker.

      ACTUAL -
      I ran this on an Apple Cinema display, with the resolution set to 1920 x 1200.

      Under JRE 1.6, I get the expected result. All three modes paint nearly
      instantaneously. Under JRE 1.7, both Bi-linear and Bi-cubic paint
      slowly. Bi-linear takes about 3 seconds, and Bi-cubic paints the image
      twice, and takes about 5 seconds for both paints.


      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.awt.BasicStroke;
      import java.awt.BorderLayout;
      import java.awt.Canvas;
      import java.awt.Color;
      import java.awt.Frame;
      import java.awt.Graphics;
      import java.awt.Graphics2D;
      import java.awt.GridLayout;
      import java.awt.Image;
      import java.awt.RenderingHints;
      import java.awt.Shape;
      import java.awt.Stroke;
      import java.awt.event.ActionEvent;
      import java.awt.event.ActionListener;
      import java.awt.event.ComponentAdapter;
      import java.awt.event.ComponentEvent;
      import java.awt.geom.AffineTransform;
      import java.awt.geom.Arc2D;
      import java.awt.image.BufferedImage;
      import java.awt.image.FilteredImageSource;
      import java.awt.image.ImageFilter;
      import java.awt.image.RGBImageFilter;
      import java.util.HashMap;
      import java.util.Map;
      import javax.swing.ButtonGroup;
      import javax.swing.JButton;
      import javax.swing.JComponent;
      import javax.swing.JFrame;
      import javax.swing.JPanel;
      import javax.swing.JRadioButton;
      import javax.swing.JToolBar;
      import javax.swing.SwingConstants;
      import javax.swing.WindowConstants;

      @SuppressWarnings({ " HardCodedStringLiteral " , " MagicNumber " })
      public class BicubicFlickerBug6 extends JPanel {
      private static final int RADIUS = 300;
      private static final int DIAMETER = RADIUS * 2;
      private static final int MARGIN = 10;
      private static final int arcCount = 36;
      private static final int radiiCount = 8;
      private static final Shape outerCircle = new Arc2D.Double(0, 0, DIAMETER, DIAMETER, 0.0, 360.0, Arc2D.OPEN);
      private static final Arc2D colorArc =
      new Arc2D.Double(-RADIUS, -RADIUS, DIAMETER, DIAMETER, 0.0, -360.0 / arcCount, Arc2D.PIE);

      private final ScaledImage scalingImage = new ScaledImage(null);
      private final JComponent photoView;
      private final Image loadedImage = makeTestImage();

      private Image makeTestImage() {

      int size = DIAMETER + (2 * MARGIN);

      BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_4BYTE_ABGR);
      Graphics2D g2 = (Graphics2D) image.getGraphics();
      setRenderHints(g2);
      final Stroke s2 = new BasicStroke(2.0f);
      g2.setStroke(s2);
      AffineTransform savedTransform = g2.getTransform();

      // move to the center
      g2.setTransform(savedTransform);
      for (int saturation = 0; saturation < radiiCount; ++saturation) {
      g2.translate(MARGIN + RADIUS, MARGIN + RADIUS);
      float sat = (radiiCount - saturation) / (float) radiiCount;
      g2.scale(sat, sat);
      g2.rotate(-Math.PI / arcCount);
      for (int ii = 0; ii < arcCount; ++ii) {
      g2.setColor(Color.getHSBColor((ii / (float) arcCount), sat, 1.0f));
      g2.fill(colorArc);
      g2.rotate((Math.PI * 2) / arcCount);
      }
      g2.setTransform(savedTransform);
      }
      g2.translate(MARGIN, MARGIN);
      g2.setColor(Color.black);
      g2.draw(outerCircle);
      g2.setTransform(savedTransform);
      g2.dispose();
      return image;
      }

      private static final Map<RenderingHints.Key, Object> hintMap = makeRenderingHints();

      private static Map<RenderingHints.Key, Object> makeRenderingHints() {
      Map<RenderingHints.Key, Object> map = new HashMap<RenderingHints.Key, Object>();
      map.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
      map.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      map.put(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
      return map;
      }

      public static void main(String[] args) {
      makeFrame();
      }

      private static void makeFrame() {
      final JFrame mainFrame = new JFrame( " BicubicFlickerBug " );
      mainFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
      mainFrame.add(new BicubicFlickerBug6(), BorderLayout.CENTER);
      mainFrame.setExtendedState(Frame.MAXIMIZED_BOTH);
      mainFrame.setVisible(true);
      }

      public BicubicFlickerBug6() {
      super(new BorderLayout());

      JToolBar toolBar = new JToolBar();
      toolBar.setOrientation(SwingConstants.VERTICAL);
      add(toolBar, BorderLayout.WEST);
      toolBar.setFloatable(false);

      JButton repaintButton = new JButton( " RePaint " );
      toolBar.add(repaintButton);
      repaintButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
      doRepaint();
      }
      });

      ButtonGroup bGroup = new ButtonGroup();

      JRadioButton biCubicButton = new JRadioButton( " BiCubic " );
      bGroup.add(biCubicButton);
      JRadioButton biLinearButton = new JRadioButton( " BiLinear " );
      bGroup.add(biLinearButton);
      JRadioButton nearestNeighborButton = new JRadioButton( " Nearest Neighbor " );
      bGroup.add(nearestNeighborButton);
      nearestNeighborButton.setSelected(true);

      biCubicButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent e) {
      hintMap.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
      }
      });
      biLinearButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent e) {
      hintMap.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
      }
      });
      nearestNeighborButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent e) {
      hintMap.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
      }
      });

      toolBar.add(nearestNeighborButton);
      toolBar.add(biLinearButton);
      toolBar.add(biCubicButton);

      photoView = createPhotoView();
      add(photoView, BorderLayout.CENTER);
      }

      private void doRepaint() {
      //photoView.removeAll();
      Image filteredImage = doFilter();
      scalingImage.setImage(filteredImage);
      //photoView.add(scalingImage);
      scalingImage.repaint();
      }

      private JComponent createPhotoView() {
      scalingImage.setImage(loadedImage);
      JPanel photoView = new JPanel(new GridLayout(0, 1));
      photoView.add(scalingImage);
      return photoView;
      }

      private class ScaledImage extends Canvas {
      private Image image;

      public ScaledImage(Image image) {
      this.image = image;

      addComponentListener(new ComponentAdapter() {
      @Override
      public void componentResized(final ComponentEvent e) {
      BicubicFlickerBug6.this.revalidate();
      }
      });
      }

      public void setImage(Image image) {
      this.image = image;
      repaint();
      }

      @Override
      public void paint(final Graphics g) {

      if (image == null) { return; }
      int displayWidth = getWidth();
      int imageWidth = image.getWidth(this);
      double widthScale = ((double) displayWidth) / imageWidth;
      int displayHeight = getHeight();
      int imageHeight = image.getHeight(this);
      double heightScale = ((double) displayHeight) / imageHeight;

      boolean useWidth;
      double scale;
      if (widthScale < heightScale) {
      useWidth = true;
      scale = widthScale;
      } else {
      useWidth = false;
      scale = heightScale;
      }
      int scaledWidth = (int) Math.round(scale * imageWidth);
      int scaledHeight = (int) Math.round(scale * imageHeight);

      Graphics2D g2 = (Graphics2D) g;
      setRenderHints(g2);
      if (useWidth) {
      long ht = Math.round(scaledHeight);
      int deltaY = (int) ((displayHeight - ht) / 2);
      g2.drawImage(image, 0, deltaY, scaledWidth, scaledHeight, this);
      } else {
      long wd = Math.round(scaledWidth);
      int deltaX = (int) ((displayWidth - wd) / 2);
      g2.drawImage(image, deltaX, 0, scaledWidth, scaledHeight, this);
      }
      }

      }

      private Image doFilter() {
      ImageFilter filter = new NullFilter();
      FilteredImageSource imageSource = new FilteredImageSource(loadedImage.getSource(), filter);
      return photoView.createImage(imageSource);
      }


      private static void setRenderHints(Graphics2D g2) {
      g2.setRenderingHints(hintMap);
      }

      private static class NullFilter extends RGBImageFilter {

      public NullFilter() { }

      @Override
      public int filterRGB(final int x, final int y, final int rgb) {
      return rgb;
      }
      }

      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      The only workaround is to use JRE 1.6

        Attachments

        1. BicubicFlickerBug6.java
          9 kB
        2. Comparison.txt
          9 kB
        3. glStatistics-with-Canvas.png
          glStatistics-with-Canvas.png
          198 kB
        4. glStatistics-with-jComponent.png
          glStatistics-with-jComponent.png
          189 kB
        5. glTrace-with-Canvas.png
          glTrace-with-Canvas.png
          654 kB
        6. glTrace-with-JComponent.png
          glTrace-with-JComponent.png
          687 kB

          Issue Links

            Activity

              People

              • Assignee:
                serb Sergey Bylokhov
                Reporter:
                webbuggrp Webbug Group
              • Votes:
                0 Vote for this issue
                Watchers:
                5 Start watching this issue

                Dates

                • Created:
                  Updated: