pixman: Branch 'master' - 4 commits

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Wed May 29 06:54:28 UTC 2019


 .gitignore                       |    1 
 demos/Makefile.am                |    5 
 demos/dither.c                   |  279 ++++++++++++++++
 demos/dither.ui                  |  147 ++++++++
 demos/meson.build                |    1 
 pixman/dither/blue-noise-64x64.h |   77 ++++
 pixman/dither/make-blue-noise.c  |  679 +++++++++++++++++++++++++++++++++++++++
 pixman/pixman-bits-image.c       |  134 +++++++
 pixman/pixman-general.c          |    3 
 pixman/pixman-image.c            |   35 ++
 pixman/pixman-private.h          |    4 
 pixman/pixman.h                  |   15 
 test/tolerance-test.c            |   23 +
 test/utils.c                     |  122 ++++++-
 test/utils.h                     |   12 
 15 files changed, 1522 insertions(+), 15 deletions(-)

New commits:
commit cb2ec4268fbde0df3b588ce5cbe2e43e04654520
Author: Basile Clement <basile-pixman at clement.pm>
Date:   Mon Apr 22 22:13:57 2019 +0200

    Ordered dithering with blue noise, v2
    
    On some screens (typically low quality laptop screens), using Bayer
    ordered dithering has been observed to cause color changes depending on
    *where the gradient is rendered on the screen*, causing visible
    flickering when moving an image on the screen.
    
    To alleviate the issue, this patch adds support for ordered dithering
    using a 64x64 matrix tuned from blue noise.  In addition to being devoid
    of the positional dependency on screen, the blue noise matrix also
    generates more pleasing and less discernable patterns.  As such, it is
    now the method used for PIXMAN_DITHER_GOOD and PIXMAN_DITHER_BEST
    dithering methods.
    
    The 64x64 blue noise matrix has been generated using the provided
    `pixman/dither/make-blue-noise.c` script, which uses the
    void-and-cluster method.
    
    Changes since v1 (thanks Bill):
     - Use uint16_t for the blue noise matrix for lower memory usage
     - Use bitwise computation for array index

