FLTK logo

Article #468: Mini-howto: Display Arbitrary Data as an Image

FLTK matrix user chat room
(using Element browser app)   FLTK gitter user chat room   GitHub FLTK Project   FLTK News RSS Feed  
  FLTK Apps      FLTK Library      Forums      Links     Login 
 Home  |  Articles & FAQs  |  Bugs & Features  |  Documentation  |  Download  |  Screenshots  ]
 

Return to Articles | Show Comments | Submit Comment ]

Article #468: Mini-howto: Display Arbitrary Data as an Image

Created at 08:24 Jun 30, 2005 by ianmacarthur

Last modified at 05:35 May 14, 2011

Following on from Matthias' article "Article #466: How to get pixel color out of Fl_Image" available here:

http://www.fltk.org/articles.php?L466+I0+T+P1+Q

I thought it would be handy to have a quick note on some simple ways to display the data you've read.

For example, If you have read in an image and processed the data in some way, or if you have generated a purely synthetic set of data you'd like to view as an image, how do you go about getting it on the display?

Firstly, you need to get your data into the right format: For FLTK, that consists generally of making it into an array of unsigned chars, of the appropriate "depth". Values of depth that are likely to be interesting are;

depth 1 - monochrome images

depth 3 - (colour) RGB images

You might also want;

depth 4 - RGBA images, i.e. RGB images with an alpha channel.

So, let us suppose we are going to make a RGB image (depth 3) which is 400 pixels wide and 250 pixels high. For this, we would allocate an array;

        int size = width * height * depth;
        unsigned char image_array = new unsigned char [size];

We than fill that array as follows;

  • Pixel (0, 0) of the image is the top left corner.
  • In this example, Pixel (399, 249) is the bottom right corner.
  • Each Pixel is "depth" bytes long. So a mono image has 1 byte per pixel, a colour image has 3 (or 4!)
  • Colour images are filled in the order R, then G, then B. (Then A for depth 4 images)

Example: Pixel (0, 0) is RED, then image_array[0] = 255; image_array[1] = 0; image_array[2] = 0;

For any pixel (x, y), the first (i.e. R) byte will be at;

        int cell = (y * width * depth) + (x * depth);
        image_array[cell] = (red value)
        image_array[cell + 1] = (green value)
        image_array[cell + 2] = (blue value)

OK. Now we have filled our image array, how do we display it? I use two different ways, both are very simple. Here's how.

Method 1 - Use an Fl_RGB_Image

The idea here is to define an Fl_Box that will "hold" the picture on the display, then create an Fl_RGB_Image and assign your array to it. Then you set the Fl_RGB_Image as the image of the box and bingo! Picture.

So:
        :
        pic_box = new Fl_Box(...);
        :
        rgb_img = new Fl_RGB_Image((const uchar *
) image_array, width, height, depth);
        pic_box->image(rgb_img);
        pic_box->redraw();

And of course, since this derives from Fl_Image, you can use all the methods that provides to mess about with the image, e.g. color_average() or desaturate() etc.

Method 2 - make your own image box

The idea with this one is to subclass Fl_Box, and override the draw() method to render the picture using  fl_draw_image(); Obviously, this is more effort than just using Fl_RGB_Image and it doesn't provide for color_average() etc., so why bother? Well, since you are doing the drawing yourself, it does allow that you can do things to the data as it is rendered, for example.

In general, Method 1 is probably easiest, unless you need specific control of how it is drawn, or you want to do other things to the data that might be easier via your own class...

Anyway. Enough of that. Here's a worked example - invoke it from the command line, passing it the name of a jpg or png image.

It loads the image and scales it down to fit in a box on the left of the screen. When you press the "Process" button, two new version of the picture are displayed, each "processed" in a different way. The middle image has been rendered using a subclass of Fl_Box and fl_draw_image(); The right hand image is formed using Fl_RGB_Image. And that's about all, really.

Hope that makes some sort of sense...

/************************************************************************/
// compile as:
// fltk-config --use-images --compile twin_image.cxx
//
// invoke as:
// twin_image somefile.jpg (should work with many image types, jpg, png, etc...)

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <FL/Fl.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Image.H>
#include <FL/Fl_Shared_Image.H>
#include <FL/Fl_Button.H>
#include <FL/fl_draw.H>

Fl_Double_Window *main_win;
Fl_Shared_Image *img;
Fl_RGB_Image *img_3 = NULL;

int depth = 0;
int count = 0;
int width = 0;
int height = 0;
const char *datap;
int xoff = 0;
int yoff = 0;

/************************************************************************/
/************************************************************************/
class vw_box : public Fl_Box
{
protected:
        void draw(void);
                int iw;
                int ih;
                int id;
                char * data;
public:
     vw_box(int X, int Y, int W, int H, const char *L=0) : Fl_Box(X,Y,W,H,L) { data = NULL;}
         
