| <!doctype html public "-//w3c//dtd html 4.0 transitional//en"> |
| <html |
| |
| <head> |
| <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> |
| <meta name="Author" content="Joe Winchester"> |
| <meta name="GENERATOR" content="Microsoft FrontPage 4.0"> |
| <meta name="ProgId" content="FrontPage.Editor.Document"> |
| <title>Taking a look at SWT Images</title> |
| <link href="../default_style.css" rel=stylesheet> |
| </head> |
| |
| <body> |
| |
| <div align="right"> |
| <font face="Times New Roman, Times, serif"><font size="-1">Copyright © 2003 |
| International Business Machines Corp.</font></font> |
| </div> |
| <div align="right"> |
| <table border="0" cellspacing="0" cellpadding="2" width="100%"> |
| <tr> |
| <td align="LEFT" valign="TOP" colspan="2" bgcolor="#0080C0"><b><font face="Arial,Helvetica"><font color="#FFFFFF">Eclipse |
| Corner Article</font></font></b></td> |
| </tr> |
| </table> |
| </div> |
| <h1><img src="images/Idea.jpg" height="86" width="120" align="CENTER"></h1> |
| <center> |
| <h1>Taking a look at SWT Images</h1> |
| </center> |
| <blockquote> |
| <b>Summary</b><br> |
| SWT's Image class can be used to display images in a GUI. The most common |
| source of images is to load from a standard file format such as GIF, JPEG, |
| PNG, or BMP. Some controls, including Buttons and TreeItems, are able to |
| display an Image directly through the setImage(Image) method, but any |
| control's paint event allows images to be drawn through the callback's graphic |
| context. SWT's ImageData class represents the raw data making up an SWT Image |
| and determines the color for each pixel coordinate. This article shows the |
| correct uses of ImageData and Image, shows how to load images from files, and |
| how to achieve graphic effects such as transparency, alpha blending, |
| animation, scaling, and custom cursors. |
| <p><b>By Joe Winchester, IBM</b><br> |
| <font size="-1">September 10th, 2003</font></blockquote> |
| <hr width="100%"> |
| <p>This first section of this article gives an introduction to colors and shows |
| how an image records the color value of each pixel. |
| <ul> |
| <li><a href="#Introduction">Introduction</a></li> |
| <li><a href="#Image lifecycle">Image lifecycle</a></li> |
| <li><a href="#ImageData">ImageData</a></li> |
| <li><a href="#Colors">Color</a></li> |
| <li><a href="#PaletteData">PaletteData</a></li> |
| <ul> |
| <li><a href="#Indexed palette">Indexed palette</a></li> |
| <li><a href="#Direct palette">Direct palette</a></li> |
| </ul> |
| </ul> |
| The next section describes image transparency, alpha blending, animation, and |
| how to scale images. |
| <ul> |
| <li><a href="#Transparency">Transparency</a></li> |
| <li><a href="#Manipulating Image Data">Manipulating ImageData</a></li> |
| <li><a href="#Saving Images">Saving Images</a></li> |
| <li><a href="#Blending">Blending</a></li> |
| <ul> |
| <li><a href="#Single alpha value">Single alpha value</a></li> |
| <li><a href="#Different alpha value per pixel">Different alpha value per |
| pixel</a></li> |
| </ul> |
| <li><a href="#Image Effects">Image effects</a></li> |
| <li><a href="#Animation">GIF animation</a></li> |
| <li><a href="#Scaling">Scaling</a></li> |
| </ul> |
| Finally, the article shows how to create cursors from images, by using a source |
| image together with a mask. |
| <ul> |
| <li><a href="#Cursor">Cursors</a></li> |
| <ul> |
| <li><a href="#Platform cursors">Platform cursors</a></li> |
| <li><a href="#Custom cursors">Custom cursors</a></li> |
| </ul> |
| </ul> |
| <h1><a name="Introduction"></a>Introduction</h1> |
| The simplest way to create an SWT Image is to load it from a recognized graphic |
| file format. This includes GIF, BMP (Windows format bitmap), JPG, and PNG. The |
| TIFF format is also supported in more recent Eclipse releases. Images can be |
| loaded from a known location in the file system using the constructor <tt>Image(Display |
| display, String fileLocation)</tt>: |
| <p><tt>Image image = new Image(display,<br> |
| |
| "C:/eclipse/eclipse/plugins/org.eclipse.platform_2.0.2/eclipse_lg.gif");</tt> |
| <p>Instead of hard-coding the location of the image, it's more common to load |
| the Image from a folder location relative to a given class. This is done by |
| creating an InputStream pointing to the file with the method <tt>Class.getResourceAsStream(String |
| name)</tt>, and using the result as the argument to the constructor <tt>Image(Display |
| display, InputStream inputStream)</tt>. |
| <p>The Eclipse package explorer below shows the class <tt>com.foo.ShellWithButtonShowingEclipseLogo |
| </tt>and the <tt>eclipse_lg.gif</tt> in the same folder. To following code would |
| load the graphic from its location relative to the class. |
| <p><tt>Image image = new Image(display, <br> |
| |
| ShellWithButtonShowingEclipseLogo.class.getResourceAsStream(<br> |
| "eclipse_lg.gif"));</tt> |
| <p><img src="images/IntroductionRelativeIcon.gif" height="184" width="298"> |
| <p>Once the image has been created it can be used as part of a control such as a |
| Button or Label that is able to render the graphic as part of their <tt>setImage(Image |
| image)</tt> methods. |
| <p><tt>Button button = new Button(shell,SWT.PUSH);</tt><br> |
| <tt>button.setImage(image);</tt> |
| <p><img src="images/ButonLogo.gif" height="220" width="278"> |
| <p>Images can be drawn onto using a graphics context that is created with the |
| constructor <tt>GC(Drawable drawable)</tt> with the Image as the argument. |
| <p><tt>GC gc = new GC(image);</tt><br> |
| <tt>gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));</tt><br> |
| <tt>gc.drawText("I've been drawn on",0,0,true);</tt><br> |
| <tt>gc.dispose();</tt> |
| <p><img src="images/IveBeenDrawnOn.gif" height="218" width="276"> |
| <p>Using a GC to draw onto an Image permanently alters the graphic. More |
| information on how to use a GC is covered in the article <a href="http://www.eclipse.org/articles/Article-SWT-graphics/SWT_graphics.html">Graphics |
| Context - Quick on the draw.</a> |
| <h1><a name="Image lifecycle"></a>Image lifecycle</h1> |
| When an image is loaded, the first step is to create device independent ImageData |
| represented by the class <tt>org.eclipse.swt.graphics.ImageData</tt>. Following |
| this, the data is prepared for a specific device by creating an actual Image instance. |
| <p>As well as loading an Image directly from a file, you can separately create |
| the ImageData object and then construct the Image using <tt>Image(Device device, |
| ImageData imageData)</tt>. The data for an existing Image can be retrieved using |
| <tt>getImageData()</tt>, although this will not be the same object that was |
| used to create the image. This is because when preparing an image to be drawn |
| onto a screen, properties such as its color depth might be different from the |
| initial image data. |
| <p>Instances of Image represent an underlying resource that has been prepared |
| for a specific device and they must be disposed when they are no longer required |
| to free up the allocated resource. There is no finalization of resources in SWT |
| when an object is garbage collected. For more information see <a href="http://www.eclipse.org/articles/swt-design-2/swt-design-2.html">SWT: |
| The Standard Widget Toolkit: Managing Operating System Resources.</a> |
| <h2><a name="ImageData"></a>ImageData</h2> |
| ImageData can be thought of as the model for an image, whereas the Image is the |
| view that's been prepared for output onto a specific device. The ImageData has a |
| width and height, and a pixel value for each coordinate. The raw data of the |
| image is a byte[], and the depth of the image specifies the number of bits that |
| are used for each coordinate. An image depth of 1 can store two possible values |
| for each pixel (0 and 1), a depth of 4 can store 2^4=16, a depth of 8 can |
| store 2^8=256 values and a depth of 24 can represent 2^24=16 million different |
| values per pixel. The larger the depth of an image the more bytes are required |
| for its pixels, and some formats such as GIF that were initially designed for |
| download across internet connections only support a maximum depth of 8. How the |
| value of each pixel value translates into an actual color depends on its palette |
| represented by the class <tt>org.eclipse.swt.graphics.PaletteData.</tt> |
| <p>The next section describes how <a href="#Colors">colors</a> are represented |
| by their RGB values, and how <a href="#PaletteData">PaletteData</a> maps a map |
| pixel value to a particular color. |
| <h2><a name="Colors"></a>Color</h2> |
| The class <tt>org.eclipse.swt.graphics.Color </tt>is used to manage resources |
| that implement the RGB color model. Each color is described in terms of its red, |
| green and blue component (each expressed as an integer value from 0 for no color |
| to 255 for full color). |
| <p><tt>Color cyanColor = new Color(display,0,255,255);</tt><br> |
| <tt><font color="#00CC00">// ... Code to use the Color</font></tt><br> |
| <tt>cyanColor.dispose();</tt> |
| <p>The convenience class <tt>org.eclipse.swt.graphics.RGB </tt>exists in SWT |
| that combines a color's red, green and blue values into a single object. |
| <p><tt>RGB cyanRGB = new RGB(0,255,255);</tt><br> |
| <tt>Color cyanColor = new Color(display,cyanRGB);</tt><br> |
| <tt><font color="#009900">// ... Code to use the Color</font></tt><br> |
| <tt>cyanColor.dispose();</tt> |
| <p>The Color instance should be disposed when it is no longer required, whereas |
| the RGB has no need to be disposed. This is similar to the relationship between |
| an Image and its ImageData, where Color and Image are device specific objects |
| using underlying native resources, while RGB and ImageData are the underlying |
| model data. |
| <p>To avoid having to create and manage instances of the commonly used colors, |
| the Display class allow these to be retrieved using the method <tt>Display.getSystemColor(int |
| id)</tt>. |
| <p><tt>Color cyanColor = display.getSystemColor(SWT.COLOR_CYAN)</tt> |
| <p>When a Color is obtained by an SWT program using the method <tt>Display.getSystemColor(int |
| id) </tt>method, it must not be disposed. The rule of thumb that works for any |
| SWT resource is <i>"If you created it, you are responsible for disposing it</i>". |
| Because the statement above retrieved the cyan color instance, and didn't |
| explicitly construct it, it should not be disposed. |
| <p>How a Color is actually represented on the display depends on factors such |
| as the resolution and depth of the display. For more information on this and |
| the SWT color model see <a href="http://www.eclipse.org/articles/SWT%20Color%20Model/swt-color-model.htm">SWT |
| Color Model.</a> |
| <h2><a name="PaletteData"></a>PaletteData</h2> |
| There are two kinds of PaletteData, an <a href="#Indexed palette">indexed palette |
| </a>and a <a href="#Direct palette">direct palette</a>. PaletteData is a model |
| of how pixel values map to RGB values, and because they do not represent an underlying |
| resource, they do not require disposing. |
| <h3><a name="Indexed palette"></a>Indexed palette</h3> |
| With an indexed palette each pixel value represents a number that is then cross |
| indexed with the palette to determine the actual color. The range of allowable |
| pixel values is the depth of the image. |
| <p>The example below is a 48 by 48 square image created with a depth of 1, and |
| an indexed color palette. The indexed palette assigns <img src="images/tag_1.gif" height="13" width="24"> |
| 0 to be red and 1 to be green (by virtue of their order in the RGB[] in the |
| constructor). The ImageData's un-initialized pixel values will initially be |
| 0 (red), and two for loops <img src="images/tag_2.gif" height="13" width="24"> |
| set a 34 by 34 square in the center of the ImageData to be 1 (green). |
| <p><img src="images/tag_1.gif" height="13" width="24"><tt> PaletteData |
| paletteData = new PaletteData(<br> |
| new RGB[] {new RGB(255,0,0), new |
| RGB(0,255,0)});</tt><br> |
| <tt> ImageData imageData = new ImageData(48,48,1,paletteData);</tt><br> |
| <img src="images/tag_2.gif" height="13" width="24"><tt> for(int |
| x=11;x<35;x++){</tt><br> |
| <tt> for(int y=11;y<35;y++){</tt><br> |
| <tt> |
| imageData.setPixel(x,y,1);</tt><br> |
| <tt> }</tt><br> |
| <tt> }</tt><br> |
| <tt> Image image = new Image(display,imageData);</tt> |
| <p><img src="images/RedGreenImage.gif" height="48" width="48"> |
| <p>The above example has a depth of 1 so it can store 2 colors, but as the color |
| depth of the ImageData increases then so can the number of colors in the palette. |
| An indexed palette can have a 1, 2, 4, or 8 bit depths, and an 8 bit depth provides |
| 2^8 = 256 possible colors. To have a higher color depth (such as 16, 24, |
| or 32) a direct palette must be used. |
| <h3><a name="Direct palette"></a>Direct palette</h3> |
| Instead of having each pixel value represent an index in the palette |
| corresponding to its color, a direct palette allows each pixel value to directly |
| record its red, green and blue component. A direct <tt>PaletteData</tt> |
| defines red, green and blue masks. These masks are number of bits required |
| to shift a pixel value to the left so the high bit of the mask aligns with the |
| high bit of the first byte of color. For example, a 24 bit direct palette |
| can divide itself into 3 portions, storing red in the lowest 8 bits, green in |
| the central 8 bits and blue in the highest 8 bits. The red shift mask |
| would be 0xFF, green 0xFF00 and blue 0xFF0000. |
| <p><img src="images/DirectPalette_02.gif" height="250" width="596"> |
| <p>The value for each pixel represents a combination of the red, green and blue |
| components into a single 24 bit integer. To construct an indexed palette the |
| constructor used <img src="images/tag_1.gif" height="13" width="24"> allows |
| the red, green and blue color masks to be specified. |
| <p><img src="images/tag_1.gif" height="13" width="24"><tt> PaletteData palette = |
| new PaletteData(0xFF , 0xFF00 , 0xFF0000);</tt><br> |
| <tt> ImageData imageData = new ImageData(48,48,24,palette);</tt> |
| <p>Using the same technique as earlier, the code iterates over every pixel coordinate |
| setting it to either <img src="images/tag_2.gif" height="13" width="24"> |
| 0xFF (for red) or <img src="images/tag_3.gif" height="13" width="24"> 0xFF00 |
| (for green). |
| <p><tt> for (int x=0;x<48;x++){</tt><br> |
| <tt> for(int y=0;y<48;y++){</tt><br> |
| <tt> if(y > |
| 11 && y < 35 && x > 11 && x < 35){</tt><br> |
| <img src="images/tag_2.gif" height="13" width="24"><tt> |
| imageData.setPixel(x,y,0xFF00); <font color="#009900">// Set the |
| center to green</font></tt><br> |
| <tt> } else {</tt><br> |
| <tt> |
| imageData.setPixel(x,y,0xFF); <font color="#009900">// and |
| everything else to red</font></tt><br> |
| <img src="images/tag_1.gif" height="13" width="24"><tt> |
| }</tt><br> |
| <tt> }</tt><br> |
| <tt> };</tt><br> |
| <tt> Image image = new Image(display,imageData);</tt> |
| <p>This creates the result below where the image is red with a green center. |
| <p><img src="images/RedGreenImage.gif" height="48" width="48"> |
| <p>Because you can use color depths of 16, 24 and 32 bits with direct palettes, |
| you can represent more colors than are available with an indexed palette whose |
| maximum depth is 8. A color depth of 24 allows you to represent 16 million colors |
| (2^24). The tradeoff however is size, because an indexed palette with a depth |
| of 8 requires one byte per image coordinate whereas a direct palette with a |
| depth of 24 requires three bytes per image coordinate. |
| <p>With both direct and indexed palettes you can go from an RGB to a pixel value |
| and vice-versa using the public methods <tt>int getPixel(RGB rgb)</tt> and <tt>RGB getRGB(int |
| pixelValue)</tt>. |
| <h2><a name="Transparency"></a>Transparency</h2> |
| The purpose of transparency is to make a portion of the image non-opaque, so |
| when it is drawn on a GUI surface the original background shows through. This is |
| done by specifying that one of the image colors is transparent. Whenever a pixel |
| with the transparent color value is drawn, instead of using the RGB value |
| defined in the palette the original destination background pixel color is used |
| instead. The effect to the user is that the areas of the image that equal the |
| transparent pixel show the background of whatever the image is being drawn over, |
| thus achieving transparency. Persisted image file formats such as GIF or BMP |
| allow you to specify a transparent pixel value, although only if the palette is |
| indexed and the color depth is 8 or less. |
| <p>When Images are used directly on controls such as Button or Label the native |
| behavior may be that transparent pixels are ignored and drawn in the pixel color |
| specified by the source. Native image transparency however is supported in SWT |
| for operations involving a GC. To illustrate this the following file Idea.gif |
| has a color depth of 8, and the white pixel (index 255 in the palette) set to be |
| the transparent pixel. |
| <p><img src="images/Idea.gif" border="0" height="86" width="120"> |
| <p>The shell below has a Label on the left with a Canvas next to it. The |
| Idea.gif is used <img src="images/tag_1.gif" height="13" width="24"> as the |
| label's image, and also in the paint event <img src="images/tag_2.gif" height="13" width="24"> |
| of the Canvas. Because the Label does not support native transparency the |
| original white color of the transparent pixel is used as the background, however |
| the GC in the paint event respects the transparent pixel and the grey background |
| shows through. |
| <p><tt> Image ideaImage = new |
| ImageData(getClass().getResourceAsStream("Idea.gif"));</tt><br> |
| <tt> Label label = new Label(shell,SWT.NONE);</tt><br> |
| <img src="images/tag_1.gif" height="13" width="24"><tt> |
| label.setImage(ideaImage);</tt><br> |
| <tt> Canvas canvas = new Canvas(shell,SWT.NO_REDRAW_RESIZE);</tt><br> |
| <tt> canvas.addPaintListener(new PaintListener() {</tt><br> |
| <tt> public void |
| paintControl(PaintEvent e) {</tt><br> |
| <img src="images/tag_2.gif" height="13" width="24"><tt> |
| e.gc.drawImage(ideaImage,0,0);</tt><br> |
| <tt> }</tt><br> |
| <tt> });</tt> |
| <p><img src="images/TransparentIdea.gif" height="146" width="270"> |
| <p>For the above example I stacked the deck in my favor, because I didn't |
| actually use the idea graphic that is included with eclipse articles in their |
| banner. The reason is that the original graphic is a JPG file which doesn't |
| support transparency, so I used a graphics tool to convert it to a GIF and set |
| the value of the white pixel in the palette to be the transparency pixel. The |
| original Idea.jpg is shown below, and although it looks the same as the |
| Idea.gif, this is because it is on the white background of the HTML browser. |
| <p><img src="images/Idea.jpg" border="0" height="86" width="120"> |
| <p>By using the original JPG file this offers a good example of how we to |
| achieve a transparency effect progrmatically by manpulating its ImageData. The |
| ImageData class has a public field <tt>transparentPixel</tt> to specify which |
| pixel is transparent that can be set once a persisted image file is loaded into |
| an ImageData instance, irrespective of whether the persisted file format |
| supports transparency. |
| <p>The code below loads the Idea.jpg file in an ImageData object and <img src="images/tag_1.gif" height="13" width="24"> |
| sets the transparent pixel for the ImageData to be the pixel value of the color |
| white in the palette. The pixel value in the indexed palette that represents |
| white is retrieved <img src="images/tag_2.gif" height="13" width="24"> by |
| using getPixel(RGB). The manipulated ImageData is used to create an Image |
| transparentIdeaImage that now has the white pixel value specified to be |
| transparent. |
| <p><tt> ImageData ideaData = new ImageData(<br> |
| |
| getClass().getResourceAsStream("Idea.jpg"));</tt><br> |
| <img src="images/tag_2.gif" height="13" width="24"><tt> int whitePixel = |
| ideaData.palette.getPixel(new RGB(255,255,255));</tt><br> |
| <img src="images/tag_1.gif" height="13" width="24"><tt> |
| ideaData.transparentPixel = whitePixel;</tt><br> |
| <tt> Image transparentIdeaImage = new Image(display,ideaData);</tt> |
| <p>Next a Shell uses the newly created image <img src="images/tag_3.gif" height="13" width="24"> |
| in a native Label and also in the paint event <img src="images/tag_4.gif" height="13" width="24"> |
| of a Canvas. A Windows Label does not support native transparency so it still |
| appears with a white background, however the GC for the Canvas uses the existing |
| background color whenever a white pixel is encountered in the source image, so |
| the image appears as transparent. |
| <p><tt> Label transparentIdeaLabel = new |
| Label(shell,SWT.NONE);</tt><br> |
| <img src="images/tag_3.gif" height="13" width="24"><tt> |
| transparentIdeaLabel.setImage(transparentIdeaImage);</tt><br> |
| <tt> Canvas canvas = new Canvas(shell,SWT.NONE);</tt><br> |
| <tt> canvas.addPaintListener(new PaintListener() {</tt><br> |
| <tt> public void |
| paintControl(PaintEvent e) {</tt><br> |
| <img src="images/tag_4.gif" height="13" width="24"><tt> |
| e.gc.drawImage(transparentIdeaImage,0,0);</tt><br> |
| <tt> }</tt><br> |
| <tt> });</tt> |
| <p><img src="images/IdeaTransparency.gif" height="146" width="280"> |
| <p>As can be seen from the second of the two images (drawn on the Canvas with |
| the white pixel set to transparent), there are still some patches of white. |
| Closer analysis reveals that this is not a bug, but that these regions are not |
| pure white (255,255,255), but are slighly off-white (such as 255,254,254). The |
| transparent pixel of an ImageData can only be used for a single value. This |
| now presents the next problem to be solved - locate all of the off-white pixels |
| in the ImageData and convert them to pure-white. To do this we will iterate |
| over each pixel in the image data and modify those that are close to white to |
| be pure white. |
| <h2><a name="Manipulating Image Data"></a>Manipulating ImageData</h2> |
| Because the graphic Idea.jpg isn't as white as we'd like it, we'll iterate over |
| its imageData and convert the off-white pixels to pure white and then we'll save |
| this into a new file Idea_white.jpg. In practice it's unlikely that you'd ever |
| do this kind of programming in SWT, however it's a good example to use showing |
| how ImageData can be analyzed and manipulated. |
| <p>The first step is to load the image and then iterate over each pixel |
| individually looking at its color. Because Idea.jpg is using a direct palette, |
| the pixel value is an int that contains the red, green and blue component as |
| masked bit areas. These mask value can be obtained from the palette. |
| <p><tt>ImageData ideaImageData = new ImageData(<br> |
| getClass().getResourceAsStream("Idea.jpg"));</tt><br> |
| <tt>int redMask = ideaImageData.palette.redMask;</tt><br> |
| <tt>int blueMask = ideaImageData.palette.blueMask;</tt><br> |
| <tt>int greenMask = ideaImageData.palette.greenMask;</tt> |
| <p>For any pixel value we can bitwise AND it with the mask to see what the color |
| component is. The red component is the low order bits so this will be the actual |
| value (from 0 to 255), however the green and blue values need adjusting as they |
| are the high order bits in the pixel value. To make this adjustment the color |
| component can be bit shifted to the right using the >> operator. If you |
| are writing generic code to do this kind of manipulating, take care that direct |
| palettes for color depths of 24 or 32 store their color components with red |
| being the low order bits, however for color depth of 16 the colors are reversed |
| and red is high order with blue being low order. The reason for this is to be |
| the same as how Windows stores images internally so there is less conversion |
| when creating the image. |
| <p>Two for loops will iterate over the imageData. The first is traversing the |
| image from top to bottom a line at a time, and <img src="images/tag_1.gif" height="13" width="24"> |
| creates an int[] to hold each line of data. The method<tt> |
| ImageData.getPixels(int x, int y, int getWidth, int[] pixels, int startIndex)</tt> |
| is used <img src="images/tag_2.gif" height="13" width="24"> to extract a |
| line at a time from the imageData's bytes. The API for this method is slightly |
| irregular, because rather than returning the resulting data it instead is |
| declared as <tt>void</tt>, and the resulting pixel data is injected into the |
| int[] that is passed in as a method argument. The int[] of pixels is then |
| iterated over and each value has its <img src="images/tag_3.gif" height="13" width="24"> |
| red, <img src="images/tag_4.gif" height="13" width="24"> green and <img src="images/tag_5.gif" height="13" width="24"> |
| blue component extracted. The desired effect we want is to determine whether the |
| pixel is off-white and if so to make it pure white - a rule that works well is |
| to assume that anything whose red and green component are higher than 230 <img src="images/tag_6.gif" height="13" width="24"> |
| and blue component higher than 150 is an off-white. |
| <p><img src="images/tag_1.gif" height="13" width="24"><tt> int[] lineData = new |
| int[ideaImageData.width];</tt><br> |
| <tt> for (int y = 0; y < ideaImageData.height; y++) {</tt><br> |
| <img src="images/tag_2.gif" height="13" width="24"><tt> |
| ideaImageData.getPixels(0,y,width,lineData,0);</tt><br> |
| <tt> <font color="#009900"> // Analyze |
| each pixel value in the line</font></tt><br> |
| <tt> for (int x=0; |
| x<lineData.length; x++){</tt><br> |
| <tt> <font color="#009900">// |
| Extract the red, green and blue component</font></tt><br> |
| <tt> int |
| pixelValue = lineData[x];</tt><br> |
| <img src="images/tag_3.gif" height="13" width="24"><tt> |
| int r = pixelValue & redShift;</tt><br> |
| <img src="images/tag_4.gif" height="13" width="24"><tt> |
| int g = (pixelValue & greenShift) >> 8;</tt><br> |
| <img src="images/tag_5.gif" height="13" width="24"><tt> |
| int b = (pixelValue & blueShift) >> 16;</tt><br> |
| <img src="images/tag_6.gif" height="13" width="24"><tt> |
| if (r > 230 && g > 230 && b > 150){</tt><br> |
| <tt> |
| ideaImageData.setPixel(x,y,0xFFFFFF);</tt><br> |
| <tt> }</tt><br> |
| <tt> }</tt><br> |
| <tt> };</tt> |
| <p>Having manipulated the raw bytes making up the ImageData we have now |
| successfully changed the off-white values to pure white. |
| <h2><a name="Saving Images"></a>Saving Images</h2> |
| Now that we have the ImageData where all of the whitish pixels have been |
| converted to white, and the transparency pixel of the palette has been set to be |
| the color white, we'll save this image so that next time an SWT program needs |
| the pure white JPF it can just load the file and use it as is. To save ImageData |
| into a file use the class <tt>org.eclipse.swt.graphics.ImageLoader</tt>. The |
| image loader has a public field data typed to ImageData[]. The reason the data |
| field is an array of ImageData is to support image file formats with more than |
| one frame such as animated GIFs or interlaced JPEG files. These are covered more |
| in the <a href="#Animation">Animation</a> section later. |
| <p><tt>ImageLoader imageLoader = new ImageLoader();</tt><br> |
| <tt>imageLoader.data = new ImageData[] {ideaImageData};</tt><br> |
| <tt>imageLoader.save("C:/temp/Idea_PureWhite.jpg",SWT.IMAGE_JPEG);</tt> |
| <p>The finished result is shown below. |
| <p><img src="images/Idea_transparent.jpg" height="86" width="120"> |
| <p>It doesn't look much different to the original Idea.jpg because it is drawn |
| on a white background, but when it is drawn on a Canvas with the white pixel set |
| to be the transparent pixel the background shows through achieving the desired |
| effect. |
| <p><tt> ImageData pureWhiteIdeaImageData =<br> |
| new ImageData("C:/temp/Idea_PureWhite.jpg");</tt><br> |
| <img src="images/tag_1.gif" height="13" width="24"><tt> |
| pureWhiteIdeaImageData.transparentPixel =<br> |
| |
| pureWhiteIdeaImageData.palette.getPixel(new RGB(255,255,255));</tt><br> |
| <tt> final Image transparentIdeaImage = new |
| Image(display,pureWhiteIdeaImageData);</tt><br> |
| <tt> Canvas canvas = new Canvas(shell,SWT.NONE);</tt><br> |
| <tt> canvas.addPaintListener(new PaintListener() {</tt><br> |
| <tt> public void |
| paintControl(PaintEvent e) {</tt><br> |
| <tt> |
| e.gc.drawImage(transparentIdeaImage,0,0);</tt><br> |
| <tt> }</tt><br> |
| <tt> });</tt> |
| <p><img src="images/Idea_Transparent_DrawnOnCanvas.gif" height="150" width="173"> |
| <p><img src="images/Note.gif" height="13" width="62"> It might seem odd that in |
| the above code that after loading the Idea_PureWhite.jpg file <img src="images/tag_1.gif" height="13" width="24"> |
| the transparent pixel was set to be white. Why not set the transparent pixel |
| before we used the ImageLoader to create the persisted Idea_PureWhite.jpg file? |
| The reason is that the JPEG image file format does not support transparency. A |
| GIF file supports native transparency, however changing the file type to |
| SWT.IMAGE_GIF on the ImageLoader would not have worked, because GIF supports a |
| maximum image depth of 8 and uses an indexed palette, whereas the JPEG has an |
| image depth of 24 and a direct palette. To convert between the two formats would |
| require analyzing the colours used by the JPEG to create the best fit 256 color |
| palette, before iterating over each JPEG pixel value and creating the GIF image |
| data by finding the closest color. Doing this conversion is outside the scope of |
| this article, although it can be done by most commercial graphics tools. To |
| match pixel values as the color depth decreases from 24 to 8 involves algorithms |
| that find the right color match for a block of pixels rather than a single pixel |
| value, and is why image quality can sometimes be reduced when switching between |
| different formats. |
| <p>We have shown how an ImageData is an array of int values representing each |
| pixel coordinate, and how each pixel value is mapped to a color through the |
| palette. This allowed us to iterate over the image data for the Idea.jpg, query |
| pixel values that were close to white, and convert these to a pure white RGB |
| value. The end result of this was that we were able to create the |
| Idea_PureWhite.jpg file that can be used as a transparent JPG by setting the |
| white pixel to be transparent. Transparency works by having a source pixel value |
| (the image being drawn), a destination pixel value (the image being drawn onto) |
| and a rule by which the resulting destination pixel value is determined. For |
| transparency the rule is that the source pixel value is used unless it's |
| transparent in which case the destination pixel is used. Another technique is to |
| use alpha values that specify the weight applied to the source relative to the |
| destination to create the final pixel value. This allows the blending between |
| the source image and the existing background it is being drawn onto. |
| <h2><a name="Blending"></a>Blending</h2> |
| Alpha blending is a technique used to merge two pixel values, where the source |
| and destination pixel each specify an alpha value that weights how much they |
| will affect the final destination pixel. An alpha value of 255 is full weight, |
| and 0 is no weight. SWT supports a <a href="#Single alpha value">single alpha |
| value</a> for the entire ImageData, or else each pixel can have its <a href="#Different alpha value per pixel">own |
| alpha value.</a> |
| <h3><a name="Single alpha value"></a>Single alpha value</h3> |
| The <tt>int</tt> field <tt>alphaValue</tt> of ImageData is used to specify a |
| single value that weights how the source pixels are combined with the |
| destination input to create the destination output. The listing below shows how |
| the same ImageData for the Idea_PureWhite.jpg is used for three separate images. |
| The first <img src="images/tag_1.gif" height="13" width="24"> is the |
| original, then <img src="images/tag_2.gif" height="13" width="24"> an alpha |
| of 128 is used, and finally <img src="images/tag_3.gif" height="13" width="24"> |
| an alpha of 64. Note that the same ImageData is continually manipulated by |
| having its alpha changed before creating each Image, and changing the ImageData |
| has no affect on Images already constructed using it. This is because the Image |
| is prepared for display on the device from the ImageData at construction time. |
| <p><tt> Shell shell = new Shell(display);</tt><br> |
| <tt> shell.setLayout(new FillLayout());</tt><br> |
| <tt> ImageData imageData = new |
| ImageData("C:/temp/Idea_PureWhite.jpg");</tt><br> |
| <img src="images/tag_1.gif" height="13" width="24"><tt> final Image fullImage = |
| new Image(display,imageData);</tt><br> |
| <tt> imageData.alpha = 128;</tt><br> |
| <img src="images/tag_2.gif" height="13" width="24"><tt> final Image halfImage = |
| new Image(display,imageData);</tt><br> |
| <tt> imageData.alpha = 64;</tt><br> |
| <img src="images/tag_3.gif" height="13" width="24"><tt> final Image quarterImage |
| = new Image(display,imageData);</tt> |
| <p><tt> Canvas canvas = new |
| Canvas(shell,SWT.NO_REDRAW_RESIZE);</tt><br> |
| <tt> canvas.addPaintListener(new PaintListener() {</tt><br> |
| <tt> public void |
| paintControl(PaintEvent e) {</tt><br> |
| <tt> |
| e.gc.drawImage(fullImage,0,0);</tt><br> |
| <tt> |
| e.gc.drawImage(halfImage,140,0);</tt><br> |
| <tt> |
| e.gc.drawImage(quarterImage,280,0);</tt><br> |
| <tt> }</tt><br> |
| <tt> });</tt> |
| <p><img src="images/SingleAlphaChannel.gif" height="137" width="417"> |
| <h3><a name="Different alpha value per pixel"></a>Different alpha value per |
| pixel</h3> |
| As well as having a single alpha value that applied to all pixels in the source |
| image, ImageData allows each pixel to have its own individual alpha value. This |
| is done with the <tt>byte[]</tt> field <tt>alphaData</tt>. This allows effects to be |
| achieved, such as having an image fade from its top to bottom. |
| <p>The following code creates <img src="images/tag_1.gif" height="13" width="24"> |
| an alphaData <tt>byte[]</tt>, and then has two loops. The outer loop y <img src="images/tag_2.gif" height="13" width="24"> |
| is from 0 to the imageData's height, and the inner loop <img src="images/tag_3.gif" height="13" width="24"> |
| creates a <tt>byte[]</tt> for the width of the imageData and initializes it with |
| a value that increases from 0 for the top row through to 255 for the bottom row. |
| A System.arrayCopy <img src="images/tag_4.gif" height="13" width="24"> then |
| builds up the alphaData <tt>byte[]</tt> with each row. |
| <p><tt> ImageData fullImageData = new |
| ImageData("C:/temp/Idea_PureWhite.jpg");</tt><br> |
| <tt> int width = fullImageData.width;</tt><br> |
| <tt> int height = fullImageData.height;</tt><br> |
| <img src="images/tag_1.gif" height="13" width="24"><tt> byte[] alphaData = new |
| byte[height * width];</tt><br> |
| <img src="images/tag_2.gif" height="13" width="24"><tt> for(int |
| y=0;y<height;y++){</tt><br> |
| <tt> byte[] alphaRow = new |
| byte[width];</tt><br> |
| <img src="images/tag_3.gif" height="13" width="24"><tt> |
| for(int x=0;x<width;x++){</tt><br> |
| <tt> |
| alphaRow[x] = (byte) ((255 * y) /height);</tt><br> |
| <tt> }</tt><br> |
| <img src="images/tag_4.gif" height="13" width="24"><tt> |
| System.arraycopy(alphaRow,0,alphaData,y*width,width);</tt><br> |
| <tt> }</tt><br> |
| <tt> fullImageData.alphaData = alphaData;</tt><br> |
| <tt> Image fullImage = new Image(display,fullImageData);</tt> |
| <p>The resulting image is shown below, and the alphaData <tt>byte[]</tt> makes |
| the top of the image transparent and the bottom opaque, with a gradual fading |
| between the two. |
| <p><img src="images/ImageBlended.gif" height="144" width="186"> |
| <h2><a name="Image Effects"></a>Image effects</h2> |
| As well as arbitrary image effects that can be achieved by manipulating image |
| data, SWT provides a number of pre-defined ways of creating new images based on |
| existing images combined with certain styles. This is used if, for example, you |
| have an image being used on a toolbar button and you wish to create a version |
| that can be used to indicate that the button is disabled or that it is inactive. |
| <p>To create an effect based on an existing image and a style flag use the |
| constructor <tt>Image(Display display, Image image, int flag)</tt>. The flag |
| argument is a static constant of either SWT.IMAGE_COPY, SWT.IMAGE_DISABLE or |
| SWT.IMAGE_GRAY. Copy creates a new image based on the original but with a copy |
| of its imageData, whereas Disable and Gray create a new image applying platform |
| specific effects. The following code shows the Idea.jpg, together with three |
| more images that we created using the style bits <img src="images/tag_1.gif" height="13" width="24"> |
| IMAGE_DISABLE, <img src="images/tag_2.gif" height="13" width="24"> |
| IMAGE_GRAY and <img src="images/tag_3.gif" height="13" width="24"> |
| IMAGE_COPY. To show that IMAGE_COPY creates a new image <img src="images/tag_4.gif" height="13" width="24"> |
| a GC is used to draw onto it that affects only the copied image, not the |
| original. |
| <p><tt> Image ideaImage = new Image(display,<br> |
| getClass().getResourceAsStream("/icons/Idea.jpg");</tt><br> |
| <img src="images/tag_1.gif" height="13" width="24"><tt> Image |
| disabledImage = new Image(display,image,SWT.IMAGE_DISABLE);</tt><br> |
| <img src="images/tag_2.gif" height="13" width="24"><tt> Image grayImage = new |
| Image(display,image,SWT.IMAGE_GRAY);</tt><br> |
| <img src="images/tag_3.gif" height="13" width="24"><tt> Image copyImage = new |
| Image(display,ideaImage,SWT.IMAGE_COPY);</tt><br> |
| <img src="images/tag_4.gif" height="13" width="24"><tt> GC gc = new |
| GC(copyImage);</tt><br> |
| <tt> gc.drawText("This is a copy",0,0);</tt><br> |
| <tt> gc.dispose();</tt> |
| <h1><img src="images/GraphicsEffects_01.JPG" height="136" width="517"></h1> |
| <h1><a name="Animation"></a>GIF Animation</h1> |
| Another important topic for images is understanding animation where an image can |
| contain a number of frames that are animated in sequence. Image animation is |
| supported by GIF images, where a single image file can contain multiple sets of |
| ImageData. Web browsers support native animation, and the following image is |
| made up of 15 frames showing the pen rotating and writing the words SWT beneath |
| the Idea logo. |
| <p><img src="images/Idea_SWT_Animation.gif" height="120" width="120"> |
| <p>While the web browser you're using to read this article should show the Idea_SWT_Animation.gif |
| file as a sequence with the moving pen, this is not true of native SWT controls |
| displaying the graphic. The animation must be done programmatically, and the |
| class <tt>org.eclipse.swt.examples.ImageAnalyzer</tt> shows how this can be |
| achieved. The ImageAnalyzer class can be obtained from the <a href="http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.examples/">SWT |
| examples project</a> in the Eclipse CVS repository. |
| <p>When an animated GIF is loaded by the ImageLoader class, each individual |
| frame is a separate element in the data field array typed to ImageData[]. |
| In the animation sequence each ImageData records how many milliseconds it should |
| be displayed for in the int field delayTime. The number of times the sequence |
| should repeat can be retrieved from the field loader.repeatCount, a value of -1 |
| indicates that the animation should repeat indefinitely and |
| Idea_SWT_Animation.gif has this value. When switching from one frame to the next |
| there are three ways that the new frame can replace the previous one, specified |
| by the int field <tt>ImageData.disposalMethod</tt>. This can take the following values |
| defined in the constant class <tt>org.eclipse.swt.SWT</tt>.<br> |
| |
| <table border="1"> |
| <tr> |
| <td>DM_FILL_NONE</td> |
| <td>Leave the previous image in place and just draw the image on top. |
| Each frame adds to the previous one.</td> |
| </tr> |
| <tr> |
| <td>DM_FILL_BACKGROUND</td> |
| <td>Fill with the background color before painting each frame. The pixel |
| value for this is defined in the field loader.backgroundPixel</td> |
| </tr> |
| <tr> |
| <td>DM_FILL_PREVIOUS</td> |
| <td>Restore the previous picture </td> |
| </tr> |
| <tr> |
| <td>DM_FILL_UNSPECIFIED</td> |
| <td>No disposal method has been defined</td> |
| </tr> |
| </table> |
| <p>To conserve on space, animated GIFs are generally optimized to just store the |
| delta that needs to be applied to the previous image. In the |
| Idea_SWT_Animation.gif above the 15 frames are shown below. Each frame stores a |
| delta against the previous image, and this was automatically generated by the |
| tool I create the animated GIF with. The disposal method for each frame is |
| DM_NONE so the each image should be drawn on top of the previous one. Each |
| individual ImageData element has the x and y for its top left corner, as well as |
| its width and height. The overall size to use can be obtained from the fields<tt> |
| loader.logicalScreenWidth</tt> and <tt>loader.logicalScreenHeight.</tt> |
| <p><img src="images/AnimationFrames.gif" height="481" width="481"> |
| <p>To illustrate how to display an animated GIF in SWT we'll create an <img src="images/tag_1.gif" height="13" width="24"> |
| initial Image from the first frame and a counter to store which frame is being |
| displayed. The image is drawn <img src="images/tag_2.gif" height="13" width="24"> |
| a paint event on a Canvas, and a GC is created <img src="images/tag_3.gif" height="13" width="24"> |
| that will be used to draw the subsequent frames onto the image. |
| <p><tt> ImageLoader loader = new ImageLoader();</tt><br> |
| <tt> |
| loader.load(getClass().getResourceAsStream("Idea_SWT_Animation.gif"));</tt><br> |
| <tt> Canvas canvas = new Canvas(shell,SWT.NONE);</tt><br> |
| <img src="images/tag_1.gif" height="13" width="24"><tt> image = new |
| Image(display,loader.data[0]);</tt><br> |
| <tt> int imageNumber;</tt><br> |
| <img src="images/tag_3.gif" height="13" width="24"><tt> final GC gc = new |
| GC(image);</tt><br> |
| <tt> canvas.addPaintListener(new PaintListener(){</tt><br> |
| <tt> public void paintControl(PaintEvent event){</tt><br> |
| <img src="images/tag_2.gif" height="13" width="24"><tt> |
| event.gc.drawImage(image,0,0);</tt><br> |
| <tt> }</tt><br> |
| <tt> });</tt> |
| <p>The body of the example will create a thread that iterates through each |
| frame, waiting until the <img src="images/tag_4.gif" height="13" width="24">delayTime |
| has passed. For each frame the <img src="images/tag_5.gif" height="13" width="24"> |
| ImageData is retrieved from the loader and a temporary Image created. This is |
| then drawn onto the image being displayed on the canvas, at the x and y position |
| specified by the frame's ImageData. Because we created the temporary |
| frameImage <img src="images/tag_7.gif" height="13" width="24"> we must |
| dispose it when it's no longer being used to free up the underlying resource. |
| <p><tt> Thread thread = new Thread(){</tt><br> |
| <tt> public void run(){</tt><br> |
| <tt> long currentTime = |
| System.currentTimeMillis();</tt><br> |
| <tt> int delayTime = |
| loader.data[imageNumber].delayTime;</tt><br> |
| <img src="images/tag_4.gif" height="13" width="24"><tt> |
| while(currentTime + delayTime * 10 > System.currentTimeMillis()){</tt><br> |
| <tt><font color="#009900"> |
| // Wait till the delay time has passed</font></tt><br> |
| <tt> }</tt><br> |
| <tt> display.asyncExec(new Runnable(){</tt><br> |
| <tt> public void run(){</tt><br> |
| <tt> <font color="#009900"> |
| // Increase the variable holding the frame number</font></tt><br> |
| <tt> imageNumber |
| = imageNumber == loader.data.length-1 ? 0 : imageNumber+1;</tt><br> |
| <tt><font color="#009900"> |
| // Draw the new data onto the image</font></tt><br> |
| <img src="images/tag_5.gif" height="13" width="24"><tt> |
| ImageData nextFrameData = loader.data[imageNumber];</tt><br> |
| <tt> Image |
| frameImage = new Image(display,nextFrameData);</tt><br> |
| <img src="images/tag_6.gif" height="13" width="24"><tt> |
| gc.drawImage(frameImage,nextFrameData.x,nextFrameData.y);</tt><br> |
| <img src="images/tag_7.gif" height="13" width="24"><tt> |
| frameImage.dispose();</tt><br> |
| <tt> |
| canvas.redraw();</tt><br> |
| <tt> }</tt><br> |
| <tt> });</tt><br> |
| <tt> }</tt><br> |
| <tt> };</tt><br> |
| <tt> shell.open();</tt><br> |
| <tt> thread.start();</tt> |
| <h1><a name="Scaling"></a>Scaling</h1> |
| In the examples so far we have loaded an image from a file and drawn it on the |
| GUI at its original size. There are times when this will not always be the case |
| and you need to stretch or shrink the image, and there are two ways to do achieve |
| this. The first is to use the GC to stretch and clip it, using <tt><font color="#000000">GC.drawImage(Image |
| image, int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int |
| dstWidth, int dstHeight)</font></tt>, and the second is to use <tt>ImageData.scaledTo(int |
| width, int height) </tt>to create a new ImageData object based on scaling the |
| receiver. |
| <p>The following code loads the Idea.jpg image <img src="images/tag_1.gif" height="13" width="24">, |
| and scales this to 1/2 and 2 times its original size <img src="images/tag_2.gif" height="13" width="24"> |
| using the<tt> ImageData.scaledTo(int width, int height)</tt>. The image is also |
| resized using <tt>GC.drawImage(...)</tt>, and the example shows two ways to |
| achieve this. The first technique<img src="images/tag_3.gif" height="13" width="24"> |
| is to specify the new width and height as part the paint event. This is |
| potentially inefficient because the scaling must be done each time the canvas |
| repaints itself. A more optimized technique is to create an image at the final |
| desired size, <img src="images/tag_4.gif" height="13" width="24"> construct |
| a GC over the this and then paint onto it so a permanent scaled image exists in |
| the program. |
| <p><font color="#000000">The end result is shown below, and both techniques |
| produce almost identical results.</font> |
| <p><img src="images/tag_1.gif" height="13" width="24"><tt> final Image image = |
| new Image(display,<br> |
| |
| getClass(),getResourceAsStream("Idea.jpg"));</tt><br> |
| <tt> final int width = image.getBounds().width;</tt><br> |
| <tt> final int height = image.getBounds().height;</tt> |
| <p><img src="images/tag_2.gif" height="13" width="24"><tt> final Image scaled050 |
| = new Image(display,<br> |
| |
| image.getImageData().scaledTo((int)(width*0.5),(int)(height*0.5)));</tt><br> |
| <tt> final Image scaled200 = new Image(display,<br> |
| |
| image.getImageData().scaledTo((int)(width*2),(int)(height*2)));</tt> |
| <p><img src="images/tag_4.gif" height="13" width="24"><tt> final Image |
| scaledGC200 = new Image(display,(int)(width*2),(int)(height*2));</tt><br> |
| <tt> GC gc = new GC(scaledGC200);</tt><br> |
| <tt> |
| gc.drawImage(image,0,0,width,height,0,0,width*2,height*2);</tt><br> |
| <tt> gc.dispose();</tt> |
| <p><tt> canvas.addPaintListener(new PaintListener() {</tt><br> |
| <tt> public void paintControl(PaintEvent e) {</tt><br> |
| <img src="images/tag_3.gif" height="13" width="24"><tt> |
| e.gc.drawImage(image,0,0,width,height,0,0,(int)(width*0.5),(int)(height*0.5));</tt><br> |
| <tt> e.gc.drawImage(scaled050,100,0);</tt><br> |
| <tt> e.gc.drawImage(scaledGC200,0,75);</tt><br> |
| <tt> |
| e.gc.drawImage(scaled200,225,175);</tt><br> |
| <tt> }</tt><br> |
| <tt> });</tt> |
| <p><img src="images/ScaledImages_01.gif" height="300" width="425"> |
| <p>When to use GC scaling, and when to use <tt>ImageData.scaledTo(...)</tt>, depends |
| on the particular scenario. The GC scaling is faster because it is native, however |
| it does assume that you have a GC and an Image to work with. Using just the |
| ImageData means that you don't need to have prepared an Image (that requires |
| a native resource and requires disposing), and an ImageData can be loaded directly |
| from a graphic file (using the constructor <tt>ImageData(String fileName)</tt> |
| or <tt>ImageData(InputStream stream)</tt>). By using raw ImageData you are delaying |
| the point at which you will need native display resources, however you will |
| eventually need to create an Image from the scaled ImageData before it can be |
| rendered onto a device. |
| <h1><a name="Cursor"></a>Cursor</h1> |
| The final section of this article covers the class <tt>org.eclipse.swt.graphics.Cursor</tt> |
| responsible for managing the operating system resource associated with the mouse |
| pointer. The reason cursors are covered in an article on images is because you |
| can create arbitrary cursors from images, and they illustrate how image masks |
| work. |
| <p>Cursors can be created in two ways, either from a pre-defined style or using |
| source and mask images. |
| <h2><a name="Platform cursors"></a>Platform cursors</h2> |
| The list of pre-defined styles are SWT constants shown below, together with |
| sample images although these will vary depending on the operating system and |
| platform settings.<br> |
| |
| <table border="1"> |
| <tr> |
| <td>CURSOR_APPSTARTING</td> |
| <td><img src="images/Image_cursor_appstarting.gif" height="28" width="28"></td> |
| <td>CURSOR_IBEAM</td> |
| <td><img src="images/Image_cursor_ibeam.gif" height="32" width="32"></td> |
| <td>CURSOR_SIZENE</td> |
| <td><img src="images/Image_cursor_NESW.gif" height="32" width="32"></td> |
| </tr> |
| <tr> |
| <td>CURSOR_ARROW</td> |
| <td><img src="images/Image_cursor_arrow.gif" height="32" width="32"></td> |
| <td>CURSOR_NO</td> |
| <td><img src="images/Image_cursor_not.gif" height="32" width="32"></td> |
| <td>CURSOR_SIZENESW</td> |
| <td><img src="images/Image_cursor_NESW.gif" height="32" width="32"></td> |
| </tr> |
| <tr> |
| <td>CURSOR_CROSS</td> |
| <td><img src="images/Image_cursor_cross.gif" height="32" width="32"></td> |
| <td>CURSOR_SIZEALL</td> |
| <td><img src="images/Image_cursor_sizeall.gif" height="27" width="26"></td> |
| <td>CURSOR_SIZENS</td> |
| <td><img src="images/Image_NS.gif" height="32" width="32"></td> |
| </tr> |
| <tr> |
| <td>CURSOR_HAND</td> |
| <td><img src="images/Image_cursor_hand.gif" height="32" width="32"></td> |
| <td>CURSOR_SIZEE</td> |
| <td><img src="images/Image_cursor_E.gif" height="32" width="32"></td> |
| <td>CURSOR_SIZENW</td> |
| <td><img src="images/Image_cursor_NW.gif" height="32" width="32"></td> |
| </tr> |
| <tr> |
| <td>CURSOR_HELP</td> |
| <td><img src="images/Image_cursor_help.gif" height="31" width="32"></td> |
| <td>CURSOR_SIZEN</td> |
| <td><img src="images/Image_cursor_N.gif" height="32" width="32"></td> |
| <td>CURSOR_SIZESNWSE</td> |
| <td><img src="images/Image_cursor_NW.gif" height="32" width="32"></td> |
| </tr> |
| <tr> |
| <td>CURSOR_SIZES</td> |
| <td><img src="images/Image_cursor_S.gif" height="32" width="32"></td> |
| <td>CURSOR_SIZESE</td> |
| <td><img src="images/Image_cursor_SE.gif" height="32" width="32"></td> |
| <td>CURSOR_SIZESW</td> |
| <td><img src="images/Image_cursor_SW.gif" height="32" width="33"></td> |
| </tr> |
| <tr> |
| <td>CURSOR_SIZEWE</td> |
| <td><img src="images/Image_cursor_W.gif" height="32" width="32"></td> |
| <td>CURSOR_UPARROW</td> |
| <td><img src="images/Imagecursor_uparrow.gif" height="40" width="40"></td> |
| <td>CURSOR_WAIT</td> |
| <td><img src="images/Image_cursor_busy.gif" height="32" width="32"></td> |
| </tr> |
| </table> |
| <p>Every Control can have a cursor associated with it, and when the mouse |
| pointer moves over the control it changes to the specified cursor. |
| Changing a cursor also affects any child controls, so if you update the cursor |
| on a Shell this affects the mouse pointer for anywhere on the shell, although if |
| the child control itself has an explicit cursor, or uses its own cursor such as |
| an I bean for Text or Combo, this takes precedence over the parent's defined |
| cursor. The following code illustrates this, by changing the shell's cursor to |
| be hand cursor, and the list's cursor to a cross. When the mouse is over the |
| shell (or its childButton that has no explicit cursor) it is a hand, and when it |
| is over the list it is a cross. |
| <p><tt>List list = new List(shell,SWT.BORDER);</tt><br> |
| <tt>Button button = new Button(shell,SWT.NONE);</tt><br> |
| <tt>button.setText("Button");</tt><br> |
| <tt>Cursor handCursor = new Cursor(display,SWT.CURSOR_HAND);</tt><br> |
| <tt>shell.setCursor(handCursor);</tt><br> |
| <tt>Cursor crossCursor = new Cursor(display,SWT.CURSOR_CROSS);</tt><br> |
| <tt>list.setCursor(crossCursor);</tt> |
| <p><img src="images/ShellCursors.gif" height="110" width="548"> |
| <p>Cursors use underlying native resources and should be disposed when they are |
| no longer required. In the above code this would be when the shell has been |
| disposed and there are no remaining controls using either the handCursor or |
| crossCursor fields. |
| <h2><a name="Custom cursors"></a>Custom cursors</h2> |
| As well as using a pre-defined style, a cursor can be created from images |
| using the constructor <tt>Cursor(Device device, ImageData source, ImageData |
| mask, int hotspotx, int hotspoty). </tt>The source imagedata argument is the |
| graphic for the cursor shape, and the mask is used to specify transparency. The |
| following example shows how to create a monochrome custom cursor, where the the |
| source and mask image data have a color depth of 1 and indexed palettes with two |
| colors. The ImageDatas height and width should be no larger than 32 and it |
| does not necessarily have to be square, although the mask and source should be |
| the same size. The hotspot is the point on the cursor that represents the |
| precise location of the mouse pointer. |
| <p>The source and mask image data pixels are combined to determine whether the |
| cursor pixel should be white, black or transparent.<br> |
| |
| <table border="1"> |
| <tr> |
| <td>Image</td> |
| <td>Mask</td> |
| <td>Cursor color</td> |
| </tr> |
| <tr> |
| <td>1</td> |
| <td>0</td> |
| <td>Transparent</td> |
| </tr> |
| <tr> |
| <td>0</td> |
| <td>0</td> |
| <td>Black</td> |
| </tr> |
| <tr> |
| <td>1</td> |
| <td>1</td> |
| <td>Black</td> |
| </tr> |
| <tr> |
| <td>0</td> |
| <td>1</td> |
| <td>White</td> |
| </tr> |
| </table> |
| <p>The ImageData can be loaded from files, and Eclipse itself does this for some |
| of its drag and drop cursors defined in <tt>org.eclipse.ui/icons/full/dnd. </tt>You |
| can also directly create and manipulate the image data within your program. The |
| code sample below creates an indexed palette with two colors. The source and |
| mask ImageData are 32 by 32 with a color depth of 1. The int[] for the source |
| and mask define an up arrow, the 0s for the source and 1s for the mask are shown |
| in bold to show how the arrow is defined with the arrays. 0 and 1 makes white |
| which is the center of the arrow, 1 and 1 is black for the edge of the arrow, |
| and 1 and 0 transparent for the remainder. The tip of the arrow is 16,3 so this |
| is made the cursor hotspot when it is created. |
| <p><tt>PaletteData paletteData = new PaletteData(new RGB[] {</tt><br> |
| <tt> new RGB(0,0,0) , new RGB(255,255,255)</tt><br> |
| <tt>});</tt><br> |
| <tt>ImageData sourceData = new ImageData(32,32,1,paletteData);</tt><br> |
| <tt>ImageData maskData = new ImageData(32,32,1,paletteData);</tt> |
| <p><tt>int[] cursorSource = new int[] {</tt><br> |
| <tt> |
| 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> |
| 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0,0,0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0,0,0,0,0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0,0,0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,<b>0</b>,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> |
| 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,</tt><br> |
| <tt> |
| 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 };</tt> |
| <p><tt>int[] cursorMask = new int[] {</tt><br> |
| <tt> |
| 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1,1,1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> 0,0,0,0,0,0,0,0,0,0,0,0,0,0,<b>1,1,1</b>,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,</tt><br> |
| <tt> |
| 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, };</tt> |
| <p><tt>sourceData.setPixels(0,0,1024,cursorSource,0);</tt><br> |
| <tt>maskData.setPixels(0,0,1024,cursorMask,0);</tt><br> |
| <tt>Cursor cursor = new Cursor(display,sourceData,maskData,16,3);</tt><br> |
| <tt>shell.setCursor(cursor);</tt> |
| <p><img src="images/CustomCursor.gif" height="107" width="157"> |
| <p>To keep the code listings narrow the above sample used an <code>int[]</code> |
| to define the source and mask imageData. A byte uses less memory than an |
| unsigned int, so when creating custom cursors it is more efficient to use a |
| byte[] instead, such as: |
| <p><tt>byte[] cursorSource = new byte[] {</tt><br> |
| <tt> (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x01, |
| (byte)0x00, // etc...</tt> |
| <p>Custom cursors need to be disposed in just the same way as pre-defined system |
| cursors, so when there is no remaining control using the cursor is must be send |
| the method <tt>dispose()</tt> to free up the underlying native resource. |
| <p>Although the above example is for a monochrome cursor, Eclipse 3.0 supports |
| color cursors on platforms that allow it (such as Windows). Two SWT code snippets |
| showing how to do this are here: <a href="http://dev.eclipse.org/viewcvs/index.cgi/~checkout~/platform-swt-home/snippits/snippet118.html">snippet |
| 1</a>, <a href="http://dev.eclipse.org/viewcvs/index.cgi/~checkout~/platform-swt-home/snippits/snippet119.html">snippet |
| 2</a>. |
| <h1><a name="Conclusion"></a>Conclusion</h1> |
| This article has showed how to create and manipulate SWT images. |
| Underlying each Image is its ImageData that records the pixel value for each |
| coordinate, and the palette that maps this to a specific color. By understanding |
| ImageData it is possible to achieve effects such as transparency, alpha |
| blending, animation, as well as customized cursors. |
| <p><font size="-1">Java and all Java-based trademarks and logos are trademarks |
| or registered trademarks of Sun Microsystems, Inc. in the United States, other |
| countries, or both.</font> |
| <p><font size="-1">Windows is a trademark of Microsoft corporation in the United |
| States, other countries, or both.</font> |
| <p><font size="-1">Other company, product, and service names may be trademarks or |
| service marks of others.</font> |
| </body> |
| |
| </html> |