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 java.awt.*;
  29 import java.awt.image.*;
  30 import javax.swing.*;
  31 import java.util.ArrayList;
  32 import java.util.List;
  33 import sun.awt.AppContext;
  34 
  35 /**
  36  * Implements the bumps used throughout the Metal Look and Feel.
  37  *
  38  * @author Tom Santos
  39  * @author Steve Wilson
  40  */
  41 
  42 
  43 class MetalBumps implements Icon {
  44 
  45     static final Color ALPHA = new Color(0, 0, 0, 0);
  46 
  47     protected int xBumps;
  48     protected int yBumps;
  49     protected Color topColor;
  50     protected Color shadowColor;
  51     protected Color backColor;
  52 
  53     private static final Object METAL_BUMPS = new Object();
  54     protected BumpBuffer buffer;
  55 
  56     /**
  57      * Creates MetalBumps of the specified size with the specified colors.
  58      * If <code>newBackColor</code> is null, the background will be
  59      * transparent.
  60      */
  61     public MetalBumps( int width, int height,
  62                        Color newTopColor, Color newShadowColor, Color newBackColor ) {
  63         setBumpArea( width, height );
  64         setBumpColors( newTopColor, newShadowColor, newBackColor );
  65     }
  66 
  67     private static BumpBuffer createBuffer(GraphicsConfiguration gc,
  68                                            Color topColor, Color shadowColor, Color backColor) {
  69         AppContext context = AppContext.getAppContext();
  70         List<BumpBuffer> buffers = (List<BumpBuffer>) context.get(METAL_BUMPS);
  71         if (buffers == null) {
  72             buffers = new ArrayList<BumpBuffer>();
  73             context.put(METAL_BUMPS, buffers);
  74         }
  75         for (BumpBuffer buffer : buffers) {
  76             if (buffer.hasSameConfiguration(gc, topColor, shadowColor, backColor)) {
  77                 return buffer;
  78             }
  79         }
  80         BumpBuffer buffer = new BumpBuffer(gc, topColor, shadowColor, backColor);
  81         buffers.add(buffer);
  82         return buffer;
  83     }
  84 
  85     public void setBumpArea( Dimension bumpArea ) {
  86         setBumpArea( bumpArea.width, bumpArea.height );
  87     }
  88 
  89     public void setBumpArea( int width, int height ) {
  90         xBumps = width / 2;
  91         yBumps = height / 2;
  92     }
  93 
  94     public void setBumpColors( Color newTopColor, Color newShadowColor, Color newBackColor ) {
  95         topColor = newTopColor;
  96         shadowColor = newShadowColor;
  97         if (newBackColor == null) {
  98             backColor = ALPHA;
  99         }
 100         else {
 101             backColor = newBackColor;
 102         }
 103     }
 104 
 105     public void paintIcon( Component c, Graphics g, int x, int y ) {
 106         GraphicsConfiguration gc = (g instanceof Graphics2D) ?
 107                 ((Graphics2D) g).getDeviceConfiguration() : null;
 108 
 109         if ((buffer == null) || !buffer.hasSameConfiguration(gc, topColor, shadowColor, backColor)) {
 110             buffer = createBuffer(gc, topColor, shadowColor, backColor);
 111         }
 112 
 113         int bufferWidth = BumpBuffer.IMAGE_SIZE;
 114         int bufferHeight = BumpBuffer.IMAGE_SIZE;
 115         int iconWidth = getIconWidth();
 116         int iconHeight = getIconHeight();
 117         int x2 = x + iconWidth;
 118         int y2 = y + iconHeight;
 119         int savex = x;
 120 
 121         while (y < y2) {
 122             int h = Math.min(y2 - y, bufferHeight);
 123             for (x = savex; x < x2; x += bufferWidth) {
 124                 int w = Math.min(x2 - x, bufferWidth);
 125                 g.drawImage(buffer.getImage(),
 126                             x, y, x+w, y+h,
 127                             0, 0, w, h,
 128                             null);
 129             }
 130             y += bufferHeight;
 131         }
 132     }
 133 
 134     public int getIconWidth() {
 135         return xBumps * 2;
 136     }
 137 
 138     public int getIconHeight() {
 139         return yBumps * 2;
 140     }
 141 }
 142 
 143 
 144 class BumpBuffer {
 145 
 146     static final int IMAGE_SIZE = 64;
 147 
 148     transient Image image;
 149     Color topColor;
 150     Color shadowColor;
 151     Color backColor;
 152     private GraphicsConfiguration gc;
 153 
 154     public BumpBuffer(GraphicsConfiguration gc, Color aTopColor,
 155                       Color aShadowColor, Color aBackColor) {
 156         this.gc = gc;
 157         topColor = aTopColor;
 158         shadowColor = aShadowColor;
 159         backColor = aBackColor;
 160         createImage();
 161         fillBumpBuffer();
 162     }
 163 
 164     public boolean hasSameConfiguration(GraphicsConfiguration gc,
 165                                         Color aTopColor, Color aShadowColor,
 166                                         Color aBackColor) {
 167         if (this.gc != null) {
 168             if (!this.gc.equals(gc)) {
 169                 return false;
 170             }
 171         }
 172         else if (gc != null) {
 173             return false;
 174         }
 175         return topColor.equals( aTopColor )       &&
 176                shadowColor.equals( aShadowColor ) &&
 177                backColor.equals( aBackColor );
 178     }
 179 
 180     /**
 181      * Returns the Image containing the bumps appropriate for the passed in
 182      * <code>GraphicsConfiguration</code>.
 183      */
 184     public Image getImage() {
 185         return image;
 186     }
 187 
 188     /**
 189      * Paints the bumps into the current image.
 190      */
 191     private void fillBumpBuffer() {
 192         Graphics g = image.getGraphics();
 193 
 194         g.setColor( backColor );
 195         g.fillRect( 0, 0, IMAGE_SIZE, IMAGE_SIZE );
 196 
 197         g.setColor(topColor);
 198         for (int x = 0; x < IMAGE_SIZE; x+=4) {
 199             for (int y = 0; y < IMAGE_SIZE; y+=4) {
 200                 g.drawLine( x, y, x, y );
 201                 g.drawLine( x+2, y+2, x+2, y+2);
 202             }
 203         }
 204 
 205         g.setColor(shadowColor);
 206         for (int x = 0; x < IMAGE_SIZE; x+=4) {
 207             for (int y = 0; y < IMAGE_SIZE; y+=4) {
 208                 g.drawLine( x+1, y+1, x+1, y+1 );
 209                 g.drawLine( x+3, y+3, x+3, y+3);
 210             }
 211         }
 212         g.dispose();
 213     }
 214 
 215     /**
 216      * Creates the image appropriate for the passed in
 217      * <code>GraphicsConfiguration</code>, which may be null.
 218      */
 219     private void createImage() {
 220         if (gc != null) {
 221             image = gc.createCompatibleImage(IMAGE_SIZE, IMAGE_SIZE,
 222                        (backColor != MetalBumps.ALPHA) ? Transparency.OPAQUE :
 223                        Transparency.BITMASK);
 224         }
 225         else {
 226             int cmap[] = { backColor.getRGB(), topColor.getRGB(),
 227                            shadowColor.getRGB() };
 228             IndexColorModel icm = new IndexColorModel(8, 3, cmap, 0, false,
 229                       (backColor == MetalBumps.ALPHA) ? 0 : -1,
 230                       DataBuffer.TYPE_BYTE);
 231             image = new BufferedImage(IMAGE_SIZE, IMAGE_SIZE,
 232                                       BufferedImage.TYPE_BYTE_INDEXED, icm);
 233         }
 234     }
 235 }