Merge branch 'gsk-flip-tests' into 'main'

more gsk tests

See merge request GNOME/gtk!5811
This commit is contained in:
Matthias Clasen 2023-05-01 21:06:32 +00:00
commit 64f9d82506
25 changed files with 839 additions and 203 deletions

View File

@ -50,6 +50,7 @@ case "${backend}" in
--suite=gtk \
--no-suite=failing \
--no-suite=flaky \
--no-suite=${backend}_failing \
--no-suite=gsk-compare-broadway
exit_code=$?

View File

@ -356,7 +356,8 @@ gsk_gl_renderer_render_texture (GskRenderer *renderer,
for (x = 0; x < width; x += max_size)
{
texture = gsk_gl_renderer_render_texture (renderer, root,
&GRAPHENE_RECT_INIT (x, y,
&GRAPHENE_RECT_INIT (viewport->origin.x + x,
viewport->origin.y + y,
MIN (max_size, viewport->size.width - x),
MIN (max_size, viewport->size.height - y)));
gdk_texture_download (texture,

View File

@ -54,27 +54,6 @@
/* Make sure gradient stops fits in packed array_count */
G_STATIC_ASSERT ((MAX_GRADIENT_STOPS * 5) < (1 << GSK_GL_UNIFORM_ARRAY_BITS));
#define rounded_rect_top_left(r) \
(GRAPHENE_RECT_INIT(r->bounds.origin.x, \
r->bounds.origin.y, \
r->corner[0].width, r->corner[0].height))
#define rounded_rect_top_right(r) \
(GRAPHENE_RECT_INIT(r->bounds.origin.x + r->bounds.size.width - r->corner[1].width, \
r->bounds.origin.y, \
r->corner[1].width, r->corner[1].height))
#define rounded_rect_bottom_right(r) \
(GRAPHENE_RECT_INIT(r->bounds.origin.x + r->bounds.size.width - r->corner[2].width, \
r->bounds.origin.y + r->bounds.size.height - r->corner[2].height, \
r->corner[2].width, r->corner[2].height))
#define rounded_rect_bottom_left(r) \
(GRAPHENE_RECT_INIT(r->bounds.origin.x, \
r->bounds.origin.y + r->bounds.size.height - r->corner[2].height, \
r->corner[3].width, r->corner[3].height))
#define rounded_rect_corner0(r) rounded_rect_top_left(r)
#define rounded_rect_corner1(r) rounded_rect_top_right(r)
#define rounded_rect_corner2(r) rounded_rect_bottom_right(r)
#define rounded_rect_corner3(r) rounded_rect_bottom_left(r)
#define rounded_rect_corner(r, i) (rounded_rect_corner##i(r))
#define ALPHA_IS_CLEAR(alpha) ((alpha) < ((float) 0x00ff / (float) 0xffff))
#define RGBA_IS_CLEAR(rgba) ALPHA_IS_CLEAR((rgba)->alpha)
@ -429,70 +408,6 @@ rect_intersects (const graphene_rect_t *r1,
return TRUE;
}
static inline gboolean
rounded_rect_has_corner (const GskRoundedRect *r,
guint i)
{
return r->corner[i].width > 0 && r->corner[i].height > 0;
}
/* Current clip is NOT rounded but new one is definitely! */
static inline gboolean
intersect_rounded_rectilinear (const graphene_rect_t *non_rounded,
const GskRoundedRect *rounded,
GskRoundedRect *result)
{
gboolean corners[4];
/* Intersects with top left corner? */
corners[0] = rounded_rect_has_corner (rounded, 0) &&
rect_intersects (non_rounded,
&rounded_rect_corner (rounded, 0));
if (corners[0] && !rect_contains_rect (non_rounded,
&rounded_rect_corner (rounded, 0)))
return FALSE;
/* top right ? */
corners[1] = rounded_rect_has_corner (rounded, 1) &&
rect_intersects (non_rounded,
&rounded_rect_corner (rounded, 1));
if (corners[1] && !rect_contains_rect (non_rounded,
&rounded_rect_corner (rounded, 1)))
return FALSE;
/* bottom right ? */
corners[2] = rounded_rect_has_corner (rounded, 2) &&
rect_intersects (non_rounded,
&rounded_rect_corner (rounded, 2));
if (corners[2] && !rect_contains_rect (non_rounded,
&rounded_rect_corner (rounded, 2)))
return FALSE;
/* bottom left ? */
corners[3] = rounded_rect_has_corner (rounded, 3) &&
rect_intersects (non_rounded,
&rounded_rect_corner (rounded, 3));
if (corners[3] && !rect_contains_rect (non_rounded,
&rounded_rect_corner (rounded, 3)))
return FALSE;
/* We do intersect with at least one of the corners, but in such a way that the
* intersection between the two clips can still be represented by a single rounded
* rect in a trivial way. do that.
*/
graphene_rect_intersection (non_rounded, &rounded->bounds, &result->bounds);
for (guint i = 0; i < 4; i++)
{
if (corners[i])
result->corner[i] = rounded->corner[i];
else
result->corner[i].width = result->corner[i].height = 0;
}
return TRUE;
}
static inline void
init_projection_matrix (graphene_matrix_t *projection,
const graphene_rect_t *viewport)
@ -551,13 +466,14 @@ extract_matrix_metadata (GskGLRenderModelview *modelview)
case GSK_TRANSFORM_CATEGORY_2D:
{
float xx, xy, yx, yy, dx, dy;
float skew_x, skew_y, angle, dx, dy;
gsk_transform_to_2d (modelview->transform,
&xx, &xy, &yx, &yy, &dx, &dy);
modelview->scale_x = sqrtf (xx * xx + xy * xy);
modelview->scale_y = sqrtf (yx * yx + yy * yy);
gsk_transform_to_2d_components (modelview->transform,
&skew_x, &skew_y,
&modelview->scale_x, &modelview->scale_y,
&angle, &dx, &dy);
modelview->dx = 0;
modelview->dy = 0;
}
break;
@ -918,10 +834,10 @@ gsk_gl_render_job_untransform_bounds (GskGLRenderJob *job,
}
static inline void
gsk_gl_render_job_transform_rounded_rect (GskGLRenderJob *job,
gsk_gl_render_job_translate_rounded_rect (GskGLRenderJob *job,
const GskRoundedRect *rect,
GskRoundedRect *out_rect)
{
{
out_rect->bounds.origin.x = job->offset_x + rect->bounds.origin.x;
out_rect->bounds.origin.y = job->offset_y + rect->bounds.origin.y;
out_rect->bounds.size.width = rect->bounds.size.width;
@ -929,6 +845,52 @@ gsk_gl_render_job_transform_rounded_rect (GskGLRenderJob *job,
memcpy (out_rect->corner, rect->corner, sizeof rect->corner);
}
static inline void
rounded_rect_scale_corners (const GskRoundedRect *rect,
GskRoundedRect *out_rect,
float scale_x,
float scale_y)
{
for (guint i = 0; i < G_N_ELEMENTS (out_rect->corner); i++)
{
out_rect->corner[i].width = rect->corner[i].width * fabs (scale_x);
out_rect->corner[i].height = rect->corner[i].height * fabs (scale_y);
}
if (scale_x < 0)
{
graphene_size_t p;
p = out_rect->corner[GSK_CORNER_TOP_LEFT];
out_rect->corner[GSK_CORNER_TOP_LEFT] = out_rect->corner[GSK_CORNER_TOP_RIGHT];
out_rect->corner[GSK_CORNER_TOP_RIGHT] = p;
p = out_rect->corner[GSK_CORNER_BOTTOM_LEFT];
out_rect->corner[GSK_CORNER_BOTTOM_LEFT] = out_rect->corner[GSK_CORNER_BOTTOM_RIGHT];
out_rect->corner[GSK_CORNER_BOTTOM_RIGHT] = p;
}
if (scale_y < 0)
{
graphene_size_t p;
p = out_rect->corner[GSK_CORNER_TOP_LEFT];
out_rect->corner[GSK_CORNER_TOP_LEFT] = out_rect->corner[GSK_CORNER_BOTTOM_LEFT];
out_rect->corner[GSK_CORNER_BOTTOM_LEFT] = p;
p = out_rect->corner[GSK_CORNER_TOP_RIGHT];
out_rect->corner[GSK_CORNER_TOP_RIGHT] = out_rect->corner[GSK_CORNER_BOTTOM_RIGHT];
out_rect->corner[GSK_CORNER_BOTTOM_RIGHT] = p;
}
}
static inline void
gsk_gl_render_job_transform_rounded_rect (GskGLRenderJob *job,
const GskRoundedRect *rect,
GskRoundedRect *out_rect)
{
gsk_gl_render_job_transform_bounds (job, &rect->bounds, &out_rect->bounds);
rounded_rect_scale_corners (rect, out_rect, job->scale_x, job->scale_y);
}
static inline void
rounded_rect_get_inner (const GskRoundedRect *rect,
graphene_rect_t *inner)
@ -1231,13 +1193,12 @@ gsk_gl_render_job_visit_as_fallback (GskGLRenderJob *job,
{
float scale_x = job->scale_x;
float scale_y = job->scale_y;
int surface_width = ceilf (node->bounds.size.width * scale_x);
int surface_height = ceilf (node->bounds.size.height * scale_y);
int surface_width = ceilf (node->bounds.size.width * fabs (scale_x));
int surface_height = ceilf (node->bounds.size.height * fabs (scale_y));
GdkTexture *texture;
cairo_surface_t *surface;
cairo_surface_t *rendered_surface;
cairo_t *cr;
int cached_id;
int texture_id;
GskTextureKey key;
@ -1249,18 +1210,10 @@ gsk_gl_render_job_visit_as_fallback (GskGLRenderJob *job,
key.scale_x = scale_x;
key.scale_y = scale_y;
cached_id = gsk_gl_driver_lookup_texture (job->driver, &key);
texture_id = gsk_gl_driver_lookup_texture (job->driver, &key);
if (cached_id != 0)
{
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit));
gsk_gl_program_set_uniform_texture (job->current_program,
UNIFORM_SHARED_SOURCE, 0,
GL_TEXTURE_2D, GL_TEXTURE0, cached_id);
gsk_gl_render_job_draw_offscreen_rect (job, &node->bounds);
gsk_gl_render_job_end_draw (job);
return;
}
if (texture_id != 0)
goto done;
/* We first draw the recording surface on an image surface,
* just because the scaleY(-1) later otherwise screws up the
@ -1270,7 +1223,7 @@ gsk_gl_render_job_visit_as_fallback (GskGLRenderJob *job,
surface_width,
surface_height);
cairo_surface_set_device_scale (rendered_surface, scale_x, scale_y);
cairo_surface_set_device_scale (rendered_surface, fabs (scale_x), fabs (scale_y));
cr = cairo_create (rendered_surface);
cairo_save (cr);
@ -1284,15 +1237,16 @@ gsk_gl_render_job_visit_as_fallback (GskGLRenderJob *job,
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
surface_width,
surface_height);
cairo_surface_set_device_scale (surface, scale_x, scale_y);
cairo_surface_set_device_scale (surface, fabs (scale_x), fabs (scale_y));
cr = cairo_create (surface);
/* We draw upside down here, so it matches what GL does. */
cairo_save (cr);
cairo_scale (cr, 1, -1);
cairo_translate (cr, 0, - surface_height / scale_y);
cairo_scale (cr, scale_x < 0 ? -1 : 1, scale_y < 0 ? 1 : -1);
cairo_translate (cr, scale_x < 0 ? - surface_width / fabs (scale_x) : 0,
scale_y < 0 ? 0 : - surface_height / fabs (scale_y));
cairo_set_source_surface (cr, rendered_surface, 0, 0);
cairo_rectangle (cr, 0, 0, surface_width / scale_x, surface_height / scale_y);
cairo_rectangle (cr, 0, 0, surface_width / fabs (scale_x), surface_height / fabs (scale_y));
cairo_fill (cr);
cairo_restore (cr);
@ -1331,6 +1285,16 @@ gsk_gl_render_job_visit_as_fallback (GskGLRenderJob *job,
gsk_gl_driver_cache_texture (job->driver, &key, texture_id);
done:
if (scale_x < 0 || scale_y < 0)
{
GskTransform *transform = gsk_transform_translate (NULL,
&GRAPHENE_POINT_INIT (scale_x < 0 ? - surface_width : 0,
scale_y < 0 ? - surface_height : 0));
gsk_gl_render_job_push_modelview (job, transform);
gsk_transform_unref (transform);
}
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit));
gsk_gl_program_set_uniform_texture (job->current_program,
UNIFORM_SHARED_SOURCE, 0,
@ -1339,6 +1303,9 @@ gsk_gl_render_job_visit_as_fallback (GskGLRenderJob *job,
texture_id);
gsk_gl_render_job_draw_offscreen_rect (job, &node->bounds);
gsk_gl_render_job_end_draw (job);
if (scale_x < 0 || scale_y < 0)
gsk_gl_render_job_pop_modelview (job);
}
static guint
@ -1493,10 +1460,10 @@ blur_node (GskGLRenderJob *job,
offscreen->texture_id = blur_offscreen (job,
offscreen,
texture_width * scale_x,
texture_height * scale_y,
blur_radius * scale_x,
blur_radius * scale_y);
texture_width * fabs (scale_x),
texture_height * fabs (scale_y),
blur_radius * fabs (scale_x),
blur_radius * fabs (scale_y));
init_full_texture_region (offscreen);
}
@ -1678,6 +1645,7 @@ gsk_gl_render_job_visit_clipped_child (GskGLRenderJob *job,
{
graphene_rect_t transformed_clip;
GskRoundedRect intersection;
GskRoundedRectIntersection result;
gsk_gl_render_job_transform_bounds (job, clip, &transformed_clip);
@ -1691,10 +1659,17 @@ gsk_gl_render_job_visit_clipped_child (GskGLRenderJob *job,
gsk_gl_render_job_push_clip (job, &intersection);
gsk_gl_render_job_visit_node (job, child);
gsk_gl_render_job_pop_clip (job);
return;
}
else if (intersect_rounded_rectilinear (&transformed_clip,
&job->current_clip->rect,
&intersection))
result = gsk_rounded_rect_intersect_with_rect (&job->current_clip->rect,
&transformed_clip,
&intersection);
if (result == GSK_INTERSECTION_EMPTY)
return;
if (result == GSK_INTERSECTION_NONEMPTY)
{
gsk_gl_render_job_push_clip (job, &intersection);
gsk_gl_render_job_visit_node (job, child);
@ -1741,28 +1716,26 @@ gsk_gl_render_job_visit_rounded_clip_node (GskGLRenderJob *job,
const GskRenderNode *child = gsk_rounded_clip_node_get_child (node);
const GskRoundedRect *clip = gsk_rounded_clip_node_get_clip (node);
GskRoundedRect transformed_clip;
float scale_x = job->scale_x;
float scale_y = job->scale_y;
gboolean need_offscreen;
if (node_is_invisible (child))
return;
gsk_gl_render_job_transform_bounds (job, &clip->bounds, &transformed_clip.bounds);
for (guint i = 0; i < G_N_ELEMENTS (transformed_clip.corner); i++)
{
transformed_clip.corner[i].width = clip->corner[i].width * scale_x;
transformed_clip.corner[i].height = clip->corner[i].height * scale_y;
}
gsk_gl_render_job_transform_rounded_rect (job, clip, &transformed_clip);
if (job->current_clip->is_rectilinear)
{
GskRoundedRect intersected_clip;
GskRoundedRectIntersection result;
if (intersect_rounded_rectilinear (&job->current_clip->rect.bounds,
&transformed_clip,
&intersected_clip))
result = gsk_rounded_rect_intersect_with_rect (&transformed_clip,
&job->current_clip->rect.bounds,
&intersected_clip);
if (result == GSK_INTERSECTION_EMPTY)
return;
if (result == GSK_INTERSECTION_NONEMPTY)
{
gsk_gl_render_job_push_clip (job, &intersected_clip);
gsk_gl_render_job_visit_node (job, child);
@ -1914,7 +1887,7 @@ gsk_gl_render_job_visit_border_node (GskGLRenderJob *job,
sizes[3].w = MAX (widths[3], rounded_outline->corner[3].width);
}
gsk_gl_render_job_transform_rounded_rect (job, rounded_outline, &outline);
gsk_gl_render_job_translate_rounded_rect (job, rounded_outline, &outline);
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, border));
@ -2018,7 +1991,7 @@ gsk_gl_render_job_visit_css_background (GskGLRenderJob *job,
rgba_to_half (&gsk_border_node_get_colors (node2)[0], color);
rgba_to_half (gsk_color_node_get_color (child), color2);
gsk_gl_render_job_transform_rounded_rect (job, rounded_outline, &outline);
gsk_gl_render_job_translate_rounded_rect (job, rounded_outline, &outline);
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, filled_border));
@ -2159,6 +2132,7 @@ gsk_gl_render_job_visit_transform_node (GskGLRenderJob *job,
scale = gsk_transform_translate (gsk_transform_scale (NULL, sx, sy), &GRAPHENE_POINT_INIT (tx, ty));
gsk_gl_render_job_push_modelview (job, scale);
transform = gsk_transform_transform (gsk_transform_invert (scale), transform);
gsk_transform_unref (scale);
}
}
@ -2209,7 +2183,7 @@ gsk_gl_render_job_visit_unblurred_inset_shadow_node (GskGLRenderJob *job,
GskRoundedRect transformed_outline;
guint16 color[4];
gsk_gl_render_job_transform_rounded_rect (job, outline, &transformed_outline);
gsk_gl_render_job_translate_rounded_rect (job, outline, &transformed_outline);
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, inset_shadow));
gsk_gl_program_set_uniform_rounded_rect (job->current_program,
@ -2309,7 +2283,7 @@ gsk_gl_render_job_visit_blurred_inset_shadow_node (GskGLRenderJob *job,
prev_fbo = gsk_gl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id);
gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
gsk_gl_render_job_transform_rounded_rect (job, &outline_to_blur, &transformed_outline);
gsk_gl_render_job_translate_rounded_rect (job, &outline_to_blur, &transformed_outline);
/* Actual inset shadow outline drawing */
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, inset_shadow));
@ -2342,8 +2316,8 @@ gsk_gl_render_job_visit_blurred_inset_shadow_node (GskGLRenderJob *job,
&offscreen,
texture_width,
texture_height,
blur_radius * scale_x,
blur_radius * scale_y);
blur_radius * fabs (scale_x),
blur_radius * fabs (scale_y));
gsk_gl_driver_release_render_target (job->driver, render_target, TRUE);
@ -2365,14 +2339,7 @@ gsk_gl_render_job_visit_blurred_inset_shadow_node (GskGLRenderJob *job,
{
GskRoundedRect node_clip;
gsk_gl_render_job_transform_bounds (job, &node_outline->bounds, &node_clip.bounds);
for (guint i = 0; i < 4; i ++)
{
node_clip.corner[i].width = node_outline->corner[i].width * scale_x;
node_clip.corner[i].height = node_outline->corner[i].height * scale_y;
}
gsk_gl_render_job_translate_rounded_rect (job, node_outline, &node_clip);
gsk_gl_render_job_push_clip (job, &node_clip);
}
@ -2422,7 +2389,7 @@ gsk_gl_render_job_visit_unblurred_outset_shadow_node (GskGLRenderJob *job,
rgba_to_half (gsk_outset_shadow_node_get_color (node), color);
gsk_gl_render_job_transform_rounded_rect (job, outline, &transformed_outline);
gsk_gl_render_job_translate_rounded_rect (job, outline, &transformed_outline);
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, unblurred_outset_shadow));
gsk_gl_program_set_uniform_rounded_rect (job->current_program,
@ -2615,8 +2582,8 @@ gsk_gl_render_job_visit_blurred_outset_shadow_node (GskGLRenderJob *job,
&offscreen,
texture_width,
texture_height,
blur_radius * scale_x,
blur_radius * scale_y);
blur_radius * fabs (scale_x),
blur_radius * fabs (scale_y));
gsk_gl_shadow_library_insert (job->driver->shadows_library,
&scaled_outline,
@ -2630,7 +2597,7 @@ gsk_gl_render_job_visit_blurred_outset_shadow_node (GskGLRenderJob *job,
blurred_texture_id = cached_tid;
}
gsk_gl_render_job_transform_rounded_rect (job, outline, &transformed_outline);
gsk_gl_render_job_translate_rounded_rect (job, outline, &transformed_outline);
if (!do_slicing)
{
@ -2866,8 +2833,12 @@ gsk_gl_render_job_visit_cross_fade_node (GskGLRenderJob *job,
offscreen_end.reset_clip = TRUE;
offscreen_end.bounds = &node->bounds;
gsk_gl_render_job_set_modelview (job, NULL);
if (!gsk_gl_render_job_visit_node_with_offscreen (job, start_node, &offscreen_start))
{
gsk_gl_render_job_pop_modelview (job);
gsk_gl_render_job_visit_node (job, end_node);
return;
}
@ -2876,12 +2847,18 @@ gsk_gl_render_job_visit_cross_fade_node (GskGLRenderJob *job,
if (!gsk_gl_render_job_visit_node_with_offscreen (job, end_node, &offscreen_end))
{
float prev_alpha = gsk_gl_render_job_set_alpha (job, job->alpha * progress);
float prev_alpha;
gsk_gl_render_job_pop_modelview (job);
prev_alpha = gsk_gl_render_job_set_alpha (job, job->alpha * progress);
gsk_gl_render_job_visit_node (job, start_node);
gsk_gl_render_job_set_alpha (job, prev_alpha);
return;
}
gsk_gl_render_job_pop_modelview (job);
g_assert (offscreen_end.texture_id);
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, cross_fade));
@ -3275,10 +3252,14 @@ gsk_gl_render_job_visit_blend_node (GskGLRenderJob *job,
bottom_offscreen.force_offscreen = TRUE;
bottom_offscreen.reset_clip = TRUE;
gsk_gl_render_job_set_modelview (job, NULL);
/* TODO: We create 2 textures here as big as the blend node, but both the
* start and the end node might be a lot smaller than that. */
if (!gsk_gl_render_job_visit_node_with_offscreen (job, bottom_child, &bottom_offscreen))
{
gsk_gl_render_job_pop_modelview (job);
gsk_gl_render_job_visit_node (job, top_child);
return;
}
@ -3287,6 +3268,8 @@ gsk_gl_render_job_visit_blend_node (GskGLRenderJob *job,
if (!gsk_gl_render_job_visit_node_with_offscreen (job, top_child, &top_offscreen))
{
gsk_gl_render_job_pop_modelview (job);
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit));
gsk_gl_program_set_uniform_texture (job->current_program,
UNIFORM_SHARED_SOURCE, 0,
@ -3300,6 +3283,8 @@ gsk_gl_render_job_visit_blend_node (GskGLRenderJob *job,
g_assert (top_offscreen.was_offscreen);
gsk_gl_render_job_pop_modelview (job);
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blend));
gsk_gl_program_set_uniform_texture (job->current_program,
UNIFORM_SHARED_SOURCE, 0,
@ -3336,11 +3321,14 @@ gsk_gl_render_job_visit_mask_node (GskGLRenderJob *job,
mask_offscreen.reset_clip = TRUE;
mask_offscreen.do_not_cache = TRUE;
gsk_gl_render_job_set_modelview (job, NULL);
/* TODO: We create 2 textures here as big as the mask node, but both
* nodes might be a lot smaller than that.
*/
if (!gsk_gl_render_job_visit_node_with_offscreen (job, source, &source_offscreen))
{
gsk_gl_render_job_pop_modelview (job);
gsk_gl_render_job_visit_node (job, source);
return;
}
@ -3349,11 +3337,14 @@ gsk_gl_render_job_visit_mask_node (GskGLRenderJob *job,
if (!gsk_gl_render_job_visit_node_with_offscreen (job, mask, &mask_offscreen))
{
gsk_gl_render_job_pop_modelview (job);
return;
}
g_assert (mask_offscreen.was_offscreen);
gsk_gl_render_job_pop_modelview (job);
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, mask));
gsk_gl_program_set_uniform_texture (job->current_program,
UNIFORM_SHARED_SOURCE, 0,
@ -3587,8 +3578,8 @@ gsk_gl_render_job_visit_texture (GskGLRenderJob *job,
float scale_y = bounds->size.height / texture->height;
gboolean use_mipmap;
use_mipmap = (scale_x * job->scale_x) < 0.5 ||
(scale_y * job->scale_y) < 0.5;
use_mipmap = (scale_x * fabs (job->scale_x)) < 0.5 ||
(scale_y * fabs (job->scale_y)) < 0.5;
if G_LIKELY (texture->width <= max_texture_size &&
texture->height <= max_texture_size)
@ -4120,7 +4111,7 @@ gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob *job,
}
if (gsk_render_node_get_node_type (node) == GSK_TEXTURE_NODE &&
offscreen->force_offscreen == FALSE)
!offscreen->force_offscreen)
{
GdkTexture *texture = gsk_texture_node_get_texture (node);
gsk_gl_render_job_upload_texture (job, texture, FALSE, offscreen);
@ -4138,6 +4129,7 @@ gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob *job,
gboolean flipped_x = job->scale_x < 0;
gboolean flipped_y = job->scale_y < 0;
graphene_rect_t viewport;
gboolean reset_clip = FALSE;
if (flipped_x || flipped_y)
{
@ -4185,6 +4177,7 @@ gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob *job,
gsk_gl_render_job_push_modelview (job, transform);
gsk_transform_unref (transform);
gsk_gl_render_job_transform_bounds (job, offscreen->bounds, &viewport);
graphene_rect_scale (&viewport, downscale_x, downscale_y, &viewport);
}
if (downscale_x == 1)
@ -4268,11 +4261,25 @@ gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob *job,
gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
if (offscreen->reset_clip)
gsk_gl_render_job_push_clip (job, &GSK_ROUNDED_RECT_INIT_FROM_RECT (job->viewport));
{
gsk_gl_render_job_push_clip (job, &GSK_ROUNDED_RECT_INIT_FROM_RECT (job->viewport));
reset_clip = TRUE;
}
else if (flipped_x || flipped_y || downscale_x != 1 || downscale_y != 1)
{
GskRoundedRect new_clip;
float scale_x = flipped_x ? - downscale_x : downscale_x;
float scale_y = flipped_y ? - downscale_y : downscale_y;
graphene_rect_scale (&job->current_clip->rect.bounds, scale_x, scale_y, &new_clip.bounds);
rounded_rect_scale_corners (&job->current_clip->rect, &new_clip, scale_x, scale_y);
gsk_gl_render_job_push_clip (job, &new_clip);
reset_clip = TRUE;
}
gsk_gl_render_job_visit_node (job, node);
if (offscreen->reset_clip)
if (reset_clip)
gsk_gl_render_job_pop_clip (job);
if (downscale_x != 1 || downscale_y != 1)

View File

@ -19,6 +19,9 @@ void main() {
gsk_rounded_rect_transform(outside, u_modelview);
gsk_rounded_rect_transform(inside, u_modelview);
gsk_rounded_rect_normalize(outside);
gsk_rounded_rect_normalize(inside);
gsk_rounded_rect_encode(outside, transformed_outside_outline);
gsk_rounded_rect_encode(inside, transformed_inside_outline);
}
@ -34,10 +37,9 @@ _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);
float outer_coverage = gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag);
float inner_coverage = gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag);
float alpha = clamp(outer_coverage - inner_coverage, 0.0, 1.0);
gskSetScaledOutputColor(final_color, alpha);
}

View File

@ -21,6 +21,9 @@ void main() {
gsk_rounded_rect_transform(outside, u_modelview);
gsk_rounded_rect_transform(inside, u_modelview);
gsk_rounded_rect_normalize(outside);
gsk_rounded_rect_normalize(inside);
gsk_rounded_rect_encode(outside, transformed_outside_outline);
gsk_rounded_rect_encode(inside, transformed_inside_outline);
}

View File

@ -22,6 +22,9 @@ void main() {
gsk_rounded_rect_transform(outside, u_modelview);
gsk_rounded_rect_transform(inside, u_modelview);
gsk_rounded_rect_normalize(outside);
gsk_rounded_rect_normalize(inside);
gsk_rounded_rect_encode(outside, transformed_outside_outline);
gsk_rounded_rect_encode(inside, transformed_inside_outline);
}

View File

@ -15,6 +15,7 @@ void main() {
GskRoundedRect outline = gsk_create_rect(u_outline_rect);
gsk_rounded_rect_transform(outline, u_modelview);
gsk_rounded_rect_normalize(outline);
gsk_rounded_rect_encode(outline, transformed_outline);
}

View File

@ -16,11 +16,15 @@ _IN_ vec2 vUv;
GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
{
GskRoundedRect rect;
#if defined(GSK_GLES) || defined(GSK_LEGACY)
return GskRoundedRect(r[0], r[1], r[2]);
rect = GskRoundedRect(r[0], r[1], r[2]);
#else
return r;
rect = r;
#endif
gsk_rounded_rect_normalize (rect);
return rect;
}
float

View File

@ -22,6 +22,55 @@ struct GskRoundedRect
vec4 corner_points2; // xy = bottom right, zw = bottom left
};
void gsk_rounded_rect_normalize(inout GskRoundedRect r)
{
if (r.bounds.x > r.bounds.z)
{
float t = r.bounds.x;
r.bounds.x = r.bounds.z;
r.bounds.z = t;
vec2 c = r.corner_points1.xy;
r.corner_points1.xy = r.corner_points1.zw;
r.corner_points1.zw = c;
c = r.corner_points2.xy;
r.corner_points2.xy = r.corner_points2.zw;
r.corner_points2.zw = c;
}
if (r.bounds.y > r.bounds.w)
{
float t = r.bounds.y;
r.bounds.y = r.bounds.w;
r.bounds.w = t;
vec2 c = r.corner_points1.xy;
r.corner_points1.xy = r.corner_points2.xy;
r.corner_points2.xy = c;
c = r.corner_points1.zw;
r.corner_points1.zw = r.corner_points2.zw;
r.corner_points2.zw = c;
}
}
void gsk_bounds_normalize (inout vec4 bounds)
{
if (bounds.x > bounds.z)
{
float t = bounds.x;
bounds.x = bounds.z;
bounds.z = t;
}
if (bounds.y > bounds.w)
{
float t = bounds.y;
bounds.y = bounds.w;
bounds.w = t;
}
}
// Transform from a C GskRoundedRect to what we need.
GskRoundedRect
gsk_create_rect(vec4[3] data)
@ -33,13 +82,21 @@ gsk_create_rect(vec4[3] data)
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);
GskRoundedRect rect = GskRoundedRect(bounds, corner_points1, corner_points2);
gsk_rounded_rect_normalize (rect);
return rect;
}
vec4
gsk_get_bounds(vec4[3] data)
{
return vec4(data[0].xy, data[0].xy + data[0].zw);
vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
gsk_bounds_normalize (bounds);
return bounds;
}
vec4 gsk_premultiply(vec4 c) {

View File

@ -519,8 +519,174 @@ gsk_rounded_rect_intersects_rect (const GskRoundedRect *self,
gsk_rounded_rect_locate_point (self, &GRAPHENE_POINT_INIT (rect->origin.x, rect->origin.y + rect->size.height)) == OUTSIDE_TOP_RIGHT ||
gsk_rounded_rect_locate_point (self, &GRAPHENE_POINT_INIT (rect->origin.x + rect->size.width, rect->origin.y + rect->size.height)) == OUTSIDE_TOP_LEFT)
return FALSE;
return TRUE;
}
return TRUE;
#define rect_point0(r) ((r)->origin)
#define rect_point1(r) (GRAPHENE_POINT_INIT ((r)->origin.x + (r)->size.width, (r)->origin.y))
#define rect_point2(r) (GRAPHENE_POINT_INIT ((r)->origin.x + (r)->size.width, (r)->origin.y + (r)->size.height))
#define rect_point3(r) (GRAPHENE_POINT_INIT ((r)->origin.x, (r)->origin.y + (r)->size.height))
#define rounded_rect_corner0(r) \
(GRAPHENE_RECT_INIT((r)->bounds.origin.x, \
(r)->bounds.origin.y, \
(r)->corner[0].width, (r)->corner[0].height))
#define rounded_rect_corner1(r) \
(GRAPHENE_RECT_INIT((r)->bounds.origin.x + (r)->bounds.size.width - (r)->corner[1].width, \
(r)->bounds.origin.y, \
(r)->corner[1].width, (r)->corner[1].height))
#define rounded_rect_corner2(r) \
(GRAPHENE_RECT_INIT((r)->bounds.origin.x + (r)->bounds.size.width - (r)->corner[2].width, \
(r)->bounds.origin.y + (r)->bounds.size.height - (r)->corner[2].height, \
(r)->corner[2].width, (r)->corner[2].height))
#define rounded_rect_corner3(r) \
(GRAPHENE_RECT_INIT((r)->bounds.origin.x, \
(r)->bounds.origin.y + (r)->bounds.size.height - (r)->corner[3].height, \
(r)->corner[3].width, (r)->corner[3].height))
enum {
BELOW,
INNER,
ABOVE
};
static inline void
classify_point (const graphene_point_t *p, const graphene_rect_t *rect, int *px, int *py)
{
if (p->x <= rect->origin.x)
*px = BELOW;
else if (p->x >= rect->origin.x + rect->size.width)
*px = ABOVE;
else
*px = INNER;
if (p->y <= rect->origin.y)
*py = BELOW;
else if (p->y >= rect->origin.y + rect->size.height)
*py = ABOVE;
else
*py = INNER;
}
GskRoundedRectIntersection
gsk_rounded_rect_intersect_with_rect (const GskRoundedRect *self,
const graphene_rect_t *rect,
GskRoundedRect *result)
{
int px, py, qx, qy;
if (!graphene_rect_intersection (&self->bounds, rect, &result->bounds))
return GSK_INTERSECTION_EMPTY;
classify_point (&rect_point0 (rect), &rounded_rect_corner0 (self), &px, &py);
if (px == BELOW && py == BELOW)
{
classify_point (&rect_point2 (rect), &rounded_rect_corner0 (self), &qx, &qy);
if (qx == BELOW || qy == BELOW)
return GSK_INTERSECTION_EMPTY;
else if (qx == INNER && qy == INNER &&
gsk_rounded_rect_locate_point (self, &rect_point2 (rect)) != INSIDE)
return GSK_INTERSECTION_EMPTY;
else if (qx == ABOVE && qy == ABOVE)
result->corner[0] = self->corner[0];
else
return GSK_INTERSECTION_NOT_REPRESENTABLE;
}
else if ((px == INNER || py == INNER) &&
gsk_rounded_rect_locate_point (self, &rect_point0 (rect)) != INSIDE)
{
if (gsk_rounded_rect_locate_point (self, &rect_point2 (rect)) == OUTSIDE_TOP_LEFT)
return GSK_INTERSECTION_EMPTY;
else
return GSK_INTERSECTION_NOT_REPRESENTABLE;
}
else
result->corner[0].width = result->corner[0].height = 0;
classify_point (&rect_point1 (rect), &rounded_rect_corner1 (self), &px, &py);
if (px == ABOVE && py == BELOW)
{
classify_point (&rect_point3 (rect), &rounded_rect_corner1 (self), &qx, &qy);
if (qx == ABOVE || qy == BELOW)
return GSK_INTERSECTION_EMPTY;
else if (qx == INNER && qy == INNER &&
gsk_rounded_rect_locate_point (self, &rect_point3 (rect)) != INSIDE)
return GSK_INTERSECTION_EMPTY;
else if (qx == BELOW && qy == ABOVE)
result->corner[1] = self->corner[1];
else
return GSK_INTERSECTION_NOT_REPRESENTABLE;
}
else if ((px == INNER || py == INNER) &&
gsk_rounded_rect_locate_point (self, &rect_point1 (rect)) != INSIDE)
{
if (gsk_rounded_rect_locate_point (self, &rect_point3 (rect)) == OUTSIDE_TOP_RIGHT)
return GSK_INTERSECTION_EMPTY;
else
return GSK_INTERSECTION_NOT_REPRESENTABLE;
}
else
result->corner[1].width = result->corner[1].height = 0;
classify_point (&rect_point2 (rect), &rounded_rect_corner2 (self), &px, &py);
if (px == ABOVE && py == ABOVE)
{
classify_point (&rect_point0 (rect), &rounded_rect_corner2 (self), &qx, &qy);
if (qx == ABOVE || qy == ABOVE)
return GSK_INTERSECTION_EMPTY;
else if (qx == INNER && qy == INNER &&
gsk_rounded_rect_locate_point (self, &rect_point0 (rect)) != INSIDE)
return GSK_INTERSECTION_EMPTY;
else if (qx == BELOW && qy == BELOW)
result->corner[2] = self->corner[2];
else
return GSK_INTERSECTION_NOT_REPRESENTABLE;
}
else if ((px == INNER || py == INNER) &&
gsk_rounded_rect_locate_point (self, &rect_point2 (rect)) != INSIDE)
{
if (gsk_rounded_rect_locate_point (self, &rect_point0 (rect)) == OUTSIDE_BOTTOM_RIGHT)
return GSK_INTERSECTION_EMPTY;
else
return GSK_INTERSECTION_NOT_REPRESENTABLE;
}
else
result->corner[2].width = result->corner[2].height = 0;
classify_point (&rect_point3 (rect), &rounded_rect_corner3 (self), &px, &py);
if (px == BELOW && py == ABOVE)
{
classify_point (&rect_point1 (rect), &rounded_rect_corner3 (self), &qx, &qy);
if (qx == BELOW || qy == ABOVE)
return GSK_INTERSECTION_EMPTY;
else if (qx == INNER && qy == INNER &&
gsk_rounded_rect_locate_point (self, &rect_point1 (rect)) != INSIDE)
return GSK_INTERSECTION_EMPTY;
else if (qx == ABOVE && qy == BELOW)
result->corner[3] = self->corner[3];
else
return GSK_INTERSECTION_NOT_REPRESENTABLE;
}
else if ((px == INNER || py == INNER) &&
gsk_rounded_rect_locate_point (self, &rect_point3 (rect)) != INSIDE)
{
if (gsk_rounded_rect_locate_point (self, &rect_point1 (rect)) == OUTSIDE_BOTTOM_LEFT)
return GSK_INTERSECTION_EMPTY;
else
return GSK_INTERSECTION_NOT_REPRESENTABLE;
}
else
result->corner[3].width = result->corner[3].height = 0;
return GSK_INTERSECTION_NONEMPTY;
}
static void
@ -565,7 +731,7 @@ gsk_rounded_rect_path (const GskRoundedRect *self,
self->corner[GSK_CORNER_TOP_LEFT].width,
self->corner[GSK_CORNER_TOP_LEFT].height,
G_PI, 3 * G_PI_2);
_cairo_ellipsis (cr,
_cairo_ellipsis (cr,
self->bounds.origin.x + self->bounds.size.width - self->corner[GSK_CORNER_TOP_RIGHT].width,
self->bounds.origin.y + self->corner[GSK_CORNER_TOP_RIGHT].height,
self->corner[GSK_CORNER_TOP_RIGHT].width,
@ -649,5 +815,4 @@ gsk_rounded_rect_to_string (const GskRoundedRect *self)
self->corner[2].height,
self->corner[3].width,
self->corner[3].height);
}

View File

@ -34,6 +34,16 @@ gboolean gsk_rounded_rect_equal (gconstpointer
gconstpointer rect2) G_GNUC_PURE;
char * gsk_rounded_rect_to_string (const GskRoundedRect *self) G_GNUC_MALLOC;
typedef enum {
GSK_INTERSECTION_EMPTY,
GSK_INTERSECTION_NONEMPTY,
GSK_INTERSECTION_NOT_REPRESENTABLE
} GskRoundedRectIntersection;
GskRoundedRectIntersection gsk_rounded_rect_intersect_with_rect (const GskRoundedRect *self,
const graphene_rect_t *rect,
GskRoundedRect *result) G_GNUC_PURE;
G_END_DECLS

View File

@ -5,6 +5,9 @@
#include "../reftests/reftest-compare.h"
static char *arg_output_dir = NULL;
static gboolean flip = FALSE;
static gboolean rotate = FALSE;
static gboolean repeat = FALSE;
static const char *
get_output_dir (void)
@ -112,6 +115,20 @@ save_image (GdkTexture *texture,
g_free (filename);
}
static void
save_node (GskRenderNode *node,
const char *test_name,
const char *extension)
{
char *filename = get_output_file (test_name, ".node", extension);
gboolean result;
g_print ("Storing modified nodes at %s\n", filename);
result = gsk_render_node_write_to_file (node, filename, NULL);
g_assert_true (result);
g_free (filename);
}
static void
deserialize_error_func (const GskParseLocation *start,
const GskParseLocation *end,
@ -136,11 +153,39 @@ deserialize_error_func (const GskParseLocation *start,
}
static const GOptionEntry options[] = {
{ "output", 0, 0, G_OPTION_ARG_FILENAME, &arg_output_dir,
"Directory to save image files to", "DIR" },
{ "output", 0, 0, G_OPTION_ARG_FILENAME, &arg_output_dir, "Directory to save image files to", "DIR" },
{ "flip", 0, 0, G_OPTION_ARG_NONE, &flip, "Do flipped test", NULL },
{ "rotate", 0, 0, G_OPTION_ARG_NONE, &rotate, "Do rotated test", NULL },
{ "repeat", 0, 0, G_OPTION_ARG_NONE, &repeat, "Do repeat test", NULL },
{ NULL }
};
static GskRenderNode *
load_node_file (const char *node_file)
{
GBytes *bytes;
gsize len;
char *contents;
GError *error = NULL;
GskRenderNode *node;
if (!g_file_get_contents (node_file, &contents, &len, &error))
{
g_print ("Could not open node file: %s\n", error->message);
g_clear_error (&error);
return NULL;
}
bytes = g_bytes_new_take (contents, len);
node = gsk_render_node_deserialize (bytes, deserialize_error_func, NULL);
g_bytes_unref (bytes);
g_assert_no_error (error);
g_assert_nonnull (node);
return node;
}
/*
* Non-option arguments:
* 1) .node file to compare
@ -159,6 +204,7 @@ main (int argc, char **argv)
gboolean success = TRUE;
GError *error = NULL;
GOptionContext *context;
GdkTexture *diff_texture;
(g_test_init) (&argc, &argv, NULL);
@ -185,64 +231,182 @@ main (int argc, char **argv)
node_file = argv[1];
png_file = argv[2];
window = gdk_surface_new_toplevel (gdk_display_get_default());
renderer = gsk_renderer_new_for_surface (window);
g_print ("Node file: '%s'\n", node_file);
g_print ("PNG file: '%s'\n", png_file);
window = gdk_surface_new_toplevel (gdk_display_get_default());
renderer = gsk_renderer_new_for_surface (window);
/* Load the render node from the given .node file */
{
GBytes *bytes;
gsize len;
char *contents;
if (!g_file_get_contents (node_file, &contents, &len, &error))
{
g_print ("Could not open node file: %s\n", error->message);
g_clear_error (&error);
return 1;
}
bytes = g_bytes_new_take (contents, len);
node = gsk_render_node_deserialize (bytes, deserialize_error_func, &success);
g_bytes_unref (bytes);
g_assert_no_error (error);
g_assert_nonnull (node);
}
node = load_node_file (node_file);
if (!node)
return 1;
/* Render the .node file and download to cairo surface */
rendered_texture = gsk_renderer_render_texture (renderer, node, NULL);
g_assert_nonnull (rendered_texture);
save_image (rendered_texture, node_file, ".out.png");
/* Load the given reference png file */
reference_texture = gdk_texture_new_from_filename (png_file, &error);
if (reference_texture == NULL)
{
g_print ("Error loading reference surface: %s\n", error->message);
g_clear_error (&error);
save_image (rendered_texture, node_file, ".out.png");
return 0;
}
/* Now compare the two */
diff_texture = reftest_compare_textures (rendered_texture, reference_texture);
if (diff_texture)
{
save_image (diff_texture, node_file, ".diff.png");
g_object_unref (diff_texture);
success = FALSE;
}
else
{
GdkTexture *diff_texture;
/* Now compare the two */
g_clear_object (&reference_texture);
g_clear_object (&rendered_texture);
if (flip)
{
GskRenderNode *node2;
GdkPixbuf *pixbuf, *pixbuf2;
node2 = gsk_transform_node_new (node, gsk_transform_scale (NULL, -1, 1));
rendered_texture = gsk_renderer_render_texture (renderer, node2, NULL);
save_image (rendered_texture, node_file, "-flipped.out.png");
pixbuf = gdk_pixbuf_new_from_file (png_file, &error);
pixbuf2 = gdk_pixbuf_flip (pixbuf, TRUE);
reference_texture = gdk_texture_new_for_pixbuf (pixbuf2);
g_object_unref (pixbuf2);
g_object_unref (pixbuf);
save_image (reference_texture, node_file, "-flipped.ref.png");
diff_texture = reftest_compare_textures (rendered_texture, reference_texture);
if (diff_texture)
{
save_image (diff_texture, node_file, ".diff.png");
save_node (node2, node_file, "-flipped.node");
save_image (diff_texture, node_file, "-flipped.diff.png");
g_object_unref (diff_texture);
success = FALSE;
}
g_clear_object (&rendered_texture);
g_clear_object (&reference_texture);
gsk_render_node_unref (node2);
}
save_image (rendered_texture, node_file, ".out.png");
if (repeat)
{
GskRenderNode *node2;
GdkPixbuf *pixbuf, *pixbuf2, *pixbuf3;
int width, height;
graphene_rect_t node_bounds;
graphene_rect_t bounds;
float offset_x, offset_y;
g_object_unref (reference_texture);
g_object_unref (rendered_texture);
gsk_render_node_get_bounds (node, &node_bounds);
if (node_bounds.size.width > 32768. || node_bounds.size.height > 32768.)
{
g_print ("Avoiding repeat test that exceeds cairo image surface dimensions");
exit (77);
}
bounds.origin.x = 0.;
bounds.origin.y = 0.;
bounds.size.width = 2 * node_bounds.size.width;
bounds.size.height = 2 * node_bounds.size.height;
offset_x = floorf (fmodf (node_bounds.origin.x, node_bounds.size.width));
offset_y = floorf (fmodf (node_bounds.origin.y, node_bounds.size.height));
if (offset_x < 0)
offset_x += node_bounds.size.width;
if (offset_y < 0)
offset_y += node_bounds.size.height;
node2 = gsk_repeat_node_new (&bounds, node, &node_bounds);
rendered_texture = gsk_renderer_render_texture (renderer, node2, NULL);
save_image (rendered_texture, node_file, "-repeated.out.png");
pixbuf = gdk_pixbuf_new_from_file (png_file, &error);
width = gdk_pixbuf_get_width (pixbuf);
height = gdk_pixbuf_get_height (pixbuf);
pixbuf2 = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (pixbuf),
gdk_pixbuf_get_has_alpha (pixbuf),
gdk_pixbuf_get_bits_per_sample (pixbuf),
width * 3,
height * 3);
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
gdk_pixbuf_copy_area (pixbuf, 0, 0, width, height, pixbuf2, i * width, j * height);
pixbuf3 = gdk_pixbuf_new_subpixbuf (pixbuf2, width - (int) offset_x, height - (int) offset_y, 2 * width, 2 * height);
reference_texture = gdk_texture_new_for_pixbuf (pixbuf3);
g_object_unref (pixbuf3);
g_object_unref (pixbuf2);
g_object_unref (pixbuf);
save_image (reference_texture, node_file, "-repeated.ref.png");
diff_texture = reftest_compare_textures (rendered_texture, reference_texture);
if (diff_texture)
{
save_node (node2, node_file, "-repeated.node");
save_image (diff_texture, node_file, "-repeated.diff.png");
g_object_unref (diff_texture);
success = FALSE;
}
g_clear_object (&rendered_texture);
g_clear_object (&reference_texture);
gsk_render_node_unref (node2);
}
if (rotate)
{
GskRenderNode *node2;
GdkPixbuf *pixbuf, *pixbuf2;
node2 = gsk_transform_node_new (node, gsk_transform_rotate (NULL, 90));
rendered_texture = gsk_renderer_render_texture (renderer, node2, NULL);
save_image (rendered_texture, node_file, "-rotated.out.png");
pixbuf = gdk_pixbuf_new_from_file (png_file, &error);
pixbuf2 = gdk_pixbuf_rotate_simple (pixbuf, GDK_PIXBUF_ROTATE_CLOCKWISE);
reference_texture = gdk_texture_new_for_pixbuf (pixbuf2);
g_object_unref (pixbuf2);
g_object_unref (pixbuf);
save_image (reference_texture, node_file, "-rotated.ref.png");
diff_texture = reftest_compare_textures (rendered_texture, reference_texture);
if (diff_texture)
{
save_node (node2, node_file, "-rotated.node");
save_image (diff_texture, node_file, "-rotated.diff.png");
g_object_unref (diff_texture);
success = FALSE;
}
g_clear_object (&rendered_texture);
g_clear_object (&reference_texture);
gsk_render_node_unref (node2);
}
gsk_render_node_unref (node);

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -0,0 +1,13 @@
transform {
transform: rotate(90);
child: blend {
bottom: color {
bounds: 0 0 100 100;
color: rgb(255,0,0);
}
top: color {
bounds: 50 50 100 100;
color: rgb(0,0,255);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 489 B

View File

@ -0,0 +1,23 @@
blend {
mode: difference;
bottom: color {
bounds: 0 0 100 100;
color: red;
}
top: color {
bounds: 50 50 100 100;
color: blue;
}
}
blend {
mode: normal;
bottom: color {
bounds: 150 0 100 100;
color: red;
}
top: color {
bounds: 200 50 100 100;
color: blue;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 B

View File

@ -0,0 +1,23 @@
border {
outline: 0 0 50 50 / 0 0 0 20;
}
color {
bounds: 0 29 4 11;
color: rgb(255,192,203);
}
color {
bounds: 2 40 4 4;
color: rgb(255,192,203);
}
color {
bounds: 4 42 4 4;
color: rgb(255,192,203);
}
color {
bounds: 6 44 4 4;
color: rgb(255,192,203);
}
color {
bounds: 9 46 11 4;
color: rgb(255,192,203);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

View File

@ -0,0 +1,14 @@
transform {
transform: rotate(90);
child: cross-fade {
progress: 0;
start: color {
bounds: 25 0 25 50;
color: rgb(255,0,0);
}
end: color {
bounds: 0 0 25 50;
color: rgb(0,0,0);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 B

View File

@ -15,9 +15,12 @@ compare_render_tests = [
'big-checkerboard-scaled-down-nearest',
'big-checkerboard-scaled-down2',
'blend-difference',
'blend-modes',
'blend-invisible-child',
'blend-in-rotate',
'blend-normal',
'border-bottom-right',
'border-one-rounded',
'borders-rotated',
'borders-scaled',
'clip-coordinates-2d',
@ -32,6 +35,7 @@ compare_render_tests = [
'color-matrix-identity',
'color-matrix-parsing',
'cross-fade-in-opacity',
'cross-fade-in-rotate',
'css-background',
'empty-blend',
'empty-blur',
@ -44,8 +48,7 @@ compare_render_tests = [
'empty-cross-fade',
'empty-debug',
'empty-inset-shadow',
# this test fails for some of the backends
# 'empty-linear-gradient',
'empty-linear-gradient',
'empty-mask',
'empty-opacity',
'empty-outset-shadow',
@ -74,7 +77,6 @@ compare_render_tests = [
'repeat-no-repeat',
'repeat-negative-coords',
'repeat-texture',
'rounded-clip-in-clip-3d', # not really 3d, but cairo fails it
'scale-textures-negative-ngl',
'scale-up-down',
'scaled-cairo',
@ -94,6 +96,7 @@ informative_render_tests = [
'big-glyph',
'empty-text',
'huge-glyph',
'rounded-clip-in-clip-3d', # both cairo and gl fail this one
]
renderers = [
@ -104,10 +107,16 @@ renderers = [
compare_xfails = [
# Both tests fail because of some font rendering issue
'empty-linear-gradient',
'unaligned-offscreen',
'upside-down-label-3d', # not really 3d, but cairo fails it
]
compare_xfails_small_texture = [
'big-checkerboard',
'big-checkerboard-scaled-down'
]
foreach renderer : renderers
foreach testname : compare_render_tests
@ -125,6 +134,10 @@ foreach renderer : renderers
suites += 'failing'
endif
if compare_xfails_small_texture.contains(testname)
suites += 'wayland_smalltexture_failing'
endif
if ((exclude_term == '' or not testname.contains(exclude_term)) and
(renderer_name != 'broadway' or broadway_enabled))
test(renderer_name + ' ' + testname, compare_render,
@ -141,6 +154,51 @@ foreach renderer : renderers
],
suite: suites,
)
test(renderer_name + ' ' + testname + ' flipped', compare_render,
args: [
'--flip',
'--output', join_paths(meson.current_build_dir(), 'compare', renderer_name),
join_paths(meson.current_source_dir(), 'compare', testname + '.node'),
join_paths(meson.current_source_dir(), 'compare', testname + '.png'),
],
env: [
'GSK_RENDERER=' + renderer_name,
'GTK_A11Y=test',
'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()),
'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir())
],
suite: suites + [ 'gsk-compare-flipped-' + renderer_name ],
)
test(renderer_name + ' ' + testname + ' repeated', compare_render,
args: [
'--repeat',
'--output', join_paths(meson.current_build_dir(), 'compare', renderer_name),
join_paths(meson.current_source_dir(), 'compare', testname + '.node'),
join_paths(meson.current_source_dir(), 'compare', testname + '.png'),
],
env: [
'GSK_RENDERER=' + renderer_name,
'GTK_A11Y=test',
'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()),
'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir())
],
suite: suites + [ 'gsk-compare-repeated-' + renderer_name ],
)
test(renderer_name + ' ' + testname + ' rotated', compare_render,
args: [
'--rotate',
'--output', join_paths(meson.current_build_dir(), 'compare', renderer_name),
join_paths(meson.current_source_dir(), 'compare', testname + '.node'),
join_paths(meson.current_source_dir(), 'compare', testname + '.png'),
],
env: [
'GSK_RENDERER=' + renderer_name,
'GTK_A11Y=test',
'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()),
'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir())
],
suite: suites + [ 'gsk-compare-rotated-' + renderer_name ],
)
endif
endforeach
endforeach

View File

@ -160,6 +160,80 @@ test_to_float (void)
g_assert_true (flt[10] == 9. && flt[11] == 11.);
}
#define ROUNDED_RECT_INIT_FULL(x,y,w,h,w0,h0,w1,h1,w2,h2,w3,h3) \
(GskRoundedRect) { .bounds = GRAPHENE_RECT_INIT (x, y, w, h), \
.corner = { \
GRAPHENE_SIZE_INIT (w0, h0), \
GRAPHENE_SIZE_INIT (w1, h1), \
GRAPHENE_SIZE_INIT (w2, h2), \
GRAPHENE_SIZE_INIT (w3, h3), \
}}
#define ROUNDED_RECT_INIT(x,y,w,h,r) \
ROUNDED_RECT_INIT_FULL (x, y, w, h, r, r, r, r, r, r, r, r)
#define ROUNDED_RECT_INIT_UNIFORM(x,y,w,h,r1,r2,r3,r4) \
ROUNDED_RECT_INIT_FULL (x, y, w, h, r1, r1, r2, r2, r3, r3, r4, r4)
static void
test_intersect_with_rect (void)
{
struct {
GskRoundedRect rounded;
graphene_rect_t rect;
GskRoundedRect expected;
GskRoundedRectIntersection result;
} test[] = {
{ ROUNDED_RECT_INIT (20, 50, 100, 100, 50), GRAPHENE_RECT_INIT (60, 80, 60, 70),
ROUNDED_RECT_INIT (0, 0, 0, 0, 0), GSK_INTERSECTION_NOT_REPRESENTABLE },
{ ROUNDED_RECT_INIT (0, 0, 100, 100, 10), GRAPHENE_RECT_INIT (0, 0, 100, 100),
ROUNDED_RECT_INIT (0, 0, 100, 100, 10), GSK_INTERSECTION_NONEMPTY },
{ ROUNDED_RECT_INIT (0, 0, 100, 100, 10), GRAPHENE_RECT_INIT (0, 0, 80, 80),
ROUNDED_RECT_INIT_UNIFORM (0, 0, 80, 80, 10, 0, 0, 0), GSK_INTERSECTION_NONEMPTY },
{ ROUNDED_RECT_INIT (0, 0, 100, 100, 10), GRAPHENE_RECT_INIT (10, 10, 80, 80),
ROUNDED_RECT_INIT (10, 10, 80, 80, 0), GSK_INTERSECTION_NONEMPTY },
{ ROUNDED_RECT_INIT (0, 0, 100, 100, 10), GRAPHENE_RECT_INIT (10, 15, 100, 70),
ROUNDED_RECT_INIT (10, 15, 90, 70, 0), GSK_INTERSECTION_NONEMPTY },
{ ROUNDED_RECT_INIT (0, 0, 100, 100, 10), GRAPHENE_RECT_INIT (110, 0, 10, 10),
ROUNDED_RECT_INIT (0, 0, 0, 0, 0), GSK_INTERSECTION_EMPTY },
{ ROUNDED_RECT_INIT (0, 0, 100, 100, 10), GRAPHENE_RECT_INIT (5, 5, 90, 90),
ROUNDED_RECT_INIT (5, 5, 90, 90, 0), GSK_INTERSECTION_NONEMPTY },
{ ROUNDED_RECT_INIT (0, 0, 100, 100, 10), GRAPHENE_RECT_INIT (1, 1, 1, 1),
ROUNDED_RECT_INIT (0, 0, 0, 0, 0), GSK_INTERSECTION_EMPTY },
{ ROUNDED_RECT_INIT (0, 0, 100, 100, 10), GRAPHENE_RECT_INIT (5, -5, 10, 20),
ROUNDED_RECT_INIT (0, 0, 0, 0, 0), GSK_INTERSECTION_NOT_REPRESENTABLE },
{ ROUNDED_RECT_INIT_UNIFORM (-200, 0, 200, 100, 0, 0, 0, 40), GRAPHENE_RECT_INIT (-200, 0, 160, 100),
ROUNDED_RECT_INIT_UNIFORM (-200, 0, 160, 100, 0, 0, 0, 40), GSK_INTERSECTION_NONEMPTY },
};
for (unsigned int i = 0; i < G_N_ELEMENTS (test); i++)
{
GskRoundedRect out;
GskRoundedRectIntersection res;
if (g_test_verbose ())
g_test_message ("intersection test %u", i);
memset (&out, 0, sizeof (GskRoundedRect));
res = gsk_rounded_rect_intersect_with_rect (&test[i].rounded, &test[i].rect, &out);
g_assert_true (res == test[i].result);
if (res == GSK_INTERSECTION_NONEMPTY)
{
if (!gsk_rounded_rect_equal (&out, &test[i].expected))
{
char *s = gsk_rounded_rect_to_string (&test[i].expected);
char *s2 = gsk_rounded_rect_to_string (&out);
g_test_message ("expected %s, got %s\n", s, s2);
}
g_assert_true (gsk_rounded_rect_equal (&out, &test[i].expected));
}
g_assert_true ((res != GSK_INTERSECTION_EMPTY) == gsk_rounded_rect_intersects_rect (&test[i].rounded, &test[i].rect));
}
}
int
main (int argc,
char *argv[])
@ -171,6 +245,7 @@ main (int argc,
g_test_add_func ("/rounded-rect/contains-point", test_contains_point);
g_test_add_func ("/rounded-rect/is-circular", test_is_circular);
g_test_add_func ("/rounded-rect/to-float", test_to_float);
g_test_add_func ("/rounded-rect/intersect-with-rect", test_intersect_with_rect);
return g_test_run ();
}

View File

@ -582,6 +582,14 @@ test_transform_bounds (void)
t = gsk_transform_translate (t, &GRAPHENE_POINT_INIT (-50, -50));
gsk_transform_transform_bounds (t, &bounds, &out);
g_assert_true (graphene_rect_equal (&out, &GRAPHENE_RECT_INIT(0, 0, 100, 100)));
t = gsk_transform_scale (NULL, -1, 1);
gsk_transform_transform_bounds (t, &bounds, &out);
g_assert_true (graphene_rect_equal (&out, &GRAPHENE_RECT_INIT(-100, 0, 100, 100)));
t = gsk_transform_scale (NULL, -1, -2);
gsk_transform_transform_bounds (t, &bounds, &out);
g_assert_true (graphene_rect_equal (&out, &GRAPHENE_RECT_INIT(-100, -200, 100, 200)));
}
#define DEG_TO_RAD(x) ((x) / 180.0 * G_PI)

View File

@ -25,7 +25,7 @@ setups = [
{ 'backend': 'broadway', 'if': broadway_enabled, },
{ 'name': 'wayland_smalltexture',
'backend': 'wayland', 'if': wayland_enabled,
'env': ['GSK_MAX_TEXTURE_SIZE=1024'], },
'env': ['GSK_MAX_TEXTURE_SIZE=1024'] },
{ 'backend': 'win32', 'if': os_win32 },
]
@ -39,6 +39,10 @@ foreach setup : setups
exclude += 'gsk-compare-broadway'
endif
if name == 'wayland_smalltexture'
exclude += 'wayland_smalltexture_failing'
endif
env = common_env + [
'GDK_BACKEND=@0@'.format(backend),
] + setup.get('env', [])