Add console drawing-logic
authorDavid Herrmann <dh.herrmann@googlemail.com>
Sun, 20 Nov 2011 16:49:21 +0000 (17:49 +0100)
committerDavid Herrmann <dh.herrmann@googlemail.com>
Sun, 20 Nov 2011 16:49:21 +0000 (17:49 +0100)
A console may be displayed on multiple outputs. We allow different screen
resolutions on these outputs so we need a way to map the console image onto
different outputs.

We simply render the console image into a cairo surface with a user-given
resolution. This should be set to the highest resultion of the used outputs. The
application can now map this image onto the different framebuffers. This will
scale the image if the target framebuffer is smaller than the cairo surface.

We also do not clear the framebuffer. This should be done by the user. This
allows us to draw with alpha values and blend the console on top of the current
framebuffer. We do not have to care for background images and the application
has full control of where the console is shown.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
Makefile
src/console.c
src/console.h

index b818d18..45c4765 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,7 @@
 # replace it with autotools, I would be glad to apply your patches.
 #
 
-CFLAGS=-g -O0 -Wall `pkg-config --cflags --libs egl gbm gl` -Isrc
+CFLAGS=-g -O0 -Wall `pkg-config --cflags --libs egl gbm gl cairo` -Isrc
 
 all:
        gcc -o kmscon src/*.c $(CFLAGS)
index 5a44bba..2c4db27 100644 (file)
@@ -20,6 +20,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <cairo.h>
 #include <GL/gl.h>
 #include <GL/glext.h>
 
 
 struct kmscon_console {
        size_t ref;
+
+       GLuint tex;
+       uint32_t res_x;
+       uint32_t res_y;
+
+       cairo_t *cr;
+       cairo_surface_t *surf;
+       unsigned char *surf_buf;
 };
 
 int kmscon_console_new(struct kmscon_console **out)
@@ -43,6 +52,8 @@ int kmscon_console_new(struct kmscon_console **out)
        memset(con, 0, sizeof(*con));
        con->ref = 1;
 
+       glGenTextures(1, &con->tex);
+
        *out = con;
        return 0;
 }
@@ -55,6 +66,10 @@ void kmscon_console_ref(struct kmscon_console *con)
        ++con->ref;
 }
 
+/*
+ * Drops one reference. If this is the last reference, the whole console is
+ * freed and the associated render-images are destroyed.
+ */
 void kmscon_console_unref(struct kmscon_console *con)
 {
        if (!con || !con->ref)
@@ -63,5 +78,142 @@ void kmscon_console_unref(struct kmscon_console *con)
        if (--con->ref)
                return;
 
+       if (con->cr) {
+               cairo_destroy(con->cr);
+               cairo_surface_destroy(con->surf);
+               free(con->surf_buf);
+       }
+
+       glDeleteTextures(1, &con->tex);
        free(con);
 }
