1 /* 2 * Copyright (c) 2005, 2013, 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 package javax.swing.plaf.nimbus; 26 27 import javax.swing.border.Border; 28 import javax.swing.JComponent; 29 import java.awt.Insets; 30 import java.awt.Component; 31 import java.awt.Graphics; 32 import java.awt.Graphics2D; 33 import java.awt.Color; 34 import java.awt.RenderingHints; 35 import java.awt.Dimension; 36 import java.awt.image.BufferedImage; 37 38 /** 39 * LoweredBorder - A recessed rounded inner shadowed border. Used as the 40 * standard Nimbus TitledBorder. This class is both a painter and a swing 41 * border. 42 * 43 * @author Jasper Potts 44 */ 45 class LoweredBorder extends AbstractRegionPainter implements Border { 46 private static final int IMG_SIZE = 30; 47 private static final int RADIUS = 13; 48 private static final Insets INSETS = new Insets(10,10,10,10); 49 private static final PaintContext PAINT_CONTEXT = new PaintContext(INSETS, 50 new Dimension(IMG_SIZE,IMG_SIZE),false, 51 PaintContext.CacheMode.NINE_SQUARE_SCALE, 52 Integer.MAX_VALUE, Integer.MAX_VALUE); 53 54 // ========================================================================= 55 // Painter Methods 56 57 @Override 58 protected Object[] getExtendedCacheKeys(JComponent c) { 59 return (c != null) 60 ? new Object[] { c.getBackground() } 61 : null; 62 } 63 64 /** 65 * Actually performs the painting operation. Subclasses must implement this 66 * method. The graphics object passed may represent the actual surface being 67 * rendered to, or it may be an intermediate buffer. It has also been 68 * pre-translated. Simply render the component as if it were located at 0, 0 69 * and had a width of {@code width} and a height of 70 * {@code height}. For performance reasons, you may want to read the 71 * clip from the Graphics2D object and only render within that space. 72 * 73 * @param g The Graphics2D surface to paint to 74 * @param c The JComponent related to the drawing event. For example, 75 * if the region being rendered is Button, then {@code c} 76 * will be a JButton. If the region being drawn is 77 * ScrollBarSlider, then the component will be JScrollBar. 78 * This value may be null. 79 * @param width The width of the region to paint. Note that in the case of 80 * painting the foreground, this value may differ from 81 * c.getWidth(). 82 * @param height The height of the region to paint. Note that in the case of 83 * painting the foreground, this value may differ from 84 * c.getHeight(). 85 */ 86 protected void doPaint(Graphics2D g, JComponent c, int width, int height, 87 Object[] extendedCacheKeys) { 88 Color color = (c == null) ? Color.BLACK : c.getBackground(); 89 BufferedImage img1 = new BufferedImage(IMG_SIZE,IMG_SIZE, 90 BufferedImage.TYPE_INT_ARGB); 91 BufferedImage img2 = new BufferedImage(IMG_SIZE,IMG_SIZE, 92 BufferedImage.TYPE_INT_ARGB); 93 // draw shadow shape 94 Graphics2D g2 = (Graphics2D)img1.getGraphics(); 95 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 96 RenderingHints.VALUE_ANTIALIAS_ON); 97 g2.setColor(color); 98 g2.fillRoundRect(2,0,26,26,RADIUS,RADIUS); 99 g2.dispose(); 100 // draw shadow 101 InnerShadowEffect effect = new InnerShadowEffect(); 102 effect.setDistance(1); 103 effect.setSize(3); 104 effect.setColor(getLighter(color, 2.1f)); 105 effect.setAngle(90); 106 effect.applyEffect(img1,img2,IMG_SIZE,IMG_SIZE); 107 // draw outline to img2 108 g2 = (Graphics2D)img2.getGraphics(); 109 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 110 RenderingHints.VALUE_ANTIALIAS_ON); 111 g2.setClip(0,28,IMG_SIZE,1); 112 g2.setColor(getLighter(color, 0.90f)); 113 g2.drawRoundRect(2,1,25,25,RADIUS,RADIUS); 114 g2.dispose(); 115 // draw final image 116 if (width != IMG_SIZE || height != IMG_SIZE){ 117 ImageScalingHelper.paint(g,0,0,width,height,img2, INSETS, INSETS, 118 ImageScalingHelper.PaintType.PAINT9_STRETCH, 119 ImageScalingHelper.PAINT_ALL); 120 } else { 121 g.drawImage(img2,0,0,c); 122 } 123 img1 = null; 124 img2 = null; 125 } 126 127 /** 128 * <p>Gets the PaintContext for this painting operation. This method is 129 * called on every paint, and so should be fast and produce no garbage. The 130 * PaintContext contains information such as cache hints. It also contains 131 * data necessary for decoding points at runtime, such as the stretching 132 * insets, the canvas size at which the encoded points were defined, and 133 * whether the stretching insets are inverted.</p> 134 * <p/> 135 * <p> This method allows for subclasses to package the painting of 136 * different states with possibly different canvas sizes, etc, into one 137 * AbstractRegionPainter implementation.</p> 138 * 139 * @return a PaintContext associated with this paint operation. 140 */ 141 protected PaintContext getPaintContext() { 142 return PAINT_CONTEXT; 143 } 144 145 // ========================================================================= 146 // Border Methods 147 148 /** 149 * Returns the insets of the border. 150 * 151 * @param c the component for which this border insets value applies 152 */ 153 public Insets getBorderInsets(Component c) { 154 return (Insets) INSETS.clone(); 155 } 156 157 /** 158 * Returns whether or not the border is opaque. If the border is opaque, it 159 * is responsible for filling in it's own background when painting. 160 */ 161 public boolean isBorderOpaque() { 162 return false; 163 } 164 165 /** 166 * Paints the border for the specified component with the specified position 167 * and size. 168 * 169 * @param c the component for which this border is being painted 170 * @param g the paint graphics 171 * @param x the x position of the painted border 172 * @param y the y position of the painted border 173 * @param width the width of the painted border 174 * @param height the height of the painted border 175 */ 176 public void paintBorder(Component c, Graphics g, int x, int y, int width, 177 int height) { 178 JComponent comp = (c instanceof JComponent)?(JComponent)c:null; 179 if (g instanceof Graphics2D){ 180 Graphics2D g2 = (Graphics2D)g; 181 g2.translate(x,y); 182 paint(g2,comp, width, height); 183 g2.translate(-x,-y); 184 } else { 185 BufferedImage img = new BufferedImage(IMG_SIZE,IMG_SIZE, 186 BufferedImage.TYPE_INT_ARGB); 187 Graphics2D g2 = (Graphics2D)img.getGraphics(); 188 paint(g2,comp, width, height); 189 g2.dispose(); 190 ImageScalingHelper.paint(g,x,y,width,height,img,INSETS, INSETS, 191 ImageScalingHelper.PaintType.PAINT9_STRETCH, 192 ImageScalingHelper.PAINT_ALL); 193 } 194 } 195 196 private Color getLighter(Color c, float factor){ 197 return new Color(Math.min((int)(c.getRed()/factor), 255), 198 Math.min((int)(c.getGreen()/factor), 255), 199 Math.min((int)(c.getBlue()/factor), 255)); 200 } 201 } 202