[PATCH 2/5] gl-renderer: Add support for per-output multi-texture borders.
Jason Ekstrand
jason at jlekstrand.net
Sat Oct 19 20:42:34 CEST 2013
The first advantage of this new API is that it is per-output instead of
global to the gl_renderer instance. This means that different windows can
have different titles, different button states, etc. The new api also uses
four textures (one for each side) instead of one. This allows you to draw
real borders with text and buttons in them instead of a simple image that
gets streached.
Images will be scaled as needed, so the right and left can be one pixel
tall if desired.
Signed-off-by: Jason Ekstrand <jason at jlekstrand.net>
---
src/gl-renderer.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
src/gl-renderer.h | 39 ++++++++++++++++
2 files changed, 168 insertions(+), 3 deletions(-)
diff --git a/src/gl-renderer.c b/src/gl-renderer.c
index f02445b..7ca5c22 100644
--- a/src/gl-renderer.c
+++ b/src/gl-renderer.c
@@ -50,9 +50,18 @@ struct gl_shader {
#define BUFFER_DAMAGE_COUNT 2
+struct gl_border_image {
+ GLuint tex;
+ int32_t width, height;
+ int32_t tex_width;
+ int dirty;
+ void *data;
+};
+
struct gl_output_state {
EGLSurface egl_surface;
pixman_region32_t buffer_damage[BUFFER_DAMAGE_COUNT];
+ struct gl_border_image borders[4];
};
enum buffer_type {
@@ -570,6 +579,104 @@ repaint_surfaces(struct weston_output *output, pixman_region32_t *damage)
draw_surface(surface, output, damage);
}
+static void
+draw_output_border_texture(struct gl_border_image *img, int32_t x, int32_t y,
+ int32_t width, int32_t height)
+{
+ static GLushort indices [] = { 0, 1, 3, 3, 1, 2 };
+
+ if (!img->data) {
+ if (img->tex) {
+ glDeleteTextures(1, &img->tex);
+ img->tex = 0;
+ }
+
+ return;
+ }
+
+ if (!img->tex) {
+ glGenTextures(1, &img->tex);
+ glBindTexture(GL_TEXTURE_2D, img->tex);
+
+ glTexParameteri(GL_TEXTURE_2D,
+ GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D,
+ GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D,
+ GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,
+ GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ } else {
+ glBindTexture(GL_TEXTURE_2D, img->tex);
+ }
+
+ if (img->dirty) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT,
+ img->tex_width, img->height, 0,
+ GL_BGRA_EXT, GL_UNSIGNED_BYTE, img->data);
+
+ }
+
+ glBindTexture(GL_TEXTURE_2D, img->tex);
+
+ GLfloat texcoord[] = {
+ 0.0f, 0.0f,
+ (GLfloat)img->width / (GLfloat)img->tex_width, 0.0f,
+ (GLfloat)img->width / (GLfloat)img->tex_width, 1.0f,
+ 0.0f, 1.0f,
+ };
+
+ GLfloat verts[] = {
+ x, y,
+ x + width, y,
+ x + width, y + height,
+ x, y + height
+ };
+
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texcoord);
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+
+ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
+
+ glDisableVertexAttribArray(1);
+ glDisableVertexAttribArray(0);
+}
+
+static void
+draw_output_border(struct weston_output *output)
+{
+ struct gl_output_state *go = get_output_state(output);
+ struct gl_renderer *gr = get_renderer(output->compositor);
+ struct gl_shader *shader = &gr->texture_shader_rgba;
+ int32_t full_width;
+
+ glDisable(GL_BLEND);
+ use_shader(gr, shader);
+
+ glUniformMatrix4fv(shader->proj_uniform,
+ 1, GL_FALSE, output->matrix.d);
+
+ glUniform1i(shader->tex_uniforms[0], 0);
+ glUniform1f(shader->alpha_uniform, 1);
+ glActiveTexture(GL_TEXTURE0);
+
+ full_width = output->width + output->border.left + output->border.right;
+ draw_output_border_texture(&go->borders[0],
+ -output->border.left, -output->border.top,
+ full_width, output->border.top);
+ draw_output_border_texture(&go->borders[1],
+ -output->border.left, 0,
+ output->border.left, output->height);
+ draw_output_border_texture(&go->borders[2],
+ output->width, 0,
+ output->border.right, output->height);
+ draw_output_border_texture(&go->borders[3],
+ -output->border.left, output->height,
+ full_width, output->border.bottom);
+}
static int
texture_border(struct weston_output *output)
@@ -648,7 +755,7 @@ texture_border(struct weston_output *output)
}
static void
-draw_border(struct weston_output *output)
+draw_global_border(struct weston_output *output)
{
struct weston_compositor *ec = output->compositor;
struct gl_renderer *gr = get_renderer(ec);
@@ -780,8 +887,11 @@ gl_renderer_repaint_output(struct weston_output *output,
pixman_region32_fini(&total_damage);
pixman_region32_fini(&buffer_damage);
- if (gr->border.texture)
- draw_border(output);
+ if (gr->border.texture) {
+ draw_global_border(output);
+ } else {
+ draw_output_border(output);
+ }
pixman_region32_copy(&output->previous_damage, output_damage);
wl_signal_emit(&output->frame_signal, output);
@@ -1432,6 +1542,21 @@ log_egl_config_info(EGLDisplay egldpy, EGLConfig eglconfig)
}
static void
+gl_renderer_output_set_border(struct weston_output *output,
+ enum gl_renderer_border_side side,
+ int32_t width, int32_t height,
+ int32_t tex_width, unsigned char *data)
+{
+ struct gl_output_state *go = get_output_state(output);
+
+ go->borders[side].width = width;
+ go->borders[side].height = height;
+ go->borders[side].tex_width = tex_width;
+ go->borders[side].data = data;
+ go->borders[side].dirty = 1;
+}
+
+static void
output_apply_border(struct weston_output *output, struct gl_renderer *gr)
{
output->border.top = gr->border.top;
@@ -1873,6 +1998,7 @@ WL_EXPORT struct gl_renderer_interface gl_renderer_interface = {
.output_create = gl_renderer_output_create,
.output_destroy = gl_renderer_output_destroy,
.output_surface = gl_renderer_output_surface,
+ .output_set_border = gl_renderer_output_set_border,
.set_border = gl_renderer_set_border,
.print_egl_error_state = gl_renderer_print_egl_error_state
};
diff --git a/src/gl-renderer.h b/src/gl-renderer.h
index 0342134..8a36c89 100644
--- a/src/gl-renderer.h
+++ b/src/gl-renderer.h
@@ -39,6 +39,13 @@ typedef intptr_t EGLNativeWindowType;
#endif
+enum gl_renderer_border_side {
+ GL_RENDERER_BORDER_TOP = 0,
+ GL_RENDERER_BORDER_LEFT = 1,
+ GL_RENDERER_BORDER_RIGHT = 2,
+ GL_RENDERER_BORDER_BOTTOM = 3,
+};
+
struct gl_renderer_interface {
const EGLint *opaque_attribs;
const EGLint *alpha_attribs;
@@ -57,6 +64,38 @@ struct gl_renderer_interface {
EGLSurface (*output_surface)(struct weston_output *output);
+ /* Sets the output border.
+ *
+ * The side specifies the side for which we are setting the border.
+ * The width and height are the width and height of the border.
+ * The tex_width patemeter specifies the width of the actual
+ * texture; this may be larger than width if the data is not
+ * tightly packed.
+ *
+ * The top and bottom textures will extend over the sides to the
+ * full width of the bordered window while. The right and left
+ * edges, however, will extend only to the top and bottom of the
+ * compositor surface. This is demonstrated by the picture below:
+ *
+ * +-----------------------+
+ * | TOP |
+ * +-+-------------------+-+
+ * | | | |
+ * |L| |R|
+ * |E| |I|
+ * |F| |G|
+ * |T| |H|
+ * | | |T|
+ * | | | |
+ * +-+-------------------+-+
+ * | BOTTOM |
+ * +-----------------------+
+ */
+ void (*output_set_border)(struct weston_output *output,
+ enum gl_renderer_border_side side,
+ int32_t width, int32_t height,
+ int32_t tex_width, unsigned char *data);
+
void (*set_border)(struct weston_compositor *ec,
int32_t width, int32_t height,
void *data, int32_t *edges);
--
1.8.3.1
More information about the wayland-devel
mailing list