2 * Copyright 2012, Red Hat, Inc.
3 * Copyright 2012, Soren Sandmann
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:
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
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.
24 * Author: Soren Sandmann <soren.sandmann@gmail.com>
33 #include "gtk-utils.h"
38 pixman_image_t * original;
39 GtkAdjustment * scale_x_adjustment;
40 GtkAdjustment * scale_y_adjustment;
41 GtkAdjustment * rotate_adjustment;
42 GtkAdjustment * subsample_adjustment;
48 get_widget (app_t *app, const char *name)
50 GtkWidget *widget = GTK_WIDGET (gtk_builder_get_object (app->builder, name));
53 g_error ("Widget %s not found\n", name);
59 min4 (double a, double b, double c, double d)
69 max4 (double a, double b, double c, double d)
79 compute_extents (pixman_f_transform_t *trans, double *sx, double *sy)
81 double min_x, max_x, min_y, max_y;
82 pixman_f_vector_t v[4] =
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]);
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]);
100 *sx = (max_x - min_x) / 2.0;
101 *sy = (max_y - min_y) / 2.0;
110 static const named_int_t filters[] =
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 },
122 static const named_int_t repeats[] =
124 { "None", PIXMAN_REPEAT_NONE },
125 { "Normal", PIXMAN_REPEAT_NORMAL },
126 { "Reflect", PIXMAN_REPEAT_REFLECT },
127 { "Pad", PIXMAN_REPEAT_PAD },
131 get_value (app_t *app, const named_int_t table[], const char *box_name)
133 GtkComboBox *box = GTK_COMBO_BOX (get_widget (app, box_name));
135 return table[gtk_combo_box_get_active (box)].value;
139 copy_to_counterpart (app_t *app, GObject *object)
141 static const char *xy_map[] =
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",
147 GObject *counterpart = NULL;
150 for (i = 0; i < G_N_ELEMENTS (xy_map); i += 2)
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]);
164 if (GTK_IS_COMBO_BOX (counterpart))
166 gtk_combo_box_set_active (
167 GTK_COMBO_BOX (counterpart),
168 gtk_combo_box_get_active (
169 GTK_COMBO_BOX (object)));
171 else if (GTK_IS_ADJUSTMENT (counterpart))
173 gtk_adjustment_set_value (
174 GTK_ADJUSTMENT (counterpart),
175 gtk_adjustment_get_value (
176 GTK_ADJUSTMENT (object)));
183 return pow (1.15, v);
187 rescale (GtkWidget *may_be_null, app_t *app)
189 pixman_f_transform_t ftransform;
190 pixman_transform_t transform;
191 double new_width, new_height;
192 double fscale_x, fscale_y;
194 pixman_fixed_t *params;
198 pixman_f_transform_init_identity (&ftransform);
200 if (may_be_null && gtk_toggle_button_get_active (
201 GTK_TOGGLE_BUTTON (get_widget (app, "lock_checkbutton"))))
203 copy_to_counterpart (app, G_OBJECT (may_be_null));
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);
210 fscale_x = to_scale (fscale_x);
211 fscale_y = to_scale (fscale_y);
213 new_width = pixman_image_get_width (app->original) * fscale_x;
214 new_height = pixman_image_get_height (app->original) * fscale_y;
216 pixman_f_transform_scale (&ftransform, NULL, fscale_x, fscale_y);
218 pixman_f_transform_translate (&ftransform, NULL, - new_width / 2.0, - new_height / 2.0);
220 rotation = (rotation / 360.0) * 2 * M_PI;
221 pixman_f_transform_rotate (&ftransform, NULL, cos (rotation), sin (rotation));
223 pixman_f_transform_translate (&ftransform, NULL, new_width / 2.0, new_height / 2.0);
225 pixman_f_transform_invert (&ftransform, &ftransform);
227 compute_extents (&ftransform, &sx, &sy);
229 pixman_transform_from_pixman_f_transform (&transform, &ftransform);
230 pixman_image_set_transform (app->original, &transform);
232 params = pixman_filter_create_separable_convolution (
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));
243 pixman_image_set_filter (app->original, PIXMAN_FILTER_SEPARABLE_CONVOLUTION, params, n_params);
245 pixman_image_set_repeat (
246 app->original, get_value (app, repeats, "repeat_combo_box"));
250 app->scaled_width = ceil (new_width);
251 app->scaled_height = ceil (new_height);
253 gtk_widget_set_size_request (
254 get_widget (app, "drawing_area"), new_width + 0.5, new_height + 0.5);
256 gtk_widget_queue_draw (
257 get_widget (app, "drawing_area"));
261 on_expose (GtkWidget *da, GdkEvent *event, gpointer data)
264 GdkRectangle *area = &event->expose.area;
265 cairo_surface_t *surface;
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);
274 if (area->x < app->scaled_width && area->y < app->scaled_height)
276 pixman_image_composite (
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);
283 surface = cairo_image_surface_create_for_data (
284 (uint8_t *)pixels, CAIRO_FORMAT_ARGB32,
285 area->width, area->height, area->width * 4);
287 cr = gdk_cairo_create (da->window);
289 cairo_set_source_surface (cr, surface, area->x, area->y);
294 cairo_surface_destroy (surface);
296 pixman_image_unref (tmp);
302 set_up_combo_box (app_t *app, const char *box_name,
303 int n_entries, const named_int_t table[])
305 GtkWidget *widget = get_widget (app, box_name);
307 GtkCellRenderer *cell;
310 model = gtk_list_store_new (1, G_TYPE_STRING);
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,
318 gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (model));
320 for (i = 0; i < n_entries; ++i)
322 const named_int_t *info = &(table[i]);
325 gtk_list_store_append (model, &iter);
326 gtk_list_store_set (model, &iter, 0, info->name, -1);
329 gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
331 g_signal_connect (widget, "changed", G_CALLBACK (rescale), app);
335 set_up_filter_box (app_t *app, const char *box_name)
337 set_up_combo_box (app, box_name, G_N_ELEMENTS (filters), filters);
341 format_value (GtkWidget *widget, double value)
343 return g_strdup_printf ("%.4f", to_scale (value));
347 app_new (pixman_image_t *original)
350 app_t *app = g_malloc (sizeof *app);
353 app->builder = gtk_builder_new ();
354 app->original = original;
356 if (!gtk_builder_add_from_file (app->builder, "scale.ui", &err))
357 g_error ("Could not read file scale.ui: %s", err->message);
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"));
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);
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);
382 widget = get_widget (app, "drawing_area");
383 g_signal_connect (widget, "expose_event", G_CALLBACK (on_expose), app);
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");
391 app, "repeat_combo_box", G_N_ELEMENTS (repeats), repeats);
394 gtk_builder_get_object (app->builder, "lock_checkbutton"),
395 "toggled", G_CALLBACK (rescale), app);
403 main (int argc, char **argv)
406 pixman_image_t *image;
409 gtk_init (&argc, &argv);
413 printf ("%s <image file>\n", argv[0]);
417 if (!(image = pixman_image_from_file (argv[1], PIXMAN_a8r8g8b8)))
419 printf ("Could not load image \"%s\"\n", argv[1]);
423 app = app_new (image);
425 window = get_widget (app, "main");
427 g_signal_connect (window, "delete_event", G_CALLBACK (gtk_main_quit), NULL);
429 gtk_window_set_default_size (GTK_WINDOW (window), 1024, 768);
431 gtk_widget_show_all (window);