1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.custom.roundeddiv;
20
21 import java.awt.AlphaComposite;
22 import java.awt.BasicStroke;
23 import java.awt.Color;
24 import java.awt.Dimension;
25 import java.awt.Graphics2D;
26 import java.awt.Polygon;
27 import java.awt.RenderingHints;
28 import java.awt.Shape;
29 import java.awt.Transparency;
30 import java.awt.geom.Arc2D;
31 import java.awt.geom.RoundRectangle2D;
32 import java.awt.image.BufferedImage;
33 import java.io.File;
34 import java.io.FileOutputStream;
35
36 import javax.imageio.ImageIO;
37
38
39
40
41
42
43
44 public class RoundedBorderGenerator
45 {
46 public final static int SECTION_TOPLEFT = 1;
47 public final static int SECTION_TOP = 2;
48 public final static int SECTION_TOPRIGHT = 3;
49 public final static int SECTION_LEFT = 4;
50 public final static int SECTION_CENTER = 5;
51 public final static int SECTION_RIGHT = 6;
52 public final static int SECTION_BOTTOMLEFT = 7;
53 public final static int SECTION_BOTTOM = 8;
54 public final static int SECTION_BOTTOMRIGHT = 9;
55
56 private final static int CORNER_DEGREES = 3;
57
58 private transient BufferedImage cachedImage;
59 private int borderWidth;
60 private int cornerRadius;
61 private Color color;
62 private Color background;
63 private Color borderColor;
64 private Dimension size;
65 private boolean inverse;
66
67
68
69
70
71
72
73
74
75
76
77
78 public RoundedBorderGenerator(Color borderColor, int borderWidth,
79 int cornerRadius, Color color, Color background, Dimension size,
80 boolean inverse)
81 {
82 this.borderWidth = borderWidth;
83 this.cornerRadius = cornerRadius;
84 this.color = color;
85 this.background = background;
86 this.borderColor = borderColor;
87 this.size = size;
88 this.inverse = inverse;
89 }
90
91 public void dispose()
92 {
93 cachedImage = null;
94 }
95
96
97
98
99 public boolean isInverse()
100 {
101 return this.inverse;
102 }
103
104
105
106
107 public Color getBackground()
108 {
109 return this.background;
110 }
111
112
113
114
115 public Color getBorderColor()
116 {
117 return this.borderColor;
118 }
119
120
121
122
123 public int getBorderWidth()
124 {
125 return this.borderWidth;
126 }
127
128
129
130
131 public Color getColor()
132 {
133 return this.color;
134 }
135
136
137
138
139 public int getCornerRadius()
140 {
141 return this.cornerRadius;
142 }
143
144
145
146
147 public Dimension getSize()
148 {
149 return this.size;
150 }
151
152 public BufferedImage createImageSection(int section)
153 {
154 BufferedImage img = createImage();
155
156 int max = Math.max(borderWidth, cornerRadius);
157
158 int x, y;
159 switch (section)
160 {
161 case SECTION_CENTER:
162 x = y = max;
163 break;
164 case SECTION_TOPLEFT:
165 x = y = 0;
166 break;
167 case SECTION_TOP:
168 x = max;
169 y = 0;
170 break;
171 case SECTION_TOPRIGHT:
172 x = img.getWidth() - max;
173 y = 0;
174 break;
175 case SECTION_RIGHT:
176 x = img.getWidth() - max;
177 y = max;
178 break;
179 case SECTION_BOTTOMRIGHT:
180 x = img.getWidth() - max;
181 y = img.getHeight() - max;
182 break;
183 case SECTION_BOTTOM:
184 x = max;
185 y = img.getHeight() - max;
186 break;
187 case SECTION_BOTTOMLEFT:
188 x = 0;
189 y = img.getHeight() - max;
190 break;
191 case SECTION_LEFT:
192 default:
193 x = 0;
194 y = max;
195 break;
196 }
197
198 return img.getSubimage(x, y, max, max);
199 }
200
201 public BufferedImage createImage()
202 {
203 if (cachedImage != null)
204 return cachedImage;
205
206
207 int w, h;
208 if (size != null)
209 {
210 w = (int) size.getWidth();
211 h = (int) size.getHeight();
212 }
213 else
214 {
215 h = w = Math.max(borderWidth, cornerRadius) * 3;
216 }
217
218 BufferedImage canvas = new BufferedImage(w, h,
219 BufferedImage.TYPE_INT_ARGB);
220 Graphics2D g = canvas.createGraphics();
221 Graphics2D g2 = null;
222
223 try
224 {
225
226 paintBackground(g, w, h);
227
228 RoundRectangle2D shape = new RoundRectangle2D.Float(0f, 0f, w, h,
229 cornerRadius * 2, cornerRadius * 2);
230
231 BufferedImage shapedImage = createImageForShape(g, shape);
232 g2 = shapedImage.createGraphics();
233
234 fillForeground(g2, shape);
235
236
237 if (borderColor == null)
238 create3DImage(g2, shape);
239 else
240 create2DImage(g2, shape);
241
242 g.drawImage(shapedImage, 0, 0, null);
243
244 cachedImage = canvas;
245 return canvas;
246 }
247 finally
248 {
249 if (g2 != null)
250 try
251 {
252 g2.dispose();
253 }
254 catch (Exception ex)
255 {
256 }
257 g.dispose();
258 }
259 }
260
261 private BufferedImage createImageForShape(Graphics2D g,
262 RoundRectangle2D shape)
263 {
264 int w = shape.getBounds().width;
265 int h = shape.getBounds().height;
266
267 BufferedImage img = g.getDeviceConfiguration().createCompatibleImage(w,
268 h, Transparency.TRANSLUCENT);
269 Graphics2D g2 = img.createGraphics();
270
271 try
272 {
273
274 g2.setComposite(AlphaComposite.Clear);
275 g2.fillRect(0, 0, w, h);
276
277
278
279
280
281 g2.setComposite(AlphaComposite.Src);
282 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
283 RenderingHints.VALUE_ANTIALIAS_ON);
284 g2.setColor(Color.WHITE);
285 g2.fill(shape);
286 }
287 finally
288 {
289 g2.dispose();
290 }
291
292 return img;
293 }
294
295 private void paintBackground(Graphics2D g, int width, int height)
296 {
297
298 if (background == null)
299 {
300 BufferedImage img = g.getDeviceConfiguration()
301 .createCompatibleImage(width, height,
302 Transparency.TRANSLUCENT);
303 Graphics2D g2 = img.createGraphics();
304
305
306 g2.setComposite(AlphaComposite.Clear);
307 g2.fillRect(0, 0, width, height);
308
309 g2.dispose();
310 }
311 else
312 {
313 g.setColor(background);
314 g.fillRect(0, 0, width, height);
315 }
316 }
317
318 private void fillForeground(Graphics2D g, RoundRectangle2D shape)
319 {
320
321 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
322 RenderingHints.VALUE_ANTIALIAS_ON);
323 g.setComposite(AlphaComposite.SrcAtop);
324 g.setPaint(color);
325 g.fill(shape);
326 }
327
328
329
330
331 private void create3DImage(Graphics2D g, RoundRectangle2D shape)
332 {
333 int w = (int) shape.getWidth();
334 int h = (int) shape.getHeight();
335
336
337 float factor = .3f;
338 Color lighter = lighter(color, factor);
339 Color darker = darker(color, factor);
340
341
342 g.setClip(new Polygon(new int[] { 0, w, 0 }, new int[] { 0, 0, h }, 3));
343 paint3DBorder(g, inverse ? darker : lighter, shape);
344
345
346 g.setClip(new Polygon(new int[] { 0, w, w }, new int[] { h, 0, h }, 3));
347 paint3DBorder(g, inverse ? lighter : darker, shape);
348
349
350
351 g.setClip(null);
352 int corner = Math.max(borderWidth, cornerRadius);
353 paint3DBorderTransition(g, inverse ? darker : lighter,
354 inverse ? lighter : darker, w - corner * 2, 0, true);
355 paint3DBorderTransition(g, inverse ? darker : lighter,
356 inverse ? lighter : darker, 0, h - corner * 2, false);
357 g.setClip(null);
358 }
359
360 private void paint3DBorderTransition(Graphics2D g, Color c1, Color c2,
361 int x, int y, boolean upperLeft)
362 {
363
364
365
366
367 int width = borderWidth * 2;
368 int size = Math.max(borderWidth, cornerRadius);
369
370 int startAngle = upperLeft ? 0 : 180;
371
372 for (int i = 0; i < 90; i += CORNER_DEGREES)
373 {
374 Color outerColor = combineColors(c1, c2, (upperLeft ? i : 90 - i) / 90f);
375 g.setClip(new Arc2D.Float(x, y, size * 2, size * 2,
376 startAngle + i, CORNER_DEGREES, Arc2D.PIE));
377 paint3DBorder(g, outerColor, new Arc2D.Float(x, y, size * 2, size * 2,
378 startAngle + i, CORNER_DEGREES, Arc2D.OPEN));
379 }
380 }
381
382 private void paint3DBorder(Graphics2D g, Color c, Shape shape)
383 {
384
385
386
387
388 int width = borderWidth * 2;
389 for (float i = 0; i <= width; i += 1)
390 {
391 float percent = (float) Math.pow((i / width), 3);
392 g.setPaint(combineColors(c, color, percent));
393 g.setStroke(new BasicStroke(width - i));
394 g.draw(shape);
395 }
396 }
397
398 private void create2DImage(Graphics2D g, RoundRectangle2D shape)
399 {
400 if (borderWidth == 0)
401 return;
402
403 RoundRectangle2D borderShape = new RoundRectangle2D.Float(0f, 0f,
404 (float) shape.getWidth() - 1, (float) shape.getHeight() - 1,
405 cornerRadius * 2, cornerRadius * 2);
406
407 g.setStroke(new BasicStroke(borderWidth));
408 g.setColor(borderColor);
409 g.draw(borderShape);
410 }
411
412
413
414
415 private Color combineColors(Color c1, Color c2, float weight)
416 {
417 float r = c1.getRed() * weight + c2.getRed() * (1 - weight);
418 float g = c1.getGreen() * weight + c2.getGreen() * (1 - weight);
419 float b = c1.getBlue() * weight + c2.getBlue() * (1 - weight);
420 float a = c1.getAlpha() * weight + c2.getAlpha() * (1 - weight);
421 return new Color((int) r, (int) g, (int) b, (int) a);
422 }
423
424
425
426
427
428
429
430
431
432 private Color lighter(Color c, float factor)
433 {
434 return new Color(Math
435 .min((int) (Math.max(c.getRed(), 50) / factor), 255), Math.min(
436 (int) (Math.max(c.getGreen(), 50) / factor), 255), Math.min(
437 (int) (Math.max(c.getBlue(), 50) / factor), 255));
438 }
439
440
441
442
443
444
445
446
447
448 private Color darker(Color c, float factor)
449 {
450 return new Color(Math.max((int) (c.getRed() * factor), 0), Math.max(
451 (int) (c.getGreen() * factor), 0), Math.max(
452 (int) (c.getBlue() * factor), 0));
453 }
454
455 public static void main(String[] args)
456 {
457 FileOutputStream fos = null;
458
459 try
460 {
461 File file;
462 if (args.length == 1)
463 {
464 file = new File(args[0]);
465 }
466 else
467 {
468 file = File.createTempFile("tmp_", ".png");
469 }
470
471 RoundedBorderGenerator rbg = new RoundedBorderGenerator(null, 10,
472 10, Color.GRAY, null, new Dimension(120,
473 60), false);
474 fos = new FileOutputStream(file);
475
476 ImageIO.write(rbg.createImage(), "png", fos);
477
478 System.out.println("written to " + file.getAbsolutePath());
479 }
480 catch (Exception ex)
481 {
482 ex.printStackTrace();
483 System.exit(1);
484 }
485 finally
486 {
487 if (fos != null)
488 {
489 try
490 {
491 fos.close();
492 }
493 catch (Exception ex)
494 {
495 }
496 }
497 }
498 }
499 }