+
+/*
+ * This resets the resolution used for drawing operations. It is recommended to
+ * set this to the size of your framebuffer, howevr, you can set this to
+ * anything except 0.
+ * This image-resolution is used internally to render the console fonts. The
+ * kmscon_console_map() function can map this image to any framebuffer size you
+ * want. Therefore, this screen resolution is just a performance and quality
+ * hint.
+ * This function must be called before drawing the console, though. Returns 0 on
+ * success, -EINVAL if con, x or y is 0/NULL and -ENOMEM on out-of-mem errors.
+ */
+int kmscon_console_set_res(struct kmscon_console *con, uint32_t x, uint32_t y)
+{
+       unsigned char *buf;
+       cairo_t *cr;
+       cairo_surface_t *surface;
+       int stride, ret;
+       cairo_format_t format = CAIRO_FORMAT_ARGB32;
+
+       if (!con || !x || !y)
+               return -EINVAL;
+
+       stride = cairo_format_stride_for_width(format, x);
+
+       buf = malloc(stride * y);
+       if (!buf)
+               return -ENOMEM;
+
+       surface = cairo_image_surface_create_for_data(buf, format, x, y,
+                                                               stride);
+       if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
+               ret = -ENOMEM;
+               goto err_free;
+       }
+
+       cr = cairo_create(surface);
+       if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) {
+               ret = -EFAULT;
+               goto err_cairo;
+       }
+
+       if (con->cr) {
+               cairo_destroy(con->cr);
+               cairo_surface_destroy(con->surf);
+               free(con->surf_buf);
+       }
+
+       con->res_x = x;
+       con->res_y = y;
+       con->surf_buf = buf;
+       con->surf = surface;
+       con->cr = cr;
+
+       glBindTexture(GL_TEXTURE_RECTANGLE, con->tex);
+       glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA, con->res_x, con->res_y,
+                               0, GL_BGRA, GL_UNSIGNED_BYTE, con->surf_buf);
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+       return 0;
+
+err_cairo:
+       cairo_destroy(cr);
+err_free:
+       cairo_surface_destroy(surface);
+       free(buf);
+       return ret;
+}
+
+/*
+ * This redraws the console. It does not clip/copy the image onto any
+ * framebuffer. You must use kmscon_console_map() to do this.
+ * This allows to draw the console once and then map it onto multiple
+ * framebuffers so it is displayed on multiple monitors with different screen
+ * resolutions.
+ * You must have called kmscon_console_set_res() before.
+ */
+void kmscon_console_draw(struct kmscon_console *con)
+{
+       if (!con || !con->cr)
+               return;
+
+       cairo_save(con->cr);
+
+       cairo_set_operator(con->cr, CAIRO_OPERATOR_OVER);
+       cairo_scale(con->cr, con->res_x, con->res_y);
+       cairo_set_source_rgba(con->cr, 0.0, 0.0, 0.0, 0.0);
+       cairo_paint(con->cr);
+
+       // TODO: draw console here
+
+       cairo_restore(con->cr);
+}
+
+/*
+ * This maps the console onto the current GL framebuffer. It expects the
+ * framebuffer to have 0/0 in the middle, -1/-1 in the upper left and 1/1 in the
+ * lower right (default GL settings).
+ * This does not clear the screen, nor does it paint the background. Instead the
+ * background is transparent and blended on top of the framebuffer.
+ *
+ * You must have called kmscon_console_draw() before, otherwise this will map an
+ * empty image onto the screen.
+ */
+void kmscon_console_map(struct kmscon_console *con)
+{
+       if (!con || !con->cr)
+               return;
+
+       glEnable(GL_BLEND);
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       glEnable(GL_TEXTURE_RECTANGLE);
+       glBindTexture(GL_TEXTURE_RECTANGLE, con->tex);
+
+       glBegin(GL_QUADS);
+               glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
+
+               glTexCoord2f(0.0f, 0.0f);
+               glVertex2f(-1.0f, -1.0f);
+
+               glTexCoord2f(con->res_x, 0.0f);
+               glVertex2f(1.0f, -1.0f);
+
+               glTexCoord2f(con->res_x, con->res_y);
+               glVertex2f(1.0f, 1.0f);
+
+               glTexCoord2f(0.0f, con->res_y);
+               glVertex2f(-1.0f, 1.0f);
+       glEnd();
+}
index 904b194..6022653 100644 (file)
@@ -35,3 +35,7 @@ int kmscon_char_append_u8(struct kmscon_char *ch, const char *str, size_t len);
 int kmscon_console_new(struct kmscon_console **out);
 void kmscon_console_ref(struct kmscon_console *con);
 void kmscon_console_unref(struct kmscon_console *con);
+
+int kmscon_console_set_res(struct kmscon_console *con, uint32_t x, uint32_t y);
+void kmscon_console_draw(struct kmscon_console *con);
+void kmscon_console_map(struct kmscon_console *con);