Переглянути джерело

Refactoring default font engine (part II). Remove font database, use a callback function for generating font textures, documentation improvements.

Michael Ragazzon 6 роки тому
батько
коміт
78acd8b900

+ 0 - 2
CMake/FileList.cmake

@@ -35,7 +35,6 @@ set(Core_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectOutlineInstancer.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadow.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadowInstancer.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontDatabaseDefault.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontEngineInterfaceDefault.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontFace.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontFaceHandleDefault.h
@@ -243,7 +242,6 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectOutlineInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadow.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadowInstancer.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontDatabaseDefault.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontEngineInterfaceDefault.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontFace.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontFaceHandleDefault.cpp

+ 19 - 4
Include/RmlUi/Core/Texture.h

@@ -31,6 +31,7 @@
 
 #include "Header.h"
 #include "Types.h"
+#include <functional>
 
 namespace Rml {
 namespace Core {
@@ -38,6 +39,16 @@ namespace Core {
 class TextureResource;
 class RenderInterface;
 
+/*
+	Callback function for generating textures.
+	/// @param[in] name The name used to set the texture.
+	/// @param[out] data The raw data of the texture, each pixel has four 8-bit channels: red-green-blue-alpha.
+	/// @param[out] dimensions The width and height of the generated texture.
+	/// @return True on success.
+*/
+using TextureCallback = std::function<bool(const String& name, UniquePtr<const byte[]>& data, Vector2i& dimensions)>;
+
+
 /**
 	Abstraction of a two-dimensional texture image, with an application-specific texture handle.
 
@@ -47,11 +58,15 @@ class RenderInterface;
 struct RMLUICORE_API Texture
 {
 public:
-	/// Attempts to load a texture.
-	/// @param[in] source The name of the texture.
+	/// Set the texture source and path. The texture is added to the global cache and only loaded on first use.
+	/// @param[in] source The source of the texture.
 	/// @param[in] source_path The path of the resource that is requesting the texture (ie, the RCSS file in which it was specified, etc).
-	/// @return True if the texture loaded successfully, false if not.
-	bool Load(const String& source, const String& source_path = "");
+	void Set(const String& source, const String& source_path = "");
+
+	/// Set a callback function for generating the texture on first use. The texture is never added to the global cache.
+	/// @param[in] name The name of the texture.
+	/// @param[in] callback The callback function which generates the data of the texture, see TextureCallback.
+	void Set(const String& name, const TextureCallback& callback);
 
 	/// Returns the texture's source name. This is usually the name of the file the texture was loaded from.
 	/// @return The name of the this texture's source. This will be the empty string if this texture is not loaded.

+ 1 - 1
Samples/shell/src/win32/InputWin32.cpp

@@ -136,7 +136,7 @@ void InputWin32::ProcessWindowsEvent(UINT message, WPARAM w_param, LPARAM l_para
 				first_u16_code_unit = 0;
 
 				// Only send through printable characters.
-				if ((unsigned int)code_point >= 32 || code_point == (Rml::Core::CodePoint)'\n')
+				if ((char32_t)code_point >= 32 || code_point == (Rml::Core::CodePoint)'\n')
 					context->ProcessTextInput(code_point);
 			}
 		}

+ 1 - 2
Source/Core/Decorator.cpp

@@ -55,8 +55,7 @@ int Decorator::LoadTexture(const String& texture_name, const String& rcss_path)
 	}
 
 	Texture texture;
-	if (!texture.Load(texture_name, rcss_path))
-		return -1;
+	texture.Set(texture_name, rcss_path);
 
 	additional_textures.push_back(texture);
 	return (int)additional_textures.size();

+ 2 - 1
Source/Core/DecoratorTiledInstancer.cpp

@@ -139,8 +139,9 @@ bool DecoratorTiledInstancer::GetTileProperties(DecoratorTiled::Tile* tiles, Tex
 			{
 				texture = previous_texture;
 			}
-			else if (src_property->source && texture.Load(texture_name, src_property->source->path))
+			else if (src_property->source)
 			{
+				texture.Set(texture_name, src_property->source->path);
 				previous_texture_name = texture_name;
 				previous_texture = texture;
 			}

+ 1 - 5
Source/Core/ElementImage.cpp

@@ -232,11 +232,7 @@ bool ElementImage::LoadTexture()
 	Rml::Core::ElementDocument* document = GetOwnerDocument();
 	URL source_url(document == nullptr ? "" : document->GetSourceURL());
 
-	if (!texture.Load(image_source, source_url.GetPath()))
-	{
-		geometry.SetTexture(nullptr);
-		return false;
-	}
+	texture.Set(image_source, source_url.GetPath());
 
 	// Set the texture onto our geometry object.
 	geometry.SetTexture(&texture);

+ 0 - 132
Source/Core/FontEngineDefault/FontDatabaseDefault.cpp

@@ -1,132 +0,0 @@
-/*
- * 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 <RmlUi/Core.h>
-#include "FontFamily.h"
-#include "FontFace.h"
-#include "FontDatabaseDefault.h"
-
-namespace Rml {
-namespace Core {
-
-#ifndef RMLUI_NO_FONT_INTERFACE_DEFAULT
-
-FontDatabaseDefault* FontDatabaseDefault::instance = nullptr;
-FontDatabaseDefault::FontProviderTable FontDatabaseDefault::font_provider_table;
-
-FontDatabaseDefault::FontDatabaseDefault()
-{
-	RMLUI_ASSERT(instance == nullptr);
-	instance = this;
-}
-
-FontDatabaseDefault::~FontDatabaseDefault()
-{
-	RMLUI_ASSERT(instance == this);
-	instance = nullptr;
-}
-
-bool FontDatabaseDefault::Initialise()
-{
-	if (instance == nullptr)
-	{
-		new FontDatabaseDefault();
-
-        if(!FontProvider::Initialise())
-            return false;
-	}
-
-	return true;
-}
-
-void FontDatabaseDefault::Shutdown()
-{
-	if (instance != nullptr)
-	{
-		FontProvider::Shutdown();
-
-		delete instance;
-	}
-}
-
-// Loads a new font face.
-bool FontDatabaseDefault::LoadFontFace(const String& file_name, bool fallback_face)
-{
-	return FontProvider::Get().LoadFontFace(file_name, fallback_face);
-}
-
-bool FontDatabaseDefault::LoadFontFace(const byte* data, int data_size, const String& font_family, Style::FontStyle style, Style::FontWeight weight, bool fallback_face)
-{
-	return FontProvider::Get().LoadFontFace(data, data_size, font_family, style, weight, fallback_face);
-}
-
-// Returns a handle to a font face that can be used to position and render text.
-SharedPtr<FontFaceHandleDefault> FontDatabaseDefault::GetFontFaceHandle(const String& family, Style::FontStyle style, Style::FontWeight weight, int size)
-{
-	return FontProvider::Get().GetFontFaceHandle(family, style, weight, size);
-}
-
-void FontDatabaseDefault::AddFontProvider(FontProvider * provider)
-{
-    instance->font_provider_table.push_back(provider);
-}
-
-void FontDatabaseDefault::RemoveFontProvider(FontProvider * provider)
-{
-    for(FontProviderTable::iterator i = instance->font_provider_table.begin(); i != instance->font_provider_table.end(); ++i)
-    {
-        if(*i == provider)
-        {
-            instance->font_provider_table.erase(i);
-            return;
-        }
-    }
-}
-
-int FontDatabaseDefault::CountFallbackFontFaces()
-{
-	return (int)FontProvider::Get().GetFallbackFontFaces().size();
-}
-
-SharedPtr<FontFaceHandleDefault> FontDatabaseDefault::GetFallbackFontFace(int index, int font_size)
-{
-	auto& faces = FontProvider::Get().GetFallbackFontFaces();
-
-	if (index >= 0 && index < (int)faces.size())
-	{
-		return faces[index]->GetHandle(font_size);
-	}
-
-	return nullptr;
-}
-
-#endif
-
-}
-}

+ 0 - 100
Source/Core/FontEngineDefault/FontDatabaseDefault.h

@@ -1,100 +0,0 @@
-/*
- * 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 RMLUICOREFONTDATABASE_H
-#define RMLUICOREFONTDATABASE_H
-
-#include <RmlUi/Core/StringUtilities.h>
-#include <RmlUi/Core/Header.h>
-#include "FontProvider.h"
-
-namespace Rml {
-namespace Core {
-
-#ifndef RMLUI_NO_FONT_INTERFACE_DEFAULT
-
-class FontEffect;
-class FontFamily;
-class FontFaceHandleDefault;
-class PropertyDictionary;
-
-/**
-	The font database contains all font families currently in use by RmlUi.
-
-	@author Peter Curry
- */
-
-class RMLUICORE_API FontDatabaseDefault
-{
-public:
-	static bool Initialise();
-	static void Shutdown();
-
-	/// Adds a new font face to the database. The face's family, style and weight will be determined from the face itself.
-	/// @param[in] file_name The file to load the face from.
-	/// @param[in] fallback_face True to use this font face for unknown characters in other font faces.
-	/// @return True if the face was loaded successfully, false otherwise.
-	static bool LoadFontFace(const String& file_name, bool fallback_face);
-
-	/// Adds a new font face from memory.
-	static bool LoadFontFace(const byte* data, int data_size, const String& font_family, Style::FontStyle style, Style::FontWeight weight, bool fallback_face);
-
-	/// Returns a handle to a font face that can be used to position and render text. This will return the closest match
-	/// it can find, but in the event a font family is requested that does not exist, nullptr will be returned instead of a
-	/// valid handle.
-	/// @param[in] family The family of the desired font handle.
-	/// @param[in] style The style of the desired font handle.
-	/// @param[in] weight The weight of the desired font handle.
-	/// @param[in] size The size of desired handle, in points.
-	/// @return A valid handle if a matching (or closely matching) font face was found, nullptr otherwise.
-	static SharedPtr<FontFaceHandleDefault> GetFontFaceHandle(const String& family, Style::FontStyle style, Style::FontWeight weight, int size);
-
-    static void AddFontProvider(FontProvider * provider);
-
-    static void RemoveFontProvider(FontProvider * provider);
-
-	static int CountFallbackFontFaces();
-
-	static SharedPtr<FontFaceHandleDefault> GetFallbackFontFace(int index, int font_size);
-
-private:
-	FontDatabaseDefault(void);
-	~FontDatabaseDefault(void);
-
-    typedef std::vector< FontProvider *> FontProviderTable;
-
-    static FontProviderTable font_provider_table;
-	static FontDatabaseDefault* instance;
-};
-
-#endif
-
-}
-}
-
-#endif

+ 7 - 7
Source/Core/FontEngineDefault/FontEngineInterfaceDefault.cpp

@@ -27,7 +27,7 @@
  */
 
 #include "precompiled.h"
-#include "FontDatabaseDefault.h"
+#include "FontProvider.h"
 #include "FontFaceHandleDefault.h"
 #include "FontEngineInterfaceDefault.h"
 
@@ -38,28 +38,28 @@ namespace Core {
 
 FontEngineInterfaceDefault::FontEngineInterfaceDefault()
 {
-	FontDatabaseDefault::Initialise();
+	FontProvider::Initialise();
 }
 
 FontEngineInterfaceDefault::~FontEngineInterfaceDefault()
 {
-	FontDatabaseDefault::Shutdown();
+	FontProvider::Shutdown();
 }
 
 bool FontEngineInterfaceDefault::LoadFontFace(const String& file_name, bool fallback_face)
 {
-	return FontDatabaseDefault::LoadFontFace(file_name, fallback_face);
+	return FontProvider::LoadFontFace(file_name, fallback_face);
 }
 
 bool FontEngineInterfaceDefault::LoadFontFace(const byte* data, int data_size, const String& font_family, Style::FontStyle style, Style::FontWeight weight, bool fallback_face)
 {
-	return FontDatabaseDefault::LoadFontFace(data, data_size, font_family, style, weight, fallback_face);
+	return FontProvider::LoadFontFace(data, data_size, font_family, style, weight, fallback_face);
 }
 
 FontFaceHandle FontEngineInterfaceDefault::GetFontFaceHandle(const String& family, Style::FontStyle style, Style::FontWeight weight, int size)
 {
-	auto handle = FontDatabaseDefault::GetFontFaceHandle(family, style, weight, size);
-	return reinterpret_cast<FontFaceHandle>(handle.get());
+	auto handle = FontProvider::GetFontFaceHandle(family, style, weight, size);
+	return reinterpret_cast<FontFaceHandle>(handle);
 }
 	
 FontEffectsHandle FontEngineInterfaceDefault::PrepareFontEffects(FontFaceHandle handle, const FontEffectList& font_effects)

+ 12 - 20
Source/Core/FontEngineDefault/FontFace.cpp

@@ -46,7 +46,11 @@ FontFace::FontFace(FontFaceHandleFreetype _face, Style::FontStyle _style, Style:
 
 FontFace::~FontFace()
 {
-	ReleaseFace();
+	if (face) 
+	{
+		FreeType::ReleaseFace(face, release_stream);
+		face = 0;
+	}
 	handles.clear();
 }
 
@@ -62,14 +66,10 @@ Style::FontWeight FontFace::GetWeight() const
 	return weight;
 }
 
-/// Returns a handle for positioning and rendering this face at the given size.
-/// @param[in] size The size of the desired handle, in points.
-/// @return The shared font handle.
-
-SharedPtr<FontFaceHandleDefault> FontFace::GetHandle(int size) {
+FontFaceHandleDefault* FontFace::GetHandle(int size) {
 	auto it = handles.find(size);
 	if (it != handles.end())
-		return it->second;
+		return it->second.get();
 
 	// See if this face has been released.
 	if (!face)
@@ -79,29 +79,21 @@ SharedPtr<FontFaceHandleDefault> FontFace::GetHandle(int size) {
 	}
 
 	// Construct and initialise the new handle.
-	auto handle = std::make_shared<FontFaceHandleDefault>();
+	auto handle = std::make_unique<FontFaceHandleDefault>();
 	if (!handle->Initialize(face, size))
 	{
 		handles[size] = nullptr;
 		return nullptr;
 	}
 
+	FontFaceHandleDefault* result = handle.get();
+
 	// Save the new handle to the font face
-	handles[size] = handle;
+	handles[size] = std::move(handle);
 
-	return handle;
+	return result;
 }
 
-/// Releases the face's FreeType face structure. This will mean handles for new sizes cannot be constructed,
-/// but existing ones can still be fetched.
-
-void FontFace::ReleaseFace() {
-	if (face)
-	{
-		FreeType::ReleaseFace(face, release_stream);
-		face = 0;
-	}
-}
 
 }
 }

+ 4 - 12
Source/Core/FontEngineDefault/FontFace.h

@@ -41,27 +41,19 @@ class FontFaceHandleDefault;
 	@author Peter Curry
  */
 
-class FontFace final
+class FontFace
 {
 public:
 	FontFace(FontFaceHandleFreetype face, Style::FontStyle style, Style::FontWeight weight, bool release_stream);
 	~FontFace();
 
-	/// Returns the style of the font face.
-	/// @return The font face's style.
 	Style::FontStyle GetStyle() const;
-	/// Returns the weight of the font face.
-	/// @return The font face's weight.
 	Style::FontWeight GetWeight() const;
 
 	/// Returns a handle for positioning and rendering this face at the given size.
 	/// @param[in] size The size of the desired handle, in points.
-	/// @return The shared font handle.
-	SharedPtr<FontFaceHandleDefault> GetHandle(int size);
-
-	/// Releases the face's FreeType face structure. This will mean handles for new sizes cannot be constructed,
-	/// but existing ones can still be fetched.
-	void ReleaseFace();
+	/// @return The font handle.
+	FontFaceHandleDefault* GetHandle(int size);
 
 private:
 	Style::FontStyle style;
@@ -70,7 +62,7 @@ private:
 	bool release_stream;
 
 	// Key is font size
-	using HandleMap = UnorderedMap< int, SharedPtr<FontFaceHandleDefault> >;
+	using HandleMap = UnorderedMap< int, UniquePtr<FontFaceHandleDefault> >;
 	HandleMap handles;
 
 	FontFaceHandleFreetype face;

+ 14 - 10
Source/Core/FontEngineDefault/FontFaceHandleDefault.cpp

@@ -29,7 +29,7 @@
 #include "precompiled.h"
 #include "../TextureLayout.h"
 #include "FontFaceHandleDefault.h"
-#include "FontDatabaseDefault.h"
+#include "FontProvider.h"
 #include "FontFaceLayer.h"
 #include "FreeTypeInterface.h"
 #include <algorithm>
@@ -58,7 +58,7 @@ bool FontFaceHandleDefault::Initialize(FontFaceHandleFreetype face, int font_siz
 
 	RMLUI_ASSERTMSG(layer_configurations.empty(), "Initialize must only be called once.");
 
-	if (!FreeType::InitialiseFaceHandle(ft_face, glyphs, metrics, font_size))
+	if (!FreeType::InitialiseFaceHandle(ft_face, font_size, glyphs, metrics))
 	{
 		return false;
 	}
@@ -195,16 +195,16 @@ int FontFaceHandleDefault::GenerateLayerConfiguration(const FontEffectList& font
 }
 
 // Generates the texture data for a layer (for the texture database).
-bool FontFaceHandleDefault::GenerateLayerTexture(UniquePtr<const byte[]>& texture_data, Vector2i& texture_dimensions, FontEffect* layer_id, int texture_id, int handle_version)
+bool FontFaceHandleDefault::GenerateLayerTexture(UniquePtr<const byte[]>& texture_data, Vector2i& texture_dimensions, const FontEffect* layer_id, int texture_id, int handle_version) const
 {
 	if (handle_version != version)
 		return false;
 
-	FontLayerMap::iterator layer_iterator = layers.find(layer_id);
+	auto layer_iterator = layers.find(layer_id);
 	if (layer_iterator == layers.end())
 		return false;
 
-	return layer_iterator->second->GenerateTexture(glyphs, texture_data, texture_dimensions, texture_id);
+	return layer_iterator->second->GenerateTexture(texture_data, texture_dimensions, texture_id, glyphs);
 }
 
 // Generates the geometry required to render a single line of text.
@@ -220,6 +220,10 @@ int FontFaceHandleDefault::GenerateString(GeometryList& geometry, const String&
 
 	// Fetch the requested configuration and generate the geometry for each one.
 	const LayerConfiguration& layer_configuration = layer_configurations[layer_configuration_index];
+
+	// Reserve for the common case of one texture per layer.
+	geometry.reserve(layer_configuration.size());
+
 	for (size_t i = 0; i < layer_configuration.size(); ++i)
 	{
 		FontFaceLayer* layer = layer_configuration[i];
@@ -303,20 +307,20 @@ int FontFaceHandleDefault::GetVersion() const
 
 bool FontFaceHandleDefault::AppendGlyph(CodePoint code_point) 
 {
-	bool result = FreeType::AppendGlyph(ft_face, code_point, metrics.size, glyphs);
+	bool result = FreeType::AppendGlyph(ft_face, metrics.size, code_point, glyphs);
 	return result;
 }
 
 int FontFaceHandleDefault::GetKerning(CodePoint lhs, CodePoint rhs) const
 {
-	int result = FreeType::GetKerning(ft_face, lhs, rhs);
+	int result = FreeType::GetKerning(ft_face, metrics.size, lhs, rhs);
 	return result;
 }
 
 const FontGlyph* FontFaceHandleDefault::GetOrAppendGlyph(CodePoint& code_point, bool look_in_fallback_fonts)
 {
 	// Don't try to render control characters
-	if ((unsigned int)code_point < (unsigned int)' ')
+	if ((char32_t)code_point < (char32_t)' ')
 		return nullptr;
 
 	auto it_glyph = glyphs.find(code_point);
@@ -337,10 +341,10 @@ const FontGlyph* FontFaceHandleDefault::GetOrAppendGlyph(CodePoint& code_point,
 		}
 		else if (look_in_fallback_fonts)
 		{
-			const int num_fallback_faces = FontDatabaseDefault::CountFallbackFontFaces();
+			const int num_fallback_faces = FontProvider::CountFallbackFontFaces();
 			for (int i = 0; i < num_fallback_faces; i++)
 			{
-				FontFaceHandleDefault* fallback_face = FontDatabaseDefault::GetFallbackFontFace(i, metrics.size).get();
+				FontFaceHandleDefault* fallback_face = FontProvider::GetFallbackFontFace(i, metrics.size);
 				if (!fallback_face || fallback_face == this)
 					continue;
 

+ 1 - 1
Source/Core/FontEngineDefault/FontFaceHandleDefault.h

@@ -88,7 +88,7 @@ public:
 	/// @param[in] layer_id The id of the layer to request the texture data from.
 	/// @param[in] texture_id The index of the texture within the layer to generate.
 	/// @param[in] handle_version The version of the handle data. Function returns false if out of date.
-	bool GenerateLayerTexture(UniquePtr<const byte[]>& texture_data, Vector2i& texture_dimensions, FontEffect* layer_id, int texture_id, int handle_version);
+	bool GenerateLayerTexture(UniquePtr<const byte[]>& texture_data, Vector2i& texture_dimensions, const FontEffect* layer_id, int texture_id, int handle_version) const;
 
 	/// Generates the geometry required to render a single line of text.
 	/// @param[out] geometry An array of geometries to generate the geometry into.

+ 11 - 6
Source/Core/FontEngineDefault/FontFaceLayer.cpp

@@ -29,7 +29,6 @@
 #include "precompiled.h"
 #include "FontFaceLayer.h"
 #include "FontFaceHandleDefault.h"
-#include "FontDatabaseDefault.h"
 
 namespace Rml {
 namespace Core {
@@ -153,24 +152,30 @@ bool FontFaceLayer::Generate(const FontFaceHandleDefault* handle, const FontFace
 			character.texcoords[1].y = float(rectangle.GetPosition().y + rectangle.GetDimensions().y) / float(texture.GetDimensions().y);
 		}
 
+		const FontEffect* effect_ptr = effect.get();
+		const int handle_version = handle->GetVersion();
 
 		// Generate the textures.
 		for (int i = 0; i < texture_layout.GetNumTextures(); ++i)
 		{
-			Texture texture;
-			if (!texture.Load(CreateString(64, "?font::%p/%p/%d/%d", handle, effect.get(), i, handle->GetVersion())))
-				return false;
+			int texture_id = i;
+
+			TextureCallback texture_callback = [handle, effect_ptr, texture_id, handle_version](const String& name, UniquePtr<const byte[]>& data, Vector2i& dimensions) -> bool {
+				bool result = handle->GenerateLayerTexture(data, dimensions, effect_ptr, texture_id, handle_version);
+				return result;
+			};
 
+			Texture texture;
+			texture.Set("font-face-layer", texture_callback);
 			textures.push_back(texture);
 		}
 	}
 
-
 	return true;
 }
 
 // Generates the texture data for a layer (for the texture database).
-bool FontFaceLayer::GenerateTexture(const FontGlyphMap& glyphs, UniquePtr<const byte[]>& texture_data, Vector2i& texture_dimensions, int texture_id)
+bool FontFaceLayer::GenerateTexture(UniquePtr<const byte[]>& texture_data, Vector2i& texture_dimensions, int texture_id, const FontGlyphMap& glyphs)
 {
 	if (texture_id < 0 ||
 		texture_id > texture_layout.GetNumTextures())

+ 3 - 2
Source/Core/FontEngineDefault/FontFaceLayer.h

@@ -66,9 +66,10 @@ public:
 	/// Generates the texture data for a layer (for the texture database).
 	/// @param[out] texture_data The pointer to be set to the generated texture data.
 	/// @param[out] texture_dimensions The dimensions of the texture.
-	/// @param[in] glyphs The glyphs required by the font face handle.
 	/// @param[in] texture_id The index of the texture within the layer to generate.
-	bool GenerateTexture(const FontGlyphMap& glyphs, UniquePtr<const byte[]>& texture_data, Vector2i& texture_dimensions, int texture_id);
+	/// @param[in] glyphs The glyphs required by the font face handle.
+	bool GenerateTexture(UniquePtr<const byte[]>& texture_data, Vector2i& texture_dimensions, int texture_id, const FontGlyphMap& glyphs);
+
 	/// Generates the geometry required to render a single character.
 	/// @param[out] geometry An array of geometries this layer will write to. It must be at least as big as the number of textures in this layer.
 	/// @param[in] character_code The character to generate geometry for.

+ 1 - 5
Source/Core/FontEngineDefault/FontFamily.cpp

@@ -37,12 +37,8 @@ FontFamily::FontFamily(const String& name) : name(name)
 {
 }
 
-FontFamily::~FontFamily()
-{
-}
-
 // Returns a handle to the most appropriate font in the family, at the correct size.
-SharedPtr<FontFaceHandleDefault> FontFamily::GetFaceHandle(Style::FontStyle style, Style::FontWeight weight, int size)
+FontFaceHandleDefault* FontFamily::GetFaceHandle(Style::FontStyle style, Style::FontWeight weight, int size)
 {
 	// Search for a face of the same style, and match the weight as closely as we can.
 	FontFace* matching_face = nullptr;

+ 2 - 3
Source/Core/FontEngineDefault/FontFamily.h

@@ -45,14 +45,13 @@ class FontFamily
 {
 public:
 	FontFamily(const String& name);
-	virtual ~FontFamily();
 
 	/// Returns a handle to the most appropriate font in the family, at the correct size.
 	/// @param[in] style The style of the desired handle.
 	/// @param[in] weight The weight of the desired handle.
 	/// @param[in] size The size of desired handle, in points.
 	/// @return A valid handle if a matching (or closely matching) font face was found, nullptr otherwise.
-	SharedPtr<FontFaceHandleDefault> GetFaceHandle(Style::FontStyle style, Style::FontWeight weight, int size);
+	FontFaceHandleDefault* GetFaceHandle(Style::FontStyle style, Style::FontWeight weight, int size);
 
 
 	/// Adds a new face to the family.
@@ -66,7 +65,7 @@ public:
 protected:
 	String name;
 
-	typedef std::vector< UniquePtr<FontFace> > FontFaceList;
+	using FontFaceList = std::vector< UniquePtr<FontFace> >;
 	FontFaceList font_faces;
 };
 

+ 20 - 8
Source/Core/FontEngineDefault/FontProvider.cpp

@@ -28,6 +28,7 @@
 
 #include "precompiled.h"
 #include "FontProvider.h"
+#include "FontFace.h"
 #include "FontFamily.h"
 #include "FreeTypeInterface.h"
 
@@ -72,22 +73,33 @@ FontProvider& FontProvider::Get()
 }
 
 // Returns a handle to a font face that can be used to position and render text.
-SharedPtr<FontFaceHandleDefault> FontProvider::GetFontFaceHandle(const String& family, Style::FontStyle style, Style::FontWeight weight, int size)
+FontFaceHandleDefault* FontProvider::GetFontFaceHandle(const String& family, Style::FontStyle style, Style::FontWeight weight, int size)
 {
 	RMLUI_ASSERTMSG(family == StringUtilities::ToLower(family), "Font family name must be converted to lowercase before entering here.");
 
-	auto it = font_families.find(family);
-	if (it == font_families.end())
+	FontFamilyMap& families = Get().font_families;
+
+	auto it = families.find(family);
+	if (it == families.end())
 		return nullptr;
 
 	return it->second->GetFaceHandle(style, weight, size);
 }
 
-const FontFaceList& FontProvider::GetFallbackFontFaces() const
+int FontProvider::CountFallbackFontFaces()
 {
-	return fallback_font_faces;
+	return (int)Get().fallback_font_faces.size();
 }
 
+FontFaceHandleDefault* FontProvider::GetFallbackFontFace(int index, int font_size)
+{
+	auto& faces = FontProvider::Get().fallback_font_faces;
+
+	if (index >= 0 && index < (int)faces.size())
+		return faces[index]->GetHandle(font_size);
+
+	return nullptr;
+}
 
 
 
@@ -109,7 +121,7 @@ bool FontProvider::LoadFontFace(const String& file_name, bool fallback_face)
 	file_interface->Read(buffer, length, handle);
 	file_interface->Close(handle);
 
-	bool result = LoadFontFace(buffer, (int)length, fallback_face, true, file_name);
+	bool result = Get().LoadFontFace(buffer, (int)length, fallback_face, true, file_name);
 
 	return result;
 }
@@ -119,7 +131,7 @@ bool FontProvider::LoadFontFace(const byte* data, int data_size, const String& f
 {
 	const String source = "memory";
 	
-	bool result = LoadFontFace(data, data_size, fallback_face, false, source, font_family, style, weight);
+	bool result = Get().LoadFontFace(data, data_size, fallback_face, false, source, font_family, style, weight);
 	
 	return result;
 }
@@ -140,7 +152,7 @@ bool FontProvider::LoadFontFace(const byte* data, int data_size, bool fallback_f
 
 	if (font_family.empty())
 	{
-		FreeType::GetFontFaceStyle(ft_face, font_family, style, weight);
+		FreeType::GetFaceStyle(ft_face, font_family, style, weight);
 	}
 
 	if (!AddFace(ft_face, font_family, style, weight, fallback_face, local_data))

+ 13 - 10
Source/Core/FontEngineDefault/FontProvider.h

@@ -40,10 +40,8 @@ class FontFace;
 class FontFamily;
 class FontFaceHandleDefault;
 
-using FontFaceList = std::vector<FontFace*>;
-
 /**
-	The font database contains all font families currently in use by RmlUi.
+	The font provider contains all font families currently in use by RmlUi.
 	@author Peter Curry
  */
 
@@ -53,8 +51,6 @@ public:
 	static bool Initialise();
 	static void Shutdown();
 
-	static FontProvider& Get();
-
 	/// Returns a handle to a font face that can be used to position and render text. This will return the closest match
 	/// it can find, but in the event a font family is requested that does not exist, nullptr will be returned instead of a
 	/// valid handle.
@@ -63,27 +59,34 @@ public:
 	/// @param[in] weight The weight of the desired font handle.
 	/// @param[in] size The size of desired handle, in points.
 	/// @return A valid handle if a matching (or closely matching) font face was found, nullptr otherwise.
-	SharedPtr<FontFaceHandleDefault> GetFontFaceHandle(const String& family, Style::FontStyle style, Style::FontWeight weight, int size);
-
-	const FontFaceList& GetFallbackFontFaces() const;
+	static FontFaceHandleDefault* GetFontFaceHandle(const String& family, Style::FontStyle style, Style::FontWeight weight, int size);
 
 	/// Adds a new font face to the database. The face's family, style and weight will be determined from the face itself.
-	bool LoadFontFace(const String& file_name, bool fallback_face);
+	static bool LoadFontFace(const String& file_name, bool fallback_face);
 
 	/// Adds a new font face from memory.
-	bool LoadFontFace(const byte* data, int data_size, const String& font_family, Style::FontStyle style, Style::FontWeight weight, bool fallback_face);
+	static bool LoadFontFace(const byte* data, int data_size, const String& font_family, Style::FontStyle style, Style::FontWeight weight, bool fallback_face);
 
+	/// Return the number of fallback font faces.
+	static int CountFallbackFontFaces();
+
+	/// Return a font face handle with the given index, at the given font size.
+	static FontFaceHandleDefault* GetFallbackFontFace(int index, int font_size);
 
 private:
 	FontProvider();
 	~FontProvider();
 
+	static FontProvider& Get();
+
 	bool LoadFontFace(const byte* data, int data_size, bool fallback_face, bool local_data, const String& source,
 		String font_family = {}, Style::FontStyle style = Style::FontStyle::Normal, Style::FontWeight weight = Style::FontWeight::Normal);
 
 	bool AddFace(FontFaceHandleFreetype face, const String& family, Style::FontStyle style, Style::FontWeight weight, bool fallback_face, bool release_stream);
 
+	using FontFaceList = std::vector<FontFace*>;
 	using FontFamilyMap = UnorderedMap< String, UniquePtr<FontFamily>>;
+
 	FontFamilyMap font_families;
 	FontFaceList fallback_font_faces;
 

+ 19 - 10
Source/Core/FontEngineDefault/FreeTypeInterface.cpp

@@ -42,12 +42,14 @@ static FT_Library ft_library = nullptr;
 
 
 static bool BuildGlyph(FT_Face ft_face, CodePoint code_point, FontGlyphMap& glyphs);
-static void BuildGlyphMap(FT_Face ft_face, FontGlyphMap& glyphs, int size);
+static void BuildGlyphMap(FT_Face ft_face, int size, FontGlyphMap& glyphs);
 static void GenerateMetrics(FT_Face ft_face, const FontGlyphMap& glyphs, FontMetrics& metrics);
 
 
 bool FreeType::Initialise()
 {
+	RMLUI_ASSERT(!ft_library);
+
 	FT_Error result = FT_Init_FreeType(&ft_library);
 	if (result != 0)
 	{
@@ -71,6 +73,8 @@ void FreeType::Shutdown()
 // Loads a FreeType face from memory.
 FontFaceHandleFreetype FreeType::LoadFace(const byte* data, int data_length, const String& source)
 {
+	RMLUI_ASSERT(ft_library);
+
 	FT_Face face = nullptr;
 	int error = FT_New_Memory_Face(ft_library, (const FT_Byte*)data, data_length, 0, &face);
 	if (error != 0)
@@ -107,7 +111,7 @@ bool FreeType::ReleaseFace(FontFaceHandleFreetype in_face, bool release_stream)
 	return (error == 0);
 }
 
-void FreeType::GetFontFaceStyle(FontFaceHandleFreetype in_face, String& font_family, Style::FontStyle& style, Style::FontWeight& weight)
+void FreeType::GetFaceStyle(FontFaceHandleFreetype in_face, String& font_family, Style::FontStyle& style, Style::FontWeight& weight)
 {
 	FT_Face face = (FT_Face)in_face;
 
@@ -119,7 +123,7 @@ void FreeType::GetFontFaceStyle(FontFaceHandleFreetype in_face, String& font_fam
 
 
 // Initialises the handle so it is able to render text.
-bool FreeType::InitialiseFaceHandle(FontFaceHandleFreetype face, FontGlyphMap& glyphs, FontMetrics& metrics, int font_size)
+bool FreeType::InitialiseFaceHandle(FontFaceHandleFreetype face, int font_size, FontGlyphMap& glyphs, FontMetrics& metrics)
 {
 	FT_Face ft_face = (FT_Face)face;
 
@@ -134,7 +138,7 @@ bool FreeType::InitialiseFaceHandle(FontFaceHandleFreetype face, FontGlyphMap& g
 	}
 
 	// Construct the initial list of glyphs.
-	BuildGlyphMap(ft_face, glyphs, font_size);
+	BuildGlyphMap(ft_face, font_size, glyphs);
 
 	// Generate the metrics for the handle.
 	GenerateMetrics(ft_face, glyphs, metrics);
@@ -142,7 +146,7 @@ bool FreeType::InitialiseFaceHandle(FontFaceHandleFreetype face, FontGlyphMap& g
 	return true;
 }
 
-bool FreeType::AppendGlyph(FontFaceHandleFreetype face, CodePoint code_point, int font_size, FontGlyphMap& glyphs)
+bool FreeType::AppendGlyph(FontFaceHandleFreetype face, int font_size, CodePoint code_point, FontGlyphMap& glyphs)
 {
 	FT_Face ft_face = (FT_Face)face;
 
@@ -164,16 +168,21 @@ bool FreeType::AppendGlyph(FontFaceHandleFreetype face, CodePoint code_point, in
 }
 
 
-int FreeType::GetKerning(FontFaceHandleFreetype face, CodePoint lhs, CodePoint rhs)
+int FreeType::GetKerning(FontFaceHandleFreetype face, int font_size, CodePoint lhs, CodePoint rhs)
 {
 	FT_Face ft_face = (FT_Face)face;
 
 	if (!FT_HAS_KERNING(ft_face))
 		return 0;
 
+	// Set face size again in case it was used at another size in another font face handle.
+	FT_Error ft_error = FT_Set_Char_Size(ft_face, 0, font_size << 6, 0, 0);
+	if (ft_error)
+		return 0;
+
 	FT_Vector ft_kerning;
 
-	FT_Error ft_error = FT_Get_Kerning(
+	ft_error = FT_Get_Kerning(
 		ft_face,
 		FT_Get_Char_Index(ft_face, (FT_ULong)lhs),
 		FT_Get_Char_Index(ft_face, (FT_ULong)rhs),
@@ -181,7 +190,7 @@ int FreeType::GetKerning(FontFaceHandleFreetype face, CodePoint lhs, CodePoint r
 		&ft_kerning
 	);
 
-	if (ft_error != 0)
+	if (ft_error)
 		return 0;
 
 	int kerning = ft_kerning.x >> 6;
@@ -190,11 +199,11 @@ int FreeType::GetKerning(FontFaceHandleFreetype face, CodePoint lhs, CodePoint r
 
 
 
-static void BuildGlyphMap(FT_Face ft_face, FontGlyphMap& glyphs, int size)
+static void BuildGlyphMap(FT_Face ft_face, int size, FontGlyphMap& glyphs)
 {
 	glyphs.reserve(128);
 
-	// Add the ASCII character set now. Other characters are added later as needed.
+	// Add the ASCII characters now. Other characters are added later as needed.
 	FT_ULong code_min = 32;
 	FT_ULong code_max = 126;
 

+ 12 - 5
Source/Core/FontEngineDefault/FreeTypeInterface.h

@@ -37,21 +37,28 @@ namespace Rml {
 namespace Core {
 namespace FreeType {
 
+// Initialize FreeType library.
 bool Initialise();
+// Shutdown FreeType library.
 void Shutdown();
 
-// Loads a FreeType face from memory.
+// Loads a FreeType face from memory, 'source' is only used for logging.
 FontFaceHandleFreetype LoadFace(const byte* data, int data_length, const String& source);
+
+// Releases the FreeType face.
 bool ReleaseFace(FontFaceHandleFreetype face, bool release_stream);
 
 // Retrieves the font family, style and weight of the given font face.
-void GetFontFaceStyle(FontFaceHandleFreetype face, String& font_family, Style::FontStyle& style, Style::FontWeight& weight);
+void GetFaceStyle(FontFaceHandleFreetype face, String& font_family, Style::FontStyle& style, Style::FontWeight& weight);
 
-bool InitialiseFaceHandle(FontFaceHandleFreetype face, FontGlyphMap& glyphs, FontMetrics& metrics, int font_size);
+// Initializes a face for a given font size. Glyphs are filled with the ASCII subset, and the font face metrics are set.
+bool InitialiseFaceHandle(FontFaceHandleFreetype face, int font_size, FontGlyphMap& glyphs, FontMetrics& metrics);
 
-bool AppendGlyph(FontFaceHandleFreetype face, CodePoint code_point, int font_size, FontGlyphMap& glyphs);
+// Build a new glyph representing the given code point and append to 'glyphs'.
+bool AppendGlyph(FontFaceHandleFreetype face, int font_size, CodePoint code_point, FontGlyphMap& glyphs);
 
-int GetKerning(FontFaceHandleFreetype face, CodePoint lhs, CodePoint rhs);
+// Returns the kerning between two characters.
+int GetKerning(FontFaceHandleFreetype face, int font_size, CodePoint lhs, CodePoint rhs);
 
 }
 }

+ 1 - 5
Source/Core/Spritesheet.cpp

@@ -38,11 +38,7 @@ bool SpritesheetList::AddSpriteSheet(const String& name, const String& image_sou
 {
 	// Load the texture
 	Texture texture;
-	if (!texture.Load(image_source, definition_source))
-	{
-		Log::Message(Log::LT_WARNING, "Could not load image '%s' specified in spritesheet '%s' at %s:%d", image_source.c_str(), name.c_str(), definition_source.c_str(), definition_line_number);
-		return false;
-	}
+	texture.Set(image_source, definition_source);
 
 	auto sprite_sheet = std::make_shared<Spritesheet>(name, image_source, definition_source, definition_line_number, texture);
 	auto result = spritesheet_map.emplace(name, sprite_sheet);

+ 7 - 2
Source/Core/Texture.cpp

@@ -35,10 +35,15 @@ namespace Rml {
 namespace Core {
 
 // Attempts to load a texture.
-bool Texture::Load(const String& source, const String& source_path)
+void Texture::Set(const String& source, const String& source_path)
 {
 	resource = TextureDatabase::Fetch(source, source_path);
-	return resource != nullptr;
+}
+
+void Texture::Set(const String& name, const TextureCallback& callback)
+{
+	resource = std::make_shared<TextureResource>();
+	resource->Set(name, callback);
 }
 
 // Returns the texture's source name. This is usually the name of the file the texture was loaded from.

+ 1 - 4
Source/Core/TextureDatabase.cpp

@@ -75,10 +75,7 @@ SharedPtr<TextureResource> TextureDatabase::Fetch(const String& source, const St
 	}
 
 	auto resource = std::make_shared<TextureResource>();
-	if (!resource->Load(path))
-	{
-		return nullptr;
-	}
+	resource->Set(path);
 
 	instance->textures[resource->GetSource()] = resource;
 	return resource;

+ 29 - 47
Source/Core/TextureResource.cpp

@@ -29,8 +29,6 @@
 #include "precompiled.h"
 #include "TextureResource.h"
 #include "TextureDatabase.h"
-#include "FontEngineDefault/FontFaceHandleDefault.h"
-#include "../../Include/RmlUi/Core.h"
 
 namespace Rml {
 namespace Core {
@@ -45,12 +43,18 @@ TextureResource::~TextureResource()
 }
 
 // Attempts to load a texture from the application into the resource.
-bool TextureResource::Load(const String& _source)
+void TextureResource::Set(const String& _source)
 {
 	Release();
+	texture_callback.reset();
 	source = _source;
+}
 
-	return true;
+void TextureResource::Set(const String& name, const TextureCallback& callback)
+{
+	Release();
+	source = name;
+	texture_callback = std::make_unique<TextureCallback>(callback);
 }
 
 // Returns the resource's underlying texture.
@@ -113,65 +117,43 @@ void TextureResource::Release(RenderInterface* render_interface)
 	}
 }
 
-// Attempts to load the texture from the source.
 bool TextureResource::Load(RenderInterface* render_interface)
 {
 	RMLUI_ZoneScoped;
 
-	// Check for special loader tokens.
-	if (!source.empty() && source[0] == '?')
+	// Generate the texture from the callback function if we have one.
+	if (texture_callback)
 	{
 		Vector2i dimensions;
-
 		UniquePtr<const byte[]> data = nullptr;
 
-		// Find the generation protocol and generate the data accordingly.
-		String protocol = source.substr(1, source.find("::") - 1);
+		TextureCallback& callback_fnc = *texture_callback;
 
-#ifndef RMLUI_NO_FONT_INTERFACE_DEFAULT
-		if (protocol == "font")
+		if (!callback_fnc(source, data, dimensions) || !data)
 		{
-			// The requested texture is a font layer.
-			FontFaceHandleDefault* handle;
-			FontEffect* layer_id;
-			int texture_id;
-			int handle_version;
-
-			if (sscanf(source.c_str(), "?font::%p/%p/%d/%d", &handle, &layer_id, &texture_id, &handle_version) == 4)
-			{
-				if (!handle->GenerateLayerTexture(data, dimensions, layer_id, texture_id, handle_version))
-				{
-					Log::Message(Log::LT_WARNING, "Failed to generate font layer texture %s.", source.c_str());
-					texture_data[render_interface] = TextureData(0, Vector2i(0, 0));
-
-					return false;
-				}
-			}
+			Log::Message(Log::LT_WARNING, "Failed to generate texture from callback function %s.", source.c_str());
+			texture_data[render_interface] = TextureData(0, Vector2i(0, 0));
+
+			return false;
 		}
-#endif
 
-		// If texture data was generated, great! Otherwise, fallback to the LoadTexture() code and
-		// hope the client knows what the hell to do with the question mark in their file name.
-		if (data)
+		TextureHandle handle;
+		bool success = render_interface->GenerateTexture(handle, data.get(), dimensions);
+
+		if (success)
 		{
-			TextureHandle handle;
-			bool success = render_interface->GenerateTexture(handle, data.get(), dimensions);
-
-			if (success)
-			{
-				texture_data[render_interface] = TextureData(handle, dimensions);
-				return true;
-			}
-			else
-			{
-				Log::Message(Log::LT_WARNING, "Failed to generate internal texture %s.", source.c_str());
-				texture_data[render_interface] = TextureData(0, Vector2i(0, 0));
-
-				return false;
-			}
+			texture_data[render_interface] = TextureData(handle, dimensions);
 		}
+		else
+		{
+			Log::Message(Log::LT_WARNING, "Failed to generate internal texture %s.", source.c_str());
+			texture_data[render_interface] = TextureData(0, Vector2i(0, 0));
+		}
+
+		return success;
 	}
 
+	// No callback function, load the texture through the render interface.
 	TextureHandle handle;
 	Vector2i dimensions;
 	if (!render_interface->LoadTexture(handle, dimensions, source))

+ 10 - 5
Source/Core/TextureResource.h

@@ -48,10 +48,13 @@ public:
 	TextureResource();
 	~TextureResource();
 
-	/// Attempts to load a texture from the application into the resource. Note that this always
-	/// succeeds now; as texture loading is now delayed until the texture is accessed by a specific
-	/// render interface, all this does is store the source.
-	bool Load(const String& source);
+	/// Clear any existing data and set the source path. Texture loading is delayed until the texture is
+	/// accessed by a specific render interface.
+	void Set(const String& source);
+
+	/// Clear any existing data and set a callback function for loading the data. Texture loading is
+	/// delayed until the texture is accessed by a specific render interface.
+	void Set(const String& name, const TextureCallback& callback);
 
 	/// Returns the resource's underlying texture handle.
 	TextureHandle GetHandle(RenderInterface* render_interface);
@@ -65,7 +68,7 @@ public:
 	void Release(RenderInterface* render_interface = nullptr);
 
 protected:
-	/// Attempts to load the texture from the source.
+	/// Attempts to load the texture from the source, or the callback function if set.
 	bool Load(RenderInterface* render_interface);
 
 private:
@@ -74,6 +77,8 @@ private:
 	typedef std::pair< TextureHandle, Vector2i > TextureData;
 	typedef SmallUnorderedMap< RenderInterface*, TextureData > TextureDataMap;
 	TextureDataMap texture_data;
+
+	UniquePtr<TextureCallback> texture_callback;
 };
 
 }