打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Taking a look at SWT Images

Taking a look at SWT Images

Summary
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.

By Joe Winchester, IBM
September 10th, 2003


This first section of this article gives an introduction to colors and showshow an image records the color value of each pixel.

The next section describes image transparency, alpha blending, animation, andhow to scale images.Finally, the article shows how to create cursors from images, by using a sourceimage together with a mask.

Introduction

The simplest way to create an SWT Image is to load it from a recognized graphicfile format. This includes GIF, BMP (Windows format bitmap), JPG, and PNG. TheTIFF format is also supported in more recent Eclipse releases. Images can beloaded from a known location in the file system using the constructor Image(Displaydisplay, String fileLocation):

Image image = new Image(display,
  "C:/eclipse/eclipse/plugins/org.eclipse.platform_2.0.2/eclipse_lg.gif");

Instead of hard-coding the location of the image, it's more common to loadthe Image from a folder location relative to a given class. This is done bycreating an InputStream pointing to the file with the method Class.getResourceAsStream(Stringname), and using the result as the argument to the constructor Image(Displaydisplay, InputStream inputStream).

The Eclipse package explorer below shows the class com.foo.ShellWithButtonShowingEclipseLogoand the eclipse_lg.gif in the same folder. To following code wouldload the graphic from its location relative to the class.

Image image = new Image(display,
   ShellWithButtonShowingEclipseLogo.class.getResourceAsStream(
      "eclipse_lg.gif"));

Once the image has been created it can be used as part of a control such as aButton or Label that is able to render the graphic as part of their setImage(Imageimage) methods.

Button button = new Button(shell,SWT.PUSH);
button.setImage(image);

Images can be drawn onto using a graphics context that is created with theconstructor GC(Drawable drawable) with the Image as the argument.

GC gc = new GC(image);
gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
gc.drawText("I've been drawn on",0,0,true);
gc.dispose();

Using a GC to draw onto an Image permanently alters the graphic. Moreinformation on how to use a GC is covered in the article GraphicsContext - Quick on the draw.

Image lifecycle

When an image is loaded, the first step is to create device independent ImageData represented by the class org.eclipse.swt.graphics.ImageData. Following this, the data is prepared for a specific device by creating an actual Image instance.

As well as loading an Image directly from a file, you can separately create the ImageData object and then construct the Image using Image(Device device, ImageData imageData). The data for an existing Image can be retrieved using getImageData(), 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.

Instances of Image represent an underlying resource that has been preparedfor a specific device and they must be disposed when they are no longer requiredto free up the allocated resource. There is no finalization of resources in SWTwhen an object is garbage collected. For more information see SWT:The Standard Widget Toolkit: Managing Operating System Resources.

ImageData

ImageData can be thought of as the model for an image, whereas the Image is theview that's been prepared for output onto a specific device. The ImageData has awidth and height, and a pixel value for each coordinate. The raw data of theimage is a byte[], and the depth of the image specifies the number of bits thatare used for each coordinate. An image depth of 1 can store two possible valuesfor each pixel (0 and 1), a depth of 4 can store 2^4=16, a depth of 8 canstore 2^8=256 values and a depth of 24 can represent 2^24=16 million differentvalues per pixel. The larger the depth of an image the more bytes are requiredfor its pixels, and some formats such as GIF that were initially designed fordownload across internet connections only support a maximum depth of 8. How thevalue of each pixel value translates into an actual color depends on its paletterepresented by the class org.eclipse.swt.graphics.PaletteData.

The next section describes how colors are representedby their RGB values, and how PaletteData maps a mappixel value to a particular color.

Color

The class org.eclipse.swt.graphics.Color is used to manage resourcesthat 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 colorto 255 for full color).

Color cyanColor = new Color(display,0,255,255);
// ... Code to use the Color
cyanColor.dispose();

The convenience class org.eclipse.swt.graphics.RGB exists in SWTthat combines a color's red, green and blue values into a single object.

RGB cyanRGB = new RGB(0,255,255);
Color cyanColor = new Color(display,cyanRGB);
// ... Code to use the Color
cyanColor.dispose();

