org.tinymediamanager.ui.components.ImageLabel.java Source code

Java tutorial

Introduction

Here is the source code for org.tinymediamanager.ui.components.ImageLabel.java

Source

/*
 * Copyright 2012 - 2016 Manuel Laggner
 *
 * 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 org.tinymediamanager.ui.components;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ResourceBundle;

import javax.swing.JLabel;
import javax.swing.SwingWorker;

import org.apache.commons.lang3.StringUtils;
import org.imgscalr.Scalr;
import org.tinymediamanager.core.ImageCache;
import org.tinymediamanager.scraper.http.Url;
import org.tinymediamanager.ui.MainWindow;
import org.tinymediamanager.ui.TmmFontHelper;
import org.tinymediamanager.ui.UTF8Control;

/**
 * The Class ImageLabel.
 * 
 * @author Manuel Laggner
 */
public class ImageLabel extends JLabel {
    public enum Position {
        TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, CENTER
    }

    private static final long serialVersionUID = -2524445544386464158L;
    protected static final ResourceBundle BUNDLE = ResourceBundle.getBundle("messages", new UTF8Control()); //$NON-NLS-1$
    private static Font FONT;

    protected BufferedImage scaledImage;
    protected String imageUrl;
    protected String imagePath;
    protected Position position = Position.TOP_LEFT;
    protected String alternativeText = null;
    protected boolean drawBorder;
    protected boolean drawFullWidth;
    protected boolean enabledLightbox = false;
    protected boolean useCache = true;
    protected float desiredAspectRatio = 0f;

    protected SwingWorker<BufferedImage, Void> worker = null;
    protected MouseListener lightboxListener = null;

    static {
        try {
            JLabel fontLabel = new JLabel("");
            TmmFontHelper.changeFont(fontLabel, 1.5);
            FONT = fontLabel.getFont();
        } catch (Exception e) {
            FONT = Font.getFont("Dialog").deriveFont(18f);
        }
    }

    public ImageLabel() {
        super("");
        this.drawBorder = true;
        this.drawFullWidth = false;
    }

    public ImageLabel(boolean drawBorder) {
        super("");
        this.drawBorder = drawBorder;
        this.drawFullWidth = false;
    }

    public ImageLabel(boolean drawBorder, boolean drawFullWidth) {
        super("");
        this.drawBorder = drawBorder;
        this.drawFullWidth = drawFullWidth;
    }

    public String getImagePath() {
        return imagePath;
    }

    public void setImagePath(String newValue) {
        String oldValue = this.imagePath;

        if (StringUtils.isNotEmpty(oldValue) && oldValue.equals(newValue)) {
            return;
        }

        this.imagePath = newValue;
        firePropertyChange("imagePath", oldValue, newValue);

        // stop previous worker
        if (worker != null && !worker.isDone()) {
            worker.cancel(true);
        }

        scaledImage = null;
        this.repaint();

        if (StringUtils.isBlank(newValue)) {
            return;
        }

        // load image in separate worker -> performance
        worker = new ImageLoader(this.imagePath, this.getSize());
        worker.execute();
    }

    public void clearImage() {
        imagePath = "";
        imageUrl = "";
        scaledImage = null;
        this.repaint();
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String newValue) {
        String oldValue = this.imageUrl;
        this.imageUrl = newValue;
        firePropertyChange("imageUrl", oldValue, newValue);

        // stop previous worker
        if (worker != null && !worker.isDone()) {
            worker.cancel(true);
        }

        scaledImage = null;
        this.repaint();

        if (StringUtils.isEmpty(newValue)) {
            return;
        }

        // fetch image in separate worker -> performance
        worker = new ImageFetcher(this.getSize());
        worker.execute();
    }

    public void setDesiredAspectRatio(float desiredAspectRatio) {
        this.desiredAspectRatio = desiredAspectRatio;
    }

    public float getDesiredAspectRatio() {
        return desiredAspectRatio;
    }

