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