The Color instance should be disposed when it is no longer required, whereasthe RGB has no need to be disposed. This is similar to the relationship betweenan Image and its ImageData, where Color and Image are device specific objectsusing underlying native resources, while RGB and ImageData are the underlyingmodel data.

To avoid having to create and manage instances of the commonly used colors,the Display class allow these to be retrieved using the method Display.getSystemColor(intid).

Color cyanColor = display.getSystemColor(SWT.COLOR_CYAN)

When a Color is obtained by an SWT program using the method Display.getSystemColor(intid) method, it must not be disposed. The rule of thumb that works for anySWT resource is "If you created it, you are responsible for disposing it".Because the statement above retrieved the cyan color instance, and didn'texplicitly construct it, it should not be disposed.

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 SWT Color Model.

PaletteData

There are two kinds of PaletteData, an indexed palette and a direct palette. 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.

Indexed palette

With an indexed palette each pixel value represents a number that is then crossindexed with the palette to determine the actual color. The range of allowablepixel values is the depth of the image.

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

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
set a 34 by 34 square in the center of the ImageData to be 1 (green).

PaletteDatapaletteData = new PaletteData(
        new RGB[] {new RGB(255,0,0), newRGB(0,255,0)});

    ImageData imageData = new ImageData(48,48,1,paletteData);
for(intx=11;x<35;x++){
        for(int y=11;y<35;y++){
           imageData.setPixel(x,y,1);
        }
    }
    Image image = new Image(display,imageData);

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.

Direct palette

Instead of having each pixel value represent an index in the palettecorresponding to its color, a direct palette allows each pixel value to directlyrecord its red, green and blue component.  A direct PaletteDatadefines red, green and blue masks.  These masks are number of bits requiredto shift a pixel value to the left so the high bit of the mask aligns with thehigh bit of the first byte of color.  For example, a 24 bit direct palettecan divide itself into 3 portions, storing red in the lowest 8 bits, green inthe central 8 bits and blue in the highest 8 bits.  The red shift maskwould be 0xFF, green 0xFF00 and blue 0xFF0000.

The value for each pixel represents a combination of the red, green and bluecomponents into a single 24 bit integer. To construct an indexed palette theconstructor used

allowsthe red, green and blue color masks to be specified.

PaletteData palette =new PaletteData(0xFF , 0xFF00 , 0xFF0000);
    ImageData imageData = new ImageData(48,48,24,palette);

Using the same technique as earlier, the code iterates over every pixel coordinate setting it to either

0xFF (for red) or
0xFF00 (for green).

    for (int x=0;x<48;x++){
        for(int y=0;y<48;y++){
            if(y >11 && y < 35 && x > 11 && x < 35){

            imageData.setPixel(x,y,0xFF00);   // Set thecenter to green
            } else {
               imageData.setPixel(x,y,0xFF);   // andeverything else to red
        }
        }
    };
    Image image = new Image(display,imageData);

This creates the result below where the image is red with a green center.

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.

With both direct and indexed palettes you can go from an RGB to a pixel valueand vice-versa using the public methods int getPixel(RGB rgb) and RGB getRGB(intpixelValue).

Transparency

The purpose of transparency is to make a portion of the image non-opaque, sowhen it is drawn on a GUI surface the original background shows through. This isdone by specifying that one of the image colors is transparent. Whenever a pixelwith the transparent color value is drawn, instead of using the RGB valuedefined in the palette the original destination background pixel color is usedinstead. The effect to the user is that the areas of the image that equal thetransparent pixel show the background of whatever the image is being drawn over,thus achieving transparency. Persisted image file formats such as GIF or BMPallow you to specify a transparent pixel value, although only if the palette isindexed and the color depth is 8 or less.

When Images are used directly on controls such as Button or Label the nativebehavior may be that transparent pixels are ignored and drawn in the pixel colorspecified by the source. Native image transparency however is supported in SWTfor operations involving a GC. To illustrate this the following file Idea.gifhas a color depth of 8, and the white pixel (index 255 in the palette) set to bethe transparent pixel.

The shell below has a Label on the left with a Canvas next to it. TheIdea.gif is used

