dither.c revision f766d2f6
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 "pixman-config.h" 27#endif 28#include <math.h> 29#include <gtk/gtk.h> 30#include <stdlib.h> 31#include "utils.h" 32#include "gtk-utils.h" 33 34#define WIDTH 1024 35#define HEIGHT 640 36 37typedef 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 47static GtkWidget * 48get_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 58typedef struct 59{ 60 char name [20]; 61 int value; 62} named_int_t; 63 64static 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 77static 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 84static int 85get_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 92static void 93rescale (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 105static gboolean 106on_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data) 107{ 108 app_t *app = user_data; 109 GdkRectangle area; 110 cairo_surface_t *surface; 111 pixman_image_t *tmp, *final; 112 uint32_t *pixels; 113 114 gdk_cairo_get_clip_rectangle(cr, &area); 115 116 tmp = pixman_image_create_bits ( 117 app->format, area.width, area.height, NULL, 0); 118 pixman_image_set_dither (tmp, app->dither); 119 120 pixman_image_composite ( 121 PIXMAN_OP_SRC, 122 app->original, NULL, tmp, 123 area.x, area.y, 0, 0, 0, 0, 124 app->width - area.x, 125 app->height - area.y); 126 127 pixels = calloc (1, area.width * area.height * 4); 128 final = pixman_image_create_bits ( 129 PIXMAN_a8r8g8b8, area.width, area.height, pixels, area.width * 4); 130 131 pixman_image_composite ( 132 PIXMAN_OP_SRC, 133 tmp, NULL, final, 134 area.x, area.y, 0, 0, 0, 0, 135 app->width - area.x, 136 app->height - area.y); 137 138 surface = cairo_image_surface_create_for_data ( 139 (uint8_t *)pixels, CAIRO_FORMAT_ARGB32, 140 area.width, area.height, area.width * 4); 141 142 cairo_set_source_surface (cr, surface, area.x, area.y); 143 144 cairo_paint (cr); 145 146 cairo_surface_destroy (surface); 147 free (pixels); 148 pixman_image_unref (final); 149 pixman_image_unref (tmp); 150 151 return TRUE; 152} 153 154static void 155set_up_combo_box (app_t *app, const char *box_name, 156 int n_entries, const named_int_t table[]) 157{ 158 GtkWidget *widget = get_widget (app, box_name); 159 GtkListStore *model; 160 GtkCellRenderer *cell; 161 int i; 162 163 model = gtk_list_store_new (1, G_TYPE_STRING); 164 165 cell = gtk_cell_renderer_text_new (); 166 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), cell, TRUE); 167 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (widget), cell, 168 "text", 0, 169 NULL); 170 171 gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (model)); 172 173 for (i = 0; i < n_entries; ++i) 174 { 175 const named_int_t *info = &(table[i]); 176 GtkTreeIter iter; 177 178 gtk_list_store_append (model, &iter); 179 gtk_list_store_set (model, &iter, 0, info->name, -1); 180 } 181 182 gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0); 183 184 g_signal_connect (widget, "changed", G_CALLBACK (rescale), app); 185} 186 187static app_t * 188app_new (pixman_image_t *original) 189{ 190 GtkWidget *widget; 191 app_t *app = g_malloc (sizeof *app); 192 GError *err = NULL; 193 194 app->builder = gtk_builder_new (); 195 app->original = original; 196 197 if (original->type == BITS) 198 { 199 app->width = pixman_image_get_width (original); 200 app->height = pixman_image_get_height (original); 201 } 202 else 203 { 204 app->width = WIDTH; 205 app->height = HEIGHT; 206 } 207 208 if (!gtk_builder_add_from_file (app->builder, "dither.ui", &err)) 209 g_error ("Could not read file dither.ui: %s", err->message); 210 211 widget = get_widget (app, "drawing_area"); 212 g_signal_connect (widget, "draw", G_CALLBACK (on_draw), app); 213 214 set_up_combo_box (app, "target_format_combo_box", 215 G_N_ELEMENTS (formats), formats); 216 set_up_combo_box (app, "dithering_combo_box", 217 G_N_ELEMENTS (dithers), dithers); 218 219 app->dither = get_value (app, dithers, "dithering_combo_box"); 220 app->format = get_value (app, formats, "target_format_combo_box"); 221 222 rescale (NULL, app); 223 224 return app; 225} 226 227int 228main (int argc, char **argv) 229{ 230 GtkWidget *window; 231 pixman_image_t *image; 232 app_t *app; 233 234 gtk_init (&argc, &argv); 235 236 if (argc < 2) 237 { 238 pixman_gradient_stop_t stops[] = { 239 /* These colors make it very obvious that dithering 240 * is useful even for 8-bit gradients 241 */ 242 { 0x00000, { 0x1b1b, 0x5d5d, 0x7c7c, 0xffff } }, 243 { 0x10000, { 0x3838, 0x3232, 0x1010, 0xffff } }, 244 }; 245 pixman_point_fixed_t p1, p2; 246 247 p1.x = p1.y = 0x0000; 248 p2.x = WIDTH << 16; 249 p2.y = HEIGHT << 16; 250 251 if (!(image = pixman_image_create_linear_gradient ( 252 &p1, &p2, stops, ARRAY_LENGTH (stops)))) 253 { 254 printf ("Could not create gradient\n"); 255 return -1; 256 } 257 } 258 else if (!(image = pixman_image_from_file (argv[1], PIXMAN_a8r8g8b8))) 259 { 260 printf ("Could not load image \"%s\"\n", argv[1]); 261 return -1; 262 } 263 264 app = app_new (image); 265 266 window = get_widget (app, "main"); 267 268 g_signal_connect (window, "delete_event", G_CALLBACK (gtk_main_quit), NULL); 269 270 gtk_window_set_default_size (GTK_WINDOW (window), 1024, 768); 271 272 gtk_widget_show_all (window); 273 274 gtk_main (); 275 276 return 0; 277} 278