    @Override
    public Dimension getPreferredSize() {
        if (desiredAspectRatio == 0) {
            // no desired aspect ratio; get the JLabel's preferred size
            return super.getPreferredSize();
        }
        if (scaledImage != null) {
            return new Dimension(getParent().getWidth(), (int) (getParent().getWidth()
                    / (float) scaledImage.getWidth() * (float) scaledImage.getHeight()));
        }
        return new Dimension(getParent().getWidth(), (int) (getParent().getWidth() / desiredAspectRatio) + 1);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        if (scaledImage != null) {
            int originalWidth = scaledImage.getWidth(null);
            int originalHeight = scaledImage.getHeight(null);

            // calculate new height/width
            int newWidth = 0;
            int newHeight = 0;

            int offsetX = 0;
            int offsetY = 0;

            if (drawBorder && !drawFullWidth) {
                Point size = ImageCache.calculateSize(this.getWidth() - 8, this.getHeight() - 8, originalWidth,
                        originalHeight, true);

                // calculate offsets
                if (position == Position.TOP_RIGHT || position == Position.BOTTOM_RIGHT) {
                    offsetX = this.getWidth() - size.x - 8;
                }

                if (position == Position.BOTTOM_LEFT || position == Position.BOTTOM_RIGHT) {
                    offsetY = this.getHeight() - size.y - 8;
                }

                if (position == Position.CENTER) {
                    offsetX = (this.getWidth() - size.x - 8) / 2;
                    offsetY = (this.getHeight() - size.y - 8) / 2;
                }

                newWidth = size.x;
                newHeight = size.y;

                // when the image size differs too much - reload and rescale the original image
                recreateScaledImageIfNeeded(originalWidth, originalHeight, newWidth, newHeight);

                g.setColor(Color.BLACK);
                g.drawRect(offsetX, offsetY, size.x + 7, size.y + 7);
                g.setColor(Color.WHITE);
                g.fillRect(offsetX + 1, offsetY + 1, size.x + 6, size.y + 6);
                // g.drawImage(Scaling.scale(originalImage, newWidth, newHeight), offsetX + 4, offsetY + 4, newWidth, newHeight, this);
                g.drawImage(scaledImage, offsetX + 4, offsetY + 4, newWidth, newHeight, this);
            } else {
                Point size = null;
                if (drawFullWidth) {
                    size = new Point(this.getWidth(), this.getWidth() * originalHeight / originalWidth);
                } else {
                    size = ImageCache.calculateSize(this.getWidth(), this.getHeight(), originalWidth,
                            originalHeight, true);
                }

                // calculate offsets
                if (position == Position.TOP_RIGHT || position == Position.BOTTOM_RIGHT) {
                    offsetX = this.getWidth() - size.x;
                }

                if (position == Position.BOTTOM_LEFT || position == Position.BOTTOM_RIGHT) {
                    offsetY = this.getHeight() - size.y;
                }

                if (position == Position.CENTER) {
                    offsetX = (this.getWidth() - size.x) / 2;
                    offsetY = (this.getHeight() - size.y) / 2;
                }

                newWidth = size.x;
                newHeight = size.y;

                // when the image size differs too much - reload and rescale the original image
                recreateScaledImageIfNeeded(originalWidth, originalHeight, newWidth, newHeight);

                // g.drawImage(Scaling.scale(originalImage, newWidth, newHeight), offsetX, offsetY, newWidth, newHeight, this);
                g.drawImage(scaledImage, offsetX, offsetY, newWidth, newHeight, this);
            }
        } else {
            // draw border and background
            if (drawBorder) {
                g.setColor(Color.BLACK);
                g.drawRect(0, 0, this.getWidth() - 1, this.getHeight() - 1);
                if (getParent().isOpaque()) {
                    g.setColor(getParent().getBackground());
                    g.fillRect(1, 1, this.getWidth() - 2, this.getHeight() - 2);
                }
            }

            // calculate diagonal
            int diagonalSize = (int) Math
                    .sqrt(this.getWidth() * this.getWidth() + this.getHeight() * this.getHeight());

            // draw text
            String text = "";
            if (alternativeText != null) {
                text = alternativeText;
            } else {
                text = BUNDLE.getString("image.nonefound"); //$NON-NLS-1$
            }
            if (!getParent().isOpaque()) {
                text = "";
            }
            Graphics2D g2 = (Graphics2D) g.create();
            g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            AffineTransform orig = g2.getTransform();
            AffineTransform at = new AffineTransform(orig);
            at.translate(0, this.getHeight());
            at.rotate(this.getWidth(), -this.getHeight());
            g2.setTransform(at);
            g2.setColor(Color.BLACK);
            g2.setFont(FONT);

            FontMetrics fm = g2.getFontMetrics();
            int x = (diagonalSize - fm.stringWidth(text)) / 2;
            int y = (fm.getAscent() - fm.getDescent()) / 2;

            g2.drawString(text, x, y);
            // g2.drawLine(0, 0, diagonalSize, 0);
            at.translate(0, -this.getHeight());
            g2.setTransform(orig);
        }
    }