as thelabel's image, and also in the paint event
of the Canvas. Because the Label does not support native transparency theoriginal white color of the transparent pixel is used as the background, howeverthe GC in the paint event respects the transparent pixel and the grey backgroundshows through.

    Image ideaImage = newImageData(getClass().getResourceAsStream("Idea.gif"));
    Label label = new Label(shell,SWT.NONE);

label.setImage(ideaImage);
    Canvas canvas = new Canvas(shell,SWT.NO_REDRAW_RESIZE);
    canvas.addPaintListener(new PaintListener() {
        public voidpaintControl(PaintEvent e) {
        e.gc.drawImage(ideaImage,0,0);
        }
    });

For the above example I stacked the deck in my favor, because I didn'tactually use the idea graphic that is included with eclipse articles in theirbanner. The reason is that the original graphic is a JPG file which doesn'tsupport transparency, so I used a graphics tool to convert it to a GIF and setthe value of the white pixel in the palette to be the transparency pixel. Theoriginal Idea.jpg is shown below, and although it looks the same as theIdea.gif, this is because it is on the white background of the HTML browser.

By using the original JPG file this offers a good example of how we toachieve a transparency effect progrmatically by manpulating its ImageData. TheImageData class has a public field transparentPixel to specify whichpixel is transparent that can be set once a persisted image file is loaded intoan ImageData instance, irrespective of whether the persisted file formatsupports transparency.

The code below loads the Idea.jpg file in an ImageData object and

sets the transparent pixel for the ImageData to be the pixel value of the colorwhite in the palette. The pixel value in the indexed palette that representswhite is retrieved
byusing getPixel(RGB). The manipulated ImageData is used to create an ImagetransparentIdeaImage that now has the white pixel value specified to betransparent.

    ImageData ideaData = new ImageData(
       getClass().getResourceAsStream("Idea.jpg"));

int whitePixel =ideaData.palette.getPixel(new RGB(255,255,255));
ideaData.transparentPixel = whitePixel;
    Image transparentIdeaImage = new Image(display,ideaData);

Next a Shell uses the newly created image

in a native Label and also in the paint event
of a Canvas. A Windows Label does not support native transparency so it stillappears with a white background, however the GC for the Canvas uses the existingbackground color whenever a white pixel is encountered in the source image, sothe image appears as transparent.

    Label transparentIdeaLabel = newLabel(shell,SWT.NONE);

transparentIdeaLabel.setImage(transparentIdeaImage);
    Canvas canvas = new Canvas(shell,SWT.NONE);
    canvas.addPaintListener(new PaintListener() {
        public voidpaintControl(PaintEvent e) {
        e.gc.drawImage(transparentIdeaImage,0,0);
        }
    });

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.

Manipulating ImageData

Because the graphic Idea.jpg isn't as white as we'd like it, we'll iterate overits imageData and convert the off-white pixels to pure white and then we'll savethis into a new file Idea_white.jpg. In practice it's unlikely that you'd everdo this kind of programming in SWT, however it's a good example to use showinghow ImageData can be analyzed and manipulated.

The first step is to load the image and then iterate over each pixelindividually 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 asmasked bit areas. These mask value can be obtained from the palette.

ImageData ideaImageData = new ImageData(
    getClass().getResourceAsStream("Idea.jpg"));

int redMask = ideaImageData.palette.redMask;
int blueMask = ideaImageData.palette.blueMask;
int greenMask = ideaImageData.palette.greenMask;

For any pixel value we can bitwise AND it with the mask to see what the colorcomponent is. The red component is the low order bits so this will be the actualvalue (from 0 to 255), however the green and blue values need adjusting as theyare the high order bits in the pixel value. To make this adjustment the colorcomponent can be bit shifted to the right using the >> operator. If youare writing generic code to do this kind of manipulating, take care that directpalettes for color depths of 24 or 32 store their color components with redbeing the low order bits, however for color depth of 16 the colors are reversedand red is high order with blue being low order. The reason for this is to bethe same as how Windows stores images internally so there is less conversionwhen creating the image.

Two for loops will iterate over the imageData. The first is traversing theimage from top to bottom a line at a time, and

creates an int[] to hold each line of data. The methodImageData.getPixels(int x, int y, int getWidth, int[] pixels, int startIndex)is used
to extract aline at a time from the imageData's bytes. The API for this method is slightlyirregular, because rather than returning the resulting data it instead isdeclared as void, and the resulting pixel data is injected into theint[] that is passed in as a method argument. The int[] of pixels is theniterated over and each value has its
red,
green and
blue component extracted. The desired effect we want is to determine whether thepixel is off-white and if so to make it pure white - a rule that works well isto assume that anything whose red and green component are higher than 230
and blue component higher than 150 is an off-white.

int[] lineData = newint[ideaImageData.width];
    for (int y = 0; y < ideaImageData.height; y++) {
    ideaImageData.getPixels(0,y,width,lineData,0);
        // Analyzeeach pixel value in the line
        for (int x=0;x<lineData.length; x++){
           //Extract the red, green and blue component
            intpixelValue = lineData[x];
        int r = pixelValue & redShift;
        int g = (pixelValue & greenShift) >> 8;
        int b = (pixelValue & blueShift) >> 16;
        if (r > 230 && g > 230 && b > 150){
               ideaImageData.setPixel(x,y,0xFFFFFF);
            }
        }
    };

Having manipulated the raw bytes making up the ImageData we have nowsuccessfully changed the off-white values to pure white.

Saving Images

Now that we have the ImageData where all of the whitish pixels have beenconverted to white, and the transparency pixel of the palette has been set to bethe color white, we'll save this image so that next time an SWT program needsthe pure white JPF it can just load the file and use it as is. To save ImageDatainto a file use the class org.eclipse.swt.graphics.ImageLoader. Theimage loader has a public field data typed to ImageData[]. The reason the datafield is an array of ImageData is to support image file formats with more thanone frame such as animated GIFs or interlaced JPEG files. These are covered morein the Animation section later.

ImageLoader imageLoader = new ImageLoader();
imageLoader.data = new ImageData[] {ideaImageData};
imageLoader.save("C:/temp/Idea_PureWhite.jpg",SWT.IMAGE_JPEG);

The finished result is shown below.

It doesn't look much different to the original Idea.jpg because it is drawnon a white background, but when it is drawn on a Canvas with the white pixel setto be the transparent pixel the background shows through achieving the desiredeffect.

    ImageData pureWhiteIdeaImageData =
        new ImageData("C:/temp/Idea_PureWhite.jpg");

pureWhiteIdeaImageData.transparentPixel =
       pureWhiteIdeaImageData.palette.getPixel(new RGB(255,255,255));

    final Image transparentIdeaImage = newImage(display,pureWhiteIdeaImageData);
    Canvas canvas = new Canvas(shell,SWT.NONE);
    canvas.addPaintListener(new PaintListener() {
        public voidpaintControl(PaintEvent e) {
           e.gc.drawImage(transparentIdeaImage,0,0);
        }
    });

It might seem odd that inthe above code that after loading the Idea_PureWhite.jpg file
the transparent pixel was set to be white. Why not set the transparent pixelbefore 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. AGIF file supports native transparency, however changing the file type toSWT.IMAGE_GIF on the ImageLoader would not have worked, because GIF supports amaximum image depth of 8 and uses an indexed palette, whereas the JPEG has animage depth of 24 and a direct palette. To convert between the two formats wouldrequire analyzing the colours used by the JPEG to create the best fit 256 colorpalette, before iterating over each JPEG pixel value and creating the GIF imagedata by finding the closest color. Doing this conversion is outside the scope ofthis article, although it can be done by most commercial graphics tools. Tomatch pixel values as the color depth decreases from 24 to 8 involves algorithmsthat find the right color match for a block of pixels rather than a single pixelvalue, and is why image quality can sometimes be reduced when switching betweendifferent formats.

