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                 ((Graphics2D) g).getDeviceConfiguration() : null;
 118 
 119         if ((buffer == null) || !buffer.hasSameConfiguration(gc, topColor, shadowColor, backColor)) {
 120             buffer = createBuffer(gc, topColor, shadowColor, backColor);
 121         }
 122 
 123         int bufferWidth = BumpBuffer.IMAGE_SIZE;
 124         int bufferHeight = BumpBuffer.IMAGE_SIZE;
 125         int iconWidth = getIconWidth();
 126         int iconHeight = getIconHeight();
 127         int x2 = x + iconWidth;
 128         int y2 = y + iconHeight;
 129         int savex = x;
 130 
 131         while (y < y2) {
 132             int h = Math.min(y2 - y, bufferHeight);
 133             for (x = savex; x < x2; x += bufferWidth) {
 134                 int w = Math.min(x2 - x, bufferWidth);
 135                 g.drawImage(buffer.getImage(),
 136                             x, y, x+w, y+h,
 137                             0, 0, w, h,
 138                             null);
 139             }
 140             y += bufferHeight;
 141         }
 142     }
 143 
 144     public int getIconWidth() {
 145         return xBumps * 2;
 146     }
 147 
 148     public int getIconHeight() {
 149         return yBumps * 2;
 150     }
 151 }
 152 
 153 
 154 class BumpBuffer {
 155 
 156     static final int IMAGE_SIZE = 64;
 157 
 158     transient Image image;
 159     Color topColor;
 160     Color shadowColor;
 161     Color backColor;
 162     private GraphicsConfiguration gc;
 163 
 164     public BumpBuffer(GraphicsConfiguration gc, Color aTopColor,
 165                       Color aShadowColor, Color aBackColor) {
 166         this.gc = gc;
 167         topColor = aTopColor;
 168         shadowColor = aShadowColor;
 169         backColor = aBackColor;
 170         createImage();
 171         fillBumpBuffer();
 172     }
 173 
 174     public boolean hasSameConfiguration(GraphicsConfiguration gc,
 175                                         Color aTopColor, Color aShadowColor,
 176                                         Color aBackColor) {
 177         if (this.gc != null) {
 178             if (!this.gc.equals(gc)) {
 179                 return false;
 180             }
 181         }
 182         else if (gc != null) {
 183             return false;
 184         }
 185         return topColor.equals( aTopColor )       &&
 186                shadowColor.equals( aShadowColor ) &&
 187                backColor.equals( aBackColor );
 188     }
 189 
 190     /**
 191      * Returns the Image containing the bumps appropriate for the passed in
 192      * <code>GraphicsConfiguration</code>.
 193      */
 194     public Image getImage() {
 195         return image;
 196     }
 197 
 198     /**
 199      * Paints the bumps into the current image.
 200      */
 201     private void fillBumpBuffer() {
 202         Graphics g = image.getGraphics();
 203 
 204         g.setColor( backColor );
 205         g.fillRect( 0, 0, IMAGE_SIZE, IMAGE_SIZE );
 206 
 207         g.setColor(topColor);
 208         for (int x = 0; x < IMAGE_SIZE; x+=4) {
 209             for (int y = 0; y < IMAGE_SIZE; y+=4) {
 210                 g.drawLine( x, y, x, y );
 211                 g.drawLine( x+2, y+2, x+2, y+2);
 212             }
 213         }
 214 
 215         g.setColor(shadowColor);
 216         for (int x = 0; x < IMAGE_SIZE; x+=4) {
 217             for (int y = 0; y < IMAGE_SIZE; y+=4) {
 218                 g.drawLine( x+1, y+1, x+1, y+1 );
 219                 g.drawLine( x+3, y+3, x+3, y+3);
 220             }
 221         }
 222         g.dispose();
 223     }
 224 
 225     /**
 226      * Creates the image appropriate for the passed in
 227      * <code>GraphicsConfiguration</code>, which may be null.
 228      */
 229     private void createImage() {
 230         if (gc != null) {
 231             image = gc.createCompatibleImage(IMAGE_SIZE, IMAGE_SIZE,
 232                        (backColor != MetalBumps.ALPHA) ? Transparency.OPAQUE :
 233                        Transparency.BITMASK);
 234         }
 235         else {
 236             int cmap[] = { backColor.getRGB(), topColor.getRGB(),
 237                            shadowColor.getRGB() };
 238             IndexColorModel icm = new IndexColorModel(8, 3, cmap, 0, false,
 239                       (backColor == MetalBumps.ALPHA) ? 0 : -1,
 240                       DataBuffer.TYPE_BYTE);
 241             image = new BufferedImage(IMAGE_SIZE, IMAGE_SIZE,
 242                                       BufferedImage.TYPE_BYTE_INDEXED, icm);
 243         }
 244     }
 245 }