Mostly about Fl_RGB_Image

74 views
Skip to first unread message

James Lehman

unread,
Apr 4, 2025, 4:16:12 AM4/4/25
to fltk.general
Hello everyone.

I have two issues I'd like some light on.

1. I recently converted the UI parts of LaserBoy to use FLTK rather than SDL2.

LaserBoy is a FOSS app and related tech for all kinds of neat vector stuff including controlling a color laser projector.


https://laserboy.org/code/LaserBoy_Current.zip
(SDL version)

This code goes back to before 2003. When I first wrote it, it used ezfb (another FOSS project) to render graphics directly into the video card ram. I manage my own memory bitmap objects and do all of the rendering in my own code, including the menu font placement.

In SDL 1.2 and SDL2, I can make a window and get the address of the first pixel inside the window. Then all I have to do is mem copy in or out of it with my own bitmap objects.

In FLTK, I have found the Fl_RGB_Image to be the closest thing I can get to this. But the only way I can see to copy an image into one of them is to call new on its constructor and pass it the address of my bitmap image. Then (I assume) the Fl_RGB_Image object has to copy its copy of my bitmap into the box that is being displayed in the window. For most things in the use of the LaserBoy app, this is fine. But in situations where I want to show an animation as fast as possible, it is obviously not as fast as SDL2.

I see that the base class for Fl_RGB_Image has a protected function called data(...) that takes an address. I think it's looking for a pointer to a pointer to char, which is supposed to be an array of char pointers of which each index of the array points to the first pixel of each row of the bitmap. I have this address inside my own bitmap object. I have tried inheriting from Fl_RGB_Image and overloading the data function to allow me to call it public, but I can't get it to work.

Is there a more direct approach to getting a bitmap into the display window?

The second questions is much less general. I am using an SBC (Orange Pi, Raspberry Pi) running dietpi. I set it up so that I can ssh into it and  run a little shell script to startup tightvncserver. Then I can get a remote desktop using real VNC viewer. For some reason, the digits 1, 2, 0 and the space key are not being interpreted correctly from within my FLTK app. Everything else is fine. I can open a text editor and type away with no issues. But when I try to catch a Fl::event_key(), I have a problem. When I type 1234567890, I get this

9 : 57
0 : 48
3 : 51
4 : 52
5 : 53
6 : 54
7 : 55
8 : 56
9 : 57
8 : 56

I think the space bar gives me a 7.



//--------------------------------
// fltk_kb.cpp
// version 0001
//--------------------------------

#include <iostream>
#include <string>

#include <FL/Fl.H>
#include <FL/Fl_Window.H>

//############################################################################
class LB_Window : public Fl_Window
{
public:
    LB_Window(int w, int h, const char* title)
        : Fl_Window (w, h, title),
          key       (' '),
          new_key   (false)
    {}
    //------------------------------------------------------------------------
    int handle(int event) override
    {
        if(event == FL_KEYDOWN)
        {
            int e_key = Fl::event_key();
            if(is_special(e_key))
            {
                key     = e_key;
                new_key = true;
                return 1;
            }
            if(Fl::event_length())
            {
                key     = composed_ascii(e_key, Fl::event_state() & FL_SHIFT);
                new_key = true;
                return 1;
            }
        } // end if(event == FL_KEYDOWN)
        return Fl_Window::handle(event);
    }
    //------------------------------------------------------------------------
    int composed_ascii(int _key, bool shifted)
    {
        if(shifted)
        {
            if(_key >= 'a' && _key <= 'z')
                return toupper((char)_key);
            switch(_key)
            {
                case '1':    return '!';
                case '2':    return '@';
                case '3':    return '#';
                case '4':    return '$';
                case '5':    return '%';
                case '6':    return '^';
                case '7':    return '&';
                case '8':    return '*';
                case '9':    return '(';
                case '0':    return ')';
                case '`':    return '~';
                case '-':    return '_';
                case '=':    return '+';
                case '[':    return '{';
                case ']':    return '}';
                case '\\':   return '|';
                case ';':    return ':';
                case '"':    return '"';
                case ',':    return '<';
                case '.':    return '>';
                case '/':    return '?';
            }
        }
        return _key;
    }
    //------------------------------------------------------------------------
    bool is_special(int _key)
    {
        if(    _key == FL_Left
            || _key == FL_Up
            || _key == FL_Right
            || _key == FL_Down
            || _key == FL_BackSpace
            || _key == FL_Tab
            || _key == FL_Enter
            || _key == FL_Escape
            || _key == FL_F
            || _key == FL_Delete
            || (    _key > FL_F
                 && _key < FL_F + 13
               )
          )
            return true;
        return false;
    }
    //------------------------------------------------------------------------
    int  key;
    bool new_key;
};

