#1938 Default Image.makePainted background to transparent

SlimerDude Wed 4 Jul 2012

When creating an Image with gfx::Image#makePainted how do I set (or make) a pixel transparent?

I can't use

g.brush = Color(argb)
g.fillRect(x, y, 1, 1)

because that draws a pixel with alpha, it doesn't set the alpha on the image

I've also messed around with gfx::Graphics#alpha whilst drawing, but it doesn't seem to do much different.

andy Thu 5 Jul 2012

SWT or JS? JS has an API to clear a region of the buffer - haven't looked at SWT though - if it does too - we should be able to add a method to Graphics for that.

SlimerDude Fri 6 Jul 2012

It was the SWT version I was looking at, with a view of porting it to JS later. What's the JS API you mention?

andy Fri 6 Jul 2012

SlimerDude Sun 22 Jul 2012

The following says you can set a particular colour to be transparent:

Taking a look at SWT Images

Perhaps we could have a method to set the entire image to be transparent - or maybe the image could be transparent when it is first initialised?

andy Sun 22 Jul 2012

gfx::Image#makePainted ... or maybe the image could be transparent when it is first initialized

I didn't realize this wasn't already the case - we should fix that.

andy Mon 20 Aug 2012

The following says you can set a particular colour to be transparent:

Really want that to be a full alpha channel - scanned that article - not sure if they we can do that or not. Lets open though - we should be able to fix somehow - patches welcome if you want to take a look.

andy Mon 20 Aug 2012

Promoted to ticket #1938 and assigned to andy

andy Mon 20 Aug 2012

Renamed from Setting Transparent Image Pixels to Default Image.makePainted background to transparent

SlimerDude Sun 8 Oct

I've spent some time looking at this because, not being able to create re-usable transparent images is a real handicap when creating graphical programs such as Escape the Mainframe.

Unfortunately there doesn't seem to be a clean, or easy way to do this in SWT. The problem is that the alpha data is kept very separate to the RGB data. Meaning I can create an image with a transparent alpha channel, but when you subsequently paint on it, only the RGB values are manipulated; the alpha channel is untouched. Meaning the image stays completely transparent!

The only way round this is (as I see it), is once the image has been created and painted on, to iterate over every pixel and if it's a certain colour (say, black) make it transparent. But this doesn't seem very efficient, and it's not always the desired behaviour; sometimes you want a black background!

So I don't see a way of achieving this ticket without altering the gfx API in some way.

Hence I think a better way would be to make a couple of FWT.java fields public (so they're more accessible) and let us use them in our own unsupported code:

diff -r a60c8b8decfb src/fwt/java/Fwt.java
--- a/src/fwt/java/Fwt.java	Fri Oct 06 09:21:02 2017 -0400
+++ b/src/fwt/java/Fwt.java	Sun Oct 08 12:20:14 2017 +0100
@@ -450,10 +450,10 @@
 // Fields
 //////////////////////////////////////////////////////////////////////////
 
-  Display display = Display.getCurrent() == null ? new Display() : Display.getCurrent(); // SWT display
+  public Display display = Display.getCurrent() == null ? new Display() : Display.getCurrent(); // SWT display
   HashMap colors = new HashMap();  // Int rgb   -> Color
   HashMap fonts = new HashMap();   // fwt::Font  -> Font
-  HashMap images = new HashMap();  // Uri -> Image
+  public HashMap images = new HashMap();  // Uri -> Image
   HashMap cursors = new HashMap(); // fwt::Cursor -> Cursor
   HashMap cursorStyles; // fwt::Cursor -> Integer
   GC scratchGC;

Fantom code that lets you paint a new image, and subsequently converts all black pixels to transparent pixels below:

using [java] fan.fwt::Fwt
using [java] fan.fwt::FwtGraphics
using [java] fanx.interop::ByteArray
using [java] fanx.interop::Interop
using [java] java.util::Arrays
using [java] org.eclipse.swt.graphics::GC          as SwtGC
using [java] org.eclipse.swt.graphics::Image       as SwtImage
using [java] org.eclipse.swt.graphics::ImageData   as SwtImageData
using [java] org.eclipse.swt.graphics::PaletteData as SwtPaletteData
using [java] org.eclipse.swt.graphics::RGB         as SwtRGB
using gfx::Image
using gfx::Graphics

class ImageUtil {
    static Image paintTransparentImage(Int w, Int h, |Graphics| f) {
        fwt         := Fwt.get
        palette     := SwtPaletteData(0xFF0000, 0xFF00 , 0xFF)
        imageData   := SwtImageData(w, h, 24, palette)
        image       := SwtImage(fwt.display, imageData)
        g           := (Graphics) FwtGraphics(SwtGC(image), 0, 0, w, h)

        try {
            f.call(g)
        
            // pixels set to this colour will become transparent - default to black
            transparentPixel := palette.getPixel(SwtRGB(0, 0, 0))
        
            // default all pixels to opaque, then loops over the images making some transparent
            imageData = image.getImageData
            imageData.alphaData = ByteArray.make(w * h)
            Arrays.fill(imageData.alphaData, 255)
            for (x:=0; x<w; x+=1)
                for (y:=0; y<h; y+=1)
                    if (imageData.getPixel(x, y) == transparentPixel)
                        imageData.setAlpha(x, y, 0)
        
            // create a new image based on the newly set alpha values
            image = SwtImage(fwt.display, imageData)
            
            imgUri  := `mem-${Uuid()}`
            fanImg  := Image.makeFields(imgUri, ``.toFile)  // Fantom Bug - File param should be nullable
            fwt.images.put(imgUri, image)
            return fanImg

        } finally {
            g.dispose
        }
    }
}

I also have a Java version of the above code if anyone is interested.


On a semi-related note and as shown in the code above, the file parameter in gfx::Image.makeFields() should be nullable, as in:

@NoDoc new makeFields(Uri uri, File? file)

The related file field is nullable and FwtEnvPeer.toFanImage() even passes null into the native Java ctor:

fan.gfx.Image fanImage = fan.gfx.Image.makeFields(uri, null);

brian Mon 16 Oct

I doubt we will spend much more time on fwt since we are moving everything to domkit and web technologies. If what we are talking about is exposing a few FWT Java methods as public for a pluggable solution that sounds like a good idea to me. So can you confirm exactly what you think needs to change for that?

SlimerDude Mon 16 Oct

exposing a few FWT Java methods as public for a pluggable solution that sounds like a good idea

That's exactly what I'm after.

I couldn't find a way to force SWT to work in a way that the fits current framework. And as you say, it's not worth the effort to change the FWT API. So I figure the best trade off is to settle with some custom Java / Fantom code.

I'm asking for the following fields in Fwt.java to become public:

  • display
  • images

Or see the patch in my last post for details.

brian Mon 16 Oct

Try out that fix I just pushed which adds images() and display() as public methods

SlimerDude Tue 17 Oct

Thanks Brian, that should work nicely!

Login or Signup to reply.