         void set_data(char *newdat, int W, int H, int D);
};
/************************************************************************/
void vw_box::set_data(char *newdat, int W, int H, int D)
{
        iw = W;
        ih = H;
        id = D;
        
        if (data) delete [] data;
        data = newdat;
}

/************************************************************************/
void vw_box::draw(void)
{
        if (!data) return;
        
        short wd = w();
        short ht = h();
        short xoff = 0;
        short yoff = 0;

        if (ih < ht) yoff = (ht - ih) / 2;

        if (iw < wd) xoff = (wd - iw) / 2;

    short xpos = x() + xoff;
    short ypos = y() + yoff;

    /* Ensure that the full window is redrawn */
    fl_push_no_clip();

    /* Redraw the whole image */
    fl_draw_image((const uchar *)data, xpos, ypos, iw, ih, id);

    fl_pop_clip();
}

/************************************************************************/
/************************************************************************/
class img_box : public Fl_Box
{
protected:
        int handle(int ev);
public:
     img_box(int X, int Y, int W, int H, const char *L=0) : Fl_Box(X,Y,W,H,L) {}
};
/************************************************************************/
int img_box::handle(int ev)
{
        int ret = Fl_Box::handle(ev);
        if (ev == FL_PUSH)
        {
                // NOTE: This only handles the simple 3 colour case...
                if ((count == 1) && (depth == 3))
                {
                        int xc = (int)Fl::event_x();
                        int yc = (int)Fl::event_y();
                        int idx;
                        unsigned char r, g, b;
                
                        if ((xc < xoff) || (xc > (200 - xoff)))
                        {
                                puts("X click not in image area");
                                fflush(stdout);
                                return ret;
                        }
                        if ((yc < yoff) || (yc > (200 - yoff)))
                        {
                                puts("Y click not in image area");
                                fflush(stdout);
                                return ret;
                        }
                
                        xc = xc - xoff;
                        yc = yc - yoff;
                        idx = (width * yc * depth) + (xc * depth);
                        r = (unsigned char)datap[idx];
                        g = (unsigned char)datap[idx + 1];
                        b = (unsigned char)datap[idx + 2];
                
                        printf("Got a click at (%d, %d) R: %d G: %d B: %d\n", xc, yc, r, g, b);
                        fflush(stdout);
                        return(1);
                }
        }
        return(ret);
}

/************************************************************************/
/************************************************************************/
img_box *box_1;
vw_box *box_2;
Fl_Box *box_3;

/************************************************************************/
void load_file(const char *n)
{
        if (img) img->release();

        img = Fl_Shared_Image::get(n);
        if (!img) {
                puts("Image file format not recognized!");
                fflush(stdout);
                return;
        }
        if (img->w() > box_1->w() || img->h() > box_1->h()) {
                Fl_Image *temp;
                if (img->w() > img->h()) temp = img->copy(box_1->w(), box_1->h() * img->h() / img->w());
                else temp = img->copy(box_1->w() * img->w() / img->h(), box_1->h());

                img->release();
                img = (Fl_Shared_Image *)temp;
        }

        box_1->image(img);
        box_1->redraw();
}

/************************************************************************/
void quit_cb(Fl_Button *, void *)
{
        exit (0);
}

/************************************************************************/
void process_cb(Fl_Button *, void *)
{
        unsigned long total = width * height * depth;
        char * data = new char [total];
        char * flip = new char [total];
        
        int idx;
        for (idx = 0; idx < total; idx ++)
        {
                data[idx] = (char)(255 - (unsigned char)datap[idx]);
                flip[total - 1 - idx] = (unsigned char)datap[idx];
        }
        
        box_2->set_data(data, width, height, depth);
        box_2->redraw();
        
        if (!img_3)
        {
                img_3 = new Fl_RGB_Image((const uchar *)flip, width, height, depth);
                box_3->image(img_3);
                box_3->redraw();
        }
}

/************************************************************************/
int main(int argc, char **argv)
{
        fl_register_images();
        Fl::get_system_colors();

        main_win = new Fl_Double_Window(615, 300);

        img_box b(0, 0, 200, 200);
        box_1 = &b;

                box_2 = new vw_box(205, 0, 200, 200);

                box_3 = new Fl_Box(410, 0, 200, 200);

                Fl_Button *quit = new Fl_Button(540, 260, 60, 30);
                quit->label("Quit");
                quit->callback((Fl_Callback *)quit_cb);

                Fl_Button *process = new Fl_Button(470, 260, 60, 30);
                process->label("Process");
                process->callback((Fl_Callback *)process_cb);

                main_win->end();
                
        if (argv[1]) load_file(argv[1]);

        depth = img->d();
        count = img->count();
        datap = img->data()[0];
        width = img->w();
        height = img->h();

        if (height < b.h())
        {
                yoff = (b.h() - height) / 2;
        }
        if (width < b.w())
        {
                xoff == (b.w() - width) / 2;
        }

        printf("Depth: %d   Count: %d  W: %d H: %d\n", depth, count, width, height);
        printf("X offs %d Y offs %d\n", xoff, yoff);
        fflush(stdout);

        main_win->show();
        return Fl::run();
}

