1 | /* |
2 | * Copyright © 2021 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 "measuregraph.h" |
23 | |
24 | /* gdk_texture_new_for_surface() */ |
25 | #include "gdk/gdktextureprivate.h" |
26 | |
27 | #define MAX_SIZES 2048 |
28 | |
29 | typedef struct _Size Size; |
30 | struct _Size |
31 | { |
32 | int min; |
33 | int nat; |
34 | }; |
35 | |
36 | struct _GtkInspectorMeasureGraph |
37 | { |
38 | GObject parent_instance; |
39 | |
40 | GdkPaintable *texture; |
41 | Size width; |
42 | Size height; |
43 | Size width_for_height[MAX_SIZES]; |
44 | Size height_for_width[MAX_SIZES]; |
45 | }; |
46 | |
47 | struct _GtkInspectorMeasureGraphClass |
48 | { |
49 | GObjectClass parent_class; |
50 | }; |
51 | |
52 | static void |
53 | gtk_inspector_measure_graph_ensure_texture (GtkInspectorMeasureGraph *self) |
54 | { |
55 | int i, width, height; |
56 | cairo_surface_t *surface; |
57 | cairo_t *cr; |
58 | |
59 | if (self->texture) |
60 | return; |
61 | |
62 | if (self->width.nat == 0 || self->height.nat == 0) |
63 | { |
64 | self->texture = gdk_paintable_new_empty (intrinsic_width: 0, intrinsic_height: 0); |
65 | return; |
66 | } |
67 | |
68 | width = self->width.nat; |
69 | for (i = 0; i < MAX_SIZES; i++) |
70 | width = MAX (width, self->width_for_height[i].nat); |
71 | width = MIN (width, MAX_SIZES); |
72 | height = self->height.nat; |
73 | for (i = 0; i < MAX_SIZES; i++) |
74 | height = MAX (height, self->height_for_width[i].nat); |
75 | height = MIN (height, MAX_SIZES); |
76 | |
77 | surface = cairo_image_surface_create (format: CAIRO_FORMAT_ARGB32, width, height); |
78 | cr = cairo_create (target: surface); |
79 | cairo_set_operator (cr, op: CAIRO_OPERATOR_ADD); |
80 | |
81 | cairo_set_source_rgba (cr, red: 0.5, green: 0, blue: 0, alpha: 1); |
82 | cairo_rectangle (cr, x: 0, y: 0, width: self->width.min, height); |
83 | cairo_fill (cr); |
84 | cairo_set_source_rgba (cr, red: 1, green: 0, blue: 0, alpha: 1); |
85 | for (i = self->width.min; i < width; i++) |
86 | cairo_rectangle (cr, x: i, y: 0, width: 1, height: self->height_for_width[i].min); |
87 | cairo_fill (cr); |
88 | cairo_set_source_rgba (cr, red: 1, green: 0, blue: 0, alpha: 0.3); |
89 | for (i = self->width.min; i < width; i++) |
90 | cairo_rectangle (cr, x: i, y: self->height_for_width[i].min, width: 1, height: self->height_for_width[i].nat - self->height_for_width[i].min); |
91 | cairo_fill (cr); |
92 | |
93 | cairo_set_source_rgba (cr, red: 0, green: 0, blue: 0.5, alpha: 1); |
94 | cairo_rectangle (cr, x: 0, y: 0, width, height: self->height.min); |
95 | cairo_fill (cr); |
96 | cairo_set_source_rgba (cr, red: 0, green: 0, blue: 1, alpha: 1); |
97 | for (i = self->height.min; i < height; i++) |
98 | cairo_rectangle (cr, x: 0, y: i, width: self->width_for_height[i].min, height: 1); |
99 | cairo_fill (cr); |
100 | cairo_set_source_rgba (cr, red: 0, green: 0, blue: 1, alpha: 0.3); |
101 | for (i = self->height.min; i < height; i++) |
102 | cairo_rectangle (cr, x: self->width_for_height[i].min, y: i, width: self->width_for_height[i].nat - self->width_for_height[i].min, height: 1); |
103 | cairo_fill (cr); |
104 | |
105 | cairo_set_operator (cr, op: CAIRO_OPERATOR_OVER); |
106 | cairo_set_source_rgba (cr, red: 0, green: 0, blue: 0, alpha: 1); |
107 | cairo_rectangle (cr, x: self->width.nat, y: 0, width: 1, height); |
108 | cairo_rectangle (cr, x: 0, y: self->height.nat, width, height: 1); |
109 | cairo_fill (cr); |
110 | |
111 | cairo_destroy (cr); |
112 | self->texture = GDK_PAINTABLE (ptr: gdk_texture_new_for_surface (surface)); |
113 | cairo_surface_destroy (surface); |
114 | } |
115 | |
116 | static void |
117 | gtk_inspector_measure_graph_paintable_snapshot (GdkPaintable *paintable, |
118 | GdkSnapshot *snapshot, |
119 | double width, |
120 | double height) |
121 | { |
122 | GtkInspectorMeasureGraph *self = GTK_INSPECTOR_MEASURE_GRAPH (ptr: paintable); |
123 | |
124 | gtk_inspector_measure_graph_ensure_texture (self); |
125 | |
126 | if (self->texture == NULL) |
127 | return; |
128 | |
129 | gdk_paintable_snapshot (paintable: self->texture, snapshot, width, height); |
130 | } |
131 | |
132 | static int |
133 | gtk_inspector_measure_graph_paintable_get_intrinsic_width (GdkPaintable *paintable) |
134 | { |
135 | GtkInspectorMeasureGraph *self = GTK_INSPECTOR_MEASURE_GRAPH (ptr: paintable); |
136 | |
137 | gtk_inspector_measure_graph_ensure_texture (self); |
138 | |
139 | return gdk_paintable_get_intrinsic_width (paintable: self->texture); |
140 | } |
141 | |
142 | static int |
143 | gtk_inspector_measure_graph_paintable_get_intrinsic_height (GdkPaintable *paintable) |
144 | { |
145 | GtkInspectorMeasureGraph *self = GTK_INSPECTOR_MEASURE_GRAPH (ptr: paintable); |
146 | |
147 | gtk_inspector_measure_graph_ensure_texture (self); |
148 | |
149 | return gdk_paintable_get_intrinsic_height (paintable: self->texture); |
150 | } |
151 | |
152 | static double |
153 | gtk_inspector_measure_graph_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable) |
154 | { |
155 | GtkInspectorMeasureGraph *self = GTK_INSPECTOR_MEASURE_GRAPH (ptr: paintable); |
156 | |
157 | gtk_inspector_measure_graph_ensure_texture (self); |
158 | |
159 | return gdk_paintable_get_intrinsic_aspect_ratio (paintable: self->texture); |
160 | } |
161 | |
162 | static void |
163 | gtk_inspector_measure_graph_paintable_init (GdkPaintableInterface *iface) |
164 | { |
165 | iface->snapshot = gtk_inspector_measure_graph_paintable_snapshot; |
166 | iface->get_intrinsic_width = gtk_inspector_measure_graph_paintable_get_intrinsic_width; |
167 | iface->get_intrinsic_height = gtk_inspector_measure_graph_paintable_get_intrinsic_height; |
168 | iface->get_intrinsic_aspect_ratio = gtk_inspector_measure_graph_paintable_get_intrinsic_aspect_ratio; |
169 | } |
170 | |
171 | G_DEFINE_TYPE_EXTENDED (GtkInspectorMeasureGraph, gtk_inspector_measure_graph, G_TYPE_OBJECT, 0, |
172 | G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE, |
173 | gtk_inspector_measure_graph_paintable_init)) |
174 | |
175 | static void |
176 | gtk_inspector_measure_graph_dispose (GObject *object) |
177 | { |
178 | GtkInspectorMeasureGraph *self = GTK_INSPECTOR_MEASURE_GRAPH (ptr: object); |
179 | |
180 | g_clear_object (&self->texture); |
181 | |
182 | G_OBJECT_CLASS (gtk_inspector_measure_graph_parent_class)->dispose (object); |
183 | } |
184 | |
185 | static void |
186 | gtk_inspector_measure_graph_class_init (GtkInspectorMeasureGraphClass *klass) |
187 | { |
188 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
189 | |
190 | gobject_class->dispose = gtk_inspector_measure_graph_dispose; |
191 | } |
192 | |
193 | static void |
194 | gtk_inspector_measure_graph_init (GtkInspectorMeasureGraph *self) |
195 | { |
196 | } |
197 | |
198 | GtkInspectorMeasureGraph * |
199 | gtk_inspector_measure_graph_new (void) |
200 | { |
201 | return g_object_new (GTK_TYPE_INSPECTOR_MEASURE_GRAPH, NULL); |
202 | } |
203 | |
204 | void |
205 | gtk_inspector_measure_graph_clear (GtkInspectorMeasureGraph *self) |
206 | { |
207 | g_clear_object (&self->texture); |
208 | |
209 | memset (s: &self->width, c: 0, n: sizeof (self->width)); |
210 | memset (s: &self->height, c: 0, n: sizeof (self->height)); |
211 | memset (s: &self->width_for_height, c: 0, n: sizeof (self->width_for_height)); |
212 | memset (s: &self->height_for_width, c: 0, n: sizeof (self->height_for_width)); |
213 | |
214 | gdk_paintable_invalidate_size (paintable: GDK_PAINTABLE (ptr: self)); |
215 | gdk_paintable_invalidate_contents (paintable: GDK_PAINTABLE (ptr: self)); |
216 | } |
217 | |
218 | void |
219 | gtk_inspector_measure_graph_measure (GtkInspectorMeasureGraph *self, |
220 | GtkWidget *widget) |
221 | { |
222 | int i; |
223 | |
224 | g_clear_object (&self->texture); |
225 | |
226 | gtk_widget_measure (widget, orientation: GTK_ORIENTATION_HORIZONTAL, for_size: -1, minimum: &self->width.min, natural: &self->width.nat, NULL, NULL); |
227 | gtk_widget_measure (widget, orientation: GTK_ORIENTATION_VERTICAL, for_size: -1 ,minimum: &self->height.min, natural: &self->height.nat, NULL, NULL); |
228 | |
229 | memset (s: &self->width_for_height, c: 0, n: sizeof (Size) * MIN (self->height.min, MAX_SIZES)); |
230 | for (i = self->height.min; i < MAX_SIZES; i++) |
231 | gtk_widget_measure (widget, orientation: GTK_ORIENTATION_HORIZONTAL, for_size: i, minimum: &self->width_for_height[i].min, natural: &self->width_for_height[i].nat, NULL, NULL); |
232 | memset (s: &self->height_for_width, c: 0, n: sizeof (Size) * MIN (self->width.min, MAX_SIZES)); |
233 | for (i = self->width.min; i < MAX_SIZES; i++) |
234 | gtk_widget_measure (widget, orientation: GTK_ORIENTATION_VERTICAL, for_size: i, minimum: &self->height_for_width[i].min, natural: &self->height_for_width[i].nat, NULL, NULL); |
235 | |
236 | gdk_paintable_invalidate_size (paintable: GDK_PAINTABLE (ptr: self)); |
237 | gdk_paintable_invalidate_contents (paintable: GDK_PAINTABLE (ptr: self)); |
238 | } |
239 | |
240 | GdkTexture * |
241 | gtk_inspector_measure_graph_get_texture (GtkInspectorMeasureGraph *self) |
242 | { |
243 | gtk_inspector_measure_graph_ensure_texture (self); |
244 | |
245 | if (!GDK_IS_TEXTURE (self->texture)) |
246 | return NULL; |
247 | |
248 | return GDK_TEXTURE (self->texture); |
249 | } |
250 | |
251 | |