diff --git a/common/color.h b/common/color.h index 8e3076493..72ec68ed4 100644 --- a/common/color.h +++ b/common/color.h @@ -1,6 +1,8 @@ #ifndef COMMON_COLOR_H #define COMMON_COLOR_H +#include + // Used to represent a color. Uses normal 8-bit-per channel RGB. // Note that these colors are in linear space and their interpretation // depends on the blade. @@ -388,6 +390,11 @@ struct OverDriveColor { bool getOverdrive() const { return overdrive; } }; +namespace color_details { + template struct IsOpaqueColor : std::false_type {}; + template<> struct IsOpaqueColor : std::true_type {}; + template<> struct IsOpaqueColor : std::true_type {}; +} // SIMPLE // Opaque.fade -> Opaque or Alpha? diff --git a/styles/layers.h b/styles/layers.h index dcf434ac9..a165b28f4 100644 --- a/styles/layers.h +++ b/styles/layers.h @@ -4,21 +4,33 @@ #include "alpha.h" #include "../functions/int.h" #include "colors.h" +#include "../common/color.h" +#include // Usage: Layers // BASE: COLOR or LAYER // LAYER1, LAYER2: LAYER // return value: COLOR or LAYER (same as BASE) -// This style works like layers in gimp or photoshop. -// In most cases, the layers are expected to be normally transparent effects -// that turn opaque when then want to paint an effect over the base color. -// If the base color is opqaque, the final result of this style will also be -// opaque. If the base color is transparent, the final result may also be transparent, -// depending on what the layers paint on top of the base color. +// This style works like layers in Gimp or Photoshop: each layer adds to the stack covering the BASE. +// The written order of layers in the blade style code puts the BASE first, +// with any following layers visually covering it. (later layers draw over earlier ones) +// The rule for opacity is as follows: +// The output of Layers<> is opaque if the BASE of that Layers<> is opaque. +// If BASE is transparent: the overall result is transparent. +// No additional solid opaque colors may be stacked on top of an opaque BASE. +// +// StylePtr<> expects its argument to be opaque. +// Therefore, when using StylePtr>(), BASE MUST be an opaque color. +// +// Layers<> itself can still be nested anywhere a STYLE/COLOR is allowed; only the top-level StylePtr<...>() +// requirement forces the requirement of the BASE to be opaque in that specific position. template class Compose { public: + static_assert(!color_details::IsOpaqueColor().getColor(0))>::value, + "\n\n" + "*** Layers<> error: Only the base color may be solid.\n\n"); LayerRunResult run(BladeBase* blade) { LayerRunResult base_run_result = RunLayer(&base_, blade); LayerRunResult layer_run_result = RunLayer(&layer_, blade); @@ -73,13 +85,20 @@ class Compose { } }; - template struct LayerSelector {}; template struct LayerSelector { typedef BASE type; }; + +// Drop fully transparent first item (Alpha 0) when there are exactly 2 args. template struct LayerSelector>, L1> { typedef L1 type; }; + +// Drop fully transparent when it’s the second (right) item in a pair. +template +struct LayerSelector>> { typedef BASE type; }; + template struct LayerSelector { typedef Compose type; }; -template struct LayerSelector { - typedef typename LayerSelector, REST...>::type type; +template +struct LayerSelector { + typedef typename LayerSelector::type, REST...>::type type; }; template using Layers = typename LayerSelector::type; diff --git a/styles/style_ptr.h b/styles/style_ptr.h index 0c85ad8ea..c2f6a3fd1 100644 --- a/styles/style_ptr.h +++ b/styles/style_ptr.h @@ -3,6 +3,21 @@ #include "blade_style.h" +template class Compose; // forward-declare Layers node + +namespace style_base_check_detail { + template struct TopBase { using type = T; }; + template struct TopBase> : TopBase {}; + template + inline void AssertLayersBaseOpaque() { + using _TopBaseT = typename TopBase