1f766d2f6Smrg/*
2f766d2f6Smrg * Copyright 2012, Red Hat, Inc.
3f766d2f6Smrg * Copyright 2012, Soren Sandmann
4f766d2f6Smrg * Copyright 2018, Basile Clement
5f766d2f6Smrg *
6f766d2f6Smrg * Permission is hereby granted, free of charge, to any person obtaining a
7f766d2f6Smrg * copy of this software and associated documentation files (the "Software"),
8f766d2f6Smrg * to deal in the Software without restriction, including without limitation
9f766d2f6Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10f766d2f6Smrg * and/or sell copies of the Software, and to permit persons to whom the
11f766d2f6Smrg * Software is furnished to do so, subject to the following conditions:
12f766d2f6Smrg *
13f766d2f6Smrg * The above copyright notice and this permission notice (including the next
14f766d2f6Smrg * paragraph) shall be included in all copies or substantial portions of the
15f766d2f6Smrg * Software.
16f766d2f6Smrg *
17f766d2f6Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18f766d2f6Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19f766d2f6Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20f766d2f6Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21f766d2f6Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22f766d2f6Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23f766d2f6Smrg * DEALINGS IN THE SOFTWARE.
24f766d2f6Smrg */
25f766d2f6Smrg#ifdef HAVE_CONFIG_H
26f766d2f6Smrg#include "pixman-config.h"
27f766d2f6Smrg#endif
28f766d2f6Smrg#include <math.h>
29f766d2f6Smrg#include <gtk/gtk.h>
30f766d2f6Smrg#include <stdlib.h>
31f766d2f6Smrg#include "utils.h"
32f766d2f6Smrg#include "gtk-utils.h"
33f766d2f6Smrg
34f766d2f6Smrg#define WIDTH 1024
35f766d2f6Smrg#define HEIGHT 640
36f766d2f6Smrg
37f766d2f6Smrgtypedef struct
38f766d2f6Smrg{
39f766d2f6Smrg    GtkBuilder *         builder;
40f766d2f6Smrg    pixman_image_t *	 original;
41f766d2f6Smrg    pixman_format_code_t format;
42f766d2f6Smrg    pixman_dither_t      dither;
43f766d2f6Smrg    int                  width;
44f766d2f6Smrg    int                  height;
45f766d2f6Smrg} app_t;
46f766d2f6Smrg
47f766d2f6Smrgstatic GtkWidget *
48f766d2f6Smrgget_widget (app_t *app, const char *name)
49f766d2f6Smrg{
50f766d2f6Smrg    GtkWidget *widget = GTK_WIDGET (gtk_builder_get_object (app->builder, name));
51f766d2f6Smrg
52f766d2f6Smrg    if (!widget)
53f766d2f6Smrg	g_error ("Widget %s not found\n", name);
54f766d2f6Smrg
55f766d2f6Smrg    return widget;
56f766d2f6Smrg}
57f766d2f6Smrg
58f766d2f6Smrgtypedef struct
59f766d2f6Smrg{
60f766d2f6Smrg    char	name [20];
61f766d2f6Smrg    int		value;
62f766d2f6Smrg} named_int_t;
63f766d2f6Smrg
64f766d2f6Smrgstatic const named_int_t formats[] =
65f766d2f6Smrg{
66f766d2f6Smrg    { "a8r8g8b8",  PIXMAN_a8r8g8b8      },
67f766d2f6Smrg    { "rgb",       PIXMAN_rgb_float     },
68f766d2f6Smrg    { "sRGB",      PIXMAN_a8r8g8b8_sRGB },
69f766d2f6Smrg    { "r5g6b5",    PIXMAN_r5g6b5        },
70f766d2f6Smrg    { "a4r4g4b4",  PIXMAN_a4r4g4b4      },
71f766d2f6Smrg    { "a2r2g2b2",  PIXMAN_a2r2g2b2      },
72f766d2f6Smrg    { "r3g3b2",    PIXMAN_r3g3b2        },
73f766d2f6Smrg    { "r1g2b1",    PIXMAN_r1g2b1        },
74f766d2f6Smrg    { "a1r1g1b1",  PIXMAN_a1r1g1b1      },
75f766d2f6Smrg};
76f766d2f6Smrg
77f766d2f6Smrgstatic const named_int_t dithers[] =
78f766d2f6Smrg{
79f766d2f6Smrg    { "None",                   PIXMAN_REPEAT_NONE },
80f766d2f6Smrg    { "Bayer 8x8",              PIXMAN_DITHER_ORDERED_BAYER_8 },
81f766d2f6Smrg    { "Blue noise 64x64",       PIXMAN_DITHER_ORDERED_BLUE_NOISE_64 },
82f766d2f6Smrg};
83f766d2f6Smrg
84f766d2f6Smrgstatic int
85f766d2f6Smrgget_value (app_t *app, const named_int_t table[], const char *box_name)
86f766d2f6Smrg{
87f766d2f6Smrg    GtkComboBox *box = GTK_COMBO_BOX (get_widget (app, box_name));
88f766d2f6Smrg
89f766d2f6Smrg    return table[gtk_combo_box_get_active (box)].value;
90f766d2f6Smrg}
91f766d2f6Smrg
92f766d2f6Smrgstatic void
93f766d2f6Smrgrescale (GtkWidget *may_be_null, app_t *app)
94f766d2f6Smrg{
95f766d2f6Smrg    app->dither = get_value (app, dithers, "dithering_combo_box");
96f766d2f6Smrg    app->format = get_value (app, formats, "target_format_combo_box");
97f766d2f6Smrg
98f766d2f6Smrg    gtk_widget_set_size_request (
99f766d2f6Smrg	get_widget (app, "drawing_area"), app->width + 0.5, app->height + 0.5);
100f766d2f6Smrg
101f766d2f6Smrg    gtk_widget_queue_draw (
102f766d2f6Smrg	get_widget (app, "drawing_area"));
103f766d2f6Smrg}
104f766d2f6Smrg
105f766d2f6Smrgstatic gboolean
106f766d2f6Smrgon_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data)
107f766d2f6Smrg{
108f766d2f6Smrg    app_t *app = user_data;
109f766d2f6Smrg    GdkRectangle area;
110f766d2f6Smrg    cairo_surface_t *surface;
111f766d2f6Smrg    pixman_image_t *tmp, *final;
112f766d2f6Smrg    uint32_t *pixels;
113f766d2f6Smrg
114f766d2f6Smrg    gdk_cairo_get_clip_rectangle(cr, &area);
115f766d2f6Smrg
116f766d2f6Smrg    tmp = pixman_image_create_bits (
117f766d2f6Smrg	app->format, area.width, area.height, NULL, 0);
118f766d2f6Smrg    pixman_image_set_dither (tmp, app->dither);
119f766d2f6Smrg
120f766d2f6Smrg    pixman_image_composite (
121f766d2f6Smrg	PIXMAN_OP_SRC,
122f766d2f6Smrg	app->original, NULL, tmp,
123f766d2f6Smrg	area.x, area.y, 0, 0, 0, 0,
124f766d2f6Smrg	app->width - area.x,
125f766d2f6Smrg	app->height - area.y);
126f766d2f6Smrg
127f766d2f6Smrg    pixels = calloc (1, area.width * area.height * 4);
128f766d2f6Smrg    final = pixman_image_create_bits (
129f766d2f6Smrg	PIXMAN_a8r8g8b8, area.width, area.height, pixels, area.width * 4);
130f766d2f6Smrg
131f766d2f6Smrg    pixman_image_composite (
132f766d2f6Smrg	PIXMAN_OP_SRC,
133f766d2f6Smrg	tmp, NULL, final,
134f766d2f6Smrg	area.x, area.y, 0, 0, 0, 0,
135f766d2f6Smrg	app->width - area.x,
136f766d2f6Smrg	app->height - area.y);
137f766d2f6Smrg
138f766d2f6Smrg    surface = cairo_image_surface_create_for_data (
139f766d2f6Smrg	(uint8_t *)pixels, CAIRO_FORMAT_ARGB32,
140f766d2f6Smrg	area.width, area.height, area.width * 4);
141f766d2f6Smrg
142f766d2f6Smrg    cairo_set_source_surface (cr, surface, area.x, area.y);
143f766d2f6Smrg
144f766d2f6Smrg    cairo_paint (cr);
145f766d2f6Smrg
146f766d2f6Smrg    cairo_surface_destroy (surface);
147f766d2f6Smrg    free (pixels);
148f766d2f6Smrg    pixman_image_unref (final);
149f766d2f6Smrg    pixman_image_unref (tmp);
150f766d2f6Smrg
151f766d2f6Smrg    return TRUE;
152f766d2f6Smrg}
153f766d2f6Smrg
154f766d2f6Smrgstatic void
155f766d2f6Smrgset_up_combo_box (app_t *app, const char *box_name,
156f766d2f6Smrg		  int n_entries, const named_int_t table[])
157f766d2f6Smrg{
158f766d2f6Smrg    GtkWidget *widget = get_widget (app, box_name);
159f766d2f6Smrg    GtkListStore *model;
160f766d2f6Smrg    GtkCellRenderer *cell;
161f766d2f6Smrg    int i;
162f766d2f6Smrg
163f766d2f6Smrg    model = gtk_list_store_new (1, G_TYPE_STRING);
164f766d2f6Smrg
165f766d2f6Smrg    cell = gtk_cell_renderer_text_new ();
166f766d2f6Smrg    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), cell, TRUE);
167f766d2f6Smrg    gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (widget), cell,
168f766d2f6Smrg				    "text", 0,
169f766d2f6Smrg				    NULL);
170f766d2f6Smrg
171f766d2f6Smrg    gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (model));
172f766d2f6Smrg
173f766d2f6Smrg    for (i = 0; i < n_entries; ++i)
174f766d2f6Smrg    {
175f766d2f6Smrg	const named_int_t *info = &(table[i]);
176f766d2f6Smrg	GtkTreeIter iter;
177f766d2f6Smrg
178f766d2f6Smrg	gtk_list_store_append (model, &iter);
179f766d2f6Smrg	gtk_list_store_set (model, &iter, 0, info->name, -1);
180f766d2f6Smrg    }
181f766d2f6Smrg
182f766d2f6Smrg    gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
183f766d2f6Smrg
184f766d2f6Smrg    g_signal_connect (widget, "changed", G_CALLBACK (rescale), app);
185f766d2f6Smrg}
186f766d2f6Smrg
187f766d2f6Smrgstatic app_t *
188f766d2f6Smrgapp_new (pixman_image_t *original)
189f766d2f6Smrg{
190f766d2f6Smrg    GtkWidget *widget;
191f766d2f6Smrg    app_t *app = g_malloc (sizeof *app);
192f766d2f6Smrg    GError *err = NULL;
193f766d2f6Smrg
194f766d2f6Smrg    app->builder = gtk_builder_new ();
195f766d2f6Smrg    app->original = original;
196f766d2f6Smrg
197f766d2f6Smrg    if (original->type == BITS)
198f766d2f6Smrg    {
199f766d2f6Smrg	app->width = pixman_image_get_width (original);
200f766d2f6Smrg	app->height = pixman_image_get_height (original);
201f766d2f6Smrg    }
202f766d2f6Smrg    else
203f766d2f6Smrg    {
204f766d2f6Smrg	app->width = WIDTH;
205f766d2f6Smrg	app->height = HEIGHT;
206f766d2f6Smrg    }
207f766d2f6Smrg
208f766d2f6Smrg    if (!gtk_builder_add_from_file (app->builder, "dither.ui", &err))
209f766d2f6Smrg	g_error ("Could not read file dither.ui: %s", err->message);
210f766d2f6Smrg
211f766d2f6Smrg    widget = get_widget (app, "drawing_area");
212f766d2f6Smrg    g_signal_connect (widget, "draw", G_CALLBACK (on_draw), app);
213f766d2f6Smrg
214f766d2f6Smrg    set_up_combo_box (app, "target_format_combo_box",
215f766d2f6Smrg		      G_N_ELEMENTS (formats), formats);
216f766d2f6Smrg    set_up_combo_box (app, "dithering_combo_box",
217f766d2f6Smrg		      G_N_ELEMENTS (dithers), dithers);
218f766d2f6Smrg
219f766d2f6Smrg    app->dither = get_value (app, dithers, "dithering_combo_box");
220f766d2f6Smrg    app->format = get_value (app, formats, "target_format_combo_box");
221f766d2f6Smrg
222f766d2f6Smrg    rescale (NULL, app);
223f766d2f6Smrg
224f766d2f6Smrg    return app;
225f766d2f6Smrg}
226f766d2f6Smrg
227f766d2f6Smrgint
228f766d2f6Smrgmain (int argc, char **argv)
229f766d2f6Smrg{
230f766d2f6Smrg    GtkWidget *window;
231f766d2f6Smrg    pixman_image_t *image;
232f766d2f6Smrg    app_t *app;
233f766d2f6Smrg
234f766d2f6Smrg    gtk_init (&argc, &argv);
235f766d2f6Smrg
236f766d2f6Smrg    if (argc < 2)
237f766d2f6Smrg    {
238f766d2f6Smrg	pixman_gradient_stop_t stops[] = {
239f766d2f6Smrg	    /* These colors make it very obvious that dithering
240f766d2f6Smrg	     * is useful even for 8-bit gradients
241f766d2f6Smrg	     */
242f766d2f6Smrg	    { 0x00000, { 0x1b1b, 0x5d5d, 0x7c7c, 0xffff } },
243f766d2f6Smrg	    { 0x10000, { 0x3838, 0x3232, 0x1010, 0xffff } },
244f766d2f6Smrg	};
245f766d2f6Smrg	pixman_point_fixed_t p1, p2;
246f766d2f6Smrg
247f766d2f6Smrg	p1.x = p1.y = 0x0000;
248f766d2f6Smrg	p2.x = WIDTH << 16;
249f766d2f6Smrg	p2.y = HEIGHT << 16;
250f766d2f6Smrg
251f766d2f6Smrg	if (!(image = pixman_image_create_linear_gradient (
252f766d2f6Smrg		  &p1, &p2, stops, ARRAY_LENGTH (stops))))
253f766d2f6Smrg	{
254f766d2f6Smrg	    printf ("Could not create gradient\n");
255f766d2f6Smrg	    return -1;
256f766d2f6Smrg	}
257f766d2f6Smrg    }
258f766d2f6Smrg    else if (!(image = pixman_image_from_file (argv[1], PIXMAN_a8r8g8b8)))
259f766d2f6Smrg    {
260f766d2f6Smrg	printf ("Could not load image \"%s\"\n", argv[1]);
261f766d2f6Smrg	return -1;
262f766d2f6Smrg    }
263f766d2f6Smrg
264f766d2f6Smrg    app = app_new (image);
265f766d2f6Smrg
266f766d2f6Smrg    window = get_widget (app, "main");
267f766d2f6Smrg
268f766d2f6Smrg    g_signal_connect (window, "delete_event", G_CALLBACK (gtk_main_quit), NULL);
269f766d2f6Smrg
270f766d2f6Smrg    gtk_window_set_default_size (GTK_WINDOW (window), 1024, 768);
271f766d2f6Smrg
272f766d2f6Smrg    gtk_widget_show_all (window);
273f766d2f6Smrg
274f766d2f6Smrg    gtk_main ();
275f766d2f6Smrg
276f766d2f6Smrg    return 0;
277f766d2f6Smrg}
278