#1156 Image API enhancements

go4 Wed 21 Jul 2010

I found the FWT API is not enough to use.

image:=widget.getImage
g:=widget.getGraphics
graphics:=image.getGraphics
image:=Image.makeMemeryBitMap(size,rgb)
image.write(outStream,jpeg)
image.getPixel(x, y)
image.setPixel(x,y)

I need these for animation\image filter\out screen buffer. Is it on the roadMap?

brian Wed 21 Jul 2010

Just so we are clear, what features exactly are you asking for?

  1. buffered images with ability to get Graphics context
  2. serialization to png, jpeg, gif

go4 Fri 23 Jul 2010

That would be nice. I just want to do some cool effects, like the Flash done.

brian Mon 26 Jul 2010

Renamed from Image API ? to Image API enhancements

brian Mon 26 Jul 2010

Promoted to ticket #1156 and assigned to brian

brian Mon 3 Jan 2011

Ticket reassigned from brian to andy

Yuri Strot Wed 16 Feb 2011

Hi,

@ikornienko and I working on the buffered image feature. We've already implemented ability to use Graphics over Image for SWT and JS and before sharing our work I'd like to discuss API.

In SWT (as well as in AWT) any loaded image can be changed using Graphics:

// SWT
GC gc = new GC(image);
// use gc to change image 
...
gc.dispose();

// AWT
Graphics g = image.getGraphics();
// use g to change image
...

In JS you can't change existing image (HTML <img>), but you can create canvas, paint your image on this canvas, paint something else and then create new image. This approach looks better for Fantom where Image is a const object.

During discussion with Brian we found useful to have two classes: const Image and mutable ImageBuf (similar to sys::Str and sys::StrBuf). The workflow is following:

buf := ImageBuf(w, h) // or ImageBuf.makeImage(image)
g := buf.getGraphics
g.drawImage(image, 0, 0)
//use g to change image
...
newImage := buf.toImage

Does this API work for everyone?

brian Thu 17 Feb 2011

Thanks for posting @ystrot.

I still haven't figured out what best design should be, but here are some random thoughts:

  • in general I've always found "buffered images" that you can paint versus images loaded from static files like PNG are often sort of confusing
  • having Image be const is a good thing because most images are loaded from a file and its nice to immutability pass them around to stick in labels, etc
  • because Image is const, we can't create a BufferedImage subclass which isn't const

Here are some (not fully thought out) proposals:

a) use Str / StrBuf model as proposed by ystrot:

class ImageBuf  // does *not* subclass Image
{
  Graphics graphics
  Image toImage()
}

Although I should not there are still some tricky things about toImage since you don't want it backed by the mutable buffered image (or at least you need to do a bitblt copy if someone tries to mutate the ImageBuf).

b) Maybe some sort of closure based factory on Image?

class Image
{
  static Image makeBuf(Int w, Int h, |Graphics| it)
}

img := Image.makeBuf(100, 100) |g| { /* paint image */ }

That seems pretty elegant and simple, however it has the huge downside that we have to reallocate the image memory and repaint it even if you just want to make a minor change to a few pixels (so by itself that doesn't seem workable)

c) Maybe create some new term which has absolutely no relationship to Image:

class PixelGrid { Graphics graphics() }

class Graphics
{
  Void drawImage(...)
  Void drawPixelGrid(...)
}

Although to tell you the truth I don't really love either one of those.

go4 Thu 17 Feb 2011

Some ideas:

  1. List model (List.toImmutable):
    class Image
    {
      override This toImmutable(){...}
      Graphics graphics(){ if(!immutable) throw Err() }
    }
  2. Image interface:
    mixin Image{}
    
    const class ConstImage : Image {}
    
    class ImageBuf : Image
    {
      Graphics graphics()
    }

Yuri Strot Thu 17 Feb 2011

I like idea of closure based factory on Image. It looks useful for major tasks with buffered images (double-buffering and custom image rendering). In the future we can extend API and replace native implementation:

static native Image makeBuf(Int w, Int h, |Graphics| it)

with something more appropriate without breaking changes. For example:

static Image makeBuf(Int w, Int h, |Graphics| it)
{
  buf := ImageBuf(w, h)
  it(buf.getGraphics())
  return buf.toImage()
}

So I think b) is something we can stay with for now.

andy Mon 21 Feb 2011

If you are planning on rendering to the Image more than a single time, I believe the proper solution there should be Canvas. So to me, I think this only a hook for dynamically generating an Image - but from there on its const - in order to use it places that accept Image (like Label for example). In which case the simplest solution would be a static factory makeBuf method. And we also avoid to complexity of dual types.

brian Tue 22 Feb 2011

Been thinking about for a while too, and agree with Andy. While there are certainly use cases for keeping a mutable Image buffer around for constant redraw, the vast majority of those can be handled by a double buffered Canvas (where you can control selective repaints of dirty regions).

So what I would suggest as action items:

  1. Add new factory to Image
  2. Add double buffering support to Canvas

For the Image factory, only trick is what to call it. I don't really like makeBuf since it implies all sorts of details related to SWT and AWT. How about:

static Image makePainted(Int w, Int h, |Graphics| f)

If everyone is in agreement with that initial direction, I can work with @ystrot on getting those into next build.

Yuri Strot Tue 22 Feb 2011

1) Works well for me. However makePainted looks too verbose... How about static draw method?

static native Image draw(Int w, Int h, |Graphics| f)

Is there any reason to make this method ctor and start with make?

2) Do you want to provide double buffering by default?

brian Tue 22 Feb 2011

I debated about the makePainted versus simple name like just draw or paint. I could go with a simple verb, sort of breaks conventions to scan the slot list looking for the "make" prefix, but still probably easily discoverable in a small class like Image. Although I think paint would probably be better than draw since we use paint as the top-level for classes in Widget, etc.

I would say double buffering is not the default for Canvas, but I don't have a strong opinion (haven't thought about it much)

Yuri Strot Tue 22 Feb 2011

OK, I'll create a patch tomorrow.

I'm going to look at double buffering deeply. With current implementation I've found that HTML5 canvas not require double buffering (no flickering). Therefore I used a trick to provide buffering in Java and avoid it in JS.

rfeldman Tue 22 Feb 2011

Is the lack of flickering true in all browsers? I remember trying out Canvas in Firefox months ago and seeing flickering, but maybe they switched to double buffering since then...

Yuri Strot Tue 22 Feb 2011

For me it works well in Chrome, Firefox and Opera. But I saw opened issues in stackoverflow which say there is no such support. For example: http://stackoverflow.com/questions/2795269/does-html5-canvas-support-double-buffering

So need a better look...

brian Mon 28 Feb 2011

Ticket resolved in 1.0.58

Many thanks to @ystrot who got the Image.makePainted factory implemented for both SWT and JS.

This original ticket was for 2 issues:

  • buffered images
  • support for image I/O to read/write png, jpeg, etc

Buffered images is done now with the new makePainted method, so I am going to close out this ticket.

We should probably create a new ticket for png/jpeg I/O. Is there anyone interested in helping with those APIs? I haven't done any investigation to see what support SWT provides for that.

Login or Signup to reply.