diff --git a/demos/dither.c b/demos/dither.c
index d4271e6..d72c250 100644
--- a/demos/dither.c
+++ b/demos/dither.c
@@ -78,6 +78,7 @@ 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
diff --git a/pixman/dither/blue-noise-64x64.h b/pixman/dither/blue-noise-64x64.h
new file mode 100644
index 0000000..93c8805
--- /dev/null
+++ b/pixman/dither/blue-noise-64x64.h
@@ -0,0 +1,77 @@
+/* WARNING: This file is generated by make-blue-noise.c
+ * Please edit that file instead of this one.
+ */
+
+#ifndef BLUE_NOISE_64X64_H
+#define BLUE_NOISE_64X64_H
+
+#include <stdint.h>
+
+static const uint16_t dither_blue_noise_64x64[4096] = {
+    3039, 1368, 3169, 103, 2211, 1248, 2981, 668, 2633, 37, 3963, 2903, 384, 2564, 3115, 1973, 3348, 830, 2505, 1293, 3054, 1060, 1505, 3268, 400, 1341, 593, 3802, 3384, 429, 4082, 1411, 2503, 3863, 126, 1292, 1887, 2855, 205, 2094, 2977, 1899, 3924, 356, 3088, 2500, 3942, 1409, 2293, 1734, 3732, 1291, 3227, 277, 2054, 786, 2871, 411, 2425, 1678, 3986, 455, 2879, 2288,
+    388, 1972, 3851, 778, 2768, 3697, 944, 2123, 1501, 3533, 937, 1713, 1381, 3888, 156, 1242, 516, 2888, 1607, 3676, 632, 2397, 3804, 2673, 1898, 3534, 2593, 1777, 1170, 2299, 3013, 1838, 523, 3053, 1647, 3601, 3197, 959, 1520, 3633, 893, 2437, 3367, 2187, 1258, 137, 1965, 401, 3546, 643, 3087, 2498, 733, 2786, 3371, 4053, 1266, 1977, 3663, 183, 2570, 2107, 1183, 3708,
+    907, 2473, 1151, 3363, 1527, 1902, 232, 3903, 3060, 496, 2486, 3206, 2165, 861, 2387, 3653, 2101, 3972, 132, 2162, 3437, 1827, 215, 895, 3114, 271, 969, 2932, 197, 1598, 878, 3696, 1140, 2120, 904, 2431, 302, 3846, 2675, 481, 3187, 66, 1440, 650, 3833, 2826, 3435, 901, 2936, 2111, 250, 1875, 3609, 1174, 1747, 162, 2346, 3420, 913, 3172, 1383, 752, 3298, 1735,
+    3540, 2938, 249, 2324, 526, 3099, 2561, 1324, 2347, 1861, 1200, 3702, 257, 3442, 1514, 2999, 992, 1766, 2735, 1163, 478, 2943, 1279, 3635, 2177, 1464, 3672, 2386, 3871, 3340, 2690, 64, 3489, 2811, 3999, 633, 1948, 1243, 2269, 1807, 1143, 2750, 3729, 1790, 2363, 1053, 1537, 2636, 4065, 1076, 1476, 3869, 450, 2200, 2676, 658, 2979, 1548, 544, 1913, 2838, 3911, 116, 2698,
+    517, 1295, 3997, 1739, 3665, 1083, 3509, 599, 3400, 118, 2956, 720, 2689, 1907, 567, 2523, 284, 3397, 711, 3219, 2450, 3985, 1665, 2549, 562, 3011, 1855, 729, 1355, 528, 1908, 2456, 1384, 337, 1540, 2654, 3138, 3513, 703, 4080, 3314, 2047, 855, 3037, 209, 3317, 577, 1828, 17, 2336, 3193, 2748, 962, 3441, 1450, 3246, 1075, 3878, 2615, 3497, 1033, 2310, 1442, 2183,
+    1654, 3254, 2061, 738, 2832, 148, 2030, 1670, 909, 3850, 2109, 1533, 4046, 1085, 3098, 3897, 1378, 2248, 3829, 1495, 1966, 23, 797, 3427, 1124, 4057, 95, 2787, 2190, 3074, 3950, 742, 3194, 1999, 3386, 1113, 16, 1657, 2804, 201, 1543, 383, 2559, 1325, 3604, 2068, 2493, 3771, 1284, 3460, 710, 1716, 2447, 80, 3811, 2032, 347, 2227, 15, 1689, 397, 3084, 662, 3798,
+    973, 43, 2608, 3143, 1459, 2423, 4066, 2770, 3191, 1283, 2630, 314, 3235, 2289, 72, 1822, 2840, 924, 350, 2653, 1057, 3715, 2235, 2775, 346, 2083, 1553, 3292, 1081, 274, 1686, 1188, 2327, 3743, 578, 2234, 3916, 2519, 1011, 3056, 2207, 3438, 3890, 537, 1617, 837, 3094, 373, 2795, 1980, 276, 3951, 1353, 3015, 844, 1724, 3651, 2923, 1316, 4092, 2504, 3627, 1936, 2854,
+    2461, 3929, 1193, 421, 3746, 820, 1180, 286, 2261, 532, 3625, 1812, 802, 1327, 3527, 670, 3730, 2025, 3124, 3565, 529, 2960, 1769, 1390, 3196, 2494, 3756, 796, 3618, 2602, 3463, 2847, 166, 953, 1745, 2900, 438, 2070, 1418, 3741, 639, 1205, 1891, 2882, 2282, 4012, 1182, 1696, 3630, 951, 2904, 2170, 3530, 375, 2320, 2742, 1132, 701, 3216, 2023, 847, 1230, 310, 3431,
+    770, 1961, 3531, 1702, 2181, 3370, 1877, 3072, 1571, 3389, 1071, 2415, 3782, 2803, 1610, 2454, 1211, 182, 1655, 2322, 1282, 3372, 287, 3935, 704, 1232, 415, 1910, 2286, 1399, 556, 1964, 4068, 2444, 3605, 1272, 3345, 816, 3526, 256, 2402, 2777, 955, 345, 3289, 111, 2727, 635, 2396, 1488, 3331, 600, 1032, 1575, 4026, 515, 3507, 2433, 1605, 460, 3364, 2783, 1810, 1397,
+    2334, 223, 2945, 688, 2533, 99, 2705, 624, 3944, 2073, 46, 2978, 508, 2132, 269, 3173, 3453, 2631, 4076, 694, 1892, 2586, 972, 2178, 3470, 1695, 2849, 3141, 77, 3884, 994, 3029, 1536, 673, 3083, 124, 2583, 1722, 2821, 1944, 4027, 1661, 3176, 3728, 1337, 1813, 3503, 2035, 3930, 157, 2537, 1865, 3096, 2646, 1941, 3252, 1449, 135, 2836, 3758, 2139, 84, 3678, 3106,
+    3862, 1545, 3307, 1320, 3955, 1031, 3664, 1306, 2460, 776, 1487, 3294, 1187, 3990, 1903, 1021, 549, 1484, 943, 3027, 97, 3853, 1499, 2880, 198, 2575, 3995, 1089, 1587, 2475, 3282, 339, 2657, 1158, 2105, 1493, 3943, 580, 3232, 1287, 846, 48, 2480, 2112, 771, 2534, 459, 3134, 850, 1298, 3790, 325, 3652, 1249, 193, 940, 2202, 3895, 1829, 911, 1366, 2577, 1069, 534,
+    2104, 1009, 2667, 392, 1983, 2917, 1645, 324, 3439, 2869, 3705, 1767, 2592, 756, 2916, 3683, 2276, 2850, 2053, 3594, 2403, 3181, 634, 3699, 1933, 906, 519, 2150, 3673, 764, 1770, 2220, 3795, 3336, 502, 3547, 2339, 1110, 301, 2210, 3354, 3643, 569, 1518, 2940, 3973, 1138, 1613, 2773, 2127, 2983, 1671, 769, 2161, 3800, 2730, 3127, 1179, 533, 3259, 2284, 4014, 1651, 2820,
+    3566, 653, 1839, 3455, 2399, 789, 3149, 2244, 1863, 1099, 474, 2307, 158, 3541, 1312, 1711, 0, 3902, 360, 1629, 1091, 395, 1781, 1191, 2374, 3353, 1419, 3225, 206, 2931, 3553, 1046, 54, 1646, 2470, 910, 1860, 3137, 3770, 2635, 1562, 2809, 1215, 3788, 222, 2199, 3335, 67, 3606, 524, 1001, 3309, 2410, 3473, 591, 1619, 291, 2502, 3629, 2891, 335, 741, 3378, 168,
+    2384, 3129, 4051, 22, 1444, 3613, 543, 3893, 186, 2665, 4062, 933, 3058, 2142, 449, 2711, 3224, 849, 1330, 3349, 2195, 2670, 3484, 2993, 32, 3774, 2722, 1859, 2548, 1268, 583, 2027, 3165, 2807, 4029, 227, 2897, 1434, 721, 1816, 195, 905, 2066, 3258, 1754, 970, 2674, 1880, 2338, 3915, 1485, 2660, 14, 1313, 2914, 2046, 4074, 791, 1917, 1301, 1725, 2687, 2019, 1443,
+    418, 1186, 1664, 2859, 1049, 2056, 2741, 1226, 1589, 3186, 2042, 1377, 3449, 1574, 3941, 1063, 1930, 2501, 3751, 2930, 671, 4031, 888, 2081, 1544, 684, 1117, 351, 4052, 1698, 2393, 3881, 1439, 785, 1277, 2013, 3488, 441, 2459, 3980, 3061, 3481, 2543, 419, 3020, 609, 3515, 1350, 799, 2878, 348, 2034, 3966, 1824, 950, 3281, 1394, 2239, 3452, 55, 3922, 3119, 892, 3785,
+    3023, 2140, 782, 2492, 3817, 241, 3355, 2424, 856, 3639, 612, 2556, 245, 2858, 705, 2316, 3562, 495, 1748, 128, 1912, 1454, 280, 2552, 3905, 3130, 2274, 3472, 834, 3055, 240, 2692, 471, 2272, 3301, 2632, 1080, 3693, 2136, 1029, 1364, 590, 1611, 4067, 1190, 2360, 3827, 261, 3180, 1768, 3471, 1103, 3003, 520, 3674, 151, 2571, 555, 3033, 982, 2353, 504, 1259, 2555,
+    149, 3889, 3380, 493, 3178, 1681, 663, 1924, 2990, 49, 1792, 3861, 1192, 1987, 3273, 297, 1457, 3043, 1177, 2292, 3249, 2829, 3682, 1154, 1758, 428, 2872, 1993, 1500, 3703, 1129, 3421, 1840, 3754, 163, 659, 1733, 3182, 38, 2875, 1957, 3614, 2237, 78, 1873, 2801, 1513, 2121, 1074, 2516, 667, 3710, 1429, 2430, 2088, 2830, 1072, 3557, 1531, 2733, 1955, 3286, 3590, 1826,
+    2778, 1068, 1932, 1452, 2279, 1185, 3564, 3952, 1391, 2726, 3313, 2331, 870, 3709, 1674, 2772, 4085, 808, 2596, 3848, 927, 538, 2335, 3334, 773, 3597, 1347, 109, 2663, 608, 2108, 2994, 936, 1524, 2922, 3968, 2422, 1467, 845, 3870, 321, 2704, 1073, 3308, 3680, 823, 430, 3375, 4030, 112, 2171, 2695, 267, 3374, 731, 1627, 3919, 1871, 352, 3839, 1370, 234, 794, 1532,
+    3245, 647, 3575, 74, 3045, 2766, 285, 2174, 498, 1059, 1551, 385, 3125, 2598, 143, 1128, 2095, 3395, 318, 1590, 3524, 1345, 1969, 242, 2759, 2092, 947, 3926, 3244, 2356, 1658, 6, 3593, 2554, 1172, 1995, 371, 2755, 3417, 2294, 1570, 3164, 748, 2517, 1401, 3111, 2420, 1662, 2910, 1276, 3276, 854, 1804, 4000, 1253, 2987, 229, 2344, 3184, 649, 2196, 2921, 4095, 2389,
+    1289, 2193, 2579, 4023, 757, 1858, 986, 3199, 2514, 3475, 4021, 2154, 651, 1432, 3468, 2404, 574, 1799, 3105, 2145, 86, 2614, 3218, 1565, 4088, 2481, 3079, 1815, 323, 1212, 3837, 759, 2159, 435, 3223, 784, 3659, 1114, 1888, 550, 1221, 3786, 1803, 499, 2117, 185, 3763, 942, 589, 2001, 3838, 1483, 3154, 2256, 468, 2544, 3403, 898, 1208, 2610, 3622, 967, 1929, 378,
+    3781, 220, 1656, 1115, 3347, 2428, 3822, 1577, 712, 1959, 110, 2765, 1762, 3854, 979, 2928, 3714, 1371, 746, 3969, 2884, 975, 3779, 641, 1142, 159, 1460, 702, 3485, 2866, 2495, 3330, 1305, 3937, 1635, 2229, 2962, 146, 4055, 3091, 2417, 100, 3508, 2933, 4006, 1167, 1920, 2760, 3552, 2545, 433, 2845, 142, 1056, 1886, 3616, 1435, 2099, 3803, 1749, 27, 1446, 3350, 2843,
+    884, 3310, 2948, 2103, 447, 1351, 187, 2895, 3655, 1256, 3036, 932, 3325, 2257, 451, 1915, 40, 2780, 2438, 1112, 1814, 423, 2290, 1905, 2898, 3419, 2306, 3760, 1938, 486, 1019, 1791, 3010, 2628, 203, 3408, 1269, 2507, 1606, 862, 2779, 2078, 952, 1529, 2638, 708, 3332, 1413, 2, 1726, 1156, 3500, 2392, 3791, 3076, 812, 107, 2861, 501, 3050, 3487, 2455, 594, 1731,
+    2685, 1498, 680, 3908, 2621, 3529, 1786, 2236, 342, 2569, 1526, 3722, 230, 1290, 3203, 3947, 1609, 3516, 467, 3267, 3685, 1461, 3140, 3569, 367, 1759, 928, 2754, 1332, 2219, 4034, 260, 655, 1984, 978, 3814, 617, 2086, 3525, 279, 3841, 1373, 3361, 319, 2251, 3066, 407, 2382, 3918, 3133, 2168, 762, 1523, 507, 2641, 1677, 4025, 2413, 1584, 793, 2049, 1109, 3962, 2218,
+    1194, 3692, 266, 1687, 981, 3103, 740, 3983, 1005, 3434, 570, 2383, 1942, 2718, 676, 2462, 1007, 2089, 1308, 2222, 233, 2568, 829, 1241, 2669, 3987, 514, 3303, 69, 3142, 1603, 3560, 2295, 3288, 1497, 2696, 1764, 2865, 1058, 3271, 1914, 477, 2529, 3927, 1736, 1273, 3752, 2029, 1012, 565, 2798, 4078, 1949, 3305, 1175, 2179, 380, 3366, 1195, 3849, 2637, 416, 2959, 125,
+    3396, 2467, 2036, 3234, 2340, 68, 2819, 1436, 2011, 3139, 1704, 4073, 860, 3582, 1468, 2969, 211, 3157, 4056, 866, 2935, 2000, 3923, 31, 2157, 1477, 2429, 1147, 3792, 2557, 774, 2802, 1153, 3747, 464, 3192, 42, 3904, 539, 1474, 2283, 803, 2876, 1061, 75, 3477, 747, 2893, 1538, 3626, 251, 1322, 2506, 189, 2791, 3667, 939, 2991, 1971, 175, 3195, 1416, 3648, 1857,
+    3052, 454, 851, 3789, 1271, 1906, 3694, 2484, 406, 2757, 26, 1189, 2909, 296, 2215, 3784, 1864, 637, 2715, 1673, 3445, 581, 1572, 3059, 3469, 761, 2984, 1737, 2058, 440, 1414, 1921, 121, 2527, 894, 2223, 1302, 2377, 3077, 2666, 3759, 3198, 1811, 3661, 2166, 2731, 1883, 359, 3285, 2458, 1805, 3459, 926, 3834, 675, 1893, 1496, 2612, 657, 3523, 1763, 2354, 564, 961,
+    1367, 3977, 1588, 2714, 322, 3446, 1088, 625, 3887, 1354, 3535, 2090, 3316, 1760, 1127, 483, 3491, 1421, 2301, 94, 1202, 3740, 2311, 1014, 1878, 3836, 180, 3412, 991, 2868, 3953, 3450, 3081, 1632, 4071, 1882, 3543, 726, 1719, 179, 1171, 364, 1420, 622, 3090, 1490, 946, 4007, 2212, 1102, 619, 2739, 2189, 1669, 2937, 3426, 39, 3940, 2191, 1264, 887, 4091, 2792, 2135,
+    4, 2883, 2281, 631, 3044, 1641, 2232, 3243, 1773, 2319, 827, 2591, 629, 3938, 2426, 3222, 2629, 1044, 3879, 3293, 1952, 2749, 275, 2590, 472, 1372, 2496, 660, 3669, 2264, 208, 915, 2167, 561, 2828, 307, 3265, 1104, 3964, 2155, 3425, 1951, 4077, 2391, 283, 3387, 2581, 115, 1415, 3069, 3896, 141, 3158, 1214, 442, 2405, 1349, 3085, 425, 2528, 3002, 312, 1602, 3588,
+    1137, 3323, 1963, 1002, 3578, 2521, 127, 925, 2970, 273, 3737, 1573, 167, 2863, 1509, 800, 147, 2059, 2942, 409, 921, 3151, 1451, 3909, 3333, 2844, 2096, 1512, 3136, 1210, 1798, 2709, 1331, 3586, 1034, 1521, 2441, 2926, 488, 2585, 775, 3031, 2693, 879, 3602, 1173, 2028, 3654, 2781, 841, 1975, 1507, 3646, 768, 3991, 2012, 996, 3544, 1666, 3810, 1990, 3360, 753, 2597,
+    3736, 304, 1473, 3828, 485, 1334, 4008, 2072, 3495, 1136, 2806, 2004, 3236, 1010, 2130, 3819, 1750, 3567, 644, 2515, 1794, 3636, 698, 2137, 1162, 832, 3761, 326, 2613, 513, 3302, 3820, 357, 3163, 2259, 3733, 101, 1922, 1386, 3587, 1640, 28, 1286, 2141, 1761, 2918, 693, 1639, 457, 3250, 2434, 365, 2599, 1729, 3284, 2643, 306, 2793, 689, 1090, 104, 1309, 2305, 1831,
+    2776, 859, 2446, 2915, 1778, 3337, 2677, 614, 1508, 2409, 469, 4033, 1321, 3563, 402, 3131, 2720, 1093, 1569, 4042, 1229, 2277, 216, 3046, 1817, 57, 3006, 1684, 4059, 2016, 795, 2440, 1652, 1960, 610, 2763, 920, 3864, 3110, 1026, 2326, 3762, 3233, 521, 3856, 173, 2457, 3939, 2138, 1262, 3572, 989, 3021, 2238, 119, 1445, 3832, 1809, 2297, 3467, 2700, 3684, 3102, 394,
+    4036, 2050, 3256, 89, 2198, 1079, 248, 1845, 3805, 3104, 880, 1779, 2688, 717, 2373, 1375, 262, 2249, 3071, 13, 2813, 3429, 1600, 3984, 2416, 3603, 1299, 2298, 998, 3492, 1393, 2951, 10, 4009, 1247, 3462, 1679, 2204, 414, 2736, 316, 1894, 2816, 1050, 3373, 1462, 3107, 817, 3464, 21, 1835, 4070, 568, 1178, 3718, 875, 3168, 466, 2974, 1458, 2084, 616, 1564, 1018,
+    1693, 546, 1244, 3899, 716, 3160, 3608, 2877, 1220, 334, 3443, 2270, 44, 3000, 1843, 3928, 3405, 766, 3686, 2040, 587, 993, 2647, 387, 930, 2753, 630, 3274, 150, 2808, 453, 3638, 1092, 2352, 3030, 239, 2562, 700, 3240, 1257, 4016, 730, 1515, 2203, 2551, 417, 1866, 1123, 2348, 2902, 1550, 2678, 2075, 3238, 1630, 2531, 2115, 1255, 4054, 840, 290, 3874, 2477, 3399,
+    2250, 3577, 2817, 1626, 2576, 1356, 2315, 792, 2087, 2618, 1612, 3855, 1263, 3637, 1036, 494, 1535, 2553, 1198, 1715, 3867, 3170, 1359, 1954, 3483, 1539, 2069, 3886, 1772, 2487, 1534, 2045, 3242, 806, 1578, 2018, 3948, 1423, 3596, 2076, 2466, 3424, 139, 3688, 871, 4049, 2852, 3342, 547, 3719, 327, 852, 3505, 207, 2794, 542, 3600, 45, 2411, 3324, 1788, 3012, 1235, 61,
+    2655, 917, 253, 1986, 3738, 313, 1706, 4072, 120, 3229, 957, 597, 2024, 3262, 2453, 2857, 2002, 3190, 210, 2784, 2206, 300, 2400, 3766, 553, 3152, 218, 1150, 2988, 883, 3753, 627, 2664, 3831, 437, 3385, 1008, 2957, 60, 1636, 891, 2899, 1776, 3062, 1315, 2026, 194, 1643, 2079, 1296, 3201, 2465, 1379, 1927, 3898, 1125, 1847, 2846, 1552, 1028, 2725, 2169, 787, 3202,
+    1441, 3982, 3032, 1052, 3251, 605, 2639, 3073, 1431, 3642, 2329, 2949, 341, 1634, 833, 129, 4020, 916, 3571, 669, 1506, 3411, 821, 2856, 1207, 2337, 2683, 3448, 340, 2214, 3128, 235, 1738, 1288, 2833, 2419, 606, 1884, 2668, 552, 3765, 1176, 399, 2302, 596, 3591, 2634, 767, 3845, 2767, 995, 3967, 491, 3057, 814, 2300, 3422, 691, 3797, 254, 3645, 509, 3478, 1836,
+    2119, 475, 2445, 1525, 2175, 3539, 914, 1926, 473, 1157, 1800, 3971, 2701, 3739, 2129, 3486, 1333, 1784, 2366, 2982, 1070, 4089, 1802, 73, 1642, 3958, 835, 1837, 1480, 4043, 1217, 2469, 3416, 2113, 88, 3668, 1240, 3255, 3920, 2355, 3167, 2003, 2645, 3936, 3228, 1592, 1144, 3474, 2394, 79, 1820, 2241, 1594, 3656, 2584, 153, 1448, 3034, 2005, 2511, 1692, 1335, 3913, 217,
+    2822, 3391, 745, 3813, 192, 1274, 2941, 3847, 2489, 3440, 744, 161, 1422, 1086, 572, 3004, 2617, 338, 3807, 2031, 236, 2472, 3065, 2098, 3358, 362, 2163, 3574, 497, 2788, 1970, 948, 3885, 685, 3100, 1712, 2228, 292, 1408, 1016, 164, 3537, 1417, 941, 34, 2172, 3001, 358, 1491, 3147, 699, 3356, 258, 1149, 2946, 1787, 3931, 382, 1146, 3291, 818, 2890, 2379, 1096,
+    3679, 1328, 1901, 3162, 2747, 1730, 2253, 5, 1556, 2818, 2093, 3166, 2522, 3410, 2287, 1701, 956, 3237, 620, 1596, 3300, 1307, 511, 3701, 1020, 2939, 1362, 2532, 3208, 749, 3641, 160, 1522, 2624, 1095, 4086, 826, 2841, 3583, 2173, 1727, 723, 2925, 1911, 2482, 3726, 863, 1962, 4028, 1111, 2835, 3773, 2449, 2022, 582, 3278, 923, 2619, 2152, 4039, 92, 1934, 3145, 677,
+    2530, 53, 2303, 1003, 458, 3989, 739, 3321, 1064, 369, 3556, 877, 1900, 426, 3876, 1, 3617, 2106, 1197, 2805, 3634, 857, 2706, 1504, 2418, 682, 3868, 20, 1139, 1688, 2333, 3311, 2907, 1945, 265, 2385, 3433, 1601, 636, 2620, 3095, 4044, 386, 3382, 1184, 527, 2814, 3414, 2342, 465, 1889, 1343, 874, 3479, 1502, 2233, 3689, 1385, 559, 2745, 1463, 3465, 376, 1718,
+    3217, 4045, 1580, 3612, 2525, 1228, 3018, 1958, 3725, 2358, 1361, 3996, 1581, 3063, 1224, 2737, 1475, 2442, 3946, 191, 1796, 2128, 3975, 134, 1916, 3318, 1597, 2071, 3749, 2672, 403, 1278, 602, 3745, 3220, 1374, 445, 2064, 3830, 243, 1252, 2390, 1563, 2724, 3875, 1818, 1346, 165, 1650, 3264, 2680, 117, 2998, 4081, 343, 2799, 9, 3122, 1743, 3724, 1040, 2231, 3842, 1209,
+    900, 398, 2851, 697, 1797, 3482, 293, 2679, 1649, 566, 2954, 91, 2697, 714, 2060, 3211, 781, 480, 3040, 1038, 2611, 666, 2989, 3458, 1201, 2796, 548, 2975, 839, 3121, 1850, 4001, 2208, 1631, 790, 2558, 2972, 1148, 3213, 1849, 3624, 971, 2102, 108, 772, 3101, 2589, 3777, 1042, 656, 3907, 2097, 1615, 2540, 805, 1935, 1231, 3494, 2451, 268, 2995, 750, 2682, 2020,
+    3024, 1392, 2124, 3279, 106, 2217, 1387, 822, 3214, 3825, 2160, 1000, 2395, 3691, 228, 4038, 1872, 3413, 1608, 2225, 3536, 303, 1653, 886, 2541, 224, 4037, 2252, 1428, 172, 3504, 958, 2848, 113, 3628, 1834, 3979, 19, 2317, 779, 2797, 518, 3174, 3549, 1482, 2266, 444, 2014, 3555, 2439, 1213, 3113, 535, 1135, 3204, 3858, 2309, 931, 623, 2009, 3359, 1566, 140, 3550,
+    1808, 3872, 2488, 1152, 3764, 2892, 3960, 2412, 353, 1223, 1825, 3444, 3116, 1717, 1082, 2313, 1280, 2661, 82, 3852, 1389, 3200, 2330, 3812, 2038, 3581, 1728, 1039, 3339, 2427, 586, 2580, 1238, 3328, 2280, 1047, 595, 2662, 1363, 3338, 1620, 3934, 2497, 1881, 1054, 3954, 3215, 864, 2887, 1801, 320, 3519, 2378, 3704, 1753, 424, 2958, 1660, 4005, 2601, 1116, 3912, 2381, 573,
+    2740, 200, 828, 1667, 432, 1931, 1035, 1616, 3598, 2640, 728, 264, 1437, 557, 3501, 2966, 372, 3734, 974, 1978, 758, 2719, 1145, 452, 1433, 725, 2681, 408, 3843, 1918, 1547, 3906, 1996, 503, 1456, 3019, 3493, 1700, 3742, 355, 2134, 176, 1311, 615, 2867, 315, 1680, 1314, 8, 3297, 1494, 783, 1950, 83, 2656, 1382, 3561, 138, 2834, 1404, 330, 1904, 3156, 1027,
+    1357, 3381, 3041, 3666, 2729, 734, 3415, 177, 3051, 2021, 4079, 2823, 3775, 2186, 2616, 869, 1668, 3148, 2367, 3315, 393, 4075, 1870, 2920, 3343, 2362, 3188, 1303, 2782, 825, 3171, 259, 2905, 3717, 2538, 184, 2074, 838, 2860, 2407, 1024, 3496, 3008, 3706, 1985, 2349, 3623, 2582, 4058, 2184, 2694, 3873, 2964, 990, 3346, 690, 2033, 1066, 2201, 3490, 2971, 718, 3700, 2188,
+    4061, 391, 1989, 2325, 1430, 3150, 2125, 2526, 592, 1403, 976, 2351, 1165, 1851, 114, 3921, 2063, 613, 1358, 2785, 1623, 2254, 25, 3542, 1045, 246, 1852, 3554, 87, 2243, 3615, 1169, 727, 1705, 968, 3957, 3185, 1251, 500, 4063, 1751, 2622, 842, 1519, 90, 3393, 819, 490, 1874, 999, 571, 1275, 2271, 1586, 4040, 2448, 3126, 3731, 436, 885, 1708, 2421, 24, 1599,
+    889, 2563, 1199, 645, 70, 4013, 1237, 3723, 1694, 3499, 3, 3266, 484, 2997, 3390, 1233, 2842, 3687, 152, 3480, 1084, 3698, 881, 2490, 1542, 3992, 2209, 692, 1690, 3022, 1470, 2625, 2114, 3512, 2359, 381, 2684, 1897, 3368, 1395, 3080, 289, 2065, 3981, 2758, 1141, 3097, 1472, 2870, 3352, 3707, 225, 3159, 505, 1895, 214, 1222, 1774, 2686, 3978, 3275, 1196, 3518, 2825,
+    3270, 1720, 3796, 3466, 2650, 1841, 298, 899, 2862, 2091, 2671, 1744, 3735, 801, 1560, 349, 2262, 903, 1833, 2524, 512, 3117, 1793, 2827, 476, 3038, 1216, 2550, 3826, 980, 431, 4048, 35, 2992, 1265, 1595, 765, 3675, 76, 2247, 696, 3456, 1254, 2452, 664, 1757, 2133, 3750, 145, 2332, 1554, 1981, 3580, 2712, 868, 3640, 2919, 638, 2275, 1427, 309, 2595, 2006, 492,
+    2226, 178, 2911, 836, 1528, 3028, 2240, 3327, 404, 3970, 707, 1294, 2464, 2131, 4032, 2600, 3319, 1406, 2913, 3974, 2156, 1425, 221, 3877, 2017, 811, 3662, 272, 3287, 1988, 2408, 3357, 1746, 598, 3239, 3823, 2182, 2934, 1078, 2604, 3840, 1697, 2906, 413, 3210, 3880, 331, 2644, 1260, 848, 3042, 2535, 1077, 1438, 3261, 2365, 1561, 3799, 85, 3082, 1876, 674, 3932, 1101,
+    3644, 1344, 1943, 2401, 390, 3835, 1048, 2572, 1541, 1133, 3075, 3584, 308, 2889, 1065, 1869, 601, 3783, 282, 1181, 736, 3312, 2368, 1126, 3383, 1675, 2734, 1426, 628, 2873, 1317, 843, 2717, 2048, 1004, 2536, 333, 1782, 3295, 1517, 219, 2153, 815, 3502, 1579, 2268, 987, 3409, 1780, 4018, 354, 665, 3914, 47, 1956, 456, 1006, 2010, 3406, 1130, 3621, 2894, 1549, 3092,
+    2485, 640, 3993, 3179, 1270, 3436, 585, 1925, 3757, 2304, 136, 1976, 1486, 646, 3520, 50, 3155, 1637, 2435, 3522, 1937, 2756, 3748, 661, 2224, 58, 3230, 2357, 1830, 3892, 170, 3607, 1447, 3949, 190, 3392, 1336, 584, 4010, 918, 3016, 3670, 1155, 2406, 52, 1304, 3009, 607, 2085, 2699, 3205, 1848, 2291, 3402, 2764, 3865, 3048, 2508, 735, 2710, 443, 2341, 897, 263,
+    1785, 2769, 983, 56, 2197, 1685, 2703, 202, 2944, 810, 3377, 2626, 3787, 3047, 2055, 1236, 2752, 2122, 945, 3093, 96, 1624, 439, 3014, 1388, 4015, 977, 448, 3506, 1098, 2242, 3026, 506, 2361, 2952, 1862, 3619, 2790, 1992, 2483, 525, 1868, 2652, 4093, 1998, 3595, 2478, 3816, 122, 1412, 929, 3716, 1166, 1648, 813, 1300, 199, 1489, 3998, 1771, 1310, 3808, 2052, 3423,
+    434, 3712, 1625, 3558, 2955, 853, 4019, 1348, 3511, 1732, 1246, 487, 934, 1672, 2510, 3965, 788, 3711, 396, 1369, 4090, 1055, 2603, 1879, 3528, 2518, 2067, 3005, 1516, 2588, 751, 1740, 3418, 1131, 1576, 686, 2296, 1118, 18, 3263, 1365, 3401, 294, 737, 3177, 410, 867, 1633, 2963, 3579, 2375, 252, 2881, 479, 2471, 3576, 2180, 3306, 332, 2255, 3035, 41, 2648, 1396,
+    2929, 2230, 1219, 2512, 446, 2008, 3189, 2388, 626, 2164, 2831, 4047, 2376, 174, 3272, 368, 1469, 3226, 2578, 1991, 2874, 2263, 3681, 876, 188, 1239, 683, 3776, 226, 3183, 4083, 2148, 63, 2649, 3859, 299, 3086, 3933, 1585, 2185, 3767, 988, 1707, 2908, 1407, 1844, 2771, 2245, 1161, 560, 1755, 3376, 2051, 4064, 3135, 1832, 652, 2853, 1051, 3649, 760, 3290, 1105, 3945,
+    872, 154, 3207, 713, 3780, 1453, 281, 1087, 3695, 30, 3299, 1919, 1400, 3551, 1119, 1890, 2314, 618, 1703, 3428, 724, 295, 3146, 1557, 3341, 2896, 1683, 2723, 1974, 1017, 541, 1380, 3720, 804, 3280, 2082, 997, 2567, 777, 2961, 213, 2707, 2328, 3632, 1025, 3891, 3304, 255, 4003, 3108, 2587, 1323, 743, 1479, 105, 1013, 3901, 1618, 2044, 2627, 1465, 1846, 576, 1994,
+    2560, 3521, 1742, 2118, 2800, 3404, 1783, 2609, 2968, 1582, 1022, 412, 2713, 687, 2976, 3857, 2761, 3620, 62, 1108, 3844, 1340, 2100, 540, 2345, 3925, 405, 3457, 1319, 2468, 3362, 2815, 1867, 2372, 1281, 1714, 3690, 482, 3498, 1842, 1285, 3994, 558, 2039, 81, 2499, 678, 1481, 1923, 964, 12, 3824, 2980, 2205, 2762, 3432, 2398, 181, 3247, 462, 4094, 2350, 3589, 3089,
+    1555, 1094, 4041, 247, 1267, 908, 3959, 2041, 732, 3860, 2343, 3132, 3769, 2144, 1621, 237, 912, 1329, 3025, 2146, 2642, 1775, 3721, 2746, 1121, 1953, 902, 2285, 130, 3671, 1659, 278, 3153, 522, 2721, 123, 2996, 1466, 2380, 377, 3231, 873, 1510, 3476, 3123, 1250, 2147, 3650, 2839, 3451, 2323, 1122, 3545, 379, 1765, 1218, 603, 3768, 1360, 938, 2885, 133, 1245, 363,
+    2364, 554, 2743, 3344, 2474, 530, 3112, 169, 1297, 3430, 536, 1741, 98, 1043, 2574, 3253, 2246, 1854, 4022, 510, 3283, 204, 858, 3398, 36, 3118, 1478, 3794, 2986, 706, 2176, 922, 3559, 1097, 3976, 3322, 2149, 1160, 2810, 3883, 2007, 2513, 2953, 328, 1721, 3793, 422, 2566, 807, 329, 1638, 1967, 648, 2520, 3727, 3109, 2116, 2927, 2491, 1939, 3365, 1709, 2728, 3815,
+    2037, 3120, 831, 1405, 1896, 3592, 1622, 2369, 2864, 2151, 1107, 2542, 3532, 1410, 3917, 427, 3568, 709, 2509, 1503, 1037, 2973, 2436, 1604, 4035, 2594, 563, 1819, 2659, 1234, 4004, 2565, 1511, 2273, 1823, 336, 882, 3772, 575, 1628, 171, 3570, 1120, 2260, 2716, 935, 3064, 1806, 1342, 3144, 3900, 2744, 3296, 985, 1546, 238, 896, 1663, 305, 3660, 695, 2213, 960, 3407,
+    144, 1795, 3894, 2267, 51, 2708, 1023, 3818, 366, 1821, 4087, 2985, 755, 2057, 2912, 949, 1583, 2774, 231, 3447, 2258, 3866, 1982, 672, 1225, 2077, 3320, 1062, 370, 3241, 1968, 7, 3068, 681, 3631, 2573, 1567, 3175, 2321, 1067, 3070, 722, 1856, 3744, 642, 1471, 4084, 131, 3514, 2443, 531, 1227, 155, 2265, 4024, 2658, 3326, 3910, 1168, 3078, 1530, 3956, 489, 1424,
+    3647, 1203, 420, 2924, 3755, 719, 3248, 1376, 3067, 890, 196, 1559, 3269, 270, 2432, 1885, 3212, 1164, 3778, 1752, 579, 1338, 344, 3585, 3017, 288, 3658, 2371, 3882, 1691, 611, 2789, 3809, 1339, 389, 2950, 2015, 59, 3548, 2751, 2158, 4011, 1352, 29, 3388, 2370, 2812, 1946, 954, 2110, 1558, 2947, 3573, 1909, 1326, 679, 1853, 2312, 551, 2702, 33, 2414, 3209, 2824,
+    2547, 2143, 3379, 966, 1492, 1979, 2479, 463, 2194, 3657, 2738, 2318, 1261, 3713, 604, 4002, 11, 2192, 2967, 919, 2607, 3369, 2837, 1676, 2539, 984, 1568, 93, 2901, 1318, 3538, 1041, 2216, 1756, 3454, 1030, 4050, 1402, 798, 1723, 311, 3277, 2546, 2886, 2043, 461, 1206, 3677, 361, 3260, 3988, 809, 2605, 470, 3007, 3517, 102, 3221, 1398, 2062, 3611, 1134, 1928, 865,
+    4060, 621, 1710, 2606, 3510, 317, 4017, 1682, 3329, 1159, 1940, 654, 3461, 1789, 1015, 2691, 1455, 3599, 374, 1947, 4069, 71, 2126, 763, 3961, 2278, 3161, 1997, 824, 2623, 2080, 244, 3257, 780, 2732, 2308, 545, 3351, 2476, 3806, 1204, 588, 1591, 963, 3610, 1699, 754, 3049, 2651, 1106, 65, 2221, 1644, 3821, 1100, 2463, 1614, 3801, 965, 2965, 715, 3394, 1593, 212,
+};
+
+#endif /* BLUE_NOISE_64X64_H */
diff --git a/pixman/dither/make-blue-noise.c b/pixman/dither/make-blue-noise.c
new file mode 100644
index 0000000..f9974b4
--- /dev/null
+++ b/pixman/dither/make-blue-noise.c
@@ -0,0 +1,679 @@
+/* Blue noise generation using the void-and-cluster method as described in
+ *
+ *     The void-and-cluster method for dither array generation
+ *     Ulichney, Robert A (1993)
+ *
+ *     http://cv.ulichney.com/papers/1993-void-cluster.pdf
+ *
+ * Note that running with openmp (-DUSE_OPENMP) will trigger additional
+ * randomness due to computing reductions in parallel, and is not recommended
+ * unless generating very large dither arrays.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <math.h>
+#include <stdio.h>
+
+/* Booleans and utility functions */
+
+#ifndef TRUE
+#   define TRUE 1
+#endif
+
+#ifndef FALSE
+#   define FALSE 0
+#endif
+
+typedef int bool_t;
+
+int
+imin (int x, int y)
+{
+    return x < y ? x : y;
+}
+
+/* Memory allocation */
+void *
+malloc_abc (unsigned int a, unsigned int b, unsigned int c)
+{
+    if (a >= INT32_MAX / b)
+	return NULL;
+    else if (a * b >= INT32_MAX / c)
+	return NULL;
+    else
+	return malloc (a * b * c);
+}
+
+/* Random number generation */
+typedef uint32_t xorwow_state_t[5];
+
+uint32_t
+xorwow_next (xorwow_state_t *state)
+{
+    uint32_t s = (*state)[0],
+    t = (*state)[3];
+    (*state)[3] = (*state)[2];
+    (*state)[2] = (*state)[1];
+    (*state)[1] = s;
+
+    t ^= t >> 2;
+    t ^= t << 1;
+    t ^= s ^ (s << 4);
+
+    (*state)[0] = t;
+    (*state)[4] += 362437;
+
+    return t + (*state)[4];
+}
+
+float
+xorwow_float (xorwow_state_t *s)
+{
+    return (xorwow_next (s) >> 9) / (float)((1 << 23) - 1);
+}
+
+/* Floating point matrices
+ *
+ * Used to cache the cluster sizes.
+ */
+typedef struct matrix_t {
+    int width;
+    int height;
+    float *buffer;
+} matrix_t;
+
+bool_t
+matrix_init (matrix_t *matrix, int width, int height)
+{
+    float *buffer;
+
+    if (!matrix)
+	return FALSE;
+
+    buffer = malloc_abc (width, height, sizeof (float));
+
+    if (!buffer)
+	return FALSE;
+
+    matrix->buffer = buffer;
+    matrix->width  = width;
+    matrix->height = height;
+
+    return TRUE;
+}
+
+bool_t
+matrix_copy (matrix_t *dst, matrix_t const *src)
+{
+    float *srcbuf = src->buffer,
+	  *srcend = src->buffer + src->width * src->height,
+	  *dstbuf = dst->buffer;
+
+    if (dst->width != src->width || dst->height != src->height)
+	return FALSE;
+
+    while (srcbuf < srcend)
+	*dstbuf++ = *srcbuf++;
+
+    return TRUE;
+}
+
+float *
+matrix_get (matrix_t *matrix, int x, int y)
+{
+    return &matrix->buffer[y * matrix->width + x];
+}
+
+void
+matrix_destroy (matrix_t *matrix)
+{
+    free (matrix->buffer);
+}
+
+/* Binary patterns */
+typedef struct pattern_t {
+    int width;
+    int height;
+    bool_t *buffer;
+} pattern_t;
+
+bool_t
+pattern_init (pattern_t *pattern, int width, int height)
+{
+    bool_t *buffer;
+
+    if (!pattern)
+	return FALSE;
+
+    buffer = malloc_abc (width, height, sizeof (bool_t));
+
+    if (!buffer)
+	return FALSE;
+
+    pattern->buffer = buffer;
+    pattern->width  = width;
+    pattern->height = height;
+
+    return TRUE;
+}
+
+bool_t
+pattern_copy (pattern_t *dst, pattern_t const *src)
+{
+    bool_t *srcbuf = src->buffer,
+	   *srcend = src->buffer + src->width * src->height,
+	   *dstbuf = dst->buffer;
+
+    if (dst->width != src->width || dst->height != src->height)
+	return FALSE;
+
+    while (srcbuf < srcend)
+	*dstbuf++ = *srcbuf++;
+
+    return TRUE;
+}
+
+bool_t *
+pattern_get (pattern_t *pattern, int x, int y)
+{
+    return &pattern->buffer[y * pattern->width + x];
+}
+
+void
+pattern_fill_white_noise (pattern_t *pattern, float fraction,
+			  xorwow_state_t *s)
+{
+    bool_t *buffer = pattern->buffer;
+    bool_t *end    = buffer + (pattern->width * pattern->height);
+
+    while (buffer < end)
+	*buffer++ = xorwow_float (s) < fraction;
+}
+
+void
+pattern_destroy (pattern_t *pattern)
+{
+    free (pattern->buffer);
+}
+
+/* Dither arrays */
+typedef struct array_t {
+    int width;
+    int height;
+    uint32_t *buffer;
+} array_t;
+
+bool_t
+array_init (array_t *array, int width, int height)
+{
+    uint32_t *buffer;
+
+    if (!array)
+	return FALSE;
+
+    buffer = malloc_abc (width, height, sizeof (uint32_t));
+
+    if (!buffer)
+	return FALSE;
+
+    array->buffer = buffer;
+    array->width  = width;
+    array->height = height;
+
+    return TRUE;
+}
+
+uint32_t *
+array_get (array_t *array, int x, int y)
+{
+    return &array->buffer[y * array->width + x];
+}
+
+bool_t
+array_save_ppm (array_t *array, const char *filename)
+{
+    FILE *f = fopen(filename, "wb");
+
+    int i   = 0;
+    int bpp = 2;
+    uint8_t buffer[1024];
+
+    if (!f)
+	return FALSE;
+
+    if (array->width * array->height - 1 < 256)
+	bpp = 1;
+
+    fprintf(f, "P5 %d %d %d\n", array->width, array->height,
+	    array->width * array->height - 1);
+    while (i < array->width * array->height)
+    {
+	    int j = 0;
+	    for (; j < 1024 / bpp && j < array->width * array->height; ++j)
+	    {
+		    uint32_t v = array->buffer[i + j];
+		    if (bpp == 2)
+		    {
+			buffer[2 * j] = v & 0xff;
+			buffer[2 * j + 1] = (v & 0xff00) >> 8;
+		    } else {
+			buffer[j] = v;
+		    }
+	    }
+
+	    fwrite((void *)buffer, bpp, j, f);
+	    i += j;
+    }
+
+    if (fclose(f) != 0)
+	return FALSE;
+
+    return TRUE;
+}
+
+bool_t
+array_save (array_t *array, const char *filename)
+{
+    int x, y;
+    FILE *f = fopen(filename, "wb");
+
+    if (!f)
+	return FALSE;
+
+    fprintf (f, 
+"/* WARNING: This file is generated by make-blue-noise.c\n"
+" * Please edit that file instead of this one.\n"
+" */\n"
+"\n"
+"#ifndef BLUE_NOISE_%dX%d_H\n"
+"#define BLUE_NOISE_%dX%d_H\n"
+"\n"
+"#include <stdint.h>\n"
+"\n", array->width, array->height, array->width, array->height);
+
+    fprintf (f, "static const uint16_t dither_blue_noise_%dx%d[%d] = {\n",
+	     array->width, array->height, array->width * array->height);
+
+    for (y = 0; y < array->height; ++y)
+    {
+	fprintf (f, "    ");
+	for (x = 0; x < array->width; ++x)
+	{
+	    if (x != 0)
+		fprintf (f, ", ");
+
+	    fprintf (f, "%d", *array_get (array, x, y));
+	}
+
+	fprintf (f, ",\n");
+    }
+    fprintf (f, "};\n");
+
+    fprintf (f, "\n#endif /* BLUE_NOISE_%dX%d_H */\n",
+	     array->width, array->height);
+
+    if (fclose(f) != 0)
+	return FALSE;
+
+    return TRUE;
+}
+
+void
+array_destroy (array_t *array)
+{
+    free (array->buffer);
+}
+
+/* Dither array generation */
+bool_t
+compute_cluster_sizes (pattern_t *pattern, matrix_t *matrix)
+{
+    int width  = pattern->width,
+	height = pattern->height;
+
+    if (matrix->width != width || matrix->height != height)
+	return FALSE;
+
+    int px, py, qx, qy, dx, dy;
+    float tsqsi = 2.f * 1.5f * 1.5f;
+
+#ifdef USE_OPENMP
+#pragma omp parallel for default (none) \
+    private (py, px, qy, qx, dx, dy) \
+    shared (height, width, pattern, matrix, tsqsi)
+#endif
+    for (py = 0; py < height; ++py)
+    {
+	for (px = 0; px < width; ++px)
+	{
+	    bool_t pixel = *pattern_get (pattern, px, py);
+	    float dist   = 0.f;
+
+	    for (qx = 0; qx < width; ++qx)
+	    {
+		dx = imin (abs (qx - px), width - abs (qx - px));
+		dx = dx * dx;
+
+		for (qy = 0; qy < height; ++qy)
+		{
+		    dy = imin (abs (qy - py), height - abs (qy - py));
+		    dy = dy * dy;
+
+		    dist += (pixel == *pattern_get (pattern, qx, qy))
+			* expf (- (dx + dy) / tsqsi);
+		}
+	    }
+
+	    *matrix_get (matrix, px, py) = dist;
+	}
+    }
+
+    return TRUE;
+}
+
+bool_t
+swap_pixel (pattern_t *pattern, matrix_t *matrix, int x, int y)
+{
+    int width  = pattern->width,
+	height = pattern->height;
+
+    bool_t new;
+
+    float f,
+          dist  = 0.f,
+	  tsqsi = 2.f * 1.5f * 1.5f;
+
+    int px, py, dx, dy;
+    bool_t b;
+
+    new = !*pattern_get (pattern, x, y);
+    *pattern_get (pattern, x, y) = new;
+
+    if (matrix->width != width || matrix->height != height)
+	return FALSE;
+
+
+#ifdef USE_OPENMP
+#pragma omp parallel for reduction (+:dist) default (none) \
+    private (px, py, dx, dy, b, f) \
+    shared (x, y, width, height, pattern, matrix, new, tsqsi)
+#endif
+    for (py = 0; py < height; ++py)
+    {
+	dy = imin (abs (py - y), height - abs (py - y));
+	dy = dy * dy;
+
+	for (px = 0; px < width; ++px)
+	{
+	    dx = imin (abs (px - x), width - abs (px - x));
+	    dx = dx * dx;
+
+	    b = (*pattern_get (pattern, px, py) == new);
+	    f = expf (- (dx + dy) / tsqsi);
+	    *matrix_get (matrix, px, py) += (2 * b - 1) * f;
+
+	    dist += b * f;
+	}
+    }
+
+    *matrix_get (matrix, x, y) = dist;
+    return TRUE;
+}
+
+void
+largest_cluster (pattern_t *pattern, matrix_t *matrix,
+		 bool_t pixel, int *xmax, int *ymax)
+{
+    int width       = pattern->width,
+	height      = pattern->height;
+
+    int   x, y;
+
+    float vmax = -INFINITY;
+
+#ifdef USE_OPENMP
+#pragma omp parallel default (none) \
+    private (x, y) \
+    shared (height, width, pattern, matrix, pixel, xmax, ymax, vmax)
+#endif
+    {
+	int xbest = -1,
+	    ybest = -1;
+
+#ifdef USE_OPENMP
+	float vbest = -INFINITY;
+
+#pragma omp for reduction (max: vmax) collapse (2)
+#endif
+	for (y = 0; y < height; ++y)
+	{
+	    for (x = 0; x < width; ++x)
+	    {
+		if (*pattern_get (pattern, x, y) != pixel)
+		    continue;
+
+		if (*matrix_get (matrix, x, y) > vmax)
+		{
+		    vmax = *matrix_get (matrix, x, y);
+#ifdef USE_OPENMP
+		    vbest = vmax;
+#endif
+		    xbest = x;
+		    ybest = y;
+		}
+	    }
+	}
+
+#ifdef USE_OPENMP
+#pragma omp barrier
+#pragma omp critical
+	{
+	    if (vmax == vbest)
+	    {
+		*xmax = xbest;
+		*ymax = ybest;
+	    }
+	}
+#else
+	*xmax = xbest;
+	*ymax = ybest;
+#endif
+    }
+
+    assert (vmax > -INFINITY);
+}
+
+void
+generate_initial_binary_pattern (pattern_t *pattern, matrix_t *matrix)
+{
+    int xcluster = 0,
+	ycluster = 0,
+	xvoid    = 0,
+	yvoid    = 0;
+
+    for (;;)
+    {
+	largest_cluster (pattern, matrix, TRUE, &xcluster, &ycluster);
+	assert (*pattern_get (pattern, xcluster, ycluster) == TRUE);
+	swap_pixel (pattern, matrix, xcluster, ycluster);
+
+	largest_cluster (pattern, matrix, FALSE, &xvoid, &yvoid);
+	assert (*pattern_get (pattern, xvoid, yvoid) == FALSE);
+	swap_pixel (pattern, matrix, xvoid, yvoid);
+
+	if (xcluster == xvoid && ycluster == yvoid)
+	    return;
+    }
+}
+
+bool_t
+generate_dither_array (array_t *array,
+		       pattern_t const *prototype, matrix_t const *matrix,
+		       pattern_t *temp_pattern, matrix_t *temp_matrix)
+{
+    int width        = prototype->width,
+	height       = prototype->height;
+
+    int x, y, rank;
+
+    int initial_rank = 0;
+
+    if (array->width != width || array->height != height)
+	return FALSE;
+
+    // Make copies of the prototype and associated sizes matrix since we will
+    // trash them
+    if (!pattern_copy (temp_pattern, prototype))
+	return FALSE;
+
+    if (!matrix_copy (temp_matrix, matrix))
+	return FALSE;
+
+    // Compute initial rank
+    for (y = 0; y < height; ++y)
+    {
+	for (x = 0; x < width; ++x)
+	{
+	    if (*pattern_get (temp_pattern, x, y))
+		initial_rank += 1;
+
+	    *array_get (array, x, y) = 0;
+	}
+    }
+
+    // Phase 1
+    for (rank = initial_rank; rank > 0; --rank)
+    {
+	largest_cluster (temp_pattern, temp_matrix, TRUE, &x, &y);
+	swap_pixel (temp_pattern, temp_matrix, x, y);
+	*array_get (array, x, y) = rank - 1;
+    }
+
+    // Make copies again for phases 2 & 3
+    if (!pattern_copy (temp_pattern, prototype))
+	return FALSE;
+
+    if (!matrix_copy (temp_matrix, matrix))
+	return FALSE;
+
+    // Phase 2 & 3
+    for (rank = initial_rank; rank < width * height; ++rank)
+    {
+	largest_cluster (temp_pattern, temp_matrix, FALSE, &x, &y);
+	swap_pixel (temp_pattern, temp_matrix, x, y);
+	*array_get (array, x, y) = rank;
+    }
+
+    return TRUE;
+}
+
+bool_t
+generate (int size, xorwow_state_t *s,
+	  char const *c_filename, char const *ppm_filename)
+{
+    bool_t ok = TRUE;
+
+    pattern_t prototype, temp_pattern;
+    array_t   array;
+    matrix_t  matrix, temp_matrix;
+
+    printf ("Generating %dx%d blue noise...\n", size, size);
+
+    if (!pattern_init (&prototype, size, size))
+	return FALSE;
+
+    if (!pattern_init (&temp_pattern, size, size))
+    {
+	pattern_destroy (&prototype);
+	return FALSE;
+    }
+
+    if (!matrix_init (&matrix, size, size))
+    {
+	pattern_destroy (&temp_pattern);
+	pattern_destroy (&prototype);
+	return FALSE;
+    }
+
+    if (!matrix_init (&temp_matrix, size, size))
+    {
+	matrix_destroy (&matrix);
+	pattern_destroy (&temp_pattern);
+	pattern_destroy (&prototype);
+	return FALSE;
+    }
+
+    if (!array_init (&array, size, size))
+    {
+	matrix_destroy (&temp_matrix);
+	matrix_destroy (&matrix);
+	pattern_destroy (&temp_pattern);
+	pattern_destroy (&prototype);
+	return FALSE;
+    }
+
+    printf("Filling initial binary pattern with white noise...\n");
+    pattern_fill_white_noise (&prototype, .1, s);
+
+    printf("Initializing cluster sizes...\n");
+    if (!compute_cluster_sizes (&prototype, &matrix))
+    {
+	fprintf (stderr, "Error while computing cluster sizes\n");
+	ok = FALSE;
+	goto out;
+    }
+
+    printf("Generating initial binary pattern...\n");
+    generate_initial_binary_pattern (&prototype, &matrix);
+
+    printf("Generating dither array...\n");
+    if (!generate_dither_array (&array, &prototype, &matrix,
+			 &temp_pattern, &temp_matrix))
+    {
+	fprintf (stderr, "Error while generating dither array\n");
+	ok = FALSE;
+	goto out;
+    }
+
+    printf("Saving dither array...\n");
+    if (!array_save (&array, c_filename))
+    {
+	fprintf (stderr, "Error saving dither array\n");
+	ok = FALSE;
+	goto out;
+    }
+
+#if SAVE_PPM
+    if (!array_save_ppm (&array, ppm_filename))
+    {
+	fprintf (stderr, "Error saving dither array PPM\n");
+	ok = FALSE;
+	goto out;
+    }
+#else
+    (void)ppm_filename;
+#endif
+
+    printf("All done!\n");
+
+out:
+    array_destroy (&array);
+    matrix_destroy (&temp_matrix);
+    matrix_destroy (&matrix);
+    pattern_destroy (&temp_pattern);
+    pattern_destroy (&prototype);
+    return ok;
+}
+
+int
+main (void)
+{
+    xorwow_state_t s = {1185956906, 12385940, 983948, 349208051, 901842};
+
+    if (!generate (64, &s, "blue-noise-64x64.h", "blue-noise-64x64.ppm"))
+	return -1;
+
+    return 0;
+}
diff --git a/pixman/pixman-bits-image.c b/pixman/pixman-bits-image.c
index fe3919b..eb8794a 100644
--- a/pixman/pixman-bits-image.c
+++ b/pixman/pixman-bits-image.c
@@ -35,6 +35,7 @@
 #include "pixman-private.h"
 #include "pixman-combine32.h"
 #include "pixman-inlines.h"
