All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.jakubec.view.ViewPanel Maven / Gradle / Ivy

/*
 * Copyright 2018 Michael Jakubec
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.jakubec.view;


import net.jakubec.view.dia.Diapresentation;
import net.jakubec.view.filedrop.FileDrop;
import net.jakubec.view.listener.ImageDisplayListener;
import net.jakubec.view.listener.ViewNavigationListener;


import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;

public class ViewPanel extends JPanel implements BasicPanel, AdjustmentListener, MouseListener,
		MouseMotionListener {



	private class ImagePainter extends JPanel {

		@Override
		public void paintComponent(final Graphics g) {
			if (img.getWidth() < 2) {
				drawImage(origin);
			}
			g.fillRect(0, 0, getWidth(), getHeight());
			g.drawImage(img, ((getWidth() - img.getWidth()) / 2),
					((getHeight() - img.getHeight()) / 2), null);
		}
	}

	/**
	 * Container for the
	 */
	private final Container cp;
	/**
	 * The factor of the image which is scaled
	 */
	private double factor;
	/**
	 * Horizontal ScrollBar
	 */
	private final JScrollBar hBar;
	/**
	 * vertical ScrollBar
	 */
	private final JScrollBar vBar;
	/**
	 * The old MouseEvent
	 */
	private MouseEvent oldEvent;
	/**
	 * The shown image
	 */
	private BufferedImage img = new BufferedImage(1, 1, 1);
	/**
	 * Class for image painting
	 */
	private final ImagePainter imp;
	/**
	 * The original image
	 */
	private BufferedImage origin = null;
	/**
	 * The height of the Screen
	 */
	private int sHeight;
	/**
	 * The width of the Screen
	 */
	private int sWidth;
	/**
	 * The y position of the middle point of the view on the Photo
	 */
	private int xmiddle = 0;
	/**
	 * The y position of the middle point of the view on the Photo
	 */
	private int ymiddle = 0;

	private final Refresher refresher = new Refresher();

	/**
	 * the current file object that is displayed
	 */
	private File currentImage;

	private boolean showScrollbars = true;

	/**
	 * Constructor for a new ViewPanel without the default toolbar.
	 */
	public ViewPanel() {
		this(false);
	}

	public ViewPanel(boolean showToolbar) {

		this.setLayout(new BorderLayout());

		cp = this;
		cp.setBackground(Color.black);


		addMouseListener(this);
		addMouseMotionListener(this);
		vBar = new JScrollBar(JScrollBar.VERTICAL, 0, 0, 0, 0);
		hBar = new JScrollBar(JScrollBar.HORIZONTAL, 0, 0, 0, 0);
		vBar.addAdjustmentListener(this);
		hBar.addAdjustmentListener(this);
		cp.add(vBar, BorderLayout.EAST);
		cp.add(hBar, BorderLayout.SOUTH);
		imp = new ImagePainter();
		cp.add(imp, BorderLayout.CENTER);
		if (showToolbar) {
			cp.add(new ViewPanel.ToolBarBuilder().buildMenuBar(new ViewNavigationListener(this)), BorderLayout.NORTH);
		}
		addComponentListener(new ComponentAdapter() {
			@Override
			public void componentResized(final ComponentEvent e) {
				calcZoom(true);
			}
		});
		addMouseWheelListener(new MouseAdapter() {
			@Override
			public void mouseWheelMoved(final MouseWheelEvent e) {
				int move = e.getWheelRotation();
				if (move < 0) {
					zoomp();
				} else if (move > 0) {
					zoomm();
				}
			}
		});
		hBar.setVisible(false);
		vBar.setVisible(false);
		new FileDrop(this, files -> {
			if (files.length > 0) {
				openImage(files[0]);
			}
		});
	}

	@Override
	public void adjustmentValueChanged(final AdjustmentEvent e) {
		calcZoom(true);
	}

	/**
	 * Specifies whether the scrollbars should be visible in case the image is larger than the screen or not. Scrollbars are only displayed if the image is larger than the screen. If the image is smaller than the screen the scrollbars are never shown, regardless of whether the
	 * {@link #isScrollBarsVisible()} returns true or not.
	 * @param show if true scrollbars are shown when needed otherwise not.
	 */
	public void setScrollBarsVisible(boolean show){
		this.showScrollbars = show;
		hBar.setVisible(show);
		vBar.setVisible(show);
	}

	/**
	 * Determines whether the scrollbars get shown if needed or not
	 * @return true if scrollbars get shown, false otherwise
	 */
	public boolean isScrollBarsVisible() {
		return showScrollbars;
	}

	/**
	 * calculates the image with the current zoom factor
	 */
	private void calcZoom(boolean fast) {
		sWidth = imp.getWidth();
		sHeight = imp.getHeight();
		if (sWidth == 0 || sHeight == 0 || origin == null) return;

		for (ImageDisplayListener l:listenerList.getListeners(ImageDisplayListener.class)){
			l.zoomLevelChanged(factor);
		}

		int originHeight = origin.getHeight();
		int originWidth = origin.getWidth();


		double pWidth = originWidth; // The width of the part of the Original
		// image that can be shown on the screen
		double pHeight = originHeight; // The height of the part of the Original
		// image that can be shown on the screen

		double iHeight = originHeight * factor; // The height of the drawn Image
		double iWidth = originWidth * factor; // The width of the drawn Image
		if (iWidth > sWidth) {
			pWidth = sWidth / iWidth * originWidth;
			iWidth = sWidth;
			hBar.setMinimum((int) -((originWidth - pWidth) / 2));
			hBar.setMaximum((int) ((originWidth - pWidth) / 2));
			hBar.setVisible(showScrollbars);
		} else {
			hBar.setMinimum(0);
			hBar.setMaximum(0);
			hBar.setVisible(false);
		}
		if (iHeight > sHeight) {
			pHeight = sHeight / iHeight * originHeight;
			iHeight = sHeight;
			int min = -(int) ((originHeight - pHeight) / 2);
			int max = (int) ((originHeight - pHeight) / 2);
			vBar.setMinimum(min);
			vBar.setMaximum(max);
			vBar.setVisible(showScrollbars);
		} else {
			vBar.setMinimum(0);
			vBar.setMaximum(0);
			vBar.setVisible(false);

		}

		BufferedImage subImage;
		try {
			subImage = origin.getSubimage((int) (xmiddle + hBar.getValue() - pWidth / 2),
					(int) (ymiddle + vBar.getValue() - pHeight / 2), (int) pWidth, (int) pHeight);
		} catch (Exception e) {

			return;
		}
		if (iWidth < 2) {
			zoomp();
		}
		img = new BufferedImage((int) iWidth, (int) iHeight, origin.getType());

		Graphics2D g2d = img.createGraphics();
		// Zeichnet das Orignial Bild skalliert
		if (fast) {
			refresher.startRefresh();
		} else {
			g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
		}
		g2d.drawImage(subImage, 0, 0, (int) Math.round(iWidth), (int) Math.round(iHeight), null);
		cp.repaint();
	}

	public void addImageDisplayListener(ImageDisplayListener listener){
		this.listenerList.add(ImageDisplayListener.class, listener);
	}

	public void removeImageDisplayListener(ImageDisplayListener listener){
		this.listenerList.remove(ImageDisplayListener.class, listener);
	}

	public void showToolbar(ToolBarBuilder viewerToolbarBuilder) {
		Component old = ((BorderLayout)cp.getLayout()).getLayoutComponent(BorderLayout.NORTH);
		if (old != null) {
			this.cp.remove(old);
		}
		cp.add(viewerToolbarBuilder.buildMenuBar(new ViewNavigationListener(this)), BorderLayout.NORTH);
		this.invalidate();
		this.revalidate();
	}

	/**
	 * Draws the Image
	 *
	 * @param origin The Original Image
	 */
	private void drawImage(final BufferedImage origin) {
		sWidth = imp.getWidth();
		sHeight = imp.getHeight();
		if (sWidth == 0 || sHeight == 0 || origin == null) return;
		double iHeight = origin.getHeight();
		double iWidth = origin.getWidth();
		double hFactor = 1.0;
		double wFactor = 1.0;

		if (sWidth >= origin.getWidth() && sHeight >= origin.getHeight()) {
			img = origin;
			factor = 1.0;

		} else {
			if (sWidth < origin.getWidth()) {
				wFactor = (double) sWidth / (double) origin.getWidth();
				iWidth = sWidth;
			}
			if (sHeight < origin.getHeight()) {
				hFactor = (double) sHeight / (double) origin.getHeight();
				iHeight = sHeight;
			}

			if (wFactor < hFactor) {
				iHeight = origin.getHeight() * wFactor;
				factor = wFactor;
			} else if (hFactor < wFactor) {
				iWidth = origin.getWidth() * hFactor;
				factor = hFactor;
			} else {
				factor = wFactor;
			}

			for (ImageDisplayListener l:listenerList.getListeners(ImageDisplayListener.class)){
				l.zoomLevelChanged(factor);
			}

			img = new BufferedImage((int) iWidth, (int) iHeight, origin.getType());
			Graphics2D g2d = img.createGraphics();
			// Zeichnet das Orignial Bild skalliert
			g2d.drawImage(origin, 0, 0, (int) Math.round(iWidth), (int) Math.round(iHeight), null);
		}
		xmiddle = origin.getWidth() / 2;
		ymiddle = origin.getHeight() / 2;
		cp.repaint();

	}

	@Override
	public void fullImage() {
		ArrayList arr = new ArrayList<>();
		arr.add(currentImage);
		Window topFrame = SwingUtilities.getWindowAncestor(this);
		new Diapresentation(topFrame, arr, false, false, -1, null);
		System.out.println("fullImage");
	}


	@Override
	public void setBackground(Color color) {
		//cp.setBackground(color);
	}


	@Override
	public void mouseClicked(final MouseEvent e) {
		// Nothing to do
	}

	@Override
	public void mouseDragged(final MouseEvent e) {
		int divX = e.getX() - oldEvent.getX();
		int divY = e.getY() - oldEvent.getY();

		hBar.setValue((int) (hBar.getValue() - divX * 1.0 / factor));
		vBar.setValue((int) (vBar.getValue() - divY * 1.0 / factor));
		oldEvent = e;

		calcZoom(true);

	}

	@Override
	public void mouseEntered(final MouseEvent e) {
		// Nothing to do

	}

	@Override
	public void mouseExited(final MouseEvent e) {
		// nothing to do

	}

	@Override
	public void mouseMoved(final MouseEvent arg0) {
		// Nothing to do
	}

	@Override
	public void mousePressed(final MouseEvent e) {
		oldEvent = e;
	}

	@Override
	public void mouseReleased(final MouseEvent e) {
		// nothing to do

	}

	@Override
	public void openImage(final File image) throws ViewException {
		if (image == null || image.equals(new File(""))) return;
		if (image.equals(currentImage)) return;

		try {

			origin = ImageIO.read(image);

			hBar.setVisible(false);
			vBar.setVisible(false);
			drawImage(origin);
			this.currentImage = image;
			for (ImageDisplayListener l: listenerList.getListeners(ImageDisplayListener.class)){
				l.imageOpened(image);
			}
		} catch (Exception e) {
			throw new ViewException(e, ViewException.OPEN_FAILED);
		}
	}

	@Override
	public void printImage() {
		// TODO Auto-generated method stub
	}

	@Override
	public void rotateImage(final boolean clockwise) {
		if (origin == null) return;
		BufferedImage rotatedImage;


//		rotatedImage = new BufferedImage(origin.getHeight(), origin.getWidth(),
//				(fileExtension.equalsIgnoreCase("jpg") ? origin.getType()
//						: BufferedImage.TYPE_INT_ARGB));

		rotatedImage = new BufferedImage(origin.getHeight(), origin.getWidth(), origin.getType());

		Graphics2D g = (Graphics2D) rotatedImage.getGraphics();
		if (clockwise) {
			g.translate(rotatedImage.getWidth(), 0);
			g.rotate(Math.toRadians(90.0));
		} else {
			g.translate(0, rotatedImage.getHeight());
			g.rotate(Math.toRadians(-90.0));

		}

		g.drawImage(origin, 0, 0, null);
		origin = rotatedImage;
		drawImage(origin);

	}


	@Override
	public void setImage(final BufferedImage img) {
		origin = img;
		drawImage(origin);

	}


	@Override
	public void zoom0() {
		hBar.setVisible(false);
		vBar.setVisible(false);
		this.revalidate();
		drawImage(origin);
	}

	@Override
	public void zoom1() {
		factor = 1;
		calcZoom(false);

	}

	@Override
	public void zoomm() {

		if (factor < 1.5) {
			if (factor > 1.0 && factor < 1.05) {
				factor = 1;
			} else {
				factor -= 0.05;
			}
		} else if (factor < 2.5) {
			factor -= 0.1;
		} else {
			factor -= 0.2;
		}

		if (factor <= 0.009) {
			factor = 0.01;
		}
		calcZoom(true);
	}


	@Override
	public void zoomp() {
		if (factor < 1.5) {
			if (factor < 1.0 && factor > 0.95) {
				factor = 1;
			} else {
				factor += 0.05;
			}
		} else if (factor < 2.5) {
			factor += 0.1;
		} else {
			factor += 0.2;
		}
		calcZoom(true);

	}


	private class Refresher {
		private final Object lock = new Object();

		private static final int WAIT = 100;

		private long start;

		private boolean run;

		void startRefresh() {
			synchronized (lock) {
				start = System.currentTimeMillis();
				if (!run) {
					run = true;
					new Thread(this::run).start();
				}
			}
		}

		private void run() {
			try {
				Thread.sleep(WAIT);

				boolean doContinue = true;
				do {
					long now = System.currentTimeMillis();
					long sleep;
					synchronized (lock) {
						sleep = now - (start + WAIT);
						if (sleep - 5 < 0) {
							doContinue = false;
							run = false;
						}
					}
					if (sleep > 0) {
						Thread.sleep(sleep);
					}

				} while (doContinue);
				SwingUtilities.invokeLater(() -> calcZoom(false));
			} catch (InterruptedException e) {
				e.printStackTrace();
				synchronized (lock) {
					run = false;
				}
			}
		}
	}

	public static class ToolBarBuilder {

		private ViewNavigationListener act;

		public void addButtonsToToolbar(JToolBar toolbar) {
			JButton btn = new JButton(new ImageIcon(ViewPanel.class.getResource("/zoom+.gif")));
			btn.setActionCommand("zoom+");
			toolbar.add(btn);
			btn.addActionListener(act);

			btn = new JButton(new ImageIcon(ViewPanel.class.getResource("/zoom-.gif")));
			btn.setActionCommand("zoom-");
			toolbar.add(btn);
			btn.addActionListener(act);

			btn = new JButton(new ImageIcon(ViewPanel.class.getResource("/zoom.gif")));
			btn.setActionCommand("zoom0");
			toolbar.add(btn);
			btn.addActionListener(act);

			btn = new JButton(new ImageIcon(ViewPanel.class.getResource("/zoom100.gif")));
			btn.setActionCommand("zoom1");
			toolbar.add(btn);
			btn.addActionListener(act);
			toolbar.addSeparator();

			btn = new JButton(new ImageIcon(ViewPanel.class.getResource("/Rot-.gif")));
			btn.setActionCommand("rot-");
			toolbar.add(btn);
			btn.addActionListener(act);
			btn = new JButton(new ImageIcon(ViewPanel.class.getResource("/Rot+.gif")));
			btn.setActionCommand("rot+");
			toolbar.add(btn);
			btn.addActionListener(act);
		}


		private JToolBar buildMenuBar(ViewNavigationListener listener) {
			this.act = listener;
			JToolBar menuBar = new JToolBar();
			addButtonsToToolbar(menuBar);
			return menuBar;
		}


	}


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy