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