+#include "dither/blue-noise-64x64.h"
 
 /* Fetch functions */
 
@@ -1049,6 +1050,13 @@ dest_write_back_narrow (pixman_iter_t *iter)
 }
 
 static const float
+dither_factor_blue_noise_64 (int x, int y)
+{
+    float m = dither_blue_noise_64x64[((y & 0x3f) << 6) | (x & 0x3f)];
+    return m * (1. / 4096.f) + (1. / 8192.f);
+}
+
+static const float
 dither_factor_bayer_8 (int x, int y)
 {
     uint32_t m;
@@ -1168,9 +1176,13 @@ dest_write_back_wide (pixman_iter_t *iter)
     case PIXMAN_DITHER_NONE:
 	break;
 
-    case PIXMAN_DITHER_FAST:
     case PIXMAN_DITHER_GOOD:
     case PIXMAN_DITHER_BEST:
+    case PIXMAN_DITHER_ORDERED_BLUE_NOISE_64:
+	buffer = dither_apply_ordered (iter, dither_factor_blue_noise_64);
+	break;
+
+    case PIXMAN_DITHER_FAST:
     case PIXMAN_DITHER_ORDERED_BAYER_8:
 	buffer = dither_apply_ordered (iter, dither_factor_bayer_8);
 	break;
diff --git a/pixman/pixman.h b/pixman/pixman.h
index 1f63f55..62dfb6a 100644
--- a/pixman/pixman.h
+++ b/pixman/pixman.h
@@ -292,6 +292,7 @@ typedef enum
     PIXMAN_DITHER_GOOD,
     PIXMAN_DITHER_BEST,
     PIXMAN_DITHER_ORDERED_BAYER_8,
+    PIXMAN_DITHER_ORDERED_BLUE_NOISE_64,
 } pixman_dither_t;
 
 typedef enum
