/* * Copyright (C) 2015 Patryk Strach * * This file is part of Virtual Slide Viewer. * * Virtual Slide Viewer is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. * * Virtual Slide Viewer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with Virtual Slide Viewer. * If not, see <http://www.gnu.org/licenses/>. */ package virtualslideviewer.ui.imageviewing.view; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import javax.swing.JPanel; import javax.swing.SwingUtilities; import virtualslideviewer.ui.imageviewing.ImagePresentationModel; import virtualslideviewer.ui.imageviewing.ImagePresentationModel.Listener; import virtualslideviewer.util.ImageUtil; /** * A panel for displaying the preview of the image. * * Besides the downscaled image, this panel also displays a marker representing the region of currently visible image part. */ public class ImagePreviewPanel extends JPanel { private static final long serialVersionUID = 1L; private final ImagePresentationModel mUIModel; private Image mCachedImage; /** * @param presentationModel The model with panel's logic. */ public ImagePreviewPanel(ImagePresentationModel presentationModel) { if(presentationModel == null) throw new IllegalArgumentException("presentationModel cannot be null."); mUIModel = presentationModel; mUIModel.addListener(new Listener() { @Override public void onVisibleImageContentUpdate() { SwingUtilities.invokeLater(() -> repaint()); } @Override public void onImageChange() { regenerateImage(); SwingUtilities.invokeLater(() -> repaint()); } }); super.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { regenerateImage(); } }); super.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { onMouseClick(e.getPoint()); } }); } @Override protected void paintComponent(Graphics drawingSurface) { super.paintComponent(drawingSurface); if(mCachedImage != null) { drawImage(drawingSurface); drawVisibleRegionMarker(drawingSurface); } } private void drawImage(Graphics drawingSurface) { drawingSurface.drawImage(mCachedImage, getImageBounds().x, getImageBounds().y, null); } private void drawVisibleRegionMarker(Graphics drawingSurface) { Rectangle visibleRegionMarkerBounds = mUIModel.getVisibleRegionMarkerBounds(getImageBounds()); drawingSurface.setColor(Color.GREEN); drawingSurface.drawRect(visibleRegionMarkerBounds.x, visibleRegionMarkerBounds.y, visibleRegionMarkerBounds.width, visibleRegionMarkerBounds.height); } /** * Gets image bounds as it is displayed. * * The image is centered if it's smaller than the panel. */ private Rectangle getImageBounds() { Dimension imageSize = new Dimension(mCachedImage.getWidth(null), mCachedImage.getHeight(null)); Point imagePos = ImageUtil.getCenteredPosition(imageSize, super.getSize()); return new Rectangle(imagePos, imageSize); } private void onMouseClick(Point clickPointInPanelSpace) { Rectangle imageBounds = getImageBounds(); if(imageBounds.contains(clickPointInPanelSpace)) { Point clickPointInImageSpace = new Point(clickPointInPanelSpace.x - imageBounds.x, clickPointInPanelSpace.y - imageBounds.y); mUIModel.handleImagePreviewClick(clickPointInImageSpace, getImageBounds().getSize()); } } /** * Regenerates the cached image rendered as the preview. * * Image resizing is done offline when panel's size change because scaling it with good quality on the fly is too costly. */ private void regenerateImage() { if(super.getSize().equals(new Dimension(0, 0))) return; // The panel's size is not set yet, so no point in generating image. BufferedImage originalImage = new BufferedImage(mUIModel.getThumbnailSize().width, mUIModel.getThumbnailSize().height, mUIModel.isImageRGB() ? BufferedImage.TYPE_3BYTE_BGR : BufferedImage.TYPE_BYTE_GRAY); ImageUtil.loadDataIntoBufferedImage(originalImage, (dst) -> mUIModel.getThumbnailData(dst)); mCachedImage = ImageUtil.scaleToFitPreservingAspectRatio(originalImage, super.getSize()); } }