View Javadoc

1   /*
2    *  File: SwtGraphicsHelper.java 
3    *  Copyright (c) 2004-2007  Peter Kliem (Peter.Kliem@jaret.de)
4    *  A commercial license is available, see http://www.jaret.de.
5    *
6    * All rights reserved. This program and the accompanying materials
7    * are made available under the terms of the Common Public License v1.0
8    * which accompanies this distribution, and is available at
9    * http://www.eclipse.org/legal/cpl-v10.html
10   */
11  package de.jaret.util.swt;
12  
13  import org.eclipse.swt.graphics.Color;
14  import org.eclipse.swt.graphics.Device;
15  import org.eclipse.swt.graphics.GC;
16  import org.eclipse.swt.graphics.Image;
17  import org.eclipse.swt.graphics.ImageData;
18  import org.eclipse.swt.graphics.PaletteData;
19  import org.eclipse.swt.graphics.Point;
20  import org.eclipse.swt.graphics.Rectangle;
21  import org.eclipse.swt.graphics.Transform;
22  
23  /**
24   * A simple class containing several static methods for convenient painting with a SWT gc.
25   * 
26   * @author Peter Kliem
27   * @version $Id: SwtGraphicsHelper.java 726 2008-03-18 21:06:14Z kliem $
28   */
29  public class SwtGraphicsHelper {
30  
31      /**
32       * Draw a string centered between x,y at top y.
33       * @param gc gc
34       * @param string string
35       * @param left left bound
36       * @param right right bound
37       * @param y top y
38       */
39      public static void drawStringCentered(GC gc, String string, int left, int right, int y) {
40          Point extent = gc.textExtent(string);
41          int width = right - left;
42          int xx = (int) ((width - extent.x) / 2);
43          gc.drawString(string, left+xx, y);
44      }
45  
46      public static void drawStringCenteredMidX(GC gc, String string, int midx, int y) {
47          Point extent = gc.textExtent(string);
48          int xx = (int) (midx - (extent.x / 2));
49          gc.drawString(string, xx, y);
50      }
51  
52      public static void drawStringCenteredVCenter(GC gc, String string, int left, int right, int yCenter) {
53          Point extent = gc.textExtent(string);
54          // int descent = graphics.getFontMetrics().getDescent();
55          int descent = 0;
56          int width = right - left;
57          int xx = (int) ((width - extent.x) / 2);
58          int y = yCenter - (int) (extent.y / 2 - descent);
59          gc.drawString(string, left + xx, y);
60  
61      }
62  
63      public static void drawStringRightAlignedVCenter(GC gc, String string, int x, int y) {
64          Point extent = gc.textExtent(string);
65  
66          int xx = (int) (x - extent.x);
67          int yy = (int) (y - (extent.y / 2));
68          gc.drawString(string, xx, yy);
69      }
70  
71      public static void drawStringLeftAlignedVCenter(GC gc, String string, int x, int y) {
72          Point extent = gc.textExtent(string);
73  
74          int xx = x;
75          int yy = (int) (y - (extent.y / 2));
76          gc.drawString(string, xx, yy);
77      }
78  
79      public static void drawStringCentered(GC gc, String string, int xCenter, int yBase) {
80          Point extent = gc.textExtent(string);
81          int xx = xCenter - (int) ((extent.x) / 2);
82          gc.drawText(string, xx, yBase - extent.y, true);
83      }
84  
85      public static void drawStringCenteredAroundPoint(GC gc, String string, int xCenter, int yCenter) {
86          Point extent = gc.textExtent(string);
87          int xx = xCenter - (int) ((extent.x) / 2);
88          int yy = yCenter - (int) ((extent.y) / 2);
89          gc.drawText(string, xx, yy, true);
90      }
91  
92      /**
93       * Draw the string centered in the given rectangle (specified by plain values)
94       * 
95       * @param gc
96       * @param string
97       * @param x
98       * @param y
99       * @param width
100      * @param height
101      */
102     public static void drawStringCentered(GC gc, String string, int x, int y, int width, int height) {
103         Point extent = gc.textExtent(string);
104         int xx = x + (width - extent.x) / 2;
105         int yy = y + (height - extent.y) / 2;
106         gc.drawText(string, xx, yy, true);
107     }
108 
109     /**
110      * Draw String centered in the given rectangle.
111      * 
112      * @param gc
113      * @param string
114      * @param rect
115      */
116     public static void drawStringCentered(GC gc, String string, Rectangle rect) {
117         drawStringCentered(gc, string, rect.x, rect.y, rect.width, rect.height);
118     }
119 
120     public static int getStringDrawingWidth(GC gc, String string) {
121         return gc.textExtent(string).x;
122     }
123 
124     public static int getStringDrawingHeight(GC gc, String string) {
125         return gc.textExtent(string).y;
126     }
127 
128     /**
129      * Draws a String right aligned with the y coordinate denoting the top of the string.
130      * @param gc GC
131      * @param string string to draw
132      * @param x right x position
133      * @param yTop top y position
134      */
135     public static void drawStringRightAlignedVTop(GC gc, String string, int x, int yTop) {
136         Point extent = gc.textExtent(string);
137         int xx = (int) (x - extent.x);
138         gc.drawText(string, xx, yTop, true);
139     }
140 
141     public static void drawArrowLine(GC gc, int x1, int y1, int x2, int y2, int dist, int height, boolean arrowLeft,
142             boolean arrowRight) {
143         int off = height;
144         gc.drawLine(x1 + off + 1, y1, x2 - off - 1, y2);
145         if (arrowLeft) {
146             gc.drawLine(x1, y1, x1 + dist, y1 - off);
147             gc.drawLine(x1, y1, x1 + dist, y1 + off);
148             gc.drawLine(x1 + dist, y1 - off, x1 + dist, y1 + off);
149         }
150         if (arrowRight) {
151             gc.drawLine(x2, y2, x2 - dist, y2 - off);
152             gc.drawLine(x2, y2, x2 - dist, y2 + off);
153             gc.drawLine(x2 - dist, y2 - off, x2 - dist, y2 + off);
154         }
155     }
156 
157     public static void drawArrowLineVertical(GC gc, int x1, int y1, int x2, int y2, int dist, int height,
158             boolean arrowUp, boolean arrowDown) {
159         int off = height;
160         gc.drawLine(x1, y1 + off + 1, x2, y2 - off - 1);
161         if (arrowUp) {
162             gc.drawLine(x1, y1, x1 - off, y1 + dist);
163             gc.drawLine(x1, y1, x1 + off, y1 + dist);
164             gc.drawLine(x1 - off, y1 + dist, x1 + off, y1 + dist);
165         }
166         if (arrowDown) {
167             gc.drawLine(x2, y2, x2 - off, y2 - dist);
168             gc.drawLine(x2, y2, x2 + off, y2 - dist);
169             gc.drawLine(x2 - off, y2 - dist, x2 + off, y2 - dist);
170         }
171     }
172 
173     /**
174      * Draw a string vertical centered beetween upper and lower y left aligned to x.
175      * 
176      * @param gc GC
177      * @param label label to draw
178      * @param x left x
179      * @param upperY upper y bound
180      * @param lowerY lower y bound
181      */
182     public static void drawStringVCentered(GC gc, String label, int x, int upperY, int lowerY) {
183         Point extent = gc.textExtent(label);
184 
185         int yy = (int) upperY + (lowerY - upperY - extent.y) / 2;
186         gc.drawText(label, x, yy, true);
187     }
188 
189     /**
190      * Draw a String vertical. This method might be quite costly since it uses an image to buffer.
191      * 
192      * @param gc gc
193      * @param string strin gto to draw
194      * @param x upper left x
195      * @param y upper left y
196      */
197     public static void drawStringVertical(GC gc, String string, int x, int y) {
198         Point extent = gc.textExtent(string);
199         Image img = new Image(gc.getDevice(), extent.x, extent.y);
200         GC imageGC = new GC(img);
201         imageGC.drawString(string, 0, 0);
202         imageGC.dispose();
203 
204         
205         Image vertImg = new Image(gc.getDevice(), extent.y, extent.x);
206         ImageData iData = img.getImageData();
207         ImageData destIData = vertImg.getImageData();
208 
209         for (int xx = 0; xx < iData.width; xx++) {
210             for (int yy = 0; yy < iData.height; yy++) {
211                 destIData.setPixel(yy, iData.width-xx-1, iData.getPixel(xx, yy));
212             }
213         }
214 
215         img.dispose();
216         Image destImg = new Image(gc.getDevice(), destIData);
217         
218         gc.drawImage(destImg, x, y);
219         vertImg.dispose();
220         destImg.dispose();
221 
222     }
223     /**
224      * Create an image with a drop shadow. This method is (c) 2007 Nicholas Rajendram, IBM Canada, see
225      * http://www.eclipse.org/articles/article.php?file=Article-SimpleImageEffectsForSWT/index.html.
226      * 
227      * @param originalImageData The original image. Transparency information will be ignored.
228      * @param color The color of the drop shadow
229      * @param radius The radius of the drop shadow in pixels
230      * @param highlightRadius The radius of the highlight area
231      * @param opacity The opacity of the drop shadow
232      * @return The drop shadowed image. This image data will be larger than the original. The same image data will be
233      * returned if the shadow radius is 0, or null if an error occured.
234      */
235     public static ImageData dropShadow(ImageData originalImageData, Color color, int radius, int highlightRadius,
236             int opacity) {
237         /*
238          * This method will create a drop shadowto the bottom-right of an existing image. This drop shadow is created by
239          * creating an altered one-sided glow, and shifting its position around the image. See the Glow class for more
240          * details of how the glow is calculated.
241          */
242         if (originalImageData == null)
243             return null;
244         if (color == null)
245             return null;
246         if (radius == 0)
247             return originalImageData;
248         int shift = (int) (radius * 1.5); // distance to shift "glow" from image
249         // the percent increase in color intensity in the highlight radius
250         double highlightRadiusIncrease = radius < highlightRadius * 2 ? .15 : radius < highlightRadius * 3 ? .09 : .02;
251         opacity = opacity > 255 ? 255 : opacity < 0 ? 0 : opacity;
252         // prepare new image data with 24-bit direct palette to hold shadowed copy of image
253         ImageData newImageData = new ImageData(originalImageData.width + radius * 2, originalImageData.height + radius
254                 * 2, 24, new PaletteData(0xFF, 0xFF00, 0xFF0000));
255         int[] pixels = new int[originalImageData.width];
256         // copy image data
257         for (int row = radius; row < radius + originalImageData.height; row++) {
258             originalImageData.getPixels(0, row - radius, originalImageData.width, pixels, 0);
259             for (int col = 0; col < pixels.length; col++)
260                 pixels[col] = newImageData.palette.getPixel(originalImageData.palette.getRGB(pixels[col]));
261             newImageData.setPixels(radius, row, originalImageData.width, pixels, 0);
262         }
263         // initialize glow pixel data
264         int colorInt = newImageData.palette.getPixel(color.getRGB());
265         pixels = new int[newImageData.width];
266         for (int i = 0; i < newImageData.width; i++) {
267             pixels[i] = colorInt;
268         }
269         // initialize alpha values
270         byte[] alphas = new byte[newImageData.width];
271         // deal with alpha values on rows above and below the photo
272         for (int row = 0; row < newImageData.height; row++) {
273             if (row < radius) {
274                 // only calculate alpha values for top border. they will reflect to the bottom border
275                 byte intensity = (byte) (opacity * ((((row + 1)) / (double) (radius))));
276                 for (int col = 0; col < alphas.length / 2 + alphas.length % 2; col++) {
277                     if (col < radius) {
278                         // deal with corners:
279                         // calculate pixel's distance from image corner
280                         double hypotenuse = Math
281                                 .sqrt(Math.pow(radius - col - 1, 2.0) + Math.pow(radius - 1 - row, 2.0));
282                         // calculate alpha based on percent distance from image
283                         alphas[col + shift] = alphas[alphas.length - col - 1] = (byte) (opacity * Math.max(
284                                 ((radius - hypotenuse) / radius), 0));
285                         // add highlight radius
286                         if (hypotenuse < Math.min(highlightRadius, radius * .5)) {
287                             alphas[col + shift] = alphas[alphas.length - col - 1] = (byte) Math.min(255, (alphas[col
288                                     + shift] & 0x0FF)
289                                     * (1 + highlightRadiusIncrease * Math.max(((radius - hypotenuse) / radius), 0)));
290                         }
291                     } else {
292                         alphas[col + shift] = alphas[alphas.length - col - 1] = (byte) ((row > Math.max(radius
293                                 - highlightRadius - 1, radius * .5)) ? Math.min(255, (intensity & 0x0FF)
294                                 * (1 + highlightRadiusIncrease * row / radius)) : intensity);
295                     }
296                 }
297                 if (row + shift < newImageData.height) {
298                     newImageData.setAlphas(newImageData.width - radius, row + shift, radius, alphas, alphas.length
299                             - radius);
300                     newImageData.setPixels(newImageData.width - radius, row + shift, radius, pixels, alphas.length
301                             - radius);
302                 }
303                 newImageData.setAlphas(0, newImageData.height - 1 - row, newImageData.width, alphas, 0);
304                 newImageData.setPixels(0, newImageData.height - 1 - row, newImageData.width, pixels, 0);
305             }
306             // deal with rows the image resides on
307             else if (row <= newImageData.height / 2) {
308                 // calculate alpha values
309                 double intensity = 0;
310                 for (int col = 0; col < alphas.length; col++) {
311                     if (col < radius) {
312                         intensity = (opacity * ((col + 1) / (double) radius));
313                         if (col > Math.max(radius - highlightRadius - 1, radius * .5)) {
314                             intensity = Math.min(255, (intensity) * (1 + highlightRadiusIncrease * col / radius));
315                         }
316                         alphas[newImageData.width - col - 1] = (byte) (int) (intensity);
317                         alphas[col] = 0;
318                     } else if (col <= newImageData.width / 2 + newImageData.width % 2) {
319                         // original image pixels are full opacity
320                         alphas[col] = alphas[newImageData.width - col - 1] = (byte) (255);
321                     }
322                 }
323                 newImageData.setPixels(0, newImageData.height - 1 - row, radius, pixels, 0);
324                 newImageData.setPixels(originalImageData.width + radius, newImageData.height - 1 - row, radius, pixels,
325                         0);
326                 newImageData.setAlphas(0, newImageData.height - 1 - row, newImageData.width, alphas, 0);
327                 if (row >= shift + radius) {
328                     newImageData.setPixels(0, row, radius, pixels, 0);
329                     newImageData.setPixels(originalImageData.width + radius, row, radius, pixels, 0);
330                     newImageData.setAlphas(0, row, newImageData.width, alphas, 0);
331                 } else {
332                     newImageData.setPixels(0, row, radius, pixels, 0);
333                     newImageData.setAlphas(0, row, newImageData.width - radius, alphas, 0);
334                 }
335             }
336         }
337         return newImageData;
338     }
339 
340     /**
341      * Create an image with a glow effect. This method is (c) 2007 Nicholas Rajendram, IBM Canada, see
342      * http://www.eclipse.org/articles/article.php?file=Article-SimpleImageEffectsForSWT/index.html.
343      * 
344      * @param originalImageData The original image. Transparency information will be ignored.
345      * @param color The color of the glow
346      * @param radius The radius of the glow in pixels
347      * @param highlightRadius The radius of the highlight area
348      * @param opacity The opacity of the glow
349      * @return The glowing image. This image data will be larger than the original. The same image data will be returned
350      * if the glow radius is 0, or null if an error occured.
351      */
352     public static ImageData glow(ImageData originalImageData, Color color, int radius, int highlightRadius, int opacity) {
353         /*
354          * This method will surround an existing image with a glowing border. This glow is created by adding a solid
355          * colored border around an image. Alpha values are then manipulated in order to blend the border with its
356          * background. This gives a glowing appearance.
357          * 
358          * To obtain the alpha value of a glow pixel, its position in the border radius as a percent of the radius'
359          * total width is first calculated. This percentage is multipled by the maximum opacity level, giving pixels an
360          * outward linear blend from the image from opaque to transparent.
361          * 
362          * A highlight radius increases the intensity of a given radius of pixels surrounding the image to better
363          * highlight it. When there is a highlight radius, the entire glow's overall alpha blending is non-linear.
364          */
365         if (originalImageData == null)
366             return null;
367         if (color == null)
368             return null;
369         if (radius == 0)
370             return originalImageData;
371         // the percent increase in color intensity in the highlight radius
372         double highlightRadiusIncrease = radius < highlightRadius * 2 ? .15 : radius < highlightRadius * 3 ? .09 : .02;
373         opacity = opacity > 255 ? 255 : opacity < 0 ? 0 : opacity;
374         // prepare new image data with 24-bit direct palette to hold glowing copy of image
375         ImageData newImageData = new ImageData(originalImageData.width + radius * 2, originalImageData.height + radius
376                 * 2, 24, new PaletteData(0xFF, 0xFF00, 0xFF0000));
377         int[] pixels = new int[originalImageData.width];
378         // copy image data
379         for (int row = radius; row < radius + originalImageData.height; row++) {
380             originalImageData.getPixels(0, row - radius, originalImageData.width, pixels, 0);
381             for (int col = 0; col < pixels.length; col++)
382                 pixels[col] = newImageData.palette.getPixel(originalImageData.palette.getRGB(pixels[col]));
383             newImageData.setPixels(radius, row, originalImageData.width, pixels, 0);
384         }
385         // initialize glow pixel data
386         int colorInt = newImageData.palette.getPixel(color.getRGB());
387         pixels = new int[newImageData.width];
388         for (int i = 0; i < newImageData.width; i++) {
389             pixels[i] = colorInt;
390         }
391         // initialize alpha values
392         byte[] alphas = new byte[newImageData.width];
393         // deal with alpha values on rows above and below the photo
394         for (int row = 0; row < newImageData.height; row++) {
395             if (row < radius) {
396                 // only calculate alpha values for top border. they will reflect to the bottom border
397                 byte intensity = (byte) (opacity * ((((row + 1)) / (double) (radius))));
398                 for (int col = 0; col < alphas.length / 2 + alphas.length % 2; col++) {
399                     if (col < radius) {
400                         // deal with corners:
401                         // calculate pixel's distance from image corner
402                         double hypotenuse = Math
403                                 .sqrt(Math.pow(radius - col - 1, 2.0) + Math.pow(radius - 1 - row, 2.0));
404                         // calculate alpha based on percent distance from image
405                         alphas[col] = alphas[alphas.length - col - 1] = (byte) (opacity * Math.max(
406                                 ((radius - hypotenuse) / radius), 0));
407                         // add highlight radius
408                         if (hypotenuse < Math.min(highlightRadius, radius * .5)) {
409                             alphas[col] = alphas[alphas.length - col - 1] = (byte) Math.min(255, (alphas[col] & 0x0FF)
410                                     * (1 + highlightRadiusIncrease * Math.max(((radius - hypotenuse) / radius), 0)));
411                         }
412                     } else {
413                         alphas[col] = alphas[alphas.length - 1 - col] = (byte) ((row > Math.max(radius
414                                 - highlightRadius - 1, radius * .5)) ? Math.min(255, (intensity & 0x0FF)
415                                 * (1 + highlightRadiusIncrease * row / radius)) : intensity);
416                     }
417                 }
418                 newImageData.setAlphas(0, row, newImageData.width, alphas, 0);
419                 newImageData.setAlphas(0, newImageData.height - 1 - row, newImageData.width, alphas, 0);
420                 newImageData.setPixels(0, row, newImageData.width, pixels, 0);
421                 newImageData.setPixels(0, newImageData.height - 1 - row, newImageData.width, pixels, 0);
422             }
423             // deal with rows the image resides on
424             else if (row <= newImageData.height / 2) {
425                 // calculate alpha values
426                 double intensity = 0;
427                 for (int col = 0; col < alphas.length; col++) {
428                     if (col < radius) {
429                         intensity = (opacity * ((col + 1) / (double) radius));
430                         if (col > Math.max(radius - highlightRadius - 1, radius * .5)) {
431                             intensity = Math.min(255, (intensity) * (1 + highlightRadiusIncrease * col / radius));
432                         }
433                         alphas[col] = alphas[newImageData.width - col - 1] = (byte) (intensity);
434                     } else if (col <= newImageData.width / 2 + newImageData.width % 2) {
435                         // original image pixels are full opacity
436                         alphas[col] = alphas[newImageData.width - col - 1] = (byte) (255);
437                     }
438                 }
439                 newImageData.setPixels(0, row, radius, pixels, 0);
440                 newImageData.setPixels(originalImageData.width + radius, row, radius, pixels, 0);
441                 newImageData.setAlphas(0, row, newImageData.width, alphas, 0);
442                 newImageData.setPixels(0, newImageData.height - 1 - row, radius, pixels, 0);
443                 newImageData.setPixels(originalImageData.width + radius, newImageData.height - 1 - row, radius, pixels,
444                         0);
445                 newImageData.setAlphas(0, newImageData.height - 1 - row, newImageData.width, alphas, 0);
446             }
447         }
448         return newImageData;
449     }
450     /**
451      * Create a reflection image (Idea taken from Daniel Spiewak: see
452      * http://www.eclipsezone.com/eclipse/forums/t91013.html?start=0).
453      * 
454      * @param img image to reflect
455      * @param device device
456      * @return image (larger than the original) containing a reflectd version of the original image
457      */
458     public static Image reflect(Image img, Device device) {
459 
460         int height = img.getImageData().height;
461         int width = img.getImageData().width;
462 
463         Image reflect = new Image(device, width, height / 2);
464         GC imageGC = new GC(reflect);
465 
466         Transform rTransform = new Transform(imageGC.getDevice(), 1, 0, 0, -.5f, 0, height / 2);
467         imageGC.setTransform(rTransform);
468 
469         imageGC.setAlpha(100);
470 
471         imageGC.drawImage(img, 0, 0);
472         // imageGC.setTransform(null);
473         // // shade it
474         // int alpha =100;
475         // imageGC.setAlpha(100);
476         // imageGC.setForeground(imageGC.getDevice().getSystemColor(SWT.COLOR_WHITE));
477         // for (int y = 0;y<height/2;y++) {
478         // imageGC.setAlpha(alpha);
479         // imageGC.drawLine(0, y, width, y);
480         // alpha = alpha + 255/height;
481         // System.out.println("ALPHA "+alpha+" y "+y);
482         // }
483 
484         imageGC.dispose();
485         return reflect;
486     }
487 
488 }