Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2023 James Westman <james@jwestman.net>
3 : : *
4 : : * This library is free software; you can redistribute it and/or
5 : : * modify it under the terms of the GNU Lesser General Public
6 : : * License as published by the Free Software Foundation; either
7 : : * version 2.1 of the License, or (at your option) any later version.
8 : : *
9 : : * This library is distributed in the hope that it will be useful,
10 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : : * Lesser General Public License for more details.
13 : : *
14 : : * You should have received a copy of the GNU Lesser General Public
15 : : * License along with this library; if not, see <https://www.gnu.org/licenses/>.
16 : : */
17 : :
18 : : #include "shumate-vector-sprite.h"
19 : :
20 : : /**
21 : : * ShumateVectorSprite:
22 : : *
23 : : * A sprite used to draw textures or icons.
24 : : *
25 : : * ## Symbolic icons
26 : : *
27 : : * If a sprite is created from a [iface@Gtk.SymbolicPaintable] source, such
28 : : * as a symbolic icon, then when the sprite is part of a symbol layer it
29 : : * will be drawn using the icon-color property (or the text color, if the
30 : : * sprite is part of a formatted string).
31 : : *
32 : : * Since: 1.1
33 : : */
34 : :
35 : :
36 : : struct _ShumateVectorSprite
37 : : {
38 : : GObject parent_instance;
39 : :
40 : : GdkPaintable *source_paintable;
41 : : int width, height;
42 : : double scale_factor;
43 : :
44 : : GdkRectangle source_rect;
45 : : gboolean source_rect_set : 1;
46 : : };
47 : :
48 : : static void shumate_vector_sprite_paintable_iface_init (GdkPaintableInterface *iface);
49 : : static void shumate_vector_sprite_symbolic_paintable_iface_init (GtkSymbolicPaintableInterface *iface);
50 : :
51 [ + + + - ]: 765 : G_DEFINE_TYPE_WITH_CODE (ShumateVectorSprite, shumate_vector_sprite, G_TYPE_OBJECT,
52 : : G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE, shumate_vector_sprite_paintable_iface_init)
53 : : G_IMPLEMENT_INTERFACE (GTK_TYPE_SYMBOLIC_PAINTABLE, shumate_vector_sprite_symbolic_paintable_iface_init))
54 : :
55 : : enum {
56 : : PROP_0,
57 : : PROP_WIDTH,
58 : : PROP_HEIGHT,
59 : : PROP_SCALE_FACTOR,
60 : : PROP_SOURCE_PAINTABLE,
61 : : PROP_SOURCE_RECT,
62 : : N_PROPERTIES
63 : : };
64 : :
65 : : static GParamSpec *properties[N_PROPERTIES];
66 : :
67 : : /**
68 : : * shumate_vector_sprite_new:
69 : : * @source_paintable: a [iface@Gdk.Paintable]
70 : : *
71 : : * Creates a new [class@VectorSprite] using the paintable's intrinsic size.
72 : : *
73 : : * Since: 1.1
74 : : */
75 : : ShumateVectorSprite *
76 : 309 : shumate_vector_sprite_new (GdkPaintable *source_paintable)
77 : : {
78 : 309 : return g_object_new (SHUMATE_TYPE_VECTOR_SPRITE,
79 : : "source-paintable", source_paintable,
80 : : "width", gdk_paintable_get_intrinsic_width (source_paintable),
81 : : "height", gdk_paintable_get_intrinsic_height (source_paintable),
82 : : NULL);
83 : : }
84 : :
85 : : /**
86 : : * shumate_vector_sprite_new_full:
87 : : * @source_paintable: a [iface@Gdk.Paintable]
88 : : * @width: the width of the sprite in pixels
89 : : * @height: the height of the sprite in pixels
90 : : * @scale_factor: the intended scale factor of the sprite
91 : : * @source_rect: (nullable): the source rectangle of the sprite, or %NULL to use the entire paintable
92 : : *
93 : : * Creates a new [class@VectorSprite] with the given size, scale factor,
94 : : * and area of the source paintable.
95 : : *
96 : : * Since: 1.1
97 : : */
98 : : ShumateVectorSprite *
99 : 15 : shumate_vector_sprite_new_full (GdkPaintable *source_paintable,
100 : : int width,
101 : : int height,
102 : : double scale_factor,
103 : : GdkRectangle *source_rect)
104 : : {
105 : 15 : return g_object_new (SHUMATE_TYPE_VECTOR_SPRITE,
106 : : "source-paintable", source_paintable,
107 : : "width", width,
108 : : "height", height,
109 : : "scale-factor", scale_factor,
110 : : "source-rect", source_rect,
111 : : NULL);
112 : : }
113 : :
114 : : static void
115 : 324 : shumate_vector_sprite_finalize (GObject *object)
116 : : {
117 : 324 : ShumateVectorSprite *self = SHUMATE_VECTOR_SPRITE (object);
118 : :
119 [ + - ]: 324 : g_clear_object (&self->source_paintable);
120 : :
121 : 324 : G_OBJECT_CLASS (shumate_vector_sprite_parent_class)->finalize (object);
122 : 324 : }
123 : :
124 : : static void
125 : 0 : shumate_vector_sprite_get_property (GObject *object,
126 : : unsigned prop_id,
127 : : GValue *value,
128 : : GParamSpec *pspec)
129 : : {
130 : 0 : ShumateVectorSprite *self = SHUMATE_VECTOR_SPRITE (object);
131 : :
132 [ # # # # : 0 : switch (prop_id)
# # ]
133 : : {
134 : 0 : case PROP_WIDTH:
135 : 0 : g_value_set_int (value, self->width);
136 : 0 : break;
137 : 0 : case PROP_HEIGHT:
138 : 0 : g_value_set_int (value, self->height);
139 : 0 : break;
140 : 0 : case PROP_SCALE_FACTOR:
141 : 0 : g_value_set_double (value, self->scale_factor);
142 : 0 : break;
143 : 0 : case PROP_SOURCE_PAINTABLE:
144 : 0 : g_value_set_object (value, self->source_paintable);
145 : 0 : break;
146 : 0 : case PROP_SOURCE_RECT:
147 [ # # ]: 0 : if (self->source_rect_set)
148 : 0 : g_value_set_boxed (value, &self->source_rect);
149 : : else
150 : 0 : g_value_set_boxed (value, NULL);
151 : : break;
152 : 0 : default:
153 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
154 : : }
155 : 0 : }
156 : :
157 : : static void
158 : 1620 : shumate_vector_sprite_set_property (GObject *object,
159 : : unsigned prop_id,
160 : : const GValue *value,
161 : : GParamSpec *pspec)
162 : : {
163 : 1620 : ShumateVectorSprite *self = SHUMATE_VECTOR_SPRITE (object);
164 : :
165 [ + + + + : 1620 : switch (prop_id)
+ - ]
166 : : {
167 : 324 : case PROP_WIDTH:
168 : 324 : self->width = g_value_get_int (value);
169 : 324 : break;
170 : 324 : case PROP_HEIGHT:
171 : 324 : self->height = g_value_get_int (value);
172 : 324 : break;
173 : 324 : case PROP_SCALE_FACTOR:
174 : 324 : self->scale_factor = g_value_get_double (value);
175 : 324 : break;
176 : 324 : case PROP_SOURCE_PAINTABLE:
177 [ + - ]: 324 : g_assert (self->source_paintable == NULL);
178 : 324 : self->source_paintable = g_value_dup_object (value);
179 : 324 : break;
180 : 324 : case PROP_SOURCE_RECT:
181 : : {
182 : 324 : GdkRectangle *source_rect = (GdkRectangle *) g_value_get_boxed (value);
183 [ + + ]: 324 : if (source_rect)
184 : : {
185 : 15 : self->source_rect = *source_rect;
186 : 15 : self->source_rect_set = TRUE;
187 : : }
188 : : else
189 : 309 : self->source_rect_set = FALSE;
190 : : }
191 : : break;
192 : 0 : default:
193 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
194 : : }
195 : 1620 : }
196 : :
197 : : static GdkPaintable *
198 : 0 : shumate_vector_sprite_get_current_image (GdkPaintable *paintable)
199 : : {
200 : 0 : ShumateVectorSprite *self = SHUMATE_VECTOR_SPRITE (paintable);
201 : 0 : g_autoptr(GdkPaintable) source_paintable = gdk_paintable_get_current_image (self->source_paintable);
202 : :
203 [ # # ]: 0 : return GDK_PAINTABLE (shumate_vector_sprite_new_full (
204 : : source_paintable,
205 : : self->width,
206 : : self->height,
207 : : self->scale_factor,
208 [ # # ]: 0 : self->source_rect_set ? &self->source_rect : NULL
209 : : ));
210 : : }
211 : :
212 : : static int
213 : 0 : shumate_vector_sprite_get_intrinsic_width (GdkPaintable *paintable)
214 : : {
215 : 0 : ShumateVectorSprite *self = SHUMATE_VECTOR_SPRITE (paintable);
216 : 0 : return self->width;
217 : : }
218 : :
219 : : static int
220 : 0 : shumate_vector_sprite_get_intrinsic_height (GdkPaintable *paintable)
221 : : {
222 : 0 : ShumateVectorSprite *self = SHUMATE_VECTOR_SPRITE (paintable);
223 : 0 : return self->height;
224 : : }
225 : :
226 : : static void
227 : 0 : do_snapshot (GdkPaintable *paintable,
228 : : GdkSnapshot *snapshot,
229 : : double width,
230 : : double height,
231 : : gboolean symbolic,
232 : : const GdkRGBA *colors,
233 : : gsize n_colors)
234 : : {
235 : 0 : ShumateVectorSprite *self = SHUMATE_VECTOR_SPRITE (paintable);
236 : :
237 [ # # ]: 0 : if (self->source_rect_set)
238 : : {
239 : 0 : double paintable_width = gdk_paintable_get_intrinsic_width (self->source_paintable);
240 : 0 : double paintable_height = gdk_paintable_get_intrinsic_height (self->source_paintable);
241 : :
242 : 0 : gtk_snapshot_save (snapshot);
243 : 0 : gtk_snapshot_push_clip (snapshot, &GRAPHENE_RECT_INIT (0, 0, width, height));
244 : :
245 : 0 : gtk_snapshot_translate (snapshot,
246 : 0 : &GRAPHENE_POINT_INIT (
247 : : -self->source_rect.x * (width / self->source_rect.width),
248 : : -self->source_rect.y * (height / self->source_rect.height)
249 : : ));
250 : :
251 : 0 : width = paintable_width * (width / self->source_rect.width);
252 : 0 : height = paintable_height * (height / self->source_rect.height);
253 : : }
254 : :
255 [ # # # # ]: 0 : if (symbolic && GTK_IS_SYMBOLIC_PAINTABLE (self->source_paintable))
256 : 0 : gtk_symbolic_paintable_snapshot_symbolic (GTK_SYMBOLIC_PAINTABLE (self->source_paintable),
257 : : snapshot,
258 : : width,
259 : : height,
260 : : colors,
261 : : n_colors);
262 : : else
263 : 0 : gdk_paintable_snapshot (self->source_paintable,
264 : : snapshot,
265 : : width,
266 : : height);
267 : :
268 [ # # ]: 0 : if (self->source_rect_set)
269 : : {
270 : 0 : gtk_snapshot_pop (snapshot);
271 : 0 : gtk_snapshot_restore (snapshot);
272 : : }
273 : 0 : }
274 : :
275 : : static void
276 : 0 : shumate_vector_sprite_snapshot_symbolic (GtkSymbolicPaintable *paintable,
277 : : GdkSnapshot *snapshot,
278 : : double width,
279 : : double height,
280 : : const GdkRGBA *colors,
281 : : gsize n_colors)
282 : : {
283 : 0 : do_snapshot (GDK_PAINTABLE (paintable), snapshot, width, height, TRUE, colors, n_colors);
284 : 0 : }
285 : :
286 : : static void
287 : 0 : shumate_vector_sprite_snapshot (GdkPaintable *paintable,
288 : : GdkSnapshot *snapshot,
289 : : double width,
290 : : double height)
291 : : {
292 : 0 : do_snapshot (GDK_PAINTABLE (paintable), snapshot, width, height, FALSE, NULL, 0);
293 : 0 : }
294 : :
295 : : static void
296 : 7 : shumate_vector_sprite_class_init (ShumateVectorSpriteClass *klass)
297 : : {
298 : 7 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
299 : :
300 : 7 : object_class->finalize = shumate_vector_sprite_finalize;
301 : 7 : object_class->get_property = shumate_vector_sprite_get_property;
302 : 7 : object_class->set_property = shumate_vector_sprite_set_property;
303 : :
304 : : /**
305 : : * ShumateVectorSprite:source-paintable:
306 : : *
307 : : * The [iface@Gdk.Paintable] used to draw the sprite.
308 : : *
309 : : * Since: 1.1
310 : : */
311 : 14 : properties[PROP_SOURCE_PAINTABLE] =
312 : 7 : g_param_spec_object ("source-paintable",
313 : : "source-paintable",
314 : : "source-paintable",
315 : : GDK_TYPE_PAINTABLE,
316 : : G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
317 : :
318 : : /**
319 : : * ShumateVectorSprite:width:
320 : : *
321 : : * The width at which the sprite should be drawn, in pixels.
322 : : *
323 : : * Since: 1.1
324 : : */
325 : 14 : properties[PROP_WIDTH] =
326 : 7 : g_param_spec_int ("width",
327 : : "width",
328 : : "width",
329 : : 0, G_MAXINT, 0,
330 : : G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
331 : :
332 : : /**
333 : : * ShumateVectorSprite:height:
334 : : *
335 : : * The height at which the sprite should be drawn, in pixels.
336 : : *
337 : : * Since: 1.1
338 : : */
339 : 14 : properties[PROP_HEIGHT] =
340 : 7 : g_param_spec_int ("height",
341 : : "height",
342 : : "height",
343 : : 0, G_MAXINT, 0,
344 : : G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
345 : :
346 : : /**
347 : : * ShumateVectorSprite:scale-factor:
348 : : *
349 : : * The intended scale factor of the sprite.
350 : : *
351 : : * Since: 1.1
352 : : */
353 : 14 : properties[PROP_SCALE_FACTOR] =
354 : 7 : g_param_spec_double ("scale-factor",
355 : : "scale-factor",
356 : : "scale-factor",
357 : : 1, G_MAXDOUBLE, 1,
358 : : G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
359 : :
360 : : /**
361 : : * ShumateVectorSprite:source-rect:
362 : : *
363 : : * The area of the source rectangle to draw, or %NULL to use the entire paintable.
364 : : *
365 : : * Since: 1.1
366 : : */
367 : 14 : properties[PROP_SOURCE_RECT] =
368 : 7 : g_param_spec_boxed ("source-rect",
369 : : "source-rect",
370 : : "source-rect",
371 : : GDK_TYPE_RECTANGLE,
372 : : G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
373 : :
374 : 7 : g_object_class_install_properties (object_class, N_PROPERTIES, properties);
375 : 7 : }
376 : :
377 : : static void
378 : 324 : shumate_vector_sprite_init (ShumateVectorSprite *self)
379 : : {
380 : 324 : self->scale_factor = 1;
381 : 324 : }
382 : :
383 : : static void
384 : 7 : shumate_vector_sprite_paintable_iface_init (GdkPaintableInterface *iface)
385 : : {
386 : 7 : iface->get_current_image = shumate_vector_sprite_get_current_image;
387 : 7 : iface->get_intrinsic_width = shumate_vector_sprite_get_intrinsic_width;
388 : 7 : iface->get_intrinsic_height = shumate_vector_sprite_get_intrinsic_height;
389 : 7 : iface->snapshot = shumate_vector_sprite_snapshot;
390 : 7 : }
391 : :
392 : : static void
393 : 7 : shumate_vector_sprite_symbolic_paintable_iface_init (GtkSymbolicPaintableInterface *iface)
394 : : {
395 : 7 : iface->snapshot_symbolic = shumate_vector_sprite_snapshot_symbolic;
396 : 7 : }
397 : :
398 : : /**
399 : : * shumate_vector_sprite_get_source_paintable:
400 : : * @self: a [class@VectorSprite]
401 : : *
402 : : * Gets the source [iface@Gdk.Paintable] used to draw the sprite.
403 : : *
404 : : * Note that [class@VectorSprite] also implements [iface@Gdk.Paintable].
405 : : * In most cases, you should draw the sprite rather than the original paintable.
406 : : *
407 : : * Returns: (transfer none): the source paintable
408 : : *
409 : : * Since: 1.1
410 : : */
411 : : GdkPaintable *
412 : 6 : shumate_vector_sprite_get_source_paintable (ShumateVectorSprite *self)
413 : : {
414 [ + - ]: 6 : g_return_val_if_fail (SHUMATE_IS_VECTOR_SPRITE (self), NULL);
415 : :
416 : 6 : return self->source_paintable;
417 : : }
418 : :
419 : : /**
420 : : * shumate_vector_sprite_get_width:
421 : : * @self: a [class@VectorSprite]
422 : : *
423 : : * Gets the width at which the sprite should be drawn.
424 : : *
425 : : * Returns: the sprite's width in pixels
426 : : *
427 : : * Since: 1.1
428 : : */
429 : : int
430 : 6 : shumate_vector_sprite_get_width (ShumateVectorSprite *self)
431 : : {
432 [ + - ]: 6 : g_return_val_if_fail (SHUMATE_IS_VECTOR_SPRITE (self), 0);
433 : :
434 : 6 : return self->width;
435 : : }
436 : :
437 : : /**
438 : : * shumate_vector_sprite_get_height:
439 : : * @self: a [class@VectorSprite]
440 : : *
441 : : * Gets the height at which the sprite should be drawn.
442 : : *
443 : : * Returns: the sprite's height in pixels
444 : : *
445 : : * Since: 1.1
446 : : */
447 : : int
448 : 0 : shumate_vector_sprite_get_height (ShumateVectorSprite *self)
449 : : {
450 [ # # ]: 0 : g_return_val_if_fail (SHUMATE_IS_VECTOR_SPRITE (self), 0);
451 : :
452 : 0 : return self->height;
453 : : }
454 : :
455 : : /**
456 : : * shumate_vector_sprite_get_scale_factor:
457 : : * @self: a [class@VectorSprite]
458 : : *
459 : : * Gets the intended scale factor of the sprite.
460 : : *
461 : : * Returns: the sprite's scale factor
462 : : *
463 : : * Since: 1.1
464 : : */
465 : : double
466 : 372 : shumate_vector_sprite_get_scale_factor (ShumateVectorSprite *self)
467 : : {
468 [ + - ]: 372 : g_return_val_if_fail (SHUMATE_IS_VECTOR_SPRITE (self), 0);
469 : :
470 : 372 : return self->scale_factor;
471 : : }
472 : :
473 : : /**
474 : : * shumate_vector_sprite_get_source_rect:
475 : : * @self: a [class@VectorSprite]
476 : : *
477 : : * Gets the source rectangle of the sprite.
478 : : *
479 : : * Returns: (nullable) (transfer none): the sprite's source rectangle, or %NULL if the entire paintable is used
480 : : *
481 : : * Since: 1.1
482 : : */
483 : : GdkRectangle *
484 : 0 : shumate_vector_sprite_get_source_rect (ShumateVectorSprite *self)
485 : : {
486 [ # # ]: 0 : g_return_val_if_fail (SHUMATE_IS_VECTOR_SPRITE (self), NULL);
487 : :
488 [ # # ]: 0 : if (self->source_rect_set)
489 : 0 : return &self->source_rect;
490 : : else
491 : : return NULL;
492 : : }
|