2014-12-01 3 views
0

У меня есть JPictureBox распространяется от java.awt.Component см. Код здесь http://pastebin.com/SAJc6Sht. Но он работает только хорошо, когда нет растяжения изображения. Особенно для большой картины, это сильно замедляет программу. Как увеличить скорость рисования JPictureBox для большого изображения?Как увеличить скорость рисования JPictureBox для большого изображения?

@Override 
public void paint(Graphics g) {  
    super.paint(g); 

    int x = 0; 
    int y = 0; 
    int w = 0; 
    int h = 0; 
    if (image != null) { 
     switch (sizeMode) { 
      case AUTO_SIZE: 
      case NORMAL: 
       w = image.getWidth(); 
       h = image.getHeight(); 
       break; 
      case CENTER_IMAGE: 
       w = image.getWidth(); 
       h = image.getHeight(); 
       x = (getWidth() - w)/2; 
       y = (getHeight() - h)/2; 
       break; 
      case STRETCH_IMAGE: 
       w = getWidth(); 
       h = getHeight(); 
       break; 
      case ZOOM: 
       w = (int) Math.round(image.getWidth() * zoomFactor); 
       h = (int) Math.round(image.getHeight() * zoomFactor); 
       break; 
      case FIT_BOTH: 
       if (image.getWidth() > image.getHeight()) { 
        w = getWidth(); 
        h = (int) (w/getAR()); 

        if (h > getHeight()) { 
         h = getHeight(); 
         w = (int) (h * getAR()); 
        } 
       } else { 
        h = getHeight(); 
        w = (int) (h * getAR()); 

        if (w > getWidth()) { 
         w = getWidth(); 
         h = (int) (w/getAR()); 
        } 
       } 
       break; 
      case FIT_WIDTH: 
       w = getWidth(); 
       h = (int) (w/getAR()); 
       break; 
      case FIT_HEIGHT: 
       h = getHeight(); 
       w = (int) (h * getAR()); 
       break; 
     } 

     Graphics2D g2d = (Graphics2D) g; 
     g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); 
     g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); 
     g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
     g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); 
     g2d.drawImage(image, x, y, w, h, this); 
    } else if (errorIcon != null) { 
     w = errorIcon.getIconWidth(); 
     h = errorIcon.getIconHeight(); 
     x = (getWidth() - w)/2; 
     y = (getHeight() - h)/2; 
     errorIcon.paintIcon(this, g, x, y); 
    } 
} 
+2

Изменение 'g2d.drawImage (image, x, y, w, h, null);' to 'g2d.drawImage (изображение, x, y, w, h, this);' не повредит. Как насчет изменения размера изображения в разделе «Диспетчер событий»? – MadProgrammer

+0

Ничего не меняется. Я только изменяю его размер с помощью JSplitPane. –

+0

