diff --git a/.gitignore b/.gitignore
index 5598044f1..60b6e53d5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -222,6 +222,13 @@ $RECYCLE.BIN/
# Files created by make in various subdirectories
.dirstamp
+# ===========================
+# POV-Ray meta-build detritus
+# ===========================
+
+# Byte-compiled python modules
+*.pyc
+
# =====================
# POV-Ray Miscellaneous
# =====================
diff --git a/changes.txt b/changes.txt
index dcf28c518..0fb73a930 100644
--- a/changes.txt
+++ b/changes.txt
@@ -69,6 +69,14 @@ Changed Behaviour
- The `defined()` pseudo-function now returns `true` (while printing a
warning) if applied to reserved words. The `#ifdef` and `#ifndef` directives
also behave accordingly.
+ - The dithering implementation has been modified, and may produce slightly
+ different results for otherwise identical scenes.
+ - The PGM (greyscale variant of PPM) output gamma handling now matches that
+ of regular PPM, honoring `File_Gamma` defaulting to ITU-R BT.709. To get
+ linear greyscale output, explicitly specify `File_Gamma=1.0`.
+ - Greyscale output no longer automatically forces bit depth to 16 bpc. To get
+ 16 bpp greyscale output, explicitly specify `Bits_Per_Color=16`.
+ - Preview now reflects greyscale setting.
New Features
------------
@@ -86,6 +94,13 @@ New Features
the list of control points.
- The `matrix` syntax now allows allows for a trailing comma at the end of
the list of coefficients.
+ - File formats supporting variable bit depths (PNG and PPM/PGM) now allow for
+ bit depths as low as 1 bit per colour channel. (1-bit greyscale PPM/PGM will
+ still be written as PGM, not PBM.)
+ - Command-line option `+F` now allows specifying both the `G` greyscale flag
+ and the bit depth.
+ - Support for blue noise dithering has been added, plus a couple more error
+ diffusion dithering filters.
Performance Improvements
------------------------
diff --git a/distribution/platform-specific/windows/Help/povray.chm b/distribution/platform-specific/windows/Help/povray.chm
index 6056d9f78..9db9f8318 100644
Binary files a/distribution/platform-specific/windows/Help/povray.chm and b/distribution/platform-specific/windows/Help/povray.chm differ
diff --git a/distribution/scenes/output/dither_showcase.pov b/distribution/scenes/output/dither_showcase.pov
new file mode 100644
index 000000000..33b767129
--- /dev/null
+++ b/distribution/scenes/output/dither_showcase.pov
@@ -0,0 +1,118 @@
+// POV-Ray 3.8 Scene File "dither_showcase.pov"
+// author: Christoph Lipka
+// date: 2018-09-30
+//
+//--------------------------------------------------------------------------
+#version 3.8;
+
+#ifndef (Glow)
+ #declare Glow = on;
+#end
+#ifndef (Brightness)
+ #declare Brightness = 4.0;
+#end
+
+global_settings {
+ max_trace_level 5
+ assumed_gamma 1.0
+ radiosity {
+ pretrace_start 0.08
+ pretrace_end 0.01
+ count 150
+ nearest_count 20
+ error_bound 0.5
+ recursion_limit 2
+ low_error_factor .5
+ gray_threshold 0.0
+ minimum_reuse 0.015
+ brightness 1
+ adc_bailout 0.01/2
+ }
+}
+
+#default {
+ texture {
+ pigment {rgb 1}
+ finish {
+ ambient 0.0
+ diffuse 0.8
+ specular albedo 1.0 roughness 0.001
+ reflection { 1.0 fresnel on }
+ conserve_energy
+ fresnel on
+ }
+ }
+}
+
+// ----------------------------------------
+
+#local TestRed = <1.0,.03,.03>;
+#local TestGreen = <.03,1.0,.03>;
+#local TestBlue = <.03,.03,1.0>;
+
+#local CameraFocus = <0,1,1>;
+#local CameraDist = 8;
+#local CameraDepth = 3.0;
+#local CameraTilt = 5;
+
+camera {
+ location <0,0,0>
+ direction z*CameraDepth
+ right x*image_width/image_height
+ up y
+ translate <0,0,-CameraDist>
+ rotate x*CameraTilt
+ translate CameraFocus
+}
+
+#macro LightSource(Pos,Color)
+ light_source {
+ Pos
+ color Color
+ area_light x*vlength(Pos)/10, y*vlength(Pos)/10, 9,9 adaptive 1 jitter circular orient
+ }
+
+#end
+
+LightSource(<-500,500,-500>, rgb Brightness)
+
+// ----------------------------------------
+
+plane {
+ y, 0
+ texture { pigment { color rgb 0.2 } }
+ interior { ior 1.5 }
+}
+
+#macro TestSphere(Pos,Radius,TargetColor,Hole)
+ #if (Hole)
+ union {
+ #local Th = 20;
+ #local R = 0.05;
+ #local SinTh = sin(Th*pi/180);
+ #local CosTh = cos(Th*pi/180);
+ difference {
+ sphere { <0,0,0>, 1 }
+ cylinder { y, y*(1-R)*CosTh, SinTh }
+ cylinder {-y,-y*(1-R)*CosTh, SinTh }
+ cylinder { y,-y,(1-R)*SinTh-R }
+ }
+ torus { (1-R)*SinTh, R translate y*(1-R)*CosTh }
+ torus { (1-R)*SinTh, R translate -y*(1-R)*CosTh }
+ #else
+ sphere { <0,0,0>, 1
+ #end
+ texture { pigment { color TargetColor }
+ finish { emission Glow * Brightness * 0.5 }
+ }
+ interior { ior 1.5 }
+ rotate z*30
+ rotate y*clock*360 - y*45
+ scale Radius
+ translate Pos + y*Radius
+ }
+#end
+
+TestSphere(<-2,0,1>, 1, TestRed, false)
+TestSphere(< 0,0,1>, 1, TestBlue, true)
+TestSphere(< 2,0,1>, 1, TestGreen, false)
diff --git a/distribution/scenes/gamma/gamma_showcase.pov b/distribution/scenes/output/gamma_showcase.pov
similarity index 100%
rename from distribution/scenes/gamma/gamma_showcase.pov
rename to distribution/scenes/output/gamma_showcase.pov
diff --git a/distribution/scenes/previews/gamma/gamma_showcase.jpg b/distribution/scenes/previews/output/gamma_showcase.jpg
similarity index 100%
rename from distribution/scenes/previews/gamma/gamma_showcase.jpg
rename to distribution/scenes/previews/output/gamma_showcase.jpg
diff --git a/doc/html/r3_0.html b/doc/html/r3_0.html
index 3b0a14a4d..60d275290 100644
--- a/doc/html/r3_0.html
+++ b/doc/html/r3_0.html
@@ -289,19 +289,37 @@
In version 3.7 the display preview window used Bayer 4x4 dithering. A Change in version 3.8 has switched that to the recently added blue noise pattern.
+Dithering works for all file formats except JPEG (where dithering would be counter-productive) and OpenEXR (which provides sufficient precision to make dithering obsolete). These file formats simply ignore the setting.
As POV-Ray has evolved from version 1.0 through to today we have made every
-effort to maintain some amount of backwards compatibility with earlier
-versions. Some old or obsolete features can be handled directly without any
-special consideration by the user. Some old or obsolete features can no
-longer be handled at all. However some old features can still be
-used if you warn POV-Ray that this is an older scene. In the POV-Ray scene
-language you can use the #version
directive to switch version
-compatibility to different settings. See section The #version Directive
-for more details about the language version directive.
-Additionally you may use the Version=
n.n option or the
-+MV
n.n switch to establish the initial
-setting. For example one feature introduced in 2.0 that was incompatible with
-any 1.0 scene files is the parsing of float expressions. Setting
-Version=1.0
or using +MV1.0
turns off expression parsing
-as well as many warning messages so that nearly all 1.0 files will still
-work. Naturally the default setting for this option is the current version number.
-
-The version directive and command-line setting no longer provide compatibility with most rendering bugs in versions prior to POV-Ray 3.5. However, compatibility with the scene language is provided for scenes as old as POV-Ray 1.0 just as in all previous versions of POV-Ray. Nevertheless, we strongly recommend you update scenes at least to POV-Ray 3.5 syntax if you plan to use them in future versions of POV-Ray.
+
2.3.5.1 Pigments
-
Every surface must have a color. In POV-Ray this color is called a pigment
.
-It does not have to be a single color. It can be a color pattern, a color
-list or even an image map. Pigments can also be layered one on top of the next
-so long as the uppermost layers are at least partially transparent so the ones
-beneath can show through. Let's play around with some of these kinds of
+
Every surface must have a color. In POV-Ray this color is called a pigment
. It does not have to be a single color. It can be a color pattern, a color list or even an image map. Pigments can also be layered one on top of the next so long as the uppermost layers are at least partially transparent so the ones beneath can show through. See also: Using Transparent Pigments and Layered Textures
+
Let's play around with some of these kinds of
pigments.
We create a file called texdemo.pov
and edit it as
@@ -8938,7 +8935,7 @@
2.3.8.5 Performance considerations
If average to good quality radiosity will work for your scene, then it's probably a good idea to spend the time to find the sweet spot that strikes the best balance between quality and speed. Especially recursion_limit
should be kept as low as possible. Sometimes 1
is sufficient, if not 2
or 3
should often be enough.
-
With high quality settings, radiosity data can take quite a lot of memory. Apart from that the other scene data is also used much more intensively than in a conventional scene. Therefore insufficient memory and swapping can slow down things even more. Here's a few radiosity options that might help.
+
With high quality settings, radiosity data can take quite a lot of memory. Apart from that the other scene data is also used much more intensively than in a conventional scene. Therefore insufficient memory and swapping can slow down things even more. Here's a few radiosity options that might help.
Finally the scene geometry and textures are important too. Objects not visible in the camera usually only increase parsing time and memory use, but in a radiosity scene, also objects behind the camera can slow down the rendering process. See the section Configuring Radiosity for some helpful hints.
diff --git a/source/backend/control/benchmark_ini.cpp b/source/backend/control/benchmark_ini.cpp
index 42809cdf3..c683f3d7d 100644
--- a/source/backend/control/benchmark_ini.cpp
+++ b/source/backend/control/benchmark_ini.cpp
@@ -8,7 +8,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -37,7 +37,7 @@
/// @attention
/// **DO NOT EDIT THIS FILE!**
/// Instead, edit `distribution/scenes/advanced/benchmark/benchmark.ini`,
-/// and re-generate this file as described in `tools/meta-make/readme.md`.
+/// and re-generate this file as described in @ref tools-metamake (`tools/meta-make/readme.md`).
namespace pov {
diff --git a/source/backend/control/benchmark_ini.h b/source/backend/control/benchmark_ini.h
index 920047f91..18f4c1721 100644
--- a/source/backend/control/benchmark_ini.h
+++ b/source/backend/control/benchmark_ini.h
@@ -8,7 +8,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -37,7 +37,7 @@
/// @attention
/// **DO NOT EDIT THIS FILE!**
/// Instead, edit `distribution/scenes/advanced/benchmark/benchmark.ini`,
-/// and re-generate this file as described in `tools/meta-make/readme.md`.
+/// and re-generate this file as described in @ref tools-metamake (`tools/meta-make/readme.md`).
namespace pov {
diff --git a/source/backend/control/benchmark_pov.cpp b/source/backend/control/benchmark_pov.cpp
index 9833ef53a..09f019823 100644
--- a/source/backend/control/benchmark_pov.cpp
+++ b/source/backend/control/benchmark_pov.cpp
@@ -8,7 +8,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -37,7 +37,7 @@
/// @attention
/// **DO NOT EDIT THIS FILE!**
/// Instead, edit `distribution/scenes/advanced/benchmark/benchmark.pov`,
-/// and re-generate this file as described in `tools/meta-make/readme.md`.
+/// and re-generate this file as described in @ref tools-metamake (`tools/meta-make/readme.md`).
namespace pov {
diff --git a/source/backend/control/benchmark_pov.h b/source/backend/control/benchmark_pov.h
index 072437853..63d16b5d1 100644
--- a/source/backend/control/benchmark_pov.h
+++ b/source/backend/control/benchmark_pov.h
@@ -8,7 +8,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -37,7 +37,7 @@
/// @attention
/// **DO NOT EDIT THIS FILE!**
/// Instead, edit `distribution/scenes/advanced/benchmark/benchmark.pov`,
-/// and re-generate this file as described in `tools/meta-make/readme.md`.
+/// and re-generate this file as described in @ref tools-metamake (`tools/meta-make/readme.md`).
namespace pov {
diff --git a/source/base/data/bluenoise64a.cpp b/source/base/data/bluenoise64a.cpp
new file mode 100644
index 000000000..44540b9d8
--- /dev/null
+++ b/source/base/data/bluenoise64a.cpp
@@ -0,0 +1,307 @@
+//******************************************************************************
+///
+/// @file base/data/bluenoise64a.cpp
+///
+/// Blue noise pattern data
+/// Auto-generated using metagen-bluenoise.py.
+///
+/// @copyright
+/// @parblock
+///
+/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
+///
+/// POV-Ray is free software: you can redistribute it and/or modify
+/// it under the terms of the GNU Affero General Public License as
+/// published by the Free Software Foundation, either version 3 of the
+/// License, or (at your option) any later version.
+///
+/// POV-Ray is distributed in the hope that it will be useful,
+/// but WITHOUT ANY WARRANTY; without even the implied warranty of
+/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+/// GNU Affero General Public License for more details.
+///
+/// You should have received a copy of the GNU Affero General Public License
+/// along with this program. If not, see
.
+///
+/// ----------------------------------------------------------------------------
+///
+/// POV-Ray is based on the popular DKB raytracer version 2.12.
+/// DKBTrace was originally written by David K. Buck.
+/// DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
+///
+/// @endparblock
+///
+//******************************************************************************
+
+/// @file
+/// @attention
+/// **DO NOT EDIT THIS FILE!**
+/// Instead, if this file needs fixing, modify metagen-bluenoise.py
+/// or its invocation in `tools/meta-make/Makefile` accordingly,
+/// and re-generate this file as described in @ref tools-metamake (`tools/meta-make/readme.md`).
+
+namespace pov_base {
+
+/// 64x64 blue noise pattern data.
+/// Generated using `make-bluenoise.py -i0.1 -s1.5 -r4711`.
+extern const unsigned short kBlueNoise64a[4096] = {
+ 654,3898,1137,1753,3811,1459,3503, 344,3083, 922,2108, 719,3676,1953, 87,3726,
+ 2596, 270, 948,2403,3763,3375,1777,3090,1566,3363,1925,1334,3282, 493,3858,1076,
+ 3696,1732, 818, 190,1272,1781, 734,2105,1329,4032,2240,3226,1463,1069,2384,4054,
+ 1276,2162,3697,1490,2381, 576,4083, 352,2019, 634,2677, 142,2170,2562, 992,3207,
+ 2458,1507,3424,2623, 259,2374,2036,1147,2438, 67,3218,2702, 489,2934, 879,2277,
+ 3346,1580,3588,1890, 429,1242, 632,2585, 94, 841,2789, 201,1023,2580,1508,2773,
+ 412,2959,3443,2337,3585,2601,3194, 172,2906,1047, 250,1710,3690,2705,1969, 383,
+ 2781,1666,3243, 283,1183,2692,1554,3108,1144,3909,1758,3506,3093,1892,4007, 162,
+ 3001, 883,2057, 559,3288, 848,4053,2857,3597,1487,3963,1784,1202,2408,4060,1345,
+ 635,2097,2936, 770,2788,2249,3867,1386,3614,2131,4048,1693,3525,2251, 807,3295,
+ 2144,1203,1562, 336,2002, 926,3954,1628,3553,2615,1952,3038, 659, 4,3126, 942,
+ 3609, 553,2555, 804,3780,2056,3483, 3,2333,2837, 846,1336, 384, 782,1607,2152,
+ 3573, 362,3742,2932,1328,1836, 138,1622, 582,1031,2319, 308,3545,3184, 375,1832,
+ 3107, 134,1145,4026,1486,3250, 369,1991,2946,1155,2469, 477,3011,3785, 152,1789,
+ 4087, 680,2644,3821,3070,1454, 415,2310, 860, 518,3828,1200,2262,3927,1592,3376,
+ 2029,1210,3918,1838,3158, 486, 961,1724,3720, 457,3278,2272,3822,2811,3379,1117,
+ 1842,2709,1633,1012,2487,3649,3122,2575,2066,3350,2982, 825,1595,2127,1072,2767,
+ 3703,2520,3407,2183, 253,2533,1044,3468, 697, 280,3270,1446, 713,2006,1371,2900,
+ 316,3554,1874,1086, 615,2744,3390,1928,2941,1397,2463,3408, 876,2604,1292, 251,
+ 2439,2987, 85,2294,1365,2814,2440,3025,1402,2063,1073,2652,1509, 206,2372, 563,
+ 4086, 61,2293,3910, 298,2151, 756,1229,3920, 173,1871,3817,2666, 5,3891, 741,
+ 1398, 490,1717, 703,3037,1821,3941,1539,2672,1882,3852,2174,2765,1118,3470,2461,
+ 959,2188,3142, 199,2362,3671,1236, 50,3750,3203, 223,1664, 371,1900,2834,3771,
+ 745,1649,3348,1016,3570, 205,3979, 682,3414, 136,4045, 590,1954,3717,1273,3212,
+ 791,1420,3385, 630,3022,1524,3410, 473,2762,1414,2368, 546,1332,3434,2392,1958,
+ 3308,2287,3838,1287,3658, 899, 40,2300,3152,1253, 858,3516, 78,3950, 451,3223,
+ 1627,2719,1315,3948,1605,2024, 816,2531,1759,1071,2089,4069,3047,3623, 514,2221,
+ 1135,4043,2614, 531,1608,2208,1893,1157,2724,2406,1773,3035,3425, 947,2928,2095,
+ 2648,3112,1175,1897,2620, 987,3801,1787,3183, 869,3681,2844, 991,3082,1639, 246,
+ 2898, 984, 109,2563,2058,2775,3388, 585,3774, 220,2992,1526,2442,1763,2284, 777,
+ 3864, 7, 723,3413,2831, 262,3306,3868, 505,2823, 760,2336,1432,1042,3296,1744,
+ 2911, 303,1999,3128,3787, 757,3289, 402,1576,3595, 837,1343, 388,2429, 133,1570,
+ 442,2256,3832, 208,3555,2110, 35,2530,2198, 354,3402,1815,2165, 439,3651,1240,
+ 3994,1798,3120,3539, 381,1493,1085,2168,1703,2573,1990, 387,3344,1048,3003,1375,
+ 2090,3524,1841,2485, 925,1437,2962,2227,1577,3279,3679, 120,2745, 665,2501, 64,
+ 3582,1505, 891,2449,1244,2849,2347,3887,2938, 233,2225,3912,2768,1700,3845,3482,
+ 1857,1000,2840,1653, 829,2979,1449,1054,3965,1660,1248, 105,4091,2512, 884,2690,
+ 591,2345,1443, 862,1902,3224,4090,2860, 827,3552,1167,4016,2656, 544,3747, 327,
+ 2856,1098,3067, 453,2163,4030, 619,1149, 307,2480,1255,1803,3497,2040,3978,1281,
+ 2180,3215,3729, 376,1848, 42,1458, 976,1978,1216,3117, 568,2012,3256,1193, 733,
+ 3645,3181, 373,2427,4035, 506,3258,2697, 674,3042,2576,3195, 720,1549,3479,2102,
+ 3287, 329,2865,3900, 621,2424, 140,1349, 463,3118,2318, 740,1482,1946,3236,1585,
+ 2522, 629,3650,1297,3239,1720,2606,3620,1987,3916, 898,2897, 452,1569,3148, 819,
+ 2730, 475,1121,2671,4011,3024,3562, 494,3381,2649,3692,1548, 985, 293,2231,2735,
+ 188,2120,1399,3421,1146,2285,1687,3490, 313,2076, 957,3699,1907,2861, 288,1350,
+ 1713,3778,1156,2126,2721,1616,3474,2075,3792,1537, 52,2832,3472, 915,2266, 145,
+ 4089,1768,2322, 112,3758, 335, 977,3077,1412, 171,3180,2149,3783,1106, 257,2953,
+ 1686,1972,3333,2202, 743,1617,2506,2114, 775,1755, 90,2475,3536,2967,3990,1521,
+ 1061,3808, 589,2722,1975, 117,3728,1279,2404,3878,1445, 492,2359,1132,3892,3058,
+ 689,2471, 24,3522, 946, 306,3010,1043,2590,1822,3664,2128, 396,3901,2738,1232,
+ 3341, 855,2780,1462,2053,2912,2360, 539,3386,2685,1671, 655,2569,3298,2331,3705,
+ 646,3923,1337, 310,3445,1084, 198,3665,3063,1179,4061, 735,2107,1359, 605,2505,
+ 2994,1647,3238, 895,3882,2955, 802,1937,3099, 139,1794,2944,3340, 71,2257, 920,
+ 3594,1983,3225,1325,2913,1863,3972, 602,3214, 787,1142,3023,1381,1807, 532,3040,
+ 1981, 408,3804,3166, 705,1190,3544,1791,2195, 831,4040,1322, 28,1917,1465,1001,
+ 2629, 81,2405,3106,1805,3818,2855,1394,2329, 401,1905,2884,3276, 149,1866,3432,
+ 2210, 65,2484,1796, 311,1525,2470, 533,1090,3465,2640, 847,3644,1536,2782,1840,
+ 403,2633,1572, 507,3737,2466,1433,2211, 163,3881,2477, 274,3365,2584,1015,3528,
+ 1501,2426,1063,1746,2595,3999, 58,1342,3691, 358,2378,3463,2883,3872, 438,3389,
+ 2027,3619,1600, 873,2653, 556,2004, 861,3859,3419,2521,1511,1008,3895,2760, 857,
+ 526,4085,1079,3513,2177,3114,3596,2776,4055,2137, 414,1270,2010, 612,4044,1316,
+ 3145,1038,3985,2288, 842, 351,3325,1176,2871,1955,3505,1662, 673,3806,2346, 14,
+ 3942,2852, 241,3394, 487,1993,3263,2787,1004,3008,1963,1081,1734, 835,2212,2981,
+ 1211, 716,2805,4075,1246,2302,3320,1682, 33,1299, 649,3730, 360,2364,1282,3575,
+ 1930,2910,1434,2634, 661,1300, 911, 240,1408,1674,3138,3775,2564,3044, 304,2421,
+ 3674, 130,2801,1908,3444,1646,2641,3713, 515,1477, 924,2255,2956,1223,1891, 808,
+ 2078,1251,3655,2199, 969,1512,2417, 622,1631,3813, 143,3248, 572,2720,3592, 278,
+ 1775,3213, 423,2071, 132,3672, 455,2559,2902,3208,2176,2696,1696,3159, 248,1541,
+ 3303, 788,3735, 411,3220,3958,1853,2344,3312, 672,2385, 263, 971,1621,3338, 747,
+ 2129,1506, 595,1233,3021, 88, 951,1765,2376,3100,3969, 97,3318, 426,2774,3446,
+ 3071, 636,1644,2684,3819,2990, 267,3476,2118,2612,1274,2434,4003,1444,1014,2534,
+ 3940,2369,1347,3372,2927,1573,1119,3964,1970, 936, 433,3383,1150,2074,3810,2627,
+ 2194, 150,2366,1676,2026, 13,2689,3638,1066,2917,3896,1812,3557,2273,1140,1864,
+ 3449,2975,3888,2515,3603,2142,4067,3217, 225, 779,2736,1373,2092,1707,4050,1439,
+ 343,2380,3253, 114, 742,1305,1827,4082, 806, 434,3543,1830, 322,2083,3124,1601,
+ 116, 953,3764,1829, 833,2474,3119, 666,1468,3635,1814,4029, 784,2893, 597,1049,
+ 3988,1348,2838,3496,1101,3018,1491, 523,2166, 164,1340, 761,2833, 6,3944,2594,
+ 275, 940,1716, 389,1096, 626,2791,1324,2041,3535,1088,2456,3715, 724,1035,2535,
+ 1859,3877,1138,1995,3423,2540,3125,1107,2750,1531,3052, 928,2799,3741, 503,3428,
+ 2007,3032, 584,2687, 333,3493,2161, 151,2432,2802, 353,1417,2532, 53,3234,1783,
+ 379,3364, 902, 536,3879,2422, 859,3827,1745,3511,2525,1986,3267,1452, 603,2919,
+ 1296,3762,2274,3316,1977,1559,2460, 328,3793,1665, 521,3435, 244,2694,3169, 74,
+ 3533, 880,2741,1552,3951, 499,2187, 45,3371,2309,3915, 166,2218,1358, 817,2592,
+ 1171,3633,2283,1441,4005,1051,1747,3833,3219,1161,2214,3113,1951,3500,1519,2459,
+ 3095,1883,2578,1594,2125, 319,3357,2793,1154,3081, 428,4006, 990,2358,3732,1685,
+ 2119, 687,2660, 57,2873,3719,3401, 901,3002,2268,2825,1429,1843,2236,3781,1335,
+ 2153, 538,3186, 204,2301, 972,1709,3710,1341, 522,1738,1168,3351,2939,1839,3830,
+ 374,1657,2877, 18,1982,2942, 472,1393, 799,3485, 195,3794, 865,1205,3694, 728,
+ 1083,3917, 174,3537,3155,1263,1935, 102,2308, 732,1540,2699, 349,1895,3087, 156,
+ 3567,3173,1470,3991, 772,1256, 482,1885,1206, 22,3886, 828,3074,1128, 574,1699,
+ 3039,2455,3709,1314,2940,3510,2676, 696,2035,3257,2622,3608, 647,2445, 69,3177,
+ 2209, 718,3499, 909,3307,2382,3593,2727,2093,1715, 650,2915,2340, 338,2783,2112,
+ 2961,1406,2259,1011, 599,2625,4093,1612,3586,3244,2064,3698,1277,3461, 776,1189,
+ 2481, 436,1074,1846,2391,3132,2167,4025,3232,2599,2015,3349, 305,3986,2772,3480,
+ 269,1022,1767, 678,1945, 364,1214,4001,2885, 939, 242,1961,1534,4034, 986,1376,
+ 2733,3966,1222,2537,1604, 651,1195, 128,3085,3874,2626,1362,1835,4049,1578, 107,
+ 3751, 541,2755,3662,1774,2998, 821, 398,2558,1007, 527,2984, 177,2430,2820,1642,
+ 3904,2039,2991,3498, 229,1619,2734, 281,1496,1017, 537,1565,2473,1898, 908,2084,
+ 1410,4063,3322,2607,3866,3216,2339, 127,1618,2393,3829,2976, 440,2172,3416,1730,
+ 289,1904,3249, 227,3718,1881,4095,2328,1476, 407,1068,3581,3162, 578,3384,2524,
+ 830,3292,1956, 393,1451,2330,3714,2042,1331,3885,1809,2289,1478,4051,1967, 321,
+ 3259, 903,2710, 592,3857, 966,3634, 715,2315,3814,3457,2916,1275,3661, 86,2418,
+ 2845, 483,2246, 10,1457, 793,1778,3060,3478,1369, 676,1109,3227,2668, 765,3752,
+ 3054, 606,2276,1383,2966, 361,3149, 943,3429,2011,2415, 11, 904,2206,1185,1865,
+ 1533,2411,1221,3939,3409, 181,1162,3105,2743, 23,3484, 795,3189, 581,1027,3504,
+ 1436, 103,1669,2143,2568,1391,1971,3324,2822,1779, 126,2106, 700,3079,1609,3824,
+ 789,3129,1635,1093,2914,2132,3767,1032, 488,2786,2192,3622,1431, 125,2003,1280,
+ 2523,1070,3897,2698, 785,2082,2608,1689, 691,2901,3975,1742,2609,3027,3889, 332,
+ 3618,3088, 44,2835, 759,2579,1858, 577,3355,1543,2447,1177,2715,3724,2157,2899,
+ 2492,3744,3146,1207,3366, 47,3056, 464,1241, 823,2542,3591,1104,2611, 551,3370,
+ 1260,1957,3518,3805, 548,3284, 218,2603,1966,4066, 191,1770,2510,3930,2890, 365,
+ 3546,2060, 75,1663,3412,1087,3856, 95,3653,1294, 479,3266,1495, 217,2033,2737,
+ 607,2103, 933,2220,1650,3206,4019, 954,2226, 366,3967,1899, 157,1691, 377,1269,
+ 693,1903, 458,4070, 749,2377,1694,3765,2160,3955,1466,3193, 279,4028,1833,2232,
+ 159,2631, 370,2286,2729,1683,1231,3571,1545, 889,2949,3395, 706,1037,1712,3290,
+ 1538, 832,3104,3648, 448,2478,1428,3221,1933,2701,2264,1124,3711, 812,3415,1065,
+ 4084,1718,3378,3797,1278, 427,2059,1357,3578,2874, 653,3055,3540,2597,3241,3981,
+ 2252,3013,1097,2769,2008,3613,1024,2598, 176,2964, 580,1735,2321,1338,3229, 930,
+ 3748,3026,1009,1418, 780,3914,2416, 613,3168,2281,1258, 331,2086,3111,2304, 611,
+ 4057,2804,2353,1239,1994,3030, 639,2314, 921, 264,3551, 669,2808,1786,2443,1423,
+ 2904, 491,2488, 238,2706,3670,3009, 141,2548,1737,1080,2130,1346, 722,1013,1568,
+ 146,3404,1750, 210,1352,2989, 604,1544,3487,1136,2022,3643, 834,2908, 470,2529,
+ 1500,1797,3977,3331,1926, 325,2954,2050, 56,3860,2628,3565,1528,3738, 15,2593,
+ 1166, 236,1817, 677,3953, 202,3514,1726,4020,2985,1636,2052,3943, 437,3191, 123,
+ 1973,1198,3179,1558,1041, 712,2312,1589, 844,3770,3200, 430,3919,2431,1948,3616,
+ 2717, 867,3893,2536,3501, 350,3971,2282,3187, 395,2842,2476, 16,3816,1949,3447,
+ 243, 686,2395, 79,2777,3617,1108,3368,1415, 758,1847, 465,2792, 800,1351,3488,
+ 2030,3178,3745,2544,1598,2821,1178,2661, 368,1310,3405, 60,2557,1165,2193,3766,
+ 2658, 815,3906,2038,3515,1844,3313,3908,2819, 254,2361,1535,2779, 38,3045, 557,
+ 2350,1442,2147, 623,1629,2100,2713, 838,1845,1401,4010,1005,3326,1483,1060,2815,
+ 2219,3663,2935,1191,1560,2215, 863,1751,3680,2335,3062,1082,2182,3922,1795,2997,
+ 885,1479, 330, 982,3403,2135, 767,3740,2021,2389, 988,3157,1510,3492, 570,1591,
+ 3076, 165,2388, 573,2829, 32,1304, 529,1164,1867,3343, 874,3657,1740,3448,1257,
+ 3757, 256,3286,2867, 980,3237,1228, 168,3721,2386, 681,1698,2164,2714, 593,4088,
+ 1354, 850,2014,3274, 617,4033,2669, 454,2826, 299,4012,1634,3188, 185,2399, 413,
+ 3862,2204,3532,2965,1377, 39,3211,1620, 586,2887,3846, 729,1901,2824, 932,3626,
+ 1268,1799,3265,1379,3809,2577,3139,2099,2541,3572, 567,2203,1194, 385,2122, 843,
+ 2790,1922,1173,3577, 21,3759,1906,2957,3377, 334,2797,3161, 224,3556,1854, 296,
+ 3233,1704, 394,3563,2482, 170,3141,1992,1226,3314, 941,2566, 664,3430,1181,2817,
+ 727,2621,1752, 530,2420,4094,2600,1078,3549, 167,1411,2527, 265,3993,2292, 315,
+ 2554,4038, 989,2158, 339, 905,1530,4092, 207,2988,1419,2862,3980,2511,3293,1502,
+ 4047, 525,2587,1757,2263, 663,2553,1522,1036,2104,1283,3685, 918,2496,1266,2996,
+ 2338,3844,2650, 974,1819,1364, 778,3803,1574,2248, 135,1950,1384,3707,2091,1571,
+ 3321, 89,1192,3659, 912,1869, 450,2081,3121,1739,2229,3466,3046,1312,1711,3360,
+ 2094, 683,2847,3700,1725,3393,2316, 684,1741, 923,3784, 92,1690, 711,2983, 178,
+ 2247,3175, 809,3850,1407,3096,4068, 469,2462,3870,1820, 550,1582,3928,2085, 670,
+ 1125, 55,1499,3084,3902,2139,3442,2436, 566,3576,2971,3839,2723, 481,3072, 952,
+ 4015,1927,2880,2223,3285,1481,3786,2813, 781,3974,1120, 616,2018, 836,2739, 498,
+ 3589,1515, 113,2401, 555,2950,1115,3632,3156,2379,1989,2647,1094,3471,1909,1025,
+ 3628,1637, 302,2895,1100, 226,1785, 919,3462, 99,3078,2667,3418, 131,3150,2582,
+ 3693,3334,2254, 657, 359,2763,1089, 25,2695,1163,1695, 397,1045,2351,1808, 213,
+ 2433,1396,3782, 300, 737,3017, 148,1306,2367, 290,2726,3683, 12,3163,3840,1131,
+ 3012,1918,3242,1295,3559,2016, 73,2700, 417,1250,3367, 337,3686,2305, 519,2746,
+ 1321,2539,2047,3441,2326,3636,2659,2138,2843,1438, 803,2317,1143,1911, 866,1471,
+ 460,1714,2848,1245,3606,1624,3268,1941,4081, 714,3436,2123,3251,3949, 753,3167,
+ 3568, 575,1057,2686,1708,2490,3426,1924,3624,1584,3199,1825,2489,1460,2239, 219,
+ 2503, 955,3924, 746,2618,1610,3899,1382,1880,4008, 645,1632,2881,1361,3854,3196,
+ 66,3959, 978, 501,1602, 752,3247,1267, 540,3612,2017,4023, 444,2809,3863,3387,
+ 2055, 801,4024,1913,2486, 273, 894,3005,1405,2363,2846,1313, 144,1513,2638,1188,
+ 2140,2930,3361,2043,4000,1213, 554, 934,2655, 420,1224, 690,4021, 962,3450,1680,
+ 3673, 431,2196,2970, 291, 983,2234,3300, 852,2444,3051,2116, 907, 245,1702,2178,
+ 774,1824,2665,3109,3826,1965, 54,3970,1721,3153, 249,1611,3086,1293,2370, 222,
+ 2662,3136, 106,1019,2958,3894,2241,3460, 447,1856, 295,3756,3015,1920,3667, 461,
+ 1722, 258,1523, 856, 26,3526,2155,3143,3956,2245,3398,2892,2045, 445,2816, 704,
+ 1235,2732,1450,1816,3788,3197, 596,2866,3598, 378,1201,3749,3311,2514,2973,1148,
+ 3604,3339,1455, 192,1172,2479,2951,1059,2190,2617, 906,2441,3716, 644,1728, 958,
+ 3802,1311,2413,3534, 608,1372,1749, 771,2602,3861,1010,2502, 658, 965,3315,2365,
+ 4074,2565,3706,3057,2390,2841,1597, 184,1327, 783,1780, 101,3566,1302,2173,3115,
+ 1943,3997, 34,3508,1158,2465,1754, 137,2044,1599,2708, 30,1818, 707,4072, 345,
+ 2423, 562,2096,2876,3475,1542, 600,3382, 340,3796,1366,3400, 59,2169,3521,3006,
+ 1889, 545,1615,3255,1996,2757, 169,3599,1220,2929,1588,3547,2068,2752, 68,1425,
+ 762,1141,1979, 638,1409,1002,3769,1877,2968,2509,3825, 999,2643,1651,3795, 189,
+ 2451,3262, 878,2324, 356,2785,1333,3883, 997,3245,3968,2275,1064,3464,1529,1939,
+ 2803,1254,3983, 424, 896,2280,3849,2742,1625, 709,2926,1896,1127,2872,1430, 301,
+ 2528,3600,2185, 268,1180,3789,2299,3205,2020, 84,2260, 363,1374,3933,1782,3165,
+ 2859,3509, 214,3202,3945, 405,2267, 695,3637, 284,1448,3073,2235, 348,3337,1034,
+ 610,1668,3019,2031,3323, 797,3530,2186,2549, 692,1390, 513,3103,2642, 180,3656,
+ 935,3277,1638,2552,3687,1862, 237,1111,2013,3294,2323, 466,4077,2491, 738,3903,
+ 1091,3091, 887,4058,2907, 380, 944,1564, 625,4037,3328,3031, 583,3431,1056,2222,
+ 516,1826,2681,2181,1623,2574,3362,2748,1113,3246,1998, 561,4078, 810,2909,1498,
+ 2703,3733, 485,1380,4079,1658, 508,3049, 221,1697,2886,3669,2037,1247,2352,2986,
+ 633,2261, 36,3075,1326, 662,3176,2493,3938, 100,1262,3602, 931,1788,3309,2046,
+ 1581, 8,2670,1480,1834,2500,3089,3682,2718,1092,1802, 868,2635,2349, 286,3702,
+ 1492,3869,1264, 913,3625, 77,1317,2034, 478,1656,2467,3358,1290,1870,2303,3890,
+ 400,1878, 981,2556, 118,2863,1934,1238,3746,3380,1915, 111, 864,3934, 462,1467,
+ 3873,1800,3491,1053,2028,2818,3550,1440, 853,2978,1692,2610,3140, 186, 558,2798,
+ 3689,2279,3417, 699,3621, 471,1339,1964, 216,2428,1473,3768,1929,1249,3053, 854,
+ 2545, 110,3297, 542,2800,1769, 813,3989,2882,3779, 914, 175,2766,3627, 63,1151,
+ 2115,3352,2952,3610,2243,1067,3853,2313, 748,2632,1062,2402,3254,1672,3439,2070,
+ 1129,2728, 441,2446,4064, 187,1733, 502,2148,3458, 390,2073,1469,3851,2355,1289,
+ 824, 410,1938,1259,3192,2216,3937, 736,2977,3560, 459,2836, 29,3976,1688,3517,
+ 2001,2980,2343,4052,2061,3080,3456,2306, 231,1344,3486,2156, 652,1593,3092,3512,
+ 2651, 235,1261, 694,1575,3299, 252,3135,1555, 409,4014,1360,2850, 601,2678, 161,
+ 3101,3754, 822,1586,3151, 975,2357,3776,2753,1126,4002, 717,2878,1050,1884,3473,
+ 3007,3926,2538,2889, 247,1029,1723,3342,1218,2077, 820,3272,2175, 701,2740, 425,
+ 1187, 675,1563,1028, 277,1400, 565,1075,1873,2591,2999,1727,3952,2517, 927,1422,
+ 3998,1652,2397,3884,1968, 547,2526, 963,3574,2145,3028, 266,1944,3772,1052,2398,
+ 399,1356,2217,3611, 571,2680,1303,3281, 129,1860,2452,3353, 49,3736,2583, 314,
+ 1547,1018,1679, 624,3837,2400,2810, 91,2560,3995,1748,1152,3615,1550,3174,2271,
+ 3800,3269,1931,3523,2472,3807,2704,3204,3640, 750, 419,1105,3201, 260,2242, 552,
+ 3050, 875,3171, 1,2778,3502,1389,1872,2691, 656,1736,3629, 840,2233,1489,4042,
+ 3335,1940,2931, 70,1811,3907,2054, 754,1553,3134, 960,1404,2197,1684, 637,3261,
+ 2228, 83,3538,2080,3291,1435, 679,3678,1546, 391,3123,2645, 194,2499, 937, 276,
+ 1456,2613, 153,2851, 786,1705,2146, 2,1497,2407,3841,2000,1388,3548,2875,1849,
+ 342,2072,3639,1134,2290, 796,4036,2969, 82,3834,1139,2508,3133, 0,2828,1776,
+ 500, 996,2572,3459,1197,3004, 346,3569,2619,3848, 285,3020,3579, 890,2853,1298,
+ 4013,2683,2993,1217, 182,1886,3098,2121,1077,2375, 640,3842,1387,2005,4046,3481,
+ 1806, 618,3962,1323,3137, 347,1196,4031,2830, 964,3391, 104,2639, 668,1026,3688,
+ 1252,2636, 614,1504,3271,1760, 309,1225,2332,3332,1532, 372,3467,1301,3607, 744,
+ 3066,3731,1527, 641,2311, 917,1677,2224,1208, 628,2341,1959, 406,2396,3777,1985,
+ 524,1766, 814,3727,2495, 916,4080, 324,3427,2974,1852,1003,3304, 456,2796,1237,
+ 3068,2325, 970,2065,3695,2356,3489,1921, 579,2258,1587,3043,2184,4041,1516,2468,
+ 3154,3911,1910,2905, 421,3723,2113,3160, 764,1916,2869, 929,1810,2654,2154,1160,
+ 2454, 326,2117,3992,2707,3198,3799, 209,3452,2894,1626,4065,1182,3190, 115,1030,
+ 3519,2419,1472, 382,3369,2879,1320,2588,1643, 46,3668,2265,2921,1654, 768,2133,
+ 43,3433,2664, 435,1484, 725,2960,1058,3230, 294,3654,1199, 367,1801,3397, 122,
+ 1661, 938, 200,3454,2464, 998,2751,1461,3605, 484,3996,2412,3743, 642, 272,3932,
+ 1855,3283,1288, 154,1831, 480,1392,2497,1912, 973,3327, 512,2794,1837,1447,2657,
+ 3069, 203,3843,2141,1675, 467,2253, 731,3558,2712,1355, 805, 179,3704,2637,3880,
+ 1110,1614,3798,1861,3347,2716, 147,1670,3865,2624,1876, 643,3264,2771, 849,2269,
+ 2870,3561,2334,1234,1645, 627,3855, 211,2581,1099,2101, 108,1385,3014,3336,1579,
+ 2749, 773,2945,3437, 870,3641,2922, 708,3957, 17,1403,2516, 798,3815,3422, 588,
+ 2088,1243,3182,2761,1020,3921,3102,1914,1116, 497,3961,1988,3374,2354,1416, 509,
+ 3147, 702,2963, 215, 950,4004,2023,2437,1363, 888,2948,3790,2425,1291,3647, 535,
+ 1378,1976, 726,4071,2947,3319,1894,2320,3235,1567,2924,3411, 851,1919,2348,1006,
+ 196,3831,2200,1603,2605,1974,1095,2171,3116,2674,1792,3675,2159, 234,2371,1681,
+ 4039, 871,1850, 660,3477,1424, 212,3734,2450,3231,1706,3016,1130, 320,1851,3527,
+ 2414,1960,1309,2238,2547,1227,3130, 620,3396, 51,2136,1046,1648, 197,2048,3973,
+ 48,3280,2688, 392,2067, 93,1318, 949, 418,3812, 631,1762,2679,4076, 476,3630,
+ 1426,2518, 422,1170,4059, 96,3302,1556, 474,1133,3392, 357,3059,1308, 956,2920,
+ 282,3564,2630, 9,2435,2062,2864, 826,1561, 155,2297, 587,2589,4073, 886,2839,
+ 160,3936,3275, 495,3739,1772, 292,3652,1590,2570,4062, 404,3494,3094,2673, 995,
+ 2453,1756,1102,3708,1518,2586,3925,3455,2756,2191,1212,3677, 228,1112,2087,2972,
+ 3273,1771,3531, 667,3034,2327, 386,3725,2383,3871,2069, 872,1667,3947,3310,2546,
+ 1427,2201,3260,1613,3684, 517,1169,3373,2675,3875, 968,3660,1520,2150,3097,1230,
+ 1659, 766,2693,1551,3453, 839,2764,2244, 792,3048,1215,1823,2298, 609,1474,3420,
+ 3836, 564,3170,2270, 811,3029, 534,1606,1936, 19,3041,2394,3228,1655,2571, 755,
+ 62,1039,2858,2111,1284,3584,1813,1021,2795, 648,1368,2854,2498, 121,1875, 698,
+ 3722, 416, 910,1265,3000,1793,4056,2179, 432,1962,1367,2747, 41,3301, 520,2009,
+ 3666,2307,1103, 20,2079,3061,1174,3913,1942, 496,3587,2807, 900,3755,1923, 323,
+ 2124,1583,2806, 239,3542,1204,2387,3172, 739,4022,1453, 881, 543,3601,1319,3905,
+ 1879,2410,3753, 232,1640,2663, 751,3131,1947, 76,3580,3252, 560,3646,2278,1114,
+ 2943,2032,2711,3835, 688,2543, 255,1464,3209, 710,3033,3495,1804, 845,3847,2784,
+ 312,3345,2923,4017,2513, 528,1503, 119,3440,2448,1413, 317,3210,2494,1153,2937,
+ 3330,1285, 893,4018,1673,2025, 158,3701,1055,2230,3356,1828,2868,2134, 297,2725,
+ 3469,1421, 790,3240,3931, 504,3451,1475,4027,2561,1701,2189,1209,1557,3064, 341,
+ 3935,1678,3399, 124,2237,3529, 897,2812,3820,2342,1122, 318,2507,1286,2250,1488,
+ 994,1790, 594,1307,1719,3760,3305,2616,1729,1033,3984,2109,1630, 80,3960, 763,
+ 355,3642,2551,2213, 510,3406,2918,1353,2550, 446,2770, 183,3946, 979,3317, 671,
+ 2207, 449,2759,1984,1123,2483,2098, 287,1184,2995, 945, 271,3982, 769,3438,2646,
+ 1370, 569,1040,1980,1494,3144,1159,1888, 72,1596,3590,1997,3929,2925, 193,3507,
+ 2519,3823,2051,3164, 230, 967,2205, 730,3185, 261,2933, 794,3354,2758,1761,2291,
+ 2827,1932, 31,3110, 993,2682, 685,1887,3876,1641,3520,1271,2457,1485,3065,1731,
+ 1186,3987,3127,1514, 27,2896, 892,3791,2295, 549,3712,2409,2888,1764,2049, 37,
+ 2296,3222,2567,4009,2891, 443,3773,2504,3329, 598,2731, 882, 468,3359,1743, 721,
+ 3036, 98, 877,2754,3541,1868,2903,1330,3631,2373,1517,3761,1219, 511,3583,1395,
+ };
+
+}
diff --git a/source/base/data/bluenoise64a.h b/source/base/data/bluenoise64a.h
new file mode 100644
index 000000000..2e250ceae
--- /dev/null
+++ b/source/base/data/bluenoise64a.h
@@ -0,0 +1,48 @@
+//******************************************************************************
+///
+/// @file base/data/bluenoise64a.h
+///
+/// Blue noise pattern data
+/// Auto-generated using metagen-bluenoise.py.
+///
+/// @copyright
+/// @parblock
+///
+/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
+///
+/// POV-Ray is free software: you can redistribute it and/or modify
+/// it under the terms of the GNU Affero General Public License as
+/// published by the Free Software Foundation, either version 3 of the
+/// License, or (at your option) any later version.
+///
+/// POV-Ray is distributed in the hope that it will be useful,
+/// but WITHOUT ANY WARRANTY; without even the implied warranty of
+/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+/// GNU Affero General Public License for more details.
+///
+/// You should have received a copy of the GNU Affero General Public License
+/// along with this program. If not, see
.
+///
+/// ----------------------------------------------------------------------------
+///
+/// POV-Ray is based on the popular DKB raytracer version 2.12.
+/// DKBTrace was originally written by David K. Buck.
+/// DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
+///
+/// @endparblock
+///
+//******************************************************************************
+
+/// @file
+/// @attention
+/// **DO NOT EDIT THIS FILE!**
+/// Instead, if this file needs fixing, modify metagen-bluenoise.py
+/// or its invocation in `tools/meta-make/Makefile` accordingly,
+/// and re-generate this file as described in @ref tools-metamake (`tools/meta-make/readme.md`).
+
+namespace pov_base {
+
+extern const unsigned short kBlueNoise64a[4096];
+
+}
diff --git a/source/base/font/crystal.cpp b/source/base/font/crystal.cpp
index 26ca51cf1..9f8ad2849 100644
--- a/source/base/font/crystal.cpp
+++ b/source/base/font/crystal.cpp
@@ -8,7 +8,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -37,7 +37,7 @@
/// @attention
/// **DO NOT EDIT THIS FILE!**
/// Instead, edit `distribution/include/crystal.ttf`,
-/// and re-generate this file as described in `tools/meta-make/readme.md`.
+/// and re-generate this file as described in @ref tools-metamake (`tools/meta-make/readme.md`).
namespace pov_base {
diff --git a/source/base/font/crystal.h b/source/base/font/crystal.h
index 2dd10735e..1ec1da1e6 100644
--- a/source/base/font/crystal.h
+++ b/source/base/font/crystal.h
@@ -8,7 +8,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -37,7 +37,7 @@
/// @attention
/// **DO NOT EDIT THIS FILE!**
/// Instead, edit `distribution/include/crystal.ttf`,
-/// and re-generate this file as described in `tools/meta-make/readme.md`.
+/// and re-generate this file as described in @ref tools-metamake (`tools/meta-make/readme.md`).
namespace pov_base {
diff --git a/source/base/font/cyrvetic.cpp b/source/base/font/cyrvetic.cpp
index ef0b6cdec..4d3d75aa6 100644
--- a/source/base/font/cyrvetic.cpp
+++ b/source/base/font/cyrvetic.cpp
@@ -8,7 +8,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -37,7 +37,7 @@
/// @attention
/// **DO NOT EDIT THIS FILE!**
/// Instead, edit `distribution/include/cyrvetic.ttf`,
-/// and re-generate this file as described in `tools/meta-make/readme.md`.
+/// and re-generate this file as described in @ref tools-metamake (`tools/meta-make/readme.md`).
namespace pov_base {
diff --git a/source/base/font/cyrvetic.h b/source/base/font/cyrvetic.h
index 497f53ee5..a914f9880 100644
--- a/source/base/font/cyrvetic.h
+++ b/source/base/font/cyrvetic.h
@@ -8,7 +8,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -37,7 +37,7 @@
/// @attention
/// **DO NOT EDIT THIS FILE!**
/// Instead, edit `distribution/include/cyrvetic.ttf`,
-/// and re-generate this file as described in `tools/meta-make/readme.md`.
+/// and re-generate this file as described in @ref tools-metamake (`tools/meta-make/readme.md`).
namespace pov_base {
diff --git a/source/base/font/povlogo.cpp b/source/base/font/povlogo.cpp
index 73535804a..7ad3721a0 100644
--- a/source/base/font/povlogo.cpp
+++ b/source/base/font/povlogo.cpp
@@ -8,7 +8,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -37,7 +37,7 @@
/// @attention
/// **DO NOT EDIT THIS FILE!**
/// Instead, edit `distribution/include/povlogo.ttf`,
-/// and re-generate this file as described in `tools/meta-make/readme.md`.
+/// and re-generate this file as described in @ref tools-metamake (`tools/meta-make/readme.md`).
namespace pov_base {
diff --git a/source/base/font/povlogo.h b/source/base/font/povlogo.h
index a5fcf8ca2..bb2794835 100644
--- a/source/base/font/povlogo.h
+++ b/source/base/font/povlogo.h
@@ -8,7 +8,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -37,7 +37,7 @@
/// @attention
/// **DO NOT EDIT THIS FILE!**
/// Instead, edit `distribution/include/povlogo.ttf`,
-/// and re-generate this file as described in `tools/meta-make/readme.md`.
+/// and re-generate this file as described in @ref tools-metamake (`tools/meta-make/readme.md`).
namespace pov_base {
diff --git a/source/base/font/timrom.cpp b/source/base/font/timrom.cpp
index 0235458de..ae4e2e3e3 100644
--- a/source/base/font/timrom.cpp
+++ b/source/base/font/timrom.cpp
@@ -8,7 +8,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -37,7 +37,7 @@
/// @attention
/// **DO NOT EDIT THIS FILE!**
/// Instead, edit `distribution/include/timrom.ttf`,
-/// and re-generate this file as described in `tools/meta-make/readme.md`.
+/// and re-generate this file as described in @ref tools-metamake (`tools/meta-make/readme.md`).
namespace pov_base {
diff --git a/source/base/font/timrom.h b/source/base/font/timrom.h
index 64e83c20a..f7aed3bec 100644
--- a/source/base/font/timrom.h
+++ b/source/base/font/timrom.h
@@ -8,7 +8,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -37,7 +37,7 @@
/// @attention
/// **DO NOT EDIT THIS FILE!**
/// Instead, edit `distribution/include/timrom.ttf`,
-/// and re-generate this file as described in `tools/meta-make/readme.md`.
+/// and re-generate this file as described in @ref tools-metamake (`tools/meta-make/readme.md`).
namespace pov_base {
diff --git a/source/base/image/dither.cpp b/source/base/image/dither.cpp
new file mode 100644
index 000000000..d1fc259cb
--- /dev/null
+++ b/source/base/image/dither.cpp
@@ -0,0 +1,429 @@
+//******************************************************************************
+///
+/// @file base/image/dither.cpp
+///
+/// Implementations related to image dithering.
+///
+/// @copyright
+/// @parblock
+///
+/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
+///
+/// POV-Ray is free software: you can redistribute it and/or modify
+/// it under the terms of the GNU Affero General Public License as
+/// published by the Free Software Foundation, either version 3 of the
+/// License, or (at your option) any later version.
+///
+/// POV-Ray is distributed in the hope that it will be useful,
+/// but WITHOUT ANY WARRANTY; without even the implied warranty of
+/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+/// GNU Affero General Public License for more details.
+///
+/// You should have received a copy of the GNU Affero General Public License
+/// along with this program. If not, see
.
+///
+/// ----------------------------------------------------------------------------
+///
+/// POV-Ray is based on the popular DKB raytracer version 2.12.
+/// DKBTrace was originally written by David K. Buck.
+/// DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
+///
+/// @endparblock
+///
+//******************************************************************************
+
+// Unit header file must be the first file included within POV-Ray *.cpp files (pulls in config)
+#include "base/image/dither.h"
+
+// Standard C++ header files
+#include
+
+// POV-Ray header files (base module)
+#include "base/data/bluenoise64a.h"
+
+// this must be the last file included
+#include "base/povdebug.h"
+
+namespace pov_base
+{
+
+//*******************************************************************************
+
+void NoDither::GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt)
+{
+ offLin.clear();
+ offQnt.clear();
+}
+
+//*******************************************************************************
+
+class OrderedDither::Pattern
+{
+public:
+ template Pattern(unsigned int size, const T* raw, unsigned int rank);
+ template Pattern(unsigned int size, const T* raw);
+ Pattern(std::initializer_list> raw);
+ ~Pattern();
+ inline unsigned int Size() const { return mSize; }
+ inline const ColourChannel& operator[](size_t i) const { return maData[i]; }
+ const ColourChannel& operator()(unsigned int x, unsigned int y) const;
+private:
+ unsigned int mSize;
+ ColourChannel* maData;
+};
+
+template
+OrderedDither::Pattern::Pattern(unsigned int size, const T* raw, unsigned int rank) :
+ mSize(size),
+ maData(nullptr)
+{
+ auto flatSize = size_t(mSize)*size_t(mSize);
+ maData = new ColourChannel[flatSize];
+ ColourChannel invRank = 1.0 / rank;
+ for (unsigned int i = 0; i < flatSize; ++i)
+ {
+ POV_ASSERT(raw[i] >= 0);
+ POV_ASSERT(raw[i] < rank);
+ maData[i] = ColourChannel(raw[i] + 0.5) * invRank - 0.5;
+ }
+}
+
+template
+OrderedDither::Pattern::Pattern(unsigned int size, const T* raw) :
+ Pattern(size, raw, size_t(size)*size_t(size))
+{}
+
+OrderedDither::Pattern::Pattern(std::initializer_list> raw) :
+ mSize(raw.size()),
+ maData(nullptr)
+{
+ auto flatSize = size_t(mSize)*size_t(mSize);
+ maData = new ColourChannel[flatSize];
+ auto rank = flatSize;
+ ColourChannel invRank = 1.0 / rank;
+ ColourChannel* pCoeff = maData;
+ for (auto&& rawRow : raw)
+ {
+ POV_ASSERT(rawRow.size() == mSize);
+ for (auto&& rawCoeff : rawRow)
+ {
+ POV_ASSERT(rawCoeff < rank);
+ *(pCoeff++) = ColourChannel(rawCoeff + 0.5) * invRank - 0.5;
+ }
+ }
+}
+
+OrderedDither::Pattern::~Pattern()
+{
+ if (maData != nullptr)
+ delete[] maData;
+}
+
+const ColourChannel& OrderedDither::Pattern::operator()(unsigned int x, unsigned int y) const
+{
+ return (*this)[wrap(y, mSize) * mSize + wrap(x, mSize)];
+}
+
+//------------------------------------------------------------------------------
+
+OrderedDither::OrderedDither(const Pattern& pattern, unsigned int width, bool invertRB) :
+ mPattern(pattern),
+ mImageWidth(width),
+ mInvertRB(invertRB)
+{}
+
+void OrderedDither::GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt)
+{
+ offLin.clear();
+ ColourChannel off = mPattern(x, y);
+ offQnt.red = (mInvertRB ? -off : off);
+ offQnt.green = off;
+ offQnt.blue = (mInvertRB ? -off : off);
+ offQnt.alpha = off;
+}
+
+//*******************************************************************************
+
+void DiffusionDither1D::GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt)
+{
+ if (x == 0)
+ offLin.clear();
+ else
+ offLin = lastErr;
+ offQnt.clear();
+}
+
+void DiffusionDither1D::SetError(unsigned int x, unsigned int y, const ColourOffset& err)
+{
+ lastErr = err;
+}
+
+//*******************************************************************************
+
+SierraLiteDither::SierraLiteDither(unsigned int width) :
+ imageWidth(width),
+ maErr(new ColourOffset[width+2])
+{}
+
+SierraLiteDither::~SierraLiteDither()
+{
+ delete[] maErr;
+}
+
+void SierraLiteDither::GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt)
+{
+ offLin = maErr[x+1];
+ offQnt.clear();
+}
+
+void SierraLiteDither::SetError(unsigned int x, unsigned int y, const ColourOffset& err)
+{
+ // NB: We're storing the propagated error for both the current and the next
+ // line in a single-line buffer, using the following scheme upon entering
+ // this function:
+ //
+ // | Offset to x |-1 | 0 | 1 | 2 | 3 |
+ // |----------------|---|---|---|---|---|
+ // | Current row | | |(B)| B | B |
+ // | Next row | B | B | | | |
+ //
+ // and the following scheme upon leaving this function:
+ //
+ // | Offset to x |-1 | 0 | 1 | 2 | 3 |
+ // |----------------|---|---|---|---|---|
+ // | Current row | | |( )| B | B |
+ // | Next row | B | B | B | | |
+ //
+ // where "()" marks the current pixel, and "B" indicates that the error is
+ // stored in the buffer at the corresponding offset. Empty cells indicate
+ // that the corresponding error is not stored anywhere.
+
+ maErr[x+2] += err * (2/4.0); // pixel to the right
+ maErr[x] += err * (1/4.0); // pixel below left
+ maErr[x+1] = err * (1/4.0); // pixel below (overwritten instead of added to)
+}
+
+//*******************************************************************************
+
+FloydSteinbergDither::FloydSteinbergDither(unsigned int width) :
+ imageWidth(width),
+ maErr(new ColourOffset[width+2])
+{}
+
+FloydSteinbergDither::~FloydSteinbergDither()
+{
+ delete[] maErr;
+}
+
+void FloydSteinbergDither::GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt)
+{
+ if (x == 0)
+ mErrX.clear();
+ offLin = maErr[x+1];
+ offQnt.clear();
+}
+
+void FloydSteinbergDither::SetError(unsigned int x, unsigned int y, const ColourOffset& err)
+{
+ // NB: We're storing the propagated error for both the current and the next
+ // line in a single-line buffer, using the following scheme upon entering
+ // this function:
+ //
+ // | Offset to x |-1 | 0 | 1 | 2 | 3 |
+ // |----------------|---|---|---|---|---|
+ // | Current row | | |(B)| B | B |
+ // | Next row | B | B | X | | |
+ //
+ // and the following scheme upon leaving this function:
+ //
+ // | Offset to x |-1 | 0 | 1 | 2 | 3 |
+ // |----------------|---|---|---|---|---|
+ // | Current row | | |( )| B | B |
+ // | Next row | B | B | B | X | |
+ //
+ // where "()" marks the current pixel, "B" indicates that the error is
+ // stored in the buffer at the corresponding offset, and "X" indicates that
+ // the error is stored in `mErrX`. Empty cells indicate that the
+ // corresponding error is not stored anywhere.
+
+ maErr[x+1] = mErrX;
+ maErr[x+2] += err * (7/16.0); // pixel to the right
+ maErr[x] += err * (3/16.0); // pixel below left
+ maErr[x+1] += err * (5/16.0); // pixel below
+ mErrX = err * (1/16.0); // pixel below right (overwritten instead of added to)
+}
+
+//*******************************************************************************
+
+class DiffusionDither::Filter
+{
+public:
+ Filter(std::initializer_list> raw, int drop = 0);
+ ~Filter();
+ inline int Rows() const { return mRows; }
+ inline int Cols() const { return mCols; }
+ inline int ColX() const { return mColX; }
+ inline const ColourChannel& operator[](unsigned int i) const { return maData[i]; }
+private:
+ unsigned int mRows;
+ unsigned int mCols;
+ unsigned int mColX;
+ ColourChannel* maData;
+};
+
+DiffusionDither::Filter::Filter(std::initializer_list> raw, int drop) :
+ mRows(raw.size()),
+ mCols((raw.end() - 1)->size()),
+ mColX(mCols - raw.begin()->size() - 1),
+ maData(new ColourChannel[mRows * mCols - mColX - 1])
+{
+ POV_ASSERT(mRows > 0);
+ for (auto&& rawRow : raw)
+ POV_ASSERT((&rawRow == raw.begin()) || (rawRow.size() == mCols));
+
+ int sum = drop;
+ for (auto&& rawRow : raw)
+ for (auto&& rawCoeff : rawRow)
+ sum += rawCoeff;
+ POV_ASSERT(sum > 0);
+ ColourChannel invSum = 1.0 / sum;
+ ColourChannel* pCoeff = maData;
+ for (auto&& rawRow : raw)
+ for (auto&& rawCoeff : rawRow)
+ *(pCoeff++) = ColourChannel(rawCoeff) * invSum;
+}
+
+DiffusionDither::Filter::~Filter()
+{
+ if (maData != nullptr)
+ delete[] maData;
+}
+
+//------------------------------------------------------------------------------
+
+DiffusionDither::DiffusionDither(const Filter& matrix, unsigned int width) :
+ mMatrix(matrix),
+ mImageWidth(width),
+ maaErrorBuffer(new ColourOffset*[matrix.Rows()])
+{
+ for (unsigned int i = 0; i < matrix.Rows(); ++i)
+ maaErrorBuffer[i] = new ColourOffset[width + matrix.Cols() - 1];
+}
+
+DiffusionDither::~DiffusionDither()
+{
+ for (unsigned int i = 0; i < mMatrix.Rows(); ++i)
+ delete[] maaErrorBuffer[i];
+ delete[] maaErrorBuffer;
+}
+
+void DiffusionDither::GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt)
+{
+ if (x == 0)
+ {
+ ColourOffset* tmp = maaErrorBuffer[0];
+ for (unsigned int i = 1; i < mMatrix.Rows(); ++i)
+ maaErrorBuffer[i - 1] = maaErrorBuffer[i];
+ maaErrorBuffer[mMatrix.Rows() - 1] = tmp;
+ for (unsigned int i = 0; i < mImageWidth + mMatrix.Cols() - 1; ++i)
+ maaErrorBuffer[mMatrix.Rows() - 1][i].clear();
+ }
+ offLin = maaErrorBuffer[0][x + mMatrix.ColX()];
+ offQnt.clear();
+}
+
+void DiffusionDither::SetError(unsigned int x, unsigned int y, const ColourOffset& err)
+{
+ unsigned int im = 0;
+ for (unsigned int iy = 0; iy < mMatrix.Rows(); ++iy)
+ for (unsigned int ix = (iy == 0 ? mMatrix.ColX() + 1 : 0); ix < mMatrix.Cols(); ++ix)
+ maaErrorBuffer[iy][x + ix] += err * mMatrix[im++];
+}
+
+//-------------------------------------------------------------------------------
+
+extern const OrderedDither::Pattern BayerMatrix2({
+ { 0, 2 },
+ { 3, 1 }});
+
+extern const OrderedDither::Pattern BayerMatrix3({
+ { 0, 3, 6 },
+ { 7, 1, 4 },
+ { 5, 8, 2 }});
+
+extern const OrderedDither::Pattern BayerMatrix4({
+ { 0, 8, 2, 10 },
+ { 12, 4, 14, 6 },
+ { 3, 11, 1, 9 },
+ { 15, 7, 13, 5 }});
+
+extern const OrderedDither::Pattern BlueNoise64a(64, kBlueNoise64a, 64 * 64);
+
+//*******************************************************************************
+
+extern const DiffusionDither::Filter AtkinsonMatrix(
+ {{ 1, 1 },
+ { 1, 1, 1, 0 },
+ { 0, 1, 0, 0 }}, 2);
+
+extern const DiffusionDither::Filter BurkesMatrix(
+ {{ 8, 4 },
+ { 2, 4, 8, 4, 2 }});
+
+extern const DiffusionDither::Filter JarvisJudiceNinkeMatrix(
+ {{ 7, 5 },
+ { 3, 5, 7, 5, 3 },
+ { 1, 3, 5, 3, 1 }});
+
+extern const DiffusionDither::Filter Sierra2Matrix(
+ {{ 4, 3 },
+ { 1, 2, 3, 2, 1 }});
+
+extern const DiffusionDither::Filter Sierra3Matrix(
+ {{ 5, 3 },
+ { 2, 4, 5, 4, 2 },
+ { 0, 2, 3, 2, 0 }});
+
+extern const DiffusionDither::Filter StuckiMatrix(
+ {{ 8, 4 },
+ { 2, 4, 8, 4, 2 },
+ { 1, 2, 4, 2, 1 }});
+
+//*******************************************************************************
+
+DitherStrategySPtr GetDitherStrategy(DitherMethodId method, unsigned int imageWidth)
+{
+ DitherStrategySPtr s;
+ switch (method)
+ {
+ case DitherMethodId::kNone: s = std::make_shared (); break;
+ case DitherMethodId::kDiffusion1D: s = std::make_shared (); break;
+ case DitherMethodId::kSierraLite: s = std::make_shared (imageWidth); break;
+ case DitherMethodId::kFloydSteinberg: s = std::make_shared (imageWidth); break;
+ case DitherMethodId::kBayer2x2: s = std::make_shared (BayerMatrix2, imageWidth); break;
+ case DitherMethodId::kBayer3x3: s = std::make_shared (BayerMatrix3, imageWidth); break;
+ case DitherMethodId::kBayer4x4: s = std::make_shared (BayerMatrix4, imageWidth); break;
+ case DitherMethodId::kBlueNoise: s = std::make_shared (BlueNoise64a, imageWidth); break;
+ case DitherMethodId::kBlueNoiseX: s = std::make_shared (BlueNoise64a, imageWidth, true); break;
+ case DitherMethodId::kAtkinson: s = std::make_shared (AtkinsonMatrix, imageWidth); break;
+ case DitherMethodId::kBurkes: s = std::make_shared (BurkesMatrix, imageWidth); break;
+ case DitherMethodId::kJarvisJudiceNinke:s = std::make_shared (JarvisJudiceNinkeMatrix, imageWidth);break;
+ case DitherMethodId::kSierra3: s = std::make_shared (Sierra3Matrix, imageWidth); break;
+ case DitherMethodId::kSierra2: s = std::make_shared (Sierra2Matrix, imageWidth); break;
+ case DitherMethodId::kStucki: s = std::make_shared (StuckiMatrix, imageWidth); break;
+ }
+ return s;
+}
+
+DitherStrategySPtr GetNoOpDitherStrategy()
+{
+ return DitherStrategySPtr(new NoDither());
+}
+
+ColourChannel GetDitherOffset(unsigned int x, unsigned int y)
+{
+ return BlueNoise64a(x, y);
+}
+
+} // end of namespace pov_base
diff --git a/source/base/image/dither.h b/source/base/image/dither.h
new file mode 100644
index 000000000..6939277c6
--- /dev/null
+++ b/source/base/image/dither.h
@@ -0,0 +1,386 @@
+//******************************************************************************
+///
+/// @file base/image/dither.h
+///
+/// Declarations related to image dithering.
+///
+/// @copyright
+/// @parblock
+///
+/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
+///
+/// POV-Ray is free software: you can redistribute it and/or modify
+/// it under the terms of the GNU Affero General Public License as
+/// published by the Free Software Foundation, either version 3 of the
+/// License, or (at your option) any later version.
+///
+/// POV-Ray is distributed in the hope that it will be useful,
+/// but WITHOUT ANY WARRANTY; without even the implied warranty of
+/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+/// GNU Affero General Public License for more details.
+///
+/// You should have received a copy of the GNU Affero General Public License
+/// along with this program. If not, see .
+///
+/// ----------------------------------------------------------------------------
+///
+/// POV-Ray is based on the popular DKB raytracer version 2.12.
+/// DKBTrace was originally written by David K. Buck.
+/// DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
+///
+/// @endparblock
+///
+//******************************************************************************
+
+#ifndef POVRAY_BASE_IMAGE_DITHER_H
+#define POVRAY_BASE_IMAGE_DITHER_H
+
+// Module config header file must be the first file included within POV-Ray unit header files
+#include "base/configbase.h"
+
+// POV-Ray header files (base module)
+#include "base/types.h"
+#include "base/colour.h"
+
+namespace pov_base
+{
+
+//##############################################################################
+///
+/// @defgroup PovBaseImageEncoding Basic Colour Encoding and Decoding
+/// @ingroup PovBaseImage
+///
+/// @{
+
+class Image;
+
+//*****************************************************************************
+///
+/// @name Dithering
+///
+/// The following types and functions provide dithering functionality.
+///
+/// @{
+
+enum class DitherMethodId
+{
+ kNone,
+ kDiffusion1D,
+ kSierraLite,
+ kFloydSteinberg,
+ kBayer2x2,
+ kBayer3x3,
+ kBayer4x4,
+ kBlueNoise,
+ kBlueNoiseX,
+ kAtkinson,
+ kBurkes,
+ kJarvisJudiceNinke,
+ kSierra2,
+ kSierra3,
+ kStucki,
+};
+
+/// Abstract class representing a dithering algorithm and state.
+///
+/// @note
+/// The interface is designed to be used in quantization of a single complete image, with the
+/// image processed line by line and pixel by pixel, starting at (x=0,y=0). Failure to adhere
+/// to this processing order may lead to undefined behaviour in stateful dithering algorithms.
+///
+class DitherStrategy
+{
+ public:
+
+ /// Represents an offset to a colour.
+ struct ColourOffset;
+
+ virtual ~DitherStrategy() {}
+
+ /// Queries a colour offset from the algorithm.
+ ///
+ /// This function computes an offset to be added to the colour of a given pixel, based on
+ /// the pixel location and/or the algorithm's state.
+ ///
+ /// @param[in] x X coordinate of the pixel (may or may not be relevant to the algorithm).
+ /// @param[in] y Y coordinate of the pixel (may or may not be relevant to the algorithm).
+ /// @param[out] offLin Linear offset to add before any encoding steps.
+ /// This is typically based on carried-over quantization errors from neighboring pixels, as
+ /// used in stateful dither algorithms.
+ /// @param[out] offQnt Offset to add right before quantization (even after scaling).
+ /// This is typically more or less random noise in the range [-0.5, 0.5], as used in
+ /// stateless dither algorithms.
+ ///
+ virtual void GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt) = 0;
+
+ /// Reports the actual quantization error to the algorithm.
+ ///
+ /// This function feeds back the actual quantization error to the algorithm, allowing a
+ /// stateful algorithm to update its state accordingly.
+ ///
+ /// @param[in] x X coordinate of the pixel (may or may not be relevant to the algorithm).
+ /// @param[in] y Y coordinate of the pixel (may or may not be relevant to the algorithm).
+ /// @param[in] err Linear quantization error (may or may not be relevant to the algorithm).
+ ///
+ virtual void SetError(unsigned int x, unsigned int y, const ColourOffset& err) {}
+};
+
+struct DitherStrategy::ColourOffset
+{
+ union { ColourChannel red, gray; };
+ ColourChannel green, blue, alpha;
+
+ inline ColourOffset() : red(0.0f), green(0.0f), blue(0.0f), alpha(0.0f) {}
+ inline ColourOffset(ColourChannel r, ColourChannel g, ColourChannel b, ColourChannel a) : red(r), green(g), blue(b), alpha(a) {}
+ inline void clear() { red = 0.0f; green = 0.0f; blue = 0.0f; alpha = 0.0f; }
+ inline void setAll(ColourChannel v) { red = v; green = v; blue = v; alpha = v; }
+ inline void setRGB(RGBColour& v) { red = v.red(); green = v.green(); blue = v.blue(); alpha = 0.0; }
+ inline RGBColour getRGB() { return RGBColour(red, green, blue); }
+ inline ColourOffset operator*(ColourChannel b) const { return ColourOffset(red*b, green*b, blue*b, alpha*b); }
+ inline ColourOffset operator+(const ColourOffset& b) const { return ColourOffset(red + b.red, green + b.green, blue + b.blue, alpha + b.alpha); }
+ inline ColourOffset& operator+=(const ColourOffset& b) { red += b.red; green += b.green; blue += b.blue; alpha += b.alpha; return *this; }
+};
+
+typedef shared_ptr DitherStrategySPtr;
+
+/// Factory function to get a dithering algorithm and state.
+DitherStrategySPtr GetDitherStrategy(DitherMethodId method, unsigned int imageWidth);
+
+/// Factory function to get a no-op dithering algorithm.
+DitherStrategySPtr GetNoOpDitherStrategy();
+
+/// Function providing simple stateless dithering.
+///
+/// This function is provided as a fallback from the @ref DitherStrategy mechanism to provide basic
+/// dithering functionality in cases where stateful operation is impractical, such as the render
+/// preview.
+///
+/// The current implementation is based on blue noise dithering.
+///
+/// @param[in] x Image x coordinate.
+/// @param[in] y Image y coordinate.
+/// @return Offset to add right before quantization (even after scaling).
+///
+ColourChannel GetDitherOffset(unsigned int x, unsigned int y);
+
+//-------------------------------------------------------------------------------
+
+/// "no-op" dithering strategy.
+///
+/// This stateless dithering strategy serves as a placeholder when dithering
+/// is not desired.
+///
+class NoDither : public DitherStrategy
+{
+public:
+ virtual void GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt) override;
+};
+
+//-------------------------------------------------------------------------------
+
+/// Generalized ordered dithering strategy.
+///
+/// This stateless dithering strategy implements a generalized ordered
+/// dithering filter. The specifics of the filter are defined by a matrix.
+///
+class OrderedDither : public DitherStrategy
+{
+public:
+ class Pattern;
+ OrderedDither(const Pattern& matrix, unsigned int width, bool invertRB = false);
+ virtual void GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt) override;
+protected:
+ const Pattern& mPattern;
+ unsigned int mImageWidth;
+ bool mInvertRB;
+};
+
+/// 2x2 Bayer ordered dithering matrix.
+///
+/// This matrix is based on principles proposed by B.E. Bayer in 1973.
+///
+extern const OrderedDither::Pattern BayerMatrix2;
+
+/// 3x3 ordered dithering matrix.
+///
+extern const OrderedDither::Pattern BayerMatrix3;
+
+/// 4x4 Bayer ordered dithering matrix.
+///
+/// This matrix is based on principles proposed by B.E. Bayer in 1973.
+///
+extern const OrderedDither::Pattern BayerMatrix4;
+
+/// 64x64 blue noise dithering matrix.
+///
+/// This matrix was generated using the void-and-cluster method proposed by
+/// R. Ulichney in 1993.
+///
+extern const OrderedDither::Pattern BlueNoise64a;
+
+//-------------------------------------------------------------------------------
+
+/// Simple 1D error diffusion dithering strategy.
+///
+/// This stateful dithering strategy implements the simplest error diffusion
+/// dithering filter possible, propagating all of the quantization error to the
+/// next pixel.
+///
+/// This dithering strategy is equivalent to the following @ref DiffusionDither::Filter:
+///
+/// DiffusionDither::Filter(
+/// {{ 1 }});
+///
+class DiffusionDither1D : public DitherStrategy
+{
+public:
+ virtual void GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt) override;
+ virtual void SetError(unsigned int x, unsigned int y, const ColourOffset& err) override;
+protected:
+ ColourOffset lastErr;
+};
+
+//-------------------------------------------------------------------------------
+
+/// Sierra Lite error diffusion dithering strategy.
+///
+/// This stateful dithering strategy implements the error diffusion dithering
+/// filter proposed by F. Sierra in 1990 as "Filter Lite" (aka "Sierra Lite"
+/// or "Sierra-2-4A"), distributing the quantization error non-uniformly between
+/// the pixel on the right and the pixels to the bottom left and straight below.
+///
+/// @note This implementation uses an additional 1-line pixel buffer to avoid manipulating the original image.
+///
+/// This dithering strategy is equivalent to the following @ref DiffusionDither::Filter:
+///
+/// DiffusionDither::Filter(
+/// {{ 2 },
+/// { 1, 1, 0 }});
+///
+class SierraLiteDither : public DitherStrategy
+{
+public:
+ SierraLiteDither(unsigned int width);
+ virtual ~SierraLiteDither();
+ virtual void GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt) override;
+ virtual void SetError(unsigned int x, unsigned int y, const ColourOffset& err) override;
+protected:
+ unsigned int imageWidth;
+ ColourOffset* maErr;
+};
+
+//-------------------------------------------------------------------------------
+
+/// Floyd-Steinberg error diffusion dithering strategy.
+///
+/// This stateful dithering strategy implements the error diffusion dithering
+/// filter proposed by R.W. Floyd and L. Steinberg in 1976.
+///
+/// The Floyd-Steinberg filter distributes the error non-uniformly among the
+/// pixel on the right as well as the three pixels below.
+///
+/// @note This implementation uses an additional 1-line pixel buffer to avoid manipulating the original image.
+///
+/// This dithering strategy is equivalent to the following @ref DiffusionDither::Filter:
+///
+/// DiffusionDither::Filter(
+/// {{ 7 },
+/// { 3, 5, 1 }});
+///
+class FloydSteinbergDither : public DitherStrategy
+{
+public:
+ FloydSteinbergDither(unsigned int width);
+ virtual ~FloydSteinbergDither();
+ virtual void GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt) override;
+ virtual void SetError(unsigned int x, unsigned int y, const ColourOffset& err) override;
+protected:
+ unsigned int imageWidth;
+ ColourOffset* maErr;
+ ColourOffset mErrX;
+};
+
+//-------------------------------------------------------------------------------
+
+/// Generalized error diffusion dithering strategy.
+///
+/// This stateful dithering strategy implements a generalized error diffusion
+/// dithering filter. The specifics of the filter are defined by a matrix.
+///
+/// @note This implementation uses an additional multi-line pixel buffer to avoid manipulating the original image.
+///
+class DiffusionDither : public DitherStrategy
+{
+public:
+ class Filter;
+ DiffusionDither(const Filter& matrix, unsigned int width);
+ virtual ~DiffusionDither();
+ virtual void GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt) override;
+ virtual void SetError(unsigned int x, unsigned int y, const ColourOffset& err) override;
+protected:
+ const Filter& mMatrix;
+ unsigned int mImageWidth;
+ ColourOffset** maaErrorBuffer;
+};
+
+/// Atkinson error diffusion dithering matrix.
+///
+/// This matrix corresponds to the filter originally implemented by B. Atkinson
+/// in Apple's HyperScan software.
+///
+/// @note This filter propagates only 75% of the quantization error.
+///
+extern const DiffusionDither::Filter AtkinsonMatrix;
+
+/// Burkes error diffusion dithering matrix.
+///
+/// This matrix corresponds to the filter proposed by D. Burkes in 1988,
+/// distributing the quantization error across five pixel columns and three
+/// pixel rows.
+///
+extern const DiffusionDither::Filter BurkesMatrix;
+
+/// Jarvis-Judice-Ninke error diffusion dithering matrix.
+///
+/// This matrix corresponds to the filter proposed by J.F. Jarvis, C.N.Judice
+/// and W.H. Ninke in 1976, distributing the quantization error across five
+/// pixel columns and three pixel rows.
+///
+extern const DiffusionDither::Filter JarvisJudiceNinkeMatrix;
+
+/// Two-Row Sierra error diffusion dithering matrix.
+///
+/// This matrix corresponds to the filter proposed by F. Sierra in 1990
+/// (aka "Sierra-2"), distributing the quantization error across five pixel
+/// columns and two pixel rows.
+///
+extern const DiffusionDither::Filter Sierra2Matrix;
+
+/// Sierra error diffusion dithering matrix.
+///
+/// This matrix corresponds to the filter proposed by F. Sierra in 1989
+/// (aka "Sierra-3"), distributing the quantization error across five pixel
+/// columns and three pixel rows.
+///
+extern const DiffusionDither::Filter Sierra3Matrix;
+
+/// Stucki error diffusion dithering matrix.
+///
+/// This matrix corresponds to the filter proposed by P. Stucki in 1981,
+/// distributing the quantization error across five pixel columns and three
+/// pixel rows.
+///
+extern const DiffusionDither::Filter StuckiMatrix;
+
+/// @}
+///
+//*****************************************************************************
+
+/// @}
+///
+//##############################################################################
+
+} // end of namespace pov_base
+
+#endif // POVRAY_BASE_IMAGE_DITHER_H
diff --git a/source/base/image/encoding.cpp b/source/base/image/encoding.cpp
index 3fd53bdb5..f751b67a5 100644
--- a/source/base/image/encoding.cpp
+++ b/source/base/image/encoding.cpp
@@ -9,7 +9,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -37,7 +37,8 @@
// Unit header file must be the first file included within POV-Ray *.cpp files (pulls in config)
#include "base/image/encoding.h"
-// POV-Ray base header files
+// POV-Ray header files (base module)
+#include "base/image/dither.h"
#include "base/image/image.h"
// this must be the last file included
@@ -50,227 +51,6 @@ namespace pov_base
#define ALPHA_EPSILON 1.0e-6 ///< Smallest alpha value we dare to safely use with premultiplied alpha.
-static const unsigned int MaxBayerMatrixSize = 4;
-typedef float BayerMatrix[MaxBayerMatrixSize][MaxBayerMatrixSize];
-
-static const BayerMatrix BayerMatrices[MaxBayerMatrixSize+1] =
-{
- // dummy for 0x0
- { { 0 } },
- // 1x1 (of little use, but here it is)
- { { 1/2.0-0.5 } },
- // 2x2
- { { 1/4.0-0.5, 3/4.0-0.5 },
- { 4/4.0-0.5, 2/4.0-0.5 } },
- // 3x3
- { { 3/9.0-0.5, 7/9.0-0.5, 4/9.0-0.5 },
- { 6/9.0-0.5, 1/9.0-0.5, 9/9.0-0.5 },
- { 2/9.0-0.5, 8/9.0-0.5, 5/9.0-0.5 } },
- // 4x4
- { { 1/16.0-0.5, 9/16.0-0.5, 3/16.0-0.5, 11/16.0-0.5 },
- { 13/16.0-0.5, 5/16.0-0.5, 15/16.0-0.5, 7/16.0-0.5 },
- { 4/16.0-0.5, 12/16.0-0.5, 2/16.0-0.5, 10/16.0-0.5 },
- { 16/16.0-0.5, 8/16.0-0.5, 14/16.0-0.5, 6/16.0-0.5 } }
-};
-
-/*******************************************************************************/
-
-/// Class representing "no-op" dithering rules.
-class NoDither : public DitherStrategy
-{
- public:
- virtual void GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt);
-};
-
-/// Class representing bayer dithering rules, generating a regular pattern.
-class BayerDither : public DitherStrategy
-{
- public:
- BayerDither(unsigned int mxSize);
- virtual void GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt);
- static inline float GetOffset(unsigned int x, unsigned int y, unsigned int ms) { return BayerMatrices[ms][x%ms][y%ms]; }
- protected:
- unsigned int matrixSize;
-};
-
-/// Class representing simple 1D error diffusion dithering rules, carrying over the error from one pixel to the next.
-class DiffusionDither1D : public DitherStrategy
-{
- public:
- virtual void GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt);
- virtual void SetError(unsigned int x, unsigned int y, const ColourOffset& err);
- protected:
- ColourOffset lastErr;
-};
-
-/// Class representing simple 2D error diffusion dithering rules, carrying over the error from one pixel to the right, as well as the two pixels below.
-/// @note This implementation uses an additional 2-line pixel buffer to avoid manipulating the original image.
-class DiffusionDither2D : public DitherStrategy
-{
- public:
- DiffusionDither2D(unsigned int width);
- virtual ~DiffusionDither2D();
- virtual void GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt);
- virtual void SetError(unsigned int x, unsigned int y, const ColourOffset& err);
- protected:
- unsigned int imageWidth;
- ColourOffset* nextRowOffset;
- ColourOffset* thisRowOffset;
-};
-
-/// Class representing Floyd-Steinberg dithering rules, carrying over the error from one pixel to the right, as well as the three pixels below.
-/// @note This implementation uses an additional 2-line pixel buffer to avoid manipulating the original image.
-class FloydSteinbergDither : public DitherStrategy
-{
- public:
- FloydSteinbergDither(unsigned int width);
- virtual ~FloydSteinbergDither();
- virtual void GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt);
- virtual void SetError(unsigned int x, unsigned int y, const ColourOffset& err);
- protected:
- unsigned int imageWidth;
- ColourOffset* nextRowOffset;
- ColourOffset* thisRowOffset;
-};
-
-/*******************************************************************************/
-
-void NoDither::GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt)
-{
- offLin.clear();
- offQnt.clear();
-}
-
-/*******************************************************************************/
-
-BayerDither::BayerDither(unsigned int mxSize) :
- matrixSize(min(mxSize,MaxBayerMatrixSize))
-{
- ;
-}
-
-void BayerDither::GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt)
-{
- offLin.clear();
- offQnt.setAll(GetOffset(x, y, matrixSize));
-}
-
-/*******************************************************************************/
-
-void DiffusionDither1D::GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt)
-{
- offLin = lastErr; lastErr.clear(); offQnt.clear();
-}
-
-void DiffusionDither1D::SetError(unsigned int x, unsigned int y, const ColourOffset& err)
-{
- lastErr = err;
-}
-
-/*******************************************************************************/
-
-DiffusionDither2D::DiffusionDither2D(unsigned int width) :
- imageWidth(width),
- thisRowOffset(new ColourOffset[width+1]),
- nextRowOffset(new ColourOffset[width+1])
-{
- ;
-}
-
-DiffusionDither2D::~DiffusionDither2D()
-{
- delete[] thisRowOffset;
- delete[] nextRowOffset;
-}
-
-void DiffusionDither2D::GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt)
-{
- offLin = thisRowOffset[x];
- offQnt.clear();
-}
-
-void DiffusionDither2D::SetError(unsigned int x, unsigned int y, const ColourOffset& err)
-{
- if (x == 0)
- {
- ColourOffset* tmp = nextRowOffset;
- nextRowOffset = thisRowOffset;
- thisRowOffset = tmp;
- for (unsigned int i = 0; i < imageWidth+1; i ++)
- nextRowOffset[i].clear();
- }
- thisRowOffset[x+1] += err * (2/4.0); // pixel to the right
- nextRowOffset[x] += err * (1/4.0); // pixel below
- nextRowOffset[x+1] += err * (1/4.0); // pixel below right
-}
-
-/*******************************************************************************/
-
-FloydSteinbergDither::FloydSteinbergDither(unsigned int width) :
- imageWidth(width),
- thisRowOffset(new ColourOffset[width+2]),
- nextRowOffset(new ColourOffset[width+2])
-{
- ;
-}
-
-FloydSteinbergDither::~FloydSteinbergDither()
-{
- delete[] thisRowOffset;
- delete[] nextRowOffset;
-}
-
-void FloydSteinbergDither::GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt)
-{
- offLin = thisRowOffset[x+1];
- offQnt.clear();
-}
-
-void FloydSteinbergDither::SetError(unsigned int x, unsigned int y, const ColourOffset& err)
-{
- if (x == 0)
- {
- ColourOffset* tmp = nextRowOffset;
- nextRowOffset = thisRowOffset;
- thisRowOffset = tmp;
- for (unsigned int i = 0; i < imageWidth+2; i ++)
- nextRowOffset[i].clear();
- }
- thisRowOffset[x+2] += err * (7/16.0); // pixel to the right
- nextRowOffset[x] += err * (3/16.0); // pixel below left
- nextRowOffset[x+1] += err * (5/16.0); // pixel below
- nextRowOffset[x+2] += err * (1/16.0); // pixel below right
-}
-
-/*******************************************************************************/
-
-DitherStrategySPtr GetDitherStrategy(DitherMethodId method, unsigned int imageWidth)
-{
- switch (method)
- {
- case kPOVList_DitherMethod_None: return DitherStrategySPtr(new NoDither());
- case kPOVList_DitherMethod_Diffusion1D: return DitherStrategySPtr(new DiffusionDither1D());
- case kPOVList_DitherMethod_Diffusion2D: return DitherStrategySPtr(new DiffusionDither2D(imageWidth));
- case kPOVList_DitherMethod_FloydSteinberg: return DitherStrategySPtr(new FloydSteinbergDither(imageWidth));
- case kPOVList_DitherMethod_Bayer2x2: return DitherStrategySPtr(new BayerDither(2));
- case kPOVList_DitherMethod_Bayer3x3: return DitherStrategySPtr(new BayerDither(3));
- case kPOVList_DitherMethod_Bayer4x4: return DitherStrategySPtr(new BayerDither(4));
- default: throw POV_EXCEPTION_STRING("Invalid dither method for output");
- }
-}
-
-DitherStrategySPtr GetNoOpDitherStrategy()
-{
- return DitherStrategySPtr(new NoDither());
-}
-
-/*******************************************************************************/
-
-float GetDitherOffset(unsigned int x, unsigned int y)
-{
- return BayerDither::GetOffset(x,y,4);
-}
-
/*******************************************************************************/
inline void AlphaPremultiply(float& fGray, float fAlpha)
@@ -443,7 +223,7 @@ unsigned int GetEncodedGrayValue(const Image* img, unsigned int x, unsigned int
}
DitherStrategy::ColourOffset linOff, encOff;
dh.GetOffset(x,y,linOff,encOff);
- unsigned int iGray = IntEncode(g,fGray+linOff.gray,max,encOff.gray,linOff.gray);
+ unsigned int iGray = IntEncode(g, fGray, max, encOff.gray, linOff.gray);
dh.SetError(x,y,linOff);
return iGray;
}
@@ -488,8 +268,8 @@ void GetEncodedGrayAValue(const Image* img, unsigned int x, unsigned int y, cons
// else no need to worry about premultiplication
DitherStrategy::ColourOffset linOff, encOff;
dh.GetOffset(x,y,linOff,encOff);
- gray = IntEncode(g, fGray + linOff.gray, max, encOff.gray, linOff.gray);
- alpha = IntEncode(fAlpha + linOff.alpha, max, encOff.alpha, linOff.alpha);
+ gray = IntEncode(g, fGray, max, encOff.gray, linOff.gray);
+ alpha = IntEncode( fAlpha, max, encOff.alpha, linOff.alpha);
dh.SetError(x,y,linOff);
}
void GetEncodedRGBValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, unsigned int max, unsigned int& red, unsigned int& green, unsigned int& blue, DitherStrategy& dh)
@@ -509,9 +289,9 @@ void GetEncodedRGBValue(const Image* img, unsigned int x, unsigned int y, const
}
DitherStrategy::ColourOffset linOff, encOff;
dh.GetOffset(x,y,linOff,encOff);
- red = IntEncode(g,fRed + linOff.red, max, encOff.red, linOff.red);
- green = IntEncode(g,fGreen + linOff.green, max, encOff.green, linOff.green);
- blue = IntEncode(g,fBlue + linOff.blue, max, encOff.blue, linOff.blue);
+ red = IntEncode(g, fRed, max, encOff.red, linOff.red);
+ green = IntEncode(g, fGreen, max, encOff.green, linOff.green);
+ blue = IntEncode(g, fBlue, max, encOff.blue, linOff.blue);
dh.SetError(x,y,linOff);
}
void GetEncodedRGBAValue(const Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, unsigned int max, unsigned int& red, unsigned int& green, unsigned int& blue, unsigned int& alpha, DitherStrategy& dh, bool premul)
@@ -562,10 +342,10 @@ void GetEncodedRGBAValue(const Image* img, unsigned int x, unsigned int y, const
// else no need to worry about premultiplication
DitherStrategy::ColourOffset linOff, encOff;
dh.GetOffset(x,y,linOff,encOff);
- red = IntEncode(g,fRed + linOff.red, max, encOff.red, linOff.red);
- green = IntEncode(g,fGreen + linOff.green, max, encOff.green, linOff.green);
- blue = IntEncode(g,fBlue + linOff.blue, max, encOff.blue, linOff.blue);
- alpha = IntEncode(fAlpha + linOff.alpha, max, encOff.alpha, linOff.alpha);
+ red = IntEncode(g, fRed, max, encOff.red, linOff.red);
+ green = IntEncode(g, fGreen, max, encOff.green, linOff.green);
+ blue = IntEncode(g, fBlue, max, encOff.blue, linOff.blue);
+ alpha = IntEncode( fAlpha, max, encOff.alpha, linOff.alpha);
dh.SetError(x,y,linOff);
}
diff --git a/source/base/image/encoding.h b/source/base/image/encoding.h
index ea9a39ff6..f4e444efd 100644
--- a/source/base/image/encoding.h
+++ b/source/base/image/encoding.h
@@ -9,7 +9,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -48,6 +48,9 @@
namespace pov_base
{
+class DitherStrategy;
+class Image;
+
//##############################################################################
///
/// @defgroup PovBaseImageEncoding Basic Colour Encoding and Decoding
@@ -55,107 +58,6 @@ namespace pov_base
///
/// @{
-class Image;
-
-enum DitherMethodId
-{
- kPOVList_DitherMethod_None,
- kPOVList_DitherMethod_Diffusion1D,
- kPOVList_DitherMethod_Diffusion2D,
- kPOVList_DitherMethod_FloydSteinberg,
- kPOVList_DitherMethod_Bayer2x2,
- kPOVList_DitherMethod_Bayer3x3,
- kPOVList_DitherMethod_Bayer4x4,
-};
-
-/// Abstract class representing a dithering algorithm and state.
-///
-/// @note
-/// The interface is designed to be used in quantization of a single complete image, with the
-/// image processed line by line and pixel by pixel, starting at (x=0,y=0). Failure to adhere
-/// to this processing order may lead to undefined behaviour in stateful dithering algorithms.
-///
-class DitherStrategy
-{
- public:
-
- /// Represents an offset to a colour.
- struct ColourOffset
- {
- union { float red, gray; }; float green, blue, alpha;
- ColourOffset() : red(0.0f), green(0.0f), blue(0.0f), alpha(0.0f) { }
- ColourOffset(float r, float g, float b, float a) : red(r), green(g), blue(b), alpha(a) { }
- void clear() { red = 0.0f; green = 0.0f; blue = 0.0f; alpha = 0.0f; }
- void setAll(float v) { red = v; green = v; blue = v; alpha = v; }
- void setRGB(RGBColour& v) { red = v.red(); green = v.green(); blue = v.blue(); alpha = 0.0; }
- RGBColour getRGB() { return RGBColour(red, green, blue); }
- ColourOffset operator*(float b) const { return ColourOffset(red*b, green*b, blue*b, alpha*b); }
- ColourOffset operator+(const ColourOffset& b) const { return ColourOffset(red+b.red, green+b.green, blue+b.blue, alpha+b.alpha); }
- void operator+=(const ColourOffset& b) { red+=b.red; green+=b.green; blue+=b.blue; alpha+=b.alpha; }
- };
-
- virtual ~DitherStrategy() {}
-
- /// Queries a colour offset from the algorithm.
- ///
- /// This function computes an offset to be added to the colour of a given pixel, based on
- /// the pixel location and/or the algorithm's state.
- ///
- /// @param[in] x X coordinate of the pixel (may or may not be relevant to the algorithm).
- /// @param[in] y Y coordinate of the pixel (may or may not be relevant to the algorithm).
- /// @param[out] offLin Linear offset to add before any encoding steps.
- /// This is typically based on carried-over quantization errors from neighboring pixels, as
- /// used in stateful dither algorithms.
- /// @param[out] offQnt Offset to add right before quantization (even after scaling).
- /// This is typically more or less random noise in the range [-0.5, 0.5], as used in
- /// stateless dither algorithms.
- ///
- virtual void GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt) = 0;
-
- /// Reports the actual quantization error to the algorithm.
- ///
- /// This function feeds back the actual quantization error to the algorithm, allowing a
- /// stateful algorithm to update its state accordingly.
- ///
- /// @param[in] x X coordinate of the pixel (may or may not be relevant to the algorithm).
- /// @param[in] y Y coordinate of the pixel (may or may not be relevant to the algorithm).
- /// @param[in] err Linear quantization error (may or may not be relevant to the algorithm).
- ///
- virtual void SetError(unsigned int x, unsigned int y, const ColourOffset& err) {}
-};
-
-typedef shared_ptr DitherStrategySPtr;
-
-//*****************************************************************************
-///
-/// @name Dithering
-///
-/// The following functions provide dithering functionality.
-///
-/// @{
-
-/// Factory function to get a dithering algorithm and state.
-DitherStrategySPtr GetDitherStrategy(DitherMethodId method, unsigned int imageWidth);
-
-/// Factory function to get a no-op dithering algorithm.
-DitherStrategySPtr GetNoOpDitherStrategy();
-
-/// Function providing simple stateless dithering.
-///
-/// This function is provided as a fallback from the `DitherStrategy` mechanism to provide basic
-/// dithering functionality in cases where stateful operation is impractical, such as the render
-/// preview.
-///
-/// The current implementation is based on 4x4 Bayer dithering.
-///
-/// @param[in] x Image x coordinate.
-/// @param[in] y Image y coordinate.
-/// @return Offset to add right before quantization (even after scaling).
-///
-float GetDitherOffset(unsigned int x, unsigned int y);
-
-/// @}
-///
//*****************************************************************************
///
/// @name Basic Decoding
@@ -202,10 +104,28 @@ inline float IntDecode(const GammaCurvePtr& g, unsigned int x, unsigned int max)
///
/// @{
-/// Linear encoding function.
+/// Linear encoding function rounding down.
///
/// This function maps a floating-point value in the range 0..1 linearly to an integer value
-/// in the range 0..max.
+/// in the range 0..max, rounding down.
+///
+/// @note
+/// Floating-point values outside the range 0..1 are clipped, mapping them to 0 or max,
+/// respectively.
+///
+/// @param[in] x Value to encode.
+/// @param[in] max Encoded value representing 1.0.
+/// @param[in] qOff Offset to add before quantization.
+///
+inline unsigned int IntEncodeDown(float x, unsigned int max, float qOff = 0.0f)
+{
+ return (unsigned int)clip(floor(x * float(max) + qOff), 0.0f, float(max));
+}
+
+/// Linear encoding function rounding to nearest.
+///
+/// This function maps a floating-point value in the range 0..1 linearly to an integer value
+/// in the range 0..max, rounding to the nearest value.
///
/// @note
/// Floating-point values outside the range 0..1 are clipped, mapping them to 0 or max,
@@ -217,7 +137,7 @@ inline float IntDecode(const GammaCurvePtr& g, unsigned int x, unsigned int max)
///
inline unsigned int IntEncode(float x, unsigned int max, float qOff = 0.0f)
{
- return (unsigned int)clip(floor(x * float(max) + qOff + 0.5f), 0.0f, float(max));
+ return IntEncodeDown(x, max, qOff + 0.5f);
}
/// Linear encoding function.
@@ -232,12 +152,13 @@ inline unsigned int IntEncode(float x, unsigned int max, float qOff = 0.0f)
/// @param[in] x Value to encode.
/// @param[in] max Encoded value representing 1.0.
/// @param[in] qOff Offset to add before quantization.
-/// @param[out] err Quantization error (including effects due to adding qOff).
+/// @param[in,out] err Quantization error (including effects due to adding qOff).
///
inline unsigned int IntEncode(float x, unsigned int max, float qOff, float& err)
{
- unsigned int v = IntEncode(x, max, qOff);
- err = clip(x, 0.0f, 1.0f) - IntDecode(v, max);
+ float xEff = clip(x, 0.0f, 1.0f) + err;
+ unsigned int v = IntEncode(xEff, max, qOff);
+ err = xEff - IntDecode(v, max);
return v;
}
@@ -247,17 +168,39 @@ inline unsigned int IntEncode(float x, unsigned int max, float qOff, float& err)
/// in the range 0..max.
///
/// @note
-/// Floating-point values outside the range 0..1 (after applying the transfer function)
-/// are clipped, mapping them to 0 or max, respectively.
+/// Floating-point values outside the range 0..1 are clipped, mapping them to 0 or max,
+/// respectively.
+/// @note
+/// The transfer function is presumed to map values 0 and 1 to the respective value itself.
///
/// @param[in] g Transfer function (gamma curve) to use.
/// @param[in] x Value to encode.
/// @param[in] max Encoded value representing 1.0.
/// @param[in] qOff Offset to add before quantization.
+/// @param[in,out] err Quantization error (including effects due to adding qOff).
///
-inline unsigned int IntEncode(const GammaCurvePtr& g, float x, unsigned int max, float qOff = 0.0f)
+inline unsigned int IntEncode(const GammaCurvePtr& g, float x, unsigned int max, float qOff, float& err)
{
- return IntEncode(GammaCurve::Encode(g, x), max, qOff);
+ if (GammaCurve::IsNeutral(g))
+ return IntEncode(x, max, qOff, err);
+
+ float xEff = clip(x, 0.0f, 1.0f) + err;
+ unsigned int v = IntEncodeDown(GammaCurve::Encode(g, xEff), max);
+ float decoded = IntDecode(g, v, max);
+ if (v >= max)
+ {
+ err = xEff - decoded;
+ return v;
+ }
+ float decodedUp = IntDecode(g, v + 1, max);
+ float threshold = (0.5 - qOff) * decoded + (0.5 + qOff) * decodedUp;
+ if (xEff > threshold)
+ {
+ decoded = decodedUp;
+ ++v;
+ }
+ err = xEff - decoded;
+ return v;
}
/// Generic encoding function.
@@ -266,20 +209,20 @@ inline unsigned int IntEncode(const GammaCurvePtr& g, float x, unsigned int max,
/// in the range 0..max.
///
/// @note
-/// Floating-point values outside the range 0..1 (after applying the transfer function)
-/// are clipped, mapping them to 0 or max, respectively.
+/// Floating-point values outside the range 0..1 are clipped, mapping them to 0 or max,
+/// respectively.
+/// @note
+/// The transfer function is presumed to map values 0 and 1 to the respective value itself.
///
/// @param[in] g Transfer function (gamma curve) to use.
/// @param[in] x Value to encode.
/// @param[in] max Encoded value representing 1.0.
/// @param[in] qOff Offset to add before quantization.
-/// @param[out] err Quantization error (including effects due to adding qOff).
///
-inline unsigned int IntEncode(const GammaCurvePtr& g, float x, unsigned int max, float qOff, float& err)
+inline unsigned int IntEncode(const GammaCurvePtr& g, float x, unsigned int max, float qOff = 0.0f)
{
- unsigned int v = IntEncode(g, x, max, qOff);
- err = clip(x, 0.0f, 1.0f) - IntDecode(g, v, max);
- return v;
+ float err = 0.0f;
+ return IntEncode(g, x, max, qOff, err);
}
/// @}
diff --git a/source/base/image/hdr.cpp b/source/base/image/hdr.cpp
index a5c489151..cc56c0047 100644
--- a/source/base/image/hdr.cpp
+++ b/source/base/image/hdr.cpp
@@ -42,15 +42,13 @@
#include "base/image/hdr.h"
// Standard C++ header files
+#include
#include
-// Boost header files
-#include
-#include
-
-// POV-Ray base header files
+// POV-Ray header files (base module)
#include "base/fileinputoutput.h"
#include "base/types.h"
+#include "base/image/dither.h"
#include "base/image/metadata.h"
// this must be the last file included
@@ -210,7 +208,7 @@ Image *Read(IStream *file, const Image::ReadOptions& options)
image = Image::Create(width, height, imagetype);
// NB: HDR files don't use alpha, so premultiplied vs. non-premultiplied is not an issue
- boost::scoped_array scanline(new unsigned char[4 * width]);
+ std::unique_ptr scanline(new unsigned char[4 * width]);
for(int row = 0; row < height; row++)
{
// determine scanline type
@@ -318,7 +316,7 @@ void Write(OStream *file, const Image *image, const Image::WriteOptions& options
file->printf("\n");
file->printf("-Y %d +X %d\n", height, width);
- boost::scoped_array scanline(new RGBE[width]);
+ std::unique_ptr scanline(new RGBE[width]);
for(int row = 0; row < height; row++)
{
diff --git a/source/base/image/image.cpp b/source/base/image/image.cpp
index 27a5dd2f1..4900187e8 100644
--- a/source/base/image/image.cpp
+++ b/source/base/image/image.cpp
@@ -45,10 +45,11 @@
#include
#include
-// POV-Ray base header files
+// POV-Ray header files (base module)
#include "base/platformbase.h"
#include "base/safemath.h"
#include "base/image/bmp.h"
+#include "base/image/dither.h"
#include "base/image/gif.h"
#include "base/image/hdr.h"
#include "base/image/iff.h"
@@ -78,6 +79,16 @@ namespace pov_base
using std::allocator;
+Image::WriteOptions::WriteOptions() :
+ ditherStrategy(GetNoOpDitherStrategy()),
+ offset_x(0),
+ offset_y(0),
+ alphaMode(kAlphaMode_None),
+ bitsPerChannel(8),
+ compression(-1),
+ grayscale(false)
+{}
+
template >
class BitMapImage : public Image
{
diff --git a/source/base/image/image.h b/source/base/image/image.h
index 3d4dc57f8..68b3d7cae 100644
--- a/source/base/image/image.h
+++ b/source/base/image/image.h
@@ -48,6 +48,9 @@
namespace pov_base
{
+class DitherStrategy;
+using DitherStrategySPtr = shared_ptr;
+
//##############################################################################
///
/// @defgroup PovBaseImage Image Handling
@@ -287,15 +290,7 @@ class Image
/// in POV-Ray.
bool grayscale : 1;
- WriteOptions() :
- ditherStrategy(GetNoOpDitherStrategy()),
- offset_x(0),
- offset_y(0),
- alphaMode(kAlphaMode_None),
- bitsPerChannel(8),
- compression(-1),
- grayscale(false)
- {}
+ WriteOptions();
inline bool AlphaIsEnabled() const
{
diff --git a/source/base/image/jpeg.cpp b/source/base/image/jpeg.cpp
index c0d0e68b2..6f6aba002 100644
--- a/source/base/image/jpeg.cpp
+++ b/source/base/image/jpeg.cpp
@@ -44,7 +44,8 @@
#ifndef LIBJPEG_MISSING
-#include
+// C++ variants of standard C header files
+#include
// Standard C++ header files
#include
@@ -58,7 +59,8 @@ extern "C"
#include
}
-// POV-Ray base header files
+// POV-Ray header files (base module)
+#include "base/image/dither.h"
#include "base/image/metadata.h"
// this must be the last file included
@@ -89,7 +91,7 @@ class POV_JPEG_Write_Buffer
struct jpeg_error_mgr jerr;
jpeg_source_mgr jsrc;
jpeg_destination_mgr jdest;
- jmp_buf setjmp_buffer; // for return to caller
+ std::jmp_buf setjmp_buffer; // for return to caller
char buffer[POV_JPEG_BUFFER_SIZE];
JSAMPROW row_pointer[1];
int row_stride;
@@ -117,7 +119,7 @@ class POV_JPEG_Read_Buffer
struct jpeg_error_mgr jerr;
jpeg_source_mgr jsrc;
jpeg_destination_mgr jdest;
- jmp_buf setjmp_buffer; // for return to caller
+ std::jmp_buf setjmp_buffer; // for return to caller
char buffer[POV_JPEG_BUFFER_SIZE];
JSAMPROW row_pointer[1];
int row_stride;
@@ -159,7 +161,7 @@ extern "C"
(*cinfo->err->output_message)(cinfo);
// Return control to the setjmp point
- longjmp(myerr->setjmp_buffer, 1);
+ std::longjmp(myerr->setjmp_buffer, 1);
}
METHODDEF(void) write_error_exit (j_common_ptr cinfo)
@@ -169,7 +171,7 @@ extern "C"
(*cinfo->err->output_message)(cinfo);
// Return control to the setjmp point
- longjmp(myerr->setjmp_buffer, 1);
+ std::longjmp(myerr->setjmp_buffer, 1);
}
METHODDEF(void) read_output_message(j_common_ptr cinfo)
@@ -530,6 +532,6 @@ void Write (OStream *file, const Image *image, const Image::WriteOptions& option
} // end of namespace Jpeg
-}
+} // end of namespace pov_base
#endif // LIBJPEG_MISSING
diff --git a/source/base/image/png.cpp b/source/base/image/png.cpp
index a98617d15..98f38de0b 100644
--- a/source/base/image/png.cpp
+++ b/source/base/image/png.cpp
@@ -43,12 +43,11 @@
#ifndef LIBPNG_MISSING
+// C++ standard headers
#include
+#include
-// Boost header files
-#include
-#include
-
+// other 3rd party library headers
#include
// POV-Ray base header files
@@ -666,23 +665,29 @@ Image *Read (IStream *file, const Image::ReadOptions& options)
return (image) ;
}
+void SetChannelValue(png_bytep& p, unsigned int v, unsigned int bpcc)
+{
+ if (bpcc > 8)
+ *(p++) = ((v >> 8) & 0xFF);
+ *(p++) = (v & 0xFF);
+}
+
void Write (OStream *file, const Image *image, const Image::WriteOptions& options)
{
- int himask;
int png_stride;
int width = image->GetWidth() ;
int height = image->GetHeight() ;
- int j;
int bpcc = options.bitsPerChannel;
bool use_alpha = image->HasTransparency() && options.AlphaIsEnabled();
- unsigned int color;
+ unsigned int octetDepth = ((bpcc + 7) / 8);
+ unsigned int bitDepth = 8 * octetDepth;
unsigned int alpha;
unsigned int r;
unsigned int g;
unsigned int b;
unsigned int maxValue;
- unsigned int hiShift;
- unsigned int loShift;
+ unsigned int mult;
+ unsigned int shift;
png_info *info_ptr = nullptr;
png_struct *png_ptr = nullptr;
Messages messages;
@@ -697,17 +702,11 @@ void Write (OStream *file, const Image *image, const Image::WriteOptions& option
// (e.g. to handle a non-compliant file).
bool premul = options.AlphaIsPremultiplied(false);
- if (bpcc == 0)
+ if (bpcc <= 0)
bpcc = image->GetMaxIntValue() == 65535 ? 16 : 8 ;
- else if (bpcc < 5)
- bpcc = 5 ;
else if (bpcc > 16)
bpcc = 16 ;
- // special case: if options.grayscale is set, we enforce 16bpp irregardless of other settings
- if (options.grayscale)
- bpcc = 16;
-
maxValue = (1< row_ptr(new png_byte[width*png_stride]);
- boost::scoped_array row_ptr (new png_byte [width*png_stride]);
+ int repeat = (bitDepth + bpcc - 1) / bpcc;
+ shift = (bpcc * repeat) - bitDepth;
+ mult = 0x01;
+ for (int i = 1; i < repeat; ++i)
+ mult = (mult << bpcc) | 0x01;
for (int row = 0 ; row < height ; row++)
{
- /*
- * We must copy all the values because PNG expects RGBRGB bytes, but
- * POV-Ray stores RGB components in separate arrays as floats. In
- * order to use the full scale values at the lower bit depth, PNG
- * recommends filling the low-order bits with a copy of the high-order
- * bits. However, studies have shown that filling the low order bits
- * with constant bits significantly improves compression, which we're
- * doing here. Note that since the true bit depth is stored in the
- * sBIT chunk, the extra packed bits are not important.
- */
-
- switch (bpcc)
+ auto p = row_ptr.get();
+ for (int col = 0; col < width; ++col)
{
- case 5:
- case 6:
- case 7:
- // Handle shifting for arbitrary output bit depth
- hiShift = 8 - bpcc;
- loShift = 2*bpcc - 8;
- if (use_color)
- {
- for (int col = j = 0; col < width; col++, j += png_stride)
- {
- if (use_alpha)
- {
- GetEncodedRGBAValue (image, col, row, gamma, maxValue, r, g, b, alpha, dither, premul);
- row_ptr[j + 3] = alpha << hiShift;
- row_ptr[j + 3] |= alpha >> loShift;
- }
- else
- GetEncodedRGBValue (image, col, row, gamma, maxValue, r, g, b, dither);
-
- row_ptr[j] = r << hiShift;
- row_ptr[j] |= r >> loShift;
-
- row_ptr[j+1] = g << hiShift;
- row_ptr[j+1] |= g >> loShift;
-
- row_ptr[j+2] = b << hiShift;
- row_ptr[j+2] |= b >> loShift;
- }
- }
- else
- {
- for (int col = j = 0; col < width; col++, j += png_stride)
- {
- if (use_alpha)
- {
- GetEncodedGrayAValue (image, col, row, gamma, maxValue, color, alpha, dither, premul);
- row_ptr[j + 1] = alpha << hiShift;
- row_ptr[j + 1] |= alpha >> loShift;
- }
- else
- color = GetEncodedGrayValue (image, col, row, gamma, maxValue, dither) ;
-
- // Use left-bit replication (LBR) for bit depths < 8
- row_ptr[j] = color << hiShift;
- row_ptr[j] |= color >> loShift;
- }
- }
- break;
-
- case 8:
- if (use_color)
- {
- for (int col = j = 0; col < width; col++, j += png_stride)
- {
- if (use_alpha)
- {
- GetEncodedRGBAValue (image, col, row, gamma, maxValue, r, g, b, alpha, dither, premul);
- row_ptr[j + 3] = alpha;
- }
- else
- GetEncodedRGBValue (image, col, row, gamma, maxValue, r, g, b, dither) ;
- row_ptr[j] = r;
- row_ptr[j + 1] = g;
- row_ptr[j + 2] = b;
- }
- }
- else
- {
- for (int col = j = 0; col < width; col++, j += png_stride)
- {
- if (use_alpha)
- {
- GetEncodedGrayAValue (image, col, row, gamma, maxValue, color, alpha, dither, premul);
- row_ptr[j + 1] = alpha;
- }
- else
- color = GetEncodedGrayValue (image, col, row, gamma, maxValue, dither) ;
- row_ptr[j] = color;
- }
- }
- break;
-
- case 16:
- if (use_color)
- {
- for (int col = j = 0; col < width; col++, j += png_stride)
- {
- if (use_alpha)
- {
- GetEncodedRGBAValue (image, col, row, gamma, maxValue, r, g, b, alpha, dither, premul);
- row_ptr[j+6] = alpha >> 8;
- row_ptr[j+7] = alpha & 0xff;
- }
- else
- GetEncodedRGBValue (image, col, row, gamma, maxValue, r, g, b, dither) ;
-
- row_ptr[j] = r >> 8;
- row_ptr[j + 1] = r & 0xFF;
-
- row_ptr[j + 2] = g >> 8;
- row_ptr[j + 3] = g & 0xFF;
-
- row_ptr[j + 4] = b >> 8;
- row_ptr[j + 5] = b & 0xFF;
- }
- }
- else
- {
- for (int col = j = 0; col < width; col++, j += png_stride)
- {
- if (use_alpha)
- {
- GetEncodedGrayAValue (image, col, row, gamma, maxValue, color, alpha, dither, premul);
- row_ptr[j+2] = alpha >> 8;
- row_ptr[j+3] = alpha & 0xff;
- }
- else
- color = GetEncodedGrayValue (image, col, row, gamma, maxValue, dither) ;
- row_ptr[j] = color >> 8;
- row_ptr[j+1] = color & 0xff;
- }
- }
- break;
-
- default: // bpcc 9 - 15
- // Handle shifting for arbitrary output bit depth
- hiShift = 16 - bpcc;
- loShift = 2*bpcc - 16;
- himask = 0xFF ^ ((1 << hiShift) - 1);
- if (use_color)
- {
- for (int col = j = 0; col < width; col++, j += png_stride)
- {
- if (use_alpha)
- {
- GetEncodedRGBAValue (image, col, row, gamma, maxValue, r, g, b, alpha, dither, premul);
- row_ptr[j + 6] = alpha >> (8 - hiShift);
- row_ptr[j + 7] = alpha << hiShift;
- row_ptr[j + 7] |= alpha >> loShift;
- }
- else
- GetEncodedRGBValue (image, col, row, gamma, maxValue, r, g, b, dither) ;
-
- row_ptr[j] = r >> (8 - hiShift);
- row_ptr[j + 1] = r << hiShift;
- row_ptr[j + 1] |= r >> loShift;
-
- row_ptr[j + 2] = g >> (8 - hiShift);
- row_ptr[j + 3] = g << hiShift;
- row_ptr[j + 3] |= g >> loShift;
+ if (use_color && use_alpha)
+ GetEncodedRGBAValue(image, col, row, gamma, maxValue, r, g, b, alpha, dither, premul);
+ else if (use_color)
+ GetEncodedRGBValue(image, col, row, gamma, maxValue, r, g, b, dither);
+ else if (use_alpha)
+ GetEncodedGrayAValue(image, col, row, gamma, maxValue, g, alpha, dither, premul);
+ else
+ g = GetEncodedGrayValue(image, col, row, gamma, maxValue, dither);
- row_ptr[j + 4] = b >> (8 - hiShift);
- row_ptr[j + 5] = b << hiShift;
- row_ptr[j + 5] |= b >> loShift;
- }
- }
- else
- {
- for (int col = j = 0; col < width; col++, j += png_stride)
- {
- if (use_alpha)
- {
- GetEncodedGrayAValue (image, col, row, gamma, maxValue, color, alpha, dither, premul);
- row_ptr[j + 2] = alpha >> (8 - hiShift);
- row_ptr[j + 3] = alpha << hiShift;
- row_ptr[j + 3] |= alpha >> loShift;
- }
- else
- color = GetEncodedGrayValue (image, col, row, gamma, maxValue, dither) ;
+ if (use_color)
+ {
+ SetChannelValue(p, (r * mult) >> shift, bpcc);
+ SetChannelValue(p, (g * mult) >> shift, bpcc);
+ SetChannelValue(p, (b * mult) >> shift, bpcc);
+ }
+ else
+ {
+ SetChannelValue(p, (g * mult) >> shift, bpcc);
+ }
- row_ptr[j] = color >> (8 - hiShift);
- row_ptr[j + 1] = color << hiShift;
- row_ptr[j + 1] |= color >> loShift;
- }
- }
+ if (use_alpha)
+ SetChannelValue(p, (alpha * mult) >> shift, bpcc);
}
if (setjmp(png_jmpbuf(png_ptr)))
@@ -1015,6 +857,6 @@ void Write (OStream *file, const Image *image, const Image::WriteOptions& option
} // end of namespace Png
-}
+} // end of namespace pov_base
#endif // LIBPNG_MISSING
diff --git a/source/base/image/ppm.cpp b/source/base/image/ppm.cpp
index f8fa357c2..6a16efa98 100644
--- a/source/base/image/ppm.cpp
+++ b/source/base/image/ppm.cpp
@@ -9,7 +9,7 @@
/// according to Netpbm specs (http://netpbm.sourceforge.net/doc/):
///
/// For input, both ASCII ("plain") and binary ("raw") formats (magic numbers
-/// `P2`/`P3` and `P5`/`P6`, respectively), are supported.
+/// `P2`/`P3` and `P5`/`P6`, respectively) are supported.
///
/// For outout we write binary ("raw") PPM files (magic number `P6`), unless
/// `Greyscale_Output=on` is specified in which case we write binary PGM files
@@ -104,10 +104,8 @@ void Write (OStream *file, const Image *image, const Image::WriteOptions& option
plainFormat = (options.compression == 0);
#endif
- if (bpcc == 0)
+ if (bpcc <= 0)
bpcc = image->GetMaxIntValue() == 65535 ? 16 : 8 ;
- else if (bpcc < 5)
- bpcc = 5 ;
else if (bpcc > 16)
bpcc = 16 ;
@@ -115,11 +113,9 @@ void Write (OStream *file, const Image *image, const Image::WriteOptions& option
// do we want 16 bit grayscale (PGM) output ?
// TODO - the check for image container type is here to mimick old code; do we still need it?
- if (image->GetImageDataType () == Image::Gray_Int16 || image->GetImageDataType () == Image::GrayA_Int16 || options.grayscale)
+ if (image->IsGrayscale() || options.grayscale)
{
- grayscale = true;
- bpcc = 16;
- gamma.reset(); // TODO - this is here to mimick old code, which never did gamma correction for greyscale output; do we want to change that?
+ grayscale = true;
if (plainFormat)
file->printf("P2\n");
else
@@ -400,5 +396,5 @@ Image *Read (IStream *file, const Image::ReadOptions& options)
} // end of namespace Netpbm
-}
+} // end of namespace pov_base
diff --git a/source/frontend/display.cpp b/source/frontend/display.cpp
index 749e7105c..865baa293 100644
--- a/source/frontend/display.cpp
+++ b/source/frontend/display.cpp
@@ -8,7 +8,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -42,10 +42,9 @@
namespace pov_frontend
{
-Display::Display(unsigned int w, unsigned int h, pov_base::GammaCurvePtr g) :
+Display::Display(unsigned int w, unsigned int h) :
width(w),
- height(h),
- gamma(g)
+ height(h)
{
// nothing to do
}
@@ -65,11 +64,6 @@ unsigned int Display::GetHeight()
return height;
}
-pov_base::GammaCurvePtr Display::GetGamma()
-{
- return pov_base::GammaCurvePtr(gamma);
-}
-
void Display::DrawRectangleFrame(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, const RGBA8& colour)
{
for(unsigned int x = x1; x <= x2; x++)
diff --git a/source/frontend/display.h b/source/frontend/display.h
index 0ae00f4a5..71c9a403b 100644
--- a/source/frontend/display.h
+++ b/source/frontend/display.h
@@ -8,7 +8,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -39,8 +39,6 @@
// Module config header file must be the first file included within POV-Ray unit header files
#include "frontend/configfrontend.h"
-#include "base/image/colourspace.h"
-
namespace pov_frontend
{
@@ -49,7 +47,7 @@ class Display
public:
struct RGBA8 { unsigned char red, green, blue, alpha; };
- Display(unsigned int w, unsigned int h, pov_base::GammaCurvePtr g);
+ Display(unsigned int w, unsigned int h);
virtual ~Display();
virtual void Initialise() = 0;
@@ -57,8 +55,6 @@ class Display
unsigned int GetWidth();
unsigned int GetHeight();
- pov_base::GammaCurvePtr GetGamma();
-
virtual void DrawPixel(unsigned int x, unsigned int y, const RGBA8& colour) = 0;
virtual void DrawRectangleFrame(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, const RGBA8& colour);
@@ -72,8 +68,6 @@ class Display
unsigned int width;
/// display height
unsigned int height;
- /// display gamma correction factor
- pov_base::GammaCurvePtr gamma;
/// not available
Display();
diff --git a/source/frontend/imagemessagehandler.cpp b/source/frontend/imagemessagehandler.cpp
index 8af713907..084601d86 100644
--- a/source/frontend/imagemessagehandler.cpp
+++ b/source/frontend/imagemessagehandler.cpp
@@ -36,9 +36,13 @@
// Unit header file must be the first file included within POV-Ray *.cpp files (pulls in config)
#include "frontend/imagemessagehandler.h"
+// POV-Ray header files (base module)
+#include "base/image/colourspace.h"
+#include "base/image/dither.h"
#include "base/image/encoding.h"
#include "base/image/image.h"
+// POV-Ray header files (frontend module)
#include "frontend/display.h"
#include "frontend/renderfrontend.h"
@@ -96,10 +100,6 @@ void ImageMessageHandler::DrawPixelSet(const SceneData& sd, const ViewData& vd,
if((pixelpositions.size() / 2) != (pixelcolors.size() / 5))
throw POV_EXCEPTION(kInvalidDataSizeErr, "Number of pixel colors and pixel positions does not match!");
- GammaCurvePtr gamma;
- if (vd.display != nullptr)
- gamma = vd.display->GetGamma();
-
for(int i = 0, ii = 0; (i < pixelcolors.size()) && (ii < pixelpositions.size()); i += 5, ii += 2)
{
RGBTColour col(pixelcolors[i], pixelcolors[i + 1], pixelcolors[i + 2], pixelcolors[i + 4]); // NB pixelcolors[i + 3] is an unused channel
@@ -114,12 +114,21 @@ void ImageMessageHandler::DrawPixelSet(const SceneData& sd, const ViewData& vd,
// TODO ALPHA - display may profit from receiving the data in its original, premultiplied form
// Premultiplied alpha was good for the math, but the display expects non-premultiplied alpha, so fix this if possible.
AlphaUnPremultiply(gcol);
- }
- rgba.red = IntEncode(gamma, gcol.red(), 255, dither);
- rgba.green = IntEncode(gamma, gcol.green(), 255, dither);
- rgba.blue = IntEncode(gamma, gcol.blue(), 255, dither);
- rgba.alpha = IntEncode( gcol.alpha(), 255, dither);
+ if (vd.greyscaleDisplay)
+ {
+ rgba.red = IntEncode(vd.displayGamma, gcol.Greyscale(), 255, dither);
+ rgba.green = rgba.red;
+ rgba.blue = rgba.red;
+ }
+ else
+ {
+ rgba.red = IntEncode(vd.displayGamma, gcol.red(), 255, dither);
+ rgba.green = IntEncode(vd.displayGamma, gcol.green(), 255, dither);
+ rgba.blue = IntEncode(vd.displayGamma, gcol.blue(), 255, dither);
+ }
+ rgba.alpha = IntEncode(gcol.alpha(), 255, dither);
+ }
if(psize == 1)
{
@@ -168,11 +177,6 @@ void ImageMessageHandler::DrawPixelBlockSet(const SceneData& sd, const ViewData&
cols.reserve(rect.GetArea());
rgbas.reserve(rect.GetArea());
- GammaCurvePtr gamma;
-
- if (vd.display != nullptr)
- gamma = vd.display->GetGamma();
-
for(i = 0; i < rect.GetArea() * 5; i += 5)
{
RGBTColour col(pixelvector[i], pixelvector[i + 1], pixelvector[i + 2], pixelvector[i + 4]); // NB pixelvector[i + 3] is an unused channel
@@ -187,15 +191,24 @@ void ImageMessageHandler::DrawPixelBlockSet(const SceneData& sd, const ViewData&
// TODO ALPHA - display may profit from receiving the data in its original, premultiplied form
// Premultiplied alpha was good for the math, but the display expects non-premultiplied alpha, so fix this if possible.
AlphaUnPremultiply(gcol);
- }
- rgba.red = IntEncode(gamma, gcol.red(), 255, dither);
- rgba.green = IntEncode(gamma, gcol.green(), 255, dither);
- rgba.blue = IntEncode(gamma, gcol.blue(), 255, dither);
- rgba.alpha = IntEncode( gcol.alpha(), 255, dither);
+ if (vd.greyscaleDisplay)
+ {
+ rgba.red = IntEncode(vd.displayGamma, gcol.Greyscale(), 255, dither);
+ rgba.green = rgba.red;
+ rgba.blue = rgba.red;
+ }
+ else
+ {
+ rgba.red = IntEncode(vd.displayGamma, gcol.red(), 255, dither);
+ rgba.green = IntEncode(vd.displayGamma, gcol.green(), 255, dither);
+ rgba.blue = IntEncode(vd.displayGamma, gcol.blue(), 255, dither);
+ }
+ rgba.alpha = IntEncode(gcol.alpha(), 255, dither);
+ rgbas.push_back(rgba);
+ }
cols.push_back(col);
- rgbas.push_back(rgba);
}
if (vd.display != nullptr)
diff --git a/source/frontend/imageprocessing.cpp b/source/frontend/imageprocessing.cpp
index c5236f718..cc5007105 100644
--- a/source/frontend/imageprocessing.cpp
+++ b/source/frontend/imageprocessing.cpp
@@ -36,10 +36,14 @@
// Unit header file must be the first file included within POV-Ray *.cpp files (pulls in config)
#include "frontend/imageprocessing.h"
-#include
+// Standard C++ header files
+#include
+// POV-Ray header files (base module)
+#include "base/image/dither.h"
#include "base/image/image.h"
+// POV-Ray header files (POVMS module)
#include "povms/povmsid.h"
// this must be the last file included
@@ -104,7 +108,7 @@ UCS2String ImageProcessing::WriteImage(POVMS_Object& ropts, POVMSInt frame, int
Image::ImageFileType imagetype = Image::SYS;
unsigned int filetype = POV_File_Image_System;
- wopts.bitsPerChannel = clip(ropts.TryGetInt(kPOVAttrib_BitsPerColor, 8), 5, 16);
+ wopts.bitsPerChannel = clip(ropts.TryGetInt(kPOVAttrib_BitsPerColor, 8), 1, 16);
wopts.alphaMode = (ropts.TryGetBool(kPOVAttrib_OutputAlpha, false) ? Image::kAlphaMode_Default : Image::kAlphaMode_None );
wopts.compression = (ropts.Exist(kPOVAttrib_Compression) ? clip(ropts.GetInt(kPOVAttrib_Compression), 0, 255) : -1);
wopts.grayscale = ropts.TryGetBool(kPOVAttrib_GrayscaleOutput, false);
@@ -173,9 +177,9 @@ UCS2String ImageProcessing::WriteImage(POVMS_Object& ropts, POVMSInt frame, int
wopts.workingGamma = GetGammaCurve(gammaType, gamma);
bool dither = ropts.TryGetBool(kPOVAttrib_Dither, false);
- DitherMethodId ditherMethod = kPOVList_DitherMethod_None;
+ DitherMethodId ditherMethod = DitherMethodId::kNone;
if (dither)
- ditherMethod = (DitherMethodId)ropts.TryGetInt(kPOVAttrib_DitherMethod, kPOVList_DitherMethod_FloydSteinberg);
+ ditherMethod = ropts.TryGetEnum(kPOVAttrib_DitherMethod, DitherMethodId::kBlueNoise);
wopts.ditherStrategy = GetDitherStrategy(ditherMethod, image->GetWidth());
// in theory this should always return a filename since the frontend code
@@ -184,7 +188,7 @@ UCS2String ImageProcessing::WriteImage(POVMS_Object& ropts, POVMSInt frame, int
if(filename.empty() == true)
filename = GetOutputFilename(ropts, frame, digits);
- boost::scoped_ptr imagefile(NewOStream(filename.c_str(), filetype, false)); // TODO - check file permissions somehow without macro [ttrf]
+ std::unique_ptr imagefile(NewOStream(filename.c_str(), filetype, false)); // TODO - check file permissions somehow without macro [ttrf]
if (imagefile == nullptr)
throw POV_EXCEPTION_CODE(kCannotOpenFileErr);
diff --git a/source/frontend/processrenderoptions.cpp b/source/frontend/processrenderoptions.cpp
index 0db38b434..162d58734 100644
--- a/source/frontend/processrenderoptions.cpp
+++ b/source/frontend/processrenderoptions.cpp
@@ -43,6 +43,7 @@
#include "base/fileutil.h"
#include "base/platformbase.h"
#include "base/image/colourspace.h"
+#include "base/image/dither.h"
#include "base/image/encoding.h"
// POV-Ray header files (POVMS module)
@@ -346,7 +347,6 @@ struct ProcessOptions::Cmd_Parser_Table RenderOptions_Cmd_Table[] =
{ nullptr }
};
-// TODO FIXME - The following are hacks of some sort, no idea what they are good for. They certainly use wrong types and probably contain other mistakes [trf]
extern struct ProcessRenderOptions::Parameter_Code_Table DitherMethodTable[];
ProcessRenderOptions::ProcessRenderOptions() : ProcessOptions(RenderOptions_INI_Table, RenderOptions_Cmd_Table)
@@ -577,25 +577,22 @@ int ProcessRenderOptions::ReadSpecialSwitchHandler(Cmd_Parser_Table *option, cha
err = POVMSUtil_SetInt(obj, option->key, intval);
file_type = *param++;
}
- if ((err == kNoErr) && (*param > ' '))
+ if ((err == kNoErr) && (tolower(*param) == 'g'))
{
- if (tolower(*param) == 'g')
+ if(!has16BitGrayscale)
{
- if(!has16BitGrayscale)
- {
- ParseError("Grayscale not currently supported with output file format '%c'.", file_type);
- err = kParamErr;
- }
- else
- {
- if ((err = POVMSUtil_SetBool(obj, kPOVAttrib_GrayscaleOutput, true)) == kNoErr && *++param > ' ')
- {
- ParseError("Unexpected '%s' following grayscale flag in +F%c option.", param, file_type);
- err = kParamErr;
- }
- }
+ ParseError("Grayscale not currently supported with output file format '%c'.", file_type);
+ err = kParamErr;
}
- else if (isdigit(*param) != 0)
+ else
+ {
+ err = POVMSUtil_SetBool(obj, kPOVAttrib_GrayscaleOutput, true);
+ ++param;
+ }
+ }
+ if ((err == kNoErr) && (*param > ' '))
+ {
+ if (isdigit(*param) != 0)
{
if (sscanf(param, "%d%n", &intval, &intval2) == 1)
{
@@ -1076,7 +1073,7 @@ struct ProcessRenderOptions::Output_FileType_Table FileTypeTable[] =
#endif // POV_SYS_IMAGE_TYPE
// [1] Alpha support for BMP uses an unofficial extension to the BMP file format, which is not recognized by
- // most image pocessing software.
+ // most image processing software.
// [2] While OpenEXR does support greyscale output at >8 bits, the variants currently supported by POV-Ray
// use 16-bit floating-point values with 10 bit mantissa, which might be insufficient for various purposes
@@ -1091,12 +1088,12 @@ struct ProcessRenderOptions::Parameter_Code_Table GammaTypeTable[] =
{
// code, internalId,
- { "BT709", kPOVList_GammaType_BT709 },
- { "BT2020", kPOVList_GammaType_BT2020 },
- { "SRGB", kPOVList_GammaType_SRGB },
+ { "BT709", kPOVList_GammaType_BT709, "ITU-R BT.709 transfer function" },
+ { "BT2020", kPOVList_GammaType_BT2020, "ITU-R BT.2020 transfer function" },
+ { "SRGB", kPOVList_GammaType_SRGB, "sRGB transfer function" },
// end-of-list marker
- { nullptr, 0 }
+ { nullptr, 0, "(unknown)" }
};
/* Supported dither types */
@@ -1104,15 +1101,23 @@ struct ProcessRenderOptions::Parameter_Code_Table DitherMethodTable[] =
{
// code, internalId,
- { "B2", kPOVList_DitherMethod_Bayer2x2 },
- { "B3", kPOVList_DitherMethod_Bayer3x3 },
- { "B4", kPOVList_DitherMethod_Bayer4x4 },
- { "D1", kPOVList_DitherMethod_Diffusion1D },
- { "D2", kPOVList_DitherMethod_Diffusion2D },
- { "FS", kPOVList_DitherMethod_FloydSteinberg },
+ { "AT", int(DitherMethodId::kAtkinson), "Atkinson error diffusion" },
+ { "B2", int(DitherMethodId::kBayer2x2), "2x2 Bayer pattern" },
+ { "B3", int(DitherMethodId::kBayer3x3), "3x3 Bayer pattern" },
+ { "B4", int(DitherMethodId::kBayer4x4), "4x4 Bayer pattern" },
+ { "BK", int(DitherMethodId::kBurkes), "Burkes error diffusion" },
+ { "BNX", int(DitherMethodId::kBlueNoiseX), "Blue noise pattern (inverted for Red/Blue)" },
+ { "BN", int(DitherMethodId::kBlueNoise), "Blue noise pattern" },
+ { "D1", int(DitherMethodId::kDiffusion1D), "Simple 1-D error diffusion" },
+ { "D2", int(DitherMethodId::kSierraLite), "Simple 2-D error diffusion (Sierra Lite)" },
+ { "FS", int(DitherMethodId::kFloydSteinberg), "Floyd-Steinberg error diffusion" },
+ { "JN", int(DitherMethodId::kJarvisJudiceNinke),"Jarvis-Judice-Ninke error diffusion" },
+ { "S2", int(DitherMethodId::kSierra2), "Two-row Sierra error diffusion" },
+ { "S3", int(DitherMethodId::kSierra3), "Sierra error diffusion" },
+ { "ST", int(DitherMethodId::kStucki), "Stucki error diffusion" },
// end-of-list marker
- { nullptr, 0 }
+ { nullptr, 0, "(unknown)" }
};
int ProcessRenderOptions::ParseFileType(char code, POVMSType attribute, int* pInternalId, bool* pHas16BitGreyscale)
@@ -1173,6 +1178,16 @@ const char* ProcessRenderOptions::UnparseGammaType(int gammaType)
return UnparseParameterCode(GammaTypeTable, gammaType);
}
+const char* ProcessRenderOptions::GetGammaTypeText(int gammaType)
+{
+ return GetParameterCodeText(GammaTypeTable, gammaType);
+}
+
+const char* ProcessRenderOptions::GetDitherMethodText(int ditherMethod)
+{
+ return GetParameterCodeText(DitherMethodTable, ditherMethod);
+}
+
int ProcessRenderOptions::ParseParameterCode(const ProcessRenderOptions::Parameter_Code_Table* codeTable, char* code, int* pInternalId)
{
for (int i = 0; code[i] != '\0'; i ++)
@@ -1196,4 +1211,13 @@ const char* ProcessRenderOptions::UnparseParameterCode(const ProcessRenderOption
return nullptr;
}
+const char* ProcessRenderOptions::GetParameterCodeText(const ProcessRenderOptions::Parameter_Code_Table* codeTable, int internalId)
+{
+ int i;
+ for (i = 0; codeTable[i].code != nullptr; i++)
+ if (internalId == codeTable[i].internalId)
+ break;
+ return codeTable[i].text;
+}
+
}
diff --git a/source/frontend/processrenderoptions.h b/source/frontend/processrenderoptions.h
index 4e4b80f09..4c2d7995b 100644
--- a/source/frontend/processrenderoptions.h
+++ b/source/frontend/processrenderoptions.h
@@ -79,14 +79,18 @@ class ProcessRenderOptions : public ProcessOptions
{
const char* code; // code used in INI and command line options
int internalId; // e.g. kPOVList_GammaType_*
+ const char* text; // human-readable text
};
int ParseFileType(char, POVMSType, int*, bool* pHas16BitGreyscale = nullptr);
- char UnparseFileType(int);
+ static char UnparseFileType(int);
int ParseGammaType(char*, int*);
- const char* UnparseGammaType(int);
- int ParseParameterCode(const ProcessRenderOptions::Parameter_Code_Table* codeTable, char* code, int* pInternalId);
- const char* UnparseParameterCode(const ProcessRenderOptions::Parameter_Code_Table* codeTable, int internalId);
+ static const char* UnparseGammaType(int);
+ static const char* GetGammaTypeText(int);
+ static const char* GetDitherMethodText(int);
+ static int ParseParameterCode(const ProcessRenderOptions::Parameter_Code_Table* codeTable, char* code, int* pInternalId);
+ static const char* UnparseParameterCode(const ProcessRenderOptions::Parameter_Code_Table* codeTable, int internalId);
+ static const char* GetParameterCodeText(const ProcessRenderOptions::Parameter_Code_Table* codeTable, int internalId);
};
}
diff --git a/source/frontend/renderfrontend.cpp b/source/frontend/renderfrontend.cpp
index bc6c6e388..4c9aa1adb 100644
--- a/source/frontend/renderfrontend.cpp
+++ b/source/frontend/renderfrontend.cpp
@@ -36,17 +36,23 @@
// Unit header file must be the first file included within POV-Ray *.cpp files (pulls in config)
#include "frontend/renderfrontend.h"
-#include
+// Standard C++ header files
+#include
+// POV-Ray header files (base module)
#include "base/platformbase.h"
#include "base/textstream.h"
#include "base/textstreambuffer.h"
+#include "base/image/dither.h"
#include "base/image/encoding.h"
+// POV-Ray header files (POVMS module)
#include "povms/povmsid.h"
+// POV-Ray header files (frontend module)
#include "frontend/console.h"
#include "frontend/processoptions.h"
+#include "frontend/processrenderoptions.h"
// this must be the last file included
#include "base/povdebug.h"
@@ -644,7 +650,7 @@ void RenderFrontendBase::ContinueBackup(POVMS_Object& ropts, ViewData& vd, ViewI
vd.imageBackup.reset();
MakeBackupPath(ropts, vd, outputpath);
- boost::scoped_ptr inbuffer(new IFileStream(vd.imageBackupFile().c_str()));
+ std::unique_ptr inbuffer(new IFileStream(vd.imageBackupFile().c_str()));
size_t pos = sizeof(Backup_File_Header);
@@ -1158,19 +1164,9 @@ void OutputOptions(POVMS_Object& cppmsg, TextStreamBuffer *tsb)
(void)POVMSUtil_GetBool(msg, kPOVAttrib_Dither, &b);
if (b)
{
- i = kPOVList_DitherMethod_FloydSteinberg;
+ i = int(DitherMethodId::kBlueNoise);
(void)POVMSUtil_GetInt(msg, kPOVAttrib_DitherMethod, &i);
- switch(i)
- {
- // TODO FIXME - for easier maintenance, this should probably be part of the DitherMethodTable.
- case kPOVList_DitherMethod_Bayer2x2: t = "2x2 Bayer pattern"; break;
- case kPOVList_DitherMethod_Bayer3x3: t = "3x3 Bayer pattern"; break;
- case kPOVList_DitherMethod_Bayer4x4: t = "4x4 Bayer pattern"; break;
- case kPOVList_DitherMethod_Diffusion1D: t = "simple 1-D error diffusion"; break;
- case kPOVList_DitherMethod_Diffusion2D: t = "simple 2-D error diffusion"; break;
- case kPOVList_DitherMethod_FloydSteinberg: t = "Floyd-Steinberg error diffusion"; break;
- default: t = "(unknown)"; break;
- }
+ t = ProcessRenderOptions::GetDitherMethodText(i);
tsb->printf(" Dithering............%s\n", t);
}
else
@@ -1196,17 +1192,10 @@ void OutputOptions(POVMS_Object& cppmsg, TextStreamBuffer *tsb)
case kPOVList_GammaType_PowerLaw:
tsb->printf(" Graphic display......On (gamma: %g)\n", (float)f);
break;
- case kPOVList_GammaType_SRGB:
- tsb->printf(" Graphic display......On (gamma: sRGB)\n");
- break;
- case kPOVList_GammaType_BT709:
- tsb->printf(" Graphic display......On (gamma: ITU-R BT.709)\n");
- break;
- case kPOVList_GammaType_BT2020:
- tsb->printf(" Graphic display......On (gamma: ITU-R BT.2020)\n");
- break;
default:
- throw POV_EXCEPTION_STRING("Unknown gamma mode in OutputOptions()");
+ t = ProcessRenderOptions::GetGammaTypeText(i);
+ tsb->printf(" Graphic display......On (gamma: %s)\n", t);
+ break;
}
}
else
diff --git a/source/frontend/renderfrontend.h b/source/frontend/renderfrontend.h
index 30074d901..92ff2db22 100644
--- a/source/frontend/renderfrontend.h
+++ b/source/frontend/renderfrontend.h
@@ -46,7 +46,6 @@
#include "base/path.h"
#include "base/platformbase.h"
-#include "base/image/colourspace.h"
#include "base/image/image.h"
#include "frontend/console.h"
@@ -143,6 +142,8 @@ struct ViewData
mutable shared_ptr image;
mutable shared_ptr display;
mutable shared_ptr imageBackup;
+ GammaCurvePtr displayGamma;
+ bool greyscaleDisplay;
Path imageBackupFile;
};
@@ -286,7 +287,7 @@ class RenderFrontend : public RenderFrontendBase
void ResumeParser(SceneId sid);
void StopParser(SceneId sid);
- ViewId CreateView(SceneId sid, POVMS_Object& obj, shared_ptr& imageProcessing, boost::function fn);
+ ViewId CreateView(SceneId sid, POVMS_Object& obj, shared_ptr& imageProcessing, boost::function fn);
void CloseView(ViewId vid);
ViewData::ViewState GetViewState(ViewId vid);
@@ -435,7 +436,7 @@ void RenderFrontend::StopParser(SceneId
}
template
-RenderFrontendBase::ViewId RenderFrontend::CreateView(SceneId sid, POVMS_Object& obj, shared_ptr& imageProcessing, boost::function fn)
+RenderFrontendBase::ViewId RenderFrontend::CreateView(SceneId sid, POVMS_Object& obj, shared_ptr& imageProcessing, boost::function fn)
{
typename SceneHandlerMap::iterator shi(scenehandler.find(sid));
@@ -522,6 +523,9 @@ RenderFrontendBase::ViewId RenderFrontend(fn(width, height, gamma));
+ vh.data.display = shared_ptr(fn(width, height));
viewhandler[vid] = vh;
view2scene[vid] = sid;
diff --git a/source/frontend/simplefrontend.h b/source/frontend/simplefrontend.h
index 026d81298..6e39fcceb 100644
--- a/source/frontend/simplefrontend.h
+++ b/source/frontend/simplefrontend.h
@@ -85,7 +85,7 @@ class SimpleFrontend
public:
SimpleFrontend(POVMSContext ctx, POVMSAddress addr, POVMS_Object& msg,
boost::function cfn,
- boost::function dfn,
+ boost::function dfn,
POVMS_Object *result = nullptr, shared_ptr console = shared_ptr());
~SimpleFrontend();
@@ -112,13 +112,13 @@ class SimpleFrontend
shared_ptr animationProcessing;
shared_ptr shelloutProcessing;
boost::function createConsole;
- boost::function createDisplay;
+ boost::function createDisplay;
};
template
SimpleFrontend::SimpleFrontend(POVMSContext ctx, POVMSAddress addr, POVMS_Object& msg,
boost::function cfn,
- boost::function dfn,
+ boost::function dfn,
POVMS_Object *result, shared_ptr console) :
renderFrontend(ctx),
backendAddress(addr),
diff --git a/source/parser/parser.cpp b/source/parser/parser.cpp
index a5b878ffa..fd08ba2cd 100644
--- a/source/parser/parser.cpp
+++ b/source/parser/parser.cpp
@@ -11220,13 +11220,13 @@ bool Parser::POV_ARRAY::HasElement(size_t i) const
const TokenId& Parser::POV_ARRAY::ElementType(size_t i) const
{
- POV_PARSER_ASSERT(i < DataPtrs.size());
+ POV_PARSER_ASSERT(resizable || (i < DataPtrs.size()));
return (mixedType ? Types[i] : Type_);
}
TokenId& Parser::POV_ARRAY::ElementType(size_t i)
{
- POV_PARSER_ASSERT(i < DataPtrs.size());
+ POV_PARSER_ASSERT(resizable || (i < DataPtrs.size()));
return (mixedType ? Types[i] : Type_);
}
diff --git a/source/parser/parser_tokenizer.cpp b/source/parser/parser_tokenizer.cpp
index eb00c53cd..3d45f3d78 100644
--- a/source/parser/parser_tokenizer.cpp
+++ b/source/parser/parser_tokenizer.cpp
@@ -627,6 +627,7 @@ void Parser::Read_Symbol(const RawToken& rawToken)
haveNextRawToken = PeekRawToken(nextRawToken);
+ SYM_TABLE* parentTable = table;
if (pseudoDictionary >= 0)
{
table = Tables [pseudoDictionary];
@@ -686,6 +687,7 @@ void Parser::Read_Symbol(const RawToken& rawToken)
else
{
breakLoop = true;
+ table = parentTable;
break;
}
@@ -2870,8 +2872,11 @@ void Parser::Parse_Initalizer (int Sub, size_t Base, POV_ARRAY *a)
CASE (RIGHT_CURLY_TOKEN)
if (a->resizable)
+ {
+ finalParameter = true;
// We reserved one element too many.
a->Shrink();
+ }
else
{
if (!(finalParameter && properlyDelimited))
diff --git a/source/povmain.cpp b/source/povmain.cpp
index eb0e0bd85..6e0eb3a0c 100644
--- a/source/povmain.cpp
+++ b/source/povmain.cpp
@@ -42,7 +42,6 @@
#include "frontend/configfrontend.h"
#include "base/timer.h"
-#include "base/image/colourspace.h"
#include "backend/povray.h"
@@ -72,23 +71,23 @@ class DefaultConsole : public pov_frontend::Console
class DefaultDisplay : public pov_frontend::Display
{
public:
- DefaultDisplay(unsigned int w, unsigned int h, pov_base::GammaCurvePtr g) : Display(w, h, g) { }
+ DefaultDisplay(unsigned int w, unsigned int h) : Display(w, h) { }
~DefaultDisplay() { }
void Initialise() { }
void DrawPixel(unsigned int, unsigned int, const RGBA8&) { }
};
pov_frontend::Console *CreateDefaultConsole();
-pov_frontend::Display *CreateDefaultDisplay(unsigned int w, unsigned int h, pov_base::GammaCurvePtr gf);
+pov_frontend::Display *CreateDefaultDisplay(unsigned int w, unsigned int h);
pov_frontend::Console *CreateDefaultConsole()
{
return new DefaultConsole();
}
-pov_frontend::Display *CreateDefaultDisplay(unsigned int w, unsigned int h, pov_base::GammaCurvePtr gf)
+pov_frontend::Display *CreateDefaultDisplay(unsigned int w, unsigned int h)
{
- return new DefaultDisplay(w, h, gf);
+ return new DefaultDisplay(w, h);
}
void BackendExitCallback()
@@ -125,7 +124,7 @@ int main(int argc, char **argv)
POVMS_Object backendMessage;
SimpleFrontend
frontend(frontendContext, backendAddress, backendMessage,
- boost::bind(CreateDefaultConsole), boost::bind(CreateDefaultDisplay, _1, _2, _3));
+ boost::bind(CreateDefaultConsole), boost::bind(CreateDefaultDisplay, _1, _2));
// Print help screens
if(argc == 1)
diff --git a/source/povms/povmscpp.h b/source/povms/povmscpp.h
index e04226006..f0e7c44f4 100644
--- a/source/povms/povmscpp.h
+++ b/source/povms/povmscpp.h
@@ -252,6 +252,8 @@ class POVMS_Object : public POVMS_Container
POVMSUCS2String TryGetUCS2String(POVMSType key, const char *alt);
POVMSUCS2String TryGetUCS2String(POVMSType key, const POVMSUCS2String& alt);
POVMSInt TryGetInt(POVMSType key, POVMSInt alt);
+ template T TryGetEnum(POVMSType key, T alt)
+ { return static_cast(TryGetInt(key, static_cast(alt))); }
POVMSLong TryGetLong(POVMSType key, POVMSLong alt);
POVMSFloat TryGetFloat(POVMSType key, POVMSFloat alt);
POVMSBool TryGetBool(POVMSType key, POVMSBool alt);
diff --git a/tools/meta-make/Makefile b/tools/meta-make/Makefile
new file mode 100644
index 000000000..0aeafb9aa
--- /dev/null
+++ b/tools/meta-make/Makefile
@@ -0,0 +1,95 @@
+#*************************************************************************************
+# Location of Stuff
+
+# Location of everything
+rootdir = ../..
+
+# Location of generated source files
+fontsrc = $(rootdir)/source/base/font
+scenesrc = $(rootdir)/source/backend/control
+bluenoisesrc = $(rootdir)/source/base/data
+
+# Location of input files
+include = $(rootdir)/distribution/include
+scenes = $(rootdir)/distribution/scenes
+
+# Location of tools
+metamake = $(rootdir)/tools/meta-make
+reswrap_header = $(metamake)/reswrap-header.cpp
+reswrap_sh = $(metamake)/reswrap.sh
+metagen_header = $(metamake)/metagen-header.cpp
+metagen_sh = $(metamake)/metagen.sh
+
+#*************************************************************************************
+# What to Generate
+
+FONTS = crystal cyrvetic povlogo timrom
+BENCHMARK = benchmark_pov benchmark_ini
+BLUENOISE = bluenoise64a
+
+#-------------------------------------------------------------------------------------
+# Generation Parameters
+
+# File Identifier Seed Size [Slices]
+bluenoise64a_opts = kBlueNoise64a 4711 64
+
+#-------------------------------------------------------------------------------------
+# Files to Generate
+
+FONTFILES = $(foreach font,$(FONTS),$(addprefix $(fontsrc)/$(font),.cpp .h))
+BENCHMARKFILES = $(foreach scene,$(BENCHMARK),$(addprefix $(scenesrc)/$(scene),.cpp .h))
+BLUENOISEFILES = $(foreach noise,$(BLUENOISE),$(addprefix $(bluenoisesrc)/$(noise),.cpp .h))
+
+ALLFILES = $(FONTFILES) $(BENCHMARKFILES) $(BLUENOISEFILES)
+
+#*************************************************************************************
+# Rules
+
+.PHONY: all
+all: $(ALLFILES)
+
+.PHONY: clean
+clean:
+ -rm $(ALLFILES)
+
+#-------------------------------------------------------------------------------------
+# Fonts
+
+vpath %.ttf $(include)
+
+$(fontsrc)/%.cpp: %.ttf $(reswrap_header)
+ $(reswrap_sh) "$@" "$<" "pov_base" "font_$(*F)" "-z"
+
+$(fontsrc)/%.h: %.ttf $(reswrap_header)
+ $(reswrap_sh) "$@" "$<" "pov_base" "font_$(*F)" "-z"
+
+#-------------------------------------------------------------------------------------
+# Scenes
+
+vpath %.ini $(scenes)
+vpath %.pov $(scenes)
+
+$(scenesrc)/benchmark_ini.%: advanced/benchmark/benchmark.ini $(reswrap_header)
+ $(reswrap_sh) "$@" "$<" "pov" "Benchmark_Options" "-m -ta -c 120"
+
+$(scenesrc)/benchmark_pov.%: advanced/benchmark/benchmark.pov $(reswrap_header)
+ $(reswrap_sh) "$@" "$<" "pov" "Benchmark_File" "-m -ta -c 120"
+
+#-------------------------------------------------------------------------------------
+# Blue Noise Patterns
+
+$(BLUENOISEFILES): GENOPTS = $($(basename $(@F))_opts)
+$(BLUENOISEFILES): NAME = $(word 1, $(GENOPTS))
+$(BLUENOISEFILES): SEED = $(word 2, $(GENOPTS))
+$(BLUENOISEFILES): SIZE = $(word 3, $(GENOPTS))
+$(BLUENOISEFILES): SLICES = $(word 4, $(GENOPTS))
+
+define metagen_bluenoise =
+$(metagen_sh) "$@" "Blue noise pattern data" "python $(metamake)/bluenoise/metagen-bluenoise.py"
+endef
+
+$(BLUENOISEFILES): %: $(metagen_header)
+ $(metagen_bluenoise) "-l -npov_base -r$(SEED) $(NAME) $(SIZE) $(SIZE) $(SLICES)" "-h"
+
+#*************************************************************************************
+# End of File
diff --git a/tools/meta-make/bluenoise/BlueNoise.py b/tools/meta-make/bluenoise/BlueNoise.py
new file mode 100644
index 000000000..03bdf27bc
--- /dev/null
+++ b/tools/meta-make/bluenoise/BlueNoise.py
@@ -0,0 +1,418 @@
+# BlueNoise.py - An implementation of the void and cluster method for generation of
+# blue noise dither arrays and related utilities.
+#
+# Written in 2016 by Christoph Peters, Christoph(at)MomentsInGraphics.de
+#
+# To the extent possible under law, the author(s) have dedicated all copyright and
+# related and neighboring rights to this software to the public domain worldwide.
+# This software is distributed without any warranty.
+#
+# You should have received a copy of the CC0 Public Domain Dedication along with
+# this software. If not, see .
+
+from os import path,makedirs
+import numpy as np
+from scipy import ndimage
+from matplotlib import pyplot
+import png
+import threading
+import struct
+
+def GetBayerPattern(Log2Width):
+ """Creates a two-dimensional Bayer pattern with a width and height of
+ 2**Log2Width."""
+ X,Y=np.meshgrid(range(2**Log2Width),range(2**Log2Width));
+ Result=np.zeros_like(X);
+ for i in range(Log2Width):
+ StripesY=np.where(np.bitwise_and(Y,2**(Log2Width-1-i))!=0,1,0);
+ StripesX=np.where(np.bitwise_and(X,2**(Log2Width-1-i))!=0,1,0);
+ Checker=np.bitwise_xor(StripesX,StripesY);
+ Result+=np.bitwise_or(StripesY*2**(2*i),Checker*2**(2*i+1));
+ return Result;
+
+
+def FindLargestVoid(BinaryPattern,StandardDeviation):
+ """This function returns the indices of the largest void in the given binary
+ pattern as defined by Ulichney.
+ \param BinaryPattern A boolean array (should be two-dimensional although the
+ implementation works in arbitrary dimensions).
+ \param StandardDeviation The standard deviation used for the Gaussian filter
+ in pixels. This can be a single float for an isotropic Gaussian or a
+ tuple with one float per dimension for an anisotropic Gaussian.
+ \return A flat index i such that BinaryPattern.flat[i] corresponds to the
+ largest void. By definition this is a majority pixel.
+ \sa GetVoidAndClusterBlueNoise"""
+ # The minority value is always True for convenience
+ if(np.count_nonzero(BinaryPattern)*2>=np.size(BinaryPattern)):
+ BinaryPattern=np.logical_not(BinaryPattern);
+ # Apply the Gaussian. We do not want to cut off the Gaussian at all because even
+ # the tiniest difference can change the ranking. Therefore we apply the Gaussian
+ # through a fast Fourier transform by means of the convolution theorem.
+ FilteredArray=np.fft.ifftn(ndimage.fourier.fourier_gaussian(np.fft.fftn(np.where(BinaryPattern,1.0,0.0)),StandardDeviation)).real;
+ # Find the largest void
+ return np.argmin(np.where(BinaryPattern,2.0,FilteredArray));
+
+
+def FindTightestCluster(BinaryPattern,StandardDeviation):
+ """Like FindLargestVoid() but finds the tightest cluster which is a minority
+ pixel by definition.
+ \sa GetVoidAndClusterBlueNoise"""
+ if(np.count_nonzero(BinaryPattern)*2>=np.size(BinaryPattern)):
+ BinaryPattern=np.logical_not(BinaryPattern);
+ FilteredArray=np.fft.ifftn(ndimage.fourier.fourier_gaussian(np.fft.fftn(np.where(BinaryPattern,1.0,0.0)),StandardDeviation)).real;
+ return np.argmax(np.where(BinaryPattern,FilteredArray,-1.0));
+
+
+def GetVoidAndClusterBlueNoise(OutputShape,StandardDeviation=1.5,InitialSeedFraction=0.1):
+ """Generates a blue noise dither array of the given shape using the method
+ proposed by Ulichney [1993] in "The void-and-cluster method for dither array
+ generation" published in Proc. SPIE 1913.
+ \param OutputShape The shape of the output array. This function works in
+ arbitrary dimension, i.e. OutputShape can have arbitrary length. Though
+ it is only tested for the 2D case where you should pass a tuple
+ (Height,Width).
+ \param StandardDeviation The standard deviation in pixels used for the
+ Gaussian filter defining largest voids and tightest clusters. Larger
+ values lead to more low-frequency content but better isotropy. Small
+ values lead to more ordered patterns with less low-frequency content.
+ Ulichney proposes to use a value of 1.5. If you want an anisotropic
+ Gaussian, you can pass a tuple of length len(OutputShape) with one
+ standard deviation per dimension.
+ \param InitialSeedFraction The only non-deterministic step in the algorithm
+ marks a small number of pixels in the grid randomly. This parameter
+ defines the fraction of such points. It has to be positive but less
+ than 0.5. Very small values lead to ordered patterns, beyond that there
+ is little change.
+ \return An integer array of shape OutputShape containing each integer from 0
+ to np.prod(OutputShape)-1 exactly once."""
+ nRank=np.prod(OutputShape);
+ # Generate the initial binary pattern with a prescribed number of ones
+ nInitialOne=max(1,min(int((nRank-1)/2),int(nRank*InitialSeedFraction)));
+ # Start from white noise (this is the only randomized step)
+ InitialBinaryPattern=np.zeros(OutputShape,dtype=np.bool);
+ InitialBinaryPattern.flat=np.random.permutation(np.arange(nRank))=np.cos(np.pi/32.0));
+ AngularPower[i]=np.sum(np.where(FullMask,np.abs(DFT),0.0))/np.count_nonzero(FullMask);
+ MeanAngularPower=np.mean(AngularPower[1:]);
+ DenseAngle=np.linspace(0.0,2.0*np.pi,256);
+ pyplot.plot(np.cos(DenseAngle)*MeanAngularPower,np.sin(DenseAngle)*MeanAngularPower,color=(0.7,0.7,0.7));
+ pyplot.plot(np.cos(BinningAngle)*AngularPower,np.sin(BinningAngle)*AngularPower);
+ return FigureList;
+
+
+def PlotBinaryPatterns(Texture,nPatternRow,nPatternColumn):
+ """This function creates a figure with a grid of thresholded versions of the
+ given 2D noise texture. It assumes that each value from 0 to
+ np.size(Texture)-1 is contained exactly once.
+ \return The created figure.
+ \note For the plots to show you have to invoke pyplot.show()."""
+ Figure=pyplot.figure();
+ nPattern=nPatternRow*nPatternColumn+1;
+ for i in range(1,nPattern):
+ Figure.add_subplot(nPatternRow,nPatternColumn,i,xticks=[],yticks=[]);
+ pyplot.imshow(np.where(Texture*nPattern4 the
+ superfluous channels are ignored. If nChannel<4 the data is expanded.
+ The alpha channel is set to 255, green and blue are filled with black
+ or with duplicates of red if nChannel==1. It is assumed that each
+ channel contains every integer value from 0 to nRank-1 exactly once.
+ The range of values is remapped linearly to span the range from 0 to
+ 255.
+ \param OutputPNGFilePath The path to the output png file including the file
+ format extension.
+ \param nRank Defaults to Width*Height if you pass a non-positive value."""
+ # Scale the array to an LDR version
+ if(nRank<=0):
+ nRank=Texture.shape[0]*Texture.shape[1];
+ Texture=np.asarray((Texture*256)//nRank,dtype=np.uint8);
+ # Get a three-dimensional array
+ if(len(Texture.shape)<3):
+ Texture=Texture[:,:,np.newaxis];
+ # Generate channels as needed
+ if(Texture.shape[2]==1):
+ Texture=np.dstack([Texture]*3+[255*np.ones_like(Texture[:,:,0])]);
+ elif(Texture.shape[2]==2):
+ Texture=np.dstack([Texture[:,:,0],Texture[:,:,1]]+[np.zeros_like(Texture[:,:,0])]+[255*np.ones_like(Texture[:,:,0])]);
+ elif(Texture.shape[2]==3):
+ Texture=np.dstack([Texture[:,:,0],Texture[:,:,1],Texture[:,:,2]]+[255*np.ones_like(Texture[:,:,0])]);
+ elif(Texture.shape[2]>4):
+ Texture=Texture[:,:,:4];
+ # Save the image
+ png.from_array(Texture,"RGBA;8").save(OutputPNGFilePath);
+
+
+def StoreNoiseTextureHDR(Texture,OutputPNGFilePath,nRank=-1):
+ """This function stores the given texture to an HDR png file with 16 bits per
+ channel and the specified number of channels.
+ \param Texture An array of shape (Height,Width) or (Height,Width,nChannel).
+ The former is handled like (Height,Width,1). It is assumed that each
+ channel contains each integer value from 0 to nRank-1 exactly once. The
+ range of values is remapped linearly to span the range from 0 to
+ 2**16-1 supported by the output format. nChannel can be 1, 2, 3 or 4.
+ \param OutputPNGFilePath The path to the output *.png file including the file
+ format extension.
+ \param nRank Defaults to Width*Height if you pass a non-positive value."""
+ # Scale the array to an HDR version
+ if(nRank<=0):
+ nRank=Texture.shape[0]*Texture.shape[1];
+ Texture=np.asarray((np.asarray(Texture,dtype=np.uint64)*(2**16))//nRank,dtype=np.uint16);
+ # Get a three-dimensional array
+ if(len(Texture.shape)<3):
+ Texture=Texture[:,:,np.newaxis];
+ # Save the image
+ Mode=["L","LA","RGB","RGBA"][Texture.shape[2]-1]+";16";
+ png.from_array(Texture,Mode).save(OutputPNGFilePath);
+
+
+def StoreNDTextureHDR(Array,OutputFilePath):
+ """This function stores the given unsigned integer array in a minimalist binary
+ file format. The last dimension is interpreted as corresponding to the
+ channels of the image. The file format consists of a sequence of unsigned,
+ least significant bit first 32-bit integers. The contained data is described
+ below:
+ - Version: File format version, should be 1.
+ - nChannel: The number of color channels in the image. This should be a value
+ between 1 (greyscale) and 4 (RGBA).
+ - nDimension: The number of dimensions of the stored array, i.e. the number of
+ indices required to uniquely identify one pixel, voxel, etc..
+ - Shape[nDimension]: nDimension integers providing the size of the array along
+ each dimension. By convention the first dimension is height, second width
+ and third depth.
+ - Data[Shape[0]*...*Shape[nDimension-1]*nChannel]: The uncompressed data of
+ the array. The channels are unrolled first, followed by all dimensions in
+ reverse order. Thus, an RG image of size 3*2 would be stored in the
+ following order: 00R, 00G, 01R, 01G, 10R, 10G, 11R, 11G, 20R, 20G, 21R,
+ 21G"""
+ # Prepare all the meta data and the data itself
+ Array=np.asarray(Array,dtype=np.uint32);
+ Version=1;
+ nDimension=len(Array.shape)-1;
+ nChannel=Array.shape[nDimension];
+ Shape=Array.shape[0:nDimension];
+ Data=Array.flatten("C");
+ # Write it to the file
+ OutputFile=open(OutputFilePath,"wb");
+ OutputFile.write(struct.pack("LLL",Version,nChannel,nDimension));
+ OutputFile.write(struct.pack("L"*nDimension,*Shape));
+ OutputFile.write(struct.pack("L"*np.size(Data),*Data));
+ OutputFile.close();
+
+
+def LoadNDTextureHDR(SourceFilePath):
+ """Loads a file generated by StoreNDTextureHDR() and returns it as an array like
+ the one that goes into StoreNDTextureHDR() using data type np.uint32. On
+ failure it returns None."""
+ # Load the meta data
+ File=open(SourceFilePath,"rb");
+ Version,nChannel,nDimension=struct.unpack_from("LLL",File.read(12));
+ if(Version!=1):
+ return None;
+ Shape=struct.unpack_from("L"*nDimension,File.read(4*nDimension));
+ nScalar=np.prod(Shape)*nChannel;
+ Data=struct.unpack_from("L"*nScalar,File.read(4*nScalar));
+ File.close();
+ # Prepare the output
+ return np.asarray(Data,dtype=np.uint32).reshape(tuple(list(Shape)+[nChannel]),order="C");
+
+
+def GenerateBlueNoiseDatabase(RandomSeedIndexList=range(1),MinResolution=16,MaxResolution=1024,ChannelCountList=[1,2,3,4],StandardDeviation=1.5):
+ """This function generates a database of blue noise textures for all sorts of
+ use cases. It includes power-of-two resolutions from MinResolution**2 up
+ to MaxResolution**2. Textures are generated with each given number of
+ channels. Each texture is generated multiple times using different random
+ numbers per entry in RandomSeedIndexList and the entries become part of the
+ file name. StandardDeviation forwards to GetVoidAndClusterBlueNoise(). The
+ results are stored as LDR and HDR files to a well-organized tree of
+ of directories."""
+ Resolution=MinResolution;
+ while(Resolution<=MaxResolution):
+ OutputDirectory="../Data/%d_%d"%(Resolution,Resolution);
+ if(not path.exists(OutputDirectory)):
+ makedirs(OutputDirectory);
+ for nChannel in ChannelCountList:
+ for i in RandomSeedIndexList:
+ Texture=np.dstack([GetVoidAndClusterBlueNoise((Resolution,Resolution),StandardDeviation) for j in range(nChannel)]);
+ LDRFormat=["LLL1","RG01","RGB1","RGBA"][nChannel-1];
+ HDRFormat=["L","LA","RGB","RGBA"][nChannel-1];
+ StoreNoiseTextureLDR(Texture,path.join(OutputDirectory,"LDR_%s_%d.png"%(LDRFormat,i)));
+ StoreNoiseTextureHDR(Texture,path.join(OutputDirectory,"HDR_%s_%d.png"%(HDRFormat,i)));
+ print("%d*%d, %s, %d"%(Resolution,Resolution,LDRFormat,i));
+ Resolution*=2;
+
+
+def Generate3DBlueNoiseTexture(Width,Height,Depth,nChannel,StandardDeviation=1.5):
+ """This function generates a single 3D blue noise texture with the specified
+ dimensions and number of channels. It then outputs it to a sequence of Depth
+ output files in LDR and HDR in a well-organized tree of directories. It also
+ outputs raw binary files.
+ \sa StoreNDTextureHDR() """
+ OutputDirectory="../Data/%d_%d_%d"%(Width,Height,Depth);
+ if(not path.exists(OutputDirectory)):
+ makedirs(OutputDirectory);
+ # Generate the blue noise for the various channels using multi-threading
+ ChannelTextureList=[None]*nChannel;
+ ChannelThreadList=[None]*nChannel;
+ def GenerateAndStoreTexture(Index):
+ ChannelTextureList[Index]=GetVoidAndClusterBlueNoise((Height,Width,Depth),StandardDeviation);
+ for i in range(nChannel):
+ ChannelThreadList[i]=threading.Thread(target=GenerateAndStoreTexture,args=(i,));
+ ChannelThreadList[i].start();
+ for Thread in ChannelThreadList:
+ Thread.join();
+ Texture=np.concatenate([ChannelTextureList[i][:,:,:,np.newaxis] for i in range(nChannel)],3);
+ LDRFormat=["LLL1","RG01","RGB1","RGBA"][nChannel-1];
+ HDRFormat=["L","LA","RGB","RGBA"][nChannel-1];
+ StoreNDTextureHDR(Texture,path.join(OutputDirectory,"HDR_"+HDRFormat+".raw"));
+ for i in range(Depth):
+ StoreNoiseTextureLDR(Texture[:,:,i,:],path.join(OutputDirectory,"LDR_%s_%d.png"%(LDRFormat,i)),Height*Width*Depth);
+ StoreNoiseTextureHDR(Texture[:,:,i,:],path.join(OutputDirectory,"HDR_%s_%d.png"%(HDRFormat,i)),Height*Width*Depth);
+
+
+def GenerateNDBlueNoiseTexture(Shape,nChannel,OutputFilePath,StandardDeviation=1.5):
+ """This function generates a single n-dimensional blue noise texture with the
+ specified shape and number of channels. It then outputs it to the specified
+ raw binary file.
+ \sa StoreNDTextureHDR() """
+ OutputDirectory=path.split(OutputFilePath)[0];
+ if(not path.exists(OutputDirectory)):
+ makedirs(OutputDirectory);
+ # Generate the blue noise for the various channels using multi-threading
+ ChannelTextureList=[None]*nChannel;
+ ChannelThreadList=[None]*nChannel;
+ def GenerateAndStoreTexture(Index):
+ ChannelTextureList[Index]=GetVoidAndClusterBlueNoise(Shape,StandardDeviation);
+ for i in range(nChannel):
+ ChannelThreadList[i]=threading.Thread(target=GenerateAndStoreTexture,args=(i,));
+ ChannelThreadList[i].start();
+ for Thread in ChannelThreadList:
+ Thread.join();
+ Texture=np.concatenate([ChannelTextureList[i][...,np.newaxis] for i in range(nChannel)],len(Shape));
+ StoreNDTextureHDR(Texture,OutputFilePath);
+
+
+def UniformToTriangularDistribution(UniformTexture):
+ """Given an array with a uniform distribution of values, this function
+ constructs an array of equal shape with a triangular distribution of values.
+ This is accomplished by applying a differentiable, monotonously growing
+ function per entry.
+ \param UniformTexture An integer array containing each value from 0 to
+ np.size(UniformTexture)-1 exactly once.
+ \return A floating-point array with values between -1 and 1 where the density
+ grows linearly between -1 and 0 and falls linearly between 0 and 1."""
+ Normalized=(np.asarray(UniformTexture,dtype=np.float)+0.5)/float(np.size(UniformTexture));
+ return np.where(Normalized<0.5,np.sqrt(2.0*Normalized)-1.0,1.0-np.sqrt(2.0-2.0*Normalized));
+
+
+if(__name__=="__main__"):
+ #GenerateBlueNoiseDatabase(range(64),16,64,range(1,5),1.9);
+ #GenerateBlueNoiseDatabase(range(16),128,128,range(1,5),1.9);
+ #GenerateBlueNoiseDatabase(range(8),256,256,range(1,5),1.9);
+ #GenerateBlueNoiseDatabase(range(1),512,512,range(1,5),1.9);
+ #GenerateBlueNoiseDatabase(range(1),1024,1024,[4],1.9);
+ #for nChannel in range(1,5):
+ #Generate3DBlueNoiseTexture(16,16,16,nChannel,1.9);
+ #Generate3DBlueNoiseTexture(32,32,32,nChannel,1.9);
+ #Generate3DBlueNoiseTexture(64,64,64,nChannel,1.9);
+ #ChannelNames=["","L","LA","RGB","RGBA"][nChannel];
+ #GenerateNDBlueNoiseTexture((8,8,8,8),nChannel,"../Data/8_8_8_8/HDR_"+ChannelNames+".raw",1.9);
+ #GenerateNDBlueNoiseTexture((16,16,16,16),nChannel,"../Data/16_16_16_16/HDR_"+ChannelNames+".raw",1.9);
+ Texture=GetVoidAndClusterBlueNoise((64,64),1.9);
+ #Texture=GetVoidAndClusterBlueNoise((32,32,32),1.9)[:,:,0];
+ AnalyzeNoiseTexture(Texture,True);
+ PlotBinaryPatterns(Texture,3,5);
+ pyplot.show();
diff --git a/tools/meta-make/bluenoise/LICENSE b/tools/meta-make/bluenoise/LICENSE
new file mode 100644
index 000000000..0e259d42c
--- /dev/null
+++ b/tools/meta-make/bluenoise/LICENSE
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
diff --git a/tools/meta-make/bluenoise/README.md b/tools/meta-make/bluenoise/README.md
new file mode 100644
index 000000000..295a3119b
--- /dev/null
+++ b/tools/meta-make/bluenoise/README.md
@@ -0,0 +1,5 @@
+# BlueNoise
+A SciPy implementation of the void-and-cluster method for generation of blue noise textures with arbitrary dimension.
+
+For more information see the corresponding blog post:
+http://momentsingraphics.de/?p=127
diff --git a/tools/meta-make/bluenoise/metagen-bluenoise.py b/tools/meta-make/bluenoise/metagen-bluenoise.py
new file mode 100644
index 000000000..3cb164157
--- /dev/null
+++ b/tools/meta-make/bluenoise/metagen-bluenoise.py
@@ -0,0 +1,203 @@
+import numpy as np
+import getopt, sys
+
+from BlueNoise import GetVoidAndClusterBlueNoise, StoreNoiseTextureLDR, StoreNoiseTextureHDR
+
+defaultInit = "0.1";
+defaultSigma = "1.5";
+
+def printerr(s):
+ sys.stderr.write("%s\n" % s);
+
+def UsageError():
+ printerr("metagen-bluenoise.py");
+ printerr(" Create tiling blue noise pattern as a C-style array of integers,");
+ printerr(" using a void-and-cluster algorithm.");
+ printerr("Usage:");
+ printerr(" python metagen-bluenoise.py [options] [ []]");
+ printerr("Parameters:");
+ printerr(" : Identifier base name for the pattern.");
+ printerr(" : Width of the pattern to generate.");
+ printerr(" : Height of the pattern to generate.");
+ printerr(" : 3D slices of the pattern to generate.");
+ printerr("Pattern generation options:");
+ printerr(" -i Initial seed fraction. Defaults to %s" % defaultInit);
+ printerr(" -r Random seed. Allows to re-produce patterns.");
+ printerr(" -s Standard deviation of filter used in pattern generation.");
+ printerr(" Higher values generate a coarser pattern. Defaults to %s." % defaultSigma);
+ printerr("Output options:");
+ printerr(" -h Generates declaration only.");
+ printerr(" -l Generates linear array instead of 2D array.");
+ printerr(" -n Wraps the declaration/definition in a namespace.");
+ printerr(" -p Write a copy of the pattern to PNG file. Primarily intended");
+ printerr(" for testing.");
+ sys.exit(2);
+
+def Error(s):
+ printerr(s);
+ sys.exit(2);
+
+header = False;
+initStr = defaultInit;
+init = float(initStr);
+linear = False;
+pngfile = "";
+sigmaStr = defaultSigma;
+sigma = float(sigmaStr);
+haveSeed = False;
+namespace = "";
+
+try:
+ opts, args = getopt.getopt(sys.argv[1:], "hi:ln:p:r:s:");
+
+except getopt.GetoptError as err:
+ UsageError();
+
+try:
+ for o, a in opts:
+ if (o == "-h"):
+ header = True;
+ elif (o == "-i"):
+ initStr = a;
+ init = float(a);
+ elif (o == "-l"):
+ linear = True;
+ elif (o == "-n"):
+ namespace = a;
+ elif (o == "-p"):
+ pngfile = a;
+ elif (o == "-r"):
+ haveSeed = True;
+ seed = int(a);
+ elif (o == "-s"):
+ sigmaStr = a;
+ sigma = float(a);
+ else:
+ Error("Unrecognized option %s" % (o, a));
+ assert False, "unhandled option";
+
+ argcount = len(args);
+ if (argcount < 2):
+ printerr("Not enough parameters.");
+ UsageError();
+ if (argcount > 4):
+ printerr("Too many parameters.");
+ UsageError();
+
+ name = args[0];
+ width = int(args[1]);
+ if (argcount > 2):
+ height = int(args[2]);
+ else:
+ height = width;
+ if (argcount > 3):
+ slices = int(args[3]);
+ else:
+ slices = 0;
+
+except ValueError as err:
+ UsageError();
+
+size = width * height;
+if (slices == 0):
+ zCount = 1;
+else:
+ zCount = slices;
+size3d = size * zCount;
+
+if (size > 1024 * 1024):
+ Error("Excessively large pattern. 1024x1024 should be enough for everybody.");
+
+if (size3d <= 256):
+ type = "unsigned char";
+elif (size3d <= 65536):
+ type = "unsigned short";
+else:
+ type = "unsigned int";
+
+if (size3d <= 10):
+ format = "%1i";
+elif (size3d <= 100):
+ format = "%2i";
+elif (size3d <= 1000):
+ format = "%3i";
+elif (size3d <= 10000):
+ format = "%4i";
+elif (size3d <= 100000):
+ format = "%5i";
+elif (size3d <= 1000000):
+ format = "%6i";
+elif (size3d <= 10000000):
+ format = "%7i";
+else:
+ format = "%i";
+itemsPerLine = 16;
+
+if (namespace):
+ print("namespace %s {" % namespace)
+ print
+
+if (linear):
+ if (slices == 0):
+ declaration = "extern const %s %s[%i]" % (type, name, size);
+ else:
+ declaration = "extern const %s %s[%i][%i]" % (type, name, slices, size);
+else:
+ if (slices == 0):
+ declaration = "extern const %s %s[%i][%i]" % (type, name, height, width);
+ else:
+ declaration = "extern const %s %s[%i][%i][%i]" % (type, name, slices, height, width);
+
+if (header):
+ print("%s;" % declaration);
+else:
+ if (haveSeed):
+ np.random.seed(seed);
+ if (slices == 0):
+ Texture=GetVoidAndClusterBlueNoise((width,height),sigma,init);
+ else:
+ Texture=GetVoidAndClusterBlueNoise((width,height,slices),sigma,init);
+ if (pngfile != ""):
+ if (size3d <= 256):
+ StoreNoiseTextureLDR(Texture,pngfile);
+ else:
+ StoreNoiseTextureHDR(Texture,pngfile);
+ print("/// %ix%i blue noise pattern data." % (width, height));
+ info = "-i%s -s%s" % (initStr, sigmaStr);
+ if (haveSeed):
+ info = info + " -r%i" % seed;
+ print("/// Generated using `make-bluenoise.py %s`." % info);
+ if (slices == 0):
+ print("%s = {" % declaration);
+ else:
+ print("%s = {{" % declaration);
+ for z in range(zCount):
+ if ((slices != 0) and (z > 0)):
+ print(" },{");
+ for y in range(width):
+ data = ""
+ for x in range(height):
+ if (x > 0):
+ data = data + ",";
+ if (x % itemsPerLine == 0):
+ if (linear):
+ data = data + "\n ";
+ else:
+ data = data + "\n ";
+ if (slices == 0):
+ value = Texture.item((x,y));
+ else:
+ value = Texture.item((x,y,z));
+ data = data + (format % value);
+ if (linear):
+ print(" %s," % data);
+ else:
+ print(" { %s }," % data);
+ if(slices == 0):
+ print(" };");
+ else:
+ print(" }};");
+
+if (namespace):
+ print
+ print("}")
diff --git a/tools/meta-make/meta-make.sh b/tools/meta-make/meta-make.sh
deleted file mode 100644
index 5b6814ecf..000000000
--- a/tools/meta-make/meta-make.sh
+++ /dev/null
@@ -1,79 +0,0 @@
-#!/bin/bash
-
-# =============================================================================
-
-# Print header for single C++ source file.
-# $1 - output file name
-# $2 - input file name
-function reswrap_header {
- sed "tools/meta-make/reswrap-header.cpp" \
- -e 's|{{filename}}|'"${1#source/}"'|g' \
- -e 's|{{source}}|'"${2#source/}"'|g'
-}
-
-# Create single C++ source file.
-# $1 - output file name
-# $2 - input file name
-# $3 - C++ namespace
-# $4 - C++ identifier
-# $5 - additional reswrap options
-function reswrap1 {
- reswrap_header "$1" "$2" > "$1"
- reswrap $5 -oa "$1" -n "$3" -e -s -r "$4" "$2"
-}
-
-# Create C++ source file pair (`.cpp`, `.h`).
-# $1 - output base file name
-# $2 - input file name
-# $3 - C++ namespace
-# $4 - C++ identifier
-# $5 - additional reswrap options
-function reswrap2 {
- reswrap1 "$1.cpp" "$2" "$3" "$4" "$5"
- reswrap1 "$1.h" "$2" "$3" "$4" "$5 -i"
-}
-
-# Create C++ source file pair for a binary file.
-# $1 - output base file name
-# $2 - input file name
-# $3 - C++ namespace
-# $4 - C++ identifier
-function reswrap_binary {
- reswrap2 "$@" "-z"
-}
-
-# Create C++ source file pair for a text file.
-# $1 - output base file name
-# $2 - input file name
-# $3 - C++ namespace
-# $4 - C++ identifier
-function reswrap_text {
- reswrap2 "$@" "-m -ta -c 120"
-}
-
-# Create C++ source file pair for a TTF font file.
-# $1 - font base file name
-function reswrap_ttf {
- reswrap_binary "source/base/font/$1" "distribution/include/$1.ttf" "pov_base" "font_$1"
-}
-
-# Create C++ source file pair for a benchmark-related file.
-# $1 - file type ("pov" or "ini")
-# $2 - C++ identifier
-function reswrap_benchmark {
- reswrap_text "source/backend/control/benchmark_$1" "distribution/scenes/advanced/benchmark/benchmark.$1" "pov" "$2"
-}
-
-# =============================================================================
-
-# TrueType Fonts
-
-reswrap_ttf "crystal"
-reswrap_ttf "cyrvetic"
-reswrap_ttf "povlogo"
-reswrap_ttf "timrom"
-
-# Benchmark Scene
-
-reswrap_benchmark "pov" "Benchmark_File"
-reswrap_benchmark "ini" "Benchmark_Options"
diff --git a/tools/meta-make/metagen-header.cpp b/tools/meta-make/metagen-header.cpp
new file mode 100644
index 000000000..c60deb231
--- /dev/null
+++ b/tools/meta-make/metagen-header.cpp
@@ -0,0 +1,43 @@
+//******************************************************************************
+///
+/// @file {{filename}}
+///
+/// {{info}}
+/// Auto-generated using {{generator}}.
+///
+/// @copyright
+/// @parblock
+///
+/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
+/// Copyright 1991-{{year}} Persistence of Vision Raytracer Pty. Ltd.
+///
+/// POV-Ray is free software: you can redistribute it and/or modify
+/// it under the terms of the GNU Affero General Public License as
+/// published by the Free Software Foundation, either version 3 of the
+/// License, or (at your option) any later version.
+///
+/// POV-Ray is distributed in the hope that it will be useful,
+/// but WITHOUT ANY WARRANTY; without even the implied warranty of
+/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+/// GNU Affero General Public License for more details.
+///
+/// You should have received a copy of the GNU Affero General Public License
+/// along with this program. If not, see .
+///
+/// ----------------------------------------------------------------------------
+///
+/// POV-Ray is based on the popular DKB raytracer version 2.12.
+/// DKBTrace was originally written by David K. Buck.
+/// DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
+///
+/// @endparblock
+///
+//******************************************************************************
+
+/// @file
+/// @attention
+/// **DO NOT EDIT THIS FILE!**
+/// Instead, if this file needs fixing, modify {{generator}}
+/// or its invocation in `tools/meta-make/Makefile` accordingly,
+/// and re-generate this file as described in @ref tools-metamake (`tools/meta-make/readme.md`).
+
diff --git a/tools/meta-make/metagen.sh b/tools/meta-make/metagen.sh
new file mode 100644
index 000000000..ad2b74e6f
--- /dev/null
+++ b/tools/meta-make/metagen.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# $1 - full output file name
+# $2 - info
+# $3 - generator tool command
+# $4 - generator tool parameters
+# $5 - include file extra parameters
+
+echo $1
+
+filename="${1#../../source/}"
+info="$2"
+generator="${3##* }"
+generator="${generator##*/}"
+
+if [ "${filename##*.}" == "h" ]; then
+ opts="$5"
+else
+ opts=""
+fi
+
+sed "metagen-header.cpp" \
+ -e 's|{{filename}}|'"$filename"'|g' \
+ -e 's|{{info}}|'"$info"'|g' \
+ -e 's|{{generator}}|'"$generator"'|g' \
+ -e 's|{{year}}|'`date +"%Y"`'|g' > "$1" || exit 2
+
+$3 $opts $4 >> "$1" && exit 0
+
+rm "$1"
+exit 2
diff --git a/tools/meta-make/readme.md b/tools/meta-make/readme.md
index 386d158d0..3b51cb76e 100644
--- a/tools/meta-make/readme.md
+++ b/tools/meta-make/readme.md
@@ -13,15 +13,25 @@ packages installed (or a compatible environment):
- `bash`
- `libfox-1.6-dev` (we expect other versions to work as well)
+ - `python`, `python-numpy`, `python-scipy`, `python-matplotlib`, `python-pypng`
Procedure
=========
-Invoke the following command from the root directory of the POV-Ray source package:
+From the root of the POV-Ray source package, change to the `tools/meta-make`
+directory, and invoke the following command:
- tools/meta-make/meta-make.sh
+ make
+
+This will re-create all missing or outdated generated files.
+
+To re-create all converted files unconditionally, use:
+
+ make clean all
+
+Note that some files may take a while to re-create.
Output
@@ -31,6 +41,7 @@ The following files will be re-created:
| Generated File | Generated From |
|:--------------------------------------------------|:----------------------------------------------------------|
+| `source/base/data/bluenoise*.cpp`/`.h` | `tools/meta-make/bluenoise/metagen-bluenoise.py` |
| `source/base/font/crystal.cpp`/`.h` | `distribution/include/crystal.ttf` |
| `source/base/font/cyrvetic.cpp`/`.h` | `distribution/include/cyrvetic.ttf` |
| `source/base/font/povlogo.cpp`/`.h` | `distribution/include/povlogo.ttf` |
diff --git a/tools/meta-make/reswrap-header.cpp b/tools/meta-make/reswrap-header.cpp
index b59f146f5..eba32fdb7 100644
--- a/tools/meta-make/reswrap-header.cpp
+++ b/tools/meta-make/reswrap-header.cpp
@@ -8,7 +8,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-{{year}} Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -37,5 +37,5 @@
/// @attention
/// **DO NOT EDIT THIS FILE!**
/// Instead, edit `{{source}}`,
-/// and re-generate this file as described in `tools/meta-make/readme.md`.
+/// and re-generate this file as described in @ref tools-metamake (`tools/meta-make/readme.md`).
diff --git a/tools/meta-make/reswrap.sh b/tools/meta-make/reswrap.sh
new file mode 100644
index 000000000..707c0aefd
--- /dev/null
+++ b/tools/meta-make/reswrap.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+# $1 - full output file name
+# $2 - full source file name
+# $3 - namespace
+# $4 - identifier
+# $5 - additional options
+
+echo $1
+
+filename="${1#../../source/}"
+source="${2#../../}"
+
+if [ "${filename##*.}" == "h" ]; then
+ opts="-i"
+else
+ opts=""
+fi
+
+sed "reswrap-header.cpp" \
+ -e 's|{{filename}}|'"$filename"'|g' \
+ -e 's|{{source}}|'"$source"'|g' \
+ -e 's|{{year}}|'`date +"%Y"`'|g' > "$1" || exit 2
+
+reswrap $opts $5 -oa "$1" -n "$3" -e -s -r "$4" "$2" && exit 0
+
+rm "$1"
+exit 2
diff --git a/unix/disp.h b/unix/disp.h
index 2306090aa..b01178119 100644
--- a/unix/disp.h
+++ b/unix/disp.h
@@ -41,8 +41,6 @@
#include "vfe.h"
-#include
-
namespace pov_frontend
{
using namespace vfe;
@@ -52,8 +50,8 @@ namespace pov_frontend
class UnixDisplay : public vfeDisplay
{
public:
- UnixDisplay(unsigned int w, unsigned int h, GammaCurvePtr gamma, vfeSession *session, bool visible) :
- vfeDisplay(w, h, gamma, session, visible) {};
+ UnixDisplay(unsigned int w, unsigned int h, vfeSession *session, bool visible) :
+ vfeDisplay(w, h, session, visible) {};
virtual ~UnixDisplay() {} ;
virtual void Initialise() = 0;
virtual void Close() = 0;
diff --git a/unix/disp_sdl.cpp b/unix/disp_sdl.cpp
index 00907494e..a1d8a0539 100644
--- a/unix/disp_sdl.cpp
+++ b/unix/disp_sdl.cpp
@@ -69,8 +69,8 @@ namespace pov_frontend
return true;
}
- UnixSDLDisplay::UnixSDLDisplay(unsigned int w, unsigned int h, GammaCurvePtr gamma, vfeSession *session, bool visible) :
- UnixDisplay(w, h, gamma, session, visible)
+ UnixSDLDisplay::UnixSDLDisplay(unsigned int w, unsigned int h, vfeSession *session, bool visible) :
+ UnixDisplay(w, h, session, visible)
{
m_valid = false;
m_display_scaled = false;
diff --git a/unix/disp_sdl.h b/unix/disp_sdl.h
index a0639a540..dbbfe2742 100644
--- a/unix/disp_sdl.h
+++ b/unix/disp_sdl.h
@@ -10,7 +10,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -57,7 +57,7 @@ namespace pov_frontend
static const UnixOptionsProcessor::Option_Info Options[];
static bool Register(vfeUnixSession *session);
- UnixSDLDisplay(unsigned int w, unsigned int h, GammaCurvePtr gamma, vfeSession *session, bool visible);
+ UnixSDLDisplay(unsigned int w, unsigned int h, vfeSession *session, bool visible);
virtual ~UnixSDLDisplay();
void Initialise();
void Close();
diff --git a/unix/disp_text.h b/unix/disp_text.h
index d24dc9806..bce8af57e 100644
--- a/unix/disp_text.h
+++ b/unix/disp_text.h
@@ -10,7 +10,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -53,8 +53,8 @@ namespace pov_frontend
static const UnixOptionsProcessor::Option_Info Options[];
static bool Register(vfeUnixSession *session);
- UnixTextDisplay(unsigned int w, unsigned int h, GammaCurvePtr gamma, vfeSession *session, bool visible) :
- UnixDisplay(w, h, gamma, session, visible) {};
+ UnixTextDisplay(unsigned int w, unsigned int h, vfeSession *session, bool visible) :
+ UnixDisplay(w, h, session, visible) {};
virtual ~UnixTextDisplay() {} ;
void Initialise() {};
void Close() {};
diff --git a/vfe/unix/unixconsole.cpp b/vfe/unix/unixconsole.cpp
index cd7337358..f35a283a9 100644
--- a/vfe/unix/unixconsole.cpp
+++ b/vfe/unix/unixconsole.cpp
@@ -37,9 +37,6 @@
#include
#include
-// Boost header files
-#include
-
// Other library header files
#include
#include
@@ -147,7 +144,7 @@ static void ProcessSignal (void)
gSignalNumber = 0;
}
-static vfeDisplay *UnixDisplayCreator (unsigned int width, unsigned int height, GammaCurvePtr gamma, vfeSession *session, bool visible)
+static vfeDisplay *UnixDisplayCreator (unsigned int width, unsigned int height, vfeSession *session, bool visible)
{
UnixDisplay *display = GetRenderWindow () ;
switch (gDisplayMode)
@@ -156,16 +153,16 @@ static vfeDisplay *UnixDisplayCreator (unsigned int width, unsigned int height,
case DISP_MODE_SDL:
if (display != nullptr && display->GetWidth() == width && display->GetHeight() == height)
{
- UnixDisplay *p = new UnixSDLDisplay (width, height, gamma, session, false) ;
+ UnixDisplay *p = new UnixSDLDisplay (width, height, session, false) ;
if (p->TakeOver (display))
return p;
delete p;
}
- return new UnixSDLDisplay (width, height, gamma, session, visible) ;
+ return new UnixSDLDisplay (width, height, session, visible) ;
break;
#endif
case DISP_MODE_TEXT:
- return new UnixTextDisplay (width, height, gamma, session, visible) ;
+ return new UnixTextDisplay (width, height, session, visible) ;
break;
default:
return nullptr;
diff --git a/vfe/vfe.cpp b/vfe/vfe.cpp
index cc09019b9..e02c528a1 100644
--- a/vfe/vfe.cpp
+++ b/vfe/vfe.cpp
@@ -1096,7 +1096,7 @@ State VirtualFrontEnd::Process()
// case.
return state;
}
- try { viewId = renderFrontend.CreateView(sceneId, options, imageProcessing, boost::bind(&vfe::VirtualFrontEnd::CreateDisplay, this, _1, _2, _3)); }
+ try { viewId = renderFrontend.CreateView(sceneId, options, imageProcessing, boost::bind(&vfe::VirtualFrontEnd::CreateDisplay, this, _1, _2)); }
catch(pov_base::Exception& e)
{
m_Session->SetFailed();
diff --git a/vfe/vfe.h b/vfe/vfe.h
index c9fc94f96..f3ca10708 100644
--- a/vfe/vfe.h
+++ b/vfe/vfe.h
@@ -10,7 +10,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -49,7 +49,6 @@
#include "base/platformbase.h"
#include "base/timer.h"
-#include "base/image/colourspace.h"
#include "frontend/console.h"
#include "frontend/display.h"
@@ -197,7 +196,7 @@ namespace vfe
class vfeDisplay : public Display
{
public:
- vfeDisplay(unsigned int width, unsigned int height, GammaCurvePtr gamma, vfeSession *session, bool visible = false);
+ vfeDisplay(unsigned int width, unsigned int height, vfeSession *session, bool visible = false);
virtual ~vfeDisplay();
virtual void Initialise();
@@ -240,8 +239,8 @@ namespace vfe
protected:
virtual Console *CreateConsole()
{ return new vfeConsole(m_Session, m_Session->GetConsoleWidth()); }
- virtual Display *CreateDisplay(unsigned int width, unsigned int height, GammaCurvePtr gamma)
- { return m_Session->CreateDisplay(width, height, gamma) ; }
+ virtual Display *CreateDisplay(unsigned int width, unsigned int height)
+ { return m_Session->CreateDisplay(width, height) ; }
bool HandleShelloutCancel();
RenderFrontend renderFrontend;
diff --git a/vfe/vfedisplay.cpp b/vfe/vfedisplay.cpp
index 2dd1d7fba..fdf59bc4d 100644
--- a/vfe/vfedisplay.cpp
+++ b/vfe/vfedisplay.cpp
@@ -10,7 +10,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -50,8 +50,8 @@
namespace vfe
{
-vfeDisplay::vfeDisplay(unsigned int w, unsigned int h, GammaCurvePtr gamma, vfeSession* session, bool visible) :
- Display(w, h, gamma),
+vfeDisplay::vfeDisplay(unsigned int w, unsigned int h, vfeSession* session, bool visible) :
+ Display(w, h),
m_Session(session),
m_VisibleOnCreation(visible)
{
diff --git a/vfe/vfesession.cpp b/vfe/vfesession.cpp
index 1ad005d61..2a5c79603 100644
--- a/vfe/vfesession.cpp
+++ b/vfe/vfesession.cpp
@@ -78,7 +78,7 @@ vfeSession::vfeSession(int id)
m_RequestFlag = rqNoRequest;
m_RequestResult = 0;
m_StartTime = 0;
- m_DisplayCreator = boost::bind(&vfe::vfeSession::DefaultDisplayCreator, this, _1, _2, _3, _4, _5);
+ m_DisplayCreator = boost::bind(&vfe::vfeSession::DefaultDisplayCreator, this, _1, _2, _3, _4);
Reset();
}
@@ -737,9 +737,9 @@ vfeSession *vfeSession::GetSessionFromThreadID()
return m_CurrentSessionTemporaryHack ;
}
-vfeDisplay *vfeSession::DefaultDisplayCreator (unsigned int width, unsigned int height, GammaCurvePtr gamma, vfeSession *session, bool visible)
+vfeDisplay *vfeSession::DefaultDisplayCreator (unsigned int width, unsigned int height, vfeSession *session, bool visible)
{
- return new vfeDisplay (width, height, gamma, session, visible) ;
+ return new vfeDisplay (width, height, session, visible) ;
}
// If a VFE implementation has provided the address of a display creator
@@ -750,9 +750,9 @@ vfeDisplay *vfeSession::DefaultDisplayCreator (unsigned int width, unsigned int
// If a display instance is returned, it is expected to conform to the
// definition of the pov_frontend::Display class, but will typically be
// a platform-specific derivative of that.
-vfeDisplay *vfeSession::CreateDisplay (unsigned int width, unsigned int height, GammaCurvePtr gamma, bool visible)
+vfeDisplay *vfeSession::CreateDisplay (unsigned int width, unsigned int height, bool visible)
{
- return m_DisplayCreator (width, height, gamma, this, visible);
+ return m_DisplayCreator (width, height, this, visible);
}
// This method causes a shutdown of the vfeSession instance. Specifically
diff --git a/vfe/vfesession.h b/vfe/vfesession.h
index 68f6441c5..e5a4c0988 100644
--- a/vfe/vfesession.h
+++ b/vfe/vfesession.h
@@ -44,8 +44,6 @@
#include
#include
-#include "base/image/colourspace.h"
-
#include "frontend/simplefrontend.h"
namespace pov_frontend
@@ -358,7 +356,7 @@ namespace vfe
public:
// Our DisplayCreator functor - see vfeSession::SetDisplayCreator().
- typedef boost::function DisplayCreator;
+ typedef boost::function DisplayCreator;
typedef enum
{
mUnclassified = 0,
@@ -542,7 +540,7 @@ namespace vfe
// window be created. The display instance returned is expected to conform
// to the definition of the pov_frontend::Display class (but it typically
// a platform-specific derivative of that.)
- virtual vfeDisplay *CreateDisplay(unsigned int width, unsigned int height, GammaCurvePtr gamma, bool visible = false);
+ virtual vfeDisplay *CreateDisplay(unsigned int width, unsigned int height, bool visible = false);
// Used by VFE implementations to allow their own custom pov_frontend::Display
// derived render preview window class to be created when the main POV-Ray code
@@ -1258,7 +1256,7 @@ namespace vfe
static vfeSession *m_CurrentSessionTemporaryHack;
shared_ptr m_Console;
- virtual vfeDisplay *DefaultDisplayCreator (unsigned int width, unsigned int height, GammaCurvePtr gamma, vfeSession *session, bool visible);
+ virtual vfeDisplay *DefaultDisplayCreator (unsigned int width, unsigned int height, vfeSession *session, bool visible);
DisplayCreator m_DisplayCreator;
int m_MaxStatusMessages;
diff --git a/windows/pvdisplay.cpp b/windows/pvdisplay.cpp
index 81b9b06a2..99fe17f52 100644
--- a/windows/pvdisplay.cpp
+++ b/windows/pvdisplay.cpp
@@ -8,7 +8,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -116,8 +116,8 @@ shared_ptr gDisplay;
BitmapInfo WinLegacyDisplay::m_BitmapTemplate;
-WinLegacyDisplay::WinLegacyDisplay(unsigned int w, unsigned int h, GammaCurvePtr gamma, vfeSession *session, bool visible) :
- WinDisplay(w, h, gamma, session, visible)
+WinLegacyDisplay::WinLegacyDisplay(unsigned int w, unsigned int h, vfeSession *session, bool visible) :
+ WinDisplay(w, h, session, visible)
{
m_BitmapSurface = NULL;
m_LastY = 0;
diff --git a/windows/pvdisplay.h b/windows/pvdisplay.h
index 97e78f2b7..1caf990c1 100644
--- a/windows/pvdisplay.h
+++ b/windows/pvdisplay.h
@@ -8,7 +8,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -51,8 +51,8 @@ namespace pov_frontend
class WinDisplay : public vfeDisplay
{
public:
- WinDisplay(unsigned int w, unsigned int h, GammaCurvePtr gamma, vfeSession *session, bool visible) :
- vfeDisplay(w, h, gamma, session, visible), m_Handle (NULL) {};
+ WinDisplay(unsigned int w, unsigned int h, vfeSession *session, bool visible) :
+ vfeDisplay(w, h, session, visible), m_Handle (NULL) {};
virtual ~WinDisplay() {} ;
virtual bool CreateRenderWindow (void) = 0;
virtual void Close() = 0;
@@ -73,7 +73,7 @@ namespace pov_frontend
class WinLegacyDisplay : public WinDisplay
{
public:
- WinLegacyDisplay(unsigned int w, unsigned int h, GammaCurvePtr gamma, vfeSession *session, bool visible);
+ WinLegacyDisplay(unsigned int w, unsigned int h, vfeSession *session, bool visible);
virtual ~WinLegacyDisplay();
void Initialise();
diff --git a/windows/pvfrontend.cpp b/windows/pvfrontend.cpp
index 98ab1af52..115d8c44a 100644
--- a/windows/pvfrontend.cpp
+++ b/windows/pvfrontend.cpp
@@ -10,7 +10,7 @@
/// @parblock
///
/// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
-/// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
+/// Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
///
/// POV-Ray is free software: you can redistribute it and/or modify
/// it under the terms of the GNU Affero General Public License as
@@ -82,7 +82,7 @@ static vfeWinSession *gSession;
// Whenever the core POV code wants to create an output window, the below code
// will therefore be executed.
//////////////////////////////////////////////////////////////////////////////
-vfeDisplay *WinDisplayCreator (unsigned int width, unsigned int height, GammaCurvePtr gamma, vfeSession *session, bool visible)
+vfeDisplay *WinDisplayCreator (unsigned int width, unsigned int height, vfeSession *session, bool visible)
{
// we attempt to minimize 'flashing' of the window (destroy followed by a re-create)
// by checking to see if the previous window (if any) had the same dimensions. if it
@@ -92,7 +92,7 @@ vfeDisplay *WinDisplayCreator (unsigned int width, unsigned int height, GammaCur
if (display != NULL && display->GetWidth() == width && display->GetHeight() == height)
{
WinDisplay *p ;
- p = new WinLegacyDisplay (width, height, gamma, session, false) ;
+ p = new WinLegacyDisplay (width, height, session, false);
if (p->TakeOver (display))
{
bool anim = gSession->RenderingAnimation();
@@ -105,7 +105,7 @@ vfeDisplay *WinDisplayCreator (unsigned int width, unsigned int height, GammaCur
}
delete p;
}
- return new WinLegacyDisplay (width, height, gamma, session, visible) ;
+ return new WinLegacyDisplay (width, height, session, visible);
}
//////////////////////////////////////////////////////////////////////////////
diff --git a/windows/vs2015/povbase.vcxproj b/windows/vs2015/povbase.vcxproj
index c69e4c616..a33b03591 100644
--- a/windows/vs2015/povbase.vcxproj
+++ b/windows/vs2015/povbase.vcxproj
@@ -386,12 +386,14 @@
+
+
@@ -446,12 +448,14 @@
+
+
diff --git a/windows/vs2015/povbase.vcxproj.filters b/windows/vs2015/povbase.vcxproj.filters
index fc762e827..1f241f597 100644
--- a/windows/vs2015/povbase.vcxproj.filters
+++ b/windows/vs2015/povbase.vcxproj.filters
@@ -25,6 +25,12 @@
{9f44d3c5-81d8-4017-a1b7-5a6f9a139e28}
+
+ {32531c25-b41d-42b5-88a0-77de8a8daa8d}
+
+
+ {b3ceef1f-ae95-40ac-b0b3-904569e9595b}
+
@@ -132,6 +138,12 @@
Base source
+
+ Base source\Data
+
+
+ Base source\Image
+
@@ -266,5 +278,11 @@
Base Headers
+
+ Base Headers\Data
+
+
+ Base Headers\Image
+
\ No newline at end of file