[4/4] demos: Add a dithering demo

Submitted by basile-pixman@clement.pm on Dec. 3, 2018, 2:55 p.m.

Details

Message ID 20181203145530.9649-4-basile-pixman@clement.pm
State New
Headers show
Series "Series without cover letter" ( rev: 2 1 ) in Pixman

Not browsing as part of any series.

Commit Message

basile-pixman@clement.pm Dec. 3, 2018, 2:55 p.m.
From: Basile Clement <basile@clement.pm>

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_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 |   6 +-
 demos/dither.c    | 274 ++++++++++++++++++++++++++++++++++++++++++++++
 demos/dither.ui   | 147 +++++++++++++++++++++++++
 4 files changed, 426 insertions(+), 2 deletions(-)
 create mode 100644 demos/dither.c
 create mode 100644 demos/dither.ui

Patch hide | download patch | download mbox

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 e04743d..8e304ec 100644
--- a/demos/Makefile.am
+++ b/demos/Makefile.am
@@ -1,4 +1,4 @@ 
-EXTRA_DIST = parrot.c parrot.jpg scale.ui
+EXTRA_DIST = parrot.c parrot.jpg scale.ui dither.ui
 
 if HAVE_GTK
 
@@ -28,7 +28,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)
@@ -46,6 +47,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..91227fb
--- /dev/null
+++ b/demos/dither.c
@@ -0,0 +1,274 @@ 
+/*
+ * 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 <math.h>
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#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  },
+    { "x14r6g6b6", PIXMAN_x14r6g6b6 },
+    { "r5g6b5",    PIXMAN_r5g6b5    },
+    { "x4r4g4b4",  PIXMAN_x4r4g4b4  },
+    { "a2r2g2b2",  PIXMAN_a2r2g2b2  },
+};
+
+static const named_int_t dithers[] =
+{
+    { "None",                   PIXMAN_REPEAT_NONE },
+    { "Bayer 8x8",              PIXMAN_DITHER_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, { 0x6666, 0x3333, 0x3333, 0xffff } },
+	    { 0x10000, { 0x3333, 0x6666, 0x6666, 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 @@ 
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="2.12"/>
+  <object class="GtkWindow" id="main">
+    <property name="can_focus">False</property>
+    <child>
+      <placeholder/>
+    </child>
+    <child>
+      <object class="GtkHBox" id="u">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="spacing">12</property>
+        <child>
+          <object class="GtkScrolledWindow" id="scrolledwindow1">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="shadow_type">in</property>
+            <child>
+              <object class="GtkViewport" id="viewport1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkDrawingArea" id="drawing_area">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVBox" id="box1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkVBox" id="box6">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkTable" id="grid1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="n_rows">2</property>
+                    <property name="n_columns">2</property>
+                    <property name="column_spacing">8</property>
+                    <property name="row_spacing">6</property>
+                    <child>
+                      <object class="GtkLabel" id="label4">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Target format:&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                        <property name="xalign">1</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label5">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Dithering:&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                        <property name="xalign">1</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkComboBox" id="target_format_combo_box">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkComboBox" id="dithering_combo_box">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="padding">6</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkAdjustment" id="rotate_adjustment">
+    <property name="lower">-180</property>
+    <property name="upper">190</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+    <property name="page_size">10</property>
+  </object>
+  <object class="GtkAdjustment" id="scale_x_adjustment">
+    <property name="lower">-32</property>
+    <property name="upper">42</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+    <property name="page_size">10</property>
+  </object>
+  <object class="GtkAdjustment" id="scale_y_adjustment">
+    <property name="lower">-32</property>
+    <property name="upper">42</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+    <property name="page_size">10</property>
+  </object>
+  <object class="GtkAdjustment" id="subsample_adjustment">
+    <property name="upper">12</property>
+    <property name="value">4</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">1</property>
+  </object>
+</interface>