Przeglądaj źródła

Implement Blur and Glow font-effects. Some clean-up.

Michael Ragazzon 6 lat temu
rodzic
commit
396e23b774

+ 4 - 0
CMake/FileList.cmake

@@ -32,6 +32,8 @@ set(Core_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/EventIterators.h
     ${PROJECT_SOURCE_DIR}/Source/Core/EventSpecification.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FileInterfaceDefault.h
+    ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectBlur.h
+    ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectGlow.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectOutline.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadow.h
     ${PROJECT_SOURCE_DIR}/Source/Core/IdNameMap.h
@@ -229,6 +231,8 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/FileInterface.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FileInterfaceDefault.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffect.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectBlur.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectGlow.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectOutline.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadow.cpp

+ 5 - 5
Include/RmlUi/Core/ConvolutionFilter.h

@@ -39,13 +39,13 @@ namespace Core {
 	@author Peter Curry
  */
 
-class ConvolutionFilter
+class RMLUICORE_API ConvolutionFilter
 {
 public:
 	enum FilterOperation
 	{
-		// The result is the mean value of all the filtered pixels.
-		MEAN,
+		// The result is the sum of all the filtered pixels.
+		SUM,
 		// The result is the smallest value of all filtered pixels.
 		DILATION,
 		// The result is the largest value of all the filtered pixels.
@@ -56,9 +56,9 @@ public:
 	~ConvolutionFilter();
 
 	/// Initialises the filter. A filter must be initialised and populated with values before use.
-	/// @param[in] kernel_size The size of the filter's kernel each side of the origin. So, for example, a filter initialised with a size of 1 will store 9 values.
+	/// @param[in] kernel_radius The size of the filter's kernel on each side of the origin. So, for example, a filter initialised with a radius of 1 will store 9 values.
 	/// @param[in] operation The operation the filter conducts to determine the result.
-	bool Initialise(int kernel_size, FilterOperation operation = MEAN);
+	bool Initialise(int kernel_radius, FilterOperation operation = SUM);
 
 	/// Returns a reference to one of the rows of the filter kernel.
 	/// @param[in] index The index of the desired row.

+ 3 - 6
Include/RmlUi/Core/FontEffect.h

@@ -41,7 +41,7 @@ namespace Core {
 class RMLUICORE_API FontEffect
 {
 public:
-	// Behind or in front of main text
+	// Behind or in front of the main text.
 	enum class Layer { Back, Front };
 
 	FontEffect();
@@ -59,19 +59,16 @@ public:
 	/// @return False if the effect is not providing support for the glyph, true otherwise.
 	virtual bool GetGlyphMetrics(Vector2i& origin, Vector2i& dimensions, const FontGlyph& glyph) const;
 
-	/// Requests the effect to generate the texture data for a single glyph's bitmap. The default implementation does
-	/// nothing.
-	/// @param[out] destination_data The top-left corner of the glyph's 32-bit, RGBA-ordered, destination texture. Note that they glyph shares its texture with other glyphs.
+	/// Requests the effect to generate the texture data for a single glyph's bitmap. The default implementation does nothing.
+	/// @param[out] destination_data The top-left corner of the glyph's 32-bit, RGBA-ordered, destination texture. Note that the glyph shares its texture with other glyphs.
 	/// @param[in] destination_dimensions The dimensions of the glyph's area on its texture.
 	/// @param[in] destination_stride The stride of the glyph's texture.
 	/// @param[in] glyph The glyph the effect is being asked to generate an effect texture for.
 	virtual void GenerateGlyphTexture(byte* destination_data, const Vector2i& destination_dimensions, int destination_stride, const FontGlyph& glyph) const;
 
 	/// Sets the colour of the effect's geometry.
-	/// @param[in] colour The effect's colour.
 	void SetColour(const Colourb& colour);
 	/// Returns the effect's colour.
-	/// @return The colour of the effect.
 	const Colourb& GetColour() const;
 
 	Layer GetLayer() const;

+ 1 - 1
Include/RmlUi/Core/FontGlyph.h

@@ -40,7 +40,7 @@ namespace Core {
 	@author Peter Curry
  */
 
-class FontGlyph
+class RMLUICORE_API FontGlyph
 {
 public:
 	FontGlyph() : dimensions(0,0), bearing(0,0), advance(0), bitmap_data(nullptr), bitmap_dimensions(0,0)

+ 17 - 22
Source/Core/ConvolutionFilter.cpp

@@ -37,7 +37,7 @@ ConvolutionFilter::ConvolutionFilter()
 	kernel_size = 0;
 	kernel = nullptr;
 
-	operation = MEAN;
+	operation = SUM;
 }
 
 ConvolutionFilter::~ConvolutionFilter()
@@ -45,13 +45,12 @@ ConvolutionFilter::~ConvolutionFilter()
 	delete[] kernel;
 }
 
-// Initialises the filter. A filter must be initialised and populated with values before use.
-bool ConvolutionFilter::Initialise(int _kernel_size, FilterOperation _operation)
+bool ConvolutionFilter::Initialise(int _kernel_radius, FilterOperation _operation)
 {
-	if (_kernel_size <= 0)
+	if (_kernel_radius <= 0)
 		return false;
 
-	kernel_size = Math::Max(_kernel_size, 1);
+	kernel_size = Math::Max(_kernel_radius, 1);
 	kernel_size = kernel_size * 2 + 1;
 
 	kernel = new float[kernel_size * kernel_size];
@@ -61,7 +60,6 @@ bool ConvolutionFilter::Initialise(int _kernel_size, FilterOperation _operation)
 	return true;
 }
 
-// Returns a reference to one of the rows of the filter kernel.
 float* ConvolutionFilter::operator[](int index)
 {
 	RMLUI_ASSERT(kernel != nullptr);
@@ -72,51 +70,48 @@ float* ConvolutionFilter::operator[](int index)
 	return kernel + kernel_size * index;
 }
 
-// Runs the convolution filter.
 void ConvolutionFilter::Run(byte* destination, const Vector2i destination_dimensions, int destination_stride, const byte* source, const Vector2i source_dimensions, const Vector2i source_offset) const
 {
+	const float initial_opacity = (operation == EROSION ? FLT_MAX : 0.f);
+
+	const int kernel_radius = (kernel_size - 1) / 2;
+
 	for (int y = 0; y < destination_dimensions.y; ++y)
 	{
 		for (int x = 0; x < destination_dimensions.x; ++x)
 		{
-			int num_pixels = 0;
-			int opacity = 0;
+			float opacity = initial_opacity;
 
 			for (int kernel_y = 0; kernel_y < kernel_size; ++kernel_y)
 			{
-				int source_y = y - source_offset.y - ((kernel_size - 1) / 2) + kernel_y;
+				int source_y = y - source_offset.y - kernel_radius + kernel_y;
 
 				for (int kernel_x = 0; kernel_x < kernel_size; ++kernel_x)
 				{
-					int pixel_opacity;
+					float pixel_opacity;
 
-					int source_x = x - source_offset.x - ((kernel_size - 1) / 2) + kernel_x;
+					int source_x = x - source_offset.x - kernel_radius + kernel_x;
 					if (source_y >= 0 &&
 						source_y < source_dimensions.y &&
 						source_x >= 0 &&
 						source_x < source_dimensions.x)
 					{
-						pixel_opacity = Math::RealToInteger(source[source_y * source_dimensions.x + source_x] * kernel[kernel_y * kernel_size + kernel_x]);
+						pixel_opacity = float(source[source_y * source_dimensions.x + source_x]) * kernel[kernel_y * kernel_size + kernel_x];
 					}
 					else
 						pixel_opacity = 0;
 
 					switch (operation)
 					{
-						case MEAN:      opacity += pixel_opacity; break;
+						case SUM:       opacity += pixel_opacity; break;
 						case DILATION:  opacity = Math::Max(opacity, pixel_opacity); break;
-						case EROSION:   opacity = num_pixels == 0 ? pixel_opacity : Math::Min(opacity, pixel_opacity); break;
+						case EROSION:   opacity = Math::Min(opacity, pixel_opacity); break;
 					}
-
-					++num_pixels;
 				}
 			}
 
-			if (operation == MEAN)
-				opacity /= num_pixels;
-
-			opacity = Math::Min(255, opacity);
-			destination[x * 4 + 3] = (byte) opacity;
+			opacity = Math::Min(255.f, opacity);
+			destination[x * 4 + 3] = byte(opacity);
 		}
 
 		destination += destination_stride;

+ 8 - 2
Source/Core/Factory.cpp

@@ -40,6 +40,8 @@
 #include "ElementImage.h"
 #include "ElementTextDefault.h"
 #include "EventInstancerDefault.h"
+#include "FontEffectBlur.h"
+#include "FontEffectGlow.h"
 #include "FontEffectOutline.h"
 #include "FontEffectShadow.h"
 #include "PluginRegistry.h"
@@ -98,8 +100,10 @@ struct DefaultInstancers {
 	Ptr<DecoratorInstancer> decorator_ninepatch = std::make_unique<DecoratorNinePatchInstancer>();
 	Ptr<DecoratorInstancer> decorator_gradient = std::make_unique<DecoratorGradientInstancer>();
 
-	Ptr<FontEffectInstancer> font_effect_shadow = std::make_unique<FontEffectShadowInstancer>();
+	Ptr<FontEffectInstancer> font_effect_blur = std::make_unique<FontEffectBlurInstancer>();
+	Ptr<FontEffectInstancer> font_effect_glow = std::make_unique<FontEffectGlowInstancer>();
 	Ptr<FontEffectInstancer> font_effect_outline = std::make_unique<FontEffectOutlineInstancer>();
+	Ptr<FontEffectInstancer> font_effect_shadow = std::make_unique<FontEffectShadowInstancer>();
 };
 
 static UniquePtr<DefaultInstancers> default_instancers;
@@ -151,8 +155,10 @@ bool Factory::Initialise()
 	RegisterDecoratorInstancer("ninepatch", default_instancers->decorator_ninepatch.get());
 	RegisterDecoratorInstancer("gradient", default_instancers->decorator_gradient.get());
 
-	RegisterFontEffectInstancer("shadow", default_instancers->font_effect_shadow.get());
+	RegisterFontEffectInstancer("blur", default_instancers->font_effect_blur.get());
+	RegisterFontEffectInstancer("glow", default_instancers->font_effect_glow.get());
 	RegisterFontEffectInstancer("outline", default_instancers->font_effect_outline.get());
+	RegisterFontEffectInstancer("shadow", default_instancers->font_effect_shadow.get());
 
 	// Register the core XML node handlers.
 	XMLParser::RegisterNodeHandler("", std::make_shared<XMLNodeHandlerDefault>());

+ 0 - 5
Source/Core/FontEffect.cpp

@@ -42,13 +42,11 @@ FontEffect::~FontEffect()
 {
 }
 
-// Asks the font effect if it requires, and will generate, its own unique texture.
 bool FontEffect::HasUniqueTexture() const
 {
 	return false;
 }
 
-// Gets the effect to resize and reposition a glyph's bitmap.
 bool FontEffect::GetGlyphMetrics(Vector2i& RMLUI_UNUSED_PARAMETER(origin), Vector2i& RMLUI_UNUSED_PARAMETER(dimensions), const FontGlyph& RMLUI_UNUSED_PARAMETER(glyph)) const
 {
 	RMLUI_UNUSED(origin);
@@ -58,7 +56,6 @@ bool FontEffect::GetGlyphMetrics(Vector2i& RMLUI_UNUSED_PARAMETER(origin), Vecto
 	return false;
 }
 
-// Requests the effect to generate the texture data for a single glyph's bitmap.
 void FontEffect::GenerateGlyphTexture(byte* RMLUI_UNUSED_PARAMETER(destination_data), const Vector2i& RMLUI_UNUSED_PARAMETER(destination_dimensions), int RMLUI_UNUSED_PARAMETER(destination_stride), const FontGlyph& RMLUI_UNUSED_PARAMETER(glyph)) const
 {
 	RMLUI_UNUSED(destination_data);
@@ -67,13 +64,11 @@ void FontEffect::GenerateGlyphTexture(byte* RMLUI_UNUSED_PARAMETER(destination_d
 	RMLUI_UNUSED(glyph);
 }
 
-// Sets the colour of the effect's geometry.
 void FontEffect::SetColour(const Colourb& _colour)
 {
 	colour = _colour;
 }
 
-// Returns the effect's colour.
 const Colourb& FontEffect::GetColour() const
 {
 	return colour;

+ 140 - 0
Source/Core/FontEffectBlur.cpp

@@ -0,0 +1,140 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "precompiled.h"
+#include "FontEffectBlur.h"
+
+namespace Rml {
+namespace Core {
+
+FontEffectBlur::FontEffectBlur()
+{
+	width = 0;
+	SetLayer(Layer::Back);
+}
+
+FontEffectBlur::~FontEffectBlur()
+{
+}
+
+bool FontEffectBlur::HasUniqueTexture() const
+{
+	return true;
+}
+
+bool FontEffectBlur::Initialise(int _width)
+{
+	if (_width <= 0)
+		return false;
+
+	width = _width;
+
+	const float std_dev = .5f * float(width);
+	const float two_variance = 2.f * std_dev * std_dev;
+	const float gain = 1.f / (Math::RMLUI_PI * two_variance);
+
+	float sum_weight = 0.f;
+
+	// @performance: Can separate into horizontal and vertical pass
+	filter.Initialise(width, ConvolutionFilter::SUM);
+	for (int x = -width; x <= width; ++x)
+	{
+		for (int y = -width; y <= width; ++y)
+		{
+			float weight = gain * Math::Exp( -Math::SquareRoot(float(x * x + y * y) / two_variance) );
+			
+			filter[x + width][y + width] = weight;
+			sum_weight += weight;
+		}
+	}
+
+	// Normalize the kernel
+	for (int x = -width; x <= width; ++x)
+		for (int y = -width; y <= width; ++y)
+			filter[x + width][y + width] /= sum_weight;
+
+	return true;
+}
+
+bool FontEffectBlur::GetGlyphMetrics(Vector2i& origin, Vector2i& dimensions, const FontGlyph& RMLUI_UNUSED_PARAMETER(glyph)) const
+{
+	RMLUI_UNUSED(glyph);
+
+	if (dimensions.x * dimensions.y > 0)
+	{
+		origin.x -= width;
+		origin.y -= width;
+
+		dimensions.x += width;
+		dimensions.y += width;
+
+		return true;
+	}
+
+	return false;
+}
+
+void FontEffectBlur::GenerateGlyphTexture(byte* destination_data, const Vector2i& destination_dimensions, int destination_stride, const FontGlyph& glyph) const
+{
+	filter.Run(destination_data, destination_dimensions, destination_stride, glyph.bitmap_data, glyph.bitmap_dimensions, Vector2i(width, width));
+}
+
+
+
+
+
+FontEffectBlurInstancer::FontEffectBlurInstancer() : id_width(PropertyId::Invalid), id_color(PropertyId::Invalid)
+{
+	id_width = RegisterProperty("width", "1px", true).AddParser("length").GetId();
+	id_color = RegisterProperty("color", "white", false).AddParser("color").GetId();
+	RegisterShorthand("font-effect", "width, color", ShorthandType::FallThrough);
+}
+
+FontEffectBlurInstancer::~FontEffectBlurInstancer()
+{
+}
+
+SharedPtr<FontEffect> FontEffectBlurInstancer::InstanceFontEffect(const String& RMLUI_UNUSED_PARAMETER(name), const PropertyDictionary& properties)
+{
+	RMLUI_UNUSED(name);
+
+	float width = properties.GetProperty(id_width)->Get< float >();
+	Colourb color = properties.GetProperty(id_color)->Get< Colourb >();
+
+	auto font_effect = std::make_shared<FontEffectBlur>();
+	if (font_effect->Initialise(Math::RealToInteger(width)))
+	{
+		font_effect->SetColour(color);
+		return font_effect;
+	}
+
+	return nullptr;
+}
+
+}
+}

+ 84 - 0
Source/Core/FontEffectBlur.h

@@ -0,0 +1,84 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef RMLUICOREFONTEFFECTBLUR_H
+#define RMLUICOREFONTEFFECTBLUR_H
+
+#include "../../Include/RmlUi/Core/ConvolutionFilter.h"
+#include "../../Include/RmlUi/Core/FontEffect.h"
+#include "../../Include/RmlUi/Core/FontEffectInstancer.h"
+
+namespace Rml {
+namespace Core {
+
+/**
+	A concrete font effect for rendering Gaussian blurred text.
+ */
+
+class FontEffectBlur : public FontEffect
+{
+public:
+	FontEffectBlur();
+	virtual ~FontEffectBlur();
+
+	bool Initialise(int width);
+
+	bool HasUniqueTexture() const override;
+
+	bool GetGlyphMetrics(Vector2i& origin, Vector2i& dimensions, const FontGlyph& glyph) const override;
+
+	void GenerateGlyphTexture(byte* destination_data, const Vector2i& destination_dimensions, int destination_stride, const FontGlyph& glyph) const override;
+
+private:
+	int width;
+	ConvolutionFilter filter;
+};
+
+
+
+/**
+	A concrete font effect instancer for the blur effect.
+ */
+
+class FontEffectBlurInstancer : public FontEffectInstancer
+{
+public:
+	FontEffectBlurInstancer();
+	virtual ~FontEffectBlurInstancer();
+
+	SharedPtr<FontEffect> InstanceFontEffect(const String& name, const PropertyDictionary& properties) override;
+
+private:
+	PropertyId id_width, id_color;
+};
+
+
+}
+}
+
+#endif

+ 180 - 0
Source/Core/FontEffectGlow.cpp

@@ -0,0 +1,180 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "precompiled.h"
+#include "FontEffectGlow.h"
+
+namespace Rml {
+namespace Core {
+
+FontEffectGlow::FontEffectGlow()
+{
+	width_blur = 0;
+	width_outline = 0;
+	combined_width = 0;
+	SetLayer(Layer::Back);
+}
+
+FontEffectGlow::~FontEffectGlow()
+{
+}
+
+bool FontEffectGlow::HasUniqueTexture() const
+{
+	return true;
+}
+
+bool FontEffectGlow::Initialise(int _width_outline, int _width_blur)
+{
+	if (_width_outline <= 0 || _width_blur <= 0)
+		return false;
+
+	width_outline = _width_outline;
+	width_blur = _width_blur;
+	combined_width = width_blur + width_outline;
+
+	// Outline filter
+	// @performance: I think we can separate into horizontal and vertical pass?
+	filter_outline.Initialise(width_outline, ConvolutionFilter::DILATION);
+	for (int x = -width_outline; x <= width_outline; ++x)
+	{
+		for (int y = -width_outline; y <= width_outline; ++y)
+		{
+			float weight = 1;
+
+			float distance = Math::SquareRoot(float(x * x + y * y));
+			if (distance > width_outline)
+			{
+				weight = (width_outline + 1) - distance;
+				weight = Math::Max(weight, 0.0f);
+			}
+
+			filter_outline[x + width_outline][y + width_outline] = weight;
+		}
+	}
+
+	// Gaussian blur filter
+	const float std_dev = .5f * float(width_blur);
+	const float two_variance = 2.f * std_dev * std_dev;
+	const float gain = 1.f / (Math::RMLUI_PI * two_variance);
+
+	float sum_weight = 0.f;
+
+	// @performance: Can separate into horizontal and vertical pass
+	filter_blur.Initialise(width_blur, ConvolutionFilter::SUM);
+	for (int x = -width_blur; x <= width_blur; ++x)
+	{
+		for (int y = -width_blur; y <= width_blur; ++y)
+		{
+			float weight = gain * Math::Exp(-Math::SquareRoot(float(x * x + y * y) / two_variance));
+
+			filter_blur[x + width_blur][y + width_blur] = weight;
+			sum_weight += weight;
+		}
+	}
+
+	// Normalize the blur kernel
+	for (int x = -width_blur; x <= width_blur; ++x)
+		for (int y = -width_blur; y <= width_blur; ++y)
+			filter_blur[x + width_blur][y + width_blur] /= sum_weight;
+
+
+	return true;
+}
+
+bool FontEffectGlow::GetGlyphMetrics(Vector2i& origin, Vector2i& dimensions, const FontGlyph& RMLUI_UNUSED_PARAMETER(glyph)) const
+{
+	RMLUI_UNUSED(glyph);
+
+	if (dimensions.x * dimensions.y > 0)
+	{
+		origin.x -= combined_width;
+		origin.y -= combined_width;
+
+		dimensions.x += combined_width;
+		dimensions.y += combined_width;
+
+		return true;
+	}
+
+	return false;
+}
+
+void FontEffectGlow::GenerateGlyphTexture(byte* destination_data, const Vector2i& destination_dimensions, int destination_stride, const FontGlyph& glyph) const
+{
+	const int buf_size = destination_dimensions.x * destination_dimensions.y * 4;
+	byte* outline_output = new byte[buf_size];
+	memset(outline_output, 0, buf_size);
+
+	filter_outline.Run(outline_output, destination_dimensions, destination_dimensions.x * 4, glyph.bitmap_data, glyph.bitmap_dimensions, Vector2i(combined_width));
+
+	for (int i = 0; i < buf_size / 4; i++)
+		outline_output[i] = outline_output[i * 4 + 3];
+
+	filter_blur.Run(destination_data, destination_dimensions, destination_stride, outline_output, destination_dimensions, Vector2i(0));
+
+	delete[] outline_output;
+}
+
+
+
+FontEffectGlowInstancer::FontEffectGlowInstancer() : id_width_outline(PropertyId::Invalid), id_width_blur(PropertyId::Invalid),id_color(PropertyId::Invalid)
+{
+	id_width_outline = RegisterProperty("width-outline", "-1px", true).AddParser("length").GetId();
+	id_width_blur = RegisterProperty("width-blur", "1px", true).AddParser("length").GetId();
+	id_color = RegisterProperty("color", "white", false).AddParser("color").GetId();
+	RegisterShorthand("font-effect", "width-outline, width-blur, color", ShorthandType::FallThrough);
+}
+
+FontEffectGlowInstancer::~FontEffectGlowInstancer()
+{
+}
+
+SharedPtr<FontEffect> FontEffectGlowInstancer::InstanceFontEffect(const String& RMLUI_UNUSED_PARAMETER(name), const PropertyDictionary& properties)
+{
+	RMLUI_UNUSED(name);
+
+	int width_outline = properties.GetProperty(id_width_outline)->Get< int >();
+	int width_blur = properties.GetProperty(id_width_blur)->Get< int >();
+	Colourb color = properties.GetProperty(id_color)->Get< Colourb >();
+
+	if (width_blur < 0)
+		width_blur = width_outline;
+
+	auto font_effect = std::make_shared<FontEffectGlow>();
+	if (font_effect->Initialise(width_blur, width_outline))
+	{
+		font_effect->SetColour(color);
+		return font_effect;
+	}
+
+	return nullptr;
+}
+
+}
+}

+ 92 - 0
Source/Core/FontEffectGlow.h

@@ -0,0 +1,92 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef RMLUICOREFONTEFFECTGLOW_H
+#define RMLUICOREFONTEFFECTGLOW_H
+
+#include "../../Include/RmlUi/Core/ConvolutionFilter.h"
+#include "../../Include/RmlUi/Core/FontEffect.h"
+#include "../../Include/RmlUi/Core/FontEffectInstancer.h"
+
+namespace Rml {
+namespace Core {
+
+/**
+	A font effect for rendering glow around text.
+
+	Glow consists of an outline pass followed by a Gaussian blur pass.
+
+ */
+
+class FontEffectGlow : public FontEffect
+{
+public:
+	FontEffectGlow();
+	virtual ~FontEffectGlow();
+
+	bool Initialise(int width_outline, int width_blur);
+
+	bool HasUniqueTexture() const override;
+
+	bool GetGlyphMetrics(Vector2i& origin, Vector2i& dimensions, const FontGlyph& glyph) const override;
+
+	/// Expands the original glyph texture for the outline.
+	/// @param[out] destination_data The top-left corner of the glyph's 32-bit, RGBA-ordered, destination texture. Note that they glyph shares its texture with other glyphs.
+	/// @param[in] destination_dimensions The dimensions of the glyph's area on its texture.
+	/// @param[in] destination_stride The stride of the glyph's texture.
+	/// @param[in] glyph The glyph the effect is being asked to generate an effect texture for.
+	void GenerateGlyphTexture(byte* destination_data, const Vector2i& destination_dimensions, int destination_stride, const FontGlyph& glyph) const override;
+
+private:
+	int width_outline, width_blur, combined_width;
+	ConvolutionFilter filter_outline, filter_blur;
+};
+
+
+
+/**
+	A concrete font effect instancer for the glow effect.
+ */
+
+class FontEffectGlowInstancer : public FontEffectInstancer
+{
+public:
+	FontEffectGlowInstancer();
+	virtual ~FontEffectGlowInstancer();
+
+	SharedPtr<FontEffect> InstanceFontEffect(const String& name, const PropertyDictionary& properties) override;
+
+private:
+	PropertyId id_width_outline, id_width_blur, id_color;
+};
+
+
+}
+}
+
+#endif

+ 0 - 10
Source/Core/FontEffectOutline.cpp

@@ -42,13 +42,11 @@ FontEffectOutline::~FontEffectOutline()
 {
 }
 
-// Returns true.
 bool FontEffectOutline::HasUniqueTexture() const
 {
 	return true;
 }
 
-// Initialise the outline effect.
 bool FontEffectOutline::Initialise(int _width)
 {
 	if (_width <= 0)
@@ -77,7 +75,6 @@ bool FontEffectOutline::Initialise(int _width)
 	return true;
 }
 
-// Resizes and repositions the glyph to fit the outline.
 bool FontEffectOutline::GetGlyphMetrics(Vector2i& origin, Vector2i& dimensions, const FontGlyph& RMLUI_UNUSED_PARAMETER(glyph)) const
 {
 	RMLUI_UNUSED(glyph);
@@ -96,7 +93,6 @@ bool FontEffectOutline::GetGlyphMetrics(Vector2i& origin, Vector2i& dimensions,
 	return false;
 }
 
-// Expands the original glyph texture for the outline.
 void FontEffectOutline::GenerateGlyphTexture(byte* destination_data, const Vector2i& destination_dimensions, int destination_stride, const FontGlyph& glyph) const
 {
 	filter.Run(destination_data, destination_dimensions, destination_stride, glyph.bitmap_data, glyph.bitmap_dimensions, Vector2i(width, width));
@@ -104,8 +100,6 @@ void FontEffectOutline::GenerateGlyphTexture(byte* destination_data, const Vecto
 
 
 
-
-
 FontEffectOutlineInstancer::FontEffectOutlineInstancer() : id_width(PropertyId::Invalid), id_color(PropertyId::Invalid)
 {
 	id_width = RegisterProperty("width", "1px", true).AddParser("length").GetId();
@@ -117,7 +111,6 @@ FontEffectOutlineInstancer::~FontEffectOutlineInstancer()
 {
 }
 
-// Instances an outline font effect.
 SharedPtr<FontEffect> FontEffectOutlineInstancer::InstanceFontEffect(const String& RMLUI_UNUSED_PARAMETER(name), const PropertyDictionary& properties)
 {
 	RMLUI_UNUSED(name);
@@ -135,8 +128,5 @@ SharedPtr<FontEffect> FontEffectOutlineInstancer::InstanceFontEffect(const Strin
 	return nullptr;
 }
 
-
-
-
 }
 }

+ 0 - 13
Source/Core/FontEffectOutline.h

@@ -48,27 +48,14 @@ public:
 	FontEffectOutline();
 	virtual ~FontEffectOutline();
 
-	/// Initialise the outline effect.
-	/// @param[in] width The width of the effect. This must be greater than zero.
-	/// @return True if the effect initialised successfully, false if not.
 	bool Initialise(int width);
 
-	/// Returns true.
-	/// @return True.
 	bool HasUniqueTexture() const override;
 
 	/// Resizes and repositions the glyph to fit the outline.
-	/// @param[out] origin The desired origin of the effect's glyph bitmap, as a pixel offset from its original origin. This defaults to (0, 0).
-	/// @param[out] dimensions The desired dimensions of the effect's glyph bitmap, in pixels. This defaults to the dimensions of the glyph's original bitmap.
-	/// @param[in] glyph The glyph the effect is being asked to size.
-	/// @return False if the effect is not providing support for the glyph, true otherwise.
 	bool GetGlyphMetrics(Vector2i& origin, Vector2i& dimensions, const FontGlyph& glyph) const override;
 
 	/// Expands the original glyph texture for the outline.
-	/// @param[out] destination_data The top-left corner of the glyph's 32-bit, RGBA-ordered, destination texture. Note that they glyph shares its texture with other glyphs.
-	/// @param[in] destination_dimensions The dimensions of the glyph's area on its texture.
-	/// @param[in] destination_stride The stride of the glyph's texture.
-	/// @param[in] glyph The glyph the effect is being asked to generate an effect texture for.
 	void GenerateGlyphTexture(byte* destination_data, const Vector2i& destination_dimensions, int destination_stride, const FontGlyph& glyph) const override;
 
 private:

+ 0 - 5
Source/Core/FontEffectShadow.cpp

@@ -41,20 +41,17 @@ FontEffectShadow::~FontEffectShadow()
 {
 }
 
-// Initialise the shadow effect.
 bool FontEffectShadow::Initialise(const Vector2i& _offset)
 {
 	offset = _offset;
 	return true;
 }
 
-// Returns true.
 bool FontEffectShadow::HasUniqueTexture() const
 {
 	return false;
 }
 
-// Resizes and repositions the glyph to fit the outline.
 bool FontEffectShadow::GetGlyphMetrics(Vector2i& origin, Vector2i& RMLUI_UNUSED_PARAMETER(dimensions), const FontGlyph& RMLUI_UNUSED_PARAMETER(glyph)) const
 {
 	RMLUI_UNUSED(dimensions);
@@ -98,7 +95,5 @@ SharedPtr<FontEffect> FontEffectShadowInstancer::InstanceFontEffect(const String
 	return nullptr;
 }
 
-
-
 }
 }

+ 0 - 10
Source/Core/FontEffectShadow.h

@@ -47,20 +47,10 @@ public:
 	FontEffectShadow();
 	virtual ~FontEffectShadow();
 
-	/// Initialise the shadow effect.
-	/// @param[in] offset The offset, in pixels, of the shadow from the original text.
-	/// @return True if the effect initialised successfully, false if not.
 	bool Initialise(const Vector2i& offset);
 
-	/// Returns false.
-	/// @return False.
 	bool HasUniqueTexture() const override;
 
-	/// Repositions the glyph by the offset.
-	/// @param[out] origin The desired origin of the effect's glyph bitmap, as a pixel offset from its original origin. This defaults to (0, 0).
-	/// @param[out] dimensions The desired dimensions of the effect's glyph bitmap, in pixels. This defaults to the dimensions of the glyph's original bitmap.
-	/// @param[in] glyph The glyph the effect is being asked to size.
-	/// @return False if the effect is not providing support for the glyph, true otherwise.
 	bool GetGlyphMetrics(Vector2i& origin, Vector2i& dimensions, const FontGlyph& glyph) const override;
 
 private: