Drawing on a transparent image using Java SWT
Asked Answered
L

4

12

How do I create an in-memory fully transparent SWT image and draw a black line on it with antialias enabled?

I expect the result to include only black color and alpha values ranging from 0 to 255 due to antialias...

I googled and tried everything that I could... is this possible at all?

Lainelainey answered 25/11, 2011 at 14:27 Comment(0)
A
7

This is how I did and it works:

    Image src = new Image(null, 16, 16);        
    ImageData imageData = src.getImageData();
    imageData.transparentPixel = imageData.getPixel(0, 0);
    src.dispose();
    Image icon = new Image(null, imageData);
    //draw on the icon with gc
Aeropause answered 12/12, 2014 at 23:37 Comment(1)
This works in both Labels and ToolbarItems, can easily be generalized and also supports drawing. This should be the accepted answer.Haldane
K
4

I was able to make this work, although it feels a bit hacky:

Display display = Display.getDefault();

int width = 10;
int height = 10;

Image canvas = new Image(display, width, height);

GC gc = new GC(canvas);

gc.setAntialias(SWT.ON);

// This sets the alpha on the entire canvas to transparent
gc.setAlpha(0);
gc.fillRectangle(0, 0, width, height);

// Reset our alpha and draw a line
gc.setAlpha(255);
gc.setForeground(display.getSystemColor(SWT.COLOR_BLACK));
gc.drawLine(0, 0, width, height);

// We're done with the GC, so dispose of it
gc.dispose();

ImageData canvasData = canvas.getImageData();
canvasData.alphaData = new byte[width * height];

// This is the hacky bit that is making assumptions about
// the underlying ImageData.  In my case it is 32 bit data
// so every 4th byte in the data array is the alpha for that
// pixel...
for (int idx = 0; idx < (width * height); idx++) {
    int coord = (idx * 4) + 3;
    canvasData.alphaData[idx] = canvasData.data[coord];
}

// Now that we've set the alphaData, we can create our
// final image
Image finalImage = new Image(canvasData);

// And get rid of the canvas
canvas.dispose();

After this, finalImage can be drawn into a GC with drawImage and the transparent parts will be respected.

Kery answered 28/3, 2013 at 15:13 Comment(5)
gc.setAlpha effects its following modifications, not the image's alpha data. so, this lines have no effect. // This sets the alpha on the entire canvas to transparent gc.setAlpha(0); gc.fillRectangle(0, 0, width, height);Saveall
Did you down-vote because the code doesn't work or because the two lines you mentioned don't have any affect?Kery
Oh, I'm sorry. There's a problem that I forgot to write. If you create a GC with the "finalImage", that GC will have alpha value per pixel. So, drawing will be affected by each pixel's alpha value. (Although GC.getAlpha reutrns 255;) that means you can't draw a fully opaque line(or rectangle, etc.) in advanced mode. I recommend to make a Image with Transparent pixel. see this: eclipse.org/articles/Article-SWT-images/… I'm really sorry If you hurted because of my mistake.Saveall
No harm done, but I'm still not entirely sure what is wrong with the code that I posted. In my tests it works as the OP requested. Also, the transparent pixel will not work in this case as the OP wanted an anti-aliased line which does not work with a single transparent pixel.Kery
My apology for late answer. Here is a step to see the problem that I mentioned. 1. make a fully transparent 32-bit PNG image file with alpha channel. 2. Create GC and Image instance with the file. 3. fill a rectangle with GC. 4. draw a image on the Canvas (or Composite, etc.) note: my test environment is Win32_x86. If you test on different environment, the result maybe different. If so, please report to me.Saveall
A
3

I made it by allocating an ImageData, making it transparent then creating the Image from the data :

static Image createTransparentImage(Display display, int width, int height) {
    // allocate an image data
    ImageData imData = new ImageData(width, height, 24, new PaletteData(0xff0000,0x00ff00, 0x0000ff));
    imData.setAlpha(0, 0, 0); // just to force alpha array allocation with the right size
    Arrays.fill(imData.alphaData, (byte) 0); // set whole image as transparent

    // Initialize image from transparent image data
    return new Image(display, imData);
}
Anthurium answered 7/1, 2015 at 15:16 Comment(1)
This works fine for Labels, but in a ToolbarItem, the result is a solid black image :(Haldane
D
0

To scale with transparency, I've found that I have to manually set the alpha byte array as shown below. So the alpha ends up with nearest-neighbor anti aliasing.

public static Image scaleImage(Device device, Image orig, int scaledWidth, int scaledHeight) {
    Rectangle origBounds = orig.getBounds();
    if (origBounds.width == scaledWidth && origBounds.height == scaledHeight) {
        return orig;
    }

    ImageData origData = orig.getImageData();
    ImageData imData = new ImageData(scaledWidth, scaledHeight, origData.depth, origData.palette);
    if (origData.alphaData != null) {
        imData.alphaData = new byte[imData.width * imData.height];
        for (int row = 0; row < imData.height; row++) {
            for (int col = 0; col < imData.width; col++) {
                int origRow = row * origData.height / imData.height;
                int origCol = col * origData.width / imData.width;
                byte origAlpha = origData.alphaData[origRow * origData.width + origCol];
                imData.alphaData[row * imData.width + col] = origAlpha;
            }
        }
    }
    final Image scaled = new Image(device, imData);
    GC gc = new GC(scaled);
    gc.setAntialias(SWT.ON);
    gc.setInterpolation(SWT.HIGH);
    gc.setBackground(device.getSystemColor(SWT.COLOR_WHITE));
    gc.fillRectangle(0, 0, scaledWidth, scaledHeight);
    gc.drawImage(orig, 0, 0, origBounds.width, origBounds.height, 0, 0, scaledWidth, scaledHeight);
    gc.dispose();
    return scaled;
}
Daric answered 17/11, 2017 at 14:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.