noesisGUI Documentation: Customizing Resource Loading
In Noesis, the process of loading resources can be customized by using resource providers. There is a resource provider for each kind of resource: xaml, texture and font. Although they can be configured independently there is a single function for setting all of them up at the same time. That function is GUI::SetResourceProvider(). For simplification purposes there is an overloaded version that takes a directory path and configures providers to load resources from disk.
Stream is the base class used by each provider to request raw bytes.
class Stream: public BaseComponent
{
public:
/// Set the current position within the stream
virtual void SetPosition(NsSize pos) = 0;
/// Returns the current position within the stream
virtual NsSize GetPosition() const = 0;
/// Returns the length of the stream in bytes
virtual NsSize GetLength() const = 0;
/// Reads data at the current position and advances it by the number of bytes read
/// Returns the total number of bytes read.
virtual NsSize Read(void* buffer, NsSize size) = 0;
};
The base class from implementing a provider of xamls is XamlProvider.
class XamlProvider: public Core::BaseComponent
{
public:
// Loads the specified XAML file. Returns null if no xaml found
virtual Ptr<Core::Stream> LoadXaml(const NsChar* filename) = 0;
};
The implementation is straightforward. You basically must provide a stream for each uri that is requested. LocalXamlProvider is an example of xaml provider that loads content from disk.
Texture providers are a bit more complex because you need to implement two different functions. At the main thread the layout process needs information about the dimensions of the texture. You provide that information through the GetTextureInfo function. The function that really loads the texture is LoadTexture and it is always called from the render thread.
class TextureProvider: public Core::BaseComponent
{
public:
/// Returns metadata for the given texture filename. Returns 0 x 0 if no texture found
virtual TextureInfo GetTextureInfo(const NsChar* filename) = 0;
/// Creates texture in the given device. Returns null if no texture found
virtual Ptr<Texture> LoadTexture(const NsChar* path, RenderDevice* device) = 0;
};
To simplify the process of creating a texture provider we provide a helper class, FileTextureProvider, that exposes a virtual function you must implement to load the requested filename.
class FileTextureProvider: public TextureProvider
{
protected:
virtual Ptr<Core::Stream> OpenStream(const NsChar* filename) const = 0;
};
LocalTextureProvider is an example of texture provider that loads textures from disk.
The font provider is the more complex to implement. The base class in charge of loading fonts is FontProvider.
class FontProvider: public BaseComponent
{
public:
/// Finds the font in the given folder that best matches the specified properties.
/// Returns a null stream if no matching found
virtual FontSource MatchFont(const NsChar* baseUri, const NsChar* familyName,
FontWeight weight, FontStretch stretch, FontStyle style) = 0;
};
To help with the implementation of this provider there is an intermediate class, CachedFontProvider, that scans folders and extracts family information from .ttf and .otf files. It also implements a font matching algorithm based on the W3C specifications.
CachedFontProvider exposes two virtual functions, ScanFolder and OpenFont:
- In ScanFolder you must register all the fonts available in the given folder. Each font is registered by calling RegisterFont.
- OpenFont is the function in charge of providing the stream corresponding to each of the registered filenames.
class CachedFontProvider: public FontProvider
{
protected:
virtual void ScanFolder(const NsChar* folder) = 0;
virtual Ptr<Core::Stream> OpenFont(const NsChar* folder, const NsChar* id) const = 0;
void RegisterFont(const NsChar* folder, const NsChar* filename);
};
Similar to the rest of providers, LocalFontProvider loads fonts stored in the disk.