[retro-gtk] Add RetroGLSLFilter
- From: Adrien Plazas <aplazas src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [retro-gtk] Add RetroGLSLFilter
- Date: Thu, 16 Nov 2017 06:54:46 +0000 (UTC)
commit 7ba901b9d33a9a25f90dee754fef773e6b552675
Author: Adrien Plazas <kekun plazas laposte net>
Date: Sun Oct 15 09:36:05 2017 +0200
Add RetroGLSLFilter
This will be used in the next commit to handle GLSL video filters.
retro-gtk/retro-glsl-filter.c | 333 +++++++++++++++++++++++++++++++++++++++++
retro-gtk/retro-glsl-filter.h | 39 +++++
2 files changed, 372 insertions(+), 0 deletions(-)
---
diff --git a/retro-gtk/retro-glsl-filter.c b/retro-gtk/retro-glsl-filter.c
new file mode 100644
index 0000000..1620b8c
--- /dev/null
+++ b/retro-gtk/retro-glsl-filter.c
@@ -0,0 +1,333 @@
+// This file is part of retro-gtk. License: GPL-3.0+.
+
+#include "retro-glsl-filter.h"
+
+struct _RetroGLSLFilter
+{
+ GObject parent_instance;
+ GBytes *vertex;
+ GBytes *fragment;
+ GLenum wrap;
+ GLenum filter;
+ GLuint vertex_shader;
+ GLuint fragment_shader;
+ GLuint program;
+};
+
+G_DEFINE_TYPE (RetroGLSLFilter, retro_glsl_filter, G_TYPE_OBJECT)
+
+#define GLSL_FILTER_GROUP "GLSL Filter"
+
+static const gchar *
+g_key_file_try_get_string (GKeyFile *key_file,
+ const gchar *group,
+ const gchar *key)
+{
+ const gchar *value;
+ GError *inner_error = NULL;
+
+ value = g_key_file_get_string (key_file, group, key, &inner_error);
+ if (G_UNLIKELY (inner_error != NULL)) {
+ g_debug ("%s", inner_error->message);
+ g_clear_error (&inner_error);
+
+ return NULL;
+ }
+
+ return value;
+}
+
+static GBytes *
+g_file_try_read_bytes (GFile *file)
+{
+ GFileInputStream *stream;
+ goffset size;
+ GBytes *bytes;
+ GError *inner_error = NULL;
+
+ stream = g_file_read (file, NULL, &inner_error);
+ if (G_UNLIKELY (inner_error != NULL)) {
+ g_debug ("%s", inner_error->message);
+ g_clear_error (&inner_error);
+
+ return NULL;
+ }
+
+ g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_END, NULL, &inner_error);
+ if (G_UNLIKELY (inner_error != NULL)) {
+ g_debug ("%s", inner_error->message);
+ g_clear_error (&inner_error);
+ g_object_unref (stream);
+
+ return NULL;
+ }
+
+ size = g_seekable_tell (G_SEEKABLE (stream));
+
+ g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, &inner_error);
+ if (G_UNLIKELY (inner_error != NULL)) {
+ g_debug ("%s", inner_error->message);
+ g_clear_error (&inner_error);
+ g_object_unref (stream);
+
+ return NULL;
+ }
+
+ bytes = g_input_stream_read_bytes (G_INPUT_STREAM (stream), size, NULL, &inner_error);
+ if (G_UNLIKELY (inner_error != NULL)) {
+ g_debug ("%s", inner_error->message);
+ g_clear_error (&inner_error);
+ g_object_unref (stream);
+
+ return NULL;
+ }
+
+ g_object_unref (stream);
+
+ return bytes;
+}
+
+static GBytes *
+g_file_try_read_child_bytes (GFile *parent,
+ const gchar *child_filename)
+{
+ GFile *file;
+ GBytes *bytes;
+
+ file = g_file_get_child (parent, child_filename);
+ bytes = g_file_try_read_bytes (file);
+ g_object_unref (file);
+
+ return bytes;
+}
+
+static GBytes *
+g_key_file_try_read_child_bytes (GKeyFile *key_file,
+ const gchar *group,
+ const gchar *key,
+ GFile *parent)
+{
+ const gchar *value;
+
+ value = g_key_file_try_get_string (key_file, group, key);
+ if (value == NULL)
+ return NULL;
+
+ return g_file_try_read_child_bytes (parent, value);
+}
+
+RetroGLSLFilter *
+retro_glsl_filter_new (const char *uri,
+ GError **error)
+{
+ RetroGLSLFilter *self;
+ GKeyFile *key_file;
+ GFile *file;
+ GFile *parent;
+ GFileInputStream *stream;
+ GBytes *bytes;
+ const gchar *value;
+ GError *inner_error = NULL;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ file = g_file_new_for_uri (uri);
+ stream = g_file_read (file, NULL, &inner_error);
+ if (G_UNLIKELY (inner_error != NULL)) {
+ g_propagate_error (error, inner_error);
+ g_object_unref (file);
+
+ return NULL;
+ }
+
+ bytes = g_input_stream_read_bytes (G_INPUT_STREAM (stream), 4096, NULL, error);
+ if (G_UNLIKELY (inner_error != NULL)) {
+ g_propagate_error (error, inner_error);
+ g_object_unref (file);
+ g_object_unref (stream);
+
+ return NULL;
+ }
+
+ g_object_unref (stream);
+
+ key_file = g_key_file_new ();
+ g_key_file_load_from_bytes (key_file, bytes, G_KEY_FILE_NONE, &inner_error);
+ if (G_UNLIKELY (inner_error != NULL)) {
+ g_propagate_error (error, inner_error);
+ g_object_unref (file);
+ g_bytes_unref (bytes);
+ g_key_file_unref (key_file);
+
+ return NULL;
+ }
+
+ g_bytes_unref (bytes);
+
+ self = g_object_new (RETRO_TYPE_GLSL_FILTER, NULL);
+
+ value = g_key_file_try_get_string (key_file, GLSL_FILTER_GROUP, "Filter");
+ if (g_strcmp0 (value, "Linear") == 0)
+ self->filter = GL_LINEAR;
+ else if (g_strcmp0 (value, "Nearest") == 0)
+ self->filter = GL_NEAREST;
+ else
+ self->filter = GL_LINEAR;
+
+ value = g_key_file_try_get_string (key_file, GLSL_FILTER_GROUP, "Wrap");
+ if (g_strcmp0 (value, "Border") == 0)
+ self->wrap = GL_CLAMP_TO_BORDER;
+ else if (g_strcmp0 (value, "Edge") == 0)
+ self->wrap = GL_CLAMP_TO_EDGE;
+ else
+ self->wrap = GL_CLAMP_TO_BORDER;
+
+ parent = g_file_get_parent (file);
+ g_object_unref (file);
+
+ self->vertex = g_key_file_try_read_child_bytes (key_file,
+ GLSL_FILTER_GROUP,
+ "Vertex",
+ parent);
+
+ if (self->vertex == NULL)
+ self->vertex = g_file_try_read_child_bytes (parent, "sharp.vs");
+
+ self->fragment = g_key_file_try_read_child_bytes (key_file,
+ GLSL_FILTER_GROUP,
+ "Fragment",
+ parent);
+
+ if (self->fragment == NULL)
+ self->fragment = g_file_try_read_child_bytes (parent, "sharp.fs");
+
+ g_object_unref (parent);
+ g_key_file_unref (key_file);
+
+ return self;
+}
+
+static void
+retro_glsl_filter_finalize (GObject *object)
+{
+ RetroGLSLFilter *self = (RetroGLSLFilter *) object;
+
+ if (self->vertex != NULL)
+ g_bytes_unref (self->vertex);
+ if (self->fragment != NULL)
+ g_bytes_unref (self->fragment);
+ glDeleteShader (self->vertex_shader);
+ glDeleteShader (self->fragment_shader);
+ glDeleteProgram (self->program);
+
+ G_OBJECT_CLASS (retro_glsl_filter_parent_class)->finalize (object);
+}
+
+static void
+retro_glsl_filter_class_init (RetroGLSLFilterClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = retro_glsl_filter_finalize;
+}
+
+static void
+retro_glsl_filter_init (RetroGLSLFilter *self)
+{
+}
+
+void
+retro_glsl_filter_apply_texture_params (RetroGLSLFilter *self)
+{
+ g_return_if_fail (RETRO_IS_GLSL_FILTER (self));
+
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, self->wrap);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, self->wrap);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, self->filter);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, self->filter);
+}
+
+static GLuint
+create_shader (GBytes *source_bytes,
+ GLenum shader_type)
+{
+ const gchar *source;
+ GLuint shader;
+
+ source = g_bytes_get_data (source_bytes, NULL);
+ shader = glCreateShader (shader_type);
+ glShaderSource (shader, 1, &source, NULL);
+ glCompileShader (shader);
+
+ return shader;
+}
+
+void
+retro_glsl_filter_prepare_program (RetroGLSLFilter *self)
+{
+ g_return_if_fail (RETRO_IS_GLSL_FILTER (self));
+
+ self->vertex_shader = create_shader (self->vertex, GL_VERTEX_SHADER);
+ self->fragment_shader = create_shader (self->fragment, GL_FRAGMENT_SHADER);
+
+ self->program = glCreateProgram();
+ glAttachShader (self->program, self->vertex_shader);
+ glAttachShader (self->program, self->fragment_shader);
+ glLinkProgram (self->program);
+}
+
+void
+retro_glsl_filter_use_program (RetroGLSLFilter *self)
+{
+ g_return_if_fail (RETRO_IS_GLSL_FILTER (self));
+
+ glUseProgram (self->program);
+}
+
+void
+retro_glsl_filter_set_attribute_pointer (RetroGLSLFilter *self,
+ const gchar *name,
+ GLint size,
+ GLenum type,
+ GLboolean normalized,
+ GLsizei stride,
+ const GLvoid *pointer)
+{
+ GLint location;
+
+ g_return_if_fail (RETRO_IS_GLSL_FILTER (self));
+
+ location = glGetAttribLocation (self->program, name);
+ glVertexAttribPointer (location, size, type, normalized, stride, pointer);
+ glEnableVertexAttribArray (location);
+}
+
+
+void
+retro_glsl_filter_set_uniform_1f (RetroGLSLFilter *self,
+ const gchar *name,
+ gfloat v0)
+{
+ GLint location;
+
+ g_return_if_fail (RETRO_IS_GLSL_FILTER (self));
+
+ location = glGetUniformLocation (self->program, name);
+ glUniform1f (location, v0);
+}
+
+void
+retro_glsl_filter_set_uniform_4f (RetroGLSLFilter *self,
+ const gchar *name,
+ gfloat v0,
+ gfloat v1,
+ gfloat v2,
+ gfloat v3)
+{
+ GLint location;
+
+ g_return_if_fail (RETRO_IS_GLSL_FILTER (self));
+
+ location = glGetUniformLocation (self->program, name);
+ glUniform4f (location, v0, v1, v2, v3);
+}
diff --git a/retro-gtk/retro-glsl-filter.h b/retro-gtk/retro-glsl-filter.h
new file mode 100644
index 0000000..a9d0e11
--- /dev/null
+++ b/retro-gtk/retro-glsl-filter.h
@@ -0,0 +1,39 @@
+#ifndef RETRO_GLSL_FILTER_H
+#define RETRO_GLSL_FILTER_H
+
+#include <epoxy/gl.h>
+#include <gio/gio.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define RETRO_TYPE_GLSL_FILTER (retro_glsl_filter_get_type())
+
+G_DECLARE_FINAL_TYPE (RetroGLSLFilter, retro_glsl_filter, RETRO, GLSL_FILTER, GObject)
+
+RetroGLSLFilter *retro_glsl_filter_new (const char *uri,
+ GError **error);
+void retro_glsl_filter_apply_texture_params (RetroGLSLFilter *self);
+void retro_glsl_filter_prepare_program (RetroGLSLFilter *self);
+void retro_glsl_filter_use_program (RetroGLSLFilter *self);
+void retro_glsl_filter_set_attribute_pointer (RetroGLSLFilter *self,
+ const gchar *name,
+ GLint size,
+ GLenum type,
+ GLboolean normalized,
+ GLsizei stride,
+ const GLvoid *pointer);
+void retro_glsl_filter_set_uniform_1f (RetroGLSLFilter *self,
+ const gchar *name,
+ gfloat v0);
+void retro_glsl_filter_set_uniform_4f (RetroGLSLFilter *self,
+ const gchar *name,
+ gfloat v0,
+ gfloat v1,
+ gfloat v2,
+ gfloat v3);
+
+G_END_DECLS
+
+#endif /* RETRO_GLSL_FILTER_H */
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]