forked from AuroraMiddleware/gtk
Merge branch 'wip/otte/dont-scale-or-it-breaks' into 'main'
Add a test for lots of texture scaling and fix the fallout See merge request GNOME/gtk!5509
This commit is contained in:
commit
90a3584c1d
@ -178,10 +178,10 @@ gdk_memory_texture_new_subtexture (GdkMemoryTexture *source,
|
||||
GBytes *bytes;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_MEMORY_TEXTURE (source), NULL);
|
||||
g_return_val_if_fail (x >= 0 || x < GDK_TEXTURE (source)->width, NULL);
|
||||
g_return_val_if_fail (y >= 0 || y < GDK_TEXTURE (source)->height, NULL);
|
||||
g_return_val_if_fail (width > 0 || x + width <= GDK_TEXTURE (source)->width, NULL);
|
||||
g_return_val_if_fail (height > 0 || y + height <= GDK_TEXTURE (source)->height, NULL);
|
||||
g_return_val_if_fail (x >= 0 && x < GDK_TEXTURE (source)->width, NULL);
|
||||
g_return_val_if_fail (y >= 0 && y < GDK_TEXTURE (source)->height, NULL);
|
||||
g_return_val_if_fail (width > 0 && x + width <= GDK_TEXTURE (source)->width, NULL);
|
||||
g_return_val_if_fail (height > 0 && y + height <= GDK_TEXTURE (source)->height, NULL);
|
||||
|
||||
texture = GDK_TEXTURE (source);
|
||||
bpp = gdk_memory_format_bytes_per_pixel (texture->format);
|
||||
|
@ -872,6 +872,21 @@ gsk_gl_render_job_transform_bounds (GskGLRenderJob *job,
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_gl_render_job_untransform_bounds (GskGLRenderJob *job,
|
||||
const graphene_rect_t *rect,
|
||||
graphene_rect_t *out_rect)
|
||||
{
|
||||
GskTransform *transform;
|
||||
|
||||
transform = gsk_transform_invert (gsk_transform_ref (job->current_modelview->transform));
|
||||
|
||||
gsk_transform_transform_bounds (transform, rect, out_rect);
|
||||
|
||||
out_rect->origin.x -= job->offset_x;
|
||||
out_rect->origin.y -= job->offset_y;
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_gl_render_job_transform_rounded_rect (GskGLRenderJob *job,
|
||||
const GskRoundedRect *rect,
|
||||
@ -3615,127 +3630,98 @@ gsk_gl_render_job_visit_texture_scale_node (GskGLRenderJob *job,
|
||||
int min_filter = min_filters[scaling_filter];
|
||||
int mag_filter = mag_filters[scaling_filter];
|
||||
int max_texture_size = job->command_queue->max_texture_size;
|
||||
graphene_rect_t clip_rect;
|
||||
GskGLRenderTarget *render_target;
|
||||
GskGLRenderOffscreen offscreen = {0};
|
||||
graphene_rect_t viewport;
|
||||
graphene_rect_t prev_viewport;
|
||||
graphene_matrix_t prev_projection;
|
||||
float prev_alpha;
|
||||
guint prev_fbo;
|
||||
guint texture_id;
|
||||
float u0, u1, v0, v1;
|
||||
|
||||
if (scaling_filter == GSK_SCALING_FILTER_LINEAR)
|
||||
gsk_gl_render_job_untransform_bounds (job, &job->current_clip->rect.bounds, &clip_rect);
|
||||
if (!graphene_rect_intersection (bounds, &clip_rect, &clip_rect))
|
||||
return;
|
||||
|
||||
if G_UNLIKELY (clip_rect.size.width > max_texture_size ||
|
||||
clip_rect.size.height > max_texture_size)
|
||||
{
|
||||
gsk_gl_render_job_visit_texture (job, texture, bounds);
|
||||
return;
|
||||
}
|
||||
|
||||
if G_LIKELY (texture->width <= max_texture_size &&
|
||||
texture->height <= max_texture_size)
|
||||
viewport = GRAPHENE_RECT_INIT (0, 0,
|
||||
clip_rect.size.width,
|
||||
clip_rect.size.height);
|
||||
|
||||
if (!gsk_gl_driver_create_render_target (job->driver,
|
||||
(int) ceilf (clip_rect.size.width),
|
||||
(int) ceilf (clip_rect.size.height),
|
||||
get_target_format (job, node),
|
||||
GL_LINEAR, GL_LINEAR,
|
||||
&render_target))
|
||||
{
|
||||
GskGLRenderTarget *render_target;
|
||||
GskGLRenderOffscreen offscreen = {0};
|
||||
graphene_rect_t viewport;
|
||||
graphene_rect_t prev_viewport;
|
||||
graphene_matrix_t prev_projection;
|
||||
float prev_alpha;
|
||||
guint prev_fbo;
|
||||
guint texture_id;
|
||||
|
||||
viewport = GRAPHENE_RECT_INIT (0, 0,
|
||||
bounds->size.width,
|
||||
bounds->size.height);
|
||||
|
||||
if (!gsk_gl_driver_create_render_target (job->driver,
|
||||
(int) ceilf (viewport.size.width),
|
||||
(int) ceilf (viewport.size.height),
|
||||
get_target_format (job, node),
|
||||
GL_LINEAR, GL_LINEAR,
|
||||
&render_target))
|
||||
{
|
||||
/* viewport is too big, slice the texture and try again */
|
||||
goto slice;
|
||||
}
|
||||
|
||||
gsk_gl_render_job_upload_texture (job, texture, min_filter, mag_filter, &offscreen);
|
||||
|
||||
g_assert (offscreen.texture_id);
|
||||
g_assert (offscreen.was_offscreen == FALSE);
|
||||
|
||||
gsk_gl_render_job_set_viewport (job, &viewport, &prev_viewport);
|
||||
gsk_gl_render_job_set_projection_from_rect (job, &viewport, &prev_projection);
|
||||
gsk_gl_render_job_set_modelview (job, NULL);
|
||||
prev_alpha = gsk_gl_render_job_set_alpha (job, 1.0f);
|
||||
gsk_gl_render_job_push_clip (job, &GSK_ROUNDED_RECT_INIT_FROM_RECT (viewport));
|
||||
|
||||
prev_fbo = gsk_gl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id);
|
||||
gsk_gl_command_queue_clear (job->command_queue, 0, &viewport);
|
||||
|
||||
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,
|
||||
offscreen.texture_id);
|
||||
gsk_gl_render_job_draw_offscreen (job, &viewport, &offscreen);
|
||||
gsk_gl_render_job_end_draw (job);
|
||||
|
||||
gsk_gl_render_job_pop_clip (job);
|
||||
gsk_gl_render_job_pop_modelview (job);
|
||||
gsk_gl_render_job_set_viewport (job, &prev_viewport, NULL);
|
||||
gsk_gl_render_job_set_projection (job, &prev_projection);
|
||||
gsk_gl_render_job_set_alpha (job, prev_alpha);
|
||||
gsk_gl_command_queue_bind_framebuffer (job->command_queue, prev_fbo);
|
||||
|
||||
texture_id = gsk_gl_driver_release_render_target (job->driver, render_target, FALSE);
|
||||
|
||||
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,
|
||||
texture_id);
|
||||
gsk_gl_render_job_draw_offscreen_rect (job, bounds);
|
||||
gsk_gl_render_job_end_draw (job);
|
||||
gsk_gl_render_job_visit_texture (job, texture, bounds);
|
||||
return;
|
||||
}
|
||||
else
|
||||
slice:
|
||||
{
|
||||
float min_x = bounds->origin.x;
|
||||
float min_y = bounds->origin.y;
|
||||
float max_x = min_x + bounds->size.width;
|
||||
float max_y = min_y + bounds->size.height;
|
||||
float scale_x = (max_x - min_x) / texture->width;
|
||||
float scale_y = (max_y - min_y) / texture->height;
|
||||
GskGLTextureSlice *slices = NULL;
|
||||
guint n_slices = 0;
|
||||
GdkGLContext *context = gsk_gl_driver_get_context (job->driver);
|
||||
guint rows, cols;
|
||||
|
||||
/* Slice enough that neither the original texture nor the scaled texture
|
||||
* exceed the texture size limit
|
||||
*/
|
||||
cols = (int)(MAX (bounds->size.width, texture->width) / (max_texture_size / 4)) + 1;
|
||||
rows = (int)(MAX (bounds->size.height, texture->height) / (max_texture_size / 4)) + 1;
|
||||
gsk_gl_render_job_upload_texture (job, texture, min_filter, mag_filter, &offscreen);
|
||||
|
||||
gsk_gl_driver_slice_texture (job->driver, texture, GL_NEAREST, GL_NEAREST, cols, rows, &slices, &n_slices);
|
||||
g_assert (offscreen.texture_id);
|
||||
g_assert (offscreen.was_offscreen == FALSE);
|
||||
|
||||
g_assert (slices != NULL);
|
||||
g_assert (n_slices > 0);
|
||||
u0 = (clip_rect.origin.x - bounds->origin.x) / bounds->size.width;
|
||||
v0 = (clip_rect.origin.y - bounds->origin.y) / bounds->size.height;
|
||||
u1 = (clip_rect.origin.x + clip_rect.size.width - bounds->origin.x) / bounds->size.width;
|
||||
v1 = (clip_rect.origin.y + clip_rect.size.height - bounds->origin.y) / bounds->size.height;
|
||||
|
||||
for (guint i = 0; i < n_slices; i ++)
|
||||
{
|
||||
const GskGLTextureSlice *slice = &slices[i];
|
||||
float x1, x2, y1, y2;
|
||||
GdkTexture *sub_texture;
|
||||
GskRenderNode *sub_node;
|
||||
gsk_gl_render_job_set_viewport (job, &viewport, &prev_viewport);
|
||||
gsk_gl_render_job_set_projection_from_rect (job, &viewport, &prev_projection);
|
||||
gsk_gl_render_job_set_modelview (job, NULL);
|
||||
prev_alpha = gsk_gl_render_job_set_alpha (job, 1.0f);
|
||||
gsk_gl_render_job_push_clip (job, &GSK_ROUNDED_RECT_INIT_FROM_RECT (viewport));
|
||||
|
||||
x1 = min_x + (scale_x * slice->rect.x);
|
||||
x2 = x1 + (slice->rect.width * scale_x);
|
||||
y1 = min_y + (scale_y * slice->rect.y);
|
||||
y2 = y1 + (slice->rect.height * scale_y);
|
||||
prev_fbo = gsk_gl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id);
|
||||
gsk_gl_command_queue_clear (job->command_queue, 0, &viewport);
|
||||
|
||||
sub_texture = gdk_gl_texture_new (context, slice->texture_id, slice->rect.width, slice->rect.height, NULL, NULL);
|
||||
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,
|
||||
offscreen.texture_id);
|
||||
gsk_gl_render_job_draw_coords (job,
|
||||
0, 0, clip_rect.size.width, clip_rect.size.height,
|
||||
u0, v0, u1, v1,
|
||||
(guint16[]) { FP16_ZERO, FP16_ZERO, FP16_ZERO, FP16_ZERO });
|
||||
gsk_gl_render_job_end_draw (job);
|
||||
|
||||
sub_node = gsk_texture_scale_node_new (sub_texture, &GRAPHENE_RECT_INIT (x1, y1, x2 - x1, y2 - y1), scaling_filter);
|
||||
gsk_gl_render_job_pop_clip (job);
|
||||
gsk_gl_render_job_pop_modelview (job);
|
||||
gsk_gl_render_job_set_viewport (job, &prev_viewport, NULL);
|
||||
gsk_gl_render_job_set_projection (job, &prev_projection);
|
||||
gsk_gl_render_job_set_alpha (job, prev_alpha);
|
||||
gsk_gl_command_queue_bind_framebuffer (job->command_queue, prev_fbo);
|
||||
|
||||
gsk_gl_render_job_visit_node (job, sub_node);
|
||||
gsk_render_node_unref (sub_node);
|
||||
g_object_unref (sub_texture);
|
||||
}
|
||||
}
|
||||
texture_id = gsk_gl_driver_release_render_target (job->driver, render_target, FALSE);
|
||||
|
||||
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,
|
||||
texture_id);
|
||||
gsk_gl_render_job_draw_coords (job,
|
||||
job->offset_x + clip_rect.origin.x,
|
||||
job->offset_y + clip_rect.origin.y,
|
||||
job->offset_x + clip_rect.origin.x + clip_rect.size.width,
|
||||
job->offset_y + clip_rect.origin.y + clip_rect.size.height,
|
||||
0, clip_rect.size.width / ceilf (clip_rect.size.width),
|
||||
clip_rect.size.height / ceilf (clip_rect.size.height), 0,
|
||||
(guint16[]){ FP16_ZERO, FP16_ZERO, FP16_ZERO, FP16_ZERO } );
|
||||
gsk_gl_render_job_end_draw (job);
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -58,6 +58,16 @@ rectangle_init_from_graphene (cairo_rectangle_int_t *cairo,
|
||||
cairo->height = ceilf (graphene->origin.y + graphene->size.height) - cairo->y;
|
||||
}
|
||||
|
||||
static void
|
||||
_graphene_rect_init_from_clip_extents (graphene_rect_t *rect,
|
||||
cairo_t *cr)
|
||||
{
|
||||
double x1c, y1c, x2c, y2c;
|
||||
|
||||
cairo_clip_extents (cr, &x1c, &y1c, &x2c, &y2c);
|
||||
graphene_rect_init (rect, x1c, y1c, x2c - x1c, y2c - y1c);
|
||||
}
|
||||
|
||||
/* {{{ GSK_COLOR_NODE */
|
||||
|
||||
/**
|
||||
@ -1625,15 +1635,21 @@ gsk_texture_scale_node_draw (GskRenderNode *node,
|
||||
};
|
||||
cairo_t *cr2;
|
||||
cairo_surface_t *surface2;
|
||||
graphene_rect_t clip_rect;
|
||||
|
||||
/* Make sure we draw the minimum region by using the clip */
|
||||
gsk_cairo_rectangle (cr, &node->bounds);
|
||||
cairo_clip (cr);
|
||||
_graphene_rect_init_from_clip_extents (&clip_rect, cr);
|
||||
if (clip_rect.size.width <= 0 || clip_rect.size.height <= 0)
|
||||
return;
|
||||
|
||||
surface2 = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
||||
(int) ceilf (node->bounds.size.width),
|
||||
(int) ceilf (node->bounds.size.height));
|
||||
(int) ceilf (clip_rect.size.width),
|
||||
(int) ceilf (clip_rect.size.height));
|
||||
cairo_surface_set_device_offset (surface2, -clip_rect.origin.x, -clip_rect.origin.y);
|
||||
cr2 = cairo_create (surface2);
|
||||
|
||||
cairo_set_source_rgba (cr2, 0, 0, 0, 0);
|
||||
cairo_paint (cr2);
|
||||
|
||||
surface = gdk_texture_download_surface (self->texture);
|
||||
pattern = cairo_pattern_create_for_surface (surface);
|
||||
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
|
||||
@ -2072,15 +2088,15 @@ gsk_inset_shadow_node_draw (GskRenderNode *node,
|
||||
GskInsetShadowNode *self = (GskInsetShadowNode *) node;
|
||||
GskRoundedRect box, clip_box;
|
||||
int clip_radius;
|
||||
double x1c, y1c, x2c, y2c;
|
||||
graphene_rect_t clip_rect;
|
||||
double blur_radius;
|
||||
|
||||
/* We don't need to draw invisible shadows */
|
||||
if (gdk_rgba_is_clear (&self->color))
|
||||
return;
|
||||
|
||||
cairo_clip_extents (cr, &x1c, &y1c, &x2c, &y2c);
|
||||
if (!gsk_rounded_rect_intersects_rect (&self->outline, &GRAPHENE_RECT_INIT (x1c, y1c, x2c - x1c, y2c - y1c)))
|
||||
_graphene_rect_init_from_clip_extents (&clip_rect, cr);
|
||||
if (!gsk_rounded_rect_intersects_rect (&self->outline, &clip_rect))
|
||||
return;
|
||||
|
||||
blur_radius = self->blur_radius / 2;
|
||||
@ -2368,7 +2384,7 @@ gsk_outset_shadow_node_draw (GskRenderNode *node,
|
||||
GskOutsetShadowNode *self = (GskOutsetShadowNode *) node;
|
||||
GskRoundedRect box, clip_box;
|
||||
int clip_radius;
|
||||
double x1c, y1c, x2c, y2c;
|
||||
graphene_rect_t clip_rect;
|
||||
float top, right, bottom, left;
|
||||
double blur_radius;
|
||||
|
||||
@ -2376,8 +2392,8 @@ gsk_outset_shadow_node_draw (GskRenderNode *node,
|
||||
if (gdk_rgba_is_clear (&self->color))
|
||||
return;
|
||||
|
||||
cairo_clip_extents (cr, &x1c, &y1c, &x2c, &y2c);
|
||||
if (gsk_rounded_rect_contains_rect (&self->outline, &GRAPHENE_RECT_INIT (x1c, y1c, x2c - x1c, y2c - y1c)))
|
||||
_graphene_rect_init_from_clip_extents (&clip_rect, cr);
|
||||
if (!gsk_rounded_rect_intersects_rect (&self->outline, &clip_rect))
|
||||
return;
|
||||
|
||||
blur_radius = self->blur_radius / 2;
|
||||
|
8
testsuite/gsk/compare/texture-scale-magnify-10000x.node
Normal file
8
testsuite/gsk/compare/texture-scale-magnify-10000x.node
Normal file
@ -0,0 +1,8 @@
|
||||
clip {
|
||||
clip: 24995 24995 10 10;
|
||||
child: texture-scale {
|
||||
texture: url("data:;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAKElEQVQYlWNkYGD4z4AG/v/HEGJgwhDBAQZQIQs2hzMyMtLBauorBACQUgcSISWLRgAAAABJRU5ErkJggg==");
|
||||
bounds: 0 0 50000 50000;
|
||||
filter: nearest;
|
||||
}
|
||||
}
|
BIN
testsuite/gsk/compare/texture-scale-magnify-10000x.png
Normal file
BIN
testsuite/gsk/compare/texture-scale-magnify-10000x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 97 B |
8
testsuite/gsk/compare/texture-scale-stripes.node
Normal file
8
testsuite/gsk/compare/texture-scale-stripes.node
Normal file
@ -0,0 +1,8 @@
|
||||
clip {
|
||||
clip: 3950 3950 100 100;
|
||||
child: texture-scale {
|
||||
bounds: 0 0 19991 19991;
|
||||
filter: nearest;
|
||||
texture: url('data:,<svg><rect width="10" height="10" style="fill:red" /></svg>');
|
||||
}
|
||||
}
|
BIN
testsuite/gsk/compare/texture-scale-stripes.png
Normal file
BIN
testsuite/gsk/compare/texture-scale-stripes.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 313 B |
@ -66,19 +66,21 @@ compare_render_tests = [
|
||||
'outset_shadow_offset_y',
|
||||
'outset_shadow_rounded_top',
|
||||
'outset_shadow_simple',
|
||||
'scaled-cairo',
|
||||
'scale-textures-negative-ngl',
|
||||
'scale-up-down',
|
||||
'shadow-in-opacity',
|
||||
'texture-url',
|
||||
'repeat',
|
||||
'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',
|
||||
'scaled-texture',
|
||||
'shadow-in-opacity',
|
||||
'texture-scale-magnify-10000x',
|
||||
'texture-scale-stripes',
|
||||
'texture-url',
|
||||
'transform-in-transform',
|
||||
'transform-in-transform-in-transform',
|
||||
'rounded-clip-in-clip-3d', # not really 3d, but cairo fails it
|
||||
'scaled-texture',
|
||||
]
|
||||
|
||||
# these are too sensitive to differences in the renderers
|
||||
|
Loading…
Reference in New Issue
Block a user