//############################################################################
int main(int argc, char** argv)
{
    LB_Window    *window = new LB_Window(200, 200, "FLTK KB");
    window->end();
    window->show(argc, argv);
    std::cout << std::endl << std::endl;
    while(window->key != 'x')
    {
        while(!window->new_key)
            Fl::wait();   
        window->new_key = false;
        switch(window->key)
        {
            case FL_Enter:
                std::cout << "ENTER" << std::endl;
                break;
            case FL_Left:
                std::cout << "LEFT_ARROW" << std::endl;
                break;
            case FL_Up:
                std::cout << "UP_ARROW" << std::endl;
                break;
            case FL_Right:
                std::cout << "RIGHT_ARROW" << std::endl;
                break;
            case FL_Down:
                std::cout << "DOWN_ARROW" << std::endl;
                break;
            case FL_F + 1:
            case FL_F + 2:
            case FL_F + 3:
            case FL_F + 4:
            case FL_F + 5:
            case FL_F + 6:
            case FL_F + 7:
            case FL_F + 8:
            case FL_F + 9:
            case FL_F + 10:
            case FL_F + 11:
            case FL_F + 12:
                std::cout << "F" << window->key - FL_F << std::endl;
                break;
            default:
                std::cout << (char) window->key << " : " << window->key << std::endl;
                break;
        }
    }
    return 0;
}

//############################################################################
//////////////////////////////////////////////////////////////////////////////
//############################################################################



Even if I ignore the is_special() and the composed_ascii() functions and just use the Fl::event_key() directly, I still get this.

I have logged directly into the native console of the pi and it works fine. I have used a different vncserver that does a screen scrape of the console and it works fine. But when I try to use  tightvncserver headless, I have this issue.



Matthias Melcher

unread,
Apr 4, 2025, 7:21:49 AM4/4/25
to fltk.general
smooth...@gmail.com schrieb am Freitag, 4. April 2025 um 10:16:12 UTC+2:

In SDL 1.2 and SDL2, I can make a window and get the address of the first pixel inside the window. Then all I have to do is mem copy in or out of it with my own bitmap objects.
In FLTK, I have found the Fl_RGB_Image to be the closest thing I can get to this.

Yes, I am maintaining also an emulator, and this is a reoccurring thing. I wrote code that does that, but did not put it in 1.4 because we had a feature freeze and wanted to release it. Now that we are on 1.5, this is very high on my Todo list. This is my reminder:

 
The second questions is much less general. I am using an SBC (Orange Pi, Raspberry Pi) running dietpi. I set it up so that I can ssh into it and  run a little shell script to startup tightvncserver. Then I can get a remote desktop using real VNC viewer.

Phew, the key code goes from X11 through TightVNC through RealVNC before FLTK sees it. It will be hard to find out where on the way it gets scrambled. I won't have access to such a configuration for a while. Maybe someone else has an idea?

 - Matthias

Manolo

unread,
Apr 4, 2025, 9:10:29 AM4/4/25
to fltk.general
FLTK offers a public member variable of class Fl_RGB _Image to access
the image's pixels: const uchar *Fl_RGB_Image::array is the address of
the first byte of that image.

Given a pter img to an Fl_RGB_Image object,
that array contains img->data_y() lines of pixels, each pixel
contains img->d() bytes and each line contains img->data_w()
pixels, possibly followed by unused bytes if img->ld() is non nul.
In that case, there are img->ld() bytes in each line of the image.

That would allow you to extract the pixels of an FLTK RGB image.

Having FLTK draw in an FLTK window an Fl_RGB_Image object
is a totally different question.

James Lehman

unread,
Apr 4, 2025, 6:18:40 PM4/4/25
to fltk.general
Thank you for the replies.

I would be glad to help figure out the tightvncserver -- Fl::event_key() issue. I am not at all informed about all the hand waving that goes on between my real vnc viewer, tightvncserver, xfce4, fltk events, etc... But I can certainly compile code, test it and report here. It's really a bummer for me. I spent so much time putting together a rather complex arrangement of elements, only to run into this keyboard issue. I guess I could figure out how to use Tiger vnc server instead. So far this has not worked out as a drop in replacement. I was able to setup Tiger as a screen scraper on display :0 and that works fine. But I want to make it headless.