We have shown how an ImageData is an array of int values representing eachpixel coordinate, and how each pixel value is mapped to a color through thepalette. This allowed us to iterate over the image data for the Idea.jpg, querypixel values that were close to white, and convert these to a pure white RGBvalue. The end result of this was that we were able to create theIdea_PureWhite.jpg file that can be used as a transparent JPG by setting thewhite 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. Fortransparency the rule is that the source pixel value is used unless it'stransparent in which case the destination pixel is used. Another technique is touse alpha values that specify the weight applied to the source relative to thedestination to create the final pixel value. This allows the blending betweenthe source image and the existing background it is being drawn onto.

Blending

Alpha blending is a technique used to merge two pixel values, where the sourceand destination pixel each specify an alpha value that weights how much theywill affect the final destination pixel. An alpha value of 255 is full weight,and 0 is no weight. SWT supports a single alphavalue for the entire ImageData, or else each pixel can have its ownalpha value.

Single alpha value

The int field alphaValue of ImageData is used to specify asingle value that weights how the source pixels are combined with thedestination input to create the destination output. The listing below shows howthe same ImageData for the Idea_PureWhite.jpg is used for three separate images.The first
is theoriginal, then
an alphaof 128 is used, and finally
an alpha of 64. Note that the same ImageData is continually manipulated byhaving its alpha changed before creating each Image, and changing the ImageDatahas no affect on Images already constructed using it. This is because the Imageis prepared for display on the device from the ImageData at construction time.

    Shell shell = new Shell(display);
    shell.setLayout(new FillLayout());
    ImageData imageData = newImageData("C:/temp/Idea_PureWhite.jpg");

final Image fullImage =new Image(display,imageData);
    imageData.alpha = 128;
final Image halfImage =new Image(display,imageData);
    imageData.alpha = 64;
final Image quarterImage= new Image(display,imageData);

    Canvas canvas = newCanvas(shell,SWT.NO_REDRAW_RESIZE);
    canvas.addPaintListener(new PaintListener() {
        public voidpaintControl(PaintEvent e) {
           e.gc.drawImage(fullImage,0,0);
           e.gc.drawImage(halfImage,140,0);
           e.gc.drawImage(quarterImage,280,0);
        }
    });

Different alpha value perpixel

As well as having a single alpha value that applied to all pixels in the sourceimage, ImageData allows each pixel to have its own individual alpha value. Thisis done with the byte[] field alphaData. This allows effects to beachieved, such as having an image fade from its top to bottom.

The following code creates

an alphaData byte[], and then has two loops. The outer loop y
is from 0 to the imageData's height, and the inner loop
creates a byte[] for the width of the imageData and initializes it witha value that increases from 0 for the top row through to 255 for the bottom row.A System.arrayCopy
thenbuilds up the alphaData byte[] with each row.

    ImageData fullImageData = newImageData("C:/temp/Idea_PureWhite.jpg");
    int width = fullImageData.width;
    int height = fullImageData.height;

byte[] alphaData = newbyte[height * width];
for(inty=0;y<height;y++){
        byte[] alphaRow = newbyte[width];
    for(int x=0;x<width;x++){
           alphaRow[x] = (byte) ((255 * y) /height);
        }
    System.arraycopy(alphaRow,0,alphaData,y*width,width);
    }
    fullImageData.alphaData = alphaData;
    Image fullImage = new Image(display,fullImageData);

The resulting image is shown below, and the alphaData byte[] makesthe top of the image transparent and the bottom opaque, with a gradual fadingbetween the two.

Image effects

As well as arbitrary image effects that can be achieved by manipulating imagedata, SWT provides a number of pre-defined ways of creating new images based onexisting images combined with certain styles. This is used if, for example, youhave an image being used on a toolbar button and you wish to create a versionthat can be used to indicate that the button is disabled or that it is inactive.

To create an effect based on an existing image and a style flag use theconstructor Image(Display display, Image image, int flag). The flagargument is a static constant of either SWT.IMAGE_COPY, SWT.IMAGE_DISABLE orSWT.IMAGE_GRAY. Copy creates a new image based on the original but with a copyof its imageData, whereas Disable and Gray create a new image applying platformspecific effects. The following code shows the Idea.jpg, together with threemore images that we created using the style bits

