Add qemu 2.4.0
[kvmfornfv.git] / qemu / pixman / demos / scale.c
1 /*
2  * Copyright 2012, Red Hat, Inc.
3  * Copyright 2012, Soren Sandmann
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  * 
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  *
24  * Author: Soren Sandmann <soren.sandmann@gmail.com>
25  */
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29 #include <math.h>
30 #include <gtk/gtk.h>
31 #include <pixman.h>
32 #include <stdlib.h>
33 #include "gtk-utils.h"
34
35 typedef struct
36 {
37     GtkBuilder *        builder;
38     pixman_image_t *    original;
39     GtkAdjustment *     scale_x_adjustment;
40     GtkAdjustment *     scale_y_adjustment;
41     GtkAdjustment *     rotate_adjustment;
42     GtkAdjustment *     subsample_adjustment;
43     int                 scaled_width;
44     int                 scaled_height;
45 } app_t;
46
47 static GtkWidget *
48 get_widget (app_t *app, const char *name)
49 {
50     GtkWidget *widget = GTK_WIDGET (gtk_builder_get_object (app->builder, name));
51
52     if (!widget)
53         g_error ("Widget %s not found\n", name);
54
55     return widget;
56 }
57
58 static double
59 min4 (double a, double b, double c, double d)
60 {
61     double m1, m2;
62
63     m1 = MIN (a, b);
64     m2 = MIN (c, d);
65     return MIN (m1, m2);
66 }
67
68 static double
69 max4 (double a, double b, double c, double d)
70 {
71     double m1, m2;
72
73     m1 = MAX (a, b);
74     m2 = MAX (c, d);
75     return MAX (m1, m2);
76 }
77
78 static void
79 compute_extents (pixman_f_transform_t *trans, double *sx, double *sy)
80 {
81     double min_x, max_x, min_y, max_y;
82     pixman_f_vector_t v[4] =
83     {
84         { { 1, 1, 1 } },
85         { { -1, 1, 1 } },
86         { { -1, -1, 1 } },
87         { { 1, -1, 1 } },
88     };
89
90     pixman_f_transform_point (trans, &v[0]);
91     pixman_f_transform_point (trans, &v[1]);
92     pixman_f_transform_point (trans, &v[2]);
93     pixman_f_transform_point (trans, &v[3]);
94
95     min_x = min4 (v[0].v[0], v[1].v[0], v[2].v[0], v[3].v[0]);
96     max_x = max4 (v[0].v[0], v[1].v[0], v[2].v[0], v[3].v[0]);
97     min_y = min4 (v[0].v[1], v[1].v[1], v[2].v[1], v[3].v[1]);
98     max_y = max4 (v[0].v[1], v[1].v[1], v[2].v[1], v[3].v[1]);
99
100     *sx = (max_x - min_x) / 2.0;
101     *sy = (max_y - min_y) / 2.0;
102 }
103
104 typedef struct
105 {
106     char        name [20];
107     int         value;
108 } named_int_t;
109
110 static const named_int_t filters[] =
111 {
112     { "Box",                    PIXMAN_KERNEL_BOX },
113     { "Impulse",                PIXMAN_KERNEL_IMPULSE },
114     { "Linear",                 PIXMAN_KERNEL_LINEAR },
115     { "Cubic",                  PIXMAN_KERNEL_CUBIC },
116     { "Lanczos2",               PIXMAN_KERNEL_LANCZOS2 },
117     { "Lanczos3",               PIXMAN_KERNEL_LANCZOS3 },
118     { "Lanczos3 Stretched",     PIXMAN_KERNEL_LANCZOS3_STRETCHED },
119     { "Gaussian",               PIXMAN_KERNEL_GAUSSIAN },
120 };
121
122 static const named_int_t repeats[] =
123 {
124     { "None",                   PIXMAN_REPEAT_NONE },
125     { "Normal",                 PIXMAN_REPEAT_NORMAL },
126     { "Reflect",                PIXMAN_REPEAT_REFLECT },
127     { "Pad",                    PIXMAN_REPEAT_PAD },
128 };
129
130 static int
131 get_value (app_t *app, const named_int_t table[], const char *box_name)
132 {
133     GtkComboBox *box = GTK_COMBO_BOX (get_widget (app, box_name));
134
135     return table[gtk_combo_box_get_active (box)].value;
136 }
137
138 static void
139 copy_to_counterpart (app_t *app, GObject *object)
140 {
141     static const char *xy_map[] =
142     {
143         "reconstruct_x_combo_box", "reconstruct_y_combo_box",
144         "sample_x_combo_box",      "sample_y_combo_box",
145         "scale_x_adjustment",      "scale_y_adjustment",
146     };
147     GObject *counterpart = NULL;
148     int i;
149
150     for (i = 0; i < G_N_ELEMENTS (xy_map); i += 2)
151     {
152         GObject *x = gtk_builder_get_object (app->builder, xy_map[i]);
153         GObject *y = gtk_builder_get_object (app->builder, xy_map[i + 1]);
154
155         if (object == x)
156             counterpart = y;
157         if (object == y)
158             counterpart = x;
159     }
160
161     if (!counterpart)
162         return;
163     
164     if (GTK_IS_COMBO_BOX (counterpart))
165     {
166         gtk_combo_box_set_active (
167             GTK_COMBO_BOX (counterpart),
168             gtk_combo_box_get_active (
169                 GTK_COMBO_BOX (object)));
170     }
171     else if (GTK_IS_ADJUSTMENT (counterpart))
172     {
173         gtk_adjustment_set_value (
174             GTK_ADJUSTMENT (counterpart),
175             gtk_adjustment_get_value (
176                 GTK_ADJUSTMENT (object)));
177     }
178 }
179
180 static double
181 to_scale (double v)
182 {
183     return pow (1.15, v);
184 }
185
186 static void
187 rescale (GtkWidget *may_be_null, app_t *app)
188 {
189     pixman_f_transform_t ftransform;
190     pixman_transform_t transform;
191     double new_width, new_height;
192     double fscale_x, fscale_y;
193     double rotation;
194     pixman_fixed_t *params;
195     int n_params;
196     double sx, sy;
197
198     pixman_f_transform_init_identity (&ftransform);
199
200     if (may_be_null && gtk_toggle_button_get_active (
201             GTK_TOGGLE_BUTTON (get_widget (app, "lock_checkbutton"))))
202     {
203         copy_to_counterpart (app, G_OBJECT (may_be_null));
204     }
205     
206     fscale_x = gtk_adjustment_get_value (app->scale_x_adjustment);
207     fscale_y = gtk_adjustment_get_value (app->scale_y_adjustment);
208     rotation = gtk_adjustment_get_value (app->rotate_adjustment);
209
210     fscale_x = to_scale (fscale_x);
211     fscale_y = to_scale (fscale_y);
212     
213     new_width = pixman_image_get_width (app->original) * fscale_x;
214     new_height = pixman_image_get_height (app->original) * fscale_y;
215
216     pixman_f_transform_scale (&ftransform, NULL, fscale_x, fscale_y);
217
218     pixman_f_transform_translate (&ftransform, NULL, - new_width / 2.0, - new_height / 2.0);
219
220     rotation = (rotation / 360.0) * 2 * M_PI;
221     pixman_f_transform_rotate (&ftransform, NULL, cos (rotation), sin (rotation));
222
223     pixman_f_transform_translate (&ftransform, NULL, new_width / 2.0, new_height / 2.0);
224
225     pixman_f_transform_invert (&ftransform, &ftransform);
226
227     compute_extents (&ftransform, &sx, &sy);
228     
229     pixman_transform_from_pixman_f_transform (&transform, &ftransform);
230     pixman_image_set_transform (app->original, &transform);
231
232     params = pixman_filter_create_separable_convolution (
233         &n_params,
234         sx * 65536.0 + 0.5,
235         sy * 65536.0 + 0.5,
236         get_value (app, filters, "reconstruct_x_combo_box"),
237         get_value (app, filters, "reconstruct_y_combo_box"),
238         get_value (app, filters, "sample_x_combo_box"),
239         get_value (app, filters, "sample_y_combo_box"),
240         gtk_adjustment_get_value (app->subsample_adjustment),
241         gtk_adjustment_get_value (app->subsample_adjustment));
242
243     pixman_image_set_filter (app->original, PIXMAN_FILTER_SEPARABLE_CONVOLUTION, params, n_params);
244
245     pixman_image_set_repeat (
246         app->original, get_value (app, repeats, "repeat_combo_box"));
247     
248     free (params);
249
250     app->scaled_width = ceil (new_width);
251     app->scaled_height = ceil (new_height);
252     
253     gtk_widget_set_size_request (
254         get_widget (app, "drawing_area"), new_width + 0.5, new_height + 0.5);
255
256     gtk_widget_queue_draw (
257         get_widget (app, "drawing_area"));
258 }
259
260 static gboolean
261 on_expose (GtkWidget *da, GdkEvent *event, gpointer data)
262 {
263     app_t *app = data;
264     GdkRectangle *area = &event->expose.area;
265     cairo_surface_t *surface;
266     pixman_image_t *tmp;
267     cairo_t *cr;
268     uint32_t *pixels;
269
270     pixels = calloc (1, area->width * area->height * 4);
271     tmp = pixman_image_create_bits (
272         PIXMAN_a8r8g8b8, area->width, area->height, pixels, area->width * 4);
273
274     if (area->x < app->scaled_width && area->y < app->scaled_height)
275     {
276         pixman_image_composite (
277             PIXMAN_OP_SRC,
278             app->original, NULL, tmp,
279             area->x, area->y, 0, 0, 0, 0,
280             app->scaled_width - area->x, app->scaled_height - area->y);
281     }
282
283     surface = cairo_image_surface_create_for_data (
284         (uint8_t *)pixels, CAIRO_FORMAT_ARGB32,
285         area->width, area->height, area->width * 4);
286
287     cr = gdk_cairo_create (da->window);
288
289     cairo_set_source_surface (cr, surface, area->x, area->y);
290
291     cairo_paint (cr);
292
293     cairo_destroy (cr);
294     cairo_surface_destroy (surface);
295     free (pixels);
296     pixman_image_unref (tmp);
297
298     return TRUE;
299 }
300
301 static void
302 set_up_combo_box (app_t *app, const char *box_name,
303                   int n_entries, const named_int_t table[])
304 {
305     GtkWidget *widget = get_widget (app, box_name);
306     GtkListStore *model;
307     GtkCellRenderer *cell;
308     int i;
309
310     model = gtk_list_store_new (1, G_TYPE_STRING);
311     
312     cell = gtk_cell_renderer_text_new ();
313     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), cell, TRUE);
314     gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (widget), cell,
315                                     "text", 0,
316                                     NULL);
317
318     gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (model));
319     
320     for (i = 0; i < n_entries; ++i)
321     {
322         const named_int_t *info = &(table[i]);
323         GtkTreeIter iter;
324
325         gtk_list_store_append (model, &iter);
326         gtk_list_store_set (model, &iter, 0, info->name, -1);
327     }
328
329     gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
330
331     g_signal_connect (widget, "changed", G_CALLBACK (rescale), app);
332 }
333
334 static void
335 set_up_filter_box (app_t *app, const char *box_name)
336 {
337     set_up_combo_box (app, box_name, G_N_ELEMENTS (filters), filters);
338 }
339
340 static char *
341 format_value (GtkWidget *widget, double value)
342 {
343     return g_strdup_printf ("%.4f", to_scale (value));
344 }
345
346 static app_t *
347 app_new (pixman_image_t *original)
348 {
349     GtkWidget *widget;
350     app_t *app = g_malloc (sizeof *app);
351     GError *err = NULL;
352
353     app->builder = gtk_builder_new ();
354     app->original = original;
355
356     if (!gtk_builder_add_from_file (app->builder, "scale.ui", &err))
357         g_error ("Could not read file scale.ui: %s", err->message);
358
359     app->scale_x_adjustment =
360         GTK_ADJUSTMENT (gtk_builder_get_object (app->builder, "scale_x_adjustment"));
361     app->scale_y_adjustment =
362         GTK_ADJUSTMENT (gtk_builder_get_object (app->builder, "scale_y_adjustment"));
363     app->rotate_adjustment =
364         GTK_ADJUSTMENT (gtk_builder_get_object (app->builder, "rotate_adjustment"));
365     app->subsample_adjustment =
366         GTK_ADJUSTMENT (gtk_builder_get_object (app->builder, "subsample_adjustment"));
367
368     g_signal_connect (app->scale_x_adjustment, "value_changed", G_CALLBACK (rescale), app);
369     g_signal_connect (app->scale_y_adjustment, "value_changed", G_CALLBACK (rescale), app);
370     g_signal_connect (app->rotate_adjustment, "value_changed", G_CALLBACK (rescale), app);
371     g_signal_connect (app->subsample_adjustment, "value_changed", G_CALLBACK (rescale), app);
372     
373     widget = get_widget (app, "scale_x_scale");
374     gtk_scale_add_mark (GTK_SCALE (widget), 0.0, GTK_POS_LEFT, NULL);
375     g_signal_connect (widget, "format_value", G_CALLBACK (format_value), app);
376     widget = get_widget (app, "scale_y_scale");
377     gtk_scale_add_mark (GTK_SCALE (widget), 0.0, GTK_POS_LEFT, NULL);
378     g_signal_connect (widget, "format_value", G_CALLBACK (format_value), app);
379     widget = get_widget (app, "rotate_scale");
380     gtk_scale_add_mark (GTK_SCALE (widget), 0.0, GTK_POS_LEFT, NULL);
381
382     widget = get_widget (app, "drawing_area");
383     g_signal_connect (widget, "expose_event", G_CALLBACK (on_expose), app);
384
385     set_up_filter_box (app, "reconstruct_x_combo_box");
386     set_up_filter_box (app, "reconstruct_y_combo_box");
387     set_up_filter_box (app, "sample_x_combo_box");
388     set_up_filter_box (app, "sample_y_combo_box");
389
390     set_up_combo_box (
391         app, "repeat_combo_box", G_N_ELEMENTS (repeats), repeats);
392
393     g_signal_connect (
394         gtk_builder_get_object (app->builder, "lock_checkbutton"),
395         "toggled", G_CALLBACK (rescale), app);
396     
397     rescale (NULL, app);
398     
399     return app;
400 }
401
402 int
403 main (int argc, char **argv)
404 {
405     GtkWidget *window;
406     pixman_image_t *image;
407     app_t *app;
408     
409     gtk_init (&argc, &argv);
410
411     if (argc < 2)
412     {
413         printf ("%s <image file>\n", argv[0]);
414         return -1;
415     }
416
417     if (!(image = pixman_image_from_file (argv[1], PIXMAN_a8r8g8b8)))
418     {
419         printf ("Could not load image \"%s\"\n", argv[1]);
420         return -1;
421     }
422
423     app = app_new (image);
424     
425     window = get_widget (app, "main");
426
427     g_signal_connect (window, "delete_event", G_CALLBACK (gtk_main_quit), NULL);
428     
429     gtk_window_set_default_size (GTK_WINDOW (window), 1024, 768);
430     
431     gtk_widget_show_all (window);
432     
433     gtk_main ();
434
435     return 0;
436 }