1 /*
2  * Copyright © 2018 Benjamin Otte
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 <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Benjamin Otte <otte@gnome.org>
18  */
19 
20 #include "config.h"
21 
22 #include "gdkpaintable.h"
23 
24 #include "gdksnapshotprivate.h"
25 
26 /* HACK: So we don't need to include any (not-yet-created) GSK or GTK headers */
27 void            gtk_snapshot_push_debug                 (GdkSnapshot            *snapshot,
28                                                          const char             *message,
29                                                          ...) G_GNUC_PRINTF (2, 3);
30 void            gtk_snapshot_pop                        (GdkSnapshot            *snapshot);
31 
32 /**
33  * GdkPaintable:
34  *
35  * `GdkPaintable` is a simple interface used by GTK to represent content that
36  * can be painted.
37  *
38  * The content of a `GdkPaintable` can be painted anywhere at any size
39  * without requiring any sort of layout. The interface is inspired by
40  * similar concepts elsewhere, such as
41  * [ClutterContent](https://developer.gnome.org/clutter/stable/ClutterContent.html),
42  * [HTML/CSS Paint Sources](https://www.w3.org/TR/css-images-4/#paint-source),
43  * or [SVG Paint Servers](https://www.w3.org/TR/SVG2/pservers.html).
44  *
45  * A `GdkPaintable` can be snapshot at any time and size using
46  * [method@Gdk.Paintable.snapshot]. How the paintable interprets that size and
47  * if it scales or centers itself into the given rectangle is implementation
48  * defined, though if you are implementing a `GdkPaintable` and don't know what
49  * to do, it is suggested that you scale your paintable ignoring any potential
50  * aspect ratio.
51  *
52  * The contents that a `GdkPaintable` produces may depend on the [class@GdkSnapshot]
53  * passed to it. For example, paintables may decide to use more detailed images
54  * on higher resolution screens or when OpenGL is available. A `GdkPaintable`
55  * will however always produce the same output for the same snapshot.
56  *
57  * A `GdkPaintable` may change its contents, meaning that it will now produce
58  * a different output with the same snapshot. Once that happens, it will call
59  * [method@Gdk.Paintable.invalidate_contents] which will emit the
60  * [signal@GdkPaintable::invalidate-contents] signal. If a paintable is known
61  * to never change its contents, it will set the %GDK_PAINTABLE_STATIC_CONTENTS
62  * flag. If a consumer cannot deal with changing contents, it may call
63  * [method@Gdk.Paintable.get_current_image] which will return a static
64  * paintable and use that.
65  *
66  * A paintable can report an intrinsic (or preferred) size or aspect ratio it
67  * wishes to be rendered at, though it doesn't have to. Consumers of the interface
68  * can use this information to layout thepaintable appropriately. Just like the
69  * contents, the size of a paintable can change. A paintable will indicate this
70  * by calling [method@Gdk.Paintable.invalidate_size] which will emit the
71  * [signal@GdkPaintable::invalidate-size] signal. And just like for contents,
72  * if a paintable is known to never change its size, it will set the
73  * %GDK_PAINTABLE_STATIC_SIZE flag.
74  *
75  * Besides API for applications, there are some functions that are only
76  * useful for implementing subclasses and should not be used by applications:
77  * [method@Gdk.Paintable.invalidate_contents],
78  * [method@Gdk.Paintable.invalidate_size],
79  * [func@Gdk.Paintable.new_empty].
80  */
81 
82 G_DEFINE_INTERFACE (GdkPaintable, gdk_paintable, G_TYPE_OBJECT)
83 
84 enum {
85   INVALIDATE_CONTENTS,
86   INVALIDATE_SIZE,
87   LAST_SIGNAL
88 };
89 
90 static guint signals[LAST_SIGNAL] = { 0 };
91 
92 static void
gdk_paintable_default_snapshot(GdkPaintable * paintable,GdkSnapshot * snapshot,double width,double height)93 gdk_paintable_default_snapshot (GdkPaintable *paintable,
94                                 GdkSnapshot  *snapshot,
95                                 double        width,
96                                 double        height)
97 {
98   g_critical ("Paintable of type '%s' does not implement GdkPaintable::snapshot", G_OBJECT_TYPE_NAME (paintable));
99 }
100 
101 static GdkPaintable *
gdk_paintable_default_get_current_image(GdkPaintable * paintable)102 gdk_paintable_default_get_current_image (GdkPaintable *paintable)
103 {
104   g_warning ("FIXME: implement by snapshotting at default size and returning a GskRendererNodePaintable");
105 
106   return paintable;
107 }
108 
109 static GdkPaintableFlags
gdk_paintable_default_get_flags(GdkPaintable * paintable)110 gdk_paintable_default_get_flags (GdkPaintable *paintable)
111 {
112   return 0;
113 }
114 
115 static int
gdk_paintable_default_get_intrinsic_width(GdkPaintable * paintable)116 gdk_paintable_default_get_intrinsic_width (GdkPaintable *paintable)
117 {
118   return 0;
119 }
120 
121 static int
gdk_paintable_default_get_intrinsic_height(GdkPaintable * paintable)122 gdk_paintable_default_get_intrinsic_height (GdkPaintable *paintable)
123 {
124   return 0;
125 }
126 
gdk_paintable_default_get_intrinsic_aspect_ratio(GdkPaintable * paintable)127 static double gdk_paintable_default_get_intrinsic_aspect_ratio (GdkPaintable *paintable)
128 {
129   int width, height;
130 
131   width = gdk_paintable_get_intrinsic_width (paintable);
132   height = gdk_paintable_get_intrinsic_height (paintable);
133 
134   if (width <= 0 || height <= 0)
135     return 0.0;
136 
137   return (double) width / height;
138 };
139 
140 static void
g_value_object_transform_value(const GValue * src_value,GValue * dest_value)141 g_value_object_transform_value (const GValue *src_value,
142 				GValue       *dest_value)
143 {
144   if (src_value->data[0].v_pointer && g_type_is_a (G_OBJECT_TYPE (src_value->data[0].v_pointer), G_VALUE_TYPE (dest_value)))
145     dest_value->data[0].v_pointer = g_object_ref (src_value->data[0].v_pointer);
146   else
147     dest_value->data[0].v_pointer = NULL;
148 }
149 
150 static void
gdk_paintable_default_init(GdkPaintableInterface * iface)151 gdk_paintable_default_init (GdkPaintableInterface *iface)
152 {
153   iface->snapshot = gdk_paintable_default_snapshot;
154   iface->get_current_image = gdk_paintable_default_get_current_image;
155   iface->get_flags = gdk_paintable_default_get_flags;
156   iface->get_intrinsic_width = gdk_paintable_default_get_intrinsic_width;
157   iface->get_intrinsic_height = gdk_paintable_default_get_intrinsic_height;
158   iface->get_intrinsic_aspect_ratio = gdk_paintable_default_get_intrinsic_aspect_ratio;
159 
160   g_value_register_transform_func (G_TYPE_OBJECT, GDK_TYPE_PAINTABLE, g_value_object_transform_value);
161   g_value_register_transform_func (GDK_TYPE_PAINTABLE, G_TYPE_OBJECT, g_value_object_transform_value);
162 
163   /**
164    * GdkPaintable::invalidate-contents
165    * @paintable: a `GdkPaintable`
166    *
167    * Emitted when the contents of the @paintable change.
168    *
169    * Examples for such an event would be videos changing to the next frame or
170    * the icon theme for an icon changing.
171    */
172   signals[INVALIDATE_CONTENTS] =
173     g_signal_new ("invalidate-contents",
174                   GDK_TYPE_PAINTABLE,
175                   G_SIGNAL_RUN_LAST,
176                   0,
177                   NULL, NULL,
178                   NULL,
179                   G_TYPE_NONE, 0);
180 
181   /**
182    * GdkPaintable::invalidate-size
183    * @paintable: a `GdkPaintable`
184    *
185    * Emitted when the intrinsic size of the @paintable changes.
186    *
187    * This means the values reported by at least one of
188    * [method@Gdk.Paintable.get_intrinsic_width],
189    * [method@Gdk.Paintable.get_intrinsic_height] or
190    * [method@Gdk.Paintable.get_intrinsic_aspect_ratio]
191    * has changed.
192    *
193    * Examples for such an event would be a paintable displaying
194    * the contents of a toplevel surface being resized.
195    */
196   signals[INVALIDATE_SIZE] =
197     g_signal_new ("invalidate-size",
198                   GDK_TYPE_PAINTABLE,
199                   G_SIGNAL_RUN_LAST,
200                   0,
201                   NULL, NULL,
202                   NULL,
203                   G_TYPE_NONE, 0);
204 }
205 
206 /**
207  * gdk_paintable_snapshot:
208  * @paintable: a `GdkPaintable`
209  * @snapshot: a `GdkSnapshot` to snapshot to
210  * @width: width to snapshot in
211  * @height: height to snapshot in
212  *
213  * Snapshots the given paintable with the given @width and @height.
214  *
215  * The paintable is drawn at the current (0,0) offset of the @snapshot.
216  * If @width and @height are not larger than zero, this function will
217  * do nothing.
218  */
219 void
gdk_paintable_snapshot(GdkPaintable * paintable,GdkSnapshot * snapshot,double width,double height)220 gdk_paintable_snapshot (GdkPaintable *paintable,
221                         GdkSnapshot  *snapshot,
222                         double        width,
223                         double        height)
224 {
225   GdkPaintableInterface *iface;
226 
227   g_return_if_fail (GDK_IS_PAINTABLE (paintable));
228   g_return_if_fail (snapshot != NULL);
229 
230   if (width <= 0.0 || height <= 0.0)
231     return;
232 
233   gtk_snapshot_push_debug (snapshot, "%s %p @ %gx%g", G_OBJECT_TYPE_NAME (paintable), paintable, width, height);
234 
235   iface = GDK_PAINTABLE_GET_IFACE (paintable);
236   iface->snapshot (paintable, snapshot, width, height);
237 
238   gtk_snapshot_pop (snapshot);
239 }
240 
241 #define GDK_PAINTABLE_IMMUTABLE (GDK_PAINTABLE_STATIC_SIZE | GDK_PAINTABLE_STATIC_CONTENTS)
242 static inline gboolean
gdk_paintable_is_immutable(GdkPaintable * paintable)243 gdk_paintable_is_immutable (GdkPaintable *paintable)
244 {
245   return (gdk_paintable_get_flags (paintable) & GDK_PAINTABLE_IMMUTABLE) == GDK_PAINTABLE_IMMUTABLE;
246 }
247 
248 /**
249  * gdk_paintable_get_current_image:
250  * @paintable: a `GdkPaintable`
251  *
252  * Gets an immutable paintable for the current contents displayed by @paintable.
253  *
254  * This is useful when you want to retain the current state of an animation,
255  * for example to take a screenshot of a running animation.
256  *
257  * If the @paintable is already immutable, it will return itself.
258  *
259  * Returns: (transfer full): An immutable paintable for the current
260  *   contents of @paintable
261  */
262 GdkPaintable *
gdk_paintable_get_current_image(GdkPaintable * paintable)263 gdk_paintable_get_current_image (GdkPaintable *paintable)
264 {
265   GdkPaintableInterface *iface;
266 
267   g_return_val_if_fail (GDK_IS_PAINTABLE (paintable), NULL);
268 
269   if (gdk_paintable_is_immutable (paintable))
270     return g_object_ref (paintable);
271 
272   iface = GDK_PAINTABLE_GET_IFACE (paintable);
273   return iface->get_current_image (paintable);
274 }
275 
276 /**
277  * gdk_paintable_get_flags:
278  * @paintable: a `GdkPaintable`
279  *
280  * Get flags for the paintable.
281  *
282  * This is oftentimes useful for optimizations.
283  *
284  * See [flags@Gdk.PaintableFlags] for the flags and what they mean.
285  *
286  * Returns: The `GdkPaintableFlags` for this paintable
287  */
288 GdkPaintableFlags
gdk_paintable_get_flags(GdkPaintable * paintable)289 gdk_paintable_get_flags (GdkPaintable *paintable)
290 {
291   GdkPaintableInterface *iface;
292 
293   g_return_val_if_fail (GDK_IS_PAINTABLE (paintable), 0);
294 
295   iface = GDK_PAINTABLE_GET_IFACE (paintable);
296   return iface->get_flags (paintable);
297 }
298 
299 /**
300  * gdk_paintable_get_intrinsic_width:
301  * @paintable: a `GdkPaintable`
302  *
303  * Gets the preferred width the @paintable would like to be displayed at.
304  *
305  * Consumers of this interface can use this to reserve enough space to draw
306  * the paintable.
307  *
308  * This is a purely informational value and does not in any way limit the
309  * values that may be passed to [method@Gdk.Paintable.snapshot].
310  *
311  * If the @paintable does not have a preferred width, it returns 0.
312  * Negative values are never returned.
313  *
314  * Returns: the intrinsic width of @paintable or 0 if none.
315  */
316 int
gdk_paintable_get_intrinsic_width(GdkPaintable * paintable)317 gdk_paintable_get_intrinsic_width (GdkPaintable *paintable)
318 {
319   GdkPaintableInterface *iface;
320 
321   g_return_val_if_fail (GDK_IS_PAINTABLE (paintable), 0);
322 
323   iface = GDK_PAINTABLE_GET_IFACE (paintable);
324   return iface->get_intrinsic_width (paintable);
325 }
326 
327 /**
328  * gdk_paintable_get_intrinsic_height:
329  * @paintable: a `GdkPaintable`
330  *
331  * Gets the preferred height the @paintable would like to be displayed at.
332  *
333  * Consumers of this interface can use this to reserve enough space to draw
334  * the paintable.
335  *
336  * This is a purely informational value and does not in any way limit the
337  * values that may be passed to [method@Gdk.Paintable.snapshot].
338  *
339  * If the @paintable does not have a preferred height, it returns 0.
340  * Negative values are never returned.
341  *
342  * Returns: the intrinsic height of @paintable or 0 if none.
343  */
344 int
gdk_paintable_get_intrinsic_height(GdkPaintable * paintable)345 gdk_paintable_get_intrinsic_height (GdkPaintable *paintable)
346 {
347   GdkPaintableInterface *iface;
348 
349   g_return_val_if_fail (GDK_IS_PAINTABLE (paintable), 0);
350 
351   iface = GDK_PAINTABLE_GET_IFACE (paintable);
352   return iface->get_intrinsic_height (paintable);
353 }
354 
355 /**
356  * gdk_paintable_get_intrinsic_aspect_ratio:
357  * @paintable: a `GdkPaintable`
358  *
359  * Gets the preferred aspect ratio the @paintable would like to be displayed at.
360  *
361  * The aspect ratio is the width divided by the height, so a value of 0.5
362  * means that the @paintable prefers to be displayed twice as high as it
363  * is wide. Consumers of this interface can use this to preserve aspect
364  * ratio when displaying the paintable.
365  *
366  * This is a purely informational value and does not in any way limit the
367  * values that may be passed to [method@Gdk.Paintable.snapshot].
368  *
369  * Usually when a @paintable returns nonzero values from
370  * [method@Gdk.Paintable.get_intrinsic_width] and
371  * [method@Gdk.Paintable.get_intrinsic_height] the aspect ratio
372  * should conform to those values, though that is not required.
373  *
374  * If the @paintable does not have a preferred aspect ratio,
375  * it returns 0. Negative values are never returned.
376  *
377  * Returns: the intrinsic aspect ratio of @paintable or 0 if none.
378  */
379 double
gdk_paintable_get_intrinsic_aspect_ratio(GdkPaintable * paintable)380 gdk_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable)
381 {
382   GdkPaintableInterface *iface;
383 
384   g_return_val_if_fail (GDK_IS_PAINTABLE (paintable), 0);
385 
386   iface = GDK_PAINTABLE_GET_IFACE (paintable);
387   return iface->get_intrinsic_aspect_ratio (paintable);
388 }
389 
390 /**
391  * gdk_paintable_invalidate_contents:
392  * @paintable: a `GdkPaintable`
393  *
394  * Called by implementations of `GdkPaintable` to invalidate their contents.
395  *
396  * Unless the contents are invalidated, implementations must guarantee that
397  * multiple calls of [method@Gdk.Paintable.snapshot] produce the same output.
398  *
399  * This function will emit the [signal@Gdk.Paintable::invalidate-contents]
400  * signal.
401  *
402  * If a @paintable reports the %GDK_PAINTABLE_STATIC_CONTENTS flag,
403  * it must not call this function.
404  */
405 void
gdk_paintable_invalidate_contents(GdkPaintable * paintable)406 gdk_paintable_invalidate_contents (GdkPaintable *paintable)
407 {
408   g_return_if_fail (GDK_IS_PAINTABLE (paintable));
409   g_return_if_fail (!(gdk_paintable_get_flags (paintable) & GDK_PAINTABLE_STATIC_CONTENTS));
410 
411   g_signal_emit (paintable, signals[INVALIDATE_CONTENTS], 0);
412 }
413 
414 /**
415  * gdk_paintable_invalidate_size:
416  * @paintable: a `GdkPaintable`
417  *
418  * Called by implementations of `GdkPaintable` to invalidate their size.
419  *
420  * As long as the size is not invalidated, @paintable must return the same
421  * values for its intrinsic width, height and aspect ratio.
422  *
423  * This function will emit the [signal@Gdk.Paintable::invalidate-size]
424  * signal.
425  *
426  * If a @paintable reports the %GDK_PAINTABLE_STATIC_SIZE flag,
427  * it must not call this function.
428  */
429 void
gdk_paintable_invalidate_size(GdkPaintable * paintable)430 gdk_paintable_invalidate_size (GdkPaintable *paintable)
431 {
432   g_return_if_fail (GDK_IS_PAINTABLE (paintable));
433   g_return_if_fail (!(gdk_paintable_get_flags (paintable) & GDK_PAINTABLE_STATIC_SIZE));
434 
435   g_signal_emit (paintable, signals[INVALIDATE_SIZE], 0);
436 }
437 
438 /**
439  * gdk_paintable_compute_concrete_size:
440  * @paintable: a `GdkPaintable`
441  * @specified_width: the width @paintable could be drawn into or
442  *   0.0 if unknown
443  * @specified_height: the height @paintable could be drawn into or
444  *   0.0 if unknown
445  * @default_width: the width @paintable would be drawn into if
446  *   no other constraints were given
447  * @default_height: the height @paintable would be drawn into if
448  *   no other constraints were given
449  * @concrete_width: (out): will be set to the concrete width computed
450  * @concrete_height: (out): will be set to the concrete height computed
451  *
452  * Compute a concrete size for the `GdkPaintable`.
453  *
454  * Applies the sizing algorithm outlined in the
455  * [CSS Image spec](https://drafts.csswg.org/css-images-3/#default-sizing)
456  * to the given @paintable. See that link for more details.
457  *
458  * It is not necessary to call this function when both @specified_width
459  * and @specified_height are known, but it is useful to call this
460  * function in GtkWidget:measure implementations to compute the
461  * other dimension when only one dimension is given.
462  */
463 void
gdk_paintable_compute_concrete_size(GdkPaintable * paintable,double specified_width,double specified_height,double default_width,double default_height,double * concrete_width,double * concrete_height)464 gdk_paintable_compute_concrete_size (GdkPaintable *paintable,
465                                      double        specified_width,
466                                      double        specified_height,
467                                      double        default_width,
468                                      double        default_height,
469                                      double       *concrete_width,
470                                      double       *concrete_height)
471 {
472   double image_width, image_height, image_aspect;
473 
474   g_return_if_fail (GDK_IS_PAINTABLE (paintable));
475   g_return_if_fail (specified_width >= 0);
476   g_return_if_fail (specified_height >= 0);
477   g_return_if_fail (default_width > 0);
478   g_return_if_fail (default_height > 0);
479   g_return_if_fail (concrete_width != NULL);
480   g_return_if_fail (concrete_height != NULL);
481 
482   /* If the specified size is a definite width and height,
483    * the concrete object size is given that width and height.
484    */
485   if (specified_width && specified_height)
486     {
487       *concrete_width = specified_width;
488       *concrete_height = specified_height;
489       return;
490     }
491 
492   image_width  = gdk_paintable_get_intrinsic_width (paintable);
493   image_height = gdk_paintable_get_intrinsic_height (paintable);
494   image_aspect = gdk_paintable_get_intrinsic_aspect_ratio (paintable);
495 
496   /* If the specified size has neither a definite width nor height,
497    * and has no additional constraints, the dimensions of the concrete
498    * object size are calculated as follows:
499    */
500   if (specified_width == 0.0 && specified_height == 0.0)
501     {
502       /* If the object has only an intrinsic aspect ratio,
503        * the concrete object size must have that aspect ratio,
504        * and additionally be as large as possible without either
505        * its height or width exceeding the height or width of the
506        * default object size.
507        */
508       if (image_aspect > 0 && image_width == 0 && image_height == 0)
509         {
510           if (image_aspect * default_height > default_width)
511             {
512               *concrete_width = default_width;
513               *concrete_height = default_width / image_aspect;
514             }
515           else
516             {
517               *concrete_width = default_height * image_aspect;
518               *concrete_height = default_height;
519             }
520         }
521       else
522         {
523           /* Otherwise, the width and height of the concrete object
524            * size is the same as the object's intrinsic width and
525            * intrinsic height, if they exist.
526            * If the concrete object size is still missing a width or
527            * height, and the object has an intrinsic aspect ratio,
528            * the missing dimension is calculated from the present
529            * dimension and the intrinsic aspect ratio.
530            * Otherwise, the missing dimension is taken from the default
531            * object size.
532            */
533           if (image_width)
534             *concrete_width = image_width;
535           else if (image_aspect)
536             *concrete_width = image_height * image_aspect;
537           else
538             *concrete_width = default_width;
539 
540           if (image_height)
541             *concrete_height = image_height;
542           else if (image_aspect)
543             *concrete_height = image_width / image_aspect;
544           else
545             *concrete_height = default_height;
546         }
547 
548       return;
549     }
550 
551   /* If the specified size has only a width or height, but not both,
552    * then the concrete object size is given that specified width or height.
553    * The other dimension is calculated as follows:
554    * If the object has an intrinsic aspect ratio, the missing dimension of
555    * the concrete object size is calculated using the intrinsic aspect-ratio
556    * and the present dimension.
557    * Otherwise, if the missing dimension is present in the object's intrinsic
558    * dimensions, the missing dimension is taken from the object's intrinsic
559    * dimensions.
560    * Otherwise, the missing dimension of the concrete object size is taken
561    * from the default object size.
562    */
563   if (specified_width)
564     {
565       *concrete_width = specified_width;
566       if (image_aspect)
567         *concrete_height = specified_width / image_aspect;
568       else if (image_height)
569         *concrete_height = image_height;
570       else
571         *concrete_height = default_height;
572     }
573   else
574     {
575       *concrete_height = specified_height;
576       if (image_aspect)
577         *concrete_width = specified_height * image_aspect;
578       else if (image_width)
579         *concrete_width = image_width;
580       else
581         *concrete_width = default_width;
582     }
583 }
584 
585 #define GDK_TYPE_EMPTY_PAINTABLE (gdk_empty_paintable_get_type())
586 static
587 G_DECLARE_FINAL_TYPE(GdkEmptyPaintable, gdk_empty_paintable, GDK, EMPTY_PAINTABLE, GObject)
588 
589 struct _GdkEmptyPaintable
590 {
591   GObject parent_instance;
592 
593   int width;
594   int height;
595 };
596 
597 struct _GdkEmptyPaintableClass
598 {
599   GObjectClass parent_class;
600 };
601 
602 static void
gdk_empty_paintable_snapshot(GdkPaintable * paintable,GdkSnapshot * snapshot,double width,double height)603 gdk_empty_paintable_snapshot (GdkPaintable *paintable,
604                               GdkSnapshot  *snapshot,
605                               double        width,
606                               double        height)
607 {
608 }
609 
610 static GdkPaintableFlags
gdk_empty_paintable_get_flags(GdkPaintable * paintable)611 gdk_empty_paintable_get_flags (GdkPaintable *paintable)
612 {
613   return GDK_PAINTABLE_STATIC_SIZE
614        | GDK_PAINTABLE_STATIC_CONTENTS;
615 }
616 
617 static int
gdk_empty_paintable_get_intrinsic_width(GdkPaintable * paintable)618 gdk_empty_paintable_get_intrinsic_width (GdkPaintable *paintable)
619 {
620   GdkEmptyPaintable *self = GDK_EMPTY_PAINTABLE (paintable);
621 
622   return self->width;
623 }
624 
625 static int
gdk_empty_paintable_get_intrinsic_height(GdkPaintable * paintable)626 gdk_empty_paintable_get_intrinsic_height (GdkPaintable *paintable)
627 {
628   GdkEmptyPaintable *self = GDK_EMPTY_PAINTABLE (paintable);
629 
630   return self->height;
631 }
632 
633 static void
gdk_empty_paintable_paintable_init(GdkPaintableInterface * iface)634 gdk_empty_paintable_paintable_init (GdkPaintableInterface *iface)
635 {
636   iface->snapshot = gdk_empty_paintable_snapshot;
637   iface->get_flags = gdk_empty_paintable_get_flags;
638   iface->get_intrinsic_width = gdk_empty_paintable_get_intrinsic_width;
639   iface->get_intrinsic_height = gdk_empty_paintable_get_intrinsic_height;
640 }
641 
G_DEFINE_TYPE_WITH_CODE(GdkEmptyPaintable,gdk_empty_paintable,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,gdk_empty_paintable_paintable_init))642 G_DEFINE_TYPE_WITH_CODE (GdkEmptyPaintable, gdk_empty_paintable, G_TYPE_OBJECT,
643                          G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
644                                                 gdk_empty_paintable_paintable_init))
645 
646 static void
647 gdk_empty_paintable_class_init (GdkEmptyPaintableClass *klass)
648 {
649 }
650 
651 static void
gdk_empty_paintable_init(GdkEmptyPaintable * self)652 gdk_empty_paintable_init (GdkEmptyPaintable *self)
653 {
654 }
655 
656 /**
657  * gdk_paintable_new_empty:
658  * @intrinsic_width: The intrinsic width to report. Can be 0 for no width.
659  * @intrinsic_height: The intrinsic height to report. Can be 0 for no height.
660  *
661  * Returns a paintable that has the given intrinsic size and draws nothing.
662  *
663  * This is often useful for implementing the
664  * [vfunc@Gdk.Paintable.get_current_image] virtual function
665  * when the paintable is in an incomplete state (like a
666  * [class@Gtk.MediaStream] before receiving the first frame).
667  *
668  * Returns: (transfer full): a `GdkPaintable`
669  */
670 GdkPaintable *
gdk_paintable_new_empty(int intrinsic_width,int intrinsic_height)671 gdk_paintable_new_empty (int intrinsic_width,
672                          int intrinsic_height)
673 {
674   GdkEmptyPaintable *result;
675 
676   g_return_val_if_fail (intrinsic_width >= 0, NULL);
677   g_return_val_if_fail (intrinsic_height >= 0, NULL);
678 
679   result = g_object_new (GDK_TYPE_EMPTY_PAINTABLE, NULL);
680 
681   result->width = intrinsic_width;
682   result->height = intrinsic_height;
683 
684   return GDK_PAINTABLE (result);
685 }
686