image.setPixel(x, y, color) color = image.getPixel(x,y)
I need to create an image buffer and draw each pixel individually, as well as read neighboring pixels. That will all get sent out as a PNG in the end.
brianTue 20 Jun 2017
You can have a new graphics context to draw to a buffered image, and then use drawLine.
SlimerDudeTue 20 Jun 2017
I've wanted these methods many a time too!
From previous investigations, if I remember correctly, there's no reason why gfx.setPixel(x, y) can't be implemented (as long as the brush is just a simple a Colour and not a pen stroke or anything).
It's on my TODO list to submit a patch for this. For now, as Brian said, I just use gfx.drawLine() with the start and end coors set to the same coors.
As for gfx.getPixel() - that would be great (as would being able to create a transparent image!) but the underlying eclipse SWT doesn't make it easy. If you can find any documentation on it, I'll try to look into it again.
KevinTue 20 Jun 2017
Is there any way to directly access the image buffer as a 2d array? Without being able to read a pixel, I will be forced to use ... gulp! ... Java. Their image classes are very scary. :(
Also, isn't using a 1-pixel line horribly, horribly inefficient rather than just terribly inefficient? I considered it before discarding the thought. I'm already going to be running some heavy maths.
I suppose I could add these methods, but I'm not sure I currently have skillz mad enough to do it yet. I'm assuming just a wrapper around the Java methods would be needed, although I haven't fully explored them yet. Just getting a mutable image buffer made me cry.
SlimerDudeTue 20 Jun 2017
Hi Kevin,
Is there any way to directly access the image buffer as a 2d array?
Sadly, no. For now, if you're creating / drawing your own images, you could try create your own image buf at the same time.
It looks like a getPixel() may be possible in Java after all. Here are some links, mainly for myself for when I get round to looking at it:
isn't using a 1-pixel line horribly, horribly inefficient rather than just terribly inefficient?
Maybe, or maybe the underlying gfx lib is smart enough to notice the 2 coors are the same? Dunno, I've not profiled it. Anyway, right now, there's no other way!
brianTue 20 Jun 2017
Maybe some confusion, but the gfx::Image API is not an API to work with low-level image data, color space, etc. It provides access to the underlying rendering framework for drawing to a buffered image to render to the screen or to encode to a file. As such drawLine with one pixel is going to have no noticeable performance difference if there was a drawPixel (which isn't exposed by most rendering APIs anyhow). I think maybe the core problem is you need to use a different sort of image library - accessing individual pixels is different then a rendering pipeline for shapes, fonts, alpha compositing, etc
KevinTue 20 Jun 2017
One last question before I give up: Can I create my own 2d array of pixels (i.e. rgba structs) to work on and then ship it off somewhere to be turned into a PNG?
The format is simple enough to write out as a byte stream, but I was hoping to enjoy some baked in goodness.
SlimerDudeTue 20 Jun 2017
rendering framework for drawing to a buffered image
Given buffered images are inherently RGB raster images, it is not unreasonable to expect to get and set RGB pixels. No one is asking for a low level colour space API, just a couple of higher level convenience methods - something that Fantom often excels at.
The coordinate system is already working at the pixel level, so I see no harm in having a Graphics.drawPixel(x, y).
Can I create my own 2d array of pixels ... turned into a PNG?
Tricky. You'd have to create an SWT Image, then set pixels from its getImageData() to create your image.
Haha! Thanks, but that's the sort of Java-esque gymnastics I'm trying to avoid. Maybe I can use JavaScript since it has an ImageData object that is stored in a flat array (image[0] = pixel1.r, image[1] = pixel1.g, etc.)
Anyway, all this groovy stuff is found in org.eclipse.swt.graphics.ImageData, but I can think of no reason why to have a separate class to do the pixel-level operations from the more comprehensive drawing ops. ¯\_(ツ)_/¯
//if you work in FWT
FwtToolkitEnv.init
Image p := BufImage.fromUri(`fan://icons/x16/folder.png`) |p|
{
//image filter
for (i:=0; i < p.size.w; ++i)
{
for (j:=0; j < p.size.h; ++j)
{
c := p.getPixel(i,j)
//echo(c)
c = c.and(0xffff0000)
//echo(c)
p.setPixel(i, j, c)
}
}
}
g := FwtToolkitEnv.toGraphics(g1)
g.drawImage(p, 0, 0)
KevinSun 25 Jun 2017
This is awesome! Thanks!
(However, I did my program in Java already...)
SlimerDudeMon 9 Oct 2017
drawPixel()
Some rendering APIs such as SWT provide an optimised drawPixel() method, whilst some (for instance, Javascript) do not.
For completeness, and to prevent future ambiguity, I see no harm in implementing a Graphics.drawPixel() method. This lets those subsystems that have it, use it - and those subsystems that don't, can default to calling fillRect().
The patch below defines a drawPixel() method, but drawPoint() may be more in keeping with the API.
diff -r a60c8b8decfb src/fwt/fan/FwtGraphics.fan
--- a/src/fwt/fan/FwtGraphics.fan Fri Oct 06 09:21:02 2017 -0400
+++ b/src/fwt/fan/FwtGraphics.fan Mon Oct 09 13:13:42 2017 +0100
@@ -20,6 +20,7 @@
override native Bool antialias
override native Int alpha
override native GraphicsPath path()
+ override native This drawPixel(Int x, Int y)
override native This drawLine(Int x1, Int y1, Int x2, Int y2)
override native This drawPolyline(Point[] p)
override native This drawPolygon(Point[] p)
diff -r a60c8b8decfb src/fwt/java/FwtGraphics.java
--- a/src/fwt/java/FwtGraphics.java Fri Oct 06 09:21:02 2017 -0400
+++ b/src/fwt/java/FwtGraphics.java Mon Oct 09 13:13:42 2017 +0100
@@ -194,6 +194,12 @@
return new FwtGraphicsPath(this);
}
+ public Graphics drawPixel(long x, long y)
+ {
+ gc.drawPoint((int)x, (int)y);
+ return this;
+ }
+
public Graphics drawLine(long x1, long y1, long x2, long y2)
{
gc.drawLine((int)x1, (int)y1, (int)x2, (int)y2);
diff -r a60c8b8decfb src/fwt/js/FwtGraphics.js
--- a/src/fwt/js/FwtGraphics.js Fri Oct 06 09:21:02 2017 -0400
+++ b/src/fwt/js/FwtGraphics.js Mon Oct 09 13:13:42 2017 +0100
@@ -149,6 +149,13 @@
return path;
}
+// This drawPixel(Int x, Int y)
+fan.fwt.FwtGraphics.prototype.drawPixel = function(x, y)
+{
+ this.cx.fillRect(x, y, 1, 1);
+ return this;
+}
+
// This drawLine(Int x1, Int y1, Int x2, Int y2)
fan.fwt.FwtGraphics.prototype.drawLine = function(x1, y1, x2, y2)
{
diff -r a60c8b8decfb src/gfx/fan/Graphics.fan
--- a/src/gfx/fan/Graphics.fan Fri Oct 06 09:21:02 2017 -0400
+++ b/src/gfx/fan/Graphics.fan Mon Oct 09 13:13:42 2017 +0100
@@ -49,6 +49,13 @@
**
** Draw a line with the current pen and brush.
+ ** May default to calling 'fillRect(x, y, 1, 1)' if
+ ** a native implementation is not supported.
+ **
+ abstract This drawPixel(Int x, Int y)
+
+ **
+ ** Draw a line with the current pen and brush.
**
abstract This drawLine(Int x1, Int y1, Int x2, Int y2)
colorAt()
Retrieving the colour at a given coordinate of an image can be useful for things like generating alpha masks or determining collision detection.
A patch for SWT and JS implementations is given below:
diff -r a60c8b8decfb src/fwt/java/FwtEnvPeer.java
--- a/src/fwt/java/FwtEnvPeer.java Fri Oct 06 09:21:02 2017 -0400
+++ b/src/fwt/java/FwtEnvPeer.java Mon Oct 09 13:28:49 2017 +0100
@@ -17,6 +17,7 @@
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.RGB;
public class FwtEnvPeer
{
@@ -138,6 +139,20 @@
}
}
+ public fan.gfx.Color imageColorAt(FwtEnv self, fan.gfx.Image img, long x, long y)
+ {
+ // map Fantom image to SWT image
+ Fwt fwt = Fwt.get();
+ Image swtImg = fwt.image(img);
+ if (swtImg == null) throw Err.make("Image not valid or not loaded yet");
+
+ // return color at pixel coordinates
+ ImageData imageData = swtImg.getImageData();
+ RGB rgb = imageData.palette.getRGB(imageData.getPixel((int) x, (int) y));
+ int a = imageData.getAlpha((int) x, (int) y);
+ return fan.gfx.Color.makeArgb(a, rgb.red, rgb.green, rgb.blue);
+ }
+
//////////////////////////////////////////////////////////////////////////
// Font Support
//////////////////////////////////////////////////////////////////////////
diff -r a60c8b8decfb src/fwt/js/FwtEnvPeer.js
--- a/src/fwt/js/FwtEnvPeer.js Fri Oct 06 09:21:02 2017 -0400
+++ b/src/fwt/js/FwtEnvPeer.js Mon Oct 09 13:28:49 2017 +0100
@@ -145,6 +145,23 @@
return fan.gfx.Image.makeFields(uri, null);
}
+// Color imageColorAt(Image i, Int x, Int y)
+fan.fwt.FwtEnvPeer.prototype.imageColorAt = function(self, fanImg, x, y)
+{
+ var jsOrig = fan.fwt.FwtEnvPeer.loadImage(fanImg);
+
+ // create temp canvas and draw our image to it
+ var canvas = document.createElement("canvas");
+ canvas.width = jsOrig.width;
+ canvas.height = jsOrig.height;
+ var cx = canvas.getContext("2d");
+ cx.drawImage(jsOrig, 0, 0);
+
+ // extract pixel color from canvas
+ var rgba = cx.getImageData(x, y, 1, 1).data;
+ return fan.gfx.Color.makeArgb(rgba[3], rgba[0], rgba[1], rgba[2]);
+}
+
// Void imageDispose(Image i)
fan.fwt.FwtEnvPeer.prototype.imageDispose = function(self, img)
{
diff -r a60c8b8decfb src/gfx/fan/GfxEnv.fan
--- a/src/gfx/fan/GfxEnv.fan Fri Oct 06 09:21:02 2017 -0400
+++ b/src/gfx/fan/GfxEnv.fan Mon Oct 09 13:28:49 2017 +0100
@@ -64,6 +64,11 @@
**
abstract Void imageWrite(Image i, MimeType type, OutStream out)
+ **
+ ** Implementation of `Image.colorAt`
+ **
+ abstract Color imageColorAt(Image i, Int x, Int y)
+
//////////////////////////////////////////////////////////////////////////
// Font Support
//////////////////////////////////////////////////////////////////////////
diff -r a60c8b8decfb src/gfx/fan/Image.fan
--- a/src/gfx/fan/Image.fan Fri Oct 06 09:21:02 2017 -0400
+++ b/src/gfx/fan/Image.fan Mon Oct 09 13:28:49 2017 +0100
@@ -94,6 +94,14 @@
}
**
+ ** Returns the RGBA value at the given pixel position.
+ ** Some subsystems may throw 'UnsupportedErr'.
+ **
+ virtual Color colorAt(Int x, Int y) {
+ GfxEnv.cur(true).imageColorAt(this, x, y)
+ }
+
+ **
** The uri which identifies this image. If this
** image maps to an image file, then this is the file's uri.
**
@@ -153,3 +161,4 @@
Void write(MimeType type, OutStream out) { GfxEnv.cur.imageWrite(this, type, out) }
}
+
I'm aware the Javascript implementation has poor performance, but I'm not aware of a better implementation.
I also looked at obtaining a colour value from a Graphics class so you can see what you're drawing - but for SWT this info seems to be embedded in methods marked not for public use.
SlimerDudeTue 10 Oct 2017
I almost forgot, here's some test code for drawPixel() and colorAt(), tested in Java and JavaScript:
We will probably never add a drawPixel to Graphics because it doesn't make sense in many graphic contexts. Many APIs don't expose that such as the HTML Canvas object. And it makes no sense for vector formats for SVG or PDF. Really these sorts of details are related to pixel based buffers or pixel based image APIs which is a slightly different beast then a graphics context.
Kevin Tue 20 Jun 2017
Are there no direct methods for these?
image.setPixel(x, y, color) color = image.getPixel(x,y)
I need to create an image buffer and draw each pixel individually, as well as read neighboring pixels. That will all get sent out as a PNG in the end.
brian Tue 20 Jun 2017
You can have a new graphics context to draw to a buffered image, and then use drawLine.
SlimerDude Tue 20 Jun 2017
I've wanted these methods many a time too!
From previous investigations, if I remember correctly, there's no reason why
gfx.setPixel(x, y)
can't be implemented (as long as the brush is just a simple aColour
and not a pen stroke or anything).It's on my TODO list to submit a patch for this. For now, as Brian said, I just use
gfx.drawLine()
with the start and end coors set to the same coors.As for
gfx.getPixel()
- that would be great (as would being able to create a transparent image!) but the underlying eclipse SWT doesn't make it easy. If you can find any documentation on it, I'll try to look into it again.Kevin Tue 20 Jun 2017
Is there any way to directly access the image buffer as a 2d array? Without being able to read a pixel, I will be forced to use ... gulp! ... Java. Their image classes are very scary. :(
Also, isn't using a 1-pixel line horribly, horribly inefficient rather than just terribly inefficient? I considered it before discarding the thought. I'm already going to be running some heavy maths.
I suppose I could add these methods, but I'm not sure I currently have skillz mad enough to do it yet. I'm assuming just a wrapper around the Java methods would be needed, although I haven't fully explored them yet. Just getting a mutable image buffer made me cry.
SlimerDude Tue 20 Jun 2017
Hi Kevin,
Sadly, no. For now, if you're creating / drawing your own images, you could try create your own image buf at the same time.
It looks like a
getPixel()
may be possible in Java after all. Here are some links, mainly for myself for when I get round to looking at it:Maybe, or maybe the underlying gfx lib is smart enough to notice the 2 coors are the same? Dunno, I've not profiled it. Anyway, right now, there's no other way!
brian Tue 20 Jun 2017
Maybe some confusion, but the gfx::Image API is not an API to work with low-level image data, color space, etc. It provides access to the underlying rendering framework for drawing to a buffered image to render to the screen or to encode to a file. As such drawLine with one pixel is going to have no noticeable performance difference if there was a drawPixel (which isn't exposed by most rendering APIs anyhow). I think maybe the core problem is you need to use a different sort of image library - accessing individual pixels is different then a rendering pipeline for shapes, fonts, alpha compositing, etc
Kevin Tue 20 Jun 2017
One last question before I give up: Can I create my own 2d array of pixels (i.e. rgba structs) to work on and then ship it off somewhere to be turned into a PNG?
The format is simple enough to write out as a byte stream, but I was hoping to enjoy some baked in goodness.
SlimerDude Tue 20 Jun 2017
Given buffered images are inherently RGB raster images, it is not unreasonable to expect to get and set RGB pixels. No one is asking for a low level colour space API, just a couple of higher level convenience methods - something that Fantom often excels at.
The coordinate system is already working at the pixel level, so I see no harm in having a
Graphics.drawPixel(x, y)
.Tricky. You'd have to create an SWT Image, then set pixels from its
getImageData()
to create your image.Then see the last comment in the forum post: gfx::Image Creation from IntArray for code on converting an SWT image to an FWT image.
Kevin Tue 20 Jun 2017
Haha! Thanks, but that's the sort of Java-esque gymnastics I'm trying to avoid. Maybe I can use JavaScript since it has an ImageData object that is stored in a flat array (image[0] = pixel1.r, image[1] = pixel1.g, etc.)
Anyway, all this groovy stuff is found in org.eclipse.swt.graphics.ImageData, but I can think of no reason why to have a separate class to do the pixel-level operations from the more comprehensive drawing ops. ¯\_(ツ)_/¯
go4 Sat 24 Jun 2017
I have written such api: getPixel and setPixel,
Example code:
Kevin Sun 25 Jun 2017
This is awesome! Thanks!
(However, I did my program in Java already...)
SlimerDude Mon 9 Oct 2017
drawPixel()
Some rendering APIs such as SWT provide an optimised
drawPixel()
method, whilst some (for instance, Javascript) do not.For completeness, and to prevent future ambiguity, I see no harm in implementing a
Graphics.drawPixel()
method. This lets those subsystems that have it, use it - and those subsystems that don't, can default to callingfillRect()
.The patch below defines a
drawPixel()
method, butdrawPoint()
may be more in keeping with the API.colorAt()
Retrieving the colour at a given coordinate of an image can be useful for things like generating alpha masks or determining collision detection.
A patch for SWT and JS implementations is given below:
I'm aware the Javascript implementation has poor performance, but I'm not aware of a better implementation.
I also looked at obtaining a colour value from a
Graphics
class so you can see what you're drawing - but for SWT this info seems to be embedded in methods marked not for public use.SlimerDude Tue 10 Oct 2017
I almost forgot, here's some test code for
drawPixel()
andcolorAt()
, tested in Java and JavaScript:brian Mon 16 Oct 2017
We will probably never add a drawPixel to Graphics because it doesn't make sense in many graphic contexts. Many APIs don't expose that such as the HTML Canvas object. And it makes no sense for vector formats for SVG or PDF. Really these sorts of details are related to pixel based buffers or pixel based image APIs which is a slightly different beast then a graphics context.