| /* GStreamer |
| * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> |
| * Copyright (C) <2016> Matthew Waters <matthew@centricular.com> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include "gltestsrc.h" |
| |
| #define MAX_ATTRIBUTES 4 |
| |
| struct vts_color_struct |
| { |
| gfloat R, G, B; |
| }; |
| |
| struct XYZWRGB |
| { |
| gfloat X, Y, Z, W, R, G, B; |
| }; |
| |
| enum |
| { |
| COLOR_WHITE = 0, |
| COLOR_YELLOW, |
| COLOR_CYAN, |
| COLOR_GREEN, |
| COLOR_MAGENTA, |
| COLOR_RED, |
| COLOR_BLUE, |
| COLOR_BLACK, |
| COLOR_NEG_I, |
| COLOR_POS_Q, |
| COLOR_SUPER_BLACK, |
| COLOR_DARK_GREY |
| }; |
| |
| static const struct vts_color_struct vts_colors[] = { |
| /* 100% white */ |
| {1.0f, 1.0f, 1.0f}, |
| /* yellow */ |
| {1.0f, 1.0f, 0.0f}, |
| /* cyan */ |
| {0.0f, 1.0f, 1.0f}, |
| /* green */ |
| {0.0f, 1.0f, 0.0f}, |
| /* magenta */ |
| {1.0f, 0.0f, 1.0f}, |
| /* red */ |
| {1.0f, 0.0f, 0.0f}, |
| /* blue */ |
| {0.0f, 0.0f, 1.0f}, |
| /* black */ |
| {0.0f, 0.0f, 0.0f}, |
| /* -I */ |
| {0.0, 0.0f, 0.5f}, |
| /* +Q */ |
| {0.0f, 0.5, 1.0f}, |
| /* superblack */ |
| {0.0f, 0.0f, 0.0f}, |
| /* 7.421875% grey */ |
| {19. / 256.0f, 19. / 256.0f, 19. / 256.0}, |
| }; |
| |
| /* *INDENT-OFF* */ |
| static const GLfloat positions[] = { |
| -1.0, 1.0, 0.0, 1.0, |
| 1.0, 1.0, 0.0, 1.0, |
| 1.0, -1.0, 0.0, 1.0, |
| -1.0, -1.0, 0.0, 1.0, |
| }; |
| |
| static const GLushort indices_quad[] = { 0, 1, 2, 0, 2, 3 }; |
| /* *INDENT-ON* */ |
| |
| struct attribute |
| { |
| const gchar *name; |
| gint location; |
| guint n_elements; |
| GLenum element_type; |
| guint offset; /* in bytes */ |
| guint stride; /* in bytes */ |
| }; |
| |
| struct SrcShader |
| { |
| struct BaseSrcImpl base; |
| |
| GstGLShader *shader; |
| |
| guint vao; |
| guint vbo; |
| guint vbo_indices; |
| |
| guint n_attributes; |
| struct attribute attributes[MAX_ATTRIBUTES]; |
| |
| gconstpointer vertices; |
| gsize vertices_size; |
| const gushort *indices; |
| guint index_offset; |
| guint n_indices; |
| }; |
| |
| static void |
| _bind_buffer (struct SrcShader *src) |
| { |
| GstGLContext *context = src->base.context; |
| const GstGLFuncs *gl = context->gl_vtable; |
| gint i; |
| |
| gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, src->vbo_indices); |
| gl->BindBuffer (GL_ARRAY_BUFFER, src->vbo); |
| |
| /* Load the vertex position */ |
| for (i = 0; i < src->n_attributes; i++) { |
| struct attribute *attr = &src->attributes[i]; |
| |
| if (attr->location == -1) |
| attr->location = |
| gst_gl_shader_get_attribute_location (src->shader, attr->name); |
| |
| gl->VertexAttribPointer (attr->location, attr->n_elements, |
| attr->element_type, GL_FALSE, attr->stride, |
| (void *) (gintptr) attr->offset); |
| |
| gl->EnableVertexAttribArray (attr->location); |
| } |
| } |
| |
| static void |
| _unbind_buffer (struct SrcShader *src) |
| { |
| GstGLContext *context = src->base.context; |
| const GstGLFuncs *gl = context->gl_vtable; |
| gint i; |
| |
| gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0); |
| gl->BindBuffer (GL_ARRAY_BUFFER, 0); |
| |
| for (i = 0; i < src->n_attributes; i++) { |
| struct attribute *attr = &src->attributes[i]; |
| |
| gl->DisableVertexAttribArray (attr->location); |
| } |
| } |
| |
| static gboolean |
| _src_shader_init (gpointer impl, GstGLContext * context, GstVideoInfo * v_info) |
| { |
| struct SrcShader *src = impl; |
| const GstGLFuncs *gl = context->gl_vtable; |
| |
| src->base.context = context; |
| |
| if (!src->vbo) { |
| if (gl->GenVertexArrays) { |
| gl->GenVertexArrays (1, &src->vao); |
| gl->BindVertexArray (src->vao); |
| } |
| |
| gl->GenBuffers (1, &src->vbo); |
| gl->BindBuffer (GL_ARRAY_BUFFER, src->vbo); |
| gl->BufferData (GL_ARRAY_BUFFER, src->vertices_size, |
| src->vertices, GL_STATIC_DRAW); |
| |
| gl->GenBuffers (1, &src->vbo_indices); |
| gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, src->vbo_indices); |
| gl->BufferData (GL_ELEMENT_ARRAY_BUFFER, src->n_indices * sizeof (gushort), |
| src->indices, GL_STATIC_DRAW); |
| |
| if (gl->GenVertexArrays) { |
| _bind_buffer (src); |
| gl->BindVertexArray (0); |
| } |
| |
| gl->BindBuffer (GL_ARRAY_BUFFER, 0); |
| gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0); |
| } |
| |
| return TRUE; |
| } |
| |
| static gboolean |
| _src_shader_fill_bound_fbo (gpointer impl) |
| { |
| struct SrcShader *src = impl; |
| const GstGLFuncs *gl; |
| |
| g_return_val_if_fail (src->base.context, FALSE); |
| g_return_val_if_fail (src->shader, FALSE); |
| gl = src->base.context->gl_vtable; |
| |
| gst_gl_shader_use (src->shader); |
| |
| if (gl->GenVertexArrays) |
| gl->BindVertexArray (src->vao); |
| else |
| _bind_buffer (src); |
| |
| gl->DrawElements (GL_TRIANGLES, src->n_indices, GL_UNSIGNED_SHORT, |
| (gpointer) (gintptr) src->index_offset); |
| |
| if (gl->GenVertexArrays) |
| gl->BindVertexArray (0); |
| else |
| _unbind_buffer (src); |
| |
| gst_gl_context_clear_shader (src->base.context); |
| |
| return TRUE; |
| } |
| |
| static void |
| _src_shader_deinit (gpointer impl) |
| { |
| struct SrcShader *src = impl; |
| const GstGLFuncs *gl = src->base.context->gl_vtable; |
| |
| if (src->shader) |
| gst_object_unref (src->shader); |
| src->shader = NULL; |
| |
| if (src->vao) |
| gl->DeleteVertexArrays (1, &src->vao); |
| src->vao = 0; |
| |
| if (src->vbo) |
| gl->DeleteBuffers (1, &src->vbo); |
| src->vbo = 0; |
| |
| if (src->vbo_indices) |
| gl->DeleteBuffers (1, &src->vbo_indices); |
| src->vbo_indices = 0; |
| } |
| |
| /* *INDENT-OFF* */ |
| static const gchar *smpte_vertex_src = |
| "attribute vec4 position;\n" |
| "attribute vec4 a_color;\n" |
| "varying vec4 color;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = position;\n" |
| " color = a_color;\n" |
| "}"; |
| |
| static const gchar *smpte_fragment_src = |
| "#ifdef GL_ES\n" |
| "precision mediump float;\n" |
| "#endif\n" |
| "varying vec4 color;\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = color;\n" |
| "}"; |
| |
| static const gchar *snow_vertex_src = |
| "attribute vec4 position;\n" |
| "varying vec2 out_uv;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = position;\n" |
| " out_uv = position.xy;\n" |
| "}"; |
| |
| static const gchar *snow_fragment_src = |
| "#ifdef GL_ES\n" |
| "precision mediump float;\n" |
| "#endif\n" |
| "uniform float time;\n" |
| "varying vec2 out_uv;\n" |
| "\n" |
| "float rand(vec2 co){\n" |
| " return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453);\n" |
| "}\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = vec4(rand(time * out_uv));\n" |
| "}"; |
| /* *INDENT-ON* */ |
| |
| #define N_QUADS 21 |
| struct SrcSMPTE |
| { |
| struct SrcShader base; |
| |
| GstGLShader *snow_shader; |
| GstGLShader *color_shader; |
| gint attr_snow_position; |
| }; |
| |
| static gpointer |
| _src_smpte_new (GstGLTestSrc * test) |
| { |
| struct SrcSMPTE *src = g_new0 (struct SrcSMPTE, 1); |
| |
| src->base.base.src = test; |
| |
| return src; |
| } |
| |
| static gboolean |
| _src_smpte_init (gpointer impl, GstGLContext * context, GstVideoInfo * v_info) |
| { |
| struct SrcSMPTE *src = impl; |
| struct XYZWRGB *coord; |
| gushort *plane_indices; |
| GError *error = NULL; |
| int color_idx = 0; |
| int i; |
| |
| src->base.base.context = context; |
| |
| coord = g_new0 (struct XYZWRGB, N_QUADS * 4); |
| plane_indices = g_new0 (gushort, N_QUADS * 6); |
| |
| /* top row */ |
| for (int i = 0; i < 7; i++) { |
| coord[color_idx * 4 + 0].X = -1.0f + i * (2.0f / 7.0f); |
| coord[color_idx * 4 + 0].Y = 1.0f / 3.0f; |
| coord[color_idx * 4 + 1].X = -1.0f + (i + 1) * (2.0f / 7.0f); |
| coord[color_idx * 4 + 1].Y = 1.0f / 3.0f; |
| coord[color_idx * 4 + 2].X = -1.0f + (i + 1) * (2.0f / 7.0f); |
| coord[color_idx * 4 + 2].Y = -1.0f; |
| coord[color_idx * 4 + 3].X = -1.0f + i * (2.0f / 7.0f); |
| coord[color_idx * 4 + 3].Y = -1.0f; |
| color_idx++; |
| } |
| |
| /* middle row */ |
| for (int i = 0; i < 7; i++) { |
| coord[color_idx * 4 + 0].X = -1.0f + i * (2.0f / 7.0f); |
| coord[color_idx * 4 + 0].Y = 0.5f; |
| coord[color_idx * 4 + 1].X = -1.0f + (i + 1) * (2.0f / 7.0f); |
| coord[color_idx * 4 + 1].Y = 0.5f; |
| coord[color_idx * 4 + 2].X = -1.0f + (i + 1) * (2.0f / 7.0f); |
| coord[color_idx * 4 + 2].Y = 1.0f / 3.0f; |
| coord[color_idx * 4 + 3].X = -1.0f + i * (2.0f / 7.0f); |
| coord[color_idx * 4 + 3].Y = 1.0f / 3.0f; |
| color_idx++; |
| } |
| |
| /* bottom row, left three */ |
| for (int i = 0; i < 3; i++) { |
| coord[color_idx * 4 + 0].X = -1.0f + i / 3.0f; |
| coord[color_idx * 4 + 0].Y = 1.0f; |
| coord[color_idx * 4 + 1].X = -1.0f + (i + 1) / 3.0f; |
| coord[color_idx * 4 + 1].Y = 1.0f; |
| coord[color_idx * 4 + 2].X = -1.0f + (i + 1) / 3.0f; |
| coord[color_idx * 4 + 2].Y = 0.5f; |
| coord[color_idx * 4 + 3].X = -1.0f + i / 3.0f; |
| coord[color_idx * 4 + 3].Y = 0.5f; |
| color_idx++; |
| } |
| |
| /* bottom row, middle three (the blacks) */ |
| for (int i = 0; i < 3; i++) { |
| coord[color_idx * 4 + 0].X = i / 6.0f; |
| coord[color_idx * 4 + 0].Y = 1.0f; |
| coord[color_idx * 4 + 1].X = (i + 1) / 6.0f; |
| coord[color_idx * 4 + 1].Y = 1.0f; |
| coord[color_idx * 4 + 2].X = (i + 1) / 6.0f; |
| coord[color_idx * 4 + 2].Y = 0.5f; |
| coord[color_idx * 4 + 3].X = i / 6.0f; |
| coord[color_idx * 4 + 3].Y = 0.5f; |
| color_idx++; |
| } |
| |
| g_assert (color_idx < N_QUADS); |
| |
| for (i = 0; i < N_QUADS - 1; i++) { |
| int j, k; |
| if (i < 7) { |
| k = i; |
| } else if ((i - 7) & 1) { |
| k = COLOR_BLACK; |
| } else { |
| k = 13 - i; |
| } |
| |
| if (i == 14) { |
| k = COLOR_NEG_I; |
| } else if (i == 15) { |
| k = COLOR_WHITE; |
| } else if (i == 16) { |
| k = COLOR_POS_Q; |
| } else if (i == 17) { |
| k = COLOR_SUPER_BLACK; |
| } else if (i == 18) { |
| k = COLOR_BLACK; |
| } else if (i == 19) { |
| k = COLOR_DARK_GREY; |
| } |
| |
| for (j = 0; j < 4; j++) { |
| coord[i * 4 + j].Z = 0.0f; |
| coord[i * 4 + j].W = 1.0f; |
| coord[i * 4 + j].R = vts_colors[k].R; |
| coord[i * 4 + j].G = vts_colors[k].G; |
| coord[i * 4 + j].B = vts_colors[k].B; |
| } |
| |
| for (j = 0; j < 6; j++) |
| plane_indices[i * 6 + j] = i * 4 + indices_quad[j]; |
| } |
| |
| /* snow */ |
| coord[color_idx * 4 + 0].X = 0.5f; |
| coord[color_idx * 4 + 0].Y = 1.0f; |
| coord[color_idx * 4 + 0].Z = 0.0f; |
| coord[color_idx * 4 + 0].W = 1.0f; |
| coord[color_idx * 4 + 1].X = 1.0f; |
| coord[color_idx * 4 + 1].Y = 1.0f; |
| coord[color_idx * 4 + 1].Z = 0.0f; |
| coord[color_idx * 4 + 1].W = 1.0f; |
| coord[color_idx * 4 + 2].X = 1.0f; |
| coord[color_idx * 4 + 2].Y = 0.5f; |
| coord[color_idx * 4 + 2].Z = 0.0f; |
| coord[color_idx * 4 + 2].W = 1.0f; |
| coord[color_idx * 4 + 3].X = 0.5f; |
| coord[color_idx * 4 + 3].Y = 0.5f; |
| coord[color_idx * 4 + 3].Z = 0.0f; |
| coord[color_idx * 4 + 3].W = 1.0f; |
| for (i = 0; i < 6; i++) |
| plane_indices[color_idx * 6 + i] = color_idx * 4 + indices_quad[i]; |
| color_idx++; |
| |
| if (src->color_shader) |
| gst_object_unref (src->color_shader); |
| src->color_shader = gst_gl_shader_new_link_with_stages (context, &error, |
| gst_glsl_stage_new_with_string (context, GL_VERTEX_SHADER, |
| GST_GLSL_VERSION_NONE, |
| GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, |
| smpte_vertex_src), |
| gst_glsl_stage_new_with_string (context, GL_FRAGMENT_SHADER, |
| GST_GLSL_VERSION_NONE, |
| GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, |
| smpte_fragment_src), NULL); |
| if (!src->color_shader) { |
| GST_ERROR_OBJECT (src->base.base.src, "%s", error->message); |
| return FALSE; |
| } |
| |
| if (src->snow_shader) |
| gst_object_unref (src->snow_shader); |
| src->snow_shader = gst_gl_shader_new_link_with_stages (context, &error, |
| gst_glsl_stage_new_with_string (context, GL_VERTEX_SHADER, |
| GST_GLSL_VERSION_NONE, |
| GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, |
| snow_vertex_src), |
| gst_glsl_stage_new_with_string (context, GL_FRAGMENT_SHADER, |
| GST_GLSL_VERSION_NONE, |
| GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, |
| snow_fragment_src), NULL); |
| if (!src->snow_shader) { |
| GST_ERROR_OBJECT (src->base.base.src, "%s", error->message); |
| return FALSE; |
| } |
| |
| src->attr_snow_position = -1; |
| |
| src->base.n_attributes = 2; |
| |
| src->base.attributes[0].name = "position"; |
| src->base.attributes[0].location = -1; |
| src->base.attributes[0].n_elements = 4; |
| src->base.attributes[0].element_type = GL_FLOAT; |
| src->base.attributes[0].offset = 0; |
| src->base.attributes[0].stride = sizeof (struct XYZWRGB); |
| |
| src->base.attributes[1].name = "a_color"; |
| src->base.attributes[1].location = -1; |
| src->base.attributes[1].n_elements = 3; |
| src->base.attributes[1].element_type = GL_FLOAT; |
| src->base.attributes[1].offset = 4 * sizeof (gfloat); |
| src->base.attributes[1].stride = sizeof (struct XYZWRGB); |
| |
| if (src->base.shader) |
| gst_object_unref (src->base.shader); |
| src->base.shader = gst_object_ref (src->color_shader); |
| src->base.vertices = (gfloat *) coord; |
| src->base.vertices_size = sizeof (struct XYZWRGB) * N_QUADS * 4; |
| src->base.indices = plane_indices; |
| src->base.n_indices = N_QUADS * 6; |
| |
| return _src_shader_init (impl, context, v_info); |
| } |
| |
| static gboolean |
| _src_smpte_fill_bound_fbo (gpointer impl) |
| { |
| struct SrcSMPTE *src = impl; |
| gint attr_color_position = -1; |
| |
| src->base.n_attributes = 2; |
| if (src->base.shader) |
| gst_object_unref (src->base.shader); |
| src->base.shader = gst_object_ref (src->color_shader); |
| src->base.n_indices = (N_QUADS - 1) * 6; |
| src->base.index_offset = 0; |
| if (!_src_shader_fill_bound_fbo (impl)) |
| return FALSE; |
| attr_color_position = src->base.attributes[0].location; |
| |
| src->base.attributes[0].location = src->attr_snow_position; |
| src->base.n_attributes = 1; |
| if (src->base.shader) |
| gst_object_unref (src->base.shader); |
| src->base.shader = gst_object_ref (src->snow_shader); |
| src->base.n_indices = 6; |
| src->base.index_offset = (N_QUADS - 1) * 6 * sizeof (gushort); |
| gst_gl_shader_use (src->snow_shader); |
| gst_gl_shader_set_uniform_1f (src->snow_shader, "time", |
| (gfloat) src->base.base.src->running_time / GST_SECOND); |
| if (!_src_shader_fill_bound_fbo (impl)) |
| return FALSE; |
| src->attr_snow_position = src->base.attributes[0].location; |
| src->base.attributes[0].location = attr_color_position; |
| |
| return TRUE; |
| } |
| |
| static void |
| _src_smpte_free (gpointer impl) |
| { |
| struct SrcSMPTE *src = impl; |
| |
| if (!impl) |
| return; |
| |
| _src_shader_deinit (impl); |
| |
| g_free ((gpointer) src->base.vertices); |
| g_free ((gpointer) src->base.indices); |
| |
| if (src->snow_shader) |
| gst_object_unref (src->snow_shader); |
| if (src->color_shader) |
| gst_object_unref (src->color_shader); |
| |
| g_free (impl); |
| } |
| |
| static const struct SrcFuncs src_smpte = { |
| GST_GL_TEST_SRC_SMPTE, |
| _src_smpte_new, |
| _src_smpte_init, |
| _src_smpte_fill_bound_fbo, |
| _src_smpte_free, |
| }; |
| |
| #undef N_QUADS |
| |
| struct SrcUniColor |
| { |
| struct BaseSrcImpl base; |
| |
| struct vts_color_struct color; |
| }; |
| |
| static gpointer |
| _src_uni_color_new (GstGLTestSrc * test) |
| { |
| struct SrcUniColor *src = g_new0 (struct SrcUniColor, 1); |
| |
| src->base.src = test; |
| |
| return src; |
| } |
| |
| static gboolean |
| _src_uni_color_init (gpointer impl, GstGLContext * context, |
| GstVideoInfo * v_info) |
| { |
| struct SrcUniColor *src = impl; |
| |
| src->base.context = context; |
| src->base.v_info = *v_info; |
| |
| return TRUE; |
| } |
| |
| static gboolean |
| _src_uni_color_fill_bound_fbo (gpointer impl) |
| { |
| struct SrcUniColor *src = impl; |
| const GstGLFuncs *gl = src->base.context->gl_vtable; |
| |
| gl->ClearColor (src->color.R, src->color.G, src->color.B, 1.0f); |
| gl->Clear (GL_COLOR_BUFFER_BIT); |
| |
| return TRUE; |
| } |
| |
| static void |
| _src_uni_color_free (gpointer impl) |
| { |
| g_free (impl); |
| } |
| |
| #define SRC_UNICOLOR(name, cap_name) \ |
| static gpointer \ |
| G_PASTE(G_PASTE(_src_unicolor_,name),_new) (GstGLTestSrc * test) \ |
| { \ |
| struct SrcUniColor *src = _src_uni_color_new (test); \ |
| src->color = vts_colors[G_PASTE(COLOR_,cap_name)]; \ |
| return src; \ |
| } \ |
| static const struct SrcFuncs G_PASTE (src_,name) = { \ |
| G_PASTE(GST_GL_TEST_SRC_,cap_name), \ |
| G_PASTE(G_PASTE(_src_unicolor_,name),_new), \ |
| _src_uni_color_init, \ |
| _src_uni_color_fill_bound_fbo, \ |
| _src_uni_color_free, \ |
| } |
| |
| SRC_UNICOLOR (white, WHITE); |
| SRC_UNICOLOR (black, BLACK); |
| SRC_UNICOLOR (red, RED); |
| SRC_UNICOLOR (green, GREEN); |
| SRC_UNICOLOR (blue, BLUE); |
| |
| static gpointer |
| _src_blink_new (GstGLTestSrc * test) |
| { |
| struct SrcUniColor *src = _src_uni_color_new (test); |
| |
| src->color = vts_colors[COLOR_WHITE]; |
| |
| return src; |
| } |
| |
| static gboolean |
| _src_blink_fill_bound_fbo (gpointer impl) |
| { |
| struct SrcUniColor *src = impl; |
| |
| if (src->color.R > 0.5) { |
| src->color = vts_colors[COLOR_BLACK]; |
| } else { |
| src->color = vts_colors[COLOR_WHITE]; |
| } |
| |
| return _src_uni_color_fill_bound_fbo (impl); |
| } |
| |
| static const struct SrcFuncs src_blink = { |
| GST_GL_TEST_SRC_BLINK, |
| _src_blink_new, |
| _src_uni_color_init, |
| _src_blink_fill_bound_fbo, |
| _src_uni_color_free, |
| }; |
| |
| /* *INDENT-OFF* */ |
| static const gchar *checkers_vertex_src = "attribute vec4 position;\n" |
| "varying vec2 uv;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = position;\n" |
| /* RPi gives incorrect results for positive uv (plus it makes us start on |
| * the right pixel color i.e. red) */ |
| " uv = position.xy - 1.0;\n" |
| "}"; |
| |
| static const gchar *checkers_fragment_src = |
| "#ifdef GL_ES\n" |
| "precision mediump float;\n" |
| "#endif\n" |
| "uniform float checker_width;\n" |
| "uniform float width;\n" |
| "uniform float height;\n" |
| "varying vec2 uv;\n" |
| "void main()\n" |
| "{\n" |
| " vec2 xy_mod = floor (0.5 * uv * vec2(width, height) / (checker_width));\n" |
| " float result = mod (xy_mod.x + xy_mod.y, 2.0);\n" |
| " gl_FragColor.r = step (result, 0.5);\n" |
| " gl_FragColor.g = 1.0 - gl_FragColor.r;\n" |
| " gl_FragColor.ba = vec2(0.0, 1.0);\n" |
| "}"; |
| /* *INDENT-ON* */ |
| |
| struct SrcCheckers |
| { |
| struct SrcShader base; |
| |
| guint checker_width; |
| }; |
| |
| static gboolean |
| _src_checkers_init (gpointer impl, GstGLContext * context, |
| GstVideoInfo * v_info) |
| { |
| struct SrcCheckers *src = impl; |
| GError *error = NULL; |
| |
| src->base.base.context = context; |
| |
| if (src->base.shader) |
| gst_object_unref (src->base.shader); |
| src->base.shader = gst_gl_shader_new_link_with_stages (context, &error, |
| gst_glsl_stage_new_with_string (context, GL_VERTEX_SHADER, |
| GST_GLSL_VERSION_NONE, |
| GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, |
| checkers_vertex_src), |
| gst_glsl_stage_new_with_string (context, GL_FRAGMENT_SHADER, |
| GST_GLSL_VERSION_NONE, |
| GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, |
| checkers_fragment_src), NULL); |
| if (!src->base.shader) { |
| GST_ERROR_OBJECT (src->base.base.src, "%s", error->message); |
| return FALSE; |
| } |
| |
| src->base.n_attributes = 1; |
| |
| src->base.attributes[0].name = "position"; |
| src->base.attributes[0].location = -1; |
| src->base.attributes[0].n_elements = 4; |
| src->base.attributes[0].element_type = GL_FLOAT; |
| src->base.attributes[0].offset = 0; |
| src->base.attributes[0].stride = 4 * sizeof (gfloat); |
| |
| src->base.vertices = positions; |
| src->base.vertices_size = sizeof (positions); |
| src->base.indices = indices_quad; |
| src->base.n_indices = 6; |
| |
| gst_gl_shader_use (src->base.shader); |
| gst_gl_shader_set_uniform_1f (src->base.shader, "checker_width", |
| src->checker_width); |
| gst_gl_shader_set_uniform_1f (src->base.shader, "width", |
| (gfloat) GST_VIDEO_INFO_WIDTH (v_info)); |
| gst_gl_shader_set_uniform_1f (src->base.shader, "height", |
| (gfloat) GST_VIDEO_INFO_HEIGHT (v_info)); |
| gst_gl_context_clear_shader (src->base.base.context); |
| |
| return _src_shader_init (impl, context, v_info); |
| } |
| |
| static void |
| _src_checkers_free (gpointer impl) |
| { |
| struct SrcCheckers *src = impl; |
| |
| if (!src) |
| return; |
| |
| _src_shader_deinit (impl); |
| |
| g_free (impl); |
| } |
| |
| static gpointer |
| _src_checkers_new (GstGLTestSrc * test) |
| { |
| struct SrcCheckers *src = g_new0 (struct SrcCheckers, 1); |
| |
| src->base.base.src = test; |
| |
| return src; |
| } |
| |
| #define SRC_CHECKERS(spacing) \ |
| static gpointer \ |
| G_PASTE(G_PASTE(_src_checkers,spacing),_new) (GstGLTestSrc * test) \ |
| { \ |
| struct SrcCheckers *src = _src_checkers_new (test); \ |
| src->checker_width = spacing; \ |
| return src; \ |
| } \ |
| static const struct SrcFuncs G_PASTE(src_checkers,spacing) = { \ |
| G_PASTE(GST_GL_TEST_SRC_CHECKERS,spacing), \ |
| G_PASTE(G_PASTE(_src_checkers,spacing),_new), \ |
| _src_checkers_init, \ |
| _src_shader_fill_bound_fbo, \ |
| _src_checkers_free, \ |
| } |
| |
| SRC_CHECKERS (1); |
| SRC_CHECKERS (2); |
| SRC_CHECKERS (4); |
| SRC_CHECKERS (8); |
| |
| static gboolean |
| _src_snow_init (gpointer impl, GstGLContext * context, GstVideoInfo * v_info) |
| { |
| struct SrcShader *src = impl; |
| GError *error = NULL; |
| |
| src->base.context = context; |
| |
| if (src->shader) |
| gst_object_unref (src->shader); |
| src->shader = gst_gl_shader_new_link_with_stages (context, &error, |
| gst_glsl_stage_new_with_string (context, GL_VERTEX_SHADER, |
| GST_GLSL_VERSION_NONE, |
| GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, |
| snow_vertex_src), |
| gst_glsl_stage_new_with_string (context, GL_FRAGMENT_SHADER, |
| GST_GLSL_VERSION_NONE, |
| GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, |
| snow_fragment_src), NULL); |
| if (!src->shader) { |
| GST_ERROR_OBJECT (src->base.src, "%s", error->message); |
| return FALSE; |
| } |
| |
| src->n_attributes = 1; |
| |
| src->attributes[0].name = "position"; |
| src->attributes[0].location = -1; |
| src->attributes[0].n_elements = 4; |
| src->attributes[0].element_type = GL_FLOAT; |
| src->attributes[0].offset = 0; |
| src->attributes[0].stride = 4 * sizeof (gfloat); |
| |
| src->vertices = positions; |
| src->vertices_size = sizeof (positions); |
| src->indices = indices_quad; |
| src->n_indices = 6; |
| |
| return _src_shader_init (impl, context, v_info); |
| } |
| |
| static gboolean |
| _src_snow_fill_bound_fbo (gpointer impl) |
| { |
| struct SrcShader *src = impl; |
| |
| g_return_val_if_fail (src->base.context, FALSE); |
| g_return_val_if_fail (src->shader, FALSE); |
| |
| gst_gl_shader_use (src->shader); |
| gst_gl_shader_set_uniform_1f (src->shader, "time", |
| (gfloat) src->base.src->running_time / GST_SECOND); |
| |
| return _src_shader_fill_bound_fbo (impl); |
| } |
| |
| static void |
| _src_snow_free (gpointer impl) |
| { |
| struct SrcShader *src = impl; |
| |
| if (!src) |
| return; |
| |
| _src_shader_deinit (impl); |
| |
| g_free (impl); |
| } |
| |
| static gpointer |
| _src_snow_new (GstGLTestSrc * test) |
| { |
| struct SrcShader *src = g_new0 (struct SrcShader, 1); |
| |
| src->base.src = test; |
| |
| return src; |
| } |
| |
| static const struct SrcFuncs src_snow = { |
| GST_GL_TEST_SRC_SNOW, |
| _src_snow_new, |
| _src_snow_init, |
| _src_snow_fill_bound_fbo, |
| _src_snow_free, |
| }; |
| |
| /* *INDENT-OFF* */ |
| static const gchar *mandelbrot_vertex_src = "attribute vec4 position;\n" |
| "uniform float aspect_ratio;\n" |
| "varying vec2 fractal_position;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = position;\n" |
| " fractal_position = vec2(position.y * 0.5 - 0.3, aspect_ratio * position.x * 0.5);\n" |
| " fractal_position *= 2.5;\n" |
| "}"; |
| |
| static const gchar *mandelbrot_fragment_src = |
| "#ifdef GL_ES\n" |
| "precision mediump float;\n" |
| "#endif\n" |
| "uniform float time;\n" |
| "varying vec2 fractal_position;\n" |
| "const vec4 K = vec4(1.0, 0.66, 0.33, 3.0);\n" |
| "vec4 hsv_to_rgb(float hue, float saturation, float value) {\n" |
| " vec4 p = abs(fract(vec4(hue) + K) * 6.0 - K.wwww);\n" |
| " return value * mix(K.xxxx, clamp(p - K.xxxx, 0.0, 1.0), saturation);\n" |
| "}\n" |
| "vec4 i_to_rgb(int i) {\n" |
| " float hue = float(i) / 100.0 + sin(time);\n" |
| " return hsv_to_rgb(hue, 0.5, 0.8);\n" |
| "}\n" |
| "vec2 pow_2_complex(vec2 c) {\n" |
| " return vec2(c.x*c.x - c.y*c.y, 2.0 * c.x * c.y);\n" |
| "}\n" |
| "vec2 mandelbrot(vec2 c, vec2 c0) {\n" |
| " return pow_2_complex(c) + c0;\n" |
| "}\n" |
| "vec4 iterate_pixel(vec2 position) {\n" |
| " vec2 c = vec2(0);\n" |
| " for (int i=0; i < 20; i++) {\n" |
| " if (c.x*c.x + c.y*c.y > 2.0*2.0)\n" |
| " return i_to_rgb(i);\n" |
| " c = mandelbrot(c, position);\n" |
| " }\n" |
| " return vec4(0, 0, 0, 1);\n" |
| "}\n" |
| "void main() {\n" |
| " gl_FragColor = iterate_pixel(fractal_position);\n" |
| "}"; |
| /* *INDENT-ON* */ |
| |
| static gboolean |
| _src_mandelbrot_init (gpointer impl, GstGLContext * context, |
| GstVideoInfo * v_info) |
| { |
| struct SrcShader *src = impl; |
| GError *error = NULL; |
| |
| src->base.context = context; |
| |
| if (src->shader) |
| gst_object_unref (src->shader); |
| src->shader = gst_gl_shader_new_link_with_stages (context, &error, |
| gst_glsl_stage_new_with_string (context, GL_VERTEX_SHADER, |
| GST_GLSL_VERSION_NONE, |
| GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, |
| mandelbrot_vertex_src), |
| gst_glsl_stage_new_with_string (context, GL_FRAGMENT_SHADER, |
| GST_GLSL_VERSION_NONE, |
| GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, |
| mandelbrot_fragment_src), NULL); |
| if (!src->shader) { |
| GST_ERROR_OBJECT (src->base.src, "%s", error->message); |
| return FALSE; |
| } |
| |
| src->n_attributes = 1; |
| |
| src->attributes[0].name = "position"; |
| src->attributes[0].location = -1; |
| src->attributes[0].n_elements = 4; |
| src->attributes[0].element_type = GL_FLOAT; |
| src->attributes[0].offset = 0; |
| src->attributes[0].stride = 4 * sizeof (gfloat); |
| |
| src->vertices = positions; |
| src->vertices_size = sizeof (positions); |
| src->indices = indices_quad; |
| src->n_indices = 6; |
| |
| gst_gl_shader_use (src->shader); |
| gst_gl_shader_set_uniform_1f (src->shader, "aspect_ratio", |
| (gfloat) GST_VIDEO_INFO_WIDTH (v_info) / |
| (gfloat) GST_VIDEO_INFO_HEIGHT (v_info)); |
| gst_gl_context_clear_shader (src->base.context); |
| |
| return _src_shader_init (impl, context, v_info); |
| } |
| |
| static gboolean |
| _src_mandelbrot_fill_bound_fbo (gpointer impl) |
| { |
| struct SrcShader *src = impl; |
| |
| g_return_val_if_fail (src->base.context, FALSE); |
| g_return_val_if_fail (src->shader, FALSE); |
| |
| gst_gl_shader_use (src->shader); |
| gst_gl_shader_set_uniform_1f (src->shader, "time", |
| (gfloat) src->base.src->running_time / GST_SECOND); |
| |
| return _src_shader_fill_bound_fbo (impl); |
| } |
| |
| static void |
| _src_mandelbrot_free (gpointer impl) |
| { |
| struct SrcShader *src = impl; |
| |
| if (!src) |
| return; |
| |
| _src_shader_deinit (impl); |
| |
| g_free (impl); |
| } |
| |
| static gpointer |
| _src_mandelbrot_new (GstGLTestSrc * test) |
| { |
| struct SrcShader *src = g_new0 (struct SrcShader, 1); |
| |
| src->base.src = test; |
| |
| return src; |
| } |
| |
| static const struct SrcFuncs src_mandelbrot = { |
| GST_GL_TEST_SRC_MANDELBROT, |
| _src_mandelbrot_new, |
| _src_mandelbrot_init, |
| _src_mandelbrot_fill_bound_fbo, |
| _src_mandelbrot_free, |
| }; |
| |
| /* *INDENT-OFF* */ |
| static const gchar *circular_vertex_src = |
| "attribute vec4 position;\n" |
| "varying vec2 uv;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = position;\n" |
| " uv = position.xy;\n" |
| "}"; |
| |
| static const gchar *circular_fragment_src = |
| "#ifdef GL_ES\n" |
| "precision mediump float;\n" |
| "#endif\n" |
| "uniform float aspect_ratio;\n" |
| "varying vec2 uv;\n" |
| "#define PI 3.14159265\n" |
| "void main() {\n" |
| " float dist = 0.5 * sqrt(uv.x * uv.x + uv.y / aspect_ratio * uv.y / aspect_ratio);\n" |
| " float seg = floor(dist * 16.0);\n" |
| " if (seg <= 0.0 || seg >= 8.0) {\n" |
| " gl_FragColor = vec4(vec3(0.0), 1.0);\n" |
| " } else {\n" |
| " float d = floor (256.0 * dist * 200.0 * pow (2.0, - (seg - 1.0) / 4.0) + 0.5) / 128.0;\n" |
| " gl_FragColor = vec4 (vec3(sin (d * PI) * 0.5 + 0.5), 1.0);\n" |
| " }\n" |
| "}"; |
| /* *INDENT-ON* */ |
| |
| static gboolean |
| _src_circular_init (gpointer impl, GstGLContext * context, |
| GstVideoInfo * v_info) |
| { |
| struct SrcShader *src = impl; |
| GError *error = NULL; |
| |
| src->base.context = context; |
| |
| if (src->shader) |
| gst_object_unref (src->shader); |
| src->shader = gst_gl_shader_new_link_with_stages (context, &error, |
| gst_glsl_stage_new_with_string (context, GL_VERTEX_SHADER, |
| GST_GLSL_VERSION_NONE, |
| GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, |
| circular_vertex_src), |
| gst_glsl_stage_new_with_string (context, GL_FRAGMENT_SHADER, |
| GST_GLSL_VERSION_NONE, |
| GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, |
| circular_fragment_src), NULL); |
| if (!src->shader) { |
| GST_ERROR_OBJECT (src->base.src, "%s", error->message); |
| return FALSE; |
| } |
| |
| src->n_attributes = 1; |
| |
| src->attributes[0].name = "position"; |
| src->attributes[0].location = -1; |
| src->attributes[0].n_elements = 4; |
| src->attributes[0].element_type = GL_FLOAT; |
| src->attributes[0].offset = 0; |
| src->attributes[0].stride = 4 * sizeof (gfloat); |
| |
| src->vertices = positions; |
| src->vertices_size = sizeof (positions); |
| src->indices = indices_quad; |
| src->n_indices = 6; |
| |
| gst_gl_shader_use (src->shader); |
| gst_gl_shader_set_uniform_1f (src->shader, "aspect_ratio", |
| (gfloat) GST_VIDEO_INFO_WIDTH (v_info) / |
| (gfloat) GST_VIDEO_INFO_HEIGHT (v_info)); |
| gst_gl_context_clear_shader (src->base.context); |
| |
| return _src_shader_init (impl, context, v_info); |
| } |
| |
| static void |
| _src_circular_free (gpointer impl) |
| { |
| struct SrcShader *src = impl; |
| |
| if (!src) |
| return; |
| |
| _src_shader_deinit (impl); |
| |
| g_free (impl); |
| } |
| |
| static gpointer |
| _src_circular_new (GstGLTestSrc * test) |
| { |
| struct SrcShader *src = g_new0 (struct SrcShader, 1); |
| |
| src->base.src = test; |
| |
| return src; |
| } |
| |
| static const struct SrcFuncs src_circular = { |
| GST_GL_TEST_SRC_CIRCULAR, |
| _src_circular_new, |
| _src_circular_init, |
| _src_mandelbrot_fill_bound_fbo, |
| _src_circular_free, |
| }; |
| |
| static const struct SrcFuncs *src_impls[] = { |
| &src_smpte, |
| &src_snow, |
| &src_black, |
| &src_white, |
| &src_red, |
| &src_green, |
| &src_blue, |
| &src_checkers1, |
| &src_checkers2, |
| &src_checkers4, |
| &src_checkers8, |
| &src_circular, |
| &src_blink, |
| &src_mandelbrot, |
| }; |
| |
| const struct SrcFuncs * |
| gst_gl_test_src_get_src_funcs_for_pattern (GstGLTestSrcPattern pattern) |
| { |
| gint i; |
| |
| for (i = 0; i < G_N_ELEMENTS (src_impls); i++) { |
| if (src_impls[i]->pattern == pattern) |
| return src_impls[i]; |
| } |
| |
| return NULL; |
| } |