/* GStreamer * Copyright (C) <1999> Erik Walthinsen * Copyright (C) <2016> Matthew Waters * * 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; }