From c0cb3c662d9f0bf2b2d1de9adf49bbaa68ac9249 Mon Sep 17 00:00:00 2001 From: Christoph Lipka Date: Wed, 26 Sep 2018 00:30:34 +0200 Subject: [PATCH 1/4] Miscellaneous changes to dithering implementation. --- changes.txt | 2 + source/base/image/encoding.cpp | 212 ++++++++++++++--------- source/base/image/encoding.h | 93 +++++++--- source/frontend/processrenderoptions.cpp | 44 +++-- source/frontend/processrenderoptions.h | 12 +- source/frontend/renderfrontend.cpp | 26 +-- 6 files changed, 244 insertions(+), 145 deletions(-) diff --git a/changes.txt b/changes.txt index 46d9aa5e6..769d372ac 100644 --- a/changes.txt +++ b/changes.txt @@ -66,6 +66,8 @@ Changed Behaviour decided to not fix the change to work as originally intended, and instead only allow type mixing if the array has explicitly been declared as `mixed`. See the documentation for details. + - The dithering implementation has been modified, and may produce slightly + different results for otherwise identical scenes. New Features ------------ diff --git a/source/base/image/encoding.cpp b/source/base/image/encoding.cpp index 3fd53bdb5..a2dd7f2fa 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,6 +37,9 @@ // Unit header file must be the first file included within POV-Ray *.cpp files (pulls in config) #include "base/image/encoding.h" +// Standard C++ header files +#include + // POV-Ray base header files #include "base/image/image.h" @@ -75,62 +78,89 @@ static const BayerMatrix BayerMatrices[MaxBayerMatrixSize+1] = /*******************************************************************************/ -/// Class representing "no-op" dithering rules. +/// "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); + virtual void GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt) override; }; -/// Class representing bayer dithering rules, generating a regular pattern. +/// Bayer ordered dithering strategy. +/// +/// This stateless dithering strategy varies the quantization threshold for +/// each pixel based on a repeating pattern as proposed by B.E. Bayer in 1973. +/// class BayerDither : public DitherStrategy { public: BayerDither(unsigned int mxSize); - virtual void GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt); + virtual void GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt) override; 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. +/// 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. +/// 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); + 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; }; -/// 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 +/// 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. +/// +class SierraLiteDither : 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); + 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* nextRowOffset; - ColourOffset* thisRowOffset; + ColourOffset* maErr; }; -/// 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. +/// 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. +/// 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); + 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* nextRowOffset; - ColourOffset* thisRowOffset; + ColourOffset* maErr; + ColourOffset mErrX; }; /*******************************************************************************/ @@ -159,7 +189,11 @@ void BayerDither::GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin void DiffusionDither1D::GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt) { - offLin = lastErr; lastErr.clear(); offQnt.clear(); + if (x == 0) + offLin.clear(); + else + offLin = lastErr; + offQnt.clear(); } void DiffusionDither1D::SetError(unsigned int x, unsigned int y, const ColourOffset& err) @@ -169,77 +203,97 @@ void DiffusionDither1D::SetError(unsigned int x, unsigned int y, const ColourOff /*******************************************************************************/ -DiffusionDither2D::DiffusionDither2D(unsigned int width) : +SierraLiteDither::SierraLiteDither(unsigned int width) : imageWidth(width), - thisRowOffset(new ColourOffset[width+1]), - nextRowOffset(new ColourOffset[width+1]) -{ - ; -} + maErr(new ColourOffset[width+2]) +{} -DiffusionDither2D::~DiffusionDither2D() +SierraLiteDither::~SierraLiteDither() { - delete[] thisRowOffset; - delete[] nextRowOffset; + delete[] maErr; } -void DiffusionDither2D::GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt) +void SierraLiteDither::GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt) { - offLin = thisRowOffset[x]; + offLin = maErr[x+1]; 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 +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), - thisRowOffset(new ColourOffset[width+2]), - nextRowOffset(new ColourOffset[width+2]) -{ - ; -} + maErr(new ColourOffset[width+2]) +{} FloydSteinbergDither::~FloydSteinbergDither() { - delete[] thisRowOffset; - delete[] nextRowOffset; + delete[] maErr; } void FloydSteinbergDither::GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt) { - offLin = thisRowOffset[x+1]; + if (x == 0) + mErrX.clear(); + offLin = maErr[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 + // 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) } /*******************************************************************************/ @@ -250,7 +304,7 @@ DitherStrategySPtr GetDitherStrategy(DitherMethodId method, unsigned int imageWi { 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_SierraLite: return DitherStrategySPtr(new SierraLiteDither(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)); @@ -443,7 +497,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 +542,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 +563,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 +616,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..25b844646 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 @@ -57,11 +57,19 @@ namespace pov_base class Image; +//***************************************************************************** +/// +/// @name Dithering +/// +/// The following types and functions provide dithering functionality. +/// +/// @{ + enum DitherMethodId { kPOVList_DitherMethod_None, kPOVList_DitherMethod_Diffusion1D, - kPOVList_DitherMethod_Diffusion2D, + kPOVList_DitherMethod_SierraLite, kPOVList_DitherMethod_FloydSteinberg, kPOVList_DitherMethod_Bayer2x2, kPOVList_DitherMethod_Bayer3x3, @@ -126,14 +134,6 @@ class DitherStrategy 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); @@ -142,7 +142,7 @@ DitherStrategySPtr GetNoOpDitherStrategy(); /// Function providing simple stateless dithering. /// -/// This function is provided as a fallback from the `DitherStrategy` mechanism to provide basic +/// 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. /// @@ -202,10 +202,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 +235,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 +250,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 +266,35 @@ 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, qOff); + err = xEff - IntDecode(g, v, max); + if (v < max) + { + float errUp = xEff - IntDecode(g, v + 1, qOff); + if (fabs(errUp) <= fabs(err)) + { + ++v; + err = errUp; + } + } + return v; } /// Generic encoding function. @@ -266,20 +303,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/frontend/processrenderoptions.cpp b/source/frontend/processrenderoptions.cpp index 0db38b434..813ea2600 100644 --- a/source/frontend/processrenderoptions.cpp +++ b/source/frontend/processrenderoptions.cpp @@ -346,7 +346,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) @@ -1076,7 +1075,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 +1090,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 +1103,15 @@ 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 }, + { "B2", kPOVList_DitherMethod_Bayer2x2, "2x2 Bayer pattern" }, + { "B3", kPOVList_DitherMethod_Bayer3x3, "3x3 Bayer pattern" }, + { "B4", kPOVList_DitherMethod_Bayer4x4, "4x4 Bayer pattern" }, + { "D1", kPOVList_DitherMethod_Diffusion1D, "Simple 1-D error diffusion" }, + { "D2", kPOVList_DitherMethod_SierraLite, "Simple 2-D error diffusion (Sierra Lite)" }, + { "FS", kPOVList_DitherMethod_FloydSteinberg, "Floyd-Steinberg error diffusion" }, // end-of-list marker - { nullptr, 0 } + { nullptr, 0, "(unknown)" } }; int ProcessRenderOptions::ParseFileType(char code, POVMSType attribute, int* pInternalId, bool* pHas16BitGreyscale) @@ -1173,6 +1172,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 +1205,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..5245153cc 100644 --- a/source/frontend/renderfrontend.cpp +++ b/source/frontend/renderfrontend.cpp @@ -47,6 +47,7 @@ #include "frontend/console.h" #include "frontend/processoptions.h" +#include "frontend/processrenderoptions.h" // this must be the last file included #include "base/povdebug.h" @@ -1160,17 +1161,7 @@ void OutputOptions(POVMS_Object& cppmsg, TextStreamBuffer *tsb) { i = kPOVList_DitherMethod_FloydSteinberg; (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 +1187,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 From b2972995d2a33c32d9f28f686e5162c75942b0f4 Mon Sep 17 00:00:00 2001 From: Christoph Lipka Date: Fri, 28 Sep 2018 01:52:27 +0200 Subject: [PATCH 2/4] Overhaul "meta-make" process to use a proper makefile. --- source/backend/control/benchmark_ini.cpp | 4 +- source/backend/control/benchmark_ini.h | 4 +- source/backend/control/benchmark_pov.cpp | 4 +- source/backend/control/benchmark_pov.h | 4 +- source/base/font/crystal.cpp | 4 +- source/base/font/crystal.h | 4 +- source/base/font/cyrvetic.cpp | 4 +- source/base/font/cyrvetic.h | 4 +- source/base/font/povlogo.cpp | 4 +- source/base/font/povlogo.h | 4 +- source/base/font/timrom.cpp | 4 +- source/base/font/timrom.h | 4 +- tools/meta-make/Makefile | 36 +++++++++++ tools/meta-make/meta-make.sh | 79 ------------------------ tools/meta-make/readme.md | 11 +++- tools/meta-make/reswrap-header.cpp | 4 +- tools/meta-make/reswrap.sh | 28 +++++++++ 17 files changed, 99 insertions(+), 107 deletions(-) create mode 100644 tools/meta-make/Makefile delete mode 100644 tools/meta-make/meta-make.sh create mode 100644 tools/meta-make/reswrap.sh 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/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/tools/meta-make/Makefile b/tools/meta-make/Makefile new file mode 100644 index 000000000..8587342bf --- /dev/null +++ b/tools/meta-make/Makefile @@ -0,0 +1,36 @@ +include = ../../distribution/include +scenes = ../../distribution/scenes +backend = ../../source/backend +base = ../../source/base +reswrap_header = reswrap-header.cpp +reswrap_sh = ./reswrap.sh + +FONTS = crystal cyrvetic povlogo timrom +BENCHMARK = benchmark_pov benchmark_ini + +all: $(FONTS) $(BENCHMARK) + +.PHONY: clean +clean: + -for name in $(FONTS); do \ + rm "$(base)/font/$${name}.cpp" "$(base)/font/$${name}.h"; \ + done + -for name in $(BENCHMARK); do \ + rm "$(backend)/control/$${name}.cpp" "$(backend)/control/$${name}.h"; \ + done + +$(FONTS): %: $(base)/font/%.cpp $(base)/font/%.h + +$(base)/font/%.cpp: $(include)/%.ttf $(reswrap_header) + $(reswrap_sh) "$@" "$<" "pov_base" "font_$(*F)" "-z" + +$(base)/font/%.h: $(include)/%.ttf $(reswrap_header) + $(reswrap_sh) "$@" "$<" "pov_base" "font_$(*F)" "-z" + +$(BENCHMARK): benchmark_%: ../../source/backend/control/benchmark_%.cpp ../../source/backend/control/benchmark_%.h + +$(backend)/control/benchmark_ini.%: $(scenes)/advanced/benchmark/benchmark.ini $(reswrap_header) + $(reswrap_sh) "$@" "$<" "pov" "Benchmark_Options" "-m -ta -c 120" + +$(backend)/control/benchmark_pov.%: $(scenes)/advanced/benchmark/benchmark.pov $(reswrap_header) + $(reswrap_sh) "$@" "$<" "pov" "Benchmark_File" "-m -ta -c 120" 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/readme.md b/tools/meta-make/readme.md index 386d158d0..f104e47db 100644 --- a/tools/meta-make/readme.md +++ b/tools/meta-make/readme.md @@ -19,9 +19,16 @@ 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 outdated converted files. + +To re-create all converted files unconditionally, use: + + make clean all Output 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 From 7b8df0446c531cffcce305d6888c122d2e954490 Mon Sep 17 00:00:00 2001 From: Christoph Lipka Date: Sat, 29 Sep 2018 21:30:46 +0200 Subject: [PATCH 3/4] Change output handling. - Add support for bit depths as low as 1 bpc. - Add support for low-depth greyscale output. - Add support for greyscale preview. - Change PGM gamma handling. - Add more dither algorithms, including blue noise ordered dither. Also refactor some related code. --- .gitignore | 7 + changes.txt | 13 + .../scenes/output/dither_showcase.pov | 118 +++++ source/base/data/bluenoise64a.cpp | 307 +++++++++++++ source/base/data/bluenoise64a.h | 48 ++ source/base/image/dither.cpp | 428 ++++++++++++++++++ source/base/image/dither.h | 386 ++++++++++++++++ source/base/image/encoding.cpp | 278 +----------- source/base/image/encoding.h | 126 +----- source/base/image/hdr.cpp | 12 +- source/base/image/image.cpp | 13 +- source/base/image/image.h | 13 +- source/base/image/jpeg.cpp | 16 +- source/base/image/png.cpp | 252 ++--------- source/base/image/ppm.cpp | 14 +- source/frontend/display.cpp | 12 +- source/frontend/display.h | 10 +- source/frontend/imagemessagehandler.cpp | 53 ++- source/frontend/imageprocessing.cpp | 14 +- source/frontend/processrenderoptions.cpp | 50 +- source/frontend/renderfrontend.cpp | 11 +- source/frontend/renderfrontend.h | 12 +- source/frontend/simplefrontend.h | 6 +- source/povmain.cpp | 11 +- source/povms/povmscpp.h | 2 + tools/meta-make/Makefile | 97 +++- tools/meta-make/bluenoise/BlueNoise.py | 418 +++++++++++++++++ tools/meta-make/bluenoise/LICENSE | 121 +++++ tools/meta-make/bluenoise/README.md | 5 + .../meta-make/bluenoise/metagen-bluenoise.py | 203 +++++++++ tools/meta-make/metagen-header.cpp | 43 ++ tools/meta-make/metagen.sh | 31 ++ tools/meta-make/readme.md | 6 +- unix/disp.h | 6 +- unix/disp_sdl.cpp | 4 +- unix/disp_sdl.h | 4 +- unix/disp_text.h | 6 +- vfe/unix/unixconsole.cpp | 11 +- vfe/vfe.cpp | 2 +- vfe/vfe.h | 9 +- vfe/vfedisplay.cpp | 6 +- vfe/vfesession.cpp | 10 +- vfe/vfesession.h | 8 +- windows/pvdisplay.cpp | 6 +- windows/pvdisplay.h | 8 +- windows/pvfrontend.cpp | 8 +- windows/vs2015/povbase.vcxproj | 4 + windows/vs2015/povbase.vcxproj.filters | 18 + 48 files changed, 2474 insertions(+), 772 deletions(-) create mode 100644 distribution/scenes/output/dither_showcase.pov create mode 100644 source/base/data/bluenoise64a.cpp create mode 100644 source/base/data/bluenoise64a.h create mode 100644 source/base/image/dither.cpp create mode 100644 source/base/image/dither.h create mode 100644 tools/meta-make/bluenoise/BlueNoise.py create mode 100644 tools/meta-make/bluenoise/LICENSE create mode 100644 tools/meta-make/bluenoise/README.md create mode 100644 tools/meta-make/bluenoise/metagen-bluenoise.py create mode 100644 tools/meta-make/metagen-header.cpp create mode 100644 tools/meta-make/metagen.sh 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 769d372ac..bd5577479 100644 --- a/changes.txt +++ b/changes.txt @@ -68,6 +68,12 @@ Changed Behaviour `mixed`. See the documentation for details. - 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 ------------ @@ -85,6 +91,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/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/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/image/dither.cpp b/source/base/image/dither.cpp new file mode 100644 index 000000000..2ff261656 --- /dev/null +++ b/source/base/image/dither.cpp @@ -0,0 +1,428 @@ +//****************************************************************************** +/// +/// @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]; +} + +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 (); + case DitherMethodId::kDiffusion1D: s = std::make_shared (); + case DitherMethodId::kSierraLite: s = std::make_shared (imageWidth); + case DitherMethodId::kFloydSteinberg: s = std::make_shared (imageWidth); + case DitherMethodId::kBayer2x2: s = std::make_shared (BayerMatrix2, imageWidth); + case DitherMethodId::kBayer3x3: s = std::make_shared (BayerMatrix3, imageWidth); + case DitherMethodId::kBayer4x4: s = std::make_shared (BayerMatrix4, imageWidth); + case DitherMethodId::kBlueNoise: s = std::make_shared (BlueNoise64a, imageWidth); + case DitherMethodId::kBlueNoiseX: s = std::make_shared (BlueNoise64a, imageWidth, true); + case DitherMethodId::kAtkinson: s = std::make_shared (AtkinsonMatrix, imageWidth); + case DitherMethodId::kBurkes: s = std::make_shared (BurkesMatrix, imageWidth); + case DitherMethodId::kJarvisJudiceNinke:s = std::make_shared (JarvisJudiceNinkeMatrix, imageWidth); + case DitherMethodId::kSierra3: s = std::make_shared (Sierra3Matrix, imageWidth); + case DitherMethodId::kSierra2: s = std::make_shared (Sierra2Matrix, imageWidth); + case DitherMethodId::kStucki: s = std::make_shared (StuckiMatrix, imageWidth); + } + 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 a2dd7f2fa..f751b67a5 100644 --- a/source/base/image/encoding.cpp +++ b/source/base/image/encoding.cpp @@ -37,10 +37,8 @@ // Unit header file must be the first file included within POV-Ray *.cpp files (pulls in config) #include "base/image/encoding.h" -// Standard C++ header files -#include - -// 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 @@ -53,278 +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 } } -}; - -/*******************************************************************************/ - -/// "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; -}; - -/// Bayer ordered dithering strategy. -/// -/// This stateless dithering strategy varies the quantization threshold for -/// each pixel based on a repeating pattern as proposed by B.E. Bayer in 1973. -/// -class BayerDither : public DitherStrategy -{ - public: - BayerDither(unsigned int mxSize); - virtual void GetOffset(unsigned int x, unsigned int y, ColourOffset& offLin, ColourOffset& offQnt) override; - static inline float GetOffset(unsigned int x, unsigned int y, unsigned int ms) { return BayerMatrices[ms][x%ms][y%ms]; } - protected: - unsigned int matrixSize; -}; - -/// 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. -/// -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. -/// -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. -/// -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; -}; - -/*******************************************************************************/ - -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) -{ - 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) -} - -/*******************************************************************************/ - -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_SierraLite: return DitherStrategySPtr(new SierraLiteDither(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) diff --git a/source/base/image/encoding.h b/source/base/image/encoding.h index 25b844646..f4e444efd 100644 --- a/source/base/image/encoding.h +++ b/source/base/image/encoding.h @@ -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; - -//***************************************************************************** -/// -/// @name Dithering -/// -/// The following types and functions provide dithering functionality. -/// -/// @{ - -enum DitherMethodId -{ - kPOVList_DitherMethod_None, - kPOVList_DitherMethod_Diffusion1D, - kPOVList_DitherMethod_SierraLite, - 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; - -/// 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 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 @@ -283,17 +185,21 @@ inline unsigned int IntEncode(const GammaCurvePtr& g, float x, unsigned int max, return IntEncode(x, max, qOff, err); float xEff = clip(x, 0.0f, 1.0f) + err; - unsigned int v = IntEncodeDown(GammaCurve::Encode(g, xEff), max, qOff); - err = xEff - IntDecode(g, v, max); - if (v < max) + 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) { - float errUp = xEff - IntDecode(g, v + 1, qOff); - if (fabs(errUp) <= fabs(err)) - { - ++v; - err = errUp; - } + decoded = decodedUp; + ++v; } + err = xEff - decoded; return v; } 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 813ea2600..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) @@ -576,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 + { + err = POVMSUtil_SetBool(obj, kPOVAttrib_GrayscaleOutput, true); + ++param; } - else if (isdigit(*param) != 0) + } + if ((err == kNoErr) && (*param > ' ')) + { + if (isdigit(*param) != 0) { if (sscanf(param, "%d%n", &intval, &intval2) == 1) { @@ -1103,12 +1101,20 @@ struct ProcessRenderOptions::Parameter_Code_Table DitherMethodTable[] = { // code, internalId, - { "B2", kPOVList_DitherMethod_Bayer2x2, "2x2 Bayer pattern" }, - { "B3", kPOVList_DitherMethod_Bayer3x3, "3x3 Bayer pattern" }, - { "B4", kPOVList_DitherMethod_Bayer4x4, "4x4 Bayer pattern" }, - { "D1", kPOVList_DitherMethod_Diffusion1D, "Simple 1-D error diffusion" }, - { "D2", kPOVList_DitherMethod_SierraLite, "Simple 2-D error diffusion (Sierra Lite)" }, - { "FS", kPOVList_DitherMethod_FloydSteinberg, "Floyd-Steinberg error diffusion" }, + { "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, "(unknown)" } diff --git a/source/frontend/renderfrontend.cpp b/source/frontend/renderfrontend.cpp index 5245153cc..4c9aa1adb 100644 --- a/source/frontend/renderfrontend.cpp +++ b/source/frontend/renderfrontend.cpp @@ -36,15 +36,20 @@ // 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" @@ -645,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); @@ -1159,7 +1164,7 @@ 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); t = ProcessRenderOptions::GetDitherMethodText(i); tsb->printf(" Dithering............%s\n", t); 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/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 index 8587342bf..0aeafb9aa 100644 --- a/tools/meta-make/Makefile +++ b/tools/meta-make/Makefile @@ -1,36 +1,95 @@ -include = ../../distribution/include -scenes = ../../distribution/scenes -backend = ../../source/backend -base = ../../source/base -reswrap_header = reswrap-header.cpp -reswrap_sh = ./reswrap.sh +#************************************************************************************* +# 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)) -all: $(FONTS) $(BENCHMARK) +ALLFILES = $(FONTFILES) $(BENCHMARKFILES) $(BLUENOISEFILES) + +#************************************************************************************* +# Rules + +.PHONY: all +all: $(ALLFILES) .PHONY: clean clean: - -for name in $(FONTS); do \ - rm "$(base)/font/$${name}.cpp" "$(base)/font/$${name}.h"; \ - done - -for name in $(BENCHMARK); do \ - rm "$(backend)/control/$${name}.cpp" "$(backend)/control/$${name}.h"; \ - done + -rm $(ALLFILES) + +#------------------------------------------------------------------------------------- +# Fonts -$(FONTS): %: $(base)/font/%.cpp $(base)/font/%.h +vpath %.ttf $(include) -$(base)/font/%.cpp: $(include)/%.ttf $(reswrap_header) +$(fontsrc)/%.cpp: %.ttf $(reswrap_header) $(reswrap_sh) "$@" "$<" "pov_base" "font_$(*F)" "-z" -$(base)/font/%.h: $(include)/%.ttf $(reswrap_header) +$(fontsrc)/%.h: %.ttf $(reswrap_header) $(reswrap_sh) "$@" "$<" "pov_base" "font_$(*F)" "-z" -$(BENCHMARK): benchmark_%: ../../source/backend/control/benchmark_%.cpp ../../source/backend/control/benchmark_%.h +#------------------------------------------------------------------------------------- +# Scenes -$(backend)/control/benchmark_ini.%: $(scenes)/advanced/benchmark/benchmark.ini $(reswrap_header) +vpath %.ini $(scenes) +vpath %.pov $(scenes) + +$(scenesrc)/benchmark_ini.%: advanced/benchmark/benchmark.ini $(reswrap_header) $(reswrap_sh) "$@" "$<" "pov" "Benchmark_Options" "-m -ta -c 120" -$(backend)/control/benchmark_pov.%: $(scenes)/advanced/benchmark/benchmark.pov $(reswrap_header) +$(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/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 f104e47db..3b51cb76e 100644 --- a/tools/meta-make/readme.md +++ b/tools/meta-make/readme.md @@ -13,6 +13,7 @@ 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 @@ -24,12 +25,14 @@ directory, and invoke the following command: make -This will re-create all outdated converted files. +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 ====== @@ -38,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/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 0ffe15633..8ad26788d 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 From fa813f12119742e5597fdc7e265c1681663b8aab Mon Sep 17 00:00:00 2001 From: Christoph Lipka Date: Sun, 30 Sep 2018 23:54:57 +0200 Subject: [PATCH 4/4] Move lonely gamma sample scene from `scenes/gamma` to `scenes/output`. --- .../scenes/{gamma => output}/gamma_showcase.pov | 0 .../previews/{gamma => output}/gamma_showcase.jpg | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename distribution/scenes/{gamma => output}/gamma_showcase.pov (100%) rename distribution/scenes/previews/{gamma => output}/gamma_showcase.jpg (100%) 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