1 /*
   2  * Copyright (c) 1998, 2014, 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         @SuppressWarnings("unchecked")
  71         List<BumpBuffer> buffers = (List<BumpBuffer>) context.get(METAL_BUMPS);
  72         if (buffers == null) {
  73             buffers = new ArrayList<BumpBuffer>();
  74             context.put(METAL_BUMPS, buffers);
  75         }
  76         for (BumpBuffer buffer : buffers) {
  77             if (buffer.hasSameConfiguration(gc, topColor, shadowColor, backColor)) {
  78                 return buffer;
  79             }
  80         }
  81         BumpBuffer buffer = new BumpBuffer(gc, topColor, shadowColor, backColor);
  82         buffers.add(buffer);
  83         return buffer;
  84     }
  85 
  86     public void setBumpArea( Dimension bumpArea ) {
  87         setBumpArea( bumpArea.width, bumpArea.height );
  88     }
  89 
  90     public void setBumpArea( int width, int height ) {
  91         xBumps = width / 2;
  92         yBumps = height / 2;
  93     }
  94 
  95     public void setBumpColors( Color newTopColor, Color newShadowColor, Color newBackColor ) {
  96         topColor = newTopColor;
  97         shadowColor = newShadowColor;
  98         if (newBackColor == null) {
  99             backColor = ALPHA;
 100         }
 101         else {
 102             backColor = newBackColor;
 103         }
 104     }
 105 
 106     public void paintIcon( Component c, Graphics g, int x, int y ) {
 107         GraphicsConfiguration gc = (g instanceof Graphics2D) ?
 108                 ((Graphics2D) g).getDeviceConfiguration() : null;
 109 
 110         if ((buffer == null) || !buffer.hasSameConfiguration(gc, topColor, shadowColor, backColor)) {
 111             buffer = createBuffer(gc, topColor, shadowColor, backColor);
 112         }
 113 
 114         int bufferWidth = BumpBuffer.IMAGE_SIZE;
 115         int bufferHeight = BumpBuffer.IMAGE_SIZE;
 116         int iconWidth = getIconWidth();
 117         int iconHeight = getIconHeight();
 118         int x2 = x + iconWidth;
 119         int y2 = y + iconHeight;
 120         int savex = x;
 121 
 122         while (y < y2) {
 123             int h = Math.min(y2 - y, bufferHeight);
 124             for (x = savex; x < x2; x += bufferWidth) {
 125                 int w = Math.min(x2 - x, bufferWidth);
 126                 g.drawImage(buffer.getImage(),
 127                             x, y, x+w, y+h,
 128                             0, 0, w, h,
 129                             null);
 130             }
 131             y += bufferHeight;
 132         }
 133     }
 134 
 135     public int getIconWidth() {
 136         return xBumps * 2;
 137     }
 138 
 139     public int getIconHeight() {
 140         return yBumps * 2;
 141     }
 142 }
 143 
 144 
 145 class BumpBuffer {
 146 
 147     static final int IMAGE_SIZE = 64;
 148 
 149     transient Image image;
 150     Color topColor;
 151     Color shadowColor;
 152     Color backColor;
 153     private GraphicsConfiguration gc;
 154 
 155     public BumpBuffer(GraphicsConfiguration gc, Color aTopColor,
 156                       Color aShadowColor, Color aBackColor) {
 157         this.gc = gc;
 158         topColor = aTopColor;
 159         shadowColor = aShadowColor;
 160         backColor = aBackColor;
 161         createImage();
 162         fillBumpBuffer();
 163     }
 164 
 165     public boolean hasSameConfiguration(GraphicsConfiguration gc,
 166                                         Color aTopColor, Color aShadowColor,
 167                                         Color aBackColor) {
 168         if (this.gc != null) {
 169             if (!this.gc.equals(gc)) {
 170                 return false;
 171             }
 172         }
 173         else if (gc != null) {
 174             return false;
 175         }
 176         return topColor.equals( aTopColor )       &&
 177                shadowColor.equals( aShadowColor ) &&
 178                backColor.equals( aBackColor );
 179     }
 180 
 181     /**
 182      * Returns the Image containing the bumps appropriate for the passed in
 183      * <code>GraphicsConfiguration</code>.
 184      */
 185     public Image getImage() {
 186         return image;
 187     }
 188 
 189     /**
 190      * Paints the bumps into the current image.
 191      */
 192     private void fillBumpBuffer() {
 193         Graphics g = image.getGraphics();
 194 
 195         g.setColor( backColor );
 196         g.fillRect( 0, 0, IMAGE_SIZE, IMAGE_SIZE );
 197 
 198         g.setColor(topColor);
 199         for (int x = 0; x < IMAGE_SIZE; x+=4) {
 200             for (int y = 0; y < IMAGE_SIZE; y+=4) {
 201                 g.drawLine( x, y, x, y );
 202                 g.drawLine( x+2, y+2, x+2, y+2);
 203             }
 204         }
 205 
 206         g.setColor(shadowColor);
 207         for (int x = 0; x < IMAGE_SIZE; x+=4) {
 208             for (int y = 0; y < IMAGE_SIZE; y+=4) {
 209                 g.drawLine( x+1, y+1, x+1, y+1 );
 210                 g.drawLine( x+3, y+3, x+3, y+3);
 211             }
 212         }
 213         g.dispose();
 214     }
 215 
 216     /**
 217      * Creates the image appropriate for the passed in
 218      * <code>GraphicsConfiguration</code>, which may be null.
 219      */
 220     private void createImage() {
 221         if (gc != null) {
 222             image = gc.createCompatibleImage(IMAGE_SIZE, IMAGE_SIZE,
 223                        (backColor != MetalBumps.ALPHA) ? Transparency.OPAQUE :
 224                        Transparency.BITMASK);
 225         }
 226         else {
 227             int cmap[] = { backColor.getRGB(), topColor.getRGB(),
 228                            shadowColor.getRGB() };
 229             IndexColorModel icm = new IndexColorModel(8, 3, cmap, 0, false,
 230                       (backColor == MetalBumps.ALPHA) ? 0 : -1,
 231                       DataBuffer.TYPE_BYTE);
 232             image = new BufferedImage(IMAGE_SIZE, IMAGE_SIZE,
 233                                       BufferedImage.TYPE_BYTE_INDEXED, icm);
 234         }
 235     }
 236 }