From cdd47a11d473b592a4516e736a65da957072c428 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Thu, 28 Jul 2016 16:00:14 -0400 Subject: Add support for Cairo on GL --- pugl/cairo_gl.h | 102 +++++++++++++++++++++++++++++++++++++++++++++ pugl/common.h | 7 ++-- pugl/pugl_internal.h | 9 ++-- pugl/pugl_x11.c | 114 ++++++++++++++++++++++++++++++++++++++++----------- 4 files changed, 201 insertions(+), 31 deletions(-) create mode 100644 pugl/cairo_gl.h diff --git a/pugl/cairo_gl.h b/pugl/cairo_gl.h new file mode 100644 index 0000000..a558b42 --- /dev/null +++ b/pugl/cairo_gl.h @@ -0,0 +1,102 @@ +/* + Copyright 2016 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#if defined(PUGL_HAVE_GL) && defined(PUGL_HAVE_CAIRO) + +#include + +typedef struct { + unsigned texture_id; + uint8_t* buffer; +} PuglCairoGL; + +static cairo_surface_t* +pugl_cairo_gl_create(PuglCairoGL* ctx, int width, int height, int bpp) +{ + free(ctx->buffer); + ctx->buffer = (uint8_t*)calloc(bpp * width * height, sizeof(uint8_t)); + if (!ctx->buffer) { + fprintf(stderr, "failed to allocate surface buffer\n"); + return NULL; + } + + return cairo_image_surface_create_for_data( + ctx->buffer, CAIRO_FORMAT_ARGB32, width, height, bpp * width); +} + +static void +pugl_cairo_gl_free(PuglCairoGL* ctx) +{ + free(ctx->buffer); + ctx->buffer = NULL; +} + +static void +pugl_cairo_gl_configure(PuglCairoGL* ctx, int width, int height) +{ + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_TEXTURE_RECTANGLE_ARB); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); + + glClear(GL_COLOR_BUFFER_BIT); + + glDeleteTextures(1, &ctx->texture_id); + glGenTextures(1, &ctx->texture_id); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, ctx->texture_id); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); +} + +static void +pugl_cairo_gl_draw(PuglCairoGL* ctx, int width, int height) +{ + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glViewport(0, 0, width, height); + glClear(GL_COLOR_BUFFER_BIT); + + glPushMatrix(); + glEnable(GL_TEXTURE_RECTANGLE_ARB); + glEnable(GL_TEXTURE_2D); + + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, + width, height, 0, + GL_BGRA, GL_UNSIGNED_BYTE, ctx->buffer); + + glBegin(GL_QUADS); + glTexCoord2f(0.0f, (GLfloat)height); + glVertex2f(-1.0f, -1.0f); + + glTexCoord2f((GLfloat)width, (GLfloat)height); + glVertex2f(1.0f, -1.0f); + + glTexCoord2f((GLfloat)width, 0.0f); + glVertex2f(1.0f, 1.0f); + + glTexCoord2f(0.0f, 0.0f); + glVertex2f(-1.0f, 1.0f); + glEnd(); + + glDisable(GL_TEXTURE_2D); + glDisable(GL_TEXTURE_RECTANGLE_ARB); + glPopMatrix(); +} + +#endif diff --git a/pugl/common.h b/pugl/common.h index 54ec6ef..3abab82 100644 --- a/pugl/common.h +++ b/pugl/common.h @@ -1,5 +1,5 @@ /* - Copyright 2014 David Robillard + Copyright 2014-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -56,8 +56,9 @@ typedef enum { Drawing context type. */ typedef enum { - PUGL_GL, - PUGL_CAIRO + PUGL_GL = 0x1, + PUGL_CAIRO = 0x2, + PUGL_CAIRO_GL = 0x3 } PuglContextType; /** diff --git a/pugl/pugl_internal.h b/pugl/pugl_internal.h index 1238ebc..778ff1f 100644 --- a/pugl/pugl_internal.h +++ b/pugl/pugl_internal.h @@ -1,5 +1,5 @@ /* - Copyright 2012-2015 David Robillard + Copyright 2012-2016 David Robillard Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -86,9 +86,10 @@ puglInit(int* pargc, char** argv) return NULL; } - view->impl = impl; - view->width = 640; - view->height = 480; + view->ctx_type = PUGL_GL; + view->impl = impl; + view->width = 640; + view->height = 480; return view; } diff --git a/pugl/pugl_x11.c b/pugl/pugl_x11.c index 94827f3..e6d240c 100644 --- a/pugl/pugl_x11.c +++ b/pugl/pugl_x11.c @@ -1,5 +1,5 @@ /* - Copyright 2012-2015 David Robillard + Copyright 2012-2016 David Robillard Copyright 2013 Robin Gareus Copyright 2011-2012 Ben Loftis, Harrison Consoles @@ -40,6 +40,7 @@ #endif #include "pugl/event.h" +#include "pugl/cairo_gl.h" #include "pugl/pugl_internal.h" #ifndef MIN @@ -93,6 +94,9 @@ struct PuglInternalsImpl { GLXContext ctx; int doubleBuffered; #endif +#if defined(PUGL_HAVE_CAIRO) && defined(PUGL_HAVE_GL) + PuglCairoGL cairo_gl; +#endif }; PuglInternals* @@ -108,7 +112,7 @@ getVisual(PuglView* view) XVisualInfo* vi = NULL; #ifdef PUGL_HAVE_GL - if (view->ctx_type == PUGL_GL) { + if (view->ctx_type & PUGL_GL) { for (int* attr = *attrLists; !vi && *attr; ++attr) { vi = glXChooseVisual(impl->display, impl->screen, attr); } @@ -126,38 +130,78 @@ getVisual(PuglView* view) return vi; } -static void +#ifdef PUGL_HAVE_CAIRO +static int +createCairoContext(PuglView* view) +{ + PuglInternals* const impl = view->impl; + + if (impl->cr) { + cairo_destroy(impl->cr); + } + + impl->cr = cairo_create(impl->surface); + return cairo_status(impl->cr); +} +#endif + +static bool createContext(PuglView* view, XVisualInfo* vi) { PuglInternals* const impl = view->impl; #ifdef PUGL_HAVE_GL - if (view->ctx_type == PUGL_GL) { + if (view->ctx_type & PUGL_GL) { impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE); glXGetConfig(impl->display, vi, GLX_DOUBLEBUFFER, &impl->doubleBuffered); } #endif #ifdef PUGL_HAVE_CAIRO if (view->ctx_type == PUGL_CAIRO) { - view->impl->surface = cairo_xlib_surface_create( + impl->surface = cairo_xlib_surface_create( impl->display, impl->win, vi->visual, view->width, view->height); - if (!(impl->cr = cairo_create(view->impl->surface))) { - fprintf(stderr, "failed to create cairo context\n"); + } +#endif +#if defined(PUGL_HAVE_GL) && defined(PUGL_HAVE_CAIRO) + if (view->ctx_type == PUGL_CAIRO_GL) { + impl->surface = pugl_cairo_gl_create( + &impl->cairo_gl, view->width, view->height, 4); + } +#endif + +#ifdef PUGL_HAVE_CAIRO + if (view->ctx_type & PUGL_CAIRO) { + if (cairo_surface_status(impl->surface) != CAIRO_STATUS_SUCCESS) { + fprintf(stderr, "error: failed to create cairo surface\n"); + return false; + } + + if (createCairoContext(view) != CAIRO_STATUS_SUCCESS) { + cairo_surface_destroy(impl->surface); + fprintf(stderr, "error: failed to create cairo context\n"); + return false; } } #endif + + return true; } static void destroyContext(PuglView* view) { +#if defined(PUGL_HAVE_CAIRO) && defined(PUGL_HAVE_GL) + if (view->ctx_type == PUGL_CAIRO_GL) { + pugl_cairo_gl_free(&view->impl->cairo_gl); + } +#endif #ifdef PUGL_HAVE_GL - if (view->ctx_type == PUGL_GL) { + if (view->ctx_type & PUGL_GL) { glXDestroyContext(view->impl->display, view->impl->ctx); } #endif #ifdef PUGL_HAVE_CAIRO - if (view->ctx_type == PUGL_CAIRO) { + if (view->ctx_type & PUGL_CAIRO) { cairo_destroy(view->impl->cr); cairo_surface_destroy(view->impl->surface); } @@ -168,12 +212,12 @@ void puglEnterContext(PuglView* view) { #ifdef PUGL_HAVE_GL - if (view->ctx_type == PUGL_GL) { + if (view->ctx_type & PUGL_GL) { glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); } #endif #ifdef PUGL_HAVE_CAIRO - if (view->ctx_type == PUGL_CAIRO) { + if (view->ctx_type & PUGL_CAIRO) { cairo_set_source_rgb(view->impl->cr, 0, 0, 0); cairo_paint(view->impl->cr); } @@ -184,12 +228,20 @@ void puglLeaveContext(PuglView* view, bool flush) { #ifdef PUGL_HAVE_GL - if (view->ctx_type == PUGL_GL && flush) { + if (flush && view->ctx_type & PUGL_GL) { +#ifdef PUGL_HAVE_CAIRO + if (view->ctx_type == PUGL_CAIRO_GL) { + pugl_cairo_gl_draw(&view->impl->cairo_gl, view->width, view->height); + } +#endif + glFlush(); if (view->impl->doubleBuffered) { glXSwapBuffers(view->impl->display, view->impl->win); } } + + glXMakeCurrent(view->impl->display, None, NULL); #endif } @@ -227,7 +279,9 @@ puglCreateWindow(PuglView* view, const char* title) 0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &attr); - createContext(view, vi); + if (!createContext(view, vi)) { + return 2; + } XSizeHints sizeHints; memset(&sizeHints, 0, sizeof(sizeHints)); @@ -304,17 +358,14 @@ puglHideWindow(PuglView* view) void puglDestroy(PuglView* view) { - if (!view) { - return; + if (view) { + destroyContext(view); + XDestroyWindow(view->impl->display, view->impl->win); + XCloseDisplay(view->impl->display); + free(view->windowClass); + free(view->impl); + free(view); } - - destroyContext(view); - XDestroyWindow(view->impl->display, view->impl->win); - XCloseDisplay(view->impl->display); - free(view->windowClass); - free(view->impl); - free(view); - view = NULL; } static PuglKey @@ -603,6 +654,21 @@ puglProcessEvents(PuglView* view) config_event.configure.width, config_event.configure.height); } +#ifdef PUGL_HAVE_GL + if (view->ctx_type == PUGL_CAIRO_GL) { + view->redisplay = true; + cairo_surface_destroy(view->impl->surface); + view->impl->surface = pugl_cairo_gl_create( + &view->impl->cairo_gl, + config_event.configure.width, + config_event.configure.height, + 4); + pugl_cairo_gl_configure(&view->impl->cairo_gl, + config_event.configure.width, + config_event.configure.height); + createCairoContext(view); + } +#endif #endif puglDispatchEvent(view, (const PuglEvent*)&config_event); } @@ -640,7 +706,7 @@ void* puglGetContext(PuglView* view) { #ifdef PUGL_HAVE_CAIRO - if (view->ctx_type == PUGL_CAIRO) { + if (view->ctx_type & PUGL_CAIRO) { return view->impl->cr; } #endif -- cgit v1.2.1