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 */
27void gtk_snapshot_push_debug (GdkSnapshot *snapshot,
28 const char *message,
29 ...) G_GNUC_PRINTF (2, 3);
30void 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
82G_DEFINE_INTERFACE (GdkPaintable, gdk_paintable, G_TYPE_OBJECT)
83
84enum {
85 INVALIDATE_CONTENTS,
86 INVALIDATE_SIZE,
87 LAST_SIGNAL
88};
89
90static guint signals[LAST_SIGNAL] = { 0 };
91
92static void
93gdk_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
101static GdkPaintable *
102gdk_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
109static GdkPaintableFlags
110gdk_paintable_default_get_flags (GdkPaintable *paintable)
111{
112 return 0;
113}
114
115static int
116gdk_paintable_default_get_intrinsic_width (GdkPaintable *paintable)
117{
118 return 0;
119}
120
121static int
122gdk_paintable_default_get_intrinsic_height (GdkPaintable *paintable)
123{
124 return 0;
125}
126
127static 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
140static void
141g_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
150static void
151gdk_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, transform_func: g_value_object_transform_value);
161 g_value_register_transform_func (GDK_TYPE_PAINTABLE, G_TYPE_OBJECT, transform_func: 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 (signal_name: "invalidate-contents",
174 GDK_TYPE_PAINTABLE,
175 signal_flags: G_SIGNAL_RUN_LAST,
176 class_offset: 0,
177 NULL, NULL,
178 NULL,
179 G_TYPE_NONE, n_params: 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 (signal_name: "invalidate-size",
198 GDK_TYPE_PAINTABLE,
199 signal_flags: G_SIGNAL_RUN_LAST,
200 class_offset: 0,
201 NULL, NULL,
202 NULL,
203 G_TYPE_NONE, n_params: 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 */
219void
220gdk_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, message: "%s %p @ %gx%g", G_OBJECT_TYPE_NAME (paintable), paintable, width, height);
234
235 iface = GDK_PAINTABLE_GET_IFACE (ptr: 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)
242static inline gboolean
243gdk_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 */
262GdkPaintable *
263gdk_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 (ptr: 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 */
288GdkPaintableFlags
289gdk_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 (ptr: 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 */
316int
317gdk_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 (ptr: 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 */
344int
345gdk_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 (ptr: 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 */
379double
380gdk_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 (ptr: 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 */
405void
406gdk_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 (instance: paintable, signal_id: signals[INVALIDATE_CONTENTS], detail: 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 */
429void
430gdk_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 (instance: paintable, signal_id: signals[INVALIDATE_SIZE], detail: 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 */
463void
464gdk_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())
586static
587G_DECLARE_FINAL_TYPE(GdkEmptyPaintable, gdk_empty_paintable, GDK, EMPTY_PAINTABLE, GObject)
588
589struct _GdkEmptyPaintable
590{
591 GObject parent_instance;
592
593 int width;
594 int height;
595};
596
597struct _GdkEmptyPaintableClass
598{
599 GObjectClass parent_class;
600};
601
602static void
603gdk_empty_paintable_snapshot (GdkPaintable *paintable,
604 GdkSnapshot *snapshot,
605 double width,
606 double height)
607{
608}
609
610static GdkPaintableFlags
611gdk_empty_paintable_get_flags (GdkPaintable *paintable)
612{
613 return GDK_PAINTABLE_STATIC_SIZE
614 | GDK_PAINTABLE_STATIC_CONTENTS;
615}
616
617static int
618gdk_empty_paintable_get_intrinsic_width (GdkPaintable *paintable)
619{
620 GdkEmptyPaintable *self = GDK_EMPTY_PAINTABLE (ptr: paintable);
621
622 return self->width;
623}
624
625static int
626gdk_empty_paintable_get_intrinsic_height (GdkPaintable *paintable)
627{
628 GdkEmptyPaintable *self = GDK_EMPTY_PAINTABLE (ptr: paintable);
629
630 return self->height;
631}
632
633static void
634gdk_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
642G_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
646static void
647gdk_empty_paintable_class_init (GdkEmptyPaintableClass *klass)
648{
649}
650
651static void
652gdk_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 */
670GdkPaintable *
671gdk_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 (ptr: result);
685}
686

source code of gtk/gdk/gdkpaintable.c