1 /*
   2  * Copyright (c) 1998, 2009, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.swing.plaf.metal;
  27 
  28 import sun.awt.AppContext;
  29 
  30 import java.awt.Color;
  31 import java.awt.Component;
  32 import java.awt.Dimension;
  33 import java.awt.Graphics;
  34 import java.awt.Graphics2D;
  35 import java.awt.GraphicsConfiguration;
  36 import java.awt.Image;
  37 import java.awt.Transparency;
  38 import java.awt.image.BufferedImage;
  39 import java.awt.image.DataBuffer;
  40 import java.awt.image.IndexColorModel;
  41 import java.util.ArrayList;
  42 import java.util.List;
  43 import javax.swing.Icon;
  44 
  45 /**
  46  * Implements the bumps used throughout the Metal Look and Feel.
  47  *
  48  * @author Tom Santos
  49  * @author Steve Wilson
  50  */
  51 
  52 
  53 class MetalBumps implements Icon {
  54 
  55     static final Color ALPHA = new Color(0, 0, 0, 0);
  56 
  57     protected int xBumps;
  58     protected int yBumps;
  59     protected Color topColor;
  60     protected Color shadowColor;
  61     protected Color backColor;
  62 
  63     private static final Object METAL_BUMPS = new Object();
  64     protected BumpBuffer buffer;
  65 
  66     /**
  67      * Creates MetalBumps of the specified size with the specified colors.
  68      * If <code>newBackColor</code> is null, the background will be
  69      * transparent.
  70      */
  71     public MetalBumps( int width, int height,
  72                        Color newTopColor, Color newShadowColor, Color newBackColor ) {
  73         setBumpArea( width, height );
  74         setBumpColors( newTopColor, newShadowColor, newBackColor );
  75     }
  76 
  77     private static BumpBuffer createBuffer(GraphicsConfiguration gc,
  78                                            Color topColor, Color shadowColor, Color backColor) {
  79         AppContext context = AppContext.getAppContext();
  80         List<BumpBuffer> buffers = (List<BumpBuffer>) context.get(METAL_BUMPS);
  81         if (buffers == null) {
  82             buffers = new ArrayList<BumpBuffer>();
  83             context.put(METAL_BUMPS, buffers);
  84         }
  85         for (BumpBuffer buffer : buffers) {
  86             if (buffer.hasSameConfiguration(gc, topColor, shadowColor, backColor)) {
  87                 return buffer;
  88             }
  89         }
  90         BumpBuffer buffer = new BumpBuffer(gc, topColor, shadowColor, backColor);
  91         buffers.add(buffer);
  92         return buffer;
  93     }
  94 
  95     public void setBumpArea( Dimension bumpArea ) {
  96         setBumpArea( bumpArea.width, bumpArea.height );
  97     }
  98 
  99     public void setBumpArea( int width, int height ) {
 100         xBumps = width / 2;
 101         yBumps = height / 2;
 102     }
 103 
 104     public void setBumpColors( Color newTopColor, Color newShadowColor, Color newBackColor ) {
 105         topColor = newTopColor;
 106         shadowColor = newShadowColor;
 107         if (newBackColor == null) {
 108             backColor = ALPHA;
 109         }
 110         else {
 111             backColor = newBackColor;
 112         }
 113     }
 114 
 115     public void paintIcon( Component c, Graphics g, int x, int y ) {
 116         GraphicsConfiguration gc = (g instanceof Graphics2D) ?
 117                                      (GraphicsConfiguration)((Graphics2D)g).
 118                                      getDeviceConfiguration() : null;
 119 
 120         if ((buffer == null) || !buffer.hasSameConfiguration(gc, topColor, shadowColor, backColor)) {
 121             buffer = createBuffer(gc, topColor, shadowColor, backColor);
 122         }
 123 
 124         int bufferWidth = BumpBuffer.IMAGE_SIZE;
 125         int bufferHeight = BumpBuffer.IMAGE_SIZE;
 126         int iconWidth = getIconWidth();
 127         int iconHeight = getIconHeight();
 128         int x2 = x + iconWidth;
 129         int y2 = y + iconHeight;
 130         int savex = x;
 131 
 132         while (y < y2) {
 133             int h = Math.min(y2 - y, bufferHeight);
 134             for (x = savex; x < x2; x += bufferWidth) {
 135                 int w = Math.min(x2 - x, bufferWidth);
 136                 g.drawImage(buffer.getImage(),
 137                             x, y, x+w, y+h,
 138                             0, 0, w, h,
 139                             null);
 140             }
 141             y += bufferHeight;
 142         }
 143     }
 144 
 145     public int getIconWidth() {
 146         return xBumps * 2;
 147     }
 148 
 149     public int getIconHeight() {
 150         return yBumps * 2;
 151     }
 152 }
 153 
 154 
 155 class BumpBuffer {
 156 
 157     static final int IMAGE_SIZE = 64;
 158 
 159     transient Image image;
 160     Color topColor;
 161     Color shadowColor;
 162     Color backColor;
 163     private GraphicsConfiguration gc;
 164 
 165     public BumpBuffer(GraphicsConfiguration gc, Color aTopColor,
 166                       Color aShadowColor, Color aBackColor) {
 167         this.gc = gc;
 168         topColor = aTopColor;
 169         shadowColor = aShadowColor;
 170         backColor = aBackColor;
 171         createImage();
 172         fillBumpBuffer();
 173     }
 174 
 175     public boolean hasSameConfiguration(GraphicsConfiguration gc,
 176                                         Color aTopColor, Color aShadowColor,
 177                                         Color aBackColor) {
 178         if (this.gc != null) {
 179             if (!this.gc.equals(gc)) {
 180                 return false;
 181             }
 182         }
 183         else if (gc != null) {
 184             return false;
 185         }
 186         return topColor.equals( aTopColor )       &&
 187                shadowColor.equals( aShadowColor ) &&
 188                backColor.equals( aBackColor );
 189     }
 190 
 191     /**
 192      * Returns the Image containing the bumps appropriate for the passed in
 193      * <code>GraphicsConfiguration</code>.
 194      */
 195     public Image getImage() {
 196         return image;
 197     }
 198 
 199     /**
 200      * Paints the bumps into the current image.
 201      */
 202     private void fillBumpBuffer() {
 203         Graphics g = image.getGraphics();
 204 
 205         g.setColor( backColor );
 206         g.fillRect( 0, 0, IMAGE_SIZE, IMAGE_SIZE );
 207 
 208         g.setColor(topColor);
 209         for (int x = 0; x < IMAGE_SIZE; x+=4) {
 210             for (int y = 0; y < IMAGE_SIZE; y+=4) {
 211                 g.drawLine( x, y, x, y );
 212                 g.drawLine( x+2, y+2, x+2, y+2);
 213             }
 214         }
 215 
 216         g.setColor(shadowColor);
 217         for (int x = 0; x < IMAGE_SIZE; x+=4) {
 218             for (int y = 0; y < IMAGE_SIZE; y+=4) {
 219                 g.drawLine( x+1, y+1, x+1, y+1 );
 220                 g.drawLine( x+3, y+3, x+3, y+3);
 221             }
 222         }
 223         g.dispose();
 224     }
 225 
 226     /**
 227      * Creates the image appropriate for the passed in
 228      * <code>GraphicsConfiguration</code>, which may be null.
 229      */
 230     private void createImage() {
 231         if (gc != null) {
 232             image = gc.createCompatibleImage(IMAGE_SIZE, IMAGE_SIZE,
 233                        (backColor != MetalBumps.ALPHA) ? Transparency.OPAQUE :
 234                        Transparency.BITMASK);
 235         }
 236         else {
 237             int cmap[] = { backColor.getRGB(), topColor.getRGB(),
 238                            shadowColor.getRGB() };
 239             IndexColorModel icm = new IndexColorModel(8, 3, cmap, 0, false,
 240                       (backColor == MetalBumps.ALPHA) ? 0 : -1,
 241                       DataBuffer.TYPE_BYTE);
 242             image = new BufferedImage(IMAGE_SIZE, IMAGE_SIZE,
 243                                       BufferedImage.TYPE_BYTE_INDEXED, icm);
 244         }
 245     }
 246 }