CairoとSDLを組合せてつかう

cairoの描画フロントエンドとしてSDLを使ってみた。ちょっと画面に表示させるだけなのにCocoaだのなんだのを使うのが面倒だったので…。

見た感じ、cairo image surfaceSDL surfaceを比較すると、cairoの方がピクセルフォーマットに対する許容度が低かったので、SDL側でフォーマット差を吸収させています。たとえば、cairo image surfaceではRGB888の24bppフォーマットが使えません。


なお、リソースの開放はしてますが、エラーチェックはしてません。

#include <cmath>
#include <memory>
#include <cairo/cairo.h>
#include <SDL/SDL.h>

const int W = 640;
const int H = 480;

template <class T, class F>
std::unique_ptr<T, F> wrap_ptr(T* p, F&& f) {
    return std::unique_ptr<T, F>(p, std::forward<F>(f));
}

template <class Pre, class Post>
struct scoped_guard_fn {
    scoped_guard_fn(Pre&& pre, Post&& post)
    : post_(std::forward<Post>(post)) {
        pre();
    }
    ~scoped_guard_fn() {
        post_();
    }
private:
    Post post_;
};

template <class Pre, class Post>
scoped_guard_fn<Pre, Post>
scoped_guard(Pre&& pre, Post&& post) {
    return scoped_guard_fn<Pre, Post>(std::forward<Pre>(pre), std::forward<Post>(post));
}

int main(int, char**) {
    auto _sdl = scoped_guard(std::bind(SDL_Init, SDL_INIT_VIDEO), SDL_Quit);

    auto screen = SDL_SetVideoMode(W, H, 0, SDL_SWSURFACE | SDL_DOUBLEBUF);
    auto cs = wrap_ptr(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, W, H), cairo_surface_destroy);
    auto cr = wrap_ptr(cairo_create(cs.get()), cairo_destroy);
    auto s = wrap_ptr(SDL_CreateRGBSurfaceFrom(cairo_image_surface_get_data(cs.get()),
                                               W, H, 32, cairo_image_surface_get_stride(cs.get()),
                                               0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000),
                      SDL_FreeSurface);

    cairo_set_source_rgb(cr.get(), 1, 1, 1);
    cairo_rectangle(cr.get(), 0, 0, W, H);
    cairo_fill(cr.get());

    cairo_set_source_rgb(cr.get(), 0, 0, 0);
    cairo_arc(cr.get(), W / 2, H / 2, 60, 0, 2 * M_PI);
    cairo_stroke(cr.get());

    SDL_BlitSurface(s.get(), NULL, screen, NULL);
    SDL_Flip(screen);

    for (;;) {
        SDL_Event ev;
        SDL_WaitEvent(&ev);
        if (ev.type == SDL_QUIT) {
            break;
        }
    }

    return 0;
}