mirror of
https://github.com/ncblakely/GiantsTools
synced 2024-12-23 15:57:22 +01:00
655 lines
20 KiB
C++
655 lines
20 KiB
C++
|
//--------------------------------------------------------------------------------------
|
||
|
// File: SpriteFont.cpp
|
||
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// Licensed under the MIT License.
|
||
|
//
|
||
|
// http://go.microsoft.com/fwlink/?LinkId=248929
|
||
|
//--------------------------------------------------------------------------------------
|
||
|
|
||
|
#include "pch.h"
|
||
|
|
||
|
#include <algorithm>
|
||
|
#include <vector>
|
||
|
|
||
|
#include "SpriteFont.h"
|
||
|
#include "DirectXHelpers.h"
|
||
|
#include "BinaryReader.h"
|
||
|
#include "LoaderHelpers.h"
|
||
|
|
||
|
using namespace DirectX;
|
||
|
using Microsoft::WRL::ComPtr;
|
||
|
|
||
|
|
||
|
// Internal SpriteFont implementation class.
|
||
|
class SpriteFont::Impl
|
||
|
{
|
||
|
public:
|
||
|
Impl(_In_ ID3D11Device* device,
|
||
|
_In_ BinaryReader* reader,
|
||
|
bool forceSRGB) noexcept(false);
|
||
|
Impl(_In_ ID3D11ShaderResourceView* texture,
|
||
|
_In_reads_(glyphCount) Glyph const* glyphs,
|
||
|
size_t glyphCount,
|
||
|
float lineSpacing) noexcept(false);
|
||
|
|
||
|
Glyph const* FindGlyph(wchar_t character) const;
|
||
|
|
||
|
void SetDefaultCharacter(wchar_t character);
|
||
|
|
||
|
template<typename TAction>
|
||
|
void ForEachGlyph(_In_z_ wchar_t const* text, TAction action, bool ignoreWhitespace) const;
|
||
|
|
||
|
void CreateTextureResource(_In_ ID3D11Device* device,
|
||
|
uint32_t width, uint32_t height,
|
||
|
DXGI_FORMAT format,
|
||
|
uint32_t stride, uint32_t rows,
|
||
|
_In_reads_(stride * rows) const uint8_t* data) noexcept(false);
|
||
|
|
||
|
const wchar_t* ConvertUTF8(_In_z_ const char *text) noexcept(false);
|
||
|
|
||
|
// Fields.
|
||
|
ComPtr<ID3D11ShaderResourceView> texture;
|
||
|
std::vector<Glyph> glyphs;
|
||
|
std::vector<uint32_t> glyphsIndex;
|
||
|
Glyph const* defaultGlyph;
|
||
|
float lineSpacing;
|
||
|
|
||
|
private:
|
||
|
size_t utfBufferSize;
|
||
|
std::unique_ptr<wchar_t[]> utfBuffer;
|
||
|
};
|
||
|
|
||
|
|
||
|
// Constants.
|
||
|
const XMFLOAT2 SpriteFont::Float2Zero(0, 0);
|
||
|
|
||
|
static const char spriteFontMagic[] = "DXTKfont";
|
||
|
|
||
|
|
||
|
// Comparison operators make our sorted glyph vector work with std::binary_search and lower_bound.
|
||
|
namespace DirectX
|
||
|
{
|
||
|
static inline bool operator< (SpriteFont::Glyph const& left, SpriteFont::Glyph const& right) noexcept
|
||
|
{
|
||
|
return left.Character < right.Character;
|
||
|
}
|
||
|
|
||
|
static inline bool operator< (wchar_t left, SpriteFont::Glyph const& right) noexcept
|
||
|
{
|
||
|
return left < right.Character;
|
||
|
}
|
||
|
|
||
|
static inline bool operator< (SpriteFont::Glyph const& left, wchar_t right) noexcept
|
||
|
{
|
||
|
return left.Character < right;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Reads a SpriteFont from the binary format created by the MakeSpriteFont utility.
|
||
|
_Use_decl_annotations_
|
||
|
SpriteFont::Impl::Impl(
|
||
|
ID3D11Device* device,
|
||
|
BinaryReader* reader,
|
||
|
bool forceSRGB) noexcept(false) :
|
||
|
defaultGlyph(nullptr),
|
||
|
lineSpacing(0),
|
||
|
utfBufferSize(0)
|
||
|
{
|
||
|
// Validate the header.
|
||
|
for (char const* magic = spriteFontMagic; *magic; magic++)
|
||
|
{
|
||
|
if (reader->Read<uint8_t>() != *magic)
|
||
|
{
|
||
|
DebugTrace("ERROR: SpriteFont provided with an invalid .spritefont file\n");
|
||
|
throw std::exception("Not a MakeSpriteFont output binary");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Read the glyph data.
|
||
|
auto glyphCount = reader->Read<uint32_t>();
|
||
|
auto glyphData = reader->ReadArray<Glyph>(glyphCount);
|
||
|
|
||
|
glyphs.assign(glyphData, glyphData + glyphCount);
|
||
|
glyphsIndex.reserve(glyphs.size());
|
||
|
|
||
|
for (auto& glyph : glyphs)
|
||
|
{
|
||
|
glyphsIndex.emplace_back(glyph.Character);
|
||
|
}
|
||
|
|
||
|
// Read font properties.
|
||
|
lineSpacing = reader->Read<float>();
|
||
|
|
||
|
SetDefaultCharacter(static_cast<wchar_t>(reader->Read<uint32_t>()));
|
||
|
|
||
|
// Read the texture data.
|
||
|
auto textureWidth = reader->Read<uint32_t>();
|
||
|
auto textureHeight = reader->Read<uint32_t>();
|
||
|
auto textureFormat = reader->Read<DXGI_FORMAT>();
|
||
|
auto textureStride = reader->Read<uint32_t>();
|
||
|
auto textureRows = reader->Read<uint32_t>();
|
||
|
|
||
|
uint64_t dataSize = uint64_t(textureStride) * uint64_t(textureRows);
|
||
|
if (dataSize > UINT32_MAX)
|
||
|
{
|
||
|
DebugTrace("ERROR: SpriteFont provided with an invalid .spritefont file\n");
|
||
|
throw std::overflow_error("Invalid .spritefont file");
|
||
|
}
|
||
|
|
||
|
auto textureData = reader->ReadArray<uint8_t>(static_cast<size_t>(dataSize));
|
||
|
|
||
|
if (forceSRGB)
|
||
|
{
|
||
|
textureFormat = LoaderHelpers::MakeSRGB(textureFormat);
|
||
|
}
|
||
|
|
||
|
// Create the D3D texture.
|
||
|
CreateTextureResource(
|
||
|
device,
|
||
|
textureWidth, textureHeight,
|
||
|
textureFormat,
|
||
|
textureStride, textureRows,
|
||
|
textureData);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Constructs a SpriteFont from arbitrary user specified glyph data.
|
||
|
_Use_decl_annotations_
|
||
|
SpriteFont::Impl::Impl(
|
||
|
ID3D11ShaderResourceView* itexture,
|
||
|
Glyph const* iglyphs,
|
||
|
size_t glyphCount,
|
||
|
float ilineSpacing) noexcept(false) :
|
||
|
texture(itexture),
|
||
|
glyphs(iglyphs, iglyphs + glyphCount),
|
||
|
defaultGlyph(nullptr),
|
||
|
lineSpacing(ilineSpacing),
|
||
|
utfBufferSize(0)
|
||
|
{
|
||
|
if (!std::is_sorted(iglyphs, iglyphs + glyphCount))
|
||
|
{
|
||
|
throw std::exception("Glyphs must be in ascending codepoint order");
|
||
|
}
|
||
|
|
||
|
glyphsIndex.reserve(glyphs.size());
|
||
|
|
||
|
for (auto& glyph : glyphs)
|
||
|
{
|
||
|
glyphsIndex.emplace_back(glyph.Character);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Looks up the requested glyph, falling back to the default character if it is not in the font.
|
||
|
SpriteFont::Glyph const* SpriteFont::Impl::FindGlyph(wchar_t character) const
|
||
|
{
|
||
|
// Rather than use std::lower_bound (which includes a slow debug path when built for _DEBUG),
|
||
|
// we implement a binary search inline to ensure sufficient Debug build performance to be useful
|
||
|
// for text-heavy applications.
|
||
|
|
||
|
size_t lower = 0;
|
||
|
size_t higher = glyphs.size() - 1;
|
||
|
size_t index = higher / 2;
|
||
|
const size_t size = glyphs.size();
|
||
|
|
||
|
while (index < size)
|
||
|
{
|
||
|
const auto curChar = glyphsIndex[index];
|
||
|
if (curChar == character) { return &glyphs[index]; }
|
||
|
if (curChar < character)
|
||
|
{
|
||
|
lower = index + 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
higher = index - 1;
|
||
|
}
|
||
|
if (higher < lower) { break; }
|
||
|
else if (higher - lower <= 4)
|
||
|
{
|
||
|
for (index = lower; index <= higher; index++)
|
||
|
{
|
||
|
if (glyphsIndex[index] == character)
|
||
|
{
|
||
|
return &glyphs[index];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
index = lower + ((higher - lower) / 2);
|
||
|
}
|
||
|
|
||
|
if (defaultGlyph)
|
||
|
{
|
||
|
return defaultGlyph;
|
||
|
}
|
||
|
|
||
|
DebugTrace("ERROR: SpriteFont encountered a character not in the font (%u, %C), and no default glyph was provided\n", character, character);
|
||
|
throw std::exception("Character not in font");
|
||
|
}
|
||
|
|
||
|
|
||
|
// Sets the missing-character fallback glyph.
|
||
|
void SpriteFont::Impl::SetDefaultCharacter(wchar_t character)
|
||
|
{
|
||
|
defaultGlyph = nullptr;
|
||
|
|
||
|
if (character)
|
||
|
{
|
||
|
defaultGlyph = FindGlyph(character);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// The core glyph layout algorithm, shared between DrawString and MeasureString.
|
||
|
template<typename TAction>
|
||
|
void SpriteFont::Impl::ForEachGlyph(_In_z_ wchar_t const* text, TAction action, bool ignoreWhitespace) const
|
||
|
{
|
||
|
float x = 0;
|
||
|
float y = 0;
|
||
|
|
||
|
for (; *text; text++)
|
||
|
{
|
||
|
wchar_t character = *text;
|
||
|
|
||
|
switch (character)
|
||
|
{
|
||
|
case '\r':
|
||
|
// Skip carriage returns.
|
||
|
continue;
|
||
|
|
||
|
case '\n':
|
||
|
// New line.
|
||
|
x = 0;
|
||
|
y += lineSpacing;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
// Output this character.
|
||
|
auto glyph = FindGlyph(character);
|
||
|
|
||
|
x += glyph->XOffset;
|
||
|
|
||
|
if (x < 0)
|
||
|
x = 0;
|
||
|
|
||
|
float advance = float(glyph->Subrect.right) - float(glyph->Subrect.left) + glyph->XAdvance;
|
||
|
|
||
|
if (!ignoreWhitespace
|
||
|
|| !iswspace(character)
|
||
|
|| ((glyph->Subrect.right - glyph->Subrect.left) > 1)
|
||
|
|| ((glyph->Subrect.bottom - glyph->Subrect.top) > 1))
|
||
|
{
|
||
|
action(glyph, x, y, advance);
|
||
|
}
|
||
|
|
||
|
x += advance;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
_Use_decl_annotations_
|
||
|
void SpriteFont::Impl::CreateTextureResource(
|
||
|
ID3D11Device* device,
|
||
|
uint32_t width, uint32_t height,
|
||
|
DXGI_FORMAT format,
|
||
|
uint32_t stride, uint32_t rows,
|
||
|
const uint8_t* data) noexcept(false)
|
||
|
{
|
||
|
uint64_t sliceBytes = uint64_t(stride) * uint64_t(rows);
|
||
|
if (sliceBytes > UINT32_MAX)
|
||
|
{
|
||
|
DebugTrace("ERROR: SpriteFont provided with an invalid .spritefont file\n");
|
||
|
throw std::overflow_error("Invalid .spritefont file");
|
||
|
}
|
||
|
|
||
|
D3D11_TEXTURE2D_DESC desc = {};
|
||
|
desc.Width = width;
|
||
|
desc.Height = height;
|
||
|
desc.MipLevels = 1;
|
||
|
desc.ArraySize = 1;
|
||
|
desc.Format = format;
|
||
|
desc.SampleDesc.Count = 1;
|
||
|
desc.Usage = D3D11_USAGE_IMMUTABLE;
|
||
|
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||
|
|
||
|
D3D11_SUBRESOURCE_DATA initData = { data, stride, static_cast<UINT>(sliceBytes) };
|
||
|
|
||
|
ComPtr<ID3D11Texture2D> texture2D;
|
||
|
ThrowIfFailed(
|
||
|
device->CreateTexture2D(&desc, &initData, &texture2D)
|
||
|
);
|
||
|
|
||
|
CD3D11_SHADER_RESOURCE_VIEW_DESC viewDesc(D3D11_SRV_DIMENSION_TEXTURE2D, format);
|
||
|
ThrowIfFailed(
|
||
|
device->CreateShaderResourceView(texture2D.Get(), &viewDesc, texture.ReleaseAndGetAddressOf())
|
||
|
);
|
||
|
|
||
|
SetDebugObjectName(texture.Get(), "DirectXTK:SpriteFont");
|
||
|
SetDebugObjectName(texture2D.Get(), "DirectXTK:SpriteFont");
|
||
|
}
|
||
|
|
||
|
|
||
|
const wchar_t* SpriteFont::Impl::ConvertUTF8(_In_z_ const char *text) noexcept(false)
|
||
|
{
|
||
|
if (!utfBuffer)
|
||
|
{
|
||
|
utfBufferSize = 1024;
|
||
|
utfBuffer.reset(new wchar_t[1024]);
|
||
|
}
|
||
|
|
||
|
int result = MultiByteToWideChar(CP_UTF8, 0, text, -1, utfBuffer.get(), static_cast<int>(utfBufferSize));
|
||
|
if (!result && (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
|
||
|
{
|
||
|
// Compute required buffer size
|
||
|
result = MultiByteToWideChar(CP_UTF8, 0, text, -1, nullptr, 0);
|
||
|
utfBufferSize = AlignUp(static_cast<size_t>(result), 1024u);
|
||
|
utfBuffer.reset(new wchar_t[utfBufferSize]);
|
||
|
|
||
|
// Retry conversion
|
||
|
result = MultiByteToWideChar(CP_UTF8, 0, text, -1, utfBuffer.get(), static_cast<int>(utfBufferSize));
|
||
|
}
|
||
|
|
||
|
if (!result)
|
||
|
{
|
||
|
DebugTrace("ERROR: MultiByteToWideChar failed with error %u.\n", GetLastError());
|
||
|
throw std::exception("MultiByteToWideChar");
|
||
|
}
|
||
|
|
||
|
return utfBuffer.get();
|
||
|
}
|
||
|
|
||
|
|
||
|
// Construct from a binary file created by the MakeSpriteFont utility.
|
||
|
_Use_decl_annotations_
|
||
|
SpriteFont::SpriteFont(ID3D11Device* device, wchar_t const* fileName, bool forceSRGB)
|
||
|
{
|
||
|
BinaryReader reader(fileName);
|
||
|
|
||
|
pImpl = std::make_unique<Impl>(device, &reader, forceSRGB);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Construct from a binary blob created by the MakeSpriteFont utility and already loaded into memory.
|
||
|
_Use_decl_annotations_
|
||
|
SpriteFont::SpriteFont(ID3D11Device* device, uint8_t const* dataBlob, size_t dataSize, bool forceSRGB)
|
||
|
{
|
||
|
BinaryReader reader(dataBlob, dataSize);
|
||
|
|
||
|
pImpl = std::make_unique<Impl>(device, &reader, forceSRGB);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Construct from arbitrary user specified glyph data (for those not using the MakeSpriteFont utility).
|
||
|
_Use_decl_annotations_
|
||
|
SpriteFont::SpriteFont(ID3D11ShaderResourceView* texture, Glyph const* glyphs, size_t glyphCount, float lineSpacing)
|
||
|
: pImpl(std::make_unique<Impl>(texture, glyphs, glyphCount, lineSpacing))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
// Move constructor.
|
||
|
SpriteFont::SpriteFont(SpriteFont&& moveFrom) noexcept
|
||
|
: pImpl(std::move(moveFrom.pImpl))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
// Move assignment.
|
||
|
SpriteFont& SpriteFont::operator= (SpriteFont&& moveFrom) noexcept
|
||
|
{
|
||
|
pImpl = std::move(moveFrom.pImpl);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Public destructor.
|
||
|
SpriteFont::~SpriteFont()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
// Wide-character / UTF-16LE
|
||
|
void XM_CALLCONV SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ wchar_t const* text, XMFLOAT2 const& position, FXMVECTOR color, float rotation, XMFLOAT2 const& origin, float scale, SpriteEffects effects, float layerDepth) const
|
||
|
{
|
||
|
DrawString(spriteBatch, text, XMLoadFloat2(&position), color, rotation, XMLoadFloat2(&origin), XMVectorReplicate(scale), effects, layerDepth);
|
||
|
}
|
||
|
|
||
|
|
||
|
void XM_CALLCONV SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ wchar_t const* text, XMFLOAT2 const& position, FXMVECTOR color, float rotation, XMFLOAT2 const& origin, XMFLOAT2 const& scale, SpriteEffects effects, float layerDepth) const
|
||
|
{
|
||
|
DrawString(spriteBatch, text, XMLoadFloat2(&position), color, rotation, XMLoadFloat2(&origin), XMLoadFloat2(&scale), effects, layerDepth);
|
||
|
}
|
||
|
|
||
|
|
||
|
void XM_CALLCONV SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ wchar_t const* text, FXMVECTOR position, FXMVECTOR color, float rotation, FXMVECTOR origin, float scale, SpriteEffects effects, float layerDepth) const
|
||
|
{
|
||
|
DrawString(spriteBatch, text, position, color, rotation, origin, XMVectorReplicate(scale), effects, layerDepth);
|
||
|
}
|
||
|
|
||
|
|
||
|
void XM_CALLCONV SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ wchar_t const* text, FXMVECTOR position, FXMVECTOR color, float rotation, FXMVECTOR origin, GXMVECTOR scale, SpriteEffects effects, float layerDepth) const
|
||
|
{
|
||
|
static_assert(SpriteEffects_FlipHorizontally == 1 &&
|
||
|
SpriteEffects_FlipVertically == 2, "If you change these enum values, the following tables must be updated to match");
|
||
|
|
||
|
// Lookup table indicates which way to move along each axis per SpriteEffects enum value.
|
||
|
static XMVECTORF32 axisDirectionTable[4] =
|
||
|
{
|
||
|
{ { { -1, -1, 0, 0 } } },
|
||
|
{ { { 1, -1, 0, 0 } } },
|
||
|
{ { { -1, 1, 0, 0 } } },
|
||
|
{ { { 1, 1, 0, 0 } } },
|
||
|
};
|
||
|
|
||
|
// Lookup table indicates which axes are mirrored for each SpriteEffects enum value.
|
||
|
static XMVECTORF32 axisIsMirroredTable[4] =
|
||
|
{
|
||
|
{ { { 0, 0, 0, 0 } } },
|
||
|
{ { { 1, 0, 0, 0 } } },
|
||
|
{ { { 0, 1, 0, 0 } } },
|
||
|
{ { { 1, 1, 0, 0 } } },
|
||
|
};
|
||
|
|
||
|
XMVECTOR baseOffset = origin;
|
||
|
|
||
|
// If the text is mirrored, offset the start position accordingly.
|
||
|
if (effects)
|
||
|
{
|
||
|
baseOffset = XMVectorNegativeMultiplySubtract(
|
||
|
MeasureString(text),
|
||
|
axisIsMirroredTable[effects & 3],
|
||
|
baseOffset);
|
||
|
}
|
||
|
|
||
|
// Draw each character in turn.
|
||
|
pImpl->ForEachGlyph(text, [&](Glyph const* glyph, float x, float y, float advance)
|
||
|
{
|
||
|
UNREFERENCED_PARAMETER(advance);
|
||
|
|
||
|
XMVECTOR offset = XMVectorMultiplyAdd(XMVectorSet(x, y + glyph->YOffset, 0, 0), axisDirectionTable[effects & 3], baseOffset);
|
||
|
|
||
|
if (effects)
|
||
|
{
|
||
|
// For mirrored characters, specify bottom and/or right instead of top left.
|
||
|
XMVECTOR glyphRect = XMConvertVectorIntToFloat(XMLoadInt4(reinterpret_cast<uint32_t const*>(&glyph->Subrect)), 0);
|
||
|
|
||
|
// xy = glyph width/height.
|
||
|
glyphRect = XMVectorSubtract(XMVectorSwizzle<2, 3, 0, 1>(glyphRect), glyphRect);
|
||
|
|
||
|
offset = XMVectorMultiplyAdd(glyphRect, axisIsMirroredTable[effects & 3], offset);
|
||
|
}
|
||
|
|
||
|
spriteBatch->Draw(pImpl->texture.Get(), position, &glyph->Subrect, color, rotation, offset, scale, effects, layerDepth);
|
||
|
}, true);
|
||
|
}
|
||
|
|
||
|
|
||
|
XMVECTOR XM_CALLCONV SpriteFont::MeasureString(_In_z_ wchar_t const* text, bool ignoreWhitespace) const
|
||
|
{
|
||
|
XMVECTOR result = XMVectorZero();
|
||
|
|
||
|
pImpl->ForEachGlyph(text, [&](Glyph const* glyph, float x, float y, float advance)
|
||
|
{
|
||
|
UNREFERENCED_PARAMETER(advance);
|
||
|
|
||
|
auto w = static_cast<float>(glyph->Subrect.right - glyph->Subrect.left);
|
||
|
auto h = static_cast<float>(glyph->Subrect.bottom - glyph->Subrect.top) + glyph->YOffset;
|
||
|
|
||
|
h = iswspace(wchar_t(glyph->Character)) ?
|
||
|
pImpl->lineSpacing :
|
||
|
std::max(h, pImpl->lineSpacing);
|
||
|
|
||
|
result = XMVectorMax(result, XMVectorSet(x + w, y + h, 0, 0));
|
||
|
}, ignoreWhitespace);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
RECT SpriteFont::MeasureDrawBounds(_In_z_ wchar_t const* text, XMFLOAT2 const& position, bool ignoreWhitespace) const
|
||
|
{
|
||
|
RECT result = { LONG_MAX, LONG_MAX, 0, 0 };
|
||
|
|
||
|
pImpl->ForEachGlyph(text, [&](Glyph const* glyph, float x, float y, float advance) noexcept
|
||
|
{
|
||
|
auto isWhitespace = iswspace(wchar_t(glyph->Character));
|
||
|
auto w = static_cast<float>(glyph->Subrect.right - glyph->Subrect.left);
|
||
|
auto h = isWhitespace ?
|
||
|
pImpl->lineSpacing :
|
||
|
static_cast<float>(glyph->Subrect.bottom - glyph->Subrect.top);
|
||
|
|
||
|
float minX = position.x + x;
|
||
|
float minY = position.y + y + (isWhitespace ? 0.0f : glyph->YOffset);
|
||
|
|
||
|
float maxX = std::max(minX + advance, minX + w);
|
||
|
float maxY = minY + h;
|
||
|
|
||
|
if (minX < float(result.left))
|
||
|
result.left = long(minX);
|
||
|
|
||
|
if (minY < float(result.top))
|
||
|
result.top = long(minY);
|
||
|
|
||
|
if (float(result.right) < maxX)
|
||
|
result.right = long(maxX);
|
||
|
|
||
|
if (float(result.bottom) < maxY)
|
||
|
result.bottom = long(maxY);
|
||
|
}, ignoreWhitespace);
|
||
|
|
||
|
if (result.left == LONG_MAX)
|
||
|
{
|
||
|
result.left = 0;
|
||
|
result.top = 0;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
RECT XM_CALLCONV SpriteFont::MeasureDrawBounds(_In_z_ wchar_t const* text, FXMVECTOR position, bool ignoreWhitespace) const
|
||
|
{
|
||
|
XMFLOAT2 pos;
|
||
|
XMStoreFloat2(&pos, position);
|
||
|
|
||
|
return MeasureDrawBounds(text, pos, ignoreWhitespace);
|
||
|
}
|
||
|
|
||
|
|
||
|
// UTF-8
|
||
|
void XM_CALLCONV SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ char const* text, XMFLOAT2 const& position, FXMVECTOR color, float rotation, XMFLOAT2 const& origin, float scale, SpriteEffects effects, float layerDepth) const
|
||
|
{
|
||
|
DrawString(spriteBatch, pImpl->ConvertUTF8(text), XMLoadFloat2(&position), color, rotation, XMLoadFloat2(&origin), XMVectorReplicate(scale), effects, layerDepth);
|
||
|
}
|
||
|
|
||
|
|
||
|
void XM_CALLCONV SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ char const* text, XMFLOAT2 const& position, FXMVECTOR color, float rotation, XMFLOAT2 const& origin, XMFLOAT2 const& scale, SpriteEffects effects, float layerDepth) const
|
||
|
{
|
||
|
DrawString(spriteBatch, pImpl->ConvertUTF8(text), XMLoadFloat2(&position), color, rotation, XMLoadFloat2(&origin), XMLoadFloat2(&scale), effects, layerDepth);
|
||
|
}
|
||
|
|
||
|
|
||
|
void XM_CALLCONV SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ char const* text, FXMVECTOR position, FXMVECTOR color, float rotation, FXMVECTOR origin, float scale, SpriteEffects effects, float layerDepth) const
|
||
|
{
|
||
|
DrawString(spriteBatch, pImpl->ConvertUTF8(text), position, color, rotation, origin, XMVectorReplicate(scale), effects, layerDepth);
|
||
|
}
|
||
|
|
||
|
|
||
|
void XM_CALLCONV SpriteFont::DrawString(_In_ SpriteBatch* spriteBatch, _In_z_ char const* text, FXMVECTOR position, FXMVECTOR color, float rotation, FXMVECTOR origin, GXMVECTOR scale, SpriteEffects effects, float layerDepth) const
|
||
|
{
|
||
|
DrawString(spriteBatch, pImpl->ConvertUTF8(text), position, color, rotation, origin, scale, effects, layerDepth);
|
||
|
}
|
||
|
|
||
|
|
||
|
XMVECTOR XM_CALLCONV SpriteFont::MeasureString(_In_z_ char const* text, bool ignoreWhitespace) const
|
||
|
{
|
||
|
return MeasureString(pImpl->ConvertUTF8(text), ignoreWhitespace);
|
||
|
}
|
||
|
|
||
|
|
||
|
RECT SpriteFont::MeasureDrawBounds(_In_z_ char const* text, XMFLOAT2 const& position, bool ignoreWhitespace) const
|
||
|
{
|
||
|
return MeasureDrawBounds(pImpl->ConvertUTF8(text), position, ignoreWhitespace);
|
||
|
}
|
||
|
|
||
|
|
||
|
RECT XM_CALLCONV SpriteFont::MeasureDrawBounds(_In_z_ char const* text, FXMVECTOR position, bool ignoreWhitespace) const
|
||
|
{
|
||
|
XMFLOAT2 pos;
|
||
|
XMStoreFloat2(&pos, position);
|
||
|
|
||
|
return MeasureDrawBounds(pImpl->ConvertUTF8(text), pos, ignoreWhitespace);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Spacing properties
|
||
|
float SpriteFont::GetLineSpacing() const noexcept
|
||
|
{
|
||
|
return pImpl->lineSpacing;
|
||
|
}
|
||
|
|
||
|
|
||
|
void SpriteFont::SetLineSpacing(float spacing)
|
||
|
{
|
||
|
pImpl->lineSpacing = spacing;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Font properties
|
||
|
wchar_t SpriteFont::GetDefaultCharacter() const noexcept
|
||
|
{
|
||
|
return static_cast<wchar_t>(pImpl->defaultGlyph ? pImpl->defaultGlyph->Character : 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
void SpriteFont::SetDefaultCharacter(wchar_t character)
|
||
|
{
|
||
|
pImpl->SetDefaultCharacter(character);
|
||
|
}
|
||
|
|
||
|
|
||
|
bool SpriteFont::ContainsCharacter(wchar_t character) const
|
||
|
{
|
||
|
return std::binary_search(pImpl->glyphs.begin(), pImpl->glyphs.end(), character);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Custom layout/rendering
|
||
|
SpriteFont::Glyph const* SpriteFont::FindGlyph(wchar_t character) const
|
||
|
{
|
||
|
return pImpl->FindGlyph(character);
|
||
|
}
|
||
|
|
||
|
|
||
|
void SpriteFont::GetSpriteSheet(ID3D11ShaderResourceView** texture) const
|
||
|
{
|
||
|
if (!texture)
|
||
|
return;
|
||
|
|
||
|
ThrowIfFailed(pImpl->texture.CopyTo(texture));
|
||
|
}
|