diff --git a/demos/gtk-demo/iconscroll.c b/demos/gtk-demo/iconscroll.c index 9e2fe9dc8a..2eb9aa6e92 100644 --- a/demos/gtk-demo/iconscroll.c +++ b/demos/gtk-demo/iconscroll.c @@ -13,7 +13,7 @@ static GtkWidget *window = NULL; static GtkWidget *scrolledwindow; static int selected; -#define N_WIDGET_TYPES 7 +#define N_WIDGET_TYPES 8 static int hincrement = 5; @@ -110,6 +110,41 @@ populate_text (gboolean highlight) gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolledwindow), textview); } +static void +populate_emoji_text (void) +{ + GtkWidget *textview; + GtkTextBuffer *buffer; + GString *s; + + s = g_string_sized_new (1000 * 30 * 4); + + for (int i = 0; i < 1000; i++) + { + if (i % 2) + g_string_append (s, "x"); + for (int j = 0; j < 30; j++) + g_string_append (s, "💓x"); + g_string_append (s, "\n"); + } + + buffer = gtk_text_buffer_new (NULL); + gtk_text_buffer_set_text (buffer, s->str, s->len); + + g_string_free (s, TRUE); + + textview = gtk_text_view_new (); + gtk_text_view_set_buffer (GTK_TEXT_VIEW (textview), buffer); + + hincrement = 0; + vincrement = 5; + + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolledwindow), textview); +} + static void populate_image (void) { @@ -224,21 +259,26 @@ set_widget_type (int type) break; case 3: + gtk_window_set_title (GTK_WINDOW (window), "Scrolling text with Emoji"); + populate_emoji_text (); + break; + + case 4: gtk_window_set_title (GTK_WINDOW (window), "Scrolling a big image"); populate_image (); break; - case 4: + case 5: gtk_window_set_title (GTK_WINDOW (window), "Scrolling a list"); populate_list (); break; - case 5: + case 6: gtk_window_set_title (GTK_WINDOW (window), "Scrolling a columned list"); populate_list2 (); break; - case 6: + case 7: gtk_window_set_title (GTK_WINDOW (window), "Scrolling a grid"); populate_grid (); break; diff --git a/gsk/gen-gsk-gresources-xml.py b/gsk/gen-gsk-gresources-xml.py index 5db5044b09..16a2c4042a 100644 --- a/gsk/gen-gsk-gresources-xml.py +++ b/gsk/gen-gsk-gresources-xml.py @@ -20,13 +20,17 @@ def replace_if_changed(new, old): else: os.remove(new) -source_shaders = [] +gl_source_shaders = [] +ngl_source_shaders = [] vulkan_compiled_shaders = [] vulkan_shaders = [] for f in sys.argv[2:]: if f.endswith('.glsl'): - source_shaders.append(f) + if f.startswith('ngl'): + ngl_source_shaders.append(f); + else: + gl_source_shaders.append(f) elif f.endswith('.spv'): vulkan_compiled_shaders.append(f) elif f.endswith('.frag') or f.endswith('.vert'): @@ -40,11 +44,16 @@ xml = ''' ''' -for f in source_shaders: +for f in gl_source_shaders: xml += ' resources/glsl/{0}\n'.format(os.path.basename(f)) xml += '\n' +for f in ngl_source_shaders: + xml += ' ngl/resources/{0}\n'.format(os.path.basename(f)) + +xml += '\n' + for f in vulkan_compiled_shaders: xml += ' resources/vulkan/{0}\n'.format(os.path.basename(f)) diff --git a/gsk/meson.build b/gsk/meson.build index dd1ac34ff9..8f9a6281c0 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -20,6 +20,28 @@ gsk_private_gl_shaders = [ 'resources/glsl/custom.glsl', ] +gsk_private_ngl_shaders = [ + 'ngl/resources/preamble.glsl', + 'ngl/resources/preamble.fs.glsl', + 'ngl/resources/preamble.vs.glsl', + 'ngl/resources/border.glsl', + 'ngl/resources/blit.glsl', + 'ngl/resources/coloring.glsl', + 'ngl/resources/color.glsl', + 'ngl/resources/linear_gradient.glsl', + 'ngl/resources/radial_gradient.glsl', + 'ngl/resources/conic_gradient.glsl', + 'ngl/resources/color_matrix.glsl', + 'ngl/resources/blur.glsl', + 'ngl/resources/inset_shadow.glsl', + 'ngl/resources/outset_shadow.glsl', + 'ngl/resources/unblurred_outset_shadow.glsl', + 'ngl/resources/cross_fade.glsl', + 'ngl/resources/blend.glsl', + 'ngl/resources/repeat.glsl', + 'ngl/resources/custom.glsl', +] + gsk_public_sources = files([ 'gskdiff.c', 'gskcairorenderer.c', @@ -150,6 +172,7 @@ gsk_resources_xml = configure_file(output: 'gsk.resources.xml', find_program('gen-gsk-gresources-xml.py'), '@OUTPUT@', gsk_private_gl_shaders, + gsk_private_ngl_shaders, gsk_private_vulkan_compiled_shaders, gsk_private_vulkan_shaders ], diff --git a/gsk/ngl/gsknglcommandqueue.c b/gsk/ngl/gsknglcommandqueue.c index 9426c199b1..7d5a8b30be 100644 --- a/gsk/ngl/gsknglcommandqueue.c +++ b/gsk/ngl/gsknglcommandqueue.c @@ -1096,6 +1096,12 @@ gsk_ngl_command_queue_execute (GskNglCommandQueue *self, sizeof (GskNglDrawVertex), (void *) G_STRUCT_OFFSET (GskNglDrawVertex, uv)); + /* 2 = color location */ + glEnableVertexAttribArray (2); + glVertexAttribPointer (2, 4, GL_FLOAT, GL_FALSE, + sizeof (GskNglDrawVertex), + (void *) G_STRUCT_OFFSET (GskNglDrawVertex, color)); + /* Setup initial scissor clip */ if (scissor != NULL) { diff --git a/gsk/ngl/gskngldriver.c b/gsk/ngl/gskngldriver.c index 692e6a9d5d..3d910e5d51 100644 --- a/gsk/ngl/gskngldriver.c +++ b/gsk/ngl/gskngldriver.c @@ -336,17 +336,18 @@ gsk_ngl_driver_load_programs (GskNglDriver *self, /* Setup preambles that are shared by all shaders */ gsk_ngl_compiler_set_preamble_from_resource (compiler, GSK_NGL_COMPILER_ALL, - "/org/gtk/libgsk/glsl/preamble.glsl"); + "/org/gtk/libgsk/ngl/preamble.glsl"); gsk_ngl_compiler_set_preamble_from_resource (compiler, GSK_NGL_COMPILER_VERTEX, - "/org/gtk/libgsk/glsl/preamble.vs.glsl"); + "/org/gtk/libgsk/ngl/preamble.vs.glsl"); gsk_ngl_compiler_set_preamble_from_resource (compiler, GSK_NGL_COMPILER_FRAGMENT, - "/org/gtk/libgsk/glsl/preamble.fs.glsl"); + "/org/gtk/libgsk/ngl/preamble.fs.glsl"); /* Setup attributes that are provided via VBO */ gsk_ngl_compiler_bind_attribute (compiler, "aPosition", 0); gsk_ngl_compiler_bind_attribute (compiler, "aUv", 1); + gsk_ngl_compiler_bind_attribute (compiler, "aColor", 2); /* Use XMacros to register all of our programs and their uniforms */ #define GSK_NGL_NO_UNIFORMS @@ -376,7 +377,6 @@ gsk_ngl_driver_load_programs (GskNglDriver *self, uniforms \ \ gsk_ngl_program_uniforms_added (program, have_source); \ - \ if (have_alpha) \ gsk_ngl_program_set_uniform1f (program, UNIFORM_SHARED_ALPHA, 0, 1.0f); \ \ @@ -1045,6 +1045,7 @@ gsk_ngl_driver_lookup_shader (GskNglDriver *self, /* Setup attributes that are provided via VBO */ gsk_ngl_compiler_bind_attribute (compiler, "aPosition", 0); gsk_ngl_compiler_bind_attribute (compiler, "aUv", 1); + gsk_ngl_compiler_bind_attribute (compiler, "aColor", 2); if ((program = gsk_ngl_compiler_compile (compiler, NULL, "", error))) { diff --git a/gsk/ngl/gskngldriverprivate.h b/gsk/ngl/gskngldriverprivate.h index 28199ec016..c50e112e86 100644 --- a/gsk/ngl/gskngldriverprivate.h +++ b/gsk/ngl/gskngldriverprivate.h @@ -58,13 +58,17 @@ typedef struct { graphene_rect_t parent_rect; /* Valid when pointer_is_child */ } GskTextureKey; -#define GSL_GK_NO_UNIFORMS UNIFORM_INVALID_##__COUNTER__ +#define GSK_NGL_NO_UNIFORMS CONCAT_EXPANDED(UNIFORM_INVALID_,__COUNTER__) +#define CONCAT_EXPANDED(a,b) CONCAT_EXPANDED2(a,b) +#define CONCAT_EXPANDED2(a,b) a##b #define GSK_NGL_ADD_UNIFORM(pos, KEY, name) UNIFORM_##KEY = UNIFORM_SHARED_LAST + pos, #define GSK_NGL_DEFINE_PROGRAM(name, resource, uniforms) enum { uniforms }; # include "gsknglprograms.defs" #undef GSK_NGL_DEFINE_PROGRAM #undef GSK_NGL_ADD_UNIFORM -#undef GSL_GK_NO_UNIFORMS +#undef GSK_NGL_NO_UNIFORMS +#undef CONCAT_EXPANDED +#undef CONCAT_EXPANDED2 #define GSK_TYPE_NGL_DRIVER (gsk_ngl_driver_get_type()) diff --git a/gsk/ngl/gsknglprograms.defs b/gsk/ngl/gsknglprograms.defs index b0b797f0e9..e20cca2fa9 100644 --- a/gsk/ngl/gsknglprograms.defs +++ b/gsk/ngl/gsknglprograms.defs @@ -1,69 +1,66 @@ GSK_NGL_DEFINE_PROGRAM (blend, - "/org/gtk/libgsk/glsl/blend.glsl", + "/org/gtk/libgsk/ngl/blend.glsl", GSK_NGL_ADD_UNIFORM (1, BLEND_SOURCE2, u_source2) GSK_NGL_ADD_UNIFORM (2, BLEND_MODE, u_mode)) GSK_NGL_DEFINE_PROGRAM (blit, - "/org/gtk/libgsk/glsl/blit.glsl", + "/org/gtk/libgsk/ngl/blit.glsl", GSK_NGL_NO_UNIFORMS) GSK_NGL_DEFINE_PROGRAM (blur, - "/org/gtk/libgsk/glsl/blur.glsl", + "/org/gtk/libgsk/ngl/blur.glsl", GSK_NGL_ADD_UNIFORM (1, BLUR_RADIUS, u_blur_radius) GSK_NGL_ADD_UNIFORM (2, BLUR_SIZE, u_blur_size) GSK_NGL_ADD_UNIFORM (3, BLUR_DIR, u_blur_dir)) GSK_NGL_DEFINE_PROGRAM (border, - "/org/gtk/libgsk/glsl/border.glsl", - GSK_NGL_ADD_UNIFORM (1, BORDER_COLOR, u_color) + "/org/gtk/libgsk/ngl/border.glsl", GSK_NGL_ADD_UNIFORM (2, BORDER_WIDTHS, u_widths) GSK_NGL_ADD_UNIFORM (3, BORDER_OUTLINE_RECT, u_outline_rect)) GSK_NGL_DEFINE_PROGRAM (color, - "/org/gtk/libgsk/glsl/color.glsl", - GSK_NGL_ADD_UNIFORM (1, COLOR_COLOR, u_color)) + "/org/gtk/libgsk/ngl/color.glsl", + GSK_NGL_NO_UNIFORMS) GSK_NGL_DEFINE_PROGRAM (coloring, - "/org/gtk/libgsk/glsl/coloring.glsl", - GSK_NGL_ADD_UNIFORM (1, COLORING_COLOR, u_color)) + "/org/gtk/libgsk/ngl/coloring.glsl", + GSK_NGL_NO_UNIFORMS) GSK_NGL_DEFINE_PROGRAM (color_matrix, - "/org/gtk/libgsk/glsl/color_matrix.glsl", + "/org/gtk/libgsk/ngl/color_matrix.glsl", GSK_NGL_ADD_UNIFORM (1, COLOR_MATRIX_COLOR_MATRIX, u_color_matrix) GSK_NGL_ADD_UNIFORM (2, COLOR_MATRIX_COLOR_OFFSET, u_color_offset)) GSK_NGL_DEFINE_PROGRAM (conic_gradient, - "/org/gtk/libgsk/glsl/conic_gradient.glsl", + "/org/gtk/libgsk/ngl/conic_gradient.glsl", GSK_NGL_ADD_UNIFORM (1, CONIC_GRADIENT_COLOR_STOPS, u_color_stops) GSK_NGL_ADD_UNIFORM (2, CONIC_GRADIENT_NUM_COLOR_STOPS, u_num_color_stops) GSK_NGL_ADD_UNIFORM (3, CONIC_GRADIENT_GEOMETRY, u_geometry)) GSK_NGL_DEFINE_PROGRAM (cross_fade, - "/org/gtk/libgsk/glsl/cross_fade.glsl", + "/org/gtk/libgsk/ngl/cross_fade.glsl", GSK_NGL_ADD_UNIFORM (1, CROSS_FADE_PROGRESS, u_progress) GSK_NGL_ADD_UNIFORM (2, CROSS_FADE_SOURCE2, u_source2)) GSK_NGL_DEFINE_PROGRAM (inset_shadow, - "/org/gtk/libgsk/glsl/inset_shadow.glsl", - GSK_NGL_ADD_UNIFORM (1, INSET_SHADOW_COLOR, u_color) + "/org/gtk/libgsk/ngl/inset_shadow.glsl", GSK_NGL_ADD_UNIFORM (2, INSET_SHADOW_SPREAD, u_spread) GSK_NGL_ADD_UNIFORM (3, INSET_SHADOW_OFFSET, u_offset) GSK_NGL_ADD_UNIFORM (4, INSET_SHADOW_OUTLINE_RECT, u_outline_rect)) GSK_NGL_DEFINE_PROGRAM (linear_gradient, - "/org/gtk/libgsk/glsl/linear_gradient.glsl", + "/org/gtk/libgsk/ngl/linear_gradient.glsl", GSK_NGL_ADD_UNIFORM (1, LINEAR_GRADIENT_COLOR_STOPS, u_color_stops) GSK_NGL_ADD_UNIFORM (2, LINEAR_GRADIENT_NUM_COLOR_STOPS, u_num_color_stops) GSK_NGL_ADD_UNIFORM (3, LINEAR_GRADIENT_POINTS, u_points) GSK_NGL_ADD_UNIFORM (4, LINEAR_GRADIENT_REPEAT, u_repeat)) GSK_NGL_DEFINE_PROGRAM (outset_shadow, - "/org/gtk/libgsk/glsl/outset_shadow.glsl", - GSK_NGL_ADD_UNIFORM (1, OUTSET_SHADOW_COLOR, u_color) + "/org/gtk/libgsk/ngl/outset_shadow.glsl", GSK_NGL_ADD_UNIFORM (2, OUTSET_SHADOW_OUTLINE_RECT, u_outline_rect)) GSK_NGL_DEFINE_PROGRAM (radial_gradient, - "/org/gtk/libgsk/glsl/radial_gradient.glsl", + "/org/gtk/libgsk/ngl/radial_gradient.glsl", GSK_NGL_ADD_UNIFORM (1, RADIAL_GRADIENT_COLOR_STOPS, u_color_stops) GSK_NGL_ADD_UNIFORM (2, RADIAL_GRADIENT_NUM_COLOR_STOPS, u_num_color_stops) GSK_NGL_ADD_UNIFORM (3, RADIAL_GRADIENT_REPEAT, u_repeat) @@ -71,13 +68,12 @@ GSK_NGL_DEFINE_PROGRAM (radial_gradient, GSK_NGL_ADD_UNIFORM (5, RADIAL_GRADIENT_GEOMETRY, u_geometry)) GSK_NGL_DEFINE_PROGRAM (repeat, - "/org/gtk/libgsk/glsl/repeat.glsl", + "/org/gtk/libgsk/ngl/repeat.glsl", GSK_NGL_ADD_UNIFORM (1, REPEAT_CHILD_BOUNDS, u_child_bounds) GSK_NGL_ADD_UNIFORM (2, REPEAT_TEXTURE_RECT, u_texture_rect)) GSK_NGL_DEFINE_PROGRAM (unblurred_outset_shadow, - "/org/gtk/libgsk/glsl/unblurred_outset_shadow.glsl", - GSK_NGL_ADD_UNIFORM (1, UNBLURRED_OUTSET_SHADOW_COLOR, u_color) + "/org/gtk/libgsk/ngl/unblurred_outset_shadow.glsl", GSK_NGL_ADD_UNIFORM (2, UNBLURRED_OUTSET_SHADOW_SPREAD, u_spread) GSK_NGL_ADD_UNIFORM (3, UNBLURRED_OUTSET_SHADOW_OFFSET, u_offset) GSK_NGL_ADD_UNIFORM (4, UNBLURRED_OUTSET_SHADOW_OUTLINE_RECT, u_outline_rect)) diff --git a/gsk/ngl/gsknglrenderjob.c b/gsk/ngl/gsknglrenderjob.c index 93ca180133..8f67575564 100644 --- a/gsk/ngl/gsknglrenderjob.c +++ b/gsk/ngl/gsknglrenderjob.c @@ -142,6 +142,9 @@ struct _GskNglRenderJob /* Our current alpha state as we process nodes */ float alpha; + /* Our current color state as we process nodes */ + GdkRGBA color; + /* Offset (delta x,y) as we process nodes. Occasionally this is merged into * a transform that is referenced from child transform nodes. */ @@ -395,6 +398,13 @@ gsk_ngl_render_job_set_alpha (GskNglRenderJob *job, return alpha; } +static inline void +gsk_ngl_render_job_set_color (GskNglRenderJob *job, + const GdkRGBA *color) +{ + job->color = *color; +} + static void extract_matrix_metadata (GskNglRenderModelview *modelview) { @@ -874,13 +884,81 @@ gsk_ngl_render_job_update_clip (GskNglRenderJob *job, return TRUE; } +/* fill_vertex_data */ +static void +gsk_ngl_render_job_draw_coords (GskNglRenderJob *job, + float min_x, + float min_y, + float max_x, + float max_y, + float min_u, + float min_v, + float max_u, + float max_v) +{ + GskNglDrawVertex *vertices = gsk_ngl_command_queue_add_vertices (job->command_queue); + + vertices[0].position[0] = min_x; + vertices[0].position[1] = min_y; + vertices[0].uv[0] = min_u; + vertices[0].uv[1] = min_v; + vertices[0].color[0] = job->color.red; + vertices[0].color[1] = job->color.green; + vertices[0].color[2] = job->color.blue; + vertices[0].color[3] = job->color.alpha; + + vertices[1].position[0] = min_x; + vertices[1].position[1] = max_y; + vertices[1].uv[0] = min_u; + vertices[1].uv[1] = max_v; + vertices[1].color[0] = job->color.red; + vertices[1].color[1] = job->color.green; + vertices[1].color[2] = job->color.blue; + vertices[1].color[3] = job->color.alpha; + + vertices[2].position[0] = max_x; + vertices[2].position[1] = min_y; + vertices[2].uv[0] = max_u; + vertices[2].uv[1] = min_v; + vertices[2].color[0] = job->color.red; + vertices[2].color[1] = job->color.green; + vertices[2].color[2] = job->color.blue; + vertices[2].color[3] = job->color.alpha; + + vertices[3].position[0] = max_x; + vertices[3].position[1] = max_y; + vertices[3].uv[0] = max_u; + vertices[3].uv[1] = max_v; + vertices[3].color[0] = job->color.red; + vertices[3].color[1] = job->color.green; + vertices[3].color[2] = job->color.blue; + vertices[3].color[3] = job->color.alpha; + + vertices[4].position[0] = min_x; + vertices[4].position[1] = max_y; + vertices[4].uv[0] = min_u; + vertices[4].uv[1] = max_v; + vertices[4].color[0] = job->color.red; + vertices[4].color[1] = job->color.green; + vertices[4].color[2] = job->color.blue; + vertices[4].color[3] = job->color.alpha; + + vertices[5].position[0] = max_x; + vertices[5].position[1] = min_y; + vertices[5].uv[0] = max_u; + vertices[5].uv[1] = min_v; + vertices[5].color[0] = job->color.red; + vertices[5].color[1] = job->color.green; + vertices[5].color[2] = job->color.blue; + vertices[5].color[3] = job->color.alpha; +} + /* load_vertex_data_with_region */ static inline void gsk_ngl_render_job_load_vertices_from_offscreen (GskNglRenderJob *job, const graphene_rect_t *bounds, const GskNglRenderOffscreen *offscreen) { - GskNglDrawVertex *vertices = gsk_ngl_command_queue_add_vertices (job->command_queue); float min_x = job->offset_x + bounds->origin.x; float min_y = job->offset_y + bounds->origin.y; float max_x = min_x + bounds->size.width; @@ -888,35 +966,9 @@ gsk_ngl_render_job_load_vertices_from_offscreen (GskNglRenderJob *jo float y1 = offscreen->was_offscreen ? offscreen->area.y2 : offscreen->area.y; float y2 = offscreen->was_offscreen ? offscreen->area.y : offscreen->area.y2; - vertices[0].position[0] = min_x; - vertices[0].position[1] = min_y; - vertices[0].uv[0] = offscreen->area.x; - vertices[0].uv[1] = y1; - - vertices[1].position[0] = min_x; - vertices[1].position[1] = max_y; - vertices[1].uv[0] = offscreen->area.x; - vertices[1].uv[1] = y2; - - vertices[2].position[0] = max_x; - vertices[2].position[1] = min_y; - vertices[2].uv[0] = offscreen->area.x2; - vertices[2].uv[1] = y1; - - vertices[3].position[0] = max_x; - vertices[3].position[1] = max_y; - vertices[3].uv[0] = offscreen->area.x2; - vertices[3].uv[1] = y2; - - vertices[4].position[0] = min_x; - vertices[4].position[1] = max_y; - vertices[4].uv[0] = offscreen->area.x; - vertices[4].uv[1] = y2; - - vertices[5].position[0] = max_x; - vertices[5].position[1] = min_y; - vertices[5].uv[0] = offscreen->area.x2; - vertices[5].uv[1] = y1; + gsk_ngl_render_job_draw_coords (job, + min_x, min_y, max_x, max_y, + offscreen->area.x, y1, offscreen->area.x2, y2); } /* load_float_vertex_data */ @@ -927,41 +979,12 @@ gsk_ngl_render_job_draw (GskNglRenderJob *job, float width, float height) { - GskNglDrawVertex *vertices = gsk_ngl_command_queue_add_vertices (job->command_queue); float min_x = job->offset_x + x; float min_y = job->offset_y + y; float max_x = min_x + width; float max_y = min_y + height; - vertices[0].position[0] = min_x; - vertices[0].position[1] = min_y; - vertices[0].uv[0] = 0; - vertices[0].uv[1] = 0; - - vertices[1].position[0] = min_x; - vertices[1].position[1] = max_y; - vertices[1].uv[0] = 0; - vertices[1].uv[1] = 1; - - vertices[2].position[0] = max_x; - vertices[2].position[1] = min_y; - vertices[2].uv[0] = 1; - vertices[2].uv[1] = 0; - - vertices[3].position[0] = max_x; - vertices[3].position[1] = max_y; - vertices[3].uv[0] = 1; - vertices[3].uv[1] = 1; - - vertices[4].position[0] = min_x; - vertices[4].position[1] = max_y; - vertices[4].uv[0] = 0; - vertices[4].uv[1] = 1; - - vertices[5].position[0] = max_x; - vertices[5].position[1] = min_y; - vertices[5].uv[0] = 1; - vertices[5].uv[1] = 0; + gsk_ngl_render_job_draw_coords (job, min_x, min_y, max_x, max_y, 0, 0, 1, 1); } /* load_vertex_data */ @@ -976,47 +999,6 @@ gsk_ngl_render_job_draw_rect (GskNglRenderJob *job, bounds->size.height); } -/* fill_vertex_data */ -static void -gsk_ngl_render_job_draw_coords (GskNglRenderJob *job, - float min_x, - float min_y, - float max_x, - float max_y) -{ - GskNglDrawVertex *vertices = gsk_ngl_command_queue_add_vertices (job->command_queue); - - vertices[0].position[0] = min_x; - vertices[0].position[1] = min_y; - vertices[0].uv[0] = 0; - vertices[0].uv[1] = 1; - - vertices[1].position[0] = min_x; - vertices[1].position[1] = max_y; - vertices[1].uv[0] = 0; - vertices[1].uv[1] = 0; - - vertices[2].position[0] = max_x; - vertices[2].position[1] = min_y; - vertices[2].uv[0] = 1; - vertices[2].uv[1] = 1; - - vertices[3].position[0] = max_x; - vertices[3].position[1] = max_y; - vertices[3].uv[0] = 1; - vertices[3].uv[1] = 0; - - vertices[4].position[0] = min_x; - vertices[4].position[1] = max_y; - vertices[4].uv[0] = 0; - vertices[4].uv[1] = 0; - - vertices[5].position[0] = max_x; - vertices[5].position[1] = min_y; - vertices[5].uv[0] = 1; - vertices[5].uv[1] = 1; -} - /* load_offscreen_vertex_data */ static inline void gsk_ngl_render_job_draw_offscreen_rect (GskNglRenderJob *job, @@ -1027,7 +1009,7 @@ gsk_ngl_render_job_draw_offscreen_rect (GskNglRenderJob *job, float max_x = min_x + bounds->size.width; float max_y = min_y + bounds->size.height; - gsk_ngl_render_job_draw_coords (job, min_x, min_y, max_x, max_y); + gsk_ngl_render_job_draw_coords (job, min_x, min_y, max_x, max_y, 0, 1, 1, 0); } static inline void @@ -1283,7 +1265,7 @@ blur_offscreen (GskNglRenderJob *job, gsk_ngl_program_set_uniform2f (job->current_program, UNIFORM_BLUR_DIR, 0, 1, 0); - gsk_ngl_render_job_draw_coords (job, 0, 0, texture_to_blur_width, texture_to_blur_height); + gsk_ngl_render_job_draw_coords (job, 0, 0, texture_to_blur_width, texture_to_blur_height, 0, 1, 1, 0); gsk_ngl_render_job_end_draw (job); /* Bind second pass framebuffer and clear it */ @@ -1307,7 +1289,7 @@ blur_offscreen (GskNglRenderJob *job, gsk_ngl_program_set_uniform2f (job->current_program, UNIFORM_BLUR_DIR, 0, 0, 1); - gsk_ngl_render_job_draw_coords (job, 0, 0, texture_to_blur_width, texture_to_blur_height); + gsk_ngl_render_job_draw_coords (job, 0, 0, texture_to_blur_width, texture_to_blur_height, 0, 1, 1, 0); gsk_ngl_render_job_end_draw (job); gsk_ngl_render_job_pop_modelview (job); @@ -1381,9 +1363,7 @@ gsk_ngl_render_job_visit_color_node (GskNglRenderJob *job, const GskRenderNode *node) { gsk_ngl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, color)); - gsk_ngl_program_set_uniform_color (job->current_program, - UNIFORM_COLOR_COLOR, 0, - gsk_color_node_get_color (node)); + gsk_ngl_render_job_set_color (job, gsk_color_node_get_color (node)); gsk_ngl_render_job_draw_rect (job, &node->bounds); gsk_ngl_render_job_end_draw (job); } @@ -1694,9 +1674,7 @@ gsk_ngl_render_job_visit_rect_border_node (GskNglRenderJob *job, gsk_ngl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, color)); - gsk_ngl_program_set_uniform_color (job->current_program, - UNIFORM_COLOR_COLOR, 0, - &colors[0]); + gsk_ngl_render_job_set_color (job, &colors[0]); gsk_ngl_render_job_draw_rect (job, &GRAPHENE_RECT_INIT (origin->x, @@ -1737,12 +1715,10 @@ gsk_ngl_render_job_visit_uniform_border_node (GskNglRenderJob *job, gsk_ngl_render_job_transform_rounded_rect (job, rounded_outline, &outline); gsk_ngl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, inset_shadow)); + gsk_ngl_render_job_set_color (job, &colors[0]); gsk_ngl_program_set_uniform_rounded_rect (job->current_program, UNIFORM_INSET_SHADOW_OUTLINE_RECT, 0, &outline); - gsk_ngl_program_set_uniform_color (job->current_program, - UNIFORM_INSET_SHADOW_COLOR, 0, - &colors[0]); gsk_ngl_program_set_uniform1f (job->current_program, UNIFORM_INSET_SHADOW_SPREAD, 0, widths[0]); @@ -1758,7 +1734,7 @@ gsk_ngl_render_job_visit_border_node (GskNglRenderJob *job, const GskRenderNode *node) { const GskRoundedRect *rounded_outline = gsk_border_node_get_outline (node); - const GdkRGBA *colors = gsk_border_node_get_colors (node); + const GdkRGBA *c = gsk_border_node_get_colors (node); const float *widths = gsk_border_node_get_widths (node); struct { float w; @@ -1818,52 +1794,48 @@ gsk_ngl_render_job_visit_border_node (GskNglRenderJob *job, const GskNglDrawVertex side_data[4][6] = { /* Top */ { - { { min_x, min_y }, { 0, 1 }, }, /* Upper left */ - { { min_x + sizes[0].w, min_y + sizes[0].h }, { 0, 0 }, }, /* Lower left */ - { { max_x, min_y }, { 1, 1 }, }, /* Upper right */ + { { min_x, min_y }, { 0, 1 }, { c[0].red, c[0].green, c[0].blue, c[0].alpha } }, /* Upper left */ + { { min_x + sizes[0].w, min_y + sizes[0].h }, { 0, 0 }, { c[0].red, c[0].green, c[0].blue, c[0].alpha } }, /* Lower left */ + { { max_x, min_y }, { 1, 1 }, { c[0].red, c[0].green, c[0].blue, c[0].alpha } }, /* Upper right */ - { { max_x - sizes[1].w, min_y + sizes[1].h }, { 1, 0 }, }, /* Lower right */ - { { min_x + sizes[0].w, min_y + sizes[0].h }, { 0, 0 }, }, /* Lower left */ - { { max_x, min_y }, { 1, 1 }, }, /* Upper right */ + { { max_x - sizes[1].w, min_y + sizes[1].h }, { 1, 0 }, { c[0].red, c[0].green, c[0].blue, c[0].alpha } }, /* Lower right */ + { { min_x + sizes[0].w, min_y + sizes[0].h }, { 0, 0 }, { c[0].red, c[0].green, c[0].blue, c[0].alpha } }, /* Lower left */ + { { max_x, min_y }, { 1, 1 }, { c[0].red, c[0].green, c[0].blue, c[0].alpha } }, /* Upper right */ }, /* Right */ { - { { max_x - sizes[1].w, min_y + sizes[1].h }, { 0, 1 }, }, /* Upper left */ - { { max_x - sizes[2].w, max_y - sizes[2].h }, { 0, 0 }, }, /* Lower left */ - { { max_x, min_y }, { 1, 1 }, }, /* Upper right */ + { { max_x - sizes[1].w, min_y + sizes[1].h }, { 0, 1 }, { c[1].red, c[1].green, c[1].blue, c[1].alpha } }, /* Upper left */ + { { max_x - sizes[2].w, max_y - sizes[2].h }, { 0, 0 }, { c[1].red, c[1].green, c[1].blue, c[1].alpha } }, /* Lower left */ + { { max_x, min_y }, { 1, 1 }, { c[1].red, c[1].green, c[1].blue, c[1].alpha } }, /* Upper right */ - { { max_x, max_y }, { 1, 0 }, }, /* Lower right */ - { { max_x - sizes[2].w, max_y - sizes[2].h }, { 0, 0 }, }, /* Lower left */ - { { max_x, min_y }, { 1, 1 }, }, /* Upper right */ + { { max_x, max_y }, { 1, 0 }, { c[1].red, c[1].green, c[1].blue, c[1].alpha } }, /* Lower right */ + { { max_x - sizes[2].w, max_y - sizes[2].h }, { 0, 0 }, { c[1].red, c[1].green, c[1].blue, c[1].alpha } }, /* Lower left */ + { { max_x, min_y }, { 1, 1 }, { c[1].red, c[1].green, c[1].blue, c[1].alpha } }, /* Upper right */ }, /* Bottom */ { - { { min_x + sizes[3].w, max_y - sizes[3].h }, { 0, 1 }, }, /* Upper left */ - { { min_x, max_y }, { 0, 0 }, }, /* Lower left */ - { { max_x - sizes[2].w, max_y - sizes[2].h }, { 1, 1 }, }, /* Upper right */ + { { min_x + sizes[3].w, max_y - sizes[3].h }, { 0, 1 }, { c[2].red, c[2].green, c[2].blue, c[2].alpha } }, /* Upper left */ + { { min_x, max_y }, { 0, 0 }, { c[2].red, c[2].green, c[2].blue, c[2].alpha } }, /* Lower left */ + { { max_x - sizes[2].w, max_y - sizes[2].h }, { 1, 1 }, { c[2].red, c[2].green, c[2].blue, c[2].alpha } }, /* Upper right */ - { { max_x, max_y }, { 1, 0 }, }, /* Lower right */ - { { min_x , max_y }, { 0, 0 }, }, /* Lower left */ - { { max_x - sizes[2].w, max_y - sizes[2].h }, { 1, 1 }, }, /* Upper right */ + { { max_x, max_y }, { 1, 0 }, { c[2].red, c[2].green, c[2].blue, c[2].alpha } }, /* Lower right */ + { { min_x , max_y }, { 0, 0 }, { c[2].red, c[2].green, c[2].blue, c[2].alpha } }, /* Lower left */ + { { max_x - sizes[2].w, max_y - sizes[2].h }, { 1, 1 }, { c[2].red, c[2].green, c[2].blue, c[2].alpha } }, /* Upper right */ }, /* Left */ { - { { min_x, min_y }, { 0, 1 }, }, /* Upper left */ - { { min_x, max_y }, { 0, 0 }, }, /* Lower left */ - { { min_x + sizes[0].w, min_y + sizes[0].h }, { 1, 1 }, }, /* Upper right */ + { { min_x, min_y }, { 0, 1 }, { c[3].red, c[3].green, c[3].blue, c[3].alpha } }, /* Upper left */ + { { min_x, max_y }, { 0, 0 }, { c[3].red, c[3].green, c[3].blue, c[3].alpha } }, /* Lower left */ + { { min_x + sizes[0].w, min_y + sizes[0].h }, { 1, 1 }, { c[3].red, c[3].green, c[3].blue, c[3].alpha } }, /* Upper right */ - { { min_x + sizes[3].w, max_y - sizes[3].h }, { 1, 0 }, }, /* Lower right */ - { { min_x, max_y }, { 0, 0 }, }, /* Lower left */ - { { min_x + sizes[0].w, min_y + sizes[0].h }, { 1, 1 }, }, /* Upper right */ + { { min_x + sizes[3].w, max_y - sizes[3].h }, { 1, 0 }, { c[3].red, c[3].green, c[3].blue, c[3].alpha } }, /* Lower right */ + { { min_x, max_y }, { 0, 0 }, { c[3].red, c[3].green, c[3].blue, c[3].alpha } }, /* Lower left */ + { { min_x + sizes[0].w, min_y + sizes[0].h }, { 1, 1 }, { c[3].red, c[3].green, c[3].blue, c[3].alpha } }, /* Upper right */ } }; - int indices[4] = { 0, 1, 2, 3 }; GskRoundedRect outline; GskNglProgram *program; - /* We sort them by color */ - sort_border_sides (colors, indices); - /* Prepare outline */ gsk_ngl_render_job_transform_rounded_rect (job, rounded_outline, &outline); @@ -1881,16 +1853,12 @@ gsk_ngl_render_job_visit_border_node (GskNglRenderJob *job, { GskNglDrawVertex *vertices; - if (widths[indices[i]] <= 0) + if (widths[i] <= 0) continue; gsk_ngl_render_job_begin_draw (job, program); - gsk_ngl_program_set_uniform4fv (job->current_program, - UNIFORM_BORDER_COLOR, 0, - 1, - (const float *)&colors[indices[i]]); vertices = gsk_ngl_command_queue_add_vertices (job->command_queue); - memcpy (vertices, side_data[indices[i]], sizeof (GskNglDrawVertex) * GSK_NGL_N_VERTICES); + memcpy (vertices, side_data[i], sizeof (GskNglDrawVertex) * GSK_NGL_N_VERTICES); gsk_ngl_render_job_end_draw (job); } } @@ -2019,12 +1987,10 @@ gsk_ngl_render_job_visit_unblurred_inset_shadow_node (GskNglRenderJob *job, gsk_ngl_render_job_transform_rounded_rect (job, outline, &transformed_outline); gsk_ngl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, inset_shadow)); + gsk_ngl_render_job_set_color (job, gsk_inset_shadow_node_get_color (node)); gsk_ngl_program_set_uniform_rounded_rect (job->current_program, UNIFORM_INSET_SHADOW_OUTLINE_RECT, 0, &transformed_outline); - gsk_ngl_program_set_uniform_color (job->current_program, - UNIFORM_INSET_SHADOW_COLOR, 0, - gsk_inset_shadow_node_get_color (node)); gsk_ngl_program_set_uniform1f (job->current_program, UNIFORM_INSET_SHADOW_SPREAD, 0, gsk_inset_shadow_node_get_spread (node)); @@ -2122,12 +2088,10 @@ gsk_ngl_render_job_visit_blurred_inset_shadow_node (GskNglRenderJob *job, /* Actual inset shadow outline drawing */ gsk_ngl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, inset_shadow)); + gsk_ngl_render_job_set_color (job, gsk_inset_shadow_node_get_color (node)); gsk_ngl_program_set_uniform_rounded_rect (job->current_program, UNIFORM_INSET_SHADOW_OUTLINE_RECT, 0, &transformed_outline); - gsk_ngl_program_set_uniform_color (job->current_program, - UNIFORM_INSET_SHADOW_COLOR, 0, - gsk_inset_shadow_node_get_color (node)); gsk_ngl_program_set_uniform1f (job->current_program, UNIFORM_INSET_SHADOW_SPREAD, 0, spread * MAX (scale_x, scale_y)); @@ -2231,12 +2195,10 @@ gsk_ngl_render_job_visit_unblurred_outset_shadow_node (GskNglRenderJob *job, gsk_ngl_render_job_transform_rounded_rect (job, outline, &transformed_outline); gsk_ngl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, unblurred_outset_shadow)); + gsk_ngl_render_job_set_color (job, gsk_outset_shadow_node_get_color (node)); gsk_ngl_program_set_uniform_rounded_rect (job->current_program, UNIFORM_UNBLURRED_OUTSET_SHADOW_OUTLINE_RECT, 0, &transformed_outline); - gsk_ngl_program_set_uniform_color (job->current_program, - UNIFORM_UNBLURRED_OUTSET_SHADOW_COLOR, 0, - gsk_outset_shadow_node_get_color (node)); gsk_ngl_program_set_uniform1f (job->current_program, UNIFORM_UNBLURRED_OUTSET_SHADOW_SPREAD, 0, spread); @@ -2387,9 +2349,7 @@ gsk_ngl_render_job_visit_blurred_outset_shadow_node (GskNglRenderJob *job, /* Draw the outline using color program */ gsk_ngl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, color)); - gsk_ngl_program_set_uniform_color (job->current_program, - UNIFORM_COLOR_COLOR, 0, - &white); + gsk_ngl_render_job_set_color (job, &white); gsk_ngl_render_job_draw (job, 0, 0, texture_width, texture_height); gsk_ngl_render_job_end_draw (job); @@ -2433,9 +2393,7 @@ gsk_ngl_render_job_visit_blurred_outset_shadow_node (GskNglRenderJob *job, init_full_texture_region (&offscreen); gsk_ngl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, outset_shadow)); - gsk_ngl_program_set_uniform_color (job->current_program, - UNIFORM_OUTSET_SHADOW_COLOR, 0, - color); + gsk_ngl_render_job_set_color (job, color); gsk_ngl_program_set_uniform_texture (job->current_program, UNIFORM_SHARED_SOURCE, 0, GL_TEXTURE_2D, @@ -2456,9 +2414,7 @@ gsk_ngl_render_job_visit_blurred_outset_shadow_node (GskNglRenderJob *job, } gsk_ngl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, outset_shadow)); - gsk_ngl_program_set_uniform_color (job->current_program, - UNIFORM_OUTSET_SHADOW_COLOR, 0, - color); + gsk_ngl_render_job_set_color (job, color); gsk_ngl_program_set_uniform_texture (job->current_program, UNIFORM_SHARED_SOURCE, 0, GL_TEXTURE_2D, @@ -2747,20 +2703,20 @@ gsk_ngl_render_job_visit_text_node (GskNglRenderJob *job, guint last_texture = 0; GskNglDrawVertex *vertices; guint used = 0; + GdkRGBA c; if (num_glyphs == 0) return; - /* If the font has color glyphs, we don't need to recolor anything */ + program = CHOOSE_PROGRAM (job, coloring); + + /* If the font has color glyphs, we don't need to recolor anything. + * We tell the shader by setting the color to vec4(-1). + */ if (!force_color && gsk_text_node_has_color_glyphs (node)) - { - program = CHOOSE_PROGRAM (job, blit); - } + c = (GdkRGBA) { -1.f, -1.f, -1.f, -1.f }; else - { - program = CHOOSE_PROGRAM (job, coloring); - gsk_ngl_program_set_uniform_color (program, UNIFORM_COLORING_COLOR, 0, color); - } + c = *color; lookup.font = (PangoFont *)font; lookup.scale = (guint) (text_scale * 1024); @@ -2835,31 +2791,55 @@ gsk_ngl_render_job_visit_text_node (GskNglRenderJob *job, vertices[base+0].position[1] = glyph_y; vertices[base+0].uv[0] = tx; vertices[base+0].uv[1] = ty; + vertices[base+0].color[0] = c.red; + vertices[base+0].color[1] = c.green; + vertices[base+0].color[2] = c.blue; + vertices[base+0].color[3] = c.alpha; vertices[base+1].position[0] = glyph_x; vertices[base+1].position[1] = glyph_y2; vertices[base+1].uv[0] = tx; vertices[base+1].uv[1] = ty2; + vertices[base+1].color[0] = c.red; + vertices[base+1].color[1] = c.green; + vertices[base+1].color[2] = c.blue; + vertices[base+1].color[3] = c.alpha; vertices[base+2].position[0] = glyph_x2; vertices[base+2].position[1] = glyph_y; vertices[base+2].uv[0] = tx2; vertices[base+2].uv[1] = ty; + vertices[base+2].color[0] = c.red; + vertices[base+2].color[1] = c.green; + vertices[base+2].color[2] = c.blue; + vertices[base+2].color[3] = c.alpha; vertices[base+3].position[0] = glyph_x2; vertices[base+3].position[1] = glyph_y2; vertices[base+3].uv[0] = tx2; vertices[base+3].uv[1] = ty2; + vertices[base+3].color[0] = c.red; + vertices[base+3].color[1] = c.green; + vertices[base+3].color[2] = c.blue; + vertices[base+3].color[3] = c.alpha; vertices[base+4].position[0] = glyph_x; vertices[base+4].position[1] = glyph_y2; vertices[base+4].uv[0] = tx; vertices[base+4].uv[1] = ty2; + vertices[base+4].color[0] = c.red; + vertices[base+4].color[1] = c.green; + vertices[base+4].color[2] = c.blue; + vertices[base+4].color[3] = c.alpha; vertices[base+5].position[0] = glyph_x2; vertices[base+5].position[1] = glyph_y; vertices[base+5].uv[0] = tx2; vertices[base+5].uv[1] = ty; + vertices[base+5].color[0] = c.red; + vertices[base+5].color[1] = c.green; + vertices[base+5].color[2] = c.blue; + vertices[base+5].color[3] = c.alpha; batch->draw.vbo_count += GSK_NGL_N_VERTICES; used++; @@ -2952,14 +2932,12 @@ gsk_ngl_render_job_visit_shadow_node (GskNglRenderJob *job, gsk_ngl_render_job_offset (job, dx, dy); gsk_ngl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, coloring)); + gsk_ngl_render_job_set_color (job, &shadow->color); gsk_ngl_program_set_uniform_texture (job->current_program, UNIFORM_SHARED_SOURCE, 0, GL_TEXTURE_2D, GL_TEXTURE0, offscreen.texture_id); - gsk_ngl_program_set_uniform_color (job->current_program, - UNIFORM_COLORING_COLOR, 0, - &shadow->color); gsk_ngl_render_job_load_vertices_from_offscreen (job, &bounds, &offscreen); gsk_ngl_render_job_end_draw (job); gsk_ngl_render_job_offset (job, -dx, -dy); @@ -3014,7 +2992,7 @@ gsk_ngl_render_job_visit_blur_node (GskNglRenderJob *job, GL_TEXTURE_2D, GL_TEXTURE0, offscreen.texture_id); - gsk_ngl_render_job_draw_coords (job, min_x, min_y, max_x, max_y); + gsk_ngl_render_job_draw_coords (job, min_x, min_y, max_x, max_y, 0, 1, 1, 0); gsk_ngl_render_job_end_draw (job); } @@ -3123,9 +3101,7 @@ gsk_ngl_render_job_visit_gl_shader_node_fallback (GskNglRenderJob *job, static const GdkRGBA pink = { 255 / 255., 105 / 255., 180 / 255., 1.0 }; gsk_ngl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, color)); - gsk_ngl_program_set_uniform_color (job->current_program, - UNIFORM_COLOR_COLOR, 0, - &pink); + gsk_ngl_render_job_set_color (job, &pink); gsk_ngl_render_job_draw_rect (job, &node->bounds); gsk_ngl_render_job_end_draw (job); } @@ -3317,7 +3293,6 @@ gsk_ngl_render_job_visit_texture_node (GskNglRenderJob *job, for (guint i = 0; i < n_slices; i ++) { - GskNglDrawVertex *vertices; const GskNglTextureSlice *slice = &slices[i]; float x1, x2, y1, y2; @@ -3333,37 +3308,8 @@ gsk_ngl_render_job_visit_texture_node (GskNglRenderJob *job, GL_TEXTURE_2D, GL_TEXTURE0, slice->texture_id); - vertices = gsk_ngl_command_queue_add_vertices (job->command_queue); - vertices[0].position[0] = x1; - vertices[0].position[1] = y1; - vertices[0].uv[0] = 0; - vertices[0].uv[1] = 0; - - vertices[1].position[0] = x1; - vertices[1].position[1] = y2; - vertices[1].uv[0] = 0; - vertices[1].uv[1] = 1; - - vertices[2].position[0] = x2; - vertices[2].position[1] = y1; - vertices[2].uv[0] = 1; - vertices[2].uv[1] = 0; - - vertices[3].position[0] = x2; - vertices[3].position[1] = y2; - vertices[3].uv[0] = 1; - vertices[3].uv[1] = 1; - - vertices[4].position[0] = x1; - vertices[4].position[1] = y2; - vertices[4].uv[0] = 0; - vertices[4].uv[1] = 1; - - vertices[5].position[0] = x2; - vertices[5].position[1] = y1; - vertices[5].uv[0] = 1; - vertices[5].uv[1] = 0; + gsk_ngl_render_job_draw_coords (job, x1, y1, x2, y2, 0, 0, 1, 1); } gsk_ngl_render_job_end_draw (job); @@ -3900,6 +3846,7 @@ gsk_ngl_render_job_new (GskNglDriver *driver, job->viewport = *viewport; gsk_ngl_render_job_set_alpha (job, 1.0); + gsk_ngl_render_job_set_color (job, &(GdkRGBA){ 0.f, 0.f, 0.f, 0.f }); gsk_ngl_render_job_set_projection_from_rect (job, viewport, NULL); gsk_ngl_render_job_set_modelview (job, gsk_transform_scale (NULL, scale_factor, scale_factor)); diff --git a/gsk/ngl/gskngltypesprivate.h b/gsk/ngl/gskngltypesprivate.h index aba6f2f4c9..dc76fb41f4 100644 --- a/gsk/ngl/gskngltypesprivate.h +++ b/gsk/ngl/gskngltypesprivate.h @@ -55,6 +55,7 @@ struct _GskNglDrawVertex { float position[2]; float uv[2]; + float color[4]; }; G_END_DECLS diff --git a/gsk/ngl/resources/blend.glsl b/gsk/ngl/resources/blend.glsl new file mode 100644 index 0000000000..066c20179d --- /dev/null +++ b/gsk/ngl/resources/blend.glsl @@ -0,0 +1,314 @@ +// VERTEX_SHADER: +// blend.glsl + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + + vUv = vec2(aUv.x, aUv.y); +} + +// FRAGMENT_SHADER: +// blend.glsl + +uniform int u_mode; +uniform sampler2D u_source2; + +float +combine (float source, float backdrop) +{ + return source + backdrop * (1.0 - source); +} + +vec4 +composite (vec4 Cs, vec4 Cb, vec3 B) +{ + float ao = Cs.a + Cb.a * (1.0 - Cs.a); + vec3 Co = (Cs.a*(1.0 - Cb.a)*Cs.rgb + Cs.a*Cb.a*B + (1.0 - Cs.a)*Cb.a*Cb.rgb) / ao; + return vec4(Co, ao); +} + +vec4 +normal (vec4 Cs, vec4 Cb) +{ + return composite (Cs, Cb, Cs.rgb); +} + +vec4 +multiply (vec4 Cs, vec4 Cb) +{ + return composite (Cs, Cb, Cs.rgb * Cb.rgb); +} + +vec4 +difference (vec4 Cs, vec4 Cb) +{ + return composite (Cs, Cb, abs(Cs.rgb - Cb.rgb)); +} + +vec4 +screen (vec4 Cs, vec4 Cb) +{ + return composite (Cs, Cb, Cs.rgb + Cb.rgb - Cs.rgb * Cb.rgb); +} + +float +hard_light (float source, float backdrop) +{ + if (source <= 0.5) + return 2.0 * backdrop * source; + else + return 2.0 * (backdrop + source - backdrop * source) - 1.0; +} + +vec4 +hard_light (vec4 Cs, vec4 Cb) +{ + vec3 B = vec3 (hard_light (Cs.r, Cb.r), + hard_light (Cs.g, Cb.g), + hard_light (Cs.b, Cb.b)); + return composite (Cs, Cb, B); +} + +float +soft_light (float source, float backdrop) +{ + float db; + + if (backdrop <= 0.25) + db = ((16.0 * backdrop - 12.0) * backdrop + 4.0) * backdrop; + else + db = sqrt (backdrop); + + if (source <= 0.5) + return backdrop - (1.0 - 2.0 * source) * backdrop * (1.0 - backdrop); + else + return backdrop + (2.0 * source - 1.0) * (db - backdrop); +} + +vec4 +soft_light (vec4 Cs, vec4 Cb) +{ + vec3 B = vec3 (soft_light (Cs.r, Cb.r), + soft_light (Cs.g, Cb.g), + soft_light (Cs.b, Cb.b)); + return composite (Cs, Cb, B); +} + +vec4 +overlay (vec4 Cs, vec4 Cb) +{ + vec3 B = vec3 (hard_light (Cb.r, Cs.r), + hard_light (Cb.g, Cs.g), + hard_light (Cb.b, Cs.b)); + return composite (Cs, Cb, B); +} + +vec4 +darken (vec4 Cs, vec4 Cb) +{ + vec3 B = min (Cs.rgb, Cb.rgb); + return composite (Cs, Cb, B); +} + +vec4 +lighten (vec4 Cs, vec4 Cb) +{ + vec3 B = max (Cs.rgb, Cb.rgb); + return composite (Cs, Cb, B); +} + +float +color_dodge (float source, float backdrop) +{ + return (source == 1.0) ? source : min (backdrop / (1.0 - source), 1.0); +} + +vec4 +color_dodge (vec4 Cs, vec4 Cb) +{ + vec3 B = vec3 (color_dodge (Cs.r, Cb.r), + color_dodge (Cs.g, Cb.g), + color_dodge (Cs.b, Cb.b)); + return composite (Cs, Cb, B); +} + + +float +color_burn (float source, float backdrop) +{ + return (source == 0.0) ? source : max ((1.0 - ((1.0 - backdrop) / source)), 0.0); +} + +vec4 +color_burn (vec4 Cs, vec4 Cb) +{ + vec3 B = vec3 (color_burn (Cs.r, Cb.r), + color_burn (Cs.g, Cb.g), + color_burn (Cs.b, Cb.b)); + return composite (Cs, Cb, B); +} + +vec4 +exclusion (vec4 Cs, vec4 Cb) +{ + vec3 B = Cb.rgb + Cs.rgb - 2.0 * Cb.rgb * Cs.rgb; + return composite (Cs, Cb, B); +} + +float +lum (vec3 c) +{ + return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; +} + +vec3 +clip_color (vec3 c) +{ + float l = lum (c); + float n = min (c.r, min (c.g, c.b)); + float x = max (c.r, max (c.g, c.b)); + if (n < 0.0) c = l + (((c - l) * l) / (l - n)); + if (x > 1.0) c = l + (((c - l) * (1.0 - l)) / (x - l)); + return c; +} + +vec3 +set_lum (vec3 c, float l) +{ + float d = l - lum (c); + return clip_color (vec3 (c.r + d, c.g + d, c.b + d)); +} + +float +sat (vec3 c) +{ + return max (c.r, max (c.g, c.b)) - min (c.r, min (c.g, c.b)); +} + +vec3 +set_sat (vec3 c, float s) +{ + float cmin = min (c.r, min (c.g, c.b)); + float cmax = max (c.r, max (c.g, c.b)); + vec3 res; + + if (cmax == cmin) + res = vec3 (0, 0, 0); + else + { + if (c.r == cmax) + { + if (c.g == cmin) + { + res.b = ((c.b - cmin) * s) / (cmax - cmin); + res.g = 0.0; + } + else + { + res.g = ((c.g - cmin) * s) / (cmax - cmin); + res.b = 0.0; + } + res.r = s; + } + else if (c.g == cmax) + { + if (c.r == cmin) + { + res.b = ((c.b - cmin) * s) / (cmax - cmin); + res.r = 0.0; + } + else + { + res.r = ((c.r - cmin) * s) / (cmax - cmin); + res.b = 0.0; + } + res.g = s; + } + else + { + if (c.r == cmin) + { + res.g = ((c.g - cmin) * s) / (cmax - cmin); + res.r = 0.0; + } + else + { + res.r = ((c.r - cmin) * s) / (cmax - cmin); + res.g = 0.0; + } + res.b = s; + } + } + return res; +} + +vec4 +color (vec4 Cs, vec4 Cb) +{ + vec3 B = set_lum (Cs.rgb, lum (Cb.rgb)); + return composite (Cs, Cb, B); +} + +vec4 +hue (vec4 Cs, vec4 Cb) +{ + vec3 B = set_lum (set_sat (Cs.rgb, sat (Cb.rgb)), lum (Cb.rgb)); + return composite (Cs, Cb, B); +} + +vec4 +saturation (vec4 Cs, vec4 Cb) +{ + vec3 B = set_lum (set_sat (Cb.rgb, sat (Cs.rgb)), lum (Cb.rgb)); + return composite (Cs, Cb, B); +} + +vec4 +luminosity (vec4 Cs, vec4 Cb) +{ + vec3 B = set_lum (Cb.rgb, lum (Cs.rgb)); + return composite (Cs, Cb, B); +} + +void main() { + vec4 bottom_color = GskTexture(u_source, vUv); + vec4 top_color = GskTexture(u_source2, vUv); + + vec4 result; + if (u_mode == 0) + result = normal(top_color, bottom_color); + else if (u_mode == 1) + result = multiply(top_color, bottom_color); + else if (u_mode == 2) + result = screen(top_color, bottom_color); + else if (u_mode == 3) + result = overlay(top_color, bottom_color); + else if (u_mode == 4) + result = darken(top_color, bottom_color); + else if (u_mode == 5) + result = lighten(top_color, bottom_color); + else if (u_mode == 6) + result = color_dodge(top_color, bottom_color); + else if (u_mode == 7) + result = color_burn(top_color, bottom_color); + else if (u_mode == 8) + result = hard_light(top_color, bottom_color); + else if (u_mode == 9) + result = soft_light(top_color, bottom_color); + else if (u_mode == 10) + result = difference(top_color, bottom_color); + else if (u_mode == 11) + result = exclusion(top_color, bottom_color); + else if (u_mode == 12) + result = color(top_color, bottom_color); + else if (u_mode == 13) + result = hue(top_color, bottom_color); + else if (u_mode == 14) + result = saturation(top_color, bottom_color); + else if (u_mode == 15) + result = luminosity(top_color, bottom_color); + else + discard; + + gskSetOutputColor(result * u_alpha); +} diff --git a/gsk/ngl/resources/blit.glsl b/gsk/ngl/resources/blit.glsl new file mode 100644 index 0000000000..c232be2bfd --- /dev/null +++ b/gsk/ngl/resources/blit.glsl @@ -0,0 +1,17 @@ +// VERTEX_SHADER: +// blit.glsl + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + + vUv = vec2(aUv.x, aUv.y); +} + +// FRAGMENT_SHADER: +// blit.glsl + +void main() { + vec4 diffuse = GskTexture(u_source, vUv); + + gskSetOutputColor(diffuse * u_alpha); +} diff --git a/gsk/ngl/resources/blur.glsl b/gsk/ngl/resources/blur.glsl new file mode 100644 index 0000000000..7e7c1c2518 --- /dev/null +++ b/gsk/ngl/resources/blur.glsl @@ -0,0 +1,59 @@ +// VERTEX_SHADER: +// blur.glsl + +uniform float u_blur_radius; +uniform vec2 u_blur_size; +uniform vec2 u_blur_dir; + +_OUT_ vec2 pixel_step; +_OUT_ float pixels_per_side; +_OUT_ vec3 initial_gaussian; + +const float PI = 3.14159265; +const float RADIUS_MULTIPLIER = 2.0; + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + + vUv = vec2(aUv.x, aUv.y); + + pixel_step = (vec2(1.0) / u_blur_size) * u_blur_dir; + pixels_per_side = floor(u_blur_radius * RADIUS_MULTIPLIER / 2.0); + + float sigma = u_blur_radius / 2.0; // *shrug* + initial_gaussian.x = 1.0 / (sqrt(2.0 * PI) * sigma); + initial_gaussian.y = exp(-0.5 / (sigma * sigma)); + initial_gaussian.z = initial_gaussian.y * initial_gaussian.y; +} + +// FRAGMENT_SHADER: +// blur.glsl + +_IN_ vec2 pixel_step; +_IN_ float pixels_per_side; +_IN_ vec3 initial_gaussian; + +// blur_radius 0 is NOT supported and MUST be caught before. + +// Partially from http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html +void main() { + vec3 incrementalGaussian = initial_gaussian; + + float coefficientSum = 0.0; + vec4 sum = GskTexture(u_source, vUv) * incrementalGaussian.x; + coefficientSum += incrementalGaussian.x; + incrementalGaussian.xy *= incrementalGaussian.yz; + + vec2 p = pixel_step; + for (int i = 1; i <= int(pixels_per_side); i++) { + sum += GskTexture(u_source, vUv - p) * incrementalGaussian.x; + sum += GskTexture(u_source, vUv + p) * incrementalGaussian.x; + + coefficientSum += 2.0 * incrementalGaussian.x; + incrementalGaussian.xy *= incrementalGaussian.yz; + + p += pixel_step; + } + + gskSetOutputColor(sum / coefficientSum); +} diff --git a/gsk/ngl/resources/border.glsl b/gsk/ngl/resources/border.glsl new file mode 100644 index 0000000000..a9c04556a4 --- /dev/null +++ b/gsk/ngl/resources/border.glsl @@ -0,0 +1,43 @@ +// VERTEX_SHADER: +// border.glsl + +uniform vec4 u_widths; +uniform vec4[3] u_outline_rect; + +_OUT_ vec4 final_color; +_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; +_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + + final_color = gsk_premultiply(aColor) * u_alpha; + + GskRoundedRect outside = gsk_create_rect(u_outline_rect); + GskRoundedRect inside = gsk_rounded_rect_shrink (outside, u_widths); + + gsk_rounded_rect_transform(outside, u_modelview); + gsk_rounded_rect_transform(inside, u_modelview); + + gsk_rounded_rect_encode(outside, transformed_outside_outline); + gsk_rounded_rect_encode(inside, transformed_inside_outline); +} + +// FRAGMENT_SHADER: +// border.glsl + +uniform vec4[3] u_outline_rect; + +_IN_ vec4 final_color; +_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; +_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; + +void main() { + vec2 frag = gsk_get_frag_coord(); + + float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) - + gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag), + 0.0, 1.0); + + gskSetOutputColor(final_color * alpha); +} diff --git a/gsk/ngl/resources/color.glsl b/gsk/ngl/resources/color.glsl new file mode 100644 index 0000000000..670b9979a2 --- /dev/null +++ b/gsk/ngl/resources/color.glsl @@ -0,0 +1,20 @@ +// VERTEX_SHADER: +// color.glsl + +_OUT_ vec4 final_color; + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + + final_color = gsk_premultiply(aColor) * u_alpha; +} + +// FRAGMENT_SHADER: +// color.glsl + +_IN_ vec4 final_color; + +void main() { + gskSetOutputColor(final_color); +} + diff --git a/gsk/ngl/resources/color_matrix.glsl b/gsk/ngl/resources/color_matrix.glsl new file mode 100644 index 0000000000..44dcf9a044 --- /dev/null +++ b/gsk/ngl/resources/color_matrix.glsl @@ -0,0 +1,29 @@ +// VERTEX_SHADER: +// color_matrix.glsl + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + + vUv = vec2(aUv.x, aUv.y); +} + +// FRAGMENT_SHADER: +// color_matrix.glsl + +uniform mat4 u_color_matrix; +uniform vec4 u_color_offset; + +void main() { + vec4 color = GskTexture(u_source, vUv); + + // Un-premultilpy + if (color.a != 0.0) + color.rgb /= color.a; + + color = u_color_matrix * color + u_color_offset; + color = clamp(color, 0.0, 1.0); + + color.rgb *= color.a; + + gskSetOutputColor(color * u_alpha); +} diff --git a/gsk/ngl/resources/coloring.glsl b/gsk/ngl/resources/coloring.glsl new file mode 100644 index 0000000000..69b21c6e2e --- /dev/null +++ b/gsk/ngl/resources/coloring.glsl @@ -0,0 +1,36 @@ +// VERTEX_SHADER: +// coloring.glsl + +_OUT_ vec4 final_color; +flat _OUT_ int use_color; + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + + vUv = vec2(aUv.x, aUv.y); + + // We use this shader for both plain glyphs (used as mask) + // and color glpyhs (used as source). The renderer sets + // aColor to vec4(-1) for color glyhs. + if (distance(aColor, vec4(-1)) < 0.001) + use_color = 0; + else + use_color = 1; + + final_color = gsk_premultiply(aColor) * u_alpha; +} + +// FRAGMENT_SHADER: +// coloring.glsl + +_IN_ vec4 final_color; +flat _IN_ int use_color; + +void main() { + vec4 diffuse = GskTexture(u_source, vUv); + + if (use_color == 1) + gskSetOutputColor(final_color * diffuse.a); + else + gskSetOutputColor(diffuse * u_alpha); +} diff --git a/gsk/ngl/resources/conic_gradient.glsl b/gsk/ngl/resources/conic_gradient.glsl new file mode 100644 index 0000000000..afb427bb15 --- /dev/null +++ b/gsk/ngl/resources/conic_gradient.glsl @@ -0,0 +1,76 @@ +// VERTEX_SHADER +// conic_gradient.glsl + +uniform vec4 u_geometry; + +_NOPERSPECTIVE_ _OUT_ vec2 coord; + +void main() { + gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0)); + + vec2 mv0 = u_modelview[0].xy; + vec2 mv1 = u_modelview[1].xy; + vec2 offset = aPosition - u_geometry.xy; + + coord = vec2(dot(mv0, offset), dot(mv1, offset)); +} + +// FRAGMENT_SHADER: +// conic_gradient.glsl + +#ifdef GSK_LEGACY +uniform int u_num_color_stops; +#else +uniform highp int u_num_color_stops; // Why? Because it works like this. +#endif + +uniform vec4 u_geometry; +uniform float u_color_stops[6 * 5]; + +_NOPERSPECTIVE_ _IN_ vec2 coord; + +float get_offset(int index) { + return u_color_stops[5 * index]; +} + +vec4 get_color(int index) { + int base = 5 * index + 1; + + return vec4(u_color_stops[base], + u_color_stops[base + 1], + u_color_stops[base + 2], + u_color_stops[base + 3]); +} + +void main() { + // direction of point in range [-PI, PI] + vec2 pos = floor(coord); + float angle = atan(pos.y, pos.x); + + // fract() does the modulo here, so now we have progress + // into the current conic + float offset = fract(angle * u_geometry.z + u_geometry.w); + + if (offset < get_offset(0)) { + gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha)); + return; + } + + int n = u_num_color_stops - 1; + for (int i = 0; i < n; i++) { + float curr_offset = get_offset(i); + float next_offset = get_offset(i + 1); + + if (offset >= curr_offset && offset < next_offset) { + float f = (offset - curr_offset) / (next_offset - curr_offset); + vec4 curr_color = gsk_premultiply(get_color(i)); + vec4 next_color = gsk_premultiply(get_color(i + 1)); + vec4 color = mix(curr_color, next_color, f); + + gskSetOutputColor(color * u_alpha); + return; + } + } + + gskSetOutputColor(gsk_scaled_premultiply(get_color(n), u_alpha)); +} diff --git a/gsk/ngl/resources/cross_fade.glsl b/gsk/ngl/resources/cross_fade.glsl new file mode 100644 index 0000000000..e61af597e9 --- /dev/null +++ b/gsk/ngl/resources/cross_fade.glsl @@ -0,0 +1,24 @@ +// VERTEX_SHADER: +// cross_fade.glsl + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + + vUv = vec2(aUv.x, aUv.y); +} + +// FRAGMENT_SHADER: +// cross_fade.glsl + +uniform float u_progress; +uniform sampler2D u_source2; + +void main() { + vec4 source1 = GskTexture(u_source, vUv); // start child + vec4 source2 = GskTexture(u_source2, vUv); // end child + + float p_start = (1.0 - u_progress) * u_alpha; + float p_end = u_progress * u_alpha; + vec4 color = (p_start * source1) + (p_end * source2); + gskSetOutputColor(color); +} diff --git a/gsk/ngl/resources/custom.glsl b/gsk/ngl/resources/custom.glsl new file mode 100644 index 0000000000..6f91df6cf4 --- /dev/null +++ b/gsk/ngl/resources/custom.glsl @@ -0,0 +1,25 @@ +// VERTEX_SHADER: +// custom.glsl + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + vUv = vec2(aUv.x, aUv.y); +} + +// FRAGMENT_SHADER: +// custom.glsl + +// The shader supplies: +void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv); + +uniform vec2 u_size; +uniform sampler2D u_source2; +uniform sampler2D u_source3; +uniform sampler2D u_source4; + +void main() { + vec4 fragColor; + vec2 fragCoord = vec2(vUv.x * u_size.x, (1.0-vUv.y) * u_size.y); + mainImage(fragColor, fragCoord, u_size, vUv); + gskSetOutputColor(fragColor); +} diff --git a/gsk/ngl/resources/inset_shadow.glsl b/gsk/ngl/resources/inset_shadow.glsl new file mode 100644 index 0000000000..1c3c955035 --- /dev/null +++ b/gsk/ngl/resources/inset_shadow.glsl @@ -0,0 +1,44 @@ +// VERTEX_SHADER: +// inset_shadow.glsl + +uniform float u_spread; +uniform vec2 u_offset; +uniform vec4[3] u_outline_rect; + +_OUT_ vec4 final_color; +_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; +_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + + final_color = gsk_premultiply(aColor) * u_alpha; + + GskRoundedRect outside = gsk_create_rect(u_outline_rect); + GskRoundedRect inside = gsk_rounded_rect_shrink(outside, vec4(u_spread)); + + gsk_rounded_rect_offset(inside, u_offset); + + gsk_rounded_rect_transform(outside, u_modelview); + gsk_rounded_rect_transform(inside, u_modelview); + + gsk_rounded_rect_encode(outside, transformed_outside_outline); + gsk_rounded_rect_encode(inside, transformed_inside_outline); +} + +// FRAGMENT_SHADER: +// inset_shadow.glsl + +_IN_ vec4 final_color; +_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; +_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; + +void main() { + vec2 frag = gsk_get_frag_coord(); + + float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) - + gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag), + 0.0, 1.0); + + gskSetOutputColor(final_color * alpha); +} diff --git a/gsk/ngl/resources/linear_gradient.glsl b/gsk/ngl/resources/linear_gradient.glsl new file mode 100644 index 0000000000..1a2f2675e0 --- /dev/null +++ b/gsk/ngl/resources/linear_gradient.glsl @@ -0,0 +1,98 @@ +// VERTEX_SHADER +// linear_gradient.glsl +uniform vec4 u_points; + +_NOPERSPECTIVE_ _OUT_ vec4 info; + +void main() { + gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0)); + + vec2 mv0 = u_modelview[0].xy; + vec2 mv1 = u_modelview[1].xy; + vec2 offset = aPosition - u_points.xy; + vec2 coord = vec2(dot(mv0, offset), + dot(mv1, offset)); + + // Original equation: + // VS | maxDist = length(end - start); + // VS | gradient = end - start; + // VS | gradientLength = length(gradient); + // FS | pos = frag_coord - start + // FS | proj = (dot(gradient, pos) / (gradientLength * gradientLength)) * gradient + // FS | offset = length(proj) / maxDist + + // Simplified formula derivation: + // 1. Notice that maxDist = gradientLength: + // offset = length(proj) / gradientLength + // 2. Let gnorm = gradient / gradientLength, then: + // proj = (dot(gnorm * gradientLength, pos) / (gradientLength * gradientLength)) * (gnorm * gradientLength) = + // = dot(gnorm, pos) * gnorm + // 3. Since gnorm is unit length then: + // length(proj) = length(dot(gnorm, pos) * gnorm) = dot(gnorm, pos) + // 4. We can avoid the FS division by passing a scaled pos from the VS: + // offset = dot(gnorm, pos) / gradientLength = dot(gnorm, pos / gradientLength) + // 5. 1.0 / length(gradient) is inversesqrt(dot(gradient, gradient)) in GLSL + vec2 gradient = vec2(dot(mv0, u_points.zw), + dot(mv1, u_points.zw)); + float rcp_gradient_length = inversesqrt(dot(gradient, gradient)); + + info = rcp_gradient_length * vec4(coord, gradient); +} + +// FRAGMENT_SHADER: +// linear_gradient.glsl + +#ifdef GSK_LEGACY +uniform int u_num_color_stops; +#else +uniform highp int u_num_color_stops; // Why? Because it works like this. +#endif + +uniform float u_color_stops[6 * 5]; +uniform bool u_repeat; + +_NOPERSPECTIVE_ _IN_ vec4 info; + +float get_offset(int index) { + return u_color_stops[5 * index]; +} + +vec4 get_color(int index) { + int base = 5 * index + 1; + + return vec4(u_color_stops[base], + u_color_stops[base + 1], + u_color_stops[base + 2], + u_color_stops[base + 3]); +} + +void main() { + float offset = dot(info.xy, info.zw); + + if (u_repeat) { + offset = fract(offset); + } + + if (offset < get_offset(0)) { + gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha)); + return; + } + + int n = u_num_color_stops - 1; + for (int i = 0; i < n; i++) { + float curr_offset = get_offset(i); + float next_offset = get_offset(i + 1); + + if (offset >= curr_offset && offset < next_offset) { + float f = (offset - curr_offset) / (next_offset - curr_offset); + vec4 curr_color = gsk_premultiply(get_color(i)); + vec4 next_color = gsk_premultiply(get_color(i + 1)); + vec4 color = mix(curr_color, next_color, f); + + gskSetOutputColor(color * u_alpha); + return; + } + } + + gskSetOutputColor(gsk_scaled_premultiply(get_color(n), u_alpha)); +} diff --git a/gsk/ngl/resources/outset_shadow.glsl b/gsk/ngl/resources/outset_shadow.glsl new file mode 100644 index 0000000000..427aee956c --- /dev/null +++ b/gsk/ngl/resources/outset_shadow.glsl @@ -0,0 +1,36 @@ +// VERTEX_SHADER: +// outset_shadow.glsl + +uniform vec4[3] u_outline_rect; + +_OUT_ vec4 final_color; +_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outline; + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + + vUv = vec2(aUv.x, aUv.y); + + final_color = gsk_premultiply(aColor) * u_alpha; + + GskRoundedRect outline = gsk_create_rect(u_outline_rect); + gsk_rounded_rect_transform(outline, u_modelview); + gsk_rounded_rect_encode(outline, transformed_outline); +} + +// FRAGMENT_SHADER: +// outset_shadow.glsl + +_IN_ vec4 final_color; +_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outline; + +void main() { + vec2 frag = gsk_get_frag_coord(); + float alpha = GskTexture(u_source, vUv).a; + + alpha *= (1.0 - clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outline), frag), 0.0, 1.0)); + + vec4 color = final_color * alpha; + + gskSetOutputColor(color); +} diff --git a/gsk/ngl/resources/preamble.fs.glsl b/gsk/ngl/resources/preamble.fs.glsl new file mode 100644 index 0000000000..e715d2526d --- /dev/null +++ b/gsk/ngl/resources/preamble.fs.glsl @@ -0,0 +1,138 @@ +uniform sampler2D u_source; +uniform mat4 u_projection; +uniform mat4 u_modelview; +uniform float u_alpha; +uniform vec4 u_viewport; +uniform vec4[3] u_clip_rect; + +#if defined(GSK_LEGACY) +_OUT_ vec4 outputColor; +#elif !defined(GSK_GLES) +_OUT_ vec4 outputColor; +#endif + +_IN_ vec2 vUv; + + +GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r) +{ +#if defined(GSK_GLES) || defined(GSK_LEGACY) + return GskRoundedRect(r[0], r[1], r[2]); +#else + return r; +#endif +} + +float +gsk_ellipsis_dist (vec2 p, vec2 radius) +{ + if (radius == vec2(0, 0)) + return 0.0; + + vec2 p0 = p / radius; + vec2 p1 = 2.0 * p0 / radius; + + return (dot(p0, p0) - 1.0) / length (p1); +} + +float +gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius) +{ + float d = gsk_ellipsis_dist (point - center, radius); + return clamp (0.5 - d, 0.0, 1.0); +} + +float +gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p) +{ + if (p.x < r.bounds.x || p.y < r.bounds.y || + p.x >= r.bounds.z || p.y >= r.bounds.w) + return 0.0; + + vec2 ref_tl = r.corner_points1.xy; + vec2 ref_tr = r.corner_points1.zw; + vec2 ref_br = r.corner_points2.xy; + vec2 ref_bl = r.corner_points2.zw; + + if (p.x >= ref_tl.x && p.x >= ref_bl.x && + p.x <= ref_tr.x && p.x <= ref_br.x) + return 1.0; + + if (p.y >= ref_tl.y && p.y >= ref_tr.y && + p.y <= ref_bl.y && p.y <= ref_br.y) + return 1.0; + + vec2 rad_tl = r.corner_points1.xy - r.bounds.xy; + vec2 rad_tr = r.corner_points1.zw - r.bounds.zy; + vec2 rad_br = r.corner_points2.xy - r.bounds.zw; + vec2 rad_bl = r.corner_points2.zw - r.bounds.xw; + + float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl); + float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr); + float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br); + float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl); + + vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl); + + bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y, + p.x > ref_tr.x && p.y < ref_tr.y, + p.x > ref_br.x && p.y > ref_br.y, + p.x < ref_bl.x && p.y > ref_bl.y); + + return 1.0 - dot(vec4(is_out), corner_coverages); +} + +float +gsk_rect_coverage (vec4 r, vec2 p) +{ + if (p.x < r.x || p.y < r.y || + p.x >= r.z || p.y >= r.w) + return 0.0; + + return 1.0; +} + +vec4 GskTexture(sampler2D sampler, vec2 texCoords) { +#if defined(GSK_GLES) || defined(GSK_LEGACY) + return texture2D(sampler, texCoords); +#else + return texture(sampler, texCoords); +#endif +} + +#ifdef GSK_GL3 +layout(origin_upper_left) in vec4 gl_FragCoord; +#endif + +vec2 gsk_get_frag_coord() { + vec2 fc = gl_FragCoord.xy; + +#ifdef GSK_GL3 + fc += u_viewport.xy; +#else + fc.x += u_viewport.x; + fc.y = (u_viewport.y + u_viewport.w) - fc.y; +#endif + + return fc; +} + +void gskSetOutputColor(vec4 color) { + vec4 result; + +#if defined(NO_CLIP) + result = color; +#elif defined(RECT_CLIP) + result = color * gsk_rect_coverage(gsk_get_bounds(u_clip_rect), + gsk_get_frag_coord()); +#else + result = color * gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect), + gsk_get_frag_coord()); +#endif + +#if defined(GSK_GLES) || defined(GSK_LEGACY) + gl_FragColor = result; +#else + outputColor = result; +#endif +} diff --git a/gsk/ngl/resources/preamble.glsl b/gsk/ngl/resources/preamble.glsl new file mode 100644 index 0000000000..8bc007ba67 --- /dev/null +++ b/gsk/ngl/resources/preamble.glsl @@ -0,0 +1,57 @@ +#ifndef GSK_LEGACY +precision highp float; +#endif + +#if defined(GSK_GLES) || defined(GSK_LEGACY) +#define _OUT_ varying +#define _IN_ varying +#define _NOPERSPECTIVE_ +#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3] +#else +#define _OUT_ out +#define _IN_ in +#define _NOPERSPECTIVE_ noperspective +#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect +#endif + + +struct GskRoundedRect +{ + vec4 bounds; // Top left and bottom right + // Look, arrays can't be in structs if you want to return the struct + // from a function in gles or whatever. Just kill me. + vec4 corner_points1; // xy = top left, zw = top right + vec4 corner_points2; // xy = bottom right, zw = bottom left +}; + +// Transform from a C GskRoundedRect to what we need. +GskRoundedRect +gsk_create_rect(vec4[3] data) +{ + vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw); + + vec4 corner_points1 = vec4(bounds.xy + data[1].xy, + bounds.zy + vec2(data[1].zw * vec2(-1, 1))); + vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)), + bounds.xw + vec2(data[2].zw * vec2(1, -1))); + + return GskRoundedRect(bounds, corner_points1, corner_points2); +} + +vec4 +gsk_get_bounds(vec4[3] data) +{ + return vec4(data[0].xy, data[0].xy + data[0].zw); +} + +vec4 gsk_premultiply(vec4 c) { + return vec4(c.rgb * c.a, c.a); +} + +vec4 gsk_scaled_premultiply(vec4 c, float s) { + // Fast version of gsk_premultiply(c) * s + // 4 muls instead of 7 + float a = s * c.a; + + return vec4(c.rgb * a, a); +} diff --git a/gsk/ngl/resources/preamble.vs.glsl b/gsk/ngl/resources/preamble.vs.glsl new file mode 100644 index 0000000000..a549ff6ded --- /dev/null +++ b/gsk/ngl/resources/preamble.vs.glsl @@ -0,0 +1,71 @@ +uniform mat4 u_projection; +uniform mat4 u_modelview; +uniform float u_alpha; + +#if defined(GSK_GLES) || defined(GSK_LEGACY) +attribute vec2 aPosition; +attribute vec2 aUv; +attribute vec4 aColor; +_OUT_ vec2 vUv; +#else +_IN_ vec2 aPosition; +_IN_ vec2 aUv; +_IN_ vec4 aColor; +_OUT_ vec2 vUv; +#endif + +// amount is: top, right, bottom, left +GskRoundedRect +gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount) +{ + vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz; + vec4 new_corner_points1 = r.corner_points1; + vec4 new_corner_points2 = r.corner_points2; + + if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy; + if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy; + if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw; + if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw; + + return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2); +} + +void +gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset) +{ + r.bounds.xy += offset; + r.bounds.zw += offset; + r.corner_points1.xy += offset; + r.corner_points1.zw += offset; + r.corner_points2.xy += offset; + r.corner_points2.zw += offset; +} + +void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat) +{ + r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy; + r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy; + + r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy; + r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy; + + r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy; + r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy; +} + +#if defined(GSK_LEGACY) +// Can't have out or inout array parameters... +#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2; +#else +void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r) +{ +#if defined(GSK_GLES) + out_r[0] = r.bounds; + out_r[1] = r.corner_points1; + out_r[2] = r.corner_points2; +#else + out_r = r; +#endif +} + +#endif diff --git a/gsk/ngl/resources/radial_gradient.glsl b/gsk/ngl/resources/radial_gradient.glsl new file mode 100644 index 0000000000..e8b57ef635 --- /dev/null +++ b/gsk/ngl/resources/radial_gradient.glsl @@ -0,0 +1,78 @@ +// VERTEX_SHADER +// radial_gradient.glsl + +uniform vec4 u_geometry; + +_NOPERSPECTIVE_ _OUT_ vec2 coord; + +void main() { + gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0)); + + vec2 mv0 = u_modelview[0].xy; + vec2 mv1 = u_modelview[1].xy; + vec2 offset = aPosition - u_geometry.xy; + vec2 dir = vec2(dot(mv0, offset), + dot(mv1, offset)); + + coord = dir * u_geometry.zw; +} + +// FRAGMENT_SHADER: +// radial_gradient.glsl + +#ifdef GSK_LEGACY +uniform int u_num_color_stops; +#else +uniform highp int u_num_color_stops; +#endif + +uniform bool u_repeat; +uniform vec2 u_range; +uniform float u_color_stops[6 * 5]; + +_NOPERSPECTIVE_ _IN_ vec2 coord; + +float get_offset(int index) { + return u_color_stops[5 * index]; +} + +vec4 get_color(int index) { + int base = 5 * index + 1; + + return vec4(u_color_stops[base], + u_color_stops[base + 1], + u_color_stops[base + 2], + u_color_stops[base + 3]); +} + +void main() { + // Reverse scale + float offset = length(coord) * u_range.x + u_range.y; + + if (u_repeat) { + offset = fract(offset); + } + + if (offset < get_offset(0)) { + gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha)); + return; + } + + int n = u_num_color_stops - 1; + for (int i = 0; i < n; i++) { + float curr_offset = get_offset(i); + float next_offset = get_offset(i + 1); + + if (offset >= curr_offset && offset < next_offset) { + float f = (offset - curr_offset) / (next_offset - curr_offset); + vec4 curr_color = gsk_premultiply(get_color(i)); + vec4 next_color = gsk_premultiply(get_color(i + 1)); + vec4 color = mix(curr_color, next_color, f); + + gskSetOutputColor(color * u_alpha); + return; + } + } + + gskSetOutputColor(gsk_scaled_premultiply(get_color(n), u_alpha)); +} diff --git a/gsk/ngl/resources/repeat.glsl b/gsk/ngl/resources/repeat.glsl new file mode 100644 index 0000000000..5d39603396 --- /dev/null +++ b/gsk/ngl/resources/repeat.glsl @@ -0,0 +1,44 @@ +// VERTEX_SHADER: +// repeat.glsl + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + + vUv = vec2(aUv.x, aUv.y); +} + +// FRAGMENT_SHADER: +// repeat.glsl + +uniform vec4 u_child_bounds; +uniform vec4 u_texture_rect; + +float wrap(float f, float wrap_for) { + return mod(f, wrap_for); +} + +/* We get the texture coordinates via vUv, + * but that might be on a texture atlas, so we need to do the + * wrapping ourselves. + */ +void main() { + + /* We map the texture coordinate to [1;0], then wrap it and scale the result again */ + + float tw = u_texture_rect.z - u_texture_rect.x; + float th = u_texture_rect.w - u_texture_rect.y; + + float mapped_x = (vUv.x - u_texture_rect.x) / tw; + float mapped_y = (vUv.y - u_texture_rect.y) / th; + + float wrapped_x = wrap(u_child_bounds.x + mapped_x * u_child_bounds.z, 1.0); + float wrapped_y = wrap(u_child_bounds.y + mapped_y * u_child_bounds.w, 1.0); + + vec2 tp; + tp.x = u_texture_rect.x + (wrapped_x * tw); + tp.y = u_texture_rect.y + (wrapped_y * th); + + vec4 diffuse = GskTexture(u_source, tp); + + gskSetOutputColor(diffuse * u_alpha); +} diff --git a/gsk/ngl/resources/unblurred_outset_shadow.glsl b/gsk/ngl/resources/unblurred_outset_shadow.glsl new file mode 100644 index 0000000000..305cc5f1e2 --- /dev/null +++ b/gsk/ngl/resources/unblurred_outset_shadow.glsl @@ -0,0 +1,44 @@ +// VERTEX_SHADER: +// unblurred_outset_shadow.glsl + +uniform float u_spread; +uniform vec2 u_offset; +uniform vec4[3] u_outline_rect; + +_OUT_ vec4 final_color; +_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; +_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; + +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + + final_color = gsk_premultiply(aColor) * u_alpha; + + GskRoundedRect inside = gsk_create_rect(u_outline_rect); + GskRoundedRect outside = gsk_rounded_rect_shrink(inside, vec4(- u_spread)); + + gsk_rounded_rect_offset(outside, u_offset); + + gsk_rounded_rect_transform(outside, u_modelview); + gsk_rounded_rect_transform(inside, u_modelview); + + gsk_rounded_rect_encode(outside, transformed_outside_outline); + gsk_rounded_rect_encode(inside, transformed_inside_outline); +} + +// FRAGMENT_SHADER: +// unblurred_outset_shadow.glsl +_IN_ vec4 final_color; +_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; +_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; + +void main() { + vec2 frag = gsk_get_frag_coord(); + + float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) - + gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag), + 0.0, 1.0); + + gskSetOutputColor(final_color * alpha); +} +