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