diff --git a/test/tolerance-test.c b/test/tolerance-test.c
index 011cdb4..3c6e818 100644
--- a/test/tolerance-test.c
+++ b/test/tolerance-test.c
@@ -79,6 +79,7 @@ static const pixman_op_t operators[] =
 static const pixman_dither_t dithers[] =
 {
     PIXMAN_DITHER_ORDERED_BAYER_8,
+    PIXMAN_DITHER_ORDERED_BLUE_NOISE_64,
 };
 
 #define RANDOM_ELT(array)                                               \
diff --git a/test/utils.c b/test/utils.c
index 9ce7a6c..2e3b547 100644
--- a/test/utils.c
+++ b/test/utils.c
@@ -1193,6 +1193,7 @@ static const dither_entry_t dither_list[] =
      */
 
     ENTRY (ORDERED_BAYER_8),
+    ENTRY (ORDERED_BLUE_NOISE_64),
     ENTRY (NONE),
 
 #undef ENTRY
commit 98b5ec74ca14448349ef6a33a663ad19d446ed6b
Author: Basile Clement <basile-pixman at clement.pm>
Date:   Tue Apr 9 23:29:26 2019 +0200

    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.

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 <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      },
+    { "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 @@
+<?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"><b>Target format:</b></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"><b>Dithering:</b></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>
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()
commit 37d2e681b32b47f66d828fb8697df952472420c8
Author: Basile Clement <basile-pixman at clement.pm>
Date:   Tue Apr 9 23:29:25 2019 +0200

    test: Check the dithering path in tolerance-test
    
    This adds support for testing dithered destinations in tolerance-test.
    When dithering is enabled, the pixel checker allows for an additional
    quantization error.

diff --git a/test/tolerance-test.c b/test/tolerance-test.c
index 320bb7f..011cdb4 100644
--- a/test/tolerance-test.c
+++ b/test/tolerance-test.c
@@ -76,6 +76,11 @@ static const pixman_op_t operators[] =
     PIXMAN_OP_EXCLUSION,
 };
 