IMAGE_DISABLE,
IMAGE_GRAY and
IMAGE_COPY. To show that IMAGE_COPY creates a new image
a GC is used to draw onto it that affects only the copied image, not theoriginal.

    Image ideaImage = new Image(display,
        getClass().getResourceAsStream("/icons/Idea.jpg");

ImagedisabledImage  = new Image(display,image,SWT.IMAGE_DISABLE);
Image grayImage = newImage(display,image,SWT.IMAGE_GRAY);
Image copyImage = newImage(display,ideaImage,SWT.IMAGE_COPY);
GC gc = newGC(copyImage);
    gc.drawText("This is a copy",0,0);
    gc.dispose();

GIF Animation

Another important topic for images is understanding animation where an image cancontain a number of frames that are animated in sequence. Image animation issupported by GIF images, where a single image file can contain multiple sets ofImageData. Web browsers support native animation, and the following image ismade up of 15 frames showing the pen rotating and writing the words SWT beneaththe Idea logo.

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 org.eclipse.swt.examples.ImageAnalyzer shows how this can be achieved. The ImageAnalyzer class can be obtained from the SWT examples project in the Eclipse CVS repository.

When an animated GIF is loaded by the ImageLoader class, each individualframe is a separate element in the data field array typed to ImageData[]. In the animation sequence each ImageData records how many milliseconds it shouldbe displayed for in the int field delayTime. The number of times the sequenceshould repeat can be retrieved from the field loader.repeatCount, a value of -1indicates that the animation should repeat indefinitely andIdea_SWT_Animation.gif has this value. When switching from one frame to the nextthere are three ways that the new frame can replace the previous one, specifiedby the int field ImageData.disposalMethod. This can take the following valuesdefined in the constant class org.eclipse.swt.SWT.
 

DM_FILL_NONE Leave the previous image in place and just draw the image on top.  Each frame adds to the previous one.
DM_FILL_BACKGROUND Fill with the background color before painting each frame. The pixel value for this is defined in the field loader.backgroundPixel
DM_FILL_PREVIOUS Restore the previous picture 
DM_FILL_UNSPECIFIED No disposal method has been defined

To conserve on space, animated GIFs are generally optimized to just store thedelta that needs to be applied to the previous image. In theIdea_SWT_Animation.gif above the 15 frames are shown below. Each frame stores adelta against the previous image, and this was automatically generated by thetool I create the animated GIF with. The disposal method for each frame isDM_NONE so the each image should be drawn on top of the previous one. Eachindividual ImageData element has the x and y for its top left corner, as well asits width and height. The overall size to use can be obtained from the fieldsloader.logicalScreenWidth and loader.logicalScreenHeight.

To illustrate how to display an animated GIF in SWT we'll create an

initial Image from the first frame and a counter to store which frame is beingdisplayed. The image is drawn
a paint event on a Canvas, and a GC is created
that will be used to draw the subsequent frames onto the image.

    ImageLoader loader = new ImageLoader();
   loader.load(getClass().getResourceAsStream("Idea_SWT_Animation.gif"));
    Canvas canvas = new Canvas(shell,SWT.NONE);

image = newImage(display,loader.data[0]);
    int imageNumber;
final GC gc = newGC(image);
    canvas.addPaintListener(new PaintListener(){
      public void paintControl(PaintEvent event){
    event.gc.drawImage(image,0,0);
      }
    });

The body of the example will create a thread that iterates through eachframe, waiting until the

delayTimehas passed. For each frame the
ImageData is retrieved from the loader and a temporary Image created. This isthen drawn onto the image being displayed on the canvas, at the x and y positionspecified by the frame's ImageData. Because we created the temporaryframeImage
we mustdispose it when it's no longer being used to free up the underlying resource.

    Thread thread = new Thread(){
      public void run(){
        long currentTime =System.currentTimeMillis();
        int delayTime =loader.data[imageNumber].delayTime;

    while(currentTime + delayTime * 10 > System.currentTimeMillis()){
         // Wait till the delay time has passed
        }
        display.asyncExec(new Runnable(){
          public void run(){
           // Increase the variable holding the frame number
            imageNumber = imageNumber == loader.data.length-1 ? 0 : imageNumber+1;
           // Draw the new data onto the image
        ImageData nextFrameData = loader.data[imageNumber];
            ImageframeImage = new Image(display,nextFrameData);
        gc.drawImage(frameImage,nextFrameData.x,nextFrameData.y);
        frameImage.dispose();
           canvas.redraw();
          }
        });
      }
    };
    shell.open();
    thread.start();

Scaling

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 GC.drawImage(Image image, int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight), and the second is to use ImageData.scaledTo(int width, int height) to create a new ImageData object based on scaling the receiver.

The following code loads the Idea.jpg image

,and scales this to 1/2 and 2 times its original size
using the ImageData.scaledTo(int width, int height). The image is alsoresized using GC.drawImage(...), and the example shows two ways toachieve this. The first technique
is to specify the new width and height as part the paint event. This ispotentially inefficient because the scaling must be done each time the canvasrepaints itself. A more optimized technique is to create an image at the finaldesired size,
constructa GC over the this and then paint onto it so a permanent scaled image exists inthe program.

The end result is shown below, and both techniquesproduce almost identical results.

final Image image =new Image(display,
       getClass(),getResourceAsStream("Idea.jpg"));

    final int width = image.getBounds().width;
    final int height = image.getBounds().height;

final Image scaled050= new Image(display,
       image.getImageData().scaledTo((int)(width*0.5),(int)(height*0.5)));

    final Image scaled200 = new Image(display,
       image.getImageData().scaledTo((int)(width*2),(int)(height*2)));

final ImagescaledGC200 = new Image(display,(int)(width*2),(int)(height*2));
    GC gc = new GC(scaledGC200);
   gc.drawImage(image,0,0,width,height,0,0,width*2,height*2);
    gc.dispose();

    canvas.addPaintListener(new PaintListener() {
      public void paintControl(PaintEvent e) {

    e.gc.drawImage(image,0,0,width,height,0,0,(int)(width*0.5),(int)(height*0.5));
        e.gc.drawImage(scaled050,100,0);
        e.gc.drawImage(scaledGC200,0,75);
       e.gc.drawImage(scaled200,225,175);
      }
    });

When to use GC scaling, and when to use ImageData.scaledTo(...), 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 ImageData(String fileName) or ImageData(InputStream stream)). 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.

