/* * Copyright 2012, Red Hat, Inc. * Copyright 2012, Soren Sandmann * Copyright 2018, Basile Clement * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "../test/utils.h" #include "gtk-utils.h" #define WIDTH 1024 #define HEIGHT 640 typedef struct { GtkBuilder * builder; pixman_image_t * original; pixman_format_code_t format; pixman_dither_t dither; int width; int height; } app_t; static GtkWidget * get_widget (app_t *app, const char *name) { GtkWidget *widget = GTK_WIDGET (gtk_builder_get_object (app->builder, name)); if (!widget) g_error ("Widget %s not found\n", name); return widget; } typedef struct { char name [20]; int value; } named_int_t; static const named_int_t formats[] = { { "a8r8g8b8", PIXMAN_a8r8g8b8 }, { "rgb", PIXMAN_rgb_float }, { "sRGB", PIXMAN_a8r8g8b8_sRGB }, { "r5g6b5", PIXMAN_r5g6b5 }, { "a4r4g4b4", PIXMAN_a4r4g4b4 }, { "a2r2g2b2", PIXMAN_a2r2g2b2 }, { "r3g3b2", PIXMAN_r3g3b2 }, { "r1g2b1", PIXMAN_r1g2b1 }, { "a1r1g1b1", PIXMAN_a1r1g1b1 }, }; static const named_int_t dithers[] = { { "None", PIXMAN_REPEAT_NONE }, { "Bayer 8x8", PIXMAN_DITHER_ORDERED_BAYER_8 }, { "Blue noise 64x64", PIXMAN_DITHER_ORDERED_BLUE_NOISE_64 }, }; static int get_value (app_t *app, const named_int_t table[], const char *box_name) { GtkComboBox *box = GTK_COMBO_BOX (get_widget (app, box_name)); return table[gtk_combo_box_get_active (box)].value; } static void rescale (GtkWidget *may_be_null, app_t *app) { app->dither = get_value (app, dithers, "dithering_combo_box"); app->format = get_value (app, formats, "target_format_combo_box"); gtk_widget_set_size_request ( get_widget (app, "drawing_area"), app->width + 0.5, app->height + 0.5); gtk_widget_queue_draw ( get_widget (app, "drawing_area")); } static gboolean on_expose (GtkWidget *da, GdkEvent *event, gpointer data) { app_t *app = data; GdkRectangle *area = &event->expose.area; cairo_surface_t *surface; pixman_image_t *tmp, *final; cairo_t *cr; uint32_t *pixels; tmp = pixman_image_create_bits ( app->format, area->width, area->height, NULL, 0); pixman_image_set_dither (tmp, app->dither); pixman_image_composite ( PIXMAN_OP_SRC, app->original, NULL, tmp, area->x, area->y, 0, 0, 0, 0, app->width - area->x, app->height - area->y); pixels = calloc (1, area->width * area->height * 4); final = pixman_image_create_bits ( PIXMAN_a8r8g8b8, area->width, area->height, pixels, area->width * 4); pixman_image_composite ( PIXMAN_OP_SRC, tmp, NULL, final, area->x, area->y, 0, 0, 0, 0, app->width - area->x, app->height - area->y); surface = cairo_image_surface_create_for_data ( (uint8_t *)pixels, CAIRO_FORMAT_ARGB32, area->width, area->height, area->width * 4); cr = gdk_cairo_create (da->window); cairo_set_source_surface (cr, surface, area->x, area->y); cairo_paint (cr); cairo_destroy (cr); cairo_surface_destroy (surface); free (pixels); pixman_image_unref (final); pixman_image_unref (tmp); return TRUE; } static void set_up_combo_box (app_t *app, const char *box_name, int n_entries, const named_int_t table[]) { GtkWidget *widget = get_widget (app, box_name); GtkListStore *model; GtkCellRenderer *cell; int i; model = gtk_list_store_new (1, G_TYPE_STRING); cell = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), cell, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (widget), cell, "text", 0, NULL); gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (model)); for (i = 0; i < n_entries; ++i) { const named_int_t *info = &(table[i]); GtkTreeIter iter; gtk_list_store_append (model, &iter); gtk_list_store_set (model, &iter, 0, info->name, -1); } gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0); g_signal_connect (widget, "changed", G_CALLBACK (rescale), app); } static app_t * app_new (pixman_image_t *original) { GtkWidget *widget; app_t *app = g_malloc (sizeof *app); GError *err = NULL; app->builder = gtk_builder_new (); app->original = original; if (original->type == BITS) { app->width = pixman_image_get_width (original); app->height = pixman_image_get_height (original); } else { app->width = WIDTH; app->height = HEIGHT; } if (!gtk_builder_add_from_file (app->builder, "dither.ui", &err)) g_error ("Could not read file dither.ui: %s", err->message); widget = get_widget (app, "drawing_area"); g_signal_connect (widget, "expose_event", G_CALLBACK (on_expose), app); set_up_combo_box (app, "target_format_combo_box", G_N_ELEMENTS (formats), formats); set_up_combo_box (app, "dithering_combo_box", G_N_ELEMENTS (dithers), dithers); app->dither = get_value (app, dithers, "dithering_combo_box"); app->format = get_value (app, formats, "target_format_combo_box"); rescale (NULL, app); return app; } int main (int argc, char **argv) { GtkWidget *window; pixman_image_t *image; app_t *app; gtk_init (&argc, &argv); if (argc < 2) { pixman_gradient_stop_t stops[] = { /* These colors make it very obvious that dithering * is useful even for 8-bit gradients */ { 0x00000, { 0x1b1b, 0x5d5d, 0x7c7c, 0xffff } }, { 0x10000, { 0x3838, 0x3232, 0x1010, 0xffff } }, }; pixman_point_fixed_t p1, p2; p1.x = p1.y = 0x0000; p2.x = WIDTH << 16; p2.y = HEIGHT << 16; if (!(image = pixman_image_create_linear_gradient ( &p1, &p2, stops, ARRAY_LENGTH (stops)))) { printf ("Could not create gradient\n"); return -1; } } else if (!(image = pixman_image_from_file (argv[1], PIXMAN_a8r8g8b8))) { printf ("Could not load image \"%s\"\n", argv[1]); return -1; } app = app_new (image); window = get_widget (app, "main"); g_signal_connect (window, "delete_event", G_CALLBACK (gtk_main_quit), NULL); gtk_window_set_default_size (GTK_WINDOW (window), 1024, 768); gtk_widget_show_all (window); gtk_main (); return 0; }