+static const pixman_dither_t dithers[] =
+{
+    PIXMAN_DITHER_ORDERED_BAYER_8,
+};
+
 #define RANDOM_ELT(array)                                               \
     (array[prng_rand_n (ARRAY_LENGTH (array))])
 
@@ -176,7 +181,8 @@ verify (int test_no,
         pixman_image_t *orig_dest,
         int x, int y,
         int width, int height,
-	pixman_bool_t component_alpha)
+	pixman_bool_t component_alpha,
+	pixman_dither_t dither)
 {
     pixel_checker_t dest_checker, src_checker, mask_checker;
     int i, j;
@@ -185,6 +191,9 @@ verify (int test_no,
     pixel_checker_init (&dest_checker, dest->bits.format);
     pixel_checker_init (&mask_checker, mask->bits.format);
 
+    if (dest->bits.dither != PIXMAN_DITHER_NONE)
+	pixel_checker_allow_dither (&dest_checker);
+
     assert (dest->bits.format == orig_dest->bits.format);
 
     for (j = y; j < y + height; ++j)
@@ -220,6 +229,7 @@ verify (int test_no,
                 
                 printf ("   operator:         %s (%s alpha)\n", operator_name (op),
 			component_alpha? "component" : "unified");
+		printf ("   dither:           %s\n", dither_name (dither));
                 printf ("   dest_x, dest_y:   %d %d\n", x, y);
                 printf ("   width, height:    %d %d\n", width, height);
                 printf ("   source:           format: %-14s  size: %2d x %2d\n",
@@ -275,6 +285,7 @@ do_check (int i)
     pixman_image_t *dest_copy;
     pixman_bool_t result = TRUE;
     pixman_bool_t component_alpha;
+    pixman_dither_t dither = PIXMAN_DITHER_NONE;
 
     prng_srand (i);
     op = RANDOM_ELT (operators);
@@ -296,6 +307,12 @@ do_check (int i)
     if (y + height > dest->bits.height)
         height = dest->bits.height - y;
 
+    if (prng_rand_n (2))
+    {
+	dither = RANDOM_ELT (dithers);
+	pixman_image_set_dither (dest, dither);
+    }
+
     component_alpha = prng_rand_n (2);
 
     pixman_image_set_component_alpha (mask, component_alpha);
@@ -305,7 +322,8 @@ do_check (int i)
                               x, y, width, height);
 
     if (!verify (i, op, source, mask, dest, dest_copy,
-		 x, y, width, height, component_alpha))
+		 x, y, width, height, component_alpha,
+	         dither))
     {
 	result = FALSE;
     }
diff --git a/test/utils.c b/test/utils.c
index 4eeb068..9ce7a6c 100644
--- a/test/utils.c
+++ b/test/utils.c
@@ -1174,6 +1174,31 @@ static const operator_entry_t op_list[] =
 #undef ALIAS
 };
 