Cursor

The final section of this article covers the class org.eclipse.swt.graphics.Cursorresponsible for managing the operating system resource associated with the mousepointer. The reason cursors are covered in an article on images is because youcan create arbitrary cursors from images, and they illustrate how image maskswork.

Cursors can be created in two ways, either from a pre-defined style or usingsource and mask images.

Platform cursors

The list of pre-defined styles are SWT constants shown below, together withsample images although these will vary depending on the operating system andplatform settings.
 
CURSOR_APPSTARTING
CURSOR_IBEAM
CURSOR_SIZENE
CURSOR_ARROW
CURSOR_NO
CURSOR_SIZENESW
CURSOR_CROSS
CURSOR_SIZEALL
CURSOR_SIZENS
CURSOR_HAND
CURSOR_SIZEE
CURSOR_SIZENW
CURSOR_HELP
CURSOR_SIZEN
CURSOR_SIZESNWSE
CURSOR_SIZES
CURSOR_SIZESE
CURSOR_SIZESW
CURSOR_SIZEWE
CURSOR_UPARROW
CURSOR_WAIT

Every Control can have a cursor associated with it, and when the mousepointer moves over the control it changes to the specified cursor. Changing a cursor also affects any child controls, so if you update the cursoron a Shell this affects the mouse pointer for anywhere on the shell, although ifthe child control itself has an explicit cursor, or uses its own cursor such asan I bean for Text or Combo, this takes precedence over the parent's definedcursor. The following code illustrates this, by changing the shell's cursor tobe hand cursor, and the list's cursor to a cross. When the mouse is over theshell (or its childButton that has no explicit cursor) it is a hand, and when itis over the list it is a cross.

List list = new List(shell,SWT.BORDER);
Button button = new Button(shell,SWT.NONE);
button.setText("Button");
Cursor handCursor = new Cursor(display,SWT.CURSOR_HAND);
shell.setCursor(handCursor);
Cursor crossCursor = new Cursor(display,SWT.CURSOR_CROSS);
list.setCursor(crossCursor);