Чтобы лучше помочь, опубликуйте [MCVE] (http://stackoverflow.com/help/mcve) (минимальный полный проверяемый пример) или [SSCCE] (http://www.sscce.org/) (Short, Self Содержащийся, правильный пример). И я имею в виду публикацию здесь, а не ссылку на него. –

ответ

2

В принципе, вы хотите, чтобы загрузить от масштабирования изображения в фоновом потоке, масштабирование занимает много времени, и вы не хотите делать в контексте событий Диспетчерская тему.

В этом случае возникает еще несколько проблем. Вы не хотите масштабировать изображение, пока вам действительно не нужно, и вам действительно нужен только последний результат.

Вместо того чтобы пытаться масштабировать изображение при каждом изменении, вы можете настроить небольшой таймер повтора, который вы каждый раз перезагружаете, когда хотите внести изменения. Это упростит запросы с несколькими размерами до максимально возможного количества запросов. В этом примере используется javax.swing.Timer, установленный на короткую задержку в 125 миллисекунд. Таким образом, он будет ждать не менее 125 миллисекунд между запросами на изменение до фактического запуска обновления.

Далее используется ExecutorService, созданный с помощью одной нити. Это дает нам возможность «попытаться» отменить какие-либо ранее существовавшие операции, так как мы не хотим, чтобы результат был таким, и начинаем наш последний запрос.

Далее в действительной операции масштабирования используется двухступенчатая шкала, во-первых, она пытается выполнить быструю и низкую шкалу качества, которую можно быстро нанести на экран, а затем выполняет более медленный, высококачественный масштаб, который обновляется на некоторых время в будущем ...

import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.Image; 
import java.awt.Rectangle; 
import java.awt.RenderingHints; 
import java.awt.Transparency; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.KeyEvent; 
import java.awt.image.BufferedImage; 
import java.io.File; 
import java.io.IOException; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.concurrent.Callable; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.Future; 
import javax.imageio.ImageIO; 
import javax.swing.AbstractAction; 
import javax.swing.ActionMap; 
import javax.swing.InputMap; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 
import javax.swing.KeyStroke; 
import javax.swing.Scrollable; 
import javax.swing.SwingUtilities; 
import javax.swing.Timer; 
import javax.swing.UIManager; 
import javax.swing.UnsupportedLookAndFeelException; 

public class Test { 

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

    public Test() { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { 
        ex.printStackTrace(); 
       } 

       JFrame frame = new JFrame("Testing"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.add(new JScrollPane(new TestPane())); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 

    public class TestPane extends JPanel implements Scrollable { 

     private BufferedImage master; 
     private Image scaled; 
     private double zoom = 1d; 
     private ExecutorService service; 
     private List<Future> scaleTasks; 
     private final Timer zoomTimer; 

     public TestPane() { 
      scaleTasks = new ArrayList<>(5); 
      service = Executors.newSingleThreadExecutor(); 
      try { 
       master = ImageIO.read(new File("Some image some where")); 
       scaled = master; 
      } catch (IOException ex) { 
       ex.printStackTrace(); 
      } 
      zoomTimer = new Timer(125, new ActionListener() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        System.out.println("Update Zoom to " + getZoom()); 
        updateToZoomFactor(getZoom()); 
       } 
      }); 
      zoomTimer.setRepeats(false); 

      InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW); 
      ActionMap am = getActionMap(); 

      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "plus"); 
      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "minus"); 

      am.put("plus", new AbstractAction() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        double zoom = getZoom() + 0.1; 
        setZoom(zoom); 
       } 
      }); 
      am.put("minus", new AbstractAction() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        double zoom = getZoom() - 0.1; 
        setZoom(zoom); 
       } 
      }); 

     } 

     @Override 
     public Dimension getPreferredSize() { 
      return scaled == null 
          ? new Dimension(master.getWidth(), master.getHeight()) 
          : new Dimension(scaled.getWidth(this), scaled.getHeight(this)); 
     } 

     public BufferedImage getMaster() { 
      return master; 
     } 

     public void setZoom(double value) { 
      if (value < 0.1) { 
       value = 0.1; 
      } else if (value > 2) { 
       value = 2d; 
      } 
      if (value != zoom) { 
       zoom = value; 
       zoomTimer.restart(); 
      } 
     } 

     public double getZoom() { 
      return zoom; 
     } 

     @Override 
     protected void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      if (scaled != null) { 
       Graphics2D g2d = (Graphics2D) g.create(); 
       int x = (getWidth() - scaled.getWidth(this))/2; 
       int y = (getHeight() - scaled.getHeight(this))/2; 
       g2d.drawImage(scaled, x, y, this); 
       g2d.dispose(); 
      } 
     } 

     protected void setScaledResult(final Image image) { 
      SwingUtilities.invokeLater(new Runnable() { 
       @Override 
       public void run() { 
        scaled = image; 
        invalidate(); 
        revalidate(); 
        repaint(); 
       } 
      }); 
     } 

     protected void updateToZoomFactor(double zoom) { 
      Future[] tasks = scaleTasks.toArray(new Future[scaleTasks.size()]); 
      for (Future task : tasks) { 
       if (!task.isCancelled()) { 
        task.cancel(true); 
       } else { 
        scaleTasks.remove(task); 
       } 
      } 
      service.submit(new RescaleTask(zoom)); 
     } 

     @Override 
     public Dimension getPreferredScrollableViewportSize() { 
      return new Dimension(400, 400); 
     } 

     @Override 
     public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { 
      return 128; 
     } 

     @Override 
     public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { 
      return 128; 
     } 

     @Override 
     public boolean getScrollableTracksViewportWidth() { 
      return false; 
     } 

     @Override 
     public boolean getScrollableTracksViewportHeight() { 
      return false; 
     } 

     protected class RescaleTask implements Callable<Image> { 

      private double zoom; 

      protected RescaleTask(double zoom) { 
       this.zoom = zoom; 
      } 

      @Override 
      public Image call() throws Exception { 
       if (zoom == 1) { 
        scaled = getMaster(); 
       } else { 
        int width = (int) (getMaster().getWidth() * zoom); 
        int height = (int) (getMaster().getHeight() * zoom); 
        Image scaled = getMaster().getScaledInstance((int) width, (int) height, Image.SCALE_FAST); 
        if (!Thread.currentThread().isInterrupted()) { 
         setScaledResult(scaled); 

         if (zoom < 1) { 
          scaled = getScaledDownInstance(getMaster(), (int) width, (int) height); 
         } else { 
          scaled = getScaledUpInstance(getMaster(), (int) width, (int) height); 
         } 

         if (!Thread.currentThread().isInterrupted()) { 
          setScaledResult(scaled); 
         } else { 
          System.out.println("Was interrupted during quality scale"); 
         } 

        } else { 
         System.out.println("Was interrupted during fast scale"); 
        } 
       } 
       return scaled; 
      } 

      protected BufferedImage getScaledDownInstance(BufferedImage img, 
          int targetWidth, 
          int targetHeight) { 

       int type = (img.getTransparency() == Transparency.OPAQUE) 
           ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; 

       BufferedImage ret = (BufferedImage) img; 

       if (targetHeight > 0 || targetWidth > 0) { 

        int w = img.getWidth(); 
        int h = img.getHeight(); 

        do { 

         System.out.println(w + "x" + h + " -> " + targetWidth + "x" + targetHeight); 

         if (w > targetWidth) { 
          w /= 2; 
          if (w < targetWidth) { 
           w = targetWidth; 
          } 
         } 

         if (h > targetHeight) { 
          h /= 2; 
          if (h < targetHeight) { 
           h = targetHeight; 
          } 
         } 

         BufferedImage tmp = new BufferedImage(Math.max(w, 1), Math.max(h, 1), type); 
         Graphics2D g2 = tmp.createGraphics(); 
         g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); 
         g2.drawImage(ret, 0, 0, w, h, null); 
         g2.dispose(); 

         ret = tmp; 

        } while (w != targetWidth || h != targetHeight); 

       } else { 

        ret = new BufferedImage(1, 1, type); 

       } 

       return ret; 

      } 

      protected BufferedImage getScaledUpInstance(BufferedImage img, 
          int targetWidth, 
          int targetHeight) { 

       int type = BufferedImage.TYPE_INT_ARGB; 

       BufferedImage ret = (BufferedImage) img; 
       int w = img.getWidth(); 
       int h = img.getHeight(); 

       do { 

        if (w < targetWidth) { 
         w *= 2; 
         if (w > targetWidth) { 
          w = targetWidth; 
         } 
        } 

        if (h < targetHeight) { 
         h *= 2; 
         if (h > targetHeight) { 
          h = targetHeight; 
         } 
        } 

//   createCompatibleImage(w, h, type) 
        BufferedImage tmp = new BufferedImage(w, h, type); 
        Graphics2D g2 = tmp.createGraphics(); 
        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); 
        g2.drawImage(ret, 0, 0, w, h, null); 
        g2.dispose(); 

        ret = tmp; 
        tmp = null; 

       } while (w != targetWidth || h != targetHeight); 

       return ret; 

      } 

     } 

    } 

} 