+typedef struct {
+    pixman_dither_t	 dither;
+    const char		*name;
+    pixman_bool_t	 is_alias;
+} dither_entry_t;
+
+static const dither_entry_t dither_list[] =
+{
+#define ENTRY(dither)							\
+    { PIXMAN_DITHER_##dither, "PIXMAN_DITHER_" #dither, FALSE }
+#define ALIAS(dither, nam)							\
+    { PIXMAN_DITHER_##dither, nam, TRUE }
+
+    /* dither_name () will return the first hit in this table,
+     * so keep the list properly ordered between entries and aliases.
+     * Aliases are not listed by list_dithers ().
+     */
+
+    ENTRY (ORDERED_BAYER_8),
+    ENTRY (NONE),
+
+#undef ENTRY
+#undef ALIAS
+};
+
 struct format_entry
 {
     pixman_format_code_t format;
@@ -1382,6 +1407,28 @@ list_operators (void)
     printf ("\n\n");
 }
 
+void
+list_dithers (void)
+{
+    int n_chars;
+    int i;
+
+    printf ("Dithers:\n    ");
+
+    n_chars = 0;
+    for (i = 0; i < ARRAY_LENGTH (dither_list); ++i)
+    {
+        const dither_entry_t *ent = &dither_list[i];
+
+        if (ent->is_alias)
+            continue;
+
+        emit (ent->name, &n_chars);
+    }
+
+    printf ("\n\n");
+}
+
 pixman_op_t
 operator_from_string (const char *s)
 {
@@ -1406,6 +1453,22 @@ operator_from_string (const char *s)
     return PIXMAN_OP_NONE;
 }
 
+pixman_dither_t
+dither_from_string (const char *s)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_LENGTH (dither_list); ++i)
+    {
+        const dither_entry_t *ent = &dither_list[i];
+
+        if (strcasecmp (ent->name, s) == 0)
+            return ent->dither;
+    }
+
+    return PIXMAN_DITHER_NONE;
+}
+
 const char *
 operator_name (pixman_op_t op)
 {
@@ -1438,6 +1501,22 @@ format_name (pixman_format_code_t format)
     return "<unknown format>";
 };
 
