From 98b5ec74ca14448349ef6a33a663ad19d446ed6b Mon Sep 17 00:00:00 2001 From: Basile Clement Date: Tue, 9 Apr 2019 23:29:26 +0200 Subject: demos: Add a dithering demo This adds a dither.c which provides a demo of the dithering feature. This is based on the scale.c demo for scaling and provides a selection of intermediate formats and dithering operators (currently, only PIXMAN_DITHER_ORDERED_BAYER_8) to use. Images are first blitted onto a surface of the intermediate format with the requested dither setup, then blitted back onto a a8r8g8b8 surface for display. --- .gitignore | 1 + demos/Makefile.am | 5 +- demos/dither.c | 278 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ demos/dither.ui | 147 +++++++++++++++++++++++++++++ demos/meson.build | 1 + 5 files changed, 431 insertions(+), 1 deletion(-) create mode 100644 demos/dither.c create mode 100644 demos/dither.ui diff --git a/.gitignore b/.gitignore index a245b69..046b161 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ demos/clip-in demos/linear-gradient demos/quad2quad demos/scale +demos/dither pixman/pixman-srgb.c pixman/pixman-version.h test/*-test diff --git a/demos/Makefile.am b/demos/Makefile.am index 44a5553..c58e359 100644 --- a/demos/Makefile.am +++ b/demos/Makefile.am @@ -2,6 +2,7 @@ EXTRA_DIST = \ parrot.c \ parrot.jpg \ scale.ui \ + dither.ui \ meson.build \ $(NULL) @@ -33,7 +34,8 @@ DEMOS = \ checkerboard \ srgb-trap-test \ srgb-test \ - scale + scale \ + dither gradient_test_SOURCES = gradient-test.c $(GTK_UTILS) alpha_test_SOURCES = alpha-test.c $(GTK_UTILS) @@ -51,6 +53,7 @@ checkerboard_SOURCES = checkerboard.c $(GTK_UTILS) srgb_test_SOURCES = srgb-test.c $(GTK_UTILS) srgb_trap_test_SOURCES = srgb-trap-test.c $(GTK_UTILS) scale_SOURCES = scale.c $(GTK_UTILS) +dither_SOURCES = dither.c $(GTK_UTILS) noinst_PROGRAMS = $(DEMOS) diff --git a/demos/dither.c b/demos/dither.c new file mode 100644 index 0000000..d4271e6 --- /dev/null +++ b/demos/dither.c @@ -0,0 +1,278 @@ +/* + * 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 }, +}; + +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; +} diff --git a/demos/dither.ui b/demos/dither.ui new file mode 100644 index 0000000..7c3d068 --- /dev/null +++ b/demos/dither.ui @@ -0,0 +1,147 @@ + + + + + False + + + + + + True + False + 12 + + + True + True + in + + + True + False + + + True + False + + + + + + + True + True + 0 + + + + + True + False + 12 + + + True + False + + + True + False + 2 + 2 + 8 + 6 + + + True + False + <b>Target format:</b> + True + 1 + + + + + True + False + <b>Dithering:</b> + True + 1 + + + 1 + + + + + True + False + + + 1 + + + + + True + False + + + 1 + 1 + + + + + False + True + 6 + 1 + + + + + False + True + 0 + + + + + False + True + 1 + + + + + + + -180 + 190 + 1 + 10 + 10 + + + -32 + 42 + 1 + 10 + 10 + + + -32 + 42 + 1 + 10 + 10 + + + 12 + 4 + 1 + 1 + + diff --git a/demos/meson.build b/demos/meson.build index 3461f79..70f7c1f 100644 --- a/demos/meson.build +++ b/demos/meson.build @@ -34,6 +34,7 @@ demos = [ 'srgb-test', 'srgb-trap-test', 'scale', + 'dither', ] if dep_gtk.found() -- cgit v1.2.1