mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-09 23:34:20 +00:00
439 lines
15 KiB
C++
439 lines
15 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE framework.
|
|
Copyright (c) Raw Material Software Limited
|
|
|
|
JUCE is an open source framework subject to commercial or open source
|
|
licensing.
|
|
|
|
By downloading, installing, or using the JUCE framework, or combining the
|
|
JUCE framework with any other source code, object code, content or any other
|
|
copyrightable work, you agree to the terms of the JUCE End User Licence
|
|
Agreement, and all incorporated terms including the JUCE Privacy Policy and
|
|
the JUCE Website Terms of Service, as applicable, which will bind you. If you
|
|
do not agree to the terms of these agreements, we will not license the JUCE
|
|
framework to you, and you must discontinue the installation or download
|
|
process and cease use of the JUCE framework.
|
|
|
|
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
|
|
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
|
|
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
|
|
|
|
Or:
|
|
|
|
You may also use this code under the terms of the AGPLv3:
|
|
https://www.gnu.org/licenses/agpl-3.0.en.html
|
|
|
|
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
|
|
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
|
|
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
namespace juce
|
|
{
|
|
|
|
//==============================================================================
|
|
/** Heap storage for a DirectWrite glyph run */
|
|
class DirectWriteGlyphRun
|
|
{
|
|
public:
|
|
void replace (Span<const Point<float>> positions, float scale);
|
|
|
|
auto* getAdvances() const { return advances.data(); }
|
|
auto* getOffsets() const { return offsets .data(); }
|
|
|
|
private:
|
|
std::vector<float> advances;
|
|
std::vector<DWRITE_GLYPH_OFFSET> offsets;
|
|
};
|
|
|
|
struct DxgiAdapter : public ReferenceCountedObject
|
|
{
|
|
using Ptr = ReferenceCountedObjectPtr<DxgiAdapter>;
|
|
|
|
static Ptr create (ComSmartPtr<ID2D1Factory2>, ComSmartPtr<IDXGIAdapter1>);
|
|
|
|
bool uniqueIDMatches (ReferenceCountedObjectPtr<DxgiAdapter> other) const;
|
|
|
|
LUID getAdapterUniqueID() const;
|
|
|
|
ComSmartPtr<ID3D11Device> direct3DDevice;
|
|
ComSmartPtr<IDXGIDevice> dxgiDevice;
|
|
ComSmartPtr<ID2D1Device1> direct2DDevice;
|
|
ComSmartPtr<IDXGIAdapter1> dxgiAdapter;
|
|
std::vector<ComSmartPtr<IDXGIOutput>> dxgiOutputs;
|
|
|
|
private:
|
|
DxgiAdapter() = default;
|
|
};
|
|
|
|
struct DxgiAdapterListener
|
|
{
|
|
virtual ~DxgiAdapterListener() = default;
|
|
virtual void adapterCreated (DxgiAdapter::Ptr adapter) = 0;
|
|
virtual void adapterRemoved (DxgiAdapter::Ptr adapter) = 0;
|
|
};
|
|
|
|
class DxgiAdapters
|
|
{
|
|
public:
|
|
explicit DxgiAdapters (ComSmartPtr<ID2D1Factory2> d2dFactoryIn);
|
|
|
|
~DxgiAdapters();
|
|
|
|
void updateAdapters();
|
|
|
|
void releaseAdapters();
|
|
|
|
const auto& getAdapterArray() const
|
|
{
|
|
return adapterArray;
|
|
}
|
|
|
|
auto getFactory() const
|
|
{
|
|
return factory;
|
|
}
|
|
|
|
DxgiAdapter::Ptr getAdapterForHwnd (HWND hwnd) const;
|
|
|
|
DxgiAdapter::Ptr getDefaultAdapter() const;
|
|
|
|
void addListener (DxgiAdapterListener& l);
|
|
void removeListener (DxgiAdapterListener& l);
|
|
|
|
private:
|
|
static ComSmartPtr<IDXGIFactory2> makeDxgiFactory();
|
|
|
|
ComSmartPtr<ID2D1Factory2> d2dFactory;
|
|
|
|
// It's possible that we'll need to add/remove listeners from background threads, especially in
|
|
// the case that Images are created on a background thread.
|
|
ThreadSafeListenerList<DxgiAdapterListener> listeners;
|
|
ComSmartPtr<IDXGIFactory2> factory = makeDxgiFactory();
|
|
ReferenceCountedArray<DxgiAdapter> adapterArray;
|
|
};
|
|
|
|
class DirectX
|
|
{
|
|
public:
|
|
DirectX();
|
|
|
|
auto getD2DFactory() const { return d2dSharedFactory; }
|
|
|
|
private:
|
|
ComSmartPtr<ID2D1Factory2> d2dSharedFactory;
|
|
|
|
public:
|
|
DxgiAdapters adapters { d2dSharedFactory };
|
|
|
|
private:
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectX)
|
|
};
|
|
|
|
struct D2DUtilities
|
|
{
|
|
template <typename Type>
|
|
static D2D1_RECT_F toRECT_F (const Rectangle<Type>& r)
|
|
{
|
|
return { (float) r.getX(), (float) r.getY(), (float) r.getRight(), (float) r.getBottom() };
|
|
}
|
|
|
|
template <typename Type>
|
|
static D2D1_RECT_U toRECT_U (const Rectangle<Type>& r)
|
|
{
|
|
return { (UINT32) r.getX(), (UINT32) r.getY(), (UINT32) r.getRight(), (UINT32) r.getBottom() };
|
|
}
|
|
|
|
template <typename Type>
|
|
static RECT toRECT (const Rectangle<Type>& r)
|
|
{
|
|
return { r.getX(), r.getY(), r.getRight(), r.getBottom() };
|
|
}
|
|
|
|
static Rectangle<int> toRectangle (const RECT& r)
|
|
{
|
|
return Rectangle<int>::leftTopRightBottom (r.left, r.top, r.right, r.bottom);
|
|
}
|
|
|
|
static Point<int> toPoint (POINT p) noexcept { return { p.x, p.y }; }
|
|
static POINT toPOINT (Point<int> p) noexcept { return { p.x, p.y }; }
|
|
|
|
static D2D1_POINT_2U toPOINT_2U (Point<int> p) { return D2D1::Point2U ((UINT32) p.x, (UINT32) p.y); }
|
|
|
|
static D2D1_COLOR_F toCOLOR_F (Colour c)
|
|
{
|
|
return { c.getFloatRed(), c.getFloatGreen(), c.getFloatBlue(), c.getFloatAlpha() };
|
|
}
|
|
|
|
static D2D1::Matrix3x2F transformToMatrix (const AffineTransform& t)
|
|
{
|
|
return { t.mat00, t.mat10, t.mat01, t.mat11, t.mat02, t.mat12 };
|
|
}
|
|
|
|
static AffineTransform matrixToTransform (const D2D1_MATRIX_3X2_F& m)
|
|
{
|
|
return { m._11, m._21, m._31, m._12, m._22, m._32 };
|
|
}
|
|
|
|
static Rectangle<int> rectFromSize (D2D1_SIZE_U s)
|
|
{
|
|
return { (int) s.width, (int) s.height };
|
|
}
|
|
|
|
static ComSmartPtr<ID2D1Device1> getDeviceForContext (ComSmartPtr<ID2D1DeviceContext1> context);
|
|
};
|
|
|
|
//==============================================================================
|
|
struct Direct2DDeviceContext
|
|
{
|
|
static ComSmartPtr<ID2D1DeviceContext1> create (ComSmartPtr<ID2D1Device1> device);
|
|
|
|
static ComSmartPtr<ID2D1DeviceContext1> create (DxgiAdapter::Ptr adapter)
|
|
{
|
|
return adapter != nullptr ? create (adapter->direct2DDevice) : nullptr;
|
|
}
|
|
|
|
Direct2DDeviceContext() = delete;
|
|
};
|
|
|
|
//==============================================================================
|
|
struct Direct2DBitmap
|
|
{
|
|
Direct2DBitmap() = delete;
|
|
|
|
static ComSmartPtr<ID2D1Bitmap1> toBitmap (const Image& image,
|
|
ComSmartPtr<ID2D1DeviceContext1> deviceContext,
|
|
Image::PixelFormat outputFormat);
|
|
|
|
static ComSmartPtr<ID2D1Bitmap1> createBitmap (ComSmartPtr<ID2D1DeviceContext1> deviceContext,
|
|
Image::PixelFormat format,
|
|
D2D_SIZE_U size,
|
|
D2D1_BITMAP_OPTIONS options);
|
|
};
|
|
|
|
//==============================================================================
|
|
/* UpdateRegion extracts the invalid region for a window
|
|
UpdateRegion is used to service WM_PAINT to add the invalid region of a window to
|
|
deferredRepaints. UpdateRegion marks the region as valid, and the region should be painted on the
|
|
next vblank.
|
|
This is similar to the invalid region update in HWNDComponentPeer::handlePaintMessage()
|
|
*/
|
|
class UpdateRegion
|
|
{
|
|
public:
|
|
void findRECTAndValidate (HWND windowHandle);
|
|
|
|
void clear()
|
|
{
|
|
numRect = 0;
|
|
}
|
|
|
|
Span<const RECT> getRects() const
|
|
{
|
|
auto header = (RGNDATAHEADER const* const) block.getData();
|
|
auto* data = (RECT*) (header + 1);
|
|
return { data, numRect };
|
|
}
|
|
|
|
private:
|
|
MemoryBlock block { 1024 };
|
|
uint32 numRect = 0;
|
|
};
|
|
|
|
class LinearGradientCache
|
|
{
|
|
public:
|
|
ComSmartPtr<ID2D1LinearGradientBrush> get (const ColourGradient& gradient,
|
|
ComSmartPtr<ID2D1DeviceContext1> deviceContext,
|
|
Direct2DMetrics* metrics);
|
|
|
|
private:
|
|
LruCache<ColourGradient, ComSmartPtr<ID2D1LinearGradientBrush>> cache;
|
|
};
|
|
|
|
class RadialGradientCache
|
|
{
|
|
public:
|
|
ComSmartPtr<ID2D1RadialGradientBrush> get (const ColourGradient& gradient,
|
|
ComSmartPtr<ID2D1DeviceContext1> deviceContext,
|
|
Direct2DMetrics* metrics);
|
|
|
|
private:
|
|
LruCache<ColourGradient, ComSmartPtr<ID2D1RadialGradientBrush>> cache;
|
|
};
|
|
|
|
class RectangleListSpriteBatch
|
|
{
|
|
struct TransformCallback
|
|
{
|
|
virtual Rectangle<float> transform (Rectangle<float>) const = 0;
|
|
};
|
|
|
|
public:
|
|
RectangleListSpriteBatch() = default;
|
|
|
|
~RectangleListSpriteBatch();
|
|
|
|
void release();
|
|
|
|
template <typename TransformRectangle>
|
|
bool fillRectangles (ComSmartPtr<ID2D1DeviceContext1> deviceContext,
|
|
const RectangleList<float>& rectangles,
|
|
const Colour colour,
|
|
TransformRectangle&& transformRectangle,
|
|
[[maybe_unused]] Direct2DMetrics* metrics)
|
|
{
|
|
struct Callback : public TransformCallback
|
|
{
|
|
explicit Callback (TransformRectangle&& x) : fn (x) {}
|
|
Rectangle<float> transform (Rectangle<float> x) const override { return fn (x); }
|
|
TransformRectangle fn;
|
|
};
|
|
|
|
const Callback callback { std::forward<TransformRectangle> (transformRectangle) };
|
|
return fillRectanglesImpl (deviceContext, rectangles, colour, callback, metrics);
|
|
}
|
|
|
|
private:
|
|
ComSmartPtr<ID2D1SpriteBatch> getSpriteBatch (ID2D1DeviceContext3& dc, uint32 key);
|
|
|
|
bool fillRectanglesImpl (ComSmartPtr<ID2D1DeviceContext1> deviceContext,
|
|
const RectangleList<float>& rectangles,
|
|
Colour colour,
|
|
const TransformCallback& transformRectangle,
|
|
[[maybe_unused]] Direct2DMetrics* metrics);
|
|
|
|
static constexpr uint32 rectangleSize = 32;
|
|
ComSmartPtr<ID2D1BitmapRenderTarget> whiteRectangle;
|
|
HeapBlock<D2D1_RECT_F> destinations;
|
|
size_t destinationsCapacity = 0;
|
|
LruCache<uint32, ComSmartPtr<ID2D1SpriteBatch>, 8> spriteBatches;
|
|
};
|
|
|
|
class Direct2DDeviceResources
|
|
{
|
|
public:
|
|
static DxgiAdapter::Ptr findAdapter (const DxgiAdapters& adapters, ID2D1Bitmap1* bitmap);
|
|
static DxgiAdapter::Ptr findAdapter (const DxgiAdapters& dxgiAdapters, IDXGIDevice* dxgiDevice);
|
|
static DxgiAdapter::Ptr findAdapter (const DxgiAdapters& dxgiAdapters, ID2D1DeviceContext1* context);
|
|
|
|
static LUID getLUID (ComSmartPtr<IDXGIAdapter1> adapter);
|
|
|
|
static std::optional<Direct2DDeviceResources> create (ComSmartPtr<ID2D1DeviceContext1> context);
|
|
|
|
ComSmartPtr<ID2D1SolidColorBrush> colourBrush;
|
|
LinearGradientCache linearGradientCache;
|
|
RadialGradientCache radialGradientCache;
|
|
std::unique_ptr<RectangleListSpriteBatch> rectangleListSpriteBatch;
|
|
|
|
private:
|
|
Direct2DDeviceResources() = default;
|
|
};
|
|
|
|
//==============================================================================
|
|
class AggregateFontCollection
|
|
{
|
|
public:
|
|
explicit AggregateFontCollection (ComSmartPtr<IDWriteFontCollection> baseCollection);
|
|
|
|
StringArray findAllTypefaceNames();
|
|
|
|
static std::vector<ComSmartPtr<IDWriteFont>> getAllFontsInFamily (IDWriteFontFamily* fontFamily);
|
|
|
|
StringArray findAllTypefaceStyles (const String& family);
|
|
|
|
ComSmartPtr<IDWriteFontFamily> getFamilyByName (const wchar_t* name);
|
|
|
|
ComSmartPtr<IDWriteFont> findFontForFace (IDWriteFontFace* face);
|
|
|
|
void addCollection (ComSmartPtr<IDWriteFontCollection> collection);
|
|
|
|
void removeCollection (ComSmartPtr<IDWriteFontCollection> collection);
|
|
|
|
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;
|
|
|
|
private:
|
|
std::vector<ComSmartPtr<IDWriteFontCollection>> collections;
|
|
std::mutex mutex;
|
|
};
|
|
|
|
class DirectWriteCustomFontCollectionLoader final : public ComBaseClassHelper<IDWriteFontCollectionLoader>
|
|
{
|
|
public:
|
|
explicit DirectWriteCustomFontCollectionLoader (IDWriteFactory& factoryIn);
|
|
|
|
~DirectWriteCustomFontCollectionLoader() override;
|
|
|
|
Uuid addRawFontData (Span<const std::byte> blob);
|
|
|
|
HRESULT WINAPI CreateEnumeratorFromKey (IDWriteFactory* factoryIn,
|
|
const void* collectionKey,
|
|
UINT32 collectionKeySize,
|
|
IDWriteFontFileEnumerator** fontFileEnumerator) noexcept override;
|
|
|
|
private:
|
|
class MemoryFontFileLoader;
|
|
class FontFileEnumerator;
|
|
|
|
ComSmartPtr<MemoryFontFileLoader> findLoaderForUuid (const Uuid&) const;
|
|
|
|
IDWriteFactory& factory;
|
|
|
|
// Allows lookup of Uuids of all loaders with data matching a particular hash
|
|
std::map<size_t, std::vector<Uuid>> uuidsForHash;
|
|
|
|
// Sorted by Uuid
|
|
std::vector<ComSmartPtr<MemoryFontFileLoader>> loaders;
|
|
};
|
|
|
|
//==============================================================================
|
|
class Direct2DFactories
|
|
{
|
|
public:
|
|
Direct2DFactories();
|
|
|
|
~Direct2DFactories();
|
|
|
|
[[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;
|
|
const ComSmartPtr<IDWriteFactory> directWriteFactory;
|
|
const ComSmartPtr<DirectWriteCustomFontCollectionLoader> collectionLoader;
|
|
const ComSmartPtr<IDWriteFactory4> directWriteFactory4 = directWriteFactory.getInterface<IDWriteFactory4>();
|
|
|
|
std::optional<AggregateFontCollection> fonts;
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Direct2DFactories)
|
|
};
|
|
|
|
} // namespace juce
|