нб: Это чуть больше убивать, но демонстрирует некоторые ключевые идеи

Одна из других вещей, которые могли бы помочь, чтобы преобразовать изображение в совместимом цвете модель для GraphicsDevice, например ...

  master = ImageIO.read(new File("Some image some where")); 
      GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); 
      GraphicsConfiguration gc = gd.getDefaultConfiguration(); 
      BufferedImage compatible = gc.createCompatibleImage(master.getWidth(), master.getHeight(), Transparency.TRANSLUCENT); 
      Graphics2D g2d = compatiable.createGraphics(); 
      g2d.drawImage(master, 0, 0, this); 
      g2d.dispose(); 
      master = compatible; 
+0

Я видел ваш код (я этого не понимаю, совсем нет: D), затем я пытаюсь внести некоторые изменения: нарисовал его в отдельном потоке и задал буфер после успешного рисования. Это заставляет мою программу работать быстрее, но то, что я видел на экране, было не очень приятным, но приемлемым. Спасибо. http://pastebin.com/XtCpyv2v –

+0

Вот почему я делаю две масштабные операции: один быстрый (и ужасный), один медленный и хороший. – MadProgrammer

+0

Не перемасштабируйте изображение каждый раз, когда вызывается «paint», можно нарисовать краску по многим причинам, не все из них, потому что компонент был изменен. Обновите изображение при изменении коэффициента масштабирования, но опять же, 'setZoomFactor' можно вызвать быстро, лучше использовать что-то вроде примера Timer, чтобы уменьшить количество попыток и масштабировать изображение. Не используйте шкалу одного шага, это действительно очень плохо. См. Http://stackoverflow.com/questions/14115950/quality-of-image-after-resize-very-low-java/14116752#14116752 для получения более подробной информации – MadProgrammer

Смежные вопросы