11176bdada62cabc6ec4b0308a930e83b679d5d36John Reck/*
21176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * Copyright 2012, Red Hat, Inc.
31176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * Copyright 2012, Soren Sandmann
41176bdada62cabc6ec4b0308a930e83b679d5d36John Reck *
51176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * Permission is hereby granted, free of charge, to any person obtaining a
61176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * copy of this software and associated documentation files (the "Software"),
71176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * to deal in the Software without restriction, including without limitation
81176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * the rights to use, copy, modify, merge, publish, distribute, sublicense,
91176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * and/or sell copies of the Software, and to permit persons to whom the
101176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * Software is furnished to do so, subject to the following conditions:
111176bdada62cabc6ec4b0308a930e83b679d5d36John Reck *
121176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * The above copyright notice and this permission notice (including the next
131176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * paragraph) shall be included in all copies or substantial portions of the
141176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * Software.
151176bdada62cabc6ec4b0308a930e83b679d5d36John Reck *
161176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
171176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
181176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
191176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
201176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
211176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
221176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * DEALINGS IN THE SOFTWARE.
231176bdada62cabc6ec4b0308a930e83b679d5d36John Reck *
241176bdada62cabc6ec4b0308a930e83b679d5d36John Reck * Author: Soren Sandmann <soren.sandmann@gmail.com>
251176bdada62cabc6ec4b0308a930e83b679d5d36John Reck */
261176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#ifdef HAVE_CONFIG_H
271176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#include "config.h"
281176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#endif
291176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#include <math.h>
301176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#include <gtk/gtk.h>
311176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#include <pixman.h>
321176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#include <stdlib.h>
331176bdada62cabc6ec4b0308a930e83b679d5d36John Reck#include "gtk-utils.h"
341176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
351176bdada62cabc6ec4b0308a930e83b679d5d36John Recktypedef struct
361176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
371176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    GtkBuilder *        builder;
381176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_image_t *	original;
391176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    GtkAdjustment *     scale_x_adjustment;
401176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    GtkAdjustment *     scale_y_adjustment;
411176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    GtkAdjustment *     rotate_adjustment;
421176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    GtkAdjustment *	subsample_adjustment;
431176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    int                 scaled_width;
441176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    int                 scaled_height;
451176bdada62cabc6ec4b0308a930e83b679d5d36John Reck} app_t;
461176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
471176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic GtkWidget *
481176bdada62cabc6ec4b0308a930e83b679d5d36John Reckget_widget (app_t *app, const char *name)
491176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
501176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    GtkWidget *widget = GTK_WIDGET (gtk_builder_get_object (app->builder, name));
511176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
521176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    if (!widget)
531176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        g_error ("Widget %s not found\n", name);
541176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
551176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return widget;
561176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
571176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
581176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic double
591176bdada62cabc6ec4b0308a930e83b679d5d36John Reckmin4 (double a, double b, double c, double d)
601176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
611176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    double m1, m2;
621176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
631176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    m1 = MIN (a, b);
641176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    m2 = MIN (c, d);
651176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return MIN (m1, m2);
661176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
671176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
681176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic double
691176bdada62cabc6ec4b0308a930e83b679d5d36John Reckmax4 (double a, double b, double c, double d)
701176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
711176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    double m1, m2;
721176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
731176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    m1 = MAX (a, b);
741176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    m2 = MAX (c, d);
751176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return MAX (m1, m2);
761176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
771176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
781176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic void
791176bdada62cabc6ec4b0308a930e83b679d5d36John Reckcompute_extents (pixman_f_transform_t *trans, double *sx, double *sy)
801176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
811176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    double min_x, max_x, min_y, max_y;
821176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_f_vector_t v[4] =
831176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
841176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	{ { 1, 1, 1 } },
851176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	{ { -1, 1, 1 } },
861176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	{ { -1, -1, 1 } },
871176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	{ { 1, -1, 1 } },
881176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    };
891176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
901176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_f_transform_point (trans, &v[0]);
911176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_f_transform_point (trans, &v[1]);
921176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_f_transform_point (trans, &v[2]);
931176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_f_transform_point (trans, &v[3]);
941176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
951176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    min_x = min4 (v[0].v[0], v[1].v[0], v[2].v[0], v[3].v[0]);
961176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    max_x = max4 (v[0].v[0], v[1].v[0], v[2].v[0], v[3].v[0]);
971176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    min_y = min4 (v[0].v[1], v[1].v[1], v[2].v[1], v[3].v[1]);
981176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    max_y = max4 (v[0].v[1], v[1].v[1], v[2].v[1], v[3].v[1]);
991176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1001176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    *sx = (max_x - min_x) / 2.0;
1011176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    *sy = (max_y - min_y) / 2.0;
1021176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
1031176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1041176bdada62cabc6ec4b0308a930e83b679d5d36John Recktypedef struct
1051176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
1061176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    char		name [20];
1071176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_kernel_t	value;
1081176bdada62cabc6ec4b0308a930e83b679d5d36John Reck} named_int_t;
1091176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1101176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic const named_int_t filters[] =
1111176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
1121176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { "Box",			PIXMAN_KERNEL_BOX },
1131176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { "Impulse",		PIXMAN_KERNEL_IMPULSE },
1141176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { "Linear",			PIXMAN_KERNEL_LINEAR },
1151176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { "Cubic",			PIXMAN_KERNEL_CUBIC },
1161176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { "Lanczos2",		PIXMAN_KERNEL_LANCZOS2 },
1171176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { "Lanczos3",		PIXMAN_KERNEL_LANCZOS3 },
1181176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { "Lanczos3 Stretched",	PIXMAN_KERNEL_LANCZOS3_STRETCHED },
1191176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { "Gaussian",		PIXMAN_KERNEL_GAUSSIAN },
1201176bdada62cabc6ec4b0308a930e83b679d5d36John Reck};
1211176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1221176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic const named_int_t repeats[] =
1231176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
1241176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { "None",                   PIXMAN_REPEAT_NONE },
1251176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { "Normal",                 PIXMAN_REPEAT_NORMAL },
1261176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { "Reflect",                PIXMAN_REPEAT_REFLECT },
1271176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    { "Pad",                    PIXMAN_REPEAT_PAD },
1281176bdada62cabc6ec4b0308a930e83b679d5d36John Reck};
1291176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1301176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic pixman_kernel_t
1311176bdada62cabc6ec4b0308a930e83b679d5d36John Reckget_value (app_t *app, const named_int_t table[], const char *box_name)
1321176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
1331176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    GtkComboBox *box = GTK_COMBO_BOX (get_widget (app, box_name));
1341176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1351176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return table[gtk_combo_box_get_active (box)].value;
1361176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
1371176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1381176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic void
1391176bdada62cabc6ec4b0308a930e83b679d5d36John Reckcopy_to_counterpart (app_t *app, GObject *object)
1401176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
1411176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    static const char *xy_map[] =
1421176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
1431176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	"reconstruct_x_combo_box", "reconstruct_y_combo_box",
1441176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	"sample_x_combo_box",      "sample_y_combo_box",
1451176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	"scale_x_adjustment",      "scale_y_adjustment",
1461176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    };
1471176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    GObject *counterpart = NULL;
1481176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    int i;
1491176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1501176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    for (i = 0; i < G_N_ELEMENTS (xy_map); i += 2)
1511176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
1521176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	GObject *x = gtk_builder_get_object (app->builder, xy_map[i]);
1531176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	GObject *y = gtk_builder_get_object (app->builder, xy_map[i + 1]);
1541176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1551176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	if (object == x)
1561176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    counterpart = y;
1571176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	if (object == y)
1581176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    counterpart = x;
1591176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    }
1601176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1611176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    if (!counterpart)
1621176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	return;
1631176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1641176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    if (GTK_IS_COMBO_BOX (counterpart))
1651176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
1661176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	gtk_combo_box_set_active (
1671176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    GTK_COMBO_BOX (counterpart),
1681176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    gtk_combo_box_get_active (
1691176bdada62cabc6ec4b0308a930e83b679d5d36John Reck		GTK_COMBO_BOX (object)));
1701176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    }
1711176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    else if (GTK_IS_ADJUSTMENT (counterpart))
1721176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
1731176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	gtk_adjustment_set_value (
1741176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    GTK_ADJUSTMENT (counterpart),
1751176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    gtk_adjustment_get_value (
1761176bdada62cabc6ec4b0308a930e83b679d5d36John Reck		GTK_ADJUSTMENT (object)));
1771176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    }
1781176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
1791176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1801176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic double
1811176bdada62cabc6ec4b0308a930e83b679d5d36John Reckto_scale (double v)
1821176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
1831176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return pow (1.15, v);
1841176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
1851176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1861176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic void
1871176bdada62cabc6ec4b0308a930e83b679d5d36John Reckrescale (GtkWidget *may_be_null, app_t *app)
1881176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
1891176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_f_transform_t ftransform;
1901176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_transform_t transform;
1911176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    double new_width, new_height;
1921176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    double fscale_x, fscale_y;
1931176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    double rotation;
1941176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_fixed_t *params;
1951176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    int n_params;
1961176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    double sx, sy;
1971176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
1981176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_f_transform_init_identity (&ftransform);
1991176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2001176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    if (may_be_null && gtk_toggle_button_get_active (
2011176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	    GTK_TOGGLE_BUTTON (get_widget (app, "lock_checkbutton"))))
2021176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
2031176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	copy_to_counterpart (app, G_OBJECT (may_be_null));
2041176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    }
2051176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2061176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    fscale_x = gtk_adjustment_get_value (app->scale_x_adjustment);
2071176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    fscale_y = gtk_adjustment_get_value (app->scale_y_adjustment);
2081176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    rotation = gtk_adjustment_get_value (app->rotate_adjustment);
2091176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2101176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    fscale_x = to_scale (fscale_x);
2111176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    fscale_y = to_scale (fscale_y);
2121176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2131176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    new_width = pixman_image_get_width (app->original) * fscale_x;
2141176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    new_height = pixman_image_get_height (app->original) * fscale_y;
2151176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2161176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_f_transform_scale (&ftransform, NULL, fscale_x, fscale_y);
2171176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2181176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_f_transform_translate (&ftransform, NULL, - new_width / 2.0, - new_height / 2.0);
2191176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2201176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    rotation = (rotation / 360.0) * 2 * M_PI;
2211176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_f_transform_rotate (&ftransform, NULL, cos (rotation), sin (rotation));
2221176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2231176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_f_transform_translate (&ftransform, NULL, new_width / 2.0, new_height / 2.0);
2241176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2251176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_f_transform_invert (&ftransform, &ftransform);
2261176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2271176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    compute_extents (&ftransform, &sx, &sy);
2281176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2291176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_transform_from_pixman_f_transform (&transform, &ftransform);
2301176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_image_set_transform (app->original, &transform);
2311176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2321176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    params = pixman_filter_create_separable_convolution (
2331176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        &n_params,
2341176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        sx * 65536.0 + 0.5,
2351176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	sy * 65536.0 + 0.5,
2361176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	get_value (app, filters, "reconstruct_x_combo_box"),
2371176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	get_value (app, filters, "reconstruct_y_combo_box"),
2381176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	get_value (app, filters, "sample_x_combo_box"),
2391176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	get_value (app, filters, "sample_y_combo_box"),
2401176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	gtk_adjustment_get_value (app->subsample_adjustment),
2411176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	gtk_adjustment_get_value (app->subsample_adjustment));
2421176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2431176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_image_set_filter (app->original, PIXMAN_FILTER_SEPARABLE_CONVOLUTION, params, n_params);
2441176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2451176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_image_set_repeat (
2461176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        app->original, get_value (app, repeats, "repeat_combo_box"));
2471176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2481176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    free (params);
2491176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2501176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    app->scaled_width = ceil (new_width);
2511176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    app->scaled_height = ceil (new_height);
2521176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2531176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    gtk_widget_set_size_request (
2541176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        get_widget (app, "drawing_area"), new_width + 0.5, new_height + 0.5);
2551176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2561176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    gtk_widget_queue_draw (
2571176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        get_widget (app, "drawing_area"));
2581176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
2591176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2601176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic gboolean
2611176bdada62cabc6ec4b0308a930e83b679d5d36John Reckon_expose (GtkWidget *da, GdkEvent *event, gpointer data)
2621176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
2631176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    app_t *app = data;
2641176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    GdkRectangle *area = &event->expose.area;
2651176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    cairo_surface_t *surface;
2661176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_image_t *tmp;
2671176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    cairo_t *cr;
2681176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    uint32_t *pixels;
2691176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2701176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixels = calloc (1, area->width * area->height * 4);
2711176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    tmp = pixman_image_create_bits (
2721176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        PIXMAN_a8r8g8b8, area->width, area->height, pixels, area->width * 4);
2731176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2741176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    if (area->x < app->scaled_width && area->y < app->scaled_height)
2751176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
2761176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        pixman_image_composite (
2771176bdada62cabc6ec4b0308a930e83b679d5d36John Reck            PIXMAN_OP_SRC,
2781176bdada62cabc6ec4b0308a930e83b679d5d36John Reck            app->original, NULL, tmp,
2791176bdada62cabc6ec4b0308a930e83b679d5d36John Reck            area->x, area->y, 0, 0, 0, 0,
2801176bdada62cabc6ec4b0308a930e83b679d5d36John Reck            app->scaled_width - area->x, app->scaled_height - area->y);
2811176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    }
2821176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2831176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    surface = cairo_image_surface_create_for_data (
2841176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        (uint8_t *)pixels, CAIRO_FORMAT_ARGB32,
2851176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        area->width, area->height, area->width * 4);
2861176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2871176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    cr = gdk_cairo_create (da->window);
2881176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2891176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    cairo_set_source_surface (cr, surface, area->x, area->y);
2901176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2911176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    cairo_paint (cr);
2921176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2931176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    cairo_destroy (cr);
2941176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    cairo_surface_destroy (surface);
2951176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    free (pixels);
2961176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_image_unref (tmp);
2971176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
2981176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return TRUE;
2991176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
3001176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3011176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic void
3021176bdada62cabc6ec4b0308a930e83b679d5d36John Reckset_up_combo_box (app_t *app, const char *box_name,
3031176bdada62cabc6ec4b0308a930e83b679d5d36John Reck                  int n_entries, const named_int_t table[])
3041176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
3051176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    GtkWidget *widget = get_widget (app, box_name);
3061176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    GtkListStore *model;
3071176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    GtkCellRenderer *cell;
3081176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    int i;
3091176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3101176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    model = gtk_list_store_new (1, G_TYPE_STRING);
3111176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3121176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    cell = gtk_cell_renderer_text_new ();
3131176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), cell, TRUE);
3141176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (widget), cell,
3151176bdada62cabc6ec4b0308a930e83b679d5d36John Reck				    "text", 0,
3161176bdada62cabc6ec4b0308a930e83b679d5d36John Reck				    NULL);
3171176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3181176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (model));
3191176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3201176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    for (i = 0; i < n_entries; ++i)
3211176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
3221176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	const named_int_t *info = &(table[i]);
3231176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	GtkTreeIter iter;
3241176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3251176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	gtk_list_store_append (model, &iter);
3261176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	gtk_list_store_set (model, &iter, 0, info->name, -1);
3271176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    }
3281176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3291176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
3301176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3311176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    g_signal_connect (widget, "changed", G_CALLBACK (rescale), app);
3321176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
3331176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3341176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic void
3351176bdada62cabc6ec4b0308a930e83b679d5d36John Reckset_up_filter_box (app_t *app, const char *box_name)
3361176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
3371176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    set_up_combo_box (app, box_name, G_N_ELEMENTS (filters), filters);
3381176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
3391176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3401176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic char *
3411176bdada62cabc6ec4b0308a930e83b679d5d36John Reckformat_value (GtkWidget *widget, double value)
3421176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
3431176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return g_strdup_printf ("%.4f", to_scale (value));
3441176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
3451176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3461176bdada62cabc6ec4b0308a930e83b679d5d36John Reckstatic app_t *
3471176bdada62cabc6ec4b0308a930e83b679d5d36John Reckapp_new (pixman_image_t *original)
3481176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
3491176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    GtkWidget *widget;
3501176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    app_t *app = g_malloc (sizeof *app);
3511176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    GError *err = NULL;
3521176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3531176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    app->builder = gtk_builder_new ();
3541176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    app->original = original;
3551176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3561176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    if (!gtk_builder_add_from_file (app->builder, "scale.ui", &err))
3571176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	g_error ("Could not read file scale.ui: %s", err->message);
3581176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3591176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    app->scale_x_adjustment =
3601176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        GTK_ADJUSTMENT (gtk_builder_get_object (app->builder, "scale_x_adjustment"));
3611176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    app->scale_y_adjustment =
3621176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        GTK_ADJUSTMENT (gtk_builder_get_object (app->builder, "scale_y_adjustment"));
3631176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    app->rotate_adjustment =
3641176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        GTK_ADJUSTMENT (gtk_builder_get_object (app->builder, "rotate_adjustment"));
3651176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    app->subsample_adjustment =
3661176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	GTK_ADJUSTMENT (gtk_builder_get_object (app->builder, "subsample_adjustment"));
3671176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3681176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    g_signal_connect (app->scale_x_adjustment, "value_changed", G_CALLBACK (rescale), app);
3691176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    g_signal_connect (app->scale_y_adjustment, "value_changed", G_CALLBACK (rescale), app);
3701176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    g_signal_connect (app->rotate_adjustment, "value_changed", G_CALLBACK (rescale), app);
3711176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    g_signal_connect (app->subsample_adjustment, "value_changed", G_CALLBACK (rescale), app);
3721176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3731176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    widget = get_widget (app, "scale_x_scale");
3741176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    gtk_scale_add_mark (GTK_SCALE (widget), 0.0, GTK_POS_LEFT, NULL);
3751176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    g_signal_connect (widget, "format_value", G_CALLBACK (format_value), app);
3761176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    widget = get_widget (app, "scale_y_scale");
3771176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    gtk_scale_add_mark (GTK_SCALE (widget), 0.0, GTK_POS_LEFT, NULL);
3781176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    g_signal_connect (widget, "format_value", G_CALLBACK (format_value), app);
3791176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    widget = get_widget (app, "rotate_scale");
3801176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    gtk_scale_add_mark (GTK_SCALE (widget), 0.0, GTK_POS_LEFT, NULL);
3811176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3821176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    widget = get_widget (app, "drawing_area");
3831176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    g_signal_connect (widget, "expose_event", G_CALLBACK (on_expose), app);
3841176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3851176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    set_up_filter_box (app, "reconstruct_x_combo_box");
3861176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    set_up_filter_box (app, "reconstruct_y_combo_box");
3871176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    set_up_filter_box (app, "sample_x_combo_box");
3881176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    set_up_filter_box (app, "sample_y_combo_box");
3891176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3901176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    set_up_combo_box (
3911176bdada62cabc6ec4b0308a930e83b679d5d36John Reck        app, "repeat_combo_box", G_N_ELEMENTS (repeats), repeats);
3921176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3931176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    g_signal_connect (
3941176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	gtk_builder_get_object (app->builder, "lock_checkbutton"),
3951176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	"toggled", G_CALLBACK (rescale), app);
3961176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3971176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    rescale (NULL, app);
3981176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
3991176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return app;
4001176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
4011176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
4021176bdada62cabc6ec4b0308a930e83b679d5d36John Reckint
4031176bdada62cabc6ec4b0308a930e83b679d5d36John Reckmain (int argc, char **argv)
4041176bdada62cabc6ec4b0308a930e83b679d5d36John Reck{
4051176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    GtkWidget *window;
4061176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    pixman_image_t *image;
4071176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    app_t *app;
4081176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
4091176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    gtk_init (&argc, &argv);
4101176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
4111176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    if (argc < 2)
4121176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
4131176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	printf ("%s <image file>\n", argv[0]);
4141176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	return -1;
4151176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    }
4161176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
4171176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    if (!(image = pixman_image_from_file (argv[1], PIXMAN_a8r8g8b8)))
4181176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    {
4191176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	printf ("Could not load image \"%s\"\n", argv[1]);
4201176bdada62cabc6ec4b0308a930e83b679d5d36John Reck	return -1;
4211176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    }
4221176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
4231176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    app = app_new (image);
4241176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
4251176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    window = get_widget (app, "main");
4261176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
4271176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    g_signal_connect (window, "delete_event", G_CALLBACK (gtk_main_quit), NULL);
4281176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
4291176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    gtk_window_set_default_size (GTK_WINDOW (window), 1024, 768);
4301176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
4311176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    gtk_widget_show_all (window);
4321176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
4331176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    gtk_main ();
4341176bdada62cabc6ec4b0308a930e83b679d5d36John Reck
4351176bdada62cabc6ec4b0308a930e83b679d5d36John Reck    return 0;
4361176bdada62cabc6ec4b0308a930e83b679d5d36John Reck}
437