Wie kann man eine Bildlupe als Vergößerung eines Bildausschnittes realisieren?

Die Klasse BufferedImage enthält eine Methode getSubimage() zum Kopieren von Bildausschnitten. Sie können dann auf einem JPanel angezeigt werden.

Das Programm erzeugt ein Fenster mit zwei nebeneinander liegenden zentralen Bildflächen und einem JSlider zum Einstellen des Vergrößerungsmaßstabs. Links werden das Originalbild und rechts ein vergrößerter Ausschnitt dargestellt. Der Ausschnitt zeigt den Bereich um die Cursorposition, wenn dieser über das linke Bild bewegt wird.

Im Konstruktor werden nacheinander das Bild geladen, ein initialer Ausschnitt kopiert [29] und schließlich das GUI erzeugt [30]. Nach dem Laden des Bildes wird in der Methode copyArea() ein Ausschnitt erzeugt. Hierzu werden in einem try-catch-Block sichergestellt, dass sich der zu kopierende Ausschnitt innerhalb des Bildbereiches liegt und der Skalierungsfaktor größer 1 ist. Ist er gleich 1, so wird das Originalbild unskaliert angezeigt [71]. Der Faktor ist in der privaten Variablen scale abgelegt [21] und nicht als Konstante definiert, um ihn durch den Slider einstellen zu können.

Die x- und y-Koordinaten der linken oberen Ausschnittecke werden aus der als Methodenparameter übergebenen Cursorposition und dem Skalierungsfaktor berechnet [75]. Das in einer Instanzvariablen gespeicherte kopierte BufferedImage wird durch die Methode getSubimage() erzeugt. Sie wird auf dem BufferedImage-Objekt des Originals aufgerufen und bekommt die Koordinaten der linken oberen Ecke, sowie Breite und Höhe des Ausschnitts als Parameter übergeben.

Nach Beenden der Methode sind das Originalbild und die Kopie jeweils in einer Variablen [22] gespeichert und stehen zur Anzeige zur Verfügung.

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.image.BufferedImage;
import java.awt.image.RasterFormatException;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;

public class Bildlupe {

    private static final String PATH = "/img/Norton_Manx_200.png";
    private static final String APP_NAME = "Bildlupe";
    private int scale = 2;
    private BufferedImage bi, copy;

    public Bildlupe() {
        if (scale <= 0) {
            System.out.println("Skalierungsfaktor muss > 0 sein.");
            System.exit(1);
        }
        loadAndCopyImage(PATH);
        createGUI();
    }

    private void createGUI() {
        ImagePanel sourcePanel = new ImagePanel(bi.getWidth(), bi.getHeight(), bi);
        ImagePanel copyPanel = new ImagePanel(bi.getWidth(), bi.getHeight(), copy);
        sourcePanel.addMouseMotionListener(new MouseMotionAdapter() {
            public void mouseMoved(MouseEvent e) {
                scaleArea(copyPanel, e.getX(), e.getY());
            }
        });
        JSlider slider = new JSlider(1, 10, 2);
        slider.addChangeListener(e -> {
            scale = slider.getValue();
            slider.setToolTipText("1 : " + scale);
            copyPanel.repaint();
        });
        JFrame frame = new JFrame(APP_NAME);
        frame.setLayout(new BorderLayout());
        JPanel imgPanel = new JPanel(new FlowLayout());
        imgPanel.add(sourcePanel);
        imgPanel.add(copyPanel);
        frame.add(imgPanel, BorderLayout.CENTER);
        frame.add(slider, BorderLayout.PAGE_END);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private void loadAndCopyImage(String path) {
        try {
            bi = ImageIO.read(getClass().getResource(path));
        } catch (IOException e) {
            System.err.println("Couldn't load image");
        }
        copyArea(0, 0);    
    }

    private void copyArea(int x, int y) {
        try {
            if (scale == 1) {
                copy = bi;
                return;
            }
            x = x == 0 ? 0 : x - bi.getWidth() / (scale * 2);
            y = y == 0 ? 0 : y - bi.getHeight() / (scale * 2);
            copy = bi.getSubimage(x, y, bi.getWidth() / scale, bi.getHeight() / scale);
        } catch (RasterFormatException rfe) {
            return;
        }
    }

    private void scaleArea(ImagePanel panel, int x, int y) {
        copyArea(x, y);
        panel.setBi(copy);
        panel.repaint();
    }

    class ImagePanel extends JPanel {

        private int width, height;
        private BufferedImage img;

        public ImagePanel(int width, int height, BufferedImage img) {
            this.width = width;
            this.height = height;
            this.img = img;
            this.setPreferredSize(new Dimension(width, height));
        }

        private void setBi(BufferedImage img) {
            this.img = img;
        }

        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawImage(img, 0, 0, width, height, null);
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new Bildlupe());
    }
}

Hierzu wird in createGUI() das Userinterface erzeugt. Es besteht aus einem JFrame mit BorderLayout. Zentral werden die Bildflächen in einem gesonderten JPanel mit FlowLayout [49], unten ein JSlider [53] geladen.

Die Bildflächen sind in einer inneren Klasse [89] von JPanel abgeleitet, um paintComponent() überschreiben zu können. Hier wird durch die Graphics-Methode drawImage() das Bild gezeichnet. Die Methode bekommt wiederum den linken oberen Punkt als x- und y-Koordinaten und Breite und Höhe übergeben. Der ebenfalls als Parameter verlangte ImageObserver wird nicht benötigt und kann null gesetzt werden.

Das Panel mit dem Original wird bei einem MouseMotionListener angemeldet. So kann beim Überfahren der Fläche mit der Maus die Methode scaleArea() kontinuierlich aufgerufen werden [83]. Sie kopiert den jeweiligen Ausschnitt abhängig von der Cursonposition und dem Skalierungsfaktor durch den internen Aufruf von copyArea() [84], übergibt diese Kopie an die Bildfläche [85] und zeichnet sie neu [86].

Im unteren Fensterbereich ist eine JSlider vorhanden [53], der zur Vorwahl des Skalierungsfaktors dient. Hierzu ist er bei einem ChangeListener angemeldet [42], der die Sliderposition an die Variable scale übergibt [43]. Durch Neuzeichnen der zugehörigen Bildfläche [45] wird dann der neu berechnete Bildausschnitt angezeigt.

Vergrößerter Bildausschnitt

Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.