1 | #include <gtk/gtk.h> |
2 | |
3 | static const char *format_name[] = { |
4 | "BGRAp" , "ARGBp" , "RGBAp" , |
5 | "BGRA" , "ARGB" , "RGBA" , "ABGR" , |
6 | "RGB" , "BGR" , NULL |
7 | }; |
8 | |
9 | static const char * |
10 | format_to_string (GdkMemoryFormat format) |
11 | { |
12 | if (format < GDK_MEMORY_N_FORMATS) |
13 | return format_name[format]; |
14 | else |
15 | return "ERROR" ; |
16 | } |
17 | |
18 | /* Copied from gdkmemorytexture.c */ |
19 | |
20 | static void |
21 | convert_memcpy (guchar *dest_data, |
22 | gsize dest_stride, |
23 | const guchar *src_data, |
24 | gsize src_stride, |
25 | gsize width, |
26 | gsize height) |
27 | { |
28 | gsize y; |
29 | |
30 | for (y = 0; y < height; y++) |
31 | memcpy (dest: dest_data + y * dest_stride, src: src_data + y * src_stride, n: 4 * width); |
32 | } |
33 | |
34 | static void |
35 | convert_memcpy3 (guchar *dest_data, |
36 | gsize dest_stride, |
37 | const guchar *src_data, |
38 | gsize src_stride, |
39 | gsize width, |
40 | gsize height) |
41 | { |
42 | gsize y; |
43 | |
44 | for (y = 0; y < height; y++) |
45 | memcpy (dest: dest_data + y * dest_stride, src: src_data + y * src_stride, n: 3 * width); |
46 | } |
47 | |
48 | #define SWIZZLE3(R,G,B) \ |
49 | static void \ |
50 | convert_swizzle ## R ## G ## B (guchar *dest_data, \ |
51 | gsize dest_stride, \ |
52 | const guchar *src_data, \ |
53 | gsize src_stride, \ |
54 | gsize width, \ |
55 | gsize height) \ |
56 | { \ |
57 | gsize x, y; \ |
58 | \ |
59 | for (y = 0; y < height; y++) \ |
60 | { \ |
61 | for (x = 0; x < width; x++) \ |
62 | { \ |
63 | dest_data[3 * x + R] = src_data[3 * x + 0]; \ |
64 | dest_data[3 * x + G] = src_data[3 * x + 1]; \ |
65 | dest_data[3 * x + B] = src_data[3 * x + 2]; \ |
66 | } \ |
67 | \ |
68 | dest_data += dest_stride; \ |
69 | src_data += src_stride; \ |
70 | } \ |
71 | } |
72 | |
73 | SWIZZLE3(2,1,0) |
74 | |
75 | #define SWIZZLE(A,R,G,B) \ |
76 | static void \ |
77 | convert_swizzle ## A ## R ## G ## B (guchar *dest_data, \ |
78 | gsize dest_stride, \ |
79 | const guchar *src_data, \ |
80 | gsize src_stride, \ |
81 | gsize width, \ |
82 | gsize height) \ |
83 | { \ |
84 | gsize x, y; \ |
85 | \ |
86 | for (y = 0; y < height; y++) \ |
87 | { \ |
88 | for (x = 0; x < width; x++) \ |
89 | { \ |
90 | dest_data[4 * x + A] = src_data[4 * x + 0]; \ |
91 | dest_data[4 * x + R] = src_data[4 * x + 1]; \ |
92 | dest_data[4 * x + G] = src_data[4 * x + 2]; \ |
93 | dest_data[4 * x + B] = src_data[4 * x + 3]; \ |
94 | } \ |
95 | \ |
96 | dest_data += dest_stride; \ |
97 | src_data += src_stride; \ |
98 | } \ |
99 | } |
100 | |
101 | SWIZZLE(3,2,1,0) |
102 | SWIZZLE(2,1,0,3) |
103 | SWIZZLE(3,0,1,2) |
104 | SWIZZLE(1,2,3,0) |
105 | |
106 | #define SWIZZLE_OPAQUE(A,R,G,B) \ |
107 | static void \ |
108 | convert_swizzle_opaque_## A ## R ## G ## B (guchar *dest_data, \ |
109 | gsize dest_stride, \ |
110 | const guchar *src_data, \ |
111 | gsize src_stride, \ |
112 | gsize width, \ |
113 | gsize height) \ |
114 | { \ |
115 | gsize x, y; \ |
116 | \ |
117 | for (y = 0; y < height; y++) \ |
118 | { \ |
119 | for (x = 0; x < width; x++) \ |
120 | { \ |
121 | dest_data[4 * x + A] = 0xFF; \ |
122 | dest_data[4 * x + R] = src_data[3 * x + 0]; \ |
123 | dest_data[4 * x + G] = src_data[3 * x + 1]; \ |
124 | dest_data[4 * x + B] = src_data[3 * x + 2]; \ |
125 | } \ |
126 | \ |
127 | dest_data += dest_stride; \ |
128 | src_data += src_stride; \ |
129 | } \ |
130 | } |
131 | |
132 | SWIZZLE_OPAQUE(3,2,1,0) |
133 | SWIZZLE_OPAQUE(3,0,1,2) |
134 | SWIZZLE_OPAQUE(0,1,2,3) |
135 | SWIZZLE_OPAQUE(0,3,2,1) |
136 | |
137 | #define PREMULTIPLY(d,c,a) G_STMT_START { guint t = c * a + 0x80; d = ((t >> 8) + t) >> 8; } G_STMT_END |
138 | #define SWIZZLE_PREMULTIPLY(A,R,G,B, A2,R2,G2,B2) \ |
139 | static void \ |
140 | convert_swizzle_premultiply_ ## A ## R ## G ## B ## _ ## A2 ## R2 ## G2 ## B2 \ |
141 | (guchar *dest_data, \ |
142 | gsize dest_stride, \ |
143 | const guchar *src_data, \ |
144 | gsize src_stride, \ |
145 | gsize width, \ |
146 | gsize height) \ |
147 | { \ |
148 | gsize x, y; \ |
149 | \ |
150 | for (y = 0; y < height; y++) \ |
151 | { \ |
152 | for (x = 0; x < width; x++) \ |
153 | { \ |
154 | dest_data[4 * x + A] = src_data[4 * x + A2]; \ |
155 | PREMULTIPLY(dest_data[4 * x + R], src_data[4 * x + R2], src_data[4 * x + A2]); \ |
156 | PREMULTIPLY(dest_data[4 * x + G], src_data[4 * x + G2], src_data[4 * x + A2]); \ |
157 | PREMULTIPLY(dest_data[4 * x + B], src_data[4 * x + B2], src_data[4 * x + A2]); \ |
158 | } \ |
159 | \ |
160 | dest_data += dest_stride; \ |
161 | src_data += src_stride; \ |
162 | } \ |
163 | } |
164 | |
165 | SWIZZLE_PREMULTIPLY (3,2,1,0, 3,2,1,0) |
166 | SWIZZLE_PREMULTIPLY (0,1,2,3, 3,2,1,0) |
167 | SWIZZLE_PREMULTIPLY (3,2,1,0, 0,1,2,3) |
168 | SWIZZLE_PREMULTIPLY (0,1,2,3, 0,1,2,3) |
169 | SWIZZLE_PREMULTIPLY (3,2,1,0, 3,0,1,2) |
170 | SWIZZLE_PREMULTIPLY (0,1,2,3, 3,0,1,2) |
171 | SWIZZLE_PREMULTIPLY (3,2,1,0, 0,3,2,1) |
172 | SWIZZLE_PREMULTIPLY (0,1,2,3, 0,3,2,1) |
173 | SWIZZLE_PREMULTIPLY (3,0,1,2, 3,2,1,0) |
174 | SWIZZLE_PREMULTIPLY (3,0,1,2, 0,1,2,3) |
175 | SWIZZLE_PREMULTIPLY (3,0,1,2, 3,0,1,2) |
176 | SWIZZLE_PREMULTIPLY (3,0,1,2, 0,3,2,1) |
177 | |
178 | typedef void (* ConversionFunc) (guchar *dest_data, |
179 | gsize dest_stride, |
180 | const guchar *src_data, |
181 | gsize src_stride, |
182 | gsize width, |
183 | gsize height); |
184 | |
185 | static ConversionFunc converters[GDK_MEMORY_N_FORMATS][GDK_MEMORY_N_FORMATS] = |
186 | { |
187 | { convert_memcpy, convert_swizzle3210, convert_swizzle2103, NULL, NULL, NULL, NULL, NULL, NULL }, |
188 | { convert_swizzle3210, convert_memcpy, convert_swizzle3012, NULL, NULL, NULL, NULL, NULL, NULL }, |
189 | { convert_swizzle2103, convert_swizzle1230, convert_memcpy, NULL, NULL, NULL, NULL, NULL, NULL }, |
190 | { convert_swizzle_premultiply_3210_3210, convert_swizzle_premultiply_0123_3210, convert_swizzle_premultiply_3012_3210, convert_memcpy, NULL, NULL, NULL, NULL, NULL }, |
191 | { convert_swizzle_premultiply_3210_0123, convert_swizzle_premultiply_0123_0123, convert_swizzle_premultiply_3012_0123, NULL, convert_memcpy, NULL, NULL, NULL, NULL }, |
192 | { convert_swizzle_premultiply_3210_3012, convert_swizzle_premultiply_0123_3012, convert_swizzle_premultiply_3012_3012, convert_swizzle2103, convert_swizzle1230, convert_memcpy, convert_swizzle3210, NULL, NULL }, |
193 | { convert_swizzle_premultiply_3210_0321, convert_swizzle_premultiply_0123_0321, convert_swizzle_premultiply_3012_0321, NULL, NULL, NULL, convert_memcpy, NULL, NULL }, |
194 | { convert_swizzle_opaque_3210, convert_swizzle_opaque_0123, convert_swizzle_opaque_3012, NULL, NULL, NULL, NULL, convert_memcpy3, convert_swizzle210 }, |
195 | { convert_swizzle_opaque_3012, convert_swizzle_opaque_0321, convert_swizzle_opaque_3210, NULL, NULL, NULL, NULL, convert_swizzle210, convert_memcpy3 }, |
196 | }; |
197 | |
198 | static void |
199 | gdk_memory_convert (guchar *dest_data, |
200 | gsize dest_stride, |
201 | GdkMemoryFormat dest_format, |
202 | const guchar *src_data, |
203 | gsize src_stride, |
204 | GdkMemoryFormat src_format, |
205 | gsize width, |
206 | gsize height) |
207 | { |
208 | g_assert (dest_format < GDK_MEMORY_N_FORMATS); |
209 | g_assert (src_format < GDK_MEMORY_N_FORMATS); |
210 | |
211 | if (converters[src_format][dest_format] == NULL) |
212 | g_error ("Conversion from %s to %s not supported" , format_to_string (src_format), format_to_string (dest_format)); |
213 | |
214 | converters[src_format][dest_format] (dest_data, dest_stride, src_data, src_stride, width, height); |
215 | } |
216 | |
217 | /* End of copied code */ |
218 | |
219 | static GdkTexture * |
220 | make_texture (GdkMemoryFormat format, |
221 | int padding, |
222 | int *out_stride, |
223 | int *out_bpp) |
224 | { |
225 | GdkPixbuf *source; |
226 | GdkMemoryFormat source_format; |
227 | int width, height, stride, bpp; |
228 | guchar *data; |
229 | GBytes *bytes; |
230 | GdkTexture *texture; |
231 | GError *error = NULL; |
232 | |
233 | width = height = 200; |
234 | |
235 | source = gdk_pixbuf_new_from_file_at_scale (filename: "tests/portland-rose.jpg" , |
236 | width, height, TRUE, |
237 | error: &error); |
238 | if (!source) |
239 | g_error ("%s" , error->message); |
240 | |
241 | source_format = GDK_MEMORY_R8G8B8; |
242 | bpp = 3; |
243 | |
244 | if (format != GDK_MEMORY_R8G8B8 && format != GDK_MEMORY_B8G8R8) |
245 | { |
246 | bpp = 4; |
247 | |
248 | /* add an alpha channel with 50% alpha */ |
249 | GdkPixbuf *pb = gdk_pixbuf_new (colorspace: GDK_COLORSPACE_RGB, TRUE, bits_per_sample: 8, width, height); |
250 | gdk_pixbuf_composite (src: source, dest: pb, dest_x: 0, dest_y: 0, dest_width: width, dest_height: height, offset_x: 0, offset_y: 0, scale_x: 1, scale_y: 1, interp_type: GDK_INTERP_BILINEAR, overall_alpha: 128); |
251 | g_object_unref (object: source); |
252 | source = pb; |
253 | |
254 | source_format = GDK_MEMORY_R8G8B8A8; |
255 | } |
256 | |
257 | stride = bpp * width + padding; |
258 | data = g_new0 (guchar, stride * height); |
259 | |
260 | gdk_memory_convert (dest_data: data, dest_stride: stride, dest_format: format, |
261 | src_data: gdk_pixbuf_get_pixels (pixbuf: source), |
262 | src_stride: gdk_pixbuf_get_rowstride (pixbuf: source), |
263 | src_format: source_format, |
264 | width, height); |
265 | |
266 | g_object_unref (object: source); |
267 | |
268 | bytes = g_bytes_new_take (data, size: stride * height); |
269 | texture = gdk_memory_texture_new (width, height, format, bytes, stride); |
270 | g_bytes_unref (bytes); |
271 | |
272 | *out_stride = stride; |
273 | *out_bpp = bpp; |
274 | |
275 | return texture; |
276 | } |
277 | |
278 | static void |
279 | update_picture (GtkWidget *picture) |
280 | { |
281 | GdkMemoryFormat format; |
282 | int padding; |
283 | GdkTexture *texture; |
284 | GtkLabel *label; |
285 | char *text; |
286 | int stride; |
287 | int bpp; |
288 | |
289 | format = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (picture), "format" )); |
290 | padding = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (picture), "padding" )); |
291 | |
292 | texture = make_texture (format, padding, out_stride: &stride, out_bpp: &bpp); |
293 | gtk_picture_set_paintable (self: GTK_PICTURE (ptr: picture), paintable: GDK_PAINTABLE (ptr: texture)); |
294 | |
295 | label = GTK_LABEL (g_object_get_data (G_OBJECT (picture), "size_label" )); |
296 | text = g_strdup_printf (format: "%d x %d @ %d" , |
297 | gdk_texture_get_width (texture), |
298 | gdk_texture_get_height (texture), |
299 | bpp); |
300 | gtk_label_set_label (self: label, str: text); |
301 | g_free (mem: text); |
302 | |
303 | label = GTK_LABEL (g_object_get_data (G_OBJECT (picture), "stride_label" )); |
304 | text = g_strdup_printf (format: "%d" , stride); |
305 | gtk_label_set_label (self: label, str: text); |
306 | g_free (mem: text); |
307 | |
308 | g_object_unref (object: texture); |
309 | } |
310 | |
311 | static void |
312 | update_format (GtkDropDown *dropdown, |
313 | GParamSpec *pspec, |
314 | GtkWidget *picture) |
315 | { |
316 | g_object_set_data (G_OBJECT (picture), key: "format" , GINT_TO_POINTER (gtk_drop_down_get_selected (dropdown))); |
317 | update_picture (picture); |
318 | } |
319 | |
320 | static void |
321 | update_padding (GtkSpinButton *spinbutton, |
322 | GParamSpec *pspec, |
323 | GtkWidget *picture) |
324 | { |
325 | g_object_set_data (G_OBJECT (picture), key: "padding" , GINT_TO_POINTER (gtk_spin_button_get_value_as_int (spinbutton))); |
326 | update_picture (picture); |
327 | } |
328 | |
329 | static void |
330 | add_to_grid (GtkWidget *grid, |
331 | int left, |
332 | int top, |
333 | GdkMemoryFormat format, |
334 | int padding) |
335 | { |
336 | GtkWidget *dropdown, *spin, *picture, *label; |
337 | |
338 | picture = gtk_picture_new (); |
339 | gtk_grid_attach (GTK_GRID (grid), child: picture, column: left + 2, row: top + 0, width: 1, height: 4); |
340 | |
341 | g_object_set_data (G_OBJECT (picture), key: "format" , GINT_TO_POINTER (format)); |
342 | g_object_set_data (G_OBJECT (picture), key: "padding" , GINT_TO_POINTER (padding)); |
343 | |
344 | dropdown = gtk_drop_down_new_from_strings (strings: format_name); |
345 | gtk_widget_set_valign (widget: dropdown, align: GTK_ALIGN_CENTER); |
346 | gtk_drop_down_set_selected (self: GTK_DROP_DOWN (ptr: dropdown), position: format); |
347 | g_signal_connect (dropdown, "notify::selected" , G_CALLBACK (update_format), picture); |
348 | |
349 | gtk_grid_attach (GTK_GRID (grid), child: gtk_label_new (str: "Format" ), column: left, row: top, width: 1, height: 1); |
350 | gtk_grid_attach (GTK_GRID (grid), child: dropdown, column: left + 1, row: top, width: 1, height: 1); |
351 | |
352 | spin = gtk_spin_button_new_with_range (min: 0, max: 10, step: 1); |
353 | gtk_widget_set_valign (widget: spin, align: GTK_ALIGN_CENTER); |
354 | gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), value: padding); |
355 | g_signal_connect (spin, "notify::value" , G_CALLBACK (update_padding), picture); |
356 | |
357 | gtk_grid_attach (GTK_GRID (grid), child: gtk_label_new (str: "Padding" ), column: left, row: top + 1, width: 1, height: 1); |
358 | gtk_grid_attach (GTK_GRID (grid), child: spin, column: left + 1, row: top + 1, width: 1, height: 1); |
359 | |
360 | label = gtk_label_new (str: "" ); |
361 | gtk_label_set_xalign (GTK_LABEL (label), xalign: 0); |
362 | g_object_set_data (G_OBJECT (picture), key: "size_label" , data: label); |
363 | gtk_grid_attach (GTK_GRID (grid), child: gtk_label_new (str: "Size" ), column: left, row: top + 2, width: 1, height: 1); |
364 | gtk_grid_attach (GTK_GRID (grid), child: label, column: left + 1, row: top + 2, width: 1, height: 1); |
365 | label = gtk_label_new (str: "" ); |
366 | gtk_label_set_xalign (GTK_LABEL (label), xalign: 0); |
367 | g_object_set_data (G_OBJECT (picture), key: "stride_label" , data: label); |
368 | gtk_grid_attach (GTK_GRID (grid), child: gtk_label_new (str: "Stride" ), column: left, row: top + 3, width: 1, height: 1); |
369 | gtk_grid_attach (GTK_GRID (grid), child: label, column: left + 1, row: top + 3, width: 1, height: 1); |
370 | |
371 | update_picture (picture); |
372 | } |
373 | |
374 | int |
375 | main (int argc, char *argv[]) |
376 | { |
377 | GtkWidget *window, *grid; |
378 | |
379 | gtk_init (); |
380 | |
381 | window = gtk_window_new (); |
382 | grid = gtk_grid_new (); |
383 | gtk_widget_set_margin_top (widget: grid, margin: 10); |
384 | gtk_widget_set_margin_bottom (widget: grid, margin: 10); |
385 | gtk_widget_set_margin_start (widget: grid, margin: 10); |
386 | gtk_widget_set_margin_end (widget: grid, margin: 10); |
387 | gtk_grid_set_row_spacing (GTK_GRID (grid), spacing: 6); |
388 | gtk_grid_set_column_spacing (GTK_GRID (grid), spacing: 6); |
389 | gtk_window_set_child (GTK_WINDOW (window), child: grid); |
390 | |
391 | add_to_grid (grid, left: 0, top: 0, format: GDK_MEMORY_R8G8B8, padding: 0); |
392 | |
393 | gtk_window_present (GTK_WINDOW (window)); |
394 | |
395 | while (g_list_model_get_n_items (list: gtk_window_get_toplevels ()) > 0) |
396 | g_main_context_iteration (NULL, TRUE); |
397 | |
398 | return 0; |
399 | } |
400 | |