render to image surface

This commit is contained in:
Florian Stecker 2018-08-18 18:27:33 +02:00
parent c3deff6d64
commit 000d8a53b9
3 changed files with 107 additions and 52 deletions

View File

@ -54,9 +54,17 @@ GraphicsInfo *initCairo(int screen, int mask, int width, int height, const char
XMapWindow(info->display, info->win);
// create xlib surface
info->surface = cairo_xlib_surface_create(info->display, info->win, visual, width, height);
cairo_xlib_surface_set_size(info->surface, width, height);
info->context->cairo = cairo_create(info->surface);
info->front_context = cairo_create(info->surface);
cairo_set_antialias(info->front_context, CAIRO_ANTIALIAS_NONE);
// create backbuffer surface
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
info->buffer = malloc(stride*height);
info->buffer_surface = cairo_image_surface_create_for_data(info->buffer, CAIRO_FORMAT_ARGB32, width, height, stride);
info->context->cairo = cairo_create(info->buffer_surface);
cairo_matrix_init_identity(&info->context->matrix);
info->context->width = width;
info->context->height = height;
@ -65,11 +73,18 @@ GraphicsInfo *initCairo(int screen, int mask, int width, int height, const char
info->wm_protocols = XInternAtom(info->display, "WM_PROTOCOLS", 0);
XSetWMProtocols(info->display, info->win, &info->wm_delete_window, 1);
XFlush(info->display);
return info;
}
void destroyCairo(GraphicsInfo *info)
{
cairo_destroy(info->context->cairo);
cairo_destroy(info->front_context);
cairo_surface_destroy(info->surface);
cairo_surface_destroy(info->buffer_surface);
free(info->buffer);
XDestroyWindow(info->display, info->win);
XFreeColormap(info->display, info->cmap);
XCloseDisplay(info->display);
@ -107,26 +122,35 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingCon
{
int state;
static int last_x, last_y;
int stride;
int status = STATUS_NOTHING;
switch(ev->type) {
case Expose:
draw(info->context);
break;
return STATUS_REDRAW;
case ConfigureNotify:
printf("ConfigureNotify Event, new dimensions: %d %d %d %d\n", ev->xconfigure.x, ev->xconfigure.y, ev->xconfigure.width, ev->xconfigure.height);
info->context->width = ev->xconfigure.width;
info->context->height = ev->xconfigure.height;
cairo_xlib_surface_set_size(info->surface, ev->xconfigure.width, ev->xconfigure.height);
draw(info->context);
break;
cairo_xlib_surface_set_size(info->surface, info->context->width, info->context->height);
cairo_destroy(info->context->cairo);
cairo_surface_destroy(info->buffer_surface);
free(info->buffer);
stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, info->context->width);
info->buffer = malloc(stride*info->context->height);
info->buffer_surface = cairo_image_surface_create_for_data(info->buffer, CAIRO_FORMAT_ARGB32, info->context->width, info->context->height, stride);
info->context->cairo = cairo_create(info->buffer_surface);
return STATUS_REDRAW;
case KeyPress:
state = ev->xkey.state & (ShiftMask | LockMask | ControlMask);
if(state == 0 && ev->xkey.keycode == 24)
return true;
break;
return STATUS_QUIT;
return STATUS_NOTHING;
case ButtonPress:
if(ev->xbutton.button == 4) {
@ -136,7 +160,7 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingCon
cairo_matrix_scale(&transform, 5.0/4.0, 5.0/4.0);
cairo_matrix_translate(&transform, -ev->xbutton.x, -ev->xbutton.y);
cairo_matrix_multiply(&info->context->matrix, &info->context->matrix, &transform);
draw(info->context);
status = STATUS_REDRAW;
} else if(ev->xbutton.button == 5) {
cairo_matrix_t transform;
cairo_matrix_init_identity(&transform);
@ -144,11 +168,12 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingCon
cairo_matrix_scale(&transform, 4.0/5.0, 4.0/5.0);
cairo_matrix_translate(&transform, -ev->xbutton.x, -ev->xbutton.y);
cairo_matrix_multiply(&info->context->matrix, &info->context->matrix, &transform);
draw(info->context);
status = STATUS_REDRAW;
}
last_x = ev->xbutton.x;
last_y = ev->xbutton.y;
break;
return status;
case MotionNotify:
if(ev->xmotion.state & Button1Mask && !(ev->xmotion.state & ShiftMask)) {
@ -159,7 +184,7 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingCon
cairo_matrix_init_identity(&transform);
cairo_matrix_translate(&transform, dx, dy);
cairo_matrix_multiply(&info->context->matrix, &info->context->matrix, &transform);
draw(info->context);
status = STATUS_REDRAW;
} else if(ev->xmotion.state & Button1Mask && ev->xmotion.state & ShiftMask) {
double width = (double) cairo_xlib_surface_get_width(info->surface);
double height = (double) cairo_xlib_surface_get_height(info->surface);
@ -174,50 +199,67 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingCon
cairo_matrix_rotate(&transform, angle);
cairo_matrix_translate(&transform, -width/2, -height/2);
cairo_matrix_multiply(&info->context->matrix, &info->context->matrix, &transform);
draw(info->context);
status = STATUS_REDRAW;
}
last_x = ev->xmotion.x;
last_y = ev->xmotion.y;
break;
return status;
case ClientMessage:
if((Atom)ev->xclient.message_type == info->wm_protocols && (Atom)ev->xclient.data.l[0] == info->wm_delete_window) {
// printf("Window closed\n");
return true;
return STATUS_QUIT;
}
break;
return STATUS_NOTHING;
}
return 0;
return STATUS_NOTHING;
}
int checkEvents(GraphicsInfo *info, int (*process)(GraphicsInfo*, XEvent*), void (*draw)(DrawingContext*)) // get any events from the queue and the server, process them if neccessary, quit if wanted
{
XEvent ev;
int x11_fd;
fd_set fds;
int status = STATUS_NOTHING, result;
// while(XCheckIfEvent(info->display, &ev, alwaysTruePredicate, NULL)) { // we essentially want XCheckWindowEvent, but we want to avoid that events for other windows fill up the queue
int x11_event = 0;
XNextEvent(info->display, &ev);
if(XPending(info->display)) { // select() would miss events which are picked up already, so we have to catch them first
x11_event = 1;
} else {
x11_fd = ConnectionNumber(info->display);
FD_ZERO(&fds);
FD_SET(x11_fd, &fds);
if(ev.xany.window != info->win)
return 0;
select(x11_fd + 1, &fds, NULL, NULL, NULL);
/* if(ev.type == KeyRelease) { // deal with autorepeat
XEvent nev;
if(XCheckIfEvent(info->display, &nev, alwaysTruePredicate, NULL)) { // is there another event?
if (nev.type == KeyPress && nev.xkey.time == ev.xkey.time && nev.xkey.keycode == ev.xkey.keycode) // which is equal, but KeyPress? Then it's just auto-repeat
return 0; // so we ignore both
if(FD_ISSET(x11_fd, &fds))
x11_event = 1;
}
XPutBackEvent(info->display, &nev); // otherwise put the event back, we will consider it in the next round
if(x11_event) {
while(XPending(info->display)) {
XNextEvent(info->display, &ev);
if(ev.xany.window != info->win)
continue;
result = processStandardEvent(info, &ev, draw);
if(result == STATUS_QUIT)
return STATUS_QUIT;
else if(result == STATUS_REDRAW)
status = STATUS_REDRAW;
result = process(info, &ev);
if(result == STATUS_QUIT)
return STATUS_QUIT;
else if(result == STATUS_REDRAW)
status = STATUS_REDRAW;
}
} */
}
if(processStandardEvent(info, &ev, draw))
return 1;
if(process(info, &ev))
return 1; // quit event queue and application
return 0;
return status;
}

View File

@ -12,6 +12,10 @@
#include <cairo.h>
#include <cairo-xlib.h>
#define STATUS_NOTHING 0
#define STATUS_REDRAW 1
#define STATUS_QUIT 2
typedef struct {
cairo_t *cairo;
cairo_matrix_t matrix;
@ -33,6 +37,11 @@ typedef struct {
Atom wm_protocols;
Atom wm_delete_window;
cairo_surface_t *surface;
cairo_t *front_context;
cairo_surface_t *buffer_surface;
unsigned char *buffer;
struct timeval start_time;
unsigned long frames;
double elapsed, frametime;

View File

@ -358,14 +358,8 @@ void drawAttractors(DrawingContext *ctx)
void draw(DrawingContext *ctx)
{
struct timeval current_time;
gettimeofday(&current_time, 0);
double start_time = current_time.tv_sec + current_time.tv_usec*1e-6;
cairo_t *C = ctx->cairo;
updateDimensions(ctx);
cairo_push_group(C);
cairo_set_source_rgb(C, 1, 1, 1);
cairo_paint(C);
@ -378,7 +372,6 @@ void draw(DrawingContext *ctx)
cairo_set_line_cap(C, CAIRO_LINE_CAP_ROUND);
if(limit_curve_valid) {
if(show_limit) {
int last_inside = 0;
for(int i = 0; i < n_group_elements; i++) {
@ -421,13 +414,7 @@ void draw(DrawingContext *ctx)
sprintf(buf, "t = %.8f", parameter);
cairo_show_text(C, buf);
cairo_pop_group_to_source(C);
cairo_paint(C);
cairo_surface_flush(cairo_get_target(C));
gettimeofday(&current_time, 0);
double end_time = current_time.tv_sec + current_time.tv_usec*1e-6;
printf("draw() finished in %g seconds\n", end_time - start_time);
}
void setup()
@ -544,10 +531,10 @@ int processEvent(GraphicsInfo *info, XEvent *ev)
break;
}
draw(info->context);
return STATUS_REDRAW;
}
return 0;
return STATUS_NOTHING;
}
int main()
@ -586,7 +573,24 @@ int main()
startTimer(info);
while(!checkEvents(info, processEvent, draw)) {
while(1) {
int result = checkEvents(info, processEvent, NULL);
if(result == STATUS_QUIT)
return 0;
else if(result == STATUS_REDRAW) {
struct timeval current_time;
gettimeofday(&current_time, 0);
double start_time = current_time.tv_sec + current_time.tv_usec*1e-6;
updateDimensions(info->context);
draw(info->context);
cairo_set_source_surface(info->front_context, info->buffer_surface, 0, 0);
cairo_paint(info->front_context);
gettimeofday(&current_time, 0);
double end_time = current_time.tv_sec + current_time.tv_usec*1e-6;
printf("drawing finished in %g seconds\n", end_time - start_time);
}
waitUpdateTimer(info);
}