views:

467

answers:

4

Hi, I'm a scientist who is quite comfortable with C for numerical computation, but I need some help with displaying the results. I want to be able to display a continuously updated bitmap in a window, which is calculated from realtime data. I'd like to be able to update the image quite quickly (e.g. faster than 1 frame/second, preferably 100 fps). For example:

char image_buffer[width*height*3];//rgb data
initializewindow();

for (t=0;t<t_end;t++)
{
   getdata(data);//get some realtime data
   docalcs(image_buffer, data);//process the data into an image
   drawimage(image_buffer);//draw the image
}

What's the easiest way to do this on linux (Ubuntu)? What should I use for initializewindow() and drawimage()?

+6  A: 

GUI stuff is a regularly-reinvented wheel, and there's no reason to not use a framework.

I'd recommend using either QT4 or wxWidgets. If you're using Ubuntu, GTK+ will suffice as it talks to GNOME and may be more comfortable to you (QT and wxWidgets both require C++).

Have a look at GTK+, QT, and wxWidgets.

Here's the tutorials for all 3:

Jed Smith
+12  A: 

If all you want to do is display the data (ie no need for a GUI), you might want to take a look at SDL: It's straight-forward to create a surface from your pixel data and then display it on screen.

Inspired by Artelius' answer, I also hacked up an example program:

#include <SDL/SDL.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>

#define WIDTH 256
#define HEIGHT 256

static _Bool init_app(const char * name, SDL_Surface * icon, uint32_t flags)
{
    atexit(SDL_Quit);
    if(SDL_Init(flags) < 0)
        return 0;

    SDL_WM_SetCaption(name, name);
    SDL_WM_SetIcon(icon, NULL);

    return 1;
}

static uint8_t * init_data(uint8_t * data)
{
    for(size_t i = WIDTH * HEIGHT * 3; i--; )
        data[i] = (i % 3 == 0) ? (i / 3) % WIDTH :
            (i % 3 == 1) ? (i / 3) / WIDTH : 0;

    return data;
}

static _Bool process(uint8_t * data)
{
    for(SDL_Event event; SDL_PollEvent(&event);)
        if(event.type == SDL_QUIT) return 0;

    for(size_t i = 0; i < WIDTH * HEIGHT * 3; i += 1 + rand() % 3)
        data[i] -= rand() % 8;

    return 1;
}

static void render(SDL_Surface * sf)
{
    SDL_Surface * screen = SDL_GetVideoSurface();
    if(SDL_BlitSurface(sf, NULL, screen, NULL) == 0)
        SDL_UpdateRect(screen, 0, 0, 0, 0);
}

static int filter(const SDL_Event * event)
{ return event->type == SDL_QUIT; }

#define mask32(BYTE) (*(uint32_t *)(uint8_t [4]){ [BYTE] = 0xff })

int main(int argc, char * argv[])
{
    (void)argc, (void)argv;
    static uint8_t buffer[WIDTH * HEIGHT * 3];

    _Bool ok =
        init_app("SDL example", NULL, SDL_INIT_VIDEO) &&
        SDL_SetVideoMode(WIDTH, HEIGHT, 24, SDL_HWSURFACE);

    assert(ok);

    SDL_Surface * data_sf = SDL_CreateRGBSurfaceFrom(
        init_data(buffer), WIDTH, HEIGHT, 24, WIDTH * 3,
        mask32(0), mask32(1), mask32(2), 0);

    SDL_SetEventFilter(filter);

    for(; process(buffer); SDL_Delay(10))
        render(data_sf);

    return 0;
}
Christoph
+1 for SDL being most suitable. It's fast, portable, easy and does exactly what the asker wants.
James
I'd recommend SDL too. Unfortunately I've not found an SDL guide that provides the right level of detail (I'm thinking of writing my own). So you might have to do a lot of reading and putting-together if you want the best results.
Artelius
Thanks, this worked well. I got a sample up and running this afternoon. Also see Artelius's demo.
tkw954
+2  A: 

In addition to Jed Smith's answer, there are also lower-level frameworks, like OpenGL, which is often used for game programming. Given that you want to use a high frame rate, I'd consider something like that. GTK and the like aren't primarily intended for rapidly updating displays.

David Zaslavsky
I think you'll need something with OpenGL to handle opening a window and such, and I think SDL is a good choice there. I suggest you start with SDL, (with the updateRect or flip code described in other answers) then, if that's not fast enough, try the OpenGL calls from the SDL framework.
JasonWoof
+4  A: 

I'd recommend SDL too. However, there's a bit of understanding you need to gather if you want to write fast programs, and that's not the easiest thing to do.

I would suggest this O'Reilly article as a starting point.

But I shall boil down the most important points from a computations perspective.

Double buffering

What SDL calls "double buffering" is generally called page flipping.

This basically means that on the graphics card, there are two chunks of memory called pages, each one large enough to hold a screen's worth of data. One is made visible on the monitor, the other one is accessible by your program. When you call SDL_Flip(), the graphics card switches their roles (i.e. the visible one becomes program-accessible and vice versa).

The alternative is, rather than swapping the roles of the pages, instead copy the data from the program-accessible page to the monitor page (using SDL_UpdateRect()).

Page flipping is fast, but has a drawback: after page flipping, your program is presented with a buffer that contains the pixels from 2 frames ago. This is fine if you need to recalculate every pixel every frame.

However, if you only need to modify smallish regions on the screen every frame, and the rest of the screen does not need to change, then UpdateRect can be a better way (see also: SDL_UpdateRects()).

This of course depends on what it is you're computing and how you're visualising it. Analyse your image-generating code - maybe you can restructure it to get something more efficient out of it?

Note that if your graphics hardware doesn't support page flipping, SDL will gracefully use the other method for you.

Software/Hardware/OpenGL

This is another question you face. Basically, software surfaces live in RAM, hardware surfaces live in Video RAM, and OpenGL surfaces are managed by OpenGL magic.

Depending on your hardware, OS, and SDL version, programatically modifying the pixels of a hardware surface can involve a LOT of memory copying (VRAM to RAM, and then back!). You don't want this to happen every frame. In such cases, software surfaces work better. But then, you can't take advantage of double buffering, nor hardware accelerated blits.

Blits are block-copies of pixels from one surface to another. This works well if you want to draw a whole lot of identical icons on a surface. Not so useful if you're generating a temperature map.

OpenGL lets you do much more with your graphics hardware (3D acceleration for a start). Modern graphics cards have a lot of processing power, but it's kind of hard to use unless you're making a 3D simulation. Writing code for a graphics processor is possible but quite different to ordinary C.

Demo

Here's a quick demo SDL program that I made. It's not supposed to be a perfect example, and may have some portability problems. (I will try to edit a better program into this post when I get time.)

#include "SDL.h"
#include <assert.h>
#include <math.h>

/* This macro simplifies accessing a given pixel component on a surface. */
#define pel(surf, x, y, rgb) ((unsigned char *)(surf->pixels))[y*(surf->pitch)+x*3+rgb]


int main(int argc, char *argv[])
{
    int x, y, t;

    /* Event information is placed in here */
    SDL_Event event;

    /* This will be used as our "handle" to the screen surface */
    SDL_Surface *scr;

    SDL_Init(SDL_INIT_VIDEO);

    /* Get a 640x480, 24-bit software screen surface */
    scr = SDL_SetVideoMode(640, 480, 24, SDL_SWSURFACE);
    assert(scr);

    /* Ensures we have exclusive access to the pixels */
    SDL_LockSurface(scr);

    for(y = 0; y < scr->h; y++)
     for(x = 0; x < scr->w; x++)
     {
      /* This is what generates the pattern based on the xy co-ord */
      t = ((x*x + y*y) & 511) - 256;
      if (t < 0)
       t = -(t + 1);

      /* Now we write to the surface */
      pel(scr, x, y, 0) = 255 - t; //red
      pel(scr, x, y, 1) = t; //green
      pel(scr, x, y, 2) = t; //blue
     }
    SDL_UnlockSurface(scr);

    /* Copies the `scr' surface to the _actual_ screen */
    SDL_UpdateRect(scr, 0, 0, 0, 0);


    /* Now we wait for an event to arrive */
    while(SDL_WaitEvent(&event))
    {
     /* Any of these event types will end the program */
     if (event.type == SDL_QUIT
      || event.type == SDL_KEYDOWN
      || event.type == SDL_KEYUP)
      break;
    }
    SDL_Quit();
    return EXIT_SUCCESS;
}
Artelius
Your "pel" macro didn't seem to put the correct pixels in the right place. However, the rest put me on the right track. Thanks.
tkw954
I think I fixed the bug.
Artelius