From 98b255244f7551b5ef7c5d0c4b09db85b7f75f7f Mon Sep 17 00:00:00 2001 From: scheffle Date: Thu, 12 Jan 2023 15:08:08 +0100 Subject: [PATCH] Revert "Merge branch 'develop' into master" This reverts commit b9546b534f5f7e60649da08eda77d60641b53a18, reversing changes made to c24309a74bf9899f299bc80dbad914cd9d86236d. --- vstgui/contrib/datepicker.h | 54 - vstgui/contrib/datepicker.mm | 196 ---- vstgui/contrib/datepicker_win32.cpp | 132 --- vstgui/contrib/externalview_direct3d12.h | 481 --------- vstgui/contrib/externalview_hwnd.h | 223 ---- vstgui/contrib/externalview_metal.h | 276 ----- vstgui/contrib/externalview_nsview.h | 225 ----- vstgui/doxygen/page_changes.h | 10 +- vstgui/lib/CMakeLists.txt | 17 +- vstgui/lib/cdatabrowser.cpp | 1 - vstgui/lib/cdrawcontext.cpp | 523 ++-------- vstgui/lib/cdrawcontext.h | 162 +-- vstgui/lib/cexternalview.cpp | 189 ---- vstgui/lib/cexternalview.h | 52 - vstgui/lib/cfont.h | 12 + vstgui/lib/cframe.cpp | 9 +- vstgui/lib/cframe.h | 3 +- vstgui/lib/cgraphicspath.cpp | 2 - vstgui/lib/clayeredviewcontainer.cpp | 30 +- vstgui/lib/clayeredviewcontainer.h | 3 +- vstgui/lib/coffscreencontext.cpp | 27 +- vstgui/lib/coffscreencontext.h | 3 - vstgui/lib/controls/cfontchooser.cpp | 1 - vstgui/lib/controls/csearchtextedit.cpp | 1 - vstgui/lib/controls/ctextedit.cpp | 1 - vstgui/lib/controls/ctextlabel.cpp | 6 +- vstgui/lib/iexternalview.h | 121 --- .../lib/platform/common/genericoptionmenu.cpp | 1 - .../lib/platform/common/generictextedit.cpp | 1 - vstgui/lib/platform/iplatformfont.h | 7 +- vstgui/lib/platform/iplatformframecallback.h | 5 +- vstgui/lib/platform/iplatformgraphicsdevice.h | 124 --- vstgui/lib/platform/iplatformtextedit.h | 2 +- vstgui/lib/platform/iplatformviewlayer.h | 6 +- vstgui/lib/platform/linux/cairocontext.cpp | 562 +++++++++++ vstgui/lib/platform/linux/cairocontext.h | 115 +++ vstgui/lib/platform/linux/cairofont.cpp | 178 ++-- vstgui/lib/platform/linux/cairofont.h | 8 +- vstgui/lib/platform/linux/cairogradient.cpp | 5 +- vstgui/lib/platform/linux/cairogradient.h | 10 +- .../platform/linux/cairographicscontext.cpp | 712 ------------- .../lib/platform/linux/cairographicscontext.h | 107 -- vstgui/lib/platform/linux/cairopath.cpp | 17 +- vstgui/lib/platform/linux/cairopath.h | 3 +- vstgui/lib/platform/linux/linuxfactory.cpp | 29 +- vstgui/lib/platform/linux/linuxfactory.h | 16 +- vstgui/lib/platform/linux/x11frame.cpp | 54 +- vstgui/lib/platform/mac/caviewlayer.h | 27 +- vstgui/lib/platform/mac/caviewlayer.mm | 117 +-- vstgui/lib/platform/mac/cfontmac.h | 9 +- vstgui/lib/platform/mac/cfontmac.mm | 115 ++- vstgui/lib/platform/mac/cgdrawcontext.cpp | 953 +++++++++++++++++ vstgui/lib/platform/mac/cgdrawcontext.h | 89 ++ .../lib/platform/mac/cocoa/cocoatextedit.mm | 3 + vstgui/lib/platform/mac/cocoa/nsviewframe.h | 8 +- vstgui/lib/platform/mac/cocoa/nsviewframe.mm | 171 ++-- .../platform/mac/coregraphicsdevicecontext.h | 115 --- .../platform/mac/coregraphicsdevicecontext.mm | 955 ------------------ vstgui/lib/platform/mac/ios/uiviewframe.mm | 16 +- vstgui/lib/platform/mac/macfactory.h | 14 +- vstgui/lib/platform/mac/macfactory.mm | 21 +- vstgui/lib/platform/mac/mactimer.cpp | 2 +- .../lib/platform/mac/quartzgraphicspath.cpp | 1 + vstgui/lib/platform/platformfactory.h | 15 +- vstgui/lib/platform/win32/direct2d/d2d.h | 80 -- .../win32/direct2d/d2ddrawcontext.cpp | 879 ++++++++++++++++ .../platform/win32/direct2d/d2ddrawcontext.h | 166 +++ .../lib/platform/win32/direct2d/d2dfont.cpp | 119 ++- vstgui/lib/platform/win32/direct2d/d2dfont.h | 6 +- .../platform/win32/direct2d/d2dgradient.cpp | 4 +- .../win32/direct2d/d2dgraphicscontext.cpp | 853 ---------------- .../win32/direct2d/d2dgraphicscontext.h | 116 --- .../win32/direct2d/d2dgraphicspath.cpp | 178 +++- .../platform/win32/direct2d/d2dgraphicspath.h | 2 + .../platform/win32/win32directcomposition.cpp | 6 - .../platform/win32/win32directcomposition.h | 3 - vstgui/lib/platform/win32/win32dragging.cpp | 24 +- vstgui/lib/platform/win32/win32factory.cpp | 79 +- vstgui/lib/platform/win32/win32factory.h | 19 +- vstgui/lib/platform/win32/win32frame.cpp | 84 +- vstgui/lib/platform/win32/win32frame.h | 3 +- vstgui/lib/platform/win32/win32support.cpp | 13 + vstgui/lib/platform/win32/win32support.h | 1 + vstgui/lib/platform/win32/win32viewlayer.cpp | 26 +- vstgui/lib/platform/win32/win32viewlayer.h | 1 + vstgui/lib/vstguifwd.h | 23 - .../examples/standalone/CMakeLists.txt | 33 - .../standalone/resource/direct3dwindow.uidesc | 67 -- .../standalone/resource/metalwindow.uidesc | 68 -- .../examples/standalone/resource/test.uidesc | 208 ++-- .../standalone/source/direct3dshader.h | 37 - .../standalone/source/direct3dwindow.cpp | 696 ------------- .../standalone/source/direct3dwindow.h | 17 - .../examples/standalone/source/metalshader.h | 78 -- .../examples/standalone/source/metaltypes.h | 24 - .../examples/standalone/source/metalwindow.h | 17 - .../examples/standalone/source/metalwindow.mm | 246 ----- .../standalone/source/testappdelegate.cpp | 129 +-- .../source/platform/win32/win32window.cpp | 1 + .../editing/uibitmapscontroller.cpp | 1 - vstgui/uidescription/editing/uieditview.cpp | 98 +- vstgui/uidescription/editing/uieditview.h | 4 - .../editing/uigradientscontroller.cpp | 1 - vstgui/uidescription/editing/uiselection.cpp | 11 +- vstgui/vstgui.cpp | 1 - vstgui/vstgui_ios.mm | 2 +- vstgui/vstgui_linux.cpp | 2 +- vstgui/vstgui_mac.mm | 2 +- vstgui/vstgui_win32.cpp | 4 +- 109 files changed, 3933 insertions(+), 7852 deletions(-) delete mode 100644 vstgui/contrib/datepicker.h delete mode 100644 vstgui/contrib/datepicker.mm delete mode 100644 vstgui/contrib/datepicker_win32.cpp delete mode 100644 vstgui/contrib/externalview_direct3d12.h delete mode 100644 vstgui/contrib/externalview_hwnd.h delete mode 100644 vstgui/contrib/externalview_metal.h delete mode 100644 vstgui/contrib/externalview_nsview.h delete mode 100644 vstgui/lib/cexternalview.cpp delete mode 100644 vstgui/lib/cexternalview.h delete mode 100644 vstgui/lib/iexternalview.h delete mode 100644 vstgui/lib/platform/iplatformgraphicsdevice.h create mode 100644 vstgui/lib/platform/linux/cairocontext.cpp create mode 100644 vstgui/lib/platform/linux/cairocontext.h delete mode 100644 vstgui/lib/platform/linux/cairographicscontext.cpp delete mode 100644 vstgui/lib/platform/linux/cairographicscontext.h create mode 100644 vstgui/lib/platform/mac/cgdrawcontext.cpp create mode 100644 vstgui/lib/platform/mac/cgdrawcontext.h delete mode 100644 vstgui/lib/platform/mac/coregraphicsdevicecontext.h delete mode 100644 vstgui/lib/platform/mac/coregraphicsdevicecontext.mm delete mode 100644 vstgui/lib/platform/win32/direct2d/d2d.h create mode 100644 vstgui/lib/platform/win32/direct2d/d2ddrawcontext.cpp create mode 100644 vstgui/lib/platform/win32/direct2d/d2ddrawcontext.h delete mode 100644 vstgui/lib/platform/win32/direct2d/d2dgraphicscontext.cpp delete mode 100644 vstgui/lib/platform/win32/direct2d/d2dgraphicscontext.h delete mode 100644 vstgui/standalone/examples/standalone/resource/direct3dwindow.uidesc delete mode 100644 vstgui/standalone/examples/standalone/resource/metalwindow.uidesc delete mode 100644 vstgui/standalone/examples/standalone/source/direct3dshader.h delete mode 100644 vstgui/standalone/examples/standalone/source/direct3dwindow.cpp delete mode 100644 vstgui/standalone/examples/standalone/source/direct3dwindow.h delete mode 100644 vstgui/standalone/examples/standalone/source/metalshader.h delete mode 100644 vstgui/standalone/examples/standalone/source/metaltypes.h delete mode 100644 vstgui/standalone/examples/standalone/source/metalwindow.h delete mode 100644 vstgui/standalone/examples/standalone/source/metalwindow.mm diff --git a/vstgui/contrib/datepicker.h b/vstgui/contrib/datepicker.h deleted file mode 100644 index 7d6346e7d..000000000 --- a/vstgui/contrib/datepicker.h +++ /dev/null @@ -1,54 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#pragma once - -#include "../lib/iexternalview.h" -#include -#include - -//------------------------------------------------------------------------ -namespace VSTGUI { -namespace ExternalView { - -//------------------------------------------------------------------------ -class DatePicker : public ViewAdapter -{ -public: - DatePicker (); - ~DatePicker () noexcept; - - struct Date - { - int32_t day {0}; - int32_t month {0}; - int32_t year {0}; - }; - void setDate (Date date); - - using ChangeCallback = std::function; - void setChangeCallback (const ChangeCallback& callback); - -private: - bool platformViewTypeSupported (PlatformViewType type) override; - bool attach (void* parent, PlatformViewType parentViewType) override; - bool remove () override; - - void setViewSize (IntRect frame, IntRect visible) override; - void setContentScaleFactor (double scaleFactor) override; - - void setMouseEnabled (bool state) override; - - void takeFocus () override; - void looseFocus () override; - - void setTookFocusCallback (const TookFocusCallback& callback) override; - - struct Impl; - std::unique_ptr impl; -}; - -//------------------------------------------------------------------------ -} // ExternalView -} // VSTGUI diff --git a/vstgui/contrib/datepicker.mm b/vstgui/contrib/datepicker.mm deleted file mode 100644 index aa1a4a245..000000000 --- a/vstgui/contrib/datepicker.mm +++ /dev/null @@ -1,196 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#import "datepicker.h" -#import "externalview_nsview.h" - -//------------------------------------------------------------------------ -namespace VSTGUI { -namespace ExternalView { - -//------------------------------------------------------------------------ -struct DatePickerDelegate : RuntimeObjCClass -{ - using DoneCallback = std::function; - using ValidateCallback = std::function; - - static constexpr const auto DoneCallbackVarName = "DoneCallback"; - static constexpr const auto ValidateCallbackVarName = "ValidateCallback"; - - static id allocAndInit (DoneCallback&& doneCallback, ValidateCallback&& callback) - { - id obj = Base::alloc (); - initWithCallbacks (obj, std::move (doneCallback), std::move (callback)); - return obj; - } - - static Class CreateClass () - { - return ObjCClassBuilder () - .init ("DatePickerDelegate", [NSObject class]) - .addProtocol ("NSDatePickerCellDelegate") - .addMethod (@selector (datePickerCell:validateProposedDateValue:timeInterval:), - validate) - .addMethod (@selector (complete:), complete) - .addIvar (ValidateCallbackVarName) - .addIvar (DoneCallbackVarName) - .finalize (); - } - - static id initWithCallbacks (id self, DoneCallback&& doneCallback, ValidateCallback&& callback) - { - if ((self = makeInstance (self).callSuper (@selector (init)))) - { - auto instance = makeInstance (self); - if (auto var = instance.getVariable (DoneCallbackVarName)) - var->set (doneCallback); - if (auto var = instance.getVariable (ValidateCallbackVarName)) - var->set (callback); - } - return self; - } - - static void complete (id self, SEL cmd, id sender) - { - if (auto var = makeInstance (self).getVariable (DoneCallbackVarName)) - { - const auto& callback = var->get (); - if (callback) - callback (); - } - } - - static void validate (id self, SEL cmd, NSDatePickerCell* datePickerCell, - NSDate* _Nonnull* _Nonnull proposedDateValue, - NSTimeInterval* _Nullable proposedTimeInterval) - { - if (auto var = makeInstance (self).getVariable (ValidateCallbackVarName)) - { - const auto& callback = var->get (); - if (callback) - callback (proposedDateValue, proposedTimeInterval); - } - } -}; - -//------------------------------------------------------------------------ -struct DatePicker::Impl : ExternalNSViewBase -{ - using Base::Base; - - id delegate {nil}; - ChangeCallback changeCallback; - -#if !__has_feature(objc_arc) - ~Impl () noexcept - { - if (delegate) - [delegate release]; - } -#endif -}; - -//------------------------------------------------------------------------ -DatePicker::DatePicker () -{ - impl = std::make_unique ([[NSDatePicker alloc] initWithFrame: {0., 0., 10., 10.}]); - impl->view.datePickerStyle = NSDatePickerStyleTextField; - impl->view.datePickerMode = NSDatePickerModeSingle; - impl->view.datePickerElements = NSDatePickerElementFlagYearMonthDay; - if (@available (macOS 10.15.4, *)) - impl->view.presentsCalendarOverlay = YES; - impl->view.dateValue = [NSDate date]; - impl->view.calendar = [NSCalendar currentCalendar]; - [impl->container addSubview:impl->view]; - - impl->delegate = DatePickerDelegate::allocAndInit ( - [impl = impl.get ()] () { - if (impl->changeCallback) - { - auto dateValue = impl->view.dateValue; - auto calendar = impl->view.calendar; - auto components = [calendar - components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay - fromDate:dateValue]; - Date date; - date.day = static_cast (components.day); - date.month = static_cast (components.month); - date.year = static_cast (components.year); - impl->changeCallback (date); - } - }, - [] (NSDate** date, NSTimeInterval* time) { - // TODO: add validation mechanism - }); - impl->view.delegate = impl->delegate; - impl->view.target = impl->delegate; - impl->view.action = @selector (complete:); -} - -//------------------------------------------------------------------------ -DatePicker::~DatePicker () noexcept {} - -//------------------------------------------------------------------------ -void DatePicker::setDate (Date date) -{ - auto calendar = impl->view.calendar; - auto dateComponents = [NSDateComponents new]; - dateComponents.calendar = calendar; - dateComponents.day = date.day; - dateComponents.month = date.month; - dateComponents.year = date.year; - impl->view.dateValue = [calendar dateFromComponents:dateComponents]; -} - -//------------------------------------------------------------------------ -void DatePicker::setChangeCallback (const ChangeCallback& callback) -{ - impl->changeCallback = callback; -} - -//------------------------------------------------------------------------ -bool DatePicker::platformViewTypeSupported (PlatformViewType type) -{ - return impl->platformViewTypeSupported (type); -} - -//------------------------------------------------------------------------ -bool DatePicker::attach (void* parent, PlatformViewType parentViewType) -{ - return impl->attach (parent, parentViewType); -} - -//------------------------------------------------------------------------ -bool DatePicker::remove () { return impl->remove (); } - -//------------------------------------------------------------------------ -void DatePicker::setViewSize (IntRect frame, IntRect visible) -{ - impl->setViewSize (frame, visible); -} - -//------------------------------------------------------------------------ -void DatePicker::setContentScaleFactor (double scaleFactor) -{ - impl->setContentScaleFactor (scaleFactor); -} - -//------------------------------------------------------------------------ -void DatePicker::setMouseEnabled (bool state) { impl->setMouseEnabled (state); } - -//------------------------------------------------------------------------ -void DatePicker::takeFocus () { impl->takeFocus (); } - -//------------------------------------------------------------------------ -void DatePicker::looseFocus () { impl->looseFocus (); } - -//------------------------------------------------------------------------ -void DatePicker::setTookFocusCallback (const TookFocusCallback& callback) -{ - impl->setTookFocusCallback (callback); -} - -//------------------------------------------------------------------------ -} // ExternalView -} // VSTGUI diff --git a/vstgui/contrib/datepicker_win32.cpp b/vstgui/contrib/datepicker_win32.cpp deleted file mode 100644 index 8dda2c060..000000000 --- a/vstgui/contrib/datepicker_win32.cpp +++ /dev/null @@ -1,132 +0,0 @@ - -#include "datepicker.h" -#include "externalview_hwnd.h" -#include "vstgui/lib/platform/win32/win32factory.h" - -#include - -//------------------------------------------------------------------------ -namespace VSTGUI { -namespace ExternalView { - -//------------------------------------------------------------------------ -struct DatePicker::Impl : ExternalHWNDBase -{ - using Base::Base; - - ~Impl () noexcept - { - if (font) - DeleteObject (font); - } - - ChangeCallback changeCallback; - HFONT font {nullptr}; -}; - -//------------------------------------------------------------------------ -DatePicker::DatePicker () -{ - auto hInstance = getPlatformFactory ().asWin32Factory ()->getInstance (); - impl = std::make_unique (hInstance); - impl->child = CreateWindowExW (0, DATETIMEPICK_CLASS, TEXT ("DateTime"), - WS_BORDER | WS_CHILD | WS_VISIBLE | DTS_SHORTDATEFORMAT, 0, 0, - 80, 20, impl->container.getHWND (), NULL, hInstance, NULL); - impl->container.setWindowProc ([this] (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { - switch (message) - { - case WM_NOTIFY: - { - LPNMHDR hdr = reinterpret_cast (lParam); - switch (hdr->code) - { - case DTN_DATETIMECHANGE: - { - LPNMDATETIMECHANGE lpChange = reinterpret_cast (lParam); - if (impl->changeCallback) - { - Date date; - date.day = lpChange->st.wDay; - date.month = lpChange->st.wMonth; - date.year = lpChange->st.wYear; - impl->changeCallback (date); - } - break; - } - } - break; - } - } - return DefWindowProc (hwnd, message, wParam, lParam); - }); -} - -//------------------------------------------------------------------------ -DatePicker::~DatePicker () noexcept {} - -//------------------------------------------------------------------------ -void DatePicker::setDate (Date date) -{ - SYSTEMTIME st = {}; - st.wDay = date.day; - st.wMonth = date.month; - st.wYear = date.year; - DateTime_SetSystemtime (impl->child, GDT_VALID, &st); -} - -//------------------------------------------------------------------------ -void DatePicker::setChangeCallback (const ChangeCallback& callback) -{ - impl->changeCallback = callback; -} - -//------------------------------------------------------------------------ -bool DatePicker::platformViewTypeSupported (PlatformViewType type) -{ - return impl->platformViewTypeSupported (type); -} - -//------------------------------------------------------------------------ -bool DatePicker::attach (void* parent, PlatformViewType parentViewType) -{ - return impl->attach (parent, parentViewType); -} - -//------------------------------------------------------------------------ -bool DatePicker::remove () { return impl->remove (); } - -//------------------------------------------------------------------------ -void DatePicker::setViewSize (IntRect frame, IntRect visible) -{ - impl->setViewSize (frame, visible); -} - -//------------------------------------------------------------------------ -void DatePicker::setContentScaleFactor (double scaleFactor) -{ - if (impl->font) - DeleteObject (impl->font); - auto logFont = NonClientMetrics::get ().lfCaptionFont; - logFont.lfHeight = static_cast (std::round (logFont.lfHeight * scaleFactor)); - impl->font = CreateFontIndirect (&logFont); - if (impl->font) - SendMessage (impl->child, WM_SETFONT, (WPARAM)impl->font, 0); -} - -//------------------------------------------------------------------------ -void DatePicker::setMouseEnabled (bool state) { impl->setMouseEnabled (state); } - -//------------------------------------------------------------------------ -void DatePicker::takeFocus () { impl->takeFocus (); } - -//------------------------------------------------------------------------ -void DatePicker::looseFocus () { impl->looseFocus (); } - -void DatePicker::setTookFocusCallback (const TookFocusCallback& callback) -{ - impl->setTookFocusCallback (callback); -} - -//------------------------------------------------------------------------ -} // ExternalView -} // VSTGUI diff --git a/vstgui/contrib/externalview_direct3d12.h b/vstgui/contrib/externalview_direct3d12.h deleted file mode 100644 index 2102146ac..000000000 --- a/vstgui/contrib/externalview_direct3d12.h +++ /dev/null @@ -1,481 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#pragma once - -#include "externalview_hwnd.h" - -#include -#include -#include -#include -#include - -#include - -#ifdef _MSC_VER -#pragma comment(lib, "dcomp.lib") -#pragma comment(lib, "d3d12.lib") -#pragma comment(lib, "dxgi.lib") -#endif - -//------------------------------------------------------------------------ -namespace VSTGUI { -//------------------------------------------------------------------------ - -//------------------------------------------------------------------------ -struct Win32Exception : std::exception -{ - explicit Win32Exception (HRESULT hr) : _hr (hr) - { - FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, hr, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&_errorStr, 0, - NULL); - } - - ~Win32Exception () noexcept - { - if (_errorStr) - LocalFree ((HLOCAL)_errorStr); - } - - const char* what () const noexcept override { return _errorStr; } - - HRESULT hr () const noexcept { return _hr; } - -private: - HRESULT _hr; - char* _errorStr {nullptr}; -}; - -inline void ThrowIfFailed (HRESULT hr) -{ - if (FAILED (hr)) - { - throw Win32Exception (hr); - } -} - -//------------------------------------------------------------------------ -namespace ExternalView { - -//------------------------------------------------------------------------ -struct IDirect3D12View -{ - virtual ~IDirect3D12View () noexcept = default; - - virtual ID3D12CommandAllocator* getCommandAllocator () const = 0; - virtual IDXGISwapChain3* getSwapChain () const = 0; - virtual ID3D12Device* getDevice () const = 0; - - virtual INT getFrameIndex () const = 0; - virtual void setFrameIndex (INT index) = 0; - - virtual void render () = 0; -}; - -//------------------------------------------------------------------------ -struct IDirect3D12Renderer -{ - virtual ~IDirect3D12Renderer () noexcept = default; - - virtual bool init (IDirect3D12View* view) = 0; - virtual void render (ID3D12CommandQueue* queue) = 0; - virtual void beforeSizeUpdate () = 0; - virtual void onSizeUpdate (IntSize newSize, double scaleFactor) = 0; - virtual void onAttach () = 0; - virtual void onRemove () = 0; - - virtual uint32_t getFrameCount () const = 0; -}; - -//------------------------------------------------------------------------ -using Direct3D12RendererPtr = std::shared_ptr; - -//------------------------------------------------------------------------ -struct GPUFence -{ - template - using ComPtr = Microsoft::WRL::ComPtr; - - GPUFence () = default; - GPUFence (ID3D12Device* device, UINT64 initialValue = 0) - { - ThrowIfFailed (device->CreateFence (0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS (&m_fence))); - m_event = CreateEvent (nullptr, FALSE, FALSE, nullptr); - if (m_event == nullptr) - { - ThrowIfFailed (HRESULT_FROM_WIN32 (GetLastError ())); - } - m_value = initialValue; - } - ~GPUFence () noexcept - { - if (m_event) - CloseHandle (m_event); - } - - GPUFence& operator=(GPUFence&& o) noexcept - { - m_event = o.m_event; - m_fence = o.m_fence; - m_value = o.m_value; - o.m_event = nullptr; - o.m_fence.Reset (); - o.m_value = {}; - return *this; - } - - void wait (ID3D12CommandQueue* queue) - { - if (m_fence == nullptr) - return; - - const auto value = m_value; - ThrowIfFailed (queue->Signal (m_fence.Get (), value)); - m_value++; - if (m_fence->GetCompletedValue () < value) - { - ThrowIfFailed (m_fence->SetEventOnCompletion (value, m_event)); - WaitForSingleObject (m_event, INFINITE); - } - } - - HANDLE m_event {nullptr}; - ComPtr m_fence; - UINT64 m_value {0}; -}; - -//------------------------------------------------------------------------ -struct Direct3D12View : public ExternalHWNDBase, - IDirect3D12View -{ - template - using ComPtr = Microsoft::WRL::ComPtr; - - Direct3D12View (HINSTANCE instance, const Direct3D12RendererPtr& renderer, - ComPtr factory = nullptr, ComPtr device = nullptr, ComPtr commandQueue = nullptr) - : Base (instance), m_renderer (renderer), m_factory (factory), m_device (device), m_commandQueue (commandQueue) - { - vstgui_assert ((factory && device) || (!factory && !device), "Either both factory and device are provided or none of both!"); - vstgui_assert (commandQueue ? device : true, "If a command queue is provided, the device must also be provided!"); - } - - static std::shared_ptr make (HINSTANCE instance, - const Direct3D12RendererPtr& renderer, - ComPtr factory = nullptr, - ComPtr device = nullptr, - ComPtr queue = nullptr) - { - return std::make_shared (instance, renderer, factory, device, queue); - } - - void render () override { doRender (); } - - Direct3D12RendererPtr& getRenderer () { return m_renderer; } - const Direct3D12RendererPtr& getRenderer () const { return m_renderer; } - -private: - ID3D12CommandAllocator* getCommandAllocator () const { return m_commandAllocator.Get (); } - IDXGISwapChain3* getSwapChain () const { return m_swapChain.Get (); } - ID3D12Device* getDevice () const { return m_device.Get (); } - INT getFrameIndex () const { return m_frameIndex; } - void setFrameIndex (INT index) { m_frameIndex = index; } - - void doRender () - { - if (mutex.try_lock ()) - { - if (m_commandQueue) - { - HRESULT result = S_FALSE; - try - { - waitForPreviousFrame (); - m_renderer->render (m_commandQueue.Get ()); - result = getSwapChain ()->Present (1, 0); - ThrowIfFailed (result); - } - catch (const Win32Exception& e) - { - try - { - freeResources (); - } - catch (...) - { - } - throw (e); - } - } - mutex.unlock (); - } - } - - bool attach (void* parent, PlatformViewType parentViewType) override - { - if (Base::attach (parent, parentViewType)) - { - try - { - if (m_renderer->init (this)) - { - init (); - m_renderer->onAttach (); - } - } - catch (...) - { - auto reasonHR = m_device->GetDeviceRemovedReason (); - Win32Exception e (reasonHR); - freeResources (); - } - return true; - } - return false; - } - - bool remove () override - { - Guard g (mutex); - waitForPreviousFrame (); - freeResources (); - return Base::remove (); - } - - void setViewSize (IntRect frame, IntRect visible) override - { - Guard g (mutex); - Base::setViewSize (frame, visible); - m_visibleRect = visible; - updateSizes (); - } - - void setContentScaleFactor (double factor) override - { - Guard g (mutex); - m_scaleFactor = factor; - updateSizes (); - } - - void updateSizes () - { - if (m_swapChain) - { - if (m_size.width == m_visibleRect.size.width && - m_size.height == m_visibleRect.size.height) - return; - m_size = m_visibleRect.size; - waitForPreviousFrame (); - m_renderer->beforeSizeUpdate (); - ThrowIfFailed (m_dcompVisual->SetContent (nullptr)); - ThrowIfFailed (m_swapChain->ResizeBuffers ( - m_renderer->getFrameCount (), static_cast (m_size.width), - static_cast (m_size.height), DXGI_FORMAT_R8G8B8A8_UNORM, - DXGI_SWAP_EFFECT_FLIP_DISCARD)); - ThrowIfFailed (m_dcompVisual->SetContent (m_swapChain.Get ())); - m_renderer->onSizeUpdate (m_size, m_scaleFactor); - ThrowIfFailed (m_dcompDevice->Commit ()); - } - } - - void freeResources () - { - m_fence = {}; - - try - { - m_renderer->onRemove (); - } - catch (...) - { - } - - m_commandAllocator.Reset (); - m_commandQueue.Reset (); - - m_dcompDevice.Reset (); - m_dcompTarget.Reset (); - m_dcompVisual.Reset (); - - m_swapChain.Reset (); - m_device.Reset (); - } - - void init () - { -#if defined(_DEBUG) - // Enable the D3D12 debug layer. - { - ComPtr debugController; - if (SUCCEEDED (D3D12GetDebugInterface (IID_PPV_ARGS (&debugController)))) - { - debugController->EnableDebugLayer (); - } - } -#endif - if (!m_factory) - ThrowIfFailed (CreateDXGIFactory1 (IID_PPV_ARGS (&m_factory))); - if (m_device == nullptr) - { - ComPtr hardwareAdapter; - getHardwareAdapter (m_factory.Get (), &hardwareAdapter); - if (!hardwareAdapter) - { - throw; - } - - ThrowIfFailed (D3D12CreateDevice (hardwareAdapter.Get (), D3D_FEATURE_LEVEL_11_0, - IID_PPV_ARGS (&m_device))); - } - if (!m_commandQueue) - { - // Describe and create the command queue. - D3D12_COMMAND_QUEUE_DESC queueDesc = {}; - queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; - queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; - ThrowIfFailed (m_device->CreateCommandQueue (&queueDesc, IID_PPV_ARGS (&m_commandQueue))); - } - - try - { - createSwapChain (m_factory.Get ()); - setupDirectComposition (); - } - catch (...) - { - auto exc = std::current_exception (); - throw (exc); - } - - ThrowIfFailed ( - m_factory->MakeWindowAssociation (container.getHWND (), DXGI_MWA_NO_ALT_ENTER)); - - ThrowIfFailed (m_device->CreateCommandAllocator (D3D12_COMMAND_LIST_TYPE_DIRECT, - IID_PPV_ARGS (&m_commandAllocator))); - - m_fence = GPUFence (m_device.Get (), 1); - } - - void createSwapChain (IDXGIFactory4* factory) - { - // Describe and create the swap chain. - DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; - swapChainDesc.BufferCount = m_renderer->getFrameCount (); - swapChainDesc.Width = static_cast (m_size.width); - swapChainDesc.Height = static_cast (m_size.height); - swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; - swapChainDesc.SampleDesc.Count = 1; - swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; - - ComPtr swapChain; - ThrowIfFailed (factory->CreateSwapChainForComposition ( - m_commandQueue.Get (), // Swap chain needs the queue so that it can force a flush on it. - &swapChainDesc, nullptr, &swapChain)); - - ThrowIfFailed (swapChain.As (&m_swapChain)); - } - - void setupDirectComposition () - { - // Create the DirectComposition device - ThrowIfFailed (DCompositionCreateDevice ( - nullptr, IID_PPV_ARGS (m_dcompDevice.ReleaseAndGetAddressOf ()))); - - // Create a DirectComposition target associated with the window (pass in hWnd here) - ThrowIfFailed (m_dcompDevice->CreateTargetForHwnd ( - container.getHWND (), true, m_dcompTarget.ReleaseAndGetAddressOf ())); - - // Create a DirectComposition "visual" - ThrowIfFailed (m_dcompDevice->CreateVisual (m_dcompVisual.ReleaseAndGetAddressOf ())); - - // Associate the visual with the swap chain - ThrowIfFailed (m_dcompVisual->SetContent (m_swapChain.Get ())); - - // Set the visual as the root of the DirectComposition target's composition tree - ThrowIfFailed (m_dcompTarget->SetRoot (m_dcompVisual.Get ())); - ThrowIfFailed (m_dcompDevice->Commit ()); - } - - static void getHardwareAdapter (IDXGIFactory2* pFactory, IDXGIAdapter1** ppAdapter) - { - ComPtr adapter; - *ppAdapter = nullptr; - - for (UINT adapterIndex = 0; - DXGI_ERROR_NOT_FOUND != pFactory->EnumAdapters1 (adapterIndex, &adapter); - ++adapterIndex) - { - DXGI_ADAPTER_DESC1 desc; - adapter->GetDesc1 (&desc); - - if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) - { - // Don't select the Basic Render Driver adapter. - // If you want a software adapter, pass in "/warp" on the command line. - continue; - } - - // Check to see if the adapter supports Direct3D 12, but don't create the - // actual device yet. - if (SUCCEEDED (D3D12CreateDevice (adapter.Get (), D3D_FEATURE_LEVEL_11_0, - _uuidof(ID3D12Device), nullptr))) - { - break; - } - } - - *ppAdapter = adapter.Detach (); - } - - void waitForPreviousFrame () - { - if (!m_commandQueue || !m_swapChain) - return; - - // WAITING FOR THE FRAME TO COMPLETE BEFORE CONTINUING IS NOT BEST PRACTICE. - // This is code implemented as such for simplicity. The D3D12HelloFrameBuffering - // sample illustrates how to use fences for efficient resource usage and to - // maximize GPU utilization. - - m_fence.wait (m_commandQueue.Get ()); - m_frameIndex = m_swapChain->GetCurrentBackBufferIndex (); - } - - using Mutex = std::recursive_mutex; - using Guard = std::lock_guard; - - Mutex mutex; - - IntSize m_size {100, 100}; - IntRect m_visibleRect {}; - double m_scaleFactor {1.}; - - UINT m_frameIndex {0}; - - // Synchronization objects. - GPUFence m_fence; - - ComPtr m_factory; - - ComPtr m_swapChain; - - ComPtr m_device; - ComPtr m_commandQueue; - ComPtr m_commandAllocator; - - // DirectComposition objects. - ComPtr m_dcompDevice; - ComPtr m_dcompTarget; - ComPtr m_dcompVisual; - - Direct3D12RendererPtr m_renderer; -}; - -//------------------------------------------------------------------------ -} // ExternalView -} // VSTGUI diff --git a/vstgui/contrib/externalview_hwnd.h b/vstgui/contrib/externalview_hwnd.h deleted file mode 100644 index acb7367ac..000000000 --- a/vstgui/contrib/externalview_hwnd.h +++ /dev/null @@ -1,223 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#pragma once - -#include "vstgui/lib/vstguibase.h" -#include "vstgui/lib/iexternalview.h" - -#include -#include -#include -#include -#include - -//------------------------------------------------------------------------ -namespace VSTGUI { -namespace ExternalView { - -//------------------------------------------------------------------------ -inline void setWindowSize (HWND window, IntRect r) -{ - SetWindowPos (window, HWND_TOP, static_cast (r.origin.x), static_cast (r.origin.y), - static_cast (r.size.width), static_cast (r.size.height), - SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_DEFERERASE); -} - -//------------------------------------------------------------------------ -struct HWNDWindow final -{ - using WindowProcFunc = - std::function; - - HWNDWindow (HINSTANCE instance) : instance (instance) {} - - ~HWNDWindow () noexcept - { - if (window) - { - SetWindowLongPtr (window, GWLP_USERDATA, (__int3264)(LONG_PTR) nullptr); - DestroyWindow (window); - } - destroyWindowClass (); - } - - bool create (const TCHAR* title, const IntRect& frame, HWND parent, DWORD exStyle = 0, - DWORD style = WS_CHILD) - { - if (!initWindowClass ()) - return false; - window = CreateWindowEx ( - exStyle, MAKEINTATOM (windowClassAtom), title, style, static_cast (frame.origin.x), - static_cast (frame.origin.y), static_cast (frame.size.width), - static_cast (frame.size.height), parent, nullptr, instance, nullptr); - if (!window) - return false; - SetWindowLongPtr (window, GWLP_USERDATA, (__int3264)(LONG_PTR)this); - return true; - } - - void setWindowProc (WindowProcFunc&& func) { windowProc = std::move (func); } - - void setSize (const IntRect& r) - { - if (!window) - return; - setWindowSize (window, r); - } - - void show (bool state) { ShowWindow (window, state ? SW_SHOW : SW_HIDE); } - void setEnabled (bool state) { EnableWindow (window, state); } - - HWND getHWND () const { return window; } - HINSTANCE getInstance () const { return instance; } - -private: - bool initWindowClass () - { - assert (instance != nullptr); - - if (windowClassAtom != 0) - return true; - - std::wstring windowClassName; - windowClassName = TEXT ("VSTGUI ExternalView Container "); - windowClassName += std::to_wstring (reinterpret_cast (this)); - - WNDCLASS windowClass; - windowClass.style = CS_GLOBALCLASS; - - windowClass.lpfnWndProc = WindowProc; - windowClass.cbClsExtra = 0; - windowClass.cbWndExtra = 0; - windowClass.hInstance = instance; - windowClass.hIcon = 0; - - windowClass.hCursor = LoadCursor (NULL, IDC_ARROW); - windowClass.hbrBackground = 0; - - windowClass.lpszMenuName = 0; - windowClass.lpszClassName = windowClassName.data (); - windowClassAtom = RegisterClass (&windowClass); - return windowClassAtom != 0; - } - - void destroyWindowClass () - { - if (windowClassAtom == 0) - return; - UnregisterClass (MAKEINTATOM (windowClassAtom), instance); - windowClassAtom = 0; - } - - static LONG_PTR WINAPI WindowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) - { - if (message == WM_ERASEBKGND) - return 1; - auto instance = reinterpret_cast (GetWindowLongPtr (hwnd, GWLP_USERDATA)); - if (instance && instance->windowProc) - return instance->windowProc (hwnd, message, wParam, lParam); - return DefWindowProc (hwnd, message, wParam, lParam); - } - - WindowProcFunc windowProc; - HWND window {nullptr}; - HINSTANCE instance {nullptr}; - ATOM windowClassAtom {0}; -}; - -//------------------------------------------------------------------------ -struct NonClientMetrics -{ - static const NONCLIENTMETRICS& get () - { - static NonClientMetrics gInstance; - return gInstance.nonClientMetrics; - } - -private: - NonClientMetrics () - { - nonClientMetrics.cbSize = sizeof (nonClientMetrics); - SystemParametersInfoForDpi (SPI_GETNONCLIENTMETRICS, nonClientMetrics.cbSize, - &nonClientMetrics, 0, 96); - } - - NONCLIENTMETRICS nonClientMetrics {}; -}; - -//------------------------------------------------------------------------ -struct ExternalHWNDBase : ViewAdapter -{ - using Base = ExternalHWNDBase; - using PlatformViewType = ExternalView::PlatformViewType; - using IntRect = ExternalView::IntRect; - - HWNDWindow container; - HWND child {nullptr}; - - ExternalHWNDBase (HINSTANCE hInst) : container (hInst) - { - container.create (nullptr, {{0, 0}, {1, 1}}, HWND_MESSAGE, - WS_EX_NOPARENTNOTIFY | WS_EX_COMPOSITED, WS_CHILD | WS_VISIBLE); - } - - virtual ~ExternalHWNDBase () noexcept - { - if (child) - DestroyWindow (child); - } - - bool platformViewTypeSupported (PlatformViewType type) override - { - return type == PlatformViewType::HWND; - } - - bool attach (void* parent, PlatformViewType parentViewType) override - { - assert (container.getHWND ()); - if (parent == nullptr || parentViewType != PlatformViewType::HWND) - return false; - auto parentHWND = reinterpret_cast (parent); - SetParent (container.getHWND (), parentHWND); - return true; - } - - bool remove () override - { - assert (container.getHWND ()); - SetParent (container.getHWND (), HWND_MESSAGE); - return true; - } - - void setViewSize (IntRect frame, IntRect visible) override - { - assert (container.getHWND ()); - container.setSize (visible); - if (child) - { - frame.origin.x -= visible.origin.x; - frame.origin.y -= visible.origin.y; - setWindowSize (child, frame); - } - } - - void setContentScaleFactor (double scaleFactor) override {} - - void setMouseEnabled (bool state) override { EnableWindow (container.getHWND (), state); } - - void takeFocus () override { SetFocus (child); } - - void looseFocus () override - { - if (GetFocus () == child) - { - SetFocus (GetParent (container.getHWND ())); - } - } -}; - -//------------------------------------------------------------------------ -} // ExternalView -} // VSTGUI diff --git a/vstgui/contrib/externalview_metal.h b/vstgui/contrib/externalview_metal.h deleted file mode 100644 index e0aa06c15..000000000 --- a/vstgui/contrib/externalview_metal.h +++ /dev/null @@ -1,276 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#pragma once - -#import "externalview_nsview.h" - -#import -#import -#import -#import - -//------------------------------------------------------------------------ -using VSTGUIMetalLayerDelegateDrawCallback = std::function; -using VSTGUIMetalViewScreenChangedCallack = std::function; - -@interface NSObject () -- (void)setDrawCallback:(const VSTGUIMetalLayerDelegateDrawCallback&)callback; -- (void)setScreenChangedCallback:(const VSTGUIMetalViewScreenChangedCallack&)callback; -@end - -//------------------------------------------------------------------------ -namespace VSTGUI { -namespace ExternalView { - -//------------------------------------------------------------------------ -struct IMetalView -{ - virtual ~IMetalView () noexcept = default; - - virtual void render () = 0; -}; - -//------------------------------------------------------------------------ -/** metal render interface to be used as the renderer of the MetalView - * - * The renderer has to set the metal device of the metal layer before it can draw to it. - */ -struct IMetalRenderer -{ - virtual ~IMetalRenderer () noexcept = default; - - virtual bool init (IMetalView* metalView, CAMetalLayer* metalLayer) = 0; - virtual void draw (id drawable) = 0; - virtual void onSizeUpdate (int32_t width, int32_t height, double scaleFactor) = 0; - virtual void onAttached () = 0; - virtual void onRemoved () = 0; - virtual void onScreenChanged (NSScreen* screen) = 0; -}; - -using MetalRendererPtr = std::shared_ptr; - -//------------------------------------------------------------------------ -struct MetalLayerDelegate : RuntimeObjCClass -{ - static constexpr auto CallbackVarName = "callback"; - - static Class CreateClass () - { - return ObjCClassBuilder () - .init ("MetalLayerDelegate", [NSObject class]) - .addMethod (@selector (displayLayer:), displayLayer) - .addMethod (@selector (actionForLayer:forKey:), actionForLayer) - .addMethod (@selector (setDrawCallback:), setCallback) - .addProtocol ("CALayerDelegate") - .addIvar (CallbackVarName) - .finalize (); - } - - static void setCallback (id self, SEL cmd, VSTGUIMetalLayerDelegateDrawCallback callback) - { - auto instance = makeInstance (self); - if (auto var = instance.getVariable (CallbackVarName)) - var->set (callback); - } - - static void displayLayer (id self, SEL cmd, CALayer* layer) - { - auto instance = makeInstance (self); - if (auto var = instance.getVariable (CallbackVarName)) - { - if (auto callback = var->get ()) - callback (); - } - } - - static id actionForLayer (CALayer* layer, NSString* key) { return [NSNull null]; } -}; - -//------------------------------------------------------------------------ -struct MetalNSView : RuntimeObjCClass -{ - static constexpr auto CallbackVarName = "callback"; - - static Class CreateClass () - { - return ObjCClassBuilder () - .init ("MetalNSView", [NSView class]) - .addIvar (CallbackVarName) - .addMethod (@selector (viewDidMoveToWindow), viewDidMoveToWindow) - .addMethod (@selector (viewWillMoveToWindow:), viewWillMoveToWindow) - .addMethod (@selector (windowDidChangeScreen:), windowDidChangeScreen) - .addMethod (@selector (setScreenChangedCallback:), setCallback) - .finalize (); - } - - static void setCallback (id self, SEL cmd, VSTGUIMetalViewScreenChangedCallack callback) - { - auto instance = makeInstance (self); - if (auto var = instance.getVariable (CallbackVarName)) - var->set (callback); - } - - static void viewDidMoveToWindow (id self, SEL cmd) - { - windowDidChangeScreen (self, cmd, nullptr); - makeInstance (self).callSuper (cmd); - } - - static void viewWillMoveToWindow (id self, SEL cmd, NSWindow* window) - { - if (auto prevWindow = [self window]) - { - [NSNotificationCenter.defaultCenter removeObserver:self]; - } - if (window) - { - [NSNotificationCenter.defaultCenter addObserver:self - selector:@selector (windowDidChangeScreen:) - name:NSWindowDidChangeScreenNotification - object:window]; - } - makeInstance (self).callSuper (cmd, window); - } - - static void windowDidChangeScreen (id self, SEL cmd, NSNotification* n) - { - if (NSScreen* screen = [[self window] screen]) - { - auto instance = makeInstance (self); - if (auto var = - instance.getVariable (CallbackVarName)) - { - if (auto callback = var->get ()) - callback (screen); - } - } - } -}; - -//------------------------------------------------------------------------ -struct MetalView : ExternalNSViewBase, - IMetalView -{ - /** make a new metal view. - * - * The metal view can render on a background thread (only use one thread for rendering) or on - * the main thread. - * Rendering and view resizing is automatically guarded by a mutex. - * The view will automatically trigger a rendering when the view is resized. - */ - static std::shared_ptr make (const MetalRendererPtr& renderer) - { - if (!renderer) - return {}; - if (auto metalView = std::shared_ptr (new MetalView (renderer))) - { - if (renderer->init (metalView.get (), metalView->metalLayer)) - return metalView; - } - return {}; - } - - /** immediately render the view [thread safe] */ - void render () override - { - doLocked ([&] () { renderer->draw (metalLayer.nextDrawable); }); - } - - /** do something locked [thread safe] */ - template - void doLocked (Proc proc) - { - LockGuard g (mutex); - @autoreleasepool - { - proc (); - } - } - -private: - CAMetalLayer* metalLayer {nullptr}; - id metalLayerDelegate {nullptr}; - double contentScaleFactor {1.}; - using Mutex = std::recursive_mutex; - using LockGuard = std::lock_guard; - Mutex mutex; - MetalRendererPtr renderer; - - MetalView (const MetalRendererPtr& renderer) - : Base ([MetalNSView::alloc () init]), renderer (renderer) - { - metalLayerDelegate = [MetalLayerDelegate::alloc () init]; - metalLayer = [CAMetalLayer new]; - metalLayer.delegate = metalLayerDelegate; - view.layer = metalLayer; - metalLayer.needsDisplayOnBoundsChange = YES; - metalLayer.geometryFlipped = YES; - metalLayer.opaque = NO; - metalLayer.contentsGravity = kCAGravityBottomLeft; - [metalLayerDelegate setDrawCallback:[this] () { - render (); - }]; - [view setScreenChangedCallback:[this] (NSScreen* screen) { - this->renderer->onScreenChanged (screen); - }]; - } - - bool attach (void* parent, PlatformViewType parentViewType) override - { - if (Base::attach (parent, parentViewType)) - { - renderer->onAttached (); - return true; - } - return false; - } - - bool remove () override - { - if (Base::remove ()) - { - renderer->onRemoved (); - return true; - } - return false; - } - - void setContentScaleFactor (double scaleFactor) override - { - contentScaleFactor = scaleFactor; - metalLayer.contentsScale = scaleFactor; - [metalLayer setNeedsDisplay]; - onSizeUpdate (); - } - - void setViewSize (IntRect frame, IntRect visible) override - { - Base::setViewSize (frame, visible); - onSizeUpdate (); - } - - void onSizeUpdate () - { - doLocked ([this] () { - auto size = view.frame.size; - metalLayer.drawableSize = - NSMakeSize (size.width * contentScaleFactor, size.height * contentScaleFactor); - renderer->onSizeUpdate (size.width, size.height, contentScaleFactor); - }); - } - -#if !__has_feature(objc_arc) -public: - ~MetalView () noexcept override - { - [metalLayerDelegate release]; - [metalLayer release]; - } -#endif -}; - -//------------------------------------------------------------------------ -} // ExternalView -} // VSTGUI diff --git a/vstgui/contrib/externalview_nsview.h b/vstgui/contrib/externalview_nsview.h deleted file mode 100644 index 2e11a76dd..000000000 --- a/vstgui/contrib/externalview_nsview.h +++ /dev/null @@ -1,225 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#pragma once - -#import "../lib/platform/mac/cocoa/objcclassbuilder.h" -#import "../lib/iexternalview.h" -#import - -//------------------------------------------------------------------------ -namespace VSTGUI { -namespace ExternalView { - -//------------------------------------------------------------------------ -inline NSRect toNSRect (const IntRect& r) -{ - return NSMakeRect (r.origin.x, r.origin.y, r.size.width, r.size.height); -} - -//------------------------------------------------------------------------ -/** a NSView that has a flipped coordinate system (top-left is {0, 0}, not AppKits default which is - * bottom-left) - * - * to create it call [ExternalViewContainerNSView::alloc () initWithFrame: rect] - */ -struct ExternalViewContainerNSView : RuntimeObjCClass -{ - static constexpr auto TookFocusCallbackVarName = "TookFocusCallback"; - - static Class CreateClass () - { - return ObjCClassBuilder () - .init ("ExternalViewContainerNSView", [NSView class]) - .addMethod (@selector (isFlipped), isFlipped) - .addMethod (@selector (viewWillMoveToWindow:), viewWillMoveToWindow) - .addMethod (@selector (observeValueForKeyPath:ofObject:change:context:), - observeValueForKeyPath) - .addIvar (TookFocusCallbackVarName) - .finalize (); - } - - static BOOL isFlipped (id self, SEL cmd) { return YES; } - static void viewWillMoveToWindow (id self, SEL _cmd, NSWindow* window) - { - if ([self window] && [self window] != window) - { - [[self window] removeObserver:self forKeyPath:@"firstResponder"]; - } - if (window) - { - [window addObserver:self forKeyPath:@"firstResponder" options:0 context:nullptr]; - } - } - - static void observeValueForKeyPath (id self, SEL cmd, NSString* keyPath, id object, - NSDictionary* change, - void* context) - { - if ([keyPath isEqualToString:@"firstResponder"]) - { - auto view = [self window].firstResponder; - if ([view isKindOfClass:[NSView class]] && - [static_cast (view) isDescendantOf:self]) - { - if (auto var = makeInstance (self).getVariable ( - TookFocusCallbackVarName)) - { - if (var.value ().get ()) - var.value ().get () (); - } - } - } - } -}; - -//------------------------------------------------------------------------ -/** a template helper class for embedding NSViews into VSTGUI via ExternalView::IView - * - * Example to add a simple NSView: - * - * // Header: ExampleNSView.h - * - * class ExampleNSView : IView - * { - * public: - * ExampleNSView (); - * ~ExampleNSView () noexcept; - * - * private: - * bool platformViewTypeSupported (PlatformViewType type) override; - * bool attach (void* parent, PlatformViewType parentViewType) override; - * bool remove () override; - * void setViewSize (IntRect frame, IntRect visible) override; - * void setContentScaleFactor (double scaleFactor) override; - * void setMouseEnabled (bool state) override; - * void takeFocus () override; - * void looseFocus () override; - * - * struct Impl; - * std::unique_ptr impl; - * }; - * - * // Source: ExampleNSView.mm - * - * #import "ExampleNSView.h" - * #import "externalview_nsview.h" - * - * struct ExampleNSView::Impl : ExternalNSViewBase - * { - * Impl () : Base ([NSView new]) - * { - * // configure the view here - * view.alphaValue = 0.5; - * } - * }; - * - * ExampleNSView::ExampleNSView () { impl = std::make_unique (); } - * ExampleNSView::~ExampleNSView () noexcept = default; - * bool ExampleNSView::platformViewTypeSupported (PlatformViewType type) - * { - * return impl->platformViewTypeSupported (type); - * } - * bool ExampleNSView::attach (void* parent, PlatformViewType parentViewType) - * { - * return impl->attach (parent, parentViewType); - * } - * bool ExampleNSView::remove () { return impl->remove (); } - * void ExampleNSView::setViewSize (IntRect frame, IntRect visible) - * { - * impl->setViewSize (frame, visible); - * } - * void ExampleNSView::setContentScaleFactor (double scaleFactor) - * { - * impl->setContentScaleFactor (scaleFactor); - * } - * void ExampleNSView::setMouseEnabled (bool state) { impl->setMouseEnabled (state); } - * void ExampleNSView::takeFocus () { impl->takeFocus (); } - * void ExampleNSView::looseFocus () { impl->looseFocus (); } - * - */ -template -struct ExternalNSViewBase : ViewAdapter -{ - using Base = ExternalNSViewBase; - using PlatformViewType = ExternalView::PlatformViewType; - using IntRect = ExternalView::IntRect; - - NSView* container {[ExternalViewContainerNSView::alloc () initWithFrame: {0., 0., 10., 10.}]}; - ViewType* view {nullptr}; - - ExternalNSViewBase (ViewType* inView) : view (inView) { [container addSubview:view]; } - -#if !__has_feature(objc_arc) - virtual ~ExternalNSViewBase () noexcept - { - [container release]; - [view release]; - } -#endif - - bool platformViewTypeSupported (PlatformViewType type) override - { - return type == PlatformViewType::NSView; - } - - bool attach (void* parent, PlatformViewType parentViewType) override - { - if (!parent || parentViewType != PlatformViewType::NSView) - return false; - auto parentNSView = (__bridge NSView*)parent; - [parentNSView addSubview:container]; - return true; - } - - bool remove () override - { - [container removeFromSuperview]; - return true; - } - - void setViewSize (IntRect frame, IntRect visible) override - { - container.frame = toNSRect (visible); - frame.origin.x -= visible.origin.x; - frame.origin.y -= visible.origin.y; - view.frame = toNSRect (frame); - } - - void setContentScaleFactor (double scaleFactor) override {} - - void setMouseEnabled (bool state) override - { - if ([view respondsToSelector:@selector (setEnabled:)]) - [(id)view setEnabled:state]; - } - - void takeFocus () override - { - if (view.acceptsFirstResponder) - { - if (auto window = view.window) - [window makeFirstResponder:view]; - } - } - - void looseFocus () override - { - if (auto window = view.window) - [window makeFirstResponder:container.superview]; - } - - void setTookFocusCallback (const TookFocusCallback& callback) override - { - if (auto var = ObjCInstance (container).getVariable ( - ExternalViewContainerNSView::TookFocusCallbackVarName)) - { - var->set (callback); - } - } -}; - -//------------------------------------------------------------------------ -} // ExternalView -} // VSTGUI diff --git a/vstgui/doxygen/page_changes.h b/vstgui/doxygen/page_changes.h index 4f413e3a6..09034e5e3 100644 --- a/vstgui/doxygen/page_changes.h +++ b/vstgui/doxygen/page_changes.h @@ -22,11 +22,7 @@ It's recommended to start new projects with version 4 while old projects should @section new_stuff New Stuff -@subsection version4_13 Version 4.13 - -- support embedding platform views (HWND & NSView) as sub views (see CExternalView and ExternalView::IView) and examples in the contrib folder. - -@subsection version4_12_1 Version 4.12.1 +@subsection version4_12 Version 4.12.1 - make it possible to use the new multi frame bitmap feature with custom value to frame index mappings by subclassing. @@ -132,10 +128,6 @@ Note: All current deprecated methods will be removed in the next version. So mak @section code_changes Changes for existing VSTGUI code -@subsection code_changes_4_12_to_4_13 VSTGUI 4.12 -> VSTGUI 4.13 - -- the context argument of IFontPainter has changed to use the new platform graphics device context - @subsection code_changes_4_11_to_4_12 VSTGUI 4.11 -> VSTGUI 4.12 - The CMultiFrameBitmap change deprecated the VSTGUI::IMultiBitmapControl class. If you use it, diff --git a/vstgui/lib/CMakeLists.txt b/vstgui/lib/CMakeLists.txt index a4be36e43..7d13703c2 100644 --- a/vstgui/lib/CMakeLists.txt +++ b/vstgui/lib/CMakeLists.txt @@ -31,8 +31,6 @@ set(${target}_common_sources cdrawmethods.h cdropsource.cpp cdropsource.h - cexternalview.cpp - cexternalview.h cfileselector.cpp cfileselector.h cfont.cpp @@ -141,7 +139,6 @@ set(${target}_common_sources idatabrowserdelegate.h idatapackage.h idependency.h - iexternalview.h ifocusdrawing.h iscalefactorchangedlistener.h itouchevent.h @@ -151,7 +148,6 @@ set(${target}_common_sources pixelbuffer.h pixelbuffer.cpp platform/iplatformbitmap.h - platform/iplatformgraphicsdevice.h platform/iplatformfileselector.h platform/iplatformfont.h platform/iplatformframe.h @@ -197,6 +193,8 @@ set(${target}_mac_sources platform/mac/cfontmac.mm platform/mac/cgbitmap.cpp platform/mac/cgbitmap.h + platform/mac/cgdrawcontext.cpp + platform/mac/cgdrawcontext.h platform/mac/cocoa/autoreleasepool.h platform/mac/cocoa/autoreleasepool.mm platform/mac/cocoa/cocoahelpers.h @@ -212,8 +210,6 @@ set(${target}_mac_sources platform/mac/cocoa/nsviewoptionmenu.h platform/mac/cocoa/nsviewoptionmenu.mm platform/mac/cocoa/objcclassbuilder.h - platform/mac/coregraphicsdevicecontext.h - platform/mac/coregraphicsdevicecontext.mm platform/mac/macclipboard.h platform/mac/macclipboard.mm platform/mac/macfactory.h @@ -236,17 +232,16 @@ set(${target}_mac_sources ########################################################################################## set(${target}_win32_sources - platform/win32/direct2d/d2d.h platform/win32/direct2d/d2dbitmap.cpp platform/win32/direct2d/d2dbitmap.h platform/win32/direct2d/d2dbitmapcache.cpp platform/win32/direct2d/d2dbitmapcache.h + platform/win32/direct2d/d2ddrawcontext.cpp + platform/win32/direct2d/d2ddrawcontext.h platform/win32/direct2d/d2dfont.cpp platform/win32/direct2d/d2dfont.h platform/win32/direct2d/d2dgradient.cpp platform/win32/direct2d/d2dgradient.h - platform/win32/direct2d/d2dgraphicscontext.cpp - platform/win32/direct2d/d2dgraphicscontext.h platform/win32/direct2d/d2dgraphicspath.cpp platform/win32/direct2d/d2dgraphicspath.h platform/win32/win32bitmapbase.h @@ -285,12 +280,12 @@ set(${target}_win32_sources set(${target}_linux_sources platform/linux/cairobitmap.cpp platform/linux/cairobitmap.h + platform/linux/cairocontext.cpp + platform/linux/cairocontext.h platform/linux/cairofont.cpp platform/linux/cairofont.h platform/linux/cairogradient.cpp platform/linux/cairogradient.h - platform/linux/cairographicscontext.cpp - platform/linux/cairographicscontext.h platform/linux/cairopath.cpp platform/linux/cairopath.h platform/linux/cairoutils.h diff --git a/vstgui/lib/cdatabrowser.cpp b/vstgui/lib/cdatabrowser.cpp index 4e771d238..ab1d246a3 100644 --- a/vstgui/lib/cdatabrowser.cpp +++ b/vstgui/lib/cdatabrowser.cpp @@ -9,7 +9,6 @@ #include "controls/cscrollbar.h" #include "ifocusdrawing.h" #include "cgraphicspath.h" -#include "cdrawcontext.h" #include "idatabrowserdelegate.h" #include #include diff --git a/vstgui/lib/cdrawcontext.cpp b/vstgui/lib/cdrawcontext.cpp index 7a99a75c2..61928608b 100644 --- a/vstgui/lib/cdrawcontext.cpp +++ b/vstgui/lib/cdrawcontext.cpp @@ -4,81 +4,27 @@ #include "cdrawcontext.h" #include "cgraphicspath.h" -#include "cgradient.h" #include "cbitmap.h" #include "cstring.h" -#include "platform/iplatformgraphicsdevice.h" #include "platform/iplatformfont.h" -#include "platform/iplatformgraphicspath.h" -#include "platform/iplatformgradient.h" -#include "platform/platformfactory.h" #include -#include namespace VSTGUI { //----------------------------------------------------------------------------- -CDrawContext::Transform::Transform (CDrawContext& context, const CGraphicsTransform& transformation) -: context (context) -, transformation (transformation) +CDrawContext::CDrawContextState::CDrawContextState (const CDrawContextState& state) { - if (transformation.isInvariant () == false) - context.pushTransform (transformation); + *this = state; } //----------------------------------------------------------------------------- -CDrawContext::Transform::~Transform () noexcept +CDrawContext::CDrawContextState::CDrawContextState (CDrawContextState&& state) noexcept { - if (transformation.isInvariant () == false) - context.popTransform (); + *this = std::move (state); } //----------------------------------------------------------------------------- -struct CDrawContext::Impl -{ - //----------------------------------------------------------------------------- - struct State - { - SharedPointer font; - CColor frameColor {kTransparentCColor}; - CColor fillColor {kTransparentCColor}; - CColor fontColor {kTransparentCColor}; - CCoord frameWidth {0.}; - CPoint penLoc {}; - CRect clipRect {}; - CLineStyle lineStyle {kLineOnOffDash}; - CDrawMode drawMode {kAntiAliasing}; - float globalAlpha {1.f}; - BitmapInterpolationQuality bitmapQuality {BitmapInterpolationQuality::kDefault}; - - State () = default; - State (const State& state); - State& operator= (const State& state) = default; - State (State&& state) noexcept; - State& operator= (State&& state) noexcept; - }; - - UTF8String* drawStringHelper {nullptr}; - CRect surfaceRect; - double scaleFactor {1.}; - - State currentState; - - std::stack globalStatesStack; - std::stack transformStack; - - PlatformGraphicsDeviceContextPtr device; -}; - -//----------------------------------------------------------------------------- -CDrawContext::Impl::State::State (const State& state) { *this = state; } - -//----------------------------------------------------------------------------- -CDrawContext::Impl::State::State (State&& state) noexcept { *this = std::move (state); } - -//----------------------------------------------------------------------------- -CDrawContext::Impl::State& - CDrawContext::Impl::State::operator= (CDrawContext::Impl::State&& state) noexcept +CDrawContext::CDrawContextState& CDrawContext::CDrawContextState::operator= (CDrawContextState&& state) noexcept { font = std::move (state.font); frameColor = std::move (state.frameColor); @@ -94,47 +40,39 @@ CDrawContext::Impl::State& } //----------------------------------------------------------------------------- -CDrawContext::CDrawContext (const CRect& surfaceRect) +CDrawContext::Transform::Transform (CDrawContext& context, const CGraphicsTransform& transformation) +: context (context) +, transformation (transformation) { - impl = std::make_unique (); - impl->surfaceRect = surfaceRect; + if (transformation.isInvariant () == false) + context.pushTransform (transformation); +} - impl->transformStack.push (CGraphicsTransform ()); +//----------------------------------------------------------------------------- +CDrawContext::Transform::~Transform () noexcept +{ + if (transformation.isInvariant () == false) + context.popTransform (); } -//------------------------------------------------------------------------ -CDrawContext::CDrawContext (const PlatformGraphicsDeviceContextPtr device, const CRect& surfaceRect, - double scaleFactor) -: CDrawContext (surfaceRect) +//----------------------------------------------------------------------------- +CDrawContext::CDrawContext (const CRect& surfaceRect) +: surfaceRect (surfaceRect) { - impl->device = device; - impl->scaleFactor = scaleFactor; - setClipRect (surfaceRect); + transformStack.push (CGraphicsTransform ()); } //----------------------------------------------------------------------------- CDrawContext::~CDrawContext () noexcept { #if DEBUG - if (!impl->globalStatesStack.empty ()) + if (!globalStatesStack.empty ()) DebugPrint ("Global state stack not empty. Save and restore global state must be called in sequence !\n"); #endif - if (impl->drawStringHelper) - delete impl->drawStringHelper; -} - -//------------------------------------------------------------------------ -const PlatformGraphicsDeviceContextPtr& CDrawContext::getPlatformDeviceContext () const -{ - return impl->device; + if (drawStringHelper) + delete drawStringHelper; } -//----------------------------------------------------------------------------- -const CRect& CDrawContext::getSurfaceRect () const { return impl->surfaceRect; } - -//------------------------------------------------------------------------ -double CDrawContext::getScaleFactor () const { return impl->scaleFactor; } - //----------------------------------------------------------------------------- void CDrawContext::init () { @@ -146,26 +84,22 @@ void CDrawContext::init () setFontColor (kWhiteCColor); setFont (kSystemFont); setDrawMode (kAliasing); - setClipRect (impl->surfaceRect); + setClipRect (surfaceRect); } //----------------------------------------------------------------------------- void CDrawContext::saveGlobalState () { - impl->globalStatesStack.push (impl->currentState); - if (impl->device) - impl->device->saveGlobalState (); + globalStatesStack.push (currentState); } //----------------------------------------------------------------------------- void CDrawContext::restoreGlobalState () { - if (impl->device) - impl->device->restoreGlobalState (); - if (!impl->globalStatesStack.empty ()) + if (!globalStatesStack.empty ()) { - impl->currentState = std::move (impl->globalStatesStack.top ()); - impl->globalStatesStack.pop (); + currentState = std::move (globalStatesStack.top ()); + globalStatesStack.pop (); } else { @@ -176,107 +110,69 @@ void CDrawContext::restoreGlobalState () } //----------------------------------------------------------------------------- -void CDrawContext::setBitmapInterpolationQuality (BitmapInterpolationQuality quality) +void CDrawContext::setBitmapInterpolationQuality(BitmapInterpolationQuality quality) { - impl->currentState.bitmapQuality = quality; -} - -//----------------------------------------------------------------------------- -BitmapInterpolationQuality CDrawContext::getBitmapInterpolationQuality () const -{ - return impl->currentState.bitmapQuality; + currentState.bitmapQuality = quality; } //----------------------------------------------------------------------------- void CDrawContext::setLineStyle (const CLineStyle& style) { - if (impl->device) - impl->device->setLineStyle (style); - impl->currentState.lineStyle = style; + currentState.lineStyle = style; } -//----------------------------------------------------------------------------- -const CLineStyle& CDrawContext::getLineStyle () const { return impl->currentState.lineStyle; } - //----------------------------------------------------------------------------- void CDrawContext::setLineWidth (CCoord width) { - if (impl->device) - impl->device->setLineWidth (width); - impl->currentState.frameWidth = width; + currentState.frameWidth = width; } -//----------------------------------------------------------------------------- -CCoord CDrawContext::getLineWidth () const { return impl->currentState.frameWidth; } - //----------------------------------------------------------------------------- void CDrawContext::setDrawMode (CDrawMode mode) { - if (impl->device) - impl->device->setDrawMode (mode); - impl->currentState.drawMode = mode; + currentState.drawMode = mode; } -//----------------------------------------------------------------------------- -CDrawMode CDrawContext::getDrawMode () const { return impl->currentState.drawMode; } - //----------------------------------------------------------------------------- CRect& CDrawContext::getClipRect (CRect &clip) const { - clip = impl->currentState.clipRect; + clip = currentState.clipRect; getCurrentTransform ().inverse ().transform (clip); clip.normalize (); return clip; } -//----------------------------------------------------------------------------- -const CRect& CDrawContext::getAbsoluteClipRect () const { return impl->currentState.clipRect; } - //----------------------------------------------------------------------------- void CDrawContext::setClipRect (const CRect &clip) { - impl->currentState.clipRect = clip; - getCurrentTransform ().transform (impl->currentState.clipRect); - impl->currentState.clipRect.normalize (); - if (impl->device) - impl->device->setClipRect (impl->currentState.clipRect); + currentState.clipRect = clip; + getCurrentTransform ().transform (currentState.clipRect); + currentState.clipRect.normalize (); } //----------------------------------------------------------------------------- void CDrawContext::resetClipRect () { - if (impl->device) - impl->device->setClipRect (getSurfaceRect ()); - impl->currentState.clipRect = getSurfaceRect (); + currentState.clipRect = surfaceRect; } //----------------------------------------------------------------------------- void CDrawContext::setFillColor (const CColor& color) { - if (impl->device) - impl->device->setFillColor (color); - impl->currentState.fillColor = color; + currentState.fillColor = color; } -//----------------------------------------------------------------------------- -CColor CDrawContext::getFillColor () const { return impl->currentState.fillColor; } - //----------------------------------------------------------------------------- void CDrawContext::setFrameColor (const CColor& color) { - if (impl->device) - impl->device->setFrameColor (color); - impl->currentState.frameColor = color; + currentState.frameColor = color; } //----------------------------------------------------------------------------- -CColor CDrawContext::getFrameColor () const { return impl->currentState.frameColor; } - -//----------------------------------------------------------------------------- -void CDrawContext::setFontColor (const CColor& color) { impl->currentState.fontColor = color; } - -//----------------------------------------------------------------------------- -CColor CDrawContext::getFontColor () const { return impl->currentState.fontColor; } +void CDrawContext::setFontColor (const CColor& color) +{ + currentState.fontColor = color; +} //----------------------------------------------------------------------------- void CDrawContext::setFont (const CFontRef newFont, const CCoord& size, const int32_t& style) @@ -285,103 +181,94 @@ void CDrawContext::setFont (const CFontRef newFont, const CCoord& size, const in return; if ((size > 0 && newFont->getSize () != size) || (style != -1 && newFont->getStyle () != style)) { - impl->currentState.font = makeOwned (*newFont); + currentState.font = makeOwned (*newFont); if (size > 0) - impl->currentState.font->setSize (size); + currentState.font->setSize (size); if (style != -1) - impl->currentState.font->setStyle (style); + currentState.font->setStyle (style); } else { - impl->currentState.font = newFont; + currentState.font = newFont; } } -//----------------------------------------------------------------------------- -const CFontRef CDrawContext::getFont () const { return impl->currentState.font; } - //----------------------------------------------------------------------------- void CDrawContext::setGlobalAlpha (float newAlpha) { - if (impl->device) - impl->device->setGlobalAlpha (newAlpha); - impl->currentState.globalAlpha = newAlpha; + currentState.globalAlpha = newAlpha; } -//----------------------------------------------------------------------------- -float CDrawContext::getGlobalAlpha () const { return impl->currentState.globalAlpha; } - //----------------------------------------------------------------------------- const UTF8String& CDrawContext::getDrawString (UTF8StringPtr string) { - if (impl->drawStringHelper == nullptr) - impl->drawStringHelper = new UTF8String (string); + if (drawStringHelper == nullptr) + drawStringHelper = new UTF8String (string); else - impl->drawStringHelper->assign (string); - return *impl->drawStringHelper; + drawStringHelper->assign (string); + return *drawStringHelper; } //----------------------------------------------------------------------------- void CDrawContext::clearDrawString () { - if (impl->drawStringHelper) - impl->drawStringHelper->clear (); + if (drawStringHelper) + drawStringHelper->clear (); } //------------------------------------------------------------------------ CCoord CDrawContext::getStringWidth (IPlatformString* string) { CCoord result = -1; - if (impl->currentState.font == nullptr || string == nullptr) + if (currentState.font == nullptr || string == nullptr) return result; - - if (auto painter = impl->currentState.font->getFontPainter ()) - result = painter->getStringWidth (impl->device, string, true); - + + if (auto painter = currentState.font->getFontPainter ()) + result = painter->getStringWidth (this, string, true); + return result; } //------------------------------------------------------------------------ void CDrawContext::drawString (IPlatformString* string, const CRect& _rect, const CHoriTxtAlign hAlign, bool antialias) { - if (!string || impl->currentState.font == nullptr) + if (!string || currentState.font == nullptr) return; - auto painter = impl->currentState.font->getFontPainter (); + auto painter = currentState.font->getFontPainter (); if (painter == nullptr) return; CRect rect (_rect); double capHeight = -1; - auto platformFont = impl->currentState.font->getPlatformFont (); + auto platformFont = currentState.font->getPlatformFont (); if (platformFont) capHeight = platformFont->getCapHeight (); if (capHeight > 0.) rect.bottom -= (rect.getHeight () / 2. - capHeight / 2.); else - rect.bottom -= (rect.getHeight () / 2. - impl->currentState.font->getSize () / 2.) + 1.; + rect.bottom -= (rect.getHeight () / 2. - currentState.font->getSize () / 2.) + 1.; if (hAlign != kLeftText) { - CCoord stringWidth = painter->getStringWidth (impl->device, string, antialias); + CCoord stringWidth = painter->getStringWidth (this, string, antialias); if (hAlign == kRightText) rect.left = rect.right - stringWidth; else rect.left = rect.left + (rect.getWidth () / 2.) - (stringWidth / 2.); } - painter->drawString (impl->device, string, CPoint (rect.left, rect.bottom), - impl->currentState.fontColor, antialias); + painter->drawString (this, string, CPoint (rect.left, rect.bottom), antialias); } //------------------------------------------------------------------------ void CDrawContext::drawString (IPlatformString* string, const CPoint& point, bool antialias) { - if (string == nullptr || impl->currentState.font == nullptr) + if (string == nullptr || currentState.font == nullptr) return; - - if (auto painter = impl->currentState.font->getFontPainter ()) - painter->drawString (impl->device, string, point, impl->currentState.fontColor, antialias); + + if (auto painter = currentState.font->getFontPainter ()) + painter->drawString (this, string, point, antialias); } //----------------------------------------------------------------------------- @@ -410,26 +297,6 @@ void CDrawContext::fillRectWithBitmap (CBitmap* bitmap, const CRect& srcRect, co if (srcRect.isEmpty () || dstRect.isEmpty ()) return; - if (impl->device) - { - if (auto deviceBitmapExt = impl->device->asBitmapExt ()) - { - double transformedScaleFactor = getScaleFactor (); - CGraphicsTransform t = getCurrentTransform (); - if (t.m11 == t.m22 && t.m12 == 0 && t.m21 == 0) - transformedScaleFactor *= t.m11; - - if (auto pb = bitmap->getBestPlatformBitmapForScaleFactor (transformedScaleFactor)) - { - if (deviceBitmapExt->fillRectWithBitmap (*pb, srcRect, dstRect, alpha, - getBitmapInterpolationQuality ())) - { - return; - } - } - } - } - CRect bitmapPartRect; CPoint sourceOffset (srcRect.left, srcRect.top); @@ -461,26 +328,6 @@ void CDrawContext::fillRectWithBitmap (CBitmap* bitmap, const CRect& srcRect, co //----------------------------------------------------------------------------- void CDrawContext::drawBitmapNinePartTiled (CBitmap* bitmap, const CRect& dest, const CNinePartTiledDescription& desc, float alpha) { - if (impl->device) - { - if (auto deviceBitmapExt = impl->device->asBitmapExt ()) - { - double transformedScaleFactor = getScaleFactor (); - CGraphicsTransform t = getCurrentTransform (); - if (t.m11 == t.m22 && t.m12 == 0 && t.m21 == 0) - transformedScaleFactor *= t.m11; - - if (auto pb = bitmap->getBestPlatformBitmapForScaleFactor (transformedScaleFactor)) - { - if (deviceBitmapExt->drawBitmapNinePartTiled (*pb, dest, desc, alpha, - getBitmapInterpolationQuality ())) - { - return; - } - } - } - } - CRect myBitmapBounds (0, 0, bitmap->getWidth (), bitmap->getHeight ()); CRect mySourceRect [CNinePartTiledDescription::kPartCount]; CRect myDestRect [CNinePartTiledDescription::kPartCount]; @@ -495,38 +342,34 @@ void CDrawContext::drawBitmapNinePartTiled (CBitmap* bitmap, const CRect& dest, //----------------------------------------------------------------------------- CGraphicsPath* CDrawContext::createRoundRectGraphicsPath (const CRect& size, CCoord radius) { - if (auto path = createGraphicsPath ()) + CGraphicsPath* path = createGraphicsPath (); + if (path) { path->addRoundRect (size, radius); - return path; } - return {}; + return path; } //----------------------------------------------------------------------------- void CDrawContext::pushTransform (const CGraphicsTransform& transformation) { - vstgui_assert (!impl->transformStack.empty ()); - const CGraphicsTransform& currentTransform = impl->transformStack.top (); + vstgui_assert (!transformStack.empty ()); + const CGraphicsTransform& currentTransform = transformStack.top (); CGraphicsTransform newTransform = currentTransform * transformation; - impl->transformStack.push (newTransform); - if (impl->device) - impl->device->setTransformMatrix (newTransform); + transformStack.push (newTransform); } //----------------------------------------------------------------------------- void CDrawContext::popTransform () { - vstgui_assert (impl->transformStack.size () > 1); - impl->transformStack.pop (); - if (impl->device) - impl->device->setTransformMatrix (impl->transformStack.top ()); + vstgui_assert (transformStack.size () > 1); + transformStack.pop (); } //----------------------------------------------------------------------------- const CGraphicsTransform& CDrawContext::getCurrentTransform () const { - return impl->transformStack.top (); + return transformStack.top (); } //------------------------------------------------------------------------ @@ -535,212 +378,4 @@ CCoord CDrawContext::getHairlineSize () const return 1. / (getScaleFactor () * getCurrentTransform ().m11); } -//------------------------------------------------------------------------ -static PlatformGraphicsDrawStyle convert (CDrawStyle s) -{ - switch (s) - { - case CDrawStyle::kDrawFilled: - return PlatformGraphicsDrawStyle::Filled; - case CDrawStyle::kDrawStroked: - return PlatformGraphicsDrawStyle::Stroked; - case CDrawStyle::kDrawFilledAndStroked: - return PlatformGraphicsDrawStyle::FilledAndStroked; - default: - assert (false); - } - return {}; -} - -//------------------------------------------------------------------------ -void CDrawContext::drawLine (const LinePair& line) -{ - if (impl->device) - impl->device->drawLine (line); -} - -//------------------------------------------------------------------------ -void CDrawContext::drawLines (const LineList& lines) -{ - if (impl->device) - impl->device->drawLines (lines); -} - -//------------------------------------------------------------------------ -void CDrawContext::drawPolygon (const PointList& polygonPointList, const CDrawStyle drawStyle) -{ - if (impl->device) - impl->device->drawPolygon (polygonPointList, convert (drawStyle)); -} - -//------------------------------------------------------------------------ -void CDrawContext::drawRect (const CRect& rect, const CDrawStyle drawStyle) -{ - if (impl->device) - impl->device->drawRect (rect, convert (drawStyle)); -} - -//------------------------------------------------------------------------ -void CDrawContext::drawArc (const CRect& rect, const float startAngle1, const float endAngle2, - const CDrawStyle drawStyle) -{ - if (impl->device) - impl->device->drawArc (rect, startAngle1, endAngle2, convert (drawStyle)); -} - -//------------------------------------------------------------------------ -void CDrawContext::drawEllipse (const CRect& rect, const CDrawStyle drawStyle) -{ - if (impl->device) - impl->device->drawEllipse (rect, convert (drawStyle)); -} - -//------------------------------------------------------------------------ -void CDrawContext::drawPoint (const CPoint& point, const CColor& color) -{ - if (impl->device && impl->device->drawPoint (point, color)) - return; - - // if the platform does not support drawing points, emulate it somehow - - saveGlobalState (); - - CRect r (point.x, point.y, point.x, point.y); - r.inset (-0.5, -0.5); - setDrawMode (kAliasing); - setFillColor (color); - drawRect (r, kDrawFilled); - - restoreGlobalState (); -} - -//------------------------------------------------------------------------ -void CDrawContext::drawBitmap (CBitmap* bitmap, const CRect& dest, const CPoint& offset, - float alpha) -{ - if (impl->device) - { - double transformedScaleFactor = getScaleFactor (); - CGraphicsTransform t = getCurrentTransform (); - if (t.m11 == t.m22 && t.m12 == 0 && t.m21 == 0) - transformedScaleFactor *= t.m11; - if (auto pb = bitmap->getBestPlatformBitmapForScaleFactor (transformedScaleFactor)) - impl->device->drawBitmap (*pb, dest, offset, alpha, getBitmapInterpolationQuality ()); - } -} - -//------------------------------------------------------------------------ -void CDrawContext::clearRect (const CRect& rect) -{ - if (impl->device) - impl->device->clearRect (rect); -} - -//------------------------------------------------------------------------ -static PlatformGraphicsPathDrawMode convert (CDrawContext::PathDrawMode mode) -{ - switch (mode) - { - case CDrawContext::PathDrawMode::kPathFilled: - return PlatformGraphicsPathDrawMode::Filled; - case CDrawContext::PathDrawMode::kPathStroked: - return PlatformGraphicsPathDrawMode::Stroked; - case CDrawContext::PathDrawMode::kPathFilledEvenOdd: - return PlatformGraphicsPathDrawMode::FilledEvenOdd; - default: - assert (false); - } - return {}; -} - -//------------------------------------------------------------------------ -void CDrawContext::drawGraphicsPath (CGraphicsPath* path, PathDrawMode mode, - CGraphicsTransform* transformation) -{ - if (impl->device) - { - if (auto& pp = path->getPlatformPath (mode == kPathFilledEvenOdd - ? PlatformGraphicsPathFillMode::Alternate - : PlatformGraphicsPathFillMode::Winding)) - impl->device->drawGraphicsPath (*pp.get (), convert (mode), transformation); - } -} - -//------------------------------------------------------------------------ -void CDrawContext::fillLinearGradient (CGraphicsPath* path, const CGradient& gradient, - const CPoint& startPoint, const CPoint& endPoint, - bool evenOdd, CGraphicsTransform* transformation) -{ - if (impl->device) - { - if (auto& platformGradient = gradient.getPlatformGradient ()) - { - if (auto& pp = path->getPlatformPath (evenOdd ? PlatformGraphicsPathFillMode::Alternate - : PlatformGraphicsPathFillMode::Winding)) - { - impl->device->fillLinearGradient (*pp.get (), *platformGradient, startPoint, - endPoint, evenOdd, transformation); - } - } - } -} - -//------------------------------------------------------------------------ -void CDrawContext::fillRadialGradient (CGraphicsPath* path, const CGradient& gradient, - const CPoint& center, CCoord radius, - const CPoint& originOffset, bool evenOdd, - CGraphicsTransform* transformation) -{ - if (impl->device) - { - if (auto& platformGradient = gradient.getPlatformGradient ()) - { - if (auto& pp = path->getPlatformPath (evenOdd ? PlatformGraphicsPathFillMode::Alternate - : PlatformGraphicsPathFillMode::Winding)) - { - impl->device->fillRadialGradient (*pp.get (), *platformGradient, center, radius, - originOffset, evenOdd, transformation); - } - } - } -} - -//------------------------------------------------------------------------ -CGraphicsPath* CDrawContext::createGraphicsPath () -{ - if (impl->device) - return new CGraphicsPath (impl->device->getGraphicsPathFactory ()); - return nullptr; -} - -//------------------------------------------------------------------------ -CGraphicsPath* CDrawContext::createTextPath (const CFontRef font, UTF8StringPtr text) -{ - if (impl->device) - { - auto platformFont = font->getPlatformFont (); - auto pathFactory = impl->device->getGraphicsPathFactory (); - if (platformFont && pathFactory) - { - if (auto textPath = pathFactory->createTextPath (platformFont, text)) - return new CGraphicsPath (pathFactory, std::move (textPath)); - } - } - return nullptr; -} - -//------------------------------------------------------------------------ -void CDrawContext::beginDraw () -{ - if (impl->device) - impl->device->beginDraw (); -} - -//------------------------------------------------------------------------ -void CDrawContext::endDraw () -{ - if (impl->device) - impl->device->endDraw (); -} - } // VSTGUI diff --git a/vstgui/lib/cdrawcontext.h b/vstgui/lib/cdrawcontext.h index 6aa80f83e..697b8f37a 100644 --- a/vstgui/lib/cdrawcontext.h +++ b/vstgui/lib/cdrawcontext.h @@ -14,6 +14,7 @@ #include "clinestyle.h" #include "cdrawdefs.h" #include +#include #include namespace VSTGUI { @@ -44,49 +45,41 @@ class CDrawContext : public AtomicReferenceCounted /// @name Draw primitives //----------------------------------------------------------------------------- //@{ - using LinePair = VSTGUI::LinePair; - using LineList = VSTGUI::LineList; - using PointList = VSTGUI::PointList; + using LinePair = std::pair; + using LineList = std::vector; + using PointList = std::vector; - inline void drawLine (const CPoint& start, const CPoint& end) - { - drawLine (std::make_pair (start, end)); - } + inline void drawLine (const CPoint& start, const CPoint& end) { drawLine (std::make_pair (start, end)); } /** draw a line */ - void drawLine (const LinePair& line); + virtual void drawLine (const LinePair& line) = 0; /** draw multiple lines at once */ - void drawLines (const LineList& lines); + virtual void drawLines (const LineList& lines) = 0; /** draw a polygon */ - void drawPolygon (const PointList& polygonPointList, const CDrawStyle drawStyle = kDrawStroked); + virtual void drawPolygon (const PointList& polygonPointList, const CDrawStyle drawStyle = kDrawStroked) = 0; /** draw a rect */ - void drawRect (const CRect& rect, const CDrawStyle drawStyle = kDrawStroked); + virtual void drawRect (const CRect &rect, const CDrawStyle drawStyle = kDrawStroked) = 0; /** draw an arc, angles are in degree */ - void drawArc (const CRect& rect, const float startAngle1, const float endAngle2, - const CDrawStyle drawStyle = kDrawStroked); + virtual void drawArc (const CRect &rect, const float startAngle1, const float endAngle2, const CDrawStyle drawStyle = kDrawStroked) = 0; /** draw an ellipse */ - void drawEllipse (const CRect& rect, const CDrawStyle drawStyle = kDrawStroked); + virtual void drawEllipse (const CRect &rect, const CDrawStyle drawStyle = kDrawStroked) = 0; /** draw a point */ - void drawPoint (const CPoint& point, const CColor& color); + virtual void drawPoint (const CPoint &point, const CColor& color) = 0; /** don't call directly, please use CBitmap::draw instead */ - void drawBitmap (CBitmap* bitmap, const CRect& dest, const CPoint& offset = CPoint (0, 0), - float alpha = 1.f); - void drawBitmapNinePartTiled (CBitmap* bitmap, const CRect& dest, - const CNinePartTiledDescription& desc, float alpha = 1.f); - void fillRectWithBitmap (CBitmap* bitmap, const CRect& srcRect, const CRect& dstRect, - float alpha); + virtual void drawBitmap (CBitmap* bitmap, const CRect& dest, const CPoint& offset = CPoint (0, 0), float alpha = 1.f) = 0; + virtual void drawBitmapNinePartTiled (CBitmap* bitmap, const CRect& dest, const CNinePartTiledDescription& desc, float alpha = 1.f); + virtual void fillRectWithBitmap (CBitmap* bitmap, const CRect& srcRect, const CRect& dstRect, float alpha); /** clears the rect (makes r = 0, g = 0, b = 0, a = 0) */ - void clearRect (const CRect& rect); + virtual void clearRect (const CRect& rect) = 0; //@} //----------------------------------------------------------------------------- // @name Bitmap Interpolation Quality //----------------------------------------------------------------------------- //@{ - /** set the current bitmap interpolation quality */ - void setBitmapInterpolationQuality (BitmapInterpolationQuality quality); - /** get the current bitmap interpolation quality */ - BitmapInterpolationQuality getBitmapInterpolationQuality () const; + virtual void setBitmapInterpolationQuality (BitmapInterpolationQuality quality); ///< set the current bitmap interpolation quality + const BitmapInterpolationQuality& getBitmapInterpolationQuality () const { return currentState.bitmapQuality; } ///< get the current bitmap interpolation quality + //@} //----------------------------------------------------------------------------- @@ -94,14 +87,14 @@ class CDrawContext : public AtomicReferenceCounted //----------------------------------------------------------------------------- //@{ /** set the current line style */ - void setLineStyle (const CLineStyle& style); + virtual void setLineStyle (const CLineStyle& style); /** get the current line style */ - const CLineStyle& getLineStyle () const; + const CLineStyle& getLineStyle () const { return currentState.lineStyle; } /** set the current line width */ - void setLineWidth (CCoord width); + virtual void setLineWidth (CCoord width); /** get the current line width */ - CCoord getLineWidth () const; + CCoord getLineWidth () const { return currentState.frameWidth; } //@} //----------------------------------------------------------------------------- @@ -109,9 +102,9 @@ class CDrawContext : public AtomicReferenceCounted //----------------------------------------------------------------------------- //@{ /** set the current draw mode, see CDrawMode */ - void setDrawMode (CDrawMode mode); + virtual void setDrawMode (CDrawMode mode); /** get the current draw mode, see CDrawMode */ - CDrawMode getDrawMode () const; + CDrawMode getDrawMode () const { return currentState.drawMode; } //@} //----------------------------------------------------------------------------- @@ -119,11 +112,11 @@ class CDrawContext : public AtomicReferenceCounted //----------------------------------------------------------------------------- //@{ /** set the current clip */ - void setClipRect (const CRect& clip); + virtual void setClipRect (const CRect &clip); /** get the current clip */ CRect& getClipRect (CRect &clip) const; /** reset the clip to the default state */ - void resetClipRect (); + virtual void resetClipRect (); //@} //----------------------------------------------------------------------------- @@ -131,13 +124,13 @@ class CDrawContext : public AtomicReferenceCounted //----------------------------------------------------------------------------- //@{ /** set current fill color */ - void setFillColor (const CColor& color); + virtual void setFillColor (const CColor& color); /** get current fill color */ - CColor getFillColor () const; + CColor getFillColor () const { return currentState.fillColor; } /** set current stroke color */ - void setFrameColor (const CColor& color); + virtual void setFrameColor (const CColor& color); /** get current stroke color */ - CColor getFrameColor () const; + CColor getFrameColor () const { return currentState.frameColor; } //@} //----------------------------------------------------------------------------- @@ -145,13 +138,13 @@ class CDrawContext : public AtomicReferenceCounted //----------------------------------------------------------------------------- //@{ /** set current font color */ - void setFontColor (const CColor& color); + virtual void setFontColor (const CColor& color); /** get current font color */ - CColor getFontColor () const; + CColor getFontColor () const { return currentState.fontColor; } /** set current font */ - void setFont (const CFontRef font, const CCoord& size = 0, const int32_t& style = -1); + virtual void setFont (const CFontRef font, const CCoord& size = 0, const int32_t& style = -1); /** get current font */ - const CFontRef getFont () const; + const CFontRef getFont () const { return currentState.font; } //@} //----------------------------------------------------------------------------- @@ -161,16 +154,14 @@ class CDrawContext : public AtomicReferenceCounted /** get the width of an UTF-8 encoded string */ CCoord getStringWidth (UTF8StringPtr pStr); /** draw an UTF-8 encoded string */ - void drawString (UTF8StringPtr string, const CRect& _rect, - const CHoriTxtAlign hAlign = kCenterText, bool antialias = true); + void drawString (UTF8StringPtr string, const CRect& _rect, const CHoriTxtAlign hAlign = kCenterText, bool antialias = true); /** draw an UTF-8 encoded string */ void drawString (UTF8StringPtr string, const CPoint& _point, bool antialias = true); /** get the width of a platform string */ CCoord getStringWidth (IPlatformString* pStr); /** draw a platform string */ - void drawString (IPlatformString* string, const CRect& _rect, - const CHoriTxtAlign hAlign = kCenterText, bool antialias = true); + void drawString (IPlatformString* string, const CRect& _rect, const CHoriTxtAlign hAlign = kCenterText, bool antialias = true); /** draw a platform string */ void drawString (IPlatformString* string, const CPoint& _point, bool antialias = true); //@} @@ -180,17 +171,17 @@ class CDrawContext : public AtomicReferenceCounted //----------------------------------------------------------------------------- //@{ /** sets the global alpha value[0..1] */ - void setGlobalAlpha (float newAlpha); + virtual void setGlobalAlpha (float newAlpha); /** get current global alpha value */ - float getGlobalAlpha () const; + float getGlobalAlpha () const { return currentState.globalAlpha; } //@} //----------------------------------------------------------------------------- /// @name Global State Stack //----------------------------------------------------------------------------- //@{ - void saveGlobalState (); - void restoreGlobalState (); + virtual void saveGlobalState (); + virtual void restoreGlobalState (); //@} //----------------------------------------------------------------------------- @@ -198,11 +189,11 @@ class CDrawContext : public AtomicReferenceCounted //----------------------------------------------------------------------------- //@{ const CGraphicsTransform& getCurrentTransform () const; - const CRect& getAbsoluteClipRect () const; + const CRect& getAbsoluteClipRect () const { return currentState.clipRect; } /** returns the backend scale factor. */ - double getScaleFactor () const; - + virtual double getScaleFactor () const { return 1.; } + /** returns the current line size which corresponds to one pixel on screen. * * do not cache this value, instead ask for it every time you need it. @@ -215,9 +206,9 @@ class CDrawContext : public AtomicReferenceCounted //----------------------------------------------------------------------------- //@{ /** create a graphics path object, you need to forget it after usage */ - CGraphicsPath* createGraphicsPath (); + virtual CGraphicsPath* createGraphicsPath () = 0; /** create a graphics path from a text */ - CGraphicsPath* createTextPath (const CFontRef font, UTF8StringPtr text); + virtual CGraphicsPath* createTextPath (const CFontRef font, UTF8StringPtr text) = 0; /** create a rect with round corners as graphics path, you need to forget it after usage */ CGraphicsPath* createRoundRectGraphicsPath (const CRect& size, CCoord radius); @@ -229,32 +220,22 @@ class CDrawContext : public AtomicReferenceCounted kPathStroked }; - void drawGraphicsPath (CGraphicsPath* path, PathDrawMode mode = kPathFilled, - CGraphicsTransform* transformation = nullptr); - void fillLinearGradient (CGraphicsPath* path, const CGradient& gradient, - const CPoint& startPoint, const CPoint& endPoint, bool evenOdd = false, - CGraphicsTransform* transformation = nullptr); - void fillRadialGradient (CGraphicsPath* path, const CGradient& gradient, const CPoint& center, - CCoord radius, const CPoint& originOffset = CPoint (0, 0), - bool evenOdd = false, CGraphicsTransform* transformation = nullptr); + virtual void drawGraphicsPath (CGraphicsPath* path, PathDrawMode mode = kPathFilled, CGraphicsTransform* transformation = nullptr) = 0; + virtual void fillLinearGradient (CGraphicsPath* path, const CGradient& gradient, const CPoint& startPoint, const CPoint& endPoint, bool evenOdd = false, CGraphicsTransform* transformation = nullptr) = 0; + virtual void fillRadialGradient (CGraphicsPath* path, const CGradient& gradient, const CPoint& center, CCoord radius, const CPoint& originOffset = CPoint (0,0), bool evenOdd = false, CGraphicsTransform* transformation = nullptr) = 0; //@} - void beginDraw (); - void endDraw (); - - const CRect& getSurfaceRect () const; - - CDrawContext (const PlatformGraphicsDeviceContextPtr device, const CRect& surfaceRect, - double scaleFactor); - ~CDrawContext () noexcept override; + virtual void beginDraw () {} + virtual void endDraw () {} - const PlatformGraphicsDeviceContextPtr& getPlatformDeviceContext () const; + const CRect& getSurfaceRect () const { return surfaceRect; } protected: CDrawContext () = delete; explicit CDrawContext (const CRect& surfaceRect); + ~CDrawContext () noexcept override; - void init (); + virtual void init (); void pushTransform (const CGraphicsTransform& transformation); void popTransform (); @@ -262,9 +243,40 @@ class CDrawContext : public AtomicReferenceCounted const UTF8String& getDrawString (UTF8StringPtr string); void clearDrawString (); + /// @cond ignore + struct CDrawContextState + { + SharedPointer font; + CColor frameColor {kTransparentCColor}; + CColor fillColor {kTransparentCColor}; + CColor fontColor {kTransparentCColor}; + CCoord frameWidth {0.}; + CPoint penLoc {}; + CRect clipRect {}; + CLineStyle lineStyle {kLineOnOffDash}; + CDrawMode drawMode {kAntiAliasing}; + float globalAlpha {1.f}; + BitmapInterpolationQuality bitmapQuality {BitmapInterpolationQuality::kDefault}; + + CDrawContextState () = default; + CDrawContextState (const CDrawContextState& state); + CDrawContextState& operator= (const CDrawContextState& state) = default; + CDrawContextState (CDrawContextState&& state) noexcept; + CDrawContextState& operator= (CDrawContextState&& state) noexcept; + }; + /// @endcond + + const CDrawContextState& getCurrentState () const { return currentState; } + CDrawContextState& getCurrentState () { return currentState; } + private: - struct Impl; - std::unique_ptr impl; + UTF8String* drawStringHelper {nullptr}; + CRect surfaceRect; + + CDrawContextState currentState; + + std::stack globalStatesStack; + std::stack transformStack; }; //----------------------------------------------------------------------------- diff --git a/vstgui/lib/cexternalview.cpp b/vstgui/lib/cexternalview.cpp deleted file mode 100644 index 1cd128db2..000000000 --- a/vstgui/lib/cexternalview.cpp +++ /dev/null @@ -1,189 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#include "cexternalview.h" -#include "cframe.h" -#include "platform/iplatformframe.h" - -//------------------------------------------------------------------------ -namespace VSTGUI { - -//------------------------------------------------------------------------ -struct CExternalView::Impl -{ -private: - ExternalViewPtr view; - bool isAttached {false}; - - static ExternalView::IntRect fromCRect (const CRect& r) - { - ExternalView::IntRect res; - res.origin.x = static_cast (std::ceil (r.left)); - res.origin.y = static_cast (std::ceil (r.top)); - res.size.width = static_cast (std::floor (r.getWidth ())); - res.size.height = static_cast (std::floor (r.getHeight ())); - return res; - } - - static CRect calculateSize (CViewContainer* parent, CRect newSize) - { - CFrame* frame = parent ? parent->getFrame () : nullptr; - while (parent && parent != frame) - { - CRect parentSize = parent->getViewSize (); - parent->getTransform ().transform (newSize); - newSize.offset (parentSize.left, parentSize.top); - newSize.bound (parentSize); - parent = static_cast (parent->getParentView ()); - } - if (frame) - frame->getTransform ().transform (newSize); - return newSize; - } - -public: - Impl (const ExternalViewPtr& v) : view (v) {} - - void updateSize (CViewContainer* parent, CRect localSize, CRect globalSize) - { - if (!view) - return; - localSize = calculateSize (parent, localSize); - view->setViewSize (fromCRect (globalSize), fromCRect (localSize)); - } - - void attach (CFrame* frame) - { - if (!frame || !view) - return; - auto platformFrame = frame->getPlatformFrame (); - if (!platformFrame) - return; - ExternalView::PlatformViewType viewType = {}; - switch (platformFrame->getPlatformType ()) - { - case PlatformType::kHWND: - viewType = ExternalView::PlatformViewType::HWND; - break; - case PlatformType::kNSView: - viewType = ExternalView::PlatformViewType::NSView; - break; - default: - return; - } - if (!view->platformViewTypeSupported (viewType)) - return; - isAttached = view->attach (platformFrame->getPlatformRepresentation (), viewType); - } - void remove () - { - if (view && isAttached) - { - view->remove (); - isAttached = false; - } - } - void scaleFactorChanged (double scaleFactor) - { - if (view) - view->setContentScaleFactor (scaleFactor); - } - void takeFocus () - { - if (view) - view->takeFocus (); - } - void looseFocus () - { - if (view) - view->looseFocus (); - } - void enableMouse (bool state) - { - if (view) - view->setMouseEnabled (state); - } - - ExternalView::IView* getView () const { return view.get (); } -}; - -//------------------------------------------------------------------------ -CExternalView::CExternalView (const CRect& r, const ExternalViewPtr& view) : CView (r) -{ - impl = std::make_unique (view); - impl->getView ()->setTookFocusCallback ([this] () { - if (auto frame = getFrame ()) - frame->setFocusView (this); - }); -} - -//------------------------------------------------------------------------ -CExternalView::~CExternalView () noexcept { impl->getView ()->setTookFocusCallback (nullptr); } - -//------------------------------------------------------------------------ -bool CExternalView::attached (CView* parent) -{ - if (CView::attached (parent)) - { - auto frame = parent->getFrame (); - impl->updateSize (parent->asViewContainer (), getViewSize (), - translateToGlobal (getViewSize ())); - impl->scaleFactorChanged (frame->getScaleFactor ()); - impl->attach (frame); - frame->registerScaleFactorChangedListener (this); - return true; - } - return false; -} - -//------------------------------------------------------------------------ -bool CExternalView::removed (CView* parent) -{ - if (auto frame = parent->getFrame ()) - { - frame->unregisterScaleFactorChangedListener (this); - } - impl->remove (); - return CView::removed (parent); -} - -//------------------------------------------------------------------------ -void CExternalView::takeFocus () { impl->takeFocus (); } - -//------------------------------------------------------------------------ -void CExternalView::looseFocus () { impl->looseFocus (); } - -//------------------------------------------------------------------------ -void CExternalView::setViewSize (const CRect& rect, bool invalid) -{ - CView::setViewSize (rect, invalid); - impl->updateSize (getParentView () ? getParentView ()->asViewContainer () : nullptr, - getViewSize (), translateToGlobal (getViewSize ())); -} - -//------------------------------------------------------------------------ -void CExternalView::parentSizeChanged () -{ - impl->updateSize (getParentView () ? getParentView ()->asViewContainer () : nullptr, - getViewSize (), translateToGlobal (getViewSize ())); -} - -//------------------------------------------------------------------------ -void CExternalView::onScaleFactorChanged (CFrame* frame, double newScaleFactor) -{ - impl->scaleFactorChanged (newScaleFactor); -} - -//------------------------------------------------------------------------ -void CExternalView::setMouseEnabled (bool enable) -{ - impl->enableMouse (enable); - CView::setMouseEnabled (enable); -} - -//------------------------------------------------------------------------ -ExternalView::IView* CExternalView::getExternalView () const { return impl->getView (); } - -//------------------------------------------------------------------------ -} // VSTGUI diff --git a/vstgui/lib/cexternalview.h b/vstgui/lib/cexternalview.h deleted file mode 100644 index 1d468e907..000000000 --- a/vstgui/lib/cexternalview.h +++ /dev/null @@ -1,52 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#pragma once - -#include "cview.h" -#include "iscalefactorchangedlistener.h" -#include "iexternalview.h" -#include - -//------------------------------------------------------------------------ -namespace VSTGUI { - -//------------------------------------------------------------------------ -/** View to embed non CView views into VSTGUI - * - * This view is the umbrella for views from other view systems (like HWND child windows or - * NSViews). - * The actual implementation for the external view must be done via ExternalView::IView - * - * @ingroup new_in_4_13 - */ -class CExternalView : public CView, - public IScaleFactorChangedListener, - public ExternalView::IViewEmbedder -{ -public: - using ExternalViewPtr = std::shared_ptr; - - CExternalView (const CRect& r, const ExternalViewPtr& view); - ~CExternalView () noexcept; - - bool attached (CView* parent) override; - bool removed (CView* parent) override; - void takeFocus () override; - void looseFocus () override; - void setViewSize (const CRect& rect, bool invalid = true) override; - void parentSizeChanged () override; - void onScaleFactorChanged (CFrame* frame, double newScaleFactor) override; - void setMouseEnabled (bool enable = true) override; - - ExternalView::IView* getExternalView () const override; - -private: - - struct Impl; - std::unique_ptr impl; -}; - -//------------------------------------------------------------------------ -} // VSTGUI diff --git a/vstgui/lib/cfont.h b/vstgui/lib/cfont.h index b4688ed41..5588e6626 100644 --- a/vstgui/lib/cfont.h +++ b/vstgui/lib/cfont.h @@ -11,6 +11,18 @@ namespace VSTGUI { +//----------- +// @brief Text Face +//----------- +enum CTxtFace +{ + kNormalFace = 0, + kBoldFace = 1 << 1, + kItalicFace = 1 << 2, + kUnderlineFace = 1 << 3, + kStrikethroughFace = 1 << 4 +}; + //----------------------------------------------------------------------------- // CFontDesc Declaration //! @brief font class diff --git a/vstgui/lib/cframe.cpp b/vstgui/lib/cframe.cpp index e809e95ee..5e5e7f4b1 100644 --- a/vstgui/lib/cframe.cpp +++ b/vstgui/lib/cframe.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include namespace VSTGUI { @@ -1661,12 +1660,10 @@ IPlatformFrame* CFrame::getPlatformFrame () const } //----------------------------------------------------------------------------- -void CFrame::platformDrawRects (const PlatformGraphicsDeviceContextPtr& context, double scaleFactor, - const std::vector& rects) +bool CFrame::platformDrawRect (CDrawContext* context, const CRect& rect) { - CDrawContext drawContext (context, getViewSize (), scaleFactor); - for (auto rect : rects) - drawRect (&drawContext, rect); + drawRect (context, rect); + return true; } //----------------------------------------------------------------------------- diff --git a/vstgui/lib/cframe.h b/vstgui/lib/cframe.h index b946cb301..f5804721a 100644 --- a/vstgui/lib/cframe.h +++ b/vstgui/lib/cframe.h @@ -256,8 +256,7 @@ class CFrame final : public CViewContainer, public IPlatformFrameCallback void dispatchNewScaleFactor (double newScaleFactor); // platform frame - void platformDrawRects (const PlatformGraphicsDeviceContextPtr& context, double scaleFactor, - const std::vector& rects) override; + bool platformDrawRect (CDrawContext* context, const CRect& rect) override; void platformOnEvent (Event& event) override; DragOperation platformOnDragEnter (DragEventData data) override; DragOperation platformOnDragMove (DragEventData data) override; diff --git a/vstgui/lib/cgraphicspath.cpp b/vstgui/lib/cgraphicspath.cpp index f49d308da..9ca5c162b 100644 --- a/vstgui/lib/cgraphicspath.cpp +++ b/vstgui/lib/cgraphicspath.cpp @@ -178,8 +178,6 @@ CGradient* CGraphicsPath::createGradient (double color1Start, double color2Start //----------------------------------------------------------------------------- void CGraphicsPath::makePlatformGraphicsPath (PlatformGraphicsPathFillMode fillMode) { - if (!factory) - return; path = factory->createPath (fillMode); if (!path) return; diff --git a/vstgui/lib/clayeredviewcontainer.cpp b/vstgui/lib/clayeredviewcontainer.cpp index e03512276..cf4f43267 100644 --- a/vstgui/lib/clayeredviewcontainer.cpp +++ b/vstgui/lib/clayeredviewcontainer.cpp @@ -191,35 +191,27 @@ void CLayeredViewContainer::drawRect (CDrawContext* pContext, const CRect& updat auto drawsIntoBitmap = false; if (auto offscreenContext = dynamic_cast (pContext)) drawsIntoBitmap = offscreenContext->getBitmap () != nullptr; - if (!layer || drawsIntoBitmap) + if (layer && !drawsIntoBitmap) + layer->draw (pContext, updateRect); + else CViewContainer::drawRect (pContext, updateRect); } //----------------------------------------------------------------------------- -void CLayeredViewContainer::drawViewLayerRects (const PlatformGraphicsDeviceContextPtr& context, - double scaleFactor, const std::vector& rects) +void CLayeredViewContainer::drawViewLayer (CDrawContext* context, const CRect& _dirtyRect) { + CRect dirtyRect (_dirtyRect); + CGraphicsTransform drawTransform = getDrawTransform (); + drawTransform.inverse ().transform (dirtyRect); CRect visibleSize = getVisibleViewSize (); CRect viewSize = getViewSize (); CPoint p (viewSize.left < 0 ? viewSize.left - visibleSize.left : visibleSize.left, - viewSize.top < 0 ? viewSize.top - visibleSize.top : visibleSize.top); - - auto surfaceSize = getViewSize (); - surfaceSize.originize (); - CDrawContext drawContext (context, surfaceSize, scaleFactor); - CDrawContext::Transform transform ( - drawContext, drawTransform * CGraphicsTransform ().translate (-p.x, -p.y)); - for (auto dirtyRect : rects) - { - drawTransform.inverse ().transform (dirtyRect); - dirtyRect.offset (p.x, p.y); - drawContext.saveGlobalState (); - drawContext.setClipRect (dirtyRect); - CViewContainer::drawRect (&drawContext, dirtyRect); - drawContext.restoreGlobalState (); - } + viewSize.top < 0 ? viewSize.top - visibleSize.top : visibleSize.top); + dirtyRect.offset (p.x, p.y); + CDrawContext::Transform transform (*context, drawTransform * CGraphicsTransform ().translate (-p.x, -p.y)); + CViewContainer::drawRect (context, dirtyRect); } //----------------------------------------------------------------------------- diff --git a/vstgui/lib/clayeredviewcontainer.h b/vstgui/lib/clayeredviewcontainer.h index c69bd2ccb..73c958417 100644 --- a/vstgui/lib/clayeredviewcontainer.h +++ b/vstgui/lib/clayeredviewcontainer.h @@ -43,8 +43,7 @@ class CLayeredViewContainer : public CViewContainer, //----------------------------------------------------------------------------- protected: void drawRect (CDrawContext* pContext, const CRect& updateRect) override; - void drawViewLayerRects (const PlatformGraphicsDeviceContextPtr& context, double scaleFactor, - const std::vector& rects) override; + void drawViewLayer (CDrawContext* context, const CRect& dirtyRect) override; void viewContainerTransformChanged (CViewContainer* container) override; void onScaleFactorChanged (CFrame* frame, double newScaleFactor) override; void updateLayerSize (); diff --git a/vstgui/lib/coffscreencontext.cpp b/vstgui/lib/coffscreencontext.cpp index 0939c0e87..a92120d2f 100644 --- a/vstgui/lib/coffscreencontext.cpp +++ b/vstgui/lib/coffscreencontext.cpp @@ -6,7 +6,6 @@ #include "cframe.h" #include "cbitmap.h" #include "platform/platformfactory.h" -#include "platform/iplatformgraphicsdevice.h" namespace VSTGUI { @@ -23,15 +22,6 @@ COffscreenContext::COffscreenContext (const CRect& surfaceRect) { } -//----------------------------------------------------------------------------- -COffscreenContext::COffscreenContext (const PlatformGraphicsDeviceContextPtr device, - const CRect& surfaceRect, - const PlatformBitmapPtr& platformBitmap) -: CDrawContext (device, surfaceRect, platformBitmap->getScaleFactor ()) -, bitmap (makeOwned (platformBitmap)) -{ -} - //----------------------------------------------------------------------------- void COffscreenContext::copyFrom (CDrawContext *pContext, CRect destRect, CPoint srcOffset) { @@ -51,22 +41,7 @@ SharedPointer COffscreenContext::create (CFrame* frame, CCoor SharedPointer COffscreenContext::create (const CPoint& size, double scaleFactor) { if (size.x >= 1. && size.y >= 1.) - { - if (auto graphicsDevice = - getPlatformFactory ().getGraphicsDeviceFactory ().getDeviceForScreen ( - DefaultScreenIdentifier)) - { - if (auto bitmap = getPlatformFactory ().createBitmap (size * scaleFactor)) - { - bitmap->setScaleFactor (scaleFactor); - if (auto context = graphicsDevice->createBitmapContext (bitmap)) - { - CRect surfaceRect (CPoint (), size * scaleFactor); - return makeOwned (context, surfaceRect, bitmap); - } - } - } - } + return getPlatformFactory ().createOffscreenContext (size, scaleFactor); return nullptr; } diff --git a/vstgui/lib/coffscreencontext.h b/vstgui/lib/coffscreencontext.h index d140d2bd4..e4ef94f80 100644 --- a/vstgui/lib/coffscreencontext.h +++ b/vstgui/lib/coffscreencontext.h @@ -75,9 +75,6 @@ class COffscreenContext : public CDrawContext CBitmap* getBitmap () const { return bitmap; } - COffscreenContext (const PlatformGraphicsDeviceContextPtr device, const CRect& surfaceRect, - const PlatformBitmapPtr& platformBitmap); - protected: explicit COffscreenContext (CBitmap* bitmap); explicit COffscreenContext (const CRect& surfaceRect); diff --git a/vstgui/lib/controls/cfontchooser.cpp b/vstgui/lib/controls/cfontchooser.cpp index 95785fcdd..988f7dba8 100644 --- a/vstgui/lib/controls/cfontchooser.cpp +++ b/vstgui/lib/controls/cfontchooser.cpp @@ -4,7 +4,6 @@ #include "cfontchooser.h" #include "../cdatabrowser.h" -#include "../cdrawcontext.h" #include "cbuttons.h" #include "ctextedit.h" #include "cscrollbar.h" diff --git a/vstgui/lib/controls/csearchtextedit.cpp b/vstgui/lib/controls/csearchtextedit.cpp index 329da775d..48a36353e 100644 --- a/vstgui/lib/controls/csearchtextedit.cpp +++ b/vstgui/lib/controls/csearchtextedit.cpp @@ -6,7 +6,6 @@ #include "../cframe.h" #include "../cgraphicspath.h" -#include "../cdrawcontext.h" namespace VSTGUI { diff --git a/vstgui/lib/controls/ctextedit.cpp b/vstgui/lib/controls/ctextedit.cpp index bf62350c9..082dae08e 100644 --- a/vstgui/lib/controls/ctextedit.cpp +++ b/vstgui/lib/controls/ctextedit.cpp @@ -5,7 +5,6 @@ #include "ctextedit.h" #include "itexteditlistener.h" #include "../cframe.h" -#include "../cdrawcontext.h" #include "../events.h" #include "../platform/iplatformframe.h" #include diff --git a/vstgui/lib/controls/ctextlabel.cpp b/vstgui/lib/controls/ctextlabel.cpp index 3e3e29fe6..2a3659e3b 100644 --- a/vstgui/lib/controls/ctextlabel.cpp +++ b/vstgui/lib/controls/ctextlabel.cpp @@ -382,8 +382,7 @@ void CMultiLineTextLabel::calculateWrapLine (CDrawContext* context, break; auto tmpEnd = pos; UTF8String tmp ({start.base (), (++tmpEnd).base ()}); - auto width = fontPainter->getStringWidth ( - context ? context->getPlatformDeviceContext () : nullptr, tmp.getPlatformString ()); + auto width = fontPainter->getStringWidth (context, tmp.getPlatformString ()); if (width > maxWidth) { if (lastSeparator == element.first.end ()) @@ -429,8 +428,7 @@ void CMultiLineTextLabel::recalculateLines (CDrawContext* context) while (std::getline (stream, line, '\n')) { UTF8String str (std::move (line)); - auto width = fontPainter->getStringWidth ( - context ? context->getPlatformDeviceContext () : nullptr, str.getPlatformString ()); + auto width = fontPainter->getStringWidth (context, str.getPlatformString ()); elements.emplace_back (std::move (str), width); } diff --git a/vstgui/lib/iexternalview.h b/vstgui/lib/iexternalview.h deleted file mode 100644 index b75a94c7d..000000000 --- a/vstgui/lib/iexternalview.h +++ /dev/null @@ -1,121 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#pragma once - -#include -#include - -//------------------------------------------------------------------------ -namespace VSTGUI { -namespace ExternalView { - -//------------------------------------------------------------------------ -enum class PlatformViewType : uint32_t -{ - HWND, - NSView, - - Unknown -}; - -//------------------------------------------------------------------------ -struct IntPoint -{ - int64_t x {0}; - int64_t y {0}; -}; - -//------------------------------------------------------------------------ -struct IntSize -{ - int64_t width {0}; - int64_t height {0}; -}; - -//------------------------------------------------------------------------ -struct IntRect -{ - IntPoint origin; - IntSize size; -}; - -//------------------------------------------------------------------------ -/** interface for embedding views from external view systems - * - * @ingroup new_in_4_13 - */ -struct IView -{ - virtual ~IView () noexcept = default; - - /** check if the view supports the platform view type */ - virtual bool platformViewTypeSupported (PlatformViewType type) = 0; - /** attach the view to the parent */ - virtual bool attach (void* parent, PlatformViewType parentViewType) = 0; - /** remove the view from its parent */ - virtual bool remove () = 0; - - /** set the size and position of the view - * - * the coordinate system for the parameters used are system native, - * this means on Windows the scale factor is already applied to the coordinates - * and on macOS they are not (as with all NSViews) - * - * the visible rectangle is the rectangle clipped to all its parent views - * while the frame rectangle is the full size of the view if it would not - * be clipped. this way it is possible to support views inside of scroll views - * and the like. See the examples how this is done. - */ - virtual void setViewSize (IntRect frame, IntRect visible) = 0; - /** set the scale factor in use */ - virtual void setContentScaleFactor (double scaleFactor) = 0; - - /** enable or disable mouse handling for the view */ - virtual void setMouseEnabled (bool state) = 0; - - /** the view should take focus */ - virtual void takeFocus () = 0; - /** the view should loose focus */ - virtual void looseFocus () = 0; - - using TookFocusCallback = std::function; - /** a callback the embedder sets on the view to get notified when the view took focus */ - virtual void setTookFocusCallback (const TookFocusCallback& callback) = 0; -}; - -//------------------------------------------------------------------------ -/** interface for view embedder classes - * - * @ingroup new_in_4_13 - */ -struct IViewEmbedder -{ - virtual ~IViewEmbedder () noexcept = default; - - /** returns the embedded view or nullptr if it has none */ - virtual IView* getExternalView () const = 0; -}; - -//------------------------------------------------------------------------ -/** adapter for the IView interface - * - * @ingroup new_in_4_13 - */ -struct ViewAdapter : IView -{ - bool platformViewTypeSupported (PlatformViewType type) override { return false; } - bool attach (void* parent, PlatformViewType parentViewType) override { return false; } - bool remove () override { return false; } - void setViewSize (IntRect frame, IntRect visible) override {} - void setContentScaleFactor (double scaleFactor) override {} - void setMouseEnabled (bool state) override {} - void takeFocus () override {} - void looseFocus () override {} - void setTookFocusCallback (const TookFocusCallback& callback) override {} -}; - -//------------------------------------------------------------------------ -} // ExternalView -} // VSTGUI diff --git a/vstgui/lib/platform/common/genericoptionmenu.cpp b/vstgui/lib/platform/common/genericoptionmenu.cpp index 261997c2b..9188e6a20 100644 --- a/vstgui/lib/platform/common/genericoptionmenu.cpp +++ b/vstgui/lib/platform/common/genericoptionmenu.cpp @@ -60,7 +60,6 @@ class DataSource : public DataBrowserDelegateAdapter, if (maxWidth >= 0.) return maxWidth; auto context = COffscreenContext::create ({1., 1.}); - context->setFont (theme.font); maxWidth = 0.; maxTitleWidth = 0.; hasRightMargin = false; diff --git a/vstgui/lib/platform/common/generictextedit.cpp b/vstgui/lib/platform/common/generictextedit.cpp index e52cc865c..04e2c715e 100644 --- a/vstgui/lib/platform/common/generictextedit.cpp +++ b/vstgui/lib/platform/common/generictextedit.cpp @@ -14,7 +14,6 @@ #include "../../cvstguitimer.h" #include "../../cdropsource.h" #include "../../events.h" -#include "../../cdrawcontext.h" #include #include diff --git a/vstgui/lib/platform/iplatformfont.h b/vstgui/lib/platform/iplatformfont.h index 69d1666ac..2bdf9be6b 100644 --- a/vstgui/lib/platform/iplatformfont.h +++ b/vstgui/lib/platform/iplatformfont.h @@ -18,11 +18,10 @@ class IFontPainter public: virtual ~IFontPainter () noexcept = default; - virtual void drawString (const PlatformGraphicsDeviceContextPtr& context, - IPlatformString* string, const CPoint& p, const CColor& color, + virtual void drawString (CDrawContext* context, IPlatformString* string, const CPoint& p, bool antialias = true) const = 0; - virtual CCoord getStringWidth (const PlatformGraphicsDeviceContextPtr& context, - IPlatformString* string, bool antialias = true) const = 0; + virtual CCoord getStringWidth (CDrawContext* context, IPlatformString* string, + bool antialias = true) const = 0; }; //----------------------------------------------------------------------------- diff --git a/vstgui/lib/platform/iplatformframecallback.h b/vstgui/lib/platform/iplatformframecallback.h index 8b6c0737d..18b395933 100644 --- a/vstgui/lib/platform/iplatformframecallback.h +++ b/vstgui/lib/platform/iplatformframecallback.h @@ -30,10 +30,7 @@ enum class PlatformType : int32_t { class IPlatformFrameCallback { public: - virtual ~IPlatformFrameCallback () = default; - - virtual void platformDrawRects (const PlatformGraphicsDeviceContextPtr& context, - double scaleFactor, const std::vector& rects) = 0; + virtual bool platformDrawRect (CDrawContext* context, const CRect& rect) = 0; virtual void platformOnEvent (Event& event) = 0; diff --git a/vstgui/lib/platform/iplatformgraphicsdevice.h b/vstgui/lib/platform/iplatformgraphicsdevice.h deleted file mode 100644 index c29baf2b0..000000000 --- a/vstgui/lib/platform/iplatformgraphicsdevice.h +++ /dev/null @@ -1,124 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#pragma once - -#include "../vstguifwd.h" - -//------------------------------------------------------------------------ -namespace VSTGUI { - -//------------------------------------------------------------------------ -enum class PlatformGraphicsDrawStyle : uint32_t -{ - Stroked, - Filled, - FilledAndStroked -}; - -//------------------------------------------------------------------------ -enum class PlatformGraphicsPathDrawMode : uint32_t -{ - Filled, - FilledEvenOdd, - Stroked -}; - -//------------------------------------------------------------------------ -using TransformMatrix = CGraphicsTransform; - -//------------------------------------------------------------------------ -struct ScreenInfo -{ - using Identifier = uint32_t; - /* - Identifier identifier; - uint32_t width; - uint32_t height; - */ -}; -static constexpr const ScreenInfo::Identifier DefaultScreenIdentifier = 0u; - -//------------------------------------------------------------------------ -class IPlatformGraphicsDeviceFactory -{ -public: - virtual ~IPlatformGraphicsDeviceFactory () noexcept = default; - - virtual PlatformGraphicsDevicePtr getDeviceForScreen (ScreenInfo::Identifier screen) const = 0; -}; - -//------------------------------------------------------------------------ -class IPlatformGraphicsDevice -{ -public: - virtual ~IPlatformGraphicsDevice () noexcept = default; - - virtual PlatformGraphicsDeviceContextPtr - createBitmapContext (const PlatformBitmapPtr& bitmap) const = 0; -}; - -//------------------------------------------------------------------------ -class IPlatformGraphicsDeviceContext -{ -public: - virtual ~IPlatformGraphicsDeviceContext () noexcept = default; - - virtual const IPlatformGraphicsDevice& getDevice () const = 0; - virtual PlatformGraphicsPathFactoryPtr getGraphicsPathFactory () const = 0; - - virtual bool beginDraw () const = 0; - virtual bool endDraw () const = 0; - // draw commands - virtual bool drawLine (LinePair line) const = 0; - virtual bool drawLines (const LineList& lines) const = 0; - virtual bool drawPolygon (const PointList& polygonPointList, - PlatformGraphicsDrawStyle drawStyle) const = 0; - virtual bool drawRect (CRect rect, PlatformGraphicsDrawStyle drawStyle) const = 0; - virtual bool drawArc (CRect rect, double startAngle1, double endAngle2, - PlatformGraphicsDrawStyle drawStyle) const = 0; - virtual bool drawEllipse (CRect rect, PlatformGraphicsDrawStyle drawStyle) const = 0; - virtual bool drawPoint (CPoint point, CColor color) const = 0; - virtual bool drawBitmap (IPlatformBitmap& bitmap, CRect dest, CPoint offset, double alpha, - BitmapInterpolationQuality quality) const = 0; - virtual bool clearRect (CRect rect) const = 0; - virtual bool drawGraphicsPath (IPlatformGraphicsPath& path, PlatformGraphicsPathDrawMode mode, - TransformMatrix* transformation) const = 0; - virtual bool fillLinearGradient (IPlatformGraphicsPath& path, const IPlatformGradient& gradient, - CPoint startPoint, CPoint endPoint, bool evenOdd, - TransformMatrix* transformation) const = 0; - virtual bool fillRadialGradient (IPlatformGraphicsPath& path, const IPlatformGradient& gradient, - CPoint center, CCoord radius, CPoint originOffset, - bool evenOdd, TransformMatrix* transformation) const = 0; - // state - virtual void saveGlobalState () const = 0; - virtual void restoreGlobalState () const = 0; - virtual void setLineStyle (const CLineStyle& style) const = 0; - virtual void setLineWidth (CCoord width) const = 0; - virtual void setDrawMode (CDrawMode mode) const = 0; - virtual void setClipRect (CRect clip) const = 0; - virtual void setFillColor (CColor color) const = 0; - virtual void setFrameColor (CColor color) const = 0; - virtual void setGlobalAlpha (double newAlpha) const = 0; - virtual void setTransformMatrix (const TransformMatrix& tm) const = 0; - - // extension - virtual const IPlatformGraphicsDeviceContextBitmapExt* asBitmapExt () const = 0; -}; - -//------------------------------------------------------------------------ -class IPlatformGraphicsDeviceContextBitmapExt -{ -public: - virtual ~IPlatformGraphicsDeviceContextBitmapExt () noexcept = default; - - virtual bool drawBitmapNinePartTiled (IPlatformBitmap& bitmap, CRect dest, - const CNinePartTiledDescription& desc, double alpha, - BitmapInterpolationQuality quality) const = 0; - virtual bool fillRectWithBitmap (IPlatformBitmap& bitmap, CRect srcRect, CRect dstRect, - double alpha, BitmapInterpolationQuality quality) const = 0; -}; - -//------------------------------------------------------------------------ -} // VSTGUI diff --git a/vstgui/lib/platform/iplatformtextedit.h b/vstgui/lib/platform/iplatformtextedit.h index 7771a0782..224ce2a1c 100644 --- a/vstgui/lib/platform/iplatformtextedit.h +++ b/vstgui/lib/platform/iplatformtextedit.h @@ -9,7 +9,7 @@ #include "../cfont.h" #include "../ccolor.h" #include "../crect.h" -#include "../cdrawdefs.h" +#include "../cdrawcontext.h" struct VstKeyCode; diff --git a/vstgui/lib/platform/iplatformviewlayer.h b/vstgui/lib/platform/iplatformviewlayer.h index 703a32cc4..e1bbfc44d 100644 --- a/vstgui/lib/platform/iplatformviewlayer.h +++ b/vstgui/lib/platform/iplatformviewlayer.h @@ -14,9 +14,8 @@ class IPlatformViewLayerDelegate public: virtual ~IPlatformViewLayerDelegate () noexcept = default; - /** rects are in client coordinates (top-left is 0, 0) */ - virtual void drawViewLayerRects (const PlatformGraphicsDeviceContextPtr& context, - double scaleFactor, const std::vector& rects) = 0; + /** dirtyRect is in client coordinated (top-left is 0, 0) */ + virtual void drawViewLayer (CDrawContext* context, const CRect& dirtyRect) = 0; }; //----------------------------------------------------------------------------- @@ -29,6 +28,7 @@ class IPlatformViewLayer : public AtomicReferenceCounted virtual void setSize (const CRect& size) = 0; virtual void setZIndex (uint32_t zIndex) = 0; virtual void setAlpha (float alpha) = 0; + virtual void draw (CDrawContext* context, const CRect& updateRect) = 0; virtual void onScaleFactorChanged (double newScaleFactor) = 0; }; diff --git a/vstgui/lib/platform/linux/cairocontext.cpp b/vstgui/lib/platform/linux/cairocontext.cpp new file mode 100644 index 000000000..578ded239 --- /dev/null +++ b/vstgui/lib/platform/linux/cairocontext.cpp @@ -0,0 +1,562 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#include "cairocontext.h" +#include "../../cbitmap.h" +#include "../../cgradient.h" +#include "cairobitmap.h" +#include "cairogradient.h" +#include "cairopath.h" + +//------------------------------------------------------------------------ +namespace VSTGUI { +namespace Cairo { + +//------------------------------------------------------------------------ +namespace { + +struct SaveCairoState +{ + SaveCairoState (ContextHandle& h) : h (h) { cairo_save (h); } + ~SaveCairoState () { cairo_restore (h); } + +private: + ContextHandle& h; +}; + +//------------------------------------------------------------------------ +void checkCairoStatus (const ContextHandle& handle) +{ +#if DEBUG + auto status = cairo_status (handle); + if (status != CAIRO_STATUS_SUCCESS) + { + auto msg = cairo_status_to_string (status); + DebugPrint ("%s\n", msg); + } +#endif +} + +//------------------------------------------------------------------------ +cairo_matrix_t convert (CGraphicsTransform& ct) +{ + return {ct.m11, ct.m21, ct.m12, ct.m22, ct.dx, ct.dy}; +} + +//----------------------------------------------------------------------------- +inline bool needPixelAlignment (CDrawMode mode) +{ + return (mode.integralMode () && mode.modeIgnoringIntegralMode () == kAntiAliasing); +} + +//------------------------------------------------------------------------ +} // anonymous + +//------------------------------------------------------------------------ +DrawBlock::DrawBlock (Context& context) : context (context) +{ + auto ct = context.getCurrentTransform (); + CRect clip = context.getCurrentStateClipRect (); + if (clip.isEmpty ()) + { + clipIsEmpty = true; + } + else + { + cairo_save (context.getCairo ()); + cairo_rectangle (context.getCairo (), clip.left, clip.top, clip.getWidth (), + clip.getHeight ()); + cairo_clip (context.getCairo ()); + auto matrix = convert (ct); + cairo_set_matrix (context.getCairo (), &matrix); + auto antialiasMode = context.getDrawMode ().modeIgnoringIntegralMode () == kAntiAliasing + ? CAIRO_ANTIALIAS_BEST + : CAIRO_ANTIALIAS_NONE; + cairo_set_antialias (context.getCairo (), antialiasMode); + } +} + +//------------------------------------------------------------------------ +DrawBlock::~DrawBlock () +{ + if (!clipIsEmpty) + { + cairo_restore (context.getCairo ()); + } +} + +//------------------------------------------------------------------------ +DrawBlock DrawBlock::begin (Context& context) +{ + return DrawBlock (context); +} + +//----------------------------------------------------------------------------- +Context::Context (const CRect& rect, const SurfaceHandle& surface) : super (rect), surface (surface) +{ + init (); +} + +//----------------------------------------------------------------------------- +Context::Context (Bitmap* bitmap) : super (new CBitmap (bitmap)), surface (bitmap->getSurface ()) +{ + init (); +} + +//----------------------------------------------------------------------------- +Context::Context (CRect r, cairo_t* context) : super (r) +{ + cr = ContextHandle {cairo_reference (context)}; + init (); +} + +//----------------------------------------------------------------------------- +Context::~Context () {} + +//----------------------------------------------------------------------------- +void Context::init () +{ + if (surface) + cr.assign (cairo_create (surface)); + super::init (); +} + +//----------------------------------------------------------------------------- +CRect Context::getCurrentStateClipRect () const +{ + return getCurrentState ().clipRect; +} + +//----------------------------------------------------------------------------- +void Context::beginDraw () +{ + super::beginDraw (); + cairo_save (cr); + checkCairoStatus (cr); +} + +//----------------------------------------------------------------------------- +void Context::endDraw () +{ + cairo_restore (cr); + if (surface) + cairo_surface_flush (surface); + checkCairoStatus (cr); + super::endDraw (); +} + +//----------------------------------------------------------------------------- +void Context::saveGlobalState () +{ + super::saveGlobalState (); +} + +//----------------------------------------------------------------------------- +void Context::restoreGlobalState () +{ + super::restoreGlobalState (); +} + +//----------------------------------------------------------------------------- +void Context::setSourceColor (CColor color) +{ + auto alpha = color.normAlpha (); + alpha *= getGlobalAlpha (); + cairo_set_source_rgba (cr, color.normRed (), color.normGreen (), + color.normBlue (), alpha); + checkCairoStatus (cr); +} + +//----------------------------------------------------------------------------- +void Context::setupCurrentStroke () +{ + auto lineWidth = getLineWidth (); + cairo_set_line_width (cr, lineWidth); + const auto& style = getLineStyle (); + if (!style.getDashLengths ().empty ()) + { + auto lengths = style.getDashLengths (); + for (auto& l : lengths) + l *= lineWidth; + cairo_set_dash (cr, lengths.data (), lengths.size (), style.getDashPhase ()); + } + cairo_line_cap_t lineCap; + switch (style.getLineCap ()) + { + case CLineStyle::kLineCapButt: + { + lineCap = CAIRO_LINE_CAP_BUTT; + break; + } + case CLineStyle::kLineCapRound: + { + lineCap = CAIRO_LINE_CAP_ROUND; + break; + } + case CLineStyle::kLineCapSquare: + { + lineCap = CAIRO_LINE_CAP_SQUARE; + break; + } + } + cairo_set_line_cap (cr, lineCap); + cairo_line_join_t lineJoin; + switch (style.getLineJoin ()) + { + case CLineStyle::kLineJoinBevel: + { + lineJoin = CAIRO_LINE_JOIN_BEVEL; + break; + } + case CLineStyle::kLineJoinMiter: + { + lineJoin = CAIRO_LINE_JOIN_MITER; + break; + } + case CLineStyle::kLineJoinRound: + { + lineJoin = CAIRO_LINE_JOIN_ROUND; + break; + } + } + + cairo_set_line_join (cr, lineJoin); +} + +//----------------------------------------------------------------------------- +void Context::draw (CDrawStyle drawStyle) +{ + switch (drawStyle) + { + case kDrawStroked: + { + setupCurrentStroke (); + setSourceColor (getFrameColor ()); + cairo_stroke (cr); + break; + } + case kDrawFilled: + { + setSourceColor (getFillColor ()); + cairo_fill (cr); + break; + } + case kDrawFilledAndStroked: + { + setSourceColor (getFillColor ()); + cairo_fill_preserve (cr); + setupCurrentStroke (); + setSourceColor (getFrameColor ()); + cairo_stroke (cr); + break; + } + } + checkCairoStatus (cr); +} + +//----------------------------------------------------------------------------- +void Context::drawLine (const CDrawContext::LinePair& line) +{ + if (auto cd = DrawBlock::begin (*this)) + { + setupCurrentStroke (); + setSourceColor (getFrameColor ()); + if (getDrawMode ().integralMode ()) + { + CPoint start = pixelAlign (getCurrentTransform (), line.first); + CPoint end = pixelAlign (getCurrentTransform (), line.second); + cairo_move_to (cr, start.x, start.y); + cairo_line_to (cr, end.x, end.y); + } + else + { + cairo_move_to (cr, line.first.x, line.first.y); + cairo_line_to (cr, line.second.x, line.second.y); + } + cairo_stroke (cr); + } + checkCairoStatus (cr); +} + +//----------------------------------------------------------------------------- +void Context::drawLines (const CDrawContext::LineList& lines) +{ + if (auto cd = DrawBlock::begin (*this)) + { + setupCurrentStroke (); + setSourceColor (getFrameColor ()); + if (getDrawMode ().integralMode ()) + { + for (auto& line : lines) + { + CPoint start = pixelAlign (getCurrentTransform (), line.first); + CPoint end = pixelAlign (getCurrentTransform (), line.second); + cairo_move_to (cr, start.x, start.y); + cairo_line_to (cr, end.x, end.y); + cairo_stroke (cr); + } + } + else + { + for (auto& line : lines) + { + cairo_move_to (cr, line.first.x, line.first.y); + cairo_line_to (cr, line.second.x, line.second.y); + cairo_stroke (cr); + } + } + } +} + +//----------------------------------------------------------------------------- +void Context::drawPolygon (const CDrawContext::PointList& polygonPointList, + const CDrawStyle drawStyle) +{ + if (polygonPointList.size () < 2) + return; + + if (auto cd = DrawBlock::begin (*this)) + { + auto& last = polygonPointList.back (); + cairo_move_to (cr, last.x, last.y); + for (auto& it : polygonPointList) + cairo_line_to (cr, it.x, it.y); + + draw (drawStyle); + } +} + +//----------------------------------------------------------------------------- +void Context::drawRect (const CRect& rect, const CDrawStyle drawStyle) +{ + if (auto cd = DrawBlock::begin (*this)) + { + CRect r (rect); + if (needPixelAlignment (getDrawMode ())) + { + r = pixelAlign (getCurrentTransform (), r); + cairo_rectangle (cr, r.left, r.top, r.getWidth (), r.getHeight ()); + } + else + cairo_rectangle (cr, r.left + 0.5, r.top + 0.5, r.getWidth () - 0.5, + r.getHeight () - 0.5); + draw (drawStyle); + } +} + +//----------------------------------------------------------------------------- +void Context::drawArc (const CRect& rect, const float startAngle1, const float endAngle2, + const CDrawStyle drawStyle) +{ + if (auto cd = DrawBlock::begin (*this)) + { + CPoint center = rect.getCenter (); + cairo_translate (cr, center.x, center.y); + cairo_scale (cr, 2.0 / rect.getWidth (), 2.0 / rect.getHeight ()); + cairo_arc (cr, 0, 0, 1, startAngle1, endAngle2); + draw (drawStyle); + } +} + +//----------------------------------------------------------------------------- +void Context::drawEllipse (const CRect& rect, const CDrawStyle drawStyle) +{ + if (auto cd = DrawBlock::begin (*this)) + { + CPoint center = rect.getCenter (); + cairo_translate (cr, center.x, center.y); + cairo_scale (cr, 2.0 / rect.getWidth (), 2.0 / rect.getHeight ()); + cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI); + draw (drawStyle); + } +} + +//----------------------------------------------------------------------------- +void Context::drawPoint (const CPoint& point, const CColor& color) +{ + if (auto cd = DrawBlock::begin (*this)) + { + setSourceColor (color); + cairo_rectangle (cr, point.x + 0.5, point.y + 0.5, 1, 1); + cairo_fill (cr); + } + checkCairoStatus (cr); +} + +//----------------------------------------------------------------------------- +void Context::drawBitmap (CBitmap* bitmap, const CRect& dest, const CPoint& offset, float alpha) +{ + if (auto cd = DrawBlock::begin (*this)) + { + double transformedScaleFactor = getScaleFactor (); + CGraphicsTransform t = getCurrentTransform (); + if (t.m11 == t.m22 && t.m12 == 0 && t.m21 == 0) + transformedScaleFactor *= t.m11; + auto cairoBitmap = + bitmap->getBestPlatformBitmapForScaleFactor (transformedScaleFactor).cast (); + if (cairoBitmap) + { + cairo_translate (cr, dest.left, dest.top); + cairo_rectangle (cr, 0, 0, dest.getWidth (), dest.getHeight ()); + cairo_clip (cr); + + // Setup a pattern for scaling bitmaps and take it as source afterwards. + auto pattern = cairo_pattern_create_for_surface (cairoBitmap->getSurface ()); + cairo_matrix_t matrix; + cairo_pattern_get_matrix (pattern, &matrix); + cairo_matrix_init_scale (&matrix, cairoBitmap->getScaleFactor (), + cairoBitmap->getScaleFactor ()); + cairo_matrix_translate (&matrix, offset.x, offset.y); + cairo_pattern_set_matrix (pattern, &matrix); + cairo_set_source (cr, pattern); + + cairo_rectangle (cr, -offset.x, -offset.y, dest.getWidth () + offset.x, + dest.getHeight () + offset.y); + alpha *= getGlobalAlpha (); + if (alpha != 1.f) + { + cairo_paint_with_alpha (cr, alpha); + } + else + { + cairo_fill (cr); + } + + cairo_pattern_destroy (pattern); + } + } + checkCairoStatus (cr); +} + +//----------------------------------------------------------------------------- +void Context::clearRect (const CRect& rect) +{ + if (auto cd = DrawBlock::begin (*this)) + { + cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); + cairo_rectangle (cr, rect.left, rect.top, rect.getWidth (), rect.getHeight ()); + cairo_fill (cr); + } + checkCairoStatus (cr); +} + +//----------------------------------------------------------------------------- +CGraphicsPath* Context::createGraphicsPath () +{ + if (!graphicsPathFactory) + graphicsPathFactory = std::make_shared (cr); + return new CGraphicsPath (graphicsPathFactory); +} + +//----------------------------------------------------------------------------- +CGraphicsPath* Context::createTextPath (const CFontRef font, UTF8StringPtr text) +{ +#warning TODO: Implementation + return nullptr; +} + +//----------------------------------------------------------------------------- +void Context::drawGraphicsPath (CGraphicsPath* path, CDrawContext::PathDrawMode mode, + CGraphicsTransform* transformation) +{ + if (path) + { + auto graphicsPath = dynamic_cast ( + path->getPlatformPath (PlatformGraphicsPathFillMode::Ignored).get ()); + if (!graphicsPath) + return; + if (auto cd = DrawBlock::begin (*this)) + { + std::unique_ptr alignedPath; + if (needPixelAlignment (getDrawMode ())) + alignedPath = graphicsPath->copyPixelAlign (getCurrentTransform ()); + auto p = alignedPath ? alignedPath->getCairoPath () : graphicsPath->getCairoPath (); + if (transformation) + { + cairo_matrix_t currentMatrix; + cairo_matrix_t resultMatrix; + auto matrix = convert (*transformation); + cairo_get_matrix (cr, ¤tMatrix); + cairo_matrix_multiply (&resultMatrix, &matrix, ¤tMatrix); + cairo_set_matrix (cr, &resultMatrix); + } + cairo_append_path (cr, p); + switch (mode) + { + case PathDrawMode::kPathFilled: + { + setSourceColor (getFillColor ()); + cairo_fill (cr); + break; + } + case PathDrawMode::kPathFilledEvenOdd: + { + setSourceColor (getFillColor ()); + cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); + cairo_fill (cr); + break; + } + case PathDrawMode::kPathStroked: + { + setupCurrentStroke (); + setSourceColor (getFrameColor ()); + cairo_stroke (cr); + break; + } + } + } + } + checkCairoStatus (cr); +} + +//----------------------------------------------------------------------------- +void Context::fillLinearGradient (CGraphicsPath* path, const CGradient& gradient, + const CPoint& startPoint, const CPoint& endPoint, bool evenOdd, + CGraphicsTransform* transformation) +{ + if (path) + { + auto graphicsPath = dynamic_cast ( + path->getPlatformPath (PlatformGraphicsPathFillMode::Ignored).get ()); + if (!graphicsPath) + return; + std::unique_ptr alignedPath; + if (needPixelAlignment (getDrawMode ())) + alignedPath = graphicsPath->copyPixelAlign (getCurrentTransform ()); + if (auto cairoGradient = dynamic_cast (gradient.getPlatformGradient ().get ())) + { + if (auto cd = DrawBlock::begin (*this)) + { + auto p = alignedPath ? alignedPath->getCairoPath () : graphicsPath->getCairoPath (); + cairo_append_path (cr, p); + cairo_set_source (cr, cairoGradient->getLinearGradient (startPoint, endPoint)); + if (evenOdd) + { + cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); + cairo_fill (cr); + } + else + { + cairo_fill (cr); + } + } + } + } +} + +//----------------------------------------------------------------------------- +void Context::fillRadialGradient (CGraphicsPath* path, const CGradient& gradient, + const CPoint& center, CCoord radius, const CPoint& originOffset, + bool evenOdd, CGraphicsTransform* transformation) +{ +#warning TODO: Implementation + auto cd = DrawBlock::begin (*this); + if (cd) + { + } +} + +//----------------------------------------------------------------------------- +} // Cairo +} // VSTGUI diff --git a/vstgui/lib/platform/linux/cairocontext.h b/vstgui/lib/platform/linux/cairocontext.h new file mode 100644 index 000000000..5cc75f881 --- /dev/null +++ b/vstgui/lib/platform/linux/cairocontext.h @@ -0,0 +1,115 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once + +#include "cairoutils.h" +#include "cairopath.h" + +#include "../../coffscreencontext.h" + +//------------------------------------------------------------------------ +namespace VSTGUI { +namespace Cairo { + +class Bitmap; + +//------------------------------------------------------------------------ +class Context : public COffscreenContext +{ +public: + using super = COffscreenContext; + + Context (const CRect& rect, const SurfaceHandle& surface); + Context (CRect r, cairo_t* context); + Context (Bitmap* bitmap); + + ~Context (); + + bool valid () const { return cr != nullptr; } + const SurfaceHandle& getSurface () const { return surface; } + const ContextHandle& getCairo () const { return cr; } + + void drawLine (const LinePair& line) override; + void drawLines (const LineList& lines) override; + void drawPolygon (const PointList& polygonPointList, const CDrawStyle drawStyle) override; + void drawRect (const CRect& rect, const CDrawStyle drawStyle) override; + void drawArc (const CRect& rect, const float startAngle1, const float endAngle2, + const CDrawStyle drawStyle) override; + void drawEllipse (const CRect& rect, const CDrawStyle drawStyle) override; + void drawPoint (const CPoint& point, const CColor& color) override; + void drawBitmap (CBitmap* bitmap, const CRect& dest, const CPoint& offset, + float alpha) override; + void clearRect (const CRect& rect) override; + CGraphicsPath* createGraphicsPath () override; + CGraphicsPath* createTextPath (const CFontRef font, UTF8StringPtr text) override; + void drawGraphicsPath (CGraphicsPath* path, PathDrawMode mode, + CGraphicsTransform* transformation) override; + void fillLinearGradient (CGraphicsPath* path, const CGradient& gradient, + const CPoint& startPoint, const CPoint& endPoint, bool evenOdd, + CGraphicsTransform* transformation) override; + void fillRadialGradient (CGraphicsPath* path, const CGradient& gradient, const CPoint& center, + CCoord radius, const CPoint& originOffset, bool evenOdd, + CGraphicsTransform* transformation) override; + + void saveGlobalState () override; + void restoreGlobalState () override; + + void beginDraw () override; + void endDraw () override; + + CRect getCurrentStateClipRect () const; +private: + void init () override; + void setSourceColor (CColor color); + void setupCurrentStroke (); + void draw (CDrawStyle drawstyle); + + SurfaceHandle surface; + ContextHandle cr; + + PlatformGraphicsPathFactoryPtr graphicsPathFactory; +}; + +//------------------------------------------------------------------------ +struct DrawBlock +{ + static DrawBlock begin (Context& context); + + ~DrawBlock (); + operator bool () { return !clipIsEmpty; } + +private: + explicit DrawBlock (Context& context); + Context& context; + bool clipIsEmpty {false}; +}; + +//----------------------------------------------------------------------------- +inline CPoint pixelAlign (const CGraphicsTransform& tm, const CPoint& p) +{ + auto obj = p; + tm.transform (obj); + obj.x = std::round (obj.x) - 0.5; + obj.y = std::round (obj.y) - 0.5; + tm.inverse ().transform (obj); + return obj; +} + +//----------------------------------------------------------------------------- +inline CRect pixelAlign (const CGraphicsTransform& tm, const CRect& r) +{ + auto obj = r; + tm.transform (obj); + obj.left = std::round (obj.left) - 0.5; + obj.right = std::round (obj.right) - 0.5; + obj.top = std::round (obj.top) - 0.5; + obj.bottom = std::round (obj.bottom) - 0.5; + tm.inverse ().transform (obj); + return obj; +} + +//------------------------------------------------------------------------ +} // Cairo +} // VSTGUI diff --git a/vstgui/lib/platform/linux/cairofont.cpp b/vstgui/lib/platform/linux/cairofont.cpp index d1043bd54..f906e0e22 100644 --- a/vstgui/lib/platform/linux/cairofont.cpp +++ b/vstgui/lib/platform/linux/cairofont.cpp @@ -3,11 +3,8 @@ // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #include "cairofont.h" -#include "../../cstring.h" -#include "../../cfont.h" -#include "../../cpoint.h" -#include "../../ccolor.h" -#include "cairographicscontext.h" +#include "../../../lib/cstring.h" +#include "cairocontext.h" #include "linuxstring.h" #include "linuxfactory.h" #include @@ -20,8 +17,9 @@ namespace VSTGUI { namespace Cairo { namespace { -using PangoFontHandle = Handle; +using PangoFontHandle = + Handle; //------------------------------------------------------------------------ class FontList @@ -33,11 +31,20 @@ class FontList return gInstance; } - FcConfig* getFontConfig () { return fcConfig; } + FcConfig* getFontConfig () + { + return fcConfig; + } - PangoFontMap* getFontMap () { return fontMap; } + PangoFontMap* getFontMap () + { + return fontMap; + } - PangoContext* getFontContext () { return fontContext; } + PangoContext* getFontContext () + { + return fontContext; + } bool queryFont (UTF8StringPtr name, CCoord size, int32_t style, PangoFontHandle& fontHandle) { @@ -55,7 +62,7 @@ class FontList return font != nullptr; } - bool getAllFontFamilies (const FontFamilyCallback& callback) + bool getAllFontFamilies(const FontFamilyCallback& callback) { if (!fontContext) return false; @@ -78,8 +85,7 @@ class FontList fontMap = pango_cairo_font_map_new (); fontContext = pango_font_map_create_context (fontMap); - PangoFcFontMap* fcMap = - G_TYPE_CHECK_INSTANCE_CAST (fontMap, PANGO_TYPE_FC_FONT_MAP, PangoFcFontMap); + PangoFcFontMap *fcMap = G_TYPE_CHECK_INSTANCE_CAST (fontMap, PANGO_TYPE_FC_FONT_MAP, PangoFcFontMap); if (fcMap && FcInit () && (fcConfig = FcInitLoadConfigAndFonts ())) { if (auto linuxFactory = getPlatformFactory ().asLinuxFactory ()) @@ -88,8 +94,7 @@ class FontList if (!resourcePath.empty ()) { auto fontDir = resourcePath + "Fonts/"; - FcConfigAppFontAddDir (fcConfig, - reinterpret_cast (fontDir.data ())); + FcConfigAppFontAddDir (fcConfig, reinterpret_cast (fontDir.data ())); } pango_fc_font_map_set_config (fcMap, fcConfig); FcConfigDestroy (fcConfig); @@ -160,7 +165,7 @@ Font::Font (UTF8StringPtr name, const CCoord& size, const int32_t& style) pango_font_metrics_unref (metrics); } - PangoContext* context = fontList.getFontContext (); + PangoContext* context = fontList.getFontContext(); if (context) { PangoLayout* layout = pango_layout_new (context); @@ -191,87 +196,114 @@ Font::Font (UTF8StringPtr name, const CCoord& size, const int32_t& style) Font::~Font () {} //------------------------------------------------------------------------ -bool Font::valid () const { return impl->font; } +bool Font::valid () const +{ + return impl->font; +} //------------------------------------------------------------------------ -double Font::getAscent () const { return impl->ascent; } +double Font::getAscent () const +{ + return impl->ascent; +} //------------------------------------------------------------------------ -double Font::getDescent () const { return impl->descent; } +double Font::getDescent () const +{ + return impl->descent; +} //------------------------------------------------------------------------ -double Font::getLeading () const { return impl->leading; } +double Font::getLeading () const +{ + return impl->leading; +} //------------------------------------------------------------------------ -double Font::getCapHeight () const { return impl->capHeight; } +double Font::getCapHeight () const +{ + return impl->capHeight; +} //------------------------------------------------------------------------ -const IFontPainter* Font::getPainter () const { return this; } +const IFontPainter* Font::getPainter () const +{ + return this; +} //------------------------------------------------------------------------ -void Font::drawString (const PlatformGraphicsDeviceContextPtr& context, IPlatformString* string, - const CPoint& p, const CColor& color, bool antialias) const +void Font::drawString (CDrawContext* context, IPlatformString* string, const CPoint& p, + bool antialias) const { - auto cairoContext = std::dynamic_pointer_cast (context); - if (!cairoContext) - return; - auto linuxString = dynamic_cast (string); - if (!linuxString) - return; - PangoContext* pangoContext = FontList::instance ().getFontContext (); - if (!pangoContext) - return; - PangoLayout* layout = pango_layout_new (pangoContext); - if (!layout) - return; - - if (impl->font) + if (auto cairoContext = dynamic_cast (context)) { - PangoFontDescription* desc = pango_font_describe (impl->font); - if (desc) + if (auto cd = DrawBlock::begin (*cairoContext)) { - pango_layout_set_font_description (layout, desc); - pango_font_description_free (desc); + if (auto linuxString = dynamic_cast (string)) + { + auto color = cairoContext->getFontColor (); + const auto& cr = cairoContext->getCairo (); + auto alpha = color.normAlpha () * cairoContext->getGlobalAlpha (); + cairo_set_source_rgba (cr, color.normRed (), color.normGreen (), + color.normBlue (), alpha); + + PangoContext* context = FontList::instance ().getFontContext(); + if (context) + { + PangoLayout* layout = pango_layout_new (context); + if (layout) + { + if (impl->font) + { + PangoFontDescription* desc = pango_font_describe (impl->font); + if (desc) + { + pango_layout_set_font_description (layout, desc); + pango_font_description_free (desc); + } + } + + PangoAttrList* attrs = pango_attr_list_new (); + if (attrs) + { + if (impl->style & kUnderlineFace) + pango_attr_list_insert (attrs, pango_attr_underline_new (PANGO_UNDERLINE_SINGLE)); + if (impl->style & kStrikethroughFace) + pango_attr_list_insert (attrs, pango_attr_strikethrough_new (true)); + pango_layout_set_attributes(layout, attrs); + pango_attr_list_unref (attrs); + } + + pango_layout_set_text (layout, linuxString->get ().c_str (), -1); + + PangoRectangle extents {}; + pango_layout_get_pixel_extents (layout, nullptr, &extents); + + PangoLayoutIter* iter = pango_layout_get_iter (layout); + CCoord baseline = 0.0; + if (iter) + { + baseline = pango_units_to_double (pango_layout_iter_get_baseline (iter)); + pango_layout_iter_free (iter); + } + + cairo_move_to (cr, p.x + extents.x, p.y + extents.y - baseline); + pango_cairo_show_layout (cr, layout); + g_object_unref (layout); + } + } + } } } - - PangoAttrList* attrs = pango_attr_list_new (); - if (attrs) - { - if (impl->style & kUnderlineFace) - pango_attr_list_insert (attrs, pango_attr_underline_new (PANGO_UNDERLINE_SINGLE)); - if (impl->style & kStrikethroughFace) - pango_attr_list_insert (attrs, pango_attr_strikethrough_new (true)); - pango_layout_set_attributes (layout, attrs); - pango_attr_list_unref (attrs); - } - - pango_layout_set_text (layout, linuxString->get ().c_str (), -1); - - PangoRectangle extents {}; - pango_layout_get_pixel_extents (layout, nullptr, &extents); - - PangoLayoutIter* iter = pango_layout_get_iter (layout); - CCoord baseline = 0.0; - if (iter) - { - baseline = pango_units_to_double (pango_layout_iter_get_baseline (iter)); - pango_layout_iter_free (iter); - } - - cairoContext->drawPangoLayout (layout, {p.x + extents.x, p.y + extents.y - baseline}, color); - - g_object_unref (layout); } //------------------------------------------------------------------------ -CCoord Font::getStringWidth (const PlatformGraphicsDeviceContextPtr&, IPlatformString* string, - bool antialias) const +CCoord Font::getStringWidth (CDrawContext* context, IPlatformString* string, bool antialias) const { if (auto linuxString = dynamic_cast (string)) { int pangoWidth = 0; - PangoContext* context = FontList::instance ().getFontContext (); + PangoContext* context = FontList::instance ().getFontContext(); if (context) { PangoLayout* layout = pango_layout_new (context); diff --git a/vstgui/lib/platform/linux/cairofont.h b/vstgui/lib/platform/linux/cairofont.h index 5e6759cd5..2895eb736 100644 --- a/vstgui/lib/platform/linux/cairofont.h +++ b/vstgui/lib/platform/linux/cairofont.h @@ -1,4 +1,4 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE @@ -29,9 +29,9 @@ class Font double getCapHeight () const override; const IFontPainter* getPainter () const override; - void drawString (const PlatformGraphicsDeviceContextPtr& context, IPlatformString* string, - const CPoint& p, const CColor& color, bool antialias = true) const override; - CCoord getStringWidth (const PlatformGraphicsDeviceContextPtr& context, IPlatformString* string, + void drawString (CDrawContext* context, IPlatformString* string, const CPoint& p, + bool antialias = true) const override; + CCoord getStringWidth (CDrawContext* context, IPlatformString* string, bool antialias = true) const override; static bool getAllFamilies (const FontFamilyCallback& callback); diff --git a/vstgui/lib/platform/linux/cairogradient.cpp b/vstgui/lib/platform/linux/cairogradient.cpp index 9488d9916..f2abb35ad 100644 --- a/vstgui/lib/platform/linux/cairogradient.cpp +++ b/vstgui/lib/platform/linux/cairogradient.cpp @@ -22,12 +22,11 @@ void Gradient::changed () } //------------------------------------------------------------------------ -const PatternHandle& Gradient::getLinearGradient (CPoint start, CPoint end) const +const PatternHandle& Gradient::getLinearGradient (CPoint start, CPoint end) { if (!linearGradient || start != linearGradientStart || end != linearGradientEnd) { - linearGradient.reset (); - radialGradient.reset (); + changed (); linearGradientStart = start; linearGradientEnd = end; linearGradient = diff --git a/vstgui/lib/platform/linux/cairogradient.h b/vstgui/lib/platform/linux/cairogradient.h index 785247db9..d2a3fc896 100644 --- a/vstgui/lib/platform/linux/cairogradient.h +++ b/vstgui/lib/platform/linux/cairogradient.h @@ -19,18 +19,18 @@ class Gradient : public PlatformGradientBase public: ~Gradient () noexcept override; - const PatternHandle& getLinearGradient (CPoint start, CPoint end) const; + const PatternHandle& getLinearGradient (CPoint start, CPoint end); const PatternHandle& getRadialGradient (); private: void changed () override; /* we want to calculate a normalized linear and radial gradiant */ - mutable PatternHandle linearGradient; - mutable PatternHandle radialGradient; + PatternHandle linearGradient; + PatternHandle radialGradient; - mutable CPoint linearGradientStart; - mutable CPoint linearGradientEnd; + CPoint linearGradientStart; + CPoint linearGradientEnd; }; //------------------------------------------------------------------------ diff --git a/vstgui/lib/platform/linux/cairographicscontext.cpp b/vstgui/lib/platform/linux/cairographicscontext.cpp deleted file mode 100644 index ab620cc91..000000000 --- a/vstgui/lib/platform/linux/cairographicscontext.cpp +++ /dev/null @@ -1,712 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#include "cairographicscontext.h" -#include "cairobitmap.h" -#include "cairopath.h" -#include "cairogradient.h" -#include "../../crect.h" -#include "../../cgraphicstransform.h" -#include "../../ccolor.h" -#include "../../cdrawdefs.h" -#include "../../clinestyle.h" - -#include -#include - -//------------------------------------------------------------------------ -namespace VSTGUI { - -using TransformMatrix = CGraphicsTransform; - -//------------------------------------------------------------------------ -inline void checkCairoStatus (const Cairo::ContextHandle& handle) -{ -#if DEBUG - auto status = cairo_status (handle); - if (status != CAIRO_STATUS_SUCCESS) - { - auto msg = cairo_status_to_string (status); - DebugPrint ("%s\n", msg); - } -#endif -} - -//------------------------------------------------------------------------ -inline cairo_matrix_t convert (const TransformMatrix& ct) -{ - return {ct.m11, ct.m21, ct.m12, ct.m22, ct.dx, ct.dy}; -} - -//----------------------------------------------------------------------------- -struct CairoGraphicsDeviceFactory::Impl -{ - std::vector> devices; -}; - -//----------------------------------------------------------------------------- -CairoGraphicsDeviceFactory::CairoGraphicsDeviceFactory () -{ - impl = std::make_unique (); -} - -//----------------------------------------------------------------------------- -CairoGraphicsDeviceFactory::~CairoGraphicsDeviceFactory () noexcept = default; - -//----------------------------------------------------------------------------- -PlatformGraphicsDevicePtr CairoGraphicsDeviceFactory::getDeviceForScreen (ScreenInfo::Identifier screen) const -{ - if (impl->devices.empty ()) - { - // just create a dummy device as we don't really need the cairo device at the moment - impl->devices.push_back (std::make_shared (nullptr)); - } - return impl->devices.front (); -} - -//----------------------------------------------------------------------------- -PlatformGraphicsDevicePtr CairoGraphicsDeviceFactory::addDevice (cairo_device_t* device) -{ - for (auto& dev : impl->devices) - { - if (dev->get () == device) - return dev; - } - impl->devices.push_back (std::make_shared (device)); - return impl->devices.back (); -} - -//----------------------------------------------------------------------------- -struct CairoGraphicsDevice::Impl -{ - cairo_device_t* device; -}; - -//----------------------------------------------------------------------------- -CairoGraphicsDevice::CairoGraphicsDevice (cairo_device_t* device) -{ - impl = std::make_unique (); - impl->device = device; - if (device) - cairo_device_reference (device); -} - -//----------------------------------------------------------------------------- -CairoGraphicsDevice::~CairoGraphicsDevice () noexcept -{ - if (impl->device) - cairo_device_destroy (impl->device); -} - -//----------------------------------------------------------------------------- -cairo_device_t* CairoGraphicsDevice::get () const { return impl->device; } - -//----------------------------------------------------------------------------- -PlatformGraphicsDeviceContextPtr - CairoGraphicsDevice::createBitmapContext (const PlatformBitmapPtr& bitmap) const -{ - auto cairoBitmap = bitmap.cast (); - if (cairoBitmap) - { - return std::make_shared (*this, cairoBitmap->getSurface ()); - } - return nullptr; -} - -//----------------------------------------------------------------------------- -inline CPoint pixelAlign (const CGraphicsTransform& tm, const CPoint& p) -{ - auto obj = p; - tm.transform (obj); - obj.x = std::round (obj.x); - obj.y = std::round (obj.y); - tm.inverse ().transform (obj); - return obj; -} - -//----------------------------------------------------------------------------- -inline CRect pixelAlign (const CGraphicsTransform& tm, const CRect& r) -{ - auto obj = r; - tm.transform (obj); - obj.left = std::round (obj.left); - obj.right = std::round (obj.right); - obj.top = std::round (obj.top); - obj.bottom = std::round (obj.bottom); - tm.inverse ().transform (obj); - return obj; -} - -//------------------------------------------------------------------------ -struct CairoGraphicsDeviceContext::Impl -{ - const CairoGraphicsDevice& device; - Cairo::ContextHandle context; - Cairo::SurfaceHandle surface; - - Impl (const CairoGraphicsDevice& device, const Cairo::SurfaceHandle& surface) - : device (device), surface (surface) - { - context.assign (cairo_create (surface)); - } - - template - void doInContext (Proc p) - { - auto ct = state.tm; - CRect clip = state.clip; - if (clip.isEmpty ()) - return; - cairo_save (context); - cairo_rectangle (context, clip.left, clip.top, clip.getWidth (), clip.getHeight ()); - cairo_clip (context); - auto matrix = convert (ct); - cairo_set_matrix (context, &matrix); - auto antialiasMode = state.drawMode.modeIgnoringIntegralMode () == kAntiAliasing - ? CAIRO_ANTIALIAS_BEST - : CAIRO_ANTIALIAS_NONE; - cairo_set_antialias (context, antialiasMode); - p (); - checkCairoStatus (context); - cairo_restore (context); - } - - void applyLineWidthCTM () - { - auto p = calcLineTranslate (); - cairo_translate (context, p.x, p.y); - } - - CPoint calcLineTranslate () const - { - CPoint p {}; - int32_t lineWidthInt = static_cast (state.lineWidth); - if (static_cast (lineWidthInt) == state.lineWidth && lineWidthInt % 2) - p.x = p.y = 0.5; - return p; - } - - void applyLineStyle () - { - auto lineWidth = state.lineWidth; - cairo_set_line_width (context, lineWidth); - const auto& style = state.lineStyle; - if (!style.getDashLengths ().empty ()) - { - auto lengths = style.getDashLengths (); - for (auto& l : lengths) - l *= lineWidth; - cairo_set_dash (context, lengths.data (), lengths.size (), style.getDashPhase ()); - } - cairo_line_cap_t lineCap; - switch (style.getLineCap ()) - { - case CLineStyle::kLineCapButt: - { - lineCap = CAIRO_LINE_CAP_BUTT; - break; - } - case CLineStyle::kLineCapRound: - { - lineCap = CAIRO_LINE_CAP_ROUND; - break; - } - case CLineStyle::kLineCapSquare: - { - lineCap = CAIRO_LINE_CAP_SQUARE; - break; - } - } - cairo_set_line_cap (context, lineCap); - cairo_line_join_t lineJoin; - switch (style.getLineJoin ()) - { - case CLineStyle::kLineJoinBevel: - { - lineJoin = CAIRO_LINE_JOIN_BEVEL; - break; - } - case CLineStyle::kLineJoinMiter: - { - lineJoin = CAIRO_LINE_JOIN_MITER; - break; - } - case CLineStyle::kLineJoinRound: - { - lineJoin = CAIRO_LINE_JOIN_ROUND; - break; - } - } - - cairo_set_line_join (context, lineJoin); - } - - void setupSourceColor (CColor color) - { - auto alpha = color.normAlpha (); - alpha *= state.globalAlpha; - cairo_set_source_rgba (context, color.normRed (), color.normGreen (), - color.normBlue (), alpha); - checkCairoStatus (context); - } - void applyFillColor () { setupSourceColor (state.fillColor); } - void applyFrameColor () { setupSourceColor (state.frameColor); } - void applyFontColor (CColor color) { setupSourceColor (color); } - - void draw (PlatformGraphicsDrawStyle drawStyle) - { - switch (drawStyle) - { - case PlatformGraphicsDrawStyle::Stroked: - { - applyLineStyle (); - applyFrameColor (); - cairo_stroke (context); - break; - } - case PlatformGraphicsDrawStyle::Filled: - { - applyFillColor (); - cairo_fill (context); - break; - } - case PlatformGraphicsDrawStyle::FilledAndStroked: - { - applyFillColor (); - cairo_fill_preserve (context); - applyLineStyle (); - applyFrameColor (); - cairo_stroke (context); - break; - } - } - checkCairoStatus (context); - } - - struct State - { - CRect clip {}; - CLineStyle lineStyle {kLineSolid}; - CDrawMode drawMode {}; - CColor fillColor {kTransparentCColor}; - CColor frameColor {kTransparentCColor}; - CCoord lineWidth {1.}; - double globalAlpha {1.}; - TransformMatrix tm {}; - }; - State state; - std::stack stateStack; - double scaleFactor {1.}; - - PlatformGraphicsPathFactoryPtr pathFactory; -}; - -//------------------------------------------------------------------------ -CairoGraphicsDeviceContext::CairoGraphicsDeviceContext (const CairoGraphicsDevice& device, - const Cairo::SurfaceHandle& handle) -{ - impl = std::make_unique (device, handle); -} - -//------------------------------------------------------------------------ -CairoGraphicsDeviceContext::~CairoGraphicsDeviceContext () noexcept {} - -//------------------------------------------------------------------------ -const IPlatformGraphicsDevice& CairoGraphicsDeviceContext::getDevice () const -{ - return impl->device; -} - -//------------------------------------------------------------------------ -PlatformGraphicsPathFactoryPtr CairoGraphicsDeviceContext::getGraphicsPathFactory () const -{ - if (!impl->pathFactory) - impl->pathFactory = std::make_shared (impl->context); - return impl->pathFactory; -} - -//------------------------------------------------------------------------ -bool CairoGraphicsDeviceContext::beginDraw () const -{ - if (impl->context) - cairo_save (impl->context); - return true; -} - -//------------------------------------------------------------------------ -bool CairoGraphicsDeviceContext::endDraw () const -{ - if (impl->context) - cairo_restore (impl->context); - if (impl->surface) - cairo_surface_flush (impl->surface); - return true; -} - -//------------------------------------------------------------------------ -bool CairoGraphicsDeviceContext::drawLine (LinePair line) const -{ - impl->doInContext ([&] () { - impl->applyLineStyle (); - impl->applyFrameColor (); - if (impl->state.drawMode.integralMode ()) - { - CPoint start = pixelAlign (impl->state.tm, line.first); - CPoint end = pixelAlign (impl->state.tm, line.second); - impl->applyLineWidthCTM (); - cairo_move_to (impl->context, start.x, start.y); - cairo_line_to (impl->context, end.x, end.y); - } - else - { - cairo_move_to (impl->context, line.first.x, line.first.y); - cairo_line_to (impl->context, line.second.x, line.second.y); - } - cairo_stroke (impl->context); - }); - return true; -} - -//------------------------------------------------------------------------ -bool CairoGraphicsDeviceContext::drawLines (const LineList& lines) const -{ - impl->doInContext ([&] () { - impl->applyLineStyle (); - impl->applyFrameColor (); - if (impl->state.drawMode.integralMode ()) - { - auto pt = impl->calcLineTranslate (); - for (auto& line : lines) - { - CPoint start = pixelAlign (impl->state.tm, line.first); - CPoint end = pixelAlign (impl->state.tm, line.second); - cairo_move_to (impl->context, start.x + pt.x, start.y + pt.y); - cairo_line_to (impl->context, end.x + pt.x, end.y + pt.y); - cairo_stroke (impl->context); - } - } - else - { - for (auto& line : lines) - { - cairo_move_to (impl->context, line.first.x, line.first.y); - cairo_line_to (impl->context, line.second.x, line.second.y); - cairo_stroke (impl->context); - } - } - }); - return true; -} - -//------------------------------------------------------------------------ -bool CairoGraphicsDeviceContext::drawPolygon (const PointList& polygonPointList, - PlatformGraphicsDrawStyle drawStyle) const -{ - vstgui_assert (polygonPointList.empty () == false); - impl->doInContext ([&] () { - bool doPixelAlign = impl->state.drawMode.integralMode (); - auto last = polygonPointList.back (); - if (doPixelAlign) - last = pixelAlign (impl->state.tm, last); - cairo_move_to (impl->context, last.x, last.y); - for (auto p : polygonPointList) - { - if (doPixelAlign) - p = pixelAlign (impl->state.tm, p); - cairo_line_to (impl->context, p.x, p.y); - } - impl->draw (drawStyle); - }); - return true; -} - -//------------------------------------------------------------------------ -bool CairoGraphicsDeviceContext::drawRect (CRect rect, PlatformGraphicsDrawStyle drawStyle) const -{ - impl->doInContext ([&] () { - if (drawStyle != PlatformGraphicsDrawStyle::Filled) - { - rect.right -= 1.; - rect.bottom -= 1.; - } - if (impl->state.drawMode.integralMode ()) - { - rect = pixelAlign (impl->state.tm, rect); - if (drawStyle != PlatformGraphicsDrawStyle::Filled) - impl->applyLineWidthCTM (); - cairo_rectangle (impl->context, rect.left, rect.top, rect.getWidth (), - rect.getHeight ()); - } - else - { - cairo_rectangle (impl->context, rect.left + 0.5, rect.top + 0.5, rect.getWidth () - 0.5, - rect.getHeight () - 0.5); - } - impl->draw (drawStyle); - }); - return true; -} - -//------------------------------------------------------------------------ -bool CairoGraphicsDeviceContext::drawArc (CRect rect, double startAngle1, double endAngle2, - PlatformGraphicsDrawStyle drawStyle) const -{ - impl->doInContext ([&] () { - CPoint center = rect.getCenter (); - cairo_translate (impl->context, center.x, center.y); - cairo_scale (impl->context, 2.0 / rect.getWidth (), 2.0 / rect.getHeight ()); - cairo_arc (impl->context, 0, 0, 1, startAngle1, endAngle2); - impl->draw (drawStyle); - }); - return true; -} - -//------------------------------------------------------------------------ -bool CairoGraphicsDeviceContext::drawEllipse (CRect rect, PlatformGraphicsDrawStyle drawStyle) const -{ - impl->doInContext ([&] () { - CPoint center = rect.getCenter (); - cairo_translate (impl->context, center.x, center.y); - cairo_scale (impl->context, 2.0 / rect.getWidth (), 2.0 / rect.getHeight ()); - cairo_arc (impl->context, 0, 0, 1, 0, 2 * M_PI); - impl->draw (drawStyle); - }); - return true; -} - -//------------------------------------------------------------------------ -bool CairoGraphicsDeviceContext::drawPoint (CPoint point, CColor color) const { return false; } - -//------------------------------------------------------------------------ -bool CairoGraphicsDeviceContext::drawBitmap (IPlatformBitmap& bitmap, CRect dest, CPoint offset, - double alpha, BitmapInterpolationQuality quality) const -{ - auto cairoBitmap = dynamic_cast (&bitmap); - if (!cairoBitmap) - return false; - impl->doInContext ([&] () { - cairo_translate (impl->context, dest.left, dest.top); - cairo_rectangle (impl->context, 0, 0, dest.getWidth (), dest.getHeight ()); - cairo_clip (impl->context); - - // Setup a pattern for scaling bitmaps and take it as source afterwards. - auto pattern = cairo_pattern_create_for_surface (cairoBitmap->getSurface ()); - cairo_matrix_t matrix; - cairo_pattern_get_matrix (pattern, &matrix); - cairo_matrix_init_scale (&matrix, cairoBitmap->getScaleFactor (), - cairoBitmap->getScaleFactor ()); - cairo_matrix_translate (&matrix, offset.x, offset.y); - cairo_pattern_set_matrix (pattern, &matrix); - cairo_set_source (impl->context, pattern); - - cairo_rectangle (impl->context, -offset.x, -offset.y, dest.getWidth () + offset.x, - dest.getHeight () + offset.y); - alpha *= impl->state.globalAlpha; - if (alpha != 1.f) - { - cairo_paint_with_alpha (impl->context, alpha); - } - else - { - cairo_fill (impl->context); - } - cairo_pattern_destroy (pattern); - }); - return true; -} - -//------------------------------------------------------------------------ -bool CairoGraphicsDeviceContext::clearRect (CRect rect) const -{ - impl->doInContext ([&] () { - cairo_set_operator (impl->context, CAIRO_OPERATOR_CLEAR); - cairo_rectangle (impl->context, rect.left, rect.top, rect.getWidth (), rect.getHeight ()); - cairo_fill (impl->context); - }); - return true; -} - -//------------------------------------------------------------------------ -bool CairoGraphicsDeviceContext::drawGraphicsPath (IPlatformGraphicsPath& path, - PlatformGraphicsPathDrawMode mode, - TransformMatrix* transformation) const -{ - auto cairoPath = dynamic_cast (&path); - if (!cairoPath) - return false; - impl->doInContext ([&] () { - std::unique_ptr alignedPath; - if (impl->state.drawMode.integralMode ()) - { - alignedPath = cairoPath->copyPixelAlign ([&] (CPoint p) { - p = pixelAlign (impl->state.tm, p); - return p; - }); - } - auto p = alignedPath ? alignedPath->getCairoPath () : cairoPath->getCairoPath (); - if (transformation) - { - cairo_matrix_t currentMatrix; - cairo_matrix_t resultMatrix; - auto matrix = convert (*transformation); - cairo_get_matrix (impl->context, ¤tMatrix); - cairo_matrix_multiply (&resultMatrix, &matrix, ¤tMatrix); - cairo_set_matrix (impl->context, &resultMatrix); - } - cairo_append_path (impl->context, p); - switch (mode) - { - case PlatformGraphicsPathDrawMode::Filled: - { - impl->applyFillColor (); - cairo_fill (impl->context); - break; - } - case PlatformGraphicsPathDrawMode::FilledEvenOdd: - { - impl->applyFillColor (); - cairo_set_fill_rule (impl->context, CAIRO_FILL_RULE_EVEN_ODD); - cairo_fill (impl->context); - break; - } - case PlatformGraphicsPathDrawMode::Stroked: - { - impl->applyLineStyle (); - impl->applyFrameColor (); - cairo_stroke (impl->context); - break; - } - } - }); - return true; -} - -//------------------------------------------------------------------------ -bool CairoGraphicsDeviceContext::fillLinearGradient (IPlatformGraphicsPath& path, - const IPlatformGradient& gradient, - CPoint startPoint, CPoint endPoint, - bool evenOdd, - TransformMatrix* transformation) const -{ - auto cairoPath = dynamic_cast (&path); - if (!cairoPath) - return false; - auto cairoGradient = dynamic_cast (&gradient); - if (!cairoGradient) - return false; - impl->doInContext ([&] () { - std::unique_ptr alignedPath; - if (impl->state.drawMode.integralMode ()) - { - alignedPath = cairoPath->copyPixelAlign ([&] (CPoint p) { - p = pixelAlign (impl->state.tm, p); - return p; - }); - } - auto p = alignedPath ? alignedPath->getCairoPath () : cairoPath->getCairoPath (); - cairo_append_path (impl->context, p); - cairo_set_source (impl->context, cairoGradient->getLinearGradient (startPoint, endPoint)); - if (evenOdd) - cairo_set_fill_rule (impl->context, CAIRO_FILL_RULE_EVEN_ODD); - cairo_fill (impl->context); - }); - return true; -} - -//------------------------------------------------------------------------ -bool CairoGraphicsDeviceContext::fillRadialGradient (IPlatformGraphicsPath& path, - const IPlatformGradient& gradient, - CPoint center, CCoord radius, - CPoint originOffset, bool evenOdd, - TransformMatrix* transformation) const -{ - auto cairoPath = dynamic_cast (&path); - if (!cairoPath) - return false; - // TODO: Implementation - return false; -} - -//------------------------------------------------------------------------ -void CairoGraphicsDeviceContext::saveGlobalState () const -{ - cairo_save (impl->context); - impl->stateStack.push (impl->state); -} - -//------------------------------------------------------------------------ -void CairoGraphicsDeviceContext::restoreGlobalState () const -{ - vstgui_assert (impl->stateStack.empty () == false, - "Unbalanced calls to saveGlobalState and restoreGlobalState"); -#if NDEBUG - if (impl->stateStack.empty ()) - return; -#endif - cairo_restore (impl->context); - impl->state = impl->stateStack.top (); - impl->stateStack.pop (); -} - -//------------------------------------------------------------------------ -void CairoGraphicsDeviceContext::setLineStyle (const CLineStyle& style) const -{ - impl->state.lineStyle = style; -} - -//------------------------------------------------------------------------ -void CairoGraphicsDeviceContext::setLineWidth (CCoord width) const -{ - - impl->state.lineWidth = width; -} - -//------------------------------------------------------------------------ -void CairoGraphicsDeviceContext::setDrawMode (CDrawMode mode) const { impl->state.drawMode = mode; } - -//------------------------------------------------------------------------ -void CairoGraphicsDeviceContext::setClipRect (CRect clip) const { impl->state.clip = clip; } - -//------------------------------------------------------------------------ -void CairoGraphicsDeviceContext::setFillColor (CColor color) const -{ - impl->state.fillColor = color; -} - -//------------------------------------------------------------------------ -void CairoGraphicsDeviceContext::setFrameColor (CColor color) const -{ - impl->state.frameColor = color; -} - -//------------------------------------------------------------------------ -void CairoGraphicsDeviceContext::setGlobalAlpha (double newAlpha) const -{ - impl->state.globalAlpha = newAlpha; -} - -//------------------------------------------------------------------------ -void CairoGraphicsDeviceContext::setTransformMatrix (const TransformMatrix& tm) const -{ - impl->state.tm = tm; -} - -//------------------------------------------------------------------------ -const IPlatformGraphicsDeviceContextBitmapExt* CairoGraphicsDeviceContext::asBitmapExt () const -{ - return nullptr; -} - -//------------------------------------------------------------------------ -void CairoGraphicsDeviceContext::drawPangoLayout (void* layout, CPoint pos, CColor color) const -{ - impl->doInContext ([&] () { - impl->applyFontColor (color); - cairo_move_to (impl->context, pos.x, pos.y); - pango_cairo_show_layout (impl->context, reinterpret_cast (layout)); - }); -} - -//------------------------------------------------------------------------ -} // VSTGUI diff --git a/vstgui/lib/platform/linux/cairographicscontext.h b/vstgui/lib/platform/linux/cairographicscontext.h deleted file mode 100644 index 9c65d64ec..000000000 --- a/vstgui/lib/platform/linux/cairographicscontext.h +++ /dev/null @@ -1,107 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#pragma once - -#include "../iplatformgraphicsdevice.h" - -#include "cairoutils.h" - -//------------------------------------------------------------------------ -namespace VSTGUI { - -class CairoGraphicsDevice; - -//------------------------------------------------------------------------ -class CairoGraphicsDeviceContext : public IPlatformGraphicsDeviceContext -{ -public: - CairoGraphicsDeviceContext (const CairoGraphicsDevice& device, - const Cairo::SurfaceHandle& handle); - ~CairoGraphicsDeviceContext () noexcept; - - const IPlatformGraphicsDevice& getDevice () const override; - PlatformGraphicsPathFactoryPtr getGraphicsPathFactory () const override; - - bool beginDraw () const override; - bool endDraw () const override; - // draw commands - bool drawLine (LinePair line) const override; - bool drawLines (const LineList& lines) const override; - bool drawPolygon (const PointList& polygonPointList, - PlatformGraphicsDrawStyle drawStyle) const override; - bool drawRect (CRect rect, PlatformGraphicsDrawStyle drawStyle) const override; - bool drawArc (CRect rect, double startAngle1, double endAngle2, - PlatformGraphicsDrawStyle drawStyle) const override; - bool drawEllipse (CRect rect, PlatformGraphicsDrawStyle drawStyle) const override; - bool drawPoint (CPoint point, CColor color) const override; - bool drawBitmap (IPlatformBitmap& bitmap, CRect dest, CPoint offset, double alpha, - BitmapInterpolationQuality quality) const override; - bool clearRect (CRect rect) const override; - bool drawGraphicsPath (IPlatformGraphicsPath& path, PlatformGraphicsPathDrawMode mode, - TransformMatrix* transformation) const override; - bool fillLinearGradient (IPlatformGraphicsPath& path, const IPlatformGradient& gradient, - CPoint startPoint, CPoint endPoint, bool evenOdd, - TransformMatrix* transformation) const override; - bool fillRadialGradient (IPlatformGraphicsPath& path, const IPlatformGradient& gradient, - CPoint center, CCoord radius, CPoint originOffset, bool evenOdd, - TransformMatrix* transformation) const override; - // state - void saveGlobalState () const override; - void restoreGlobalState () const override; - void setLineStyle (const CLineStyle& style) const override; - void setLineWidth (CCoord width) const override; - void setDrawMode (CDrawMode mode) const override; - void setClipRect (CRect clip) const override; - void setFillColor (CColor color) const override; - void setFrameColor (CColor color) const override; - void setGlobalAlpha (double newAlpha) const override; - void setTransformMatrix (const TransformMatrix& tm) const override; - - // extension - const IPlatformGraphicsDeviceContextBitmapExt* asBitmapExt () const override; - - // private - void drawPangoLayout (void* layout, CPoint pos, CColor color) const; - -private: - struct Impl; - std::unique_ptr impl; -}; - -//------------------------------------------------------------------------ -class CairoGraphicsDevice : public IPlatformGraphicsDevice -{ -public: - CairoGraphicsDevice (cairo_device_t* device); - ~CairoGraphicsDevice () noexcept; - - PlatformGraphicsDeviceContextPtr - createBitmapContext (const PlatformBitmapPtr& bitmap) const override; - - cairo_device_t* get () const; - -private: - struct Impl; - std::unique_ptr impl; -}; - -//------------------------------------------------------------------------ -class CairoGraphicsDeviceFactory : public IPlatformGraphicsDeviceFactory -{ -public: - CairoGraphicsDeviceFactory (); - ~CairoGraphicsDeviceFactory () noexcept; - - PlatformGraphicsDevicePtr getDeviceForScreen (ScreenInfo::Identifier screen) const override; - - PlatformGraphicsDevicePtr addDevice (cairo_device_t* device); - -private: - struct Impl; - std::unique_ptr impl; -}; - -//------------------------------------------------------------------------ -} // VSTGUI diff --git a/vstgui/lib/platform/linux/cairopath.cpp b/vstgui/lib/platform/linux/cairopath.cpp index 3dae6ab5f..e7a3d6535 100644 --- a/vstgui/lib/platform/linux/cairopath.cpp +++ b/vstgui/lib/platform/linux/cairopath.cpp @@ -4,6 +4,7 @@ #include "../../cgradient.h" #include "../../cgraphicstransform.h" +#include "cairocontext.h" #include "cairopath.h" //------------------------------------------------------------------------ @@ -115,16 +116,16 @@ void GraphicsPath::closeSubpath () } //------------------------------------------------------------------------ -std::unique_ptr GraphicsPath::copyPixelAlign (const std::function& pixelAlignFunc) +std::unique_ptr GraphicsPath::copyPixelAlign (const CGraphicsTransform& tm) { auto result = std::make_unique (context); cairo_append_path (context, path); result->finishBuilding (); auto rpath = result->path; - auto align = [&] (_cairo_path_data_t* data, int index) { + auto align = [] (_cairo_path_data_t* data, int index, const CGraphicsTransform& tm) { CPoint input (data[index].point.x, data[index].point.y); - auto output = pixelAlignFunc (input); + auto output = Cairo::pixelAlign (tm, input); data[index].point.x = output.x; data[index].point.y = output.y; }; @@ -135,19 +136,19 @@ std::unique_ptr GraphicsPath::copyPixelAlign (const std::function< { case CAIRO_PATH_MOVE_TO: { - align (data, 1); + align (data, 1, tm); break; } case CAIRO_PATH_LINE_TO: { - align (data, 1); + align (data, 1, tm); break; } case CAIRO_PATH_CURVE_TO: { - align (data, 1); - align (data, 2); - align (data, 3); + align (data, 1, tm); + align (data, 2, tm); + align (data, 3, tm); break; } case CAIRO_PATH_CLOSE_PATH: { break; diff --git a/vstgui/lib/platform/linux/cairopath.h b/vstgui/lib/platform/linux/cairopath.h index 25477f957..8aede58a7 100644 --- a/vstgui/lib/platform/linux/cairopath.h +++ b/vstgui/lib/platform/linux/cairopath.h @@ -34,8 +34,7 @@ class GraphicsPath : public IPlatformGraphicsPath ~GraphicsPath () noexcept; cairo_path_t* getCairoPath () const { return path; } - std::unique_ptr - copyPixelAlign (const std::function& pixelAlignFunc); + std::unique_ptr copyPixelAlign (const CGraphicsTransform& tm); // IPlatformGraphicsPath void addArc (const CRect& rect, double startAngle, double endAngle, bool clockwise) override; diff --git a/vstgui/lib/platform/linux/linuxfactory.cpp b/vstgui/lib/platform/linux/linuxfactory.cpp index 9bbae8e40..202ed91a7 100644 --- a/vstgui/lib/platform/linux/linuxfactory.cpp +++ b/vstgui/lib/platform/linux/linuxfactory.cpp @@ -5,12 +5,11 @@ #include "cairobitmap.h" #include "cairofont.h" #include "cairogradient.h" -#include "cairographicscontext.h" +#include "cairocontext.h" #include "x11frame.h" #include "../iplatformframecallback.h" #include "../common/fileresourceinputstream.h" #include "../iplatformresourceinputstream.h" -#include "../iplatformgraphicsdevice.h" #include "linuxstring.h" #include "x11timer.h" #include "x11fileselector.h" @@ -29,7 +28,6 @@ namespace VSTGUI { struct LinuxFactory::Impl { std::string resPath; - std::unique_ptr graphicsDeviceFactory {std::make_unique ()}; void setupResPath (void* handle) { @@ -189,6 +187,19 @@ auto LinuxFactory::getClipboard () const noexcept -> DataPackagePtr return nullptr; } +//------------------------------------------------------------------------ +auto LinuxFactory::createOffscreenContext (const CPoint& size, double scaleFactor) const noexcept + -> COffscreenContextPtr +{ + auto bitmap = new Cairo::Bitmap (size * scaleFactor); + bitmap->setScaleFactor (scaleFactor); + auto context = owned (new Cairo::Context (bitmap)); + bitmap->forget (); + if (context->valid ()) + return context; + return nullptr; +} + //----------------------------------------------------------------------------- PlatformGradientPtr LinuxFactory::createGradient () const noexcept { @@ -203,12 +214,6 @@ PlatformFileSelectorPtr LinuxFactory::createFileSelector (PlatformFileSelectorSt return X11::createFileSelector (style, x11Frame); } -//----------------------------------------------------------------------------- -const IPlatformGraphicsDeviceFactory& LinuxFactory::getGraphicsDeviceFactory () const noexcept -{ - return *impl->graphicsDeviceFactory.get (); -} - //----------------------------------------------------------------------------- const LinuxFactory* LinuxFactory::asLinuxFactory () const noexcept { @@ -227,11 +232,5 @@ const Win32Factory* LinuxFactory::asWin32Factory () const noexcept return nullptr; } -//----------------------------------------------------------------------------- -CairoGraphicsDeviceFactory& LinuxFactory::getCairoGraphicsDeviceFactory () const noexcept -{ - return *impl->graphicsDeviceFactory.get (); -} - //----------------------------------------------------------------------------- } // VSTGUI diff --git a/vstgui/lib/platform/linux/linuxfactory.h b/vstgui/lib/platform/linux/linuxfactory.h index a2784c2cf..306085c51 100644 --- a/vstgui/lib/platform/linux/linuxfactory.h +++ b/vstgui/lib/platform/linux/linuxfactory.h @@ -8,7 +8,6 @@ //----------------------------------------------------------------------------- namespace VSTGUI { -class CairoGraphicsDeviceFactory; //----------------------------------------------------------------------------- class LinuxFactory final : public IPlatformFactory @@ -109,6 +108,14 @@ class LinuxFactory final : public IPlatformFactory */ DataPackagePtr getClipboard () const noexcept final; + /** create an offscreen draw device + * @param size the size of the bitmap where the offscreen renders to + * @param scaleFactor the scale factor for drawing + * @return an offscreen context object or nullptr on failure + */ + COffscreenContextPtr createOffscreenContext (const CPoint& size, + double scaleFactor = 1.) const noexcept final; + /** Create a platform gradient object * @return platform gradient object or nullptr on failure */ @@ -122,17 +129,10 @@ class LinuxFactory final : public IPlatformFactory PlatformFileSelectorPtr createFileSelector (PlatformFileSelectorStyle style, IPlatformFrame* frame) const noexcept final; - /** Get the graphics device factory - * - * @return platform graphics device factory - */ - const IPlatformGraphicsDeviceFactory& getGraphicsDeviceFactory () const noexcept final; - const LinuxFactory* asLinuxFactory () const noexcept final; const MacFactory* asMacFactory () const noexcept final; const Win32Factory* asWin32Factory () const noexcept final; - CairoGraphicsDeviceFactory& getCairoGraphicsDeviceFactory () const noexcept; private: struct Impl; std::unique_ptr impl; diff --git a/vstgui/lib/platform/linux/x11frame.cpp b/vstgui/lib/platform/linux/x11frame.cpp index 8eb331cb0..9975194d0 100644 --- a/vstgui/lib/platform/linux/x11frame.cpp +++ b/vstgui/lib/platform/linux/x11frame.cpp @@ -19,8 +19,7 @@ #include "../common/generictextedit.h" #include "../common/genericoptionmenu.h" #include "cairobitmap.h" -#include "linuxfactory.h" -#include "cairographicscontext.h" +#include "cairocontext.h" #include "x11platform.h" #include "x11utils.h" #include @@ -172,10 +171,8 @@ struct DrawHandler window.getID (), window.getVisual (), window.getSize ().x, window.getSize ().y); windowSurface.assign (s); - device = - getPlatformFactory ().asLinuxFactory ()->getCairoGraphicsDeviceFactory ().addDevice ( - cairo_surface_get_device (s)); onSizeChanged (window.getSize ()); + RunLoop::instance ().setDevice (cairo_surface_get_device (s)); } void onSizeChanged (const CPoint& size) @@ -183,40 +180,45 @@ struct DrawHandler cairo_xcb_surface_set_size (windowSurface, size.x, size.y); backBuffer = Cairo::SurfaceHandle (cairo_surface_create_similar ( windowSurface, CAIRO_CONTENT_COLOR_ALPHA, size.x, size.y)); - backBufferSize.setSize (size); - auto cairoDevice = std::static_pointer_cast (device); - drawContext = std::make_shared (*cairoDevice, backBuffer); + CRect r; + r.setSize (size); + drawContext = makeOwned (r, backBuffer); } - void draw (const CInvalidRectList& dirtyRects, IPlatformFrameCallback* frame) + template + void draw (const RectList& dirtyRects, Proc proc) { + CRect copyRect; drawContext->beginDraw (); - frame->platformDrawRects (drawContext, 1, dirtyRects.data ()); + for (auto rect : dirtyRects) + { + drawContext->setClipRect (rect); + drawContext->saveGlobalState (); + proc (drawContext, rect); + drawContext->restoreGlobalState (); + if (copyRect.isEmpty ()) + copyRect = rect; + else + copyRect.unite (rect); + } drawContext->endDraw (); - - blitBackbufferToWindow (dirtyRects); + blitBackbufferToWindow (copyRect); xcb_flush (RunLoop::instance ().getXcbConnection ()); } private: Cairo::SurfaceHandle windowSurface; Cairo::SurfaceHandle backBuffer; - CRect backBufferSize; - std::shared_ptr drawContext; - PlatformGraphicsDevicePtr device; + SharedPointer drawContext; - void blitBackbufferToWindow (const CInvalidRectList& rects) + void blitBackbufferToWindow (const CRect& rect) { Cairo::ContextHandle windowContext (cairo_create (windowSurface)); + cairo_rectangle (windowContext, rect.left, rect.top, rect.getWidth (), rect.getHeight ()); + cairo_clip (windowContext); cairo_set_source_surface (windowContext, backBuffer, 0, 0); - for (auto rect : rects) - { - cairo_rectangle (windowContext, rect.left, rect.top, rect.getWidth (), - rect.getHeight ()); - cairo_clip_preserve (windowContext); - cairo_fill (windowContext); - cairo_reset_clip (windowContext); - } + cairo_rectangle (windowContext, rect.left, rect.top, rect.getWidth (), rect.getHeight ()); + cairo_fill (windowContext); cairo_surface_flush (windowSurface); } }; @@ -364,7 +366,9 @@ struct Frame::Impl : IFrameEventHandler //------------------------------------------------------------------------ void redraw () { - drawHandler.draw (dirtyRects, frame); + drawHandler.draw (dirtyRects, [&] (CDrawContext* context, const CRect& rect) { + frame->platformDrawRect (context, rect); + }); dirtyRects.clear (); } diff --git a/vstgui/lib/platform/mac/caviewlayer.h b/vstgui/lib/platform/mac/caviewlayer.h index d83692e99..b1eaef1f5 100644 --- a/vstgui/lib/platform/mac/caviewlayer.h +++ b/vstgui/lib/platform/mac/caviewlayer.h @@ -1,4 +1,4 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE @@ -9,21 +9,11 @@ #if MAC_COCOA #include "../platform_macos.h" -#include namespace VSTGUI { - -//------------------------------------------------------------------------ -struct ICAViewLayerPrivate -{ - virtual ~ICAViewLayerPrivate () = default; - virtual void drawLayer (void* cgContext) = 0; -}; - + //----------------------------------------------------------------------------- -class CAViewLayer : public IPlatformViewLayer, - public ICocoaViewLayer, - private ICAViewLayerPrivate +class CAViewLayer : public IPlatformViewLayer, public ICocoaViewLayer //----------------------------------------------------------------------------- { public: @@ -31,21 +21,18 @@ class CAViewLayer : public IPlatformViewLayer, ~CAViewLayer () noexcept override; bool init (IPlatformViewLayerDelegate* drawDelegate); - + void invalidRect (const CRect& size) override; void setSize (const CRect& size) override; void setZIndex (uint32_t zIndex) override; void setAlpha (float alpha) override; + void draw (CDrawContext* context, const CRect& updateRect) override; void onScaleFactorChanged (double newScaleFactor) override; CALayer* getCALayer () const override { return layer; } - //----------------------------------------------------------------------------- -private: - void drawLayer (void* cgContext) final; - - CALayer* layer {nullptr}; - IPlatformViewLayerDelegate* drawDelegate {nullptr}; +protected: + CALayer* layer; }; } // VSTGUI diff --git a/vstgui/lib/platform/mac/caviewlayer.mm b/vstgui/lib/platform/mac/caviewlayer.mm index 3163d16c6..b2b754b08 100644 --- a/vstgui/lib/platform/mac/caviewlayer.mm +++ b/vstgui/lib/platform/mac/caviewlayer.mm @@ -6,7 +6,7 @@ #if MAC_COCOA -#import "coregraphicsdevicecontext.h" +#import "cgdrawcontext.h" #import "macglobals.h" #import @@ -21,7 +21,7 @@ @interface VSTGUI_CALayer : CALayer //----------------------------------------------------------------------------- { - VSTGUI::ICAViewLayerPrivate* _viewLayer; + VSTGUI::IPlatformViewLayerDelegate* _viewLayerDelegate; } @end @@ -48,16 +48,20 @@ - (id)init } //----------------------------------------------------------------------------- -- (void)setCAViewLayer:(VSTGUI::ICAViewLayerPrivate*)viewLayer +- (void)setDrawDelegate:(VSTGUI::IPlatformViewLayerDelegate*)viewLayerDelegate { - _viewLayer = viewLayer; + _viewLayerDelegate = viewLayerDelegate; } //----------------------------------------------------------------------------- - (void)drawInContext:(CGContextRef)ctx { - if (_viewLayer) - _viewLayer->drawLayer (ctx); + if (_viewLayerDelegate) + { + CGRect dirtyRect = CGContextGetClipBoundingBox (ctx); + VSTGUI::CGDrawContext drawContext (ctx, VSTGUI::CRectFromCGRect (self.bounds)); + _viewLayerDelegate->drawViewLayer (&drawContext, VSTGUI::CRectFromCGRect (dirtyRect)); + } } @end @@ -68,13 +72,13 @@ - (void)drawInContext:(CGContextRef)ctx //----------------------------------------------------------------------------- @interface VSTGUI_CALayer : CALayer //----------------------------------------------------------------------------- -- (void)setCAViewLayer:(VSTGUI::ICAViewLayerPrivate*)viewLayer; +- (void)setDrawDelegate:(VSTGUI::IPlatformViewLayerDelegate*)viewLayerDelegate; @end //----------------------------------------------------------------------------- struct VSTGUI_macOS_CALayer : VSTGUI::RuntimeObjCClass { - static constexpr const auto viewLayerVarName = "_viewLayer"; + static constexpr const auto viewLayerDelegateVarName = "_viewLayerDelegate"; //----------------------------------------------------------------------------- static Class CreateClass () @@ -83,9 +87,9 @@ static Class CreateClass () .init ("VSTGUI_CALayer", [CALayer class]) .addMethod (@selector (init), Init) .addMethod (@selector (actionForKey:), ActionForKey) - .addMethod (@selector (setCAViewLayer:), SetCAViewLayer) + .addMethod (@selector (setDrawDelegate:), SetDrawDelegate) .addMethod (@selector (drawInContext:), DrawInContext) - .addIvar (viewLayerVarName) + .addIvar (viewLayerDelegateVarName) .finalize (); } @@ -104,11 +108,12 @@ static id Init (id self, SEL _cmd) static id ActionForKey (id self, SEL _cmd, NSString* event) { return nil; } //----------------------------------------------------------------------------- - static void SetCAViewLayer (id self, SEL _cmd, VSTGUI::CAViewLayer* viewLayer) + static void SetDrawDelegate (id self, SEL _cmd, VSTGUI::IPlatformViewLayerDelegate* delegate) { using namespace VSTGUI; - if (auto var = makeInstance (self).getVariable (viewLayerVarName)) - var->set (viewLayer); + if (auto var = makeInstance (self).getVariable ( + viewLayerDelegateVarName)) + var->set (delegate); } //----------------------------------------------------------------------------- @@ -116,11 +121,34 @@ static void DrawInContext (id self, SEL _cmd, CGContextRef ctx) { using namespace VSTGUI; - if (auto var = - makeInstance (self).getVariable (viewLayerVarName); + if (auto var = makeInstance (self).getVariable ( + viewLayerDelegateVarName); var.has_value ()) { - var->get ()->drawLayer (ctx); + static bool visualizeLayer = false; + if (visualizeLayer) + CGContextClearRect (ctx, [self bounds]); + + CGRect dirtyRect = CGContextGetClipBoundingBox (ctx); + if ([self contentsAreFlipped] == [self isGeometryFlipped]) + { + CGContextScaleCTM (ctx, 1, -1); + CGContextTranslateCTM (ctx, 0, -[self bounds].size.height); + dirtyRect.origin.y = + (-dirtyRect.origin.y - dirtyRect.size.height) + [self bounds].size.height; + } + CGContextSaveGState (ctx); + CGDrawContext drawContext (ctx, CRectFromCGRect ([(CALayer*)self bounds])); + var->get ()->drawViewLayer (&drawContext, CRectFromCGRect (dirtyRect)); + CGContextRestoreGState (ctx); + + if (visualizeLayer) + { + CGContextSetRGBFillColor (ctx, 1., 0., 0., 0.3); + CGContextFillRect (ctx, [self bounds]); + CGContextSetRGBFillColor (ctx, 0., 1., 0., 0.3); + CGContextFillRect (ctx, dirtyRect); + } } } }; @@ -131,6 +159,7 @@ static void DrawInContext (id self, SEL _cmd, CGContextRef ctx) //----------------------------------------------------------------------------- CAViewLayer::CAViewLayer (CALayer* parent) +: layer (nullptr) { #if !TARGET_OS_IPHONE layer = [VSTGUI_macOS_CALayer::alloc () init]; @@ -139,7 +168,6 @@ static void DrawInContext (id self, SEL _cmd, CGContextRef ctx) #endif [layer setContentsScale:parent.contentsScale]; [parent addSublayer:layer]; - [(id)layer setCAViewLayer:this]; } //----------------------------------------------------------------------------- @@ -155,9 +183,9 @@ static void DrawInContext (id self, SEL _cmd, CGContextRef ctx) } //----------------------------------------------------------------------------- -bool CAViewLayer::init (IPlatformViewLayerDelegate* delegate) +bool CAViewLayer::init (IPlatformViewLayerDelegate* drawDelegate) { - drawDelegate = delegate; + [static_cast (layer) setDrawDelegate:drawDelegate]; return true; } @@ -205,58 +233,15 @@ static void DrawInContext (id self, SEL _cmd, CGContextRef ctx) } //----------------------------------------------------------------------------- -void CAViewLayer::onScaleFactorChanged (double newScaleFactor) +void CAViewLayer::draw (CDrawContext* context, const CRect& updateRect) { - if (layer) - layer.contentsScale = newScaleFactor; } //----------------------------------------------------------------------------- -void CAViewLayer::drawLayer (void* cgContext) +void CAViewLayer::onScaleFactorChanged (double newScaleFactor) { - CGContextRef ctx = reinterpret_cast (cgContext); - -#if DEBUG - static bool visualizeLayer = false; - if (visualizeLayer) - CGContextClearRect (ctx, [layer bounds]); -#endif - - CGRect dirtyRect = CGContextGetClipBoundingBox (ctx); - if ([layer contentsAreFlipped] == [layer isGeometryFlipped]) - { - CGContextScaleCTM (ctx, 1, -1); - CGContextTranslateCTM (ctx, 0, -[layer bounds].size.height); - dirtyRect.origin.y = - (-dirtyRect.origin.y - dirtyRect.size.height) + [layer bounds].size.height; - } - CGContextSaveGState (ctx); - - auto device = getPlatformFactory ().getGraphicsDeviceFactory ().getDeviceForScreen ( - DefaultScreenIdentifier); - if (!device) - return; - auto cgDevice = std::static_pointer_cast (device); - if (auto deviceContext = - std::make_shared (*cgDevice.get (), cgContext)) - { - deviceContext->beginDraw (); - drawDelegate->drawViewLayerRects (deviceContext, layer.contentsScale, - {1, CRectFromCGRect (dirtyRect)}); - deviceContext->endDraw (); - } - - CGContextRestoreGState (ctx); - -#if DEBUG - if (visualizeLayer) - { - CGContextSetRGBFillColor (ctx, 1., 0., 0., 0.3); - CGContextFillRect (ctx, [layer bounds]); - CGContextSetRGBFillColor (ctx, 0., 1., 0., 0.3); - CGContextFillRect (ctx, dirtyRect); - } -#endif + if (layer) + layer.contentsScale = newScaleFactor; } } // VSTGUI diff --git a/vstgui/lib/platform/mac/cfontmac.h b/vstgui/lib/platform/mac/cfontmac.h index 1d2f43dab..bb86bcb8f 100644 --- a/vstgui/lib/platform/mac/cfontmac.h +++ b/vstgui/lib/platform/mac/cfontmac.h @@ -40,14 +40,11 @@ class CoreTextFont : public IPlatformFont, public IFontPainter protected: ~CoreTextFont () noexcept override; - void drawString (const PlatformGraphicsDeviceContextPtr& context, IPlatformString* string, - const CPoint& p, const CColor& color, bool antialias = true) const override; - CCoord getStringWidth (const PlatformGraphicsDeviceContextPtr& context, IPlatformString* string, - bool antialias = true) const override; + void drawString (CDrawContext* context, IPlatformString* string, const CPoint& p, bool antialias = true) const override; + CCoord getStringWidth (CDrawContext* context, IPlatformString* string, bool antialias = true) const override; CFDictionaryRef getStringAttributes (const CGColorRef color = nullptr) const; - CTLineRef createCTLine (const PlatformGraphicsDeviceContextPtr& context, MacString* macString, - const CColor& color) const; + CTLineRef createCTLine (CDrawContext* context, MacString* macString) const; CTFontRef fontRef; int32_t style; diff --git a/vstgui/lib/platform/mac/cfontmac.mm b/vstgui/lib/platform/mac/cfontmac.mm index 90ebe472f..0d8f0757f 100644 --- a/vstgui/lib/platform/mac/cfontmac.mm +++ b/vstgui/lib/platform/mac/cfontmac.mm @@ -3,10 +3,11 @@ // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #import "cfontmac.h" +#import "../../cdrawcontext.h" #if MAC #import "macstring.h" -#import "coregraphicsdevicecontext.h" +#import "cgdrawcontext.h" #import "macglobals.h" #if TARGET_OS_IPHONE #import @@ -124,8 +125,7 @@ void getUrlsForType (CFStringRef fontType, CFMutableArrayRef& array) //----------------------------------------------------------------------------- static CTFontRef CoreTextCreateTraitsVariant (CTFontRef fontRef, CTFontSymbolicTraits trait) { - auto traitsFontRef = CTFontCreateCopyWithSymbolicTraits (fontRef, CTFontGetSize (fontRef), - nullptr, trait, trait); + CTFontRef traitsFontRef = CTFontCreateCopyWithSymbolicTraits (fontRef, CTFontGetSize (fontRef), nullptr, trait, trait); if (traitsFontRef) { CFRelease (fontRef); @@ -134,8 +134,7 @@ static CTFontRef CoreTextCreateTraitsVariant (CTFontRef fontRef, CTFontSymbolicT else if (trait == kCTFontItalicTrait) { CGAffineTransform transform = { 1, 0, -0.5, 1, 0, 0 }; - traitsFontRef = - CTFontCreateCopyWithAttributes (fontRef, CTFontGetSize (fontRef), &transform, nullptr); + traitsFontRef = CTFontCreateCopyWithAttributes (fontRef, CTFontGetSize (fontRef), &transform, nullptr); if (traitsFontRef) { CFRelease (fontRef); @@ -163,9 +162,7 @@ static CTFontRef CoreTextCreateTraitsVariant (CTFontRef fontRef, CTFontSymbolicT { if (@available (macOS 10.10, *)) { - auto attributes = - CFDictionaryCreateMutable (kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); + CFMutableDictionaryRef attributes = CFDictionaryCreateMutable (kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionaryAddValue (attributes, kCTFontFamilyNameAttribute, fontNameRef); CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes (attributes); fontRef = CTFontCreateWithFontDescriptor (descriptor, static_cast (size), nullptr); @@ -229,9 +226,7 @@ static CTFontRef CoreTextCreateTraitsVariant (CTFontRef fontRef, CTFontSymbolicT { if (stringAttributes == nullptr) { - stringAttributes = - CFDictionaryCreateMutable (kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); + stringAttributes = CFDictionaryCreateMutable (kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue (stringAttributes, kCTFontAttributeName, fontRef); } if (color) @@ -242,14 +237,17 @@ static CTFontRef CoreTextCreateTraitsVariant (CTFontRef fontRef, CTFontSymbolicT } //----------------------------------------------------------------------------- -CTLineRef CoreTextFont::createCTLine (const PlatformGraphicsDeviceContextPtr& context, - MacString* macString, const CColor& color) const +CTLineRef CoreTextFont::createCTLine (CDrawContext* context, MacString* macString) const { - if (macString->getCTLineFontRef () == this && macString->getCTLineColor () == color) + CColor fontColor = context ? context->getFontColor () : kBlackCColor; + if (context) { - CTLineRef line = macString->getCTLine (); - CFRetain (line); - return line; + if (macString->getCTLineFontRef () == this && macString->getCTLineColor () == fontColor) + { + CTLineRef line = macString->getCTLine (); + CFRetain (line); + return line; + } } CFStringRef cfStr = macString->getCFString (); if (cfStr == nullptr) @@ -261,19 +259,18 @@ static CTFontRef CoreTextCreateTraitsVariant (CTFontRef fontRef, CTFontSymbolicT } CGColorRef cgColorRef = nullptr; - if (color != lastColor) + if (fontColor != lastColor) { - cgColorRef = getCGColor (color); - lastColor = color; + cgColorRef = getCGColor (fontColor); + lastColor = fontColor; } - - if (auto attrStr = - CFAttributedStringCreate (kCFAllocatorDefault, cfStr, getStringAttributes (cgColorRef))) + CFAttributedStringRef attrStr = CFAttributedStringCreate (kCFAllocatorDefault, cfStr, getStringAttributes (cgColorRef)); + if (attrStr) { CTLineRef line = CTLineCreateWithAttributedString (attrStr); if (context && line) { - macString->setCTLine (line, this, color); + macString->setCTLine (line, this, fontColor); } CFRelease (attrStr); return line; @@ -283,38 +280,70 @@ static CTFontRef CoreTextCreateTraitsVariant (CTFontRef fontRef, CTFontSymbolicT } //----------------------------------------------------------------------------- -void CoreTextFont::drawString (const PlatformGraphicsDeviceContextPtr& context, - IPlatformString* string, const CPoint& point, const CColor& color, - bool antialias) const +void CoreTextFont::drawString (CDrawContext* context, IPlatformString* string, const CPoint& point, bool antialias) const { MacString* macString = dynamic_cast (string); if (macString == nullptr) return; - auto deviceContext = std::dynamic_pointer_cast (context); - if (!deviceContext) - return; - - CTLineRef line = createCTLine (context, macString, color); - if (!line) - return; - - CGPoint cgPoint = CGPointFromCPoint (point); - deviceContext->drawCTLine (line, cgPoint, fontRef, color, style & kUnderlineFace, - style & kStrikethroughFace, antialias); - CFRelease (line); + CTLineRef line = createCTLine (context, macString); + if (line) + { + bool integralMode = context->getDrawMode ().integralMode (); + CGDrawContext* cgDrawContext = dynamic_cast (context); + CGContextRef cgContext = cgDrawContext ? cgDrawContext->beginCGContext (true, integralMode) : nullptr; + if (cgContext) + { + CGPoint cgPoint = CGPointFromCPoint (point); + if (integralMode) + cgPoint = cgDrawContext->pixelAlligned (cgPoint); + CGContextSetShouldAntialias (cgContext, antialias); + CGContextSetShouldSmoothFonts (cgContext, true); + CGContextSetShouldSubpixelPositionFonts (cgContext, true); + CGContextSetShouldSubpixelQuantizeFonts (cgContext, true); + CGContextSetTextPosition (cgContext, static_cast (point.x), cgPoint.y); + CTLineDraw (line, cgContext); + if (style & kUnderlineFace) + { + CGColorRef cgColorRef = getCGColor (context->getFontColor ()); + CGFloat underlineOffset = CTFontGetUnderlinePosition (fontRef) - 1.f; + CGFloat underlineThickness = CTFontGetUnderlineThickness (fontRef); + CGContextSetStrokeColorWithColor (cgContext, cgColorRef); + CGContextSetLineWidth (cgContext, underlineThickness); + cgPoint = CGContextGetTextPosition (cgContext); + CGContextBeginPath (cgContext); + CGContextMoveToPoint (cgContext, static_cast (point.x), cgPoint.y - underlineOffset); + CGContextAddLineToPoint (cgContext, cgPoint.x, cgPoint.y - underlineOffset); + CGContextDrawPath (cgContext, kCGPathStroke); + } + if (style & kStrikethroughFace) + { + CGColorRef cgColorRef = getCGColor (context->getFontColor ()); + CGFloat underlineThickness = CTFontGetUnderlineThickness (fontRef); + CGFloat offset = CTFontGetXHeight (fontRef) * 0.5f; + CGContextSetStrokeColorWithColor (cgContext, cgColorRef); + CGContextSetLineWidth (cgContext, underlineThickness); + cgPoint = CGContextGetTextPosition (cgContext); + CGContextBeginPath (cgContext); + CGContextMoveToPoint (cgContext, static_cast (point.x), cgPoint.y - offset); + CGContextAddLineToPoint (cgContext, cgPoint.x, cgPoint.y - offset); + CGContextDrawPath (cgContext, kCGPathStroke); + } + cgDrawContext->releaseCGContext (cgContext); + } + CFRelease (line); + } } //----------------------------------------------------------------------------- -CCoord CoreTextFont::getStringWidth (const PlatformGraphicsDeviceContextPtr& context, - IPlatformString* string, bool antialias) const +CCoord CoreTextFont::getStringWidth (CDrawContext* context, IPlatformString* string, bool antialias) const { CCoord result = 0; MacString* macString = dynamic_cast (string); if (macString == nullptr) return result; - - CTLineRef line = createCTLine (context, macString, kBlackCColor); + + CTLineRef line = createCTLine (context, macString); if (line) { result = CTLineGetTypographicBounds (line, nullptr, nullptr, nullptr); diff --git a/vstgui/lib/platform/mac/cgdrawcontext.cpp b/vstgui/lib/platform/mac/cgdrawcontext.cpp new file mode 100644 index 000000000..1183a8de1 --- /dev/null +++ b/vstgui/lib/platform/mac/cgdrawcontext.cpp @@ -0,0 +1,953 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#include "cgdrawcontext.h" + +#if MAC + +#include "macglobals.h" +#include "cgbitmap.h" +#include "quartzgraphicspath.h" +#include "cfontmac.h" +#include "../../cbitmap.h" +#include "../../cgradient.h" + +#ifndef CGFLOAT_DEFINED + #define CGFLOAT_DEFINED + using CGFloat = float; +#endif // CGFLOAT_DEFINED + +namespace VSTGUI { + +//----------------------------------------------------------------------------- +template +void DoGraphicStateSave (CGContextRef cgContext, Proc proc) +{ + CGContextSaveGState (cgContext); + proc (); + CGContextRestoreGState (cgContext); +} + +//----------------------------------------------------------------------------- +CGDrawContext::CGDrawContext (CGContextRef cgContext, const CRect& rect) +: COffscreenContext (rect), cgContext (cgContext), scaleFactor (1.) +{ + CFRetain (cgContext); + + // Get the scale for the context to check if it is for a Retina display + CGRect userRect = CGRectMake (0, 0, 100, 100); + CGRect deviceRect = CGContextConvertRectToDeviceSpace (cgContext, userRect); + scaleFactor = deviceRect.size.height / userRect.size.height; + + init (); +} + +//----------------------------------------------------------------------------- +CGDrawContext::CGDrawContext (CGBitmap* _bitmap) +: COffscreenContext (new CBitmap (_bitmap)) +, cgContext (_bitmap->createCGContext ()) +, scaleFactor (_bitmap->getScaleFactor ()) +{ + if (scaleFactor != 1.) + { + CGContextConcatCTM (cgContext, + CGAffineTransformMakeScale (static_cast (scaleFactor), + static_cast (scaleFactor))); + } + + init (); + bitmap->forget (); +} + +//----------------------------------------------------------------------------- +void CGDrawContext::init () +{ + CGContextSaveGState (cgContext); + CGContextSetAllowsAntialiasing (cgContext, true); + CGContextSetAllowsFontSmoothing (cgContext, true); + CGContextSetAllowsFontSubpixelPositioning (cgContext, true); + CGContextSetAllowsFontSubpixelQuantization (cgContext, true); + CGContextSetShouldAntialias (cgContext, false); + CGContextSetFillColorSpace (cgContext, GetCGColorSpace ()); + CGContextSetStrokeColorSpace (cgContext, GetCGColorSpace ()); + CGContextSaveGState (cgContext); + CGAffineTransform cgCTM = CGAffineTransformMake (1.0, 0.0, 0.0, -1.0, 0.0, 0.0); + CGContextSetTextMatrix (cgContext, cgCTM); + + CDrawContext::init (); +} + +//----------------------------------------------------------------------------- +CGDrawContext::~CGDrawContext () noexcept +{ + CGContextRestoreGState (cgContext); // restore the original state + CGContextRestoreGState (cgContext); // we need to do it twice !!! + CFRelease (cgContext); +} + +//----------------------------------------------------------------------------- +void CGDrawContext::endDraw () +{ + if (bitmap && bitmap->getPlatformBitmap ()) + { + if (auto cgBitmap = bitmap->getPlatformBitmap ().cast ()) + cgBitmap->setDirty (); + } + bitmapDrawCount.clear (); +} + +//----------------------------------------------------------------------------- +CGraphicsPath* CGDrawContext::createGraphicsPath () +{ + return new CGraphicsPath (CGGraphicsPathFactory::instance ()); +} + +//----------------------------------------------------------------------------- +CGraphicsPath* CGDrawContext::createTextPath (const CFontRef font, UTF8StringPtr text) +{ + if (auto path = + CGGraphicsPathFactory::instance ()->createTextPath (font->getPlatformFont (), text)) + { + return new CGraphicsPath (CGGraphicsPathFactory::instance (), std::move (path)); + } + return nullptr; +} + +//----------------------------------------------------------------------------- +void CGDrawContext::drawGraphicsPath (CGraphicsPath* path, PathDrawMode mode, CGraphicsTransform* t) +{ + if (!path) + return; + const auto& graphicsPath = path->getPlatformPath (PlatformGraphicsPathFillMode::Ignored); + if (!graphicsPath) + return; + auto cgPath = dynamic_cast (graphicsPath.get ()); + if (!cgPath) + return; + + if (auto context = beginCGContext (true, getDrawMode ().integralMode ())) + { + CGPathDrawingMode cgMode; + switch (mode) + { + case kPathFilledEvenOdd: + { + cgMode = kCGPathEOFill; + break; + } + case kPathStroked: + { + cgMode = kCGPathStroke; + applyLineStyle (context); + break; + } + default: + { + cgMode = kCGPathFill; + break; + } + } + + DoGraphicStateSave (context, [&] () { + if (t) + { + CGAffineTransform transform = createCGAffineTransform (*t); + CGContextConcatCTM (context, transform); + } + if (getDrawMode ().integralMode () && getDrawMode ().aliasing ()) + { + DoGraphicStateSave (context, [&] () { + applyLineWidthCTM (context); + cgPath->pixelAlign ( + [] (const CGPoint& p, void* context) { + auto cgDrawContext = reinterpret_cast (context); + return cgDrawContext->pixelAlligned (p); + }, + this); + }); + CGContextAddPath (context, cgPath->getCGPathRef ()); + } + else + CGContextAddPath (context, cgPath->getCGPathRef ()); + + }); + CGContextDrawPath (context, cgMode); + + releaseCGContext (context); + } +} + +//----------------------------------------------------------------------------- +void CGDrawContext::fillLinearGradient (CGraphicsPath* path, const CGradient& gradient, + const CPoint& startPoint, const CPoint& endPoint, + bool evenOdd, CGraphicsTransform* t) +{ + if (path == nullptr) + return; + + auto cgGradient = dynamic_cast (gradient.getPlatformGradient ().get ()); + if (cgGradient == nullptr) + return; + + const auto& graphicsPath = path->getPlatformPath (PlatformGraphicsPathFillMode::Ignored); + if (!graphicsPath) + return; + auto cgPath = dynamic_cast (graphicsPath.get ()); + if (!cgPath) + return; + + if (auto context = beginCGContext (true, getDrawMode ().integralMode ())) + { + CGPoint start = CGPointFromCPoint (startPoint); + CGPoint end = CGPointFromCPoint (endPoint); + DoGraphicStateSave (context, [&] () { + if (getDrawMode ().integralMode ()) + { + start = pixelAlligned (start); + end = pixelAlligned (end); + } + if (t) + { + CGAffineTransform transform = createCGAffineTransform (*t); + CGContextConcatCTM (context, transform); + } + if (getDrawMode ().integralMode () && getDrawMode ().aliasing ()) + { + cgPath->pixelAlign ( + [] (const CGPoint& p, void* context) { + auto cgDrawContext = reinterpret_cast (context); + return cgDrawContext->pixelAlligned (p); + }, + this); + } + + CGContextAddPath (context, cgPath->getCGPathRef ()); + }); + + if (evenOdd) + CGContextEOClip (context); + else + CGContextClip (context); + + CGContextDrawLinearGradient (context, *cgGradient, start, end, + kCGGradientDrawsBeforeStartLocation | + kCGGradientDrawsAfterEndLocation); + + releaseCGContext (context); + } +} + +//----------------------------------------------------------------------------- +void CGDrawContext::fillRadialGradient (CGraphicsPath* path, const CGradient& gradient, + const CPoint& center, CCoord radius, + const CPoint& originOffset, bool evenOdd, + CGraphicsTransform* t) +{ + if (path == nullptr) + return; + + auto cgGradient = dynamic_cast (gradient.getPlatformGradient ().get ()); + if (cgGradient == nullptr) + return; + + const auto& graphicsPath = path->getPlatformPath (PlatformGraphicsPathFillMode::Ignored); + if (!graphicsPath) + return; + auto cgPath = dynamic_cast (graphicsPath.get ()); + if (!cgPath) + return; + + if (auto context = beginCGContext (true, getDrawMode ().integralMode ())) + { + DoGraphicStateSave (context, [&] () { + if (t) + { + CGAffineTransform transform = createCGAffineTransform (*t); + CGContextConcatCTM (context, transform); + } + if (getDrawMode ().integralMode () && getDrawMode ().aliasing ()) + { + cgPath->pixelAlign ( + [] (const CGPoint& p, void* context) { + auto cgDrawContext = reinterpret_cast (context); + return cgDrawContext->pixelAlligned (p); + }, + this); + } + + CGContextAddPath (context, cgPath->getCGPathRef ()); + }); + + if (evenOdd) + CGContextEOClip (context); + else + CGContextClip (context); + + CPoint startCenter = center + originOffset; + CGContextDrawRadialGradient (context, *cgGradient, CGPointFromCPoint (startCenter), 0, + CGPointFromCPoint (center), static_cast (radius), + kCGGradientDrawsBeforeStartLocation | + kCGGradientDrawsAfterEndLocation); + + releaseCGContext (context); + } +} + +//----------------------------------------------------------------------------- +void CGDrawContext::saveGlobalState () +{ + CGContextSaveGState (cgContext); + CDrawContext::saveGlobalState (); +} + +//----------------------------------------------------------------------------- +void CGDrawContext::restoreGlobalState () +{ + CDrawContext::restoreGlobalState (); + CGContextRestoreGState (cgContext); +} + +//----------------------------------------------------------------------------- +void CGDrawContext::setGlobalAlpha (float newAlpha) +{ + if (newAlpha == getCurrentState ().globalAlpha) + return; + + CGContextSetAlpha (cgContext, newAlpha); + + CDrawContext::setGlobalAlpha (newAlpha); +} + +//----------------------------------------------------------------------------- +void CGDrawContext::setLineStyle (const CLineStyle& style) +{ + if (getCurrentState ().lineStyle == style) + return; + + CDrawContext::setLineStyle (style); +} + +//----------------------------------------------------------------------------- +void CGDrawContext::setLineWidth (CCoord width) +{ + if (getCurrentState ().frameWidth == width) + return; + + CGContextSetLineWidth (cgContext, static_cast (width)); + + CDrawContext::setLineWidth (width); +} + +//----------------------------------------------------------------------------- +void CGDrawContext::setDrawMode (CDrawMode mode) +{ + if (cgContext) + CGContextSetShouldAntialias (cgContext, mode.antiAliasing ()); + + CDrawContext::setDrawMode (mode); +} + +//------------------------------------------------------------------------------ +void CGDrawContext::setClipRect (const CRect& clip) +{ + CDrawContext::setClipRect (clip); +} + +//------------------------------------------------------------------------------ +void CGDrawContext::resetClipRect () +{ + CDrawContext::resetClipRect (); +} + +//----------------------------------------------------------------------------- +void CGDrawContext::drawLine (const LinePair& line) +{ + if (auto context = beginCGContext (true, getDrawMode ().integralMode ())) + { + applyLineStyle (context); + + CGContextBeginPath (context); + CGPoint first = CGPointFromCPoint (line.first); + CGPoint second = CGPointFromCPoint (line.second); + + if (getDrawMode ().integralMode ()) + { + first = pixelAlligned (first); + second = pixelAlligned (second); + applyLineWidthCTM (context); + } + + CGContextMoveToPoint (context, first.x, first.y); + CGContextAddLineToPoint (context, second.x, second.y); + + CGContextDrawPath (context, kCGPathStroke); + releaseCGContext (context); + } +} + +//----------------------------------------------------------------------------- +void CGDrawContext::drawLines (const LineList& lines) +{ + if (lines.size () == 0) + return; + if (auto context = beginCGContext (true, getDrawMode ().integralMode ())) + { + applyLineStyle (context); + + static constexpr auto numStackPoints = 32; + CGPoint stackPoints[numStackPoints]; + + auto cgPointsPtr = (lines.size () * 2) < numStackPoints + ? nullptr + : std::unique_ptr (new CGPoint[lines.size () * 2]); + auto cgPoints = cgPointsPtr ? cgPointsPtr.get () : stackPoints; + uint32_t index = 0; + if (getDrawMode ().integralMode ()) + { + for (const auto& line : lines) + { + cgPoints[index] = pixelAlligned (CGPointFromCPoint (line.first)); + cgPoints[index + 1] = pixelAlligned (CGPointFromCPoint (line.second)); + index += 2; + } + } + else + { + for (const auto& line : lines) + { + cgPoints[index] = CGPointFromCPoint (line.first); + cgPoints[index + 1] = CGPointFromCPoint (line.second); + index += 2; + } + } + + if (getDrawMode ().integralMode ()) + applyLineWidthCTM (context); + + const size_t maxPointsPerIteration = 16; + const CGPoint* pointPtr = cgPoints; + size_t numPoints = lines.size () * 2; + while (numPoints) + { + size_t np = std::min (numPoints, std::min (maxPointsPerIteration, numPoints)); + CGContextStrokeLineSegments (context, pointPtr, np); + numPoints -= np; + pointPtr += np; + } + + releaseCGContext (context); + } +} + +//----------------------------------------------------------------------------- +void CGDrawContext::drawPolygon (const PointList& polygonPointList, const CDrawStyle drawStyle) +{ + if (polygonPointList.size () == 0) + return; + if (auto context = beginCGContext (true, getDrawMode ().integralMode ())) + { + CGPathDrawingMode m; + switch (drawStyle) + { + case kDrawFilled: m = kCGPathFill; break; + case kDrawFilledAndStroked: m = kCGPathFillStroke; break; + default: m = kCGPathStroke; break; + } + applyLineStyle (context); + + CGContextBeginPath (context); + CGPoint p = CGPointFromCPoint (polygonPointList[0]); + if (getDrawMode ().integralMode ()) + p = pixelAlligned (p); + CGContextMoveToPoint (context, p.x, p.y); + for (uint32_t i = 1; i < polygonPointList.size (); i++) + { + p = CGPointFromCPoint (polygonPointList[i]); + if (getDrawMode ().integralMode ()) + p = pixelAlligned (p); + CGContextAddLineToPoint (context, p.x, p.y); + } + CGContextDrawPath (context, m); + releaseCGContext (context); + } +} + +//----------------------------------------------------------------------------- +void CGDrawContext::applyLineWidthCTM (CGContextRef context) const +{ + int32_t frameWidth = static_cast (getCurrentState ().frameWidth); + if (static_cast (frameWidth) == getCurrentState ().frameWidth && frameWidth % 2) + CGContextTranslateCTM (context, 0.5, 0.5); +} + +//----------------------------------------------------------------------------- +void CGDrawContext::drawRect (const CRect& rect, const CDrawStyle drawStyle) +{ + if (auto context = beginCGContext (true, getDrawMode ().integralMode ())) + { + CGRect r = CGRectFromCRect (rect); + if (drawStyle != kDrawFilled) + { + r.size.width -= 1.; + r.size.height -= 1.; + } + + CGPathDrawingMode m; + switch (drawStyle) + { + case kDrawFilled: m = kCGPathFill; break; + case kDrawFilledAndStroked: m = kCGPathFillStroke; break; + default: m = kCGPathStroke; break; + } + applyLineStyle (context); + + if (getDrawMode ().integralMode ()) + { + r = pixelAlligned (r); + if (drawStyle != kDrawFilled) + applyLineWidthCTM (context); + } + + CGContextBeginPath (context); + CGContextAddRect (context, r); + CGContextDrawPath (context, m); + + releaseCGContext (context); + } +} + +//----------------------------------------------------------------------------- +void CGDrawContext::drawEllipse (const CRect& rect, const CDrawStyle drawStyle) +{ + if (auto context = beginCGContext (true, getDrawMode ().integralMode ())) + { + CGRect r = CGRectFromCRect (rect); + if (drawStyle != kDrawFilled) + { + r.size.width -= 1.; + r.size.height -= 1.; + } + + CGPathDrawingMode m; + switch (drawStyle) + { + case kDrawFilled: m = kCGPathFill; break; + case kDrawFilledAndStroked: m = kCGPathFillStroke; break; + default: m = kCGPathStroke; break; + } + applyLineStyle (context); + if (getDrawMode ().integralMode ()) + { + if (drawStyle != kDrawFilled) + applyLineWidthCTM (context); + r = pixelAlligned (r); + } + + CGContextAddEllipseInRect (context, r); + CGContextDrawPath (context, m); + + releaseCGContext (context); + } +} + +//----------------------------------------------------------------------------- +void CGDrawContext::drawPoint (const CPoint& point, const CColor& color) +{ + saveGlobalState (); + + setLineWidth (1); + setFrameColor (color); + CPoint point2 (point); + point2.x++; + COffscreenContext::drawLine (point, point2); + + restoreGlobalState (); +} + +//----------------------------------------------------------------------------- +void CGDrawContext::addOvalToPath (CGContextRef c, CPoint center, CGFloat a, CGFloat b, + CGFloat start_angle, CGFloat end_angle) const +{ + DoGraphicStateSave (c, [&] () { + CGContextTranslateCTM (c, static_cast (center.x), static_cast (center.y)); + CGContextScaleCTM (c, a, b); + + auto startAngle = radians (start_angle); + auto endAngle = radians (end_angle); + if (a != b) + { + startAngle = std::atan2 (std::sin (startAngle) * a, std::cos (startAngle) * b); + endAngle = std::atan2 (std::sin (endAngle) * a, std::cos (endAngle) * b); + } + CGContextMoveToPoint (c, static_cast (std::cos (startAngle)), + static_cast (std::sin (startAngle))); + CGContextAddArc (c, 0, 0, 1, static_cast (startAngle), + static_cast (endAngle), 0); + }); +} + +//----------------------------------------------------------------------------- +void CGDrawContext::drawArc (const CRect& rect, const float _startAngle, const float _endAngle, + const CDrawStyle drawStyle) +{ + if (auto context = beginCGContext (true, getDrawMode ().integralMode ())) + { + CGPathDrawingMode m; + switch (drawStyle) + { + case kDrawFilled: m = kCGPathFill; break; + case kDrawFilledAndStroked: m = kCGPathFillStroke; break; + default: m = kCGPathStroke; break; + } + applyLineStyle (context); + + CGContextBeginPath (context); + addOvalToPath ( + context, CPoint (rect.left + rect.getWidth () / 2., rect.top + rect.getHeight () / 2.), + static_cast (rect.getWidth () / 2.), + static_cast (rect.getHeight () / 2.), _startAngle, _endAngle); + CGContextDrawPath (context, m); + releaseCGContext (context); + } +} + +//----------------------------------------------------------------------------- +void CGDrawContext::drawBitmapNinePartTiled (CBitmap* bitmap, const CRect& inRect, + const CNinePartTiledDescription& desc, float alpha) +{ + // TODO: When drawing on a scaled transform the bitmaps are not alligned correctly + CDrawContext::drawBitmapNinePartTiled (bitmap, inRect, desc, alpha); + return; +} + +//----------------------------------------------------------------------------- +void CGDrawContext::fillRectWithBitmap (CBitmap* bitmap, const CRect& srcRect, const CRect& dstRect, + float alpha) +{ + if (bitmap == nullptr || alpha == 0.f || srcRect.isEmpty () || dstRect.isEmpty ()) + return; + + if (!(srcRect.left == 0 && srcRect.right == 0 && srcRect.right == bitmap->getWidth () && + srcRect.bottom == bitmap->getHeight ())) + { + // CGContextDrawTiledImage does not work with parts of a bitmap + CDrawContext::fillRectWithBitmap (bitmap, srcRect, dstRect, alpha); + return; + } + + auto platformBitmap = bitmap->getBestPlatformBitmapForScaleFactor (scaleFactor); + if (!platformBitmap) + return; + CPoint bitmapSize = platformBitmap->getSize (); + if (srcRect.right > bitmapSize.x || srcRect.bottom > bitmapSize.y) + return; + + auto cgBitmap = platformBitmap.cast (); + if (CGImageRef image = cgBitmap ? cgBitmap->getCGImage () : nullptr) + { + if (auto context = beginCGContext (false, true)) + { + // TODO: Check if this works with retina images + CGRect clipRect = CGRectFromCRect (dstRect); + clipRect.origin.y = -(clipRect.origin.y) - clipRect.size.height; + clipRect = pixelAlligned (clipRect); + CGContextClipToRect (context, clipRect); + + CGRect r = {}; + r.size.width = CGImageGetWidth (image); + r.size.height = CGImageGetHeight (image); + + setCGDrawContextQuality (context); + + CGContextDrawTiledImage (context, r, image); + + releaseCGContext (context); + } + } +} + +//----------------------------------------------------------------------------- +void CGDrawContext::drawBitmap (CBitmap* bitmap, const CRect& inRect, const CPoint& inOffset, + float alpha) +{ + if (bitmap == nullptr || alpha == 0.f) + return; + double transformedScaleFactor = scaleFactor; + CGraphicsTransform t = getCurrentTransform (); + if (t.m11 == t.m22 && t.m12 == 0 && t.m21 == 0) + transformedScaleFactor *= t.m11; + auto platformBitmap = bitmap->getBestPlatformBitmapForScaleFactor (transformedScaleFactor); + if (!platformBitmap) + return; + auto cgBitmap = platformBitmap.cast (); + if (CGImageRef image = cgBitmap ? cgBitmap->getCGImage () : nullptr) + { + if (auto context = beginCGContext (false, true)) + { + CGLayerRef layer = nullptr; + if (scaleFactor == 1.) + { + layer = cgBitmap->getCGLayer (); + if (layer == nullptr) + { + auto it = bitmapDrawCount.find (cgBitmap); + if (it == bitmapDrawCount.end ()) + { + bitmapDrawCount.emplace (cgBitmap, 1); + } + else + { + it->second++; + layer = cgBitmap->createCGLayer (context); + } + } + } + + drawCGImageRef (context, image, layer, cgBitmap->getScaleFactor (), inRect, inOffset, + alpha, bitmap); + + releaseCGContext (context); + } + } +} + +//----------------------------------------------------------------------------- +void CGDrawContext::drawCGImageRef (CGContextRef context, CGImageRef image, CGLayerRef layer, + double bitmapScaleFactor, const CRect& inRect, + const CPoint& inOffset, float alpha, CBitmap* bitmap) +{ + setCGDrawContextQuality (context); + + CRect rect (inRect); + CPoint offset (inOffset); + + CGContextSetAlpha (context, (CGFloat)alpha * getCurrentState ().globalAlpha); + + CGRect dest; + dest.origin.x = static_cast (rect.left - offset.x); + dest.origin.y = static_cast (-(rect.top) - (bitmap->getHeight () - offset.y)); + dest.size.width = static_cast (bitmap->getWidth ()); + dest.size.height = static_cast (bitmap->getHeight ()); + + CGRect clipRect; + clipRect.origin.x = static_cast (rect.left); + clipRect.origin.y = static_cast (-(rect.top) - rect.getHeight ()); + clipRect.size.width = static_cast (rect.getWidth ()); + clipRect.size.height = static_cast (rect.getHeight ()); + + if (bitmapScaleFactor != 1.) + { + CGContextConcatCTM ( + context, CGAffineTransformMakeScale (static_cast (1. / bitmapScaleFactor), + static_cast (1. / bitmapScaleFactor))); + CGAffineTransform transform = CGAffineTransformMakeScale ( + static_cast (bitmapScaleFactor), static_cast (bitmapScaleFactor)); + clipRect.origin = CGPointApplyAffineTransform (clipRect.origin, transform); + clipRect.size = CGSizeApplyAffineTransform (clipRect.size, transform); + dest.origin = CGPointApplyAffineTransform (dest.origin, transform); + dest.size = CGSizeApplyAffineTransform (dest.size, transform); + } + // dest.origin = pixelAlligned (dest.origin); + clipRect.origin = pixelAlligned (clipRect.origin); + + CGContextClipToRect (context, clipRect); + + if (layer) + { + CGContextDrawLayerInRect (context, dest, layer); + } + else + { + CGContextDrawImage (context, dest, image); + } +} + +//----------------------------------------------------------------------------- +void CGDrawContext::setCGDrawContextQuality (CGContextRef context) +{ + switch (getCurrentState ().bitmapQuality) + { + case BitmapInterpolationQuality::kLow: + { + CGContextSetShouldAntialias (context, false); + CGContextSetInterpolationQuality (context, kCGInterpolationNone); + break; + } + case BitmapInterpolationQuality::kMedium: + { + CGContextSetShouldAntialias (context, true); + CGContextSetInterpolationQuality (context, kCGInterpolationMedium); + break; + } + case BitmapInterpolationQuality::kHigh: + { + CGContextSetShouldAntialias (context, true); + CGContextSetInterpolationQuality (context, kCGInterpolationHigh); + break; + } + default: break; + } +} + +//----------------------------------------------------------------------------- +void CGDrawContext::clearRect (const CRect& rect) +{ + if (auto context = beginCGContext (true, getDrawMode ().integralMode ())) + { + CGRect cgRect = CGRectFromCRect (rect); + if (getDrawMode ().integralMode ()) + { + cgRect = pixelAlligned (cgRect); + } + CGContextClearRect (context, cgRect); + releaseCGContext (context); + } +} + +//----------------------------------------------------------------------------- +void CGDrawContext::setFontColor (const CColor& color) +{ + if (getCurrentState ().fontColor == color) + return; + + CDrawContext::setFontColor (color); +} + +//----------------------------------------------------------------------------- +void CGDrawContext::setFrameColor (const CColor& color) +{ + if (getCurrentState ().frameColor == color) + return; + + if (cgContext) + CGContextSetStrokeColorWithColor (cgContext, getCGColor (color)); + + CDrawContext::setFrameColor (color); +} + +//----------------------------------------------------------------------------- +void CGDrawContext::setFillColor (const CColor& color) +{ + if (getCurrentState ().fillColor == color) + return; + + if (cgContext) + CGContextSetFillColorWithColor (cgContext, getCGColor (color)); + + CDrawContext::setFillColor (color); +} + +#if DEBUG +bool showClip = false; +#endif + +//----------------------------------------------------------------------------- +CGContextRef CGDrawContext::beginCGContext (bool swapYAxis, bool integralOffset) +{ + if (cgContext) + { + if (getCurrentState ().clipRect.isEmpty ()) + return nullptr; + + CGContextSaveGState (cgContext); + + CGRect cgClipRect = CGRectFromCRect (getCurrentState ().clipRect); + if (integralOffset) + cgClipRect = pixelAlligned (cgClipRect); + CGContextClipToRect (cgContext, cgClipRect); +#if DEBUG + if (showClip) + { + CGContextSetRGBFillColor (cgContext, 1, 0, 0, 0.5); + CGContextFillRect (cgContext, cgClipRect); + } +#endif + + if (getCurrentTransform ().isInvariant () == false) + { + CGraphicsTransform t = getCurrentTransform (); + if (integralOffset) + { + CGPoint p = pixelAlligned (CGPointFromCPoint (CPoint (t.dx, t.dy))); + t.dx = p.x; + t.dy = p.y; + } + CGContextConcatCTM (cgContext, createCGAffineTransform (t)); + } + + if (!swapYAxis) + CGContextScaleCTM (cgContext, 1, -1); + + return cgContext; + } + return nullptr; +} + +//----------------------------------------------------------------------------- +void CGDrawContext::releaseCGContext (CGContextRef context) +{ + if (context) + { + CGContextRestoreGState (context); + } +} + +//----------------------------------------------------------------------------- +void CGDrawContext::applyLineStyle (CGContextRef context) +{ + switch (getCurrentState ().lineStyle.getLineCap ()) + { + case CLineStyle::kLineCapButt: CGContextSetLineCap (context, kCGLineCapButt); break; + case CLineStyle::kLineCapRound: CGContextSetLineCap (context, kCGLineCapRound); break; + case CLineStyle::kLineCapSquare: CGContextSetLineCap (context, kCGLineCapSquare); break; + } + switch (getCurrentState ().lineStyle.getLineJoin ()) + { + case CLineStyle::kLineJoinMiter: CGContextSetLineJoin (context, kCGLineJoinMiter); break; + case CLineStyle::kLineJoinRound: CGContextSetLineJoin (context, kCGLineJoinRound); break; + case CLineStyle::kLineJoinBevel: CGContextSetLineJoin (context, kCGLineJoinBevel); break; + } + if (getCurrentState ().lineStyle.getDashCount () > 0) + { + CGFloat* dashLengths = new CGFloat[getCurrentState ().lineStyle.getDashCount ()]; + for (uint32_t i = 0; i < getCurrentState ().lineStyle.getDashCount (); i++) + { + dashLengths[i] = static_cast ( + getCurrentState ().frameWidth * getCurrentState ().lineStyle.getDashLengths ()[i]); + } + CGContextSetLineDash (context, + static_cast (getCurrentState ().lineStyle.getDashPhase ()), + dashLengths, getCurrentState ().lineStyle.getDashCount ()); + delete[] dashLengths; + } +} + +//----------------------------------------------------------------------------- +CGRect CGDrawContext::pixelAlligned (const CGRect& r) const +{ + CGRect result; + result.origin = CGContextConvertPointToDeviceSpace (cgContext, r.origin); + result.size = CGContextConvertSizeToDeviceSpace (cgContext, r.size); + result.origin.x = static_cast (std::round (result.origin.x)); + result.origin.y = static_cast (std::round (result.origin.y)); + result.size.width = static_cast (std::round (result.size.width)); + result.size.height = static_cast (std::round (result.size.height)); + result.origin = CGContextConvertPointToUserSpace (cgContext, result.origin); + result.size = CGContextConvertSizeToUserSpace (cgContext, result.size); + return result; +} + +//----------------------------------------------------------------------------- +CGPoint CGDrawContext::pixelAlligned (const CGPoint& p) const +{ + CGPoint result = CGContextConvertPointToDeviceSpace (cgContext, p); + result.x = static_cast (std::round (result.x)); + result.y = static_cast (std::round (result.y)); + result = CGContextConvertPointToUserSpace (cgContext, result); + return result; +} + +} // VSTGUI + +#endif // MAC diff --git a/vstgui/lib/platform/mac/cgdrawcontext.h b/vstgui/lib/platform/mac/cgdrawcontext.h new file mode 100644 index 000000000..0296db671 --- /dev/null +++ b/vstgui/lib/platform/mac/cgdrawcontext.h @@ -0,0 +1,89 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once + +#include "../../coffscreencontext.h" + +#if MAC + +#if TARGET_OS_IPHONE + #include + #include +#else + #include +#endif + +#include + +namespace VSTGUI { +class CGBitmap; + +//----------------------------------------------------------------------------- +class CGDrawContext : public COffscreenContext +{ +public: + CGDrawContext (CGContextRef cgContext, const CRect& rect); + explicit CGDrawContext (CGBitmap* bitmap); + ~CGDrawContext () noexcept override; + + void drawLine (const LinePair& line) override; + void drawLines (const LineList& lines) override; + void drawPolygon (const PointList& polygonPointList, const CDrawStyle drawStyle = kDrawStroked) override; + void drawRect (const CRect &rect, const CDrawStyle drawStyle = kDrawStroked) override; + void drawArc (const CRect &rect, const float startAngle1, const float endAngle2, const CDrawStyle drawStyle = kDrawStroked) override; + void drawEllipse (const CRect &rect, const CDrawStyle drawStyle = kDrawStroked) override; + void drawPoint (const CPoint &point, const CColor& color) override; + void drawBitmap (CBitmap* bitmap, const CRect& dest, const CPoint& offset = CPoint (0, 0), float alpha = 1.f) override; + void drawBitmapNinePartTiled (CBitmap* bitmap, const CRect& dest, const CNinePartTiledDescription& desc, float alpha = 1.f) override; + void fillRectWithBitmap (CBitmap* bitmap, const CRect& srcRect, const CRect& dstRect, float alpha) override; + void clearRect (const CRect& rect) override; + void setLineStyle (const CLineStyle& style) override; + void setLineWidth (CCoord width) override; + void setDrawMode (CDrawMode mode) override; + void setClipRect (const CRect &clip) override; + void resetClipRect () override; + void setFillColor (const CColor& color) override; + void setFrameColor (const CColor& color) override; + void setFontColor (const CColor& color) override; + void setGlobalAlpha (float newAlpha) override; + void saveGlobalState () override; + void restoreGlobalState () override; + void endDraw () override; + CGraphicsPath* createGraphicsPath () override; + CGraphicsPath* createTextPath (const CFontRef font, UTF8StringPtr text) override; + void drawGraphicsPath (CGraphicsPath* path, PathDrawMode mode = kPathFilled, CGraphicsTransform* transformation = nullptr) override; + void fillLinearGradient (CGraphicsPath* path, const CGradient& gradient, const CPoint& startPoint, const CPoint& endPoint, bool evenOdd = false, CGraphicsTransform* transformation = nullptr) override; + void fillRadialGradient (CGraphicsPath* path, const CGradient& gradient, const CPoint& center, CCoord radius, const CPoint& originOffset = CPoint (0, 0), bool evenOdd = false, CGraphicsTransform* transformation = nullptr) override; + double getScaleFactor () const override { return scaleFactor; } + + CGContextRef beginCGContext (bool swapYAxis = false, bool integralOffset = false); + void releaseCGContext (CGContextRef context); + + CGContextRef getCGContext () const { return cgContext; } + void applyLineStyle (CGContextRef context); + void applyLineWidthCTM (CGContextRef context) const; + + CGRect pixelAlligned (const CGRect& r) const; + CGPoint pixelAlligned (const CGPoint& p) const; + +//------------------------------------------------------------------------------------ +protected: + void init () override; + void drawCGImageRef (CGContextRef context, CGImageRef image, CGLayerRef layer, double imageScaleFactor, const CRect& inRect, const CPoint& inOffset, float alpha, CBitmap* bitmap); + void setCGDrawContextQuality (CGContextRef context); + void addOvalToPath (CGContextRef c, CPoint center, CGFloat a, CGFloat b, CGFloat start_angle, + CGFloat end_angle) const; + + CGContextRef cgContext; + + using BitmapDrawCountMap = std::map; + BitmapDrawCountMap bitmapDrawCount; + + double scaleFactor; +}; + +} // VSTGUI + +#endif // MAC diff --git a/vstgui/lib/platform/mac/cocoa/cocoatextedit.mm b/vstgui/lib/platform/mac/cocoa/cocoatextedit.mm index 4e906958b..02c4ad1de 100644 --- a/vstgui/lib/platform/mac/cocoa/cocoatextedit.mm +++ b/vstgui/lib/platform/mac/cocoa/cocoatextedit.mm @@ -188,6 +188,9 @@ static id Init (id self, SEL _cmd, void* textEdit) if (auto tv = static_cast ([[self window] fieldEditor:YES forObject:self])) tv.insertionPointColor = nsColorFromCColor (tec->platformGetFontColor ()); + if ([frameView respondsToSelector:@selector (makeSubViewFirstResponder:)]) + [frameView performSelector:@selector (makeSubViewFirstResponder:) withObject:self]; + dispatch_after (dispatch_time (DISPATCH_TIME_NOW, (int64_t) (1 * NSEC_PER_MSEC)), dispatch_get_main_queue (), ^{ [[self window] makeFirstResponder:self]; diff --git a/vstgui/lib/platform/mac/cocoa/nsviewframe.h b/vstgui/lib/platform/mac/cocoa/nsviewframe.h index fec78e272..f470a68a6 100644 --- a/vstgui/lib/platform/mac/cocoa/nsviewframe.h +++ b/vstgui/lib/platform/mac/cocoa/nsviewframe.h @@ -9,9 +9,9 @@ #if MAC_COCOA && !TARGET_OS_IPHONE #include "../../platform_macos.h" +#include "../../../cview.h" #include "../../../cinvalidrectlist.h" #include "../../../idatapackage.h" -#import "../coregraphicsdevicecontext.h" #include "nsviewdraggingsession.h" #include @@ -47,6 +47,8 @@ class NSViewFrame : public IPlatformFrame, public ICocoaPlatformFrame, public IP #if VSTGUI_ENABLE_DEPRECATED_METHODS void setLastDragOperationResult (DragResult result) { lastDragOperationResult = result; } #endif + void setIgnoreNextResignFirstResponder (bool state) { ignoreNextResignFirstResponder = state; } + bool getIgnoreNextResignFirstResponder () const { return ignoreNextResignFirstResponder; } void setDragDataPackage (SharedPointer&& package) { dragDataPackage = std::move (package); } const SharedPointer& getDragDataPackage () const { return dragDataPackage; } @@ -54,8 +56,8 @@ class NSViewFrame : public IPlatformFrame, public ICocoaPlatformFrame, public IP void initTrackingArea (); void scaleFactorChanged (double newScaleFactor); void cursorUpdate (); + virtual void drawRect (NSRect* rect); void drawLayer (CALayer* layer, CGContextRef ctx); - void drawRect (NSRect* rect); bool onMouseDown (NSEvent* evt); bool onMouseUp (NSEvent* evt); bool onMouseMoved (NSEvent* evt); @@ -95,7 +97,6 @@ class NSViewFrame : public IPlatformFrame, public ICocoaPlatformFrame, public IP //----------------------------------------------------------------------------- protected: - void draw (CGContextRef context, CRect updateRect, double scaleFactor); void addDebugRedrawRect (CRect r, bool isClipBoundingBox = false); NSView* nsView {nullptr}; @@ -109,6 +110,7 @@ class NSViewFrame : public IPlatformFrame, public ICocoaPlatformFrame, public IP #if VSTGUI_ENABLE_DEPRECATED_METHODS DragResult lastDragOperationResult; #endif + bool ignoreNextResignFirstResponder; bool trackingAreaInitialized; bool inDraw; bool useInvalidRects {false}; diff --git a/vstgui/lib/platform/mac/cocoa/nsviewframe.mm b/vstgui/lib/platform/mac/cocoa/nsviewframe.mm index f79821af4..bca36f481 100644 --- a/vstgui/lib/platform/mac/cocoa/nsviewframe.mm +++ b/vstgui/lib/platform/mac/cocoa/nsviewframe.mm @@ -1,4 +1,4 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE @@ -14,6 +14,7 @@ #import "autoreleasepool.h" #import "../macclipboard.h" #import "../macfactory.h" +#import "../cgdrawcontext.h" #import "../cgbitmap.h" #import "../quartzgraphicspath.h" #import "../caviewlayer.h" @@ -186,8 +187,6 @@ static Class CreateClass () .addMethod (@selector (acceptsFirstResponder), acceptsFirstResponder) .addMethod (@selector (becomeFirstResponder), becomeFirstResponder) .addMethod (@selector (resignFirstResponder), resignFirstResponder) - .addMethod (@selector (nextValidKeyView), nextValidKeyView) - .addMethod (@selector (previousValidKeyView), previousValidKeyView) .addMethod (@selector (canBecomeKeyView), canBecomeKeyView) .addMethod (@selector (wantsDefaultClipping), wantsDefaultClipping) .addMethod (@selector (isOpaque), isOpaque) @@ -218,6 +217,7 @@ static Class CreateClass () .addMethod (@selector (keyUp:), keyUp) .addMethod (@selector (magnifyWithEvent:), magnifyWithEvent) .addMethod (@selector (focusRingType), focusRingType) + .addMethod (@selector (makeSubViewFirstResponder:), makeSubViewFirstResponder) .addMethod (@selector (draggingEntered:), draggingEntered) .addMethod (@selector (draggingUpdated:), draggingUpdated) .addMethod (@selector (draggingExited:), draggingExited) @@ -309,6 +309,18 @@ static id init (id self, SEL _cmd, void* _frame, NSView* parentView, const void* static NSFocusRingType focusRingType (id self) { return NSFocusRingTypeNone; } static BOOL shouldBeTreatedAsInkEvent (id self, SEL _cmd, NSEvent* event) { return NO; } + //------------------------------------------------------------------------------------ + static void makeSubViewFirstResponder (id self, SEL _cmd, NSResponder* newFirstResponder) + { + NSViewFrame* nsFrame = getNSViewFrame (self); + if (nsFrame) + { + nsFrame->setIgnoreNextResignFirstResponder (true); + [[self window] makeFirstResponder:newFirstResponder]; + nsFrame->setIgnoreNextResignFirstResponder (false); + } + } + //------------------------------------------------------------------------------------ static BOOL becomeFirstResponder (id self, SEL _cmd) { @@ -329,9 +341,15 @@ static BOOL resignFirstResponder (id self, SEL _cmd) firstResponder = nil; if (firstResponder) { - if ([firstResponder isDescendantOf:self]) + NSViewFrame* nsFrame = getNSViewFrame (self); + if (nsFrame && nsFrame->getIgnoreNextResignFirstResponder ()) { - return YES; + while (firstResponder != self && firstResponder != nil) + firstResponder = [firstResponder superview]; + if (firstResponder == self && [[self window] isKeyWindow]) + { + return YES; + } } IPlatformFrameCallback* frame = getFrame (self); if (frame) @@ -340,26 +358,6 @@ static BOOL resignFirstResponder (id self, SEL _cmd) return YES; } - //------------------------------------------------------------------------------------ - static NSView* nextValidKeyView (id self, SEL _cmd) - { - auto view = - makeInstance (self).callSuper (@selector (nextValidKeyView)); - while (view != self && [view isDescendantOf:self]) - view = view.nextValidKeyView; - return view; - } - - //------------------------------------------------------------------------------------ - static NSView* previousValidKeyView (id self, SEL _cmd) - { - auto view = makeInstance (self).callSuper ( - @selector (previousValidKeyView)); - while (view != self && [view isDescendantOf:self]) - view = view.previousValidKeyView; - return view; - } - //------------------------------------------------------------------------------------ static void updateTrackingAreas (id self, SEL _cmd) { @@ -939,6 +937,7 @@ static void draggedImageEndedAtOperation (id self, SEL _cmd, NSImage* image, NSP NSViewFrame::NSViewFrame (IPlatformFrameCallback* frame, const CRect& size, NSView* parent, IPlatformFrameConfig* config) : IPlatformFrame (frame) +, ignoreNextResignFirstResponder (false) , trackingAreaInitialized (false) , inDraw (false) , cursor (kCursorDefault) @@ -950,29 +949,33 @@ static void draggedImageEndedAtOperation (id self, SEL _cmd, NSImage* image, NSP if (cocoaConfig && cocoaConfig->flags & CocoaFrameConfig::kNoCALayer) return; - // on Mac OS X 10.11 we activate layer drawing as this fixes a few issues like that only a - // few parts of a window are updated permanently when scrolling or manipulating a control - // while other parts are only updated when the malipulation ended, or CNinePartTiledBitmap - // are drawn incorrectly when scaled. - if (@available (macOS 10.11, *)) + auto processInfo = [NSProcessInfo processInfo]; + if ([processInfo respondsToSelector:@selector(operatingSystemVersion)]) { - [nsView setWantsLayer:YES]; - caLayer = [CALayer new]; - caLayer.delegate = static_cast> (nsView); - caLayer.frame = nsView.layer.bounds; - [caLayer setContentsScale:nsView.layer.contentsScale]; - [nsView.layer addSublayer:caLayer]; - useInvalidRects = true; - if (@available (macOS 10.13, *)) + // on Mac OS X 10.11 we activate layer drawing as this fixes a few issues like that only a + // few parts of a window are updated permanently when scrolling or manipulating a control + // while other parts are only updated when the malipulation ended, or CNinePartTiledBitmap + // are drawn incorrectly when scaled. + if (@available (macOS 10.11, *)) { - nsView.layer.contentsFormat = kCAContentsFormatRGBA8Uint; - // asynchronous layer drawing or drawing only dirty rectangles are exclusive as - // the CoreGraphics engineers decided to be clever and join dirty rectangles without - // letting us know - if (getPlatformFactory ().asMacFactory ()->getUseAsynchronousLayerDrawing ()) + [nsView setWantsLayer:YES]; + caLayer = [CALayer new]; + caLayer.delegate = static_cast> (nsView); + caLayer.frame = nsView.layer.bounds; + [caLayer setContentsScale:nsView.layer.contentsScale]; + [nsView.layer addSublayer:caLayer]; + useInvalidRects = true; + if (@available (macOS 10.13, *)) { - caLayer.drawsAsynchronously = YES; - useInvalidRects = false; + nsView.layer.contentsFormat = kCAContentsFormatRGBA8Uint; + // asynchronous layer drawing or drawing only dirty rectangles are exclusive as + // the CoreGraphics engineers decided to be clever and join dirty rectangles without + // letting us know + if (getPlatformFactory ().asMacFactory ()->getUseAsynchronousLayerDrawing ()) + { + caLayer.drawsAsynchronously = YES; + useInvalidRects = false; + } } } } @@ -1077,53 +1080,82 @@ static void draggedImageEndedAtOperation (id self, SEL _cmd, NSImage* image, NSP } //----------------------------------------------------------------------------- -void NSViewFrame::draw (CGContextRef cgContext, CRect updateRect, double scaleFactor) +void NSViewFrame::drawLayer (CALayer* layer, CGContextRef ctx) { - auto device = getPlatformFactory ().getGraphicsDeviceFactory ().getDeviceForScreen ( - DefaultScreenIdentifier); - if (!device) - return; - auto cgDevice = std::static_pointer_cast (device); - auto deviceContext = std::make_shared (*cgDevice.get (), cgContext); + inDraw = true; - addDebugRedrawRect (updateRect, true); + auto clipBoundingBoxNSRect = CGContextGetClipBoundingBox (ctx); + auto clipBoundingBox = rectFromNSRect (clipBoundingBoxNSRect); - inDraw = true; - deviceContext->beginDraw (); + addDebugRedrawRect (clipBoundingBox, true); + + CGDrawContext drawContext (ctx, rectFromNSRect ([nsView bounds])); + drawContext.beginDraw (); if (useInvalidRects) { joinNearbyInvalidRects (invalidRectList, 24.); - frame->platformDrawRects (deviceContext, scaleFactor, invalidRectList.data ()); for (auto r : invalidRectList) + { + frame->platformDrawRect (&drawContext, r); addDebugRedrawRect (r, false); + } invalidRectList.clear (); } else { - frame->platformDrawRects (deviceContext, scaleFactor, {1, updateRect}); - addDebugRedrawRect (updateRect, false); + frame->platformDrawRect (&drawContext, clipBoundingBox); + addDebugRedrawRect (clipBoundingBox, false); } + drawContext.endDraw (); - deviceContext->endDraw (); inDraw = false; } -//----------------------------------------------------------------------------- -void NSViewFrame::drawLayer (CALayer* layer, CGContextRef ctx) -{ - auto clipBoundingBox = CGContextGetClipBoundingBox (ctx); - draw (ctx, rectFromNSRect (clipBoundingBox), layer.contentsScale); -} - //----------------------------------------------------------------------------- void NSViewFrame::drawRect (NSRect* rect) { if (caLayer) return; + inDraw = true; NSGraphicsContext* nsContext = [NSGraphicsContext currentContext]; - draw (nsContext.CGContext, rectFromNSRect (*rect), nsView.window.backingScaleFactor); + +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAX_OS_X_VERSION_10_10 + auto cgContext = static_cast ([nsContext graphicsPort]); +#else + auto cgContext = static_cast ([nsContext CGContext]); +#endif + + addDebugRedrawRect (rectFromNSRect (*rect), true); + + CGDrawContext drawContext (cgContext, rectFromNSRect ([nsView bounds])); + drawContext.beginDraw (); + + if (useInvalidRects) + { + joinNearbyInvalidRects (invalidRectList, 24.); + for (auto r : invalidRectList) + { + frame->platformDrawRect (&drawContext, r); + addDebugRedrawRect (r, false); + } + invalidRectList.clear (); + } + else + { + const NSRect* dirtyRects; + NSInteger numDirtyRects; + [nsView getRectsBeingDrawn:&dirtyRects count:&numDirtyRects]; + for (NSInteger i = 0; i < numDirtyRects; i++) + { + auto r = rectFromNSRect (dirtyRects[i]); + frame->platformDrawRect (&drawContext, r); + addDebugRedrawRect (r, false); + } + } + drawContext.endDraw (); + inDraw = false; } //------------------------------------------------------------------------ @@ -1282,7 +1314,7 @@ static MouseEventButtonState buttonStateFromNSEvent (NSEvent* theEvent) buttons |= kButton4; if (mouseButtons & (1 << 4)) buttons |= kButton5; - + return true; } @@ -1482,7 +1514,6 @@ static MouseEventButtonState buttonStateFromNSEvent (NSEvent* theEvent) [nsView setWantsLayer:YES]; caLayer.actions = nil; } - auto caParentLayer = parentViewLayer ? parentViewLayer->getCALayer () : (caLayer ? caLayer : nsView.layer); auto layer = makeOwned (caParentLayer); @@ -1605,7 +1636,7 @@ static MouseEventButtonState buttonStateFromNSEvent (NSEvent* theEvent) return; if (![nsView respondsToSelector:@selector(setTouchBar:)]) return; - + if (!touchBarCreator) [nsView performSelector:@selector(setTouchBar:) withObject:nil]; else diff --git a/vstgui/lib/platform/mac/coregraphicsdevicecontext.h b/vstgui/lib/platform/mac/coregraphicsdevicecontext.h deleted file mode 100644 index b9241f65a..000000000 --- a/vstgui/lib/platform/mac/coregraphicsdevicecontext.h +++ /dev/null @@ -1,115 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#pragma once - -#include "../iplatformgraphicsdevice.h" - -#if TARGET_OS_IPHONE -#include -#include -#else -#include -#endif - -//------------------------------------------------------------------------ -namespace VSTGUI { - -class CoreGraphicsDevice; - -//------------------------------------------------------------------------ -class CoreGraphicsDeviceContext : public IPlatformGraphicsDeviceContext, - public IPlatformGraphicsDeviceContextBitmapExt -{ -public: - CoreGraphicsDeviceContext (const CoreGraphicsDevice& device, void* cgContext); - ~CoreGraphicsDeviceContext () noexcept override; - - const IPlatformGraphicsDevice& getDevice () const override; - PlatformGraphicsPathFactoryPtr getGraphicsPathFactory () const override; - - bool beginDraw () const override; - bool endDraw () const override; - // draw commands - bool drawLine (LinePair line) const override; - bool drawLines (const LineList& lines) const override; - bool drawPolygon (const PointList& polygonPointList, - PlatformGraphicsDrawStyle drawStyle) const override; - bool drawRect (CRect rect, PlatformGraphicsDrawStyle drawStyle) const override; - bool drawArc (CRect rect, double startAngle1, double endAngle2, - PlatformGraphicsDrawStyle drawStyle) const override; - bool drawEllipse (CRect rect, PlatformGraphicsDrawStyle drawStyle) const override; - bool drawPoint (CPoint point, CColor color) const override; - bool drawBitmap (IPlatformBitmap& bitmap, CRect dest, CPoint offset, double alpha, - BitmapInterpolationQuality quality) const override; - bool clearRect (CRect rect) const override; - bool drawGraphicsPath (IPlatformGraphicsPath& path, PlatformGraphicsPathDrawMode mode, - TransformMatrix* transformation) const override; - bool fillLinearGradient (IPlatformGraphicsPath& path, const IPlatformGradient& gradient, - CPoint startPoint, CPoint endPoint, bool evenOdd, - TransformMatrix* transformation) const override; - bool fillRadialGradient (IPlatformGraphicsPath& path, const IPlatformGradient& gradient, - CPoint center, CCoord radius, CPoint originOffset, bool evenOdd, - TransformMatrix* transformation) const override; - // state - void saveGlobalState () const override; - void restoreGlobalState () const override; - void setLineStyle (const CLineStyle& style) const override; - void setLineWidth (CCoord width) const override; - void setDrawMode (CDrawMode mode) const override; - void setClipRect (CRect clip) const override; - void setFillColor (CColor color) const override; - void setFrameColor (CColor color) const override; - void setGlobalAlpha (double newAlpha) const override; - void setTransformMatrix (const TransformMatrix& tm) const override; - - // extension - const IPlatformGraphicsDeviceContextBitmapExt* asBitmapExt () const override; - - // IPlatformGraphicsDeviceContextBitmapExt - bool drawBitmapNinePartTiled (IPlatformBitmap& bitmap, CRect dest, - const CNinePartTiledDescription& desc, double alpha, - BitmapInterpolationQuality quality) const override; - bool fillRectWithBitmap (IPlatformBitmap& bitmap, CRect srcRect, CRect dstRect, double alpha, - BitmapInterpolationQuality quality) const override; - - // private - void drawCTLine (CTLineRef line, CGPoint cgPoint, CTFontRef fontRef, CColor color, - bool underline, bool strikeThrough, bool antialias) const; - -private: - struct Impl; - std::unique_ptr impl; -}; - -//------------------------------------------------------------------------ -class CoreGraphicsBitmapContext : public CoreGraphicsDeviceContext -{ -public: - using EndDrawFunc = std::function; - CoreGraphicsBitmapContext (const CoreGraphicsDevice& device, void* cgContext, EndDrawFunc&& f); - - bool endDraw () const override; - -private: - EndDrawFunc endDrawFunc; -}; - -//------------------------------------------------------------------------ -class CoreGraphicsDevice : public IPlatformGraphicsDevice -{ -public: - PlatformGraphicsDeviceContextPtr - createBitmapContext (const PlatformBitmapPtr& bitmap) const override; -}; - -//------------------------------------------------------------------------ -class CoreGraphicsDeviceFactory : public IPlatformGraphicsDeviceFactory -{ -public: - PlatformGraphicsDevicePtr getDeviceForScreen (ScreenInfo::Identifier screen) const override; -}; - -//------------------------------------------------------------------------ -} // VSTGUI diff --git a/vstgui/lib/platform/mac/coregraphicsdevicecontext.mm b/vstgui/lib/platform/mac/coregraphicsdevicecontext.mm deleted file mode 100644 index d524b595d..000000000 --- a/vstgui/lib/platform/mac/coregraphicsdevicecontext.mm +++ /dev/null @@ -1,955 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#include "../../ccolor.h" -#include "../../cdrawdefs.h" -#include "../../cgraphicstransform.h" -#include "../../clinestyle.h" -#include "../../crect.h" -#include "cgbitmap.h" -#include "macglobals.h" -#include "coregraphicsdevicecontext.h" -#include "quartzgraphicspath.h" - -#include - -#if TARGET_OS_IPHONE -#include -#endif - -//------------------------------------------------------------------------ -namespace VSTGUI { - -//------------------------------------------------------------------------ -PlatformGraphicsDevicePtr - CoreGraphicsDeviceFactory::getDeviceForScreen (ScreenInfo::Identifier screen) const -{ - static PlatformGraphicsDevicePtr gCoreGraphicsDevice = std::make_shared (); - return gCoreGraphicsDevice; -} - -//------------------------------------------------------------------------ -static CGPathDrawingMode convert (PlatformGraphicsPathDrawMode mode) -{ - switch (mode) - { - case PlatformGraphicsPathDrawMode::FilledEvenOdd: - return kCGPathEOFill; - case PlatformGraphicsPathDrawMode::Filled: - return kCGPathFill; - case PlatformGraphicsPathDrawMode::Stroked: - return kCGPathStroke; - } - vstgui_assert (false); -} - -//------------------------------------------------------------------------ -static CGPathDrawingMode convert (PlatformGraphicsDrawStyle drawStyle) -{ - switch (drawStyle) - { - case PlatformGraphicsDrawStyle::Filled: - return kCGPathFill; - case PlatformGraphicsDrawStyle::FilledAndStroked: - return kCGPathFillStroke; - case PlatformGraphicsDrawStyle::Stroked: - return kCGPathStroke; - } - vstgui_assert (false); -} - -//------------------------------------------------------------------------ -auto CoreGraphicsDevice::createBitmapContext (const PlatformBitmapPtr& bitmap) const - -> PlatformGraphicsDeviceContextPtr -{ - auto cgBitmap = bitmap.cast (); - if (cgBitmap) - { - auto scaleFactor = bitmap->getScaleFactor (); - auto cgContext = cgBitmap->createCGContext (); - CGContextConcatCTM (cgContext, - CGAffineTransformMakeScale (static_cast (scaleFactor), - static_cast (scaleFactor))); - auto bitmapContext = std::make_shared ( - *this, cgContext, [cgBitmap = shared (cgBitmap)] () { cgBitmap->setDirty (); }); - CFRelease (cgContext); - return bitmapContext; - } - return nullptr; -} - -//------------------------------------------------------------------------ -//------------------------------------------------------------------------ -//------------------------------------------------------------------------ -struct CoreGraphicsDeviceContext::Impl -{ - //----------------------------------------------------------------------------- - Impl (const CoreGraphicsDevice& d, CGContextRef cgContext) : device (d), cgContext (cgContext) - { - CFRetain (cgContext); - - auto userRect = CGRectMake (0, 0, 100, 100); - auto deviceRect = CGContextConvertRectToDeviceSpace (cgContext, userRect); - backendScaleFactor = deviceRect.size.height / userRect.size.height; - - CGContextSaveGState (cgContext); - CGContextSetAllowsAntialiasing (cgContext, true); - CGContextSetAllowsFontSmoothing (cgContext, true); - CGContextSetAllowsFontSubpixelPositioning (cgContext, true); - CGContextSetAllowsFontSubpixelQuantization (cgContext, true); - CGContextSetShouldAntialias (cgContext, false); - CGContextSetFillColorSpace (cgContext, GetCGColorSpace ()); - CGContextSetStrokeColorSpace (cgContext, GetCGColorSpace ()); - CGContextSaveGState (cgContext); - CGAffineTransform cgCTM = CGAffineTransformMake (1.0, 0.0, 0.0, -1.0, 0.0, 0.0); - CGContextSetTextMatrix (cgContext, cgCTM); - } - - //----------------------------------------------------------------------------- - ~Impl () - { - CGContextRestoreGState (cgContext); // restore the original state - CGContextRestoreGState (cgContext); // we need to do it twice !!! - CFRelease (cgContext); - } - - //----------------------------------------------------------------------------- - template - static void DoGraphicStateSave (CGContextRef context, Proc proc) - { - CGContextSaveGState (context); - proc (); - CGContextRestoreGState (context); - } - - //------------------------------------------------------------------------ - template - void doInCGContext (bool swapYAxis, bool integralOffset, Proc proc) const - { - if (state.clipRect.isEmpty ()) - return; - - CGContextSaveGState (cgContext); - - CGRect cgClipRect = CGRectFromCRect (state.clipRect); - if (integralOffset) - cgClipRect = pixelAlligned (cgClipRect); - CGContextClipToRect (cgContext, cgClipRect); -#if DEBUG - if (showClip) - { - CGContextSetRGBFillColor (cgContext, 1, 0, 0, 0.5); - CGContextFillRect (cgContext, cgClipRect); - } -#endif - - if (state.tm.isInvariant () == false) - { - auto t = state.tm; - if (integralOffset) - { - CGPoint p = pixelAlligned (CGPointFromCPoint (CPoint (t.dx, t.dy))); - t.dx = p.x; - t.dy = p.y; - } - CGContextConcatCTM (cgContext, createCGAffineTransform (t)); - } - - if (!swapYAxis) - CGContextScaleCTM (cgContext, 1, -1); - - CGContextSetAlpha (cgContext, state.globalAlpha); - - proc (cgContext); - - CGContextRestoreGState (cgContext); - } - - //----------------------------------------------------------------------------- - CGRect pixelAlligned (const CGRect& r) const - { - CGRect result; - result.origin = CGContextConvertPointToDeviceSpace (cgContext, r.origin); - result.size = CGContextConvertSizeToDeviceSpace (cgContext, r.size); - result.origin.x = static_cast (std::round (result.origin.x)); - result.origin.y = static_cast (std::round (result.origin.y)); - result.size.width = static_cast (std::round (result.size.width)); - result.size.height = static_cast (std::round (result.size.height)); - result.origin = CGContextConvertPointToUserSpace (cgContext, result.origin); - result.size = CGContextConvertSizeToUserSpace (cgContext, result.size); - return result; - } - - //----------------------------------------------------------------------------- - CGPoint pixelAlligned (const CGPoint& p) const - { - CGPoint result = CGContextConvertPointToDeviceSpace (cgContext, p); - result.x = static_cast (std::round (result.x)); - result.y = static_cast (std::round (result.y)); - result = CGContextConvertPointToUserSpace (cgContext, result); - return result; - } - - //------------------------------------------------------------------------ - void applyLineStyle () const - { - switch (state.lineStyle.getLineCap ()) - { - case CLineStyle::kLineCapButt: - CGContextSetLineCap (cgContext, kCGLineCapButt); - break; - case CLineStyle::kLineCapRound: - CGContextSetLineCap (cgContext, kCGLineCapRound); - break; - case CLineStyle::kLineCapSquare: - CGContextSetLineCap (cgContext, kCGLineCapSquare); - break; - } - switch (state.lineStyle.getLineJoin ()) - { - case CLineStyle::kLineJoinMiter: - CGContextSetLineJoin (cgContext, kCGLineJoinMiter); - break; - case CLineStyle::kLineJoinRound: - CGContextSetLineJoin (cgContext, kCGLineJoinRound); - break; - case CLineStyle::kLineJoinBevel: - CGContextSetLineJoin (cgContext, kCGLineJoinBevel); - break; - } - if (state.lineStyle.getDashCount () > 0) - { - CGFloat* dashLengths = new CGFloat[state.lineStyle.getDashCount ()]; - for (uint32_t i = 0; i < state.lineStyle.getDashCount (); i++) - { - dashLengths[i] = - static_cast (state.lineWidth * state.lineStyle.getDashLengths ()[i]); - } - CGContextSetLineDash (cgContext, static_cast (state.lineStyle.getDashPhase ()), - dashLengths, state.lineStyle.getDashCount ()); - delete[] dashLengths; - } - } - - //----------------------------------------------------------------------------- - void applyLineWidthCTM () const - { - int32_t lineWidthInt = static_cast (state.lineWidth); - if (static_cast (lineWidthInt) == state.lineWidth && lineWidthInt % 2) - CGContextTranslateCTM (cgContext, 0.5, 0.5); - } - - //------------------------------------------------------------------------ - void setBitmapInterpolationQuality (BitmapInterpolationQuality q) const - { - switch (q) - { - case BitmapInterpolationQuality::kLow: - { - CGContextSetShouldAntialias (cgContext, false); - CGContextSetInterpolationQuality (cgContext, kCGInterpolationNone); - break; - } - case BitmapInterpolationQuality::kMedium: - { - CGContextSetShouldAntialias (cgContext, true); - CGContextSetInterpolationQuality (cgContext, kCGInterpolationMedium); - break; - } - case BitmapInterpolationQuality::kHigh: - { - CGContextSetShouldAntialias (cgContext, true); - CGContextSetInterpolationQuality (cgContext, kCGInterpolationHigh); - break; - } - case BitmapInterpolationQuality::kDefault: - { - CGContextSetShouldAntialias (cgContext, true); - CGContextSetInterpolationQuality (cgContext, kCGInterpolationDefault); - break; - } - default: - vstgui_assert (false); - break; - } - } - - //----------------------------------------------------------------------------- - void drawCGImageRef (CGContextRef context, CGImageRef image, CGLayerRef layer, - double bitmapScaleFactor, const CRect& inRect, const CPoint& inOffset, - float alpha) const - { - CRect rect (inRect); - CPoint offset (inOffset); - CGSize bitmapNormSize; - if (layer) - { - bitmapNormSize = CGLayerGetSize (layer); - } - else - { - bitmapNormSize.width = CGImageGetWidth (image); - bitmapNormSize.height = CGImageGetHeight (image); - } - bitmapNormSize.width /= bitmapScaleFactor; - bitmapNormSize.height /= bitmapScaleFactor; - - CGContextSetAlpha (context, (CGFloat)alpha * state.globalAlpha); - - CGRect dest; - dest.origin.x = static_cast (rect.left - offset.x); - dest.origin.y = static_cast (-(rect.top) - (bitmapNormSize.height - offset.y)); - dest.size = bitmapNormSize; - - CGRect cgClipRect; - cgClipRect.origin.x = static_cast (rect.left); - cgClipRect.origin.y = static_cast (-(rect.top) - rect.getHeight ()); - cgClipRect.size.width = static_cast (rect.getWidth ()); - cgClipRect.size.height = static_cast (rect.getHeight ()); - - if (bitmapScaleFactor != 1.) - { - CGContextConcatCTM (context, CGAffineTransformMakeScale ( - static_cast (1. / bitmapScaleFactor), - static_cast (1. / bitmapScaleFactor))); - CGAffineTransform transform = CGAffineTransformMakeScale ( - static_cast (bitmapScaleFactor), static_cast (bitmapScaleFactor)); - cgClipRect.origin = CGPointApplyAffineTransform (cgClipRect.origin, transform); - cgClipRect.size = CGSizeApplyAffineTransform (cgClipRect.size, transform); - dest.origin = CGPointApplyAffineTransform (dest.origin, transform); - dest.size = CGSizeApplyAffineTransform (dest.size, transform); - } - cgClipRect.origin = pixelAlligned (cgClipRect.origin); - - CGContextClipToRect (context, cgClipRect); - - if (layer) - { - CGContextDrawLayerInRect (context, dest, layer); - } - else - { - CGContextDrawImage (context, dest, image); - } - } - - //------------------------------------------------------------------------ - const CoreGraphicsDevice& device; - CGContextRef cgContext {nullptr}; - - struct State - { - CLineStyle lineStyle {kLineSolid}; - CCoord lineWidth {1}; - CDrawMode drawMode {}; - CRect clipRect {}; - double globalAlpha {1.}; - TransformMatrix tm {}; - }; - - State state; - std::stack stateStack; - - double backendScaleFactor {1.}; - - using BitmapDrawCountMap = std::map; - BitmapDrawCountMap bitmapDrawCount; - -#if DEBUG - bool showClip {false}; -#endif -}; - -//------------------------------------------------------------------------ -CoreGraphicsDeviceContext::CoreGraphicsDeviceContext (const CoreGraphicsDevice& device, - void* cgContext) -{ - impl = std::make_unique (device, static_cast (cgContext)); -} - -//------------------------------------------------------------------------ -CoreGraphicsDeviceContext::~CoreGraphicsDeviceContext () noexcept {} - -//------------------------------------------------------------------------ -const IPlatformGraphicsDevice& CoreGraphicsDeviceContext::getDevice () const -{ - return impl->device; -} - -//------------------------------------------------------------------------ -PlatformGraphicsPathFactoryPtr CoreGraphicsDeviceContext::getGraphicsPathFactory () const -{ - return CGGraphicsPathFactory::instance (); -} - -//------------------------------------------------------------------------ -bool CoreGraphicsDeviceContext::beginDraw () const { return true; } - -//------------------------------------------------------------------------ -bool CoreGraphicsDeviceContext::endDraw () const -{ - impl->bitmapDrawCount.clear (); - return true; -} - -//------------------------------------------------------------------------ -bool CoreGraphicsDeviceContext::drawLine (LinePair line) const -{ - impl->doInCGContext (true, impl->state.drawMode.integralMode (), [&] (auto context) { - impl->applyLineStyle (); - - CGContextBeginPath (context); - CGPoint first = CGPointFromCPoint (line.first); - CGPoint second = CGPointFromCPoint (line.second); - - if (impl->state.drawMode.integralMode ()) - { - first = impl->pixelAlligned (first); - second = impl->pixelAlligned (second); - impl->applyLineWidthCTM (); - } - - CGContextMoveToPoint (context, first.x, first.y); - CGContextAddLineToPoint (context, second.x, second.y); - - CGContextDrawPath (context, kCGPathStroke); - }); - return true; -} - -//------------------------------------------------------------------------ -bool CoreGraphicsDeviceContext::drawLines (const LineList& lines) const -{ - vstgui_assert (lines.empty () == false); - impl->doInCGContext (true, impl->state.drawMode.integralMode (), [&] (auto context) { - impl->applyLineStyle (); - static constexpr auto numStackPoints = 32; - CGPoint stackPoints[numStackPoints]; - - auto cgPointsPtr = (lines.size () * 2) < numStackPoints - ? nullptr - : std::unique_ptr (new CGPoint[lines.size () * 2]); - auto cgPoints = cgPointsPtr ? cgPointsPtr.get () : stackPoints; - uint32_t index = 0; - if (impl->state.drawMode.integralMode ()) - { - for (const auto& line : lines) - { - cgPoints[index] = impl->pixelAlligned (CGPointFromCPoint (line.first)); - cgPoints[index + 1] = impl->pixelAlligned (CGPointFromCPoint (line.second)); - index += 2; - } - } - else - { - for (const auto& line : lines) - { - cgPoints[index] = CGPointFromCPoint (line.first); - cgPoints[index + 1] = CGPointFromCPoint (line.second); - index += 2; - } - } - - if (impl->state.drawMode.integralMode ()) - impl->applyLineWidthCTM (); - - const size_t maxPointsPerIteration = 16; - const CGPoint* pointPtr = cgPoints; - size_t numPoints = lines.size () * 2; - while (numPoints) - { - size_t np = std::min (numPoints, std::min (maxPointsPerIteration, numPoints)); - CGContextStrokeLineSegments (context, pointPtr, np); - numPoints -= np; - pointPtr += np; - } - }); - return true; -} - -//------------------------------------------------------------------------ -bool CoreGraphicsDeviceContext::drawPolygon (const PointList& polygonPointList, - PlatformGraphicsDrawStyle drawStyle) const -{ - vstgui_assert (polygonPointList.empty () == false); - impl->doInCGContext (true, impl->state.drawMode.integralMode (), [&] (auto context) { - CGPathDrawingMode m = convert (drawStyle); - impl->applyLineStyle (); - - CGContextBeginPath (context); - CGPoint p = CGPointFromCPoint (polygonPointList[0]); - if (impl->state.drawMode.integralMode ()) - p = impl->pixelAlligned (p); - CGContextMoveToPoint (context, p.x, p.y); - for (uint32_t i = 1; i < polygonPointList.size (); i++) - { - p = CGPointFromCPoint (polygonPointList[i]); - if (impl->state.drawMode.integralMode ()) - p = impl->pixelAlligned (p); - CGContextAddLineToPoint (context, p.x, p.y); - } - CGContextDrawPath (context, m); - }); - return true; -} - -//------------------------------------------------------------------------ -bool CoreGraphicsDeviceContext::drawRect (CRect rect, PlatformGraphicsDrawStyle drawStyle) const -{ - impl->doInCGContext (true, impl->state.drawMode.integralMode (), [&] (auto context) { - CGRect r = CGRectFromCRect (rect); - if (drawStyle != PlatformGraphicsDrawStyle::Filled) - { - r.size.width -= 1.; - r.size.height -= 1.; - } - - CGPathDrawingMode m = convert (drawStyle); - impl->applyLineStyle (); - - if (impl->state.drawMode.integralMode ()) - { - r = impl->pixelAlligned (r); - if (drawStyle != PlatformGraphicsDrawStyle::Filled) - impl->applyLineWidthCTM (); - } - - CGContextBeginPath (context); - CGContextAddRect (context, r); - CGContextDrawPath (context, m); - }); - return true; -} - -//------------------------------------------------------------------------ -bool CoreGraphicsDeviceContext::drawArc (CRect rect, double startAngle1, double endAngle2, - PlatformGraphicsDrawStyle drawStyle) const -{ - impl->doInCGContext (true, impl->state.drawMode.integralMode (), [&] (auto context) { - CGPathDrawingMode m = convert (drawStyle); - impl->applyLineStyle (); - - CGContextBeginPath (context); - - CPoint center (rect.left + rect.getWidth () / 2., rect.top + rect.getHeight () / 2.); - auto rX = rect.getWidth () / 2.; - auto rY = rect.getHeight () / 2.; - - Impl::DoGraphicStateSave (context, [&] () { - CGContextTranslateCTM (context, static_cast (center.x), - static_cast (center.y)); - CGContextScaleCTM (context, rX, rY); - - auto startAngle = radians (startAngle1); - auto endAngle = radians (endAngle2); - if (rX != rY) - { - startAngle = std::atan2 (std::sin (startAngle) * rX, std::cos (startAngle) * rY); - endAngle = std::atan2 (std::sin (endAngle) * rX, std::cos (endAngle) * rY); - } - CGContextMoveToPoint (context, static_cast (std::cos (startAngle)), - static_cast (std::sin (startAngle))); - CGContextAddArc (context, 0, 0, 1, static_cast (startAngle), - static_cast (endAngle), 0); - }); - CGContextDrawPath (context, m); - }); - return true; -} - -//------------------------------------------------------------------------ -bool CoreGraphicsDeviceContext::drawEllipse (CRect rect, PlatformGraphicsDrawStyle drawStyle) const -{ - impl->doInCGContext (true, impl->state.drawMode.integralMode (), [&] (auto context) { - CGRect r = CGRectFromCRect (rect); - if (drawStyle != PlatformGraphicsDrawStyle::Filled) - { - r.size.width -= 1.; - r.size.height -= 1.; - } - - CGPathDrawingMode m = convert (drawStyle); - impl->applyLineStyle (); - if (impl->state.drawMode.integralMode ()) - { - if (drawStyle != PlatformGraphicsDrawStyle::Filled) - impl->applyLineWidthCTM (); - r = impl->pixelAlligned (r); - } - - CGContextAddEllipseInRect (context, r); - CGContextDrawPath (context, m); - }); - return true; -} - -//------------------------------------------------------------------------ -bool CoreGraphicsDeviceContext::drawPoint (CPoint point, CColor color) const { return false; } - -//------------------------------------------------------------------------ -bool CoreGraphicsDeviceContext::drawBitmap (IPlatformBitmap& bitmap, CRect dest, CPoint offset, - double alpha, BitmapInterpolationQuality quality) const -{ - auto cgBitmap = dynamic_cast (&bitmap); - if (!cgBitmap) - return false; - auto cgImage = cgBitmap->getCGImage (); - if (!cgImage) - return false; - impl->doInCGContext (false, true, [&] (auto context) { - impl->setBitmapInterpolationQuality (quality); - - CGLayerRef layer = nullptr; - if (impl->backendScaleFactor == 1.) - { - layer = cgBitmap->getCGLayer (); - if (layer == nullptr) - { - auto it = impl->bitmapDrawCount.find (cgBitmap); - if (it == impl->bitmapDrawCount.end ()) - { - impl->bitmapDrawCount.emplace (cgBitmap, 1); - } - else - { - it->second++; - layer = cgBitmap->createCGLayer (context); - } - } - } - - impl->drawCGImageRef (context, cgImage, layer, cgBitmap->getScaleFactor (), dest, offset, - alpha); - }); - return true; -} - -//------------------------------------------------------------------------ -bool CoreGraphicsDeviceContext::clearRect (CRect rect) const -{ - impl->doInCGContext (true, impl->state.drawMode.integralMode (), [&] (auto context) { - CGRect cgRect = CGRectFromCRect (rect); - if (impl->state.drawMode.integralMode ()) - cgRect = impl->pixelAlligned (cgRect); - CGContextClearRect (context, cgRect); - }); - return true; -} - -//------------------------------------------------------------------------ -bool CoreGraphicsDeviceContext::drawGraphicsPath (IPlatformGraphicsPath& path, - PlatformGraphicsPathDrawMode mode, - TransformMatrix* transformation) const -{ - auto cgPath = dynamic_cast (&path); - if (!cgPath) - return false; - - impl->doInCGContext (true, impl->state.drawMode.integralMode (), [&] (auto context) { - auto cgMode = convert (mode); - if (cgMode == kCGPathStroke) - impl->applyLineStyle (); - Impl::DoGraphicStateSave (context, [&] () { - if (transformation) - { - CGAffineTransform transform = createCGAffineTransform (*transformation); - CGContextConcatCTM (context, transform); - } - if (impl->state.drawMode.integralMode () && impl->state.drawMode.aliasing ()) - { - Impl::DoGraphicStateSave (context, [&] () { - impl->applyLineWidthCTM (); - cgPath->pixelAlign ( - [] (const CGPoint& p, void* context) { - auto devContext = reinterpret_cast (context); - return devContext->pixelAlligned (p); - }, - impl.get ()); - }); - CGContextAddPath (context, cgPath->getCGPathRef ()); - } - else - CGContextAddPath (context, cgPath->getCGPathRef ()); - }); - CGContextDrawPath (context, cgMode); - }); - return true; -} - -//------------------------------------------------------------------------ -bool CoreGraphicsDeviceContext::fillLinearGradient (IPlatformGraphicsPath& path, - const IPlatformGradient& gradient, - CPoint startPoint, CPoint endPoint, - bool evenOdd, - TransformMatrix* transformation) const -{ - auto cgPath = dynamic_cast (&path); - if (!cgPath) - return false; - auto cgGradient = dynamic_cast (&gradient); - if (!cgGradient) - return false; - - impl->doInCGContext (true, impl->state.drawMode.integralMode (), [&] (auto context) { - CGPoint start = CGPointFromCPoint (startPoint); - CGPoint end = CGPointFromCPoint (endPoint); - Impl::DoGraphicStateSave (context, [&] () { - if (impl->state.drawMode.integralMode ()) - { - start = impl->pixelAlligned (start); - end = impl->pixelAlligned (end); - } - if (transformation) - { - CGAffineTransform transform = createCGAffineTransform (*transformation); - CGContextConcatCTM (context, transform); - } - if (impl->state.drawMode.integralMode () && impl->state.drawMode.aliasing ()) - { - cgPath->pixelAlign ( - [] (const CGPoint& p, void* context) { - auto devContext = reinterpret_cast (context); - return devContext->pixelAlligned (p); - }, - impl.get ()); - } - - CGContextAddPath (context, cgPath->getCGPathRef ()); - }); - - if (evenOdd) - CGContextEOClip (context); - else - CGContextClip (context); - - CGContextDrawLinearGradient (context, *cgGradient, start, end, - kCGGradientDrawsBeforeStartLocation | - kCGGradientDrawsAfterEndLocation); - }); - return true; -} - -//------------------------------------------------------------------------ -bool CoreGraphicsDeviceContext::fillRadialGradient (IPlatformGraphicsPath& path, - const IPlatformGradient& gradient, - CPoint center, CCoord radius, - CPoint originOffset, bool evenOdd, - TransformMatrix* transformation) const -{ - auto cgPath = dynamic_cast (&path); - if (!cgPath) - return false; - auto cgGradient = dynamic_cast (&gradient); - if (!cgGradient) - return false; - - impl->doInCGContext (true, impl->state.drawMode.integralMode (), [&] (auto context) { - Impl::DoGraphicStateSave (context, [&] () { - if (transformation) - { - CGAffineTransform transform = createCGAffineTransform (*transformation); - CGContextConcatCTM (context, transform); - } - if (impl->state.drawMode.integralMode () && impl->state.drawMode.aliasing ()) - { - cgPath->pixelAlign ( - [] (const CGPoint& p, void* context) { - auto devContext = reinterpret_cast (context); - return devContext->pixelAlligned (p); - }, - impl.get ()); - } - - CGContextAddPath (context, cgPath->getCGPathRef ()); - }); - - if (evenOdd) - CGContextEOClip (context); - else - CGContextClip (context); - - CPoint startCenter = center + originOffset; - CGContextDrawRadialGradient (context, *cgGradient, CGPointFromCPoint (startCenter), 0, - CGPointFromCPoint (center), static_cast (radius), - kCGGradientDrawsBeforeStartLocation | - kCGGradientDrawsAfterEndLocation); - }); - return true; -} - -//------------------------------------------------------------------------ -void CoreGraphicsDeviceContext::saveGlobalState () const -{ - CGContextSaveGState (impl->cgContext); - impl->stateStack.push (impl->state); -} - -//------------------------------------------------------------------------ -void CoreGraphicsDeviceContext::restoreGlobalState () const -{ - vstgui_assert (impl->stateStack.empty () == false, - "Unbalanced calls to saveGlobalState and restoreGlobalState"); -#if NDEBUG - if (impl->stateStack.empty ()) - return; -#endif - CGContextRestoreGState (impl->cgContext); - impl->state = impl->stateStack.top (); - impl->stateStack.pop (); -} - -//------------------------------------------------------------------------ -void CoreGraphicsDeviceContext::setLineStyle (const CLineStyle& style) const -{ - impl->state.lineStyle = style; -} - -//------------------------------------------------------------------------ -void CoreGraphicsDeviceContext::setLineWidth (CCoord width) const -{ - impl->state.lineWidth = width; - CGContextSetLineWidth (impl->cgContext, static_cast (width)); -} - -//------------------------------------------------------------------------ -void CoreGraphicsDeviceContext::setDrawMode (CDrawMode mode) const -{ - impl->state.drawMode = mode; - CGContextSetShouldAntialias (impl->cgContext, mode.antiAliasing ()); -} - -//------------------------------------------------------------------------ -void CoreGraphicsDeviceContext::setClipRect (CRect clip) const { impl->state.clipRect = clip; } - -//------------------------------------------------------------------------ -void CoreGraphicsDeviceContext::setFillColor (CColor color) const -{ - CGContextSetFillColorWithColor (impl->cgContext, getCGColor (color)); -} - -//------------------------------------------------------------------------ -void CoreGraphicsDeviceContext::setFrameColor (CColor color) const -{ - CGContextSetStrokeColorWithColor (impl->cgContext, getCGColor (color)); -} - -//------------------------------------------------------------------------ -void CoreGraphicsDeviceContext::setGlobalAlpha (double newAlpha) const -{ - impl->state.globalAlpha = newAlpha; -} - -//------------------------------------------------------------------------ -void CoreGraphicsDeviceContext::setTransformMatrix (const TransformMatrix& tm) const -{ - impl->state.tm = tm; -} - -//------------------------------------------------------------------------ -const IPlatformGraphicsDeviceContextBitmapExt* CoreGraphicsDeviceContext::asBitmapExt () const -{ - return this; -} - -//------------------------------------------------------------------------ -bool CoreGraphicsDeviceContext::drawBitmapNinePartTiled (IPlatformBitmap& bitmap, CRect dest, - const CNinePartTiledDescription& desc, - double alpha, - BitmapInterpolationQuality quality) const -{ - // Not Supported - return false; -} - -//------------------------------------------------------------------------ -bool CoreGraphicsDeviceContext::fillRectWithBitmap (IPlatformBitmap& bitmap, CRect srcRect, - CRect dstRect, double alpha, - BitmapInterpolationQuality quality) const -{ - auto cgBitmap = dynamic_cast (&bitmap); - if (!cgBitmap) - return false; - auto cgImage = cgBitmap->getCGImage (); - if (cgImage) - return false; - impl->doInCGContext (false, true, [&] (auto context) { - impl->setBitmapInterpolationQuality (quality); - - // TODO: Check if this works with retina images - - CGRect clipRect = CGRectFromCRect (dstRect); - clipRect.origin.y = -(clipRect.origin.y) - clipRect.size.height; - clipRect = impl->pixelAlligned (clipRect); - CGContextClipToRect (context, clipRect); - - CGRect r = {}; - r.size.width = CGImageGetWidth (cgImage); - r.size.height = CGImageGetHeight (cgImage); - - CGContextDrawTiledImage (context, r, cgImage); - }); - return false; -} - -//------------------------------------------------------------------------ -void CoreGraphicsDeviceContext::drawCTLine (CTLineRef line, CGPoint cgPoint, CTFontRef fontRef, - CColor color, bool underline, bool strikeThrough, - bool antialias) const -{ - impl->doInCGContext (true, impl->state.drawMode.integralMode (), [&] (auto context) { - if (impl->state.drawMode.integralMode ()) - cgPoint = impl->pixelAlligned (cgPoint); - CGContextSetShouldAntialias (context, antialias); - CGContextSetShouldSmoothFonts (context, true); - CGContextSetShouldSubpixelPositionFonts (context, true); - CGContextSetShouldSubpixelQuantizeFonts (context, true); - CGContextSetTextPosition (context, cgPoint.x, cgPoint.y); - CTLineDraw (line, context); - CGColorRef cgColorRef = nullptr; - if (underline) - { - cgColorRef = getCGColor (color); - CGFloat underlineOffset = CTFontGetUnderlinePosition (fontRef) - 1.f; - CGFloat underlineThickness = CTFontGetUnderlineThickness (fontRef); - CGContextSetStrokeColorWithColor (context, cgColorRef); - CGContextSetLineWidth (context, underlineThickness); - auto cgPoint2 = CGContextGetTextPosition (context); - CGContextBeginPath (context); - CGContextMoveToPoint (context, cgPoint.x, cgPoint.y - underlineOffset); - CGContextAddLineToPoint (context, cgPoint2.x, cgPoint.y - underlineOffset); - CGContextDrawPath (context, kCGPathStroke); - } - if (strikeThrough) - { - if (!cgColorRef) - cgColorRef = getCGColor (color); - CGFloat underlineThickness = CTFontGetUnderlineThickness (fontRef); - CGFloat offset = CTFontGetXHeight (fontRef) * 0.5f; - CGContextSetStrokeColorWithColor (context, cgColorRef); - CGContextSetLineWidth (context, underlineThickness); - auto cgPoint2 = CGContextGetTextPosition (context); - CGContextBeginPath (context); - CGContextMoveToPoint (context, cgPoint.x, cgPoint.y - offset); - CGContextAddLineToPoint (context, cgPoint2.x, cgPoint.y - offset); - CGContextDrawPath (context, kCGPathStroke); - } - }); -} - -//------------------------------------------------------------------------ -CoreGraphicsBitmapContext::CoreGraphicsBitmapContext (const CoreGraphicsDevice& device, - void* cgContext, EndDrawFunc&& f) -: CoreGraphicsDeviceContext (device, cgContext), endDrawFunc (std::move (f)) -{ -} - -//------------------------------------------------------------------------ -bool CoreGraphicsBitmapContext::endDraw () const -{ - endDrawFunc (); - return CoreGraphicsDeviceContext::endDraw (); -} - -//------------------------------------------------------------------------ -} // VSTGUI diff --git a/vstgui/lib/platform/mac/ios/uiviewframe.mm b/vstgui/lib/platform/mac/ios/uiviewframe.mm index e7525a472..4b225f32b 100644 --- a/vstgui/lib/platform/mac/ios/uiviewframe.mm +++ b/vstgui/lib/platform/mac/ios/uiviewframe.mm @@ -8,7 +8,7 @@ #import "../../../cfileselector.h" #import "../../../idatapackage.h" #import "../../iplatformoptionmenu.h" -#import "../coregraphicsdevicecontext.h" +#import "../cgdrawcontext.h" #import "../cgbitmap.h" #import "../quartzgraphicspath.h" #import "../caviewlayer.h" @@ -55,19 +55,7 @@ - (id)initWithUIViewFrame:(UIViewFrame*)viewFrame parent:(UIView*)parent size:(c //----------------------------------------------------------------------------- - (void)drawRect:(CGRect)rect { - auto device = getPlatformFactory ().getGraphicsDeviceFactory ().getDeviceForScreen ( - DefaultScreenIdentifier); - if (!device) - return; - auto cgDevice = std::static_pointer_cast (device); - auto deviceContext = std::make_shared ( - *cgDevice.get (), UIGraphicsGetCurrentContext ()); - - // TODO: refactor IPlatformFrameCallback to take a platform graphics device context - CDrawContext drawContext ( - std::static_pointer_cast (deviceContext), - CRectFromCGRect ([self bounds]), self.layer.contentsScale); - + CGDrawContext drawContext (UIGraphicsGetCurrentContext (), CRectFromCGRect (self.bounds)); uiViewFrame->getFrame ()->platformDrawRect (&drawContext, CRectFromCGRect (rect)); } diff --git a/vstgui/lib/platform/mac/macfactory.h b/vstgui/lib/platform/mac/macfactory.h index e77d04ea8..3ed621fa2 100644 --- a/vstgui/lib/platform/mac/macfactory.h +++ b/vstgui/lib/platform/mac/macfactory.h @@ -115,6 +115,14 @@ class MacFactory final : public IPlatformFactory */ DataPackagePtr getClipboard () const noexcept final; + /** create an offscreen draw device + * @param size the size of the bitmap where the offscreen renders to + * @param scaleFactor the scale factor for drawing + * @return an offscreen context object or nullptr on failure + */ + COffscreenContextPtr createOffscreenContext (const CPoint& size, + double scaleFactor = 1.) const noexcept final; + /** Create a platform gradient object * @return platform gradient object or nullptr on failure */ @@ -128,12 +136,6 @@ class MacFactory final : public IPlatformFactory PlatformFileSelectorPtr createFileSelector (PlatformFileSelectorStyle style, IPlatformFrame* frame) const noexcept final; - /** Get the graphics device factory - * - * @return platform graphics device factory - */ - const IPlatformGraphicsDeviceFactory& getGraphicsDeviceFactory () const noexcept final; - const LinuxFactory* asLinuxFactory () const noexcept final; const MacFactory* asMacFactory () const noexcept final; const Win32Factory* asWin32Factory () const noexcept final; diff --git a/vstgui/lib/platform/mac/macfactory.mm b/vstgui/lib/platform/mac/macfactory.mm index 474a55d28..298194c58 100644 --- a/vstgui/lib/platform/mac/macfactory.mm +++ b/vstgui/lib/platform/mac/macfactory.mm @@ -11,8 +11,8 @@ #include "../iplatformtimer.h" #include "cfontmac.h" #include "cgbitmap.h" +#include "cgdrawcontext.h" #include "quartzgraphicspath.h" -#include "coregraphicsdevicecontext.h" #include "cocoa/nsviewframe.h" #include "ios/uiviewframe.h" #include "macclipboard.h" @@ -35,7 +35,6 @@ CFBundleRef bundle {nullptr}; bool useAsynchronousLayerDrawing {true}; bool visualizeRedrawAreas {false}; - CoreGraphicsDeviceFactory graphicsDeviceFactory; }; //----------------------------------------------------------------------------- @@ -211,6 +210,18 @@ #endif } +//------------------------------------------------------------------------ +auto MacFactory::createOffscreenContext (const CPoint& size, double scaleFactor) const noexcept + -> COffscreenContextPtr +{ + auto bitmap = makeOwned (size * scaleFactor); + bitmap->setScaleFactor (scaleFactor); + auto context = makeOwned (bitmap); + if (context->getCGContext ()) + return std::move (context); + return nullptr; +} + //----------------------------------------------------------------------------- PlatformGradientPtr MacFactory::createGradient () const noexcept { @@ -228,12 +239,6 @@ return nullptr; } -//----------------------------------------------------------------------------- -const IPlatformGraphicsDeviceFactory& MacFactory::getGraphicsDeviceFactory () const noexcept -{ - return impl->graphicsDeviceFactory; -} - //----------------------------------------------------------------------------- const LinuxFactory* MacFactory::asLinuxFactory () const noexcept { diff --git a/vstgui/lib/platform/mac/mactimer.cpp b/vstgui/lib/platform/mac/mactimer.cpp index d4f01bcc7..884bd8544 100644 --- a/vstgui/lib/platform/mac/mactimer.cpp +++ b/vstgui/lib/platform/mac/mactimer.cpp @@ -32,7 +32,7 @@ bool MacTimer::start (uint32_t fireTime) if (timer) { #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_8 - #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_8 + #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAX_OS_X_VERSION_10_8 if (CFRunLoopTimerSetTolerance) #endif CFRunLoopTimerSetTolerance (timer, fireTime * 0.0001f); diff --git a/vstgui/lib/platform/mac/quartzgraphicspath.cpp b/vstgui/lib/platform/mac/quartzgraphicspath.cpp index a5731becc..8b0638df8 100644 --- a/vstgui/lib/platform/mac/quartzgraphicspath.cpp +++ b/vstgui/lib/platform/mac/quartzgraphicspath.cpp @@ -7,6 +7,7 @@ #if MAC #include "cfontmac.h" +#include "cgdrawcontext.h" #include "../../cgradient.h" diff --git a/vstgui/lib/platform/platformfactory.h b/vstgui/lib/platform/platformfactory.h index e6673cbc7..45822cc08 100644 --- a/vstgui/lib/platform/platformfactory.h +++ b/vstgui/lib/platform/platformfactory.h @@ -33,6 +33,7 @@ class IPlatformFactory { public: using DataPackagePtr = SharedPointer; + using COffscreenContextPtr = SharedPointer; virtual ~IPlatformFactory () noexcept = default; @@ -129,6 +130,14 @@ class IPlatformFactory */ virtual DataPackagePtr getClipboard () const noexcept = 0; + /** create an offscreen draw device + * @param size the size of the bitmap where the offscreen renders to + * @param scaleFactor the scale factor for drawing + * @return an offscreen context object or nullptr on failure + */ + virtual COffscreenContextPtr + createOffscreenContext (const CPoint& size, double scaleFactor = 1.) const noexcept = 0; + /** Create a platform gradient object * @return platform gradient object or nullptr on failure */ @@ -142,12 +151,6 @@ class IPlatformFactory virtual PlatformFileSelectorPtr createFileSelector (PlatformFileSelectorStyle style, IPlatformFrame* frame) const noexcept = 0; - /** Get the graphics device factory - * - * @return platform graphics device factory - */ - virtual const IPlatformGraphicsDeviceFactory& getGraphicsDeviceFactory () const noexcept = 0; - virtual const LinuxFactory* asLinuxFactory () const noexcept = 0; virtual const MacFactory* asMacFactory () const noexcept = 0; virtual const Win32Factory* asWin32Factory () const noexcept = 0; diff --git a/vstgui/lib/platform/win32/direct2d/d2d.h b/vstgui/lib/platform/win32/direct2d/d2d.h deleted file mode 100644 index b8514340a..000000000 --- a/vstgui/lib/platform/win32/direct2d/d2d.h +++ /dev/null @@ -1,80 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#pragma once - -#include "../../../cgraphicstransform.h" -#include "../../../ccolor.h" - -#include - -//------------------------------------------------------------------------ -namespace VSTGUI { - -namespace { - -using TransformMatrix = CGraphicsTransform; - -//----------------------------------------------------------------------------- -template -void pixelAlign (const TransformMatrix& tm, T& obj) -{ - CGraphicsTransform tInv = tm.inverse (); - tm.transform (obj); - obj.makeIntegral (); - tInv.transform (obj); -} - -//------------------------------------------------------------------------ -inline D2D1_POINT_2F convert (const CPoint& p) -{ - D2D1_POINT_2F dp = {(FLOAT)p.x, (FLOAT)p.y}; - return dp; -} - -//------------------------------------------------------------------------ -inline D2D1::ColorF convert (CColor c, double alpha) -{ - return D2D1::ColorF (c.normRed (), c.normGreen (), c.normBlue (), - static_cast (c.normAlpha () * alpha)); -} - -//------------------------------------------------------------------------ -inline D2D1_RECT_F convert (const CRect& r) -{ - D2D1_RECT_F dr = {(FLOAT)r.left, (FLOAT)r.top, (FLOAT)r.right, (FLOAT)r.bottom}; - return dr; -} - -//------------------------------------------------------------------------ -inline D2D1_MATRIX_3X2_F convert (const TransformMatrix& t) -{ - D2D1_MATRIX_3X2_F matrix; - matrix._11 = static_cast (t.m11); - matrix._12 = static_cast (t.m21); - matrix._21 = static_cast (t.m12); - matrix._22 = static_cast (t.m22); - matrix._31 = static_cast (t.dx); - matrix._32 = static_cast (t.dy); - return matrix; -} - -//------------------------------------------------------------------------ -inline TransformMatrix convert (const D2D1_MATRIX_3X2_F& t) -{ - TransformMatrix matrix; - matrix.m11 = static_cast (t._11); - matrix.m21 = static_cast (t._12); - matrix.m12 = static_cast (t._21); - matrix.m22 = static_cast (t._22); - matrix.dx = static_cast (t._31); - matrix.dy = static_cast (t._32); - return matrix; -} - -//------------------------------------------------------------------------ -} // anonymous namespace - -//------------------------------------------------------------------------ -} // VSTGUI diff --git a/vstgui/lib/platform/win32/direct2d/d2ddrawcontext.cpp b/vstgui/lib/platform/win32/direct2d/d2ddrawcontext.cpp new file mode 100644 index 000000000..bb3f593b6 --- /dev/null +++ b/vstgui/lib/platform/win32/direct2d/d2ddrawcontext.cpp @@ -0,0 +1,879 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#include "d2ddrawcontext.h" + +#if WINDOWS + +#include "../win32support.h" +#include "../win32factory.h" +#include "../../../cgradient.h" +#include "d2dbitmap.h" +#include "d2dbitmapcache.h" +#include "d2dgraphicspath.h" +#include "d2dfont.h" +#include "d2dgradient.h" +#include + +namespace VSTGUI { + +//----------------------------------------------------------------------------- +D2DDrawContext::D2DApplyClip::D2DApplyClip (D2DDrawContext* drawContext, bool halfPointOffset) +: drawContext (drawContext) +{ + CGraphicsTransform transform = drawContext->getCurrentTransform (); + auto scale = drawContext->getScaleFactor (); + transform.scale (scale, scale); + if (halfPointOffset) + { + CPoint offset (0.5, 0.5); + transform.translate (offset); + } + if (transform.m12 != 0. || transform.m21 != 0.) + { // we have a rotated matrix, we need to use a layer + layerIsUsed = true; + if (drawContext->currentClip.isEmpty () == false) + drawContext->getRenderTarget ()->PopAxisAlignedClip (); + + ID2D1RectangleGeometry* geometry; + if (FAILED (getD2DFactory ()->CreateRectangleGeometry (makeD2DRect (drawContext->getCurrentState ().clipRect), &geometry))) + return; + auto d2dMatrix = convert (transform); + drawContext->getRenderTarget ()->PushLayer ( + D2D1::LayerParameters (D2D1::InfiniteRect (), geometry, + D2D1_ANTIALIAS_MODE_ALIASED), + nullptr); + drawContext->getRenderTarget ()->SetTransform (d2dMatrix); + geometry->Release (); + applyClip = drawContext->getCurrentState ().clipRect; + drawContext->currentClip = {}; + } + else + { + if (drawContext->currentClip != drawContext->getCurrentState ().clipRect) + { + CRect clip = drawContext->getCurrentState ().clipRect; + if (drawContext->currentClip.isEmpty () == false) + drawContext->getRenderTarget ()->PopAxisAlignedClip (); + if (clip.isEmpty () == false) + drawContext->getRenderTarget ()->PushAxisAlignedClip (makeD2DRect (clip), D2D1_ANTIALIAS_MODE_ALIASED); + drawContext->currentClip = applyClip = clip; + } + else + { + applyClip = drawContext->currentClip; + } + drawContext->getRenderTarget ()->SetTransform (convert (transform)); + } +} + +//----------------------------------------------------------------------------- +D2DDrawContext::D2DApplyClip::~D2DApplyClip () +{ + if (layerIsUsed) + { + drawContext->getRenderTarget ()->PopLayer (); + } + auto scale = drawContext->getScaleFactor (); + CGraphicsTransform transform; + transform.scale (scale, scale); + drawContext->getRenderTarget ()->SetTransform (convert (transform)); +} + +//----------------------------------------------------------------------------- +D2DDrawContext::D2DDrawContext (HWND window, const CRect& drawSurface) +: COffscreenContext (drawSurface) +, window (window) +, renderTarget (nullptr) +, fillBrush (nullptr) +, strokeBrush (nullptr) +, fontBrush (nullptr) +, strokeStyle (nullptr) +{ + createRenderTarget (); +} + +//----------------------------------------------------------------------------- +D2DDrawContext::D2DDrawContext (D2DBitmap* inBitmap) +: COffscreenContext (new CBitmap (inBitmap)) +, window (nullptr) +, renderTarget (nullptr) +, fillBrush (nullptr) +, strokeBrush (nullptr) +, fontBrush (nullptr) +, strokeStyle (nullptr) +, scaleFactor (inBitmap->getScaleFactor ()) +{ + createRenderTarget (); + bitmap->forget (); +} + +//----------------------------------------------------------------------------- +D2DDrawContext::D2DDrawContext (ID2D1DeviceContext* deviceContext, const CRect& drawSurface, + ID2D1Device* device) +: COffscreenContext (drawSurface) +, window (nullptr) +, device (device) +, renderTarget (nullptr) +, fillBrush (nullptr) +, strokeBrush (nullptr) +, fontBrush (nullptr) +, strokeStyle (nullptr) +{ + renderTarget = deviceContext; + renderTarget->AddRef (); + init (); +} + +//----------------------------------------------------------------------------- +D2DDrawContext::~D2DDrawContext () +{ + releaseRenderTarget (); +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::createRenderTarget () +{ + if (window) + { + D2D1_RENDER_TARGET_TYPE renderTargetType = D2D1_RENDER_TARGET_TYPE_SOFTWARE; + if (auto pf = getPlatformFactory ().asWin32Factory ()) + { + renderTargetType = pf->useD2DHardwareRenderer () ? D2D1_RENDER_TARGET_TYPE_HARDWARE : + D2D1_RENDER_TARGET_TYPE_SOFTWARE; + } + RECT rc; + GetClientRect (window, &rc); + + D2D1_SIZE_U size = D2D1::SizeU (static_cast (rc.right - rc.left), static_cast (rc.bottom - rc.top)); + ID2D1HwndRenderTarget* hwndRenderTarget = nullptr; + D2D1_PIXEL_FORMAT pixelFormat = D2D1::PixelFormat (DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED); + HRESULT hr = getD2DFactory ()->CreateHwndRenderTarget (D2D1::RenderTargetProperties (renderTargetType, pixelFormat), D2D1::HwndRenderTargetProperties (window, size, D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS), &hwndRenderTarget); + if (SUCCEEDED (hr)) + { + renderTarget = hwndRenderTarget; + renderTarget->SetDpi (96, 96); + } + } + else if (bitmap) + { + D2DBitmap* d2dBitmap = dynamic_cast (bitmap->getPlatformBitmap ().get ()); + if (d2dBitmap) + { + D2D1_RENDER_TARGET_TYPE targetType = D2D1_RENDER_TARGET_TYPE_SOFTWARE; + D2D1_PIXEL_FORMAT pixelFormat = D2D1::PixelFormat (DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED); + getD2DFactory ()->CreateWicBitmapRenderTarget (d2dBitmap->getBitmap (), D2D1::RenderTargetProperties (targetType, pixelFormat), &renderTarget); + } + } + vstgui_assert (renderTarget); + init (); +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::releaseRenderTarget () +{ + if (fillBrush) + { + fillBrush->Release (); + fillBrush = nullptr; + } + if (strokeBrush) + { + strokeBrush->Release (); + strokeBrush = nullptr; + } + if (fontBrush) + { + fontBrush->Release (); + fontBrush = nullptr; + } + if (strokeStyle) + { + strokeStyle->Release (); + strokeStyle = nullptr; + } + if (renderTarget) + { + if (!device) + D2DBitmapCache::removeRenderTarget (renderTarget); + renderTarget->Release (); + renderTarget = nullptr; + } +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::beginDraw () +{ + if (renderTarget) + { + auto scale = getScaleFactor (); + CGraphicsTransform transform; + transform.scale (scale, scale); + renderTarget->BeginDraw (); + renderTarget->SetTransform (convert (transform)); + } +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::endDraw () +{ + if (renderTarget) + { + if (currentClip.isEmpty () == false) + { + getRenderTarget ()->PopAxisAlignedClip (); + currentClip = CRect (); + } + renderTarget->Flush (); + HRESULT result = renderTarget->EndDraw (); + if (result == (HRESULT)D2DERR_RECREATE_TARGET) + { + releaseRenderTarget (); + createRenderTarget (); + } + else + { + vstgui_assert (result == S_OK); + } + if (bitmap) + { + D2DBitmap* d2dBitmap = dynamic_cast (bitmap->getPlatformBitmap ().get ()); + D2DBitmapCache::removeBitmap (d2dBitmap); + } + } +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::init () +{ + COffscreenContext::init (); +} + +//----------------------------------------------------------------------------- +CGraphicsPath* D2DDrawContext::createGraphicsPath () +{ + return new CGraphicsPath (D2DGraphicsPathFactory::instance ()); +} + +//----------------------------------------------------------------------------- +CGraphicsPath* D2DDrawContext::createTextPath (const CFontRef font, UTF8StringPtr text) +{ + auto factory = D2DGraphicsPathFactory::instance (); + if (auto path = factory->createTextPath (font->getPlatformFont (), text)) + { + return new CGraphicsPath (D2DGraphicsPathFactory::instance (), std::move (path)); + } + return nullptr; +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::drawGraphicsPath (CGraphicsPath* graphicsPath, PathDrawMode mode, + CGraphicsTransform* t) +{ + if (renderTarget == nullptr || graphicsPath == nullptr) + return; + D2DApplyClip ac (this); + if (ac.isEmpty ()) + return; + + auto d2dPath = dynamic_cast ( + graphicsPath + ->getPlatformPath (mode == kPathFilledEvenOdd ? PlatformGraphicsPathFillMode::Alternate + : PlatformGraphicsPathFillMode::Winding) + .get ()); + if (d2dPath == nullptr) + return; + + ID2D1Geometry* path = nullptr; + if (t) + path = d2dPath->createTransformedGeometry (getD2DFactory (), *t); + else + { + path = d2dPath->getPathGeometry (); + path->AddRef (); + } + if (path) + { + if (mode == kPathFilled || mode == kPathFilledEvenOdd) + getRenderTarget ()->FillGeometry (path, getFillBrush ()); + else if (mode == kPathStroked) + getRenderTarget ()->DrawGeometry (path, getStrokeBrush (), (FLOAT)getLineWidth (), getStrokeStyle ()); + path->Release (); + } +} + +//----------------------------------------------------------------------------- +ID2D1GradientStopCollection* D2DDrawContext::createGradientStopCollection (const CGradient& gradient) const +{ + if (auto d2dGradient = dynamic_cast (gradient.getPlatformGradient ().get ())) + return d2dGradient->create (getRenderTarget (), getCurrentState ().globalAlpha); + return nullptr; +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::fillLinearGradient (CGraphicsPath* graphicsPath, const CGradient& gradient, + const CPoint& startPoint, const CPoint& endPoint, + bool evenOdd, CGraphicsTransform* t) +{ + if (renderTarget == nullptr || graphicsPath == nullptr) + return; + + D2DApplyClip ac (this, true); + if (ac.isEmpty ()) + return; + + auto d2dPath = dynamic_cast ( + graphicsPath + ->getPlatformPath (evenOdd ? PlatformGraphicsPathFillMode::Alternate + : PlatformGraphicsPathFillMode::Winding) + .get ()); + if (d2dPath == nullptr) + return; + ID2D1Geometry* path = nullptr; + if (t) + path = d2dPath->createTransformedGeometry (getD2DFactory (), *t); + else + { + path = d2dPath->getPathGeometry (); + path->AddRef (); + } + + if (path) + { + ID2D1GradientStopCollection* collection = createGradientStopCollection (gradient); + if (collection) + { + ID2D1LinearGradientBrush* brush = nullptr; + D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES properties; + properties.startPoint = makeD2DPoint (startPoint); + properties.endPoint = makeD2DPoint (endPoint); + if (SUCCEEDED (getRenderTarget ()->CreateLinearGradientBrush (properties, collection, &brush))) + { + getRenderTarget ()->FillGeometry (path, brush); + brush->Release (); + } + collection->Release (); + } + path->Release (); + } +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::fillRadialGradient (CGraphicsPath* graphicsPath, const CGradient& gradient, + const CPoint& center, CCoord radius, + const CPoint& originOffset, bool evenOdd, + CGraphicsTransform* t) +{ + if (renderTarget == nullptr || graphicsPath == nullptr) + return; + + D2DApplyClip ac (this, true); + if (ac.isEmpty ()) + return; + + auto d2dPath = dynamic_cast ( + graphicsPath + ->getPlatformPath (evenOdd ? PlatformGraphicsPathFillMode::Alternate + : PlatformGraphicsPathFillMode::Winding) + .get ()); + if (d2dPath == nullptr) + return; + + ID2D1Geometry* path = nullptr; + if (t) + path = d2dPath->createTransformedGeometry (getD2DFactory (), *t); + else + { + path = d2dPath->getPathGeometry (); + path->AddRef (); + } + + if (path) + { + if (ID2D1GradientStopCollection* collection = createGradientStopCollection (gradient)) + { + // brush properties + ID2D1RadialGradientBrush* brush = nullptr; + D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES properties; + properties.center = makeD2DPoint (center); + properties.gradientOriginOffset = makeD2DPoint (originOffset); + properties.radiusX = (FLOAT)radius; + properties.radiusY = (FLOAT)radius; + + if (SUCCEEDED (getRenderTarget ()->CreateRadialGradientBrush (properties, + collection, &brush))) + { + getRenderTarget ()->FillGeometry (path, brush); + brush->Release (); + } + collection->Release (); + } + path->Release (); + } +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::clearRect (const CRect& rect) +{ + if (renderTarget) + { + CRect oldClip = getCurrentState ().clipRect; + setClipRect (rect); + D2DApplyClip ac (this); + renderTarget->Clear (D2D1::ColorF (1.f, 1.f, 1.f, 0.f)); + setClipRect (oldClip); + } +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::drawBitmap (CBitmap* bitmap, const CRect& dest, const CPoint& offset, float alpha) +{ + if (renderTarget == nullptr) + return; + ConcatClip concatClip (*this, dest); + D2DApplyClip ac (this); + if (ac.isEmpty ()) + return; + + double transformedScaleFactor = getScaleFactor (); + CGraphicsTransform t = getCurrentTransform (); + if (t.m11 == t.m22 && t.m12 == 0 && t.m21 == 0) + transformedScaleFactor *= t.m11; + IPlatformBitmap* platformBitmap = bitmap->getBestPlatformBitmapForScaleFactor (transformedScaleFactor); + D2DBitmap* d2dBitmap = platformBitmap ? dynamic_cast (platformBitmap) : nullptr; + if (d2dBitmap && d2dBitmap->getSource ()) + { + if (auto d2d1Bitmap = D2DBitmapCache::getBitmap (d2dBitmap, renderTarget, device)) + { + double bitmapScaleFactor = platformBitmap->getScaleFactor (); + CGraphicsTransform bitmapTransform; + bitmapTransform.scale (1./bitmapScaleFactor, 1./bitmapScaleFactor); + Transform transform (*this, bitmapTransform); + + CRect d (dest); + d.setWidth (bitmap->getWidth ()); + d.setHeight (bitmap->getHeight ()); + d.offset (-offset.x, -offset.y); + d.makeIntegral (); + CRect source; + source.setWidth (d2d1Bitmap->GetSize ().width); + source.setHeight (d2d1Bitmap->GetSize ().height); + + D2D1_BITMAP_INTERPOLATION_MODE mode; + switch (getCurrentState ().bitmapQuality) + { + case BitmapInterpolationQuality::kLow: + mode = D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; + break; + + case BitmapInterpolationQuality::kMedium: + case BitmapInterpolationQuality::kHigh: + default: + mode = D2D1_BITMAP_INTERPOLATION_MODE_LINEAR; + break; + } + + D2D1_RECT_F sourceRect = makeD2DRect (source); + renderTarget->DrawBitmap (d2d1Bitmap, makeD2DRect (d), alpha * getCurrentState ().globalAlpha, mode, &sourceRect); + } + } +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::drawLineInternal (CPoint start, CPoint end) +{ + renderTarget->DrawLine (makeD2DPoint (start), makeD2DPoint (end), strokeBrush, (FLOAT)getCurrentState ().frameWidth, strokeStyle); +} + +//----------------------------------------------------------------------------- +bool D2DDrawContext::needsHalfPointOffset () const +{ + return static_cast (getCurrentState ().frameWidth) % 2 != 0; +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::drawLine (const LinePair& line) +{ + if (renderTarget == nullptr) + return; + D2DApplyClip ac (this); + if (ac.isEmpty ()) + return; + + CPoint start (line.first); + CPoint end (line.second); + if (getDrawMode ().integralMode ()) + { + pixelAllign (start); + pixelAllign (end); + } + if (needsHalfPointOffset ()) + { + start.offset (0.5, 0.5); + end.offset (0.5, 0.5); + } + drawLineInternal (start, end); +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::drawLines (const LineList& lines) +{ + if (lines.empty () || renderTarget == nullptr) + return; + D2DApplyClip ac (this); + if (ac.isEmpty ()) + return; + + bool needsOffset = needsHalfPointOffset (); + bool integralMode = getDrawMode ().integralMode (); + for (const auto& line : lines) + { + CPoint start (line.first); + CPoint end (line.second); + if (integralMode) + { + pixelAllign (start); + pixelAllign (end); + } + if (needsOffset) + { + start.offset (0.5, 0.5); + end.offset (0.5, 0.5); + } + drawLineInternal (start, end); + } +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::drawPolygon (const PointList& polygonPointList, const CDrawStyle drawStyle) +{ + if (renderTarget == nullptr || polygonPointList.empty ()) + return; + D2DApplyClip ac (this); + if (ac.isEmpty ()) + return; + + auto path = owned (createGraphicsPath ()); + path->beginSubpath (polygonPointList[0]); + for (uint32_t i = 1; i < polygonPointList.size (); ++i) + { + path->addLine (polygonPointList[i]); + } + if (drawStyle == kDrawFilled || drawStyle == kDrawFilledAndStroked) + { + drawGraphicsPath (path, kPathFilled); + } + if (drawStyle == kDrawStroked || drawStyle == kDrawFilledAndStroked) + { + drawGraphicsPath (path, kPathStroked); + } +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::drawRect (const CRect &_rect, const CDrawStyle drawStyle) +{ + if (renderTarget == nullptr) + return; + D2DApplyClip ac (this); + if (ac.isEmpty ()) + return; + CRect rect (_rect); + if (drawStyle != kDrawFilled) + { + rect.right -= 1.; + rect.bottom -= 1.; + } + if (drawStyle == kDrawFilled || drawStyle == kDrawFilledAndStroked) + { + renderTarget->FillRectangle (makeD2DRect (rect), fillBrush); + } + if (drawStyle == kDrawStroked || drawStyle == kDrawFilledAndStroked) + { + if (needsHalfPointOffset ()) + { + rect.offset (0.5, 0.5); + } + renderTarget->DrawRectangle (makeD2DRect (rect), strokeBrush, (FLOAT)getCurrentState ().frameWidth, strokeStyle); + } +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::drawArc (const CRect& _rect, const float _startAngle, const float _endAngle, const CDrawStyle drawStyle) +{ + if (auto path = owned (createGraphicsPath ())) + { + CRect rect (_rect); + if (getDrawMode ().integralMode ()) + pixelAllign (rect); + path->addArc (rect, _startAngle, _endAngle, true); + if (drawStyle == kDrawFilled || drawStyle == kDrawFilledAndStroked) + drawGraphicsPath (path, kPathFilled); + if (drawStyle == kDrawStroked || drawStyle == kDrawFilledAndStroked) + drawGraphicsPath (path, kPathStroked); + } +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::drawEllipse (const CRect &_rect, const CDrawStyle drawStyle) +{ + if (renderTarget == nullptr) + return; + D2DApplyClip ac (this); + if (ac.isEmpty ()) + return; + CRect rect (_rect); + if (getDrawMode ().integralMode ()) + pixelAllign (rect); + if (drawStyle == kDrawStroked) + rect.inset (0.5, 0.5); + CPoint center (rect.getTopLeft ()); + center.offset (rect.getWidth () / 2., rect.getHeight () / 2.); + D2D1_ELLIPSE ellipse; + ellipse.point = makeD2DPoint (center); + ellipse.radiusX = (FLOAT)(rect.getWidth () / 2.); + ellipse.radiusY = (FLOAT)(rect.getHeight () / 2.); + if (drawStyle == kDrawFilled || drawStyle == kDrawFilledAndStroked) + { + renderTarget->FillEllipse (ellipse, fillBrush); + } + if (drawStyle == kDrawStroked || drawStyle == kDrawFilledAndStroked) + { + renderTarget->DrawEllipse (ellipse, strokeBrush, (FLOAT)getCurrentState ().frameWidth, strokeStyle); + } +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::drawPoint (const CPoint &point, const CColor& color) +{ + saveGlobalState (); + setLineWidth (1); + setFrameColor (color); + CPoint point2 (point); + point2.x++; + COffscreenContext::drawLine (point, point2); + restoreGlobalState (); +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::setLineStyle (const CLineStyle& style) +{ + if (strokeStyle && getCurrentState ().lineStyle == style) + return; + setLineStyleInternal (style); + COffscreenContext::setLineStyle (style); +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::setLineStyleInternal (const CLineStyle& style) +{ + if (strokeStyle) + { + strokeStyle->Release (); + strokeStyle = nullptr; + } + D2D1_STROKE_STYLE_PROPERTIES properties; + switch (style.getLineCap ()) + { + case CLineStyle::kLineCapButt: properties.startCap = properties.endCap = properties.dashCap = D2D1_CAP_STYLE_FLAT; break; + case CLineStyle::kLineCapRound: properties.startCap = properties.endCap = properties.dashCap = D2D1_CAP_STYLE_ROUND; break; + case CLineStyle::kLineCapSquare: properties.startCap = properties.endCap = properties.dashCap = D2D1_CAP_STYLE_SQUARE; break; + } + switch (style.getLineJoin ()) + { + case CLineStyle::kLineJoinMiter: properties.lineJoin = D2D1_LINE_JOIN_MITER; break; + case CLineStyle::kLineJoinRound: properties.lineJoin = D2D1_LINE_JOIN_ROUND; break; + case CLineStyle::kLineJoinBevel: properties.lineJoin = D2D1_LINE_JOIN_BEVEL; break; + } + properties.dashOffset = (FLOAT)style.getDashPhase (); + properties.miterLimit = 10.f; + if (style.getDashCount ()) + { + properties.dashStyle = D2D1_DASH_STYLE_CUSTOM; + FLOAT* lengths = new FLOAT[style.getDashCount ()]; + for (uint32_t i = 0; i < style.getDashCount (); i++) + lengths[i] = (FLOAT)style.getDashLengths ()[i]; + getD2DFactory ()->CreateStrokeStyle (properties, lengths, style.getDashCount (), &strokeStyle); + delete [] lengths; + } + else + { + properties.dashStyle = D2D1_DASH_STYLE_SOLID; + getD2DFactory ()->CreateStrokeStyle (properties, nullptr, 0, &strokeStyle); + } +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::setLineWidth (CCoord width) +{ + if (getCurrentState ().frameWidth == width) + return; + COffscreenContext::setLineWidth (width); +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::setDrawMode (CDrawMode mode) +{ + if (getCurrentState ().drawMode == mode && getCurrentState ().drawMode.integralMode () == mode.integralMode ()) + return; + setDrawModeInternal (mode); + COffscreenContext::setDrawMode (mode); +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::setDrawModeInternal (CDrawMode mode) +{ + if (renderTarget) + { + if (mode == kAntiAliasing) + renderTarget->SetAntialiasMode (D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + else + renderTarget->SetAntialiasMode (D2D1_ANTIALIAS_MODE_ALIASED); + } +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::setClipRect (const CRect &clip) +{ + COffscreenContext::setClipRect (clip); +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::resetClipRect () +{ + COffscreenContext::resetClipRect (); +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::setFillColor (const CColor& color) +{ + if (getCurrentState ().fillColor == color) + return; + setFillColorInternal (color); + COffscreenContext::setFillColor (color); +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::setFrameColor (const CColor& color) +{ + if (getCurrentState ().frameColor == color) + return; + setFrameColorInternal (color); + COffscreenContext::setFrameColor (color); +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::setFontColor (const CColor& color) +{ + if (getCurrentState ().fontColor == color) + return; + setFontColorInternal (color); + COffscreenContext::setFontColor (color); +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::setFillColorInternal (const CColor& color) +{ + if (fillBrush) + { + fillBrush->Release (); + fillBrush = nullptr; + } + if (renderTarget) + { + renderTarget->CreateSolidColorBrush (toColorF (color, getCurrentState ().globalAlpha), + &fillBrush); + } +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::setFrameColorInternal (const CColor& color) +{ + if (strokeBrush) + { + strokeBrush->Release (); + strokeBrush = nullptr; + } + if (renderTarget) + { + renderTarget->CreateSolidColorBrush (toColorF (color, getCurrentState ().globalAlpha), + &strokeBrush); + } +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::setFontColorInternal (const CColor& color) +{ + if (fontBrush) + { + fontBrush->Release (); + fontBrush = nullptr; + } + if (renderTarget) + { + renderTarget->CreateSolidColorBrush (toColorF (color, getCurrentState ().globalAlpha), + &fontBrush); + } +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::setGlobalAlpha (float newAlpha) +{ + if (getCurrentState ().globalAlpha == newAlpha) + return; + COffscreenContext::setGlobalAlpha (newAlpha); + setFrameColorInternal (getCurrentState ().frameColor); + setFillColorInternal (getCurrentState ().fillColor); + setFontColorInternal (getCurrentState ().fontColor); +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::saveGlobalState () +{ + COffscreenContext::saveGlobalState (); +} + +//----------------------------------------------------------------------------- +void D2DDrawContext::restoreGlobalState () +{ + CColor prevFillColor = getCurrentState ().fillColor; + CColor prevFrameColor = getCurrentState ().frameColor; + CColor prevFontColor = getCurrentState ().fontColor; + CLineStyle prevLineStye = getCurrentState ().lineStyle; + CDrawMode prevDrawMode = getCurrentState ().drawMode; + float prevAlpha = getCurrentState ().globalAlpha; + COffscreenContext::restoreGlobalState (); + if (prevAlpha != getCurrentState ().globalAlpha) + { + float _prevAlpha = getCurrentState ().globalAlpha; + getCurrentState ().globalAlpha = -1.f; + setGlobalAlpha (_prevAlpha); + } + else + { + if (prevFillColor != getCurrentState ().fillColor) + { + setFillColorInternal (getCurrentState ().fillColor); + } + if (prevFrameColor != getCurrentState ().frameColor) + { + setFrameColorInternal (getCurrentState ().frameColor); + } + if (prevFontColor != getCurrentState ().fontColor) + { + setFontColorInternal (getCurrentState ().fontColor); + } + } + if (prevLineStye != getCurrentState ().lineStyle) + { + setLineStyleInternal (getCurrentState ().lineStyle); + } + if (prevDrawMode != getCurrentState ().drawMode) + { + setDrawModeInternal (getCurrentState ().drawMode); + } +} + +} // VSTGUI + +#endif // WINDOWS diff --git a/vstgui/lib/platform/win32/direct2d/d2ddrawcontext.h b/vstgui/lib/platform/win32/direct2d/d2ddrawcontext.h new file mode 100644 index 000000000..acbd5b0b4 --- /dev/null +++ b/vstgui/lib/platform/win32/direct2d/d2ddrawcontext.h @@ -0,0 +1,166 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once + +#include "../../../coffscreencontext.h" + +#if WINDOWS +struct IUnknown; +struct ID2D1DeviceContext; + +#include "d2dbitmap.h" +#include +#include +#include + +namespace VSTGUI { +class CGradient; + +//----------------------------------------------------------------------------- +class D2DDrawContext final : public COffscreenContext +{ +public: + D2DDrawContext (HWND window, const CRect& drawSurface); + D2DDrawContext (ID2D1DeviceContext* deviceContext, const CRect& drawSurface, + ID2D1Device* device = nullptr); + D2DDrawContext (D2DBitmap* bitmap); + ~D2DDrawContext (); + + bool usable () const { return getRenderTarget () != nullptr; } + + ID2D1RenderTarget* getRenderTarget () const { return renderTarget; } + ID2D1SolidColorBrush* getFillBrush () const { return fillBrush; } + ID2D1SolidColorBrush* getStrokeBrush () const { return strokeBrush; } + ID2D1SolidColorBrush* getFontBrush () const { return fontBrush; } + ID2D1StrokeStyle* getStrokeStyle () const { return strokeStyle; } + + // CDrawContext + void drawLine (const LinePair& line) override; + void drawLines (const LineList& lines) override; + void drawPolygon (const PointList& polygonPointList, const CDrawStyle drawStyle = kDrawStroked) override; + void drawRect (const CRect &rect, const CDrawStyle drawStyle = kDrawStroked) override; + void drawArc (const CRect &rect, const float startAngle1, const float endAngle2, const CDrawStyle drawStyle = kDrawStroked) override; + void drawEllipse (const CRect &rect, const CDrawStyle drawStyle = kDrawStroked) override; + void drawPoint (const CPoint &point, const CColor& color) override; + void drawBitmap (CBitmap* bitmap, const CRect& dest, const CPoint& offset = CPoint (0, 0), float alpha = 1.f) override; + void clearRect (const CRect& rect) override; + void setLineStyle (const CLineStyle& style) override; + void setLineWidth (CCoord width) override; + void setDrawMode (CDrawMode mode) override; + void setClipRect (const CRect &clip) override; + void resetClipRect () override; + void setFillColor (const CColor& color) override; + void setFrameColor (const CColor& color) override; + void setFontColor (const CColor& color) override; + void setGlobalAlpha (float newAlpha) override; + void saveGlobalState () override; + void restoreGlobalState () override; + CGraphicsPath* createGraphicsPath () override; + CGraphicsPath* createTextPath (const CFontRef font, UTF8StringPtr text) override; + void drawGraphicsPath (CGraphicsPath* path, PathDrawMode mode = kPathFilled, CGraphicsTransform* transformation = nullptr) override; + void fillLinearGradient (CGraphicsPath* path, const CGradient& gradient, const CPoint& startPoint, const CPoint& endPoint, bool evenOdd = false, CGraphicsTransform* transformation = nullptr) override; + void fillRadialGradient (CGraphicsPath* path, const CGradient& gradient, const CPoint& center, CCoord radius, const CPoint& originOffset = CPoint (0, 0), bool evenOdd = false, CGraphicsTransform* transformation = nullptr) override; + + void beginDraw () override; + void endDraw () override; + + double getScaleFactor () const override { return scaleFactor; } + + //----------------------------------------------------------------------------- + class D2DApplyClip + { + public: + D2DApplyClip (D2DDrawContext* drawContext, bool halfPointOffset = false); + ~D2DApplyClip (); + bool isEmpty () const { return applyClip.isEmpty (); } + protected: + D2DDrawContext* drawContext; + CRect applyClip; + bool layerIsUsed {false}; + }; + + template void pixelAllign (T& rect) const; + +//----------------------------------------------------------------------------- +protected: + void init () override; + void createRenderTarget (); + void releaseRenderTarget (); + ID2D1GradientStopCollection* createGradientStopCollection (const CGradient& gradient) const; + + void setFillColorInternal (const CColor& color); + void setFrameColorInternal (const CColor& color); + void setFontColorInternal (const CColor& color); + void setLineStyleInternal (const CLineStyle& style); + void setDrawModeInternal (CDrawMode mode); + void drawLineInternal (CPoint start, CPoint end); + + bool needsHalfPointOffset () const; + + HWND window; + ID2D1Device* device {nullptr}; + ID2D1RenderTarget* renderTarget; + ID2D1SolidColorBrush* fillBrush; + ID2D1SolidColorBrush* strokeBrush; + ID2D1SolidColorBrush* fontBrush; + ID2D1StrokeStyle* strokeStyle; + CRect currentClip; + double scaleFactor {1.}; +}; + +//----------------------------------------------------------------------------- +template void D2DDrawContext::pixelAllign (T& obj) const +{ + const CGraphicsTransform& t = getCurrentTransform (); + CGraphicsTransform tInv = t.inverse (); + t.transform (obj); + obj.makeIntegral (); + tInv.transform (obj); +} + +//----------------------------------------------------------------------------- +static inline D2D1_RECT_F makeD2DRect (const CRect& r) +{ + D2D1_RECT_F dr = {(FLOAT)r.left, (FLOAT)r.top, (FLOAT)r.right, (FLOAT)r.bottom}; + return dr; +} + +//----------------------------------------------------------------------------- +static inline D2D1_POINT_2F makeD2DPoint (const CPoint& p) +{ + D2D1_POINT_2F dp = {(FLOAT)p.x, (FLOAT)p.y}; + return dp; +} + +static inline D2D1_SIZE_F makeD2DSize (CCoord width, CCoord height) +{ + D2D1_SIZE_F ds = {(FLOAT)width, (FLOAT)height}; + return ds; +} + +//----------------------------------------------------------------------------- +static inline D2D1_MATRIX_3X2_F convert (const CGraphicsTransform& t) +{ + D2D1_MATRIX_3X2_F matrix; + matrix._11 = static_cast (t.m11); + matrix._12 = static_cast (t.m21); + matrix._21 = static_cast (t.m12); + matrix._22 = static_cast (t.m22); + matrix._31 = static_cast (t.dx); + matrix._32 = static_cast (t.dy); + return matrix; +} + +//----------------------------------------------------------------------------- +static inline D2D1::ColorF toColorF (CColor c, float alpha) +{ + return D2D1::ColorF (c.normRed (), c.normGreen (), c.normBlue (), + c.normAlpha () * alpha); +} + + +} // VSTGUI + +#endif // WINDOWS diff --git a/vstgui/lib/platform/win32/direct2d/d2dfont.cpp b/vstgui/lib/platform/win32/direct2d/d2dfont.cpp index 13396d7bd..cd815ae5d 100644 --- a/vstgui/lib/platform/win32/direct2d/d2dfont.cpp +++ b/vstgui/lib/platform/win32/direct2d/d2dfont.cpp @@ -1,4 +1,4 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE @@ -11,8 +11,7 @@ #include "../win32factory.h" #include "../winstring.h" #include "../comptr.h" -#include "../../../ccolor.h" -#include "d2dgraphicscontext.h" +#include "d2ddrawcontext.h" #include #include #include @@ -25,8 +24,8 @@ #define VSTGUI_WIN32_CUSTOMFONT_SUPPORT 1 #else #define VSTGUI_WIN32_CUSTOMFONT_SUPPORT 0 -#pragma message( \ - "Warning: VSTGUI Custom Font support is only available when building with the Windows 10 Creator Update SDK or newer") +#pragma message( \ + "Warning: VSTGUI Custom Font support is only available when building with the Windows 10 Creator Update SDK or newer") #endif namespace VSTGUI { @@ -72,7 +71,7 @@ struct CustomFonts { COM::Ptr fontFile; if (FAILED (factory5->CreateFontFileReference (file.data (), nullptr, - fontFile.adoptPtr ()))) + fontFile.adoptPtr ()))) continue; fontSetBuilder->AddFontFile (fontFile.get ()); } @@ -121,7 +120,9 @@ static std::once_flag customFontsOnceFlag; //----------------------------------------------------------------------------- static CustomFonts* getCustomFonts () { - std::call_once (customFontsOnceFlag, [] () { customFonts = std::make_unique (); }); + std::call_once (customFontsOnceFlag, [] () { + customFonts = std::make_unique (); + }); return customFonts.get (); } @@ -153,16 +154,14 @@ static void gatherFonts (const FontFamilyCallback& callback, IDWriteFontCollecti //----------------------------------------------------------------------------- static COM::Ptr getFont (IDWriteTextFormat* format, int32_t style) { - DWRITE_FONT_STYLE fontStyle = - (style & kItalicFace) ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL; - DWRITE_FONT_WEIGHT fontWeight = - (style & kBoldFace) ? DWRITE_FONT_WEIGHT_BOLD : DWRITE_FONT_WEIGHT_NORMAL; + DWRITE_FONT_STYLE fontStyle = (style & kItalicFace) ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL; + DWRITE_FONT_WEIGHT fontWeight = (style & kBoldFace) ? DWRITE_FONT_WEIGHT_BOLD : DWRITE_FONT_WEIGHT_NORMAL; IDWriteFontCollection* fontCollection = nullptr; format->GetFontCollection (&fontCollection); if (!fontCollection) return {}; auto nameLength = format->GetFontFamilyNameLength () + 1; - auto familyName = std::unique_ptr (new WCHAR[nameLength]); + auto familyName = std::unique_ptr (new WCHAR [nameLength]); if (FAILED (format->GetFontFamilyName (familyName.get (), nameLength))) return {}; UINT32 index = 0; @@ -174,8 +173,7 @@ static COM::Ptr getFont (IDWriteTextFormat* format, int32_t style) if (fontFamily) { COM::Ptr font; - fontFamily->GetFirstMatchingFont (fontWeight, DWRITE_FONT_STRETCH_NORMAL, fontStyle, - font.adoptPtr ()); + fontFamily->GetFirstMatchingFont (fontWeight, DWRITE_FONT_STRETCH_NORMAL, fontStyle, font.adoptPtr ()); return font; } return {}; @@ -199,16 +197,22 @@ bool D2DFont::getAllFontFamilies (const FontFamilyCallback& callback) } //----------------------------------------------------------------------------- -void D2DFont::terminate () { D2DFontPrivate::customFonts = nullptr; } +void D2DFont::terminate () +{ + D2DFontPrivate::customFonts = nullptr; +} //----------------------------------------------------------------------------- D2DFont::D2DFont (const UTF8String& name, const CCoord& size, const int32_t& style) -: textFormat (nullptr), ascent (-1), descent (-1), leading (-1), capHeight (-1), style (style) +: textFormat (nullptr) +, ascent (-1) +, descent (-1) +, leading (-1) +, capHeight (-1) +, style (style) { - DWRITE_FONT_STYLE fontStyle = - (style & kItalicFace) ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL; - DWRITE_FONT_WEIGHT fontWeight = - (style & kBoldFace) ? DWRITE_FONT_WEIGHT_BOLD : DWRITE_FONT_WEIGHT_NORMAL; + DWRITE_FONT_STYLE fontStyle = (style & kItalicFace) ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL; + DWRITE_FONT_WEIGHT fontWeight = (style & kBoldFace) ? DWRITE_FONT_WEIGHT_BOLD : DWRITE_FONT_WEIGHT_NORMAL; UTF8StringHelper nameStr (name.data ()); IDWriteFontCollection* fontCollection = nullptr; @@ -218,8 +222,8 @@ D2DFont::D2DFont (const UTF8String& name, const CCoord& size, const int32_t& sty fontCollection = customFonts->getFontCollection (); getDWriteFactory ()->CreateTextFormat (nameStr, fontCollection, fontWeight, fontStyle, - DWRITE_FONT_STRETCH_NORMAL, (FLOAT)size, L"en-us", - &textFormat); + DWRITE_FONT_STRETCH_NORMAL, (FLOAT)size, L"en-us", + &textFormat); if (textFormat) { if (auto font = D2DFontPrivate::getFont (textFormat, style)) @@ -267,50 +271,59 @@ IDWriteTextLayout* D2DFont::createTextLayout (IPlatformString* string) const const auto* winString = dynamic_cast (string); IDWriteTextLayout* textLayout = nullptr; if (winString) - getDWriteFactory ()->CreateTextLayout (winString->getWideString (), - (UINT32)wcslen (winString->getWideString ()), - textFormat, 10000, 1000, &textLayout); + getDWriteFactory ()->CreateTextLayout (winString->getWideString (), (UINT32)wcslen (winString->getWideString ()), textFormat, 10000, 1000, &textLayout); return textLayout; } //----------------------------------------------------------------------------- -void D2DFont::drawString (const PlatformGraphicsDeviceContextPtr& context, IPlatformString* string, - const CPoint& p, const CColor& color, bool antialias) const +void D2DFont::drawString (CDrawContext* context, IPlatformString* string, const CPoint& p, bool antialias) const { - if (!textFormat || !context || !string) - return; - - if (auto graphicsContext = std::dynamic_pointer_cast (context)) + auto* d2dContext = dynamic_cast (context); + if (d2dContext && textFormat) { - IDWriteTextLayout* textLayout = createTextLayout (string); - if (!textLayout) + D2DDrawContext::D2DApplyClip ac (d2dContext); + if (ac.isEmpty ()) return; - if (style & kUnderlineFace) - { - DWRITE_TEXT_RANGE range = {0, UINT_MAX}; - textLayout->SetUnderline (true, range); - } - if (style & kStrikethroughFace) + ID2D1RenderTarget* renderTarget = d2dContext->getRenderTarget (); + if (renderTarget) { - DWRITE_TEXT_RANGE range = {0, UINT_MAX}; - textLayout->SetStrikethrough (true, range); - } - CPoint pos (p); - DWRITE_LINE_METRICS lm; - UINT lineCount; - if (SUCCEEDED (textLayout->GetLineMetrics (&lm, 1, &lineCount))) - pos.y -= lm.baseline; - else - pos.y -= textFormat->GetFontSize (); + IDWriteTextLayout* textLayout = createTextLayout (string); + if (textLayout) + { + if (style & kUnderlineFace) + { + DWRITE_TEXT_RANGE range = { 0, UINT_MAX }; + textLayout->SetUnderline (true, range); + } + if (style & kStrikethroughFace) + { + DWRITE_TEXT_RANGE range = { 0, UINT_MAX }; + textLayout->SetStrikethrough (true, range); + } + renderTarget->SetTextAntialiasMode (antialias ? D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE : D2D1_TEXT_ANTIALIAS_MODE_ALIASED); + + CPoint pos (p); + DWRITE_LINE_METRICS lm; + UINT lineCount; + if (SUCCEEDED (textLayout->GetLineMetrics (&lm, 1, &lineCount))) + pos.y -= lm.baseline; + else + pos.y -= textFormat->GetFontSize (); - graphicsContext->drawTextLayout (textLayout, pos, color, antialias); - textLayout->Release (); + if (context->getDrawMode ().integralMode ()) + pos.makeIntegral (); + pos.y += 0.5; + + D2D1_POINT_2F origin = {(FLOAT)(p.x), (FLOAT)(pos.y)}; + d2dContext->getRenderTarget ()->DrawTextLayout (origin, textLayout, d2dContext->getFontBrush ()); + textLayout->Release (); + } + } } } //----------------------------------------------------------------------------- -CCoord D2DFont::getStringWidth (const PlatformGraphicsDeviceContextPtr&, IPlatformString* string, - bool antialias) const +CCoord D2DFont::getStringWidth (CDrawContext* context, IPlatformString* string, bool antialias) const { CCoord result = 0; if (textFormat) diff --git a/vstgui/lib/platform/win32/direct2d/d2dfont.h b/vstgui/lib/platform/win32/direct2d/d2dfont.h index 044e7f8fb..7b641efbd 100644 --- a/vstgui/lib/platform/win32/direct2d/d2dfont.h +++ b/vstgui/lib/platform/win32/direct2d/d2dfont.h @@ -40,10 +40,8 @@ class D2DFont final : public IPlatformFont, public IFontPainter const IFontPainter* getPainter () const override { return this; } - void drawString (const PlatformGraphicsDeviceContextPtr& context, IPlatformString* string, - const CPoint& p, const CColor& color, bool antialias = true) const override; - CCoord getStringWidth (const PlatformGraphicsDeviceContextPtr& context, IPlatformString* string, - bool antialias = true) const override; + void drawString (CDrawContext* context, IPlatformString* string, const CPoint& p, bool antialias = true) const override; + CCoord getStringWidth (CDrawContext* context, IPlatformString* string, bool antialias = true) const override; IDWriteTextFormat* textFormat; double ascent; diff --git a/vstgui/lib/platform/win32/direct2d/d2dgradient.cpp b/vstgui/lib/platform/win32/direct2d/d2dgradient.cpp index ed45b1805..8a21afad7 100644 --- a/vstgui/lib/platform/win32/direct2d/d2dgradient.cpp +++ b/vstgui/lib/platform/win32/direct2d/d2dgradient.cpp @@ -6,7 +6,7 @@ #if WINDOWS -#include "d2d.h" +#include "d2ddrawcontext.h" #include namespace VSTGUI { @@ -21,7 +21,7 @@ ID2D1GradientStopCollection* D2DGradient::create (ID2D1RenderTarget* renderTarge for (auto it = getColorStops ().begin (); it != getColorStops ().end (); ++it, ++index) { gradientStops[index].position = static_cast (it->first); - gradientStops[index].color = convert (it->second, globalAlpha); + gradientStops[index].color = toColorF (it->second, globalAlpha); } ID2D1GradientStopCollection* collection = nullptr; auto hr = renderTarget->CreateGradientStopCollection ( diff --git a/vstgui/lib/platform/win32/direct2d/d2dgraphicscontext.cpp b/vstgui/lib/platform/win32/direct2d/d2dgraphicscontext.cpp deleted file mode 100644 index c2565ddf4..000000000 --- a/vstgui/lib/platform/win32/direct2d/d2dgraphicscontext.cpp +++ /dev/null @@ -1,853 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#include "d2dgraphicscontext.h" -#include "d2dgradient.h" -#include "d2dgraphicspath.h" -#include "d2dbitmap.h" -#include "d2dbitmapcache.h" -#include "d2dgradient.h" -#include "d2d.h" -#include "../comptr.h" -#include "../../../crect.h" -#include "../../../cgraphicstransform.h" -#include "../../../ccolor.h" -#include "../../../cdrawdefs.h" -#include "../../../clinestyle.h" -#include - -#include -#include - -//------------------------------------------------------------------------ -namespace VSTGUI { -namespace { - -//------------------------------------------------------------------------ -struct TransformGuard -{ - TransformGuard (ID2D1DeviceContext* context) : context (context) - { - context->GetTransform (&matrix); - } - ~TransformGuard () noexcept { context->SetTransform (matrix); } - - D2D1_MATRIX_3X2_F matrix; - ID2D1DeviceContext* context; -}; - -//------------------------------------------------------------------------ -} // anonymous namespace - -//------------------------------------------------------------------------ -struct D2DGraphicsDeviceFactory::Impl -{ - std::vector> devices; -}; - -//------------------------------------------------------------------------ -D2DGraphicsDeviceFactory::D2DGraphicsDeviceFactory () { impl = std::make_unique (); } - -//------------------------------------------------------------------------ -D2DGraphicsDeviceFactory::~D2DGraphicsDeviceFactory () noexcept {} - -//------------------------------------------------------------------------ -PlatformGraphicsDevicePtr - D2DGraphicsDeviceFactory::getDeviceForScreen (ScreenInfo::Identifier screen) const -{ - if (impl->devices.empty ()) - return nullptr; - return impl->devices.front (); -} - -//------------------------------------------------------------------------ -PlatformGraphicsDevicePtr D2DGraphicsDeviceFactory::find (ID2D1Device* dev) const -{ - auto it = std::find_if (impl->devices.begin (), impl->devices.end (), - [dev] (const auto& el) { return el->get () == dev; }); - if (it != impl->devices.end ()) - return *it; - return nullptr; -} - -//------------------------------------------------------------------------ -void D2DGraphicsDeviceFactory::addDevice (const std::shared_ptr& device) const -{ - impl->devices.push_back (device); -} - -//------------------------------------------------------------------------ -void D2DGraphicsDeviceFactory::removeDevice (const std::shared_ptr& device) const -{ - auto it = std::find (impl->devices.begin (), impl->devices.end (), device); - if (it != impl->devices.end ()) - impl->devices.erase (it); -} - -//------------------------------------------------------------------------ -struct D2DGraphicsDevice::Impl -{ - COM::Ptr device; -}; - -//------------------------------------------------------------------------ -D2DGraphicsDevice::D2DGraphicsDevice (ID2D1Device* device) -{ - impl = std::make_unique (); - impl->device = COM::share (device); -} - -//------------------------------------------------------------------------ -D2DGraphicsDevice::~D2DGraphicsDevice () noexcept {} - -//------------------------------------------------------------------------ -PlatformGraphicsDeviceContextPtr - D2DGraphicsDevice::createBitmapContext (const PlatformBitmapPtr& bitmap) const -{ - auto d2dBitmap = bitmap.cast (); - if (d2dBitmap) - { - COM::Ptr factory; - impl->device->GetFactory (factory.adoptPtr ()); - - COM::Ptr renderTarget; - D2D1_RENDER_TARGET_TYPE targetType = D2D1_RENDER_TARGET_TYPE_SOFTWARE; - D2D1_PIXEL_FORMAT pixelFormat = - D2D1::PixelFormat (DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED); - auto hr = factory->CreateWicBitmapRenderTarget ( - d2dBitmap->getBitmap (), D2D1::RenderTargetProperties (targetType, pixelFormat), - renderTarget.adoptPtr ()); - - if (FAILED (hr)) - return nullptr; - - COM::Ptr deviceContext; - hr = renderTarget->QueryInterface (__uuidof (ID2D1DeviceContext), - reinterpret_cast (deviceContext.adoptPtr ())); - if (FAILED (hr)) - return nullptr; - - D2DBitmapCache::removeBitmap (d2dBitmap); - - TransformMatrix tm; - tm.scale (d2dBitmap->getScaleFactor (), d2dBitmap->getScaleFactor ()); - deviceContext->SetTransform (convert (tm)); - - return std::make_shared (*this, deviceContext.get (), - TransformMatrix {}); - } - return nullptr; -} - -//------------------------------------------------------------------------ -ID2D1Device* D2DGraphicsDevice::get () const { return impl->device.get (); } - -//------------------------------------------------------------------------ -struct D2DGraphicsDeviceContext::Impl -{ - Impl (const D2DGraphicsDevice& device, ID2D1DeviceContext* deviceContext, - const TransformMatrix& tm) - : device (device), deviceContext (COM::share (deviceContext)), globalTM (tm) - { - } - - template - void doInContext (Proc p, CPoint transformOffset = {}) - { - if (state.clip.isEmpty ()) - return; - - TransformGuard tmGuard (deviceContext.get ()); - - auto transform = convert (tmGuard.matrix) * globalTM * state.tm; - transform.scale (scaleFactor, scaleFactor); - transform.translate (transformOffset); - bool useLayer = transform.m12 != 0. || transform.m21 != 0.; - if (useLayer) - { // we have a rotated matrix, we need to use a layer - COM::Ptr factory {}; - deviceContext->GetFactory (factory.adoptPtr ()); - COM::Ptr geometry; - if (SUCCEEDED ( - factory->CreateRectangleGeometry (convert (state.clip), geometry.adoptPtr ()))) - { - if (applyClip.isEmpty () == false) - deviceContext->PopAxisAlignedClip (); - deviceContext->PushLayer (D2D1::LayerParameters (D2D1::InfiniteRect (), - geometry.get (), - D2D1_ANTIALIAS_MODE_ALIASED), - nullptr); - geometry->Release (); - applyClip = state.clip; - } - else - { - useLayer = false; - } - } - if (!useLayer) - { - auto newClip = state.clip; - globalTM.transform (newClip); - if (applyClip != newClip) - { - if (applyClip.isEmpty () == false) - deviceContext->PopAxisAlignedClip (); - if (newClip.isEmpty () == false) - deviceContext->PushAxisAlignedClip (convert (newClip), - D2D1_ANTIALIAS_MODE_ALIASED); - applyClip = newClip; - } - } - deviceContext->SetTransform (convert (transform)); - - p (deviceContext.get ()); - - if (useLayer) - deviceContext->PopLayer (); - } - - //----------------------------------------------------------------------------- - void applyFrameColor () - { - if (state.frameBrush) - return; - deviceContext->CreateSolidColorBrush (convert (state.frameColor, state.globalAlpha), - state.frameBrush.adoptPtr ()); - } - - //----------------------------------------------------------------------------- - void applyFillColor () - { - if (state.fillBrush) - return; - deviceContext->CreateSolidColorBrush (convert (state.fillColor, state.globalAlpha), - state.fillBrush.adoptPtr ()); - } - - //----------------------------------------------------------------------------- - void applyFontColor (CColor color) - { - if (state.fontColor != color) - state.fontBrush.reset (); - if (state.fontBrush) - return; - state.fontColor = color; - deviceContext->CreateSolidColorBrush (convert (state.fontColor, state.globalAlpha), - state.fontBrush.adoptPtr ()); - } - - //----------------------------------------------------------------------------- - CPoint lineWidthTransformMatrixOffset () const - { - int32_t lineWidthInt = static_cast (state.lineWidth); - if (static_cast (lineWidthInt) == state.lineWidth && lineWidthInt % 2) - { - return {0.5, 0.5}; - } - return {}; - } - - //----------------------------------------------------------------------------- - void applyLineStyle () - { - if (state.strokeStyle) - return; - - COM::Ptr factory {}; - deviceContext->GetFactory (factory.adoptPtr ()); - - D2D1_STROKE_STYLE_PROPERTIES properties; - switch (state.lineStyle.getLineCap ()) - { - case CLineStyle::kLineCapButt: - properties.startCap = properties.endCap = properties.dashCap = D2D1_CAP_STYLE_FLAT; - break; - case CLineStyle::kLineCapRound: - properties.startCap = properties.endCap = properties.dashCap = D2D1_CAP_STYLE_ROUND; - break; - case CLineStyle::kLineCapSquare: - properties.startCap = properties.endCap = properties.dashCap = - D2D1_CAP_STYLE_SQUARE; - break; - } - switch (state.lineStyle.getLineJoin ()) - { - case CLineStyle::kLineJoinMiter: - properties.lineJoin = D2D1_LINE_JOIN_MITER; - break; - case CLineStyle::kLineJoinRound: - properties.lineJoin = D2D1_LINE_JOIN_ROUND; - break; - case CLineStyle::kLineJoinBevel: - properties.lineJoin = D2D1_LINE_JOIN_BEVEL; - break; - } - properties.dashOffset = static_cast (state.lineStyle.getDashPhase ()); - properties.miterLimit = 10.f; - if (state.lineStyle.getDashCount ()) - { - properties.dashStyle = D2D1_DASH_STYLE_CUSTOM; - FLOAT* lengths = new FLOAT[state.lineStyle.getDashCount ()]; - for (uint32_t i = 0; i < state.lineStyle.getDashCount (); i++) - lengths[i] = static_cast (state.lineStyle.getDashLengths ()[i]); - factory->CreateStrokeStyle (properties, lengths, state.lineStyle.getDashCount (), - state.strokeStyle.adoptPtr ()); - delete[] lengths; - } - else - { - properties.dashStyle = D2D1_DASH_STYLE_SOLID; - factory->CreateStrokeStyle (properties, nullptr, 0, state.strokeStyle.adoptPtr ()); - } - } - - struct State - { - CRect clip {}; - CLineStyle lineStyle {kLineSolid}; - CDrawMode drawMode {}; - COM::Ptr strokeStyle; - COM::Ptr fillBrush; - COM::Ptr frameBrush; - COM::Ptr fontBrush; - CColor fillColor {kTransparentCColor}; - CColor frameColor {kTransparentCColor}; - CColor fontColor {kTransparentCColor}; - CCoord lineWidth {1.}; - double globalAlpha {1.}; - TransformMatrix tm {}; - }; - State state; - std::stack stateStack; - double scaleFactor {1.}; - CRect applyClip {}; - bool beginDrawCalled {false}; - TransformMatrix globalTM; - - const D2DGraphicsDevice& device; - COM::Ptr deviceContext; -}; - -//------------------------------------------------------------------------ -D2DGraphicsDeviceContext::D2DGraphicsDeviceContext (const D2DGraphicsDevice& device, - ID2D1DeviceContext* deviceContext, - const TransformMatrix& tm) -{ - impl = std::make_unique (device, deviceContext, tm); -} - -//------------------------------------------------------------------------ -D2DGraphicsDeviceContext::~D2DGraphicsDeviceContext () noexcept { endDraw (); } - -//------------------------------------------------------------------------ -ID2D1DeviceContext* D2DGraphicsDeviceContext::getID2D1DeviceContext () const -{ - return impl->deviceContext.get (); -} - -//------------------------------------------------------------------------ -const IPlatformGraphicsDevice& D2DGraphicsDeviceContext::getDevice () const { return impl->device; } - -//------------------------------------------------------------------------ -PlatformGraphicsPathFactoryPtr D2DGraphicsDeviceContext::getGraphicsPathFactory () const -{ - return D2DGraphicsPathFactory::instance (); -} - -//------------------------------------------------------------------------ -bool D2DGraphicsDeviceContext::beginDraw () const -{ - impl->beginDrawCalled = true; - impl->deviceContext->BeginDraw (); - return true; -} - -//------------------------------------------------------------------------ -bool D2DGraphicsDeviceContext::endDraw () const -{ - if (impl->applyClip.isEmpty () == false) - impl->deviceContext->PopAxisAlignedClip (); - impl->applyClip = {}; - if (impl->beginDrawCalled) - { - auto hr = impl->deviceContext->EndDraw (); - vstgui_assert (SUCCEEDED (hr)); - impl->beginDrawCalled = false; - } - return true; -} - -//------------------------------------------------------------------------ -bool D2DGraphicsDeviceContext::drawLine (LinePair line) const -{ - impl->doInContext ( - [&] (auto deviceContext) { - impl->applyFrameColor (); - impl->applyLineStyle (); - - CPoint start (line.first); - CPoint end (line.second); - if (impl->state.drawMode.integralMode ()) - { - pixelAlign (impl->state.tm, start); - pixelAlign (impl->state.tm, end); - } - deviceContext->DrawLine (convert (start), convert (end), impl->state.frameBrush.get (), - static_cast (impl->state.lineWidth), - impl->state.strokeStyle.get ()); - }, - impl->lineWidthTransformMatrixOffset ()); - return true; -} - -//------------------------------------------------------------------------ -bool D2DGraphicsDeviceContext::drawLines (const LineList& lines) const -{ - impl->doInContext ( - [&] (auto deviceContext) { - impl->applyFrameColor (); - impl->applyLineStyle (); - - if (impl->state.drawMode.integralMode ()) - { - for (const auto& line : lines) - { - CPoint start (line.first); - CPoint end (line.second); - pixelAlign (impl->state.tm, start); - pixelAlign (impl->state.tm, end); - deviceContext->DrawLine ( - convert (start), convert (end), impl->state.frameBrush.get (), - static_cast (impl->state.lineWidth), impl->state.strokeStyle.get ()); - } - } - else - { - for (const auto& line : lines) - { - CPoint start (line.first); - CPoint end (line.second); - deviceContext->DrawLine ( - convert (start), convert (end), impl->state.frameBrush.get (), - static_cast (impl->state.lineWidth), impl->state.strokeStyle.get ()); - } - } - }, - impl->lineWidthTransformMatrixOffset ()); - return true; -} - -//------------------------------------------------------------------------ -bool D2DGraphicsDeviceContext::drawPolygon (const PointList& polygonPointList, - PlatformGraphicsDrawStyle drawStyle) const -{ - auto path = getGraphicsPathFactory ()->createPath (); - if (!path) - return false; - - path->beginSubpath (polygonPointList[0]); - for (uint32_t i = 1; i < polygonPointList.size (); ++i) - { - path->addLine (polygonPointList[i]); - } - if (drawStyle == PlatformGraphicsDrawStyle::Filled || - drawStyle == PlatformGraphicsDrawStyle::FilledAndStroked) - { - drawGraphicsPath (*path, PlatformGraphicsPathDrawMode::Filled, nullptr); - } - if (drawStyle == PlatformGraphicsDrawStyle::Stroked || - drawStyle == PlatformGraphicsDrawStyle::FilledAndStroked) - { - drawGraphicsPath (*path, PlatformGraphicsPathDrawMode::Stroked, nullptr); - } - return true; -} - -//------------------------------------------------------------------------ -bool D2DGraphicsDeviceContext::drawRect (CRect rect, PlatformGraphicsDrawStyle drawStyle) const -{ - impl->doInContext ([&] (auto deviceContext) { - if (drawStyle != PlatformGraphicsDrawStyle::Filled) - { - rect.right -= 1.; - rect.bottom -= 1.; - } - if (drawStyle == PlatformGraphicsDrawStyle::Filled || - drawStyle == PlatformGraphicsDrawStyle::FilledAndStroked) - { - impl->applyFillColor (); - deviceContext->FillRectangle (convert (rect), impl->state.fillBrush.get ()); - } - if (drawStyle == PlatformGraphicsDrawStyle::Stroked || - drawStyle == PlatformGraphicsDrawStyle::FilledAndStroked) - { - rect.offset (impl->lineWidthTransformMatrixOffset ()); - impl->applyFrameColor (); - impl->applyLineStyle (); - deviceContext->DrawRectangle (convert (rect), impl->state.frameBrush.get (), - static_cast (impl->state.lineWidth), - impl->state.strokeStyle.get ()); - } - }); - return true; -} - -//------------------------------------------------------------------------ -bool D2DGraphicsDeviceContext::drawArc (CRect rect, double startAngle1, double endAngle2, - PlatformGraphicsDrawStyle drawStyle) const -{ - auto path = getGraphicsPathFactory ()->createPath (); - if (!path) - return false; - if (impl->state.drawMode.integralMode ()) - pixelAlign (impl->state.tm, rect); - path->addArc (rect, startAngle1, endAngle2, true); - if (drawStyle == PlatformGraphicsDrawStyle::Filled || - drawStyle == PlatformGraphicsDrawStyle::FilledAndStroked) - drawGraphicsPath (*path, PlatformGraphicsPathDrawMode::Filled, nullptr); - if (drawStyle == PlatformGraphicsDrawStyle::Stroked || - drawStyle == PlatformGraphicsDrawStyle::FilledAndStroked) - drawGraphicsPath (*path, PlatformGraphicsPathDrawMode::Stroked, nullptr); - return true; -} - -//------------------------------------------------------------------------ -bool D2DGraphicsDeviceContext::drawEllipse (CRect rect, PlatformGraphicsDrawStyle drawStyle) const -{ - impl->doInContext ([&] (auto deviceContext) { - if (impl->state.drawMode.integralMode ()) - pixelAlign (impl->state.tm, rect); - if (drawStyle == PlatformGraphicsDrawStyle::Stroked) - rect.inset (0.5, 0.5); - CPoint center (rect.getTopLeft ()); - center.offset (rect.getWidth () / 2., rect.getHeight () / 2.); - D2D1_ELLIPSE ellipse; - ellipse.point = convert (center); - ellipse.radiusX = (FLOAT)(rect.getWidth () / 2.); - ellipse.radiusY = (FLOAT)(rect.getHeight () / 2.); - if (drawStyle == PlatformGraphicsDrawStyle::Filled || - drawStyle == PlatformGraphicsDrawStyle::FilledAndStroked) - { - impl->applyFillColor (); - deviceContext->FillEllipse (ellipse, impl->state.fillBrush.get ()); - } - if (drawStyle == PlatformGraphicsDrawStyle::Stroked || - drawStyle == PlatformGraphicsDrawStyle::FilledAndStroked) - { - impl->applyFrameColor (); - impl->applyLineStyle (); - deviceContext->DrawEllipse (ellipse, impl->state.frameBrush.get (), - static_cast (impl->state.lineWidth), - impl->state.strokeStyle.get ()); - } - }); - return true; -} - -//------------------------------------------------------------------------ -bool D2DGraphicsDeviceContext::drawPoint (CPoint point, CColor color) const { return false; } - -//------------------------------------------------------------------------ -bool D2DGraphicsDeviceContext::drawBitmap (IPlatformBitmap& bitmap, CRect dest, CPoint offset, - double alpha, BitmapInterpolationQuality quality) const -{ - D2DBitmap* d2dBitmap = dynamic_cast (&bitmap); - if (!d2dBitmap || !d2dBitmap->getSource ()) - return false; - auto d2d1Bitmap = - D2DBitmapCache::getBitmap (d2dBitmap, impl->deviceContext.get (), impl->device.get ()); - if (!d2d1Bitmap) - return false; - - double bitmapScaleFactor = d2dBitmap->getScaleFactor (); - CGraphicsTransform bitmapTransform; - bitmapTransform.scale (1. / bitmapScaleFactor, 1. / bitmapScaleFactor); - auto originalTransformMatrix = impl->state.tm; - TransformMatrix tm = originalTransformMatrix * bitmapTransform; - setTransformMatrix (tm); - bitmapTransform.inverse ().transform (dest); - - impl->doInContext ([&] (auto deviceContext) { - auto bitmapSize = bitmap.getSize (); - CRect d (dest); - d.setWidth (bitmapSize.x); - d.setHeight (bitmapSize.y); - d.offset (-offset.x, -offset.y); - d.makeIntegral (); - CRect source; - source.setWidth (d2d1Bitmap->GetSize ().width); - source.setHeight (d2d1Bitmap->GetSize ().height); - - D2D1_BITMAP_INTERPOLATION_MODE mode; - switch (quality) - { - case BitmapInterpolationQuality::kLow: - mode = D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; - break; - - case BitmapInterpolationQuality::kMedium: - case BitmapInterpolationQuality::kHigh: - default: - mode = D2D1_BITMAP_INTERPOLATION_MODE_LINEAR; - break; - } - - D2D1_RECT_F sourceRect = convert (source); - deviceContext->DrawBitmap (d2d1Bitmap, convert (d), - static_cast (alpha * impl->state.globalAlpha), mode, - &sourceRect); - }); - setTransformMatrix (originalTransformMatrix); - return true; -} - -//------------------------------------------------------------------------ -bool D2DGraphicsDeviceContext::clearRect (CRect rect) const -{ -#if 1 - TransformGuard tmGuard (impl->deviceContext.get ()); - - impl->deviceContext->SetTransform (convert (impl->globalTM * impl->state.tm)); - impl->deviceContext->PushAxisAlignedClip (convert (rect), D2D1_ANTIALIAS_MODE_ALIASED); - impl->deviceContext->Clear (D2D1::ColorF (1.f, 1.f, 1.f, 0.f)); - impl->deviceContext->PopAxisAlignedClip (); -#else - CRect oldClip = impl->state.clip; - rect.bound (oldClip); - setClipRect (rect); - impl->doInContext ( - [] (auto deviceContext) { deviceContext->Clear (D2D1::ColorF (1.f, 1.f, 1.f, 0.f)); }); - setClipRect (oldClip); -#endif - return true; -} - -//------------------------------------------------------------------------ -bool D2DGraphicsDeviceContext::drawGraphicsPath (IPlatformGraphicsPath& path, - PlatformGraphicsPathDrawMode mode, - TransformMatrix* transformation) const -{ - auto d2dPath = dynamic_cast (&path); - if (d2dPath == nullptr) - return false; - - impl->doInContext ([&] (auto deviceContext) { - COM::Ptr path; - if (transformation) - { - COM::Ptr factory {}; - deviceContext->GetFactory (factory.adoptPtr ()); - path = COM::adopt ( - d2dPath->createTransformedGeometry (factory.get (), *transformation)); - } - else - { - path = COM::share (d2dPath->getPathGeometry ()); - } - if (path) - { - if (mode == PlatformGraphicsPathDrawMode::Filled || - mode == PlatformGraphicsPathDrawMode::FilledEvenOdd) - { - impl->applyFillColor (); - deviceContext->FillGeometry (path.get (), impl->state.fillBrush.get ()); - } - else if (mode == PlatformGraphicsPathDrawMode::Stroked) - { - impl->applyFrameColor (); - impl->applyLineStyle (); - deviceContext->DrawGeometry (path.get (), impl->state.frameBrush.get (), - (FLOAT)impl->state.lineWidth, - impl->state.strokeStyle.get ()); - } - } - }); - return true; -} - -//------------------------------------------------------------------------ -bool D2DGraphicsDeviceContext::fillLinearGradient (IPlatformGraphicsPath& path, - const IPlatformGradient& gradient, - CPoint startPoint, CPoint endPoint, bool evenOdd, - TransformMatrix* transformation) const -{ - auto d2dPath = dynamic_cast (&path); - const auto d2dGradient = dynamic_cast (&gradient); - if (d2dPath == nullptr || d2dGradient == nullptr) - return false; - impl->doInContext ([&] (auto deviceContext) { - auto stopCollection = COM::adopt ( - d2dGradient->create (deviceContext, static_cast (impl->state.globalAlpha))); - if (!stopCollection) - return; - COM::Ptr path; - if (transformation) - { - COM::Ptr factory {}; - deviceContext->GetFactory (factory.adoptPtr ()); - path = COM::adopt ( - d2dPath->createTransformedGeometry (factory.get (), *transformation)); - } - else - { - path = COM::share (d2dPath->getPathGeometry ()); - } - if (path) - { - COM::Ptr brush; - D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES properties; - properties.startPoint = convert (startPoint); - properties.endPoint = convert (endPoint); - if (SUCCEEDED (deviceContext->CreateLinearGradientBrush ( - properties, stopCollection.get (), brush.adoptPtr ()))) - { - deviceContext->FillGeometry (path.get (), brush.get ()); - } - } - }); - return true; -} - -//------------------------------------------------------------------------ -bool D2DGraphicsDeviceContext::fillRadialGradient (IPlatformGraphicsPath& path, - const IPlatformGradient& gradient, CPoint center, - CCoord radius, CPoint originOffset, bool evenOdd, - TransformMatrix* transformation) const -{ - auto d2dPath = dynamic_cast (&path); - const auto d2dGradient = dynamic_cast (&gradient); - if (d2dPath == nullptr || d2dGradient == nullptr) - return false; - impl->doInContext ([&] (auto deviceContext) { - auto stopCollection = COM::adopt ( - d2dGradient->create (deviceContext, static_cast (impl->state.globalAlpha))); - if (!stopCollection) - return; - COM::Ptr path; - if (transformation) - { - COM::Ptr factory {}; - deviceContext->GetFactory (factory.adoptPtr ()); - path = COM::adopt ( - d2dPath->createTransformedGeometry (factory.get (), *transformation)); - } - else - { - path = COM::share (d2dPath->getPathGeometry ()); - } - if (path) - { - COM::Ptr brush; - D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES properties; - properties.center = convert (center); - properties.gradientOriginOffset = convert (originOffset); - properties.radiusX = (FLOAT)radius; - properties.radiusY = (FLOAT)radius; - if (SUCCEEDED (deviceContext->CreateRadialGradientBrush ( - properties, stopCollection.get (), brush.adoptPtr ()))) - { - deviceContext->FillGeometry (path.get (), brush.get ()); - } - } - }); - return true; -} - -//------------------------------------------------------------------------ -void D2DGraphicsDeviceContext::saveGlobalState () const { impl->stateStack.push (impl->state); } - -//------------------------------------------------------------------------ -void D2DGraphicsDeviceContext::restoreGlobalState () const -{ - vstgui_assert (impl->stateStack.empty () == false, - "Unbalanced calls to saveGlobalState and restoreGlobalState"); -#if NDEBUG - if (impl->stateStack.empty ()) - return; -#endif - impl->state = impl->stateStack.top (); - impl->stateStack.pop (); -} - -//------------------------------------------------------------------------ -void D2DGraphicsDeviceContext::setLineStyle (const CLineStyle& style) const -{ - if (impl->state.lineStyle != style) - { - impl->state.lineStyle = style; - impl->state.strokeStyle.reset (); - } -} - -//------------------------------------------------------------------------ -void D2DGraphicsDeviceContext::setLineWidth (CCoord width) const { impl->state.lineWidth = width; } - -//------------------------------------------------------------------------ -void D2DGraphicsDeviceContext::setDrawMode (CDrawMode mode) const { impl->state.drawMode = mode; } - -//------------------------------------------------------------------------ -void D2DGraphicsDeviceContext::setClipRect (CRect clip) const { impl->state.clip = clip; } - -//------------------------------------------------------------------------ -void D2DGraphicsDeviceContext::setFillColor (CColor color) const -{ - if (impl->state.fillColor != color) - { - impl->state.fillColor = color; - impl->state.fillBrush.reset (); - } -} - -//------------------------------------------------------------------------ -void D2DGraphicsDeviceContext::setFrameColor (CColor color) const -{ - if (impl->state.frameColor != color) - { - impl->state.frameColor = color; - impl->state.frameBrush.reset (); - } -} - -//------------------------------------------------------------------------ -void D2DGraphicsDeviceContext::setGlobalAlpha (double newAlpha) const -{ - if (impl->state.globalAlpha != newAlpha) - { - impl->state.globalAlpha = newAlpha; - impl->state.fillBrush.reset (); - impl->state.frameBrush.reset (); - impl->state.fontBrush.reset (); - } -} - -//------------------------------------------------------------------------ -void D2DGraphicsDeviceContext::setTransformMatrix (const TransformMatrix& tm) const -{ - impl->state.tm = tm; -} - -//------------------------------------------------------------------------ -const IPlatformGraphicsDeviceContextBitmapExt* D2DGraphicsDeviceContext::asBitmapExt () const -{ - return nullptr; -} - -//------------------------------------------------------------------------ -void D2DGraphicsDeviceContext::drawTextLayout (IDWriteTextLayout* textLayout, CPoint pos, - CColor color, bool antialias) -{ - impl->doInContext ([&] (auto deviceContext) { - deviceContext->SetTextAntialiasMode (antialias ? D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE - : D2D1_TEXT_ANTIALIAS_MODE_ALIASED); - if (impl->state.drawMode.integralMode ()) - pos.makeIntegral (); - pos.y += 0.5; - impl->applyFontColor (color); - deviceContext->DrawTextLayout (convert (pos), textLayout, impl->state.fontBrush.get ()); - }); -} - -//------------------------------------------------------------------------ -} // VSTGUI diff --git a/vstgui/lib/platform/win32/direct2d/d2dgraphicscontext.h b/vstgui/lib/platform/win32/direct2d/d2dgraphicscontext.h deleted file mode 100644 index c9fabfe49..000000000 --- a/vstgui/lib/platform/win32/direct2d/d2dgraphicscontext.h +++ /dev/null @@ -1,116 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#pragma once - -#include "../../iplatformgraphicsdevice.h" - -struct ID2D1DeviceContext; -struct ID2D1Device; -struct ID2D1SolidColorBrush; -struct IDWriteTextLayout; - -//------------------------------------------------------------------------ -namespace VSTGUI { - -class D2DGraphicsDevice; - -//------------------------------------------------------------------------ -class D2DGraphicsDeviceContext : public IPlatformGraphicsDeviceContext -{ -public: - D2DGraphicsDeviceContext (const D2DGraphicsDevice& device, ID2D1DeviceContext* deviceContext, - const TransformMatrix& tm); - ~D2DGraphicsDeviceContext () noexcept; - - const IPlatformGraphicsDevice& getDevice () const override; - PlatformGraphicsPathFactoryPtr getGraphicsPathFactory () const override; - - bool beginDraw () const override; - bool endDraw () const override; - // draw commands - bool drawLine (LinePair line) const override; - bool drawLines (const LineList& lines) const override; - bool drawPolygon (const PointList& polygonPointList, - PlatformGraphicsDrawStyle drawStyle) const override; - bool drawRect (CRect rect, PlatformGraphicsDrawStyle drawStyle) const override; - bool drawArc (CRect rect, double startAngle1, double endAngle2, - PlatformGraphicsDrawStyle drawStyle) const override; - bool drawEllipse (CRect rect, PlatformGraphicsDrawStyle drawStyle) const override; - bool drawPoint (CPoint point, CColor color) const override; - bool drawBitmap (IPlatformBitmap& bitmap, CRect dest, CPoint offset, double alpha, - BitmapInterpolationQuality quality) const override; - bool clearRect (CRect rect) const override; - bool drawGraphicsPath (IPlatformGraphicsPath& path, PlatformGraphicsPathDrawMode mode, - TransformMatrix* transformation) const override; - bool fillLinearGradient (IPlatformGraphicsPath& path, const IPlatformGradient& gradient, - CPoint startPoint, CPoint endPoint, bool evenOdd, - TransformMatrix* transformation) const override; - bool fillRadialGradient (IPlatformGraphicsPath& path, const IPlatformGradient& gradient, - CPoint center, CCoord radius, CPoint originOffset, bool evenOdd, - TransformMatrix* transformation) const override; - // state - void saveGlobalState () const override; - void restoreGlobalState () const override; - void setLineStyle (const CLineStyle& style) const override; - void setLineWidth (CCoord width) const override; - void setDrawMode (CDrawMode mode) const override; - void setClipRect (CRect clip) const override; - void setFillColor (CColor color) const override; - void setFrameColor (CColor color) const override; - void setGlobalAlpha (double newAlpha) const override; - void setTransformMatrix (const TransformMatrix& tm) const override; - - // extension - const IPlatformGraphicsDeviceContextBitmapExt* asBitmapExt () const override; - - // private - void drawTextLayout (IDWriteTextLayout* textLayout, CPoint pos, CColor color, bool antialias); - - ID2D1DeviceContext* getID2D1DeviceContext () const; - -protected: -private: - struct Impl; - std::unique_ptr impl; -}; - -//------------------------------------------------------------------------ -class D2DGraphicsDevice : public IPlatformGraphicsDevice -{ -public: - D2DGraphicsDevice (ID2D1Device* device); - ~D2DGraphicsDevice () noexcept; - - PlatformGraphicsDeviceContextPtr - createBitmapContext (const PlatformBitmapPtr& bitmap) const override; - - ID2D1Device* get () const; - -private: - struct Impl; - std::unique_ptr impl; -}; - -//------------------------------------------------------------------------ -class D2DGraphicsDeviceFactory : public IPlatformGraphicsDeviceFactory -{ -public: - D2DGraphicsDeviceFactory (); - ~D2DGraphicsDeviceFactory () noexcept; - - PlatformGraphicsDevicePtr getDeviceForScreen (ScreenInfo::Identifier screen) const override; - - PlatformGraphicsDevicePtr find (ID2D1Device* dev) const; - - void addDevice (const std::shared_ptr& device) const; - void removeDevice (const std::shared_ptr& device) const; - -private: - struct Impl; - std::unique_ptr impl; -}; - -//------------------------------------------------------------------------ -} // VSTGUI diff --git a/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.cpp b/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.cpp index 48088f16a..5349668a6 100644 --- a/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.cpp +++ b/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.cpp @@ -9,8 +9,8 @@ #include "../../../cgradient.h" #include "../../../cstring.h" #include "../win32support.h" +#include "d2ddrawcontext.h" #include "d2dfont.h" -#include "d2d.h" #include #include #include @@ -103,6 +103,132 @@ class D2DPathTextRenderer final : public IDWriteTextRenderer ID2D1GeometrySink* sink; }; +//----------------------------------------------------------------------------- +class AlignPixelSink final : public ID2D1SimplifiedGeometrySink +{ +public: + HRESULT STDMETHODCALLTYPE QueryInterface (REFIID iid, void** ppvObject) override + { + if (iid == __uuidof(IUnknown) || iid == __uuidof(ID2D1SimplifiedGeometrySink)) + { + *ppvObject = static_cast (this); + AddRef (); + return S_OK; + } + else + return E_NOINTERFACE; + } + ULONG STDMETHODCALLTYPE AddRef () override { return 1; } + ULONG STDMETHODCALLTYPE Release () override { return 1; } + + D2D1_POINT_2F alignPoint (const D2D1_POINT_2F& p) + { + CPoint point (p.x, p.y); + if (context->getDrawMode ().antiAliasing ()) + point.offset (-0.5, -0.5); + if (context) + context->pixelAllign (point); + return D2D1::Point2F (static_cast (point.x), static_cast (point.y)); + } + + STDMETHOD_ (void, AddBeziers) (const D2D1_BEZIER_SEGMENT* beziers, UINT beziersCount) override + { + for (UINT i = 0; i < beziersCount; ++i) + { + D2D1_BEZIER_SEGMENT segment = {}; + segment.point1 = alignPoint (beziers[i].point1); + segment.point2 = alignPoint (beziers[i].point2); + segment.point3 = alignPoint (beziers[i].point3); + sink->AddBezier (segment); + } + } + + STDMETHOD_ (void, AddLines) (const D2D1_POINT_2F* points, UINT pointsCount) override + { + for (UINT i = 0; i < pointsCount; ++i) + { + D2D_POINT_2F point = alignPoint (points[i]); + sink->AddLine (point); + } + } + + STDMETHOD_ (void, BeginFigure) + (D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) override + { + startPoint = alignPoint (startPoint); + sink->BeginFigure (startPoint, figureBegin); + } + + STDMETHOD_ (void, EndFigure) (D2D1_FIGURE_END figureEnd) override + { + sink->EndFigure (figureEnd); + } + + STDMETHOD_ (void, SetFillMode) (D2D1_FILL_MODE fillMode) override + { + sink->SetFillMode (fillMode); + } + + STDMETHOD_ (void, SetSegmentFlags) (D2D1_PATH_SEGMENT vertexFlags) override + { + sink->SetSegmentFlags (vertexFlags); + } + + STDMETHOD (Close) () override + { + isClosed = true; + return sink->Close (); + } + + AlignPixelSink (D2DDrawContext* context) + : path (nullptr), sink (nullptr), context (context), isClosed (true) + { + } + + ~AlignPixelSink () + { + if (sink) + sink->Release (); + if (path) + path->Release (); + } + + bool init () + { + getD2DFactory ()->CreatePathGeometry (&path); + if (path == nullptr) + return false; + if (!SUCCEEDED (path->Open (&sink))) + return false; + isClosed = false; + return true; + } + + ID2D1PathGeometry* get () + { + if (path) + { + if (sink) + { + if (!isClosed) + sink->Close (); + sink->Release (); + sink = nullptr; + } + ID2D1PathGeometry* result = path; + path = nullptr; + return result; + } + return nullptr; + } + +private: + ID2D1PathGeometry* path; + ID2D1GeometrySink* sink; + D2DDrawContext* context; + bool isClosed; +}; + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -130,6 +256,33 @@ ID2D1Geometry* D2DGraphicsPath::createTransformedGeometry (ID2D1Factory* factory ; } +//----------------------------------------------------------------------------- +ID2D1Geometry* D2DGraphicsPath::createPixelAlignedGeometry (ID2D1Factory* factory, + D2DDrawContext& context, + const CGraphicsTransform* tm) const +{ + ID2D1Geometry* workingPath = path; + workingPath->AddRef (); + if (tm) + workingPath = createTransformedGeometry (factory, *tm); + + AlignPixelSink alignSink (&context); + if (alignSink.init () == false) + { + workingPath->Release (); + return nullptr; + } + if (!SUCCEEDED (workingPath->Simplify (D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES, + nullptr, &alignSink))) + { + workingPath->Release (); + return nullptr; + } + workingPath->Release (); + + return alignSink.get (); +} + //----------------------------------------------------------------------------- ID2D1GeometrySink* D2DGraphicsPath::getSink () { @@ -171,12 +324,12 @@ void D2DGraphicsPath::addArc (const CRect& rect, double startAngle, double endAn start.y = r.top + center.y + center.y * sin (startAngle); if (!figureOpen) { - sink->BeginFigure (convert (start), D2D1_FIGURE_BEGIN_FILLED); + sink->BeginFigure (makeD2DPoint (start), D2D1_FIGURE_BEGIN_FILLED); figureOpen = true; } else if (lastPos != start) { - sink->AddLine (convert (start)); + sink->AddLine (makeD2DPoint (start)); } double sweepangle = endAngle - startAngle; @@ -202,12 +355,11 @@ void D2DGraphicsPath::addArc (const CRect& rect, double startAngle, double endAn endPoint.y = r.top + center.y + center.y * sin (endAngle); D2D1_ARC_SEGMENT arc; - arc.size = {static_cast (r.getWidth () / 2.), - static_cast (r.getHeight () / 2.)}; + arc.size = makeD2DSize (r.getWidth () / 2., r.getHeight () / 2.); arc.rotationAngle = 0; arc.sweepDirection = clockwise ? D2D1_SWEEP_DIRECTION_CLOCKWISE : D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE; - arc.point = convert (endPoint); + arc.point = makeD2DPoint (endPoint); arc.arcSize = fabs (sweepangle) <= M_PI ? D2D1_ARC_SIZE_SMALL : D2D1_ARC_SIZE_LARGE; sink->AddArc (arc); lastPos = endPoint; @@ -230,16 +382,16 @@ void D2DGraphicsPath::addEllipse (const CRect& rect) } if (!figureOpen) { - sink->BeginFigure (convert (top), D2D1_FIGURE_BEGIN_FILLED); + sink->BeginFigure (makeD2DPoint (top), D2D1_FIGURE_BEGIN_FILLED); figureOpen = true; } D2D1_ARC_SEGMENT arc = - D2D1::ArcSegment (convert (bottom), - D2D1::SizeF (static_cast (rect.getWidth () / 2.f), - static_cast (rect.getHeight () / 2.f)), - 180.f, D2D1_SWEEP_DIRECTION_CLOCKWISE, D2D1_ARC_SIZE_SMALL); + D2D1::ArcSegment (makeD2DPoint (bottom), + D2D1::SizeF (static_cast (rect.getWidth () / 2.f), + static_cast (rect.getHeight () / 2.f)), + 180.f, D2D1_SWEEP_DIRECTION_CLOCKWISE, D2D1_ARC_SIZE_SMALL); sink->AddArc (arc); - arc.point = convert (top); + arc.point = makeD2DPoint (top); sink->AddArc (arc); lastPos = top; } @@ -361,7 +513,7 @@ bool D2DGraphicsPath::hitTest (const CPoint& p, bool evenOddFilled, matrix._32 = (FLOAT)transform->dy; } BOOL result = false; - path->FillContainsPoint (convert (p), matrix, &result); + path->FillContainsPoint (makeD2DPoint (p), matrix, &result); return result ? true : false; } diff --git a/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.h b/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.h index 1149b1369..fb67ccd52 100644 --- a/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.h +++ b/vstgui/lib/platform/win32/direct2d/d2dgraphicspath.h @@ -42,6 +42,8 @@ class D2DGraphicsPath : public IPlatformGraphicsPath ID2D1PathGeometry* getPathGeometry () const { return path; } ID2D1Geometry* createTransformedGeometry (ID2D1Factory* factory, const CGraphicsTransform& tm) const; + ID2D1Geometry* createPixelAlignedGeometry (ID2D1Factory* factory, D2DDrawContext& context, + const CGraphicsTransform* tm = nullptr) const; // IPlatformGraphicsPath void addArc (const CRect& rect, double startAngle, double endAngle, bool clockwise) override; diff --git a/vstgui/lib/platform/win32/win32directcomposition.cpp b/vstgui/lib/platform/win32/win32directcomposition.cpp index cef864410..75d47cb63 100644 --- a/vstgui/lib/platform/win32/win32directcomposition.cpp +++ b/vstgui/lib/platform/win32/win32directcomposition.cpp @@ -821,12 +821,6 @@ bool Factory::removeVisual (const VisualPtr& visualPtr) return false; } -//------------------------------------------------------------------------ -ID2D1Device* Factory::getDevice () const -{ - return impl->d2dDevice.get (); -} - //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- diff --git a/vstgui/lib/platform/win32/win32directcomposition.h b/vstgui/lib/platform/win32/win32directcomposition.h index 56f1b27f8..7ba9b6ac1 100644 --- a/vstgui/lib/platform/win32/win32directcomposition.h +++ b/vstgui/lib/platform/win32/win32directcomposition.h @@ -12,7 +12,6 @@ #include interface ID2D1DeviceContext; -interface ID2D1Device; //----------------------------------------------------------------------------- namespace VSTGUI { @@ -48,8 +47,6 @@ struct Factory VisualPtr createChildVisual (const VisualPtr& parent, uint32_t width, uint32_t height); bool removeVisual (const VisualPtr& visual); - ID2D1Device* getDevice () const; - ~Factory () noexcept; struct Impl; diff --git a/vstgui/lib/platform/win32/win32dragging.cpp b/vstgui/lib/platform/win32/win32dragging.cpp index ad84ff2b5..864a9a6e2 100644 --- a/vstgui/lib/platform/win32/win32dragging.cpp +++ b/vstgui/lib/platform/win32/win32dragging.cpp @@ -4,10 +4,9 @@ #include "win32dragging.h" #include "win32support.h" -#include "win32factory.h" #include "winstring.h" -#include "../../cdrawcontext.h" #include "../../cstring.h" +#include "../../cdrawcontext.h" #include "../../cvstguitimer.h" #include "win32dll.h" #include @@ -787,7 +786,7 @@ void Win32DragBitmapWindow::updateWindowPosition (POINT where) void Win32DragBitmapWindow::paint () { PAINTSTRUCT ps; - BeginPaint (hwnd, &ps); + HDC hdc = BeginPaint (hwnd, &ps); RECT clientRect; GetClientRect (hwnd, &clientRect); @@ -796,22 +795,17 @@ void Win32DragBitmapWindow::paint () rect.setWidth (clientRect.right - clientRect.left); rect.setHeight (clientRect.bottom - clientRect.top); - if (auto deviceContext = - getPlatformFactory ().asWin32Factory ()->createGraphicsDeviceContext (hwnd)) + if (auto drawContext = owned (createDrawContext (hwnd, hdc, rect))) { - CDrawContext drawContext (deviceContext, rect, 1.); + drawContext->beginDraw (); - drawContext.beginDraw (); + drawContext->clearRect (rect); + drawContext->setGlobalAlpha (0.9f); + CDrawContext::Transform t (*drawContext, CGraphicsTransform ().scale (scaleFactor, scaleFactor)); + bitmap->draw (drawContext, rect); - drawContext.clearRect (rect); - drawContext.setGlobalAlpha (0.9f); - CDrawContext::Transform t (drawContext, - CGraphicsTransform ().scale (scaleFactor, scaleFactor)); - bitmap->draw (&drawContext, rect); - - drawContext.endDraw (); + drawContext->endDraw (); } - EndPaint (hwnd, &ps); } diff --git a/vstgui/lib/platform/win32/win32factory.cpp b/vstgui/lib/platform/win32/win32factory.cpp index 1b17040f1..02e7153f3 100644 --- a/vstgui/lib/platform/win32/win32factory.cpp +++ b/vstgui/lib/platform/win32/win32factory.cpp @@ -8,14 +8,13 @@ #include "../iplatformfont.h" #include "../iplatformframe.h" #include "../iplatformframecallback.h" -#include "../iplatformgraphicsdevice.h" #include "../iplatformresourceinputstream.h" #include "../iplatformstring.h" #include "../iplatformtimer.h" #include "../common/fileresourceinputstream.h" #include "direct2d/d2dbitmap.h" #include "direct2d/d2dbitmapcache.h" -#include "direct2d/d2dgraphicscontext.h" +#include "direct2d/d2ddrawcontext.h" #include "direct2d/d2dfont.h" #include "direct2d/d2dgradient.h" #include "win32frame.h" @@ -53,7 +52,6 @@ struct Win32Factory::Impl COM::Ptr wicImagingFactory; std::unique_ptr directCompositionFactory; - D2DGraphicsDeviceFactory graphicsDeviceFactory; UTF8String resourceBasePath; bool useD2DHardwareRenderer {false}; @@ -114,13 +112,6 @@ Win32Factory::Win32Factory (HINSTANCE instance) impl->directCompositionFactory = DirectComposition::Factory::create (impl->d2dFactory.get ()); D2DBitmapCache::init (); - if (impl->directCompositionFactory) - { - auto device = impl->directCompositionFactory->getDevice (); - vstgui_assert (device, "if there's a direct composition factory it must have a device"); - auto d2dDevice = std::make_shared (device); - impl->graphicsDeviceFactory.addDevice (d2dDevice); - } } //----------------------------------------------------------------------------- @@ -197,56 +188,6 @@ DirectComposition::Factory* Win32Factory::getDirectCompositionFactory () const n return impl->directCompositionFactory.get (); } -//----------------------------------------------------------------------------- -PlatformGraphicsDeviceContextPtr - Win32Factory::createGraphicsDeviceContext (void* hwnd) const noexcept -{ - auto window = reinterpret_cast (hwnd); - auto renderTargetType = useD2DHardwareRenderer () ? D2D1_RENDER_TARGET_TYPE_HARDWARE - : D2D1_RENDER_TARGET_TYPE_SOFTWARE; - RECT rc; - GetClientRect (window, &rc); - - auto size = D2D1::SizeU (static_cast (rc.right - rc.left), - static_cast (rc.bottom - rc.top)); - COM::Ptr hwndRenderTarget; - D2D1_PIXEL_FORMAT pixelFormat = - D2D1::PixelFormat (DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED); - HRESULT hr = getD2DFactory ()->CreateHwndRenderTarget ( - D2D1::RenderTargetProperties (renderTargetType, pixelFormat), - D2D1::HwndRenderTargetProperties (window, size, D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS), - hwndRenderTarget.adoptPtr ()); - if (FAILED (hr)) - return nullptr; - hwndRenderTarget->SetDpi (96, 96); - - COM::Ptr deviceContext; - hr = hwndRenderTarget->QueryInterface (__uuidof (ID2D1DeviceContext), - reinterpret_cast (deviceContext.adoptPtr ())); - if (FAILED (hr)) - return nullptr; - - ID2D1Device* d2ddevice {}; - deviceContext->GetDevice (&d2ddevice); - auto device = impl->graphicsDeviceFactory.find (d2ddevice); - if (!device) - { - impl->graphicsDeviceFactory.addDevice (std::make_shared (d2ddevice)); - device = impl->graphicsDeviceFactory.find (d2ddevice); - vstgui_assert (device); - } - - return std::make_shared ( - *std::static_pointer_cast (device).get (), deviceContext.get (), - TransformMatrix {}); -} - -//----------------------------------------------------------------------------- -void Win32Factory::disableDirectComposition () const noexcept -{ - impl->directCompositionFactory.reset (); -} - //----------------------------------------------------------------------------- uint64_t Win32Factory::getTicks () const noexcept { @@ -383,6 +324,18 @@ auto Win32Factory::getClipboard () const noexcept -> DataPackagePtr return makeOwned (dataObject); } +//------------------------------------------------------------------------ +auto Win32Factory::createOffscreenContext (const CPoint& size, double scaleFactor) const noexcept + -> COffscreenContextPtr +{ + if (auto bitmap = makeOwned (size * scaleFactor)) + { + bitmap->setScaleFactor (scaleFactor); + return owned (new D2DDrawContext (bitmap)); + } + return nullptr; +} + //----------------------------------------------------------------------------- PlatformGradientPtr Win32Factory::createGradient () const noexcept { @@ -397,12 +350,6 @@ PlatformFileSelectorPtr Win32Factory::createFileSelector (PlatformFileSelectorSt return createWinFileSelector (style, win32Frame ? win32Frame->getHWND () : nullptr); } -//----------------------------------------------------------------------------- -const IPlatformGraphicsDeviceFactory& Win32Factory::getGraphicsDeviceFactory () const noexcept -{ - return impl->graphicsDeviceFactory; -} - //----------------------------------------------------------------------------- const LinuxFactory* Win32Factory::asLinuxFactory () const noexcept { diff --git a/vstgui/lib/platform/win32/win32factory.h b/vstgui/lib/platform/win32/win32factory.h index af83db82d..03b09b508 100644 --- a/vstgui/lib/platform/win32/win32factory.h +++ b/vstgui/lib/platform/win32/win32factory.h @@ -42,11 +42,6 @@ class Win32Factory final : public IPlatformFactory IDWriteFactory* getDirectWriteFactory () const noexcept; DirectComposition::Factory* getDirectCompositionFactory () const noexcept; - PlatformGraphicsDeviceContextPtr createGraphicsDeviceContext (void* hwnd) const noexcept; - - /** disable the use of direct composition. must be called before anything else or the behaviour - * is undefined. */ - void disableDirectComposition () const noexcept; /** Return platform ticks (millisecond resolution) * @return ticks @@ -138,6 +133,14 @@ class Win32Factory final : public IPlatformFactory */ DataPackagePtr getClipboard () const noexcept final; + /** create an offscreen draw device + * @param size the size of the bitmap where the offscreen renders to + * @param scaleFactor the scale factor for drawing + * @return an offscreen context object or nullptr on failure + */ + COffscreenContextPtr createOffscreenContext (const CPoint& size, + double scaleFactor = 1.) const noexcept final; + /** Create a platform gradient object * @return platform gradient object or nullptr on failure */ @@ -151,12 +154,6 @@ class Win32Factory final : public IPlatformFactory PlatformFileSelectorPtr createFileSelector (PlatformFileSelectorStyle style, IPlatformFrame* frame) const noexcept; - /** Get the graphics device factory - * - * @return platform graphics device factory - */ - const IPlatformGraphicsDeviceFactory& getGraphicsDeviceFactory () const noexcept final; - const LinuxFactory* asLinuxFactory () const noexcept final; const MacFactory* asMacFactory () const noexcept final; const Win32Factory* asWin32Factory () const noexcept final; diff --git a/vstgui/lib/platform/win32/win32frame.cpp b/vstgui/lib/platform/win32/win32frame.cpp index c6bd7b088..f17b2d13b 100644 --- a/vstgui/lib/platform/win32/win32frame.cpp +++ b/vstgui/lib/platform/win32/win32frame.cpp @@ -9,9 +9,9 @@ #include #include #include +#include "direct2d/d2ddrawcontext.h" #include "direct2d/d2dbitmap.h" #include "direct2d/d2dgraphicspath.h" -#include "direct2d/d2dgraphicscontext.h" #include "win32factory.h" #include "win32textedit.h" #include "win32optionmenu.h" @@ -71,13 +71,13 @@ static bool isParentLayered (HWND parent) } //----------------------------------------------------------------------------- -Win32Frame::Win32Frame (IPlatformFrameCallback* frame, const CRect& size, HWND parent, - PlatformType parentType) +Win32Frame::Win32Frame (IPlatformFrameCallback* frame, const CRect& size, HWND parent, PlatformType parentType) : IPlatformFrame (frame) , parentWindow (parent) , windowHandle (nullptr) , tooltipWindow (nullptr) , oldFocusWindow (nullptr) +, deviceContext (nullptr) , inPaint (false) , mouseInside (false) , updateRegionList (nullptr) @@ -129,8 +129,12 @@ Win32Frame::~Win32Frame () noexcept if (updateRegionList) std::free (updateRegionList); + if (deviceContext) + deviceContext->forget (); if (tooltipWindow) DestroyWindow (tooltipWindow); + if (backBuffer) + backBuffer = nullptr; if (windowHandle) RevokeDragDrop (windowHandle); if (parentWindow) @@ -281,7 +285,15 @@ bool Win32Frame::getGlobalPosition (CPoint& pos) const //----------------------------------------------------------------------------- bool Win32Frame::setSize (const CRect& newSize) { - legacyDrawDevice.reset (); + if (deviceContext) + { + deviceContext->forget (); + deviceContext = nullptr; + } + if (backBuffer) + { + backBuffer = getPlatformFactory ().createOffscreenContext (newSize.getSize ()); + } if (!parentWindow) return true; SetWindowPos (windowHandle, HWND_TOP, (int)newSize.left, (int)newSize.top, (int)newSize.getWidth (), (int)newSize.getHeight (), SWP_NOZORDER|SWP_NOCOPYBITS|SWP_NOREDRAW|SWP_DEFERERASE); @@ -645,24 +657,22 @@ void Win32Frame::paint (HWND hwnd) directCompositionVisual->resize (static_cast (frameSize.getWidth ()), static_cast (frameSize.getHeight ())); iterateRegion (rgn, [&] (const auto& rect) { - directCompositionVisual->update (rect, [&] (auto deviceContext, auto rect, - auto offsetX, auto offsetY) { - COM::Ptr device; - deviceContext->GetDevice (device.adoptPtr ()); - - CGraphicsTransform tm; - tm.translate (offsetX - rect.left, offsetY - rect.top); - - const auto& graphicsDeviceFactory = - static_cast ( - getPlatformFactory ().asWin32Factory ()->getGraphicsDeviceFactory ()); - auto graphicsDevice = graphicsDeviceFactory.find (device.get ()); - auto drawDevice = std::make_shared ( - *static_cast (graphicsDevice.get ()), deviceContext, - tm); - - getFrame ()->platformDrawRects (drawDevice, 1., {1, rect}); - }); + directCompositionVisual->update ( + rect, [&] (auto deviceContext, auto rect, auto offsetX, auto offsetY) { + COM::Ptr device; + deviceContext->GetDevice (device.adoptPtr ()); + D2DDrawContext drawContext (deviceContext, frameSize, device.get ()); + drawContext.setClipRect (rect); + CGraphicsTransform tm; + tm.translate (offsetX - rect.left, offsetY - rect.top); + CDrawContext::Transform transform (drawContext, tm); + { + drawContext.saveGlobalState (); + drawContext.clearRect (rect); + getFrame ()->platformDrawRect (&drawContext, rect); + drawContext.restoreGlobalState (); + } + }); }); for (auto& vl : viewLayers) vl->drawInvalidRects (); @@ -671,28 +681,32 @@ void Win32Frame::paint (HWND hwnd) } else { - if (!legacyDrawDevice) - legacyDrawDevice = - getPlatformFactory ().asWin32Factory ()->createGraphicsDeviceContext (hwnd); - if (legacyDrawDevice) + if (deviceContext == nullptr) + deviceContext = createDrawContext (hwnd, hdc, frameSize); + if (deviceContext) { GetRgnBox (rgn, &ps.rcPaint); CRect updateRect ((CCoord)ps.rcPaint.left, (CCoord)ps.rcPaint.top, (CCoord)ps.rcPaint.right, (CCoord)ps.rcPaint.bottom); + deviceContext->setClipRect (updateRect); - legacyDrawDevice->setClipRect (updateRect); - - legacyDrawDevice->beginDraw (); + CDrawContext* drawContext = backBuffer ? backBuffer : deviceContext; + drawContext->beginDraw (); iterateRegion (rgn, [&] (const auto& rect) { - legacyDrawDevice->saveGlobalState (); - legacyDrawDevice->setClipRect (updateRect); - legacyDrawDevice->clearRect (rect); - getFrame ()->platformDrawRects (legacyDrawDevice, 1, {1, rect}); - legacyDrawDevice->restoreGlobalState (); + drawContext->clearRect (rect); + getFrame ()->platformDrawRect (drawContext, rect); }); - legacyDrawDevice->endDraw (); + drawContext->endDraw (); + if (backBuffer) + { + deviceContext->beginDraw (); + deviceContext->clearRect (updateRect); + backBuffer->copyFrom (deviceContext, updateRect, + CPoint (updateRect.left, updateRect.top)); + deviceContext->endDraw (); + } } } } diff --git a/vstgui/lib/platform/win32/win32frame.h b/vstgui/lib/platform/win32/win32frame.h index 262b70d79..80ba85413 100644 --- a/vstgui/lib/platform/win32/win32frame.h +++ b/vstgui/lib/platform/win32/win32frame.h @@ -80,9 +80,10 @@ class Win32Frame final : public IPlatformFrame, public IWin32PlatformFrame HWND tooltipWindow; HWND oldFocusWindow; + SharedPointer backBuffer; + CDrawContext* deviceContext; std::unique_ptr genericOptionMenuTheme; DirectComposition::VisualPtr directCompositionVisual; - PlatformGraphicsDeviceContextPtr legacyDrawDevice; Optional currentEvent; ViewLayers viewLayers; diff --git a/vstgui/lib/platform/win32/win32support.cpp b/vstgui/lib/platform/win32/win32support.cpp index f101a49a7..1882ae28c 100644 --- a/vstgui/lib/platform/win32/win32support.cpp +++ b/vstgui/lib/platform/win32/win32support.cpp @@ -12,6 +12,7 @@ #include "win32factory.h" #include +#include "direct2d/d2ddrawcontext.h" namespace VSTGUI { @@ -47,6 +48,18 @@ void useD2D () {} //----------------------------------------------------------------------------- void unuseD2D () {} +//----------------------------------------------------------------------------- +CDrawContext* createDrawContext (HWND window, HDC device, const CRect& surfaceRect) +{ + auto context = new D2DDrawContext (window, surfaceRect); + if (!context->usable ()) + { + context->forget (); + return nullptr; + } + return context; +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- VirtualKey translateWinVirtualKey (WPARAM winVKey) diff --git a/vstgui/lib/platform/win32/win32support.h b/vstgui/lib/platform/win32/win32support.h index 8dae44efd..ee4d5cf3a 100644 --- a/vstgui/lib/platform/win32/win32support.h +++ b/vstgui/lib/platform/win32/win32support.h @@ -40,6 +40,7 @@ extern IWICImagingFactory* getWICImageingFactory (); extern void useD2D (); extern void unuseD2D (); extern IDWriteFactory* getDWriteFactory (); +extern CDrawContext* createDrawContext (HWND window, HDC device, const CRect& surfaceRect); extern VirtualKey translateWinVirtualKey (WPARAM winVKey); extern void updateModifiers (Modifiers& modifiers); extern Optional keyMessageToKeyboardEvent (WPARAM wParam, LPARAM lParam); diff --git a/vstgui/lib/platform/win32/win32viewlayer.cpp b/vstgui/lib/platform/win32/win32viewlayer.cpp index d3c223e54..ce30590d7 100644 --- a/vstgui/lib/platform/win32/win32viewlayer.cpp +++ b/vstgui/lib/platform/win32/win32viewlayer.cpp @@ -5,8 +5,7 @@ #include "win32viewlayer.h" #include "win32directcomposition.h" #include "win32factory.h" -#include "direct2d/d2dgraphicscontext.h" -#include "direct2d/d2d.h" +#include "direct2d/d2ddrawcontext.h" //------------------------------------------------------------------------ namespace VSTGUI { @@ -47,19 +46,17 @@ bool Win32ViewLayer::drawInvalidRects () visual->update (r, [&] (auto deviceContext, auto updateRect, auto offsetX, auto offsetY) { COM::Ptr device; deviceContext->GetDevice (device.adoptPtr ()); - + D2DDrawContext drawContext (deviceContext, viewSize, device.get ()); + drawContext.setClipRect (updateRect); CGraphicsTransform tm; tm.translate (offsetX - updateRect.left, offsetY - updateRect.top); - - const auto& graphicsDeviceFactory = static_cast ( - getPlatformFactory ().asWin32Factory ()->getGraphicsDeviceFactory ()); - auto graphicsDevice = graphicsDeviceFactory.find (device.get ()); - auto drawDevice = std::make_shared ( - *static_cast (graphicsDevice.get ()), deviceContext, tm); - - drawDevice->setClipRect (updateRect); - drawDevice->clearRect (updateRect); - delegate->drawViewLayerRects (drawDevice, 1, {1, updateRect}); + CDrawContext::Transform transform (drawContext, tm); + { + drawContext.saveGlobalState (); + drawContext.clearRect (updateRect); + delegate->drawViewLayer (&drawContext, updateRect); + drawContext.restoreGlobalState (); + } }); } lastDrawTime = getPlatformFactory ().getTicks (); @@ -113,6 +110,9 @@ void Win32ViewLayer::setAlpha (float alpha) visual->commit (); } +//------------------------------------------------------------------------ +void Win32ViewLayer::draw (CDrawContext* context, const CRect& updateRect) {} + //------------------------------------------------------------------------ void Win32ViewLayer::onScaleFactorChanged (double newScaleFactor) {} diff --git a/vstgui/lib/platform/win32/win32viewlayer.h b/vstgui/lib/platform/win32/win32viewlayer.h index 6f335d9d9..66a9d09c6 100644 --- a/vstgui/lib/platform/win32/win32viewlayer.h +++ b/vstgui/lib/platform/win32/win32viewlayer.h @@ -28,6 +28,7 @@ class Win32ViewLayer void setSize (const CRect& size) override; void setZIndex (uint32_t zIndex) override; void setAlpha (float alpha) override; + void draw (CDrawContext* context, const CRect& updateRect) override; void onScaleFactorChanged (double newScaleFactor) override; bool drawInvalidRects (); diff --git a/vstgui/lib/vstguifwd.h b/vstgui/lib/vstguifwd.h index f68c25968..fe1296878 100644 --- a/vstgui/lib/vstguifwd.h +++ b/vstgui/lib/vstguifwd.h @@ -9,7 +9,6 @@ #include #include #include -#include namespace VSTGUI { @@ -94,18 +93,6 @@ enum DragResult { kDragError = -1 }; -//----------- -// @brief Text Face -//----------- -enum CTxtFace -{ - kNormalFace = 0, - kBoldFace = 1 << 1, - kItalicFace = 1 << 2, - kUnderlineFace = 1 << 3, - kStrikethroughFace = 1 << 4 -}; - //---------------------------- // @brief Bitmap Interpolation //---------------------------- @@ -154,10 +141,6 @@ struct CMultiFrameBitmapDescription; using GradientColorStop = std::pair; using GradientColorStopMap = std::multimap; -using LinePair = std::pair; -using LineList = std::vector; -using PointList = std::vector; - // interfaces class IViewListener; class IViewEventListener; @@ -339,10 +322,6 @@ class IPlatformFrameConfig; class IPlatformFrameCallback; class IPlatformTimerCallback; class IPlatformFileSelector; -class IPlatformGraphicsDeviceFactory; -class IPlatformGraphicsDevice; -class IPlatformGraphicsDeviceContext; -class IPlatformGraphicsDeviceContextBitmapExt; struct PlatformFileExtension; struct PlatformFileSelectorConfig; @@ -363,7 +342,5 @@ using PlatformGradientPtr = std::unique_ptr; using PlatformGraphicsPathPtr = std::unique_ptr; using PlatformGraphicsPathFactoryPtr = std::shared_ptr; using PlatformFileSelectorPtr = std::shared_ptr; -using PlatformGraphicsDevicePtr = std::shared_ptr; -using PlatformGraphicsDeviceContextPtr = std::shared_ptr; } // VSTGUI diff --git a/vstgui/standalone/examples/standalone/CMakeLists.txt b/vstgui/standalone/examples/standalone/CMakeLists.txt index f758c3c15..c9a84974c 100644 --- a/vstgui/standalone/examples/standalone/CMakeLists.txt +++ b/vstgui/standalone/examples/standalone/CMakeLists.txt @@ -12,7 +12,6 @@ set(standalone_sources "source/testappdelegate.h" "source/testmodel.cpp" "source/testmodel.h" - "../../../contrib/datepicker.h" ) set(standalone_resources @@ -21,38 +20,12 @@ set(standalone_resources "resource/resources.uidesc" "resource/test.uidesc" "resource/testpopup.uidesc" - "resource/metalwindow.uidesc" - "resource/direct3dwindow.uidesc" ) set(standalone_font "resource/font/EffectsEighty.ttf" ) -######################################################################################### -if(CMAKE_HOST_APPLE) - set(standalone_sources - ${standalone_sources} - "source/metalwindow.mm" - "source/metalwindow.h" - "source/metaltypes.h" - "source/metalshader.h" - "../../../contrib/externalview_nsview.h" - "../../../contrib/externalview_metal.h" - "../../../contrib/datepicker.mm" - ) -elseif(MSVC) - set(standalone_sources - ${standalone_sources} - "../../../contrib/externalview_hwnd.h" - "../../../contrib/externalview_direct3d12.h" - "../../../contrib/datepicker_win32.cpp" - "source/direct3dwindow.cpp" - "source/direct3dwindow.h" - "source/direct3dshader.h" - ) -endif() - ########################################################################################## vstgui_add_executable(${target} "${standalone_sources}") vstgui_add_resources(${target} "${standalone_resources}") @@ -63,9 +36,3 @@ vstgui_set_target_rcfile(${target} "resource/standalone.rc") vstgui_set_cxx_version(${target} 17) target_include_directories(${target} PRIVATE ../../../../) set_target_properties(${target} PROPERTIES ${APP_PROPERTIES} ${VSTGUI_STANDALONE_EXAMPLES_FOLDER}) - -if(CMAKE_HOST_APPLE) - target_link_libraries(${target} PRIVATE - "-framework Metal" - ) -endif() diff --git a/vstgui/standalone/examples/standalone/resource/direct3dwindow.uidesc b/vstgui/standalone/examples/standalone/resource/direct3dwindow.uidesc deleted file mode 100644 index e86bd4c04..000000000 --- a/vstgui/standalone/examples/standalone/resource/direct3dwindow.uidesc +++ /dev/null @@ -1,67 +0,0 @@ -{ - "vstgui-ui-description": { - "version": "1", - "control-tags": {}, - "custom": { - "UIDescFilePath": { - "path": "C:\\Users\\AScheffler\\source\\git\\vstgui\\vstgui\\standalone\\examples\\standalone\\resource\\direct3dwindow.uidesc" - }, - "UIGridController": { - "Grids": "1x 1,5x 5,10x 10,12x 12,15x 15" - }, - "UIEditController": { - "EditViewScale": "1", - "EditorSize": "0, 0, 1108, 904", - "SplitViewSize_0_0": "0.7369020501138952017328165311482734978199", - "SplitViewSize_0_1": "0.2403189066059225609262028910961817018688", - "SplitViewSize_1_0": "0.4851936218678815637694867746176896616817", - "SplitViewSize_1_1": "0.5091116173120728838341619848506525158882", - "SplitViewSize_2_0": "0.6543321299638988897129365795990452170372", - "SplitViewSize_2_1": "0.3411552346570397298286536624800646677613", - "TabSwitchValue": "0", - "Version": "1" - }, - "UITemplateController": { - "SelectedTemplate": "view" - }, - "UIAttributesController": {}, - "UIViewCreatorDataSource": { - "SelectedRow": "29" - } - }, - "templates": { - "view": { - "attributes": { - "autosize": "left right top bottom ", - "background-color": "~ BlackCColor", - "background-color-draw-style": "filled and stroked", - "class": "CViewContainer", - "maxSize": "32767, 32767", - "minSize": "300, 300", - "mouse-enabled": "true", - "opacity": "1", - "origin": "0, 0", - "size": "300, 300", - "transparent": "false", - "wants-focus": "false" - }, - "children": { - "CView": { - "attributes": { - "autosize": "left right top bottom ", - "class": "CView", - "custom-view-name": "Direct3DView", - "mouse-enabled": "true", - "opacity": "1", - "origin": "20, 20", - "size": "260, 260", - "sub-controller": "Direct3DController", - "transparent": "false", - "wants-focus": "false" - } - } - } - } - } - } -} \ No newline at end of file diff --git a/vstgui/standalone/examples/standalone/resource/metalwindow.uidesc b/vstgui/standalone/examples/standalone/resource/metalwindow.uidesc deleted file mode 100644 index d7eefbc47..000000000 --- a/vstgui/standalone/examples/standalone/resource/metalwindow.uidesc +++ /dev/null @@ -1,68 +0,0 @@ -{ - "vstgui-ui-description": { - "version": "1", - "control-tags": {}, - "custom": { - "UIDescFilePath": { - "path": "/Volumes/Quasar/vstgui/vstgui/standalone/examples/standalone/resource/metalwindow.uidesc" - }, - "UIGridController": { - "Grids": "1x 1,5x 5,10x 10,12x 12,15x 15" - }, - "UIEditController": { - "EditViewScale": "1", - "EditorSize": "0, 0, 810, 808", - "SplitViewSize_0_0": "0.8337595907928389005192570948565844446421", - "SplitViewSize_0_1": "0.140664961636828650393127304596418980509", - "SplitViewSize_1_0": "0.4833759590792838789496954632340930402279", - "SplitViewSize_1_1": "0.5102301790281329463283555014641024172306", - "SplitViewSize_2_0": "0.7777777777777777901135891625017393380404", - "SplitViewSize_2_1": "0.2160493827160493707228994253455311991274", - "TabSwitchValue": "0", - "UI Theme": "Dark", - "Version": "1", - "ViewBackground": "3" - }, - "UITemplateController": { - "SelectedTemplate": "view" - }, - "UIAttributesController": {}, - "UIViewCreatorDataSource": { - "SelectedRow": "29" - } - }, - "templates": { - "view": { - "attributes": { - "autosize": "left right top bottom ", - "background-color": "~ BlackCColor", - "background-color-draw-style": "filled and stroked", - "class": "CViewContainer", - "minSize": "300, 300", - "mouse-enabled": "true", - "opacity": "1", - "origin": "0, 0", - "size": "300, 300", - "transparent": "false", - "wants-focus": "false" - }, - "children": { - "CView": { - "attributes": { - "autosize": "left right top bottom ", - "class": "CView", - "custom-view-name": "MetalView", - "mouse-enabled": "true", - "opacity": "1", - "origin": "20, 20", - "size": "260, 260", - "sub-controller": "MetalController", - "transparent": "false", - "wants-focus": "false" - } - } - } - } - } - } -} \ No newline at end of file diff --git a/vstgui/standalone/examples/standalone/resource/test.uidesc b/vstgui/standalone/examples/standalone/resource/test.uidesc index 07831ee9b..e8de96570 100644 --- a/vstgui/standalone/examples/standalone/resource/test.uidesc +++ b/vstgui/standalone/examples/standalone/resource/test.uidesc @@ -23,25 +23,25 @@ "SelectedTemplate": "view" }, "UIEditController": { - "EditViewScale": "1", - "EditorSize": "0, 0, 1158, 918", - "SplitViewSize_0_0": "0.640178321188340837544217265531187877059", - "SplitViewSize_0_1": "0.337400154147982078711720532737672328949", - "SplitViewSize_1_0": "0.5924887892376681230999224680999759584665", - "SplitViewSize_1_1": "0.4030269058295964379468045990506652742624", + "EditViewScale": "1.5", + "EditorSize": "0, 0, 1158, 997", + "SplitViewSize_0_0": "0.7415036045314109536263913469156250357628", + "SplitViewSize_0_1": "0.2378990731204943254173400646322988905013", + "SplitViewSize_1_0": "0.5849639546858907968029939183907117694616", + "SplitViewSize_1_1": "0.4109165808444902312501767482899595052004", "SplitViewSize_2_0": "0.6424870466321243034357735268713440746069", "SplitViewSize_2_1": "0.3531951640759931065893795221199980005622", - "TabSwitchValue": "0", + "TabSwitchValue": "2", "UI Theme": "Dark", "Version": "1", "ViewBackground": "3" }, "UIAttributesController": {}, "UIViewCreatorDataSource": { - "SelectedRow": "29" + "SelectedRow": "24" }, "UIDescFilePath": { - "path": "/Users/scheffle/Source/vstgui/vstgui/standalone/examples/standalone/resource/test.uidesc" + "path": "/Volumes/vst3/vstgui/vstgui/standalone/examples/standalone/resource/test.uidesc" }, "UITagsDataSource": { "SelectedRow": "-1" @@ -76,7 +76,7 @@ "mouse-enabled": "true", "opacity": "1", "origin": "0, 0", - "size": "410, 455", + "size": "410, 350", "sub-controller": "DisabledControlsController", "transparent": "true", "wants-focus": "false" @@ -104,7 +104,7 @@ "class": "CViewContainer", "mouse-enabled": "true", "opacity": "1", - "origin": "0, 435", + "origin": "0, 330", "size": "410, 20", "transparent": "false", "wants-focus": "false" @@ -126,7 +126,7 @@ "radial-center": "0.5, 0.5", "radial-radius": "1", "round-rect-radius": "5", - "size": "410, 455", + "size": "410, 350", "transparent": "false", "wants-focus": "false" } @@ -190,93 +190,6 @@ "zoom-factor": "10" } }, - "CStringListControl": { - "attributes": { - "back-color": "~ WhiteCColor", - "back-color-selected": "TextShadow", - "class": "CStringListControl", - "control-tag": "Weekdays", - "default-value": "0.5", - "font": "~ NormalFont", - "font-color": "~ BlackCColor", - "font-color-selected": "~ BlackCColor", - "hover-color": "Hover", - "line-color": "~ BlackCColor", - "line-width": "-1", - "max-value": "7", - "min-value": "0", - "mouse-enabled": "true", - "opacity": "1", - "origin": "15, 200", - "row-height": "16", - "size": "110, 144", - "style-hover": "true", - "sub-controller": "WeekdaysController", - "text-alignment": "center", - "text-inset": "5", - "tooltip": "String List Control", - "transparent": "false", - "wants-focus": "true", - "wheel-inc-value": "0.1" - } - }, - "CView": { - "attributes": { - "autosize": "left top ", - "class": "CView", - "custom-view-name": "DatePicker", - "mouse-enabled": "true", - "opacity": "1", - "origin": "15, 350", - "size": "110, 25", - "sub-controller": "DatePickerController", - "transparent": "false", - "wants-focus": "true" - } - }, - "CTextEdit": { - "attributes": { - "autosize": "left right top ", - "back-color": "~ BlackCColor", - "background-offset": "0, 0", - "class": "CTextEdit", - "control-tag": "MutableString", - "default-value": "0.5", - "font": "~ NormalFont", - "font-antialias": "true", - "font-color": "~ WhiteCColor", - "frame-color": "~ WhiteCColor", - "frame-width": "1", - "immediate-text-change": "true", - "max-value": "1", - "min-value": "0", - "mouse-enabled": "true", - "opacity": "1", - "origin": "40, 50", - "round-rect-radius": "3", - "secure-style": "false", - "shadow-color": "~ RedCColor", - "size": "260, 20", - "style-3D-in": "false", - "style-3D-out": "false", - "style-doubleclick": "false", - "style-no-draw": "false", - "style-no-frame": "false", - "style-no-text": "false", - "style-round-rect": "true", - "style-shadow-text": "false", - "text-alignment": "center", - "text-inset": "0, 0", - "text-rotation": "0", - "text-shadow-offset": "1, 1", - "title": "This is a string value", - "tooltip": "Text Edit", - "transparent": "false", - "value-precision": "2", - "wants-focus": "true", - "wheel-inc-value": "0.1" - } - }, "CSlider": { "attributes": { "autosize": "left right top ", @@ -739,7 +652,7 @@ "origin": "135, 290", "round-rect-radius": "6", "shadow-color": "TextShadow", - "size": "130, 155", + "size": "265, 50", "style-3D-in": "false", "style-3D-out": "false", "style-no-draw": "false", @@ -902,7 +815,7 @@ "min-value": "0", "mouse-enabled": "true", "opacity": "1", - "origin": "270, 225", + "origin": "265, 225", "size": "70, 70", "skip-handle-drawing": "true", "tooltip": "Knob", @@ -913,6 +826,49 @@ "zoom-factor": "1.5" } }, + "CTextEdit": { + "attributes": { + "autosize": "left right top ", + "back-color": "~ BlackCColor", + "background-offset": "0, 0", + "class": "CTextEdit", + "control-tag": "MutableString", + "default-value": "0.5", + "font": "~ NormalFont", + "font-antialias": "true", + "font-color": "~ WhiteCColor", + "frame-color": "~ WhiteCColor", + "frame-width": "1", + "immediate-text-change": "true", + "max-value": "1", + "min-value": "0", + "mouse-enabled": "true", + "opacity": "1", + "origin": "40, 50", + "round-rect-radius": "3", + "secure-style": "false", + "shadow-color": "~ RedCColor", + "size": "260, 20", + "style-3D-in": "false", + "style-3D-out": "false", + "style-doubleclick": "false", + "style-no-draw": "false", + "style-no-frame": "false", + "style-no-text": "false", + "style-round-rect": "true", + "style-shadow-text": "false", + "text-alignment": "center", + "text-inset": "0, 0", + "text-rotation": "0", + "text-shadow-offset": "1, 1", + "title": "This is a string value", + "tooltip": "Text Edit", + "transparent": "false", + "value-precision": "2", + "wants-focus": "true", + "wheel-inc-value": "0.1" + } + }, "CParamDisplay": { "attributes": { "autosize": "left right top ", @@ -951,9 +907,57 @@ "wants-focus": "false", "wheel-inc-value": "0.1" } + }, + "CStringListControl": { + "attributes": { + "back-color": "~ WhiteCColor", + "back-color-selected": "TextShadow", + "class": "CStringListControl", + "control-tag": "Weekdays", + "default-value": "0.5", + "font": "~ NormalFont", + "font-color": "~ BlackCColor", + "font-color-selected": "~ BlackCColor", + "hover-color": "Hover", + "line-color": "~ BlackCColor", + "line-width": "-1", + "max-value": "7", + "min-value": "0", + "mouse-enabled": "true", + "opacity": "1", + "origin": "15, 200", + "row-height": "16", + "size": "110, 144", + "style-hover": "true", + "sub-controller": "WeekdaysController", + "text-alignment": "center", + "text-inset": "5", + "tooltip": "String List Control", + "transparent": "false", + "wants-focus": "true", + "wheel-inc-value": "0.1" + } + }, + "CAutoAnimation": { + "attributes": { + "animation-time": "32", + "bitmap": "autoanimation", + "bitmap-offset": "5, 5", + "class": "CAutoAnimation", + "default-value": "0.5", + "max-value": "36", + "min-value": "0", + "mouse-enabled": "true", + "opacity": "1", + "origin": "85, 5", + "size": "30, 30", + "transparent": "false", + "wants-focus": "false", + "wheel-inc-value": "0.1" + } } } } } } -} +} \ No newline at end of file diff --git a/vstgui/standalone/examples/standalone/source/direct3dshader.h b/vstgui/standalone/examples/standalone/source/direct3dshader.h deleted file mode 100644 index 8b9327a4b..000000000 --- a/vstgui/standalone/examples/standalone/source/direct3dshader.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -static constexpr auto shaderCode = R"( - -//********************************************************* -// -// Copyright (c) Microsoft. All rights reserved. -// This code is licensed under the MIT License (MIT). -// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF -// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY -// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR -// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. -// -//********************************************************* - -struct PSInput -{ - float4 position : SV_POSITION; - float4 color : COLOR; -}; - -PSInput VSMain(float4 position : POSITION, float4 color : COLOR) -{ - PSInput result; - - result.position = position; - result.color = color; - - return result; -} - -float4 PSMain(PSInput input) : SV_TARGET -{ - return input.color; -} - -)"; diff --git a/vstgui/standalone/examples/standalone/source/direct3dwindow.cpp b/vstgui/standalone/examples/standalone/source/direct3dwindow.cpp deleted file mode 100644 index 2e8c0045f..000000000 --- a/vstgui/standalone/examples/standalone/source/direct3dwindow.cpp +++ /dev/null @@ -1,696 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#include "direct3dwindow.h" -#include "direct3dshader.h" -#include "vstgui/contrib/externalview_direct3d12.h" -#include "vstgui/lib/cexternalview.h" -#include "vstgui/lib/cvstguitimer.h" -#include "vstgui/lib/platform/win32/win32factory.h" -#include "vstgui/standalone/include/helpers/uidesc/customization.h" -#include "vstgui/standalone/include/helpers/windowcontroller.h" -#include "vstgui/standalone/include/iuidescwindow.h" -#include "vstgui/uidescription/delegationcontroller.h" -#include "vstgui/uidescription/iuidescription.h" -#include "vstgui/uidescription/uiattributes.h" - -#include -#include - -#include - -#define VSTGUI_USE_THREADED_DIRECT3D12_EXAMPLE 1 - -#ifdef _MSC_VER -#pragma comment(lib, "d3dcompiler.lib") -#endif - -//------------------------------------------------------------------------ -namespace VSTGUI { -namespace Standalone { - -using Microsoft::WRL::ComPtr; - -struct CD3DX12_DEFAULT -{ -}; -extern const DECLSPEC_SELECTANY CD3DX12_DEFAULT D3D12_DEFAULT; - -inline UINT8 D3D12GetFormatPlaneCount (_In_ ID3D12Device* pDevice, DXGI_FORMAT Format) noexcept -{ - D3D12_FEATURE_DATA_FORMAT_INFO formatInfo = {Format, 0}; - if (FAILED (pDevice->CheckFeatureSupport (D3D12_FEATURE_FORMAT_INFO, &formatInfo, - sizeof (formatInfo)))) - { - return 0; - } - return formatInfo.PlaneCount; -} - -//------------------------------------------------------------------------------------------------ -inline constexpr UINT D3D12CalcSubresource (UINT MipSlice, UINT ArraySlice, UINT PlaneSlice, - UINT MipLevels, UINT ArraySize) noexcept -{ - return MipSlice + ArraySlice * MipLevels + PlaneSlice * MipLevels * ArraySize; -} - -struct CD3DX12_HEAP_PROPERTIES : public D3D12_HEAP_PROPERTIES -{ - CD3DX12_HEAP_PROPERTIES () = default; - explicit CD3DX12_HEAP_PROPERTIES (const D3D12_HEAP_PROPERTIES& o) noexcept - : D3D12_HEAP_PROPERTIES (o) - { - } - CD3DX12_HEAP_PROPERTIES (D3D12_CPU_PAGE_PROPERTY cpuPageProperty, - D3D12_MEMORY_POOL memoryPoolPreference, UINT creationNodeMask = 1, - UINT nodeMask = 1) - noexcept - { - Type = D3D12_HEAP_TYPE_CUSTOM; - CPUPageProperty = cpuPageProperty; - MemoryPoolPreference = memoryPoolPreference; - CreationNodeMask = creationNodeMask; - VisibleNodeMask = nodeMask; - } - explicit CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE type, UINT creationNodeMask = 1, - UINT nodeMask = 1) noexcept - { - Type = type; - CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - CreationNodeMask = creationNodeMask; - VisibleNodeMask = nodeMask; - } - bool IsCPUAccessible () const noexcept - { - return Type == D3D12_HEAP_TYPE_UPLOAD || Type == D3D12_HEAP_TYPE_READBACK || - (Type == D3D12_HEAP_TYPE_CUSTOM && - (CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE || - CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_WRITE_BACK)); - } -}; - -struct CD3DX12_RESOURCE_DESC : public D3D12_RESOURCE_DESC -{ - CD3DX12_RESOURCE_DESC () = default; - explicit CD3DX12_RESOURCE_DESC (const D3D12_RESOURCE_DESC& o) noexcept : D3D12_RESOURCE_DESC (o) - { - } - CD3DX12_RESOURCE_DESC (D3D12_RESOURCE_DIMENSION dimension, UINT64 alignment, UINT64 width, - UINT height, UINT16 depthOrArraySize, UINT16 mipLevels, - DXGI_FORMAT format, UINT sampleCount, UINT sampleQuality, - D3D12_TEXTURE_LAYOUT layout, D3D12_RESOURCE_FLAGS flags) - noexcept - { - Dimension = dimension; - Alignment = alignment; - Width = width; - Height = height; - DepthOrArraySize = depthOrArraySize; - MipLevels = mipLevels; - Format = format; - SampleDesc.Count = sampleCount; - SampleDesc.Quality = sampleQuality; - Layout = layout; - Flags = flags; - } - static inline CD3DX12_RESOURCE_DESC - Buffer (const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo, - D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE) noexcept - { - return CD3DX12_RESOURCE_DESC (D3D12_RESOURCE_DIMENSION_BUFFER, resAllocInfo.Alignment, - resAllocInfo.SizeInBytes, 1, 1, 1, DXGI_FORMAT_UNKNOWN, 1, 0, - D3D12_TEXTURE_LAYOUT_ROW_MAJOR, flags); - } - static inline CD3DX12_RESOURCE_DESC - Buffer (UINT64 width, D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, - UINT64 alignment = 0) noexcept - { - return CD3DX12_RESOURCE_DESC (D3D12_RESOURCE_DIMENSION_BUFFER, alignment, width, 1, 1, 1, - DXGI_FORMAT_UNKNOWN, 1, 0, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, - flags); - } - static inline CD3DX12_RESOURCE_DESC Tex1D ( - DXGI_FORMAT format, UINT64 width, UINT16 arraySize = 1, UINT16 mipLevels = 0, - D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, - D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, UINT64 alignment = 0) noexcept - { - return CD3DX12_RESOURCE_DESC (D3D12_RESOURCE_DIMENSION_TEXTURE1D, alignment, width, 1, - arraySize, mipLevels, format, 1, 0, layout, flags); - } - static inline CD3DX12_RESOURCE_DESC Tex2D ( - DXGI_FORMAT format, UINT64 width, UINT height, UINT16 arraySize = 1, UINT16 mipLevels = 0, - UINT sampleCount = 1, UINT sampleQuality = 0, - D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, - D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, UINT64 alignment = 0) noexcept - { - return CD3DX12_RESOURCE_DESC (D3D12_RESOURCE_DIMENSION_TEXTURE2D, alignment, width, height, - arraySize, mipLevels, format, sampleCount, sampleQuality, - layout, flags); - } - static inline CD3DX12_RESOURCE_DESC Tex3D ( - DXGI_FORMAT format, UINT64 width, UINT height, UINT16 depth, UINT16 mipLevels = 0, - D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, - D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, UINT64 alignment = 0) noexcept - { - return CD3DX12_RESOURCE_DESC (D3D12_RESOURCE_DIMENSION_TEXTURE3D, alignment, width, height, - depth, mipLevels, format, 1, 0, layout, flags); - } - inline UINT16 Depth () const noexcept - { - return (Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? DepthOrArraySize : 1); - } - inline UINT16 ArraySize () const noexcept - { - return (Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE3D ? DepthOrArraySize : 1); - } - inline UINT8 PlaneCount (_In_ ID3D12Device* pDevice) const noexcept - { - return D3D12GetFormatPlaneCount (pDevice, Format); - } - inline UINT Subresources (_In_ ID3D12Device* pDevice) const noexcept - { - return MipLevels * ArraySize () * PlaneCount (pDevice); - } - inline UINT CalcSubresource (UINT MipSlice, UINT ArraySlice, UINT PlaneSlice) noexcept - { - return D3D12CalcSubresource (MipSlice, ArraySlice, PlaneSlice, MipLevels, ArraySize ()); - } -}; - -struct CD3DX12_RESOURCE_BARRIER : public D3D12_RESOURCE_BARRIER -{ - CD3DX12_RESOURCE_BARRIER () = default; - explicit CD3DX12_RESOURCE_BARRIER (const D3D12_RESOURCE_BARRIER& o) noexcept - : D3D12_RESOURCE_BARRIER (o) - { - } - static inline CD3DX12_RESOURCE_BARRIER - Transition (_In_ ID3D12Resource* pResource, D3D12_RESOURCE_STATES stateBefore, - D3D12_RESOURCE_STATES stateAfter, - UINT subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, - D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE) noexcept - { - CD3DX12_RESOURCE_BARRIER result = {}; - D3D12_RESOURCE_BARRIER& barrier = result; - result.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - result.Flags = flags; - barrier.Transition.pResource = pResource; - barrier.Transition.StateBefore = stateBefore; - barrier.Transition.StateAfter = stateAfter; - barrier.Transition.Subresource = subresource; - return result; - } - static inline CD3DX12_RESOURCE_BARRIER Aliasing (_In_ ID3D12Resource* pResourceBefore, - _In_ ID3D12Resource* pResourceAfter) noexcept - { - CD3DX12_RESOURCE_BARRIER result = {}; - D3D12_RESOURCE_BARRIER& barrier = result; - result.Type = D3D12_RESOURCE_BARRIER_TYPE_ALIASING; - barrier.Aliasing.pResourceBefore = pResourceBefore; - barrier.Aliasing.pResourceAfter = pResourceAfter; - return result; - } - static inline CD3DX12_RESOURCE_BARRIER UAV (_In_ ID3D12Resource* pResource) noexcept - { - CD3DX12_RESOURCE_BARRIER result = {}; - D3D12_RESOURCE_BARRIER& barrier = result; - result.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; - barrier.UAV.pResource = pResource; - return result; - } -}; - -struct CD3DX12_CPU_DESCRIPTOR_HANDLE : public D3D12_CPU_DESCRIPTOR_HANDLE -{ - CD3DX12_CPU_DESCRIPTOR_HANDLE () = default; - explicit CD3DX12_CPU_DESCRIPTOR_HANDLE (const D3D12_CPU_DESCRIPTOR_HANDLE& o) noexcept - : D3D12_CPU_DESCRIPTOR_HANDLE (o) - { - } - CD3DX12_CPU_DESCRIPTOR_HANDLE (CD3DX12_DEFAULT) noexcept { ptr = 0; } - CD3DX12_CPU_DESCRIPTOR_HANDLE (_In_ const D3D12_CPU_DESCRIPTOR_HANDLE& other, - INT offsetScaledByIncrementSize) - noexcept - { - InitOffsetted (other, offsetScaledByIncrementSize); - } - CD3DX12_CPU_DESCRIPTOR_HANDLE (_In_ const D3D12_CPU_DESCRIPTOR_HANDLE& other, - INT offsetInDescriptors, UINT descriptorIncrementSize) - noexcept - { - InitOffsetted (other, offsetInDescriptors, descriptorIncrementSize); - } - CD3DX12_CPU_DESCRIPTOR_HANDLE& Offset (INT offsetInDescriptors, - UINT descriptorIncrementSize) noexcept - { - ptr = SIZE_T (INT64 (ptr) + INT64 (offsetInDescriptors) * INT64 (descriptorIncrementSize)); - return *this; - } - CD3DX12_CPU_DESCRIPTOR_HANDLE& Offset (INT offsetScaledByIncrementSize) noexcept - { - ptr = SIZE_T (INT64 (ptr) + INT64 (offsetScaledByIncrementSize)); - return *this; - } - bool operator== (_In_ const D3D12_CPU_DESCRIPTOR_HANDLE& other) const noexcept - { - return (ptr == other.ptr); - } - bool operator!= (_In_ const D3D12_CPU_DESCRIPTOR_HANDLE& other) const noexcept - { - return (ptr != other.ptr); - } - CD3DX12_CPU_DESCRIPTOR_HANDLE& operator= (const D3D12_CPU_DESCRIPTOR_HANDLE& other) noexcept - { - ptr = other.ptr; - return *this; - } - - inline void InitOffsetted (_In_ const D3D12_CPU_DESCRIPTOR_HANDLE& base, - INT offsetScaledByIncrementSize) noexcept - { - InitOffsetted (*this, base, offsetScaledByIncrementSize); - } - - inline void InitOffsetted (_In_ const D3D12_CPU_DESCRIPTOR_HANDLE& base, - INT offsetInDescriptors, UINT descriptorIncrementSize) noexcept - { - InitOffsetted (*this, base, offsetInDescriptors, descriptorIncrementSize); - } - - static inline void InitOffsetted (_Out_ D3D12_CPU_DESCRIPTOR_HANDLE& handle, - _In_ const D3D12_CPU_DESCRIPTOR_HANDLE& base, - INT offsetScaledByIncrementSize) noexcept - { - handle.ptr = SIZE_T (INT64 (base.ptr) + INT64 (offsetScaledByIncrementSize)); - } - - static inline void InitOffsetted (_Out_ D3D12_CPU_DESCRIPTOR_HANDLE& handle, - _In_ const D3D12_CPU_DESCRIPTOR_HANDLE& base, - INT offsetInDescriptors, - UINT descriptorIncrementSize) noexcept - { - handle.ptr = SIZE_T (INT64 (base.ptr) + - INT64 (offsetInDescriptors) * INT64 (descriptorIncrementSize)); - } -}; - -//------------------------------------------------------------------------ -struct ExampleRenderer : public ExternalView::IDirect3D12Renderer -{ - static constexpr uint32_t FrameCount = 2; - - ExternalView::IDirect3D12View* m_view; - - bool init (ExternalView::IDirect3D12View* view) override - { - m_view = view; - return true; - } - - void render (ID3D12CommandQueue* queue) override { doRender (queue); } - - void beforeSizeUpdate () override { freeFrameResources (); } - - void onSizeUpdate (ExternalView::IntSize newSize, double scaleFactor) override - { - m_viewport = {0.f, 0.f, static_cast (newSize.width), - static_cast (newSize.height)}; - m_scissorRect = {0, 0, static_cast (newSize.width), - static_cast (newSize.height)}; - - createFrameResources (); - m_view->render (); - } - - void onAttach () override - { - createFrameResources (); - loadAssets (); - m_view->render (); -#if VSTGUI_USE_THREADED_DIRECT3D12_EXAMPLE - stopRenderThread = false; - renderThread = std::thread ([this] () { - while (!stopRenderThread) - { - try - { - m_view->render (); - } - catch (...) - { - stopRenderThread = true; - } - std::this_thread::sleep_for (std::chrono::milliseconds (1)); - } - }); -#else - timer = makeOwned ([this] (auto) { m_view->render (); }, 16); -#endif - } - void onRemove () override - { -#if VSTGUI_USE_THREADED_DIRECT3D12_EXAMPLE - stopRenderThread = true; - if (renderThread.joinable ()) - renderThread.join (); -#else - timer = nullptr; -#endif - freeFrameResources (); - onDestroy (); - } - - uint32_t getFrameCount () const override { return FrameCount; } - - void freeFrameResources () - { - for (UINT n = 0; n < FrameCount; n++) - m_renderTargets[n].Reset (); - m_view->setFrameIndex (0); - } - - void createFrameResources () - { - // Create descriptor heaps. - { - // Describe and create a render target view (RTV) descriptor heap. - D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {}; - rtvHeapDesc.NumDescriptors = FrameCount; - rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; - rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; - ThrowIfFailed (m_view->getDevice ()->CreateDescriptorHeap (&rtvHeapDesc, - IID_PPV_ARGS (&m_rtvHeap))); - - m_rtvDescriptorSize = m_view->getDevice ()->GetDescriptorHandleIncrementSize ( - D3D12_DESCRIPTOR_HEAP_TYPE_RTV); - } - // Create frame resources. - { - auto rtvHandle = (m_rtvHeap->GetCPUDescriptorHandleForHeapStart ()); - - // Create a RTV for each frame. - for (UINT n = 0; n < FrameCount; n++) - { - ThrowIfFailed ( - m_view->getSwapChain ()->GetBuffer (n, IID_PPV_ARGS (&m_renderTargets[n]))); - m_view->getDevice ()->CreateRenderTargetView (m_renderTargets[n].Get (), nullptr, - rtvHandle); - rtvHandle.ptr = - SIZE_T (INT64 (rtvHandle.ptr) + INT64 (1) * INT64 (m_rtvDescriptorSize)); - } - } - } - - // Load the sample assets. - void loadAssets () - { - // Create an empty root signature. - { - D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = { - 0, nullptr, 0, nullptr, - D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT}; - - ComPtr signature; - ComPtr error; - ThrowIfFailed (D3D12SerializeRootSignature ( - &rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error)); - ThrowIfFailed (m_view->getDevice ()->CreateRootSignature ( - 0, signature->GetBufferPointer (), signature->GetBufferSize (), - IID_PPV_ARGS (&m_rootSignature))); - } - - // Create the pipeline state, which includes compiling and loading shaders. - { - ComPtr vertexShader; - ComPtr pixelShader; - -#if defined(_DEBUG) - // Enable better shader debugging with the graphics debugging tools. - UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; -#else - UINT compileFlags = 0; -#endif - - ThrowIfFailed (D3DCompile (shaderCode, strlen (shaderCode), nullptr, nullptr, nullptr, - "VSMain", "vs_5_0", compileFlags, 0, &vertexShader, - nullptr)); - ThrowIfFailed (D3DCompile (shaderCode, strlen (shaderCode), nullptr, nullptr, nullptr, - "PSMain", "ps_5_0", compileFlags, 0, &pixelShader, nullptr)); - - // Define the vertex input layout. - D3D12_INPUT_ELEMENT_DESC inputElementDescs[] = { - {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, - D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, - {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, - D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}}; - - // Describe and create the graphics pipeline state object (PSO). - D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; - psoDesc.InputLayout = {inputElementDescs, _countof (inputElementDescs)}; - psoDesc.pRootSignature = m_rootSignature.Get (); - psoDesc.VS = {vertexShader.Get ()->GetBufferPointer (), - vertexShader.Get ()->GetBufferSize ()}; - psoDesc.PS = {pixelShader.Get ()->GetBufferPointer (), - pixelShader.Get ()->GetBufferSize ()}; - psoDesc.RasterizerState = {D3D12_FILL_MODE_SOLID, - D3D12_CULL_MODE_BACK, - FALSE, - D3D12_DEFAULT_DEPTH_BIAS, - D3D12_DEFAULT_DEPTH_BIAS_CLAMP, - D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS, - TRUE, - FALSE, - FALSE, - 0, - D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF}; - constexpr D3D12_BLEND_DESC DefaultBlendDesc = { - FALSE, - FALSE, - {FALSE, FALSE, D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, - D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, D3D12_LOGIC_OP_NOOP, - D3D12_COLOR_WRITE_ENABLE_ALL}}; - psoDesc.BlendState = DefaultBlendDesc; - psoDesc.DepthStencilState.DepthEnable = FALSE; - psoDesc.DepthStencilState.StencilEnable = FALSE; - psoDesc.SampleMask = UINT_MAX; - psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; - psoDesc.NumRenderTargets = 1; - psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; - psoDesc.SampleDesc.Count = 1; - ThrowIfFailed (m_view->getDevice ()->CreateGraphicsPipelineState ( - &psoDesc, IID_PPV_ARGS (&m_pipelineState))); - } - - // Create the command list. - ThrowIfFailed (m_view->getDevice ()->CreateCommandList ( - 0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_view->getCommandAllocator (), - m_pipelineState.Get (), IID_PPV_ARGS (&m_commandList))); - - // Command lists are created in the recording state, but there is nothing - // to record yet. The main loop expects it to be closed, so close it now. - ThrowIfFailed (m_commandList->Close ()); - - updateAndUploadVertexBuffer (); - } - - void updateAndUploadVertexBuffer () - { - // Create the vertex buffer. - - // Define the geometry for a triangle. - Vertex triangleVertices[] = {{{0.0f, 0.8f, 0.0f}, colorRight}, - {{0.8f, -0.8f, 0.0f}, colorLeft}, - {{-0.8f, -0.8f, 0.0f}, colorTop}}; - - const UINT vertexBufferSize = sizeof (triangleVertices); - - // Note: using upload heaps to transfer static data like vert buffers is not - // recommended. Every time the GPU needs it, the upload heap will be marshalled - // over. Please read up on Default Heap usage. An upload heap is used here for - // code simplicity and because there are very few verts to actually transfer. - ThrowIfFailed (m_view->getDevice ()->CreateCommittedResource ( - &CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_UPLOAD), D3D12_HEAP_FLAG_NONE, - &CD3DX12_RESOURCE_DESC::Buffer (vertexBufferSize), D3D12_RESOURCE_STATE_GENERIC_READ, - nullptr, IID_PPV_ARGS (&m_vertexBuffer))); - - // Copy the triangle data to the vertex buffer. - UINT8* pVertexDataBegin; - D3D12_RANGE readRange {0, 0}; // We do not intend to read from this resource on the CPU. - ThrowIfFailed ( - m_vertexBuffer->Map (0, &readRange, reinterpret_cast (&pVertexDataBegin))); - memcpy (pVertexDataBegin, triangleVertices, sizeof (triangleVertices)); - m_vertexBuffer->Unmap (0, nullptr); - - // Initialize the vertex buffer view. - m_vertexBufferView.BufferLocation = m_vertexBuffer->GetGPUVirtualAddress (); - m_vertexBufferView.StrideInBytes = sizeof (Vertex); - m_vertexBufferView.SizeInBytes = vertexBufferSize; - } - - void updateColors () - { - ++frameCounter; - colorTop.x = (1.f + std::sin (frameCounter * 0.013f)) * 0.5f; - colorTop.y = (1.f + std::sin (frameCounter * 0.021f)) * 0.5f; - colorTop.z = (1.f + std::sin (frameCounter * 0.037f)) * 0.5f; - - colorLeft.x = (1.f + std::sin (frameCounter * 0.031f)) * 0.5f; - colorLeft.y = (1.f + std::sin (frameCounter * 0.021f)) * 0.5f; - colorLeft.z = (1.f + std::sin (frameCounter * 0.011f)) * 0.5f; - - colorRight.x = (1.f + std::sin (frameCounter * 0.025f)) * 0.5f; - colorRight.y = (1.f + std::sin (frameCounter * 0.012f)) * 0.5f; - colorRight.z = (1.f + std::sin (frameCounter * 0.031f)) * 0.5f; - } - - void doRender (ID3D12CommandQueue* queue) - { - updateColors (); - updateAndUploadVertexBuffer (); - - // Record all the commands we need to render the scene into the command list. - populateCommandList (); - - // Execute the command list. - ID3D12CommandList* ppCommandLists[] = {m_commandList.Get ()}; - queue->ExecuteCommandLists (_countof (ppCommandLists), ppCommandLists); - } - - void onDestroy () - { - m_vertexBuffer.Reset (); - - for (auto i = 0; i < FrameCount; ++i) - m_renderTargets[i].Reset (); - m_rootSignature.Reset (); - m_rtvHeap.Reset (); - m_pipelineState.Reset (); - m_commandList.Reset (); - } - - void populateCommandList () - { - // Command list allocators can only be reset when the associated - // command lists have finished execution on the GPU; apps should use - // fences to determine GPU execution progress. - ThrowIfFailed (m_view->getCommandAllocator ()->Reset ()); - - // However, when ExecuteCommandList() is called on a particular command - // list, that command list can then be reset at any time and must be before - // re-recording. - ThrowIfFailed ( - m_commandList->Reset (m_view->getCommandAllocator (), m_pipelineState.Get ())); - - // Set necessary state. - m_commandList->SetGraphicsRootSignature (m_rootSignature.Get ()); - m_commandList->RSSetViewports (1, &m_viewport); - m_commandList->RSSetScissorRects (1, &m_scissorRect); - - // Indicate that the back buffer will be used as a render target. - m_commandList->ResourceBarrier (1, &CD3DX12_RESOURCE_BARRIER::Transition ( - m_renderTargets[m_view->getFrameIndex ()].Get (), - D3D12_RESOURCE_STATE_PRESENT, - D3D12_RESOURCE_STATE_RENDER_TARGET)); - - CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle (m_rtvHeap->GetCPUDescriptorHandleForHeapStart (), - m_view->getFrameIndex (), m_rtvDescriptorSize); - m_commandList->OMSetRenderTargets (1, &rtvHandle, FALSE, nullptr); - - // Record commands. - const float clearColor[] = {0.0f, 0.2f, 0.4f, 1.0f}; - m_commandList->ClearRenderTargetView (rtvHandle, clearColor, 0, nullptr); - m_commandList->IASetPrimitiveTopology (D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - m_commandList->IASetVertexBuffers (0, 1, &m_vertexBufferView); - m_commandList->DrawInstanced (3, 1, 0, 0); - - // Indicate that the back buffer will now be used to present. - m_commandList->ResourceBarrier (1, &CD3DX12_RESOURCE_BARRIER::Transition ( - m_renderTargets[m_view->getFrameIndex ()].Get (), - D3D12_RESOURCE_STATE_RENDER_TARGET, - D3D12_RESOURCE_STATE_PRESENT)); - - ThrowIfFailed (m_commandList->Close ()); - } - - float m_aspectRatio {1.f}; - - // Pipeline objects. - ComPtr m_renderTargets[FrameCount]; - ComPtr m_rootSignature; - ComPtr m_rtvHeap; - ComPtr m_pipelineState; - ComPtr m_commandList; - UINT m_rtvDescriptorSize {0}; - - // App objects - ComPtr m_vertexBuffer; - D3D12_VERTEX_BUFFER_VIEW m_vertexBufferView; - - D3D12_VIEWPORT m_viewport {}; - D3D12_RECT m_scissorRect {}; - - struct Vertex - { - DirectX::XMFLOAT3 position; - DirectX::XMFLOAT4 color; - }; - - DirectX::XMFLOAT4 colorTop {0.f, 0.f, 1.f, 1.f}; - DirectX::XMFLOAT4 colorLeft {0.f, 1.f, 0.f, 1.f}; - DirectX::XMFLOAT4 colorRight {1.f, 0.f, 0.f, 1.f}; - uint32_t frameCounter {0}; -#if VSTGUI_USE_THREADED_DIRECT3D12_EXAMPLE - bool stopRenderThread {true}; - std::thread renderThread; -#else - SharedPointer timer; -#endif -}; - -//------------------------------------------------------------------------ -struct Direct3DController : DelegationController -{ - using DelegationController::DelegationController; - - CView* createView (const UIAttributes& attributes, const IUIDescription* description) override - { - if (auto viewName = attributes.getAttributeValue (IUIDescription::kCustomViewName)) - { - if (*viewName == "Direct3DView") - { - auto renderer = std::make_shared (); - if (auto view = ExternalView::Direct3D12View::make ( - getPlatformFactory ().asWin32Factory ()->getInstance (), renderer)) - { - return new CExternalView ({}, view); - } - } - } - return DelegationController::createView (attributes, description); - } -}; - -//------------------------------------------------------------------------ -WindowPtr makeNewDirect3DExampleWindow () -{ - auto customization = UIDesc::Customization::make (); - customization->addCreateViewControllerFunc ("Direct3DController", [] (auto, auto parent, auto) { - return new Direct3DController (parent); - }); - - UIDesc::Config config; - config.uiDescFileName = "direct3dwindow.uidesc"; - config.viewName = "view"; - config.windowConfig.type = WindowType::Document; - config.windowConfig.style.close ().size ().border (); - config.windowConfig.title = "Direct3D Example"; - config.customization = customization; - - return UIDesc::makeWindow (config); -} - -//------------------------------------------------------------------------ -} // Standalone -} // VSTGUI diff --git a/vstgui/standalone/examples/standalone/source/direct3dwindow.h b/vstgui/standalone/examples/standalone/source/direct3dwindow.h deleted file mode 100644 index 1810c2651..000000000 --- a/vstgui/standalone/examples/standalone/source/direct3dwindow.h +++ /dev/null @@ -1,17 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#pragma once - -#include "vstgui/standalone/include/fwd.h" - -//------------------------------------------------------------------------ -namespace VSTGUI { -namespace Standalone { - -WindowPtr makeNewDirect3DExampleWindow (); - -//------------------------------------------------------------------------ -} // Standalone -} // VSTGUI diff --git a/vstgui/standalone/examples/standalone/source/metalshader.h b/vstgui/standalone/examples/standalone/source/metalshader.h deleted file mode 100644 index 1137582b8..000000000 --- a/vstgui/standalone/examples/standalone/source/metalshader.h +++ /dev/null @@ -1,78 +0,0 @@ -static constexpr auto shaderCode = R"( - -#include -#include - -using namespace metal; - -// Buffer index values shared between shader and C code to ensure Metal shader buffer inputs -// match Metal API buffer set calls. -typedef enum AAPLVertexInputIndex -{ - AAPLVertexInputIndexVertices = 0, - AAPLVertexInputIndexViewportSize = 1, -} AAPLVertexInputIndex; - -// This structure defines the layout of vertices sent to the vertex -// shader. This header is shared between the .metal shader and C code, to guarantee that -// the layout of the vertex array in the C code matches the layout that the .metal -// vertex shader expects. -typedef struct -{ - vector_float2 position; - vector_float4 color; -} AAPLVertex; - - - -// Vertex shader outputs and fragment shader inputs -struct RasterizerData -{ - // The [[position]] attribute of this member indicates that this value - // is the clip space position of the vertex when this structure is - // returned from the vertex function. - float4 position [[position]]; - - // Since this member does not have a special attribute, the rasterizer - // interpolates its value with the values of the other triangle vertices - // and then passes the interpolated value to the fragment shader for each - // fragment in the triangle. - float4 color; -}; - -vertex RasterizerData -vertexShader(uint vertexID [[vertex_id]], - constant AAPLVertex *vertices [[buffer(AAPLVertexInputIndexVertices)]], - constant vector_uint2 *viewportSizePointer [[buffer(AAPLVertexInputIndexViewportSize)]]) -{ - RasterizerData out; - - // Index into the array of positions to get the current vertex. - // The positions are specified in pixel dimensions (i.e. a value of 100 - // is 100 pixels from the origin). - float2 pixelSpacePosition = vertices[vertexID].position.xy; - - // Get the viewport size and cast to float. - vector_float2 viewportSize = vector_float2(*viewportSizePointer); - - - // To convert from positions in pixel space to positions in clip-space, - // divide the pixel coordinates by half the size of the viewport. - out.position = vector_float4(0.0, 0.0, 0.0, 1.0); - out.position.xy = pixelSpacePosition / (viewportSize / 2.0); - - // Pass the input color directly to the rasterizer. - out.color = vertices[vertexID].color; - - return out; -} - -fragment float4 fragmentShader(RasterizerData in [[stage_in]]) -{ - // Return the interpolated color. - return in.color; -} - - -)"; - diff --git a/vstgui/standalone/examples/standalone/source/metaltypes.h b/vstgui/standalone/examples/standalone/source/metaltypes.h deleted file mode 100644 index c0e9088a5..000000000 --- a/vstgui/standalone/examples/standalone/source/metaltypes.h +++ /dev/null @@ -1,24 +0,0 @@ - -#pragma once - -#include - -// Buffer index values shared between shader and C code to ensure Metal shader buffer inputs -// match Metal API buffer set calls. -typedef enum AAPLVertexInputIndex -{ - AAPLVertexInputIndexVertices = 0, - AAPLVertexInputIndexViewportSize = 1, -} AAPLVertexInputIndex; - -// This structure defines the layout of vertices sent to the vertex -// shader. This header is shared between the .metal shader and C code, to guarantee that -// the layout of the vertex array in the C code matches the layout that the .metal -// vertex shader expects. -typedef struct -{ - vector_float2 position; - vector_float4 color; -} AAPLVertex; - - diff --git a/vstgui/standalone/examples/standalone/source/metalwindow.h b/vstgui/standalone/examples/standalone/source/metalwindow.h deleted file mode 100644 index 7b1501075..000000000 --- a/vstgui/standalone/examples/standalone/source/metalwindow.h +++ /dev/null @@ -1,17 +0,0 @@ -// This file is part of VSTGUI. It is subject to the license terms -// in the LICENSE file found in the top-level directory of this -// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE - -#pragma once - -#include "vstgui/standalone/include/fwd.h" - -//------------------------------------------------------------------------ -namespace VSTGUI { -namespace Standalone { - -WindowPtr makeNewMetalExampleWindow (); - -//------------------------------------------------------------------------ -} // Standalone -} // VSTGUI diff --git a/vstgui/standalone/examples/standalone/source/metalwindow.mm b/vstgui/standalone/examples/standalone/source/metalwindow.mm deleted file mode 100644 index c813472ae..000000000 --- a/vstgui/standalone/examples/standalone/source/metalwindow.mm +++ /dev/null @@ -1,246 +0,0 @@ -#import "metaltypes.h" -#import "metalwindow.h" -#import "vstgui/contrib/externalview_metal.h" -#import "vstgui/lib/cexternalview.h" -#import "vstgui/standalone/include/helpers/uidesc/customization.h" -#import "vstgui/standalone/include/helpers/windowcontroller.h" -#import "vstgui/standalone/include/iuidescwindow.h" -#import "vstgui/uidescription/delegationcontroller.h" -#import "vstgui/uidescription/iuidescription.h" -#import "vstgui/uidescription/uiattributes.h" - -#import -#import "metalshader.h" - -//------------------------------------------------------------------------ -namespace VSTGUI { -namespace Standalone { - -//------------------------------------------------------------------------ -struct ExampleMetalRenderer : ExternalView::IMetalRenderer -{ - id _device {MTLCreateSystemDefaultDevice ()}; - id _pipelineState {nullptr}; - id _commandQueue {nullptr}; - MTLRenderPassDescriptor* _drawableRenderDescriptor; - vector_uint2 _viewportSize {}; - - simd::float4 colorTop {0, 0, 1, 1}; - simd::float4 colorLeft {0, 1, 0, 1}; - simd::float4 colorRight {1, 0, 0, 1}; - - uint64_t frameCounter {0}; - - ExternalView::IMetalView* _metalView {nullptr}; - CVDisplayLinkRef _displayLink {nullptr}; - -#if !__has_feature(objc_arc) - ~ExampleMetalRenderer () noexcept - { - [_pipelineState release]; - [_commandQueue release]; - [_drawableRenderDescriptor release]; - [_device release]; - } -#endif - - bool init (ExternalView::IMetalView* metalView, CAMetalLayer* metalLayer) override - { - NSError* error = nullptr; - auto defaultLibrary = - [_device newLibraryWithSource:[NSString stringWithUTF8String:shaderCode] - options:nil - error:&error]; - if (error) - return false; - - auto vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"]; - auto fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"]; - if (!vertexFunction || !fragmentFunction) - return false; - - // Configure a pipeline descriptor that is used to create a pipeline state. - auto pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; - pipelineStateDescriptor.label = @"Simple Pipeline"; - pipelineStateDescriptor.vertexFunction = vertexFunction; - pipelineStateDescriptor.fragmentFunction = fragmentFunction; - pipelineStateDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm; - - _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor - error:&error]; - -#if !__has_feature(objc_arc) - [pipelineStateDescriptor release]; - [defaultLibrary release]; - [vertexFunction release]; - [fragmentFunction release]; -#endif - if (error) - return false; - - // Create the command queue - _commandQueue = [_device newCommandQueue]; - _drawableRenderDescriptor = [MTLRenderPassDescriptor new]; - _drawableRenderDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; - _drawableRenderDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore; - _drawableRenderDescriptor.colorAttachments[0].clearColor = MTLClearColorMake (0, 0, 0, 0); - - metalLayer.device = _device; - _metalView = metalView; - return true; - } - - void onSizeUpdate (int32_t width, int32_t height, double scaleFactor) override - { - _viewportSize.x = width * scaleFactor; - _viewportSize.y = height * scaleFactor; - } - - void updateColors () - { - ++frameCounter; - colorTop.x = (1.f + std::sin (frameCounter * 0.013f)) * 0.5f; - colorTop.y = (1.f + std::sin (frameCounter * 0.021f)) * 0.5f; - colorTop.z = (1.f + std::sin (frameCounter * 0.037f)) * 0.5f; - - colorLeft.x = (1.f + std::sin (frameCounter * 0.031f)) * 0.5f; - colorLeft.y = (1.f + std::sin (frameCounter * 0.021f)) * 0.5f; - colorLeft.z = (1.f + std::sin (frameCounter * 0.011f)) * 0.5f; - - colorRight.x = (1.f + std::sin (frameCounter * 0.025f)) * 0.5f; - colorRight.y = (1.f + std::sin (frameCounter * 0.012f)) * 0.5f; - colorRight.z = (1.f + std::sin (frameCounter * 0.031f)) * 0.5f; - } - - static CVReturn displayLinkRender (CVDisplayLinkRef displayLink, const CVTimeStamp* now, - const CVTimeStamp* outputTime, CVOptionFlags flagsIn, - CVOptionFlags* flagsOut, void* displayLinkContext) - { - auto Self = reinterpret_cast (displayLinkContext); - Self->updateColors (); - Self->_metalView->render (); - } - - void onAttached () override {} - - void onRemoved () override - { - CVDisplayLinkStop (_displayLink); - CVDisplayLinkRelease (_displayLink); - _displayLink = nullptr; - } - - void onScreenChanged (NSScreen* screen) override - { - if (_displayLink) - onRemoved (); - auto result = CVDisplayLinkCreateWithActiveCGDisplays (&_displayLink); - if (result != kCVReturnSuccess) - return; - result = CVDisplayLinkSetOutputCallback (_displayLink, displayLinkRender, this); - if (result != kCVReturnSuccess) - return; - auto displayID = static_cast ( - [screen.deviceDescription[@"NSScreenNumber"] unsignedIntValue]); - result = CVDisplayLinkSetCurrentCGDisplay (_displayLink, displayID); - if (result != kCVReturnSuccess) - return; - CVDisplayLinkStart (_displayLink); - } - - void draw (id drawable) override - { - if (!_pipelineState) - return; - - float width = _viewportSize.x * 0.5; - float height = _viewportSize.y * 0.5; - const AAPLVertex triangleVertices[] = { - // 2D positions, RGBA colors - {{width, -height}, colorRight}, - {{-width, -height}, colorLeft}, - {{0, height}, colorTop}, - }; - - // Create a new command buffer for each render pass to the current drawable. - auto commandBuffer = [_commandQueue commandBuffer]; - commandBuffer.label = @"MyCommand"; - - _drawableRenderDescriptor.colorAttachments[0].texture = drawable.texture; - - // Create a render command encoder. - auto renderEncoder = - [commandBuffer renderCommandEncoderWithDescriptor:_drawableRenderDescriptor]; - renderEncoder.label = @"MyRenderEncoder"; - - // Set the region of the drawable to draw into. - [renderEncoder setViewport:(MTLViewport) {0.0, 0.0, static_cast (_viewportSize.x), - static_cast (_viewportSize.y), 0.0, 1.0}]; - - [renderEncoder setRenderPipelineState:_pipelineState]; - - // Pass in the parameter data. - [renderEncoder setVertexBytes:triangleVertices - length:sizeof (triangleVertices) - atIndex:AAPLVertexInputIndexVertices]; - - [renderEncoder setVertexBytes:&_viewportSize - length:sizeof (_viewportSize) - atIndex:AAPLVertexInputIndexViewportSize]; - - // Draw the triangle. - [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3]; - - [renderEncoder endEncoding]; - - // Schedule a present once the framebuffer is complete using the current drawable. - [commandBuffer presentDrawable:drawable]; - - // Finalize rendering here & push the command buffer to the GPU. - [commandBuffer commit]; - } -}; - -//------------------------------------------------------------------------ -struct MetalController : DelegationController -{ - using DelegationController::DelegationController; - - CView* createView (const UIAttributes& attributes, const IUIDescription* description) override - { - if (auto viewName = attributes.getAttributeValue (IUIDescription::kCustomViewName)) - { - if (*viewName == "MetalView") - { - auto renderer = std::make_shared (); - if (auto metalView = ExternalView::MetalView::make (renderer)) - { - return new CExternalView ({}, metalView); - } - } - } - return DelegationController::createView (attributes, description); - } -}; - -//------------------------------------------------------------------------ -WindowPtr makeNewMetalExampleWindow () -{ - auto customization = UIDesc::Customization::make (); - customization->addCreateViewControllerFunc ( - "MetalController", [] (auto, auto parent, auto) { return new MetalController (parent); }); - - UIDesc::Config config; - config.uiDescFileName = "metalwindow.uidesc"; - config.viewName = "view"; - config.windowConfig.type = WindowType::Document; - config.windowConfig.style.close ().size ().border (); - config.windowConfig.title = "Metal Example"; - config.customization = customization; - - return UIDesc::makeWindow (config); -} - -//------------------------------------------------------------------------ -} // Standalone -} // VSTGUI diff --git a/vstgui/standalone/examples/standalone/source/testappdelegate.cpp b/vstgui/standalone/examples/standalone/source/testappdelegate.cpp index e30f81668..0b510a1f2 100644 --- a/vstgui/standalone/examples/standalone/source/testappdelegate.cpp +++ b/vstgui/standalone/examples/standalone/source/testappdelegate.cpp @@ -1,4 +1,4 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE @@ -23,19 +23,8 @@ #include "vstgui/standalone/source/genericalertbox.h" -#include "vstgui/uidescription/uiattributes.h" -#include "vstgui/uidescription/iuidescription.h" -#include "vstgui/lib/cexternalview.h" -#include "vstgui/contrib/datepicker.h" - #include -#if MAC -#include "metalwindow.h" -#elif WINDOWS -#include "direct3dwindow.h" -#endif - //------------------------------------------------------------------------ namespace MyApp { @@ -44,8 +33,8 @@ using namespace VSTGUI::Standalone; //------------------------------------------------------------------------ class Delegate : public Application::DelegateAdapter, - public ICommandHandler, - public WindowListenerAdapter + public ICommandHandler, + public WindowListenerAdapter { public: Delegate (); @@ -69,19 +58,14 @@ class Delegate : public Application::DelegateAdapter, //------------------------------------------------------------------------ Application::Init gAppDelegate (std::make_unique (), - {{Application::ConfigKey::ShowCommandsInContextMenu, 1}}); + {{Application::ConfigKey::ShowCommandsInContextMenu, 1}}); static Command NewPopup {CommandGroup::File, "New Popup"}; static Command ShowAlertBoxDesign {CommandGroup::File, "Show AlertBox Design"}; -#if MAC -static Command NewMetalExampleWindow {CommandGroup::File, "New Metal Example Window"}; -#elif WINDOWS -static Command NewDirect3DExampleWindow {CommandGroup::File, "New Direct3D Example Window"}; -#endif //------------------------------------------------------------------------ class DisabledControlsController : public DelegationController, - public ViewListenerAdapter + public ViewListenerAdapter { public: DisabledControlsController (IController* parent) : DelegationController (parent) {} @@ -93,9 +77,9 @@ class DisabledControlsController : public DelegationController, } controls.clear (); } - + CView* verifyView (CView* view, const UIAttributes& attributes, - const IUIDescription* description) override + const IUIDescription* description) override { if (auto control = dynamic_cast (view)) { @@ -134,7 +118,7 @@ class WeekdaysListConfigurator : public StaticListControlConfigurator : StaticListControlConfigurator (c.getRowHeight (), c.getFlags ()) { } - + CListControlRowDesc getRowDesc (int32_t row) const override { if (row == 0) @@ -148,14 +132,13 @@ class WeekdaysController : public DelegationController { public: WeekdaysController (IController* parent) : DelegationController (parent) {} - + CView* verifyView (CView* view, const UIAttributes& attributes, - const IUIDescription* description) override + const IUIDescription* description) override { if (auto listControl = dynamic_cast (view)) { - auto configurator = - dynamic_cast (listControl->getConfigurator ()); + auto configurator = dynamic_cast (listControl->getConfigurator ()); if (configurator) { listControl->setConfigurator (makeOwned (*configurator)); @@ -163,34 +146,7 @@ class WeekdaysController : public DelegationController } return controller->verifyView (view, attributes, description); } -}; -//------------------------------------------------------------------------ -class DatePickerController : public DelegationController -{ -public: - DatePickerController (IController* parent) : DelegationController (parent) {} - -#if MAC || WINDOWS - CView* createView (const UIAttributes& attributes, const IUIDescription* description) override - { - if (auto customViewName = attributes.getAttributeValue (IUIDescription::kCustomViewName)) - { - if (*customViewName == "DatePicker") - { - auto datePicker = std::make_shared (); - datePicker->setDate ({12, 8, 2023}); - datePicker->setChangeCallback ([] (auto date) { -#if DEBUG - DebugPrint ("%d.%d.%d\n", date.day, date.month, date.year); -#endif - }); - return new CExternalView (CRect (), datePicker); - } - } - return controller->createView (attributes, description); - } -#endif }; //------------------------------------------------------------------------ @@ -206,11 +162,6 @@ void Delegate::finishLaunching () IApplication::instance ().registerCommand (Commands::NewDocument, 'n'); IApplication::instance ().registerCommand (NewPopup, 'N'); IApplication::instance ().registerCommand (ShowAlertBoxDesign, 'b'); -#if MAC - IApplication::instance ().registerCommand (NewMetalExampleWindow, 'M'); -#elif WINDOWS - IApplication::instance ().registerCommand (NewDirect3DExampleWindow, 'D'); -#endif handleCommand (Commands::NewDocument); } @@ -226,13 +177,6 @@ void Delegate::onClosed (const IWindow& window) //------------------------------------------------------------------------ bool Delegate::canHandleCommand (const Command& command) { -#if MAC - if (command == NewMetalExampleWindow) - return true; -#elif WINDOWS - if (command == NewDirect3DExampleWindow) - return true; -#endif return command == Commands::NewDocument || command == NewPopup || command == ShowAlertBoxDesign; } @@ -261,20 +205,15 @@ bool Delegate::handleCommand (const Command& command) config.windowConfig.style.movableByWindowBackground (); auto customization = UIDesc::Customization::make (); customization->addCreateViewControllerFunc ( - "DisabledControlsController", - [] (const UTF8StringView&, IController* parent, const IUIDescription*) { - return new DisabledControlsController (parent); - }); - customization->addCreateViewControllerFunc ( - "WeekdaysController", - [] (const UTF8StringView&, IController* parent, const IUIDescription*) { - return new WeekdaysController (parent); - }); + "DisabledControlsController", + [] (const UTF8StringView&, IController* parent, const IUIDescription*) { + return new DisabledControlsController (parent); + }); customization->addCreateViewControllerFunc ( - "DatePickerController", - [] (const UTF8StringView&, IController* parent, const IUIDescription*) { - return new DatePickerController (parent); - }); + "WeekdaysController", + [] (const UTF8StringView&, IController* parent, const IUIDescription*) { + return new WeekdaysController (parent); + }); config.customization = customization; } if (auto window = UIDesc::makeWindow (config)) @@ -290,31 +229,25 @@ bool Delegate::handleCommand (const Command& command) window->show (); return true; } -#if MAC - else if (command == NewMetalExampleWindow) - { - if (auto window = makeNewMetalExampleWindow ()) - window->show (); - return true; - } -#elif WINDOWS - else if (command == NewDirect3DExampleWindow) - { - if (auto window = makeNewDirect3DExampleWindow ()) - window->show (); - return true; - } -#endif return false; } //------------------------------------------------------------------------ -void Delegate::showAboutDialog () { About::show (); } +void Delegate::showAboutDialog () +{ + About::show (); +} //------------------------------------------------------------------------ -bool Delegate::hasAboutDialog () { return true; } +bool Delegate::hasAboutDialog () +{ + return true; +} //------------------------------------------------------------------------ -VSTGUI::UTF8StringPtr Delegate::getSharedUIResourceFilename () const { return "resources.uidesc"; } +VSTGUI::UTF8StringPtr Delegate::getSharedUIResourceFilename () const +{ + return "resources.uidesc"; +} } // MyApp diff --git a/vstgui/standalone/source/platform/win32/win32window.cpp b/vstgui/standalone/source/platform/win32/win32window.cpp index 012fa98cb..cbf593031 100644 --- a/vstgui/standalone/source/platform/win32/win32window.cpp +++ b/vstgui/standalone/source/platform/win32/win32window.cpp @@ -6,6 +6,7 @@ #include "win32menu.h" #include "win32async.h" +#include "../../../../lib/platform/win32/direct2d/d2ddrawcontext.h" #include "../../../../lib/platform/win32/win32directcomposition.h" #include "../../../../lib/platform/win32/win32factory.h" #include "../../../../lib/platform/win32/win32frame.h" diff --git a/vstgui/uidescription/editing/uibitmapscontroller.cpp b/vstgui/uidescription/editing/uibitmapscontroller.cpp index b4bf7cc7d..ed4e1db1b 100644 --- a/vstgui/uidescription/editing/uibitmapscontroller.cpp +++ b/vstgui/uidescription/editing/uibitmapscontroller.cpp @@ -18,7 +18,6 @@ #include "../../lib/cfileselector.h" #include "../../lib/idatapackage.h" #include "../../lib/dragging.h" -#include "../../lib/cdrawcontext.h" #include "../../lib/cvstguitimer.h" #include "../../lib/controls/ccolorchooser.h" #include "../../lib/controls/ctextedit.h" diff --git a/vstgui/uidescription/editing/uieditview.cpp b/vstgui/uidescription/editing/uieditview.cpp index 9a0ec81a9..bf70a9a35 100644 --- a/vstgui/uidescription/editing/uieditview.cpp +++ b/vstgui/uidescription/editing/uieditview.cpp @@ -19,7 +19,6 @@ #include "../cstream.h" #include "../detail/uiviewcreatorattributes.h" #include "../../lib/cvstguitimer.h" -#include "../../lib/cexternalview.h" #include "../../lib/cframe.h" #include "../../lib/cscrollview.h" #include "../../lib/cdropsource.h" @@ -34,7 +33,7 @@ namespace VSTGUI { namespace UIEditViewInternal { - + //---------------------------------------------------------------------------------------------------- class UISelectionView : public UIOverlayView, public UISelectionListenerAdapter //---------------------------------------------------------------------------------------------------- @@ -206,67 +205,8 @@ void UIHighlightView::draw (CDrawContext* pContext) pContext->drawRect (r, kDrawFilledAndStroked); } -//------------------------------------------------------------------------ -template -void collectExternalViewsOnInlineEditing (CViewContainer* container, T& array) -{ - container->forEachChild ([&] (auto view) { - if (view.template cast ()) - array.emplace_back (view); - else if (auto c = view->asViewContainer ()) - collectExternalViewsOnInlineEditing (c, array); - }); -} - } // UIEditViewInternal -//------------------------------------------------------------------------ -struct UIEditView::ViewAddedObserver : IViewAddedRemovedObserver, - ViewListenerAdapter -{ - ~ViewAddedObserver () override - { - for (auto view : views) - { - if (auto viewEmbedder = dynamic_cast (view)) - { - if (auto ev = viewEmbedder->getExternalView ()) - ev->setMouseEnabled (view->getMouseEnabled ()); - } - view->unregisterViewListener (this); - } - } - void onViewAdded (CFrame* frame, CView* view) override - { - if (auto viewEmbedder = dynamic_cast (view)) - { - if (auto ev = viewEmbedder->getExternalView ()) - ev->setMouseEnabled (false); - view->registerViewListener (this); - views.emplace_back (view); - } - } - void onViewRemoved (CFrame* frame, CView* view) override {} - - void viewWillDelete (CView* view) override - { - view->unregisterViewListener (this); - auto it = std::find (views.begin (), views.end (), view); - if (it != views.end ()) - views.erase (it); - } - void viewOnMouseEnabled (CView* view, bool state) override - { - if (auto viewEmbedder = dynamic_cast (view)) - { - if (auto ev = viewEmbedder->getExternalView ()) - ev->setMouseEnabled (false); - } - } - - std::vector views; -}; - //---------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------- @@ -334,7 +274,7 @@ void UIEditView::enableEditing (bool state) CFrame* parent = getFrame (); if (parent == nullptr) return; - + if (editing) { CRect r = parent->getViewSize (); @@ -346,7 +286,7 @@ void UIEditView::enableEditing (bool state) overlayView->setTransparency (true); overlayView->setZIndex (std::numeric_limits::max () - 1); parent->addView (overlayView); - + highlightView = new UIEditViewInternal::UIHighlightView (this, viewHighlightColor); overlayView->addView (highlightView); auto selectionView = new UIEditViewInternal::UISelectionView (this, getSelection (), viewSelectionColor, kResizeHandleSize); @@ -359,25 +299,6 @@ void UIEditView::enableEditing (bool state) highlightView = nullptr; lines = nullptr; } - disableExternalViewsOnInlineEditing (editing); - } -} - -//------------------------------------------------------------------------ -void UIEditView::disableExternalViewsOnInlineEditing (bool state) -{ - CFrame* parent = getFrame (); - if (editingViewAddedObserver) - parent->setViewAddedRemovedObserver (nullptr); - editingViewAddedObserver.reset (); - if (state) - { - editingViewAddedObserver = std::make_unique (); - std::vector views; - UIEditViewInternal::collectExternalViewsOnInlineEditing (this, views); - for (auto* v : views) - editingViewAddedObserver->onViewAdded (parent, v); - parent->setViewAddedRemovedObserver (editingViewAddedObserver.get ()); } } @@ -428,7 +349,6 @@ void UIEditView::setEditView (CView* view) { if (view != getEditView ()) { - disableExternalViewsOnInlineEditing (false); invalid (); removeAll (); CRect vs (getViewSize ()); @@ -444,7 +364,6 @@ void UIEditView::setEditView (CView* view) setMouseableArea (vs); setViewSize (vs); } - disableExternalViewsOnInlineEditing (editing); invalid (); } } @@ -502,7 +421,7 @@ void UIEditView::drawRect (CDrawContext *pContext, const CRect& updateRect) if (!editing && focusDrawing) getFrame ()->setFocusDrawingEnabled (focusDrawing); - + pContext->setClipRect (updateRect); CDrawContext::Transform transform (*pContext, CGraphicsTransform ().translate (getViewSize ().left, getViewSize ().top)); @@ -1423,12 +1342,6 @@ bool UIEditView::attached (CView* parent) //----------------------------------------------------------------------------- bool UIEditView::removed (CView* parent) { - auto frame = getFrame (); - if (editingViewAddedObserver) - { - frame->setViewAddedRemovedObserver (nullptr); - editingViewAddedObserver.reset (); - } IController* controller = getViewController (this, true); if (controller) { @@ -1438,10 +1351,9 @@ bool UIEditView::removed (CView* parent) } if (overlayView) { - frame->removeView (overlayView); + getFrame()->removeView (overlayView); overlayView = nullptr; } - frame->setCursor (kCursorDefault); return CViewContainer::removed (parent); } diff --git a/vstgui/uidescription/editing/uieditview.h b/vstgui/uidescription/editing/uieditview.h index 34fa18200..d2069a73f 100644 --- a/vstgui/uidescription/editing/uieditview.h +++ b/vstgui/uidescription/editing/uieditview.h @@ -140,10 +140,6 @@ class UIEditView : public CViewContainer, public IDropTarget CColor lassoFrameColor; CColor viewHighlightColor; CColor viewSelectionColor; - - struct ViewAddedObserver; - std::unique_ptr editingViewAddedObserver; - void disableExternalViewsOnInlineEditing (bool state); }; } // VSTGUI diff --git a/vstgui/uidescription/editing/uigradientscontroller.cpp b/vstgui/uidescription/editing/uigradientscontroller.cpp index 3909780ac..1e1e63d80 100644 --- a/vstgui/uidescription/editing/uigradientscontroller.cpp +++ b/vstgui/uidescription/editing/uigradientscontroller.cpp @@ -15,7 +15,6 @@ #include "../../lib/cgradientview.h" #include "../../lib/ifocusdrawing.h" #include "../../lib/cgraphicspath.h" -#include "../../lib/cdrawcontext.h" #include "../../lib/events.h" #include diff --git a/vstgui/uidescription/editing/uiselection.cpp b/vstgui/uidescription/editing/uiselection.cpp index 16bdc13bf..41a5f0f7f 100644 --- a/vstgui/uidescription/editing/uiselection.cpp +++ b/vstgui/uidescription/editing/uiselection.cpp @@ -326,7 +326,16 @@ SharedPointer createBitmapFromSelection (UISelection* selection, CFrame CDrawContext::Transform transform (context, CGraphicsTransform ().translate (p.x, p.y)); context.setClipRect (view->getViewSize ()); - view->drawRect (&context, view->getViewSize ()); + if (IPlatformViewLayerDelegate* layer = view.cast ()) + { + CRect r (view->getViewSize ()); + r.originize (); + layer->drawViewLayer (&context, r); + } + else + { + view->drawRect (&context, view->getViewSize ()); + } } } if (anchorView && anchorView->isAttached ()) diff --git a/vstgui/vstgui.cpp b/vstgui/vstgui.cpp index 18c979d6f..66c4dc818 100644 --- a/vstgui/vstgui.cpp +++ b/vstgui/vstgui.cpp @@ -10,7 +10,6 @@ #include "lib/cdrawcontext.cpp" #include "lib/cdrawmethods.cpp" #include "lib/cdropsource.cpp" -#include "lib/cexternalview.cpp" #include "lib/cfileselector.cpp" #include "lib/cfont.cpp" #include "lib/cframe.cpp" diff --git a/vstgui/vstgui_ios.mm b/vstgui/vstgui_ios.mm index 73325661c..6d1cc7904 100644 --- a/vstgui/vstgui_ios.mm +++ b/vstgui/vstgui_ios.mm @@ -4,7 +4,7 @@ #import "vstgui.cpp" -#import "lib/platform/mac/coregraphicsdevicecontext.mm" +#import "lib/platform/mac/cgdrawcontext.cpp" #import "lib/platform/mac/macglobals.cpp" #import "lib/platform/mac/cgbitmap.cpp" #import "lib/platform/mac/quartzgraphicspath.cpp" diff --git a/vstgui/vstgui_linux.cpp b/vstgui/vstgui_linux.cpp index 4f5ed8c21..2eef695a3 100644 --- a/vstgui/vstgui_linux.cpp +++ b/vstgui/vstgui_linux.cpp @@ -10,7 +10,7 @@ #include "lib/platform/linux/x11utils.cpp" #include "lib/platform/linux/cairobitmap.cpp" -#include "lib/platform/linux/cairographicscontext.cpp" +#include "lib/platform/linux/cairocontext.cpp" #include "lib/platform/linux/cairofont.cpp" #include "lib/platform/linux/cairogradient.cpp" #include "lib/platform/linux/cairopath.cpp" diff --git a/vstgui/vstgui_mac.mm b/vstgui/vstgui_mac.mm index 30ce1063c..c19da63cd 100644 --- a/vstgui/vstgui_mac.mm +++ b/vstgui/vstgui_mac.mm @@ -4,9 +4,9 @@ #import "vstgui.cpp" +#import "lib/platform/mac/cgdrawcontext.cpp" #import "lib/platform/mac/macglobals.cpp" #import "lib/platform/mac/cgbitmap.cpp" -#import "lib/platform/mac/coregraphicsdevicecontext.mm" #import "lib/platform/mac/quartzgraphicspath.cpp" #import "lib/platform/mac/macfactory.mm" #import "lib/platform/mac/macfileselector.mm" diff --git a/vstgui/vstgui_win32.cpp b/vstgui/vstgui_win32.cpp index b853aad68..d2c03ef84 100644 --- a/vstgui/vstgui_win32.cpp +++ b/vstgui/vstgui_win32.cpp @@ -1,4 +1,4 @@ -// This file is part of VSTGUI. It is subject to the license terms +// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE @@ -24,7 +24,7 @@ #include "lib/platform/win32/wintimer.cpp" #include "lib/platform/win32/direct2d/d2dbitmap.cpp" #include "lib/platform/win32/direct2d/d2dbitmapcache.cpp" +#include "lib/platform/win32/direct2d/d2ddrawcontext.cpp" #include "lib/platform/win32/direct2d/d2dfont.cpp" #include "lib/platform/win32/direct2d/d2dgradient.cpp" #include "lib/platform/win32/direct2d/d2dgraphicspath.cpp" -#include "lib/platform/win32/direct2d/d2dgraphicscontext.cpp"