mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Direct2D: Move Direct2DFactories to juce_DirectX_windows.h
This commit is contained in:
parent
18b508343d
commit
6deb508e96
2 changed files with 514 additions and 514 deletions
|
|
@ -35,520 +35,6 @@
|
|||
namespace juce
|
||||
{
|
||||
|
||||
static String getLocalisedName (IDWriteLocalizedStrings* names)
|
||||
{
|
||||
jassert (names != nullptr);
|
||||
|
||||
uint32 index = 0;
|
||||
BOOL exists = false;
|
||||
[[maybe_unused]] auto hr = names->FindLocaleName (L"en-us", &index, &exists);
|
||||
|
||||
if (! exists)
|
||||
index = 0;
|
||||
|
||||
uint32 length = 0;
|
||||
hr = names->GetStringLength (index, &length);
|
||||
|
||||
HeapBlock<wchar_t> name (length + 1);
|
||||
hr = names->GetString (index, name, length + 1);
|
||||
|
||||
return static_cast<const wchar_t*> (name);
|
||||
}
|
||||
|
||||
static String getFontFamilyName (IDWriteFontFamily* family)
|
||||
{
|
||||
jassert (family != nullptr);
|
||||
ComSmartPtr<IDWriteLocalizedStrings> familyNames;
|
||||
auto hr = family->GetFamilyNames (familyNames.resetAndGetPointerAddress());
|
||||
jassertquiet (SUCCEEDED (hr));
|
||||
return getLocalisedName (familyNames);
|
||||
}
|
||||
|
||||
static String getFontFaceName (IDWriteFont* font)
|
||||
{
|
||||
jassert (font != nullptr);
|
||||
ComSmartPtr<IDWriteLocalizedStrings> faceNames;
|
||||
auto hr = font->GetFaceNames (faceNames.resetAndGetPointerAddress());
|
||||
jassertquiet (SUCCEEDED (hr));
|
||||
return getLocalisedName (faceNames);
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
static StringArray stringArrayFromRange (Range&& range)
|
||||
{
|
||||
StringArray result;
|
||||
|
||||
for (const auto& item : range)
|
||||
result.add (item);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
class AggregateFontCollection
|
||||
{
|
||||
public:
|
||||
explicit AggregateFontCollection (ComSmartPtr<IDWriteFontCollection> baseCollection)
|
||||
: collections { std::move (baseCollection) } {}
|
||||
|
||||
StringArray findAllTypefaceNames()
|
||||
{
|
||||
const std::scoped_lock lock { mutex };
|
||||
|
||||
std::set<String> strings;
|
||||
|
||||
for (const auto& collection : collections)
|
||||
{
|
||||
const auto count = collection->GetFontFamilyCount();
|
||||
|
||||
for (auto i = decltype (count){}; i < count; ++i)
|
||||
{
|
||||
ComSmartPtr<IDWriteFontFamily> family;
|
||||
|
||||
if (FAILED (collection->GetFontFamily (i, family.resetAndGetPointerAddress())) || family == nullptr)
|
||||
continue;
|
||||
|
||||
strings.insert (getFontFamilyName (family));
|
||||
}
|
||||
}
|
||||
|
||||
return stringArrayFromRange (strings);
|
||||
}
|
||||
|
||||
static std::vector<ComSmartPtr<IDWriteFont>> getAllFontsInFamily (IDWriteFontFamily* fontFamily)
|
||||
{
|
||||
const auto fontFacesCount = fontFamily->GetFontCount();
|
||||
std::vector<ComSmartPtr<IDWriteFont>> result;
|
||||
result.reserve (fontFacesCount);
|
||||
|
||||
for (UINT32 i = 0; i < fontFacesCount; ++i)
|
||||
{
|
||||
ComSmartPtr<IDWriteFont> dwFont;
|
||||
|
||||
if (FAILED (fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress())))
|
||||
continue;
|
||||
|
||||
if (dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE)
|
||||
continue;
|
||||
|
||||
result.push_back (dwFont);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
StringArray findAllTypefaceStyles (const String& family)
|
||||
{
|
||||
const std::scoped_lock lock { mutex };
|
||||
|
||||
for (const auto& collection : collections)
|
||||
{
|
||||
BOOL fontFound = false;
|
||||
uint32 fontIndex = 0;
|
||||
|
||||
if (FAILED (collection->FindFamilyName (family.toWideCharPointer(), &fontIndex, &fontFound)) || ! fontFound)
|
||||
continue;
|
||||
|
||||
ComSmartPtr<IDWriteFontFamily> fontFamily;
|
||||
|
||||
if (FAILED (collection->GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress())) || fontFamily == nullptr)
|
||||
continue;
|
||||
|
||||
std::set<String> uniqueResults;
|
||||
StringArray orderedResults;
|
||||
|
||||
for (const auto& font : getAllFontsInFamily (fontFamily))
|
||||
{
|
||||
const auto name = getFontFaceName (font);
|
||||
|
||||
if (uniqueResults.insert (name).second)
|
||||
orderedResults.add (name);
|
||||
}
|
||||
|
||||
return orderedResults;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ComSmartPtr<IDWriteFontFamily> getFamilyByName (const wchar_t* name)
|
||||
{
|
||||
const std::scoped_lock lock { mutex };
|
||||
|
||||
for (const auto& collection : collections)
|
||||
{
|
||||
const auto fontIndex = [&]
|
||||
{
|
||||
BOOL found = false;
|
||||
UINT32 index = 0;
|
||||
|
||||
return (SUCCEEDED (collection->FindFamilyName (name, &index, &found)) && found)
|
||||
? index
|
||||
: (UINT32) -1;
|
||||
}();
|
||||
|
||||
if (fontIndex == (UINT32) -1)
|
||||
continue;
|
||||
|
||||
ComSmartPtr<IDWriteFontFamily> family;
|
||||
|
||||
if (FAILED (collection->GetFontFamily (fontIndex, family.resetAndGetPointerAddress())) || family == nullptr)
|
||||
continue;
|
||||
|
||||
return family;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ComSmartPtr<IDWriteFont> findFontForFace (IDWriteFontFace* face)
|
||||
{
|
||||
for (const auto& collection : collections)
|
||||
{
|
||||
ComSmartPtr<IDWriteFont> result;
|
||||
|
||||
if (SUCCEEDED (collection->GetFontFromFontFace (face, result.resetAndGetPointerAddress())))
|
||||
return result;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void addCollection (ComSmartPtr<IDWriteFontCollection> collection)
|
||||
{
|
||||
const std::scoped_lock lock { mutex };
|
||||
collections.push_back (std::move (collection));
|
||||
}
|
||||
|
||||
void removeCollection (ComSmartPtr<IDWriteFontCollection> collection)
|
||||
{
|
||||
const std::scoped_lock lock { mutex };
|
||||
const auto iter = std::find (collections.begin(), collections.end(), collection);
|
||||
|
||||
if (iter != collections.end())
|
||||
collections.erase (iter);
|
||||
}
|
||||
|
||||
struct MapResult
|
||||
{
|
||||
ComSmartPtr<IDWriteFont> font;
|
||||
UINT32 length{};
|
||||
float scale{};
|
||||
};
|
||||
|
||||
/* Tries matching against each collection in turn.
|
||||
If any collection is able to match the entire string, then uses the appropriate font
|
||||
from that collection.
|
||||
Otherwise, returns the font that is able to match the longest sequence of characters,
|
||||
preferring user-provided fonts.
|
||||
*/
|
||||
MapResult mapCharacters (IDWriteFontFallback* fallback,
|
||||
IDWriteTextAnalysisSource* analysisSource,
|
||||
UINT32 textPosition,
|
||||
UINT32 textLength,
|
||||
wchar_t const* baseFamilyName,
|
||||
DWRITE_FONT_WEIGHT baseWeight,
|
||||
DWRITE_FONT_STYLE baseStyle,
|
||||
DWRITE_FONT_STRETCH baseStretch) noexcept
|
||||
{
|
||||
const std::scoped_lock lock { mutex };
|
||||
|
||||
// For reasons I don't understand, the system may pick better substitutions when passing
|
||||
// nullptr, instead of the system collection, as the "default collection to use".
|
||||
auto collectionsToCheck = collections;
|
||||
collectionsToCheck.insert (collectionsToCheck.begin(), nullptr);
|
||||
|
||||
MapResult bestMatch;
|
||||
for (const auto& collection : collectionsToCheck)
|
||||
{
|
||||
MapResult result;
|
||||
const auto status = fallback->MapCharacters (analysisSource,
|
||||
textPosition,
|
||||
textLength,
|
||||
collection,
|
||||
baseFamilyName,
|
||||
baseWeight,
|
||||
baseStyle,
|
||||
baseStretch,
|
||||
&result.length,
|
||||
result.font.resetAndGetPointerAddress(),
|
||||
&result.scale);
|
||||
|
||||
if (FAILED (status) || result.font == nullptr)
|
||||
continue;
|
||||
|
||||
if (result.length == textLength)
|
||||
return result;
|
||||
|
||||
if (result.length >= bestMatch.length)
|
||||
bestMatch = result;
|
||||
}
|
||||
|
||||
return bestMatch;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<ComSmartPtr<IDWriteFontCollection>> collections;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
class MemoryFontFileStream final : public ComBaseClassHelper<IDWriteFontFileStream>
|
||||
{
|
||||
public:
|
||||
explicit MemoryFontFileStream (std::shared_ptr<const MemoryBlock> blockIn)
|
||||
: block (std::move (blockIn))
|
||||
{
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetFileSize (UINT64* fileSize) noexcept override
|
||||
{
|
||||
*fileSize = block->getSize();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetLastWriteTime (UINT64* lastWriteTime) noexcept override
|
||||
{
|
||||
*lastWriteTime = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT ReadFileFragment (const void** fragmentStart,
|
||||
UINT64 fileOffset,
|
||||
UINT64 fragmentSize,
|
||||
void** fragmentContext) noexcept override
|
||||
{
|
||||
if (fileOffset + fragmentSize > block->getSize())
|
||||
{
|
||||
*fragmentStart = nullptr;
|
||||
*fragmentContext = nullptr;
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
*fragmentStart = addBytesToPointer (block->getData(), fileOffset);
|
||||
*fragmentContext = this;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void WINAPI ReleaseFileFragment (void*) noexcept override {}
|
||||
|
||||
private:
|
||||
std::shared_ptr<const MemoryBlock> block;
|
||||
};
|
||||
|
||||
class MemoryFontFileLoader final : public ComBaseClassHelper<IDWriteFontFileLoader>
|
||||
{
|
||||
public:
|
||||
explicit MemoryFontFileLoader (MemoryBlock blob)
|
||||
: block (std::make_shared<const MemoryBlock> (std::move (blob)))
|
||||
{
|
||||
}
|
||||
|
||||
HRESULT WINAPI CreateStreamFromKey (const void* fontFileReferenceKey,
|
||||
UINT32 keySize,
|
||||
IDWriteFontFileStream** fontFileStream) noexcept override
|
||||
{
|
||||
if (keySize != Uuid::size())
|
||||
return E_INVALIDARG;
|
||||
|
||||
Uuid requestedKey { static_cast<const uint8*> (fontFileReferenceKey) };
|
||||
|
||||
if (requestedKey == uuid)
|
||||
{
|
||||
*fontFileStream = new MemoryFontFileStream { block };
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
Uuid getUuid() const { return uuid; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<const MemoryBlock> block;
|
||||
Uuid uuid;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryFontFileLoader)
|
||||
};
|
||||
|
||||
class FontFileEnumerator final : public ComBaseClassHelper<IDWriteFontFileEnumerator>
|
||||
{
|
||||
public:
|
||||
FontFileEnumerator (IDWriteFactory& factoryIn, ComSmartPtr<MemoryFontFileLoader> loaderIn)
|
||||
: factory (factoryIn), loader (loaderIn) {}
|
||||
|
||||
HRESULT WINAPI GetCurrentFontFile (IDWriteFontFile** fontFile) noexcept override
|
||||
{
|
||||
*fontFile = nullptr;
|
||||
|
||||
if (! isPositiveAndBelow (rawDataIndex, 1))
|
||||
return E_FAIL;
|
||||
|
||||
const auto uuid = loader->getUuid();
|
||||
return factory.CreateCustomFontFileReference (uuid.getRawData(),
|
||||
(UINT32) uuid.size(),
|
||||
loader,
|
||||
fontFile);
|
||||
}
|
||||
|
||||
HRESULT WINAPI MoveNext (BOOL* hasCurrentFile) noexcept override
|
||||
{
|
||||
++rawDataIndex;
|
||||
*hasCurrentFile = rawDataIndex < 1 ? TRUE : FALSE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
IDWriteFactory& factory;
|
||||
ComSmartPtr<MemoryFontFileLoader> loader;
|
||||
size_t rawDataIndex = std::numeric_limits<size_t>::max();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FontFileEnumerator)
|
||||
};
|
||||
|
||||
class DirectWriteCustomFontCollectionLoader final : public ComBaseClassHelper<IDWriteFontCollectionLoader>
|
||||
{
|
||||
public:
|
||||
explicit DirectWriteCustomFontCollectionLoader (IDWriteFactory& factoryIn)
|
||||
: factory (factoryIn)
|
||||
{
|
||||
}
|
||||
|
||||
~DirectWriteCustomFontCollectionLoader() override
|
||||
{
|
||||
for (const auto& loader : fileLoaders)
|
||||
factory.UnregisterFontFileLoader (loader);
|
||||
}
|
||||
|
||||
Uuid addRawFontData (Span<const std::byte> blob)
|
||||
{
|
||||
const auto loader = becomeComSmartPtrOwner (new MemoryFontFileLoader { { blob.data(), blob.size() } });
|
||||
|
||||
factory.RegisterFontFileLoader (loader);
|
||||
|
||||
fileLoaders.push_back (loader);
|
||||
|
||||
return fileLoaders.back()->getUuid();
|
||||
}
|
||||
|
||||
HRESULT WINAPI CreateEnumeratorFromKey (IDWriteFactory* factoryIn,
|
||||
const void* collectionKey,
|
||||
UINT32 collectionKeySize,
|
||||
IDWriteFontFileEnumerator** fontFileEnumerator) noexcept override
|
||||
{
|
||||
if (collectionKeySize != Uuid::size())
|
||||
return E_INVALIDARG;
|
||||
|
||||
const Uuid requestedCollectionKey { static_cast<const uint8*> (collectionKey) };
|
||||
|
||||
for (const auto& loader : fileLoaders)
|
||||
{
|
||||
if (loader->getUuid() != requestedCollectionKey)
|
||||
continue;
|
||||
|
||||
*fontFileEnumerator = new FontFileEnumerator { *factoryIn, loader };
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
private:
|
||||
IDWriteFactory& factory;
|
||||
std::vector<ComSmartPtr<MemoryFontFileLoader>> fileLoaders;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class Direct2DFactories
|
||||
{
|
||||
public:
|
||||
Direct2DFactories()
|
||||
{
|
||||
ComSmartPtr<IDWriteFontCollection> collection;
|
||||
|
||||
if (SUCCEEDED (directWriteFactory->GetSystemFontCollection (collection.resetAndGetPointerAddress(), FALSE)) && collection != nullptr)
|
||||
fonts.emplace (collection);
|
||||
else
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
~Direct2DFactories()
|
||||
{
|
||||
if (directWriteFactory == nullptr)
|
||||
return;
|
||||
|
||||
directWriteFactory->UnregisterFontCollectionLoader (collectionLoader);
|
||||
}
|
||||
|
||||
[[nodiscard]] ComSmartPtr<IDWriteFactory> getDWriteFactory() const { return directWriteFactory; }
|
||||
[[nodiscard]] ComSmartPtr<IDWriteFactory4> getDWriteFactory4() const { return directWriteFactory4; }
|
||||
[[nodiscard]] AggregateFontCollection& getFonts() { jassert (fonts.has_value()); return *fonts; }
|
||||
[[nodiscard]] ComSmartPtr<DirectWriteCustomFontCollectionLoader> getCollectionLoader() const { return collectionLoader; }
|
||||
|
||||
private:
|
||||
DynamicLibrary direct2dDll { "d2d1.dll" }, directWriteDll { "DWrite.dll" };
|
||||
|
||||
const ComSmartPtr<ID2D1Factory> d2dFactory = [&]() -> ComSmartPtr<ID2D1Factory>
|
||||
{
|
||||
JUCE_LOAD_WINAPI_FUNCTION (direct2dDll,
|
||||
D2D1CreateFactory,
|
||||
d2d1CreateFactory,
|
||||
HRESULT,
|
||||
(D2D1_FACTORY_TYPE, REFIID, D2D1_FACTORY_OPTIONS*, void**))
|
||||
|
||||
if (d2d1CreateFactory == nullptr)
|
||||
return {};
|
||||
|
||||
D2D1_FACTORY_OPTIONS options;
|
||||
options.debugLevel = D2D1_DEBUG_LEVEL_NONE;
|
||||
|
||||
ComSmartPtr<ID2D1Factory> result;
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
d2d1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED,
|
||||
__uuidof (ID2D1Factory),
|
||||
&options,
|
||||
(void**) result.resetAndGetPointerAddress());
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
return result;
|
||||
}();
|
||||
|
||||
const ComSmartPtr<IDWriteFactory> directWriteFactory = [&]() -> ComSmartPtr<IDWriteFactory>
|
||||
{
|
||||
JUCE_LOAD_WINAPI_FUNCTION (directWriteDll,
|
||||
DWriteCreateFactory,
|
||||
dWriteCreateFactory,
|
||||
HRESULT,
|
||||
(DWRITE_FACTORY_TYPE, REFIID, IUnknown**))
|
||||
|
||||
if (dWriteCreateFactory == nullptr)
|
||||
return {};
|
||||
|
||||
ComSmartPtr<IDWriteFactory> result;
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
dWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED,
|
||||
__uuidof (IDWriteFactory),
|
||||
(IUnknown**) result.resetAndGetPointerAddress());
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
return result;
|
||||
}();
|
||||
|
||||
const ComSmartPtr<DirectWriteCustomFontCollectionLoader> collectionLoader = [&]() -> ComSmartPtr<DirectWriteCustomFontCollectionLoader>
|
||||
{
|
||||
auto result = becomeComSmartPtrOwner (new DirectWriteCustomFontCollectionLoader { *directWriteFactory });
|
||||
directWriteFactory->RegisterFontCollectionLoader (result);
|
||||
|
||||
return result;
|
||||
}();
|
||||
|
||||
const ComSmartPtr<IDWriteFactory4> directWriteFactory4 = directWriteFactory.getInterface<IDWriteFactory4>();
|
||||
|
||||
std::optional<AggregateFontCollection> fonts;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Direct2DFactories)
|
||||
};
|
||||
|
||||
StringArray Font::findAllTypefaceNames()
|
||||
{
|
||||
SharedResourcePointer<Direct2DFactories> factories;
|
||||
|
|
|
|||
|
|
@ -1423,4 +1423,518 @@ private:
|
|||
ComSmartPtr<IDCompositionVisual> compositionVisual;
|
||||
};
|
||||
|
||||
static String getLocalisedName (IDWriteLocalizedStrings* names)
|
||||
{
|
||||
jassert (names != nullptr);
|
||||
|
||||
uint32 index = 0;
|
||||
BOOL exists = false;
|
||||
[[maybe_unused]] auto hr = names->FindLocaleName (L"en-us", &index, &exists);
|
||||
|
||||
if (! exists)
|
||||
index = 0;
|
||||
|
||||
uint32 length = 0;
|
||||
hr = names->GetStringLength (index, &length);
|
||||
|
||||
HeapBlock<wchar_t> name (length + 1);
|
||||
hr = names->GetString (index, name, length + 1);
|
||||
|
||||
return static_cast<const wchar_t*> (name);
|
||||
}
|
||||
|
||||
static String getFontFamilyName (IDWriteFontFamily* family)
|
||||
{
|
||||
jassert (family != nullptr);
|
||||
ComSmartPtr<IDWriteLocalizedStrings> familyNames;
|
||||
auto hr = family->GetFamilyNames (familyNames.resetAndGetPointerAddress());
|
||||
jassertquiet (SUCCEEDED (hr));
|
||||
return getLocalisedName (familyNames);
|
||||
}
|
||||
|
||||
static String getFontFaceName (IDWriteFont* font)
|
||||
{
|
||||
jassert (font != nullptr);
|
||||
ComSmartPtr<IDWriteLocalizedStrings> faceNames;
|
||||
auto hr = font->GetFaceNames (faceNames.resetAndGetPointerAddress());
|
||||
jassertquiet (SUCCEEDED (hr));
|
||||
return getLocalisedName (faceNames);
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
static StringArray stringArrayFromRange (Range&& range)
|
||||
{
|
||||
StringArray result;
|
||||
|
||||
for (const auto& item : range)
|
||||
result.add (item);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
class AggregateFontCollection
|
||||
{
|
||||
public:
|
||||
explicit AggregateFontCollection (ComSmartPtr<IDWriteFontCollection> baseCollection)
|
||||
: collections { std::move (baseCollection) } {}
|
||||
|
||||
StringArray findAllTypefaceNames()
|
||||
{
|
||||
const std::scoped_lock lock { mutex };
|
||||
|
||||
std::set<String> strings;
|
||||
|
||||
for (const auto& collection : collections)
|
||||
{
|
||||
const auto count = collection->GetFontFamilyCount();
|
||||
|
||||
for (auto i = decltype (count){}; i < count; ++i)
|
||||
{
|
||||
ComSmartPtr<IDWriteFontFamily> family;
|
||||
|
||||
if (FAILED (collection->GetFontFamily (i, family.resetAndGetPointerAddress())) || family == nullptr)
|
||||
continue;
|
||||
|
||||
strings.insert (getFontFamilyName (family));
|
||||
}
|
||||
}
|
||||
|
||||
return stringArrayFromRange (strings);
|
||||
}
|
||||
|
||||
static std::vector<ComSmartPtr<IDWriteFont>> getAllFontsInFamily (IDWriteFontFamily* fontFamily)
|
||||
{
|
||||
const auto fontFacesCount = fontFamily->GetFontCount();
|
||||
std::vector<ComSmartPtr<IDWriteFont>> result;
|
||||
result.reserve (fontFacesCount);
|
||||
|
||||
for (UINT32 i = 0; i < fontFacesCount; ++i)
|
||||
{
|
||||
ComSmartPtr<IDWriteFont> dwFont;
|
||||
|
||||
if (FAILED (fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress())))
|
||||
continue;
|
||||
|
||||
if (dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE)
|
||||
continue;
|
||||
|
||||
result.push_back (dwFont);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
StringArray findAllTypefaceStyles (const String& family)
|
||||
{
|
||||
const std::scoped_lock lock { mutex };
|
||||
|
||||
for (const auto& collection : collections)
|
||||
{
|
||||
BOOL fontFound = false;
|
||||
uint32 fontIndex = 0;
|
||||
|
||||
if (FAILED (collection->FindFamilyName (family.toWideCharPointer(), &fontIndex, &fontFound)) || ! fontFound)
|
||||
continue;
|
||||
|
||||
ComSmartPtr<IDWriteFontFamily> fontFamily;
|
||||
|
||||
if (FAILED (collection->GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress())) || fontFamily == nullptr)
|
||||
continue;
|
||||
|
||||
std::set<String> uniqueResults;
|
||||
StringArray orderedResults;
|
||||
|
||||
for (const auto& font : getAllFontsInFamily (fontFamily))
|
||||
{
|
||||
const auto name = getFontFaceName (font);
|
||||
|
||||
if (uniqueResults.insert (name).second)
|
||||
orderedResults.add (name);
|
||||
}
|
||||
|
||||
return orderedResults;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ComSmartPtr<IDWriteFontFamily> getFamilyByName (const wchar_t* name)
|
||||
{
|
||||
const std::scoped_lock lock { mutex };
|
||||
|
||||
for (const auto& collection : collections)
|
||||
{
|
||||
const auto fontIndex = [&]
|
||||
{
|
||||
BOOL found = false;
|
||||
UINT32 index = 0;
|
||||
|
||||
return (SUCCEEDED (collection->FindFamilyName (name, &index, &found)) && found)
|
||||
? index
|
||||
: (UINT32) -1;
|
||||
}();
|
||||
|
||||
if (fontIndex == (UINT32) -1)
|
||||
continue;
|
||||
|
||||
ComSmartPtr<IDWriteFontFamily> family;
|
||||
|
||||
if (FAILED (collection->GetFontFamily (fontIndex, family.resetAndGetPointerAddress())) || family == nullptr)
|
||||
continue;
|
||||
|
||||
return family;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ComSmartPtr<IDWriteFont> findFontForFace (IDWriteFontFace* face)
|
||||
{
|
||||
for (const auto& collection : collections)
|
||||
{
|
||||
ComSmartPtr<IDWriteFont> result;
|
||||
|
||||
if (SUCCEEDED (collection->GetFontFromFontFace (face, result.resetAndGetPointerAddress())))
|
||||
return result;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void addCollection (ComSmartPtr<IDWriteFontCollection> collection)
|
||||
{
|
||||
const std::scoped_lock lock { mutex };
|
||||
collections.push_back (std::move (collection));
|
||||
}
|
||||
|
||||
void removeCollection (ComSmartPtr<IDWriteFontCollection> collection)
|
||||
{
|
||||
const std::scoped_lock lock { mutex };
|
||||
const auto iter = std::find (collections.begin(), collections.end(), collection);
|
||||
|
||||
if (iter != collections.end())
|
||||
collections.erase (iter);
|
||||
}
|
||||
|
||||
struct MapResult
|
||||
{
|
||||
ComSmartPtr<IDWriteFont> font;
|
||||
UINT32 length{};
|
||||
float scale{};
|
||||
};
|
||||
|
||||
/* Tries matching against each collection in turn.
|
||||
If any collection is able to match the entire string, then uses the appropriate font
|
||||
from that collection.
|
||||
Otherwise, returns the font that is able to match the longest sequence of characters,
|
||||
preferring user-provided fonts.
|
||||
*/
|
||||
MapResult mapCharacters (IDWriteFontFallback* fallback,
|
||||
IDWriteTextAnalysisSource* analysisSource,
|
||||
UINT32 textPosition,
|
||||
UINT32 textLength,
|
||||
wchar_t const* baseFamilyName,
|
||||
DWRITE_FONT_WEIGHT baseWeight,
|
||||
DWRITE_FONT_STYLE baseStyle,
|
||||
DWRITE_FONT_STRETCH baseStretch) noexcept
|
||||
{
|
||||
const std::scoped_lock lock { mutex };
|
||||
|
||||
// For reasons I don't understand, the system may pick better substitutions when passing
|
||||
// nullptr, instead of the system collection, as the "default collection to use".
|
||||
auto collectionsToCheck = collections;
|
||||
collectionsToCheck.insert (collectionsToCheck.begin(), nullptr);
|
||||
|
||||
MapResult bestMatch;
|
||||
for (const auto& collection : collectionsToCheck)
|
||||
{
|
||||
MapResult result;
|
||||
const auto status = fallback->MapCharacters (analysisSource,
|
||||
textPosition,
|
||||
textLength,
|
||||
collection,
|
||||
baseFamilyName,
|
||||
baseWeight,
|
||||
baseStyle,
|
||||
baseStretch,
|
||||
&result.length,
|
||||
result.font.resetAndGetPointerAddress(),
|
||||
&result.scale);
|
||||
|
||||
if (FAILED (status) || result.font == nullptr)
|
||||
continue;
|
||||
|
||||
if (result.length == textLength)
|
||||
return result;
|
||||
|
||||
if (result.length >= bestMatch.length)
|
||||
bestMatch = result;
|
||||
}
|
||||
|
||||
return bestMatch;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<ComSmartPtr<IDWriteFontCollection>> collections;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
class MemoryFontFileStream final : public ComBaseClassHelper<IDWriteFontFileStream>
|
||||
{
|
||||
public:
|
||||
explicit MemoryFontFileStream (std::shared_ptr<const MemoryBlock> blockIn)
|
||||
: block (std::move (blockIn))
|
||||
{
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetFileSize (UINT64* fileSize) noexcept override
|
||||
{
|
||||
*fileSize = block->getSize();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetLastWriteTime (UINT64* lastWriteTime) noexcept override
|
||||
{
|
||||
*lastWriteTime = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT ReadFileFragment (const void** fragmentStart,
|
||||
UINT64 fileOffset,
|
||||
UINT64 fragmentSize,
|
||||
void** fragmentContext) noexcept override
|
||||
{
|
||||
if (fileOffset + fragmentSize > block->getSize())
|
||||
{
|
||||
*fragmentStart = nullptr;
|
||||
*fragmentContext = nullptr;
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
*fragmentStart = addBytesToPointer (block->getData(), fileOffset);
|
||||
*fragmentContext = this;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void WINAPI ReleaseFileFragment (void*) noexcept override {}
|
||||
|
||||
private:
|
||||
std::shared_ptr<const MemoryBlock> block;
|
||||
};
|
||||
|
||||
class MemoryFontFileLoader final : public ComBaseClassHelper<IDWriteFontFileLoader>
|
||||
{
|
||||
public:
|
||||
explicit MemoryFontFileLoader (MemoryBlock blob)
|
||||
: block (std::make_shared<const MemoryBlock> (std::move (blob)))
|
||||
{
|
||||
}
|
||||
|
||||
HRESULT WINAPI CreateStreamFromKey (const void* fontFileReferenceKey,
|
||||
UINT32 keySize,
|
||||
IDWriteFontFileStream** fontFileStream) noexcept override
|
||||
{
|
||||
if (keySize != Uuid::size())
|
||||
return E_INVALIDARG;
|
||||
|
||||
Uuid requestedKey { static_cast<const uint8*> (fontFileReferenceKey) };
|
||||
|
||||
if (requestedKey == uuid)
|
||||
{
|
||||
*fontFileStream = new MemoryFontFileStream { block };
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
Uuid getUuid() const { return uuid; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<const MemoryBlock> block;
|
||||
Uuid uuid;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryFontFileLoader)
|
||||
};
|
||||
|
||||
class FontFileEnumerator final : public ComBaseClassHelper<IDWriteFontFileEnumerator>
|
||||
{
|
||||
public:
|
||||
FontFileEnumerator (IDWriteFactory& factoryIn, ComSmartPtr<MemoryFontFileLoader> loaderIn)
|
||||
: factory (factoryIn), loader (loaderIn) {}
|
||||
|
||||
HRESULT WINAPI GetCurrentFontFile (IDWriteFontFile** fontFile) noexcept override
|
||||
{
|
||||
*fontFile = nullptr;
|
||||
|
||||
if (! isPositiveAndBelow (rawDataIndex, 1))
|
||||
return E_FAIL;
|
||||
|
||||
const auto uuid = loader->getUuid();
|
||||
return factory.CreateCustomFontFileReference (uuid.getRawData(),
|
||||
(UINT32) uuid.size(),
|
||||
loader,
|
||||
fontFile);
|
||||
}
|
||||
|
||||
HRESULT WINAPI MoveNext (BOOL* hasCurrentFile) noexcept override
|
||||
{
|
||||
++rawDataIndex;
|
||||
*hasCurrentFile = rawDataIndex < 1 ? TRUE : FALSE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
IDWriteFactory& factory;
|
||||
ComSmartPtr<MemoryFontFileLoader> loader;
|
||||
size_t rawDataIndex = std::numeric_limits<size_t>::max();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FontFileEnumerator)
|
||||
};
|
||||
|
||||
class DirectWriteCustomFontCollectionLoader final : public ComBaseClassHelper<IDWriteFontCollectionLoader>
|
||||
{
|
||||
public:
|
||||
explicit DirectWriteCustomFontCollectionLoader (IDWriteFactory& factoryIn)
|
||||
: factory (factoryIn)
|
||||
{
|
||||
}
|
||||
|
||||
~DirectWriteCustomFontCollectionLoader() override
|
||||
{
|
||||
for (const auto& loader : fileLoaders)
|
||||
factory.UnregisterFontFileLoader (loader);
|
||||
}
|
||||
|
||||
Uuid addRawFontData (Span<const std::byte> blob)
|
||||
{
|
||||
const auto loader = becomeComSmartPtrOwner (new MemoryFontFileLoader { { blob.data(), blob.size() } });
|
||||
|
||||
factory.RegisterFontFileLoader (loader);
|
||||
|
||||
fileLoaders.push_back (loader);
|
||||
|
||||
return fileLoaders.back()->getUuid();
|
||||
}
|
||||
|
||||
HRESULT WINAPI CreateEnumeratorFromKey (IDWriteFactory* factoryIn,
|
||||
const void* collectionKey,
|
||||
UINT32 collectionKeySize,
|
||||
IDWriteFontFileEnumerator** fontFileEnumerator) noexcept override
|
||||
{
|
||||
if (collectionKeySize != Uuid::size())
|
||||
return E_INVALIDARG;
|
||||
|
||||
const Uuid requestedCollectionKey { static_cast<const uint8*> (collectionKey) };
|
||||
|
||||
for (const auto& loader : fileLoaders)
|
||||
{
|
||||
if (loader->getUuid() != requestedCollectionKey)
|
||||
continue;
|
||||
|
||||
*fontFileEnumerator = new FontFileEnumerator { *factoryIn, loader };
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
private:
|
||||
IDWriteFactory& factory;
|
||||
std::vector<ComSmartPtr<MemoryFontFileLoader>> fileLoaders;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class Direct2DFactories
|
||||
{
|
||||
public:
|
||||
Direct2DFactories()
|
||||
{
|
||||
ComSmartPtr<IDWriteFontCollection> collection;
|
||||
|
||||
if (SUCCEEDED (directWriteFactory->GetSystemFontCollection (collection.resetAndGetPointerAddress(), FALSE)) && collection != nullptr)
|
||||
fonts.emplace (collection);
|
||||
else
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
~Direct2DFactories()
|
||||
{
|
||||
if (directWriteFactory == nullptr)
|
||||
return;
|
||||
|
||||
directWriteFactory->UnregisterFontCollectionLoader (collectionLoader);
|
||||
}
|
||||
|
||||
[[nodiscard]] ComSmartPtr<IDWriteFactory> getDWriteFactory() const { return directWriteFactory; }
|
||||
[[nodiscard]] ComSmartPtr<IDWriteFactory4> getDWriteFactory4() const { return directWriteFactory4; }
|
||||
[[nodiscard]] AggregateFontCollection& getFonts() { jassert (fonts.has_value()); return *fonts; }
|
||||
[[nodiscard]] ComSmartPtr<DirectWriteCustomFontCollectionLoader> getCollectionLoader() const { return collectionLoader; }
|
||||
|
||||
private:
|
||||
DynamicLibrary direct2dDll { "d2d1.dll" }, directWriteDll { "DWrite.dll" };
|
||||
|
||||
const ComSmartPtr<ID2D1Factory> d2dFactory = [&]() -> ComSmartPtr<ID2D1Factory>
|
||||
{
|
||||
JUCE_LOAD_WINAPI_FUNCTION (direct2dDll,
|
||||
D2D1CreateFactory,
|
||||
d2d1CreateFactory,
|
||||
HRESULT,
|
||||
(D2D1_FACTORY_TYPE, REFIID, D2D1_FACTORY_OPTIONS*, void**))
|
||||
|
||||
if (d2d1CreateFactory == nullptr)
|
||||
return {};
|
||||
|
||||
D2D1_FACTORY_OPTIONS options;
|
||||
options.debugLevel = D2D1_DEBUG_LEVEL_NONE;
|
||||
|
||||
ComSmartPtr<ID2D1Factory> result;
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
d2d1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED,
|
||||
__uuidof (ID2D1Factory),
|
||||
&options,
|
||||
(void**) result.resetAndGetPointerAddress());
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
return result;
|
||||
}();
|
||||
|
||||
const ComSmartPtr<IDWriteFactory> directWriteFactory = [&]() -> ComSmartPtr<IDWriteFactory>
|
||||
{
|
||||
JUCE_LOAD_WINAPI_FUNCTION (directWriteDll,
|
||||
DWriteCreateFactory,
|
||||
dWriteCreateFactory,
|
||||
HRESULT,
|
||||
(DWRITE_FACTORY_TYPE, REFIID, IUnknown**))
|
||||
|
||||
if (dWriteCreateFactory == nullptr)
|
||||
return {};
|
||||
|
||||
ComSmartPtr<IDWriteFactory> result;
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
dWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED,
|
||||
__uuidof (IDWriteFactory),
|
||||
(IUnknown**) result.resetAndGetPointerAddress());
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
return result;
|
||||
}();
|
||||
|
||||
const ComSmartPtr<DirectWriteCustomFontCollectionLoader> collectionLoader = [&]() -> ComSmartPtr<DirectWriteCustomFontCollectionLoader>
|
||||
{
|
||||
auto result = becomeComSmartPtrOwner (new DirectWriteCustomFontCollectionLoader { *directWriteFactory });
|
||||
directWriteFactory->RegisterFontCollectionLoader (result);
|
||||
|
||||
return result;
|
||||
}();
|
||||
|
||||
const ComSmartPtr<IDWriteFactory4> directWriteFactory4 = directWriteFactory.getInterface<IDWriteFactory4>();
|
||||
|
||||
std::optional<AggregateFontCollection> fonts;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Direct2DFactories)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue