在C / Linux中显示连续更新图像的简便方法

时间:2009-09-07 23:34:19

标签: c linux image graphics video

我是一名科学家,他对数学计算很满意C,但我需要一些帮助才能显示结果。我希望能够在窗口中显示连续更新的位图,该位图是根据实时数据计算的。我希望能够非常快速地更新图像(例如,快于1帧/秒,优选地为100帧/秒)。例如:

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
}

在linux(Ubuntu)上执行此操作的最简单方法是什么?我应该将什么用于initializewindow()和drawimage()?

5 个答案:

答案 0 :(得分:18)

如果您只想显示数据(即不需要GUI),您可能需要查看SDL:它是直接create a surface from your pixel data然后是{{ 3}}

display it on screen的启发,我还破解了一个示例程序:

#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;
}

答案 1 :(得分:9)

我也推荐SDL。但是,如果你想编写快速程序,你需要收集一些理解,而这不是最简单的事情。

我建议this O'Reilly article作为起点。

但是我将从计算的角度归结为最重要的几点。

双缓冲

SDL称之为“双缓冲”通常称为页面翻转

这基本上意味着在显卡上有两个内存块,称为页面,每个内存都足以容纳屏幕上的数据。一个在监视器上可见,另一个可由您的程序访问。当你拨打SDL_Flip()时,显卡会切换他们的角色(即可见的一个变为程序可访问的,反之亦然)。

替代方法是,而不是交换页面的角色,而是将数据从程序可访问页面复制到监控页面(使用{{3} })。

页面翻转很快,但有一个缺点:页面翻转后,程序会显示一个缓冲区,其中包含2帧前的像素。如果您需要每帧重新计算每个像素,这很好。

但是,如果您只需要在每个帧上修改屏幕上的小区域,并且屏幕的其余部分不需要更改,那么UpdateRect可能是更好的方式(另请参阅:{{3} })。

这当然取决于你的计算内容以及你如何将其可视化。分析你的图像生成代码 - 也许你可以重组它以从中获得更高效的东西?

请注意,如果您的图形硬件不支持翻页,SDL会优雅地使用其他方法。

软件/硬件/ OpenGL的

这是你面临的另一个问题。基本上,软件表面存在于RAM中,硬件表面存在于视频RAM中,OpenGL表面由OpenGL魔术管理。

根据您的硬件,操作系统和SDL版本,以编程方式修改硬件表面的像素可能需要大量内存复制(VRAM到RAM,然后再返回!)。你不希望每一帧都发生这种情况。在这种情况下,软件表面效果更好。但是,你不能利用双缓冲,也不能利用硬件加速的blits。

Blits是从一个表面到另一个表面的像素块。如果您想在曲面上绘制大量相同的图标,这很有效。如果您正在生成温度图,则不太有用。

OpenGL让您可以使用图形硬件做更多更多(一开始就是3D加速)。现代显卡具有很强的处理能力,但除非你进行3D模拟,否则它很难使用。编写图形处理器的代码是可能的,但与普通的C完全不同。

演示

这是我制作的快速演示SDL程序。它不应该是一个完美的例子,并且可能存在一些可移植性问题。 (当我有时间时,我会尝试在这篇文章中编辑一个更好的程序。)

#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;
}

答案 2 :(得分:7)

GUI内容是一个经常重新发明的轮子,没有理由不使用框架。

我建议使用QT4或wxWidgets。如果您正在使用Ubuntu,那么GTK +就可以满足它与GNOME的对话,并且对您来说可能更舒服(QT和wxWidgets都需要C ++)。

查看GTK+QTwxWidgets

以下是所有3的教程:

答案 3 :(得分:2)

除了Jed Smith的答案之外,还有一些低级框架,比如OpenGL,它通常用于游戏编程。鉴于你想要使用高帧率,我会考虑这样的事情。 GTK等主要不是用于快速更新显示器。

答案 4 :(得分:0)

根据我的经验,通过MIT-SHM扩展的Xlib明显快于SDL表面,但不确定我是否以最佳方式使用SDL。