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.
andyThu 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.
SlimerDudeFri 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?
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?
andySun 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.
andyMon 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.
andyMon 20 Aug 2012
Promoted to ticket #1938 and assigned to andy
andyMon 20 Aug 2012
Renamed from Setting Transparent Image Pixels to Default Image.makePainted background to transparent
SlimerDudeSun 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:
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?
SlimerDudeMon 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.
brianMon 16 Oct 2017
Try out that fix I just pushed which adds images() and display() as public methods
SlimerDudeTue 17 Oct 2017
Thanks Brian, that should work nicely!
SlightlyEpicGamesSat 15 Aug 2020
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.
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
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
clearRect
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
I didn't realize this wasn't already the case - we should fix that.
andy Mon 20 Aug 2012
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:Fantom code that lets you paint a new image, and subsequently converts all black pixels to transparent pixels below:
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 ingfx::Image.makeFields()
should be nullable, as in:The related
file
field is nullable andFwtEnvPeer.toFanImage()
even passesnull
into the native Java ctor: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
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 2020
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.