#include "config.h"
#include "gskgpucachedfillprivate.h"
#include "gskgpucacheprivate.h"
#include "gskgpucachedprivate.h"
#include "gskgpudeviceprivate.h"
#include "gskgpuuploadopprivate.h"
#include "gskgpuutilsprivate.h"
#include "gskgpuimageprivate.h"
#include "gskpathprivate.h"
#include "gskrectprivate.h"
#include "gsktransformprivate.h"
#include "gdk/gdkcolorstateprivate.h"
#include "gdk/gdkcolorprivate.h"
#include "gdk/gdkcairoprivate.h"
#include "gsk/gskprivate.h"
typedef struct _GskGpuCachedFill GskGpuCachedFill;
struct _GskGpuCachedFill
{
GskGpuCached parent;
GskPath *path;
GskFillRule fill_rule;
float sx, sy;
/* Subpixel position of the path bounds wrt to device grid */
guint fx, fy;
GskGpuImage *image;
graphene_point_t image_offset;
};
static void
gsk_gpu_cached_fill_finalize (GskGpuCached *cached)
{
GskGpuCachedFill *self = (GskGpuCachedFill *) cached;
GskGpuCachePrivate *priv = gsk_gpu_cache_get_private (cached->cache);
g_hash_table_remove (priv->fill_cache, self);
gsk_path_unref (self->path);
g_object_unref (self->image);
}
static gboolean
gsk_gpu_cached_fill_should_collect (GskGpuCached *cached,
gint64 cache_timeout,
gint64 timestamp)
{
if (gsk_gpu_cached_is_old (cached, cache_timeout, timestamp))
{
if (cached->atlas)
gsk_gpu_cached_set_stale (cached, TRUE );
else
return TRUE ;
}
/* Fills are only collected when their atlas is freed */
return FALSE ;
}
static guint
gsk_gpu_cached_fill_hash (gconstpointer data)
{
const GskGpuCachedFill *self = data;
return GPOINTER_TO_UINT (self->path) ^
(self->fill_rule) << 28 ^
(((guint) (self->sx * 16 )) << 16 ) ^
((guint) (self->sy * 16 ) << 8 ) ^
(self->fx << 4 ) ^
self->fy;
}
static gboolean
gsk_gpu_cached_fill_equal (gconstpointer v1,
gconstpointer v2)
{
const GskGpuCachedFill *fill1 = v1;
const GskGpuCachedFill *fill2 = v2;
return fill1->fx == fill2->fx &&
fill1->fy == fill2->fy &&
fill1->path == fill2->path &&
fill1->fill_rule == fill2->fill_rule &&
fill1->sx == fill2->sx &&
fill1->sy == fill2->sy;
}
static const GskGpuCachedClass GSK_GPU_CACHED_FILL_CLASS =
{
sizeof (GskGpuCachedFill),
"Fill" ,
FALSE ,
gsk_gpu_cached_print_no_stats,
gsk_gpu_cached_fill_finalize,
gsk_gpu_cached_fill_should_collect
};
typedef struct _FillData FillData;
struct _FillData
{
GskPath *path;
GskFillRule fill_rule;
};
static void
fill_data_free (gpointer data)
{
FillData *fill = data;
gsk_path_unref (fill->path);
g_free (fill);
}
static void
fill_path (gpointer data,
cairo_t *cr)
{
FillData *fill = data;
cairo_set_source_rgba (cr, 1 , 1 , 1 , 1 );
gsk_cairo_set_fill_rule (cr, fill->fill_rule);
gsk_path_to_cairo (fill->path, cr);
cairo_fill (cr);
}
static void
fill_path_print (gpointer data,
GString *string)
{
FillData *fill = data;
char *str;
str = gsk_path_to_string (fill->path);
g_string_append_printf (string, "fill %s %.*s%s" ,
fill->fill_rule == GSK_FILL_RULE_WINDING ? "★" : "✫" ,
20 , str, strlen (str) > 20 ? "…" : "" );
g_free (str);
}
static gsize
mod_subpixel (float pos,
float scale,
gsize subpixel_scale,
float *delta)
{
scale *= subpixel_scale;
pos = fmod (scale * pos, subpixel_scale);
*delta = (ceil (pos) - pos) / scale;
if (pos > 0 .0 )
return subpixel_scale - (gsize) ceil (pos);
else
return (gsize) - ceil (pos);
}
static void
determine_scale_and_subpixel_grid (const graphene_size_t *scale,
GskTransform *modelview,
float *sx,
float *sy,
size_t *subpixel_scale)
{
if (gsk_transform_get_fine_category (modelview) <= GSK_FINE_TRANSFORM_CATEGORY_2D)
{
*sx = *sy = ceilf (MAX (scale->width, scale->height) + 0 .5 );
*subpixel_scale = 1 ;
}
else
{
*sx = scale->width;
*sy = scale->height;
*subpixel_scale = 32 ;
}
}
GskGpuImage *
gsk_gpu_cached_fill_lookup (GskGpuCache *self,
GskGpuFrame *frame,
const graphene_size_t *scale,
const graphene_rect_t *bounds,
GskTransform *modelview,
GskPath *path,
GskFillRule fill_rule,
graphene_rect_t *out_rect)
{
GskGpuCachePrivate *priv = gsk_gpu_cache_get_private (self);
float sx, sy, dx, dy;
GskGpuCachedFill *cached;
gsize fx, fy, padding;
cairo_rectangle_int_t area;
GskGpuImage *image = NULL;
graphene_rect_t viewport;
size_t subpixel_scale;
determine_scale_and_subpixel_grid (scale, modelview, &sx, &sy, &subpixel_scale);
fx = mod_subpixel (bounds->origin.x, sx, subpixel_scale, &dx);
fy = mod_subpixel (bounds->origin.y, sy, subpixel_scale, &dy);
cached = g_hash_table_lookup (priv->fill_cache,
&(GskGpuCachedFill) {
.path = path,
.fill_rule = fill_rule,
.sx = sx,
.sy = sy,
.fx = fx,
.fy = fy,
});
if (cached)
{
gsk_gpu_cached_use ((GskGpuCached *) cached);
graphene_rect_init (out_rect,
cached->image_offset.x - dx,
cached->image_offset.y - dy,
gsk_gpu_image_get_width (cached->image) / sx,
gsk_gpu_image_get_height (cached->image) / sy);
return g_object_ref (cached->image);
}
if (!gsk_path_get_bounds (path, &viewport) ||
!gsk_rect_snap_to_grid_grow (&viewport,
scale,
&GRAPHENE_POINT_INIT ((float ) fx / (sx * subpixel_scale),
(float ) fy / (sy * subpixel_scale)),
&viewport))
return NULL;
padding = 1 ;
/* Should already be integers because of snap_to_grid() above, but round just to be sure */
cached = gsk_gpu_cached_new_from_atlas (self,
&GSK_GPU_CACHED_FILL_CLASS,
round (sx * viewport.size.width) + 2 * padding,
round (sy * viewport.size.height) + 2 * padding);
if (cached)
{
image = gsk_gpu_cached_get_atlas_image ((GskGpuCached *) cached);
area = *gsk_gpu_cached_get_atlas_area ((GskGpuCached *) cached);
g_object_ref (image);
cached->path = gsk_path_ref (path);
cached->fill_rule = fill_rule;
cached->sx = sx;
cached->sy = sy;
cached->fx = fx;
cached->fy = fy;
cached->image = g_object_ref (image);
graphene_rect_inset (&viewport, padding / -sx, padding / -sy);
cached->image_offset = GRAPHENE_POINT_INIT (viewport.origin.x - area.x / sx,
viewport.origin.y - area.y / sy);
((GskGpuCached *) cached)->pixels = area.width * area.height;
g_hash_table_insert (priv->fill_cache, cached, cached);
gsk_gpu_cached_use ((GskGpuCached *) cached);
}
else
{
/* If the unclipped path is too large to fit in the atlas,
* we give up on caching . We still need to return a mask ,
* but we ' ll use the clipped bounds that we were given .
* We ' ll also assume those are grid aligned .
*/
viewport = *bounds;
area.x = 0 ;
area.y = 0 ;
area.width = ceil (sx * viewport.size.width);
area.height = ceil (sy * viewport.size.height);
image = gsk_gpu_device_create_upload_image (gsk_gpu_cache_get_device (self),
FALSE ,
GDK_MEMORY_DEFAULT,
gsk_gpu_color_state_get_conversion (GDK_COLOR_STATE_SRGB),
area.width,
area.height);
if (image == NULL)
return NULL;
}
gsk_gpu_upload_cairo_into_op (frame,
image,
&area,
&viewport,
fill_path,
fill_path_print,
g_memdup2 (&(FillData) {
.path = gsk_path_ref (path),
.fill_rule = fill_rule,
}, sizeof (FillData)),
(GDestroyNotify) fill_data_free);
graphene_rect_init (out_rect,
viewport.origin.x - area.x / sx - dx,
viewport.origin.y - area.y / sy - dy,
gsk_gpu_image_get_width (image) / sx,
gsk_gpu_image_get_height (image) / sy);
return image;
}
void
gsk_gpu_cached_fill_init_cache (GskGpuCache *cache)
{
GskGpuCachePrivate *priv = gsk_gpu_cache_get_private (cache);
priv->fill_cache = g_hash_table_new (gsk_gpu_cached_fill_hash,
gsk_gpu_cached_fill_equal);
}
void
gsk_gpu_cached_fill_finish_cache (GskGpuCache *cache)
{
GskGpuCachePrivate *priv = gsk_gpu_cache_get_private (cache);
g_hash_table_unref (priv->fill_cache);
}
Messung V0.5 in Prozent C=98 H=96 G=96
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-07-02)
¤
*© Formatika GbR, Deutschland