+const char *
+dither_name (pixman_dither_t dither)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_LENGTH (dither_list); ++i)
+    {
+	const dither_entry_t *ent = &dither_list[i];
+
+	if (ent->dither == dither)
+	    return ent->name;
+    }
+
+    return "<unknown dither>";
+}
+
 #define IS_ZERO(f)     (-DBL_MIN < (f) && (f) < DBL_MIN)
 
 typedef double (* blend_func_t) (double as, double s, double ad, double d);
@@ -1924,6 +2003,10 @@ round_color (pixman_format_code_t format, color_t *color)
 	color->a = round_channel (color->a, PIXMAN_FORMAT_A (format));
 }
 
+/* The acceptable deviation in units of [0.0, 1.0]
+ */
+#define DEVIATION (0.0128)
+
 /* Check whether @pixel is a valid quantization of the a, r, g, b
  * parameters. Some slack is permitted.
  */
@@ -1992,6 +2075,22 @@ pixel_checker_init (pixel_checker_t *checker, pixman_format_code_t format)
     checker->rw = PIXMAN_FORMAT_R (format);
     checker->gw = PIXMAN_FORMAT_G (format);
     checker->bw = PIXMAN_FORMAT_B (format);
+
+    checker->ad = DEVIATION;
+    checker->rd = DEVIATION;
+    checker->gd = DEVIATION;
+    checker->bd = DEVIATION;
+}
+
+/* When dithering is enabled, we allow one extra pixel of tolerance
+ */
+void
+pixel_checker_allow_dither (pixel_checker_t *checker)
+{
+    checker->ad += 1 / (double)((1 << checker->aw) - 1);
+    checker->rd += 1 / (double)((1 << checker->rw) - 1);
+    checker->gd += 1 / (double)((1 << checker->gw) - 1);
+    checker->bd += 1 / (double)((1 << checker->bw) - 1);
 }
 
 static void
@@ -2085,7 +2184,7 @@ convert (double v, uint32_t width, uint32_t mask, uint32_t shift, double def)
 }
 
 static void
-get_limits (const pixel_checker_t *checker, double limit,
+get_limits (const pixel_checker_t *checker, double sign,
 	    color_t *color,
 	    int *ao, int *ro, int *go, int *bo)
 {
@@ -2101,23 +2200,23 @@ get_limits (const pixel_checker_t *checker, double limit,
 	color = &tmp;
     }
     
-    *ao = convert (color->a + limit, checker->aw, checker->am, checker->as, 1.0);
-    *ro = convert (color->r + limit, checker->rw, checker->rm, checker->rs, 0.0);
-    *go = convert (color->g + limit, checker->gw, checker->gm, checker->gs, 0.0);
-    *bo = convert (color->b + limit, checker->bw, checker->bm, checker->bs, 0.0);
+    *ao = convert (color->a + sign * checker->ad,
+		   checker->aw, checker->am, checker->as, 1.0);
+    *ro = convert (color->r + sign * checker->rd,
+		   checker->rw, checker->rm, checker->rs, 0.0);
+    *go = convert (color->g + sign * checker->gd,
+		   checker->gw, checker->gm, checker->gs, 0.0);
+    *bo = convert (color->b + sign * checker->bd,
+		   checker->bw, checker->bm, checker->bs, 0.0);
 }
 
-/* The acceptable deviation in units of [0.0, 1.0]
- */
-#define DEVIATION (0.0128)
-
 void
 pixel_checker_get_max (const pixel_checker_t *checker, color_t *color,
 		       int *am, int *rm, int *gm, int *bm)
 {
     pixel_checker_require_uint32_format(checker);
 
-    get_limits (checker, DEVIATION, color, am, rm, gm, bm);
+    get_limits (checker, 1, color, am, rm, gm, bm);
 }
 
 void
@@ -2126,7 +2225,7 @@ pixel_checker_get_min (const pixel_checker_t *checker, color_t *color,
 {
     pixel_checker_require_uint32_format(checker);
 
-    get_limits (checker, - DEVIATION, color, am, rm, gm, bm);
+    get_limits (checker, - 1, color, am, rm, gm, bm);
 }
 
 pixman_bool_t
diff --git a/test/utils.h b/test/utils.h
index e5ac945..701417f 100644
--- a/test/utils.h
+++ b/test/utils.h
@@ -219,15 +219,23 @@ list_formats (void);
 void
 list_operators (void);
 
+void list_dithers (void);
+
 pixman_op_t
 operator_from_string (const char *s);
 
+pixman_dither_t
+dither_from_string (const char *s);
+
 const char *
 operator_name (pixman_op_t op);
 
 const char *
 format_name (pixman_format_code_t format);
 
+const char *
+dither_name (pixman_dither_t dither);
+
 typedef struct
 {
     double r, g, b, a;
@@ -250,12 +258,16 @@ typedef struct
     uint32_t am, rm, gm, bm;
     uint32_t as, rs, gs, bs;
     uint32_t aw, rw, gw, bw;
+    float ad, rd, gd, bd;
 } pixel_checker_t;
 
 void
 pixel_checker_init (pixel_checker_t *checker, pixman_format_code_t format);
 
 void
+pixel_checker_allow_dither (pixel_checker_t *checker);
+
+void
 pixel_checker_split_pixel (const pixel_checker_t *checker, uint32_t pixel,
 			   int *a, int *r, int *g, int *b);
 
commit ddcc41b999562efdd9f88daa51ffbf39782748b5
Author: Basile Clement <basile-pixman at clement.pm>
Date:   Sun Apr 21 00:03:43 2019 +0200

    Implement basic dithering for the wide pipeline, v3
    
    This patch implements dithering in pixman.  A "dither" property is added
    to BITS images, which is used to:
    
     - Force rendering to the image to go through the floating point
       pipeline.  Note that this is different from FAST_PATH_NARROW_FORMAT
       as it should not enable the floating point pipeline when reading from
       the image.
    
     - Enable dithering in dest_write_back_wide.  The dithering uses the
       destination format to determine noise amplitude.
    
    This does not change pixman's behavior when dithering is disabled (the
    default).
    
    Additional types and functions are added to the public API:
    
     - The `pixman_dither_t` enum exposes the available dithering methods.
       Currently a single dithering method based on 8x8 Bayer matrices is
       implemented (PIXMAN_DITHER_ORDERED_BAYER_8).  The PIXMAN_DITHER_FAST,
       PIXMAN_DITHER_GOOD and PIXMAN_DITHER_BEST aliases are provided and
       should be used to benefit from future specializations.
    
     - The `pixman_image_set_dither` function allows to set the dithering
       method to use when rendering to a bits image.
    
     - The `pixman_image_set_dither_offset` function allows to set a
       vertical and horizontal offsets for the dither matrix.  This can be
       used after scrolling to ensure a consistent spatial positioning of
       the dither matrix.
    
    Changes since previous version (v2):
     - linear_gradient_is_horizontal optimization is still compatible with
       the wide pipeline.  The code disabling it was a remnant of a previous
       patch which performed dithering directly inside linear_get_scanline,
       and thus needed to be called independently for each scanline.
    
    Changes since v1:
     - Renamed PIXMAN_DITHER_BAYER_8 to PIXMAN_DITHER_ORDERED_BAYER_8
     - Disable dithering for channels with 32bpp or more (since they can
       represent exactly the wide values already).  This makes the patches
       compatible with the newly added floating point format.
    
    Dithering is compatible with linear_gradient_is_horizontal

diff --git a/pixman/pixman-bits-image.c b/pixman/pixman-bits-image.c
index 7bc2ba8..fe3919b 100644
--- a/pixman/pixman-bits-image.c
+++ b/pixman/pixman-bits-image.c
@@ -1048,6 +1048,112 @@ dest_write_back_narrow (pixman_iter_t *iter)
     iter->y++;
 }
 