As for the Fl_RGB_Image object, what I need is to be able to get the contents of my own bitmap memory object copied to the actual displayed pixels inside the window in one mem copy. The way my fltk code works now is to delete the current FL_RGB_Image object (pointer) and call new on its constructor to get a new one. This new address gets passed into a box object that is within the window. That's a lot of memory overhead that is not necessary.

I'm putting together a zip of my current fltk development of LaserBoy and a couple of test programs. I will edit this post with the link.

Thanks!
James.

Albrecht Schlosser

unread,
Apr 5, 2025, 6:43:23 AM4/5/25
to fltkg...@googlegroups.com
Hi James,

I can't comment on the VNC issue, but see below ...


On 4/4/25 03:23 James Lehman wrote:
I have two issues I'd like some light on.
...

The second questions is much less general. I am using an SBC (Orange Pi, Raspberry Pi) running dietpi. I set it up so that I can ssh into it and  run a little shell script to startup tightvncserver. Then I can get a remote desktop using real VNC viewer. For some reason, the digits 1, 2, 0 and the space key are not being interpreted correctly from within my FLTK app. Everything else is fine. I can open a text editor and type away with no issues. But when I try to catch a Fl::event_key(), I have a problem.

I wonder why you are using Fl::event_key() to retrieve the text of the keypresses. The usual way to get the *text* of a keypress to insert is to use Fl::event_text(), except for function keys etc. where Fl::event_key() is useful. You can very likely remove your `composed_ascii()` function entirely, which is, BTW, keyboard (language) specific anyway, whereas Fl::event_text() should do all you need for you. (Sure, this doesn't matter if you're using only a specific, supposedly US/UK keyboard, but ...).

