Albrecht Schlosser wrote:
> Alvin wrote:
>> Hello all,
>>
>> Anyone know how to draw the widgets contained in a Fl_Scroll into an
>> image file?
>
> ...
>
>> I have pasted my implementation so far at the bottom of this message.
>>
>> My idea/hopes is to have a button that will capture the contents of the
>> Fl_Scroll and save a PNG image. I have the saving part implemented and
>> test independent of this problem.
>>
>> When the button is pushed, the capture() method (see below) is called and
>> returns a Fl_RGB_Image. I then write the image data to a PNG file.
>>
>> I think my biggest problem is that I don't fully understand the
>> Fl_Offscreen and its associated functions. I have searched the HTML
>> doxygen docs, but documentation for Fl_Offscreen, fl_begin_offscreen,
>> etc. are missing.
>
> Yes, I think that the best FLTK 1.x docs can be found here:
>
> http://www.fltk.org/doc-1.1/drawing.html#offscreen
>
> but they are very terse :-(
>
>> The image I am getting in the end is a jumbled mess of the "Hello World"
>> string and random stuff from my desktop.
>>
>> Any tips or direction to some archived threads would be greatly
>> appreciated.
>
> I'll try to comment and extend your code, but I'm not sure if you will
> need everything I write.
>
>> Fl_RGB_Image* Fl_Canvas::capture()
>
> I assume that Fl_Canvas is derived from Fl_Scroll.
>
>> {
>> if(children() <= 2)
>> return NULL;
>>
>> int xdim = child(0)->w();
>> int ydim = child(0)->h();
>
> I don't know, what child(0) is, but guessing from your code,
> this should be some "maximal" box of all widgets ?
>
>> window()->make_current();
>>
>> // setup the offscreen buffer
>> Fl_Offscreen offscr = fl_create_offscreen(xdim, ydim);
>
> This *may* not be enough for drawing, because your widgets
> would be drawn with their relative x/y coordinates added
> to your offscreen, and their coordinates *may* be adjusted
> by Fl_Canvas's scrolling position. One way would be to
> position your Fl_Canvas temporary to (0,0) and set the
> scrollbars to (0,0). Please add some code to save and restore the
> positions yourself.
>
> Add here:
>
> resize(0,0,w(),h());
>
> or (maybe better?)
>
> resize(0,0,xdim,ydim);
>
> If you don't want to resize here, you would probably need:
>
> Fl_Offscreen offscr = fl_create_offscreen(x()+xdim, y()+ydim);
>
>
> Then, reset the srcolling position:
>
> scroll_to(0,0); // FLTK 1.3
> position(0,0); // FLTK 1.1
>
>> // force children to draw into the offscreen buffer
>> fl_begin_offscreen(offscr);
>> Fl_Widget *o = NULL;
>
> To be compatible with FLTK's drawing, all widgets should be
> drawn in "forward" order, if overlapping might be an issue,
> but I leave this up to you.
>
>> for(int i = children()-1; i >= 0; --i)
>> {
>> o = child(i);
>>
>> if(o != &scrollbar && o != &hscrollbar)
>> o->draw();
>
> It may depend on your widgets' draw() methods, but to prevent
> partial drawing, you should call redraw() before draw() or
> maybe damage(FL_DAMAGE_ALL):
>
> if(o != &scrollbar && o != &hscrollbar) {
> o->redraw();
> o->draw();
> }
>
>> }
>>
>> fl_rectf(10, 10, 150, 100, FL_BLACK);
>> fl_color(FL_WHITE);
>> fl_font(FL_TIMES, 15);
>> fl_draw("Hello, World!", 15, 10);
>
> You must call fl_read_image() before fl_end_offscreen().
> Otherwise you will get the pixels of your normal window.
>
> Don't bother with allocating the image buffer, though, you
> can leave this to fl_read_image. And if you repositioned
> the Fl_Canvas to (0,0), you can use
>
> uchar *offscreenImage;
> offscreenImage =
> fl_read_image(offscreenImage, 0, 0, xdim, ydim, 255);
>
> But, as Ian wrote, why would you want to use an alpha image?
> You can use instead:
>
> offscreenImage =
> fl_read_image(offscreenImage, 0, 0, xdim, ydim);
>
> This will give you RGB image data (only 3 bytes/pixel).
>
> (If you didn't call resize(), as proposed, you would have
> to use "x(),y()" instead of "0,0", however).
>
>> fl_end_offscreen();
>>
>> // copy??
>> fl_copy_offscreen(0, 0, xdim, ydim, offscr, 0, 0);
>
> No, don't call fl_copy_offscreen. This would copy your
> offscreen buffer to your current display device.
>
>> // copy the offscreen buffer into a RGBA buffer
>> uchar *offscreenImage = new uchar[xdim * ydim * 4];
>> memset(offscreenImage, 0, xdim * ydim * 4);
>>
>> fl_read_image(offscreenImage, x(), y(), xdim, ydim, 255);
>
> See above.
>
>> // Pack a Fl_RGB_Image and force its dtor to do the cleanup
>> Fl_RGB_Image *rgb_img = new Fl_RGB_Image(offscreenImage, xdim, ydim,
>> 4);
>
> Also, as Ian wrote, you may not need to pack the image data in an
> Fl_RGB_Image, but if you followed my suggestions, you would use only
> 3 instead of 4 bpp:
>
> Fl_RGB_Image *rgb_img = new Fl_RGB_Image(offscreenImage,xdim,ydim,3);
>
>> rgb_img->alloc_array = 1;
>
> (???) I don't want to investigate, what this would do, but I wouldn't
> call it.
>
> Don't forget to restore the original position(s) here.
>
>> return rgb_img;
>> }
>
> HTH,
>
> Albrecht
Hello Albrecht and Ian,
Thanks for the tips. But I am still having some issues. Before I forget, I
am using FLTK 1.3.x r6786.
This is where what I have now:
Fl_RGB_Image* Fl_Canvas::capture()
{
if(children() <= 2)
return NULL;
// Determine the full (unscrolled) size of the canvas area
int xdim = w();
int ydim = h();
if(hscrollbar.visible())
xdim += hscrollbar.maximum();
if(scrollbar.visible())
ydim += scrollbar.maximum();
window()->make_current();
// setup the offscreen buffer
Fl_Offscreen offscr = fl_create_offscreen(xdim, ydim);
// force children to draw into the offscreen buffer
fl_begin_offscreen(offscr);
Fl_Group::draw();
// Annotate the image...write on the image
fl_rectf(10, 10, 150, 100, FL_BLACK);
fl_color(FL_WHITE);
fl_font(FL_TIMES, 15);
fl_draw("Hello, World!", 25, 10);
fl_end_offscreen();
uchar *offscreenImage = new uchar[xdim * ydim * 3];
fl_read_image(offscreenImage, x(), y(), xdim, ydim, 0);
// Pack a Fl_RGB_Image and force its dtor to do the cleanup
Fl_RGB_Image *rgb_img = new Fl_RGB_Image(offscreenImage, xdim, ydim, 3);
rgb_img->alloc_array = 1;
return rgb_img;
}
Fl_Canvas is defined as "Fl_Canvas : public Fl_Scroll". In the app, I have a
Fl_Button who's callback calls "canvas.capture()" where canvas is the
Fl_Canvas object.
Note that xdim and ydim are the dimensions of the image I want to create.
Basically, I would like to create the image the same size of full area
contained within the Fl_Scroll (not just the visible portion - viewport).
If I do not call window()->make_current(), then I get a black image.
Now, if I set xdim and ydim to simply w() and h() respectively (the width
and height of the Fl_Scroll), I get an image of the Fl_Scroll's viewport
with scrollbars. If I do the adjustment (add hscrollbar's and scrollbar's
maximum value, I get a black image).
I replaced by for-loop with the call to Fl_Group::draw(). This will ensure
proper ordering. Ordering is important because the Fl_Scroll contains a
widget that displays an image and another widget that uses FLTK drawing
functions to display text and lines on the image (fl_draw, fl_rectf, etc.).
When I manage to get an image, the "Hello, World" text does not appear.
My reason for using Fl_Offscreen is because of the 2nd, top-most widget. My
goal to capture the FLTK drawing functions to the resulting PNG file.
Concerning fl_read_image, if I put it before the call to fl_end_offscreen, I
get a black image. Also, fl_read_image() doesn't seem to be creating the
buffer automagically for me. Hence, I need to create it manually.
Also, the "rgb_img->alloc_array = 1;" is a dirty hack. I noticed long ago
why reading the Fl_RGB_Image is that if I set this member to 1, then
~Fl_RGB_Image() will deallocate the image data.
So, the only way the above function gives me an image is if I comment the
xdim and ydim adjustment and simply use w() and h(). However, the image is
contains the scrollbars and does not contain the "Hello, World" text.
I'm now going to dig into FLUID's source.
Thank you you two for the help. I think part of my problem is that I am
trying to capture the entire contents within a Fl_Scroll. I was hoping that
by calling Fl_Group::draw(), I could by-pass the viewport functionality of
Fl_Scroll and just force all the children to draw as if they were in a
Fl_Group. I'm not sure if this is what is happening though.
Cheers,
Alvin
_______________________________________________
fltk mailing list
[email protected]
http://lists.easysw.com/mailman/listinfo/fltk