    private void recreateScaledImageIfNeeded(int originalWidth, int originalHeight, int newWidth, int newHeight) {
        if ((newWidth * 0.8f > originalWidth) || (originalWidth > newWidth * 1.2f)
                || (newHeight * 0.8f > originalHeight) || (originalHeight > newHeight * 1.2f) && newWidth > 10) {
            if (StringUtils.isNotBlank(imagePath)) {
                worker = new ImageLoader(imagePath, new Dimension(newWidth, newHeight));
                worker.execute();
            } else if (StringUtils.isNoneBlank(imageUrl)) {
                worker = new ImageFetcher(new Dimension(newWidth, newHeight));
                worker.execute();
            }
        }
    }

    public void setPosition(Position position) {
        this.position = position;
    }

    public void setAlternativeText(String text) {
        this.alternativeText = text;
    }

    public void enableLightbox() {
        this.enabledLightbox = true;
        if (lightboxListener == null) {
            lightboxListener = new ImageLabelClickListener();
            addMouseListener(lightboxListener);
        }
    }

    public void disableLightbox() {
        this.enabledLightbox = false;
        if (lightboxListener != null) {
            removeMouseListener(lightboxListener);
            lightboxListener = null;
        }
    }

    public void setUseCache(boolean useCache) {
        this.useCache = useCache;
    }

    /*
     * inner class for downloading online images
     */
    protected class ImageFetcher extends SwingWorker<BufferedImage, Void> {
        private Dimension newSize;

        public ImageFetcher(Dimension newSize) {
            this.newSize = newSize;
        }

        @Override
        protected BufferedImage doInBackground() throws Exception {
            try {
                Url url = new Url(imageUrl);
                return Scalr.resize(ImageCache.createImage(url.getBytes()), Scalr.Method.QUALITY,
                        Scalr.Mode.AUTOMATIC, newSize.width, newSize.height, Scalr.OP_ANTIALIAS);
            } catch (Exception e) {
                imageUrl = "";
                return null;
            }
        }

        @Override
        protected void done() {
            if (isCancelled()) {
                return;
            }

            try {
                // get fetched image
                scaledImage = get();
            } catch (Exception e) {
                scaledImage = null;
            }
            repaint();
        }
    }

    /*
     * inner class for loading local images
     */
    protected class ImageLoader extends SwingWorker<BufferedImage, Void> {
        private String imagePath;
        private Dimension newSize;

        public ImageLoader(String imagePath, Dimension newSize) {
            this.imagePath = imagePath;
            this.newSize = newSize;
        }

        @Override
        protected BufferedImage doInBackground() throws Exception {
            Path file = null;
            if (useCache) {
                file = ImageCache.getCachedFile(Paths.get(imagePath));
            } else {
                file = Paths.get(imagePath);
            }

            if (file != null && Files.exists(file)) {
                try {
                    return Scalr.resize(ImageCache.createImage(file), Scalr.Method.QUALITY, Scalr.Mode.AUTOMATIC,
                            newSize.width, newSize.height, Scalr.OP_ANTIALIAS);
                } catch (Exception e) {
                    return null;
                }
            } else {
                return null;
            }
        }

        @Override
        protected void done() {
            if (isCancelled()) {
                return;
            }

            try {
                // get fetched image
                scaledImage = get();
            } catch (Exception e) {
                scaledImage = null;
            }
            revalidate();
            repaint();
        }
    }

    /*
     * click listener for creating a lightbox effect
     */
    private class ImageLabelClickListener extends MouseAdapter {
        @Override
        public void mouseClicked(MouseEvent arg0) {
            if (arg0.getClickCount() == 1 && scaledImage != null) {
                MainWindow.getActiveInstance().createLightbox(getImagePath(), getImageUrl());
            }
        }
    }
}