#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 2017

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 2017

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 2017

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 2017

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

SlimerDude Tue 17 Oct 2017

Thanks Brian, that should work nicely!

SlightlyEpicGames Sat 15 Aug

SlimerDude, thanks for the code further above! I'm using it to generate pixelated versions of images for my PixelStitch project. I found that when bringing colors from Java to Fantom, the Red and Blue colors get switched, so I ended up modifying your code to do something like:

c :=Color(imageData.getPixel(x, y)) //first attempt to read in java color

colors[Point(x,y)] = Color.makeRgb(c.b,c.g,c.r) //swap red and blue

In your example you only cared about finding black colors, but if you tried to cull/mask R or B colors you might have run into this same issue.

Login or Signup to reply.