+static const float
+dither_factor_bayer_8 (int x, int y)
+{
+    uint32_t m;
+
+    y ^= x;
+
+    /* Compute reverse(interleave(xor(x mod n, y mod n), x mod n))
+     * Here n = 8 and `mod n` is the bottom 3 bits.
+     */
+    m = ((y & 0b001) << 5) | ((x & 0b001) << 4) |
+	((y & 0b010) << 2) | ((x & 0b010) << 1) |
+	((y & 0b100) >> 1) | ((x & 0b100) >> 2);
+
+    /* m is in range [0, 63].  We scale it to [0, 63.0f/64.0f], then
+     * shift it to to [1.0f/128.0f, 127.0f/128.0f] so that 0 < d < 1.
+     * This ensures exact values are not changed by dithering.
+     */
+    return (float)(m) * (1 / 64.0f) + (1.0f / 128.0f);
+}
+
+typedef float (* dither_factor_t)(int x, int y);
+
+static force_inline float
+dither_apply_channel (float f, float d, float s)
+{
+    /* float_to_unorm splits the [0, 1] segment in (1 << n_bits)
+     * subsections of equal length; however unorm_to_float does not
+     * map to the center of those sections.  In fact, pixel value u is
+     * mapped to:
+     *
+     *       u              u              u               1
+     * -------------- = ---------- + -------------- * ----------
+     *  2^n_bits - 1     2^n_bits     2^n_bits - 1     2^n_bits
+     *
+     * Hence if f = u / (2^n_bits - 1) is exactly representable on a
+     * n_bits palette, all the numbers between
+     *
+     *     u
+     * ----------  =  f - f * 2^n_bits = f + (0 - f) * 2^n_bits
+     *  2^n_bits
+     *
+     *  and
+     *
+     *    u + 1
+     * ---------- = f - (f - 1) * 2^n_bits = f + (1 - f) * 2^n_bits
+     *  2^n_bits
+     *
+     * are also mapped back to u.
+     *
+     * Hence the following calculation ensures that we add as much
+     * noise as possible without perturbing values which are exactly
+     * representable in the target colorspace.  Note that this corresponds to
+     * mixing the original color with noise with a ratio of `1 / 2^n_bits`.
+     */
+    return f + (d - f) * s;
+}
+
+static force_inline float
+dither_compute_scale (int n_bits)
+{
+    // No dithering for wide formats
+    if (n_bits == 0 || n_bits >= 32)
+	return 0.f;
+
+    return 1.f / (float)(1 << n_bits);
+}
+
+static const uint32_t *
+dither_apply_ordered (pixman_iter_t *iter, dither_factor_t factor)
+{
+    bits_image_t        *image  = &iter->image->bits;
+    int                  x      = iter->x + image->dither_offset_x;
+    int                  y      = iter->y + image->dither_offset_y;
+    int                  width  = iter->width;
+    argb_t              *buffer = (argb_t *)iter->buffer;
+
+    pixman_format_code_t format = image->format;
+    int                  a_size = PIXMAN_FORMAT_A (format);
+    int                  r_size = PIXMAN_FORMAT_R (format);
+    int                  g_size = PIXMAN_FORMAT_G (format);
+    int                  b_size = PIXMAN_FORMAT_B (format);
+
+    float a_scale = dither_compute_scale (a_size);
+    float r_scale = dither_compute_scale (r_size);
+    float g_scale = dither_compute_scale (g_size);
+    float b_scale = dither_compute_scale (b_size);
+
+    int   i;
+    float d;
+
+    for (i = 0; i < width; ++i)
+    {
+	d = factor (x + i, y);
+
+	buffer->a = dither_apply_channel (buffer->a, d, a_scale);
+	buffer->r = dither_apply_channel (buffer->r, d, r_scale);
+	buffer->g = dither_apply_channel (buffer->g, d, g_scale);
+	buffer->b = dither_apply_channel (buffer->b, d, b_scale);
+
+	buffer++;
+    }
+
+    return iter->buffer;
+}
+
 static void
 dest_write_back_wide (pixman_iter_t *iter)
 {
@@ -1057,6 +1163,19 @@ dest_write_back_wide (pixman_iter_t *iter)
     int             width  = iter->width;
     const uint32_t *buffer = iter->buffer;
 
+    switch (image->dither)
+    {
+    case PIXMAN_DITHER_NONE:
+	break;
+
+    case PIXMAN_DITHER_FAST:
+    case PIXMAN_DITHER_GOOD:
+    case PIXMAN_DITHER_BEST:
+    case PIXMAN_DITHER_ORDERED_BAYER_8:
+	buffer = dither_apply_ordered (iter, dither_factor_bayer_8);
+	break;
+    }
+
     image->store_scanline_float (image, x, y, width, buffer);
 
     if (image->common.alpha_map)
@@ -1172,6 +1291,9 @@ _pixman_bits_image_init (pixman_image_t *     image,
     image->bits.height = height;
     image->bits.bits = bits;
     image->bits.free_me = free_me;
+    image->bits.dither = PIXMAN_DITHER_NONE;
+    image->bits.dither_offset_x = 0;
+    image->bits.dither_offset_y = 0;
     image->bits.read_func = NULL;
     image->bits.write_func = NULL;
     image->bits.rowstride = rowstride;
diff --git a/pixman/pixman-general.c b/pixman/pixman-general.c
index 6141cb0..7d74f98 100644
--- a/pixman/pixman-general.c
+++ b/pixman/pixman-general.c
@@ -141,7 +141,8 @@ general_composite_rect  (pixman_implementation_t *imp,
     if ((src_image->common.flags & FAST_PATH_NARROW_FORMAT)		     &&
 	(!mask_image || mask_image->common.flags & FAST_PATH_NARROW_FORMAT)  &&
 	(dest_image->common.flags & FAST_PATH_NARROW_FORMAT)		     &&
-	!(operator_needs_division (op)))
+	!(operator_needs_division (op))                                      &&
+	(dest_image->bits.dither == PIXMAN_DITHER_NONE))
     {
 	width_flag = ITER_NARROW;
 	Bpp = 4;
diff --git a/pixman/pixman-image.c b/pixman/pixman-image.c
index 7a851e2..461164a 100644
--- a/pixman/pixman-image.c
+++ b/pixman/pixman-image.c
@@ -684,6 +684,41 @@ pixman_image_set_repeat (pixman_image_t *image,
     image_property_changed (image);
 }
 
+PIXMAN_EXPORT void
+pixman_image_set_dither (pixman_image_t *image,
+			 pixman_dither_t dither)
+{
+    if (image->type == BITS)
+    {
+	if (image->bits.dither == dither)
+	    return;
+
+	image->bits.dither = dither;
+
+	image_property_changed (image);
+    }
+}
+
+PIXMAN_EXPORT void
+pixman_image_set_dither_offset (pixman_image_t *image,
+				int             offset_x,
+				int             offset_y)
+{
+    if (image->type == BITS)
+    {
+	if (image->bits.dither_offset_x == offset_x &&
+	    image->bits.dither_offset_y == offset_y)
+	{
+	    return;
+	}
+
+	image->bits.dither_offset_x = offset_x;
+	image->bits.dither_offset_y = offset_y;
+
+	image_property_changed (image);
+    }
+}
+
 PIXMAN_EXPORT pixman_bool_t
 pixman_image_set_filter (pixman_image_t *      image,
                          pixman_filter_t       filter,
diff --git a/pixman/pixman-private.h b/pixman/pixman-private.h
index 1bd9695..41f430d 100644
--- a/pixman/pixman-private.h
+++ b/pixman/pixman-private.h
@@ -180,6 +180,10 @@ struct bits_image
     uint32_t *                 free_me;
     int                        rowstride;  /* in number of uint32_t's */
 
+    pixman_dither_t            dither;
+    uint32_t                   dither_offset_y;
+    uint32_t                   dither_offset_x;
+
     fetch_scanline_t           fetch_scanline_32;
     fetch_pixel_32_t	       fetch_pixel_32;
     store_scanline_t           store_scanline_32;
diff --git a/pixman/pixman.h b/pixman/pixman.h
index d644589..1f63f55 100644
--- a/pixman/pixman.h
+++ b/pixman/pixman.h
@@ -287,6 +287,15 @@ typedef enum
 
 typedef enum
 {
+    PIXMAN_DITHER_NONE,
+    PIXMAN_DITHER_FAST,
+    PIXMAN_DITHER_GOOD,
+    PIXMAN_DITHER_BEST,
+    PIXMAN_DITHER_ORDERED_BAYER_8,
+} pixman_dither_t;
+
+typedef enum
+{
     PIXMAN_FILTER_FAST,
     PIXMAN_FILTER_GOOD,
     PIXMAN_FILTER_BEST,
@@ -826,6 +835,11 @@ pixman_bool_t   pixman_image_set_transform           (pixman_image_t
 						      const pixman_transform_t     *transform);
 void            pixman_image_set_repeat              (pixman_image_t               *image,
 						      pixman_repeat_t               repeat);
+void            pixman_image_set_dither              (pixman_image_t               *image,
+						      pixman_dither_t               dither);
+void            pixman_image_set_dither_offset       (pixman_image_t               *image,
+						      int                           offset_x,
+						      int                           offset_y);
 pixman_bool_t   pixman_image_set_filter              (pixman_image_t               *image,
 						      pixman_filter_t               filter,
 						      const pixman_fixed_t         *filter_params,


More information about the xorg-commit mailing list