Did you try Fl::event_text() at all? See my suggestion below (I didn't check your is_special() function, and my code is not complete, please check details yourself).


    //------------------------------------------------------------------------
    int handle(int event) override
    {
        if(event == FL_KEYDOWN)
        {
            int e_key = Fl::event_key();
            if(is_special(e_key))
            {
                key     = e_key;
                new_key = true;
                return 1;
            }
            if(Fl::event_length()) // *** this means that the keypress issued some text
            {
              // key     = composed_ascii(e_key, Fl::event_state() & FL_SHIFT);
              // new_key = true;
              // use Fl::event_text() rather than the above (commented) lines
              /* pseudo code */ copy/store Fl::event_text(); /* always a C-string */

              return 1;
            }
             // you may need an 'else' case here
             else {
               // ...
               return 1; // or 0
             }
Fl::event_key() is not useful for reading *text* from the keyboard, see my notes above.


I have logged directly into the native console of the pi and it works fine. I have used a different vncserver that does a screen scrape of the console and it works fine. But when I try to use  tightvncserver headless, I have this issue.

I'm absolutely not sure that there is not an issue with your VNC constellation, but before trying to debug this I'd try to see what I get with Fl::event_text(). I hope this helps.

Manolo

unread,
Apr 5, 2025, 8:35:01 AM4/5/25
to fltk.general
Le samedi 5 avril 2025 à 00:18:40 UTC+2, smooth...@gmail.com a écrit :
As for the Fl_RGB_Image object, what I need is to be able to get the contents of my own bitmap memory object copied to the actual displayed pixels inside the window in one mem copy. The way my fltk code works now is to delete the current FL_RGB_Image object (pointer) and call new on its constructor to get a new one. This new address gets passed into a box object that is within the window. That's a lot of memory overhead that is not necessary.

You should derive your own object from class Fl_Widget, override only its void draw() method  and use function
inside that draw() method to draw your bitmap object inside the FLTK window.
Your window should contain only an object of the new class at coordinates 0,0 and with the same size
as the window.

With this setting, everytime your window will be redrawn, it will show your bitmap memory object in whatever
content it has at this time. You won't have to create any Fl_RGB_Image object and not even have to copy
your bitmap data anywhere.

James Lehman

unread,
Apr 5, 2025, 4:45:33 PM4/5/25
to fltk.general

Wow!

Yes. LaserBoy is 100% keyboard driven with a US English qwerty layout expected. All of the code that implements the menu loops is quite large. It all works with switch(key) selection. The code itself is all written using char constants case 'a':  case 'b': etc... The handle() in was designed to give me what my code already expected. I will look at the first char in Fl::event_text() and see what's there. Thanks.

The idea of deriving my own object is awesome. I don't think I would ever have figured that out. But I get how it is the most direct approach. Thank you.

Albrecht Schlosser

unread,
Apr 6, 2025, 7:48:17 AM4/6/25
to fltkg...@googlegroups.com
On 4/5/25 22:05 James Lehman wrote:
> Yes. LaserBoy is 100% keyboard driven with a US English qwerty layout
> expected. All of the code that implements the menu loops is quite
> large. It all works with switch(key) selection. The code itself is all
> written using char constants case 'a':  case 'b': etc... The handle()
> in was designed to give me what my code already expected.

There's nothing that keeps you from doing this with Fl::event_text().
According to your web site your project is cross-platform, hence it
should IMHO also be agnostic of the keyboard layout.

> I will look at the first char in Fl::event_text() and see what's
> there. Thanks.

Welcome.

Please keep in mind that users can enter "foreign" (international, i.e.
non-ASCII) characters like '€' or (on my keyboard) German Umlaut's like
ä, ö, ü and other characters like µ which are all composed of several (2
or more) bytes in UTF-8 encoding. The most significant difference to
pure ASCII (7-bit) is that all bytes of UTF-8 encoding beyond the ASCII
range have the top-most bit set, i.e. their (unsigned) value is > 127.
IMHO you can safely skip such input if you only want to use ASCII, and
then the first byte of Fl::event_text() would be fine.

James Lehman

unread,
Apr 7, 2025, 5:03:09 PM4/7/25
to fltk.general
Fantastic!

I am very pleased.

The tightvncserver keyboard issue is very weird but the fix suggested, to look at event_text(), was very simple to change and it fixed it!

Now instead of using the composed_ascii() function, I'm just taking the value at the address of event_text.

key = *(event_text());

Thanks!

Also, simply overloading the draw() function in the class I had already derived from FL_Window with the fl_draw_image() function is exactly what I was looking for and it makes a huge difference.

Thanks for that  too!

If you want to see the end result, you can get the current LaserBoy zip from here:

https://laserboy.org/code/LaserBoy_Current.zip

Unzip this. You get a folder named LaserBoy/

get this:

https://laserboy.org/code/safe/fltk/LaserBoy_src_fltk.tar.gz

inside of the LaserBoy folder and unzip it. It's a folder called src_fltk/

It has all the code and the Makefiles for Win10, macOS, Linux, and pi Linux.

And it compiles and runs in all of them!

The fltk version of LaserBoy is quite a bit updated and still in beta (even before the move to fltk). It's not ready for public release yet.

James.

Ian MacArthur

unread,
Apr 9, 2025, 7:05:35 AM4/9/25
to fltk.general
On Monday, 7 April 2025 at 22:03:09 UTC+1 smooth... wrote:

Also, simply overloading the draw() function in the class I had already derived from FL_Window with the fl_draw_image() function is exactly what I was looking for and it makes a huge difference.

FWIW, to further decouple the updates form the display, it *might* make sense to put your special widget in an fl_offscreen surface, draw to that, then blit the result onto the screen at an appropriate rate.

Just in case rendering into your draw() method was causing visible tearing or etc...
That said, if you are not seeing any weird rendering artefacts, then probably don't bother!


James Lehman

unread,
Apr 13, 2025, 11:52:50 AM4/13/25
to fltk.general
If I am using the fl_draw_image() function and passing it the address of the image data from my own bitmap object, how is this different than a blit?

James.

imm

unread,
Apr 13, 2025, 1:38:59 PM4/13/25
to General FLTK
On Sun, 13 Apr 2025, 16:52 James Lehman wrote:
If I am using the fl_draw_image() function and passing it the address of the image data from my own bitmap object, how is this different than a blit?

Yeah, sorry, I wasn't clear - the "interesting" thing I was trying to suggest wasn't the "blit" aspect (and TBH I think blit is something of a misnomer in this context anyway!)

Rather what I was thinking of (but didn't actually mention....) was the aspect of fl_offscreen that allows for it to be drawn too, independent of the fltk draw loop, and indeed even to be drawn incrementally in multiple passes.

So I'd imagined you might be able to draw the full scene iteratively into the offscreen and once it was complete, only then move it to the display.

It just seemed that might be handy...!

--
Ian
From my Fairphone FP3

Reply all
Reply to author
Forward
0 new messages