/* End of File */

Listing ]


Comments

Submit Comment ]

From dejan, 06:25 Jul 01, 2005 (score=3)

An excellent article Ian, thanks a lot!
Reply ]

From cable_guy_67, 20:35 Jul 11, 2005 (score=2)

Thanks for Ian.  Nifty example for everyone.

I got a couple warnings with CygWin and 1.1.svn.

g++ -Wall image_box.cpp `fltk-config --ldflags --cxxflags --use-images` -s -o ImageBox

image_box.cpp: In member function `virtual void vw_box::draw()' image_box.cpp:79: warning: statement has no effect

//        if (iw < wd) xoff == (wd - iw) / 2;
        if (iw < wd) xoff = (wd - iw) / 2;

image_box.cpp: In function `void process_cb(Fl_Button*, void*)': image_box.cpp:185: warning: comparison between signed and unsigned integer expressions

//        int idx; //        unsigned long idx;
        for (unsigned long idx = 0; idx < total; idx ++)

It's nice to have these samples from the threads wrapped up like this.

Mark
Reply ]

From Nany, 00:46 Apr 14, 2008 (score=3)

Nice example with excellent describtion but when i compile it gave me this errors fltkimagesd.lib(Fl_JPEG_Image.obj) : error LNK2001: unresolved external symbol _jpeg_read_scanlines fltkimagesd.lib(Fl_JPEG_Image.obj) : error LNK2001: unresolved external symbol _jpeg_start_decompress fltkimagesd.lib(Fl_JPEG_Image.obj) : error LNK2001: unresolved external symbol _jpeg_calc_output_dimensions fltkimagesd.lib(Fl_JPEG_Image.obj) : error LNK2001: unresolved external symbol _jpeg_read_header fltkimagesd.lib(Fl_JPEG_Image.obj) : error LNK2001: unresolved external symbol _jpeg_stdio_src fltkimagesd.lib(Fl_JPEG_Image.obj) : error LNK2001: unresolved external symbol _jpeg_CreateDecompress fltkimagesd.lib(Fl_JPEG_Image.obj) : error LNK2001: unresolved external symbol _jpeg_destroy_decompress fltkimagesd.lib(Fl_JPEG_Image.obj) : error LNK2001: unresolved external symbol _jpeg_finish_decompress fltkimagesd.lib(Fl_JPEG_Image.obj) : error LNK2001: unresolved external symbol _jpeg_std_error fltkimagesd.lib(Fl_PNG_Image.obj) : error LNK2001: unresolved external symbol _png_destroy_read_struct fltkimagesd.lib(Fl_PNG_Image.obj) : error LNK2001: unresolved external symbol _png_read_end fltkimagesd.lib(Fl_PNG_Image.obj) : error LNK2001: unresolved external symbol _png_read_rows fltkimagesd.lib(Fl_PNG_Image.obj) : error LNK2001: unresolved external symbol _png_set_interlace_handling fltkimagesd.lib(Fl_PNG_Image.obj) : error LNK2001: unresolved external symbol _png_set_tRNS_to_alpha fltkimagesd.lib(Fl_PNG_Image.obj) : error LNK2001: unresolved external symbol _png_get_valid fltkimagesd.lib(Fl_PNG_Image.obj) : error LNK2001: unresolved external symbol _png_set_strip_16 fltkimagesd.lib(Fl_PNG_Image.obj) : error LNK2001: unresolved external symbol _png_set_packing fltkimagesd.lib(Fl_PNG_Image.obj) : error LNK2001: unresolved external symbol _png_set_expand fltkimagesd.lib(Fl_PNG_Image.obj) : error LNK2001: unresolved external symbol _png_read_info fltkimagesd.lib(Fl_PNG_Image.obj) : error LNK2001: unresolved external symbol _png_init_io fltkimagesd.lib(Fl_PNG_Image.obj) : error LNK2001: unresolved external symbol _png_create_info_struct fltkimagesd.lib(Fl_PNG_Image.obj) : error LNK2001: unresolved external symbol _png_create_read_struct Debug/sample.exe : fatal error LNK1120: 22 unresolved externals Error executing link.exe.

sample.exe - 23 error(s), 0 warning(s) can i know the problem
Reply ]

From dejan, 07:49 Mar 23, 2009 (score=3)

You obviously do not link necessary libraries.
Reply ]

 
 

Comments are owned by the poster. All other content is copyright 1998-2024 by Bill Spitzak and others. This project is hosted by The FLTK Team. Please report site problems to 'erco@seriss.com'.