Cursors use underlying native resources and should be disposed when they areno longer required. In the above code this would be when the shell has beendisposed and there are no remaining controls using either the handCursor orcrossCursor fields.

Custom cursors

As well as using a pre-defined style, a cursor can be created from images using the constructor Cursor(Device device, ImageData source, ImageDatamask, int hotspotx, int hotspoty). The source imagedata argument is thegraphic for the cursor shape, and the mask is used to specify transparency. Thefollowing example shows how to create a monochrome custom cursor, where the thesource and mask image data have a color depth of 1 and indexed palettes with twocolors. The ImageData’s height and width should be no larger than 32 and itdoes not necessarily have to be square, although the mask and source should bethe same size. The hotspot is the point on the cursor that represents theprecise location of the mouse pointer.

The source and mask image data pixels are combined to determine whether thecursor pixel should be white, black or transparent.
 

Image Mask Cursor color
1 0 Transparent
0 0 Black
1 1 Black
0 1 White

The ImageData can be loaded from files, and Eclipse itself does this for someof its drag and drop cursors defined in org.eclipse.ui/icons/full/dnd. Youcan also directly create and manipulate the image data within your program. Thecode sample below creates an indexed palette with two colors. The source andmask ImageData are 32 by 32 with a color depth of 1. The int[] for the sourceand mask define an up arrow, the 0s for the source and 1s for the mask are shownin bold to show how the arrow is defined with the arrays. 0 and 1 makes whitewhich 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 thisis made the cursor hotspot when it is created.

PaletteData paletteData = new PaletteData(new RGB[] {
 new RGB(0,0,0) , new RGB(255,255,255)
});
ImageData sourceData = new ImageData(32,32,1,paletteData);
ImageData maskData = new ImageData(32,32,1,paletteData);

int[] cursorSource = new int[] {
    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,
    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,
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,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,0,0,0,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,0,0,0,0,0,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,0,0,0,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,0,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,0,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,0,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,0,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,0,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,0,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,0,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,0,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,0,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,0,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,0,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,0,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,0,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,0,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,0,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,0,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,0,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,0,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,0,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,0,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,0,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,0,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,0,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,0,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,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 };

int[] cursorMask = new int[] {
    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,
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,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,1,1,1,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,1,1,1,1,1,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,1,1,1,1,1,1,1,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,1,1,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,1,1,1,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, };

sourceData.setPixels(0,0,1024,cursorSource,0);
maskData.setPixels(0,0,1024,cursorMask,0);
Cursor cursor = new Cursor(display,sourceData,maskData,16,3);
shell.setCursor(cursor);

To keep the code listings narrow the above sample used an int[]to define the source and mask imageData. A byte uses less memory than anunsigned int, so when creating custom cursors it is more efficient to use abyte[] instead, such as:

byte[] cursorSource = new byte[] {
     (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x01,(byte)0x00, // etc...

Custom cursors need to be disposed in just the same way as pre-defined systemcursors, so when there is no remaining control using the cursor is must be sendthe method dispose() to free up the underlying native resource.

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: snippet 1, snippet 2.

Conclusion

This article has showed how to create and manipulate SWT images.Underlying each Image is its ImageData that records the pixel value for eachcoordinate, and the palette that maps this to a specific color. By understandingImageData it is possible to achieve effects such as transparency, alphablending, animation, as well as customized cursors.

Java and all Java-based trademarks and logos are trademarksor registered trademarks of Sun Microsystems, Inc. in the United States, othercountries, or both.

Windows is a trademark of Microsoft corporation in the UnitedStates, other countries, or both.

Other company, product, and service names may be trademarks orservice marks of others.

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
JAVA.SWT/JFace: SWT高级控件之SWT系统资源
SWT(JFace)体验之Canvas(画布)
swt与awt/swing互嵌
(转)Eclipse学习笔记(3.1)SWT API之图形API
如何创建Windows XP 图标
Java image的文件获取、显示、像素分解、合成
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服