001 /* 002 * Copyright (c) 2005, romain guy (romain.guy@jext.org) and craig wickesser (craig@codecraig.com) 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 006 * 007 * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 008 * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 009 * * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 010 * 011 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 012 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 013 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 014 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 015 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 016 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 017 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 018 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 019 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 020 * POSSIBILITY OF SUCH DAMAGE. 021 */ 022 023 package net.java.swingfx.waitwithstyle; 024 import java.awt.AlphaComposite; 025 import java.awt.Color; 026 import java.awt.Graphics; 027 import java.awt.Graphics2D; 028 import java.awt.Image; 029 import java.awt.RenderingHints; 030 import java.awt.font.FontRenderContext; 031 import java.awt.font.TextLayout; 032 import java.awt.geom.Rectangle2D; 033 import java.awt.image.BufferedImage; 034 import java.awt.image.BufferedImageOp; 035 import java.awt.image.ConvolveOp; 036 import java.awt.image.Kernel; 037 038 import javax.swing.ImageIcon; 039 import javax.swing.JPanel; 040 041 import net.java.swingfx.common.Utils; 042 043 /** 044 * Display an animated panel. The panel contains a picture and a text message. 045 * As soon as <code>start()</code> is called, the pictures and the text glow 046 * in cycles. The animation can be stopped at anytime by calling 047 * <code>stop()</code>. You can set the font and its color by calling 048 * <code>setFont()</code> and <code>setForeground()</code>. 049 * 050 * @author Romain Guy, 17/02/2005 051 * @since 1.0 052 * <br> 053 * $Revision: 1.2 $ 054 */ 055 public class AnimatedPanel extends JPanel { 056 private static final long serialVersionUID = 3257288036894324529L; 057 058 protected float gradient; 059 protected String message; 060 protected Thread animator; 061 protected BufferedImage convolvedImage; 062 protected BufferedImage originalImage; 063 protected static AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER); 064 protected RenderingHints brightnessHints; 065 066 /** 067 * Creates an animated panel with a message and a picture. 068 * 069 * @param message The message to display, can not be null nor empty. 070 * @param icon The picture to display, can not be null 071 */ 072 public AnimatedPanel(String message, ImageIcon icon) { 073 // since the message can not be null or empty, validate it 074 validateMessage(message); 075 // the icon can't be null either 076 if (icon == null) { 077 throw new IllegalArgumentException("Icon can not be null."); 078 } 079 080 this.message = message; 081 082 Image image = icon.getImage(); 083 originalImage = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB); 084 convolvedImage = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB); 085 Graphics g = originalImage.createGraphics(); 086 g.drawImage(image, 0, 0, this); 087 g.dispose(); 088 089 brightnessHints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); 090 091 setBrightness(1.0f); 092 setOpaque(false); 093 } 094 095 /** 096 * Performs basic validation on the given <code>msg</code> 097 * 098 * @param msg the message to validate 099 * 100 * @throws IllegalArgumentException if <code>msg</code> is <code>null</code> or empty 101 */ 102 private void validateMessage(String msg) throws IllegalArgumentException { 103 if (Utils.isNullOrEmpty(msg)) { 104 throw new IllegalArgumentException("Invalid message. Message can not be null or empty."); 105 } 106 } 107 108 /** 109 * Changes the displayed message at runtime. 110 * 111 * @param text The message to be displayed. Can not be null or empty. 112 */ 113 public void setText(String text) { 114 // since the message can not be null or empty, validate it 115 validateMessage(text); 116 117 this.message = text; 118 repaint(); 119 } 120 121 /** 122 * Returns the current message. 123 */ 124 public String getText() { 125 return message; 126 } 127 128 public void paintComponent(Graphics g) { 129 super.paintComponent(g); 130 131 if (convolvedImage != null) { 132 int width = getWidth(); 133 int height = getHeight(); 134 135 synchronized (convolvedImage) { 136 Graphics2D g2 = (Graphics2D) g; 137 g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); 138 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 139 g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); 140 141 FontRenderContext context = g2.getFontRenderContext(); 142 TextLayout layout = new TextLayout(message, getFont(), context); 143 Rectangle2D bounds = layout.getBounds(); 144 145 int x = (width - convolvedImage.getWidth(null)) / 2; 146 int y = (int) (height - (convolvedImage.getHeight(null) + bounds.getHeight() + layout.getAscent())) / 2; 147 148 g2.drawImage(convolvedImage, x, y, this); 149 Color foreground = getForeground(); 150 g2.setColor(new Color(foreground.getRed(), foreground.getGreen(), foreground.getBlue(), 151 (int) (gradient * 255))); 152 layout.draw(g2, (float) (width - bounds.getWidth()) / 2, 153 (float) (y + convolvedImage.getHeight(null) + bounds.getHeight() + layout.getAscent())); 154 } 155 } 156 } 157 158 /** 159 * Changes the image luminosity. 160 */ 161 private void setBrightness(float multiple) { 162 float[] brightKernel = { multiple }; 163 BufferedImageOp bright = new ConvolveOp(new Kernel(1, 1, brightKernel), ConvolveOp.EDGE_NO_OP, brightnessHints); 164 bright.filter(originalImage, convolvedImage); 165 repaint(); 166 } 167 168 /** 169 * Changes the text gradient control value. 170 */ 171 private void setGradientFactor(float gradient) { 172 this.gradient = gradient; 173 } 174 175 /** 176 * Starts the animation. A thread called "Highlighter" is spawned and can be 177 * interrupted at anytime by invoking <code>stop()</code>. 178 */ 179 public void start() { 180 this.animator = new Thread(new HighlightCycler(), "Highlighter"); 181 this.animator.start(); 182 } 183 184 /** 185 * Safely stops the animation. 186 */ 187 public void stop() { 188 if (this.animator != null) 189 this.animator.interrupt(); 190 this.animator = null; 191 } 192 193 /** 194 * Makes the image luminosity and the text gradient to cycle. 195 */ 196 class HighlightCycler implements Runnable { 197 198 private int way = 1; 199 private final int LOWER_BOUND = 10; 200 private final int UPPER_BOUND = 35; 201 private int value = LOWER_BOUND; 202 203 public void run() { 204 while (true) { 205 try { 206 Thread.sleep(1000 / (UPPER_BOUND - LOWER_BOUND)); 207 } catch (InterruptedException e) { 208 return; 209 } 210 211 value += this.way; 212 if (value > UPPER_BOUND) { 213 value = UPPER_BOUND; 214 this.way = -1; 215 } else if (value < LOWER_BOUND) { 216 value = LOWER_BOUND; 217 this.way = 1; 218 } 219 220 synchronized (convolvedImage) { 221 setBrightness((float) value / 10); 222 setGradientFactor((float) value / UPPER_BOUND); 223 } 224 } 225 } 226 } 227 }