diff options
-rw-r--r-- | demos/gtk-demo/demo.gresource.xml | 9 | ||||
-rw-r--r-- | demos/gtk-demo/gltransition.c | 113 | ||||
-rw-r--r-- | demos/gtk-demo/gtkshaderstack.c | 255 | ||||
-rw-r--r-- | demos/gtk-demo/gtkshaderstack.h | 19 | ||||
-rw-r--r-- | demos/gtk-demo/meson.build | 2 | ||||
-rw-r--r-- | demos/gtk-demo/transition1.glsl | 32 | ||||
-rw-r--r-- | demos/gtk-demo/transition2.glsl | 33 | ||||
-rw-r--r-- | demos/gtk-demo/transition3.glsl | 26 | ||||
-rw-r--r-- | demos/gtk-demo/transition4.glsl | 40 |
9 files changed, 529 insertions, 0 deletions
diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml index 915d65c96a..ce76d66e9d 100644 --- a/demos/gtk-demo/demo.gresource.xml +++ b/demos/gtk-demo/demo.gresource.xml @@ -136,6 +136,14 @@ <gresource prefix="/glshader"> <file>fire.glsl</file> </gresource> + <gresource prefix="/gltransition"> + <file>gtkshaderstack.c</file> + <file>gtkshaderstack.h</file> + <file>transition1.glsl</file> + <file>transition2.glsl</file> + <file>transition3.glsl</file> + <file>transition4.glsl</file> + </gresource> <gresource prefix="/iconscroll"> <file>iconscroll.ui</file> </gresource> @@ -251,6 +259,7 @@ <file>gestures.c</file> <file>glarea.c</file> <file>glshader.c</file> + <file>gltransition.c</file> <file>headerbar.c</file> <file>hypertext.c</file> <file>iconscroll.c</file> diff --git a/demos/gtk-demo/gltransition.c b/demos/gtk-demo/gltransition.c new file mode 100644 index 0000000000..4b994216c4 --- /dev/null +++ b/demos/gtk-demo/gltransition.c @@ -0,0 +1,113 @@ +/* OpenGL/Transitions + * #Keywords: OpenGL, shader + * + * Create transitions between pages using a custom fragment shader. + * + * The examples here are taken from gl-transitions.com. + */ +#include <math.h> +#include <gtk/gtk.h> +#include "gtkshaderstack.h" + +static GtkWidget *demo_window = NULL; + +static void +close_window (GtkWidget *widget) +{ + /* Reset the state */ + demo_window = NULL; +} + +static GskGLShader * +gsk_shader_new_from_resource (const char *resource_path) +{ + GBytes *shader_b; + GskGLShader *shader; + + shader_b = g_resources_lookup_data (resource_path, 0, NULL); + shader = gsk_glshader_new ((const char *)g_bytes_get_data (shader_b, NULL)); + g_bytes_unref (shader_b); + + return shader; +} + +static GtkWidget * +make_shader_stack (const char *resource_path) +{ + GtkWidget *stack, *child; + GskGLShader *shader; + + stack = gtk_shader_stack_new (); + shader = gsk_shader_new_from_resource (resource_path); + gtk_shader_stack_set_shader (GTK_SHADER_STACK (stack), shader); + g_object_unref (shader); + + child = gtk_picture_new_for_resource ("/css_pixbufs/background.jpg"); + gtk_picture_set_can_shrink (GTK_PICTURE (child), TRUE); + gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child); + + child = gtk_picture_new_for_resource ("/transparent/portland-rose.jpg"); + gtk_picture_set_can_shrink (GTK_PICTURE (child), TRUE); + gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child); + + child = gtk_picture_new_for_resource ("/css_blendmodes/ducky.png"); + gtk_picture_set_can_shrink (GTK_PICTURE (child), TRUE); + gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child); + + return stack; +} + +static GtkWidget * +create_gltransition_window (GtkWidget *do_widget) +{ + GtkWidget *window, *grid; + + window = gtk_window_new (); + gtk_window_set_display (GTK_WINDOW (window), gtk_widget_get_display (do_widget)); + gtk_window_set_title (GTK_WINDOW (window), "Transitions"); + gtk_window_set_default_size (GTK_WINDOW (window), 400, 400); + g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL); + + grid = gtk_grid_new (); + gtk_widget_set_halign (grid, GTK_ALIGN_CENTER); + gtk_widget_set_valign (grid, GTK_ALIGN_CENTER); + gtk_widget_set_margin_start (grid, 12); + gtk_widget_set_margin_end (grid, 12); + gtk_widget_set_margin_top (grid, 12); + gtk_widget_set_margin_bottom (grid, 12); + gtk_grid_set_row_spacing (GTK_GRID (grid), 6); + gtk_grid_set_column_spacing (GTK_GRID (grid), 6); + gtk_grid_set_row_homogeneous (GTK_GRID (grid), TRUE); + gtk_grid_set_column_homogeneous (GTK_GRID (grid), TRUE); + + gtk_window_set_child (GTK_WINDOW (window), grid); + + gtk_grid_attach (GTK_GRID (grid), + make_shader_stack ("/gltransition/transition1.glsl"), + 0, 0, 1, 1); + gtk_grid_attach (GTK_GRID (grid), + make_shader_stack ("/gltransition/transition2.glsl"), + 1, 0, 1, 1); + gtk_grid_attach (GTK_GRID (grid), + make_shader_stack ("/gltransition/transition3.glsl"), + 0, 1, 1, 1); + gtk_grid_attach (GTK_GRID (grid), + make_shader_stack ("/gltransition/transition4.glsl"), + 1, 1, 1, 1); + + return window; +} + +GtkWidget * +do_gltransition (GtkWidget *do_widget) +{ + if (!demo_window) + demo_window = create_gltransition_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; +} diff --git a/demos/gtk-demo/gtkshaderstack.c b/demos/gtk-demo/gtkshaderstack.c new file mode 100644 index 0000000000..3a3385b058 --- /dev/null +++ b/demos/gtk-demo/gtkshaderstack.c @@ -0,0 +1,255 @@ +#include "gtkshaderstack.h" + +struct _GtkShaderStack +{ + GtkWidget parent_instance; + + GskGLShader *shader; + GPtrArray *children; + int current; + int next; + + guint tick_id; + float time; + float duration; + gint64 start_time; +}; + +struct _GtkShaderStackClass +{ + GtkWidgetClass parent_class; +}; + + +G_DEFINE_TYPE (GtkShaderStack, gtk_shader_stack, GTK_TYPE_WIDGET) + +static void +gtk_shader_stack_finalize (GObject *object) +{ + GtkShaderStack *self = GTK_SHADER_STACK (object); + + g_object_unref (self->shader); + + G_OBJECT_CLASS (gtk_shader_stack_parent_class)->finalize (object); +} + +static gboolean +transition_cb (GtkWidget *widget, + GdkFrameClock *clock, + gpointer unused) +{ + GtkShaderStack *self = GTK_SHADER_STACK (widget); + gint64 frame_time; + + frame_time = gdk_frame_clock_get_frame_time (clock); + + if (self->start_time == 0) + self->start_time = frame_time; + + self->time = (frame_time - self->start_time) / (float)G_USEC_PER_SEC; + + gtk_widget_queue_draw (widget); + + if (self->time >= self->duration) + { + self->current = self->next; + self->next = -1; + + return G_SOURCE_REMOVE; + } + else + return G_SOURCE_CONTINUE; +} + +static void +start_transition (GtkShaderStack *self) +{ + self->start_time = 0; + self->tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (self), + transition_cb, + NULL, NULL); +} + +static void +stop_transition (GtkShaderStack *self) +{ + if (self->tick_id != 0) + { + gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->tick_id); + self->tick_id = 0; + } + + self->next = -1; +} + +static void +gtk_shader_stack_dispose (GObject *object) +{ + GtkShaderStack *self = GTK_SHADER_STACK (object); + + stop_transition (self); + + g_clear_pointer (&self->children, g_ptr_array_unref); + + G_OBJECT_CLASS (gtk_shader_stack_parent_class)->dispose (object); +} + +static void +clicked_cb (GtkGestureClick *gesture, + guint n_pressed, + double x, + double y, + gpointer data) +{ + GtkShaderStack *self = GTK_SHADER_STACK (data); + + stop_transition (self); + + self->next = (self->current + 1) % self->children->len; + + start_transition (self); +} + +static void +gtk_shader_stack_init (GtkShaderStack *self) +{ + GtkGesture *gesture; + + self->children = g_ptr_array_new_with_free_func ((GDestroyNotify)gtk_widget_unparent); + self->current = -1; + self->next = -1; + self->duration = 1.0; + + gesture = gtk_gesture_click_new (); + g_signal_connect (gesture, "pressed", G_CALLBACK (clicked_cb), self); + gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture)); +} + +static void +gtk_shader_stack_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + GtkShaderStack *self = GTK_SHADER_STACK (widget); + int i; + + *minimum = 0; + *natural = 0; + + for (i = 0; i < self->children->len; i++) + { + GtkWidget *child = g_ptr_array_index (self->children, i); + int child_min, child_nat; + + if (gtk_widget_get_visible (child)) + { + gtk_widget_measure (child, orientation, for_size, &child_min, &child_nat, NULL, NULL); + + *minimum = MAX (*minimum, child_min); + *natural = MAX (*natural, child_nat); + } + } +} + +static void +gtk_shader_stack_size_allocate (GtkWidget *widget, + int width, + int height, + int baseline) +{ + GtkShaderStack *self = GTK_SHADER_STACK (widget); + GtkAllocation child_allocation; + GtkWidget *child; + int i; + + child_allocation.x = 0; + child_allocation.y = 0; + child_allocation.width = width; + child_allocation.height = height; + + for (i = 0; i < self->children->len; i++) + { + child = g_ptr_array_index (self->children, i); + if (gtk_widget_get_visible (child)) + gtk_widget_size_allocate (child, &child_allocation, -1); + } +} + +static void +gtk_shader_stack_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + GtkShaderStack *self = GTK_SHADER_STACK (widget); + int width, height; + GtkWidget *current, *next; + + width = gtk_widget_get_width (widget); + height = gtk_widget_get_height (widget); + + current = g_ptr_array_index (self->children, self->current); + + if (self->next == -1) + { + gtk_widget_snapshot_child (widget, current, snapshot); + } + else + { + graphene_vec4_t args; + + next = g_ptr_array_index (self->children, self->next); + + gtk_snapshot_push_glshader (snapshot, self->shader, + graphene_vec4_init (&args, self->time, self->time / self->duration, 0, 0), + &GRAPHENE_RECT_INIT(0, 0, width, height), + 2); + gtk_widget_snapshot_child (widget, next, snapshot); + gtk_snapshot_pop (snapshot); /* Fallback */ + gtk_widget_snapshot_child (widget, current, snapshot); + gtk_snapshot_pop (snapshot); /* current child */ + gtk_widget_snapshot_child (widget, next, snapshot); + gtk_snapshot_pop (snapshot); /* next child */ + } +} + +static void +gtk_shader_stack_class_init (GtkShaderStackClass *class) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->finalize = gtk_shader_stack_finalize; + object_class->dispose = gtk_shader_stack_dispose; + + widget_class->snapshot = gtk_shader_stack_snapshot; + widget_class->measure = gtk_shader_stack_measure; + widget_class->size_allocate = gtk_shader_stack_size_allocate; +} + +GtkWidget * +gtk_shader_stack_new (void) +{ + return g_object_new (GTK_TYPE_SHADER_STACK, NULL); +} + +void +gtk_shader_stack_set_shader (GtkShaderStack *self, + GskGLShader *shader) +{ + g_set_object (&self->shader, shader); +} + +void +gtk_shader_stack_add_child (GtkShaderStack *self, + GtkWidget *child) +{ + g_ptr_array_add (self->children, child); + gtk_widget_set_parent (child, GTK_WIDGET (self)); + gtk_widget_queue_resize (GTK_WIDGET (self)); + + if (self->current == -1) + self->current = 0; +} diff --git a/demos/gtk-demo/gtkshaderstack.h b/demos/gtk-demo/gtkshaderstack.h new file mode 100644 index 0000000000..19ee849b1b --- /dev/null +++ b/demos/gtk-demo/gtkshaderstack.h @@ -0,0 +1,19 @@ +#ifndef __GTK_SHADER_STACK_H__ +#define __GTK_SHADER_STACK_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GTK_TYPE_SHADER_STACK (gtk_shader_stack_get_type ()) +G_DECLARE_FINAL_TYPE (GtkShaderStack, gtk_shader_stack, GTK, SHADER_STACK, GtkWidget) + +GtkWidget * gtk_shader_stack_new (void); +void gtk_shader_stack_set_shader (GtkShaderStack *self, + GskGLShader *shader); +void gtk_shader_stack_add_child (GtkShaderStack *self, + GtkWidget *child); + +G_END_DECLS + +#endif /* __GTK_SHADER_STACK_H__ */ diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build index cbc52bcca6..b80ae3bbb3 100644 --- a/demos/gtk-demo/meson.build +++ b/demos/gtk-demo/meson.build @@ -33,6 +33,7 @@ demos = files([ 'gestures.c', 'glarea.c', 'glshader.c', + 'gltransition.c', 'headerbar.c', 'hypertext.c', 'iconscroll.c', @@ -105,6 +106,7 @@ extra_demo_sources = files(['main.c', 'gtkgears.c', 'gtkshaderbin.c', 'gtkshadertoy.c', + 'gtkshaderstack.c', 'puzzlepiece.c', 'bluroverlay.c', 'demoimage.c', diff --git a/demos/gtk-demo/transition1.glsl b/demos/gtk-demo/transition1.glsl new file mode 100644 index 0000000000..15a54642c6 --- /dev/null +++ b/demos/gtk-demo/transition1.glsl @@ -0,0 +1,32 @@ +float progress = u_args.y; + +vec4 getFromColor(vec2 uv) { + return texture(u_source, uv); +} + +vec4 getToColor(vec2 uv) { + return texture(u_source2, uv); +} + + +// Source: https://gl-transitions.com/editor/wind +// Author: gre +// License: MIT + +uniform float size = 0.2; + +float rand(vec2 co) { + return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); +} + +vec4 transition(vec2 p) { + float r = rand(vec2(0, p.y)); + float m = smoothstep(0.0, -size, p.x*(1.0-size) + size*r - (progress * (1.0 + size))); + return mix(getFromColor(p), getToColor(p), m); +} + + +void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv) +{ + fragColor = transition(uv); +} diff --git a/demos/gtk-demo/transition2.glsl b/demos/gtk-demo/transition2.glsl new file mode 100644 index 0000000000..77a2647fef --- /dev/null +++ b/demos/gtk-demo/transition2.glsl @@ -0,0 +1,33 @@ +float progress = u_args.y; + +vec4 getFromColor (vec2 uv) { + return texture(u_source, uv); +} + +vec4 getToColor (vec2 uv) { + return texture(u_source2, uv); +} + + +// Source: https://gl-transitions.com/editor/Radial +// License: MIT +// Author: Xaychru + +uniform float smoothness = 1.0; + +const float PI = 3.141592653589; + +vec4 transition(vec2 p) { + vec2 rp = p*2.-1.; + return mix( + getToColor(p), + getFromColor(p), + smoothstep(0., smoothness, atan(rp.y,rp.x) - (progress-.5) * PI * 2.5) + ); +} + + +void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv) +{ + fragColor = transition(uv); +} diff --git a/demos/gtk-demo/transition3.glsl b/demos/gtk-demo/transition3.glsl new file mode 100644 index 0000000000..6b0dee2c77 --- /dev/null +++ b/demos/gtk-demo/transition3.glsl @@ -0,0 +1,26 @@ +float progress = u_args.y; + +vec4 getFromColor (vec2 uv) { + return texture(u_source, uv); +} + +vec4 getToColor (vec2 uv) { + return texture(u_source2, uv); +} + + +// Source: https://gl-transitions.com/editor/crosswarp +// Author: Eke Péter <peterekepeter@gmail.com> +// License: MIT + +vec4 transition(vec2 p) { + float x = progress; + x=smoothstep(.0,1.0,(x*2.0+p.x-1.0)); + return mix(getFromColor((p-.5)*(1.-x)+.5), getToColor((p-.5)*x+.5), x); +} + + +void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv) +{ + fragColor = transition(uv); +} diff --git a/demos/gtk-demo/transition4.glsl b/demos/gtk-demo/transition4.glsl new file mode 100644 index 0000000000..41e2cd3627 --- /dev/null +++ b/demos/gtk-demo/transition4.glsl @@ -0,0 +1,40 @@ +float progress = u_args.y; + +vec4 getFromColor (vec2 uv) { + return texture(u_source, uv); +} + +vec4 getToColor (vec2 uv) { + return texture(u_source2, uv); +} + + +// Source: https://gl-transitions.com/editor/kaleidoscope +// Author: nwoeanhinnogaehr +// License: MIT + +uniform float speed = 1.0; +uniform float angle = 1.0; +uniform float power = 1.5; + +vec4 transition(vec2 uv) { + vec2 p = uv.xy / vec2(1.0).xy; + vec2 q = p; + float t = pow(progress, power)*speed; + p = p -0.5; + for (int i = 0; i < 7; i++) { + p = vec2(sin(t)*p.x + cos(t)*p.y, sin(t)*p.y - cos(t)*p.x); + t += angle; + p = abs(mod(p, 2.0) - 1.0); + } + abs(mod(p, 1.0)); + return mix( + mix(getFromColor(q), getToColor(q), progress), + mix(getFromColor(p), getToColor(p), progress), 1.0 - 2.0*abs(progress - 0.5)); +} + + +void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv) +{ + fragColor = transition(uv); +} |