/* ============================================================================== 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> positions, float scale); auto* getAdvances() const { return advances.data(); } auto* getOffsets() const { return offsets .data(); } private: std::vector advances; std::vector offsets; }; struct DxgiAdapter : public ReferenceCountedObject { using Ptr = ReferenceCountedObjectPtr; static Ptr create (ComSmartPtr, ComSmartPtr); bool uniqueIDMatches (ReferenceCountedObjectPtr other) const; LUID getAdapterUniqueID() const; ComSmartPtr direct3DDevice; ComSmartPtr dxgiDevice; ComSmartPtr direct2DDevice; ComSmartPtr dxgiAdapter; std::vector> 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 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 makeDxgiFactory(); ComSmartPtr 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 listeners; ComSmartPtr factory = makeDxgiFactory(); ReferenceCountedArray adapterArray; }; class DirectX { public: DirectX(); auto getD2DFactory() const { return d2dSharedFactory; } private: ComSmartPtr d2dSharedFactory; public: DxgiAdapters adapters { d2dSharedFactory }; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectX) }; struct D2DUtilities { template static D2D1_RECT_F toRECT_F (const Rectangle& r) { return { (float) r.getX(), (float) r.getY(), (float) r.getRight(), (float) r.getBottom() }; } template static D2D1_RECT_U toRECT_U (const Rectangle& r) { return { (UINT32) r.getX(), (UINT32) r.getY(), (UINT32) r.getRight(), (UINT32) r.getBottom() }; } template static RECT toRECT (const Rectangle& r) { return { r.getX(), r.getY(), r.getRight(), r.getBottom() }; } static Rectangle toRectangle (const RECT& r) { return Rectangle::leftTopRightBottom (r.left, r.top, r.right, r.bottom); } static Point toPoint (POINT p) noexcept { return { p.x, p.y }; } static POINT toPOINT (Point p) noexcept { return { p.x, p.y }; } static D2D1_POINT_2U toPOINT_2U (Point 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 rectFromSize (D2D1_SIZE_U s) { return { (int) s.width, (int) s.height }; } static ComSmartPtr getDeviceForContext (ComSmartPtr context); }; //============================================================================== struct Direct2DDeviceContext { static ComSmartPtr create (ComSmartPtr device); static ComSmartPtr create (DxgiAdapter::Ptr adapter) { return adapter != nullptr ? create (adapter->direct2DDevice) : nullptr; } Direct2DDeviceContext() = delete; }; //============================================================================== struct Direct2DBitmap { Direct2DBitmap() = delete; static ComSmartPtr toBitmap (const Image& image, ComSmartPtr deviceContext, Image::PixelFormat outputFormat); static ComSmartPtr createBitmap (ComSmartPtr 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 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 get (const ColourGradient& gradient, ComSmartPtr deviceContext, Direct2DMetrics* metrics); private: LruCache> cache; }; class RadialGradientCache { public: ComSmartPtr get (const ColourGradient& gradient, ComSmartPtr deviceContext, Direct2DMetrics* metrics); private: LruCache> cache; }; class RectangleListSpriteBatch { struct TransformCallback { virtual Rectangle transform (Rectangle) const = 0; }; public: RectangleListSpriteBatch() = default; ~RectangleListSpriteBatch(); void release(); template bool fillRectangles (ComSmartPtr deviceContext, const RectangleList& rectangles, const Colour colour, TransformRectangle&& transformRectangle, [[maybe_unused]] Direct2DMetrics* metrics) { struct Callback : public TransformCallback { explicit Callback (TransformRectangle&& x) : fn (x) {} Rectangle transform (Rectangle x) const override { return fn (x); } TransformRectangle fn; }; const Callback callback { std::forward (transformRectangle) }; return fillRectanglesImpl (deviceContext, rectangles, colour, callback, metrics); } private: ComSmartPtr getSpriteBatch (ID2D1DeviceContext3& dc, uint32 key); bool fillRectanglesImpl (ComSmartPtr deviceContext, const RectangleList& rectangles, Colour colour, const TransformCallback& transformRectangle, [[maybe_unused]] Direct2DMetrics* metrics); static constexpr uint32 rectangleSize = 32; ComSmartPtr whiteRectangle; HeapBlock destinations; size_t destinationsCapacity = 0; LruCache, 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 adapter); static std::optional create (ComSmartPtr context); ComSmartPtr colourBrush; LinearGradientCache linearGradientCache; RadialGradientCache radialGradientCache; std::unique_ptr rectangleListSpriteBatch; private: Direct2DDeviceResources() = default; }; //============================================================================== class AggregateFontCollection { public: explicit AggregateFontCollection (ComSmartPtr baseCollection); StringArray findAllTypefaceNames(); static std::vector> getAllFontsInFamily (IDWriteFontFamily* fontFamily); StringArray findAllTypefaceStyles (const String& family); ComSmartPtr getFamilyByName (const wchar_t* name); ComSmartPtr findFontForFace (IDWriteFontFace* face); void addCollection (ComSmartPtr collection); void removeCollection (ComSmartPtr collection); struct MapResult { ComSmartPtr 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> collections; std::mutex mutex; }; class DirectWriteCustomFontCollectionLoader final : public ComBaseClassHelper { public: explicit DirectWriteCustomFontCollectionLoader (IDWriteFactory& factoryIn); ~DirectWriteCustomFontCollectionLoader() override; Uuid addRawFontData (Span blob); HRESULT WINAPI CreateEnumeratorFromKey (IDWriteFactory* factoryIn, const void* collectionKey, UINT32 collectionKeySize, IDWriteFontFileEnumerator** fontFileEnumerator) noexcept override; private: class MemoryFontFileLoader; class FontFileEnumerator; ComSmartPtr findLoaderForUuid (const Uuid&) const; IDWriteFactory& factory; // Allows lookup of Uuids of all loaders with data matching a particular hash std::map> uuidsForHash; // Sorted by Uuid std::vector> loaders; }; //============================================================================== class Direct2DFactories { public: Direct2DFactories(); ~Direct2DFactories(); [[nodiscard]] ComSmartPtr getDWriteFactory() const { return directWriteFactory; } [[nodiscard]] ComSmartPtr getDWriteFactory4() const { return directWriteFactory4; } [[nodiscard]] AggregateFontCollection& getFonts() { jassert (fonts.has_value()); return *fonts; } [[nodiscard]] ComSmartPtr getCollectionLoader() const { return collectionLoader; } private: DynamicLibrary direct2dDll { "d2d1.dll" }, directWriteDll { "DWrite.dll" }; const ComSmartPtr d2dFactory; const ComSmartPtr directWriteFactory; const ComSmartPtr collectionLoader; const ComSmartPtr directWriteFactory4 = directWriteFactory.getInterface(); std::optional fonts; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Direct2DFactories) }; } // namespace juce