gtk2/demos/gtk-demo/shadertoy.c
Alexander Larsson 0092a08dfc gtk-demo: Add shadertoy demo
This adds a small demo of using OpenGL shaders, it renders a quad
over the entire widget with a custom fragment shader. The coordinates
and the uniform names are compatible with the ones on shadertoy.com
(although some features, like texture inputs are missing currently).

The default shader in the demo is
https://www.shadertoy.com/view/wsjBD3 which is CC0, so it is
redistributable by Gtk+ (most other shaders are CC-BY-NC-SA which
isn't obviously compatible). I also added a set of buttons loading
a few other CC0 shaders I found.
2020-09-08 09:56:25 +02:00

192 lines
5.5 KiB
C

/* OpenGL/Shadertoy
*
* Generate pixels using a custom fragment shader.
*
* The names of the uniforms are compatible with the shaders on shadertoy.com, so
* many of the shaders there work here too.
*/
#include <math.h>
#include <gtk/gtk.h>
#include <epoxy/gl.h>
#include "gtkshadertoy.h"
static GtkWidget *demo_window = NULL;
static GtkWidget *shadertoy = NULL;
static GtkTextBuffer *textbuffer = NULL;
static void
run (void)
{
GtkTextIter start, end;
char *text;
gtk_text_buffer_get_bounds (textbuffer, &start, &end);
text = gtk_text_buffer_get_text (textbuffer, &start, &end, FALSE);
gtk_shadertoy_set_image_shader (GTK_SHADERTOY (shadertoy), text);
g_free (text);
}
static void
run_clicked_cb (GtkWidget *button,
gpointer user_data)
{
run ();
}
static void
load_clicked_cb (GtkWidget *button,
gpointer user_data)
{
const char *path = user_data;
GBytes *initial_shader;
initial_shader = g_resources_lookup_data (path, 0, NULL);
gtk_text_buffer_set_text (textbuffer, g_bytes_get_data (initial_shader, NULL), -1);
g_bytes_unref (initial_shader);
run ();
}
static void
clear_clicked_cb (GtkWidget *button,
gpointer user_data)
{
gtk_text_buffer_set_text (textbuffer, "", 0);
}
static void
close_window (GtkWidget *widget)
{
/* Reset the state */
demo_window = NULL;
shadertoy = NULL;
textbuffer = NULL;
}
static GtkWidget *
new_shadertoy (const char *path)
{
GBytes *shader;
GtkWidget *toy;
toy = gtk_shadertoy_new ();
shader = g_resources_lookup_data (path, 0, NULL);
gtk_shadertoy_set_image_shader (GTK_SHADERTOY (toy),
g_bytes_get_data (shader, NULL));
g_bytes_unref (shader);
return toy;
}
static GtkWidget *
new_button (const char *path)
{
GtkWidget *button, *toy;
button = gtk_button_new ();
g_signal_connect (button, "clicked", G_CALLBACK (load_clicked_cb), (char *)path);
toy = new_shadertoy (path);
gtk_widget_set_size_request (toy, 64, 36);
gtk_button_set_child (GTK_BUTTON (button), toy);
return button;
}
static GtkWidget *
create_shadertoy_window (GtkWidget *do_widget)
{
GtkWidget *window, *box, *hbox, *button, *textview, *sw, *aspect, *centerbox;
window = gtk_window_new ();
gtk_window_set_display (GTK_WINDOW (window), gtk_widget_get_display (do_widget));
gtk_window_set_title (GTK_WINDOW (window), "Shadertoy");
gtk_window_set_default_size (GTK_WINDOW (window), 640, 600);
g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE);
gtk_widget_set_margin_start (box, 12);
gtk_widget_set_margin_end (box, 12);
gtk_widget_set_margin_top (box, 12);
gtk_widget_set_margin_bottom (box, 12);
gtk_box_set_spacing (GTK_BOX (box), 6);
gtk_window_set_child (GTK_WINDOW (window), box);
aspect = gtk_aspect_frame_new (0.5, 0.5, 1.77777, FALSE);
gtk_widget_set_hexpand (aspect, TRUE);
gtk_widget_set_vexpand (aspect, TRUE);
gtk_box_append (GTK_BOX (box), aspect);
shadertoy = new_shadertoy ("/shadertoy/alienplanet.glsl");
gtk_aspect_frame_set_child (GTK_ASPECT_FRAME (aspect), shadertoy);
sw = gtk_scrolled_window_new ();
gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (sw), 250);
gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_widget_set_hexpand (sw, TRUE);
gtk_box_append (GTK_BOX (box), sw);
textview = gtk_text_view_new ();
gtk_text_view_set_monospace (GTK_TEXT_VIEW (textview), TRUE);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), textview);
textbuffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
gtk_text_buffer_set_text (textbuffer,
gtk_shadertoy_get_image_shader (GTK_SHADERTOY (shadertoy)),
-1);
centerbox = gtk_center_box_new ();
gtk_box_append (GTK_BOX (box), centerbox);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE);
gtk_box_set_spacing (GTK_BOX (hbox), 6);
gtk_center_box_set_start_widget (GTK_CENTER_BOX (centerbox), hbox);
button = gtk_button_new_from_icon_name ("media-playback-start-symbolic");
g_signal_connect (button, "clicked", G_CALLBACK (run_clicked_cb), NULL);
gtk_box_append (GTK_BOX (hbox), button);
button = gtk_button_new_from_icon_name ("edit-clear-all-symbolic");
g_signal_connect (button, "clicked", G_CALLBACK (clear_clicked_cb), NULL);
gtk_box_append (GTK_BOX (hbox), button);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE);
gtk_box_set_spacing (GTK_BOX (hbox), 6);
gtk_center_box_set_end_widget (GTK_CENTER_BOX (centerbox), hbox);
button = new_button ("/shadertoy/alienplanet.glsl");
gtk_box_append (GTK_BOX (hbox), button);
button = new_button ("/shadertoy/mandelbrot.glsl");
gtk_box_append (GTK_BOX (hbox), button);
button = new_button ("/shadertoy/neon.glsl");
gtk_box_append (GTK_BOX (hbox), button);
button = new_button ("/shadertoy/cogs.glsl");
gtk_box_append (GTK_BOX (hbox), button);
button = new_button ("/shadertoy/glowingstars.glsl");
gtk_box_append (GTK_BOX (hbox), button);
return window;
}
GtkWidget *
do_shadertoy (GtkWidget *do_widget)
{
if (!demo_window)
demo_window = create_shadertoy_window (do_widget);
if (!gtk_widget_get_visible (demo_window))
gtk_widget_show (demo_window);
else
gtk_window_destroy (GTK_WINDOW (demo_window));
return demo_window;
}