#include "config.h"
#include "gskgpucachedstrokeprivate.h"
#include "gskgpucacheprivate.h"
#include "gskgpucachedprivate.h"
#include "gskgpudeviceprivate.h"
#include "gskgpuuploadopprivate.h"
#include "gskgpuutilsprivate.h"
#include "gskgpuprintprivate.h"
#include "gskgpuimageprivate.h"
#include "gskenumtypes.h"
#include "gskrectprivate.h"
#include "gskstrokeprivate.h"
#include "gskpathprivate.h"
#include "gsktransformprivate.h"
#include "gdk/gdkcolorstateprivate.h"
#include "gdk/gdkcolorprivate.h"
#include "gdk/gdkcairoprivate.h"
#include "gsk/gskprivate.h"
typedef struct _GskGpuCachedStroke GskGpuCachedStroke;
struct _GskGpuCachedStroke
{
GskGpuCached parent;
GskPath *path;
GskStroke stroke;
float sx, sy;
/* Subpixel position of the stroke bounds wrt to device grid */
guint fx, fy;
GskGpuImage *image;
graphene_point_t image_offset;
};
static void
gsk_gpu_cached_stroke_finalize (GskGpuCached *cached)
{
GskGpuCachedStroke *self = (GskGpuCachedStroke *) cached;
GskGpuCachePrivate *priv = gsk_gpu_cache_get_private (cached->cache);
g_hash_table_remove (priv->stroke_cache, self);
gsk_path_unref (self->path);
gsk_stroke_clear (&self->stroke);
g_object_unref (self->image);
}
static gboolean
gsk_gpu_cached_stroke_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 ;
}
/* Strokes are only collected when their atlas is freed */
return FALSE ;
}
static guint
gsk_gpu_cached_stroke_hash (gconstpointer data)
{
const GskGpuCachedStroke *self = data;
/* We ignore dashes here */
return GPOINTER_TO_UINT (self->path) ^
((guint) (self->stroke.line_width * 10 )) ^
(((guint) (self->stroke.miter_limit * 10 )) << 4 ) ^
(self->stroke.line_cap << 6 ) ^
(self->stroke.line_join << 8 ) ^
(((guint) (self->sx * 16 )) << 16 ) ^
((guint) (self->sy * 16 ) << 8 ) ^
(self->fx << 4 ) ^
self->fy;
}
static gboolean
gsk_gpu_cached_stroke_equal (gconstpointer v1,
gconstpointer v2)
{
const GskGpuCachedStroke *stroke1 = v1;
const GskGpuCachedStroke *stroke2 = v2;
return stroke1->fx == stroke2->fx &&
stroke1->fy == stroke2->fy &&
stroke1->path == stroke2->path &&
stroke1->sx == stroke2->sx &&
stroke1->sy == stroke2->sy &&
gsk_stroke_equal (&stroke1->stroke, &stroke2->stroke);
}
static const GskGpuCachedClass GSK_GPU_CACHED_STROKE_CLASS =
{
sizeof (GskGpuCachedStroke),
"Stroke" ,
FALSE ,
gsk_gpu_cached_print_no_stats,
gsk_gpu_cached_stroke_finalize,
gsk_gpu_cached_stroke_should_collect
};
typedef struct _StrokeData StrokeData;
struct _StrokeData
{
GskPath *path;
GskStroke stroke;
};
static void
stroke_data_free (gpointer data)
{
StrokeData *stroke = data;
gsk_path_unref (stroke->path);
gsk_stroke_clear (&stroke->stroke);
g_free (stroke);
}
static void
stroke_path (gpointer data,
cairo_t *cr)
{
StrokeData *stroke = data;
cairo_set_source_rgba (cr, 1 , 1 , 1 , 1 );
gsk_cairo_stroke_path (cr, stroke->path, &stroke->stroke);
}
static void
stroke_path_print (gpointer data,
GString *string)
{
StrokeData *stroke = data;
char *str;
g_string_append_printf (string, "stroke %g " ,
gsk_stroke_get_line_width (&stroke->stroke));
gsk_gpu_print_enum (string, GSK_TYPE_LINE_CAP, gsk_stroke_get_line_cap (&stroke->stroke));
gsk_gpu_print_enum (string, GSK_TYPE_LINE_JOIN, gsk_stroke_get_line_join (&stroke->stroke));
g_string_append_printf (string, "%g " , gsk_stroke_get_miter_limit (&stroke->stroke));
str = gsk_path_to_string (stroke->path);
g_string_append_printf (string, "%.*s%s" ,
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_stroke_lookup (GskGpuCache *self,
GskGpuFrame *frame,
const graphene_size_t *scale,
const graphene_rect_t *bounds,
GskTransform *modelview,
GskPath *path,
const GskStroke *stroke,
graphene_rect_t *out_rect)
{
GskGpuCachePrivate *priv = gsk_gpu_cache_get_private (self);
float sx, sy, dx, dy;
GskGpuCachedStroke *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->stroke_cache,
&(GskGpuCachedStroke) {
.path = path,
.stroke = *stroke,
.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_stroke_bounds (path, stroke, &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_STROKE_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->stroke = GSK_STROKE_INIT_COPY (stroke);
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->stroke_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,
stroke_path,
stroke_path_print,
g_memdup2 (&(StrokeData) {
.path = gsk_path_ref (path),
.stroke = GSK_STROKE_INIT_COPY (stroke),
}, sizeof (StrokeData)),
(GDestroyNotify) stroke_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_stroke_init_cache (GskGpuCache *cache)
{
GskGpuCachePrivate *priv = gsk_gpu_cache_get_private (cache);
priv->stroke_cache = g_hash_table_new (gsk_gpu_cached_stroke_hash,
gsk_gpu_cached_stroke_equal);
}
void
gsk_gpu_cached_stroke_finish_cache (GskGpuCache *cache)
{
GskGpuCachePrivate *priv = gsk_gpu_cache_get_private (cache);
g_hash_table_unref (priv->stroke_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