From 25a055491ee8ef664497d480e1d0e06a71bbea59 Mon Sep 17 00:00:00 2001 From: Ocraftyone Date: Mon, 4 Dec 2023 10:21:49 -0500 Subject: [PATCH] Update wxWidgets to v3.2.1 (#2905) * Upgrade wxWidgets to 3.2.1 Based on prusa3d/PrusaSlicer@9a7e024 Co-authored-by: tamasmeszaros * Implement BitmapCache * update wxExtensions while keeping legacy items * update dc.DrawBitmap calls to use get_bitmap * Fix GetSize/Width/Height calls * update BitmapComboBox * fix ifndef in wxExtensions.hpp * update my todos to OcraftyoneTODO * Get to a compilable state Everything seems to be working (including the plater). I am not seeing any graphical issues * fix extruder color icons * fix crash on opening support tab * remove GetBmpSize method from DropDown.cpp * Update TextInput to use bitmap bundles * update a TODO after testing * fix the rendering of the icons on combobox * fix a few todos * fix WipeTowerDialog.cpp * Overhaul WipeTowerDialog Removed simple version of the dialog since BBS removed the functionality but left the code. Center the table (only seen when the table is smaller than the minimum size of the dialog) Fix issue where editing a value causes the m_min_flush_label to change colors slightly Fix an issue where changing a value or running an auto calc changes the disabled value from "-" to "0" * update a few todos * Update some todos * Show dropdown when editing is started * Update NanoSVG.cmake Update NanoSVG to work with PR #2780 * Dim the icon on ComboBox when disabled * solve ObjectDataViewModel todos leaving colPrint and colEditing cases alone as it does not seem to impact anything * Update names in wxExtensions -Rename msw_rescale to sys_color_changed -Replace GetBmpSize, GetBmpWidth, GetBmpHeight with renamed version (same name without "Bmp") Both of these changes were also made by PrusaSlicer. Original Commit: Prusa3D/PrusaSlicer@066b567 Co-authored-by: YuSanka * update BitmapCache::from_svg disable finding bundle in the cache to match load_svg update to match values used in load_svg * Update ScalableButton change the signature and remove functions/vars pertaining to a default bmp fix TODOs in ScalableButton Original Commit: Prusa3D/PrusaSlicer@066b567 Co-authored-by: YuSanka * fix up some more todos in wxExtensions * update ScalableBitmap to use bmp bundles use wxBitmapBundle by default add flag to use old scaled bitmap function (specifically to solve issue with advanced toggle) * attempt to fix macos deps build * fix ubuntu build * Revert "attempt to fix macos deps build" Mistakenly made change to wrong file This reverts commit d9c20b51216db1d409aeb0420d9d901000ad1b00. * update wxWidgets patch an attempt to fix macOS build * Remove duplicate variable from OrcaSlicer.cpp * Fix macOS build issue * Fix blank DataViewItem being added to objects list * Filament ComboBox editor updates -Add show drop down feature to ObjectTable -Call finish editing when ComboBox is closed in ObjectList * remove Apple specific declarations missed during refactor * delete old wxWidgets patch * fix ubuntu seg fault * include patch from #2926 * update patch to include wxWidgets/wxWidgets@991a74c * fix deps not compiling on Windows * update WipeTowerDialog relocates the recalculate button back to its previous position changes the wording of the tip message label add spacing below the matrix * finish patching wxWidgets from prusa3d/PrusaSlicer@f8477d1 and prusa3d/PrusaSlicer@066b567 Co-authored-by: YuSanka * fix combobox crash * revert outside plate changes --------- Co-authored-by: tamasmeszaros Co-authored-by: YuSanka --- deps/CMakeLists.txt | 1 + deps/NanoSVG/NanoSVG.cmake | 9 + ...=> 0001-patch-v3.2.1-for-OrcaSlicer.patch} | 276 +- deps/wxWidgets/wxWidgets.cmake | 15 +- src/CMakeLists.txt | 5 +- src/OrcaSlicer.cpp | 1 - src/nanosvg/README.txt | 1 - src/nanosvg/nanosvg.h | 3106 ----------------- src/nanosvg/nanosvgrast.h | 1482 -------- src/slic3r/CMakeLists.txt | 4 +- src/slic3r/GUI/AMSMaterialsSetting.cpp | 2 +- src/slic3r/GUI/AboutDialog.cpp | 10 +- src/slic3r/GUI/AmsMappingPopup.cpp | 14 +- src/slic3r/GUI/Auxiliary.cpp | 20 +- src/slic3r/GUI/BBLTopbar.cpp | 4 +- src/slic3r/GUI/BitmapCache.cpp | 463 ++- src/slic3r/GUI/BitmapCache.hpp | 38 +- src/slic3r/GUI/BitmapComboBox.cpp | 90 +- src/slic3r/GUI/BitmapComboBox.hpp | 17 +- src/slic3r/GUI/CalibrationPanel.cpp | 4 +- src/slic3r/GUI/CalibrationWizardPage.cpp | 14 +- .../GUI/CalibrationWizardPresetPage.cpp | 6 +- src/slic3r/GUI/CameraPopup.cpp | 8 +- src/slic3r/GUI/ConfigWizard.cpp | 28 +- src/slic3r/GUI/ConfigWizard_private.hpp | 2 +- src/slic3r/GUI/DragCanvas.cpp | 4 +- src/slic3r/GUI/ExtraRenderers.cpp | 24 +- src/slic3r/GUI/ExtraRenderers.hpp | 4 +- src/slic3r/GUI/Field.cpp | 4 +- src/slic3r/GUI/GLTexture.cpp | 4 +- src/slic3r/GUI/GUI_App.cpp | 2 +- src/slic3r/GUI/GUI_Factories.cpp | 40 +- src/slic3r/GUI/GUI_Factories.hpp | 5 +- src/slic3r/GUI/GUI_ObjectLayers.cpp | 41 +- src/slic3r/GUI/GUI_ObjectList.cpp | 42 +- src/slic3r/GUI/GUI_ObjectList.hpp | 2 +- src/slic3r/GUI/GUI_ObjectSettings.cpp | 20 +- src/slic3r/GUI/GUI_ObjectSettings.hpp | 1 - src/slic3r/GUI/GUI_ObjectTable.cpp | 37 +- src/slic3r/GUI/GUI_ObjectTable.hpp | 18 +- src/slic3r/GUI/GUI_ObjectTableSettings.cpp | 4 +- src/slic3r/GUI/ImGuiWrapper.cpp | 5 +- src/slic3r/GUI/ImageGrid.cpp | 12 +- src/slic3r/GUI/KBShortcutsDialog.cpp | 2 +- src/slic3r/GUI/MainFrame.cpp | 7 +- src/slic3r/GUI/MediaFilePanel.cpp | 6 +- src/slic3r/GUI/ModelMall.cpp | 6 +- src/slic3r/GUI/MsgDialog.cpp | 6 +- src/slic3r/GUI/Notebook.cpp | 10 + src/slic3r/GUI/Notebook.hpp | 6 + src/slic3r/GUI/OG_CustomCtrl.cpp | 46 +- src/slic3r/GUI/OG_CustomCtrl.hpp | 2 +- src/slic3r/GUI/ObjectDataViewModel.cpp | 130 +- src/slic3r/GUI/ObjectDataViewModel.hpp | 47 +- src/slic3r/GUI/OptionsGroup.cpp | 3 +- src/slic3r/GUI/ParamsPanel.cpp | 14 +- src/slic3r/GUI/PhysicalPrinterDialog.cpp | 14 +- src/slic3r/GUI/PhysicalPrinterDialog.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 51 +- src/slic3r/GUI/PresetComboBoxes.cpp | 207 +- src/slic3r/GUI/PresetComboBoxes.hpp | 22 +- src/slic3r/GUI/RecenterDialog.cpp | 6 +- src/slic3r/GUI/SavePresetDialog.cpp | 4 +- src/slic3r/GUI/Search.cpp | 12 +- src/slic3r/GUI/Search.hpp | 2 +- src/slic3r/GUI/SelectMachine.cpp | 78 +- src/slic3r/GUI/StatusPanel.cpp | 26 +- src/slic3r/GUI/SysInfoDialog.cpp | 4 +- src/slic3r/GUI/Tab.cpp | 118 +- src/slic3r/GUI/Tab.hpp | 6 - src/slic3r/GUI/TabButton.cpp | 10 +- src/slic3r/GUI/UnsavedChangesDialog.cpp | 54 +- src/slic3r/GUI/UpgradePanel.cpp | 20 +- src/slic3r/GUI/Widgets/AMSControl.cpp | 51 +- src/slic3r/GUI/Widgets/AxisCtrlButton.cpp | 3 +- src/slic3r/GUI/Widgets/Button.cpp | 10 +- src/slic3r/GUI/Widgets/CheckBox.cpp | 24 +- src/slic3r/GUI/Widgets/ComboBox.cpp | 39 +- src/slic3r/GUI/Widgets/ComboBox.hpp | 13 +- src/slic3r/GUI/Widgets/DropDown.cpp | 27 +- src/slic3r/GUI/Widgets/DropDown.hpp | 6 +- src/slic3r/GUI/Widgets/FanControl.cpp | 17 +- src/slic3r/GUI/Widgets/ImageSwitchButton.cpp | 16 +- src/slic3r/GUI/Widgets/RadioBox.cpp | 10 +- src/slic3r/GUI/Widgets/SideButton.cpp | 8 +- src/slic3r/GUI/Widgets/SideTools.cpp | 36 +- src/slic3r/GUI/Widgets/StaticLine.cpp | 12 +- src/slic3r/GUI/Widgets/StepCtrl.cpp | 30 +- src/slic3r/GUI/Widgets/SwitchButton.cpp | 10 +- src/slic3r/GUI/Widgets/TabCtrl.cpp | 11 +- src/slic3r/GUI/Widgets/TabCtrl.hpp | 3 - src/slic3r/GUI/Widgets/TempInput.cpp | 28 +- src/slic3r/GUI/Widgets/TextInput.cpp | 12 +- src/slic3r/GUI/Widgets/TextInput.hpp | 2 +- src/slic3r/GUI/WipeTowerDialog.cpp | 310 +- src/slic3r/GUI/WipeTowerDialog.hpp | 20 +- src/slic3r/GUI/wxExtensions.cpp | 264 +- src/slic3r/GUI/wxExtensions.hpp | 88 +- 98 files changed, 1639 insertions(+), 6221 deletions(-) create mode 100644 deps/NanoSVG/NanoSVG.cmake rename deps/wxWidgets/{0001-wxWidget-fix.patch => 0001-patch-v3.2.1-for-OrcaSlicer.patch} (75%) delete mode 100644 src/nanosvg/README.txt delete mode 100644 src/nanosvg/nanosvg.h delete mode 100644 src/nanosvg/nanosvgrast.h diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index aa158a69b4..f07cfacba9 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -236,6 +236,7 @@ endif () include(JPEG/JPEG.cmake) include(TIFF/TIFF.cmake) +include(NanoSVG/NanoSVG.cmake) include(wxWidgets/wxWidgets.cmake) include(OCCT/OCCT.cmake) include(FREETYPE/FREETYPE.cmake) diff --git a/deps/NanoSVG/NanoSVG.cmake b/deps/NanoSVG/NanoSVG.cmake new file mode 100644 index 0000000000..f02893efc9 --- /dev/null +++ b/deps/NanoSVG/NanoSVG.cmake @@ -0,0 +1,9 @@ +# In PrusaSlicer 2.6.0 we switched from https://github.com/memononen/nanosvg to its fork https://github.com/fltk/nanosvg +# because this last implements the new function nsvgRasterizeXY() which we now use in GLTexture::load_from_svg() +# for rasterizing svg files from their original size to a squared power of two texture on Windows systems using +# AMD Radeon graphics cards + +orcaslicer_add_cmake_project(NanoSVG + URL https://github.com/fltk/nanosvg/archive/abcd277ea45e9098bed752cf9c6875b533c0892f.zip + URL_HASH SHA256=e859938fbaee4b351bd8a8b3d3c7a75b40c36885ce00b73faa1ce0b98aa0ad34 +) \ No newline at end of file diff --git a/deps/wxWidgets/0001-wxWidget-fix.patch b/deps/wxWidgets/0001-patch-v3.2.1-for-OrcaSlicer.patch similarity index 75% rename from deps/wxWidgets/0001-wxWidget-fix.patch rename to deps/wxWidgets/0001-patch-v3.2.1-for-OrcaSlicer.patch index 1104d5d147..e96c1f4d52 100644 --- a/deps/wxWidgets/0001-wxWidget-fix.patch +++ b/deps/wxWidgets/0001-patch-v3.2.1-for-OrcaSlicer.patch @@ -1,24 +1,36 @@ -diff --git a/build/cmake/init.cmake b/build/cmake/init.cmake -index 0bc4f934b9..479431a69c 100644 ---- a/build/cmake/init.cmake -+++ b/build/cmake/init.cmake -@@ -413,7 +413,11 @@ if(wxUSE_GUI) - else() - find_package(OpenGL) - if(WXGTK3 AND OpenGL_EGL_FOUND AND wxUSE_GLCANVAS_EGL) -+ if(UNIX AND NOT APPLE) -+ set(OPENGL_LIBRARIES OpenGL EGL) -+ else() - set(OPENGL_LIBRARIES OpenGL::OpenGL OpenGL::EGL) -+ endif() - find_package(WAYLANDEGL) - if(WAYLANDEGL_FOUND AND wxHAVE_GDK_WAYLAND) - list(APPEND OPENGL_LIBRARIES ${WAYLANDEGL_LIBRARIES}) +From f4fef135f0a58ca2916c45cd539923ab096935b6 Mon Sep 17 00:00:00 2001 +From: Ocraftyone +Date: Thu, 30 Nov 2023 03:25:54 -0500 +Subject: [PATCH] patch v3.2.1 for OrcaSlicer + +--- + build/cmake/lib/webview/CMakeLists.txt | 4 +- + include/wx/fontutil.h | 15 +++++++- + include/wx/gdicmn.h | 3 ++ + include/wx/generic/grid.h | 4 +- + include/wx/msw/font.h | 2 +- + include/wx/msw/tooltip.h | 4 +- + include/wx/osx/app.h | 2 +- + src/common/combocmn.cpp | 11 +++++- + src/common/datavcmn.cpp | 6 ++- + src/common/dcbufcmn.cpp | 6 +++ + src/common/gdicmn.cpp | 14 +++++++ + src/common/image.cpp | 6 +-- + src/generic/grid.cpp | 50 ++++++++++++++++++++----- + src/msw/bmpcbox.cpp | 9 ++++- + src/msw/font.cpp | 14 +++---- + src/msw/menuitem.cpp | 2 + + src/msw/window.cpp | 52 +++++++++++++++++--------- + src/osx/cocoa/dataview.mm | 26 +++++++++++-- + src/osx/cocoa/settings.mm | 6 +-- + src/osx/cocoa/window.mm | 4 ++ + 20 files changed, 184 insertions(+), 56 deletions(-) + diff --git a/build/cmake/lib/webview/CMakeLists.txt b/build/cmake/lib/webview/CMakeLists.txt -index cc3298ff33..8adbeaea4f 100644 +index 085381d785..62146abc04 100644 --- a/build/cmake/lib/webview/CMakeLists.txt +++ b/build/cmake/lib/webview/CMakeLists.txt -@@ -56,7 +56,7 @@ if(APPLE) +@@ -46,9 +46,9 @@ if(APPLE) elseif(WXMSW) if(wxUSE_WEBVIEW_EDGE) # Update the following variables if updating WebView2 SDK @@ -31,7 +43,7 @@ index cc3298ff33..8adbeaea4f 100644 set(WEBVIEW2_DEFAULT_PACKAGE_DIR "${CMAKE_BINARY_DIR}/packages/Microsoft.Web.WebView2.${WEBVIEW2_VERSION}") diff --git a/include/wx/fontutil.h b/include/wx/fontutil.h -index 09ad8c8ef3..3c0c2d8f7e 100644 +index 30529db8ce..e6a12366d5 100644 --- a/include/wx/fontutil.h +++ b/include/wx/fontutil.h @@ -294,7 +294,11 @@ public: @@ -69,7 +81,7 @@ index 09ad8c8ef3..3c0c2d8f7e 100644 } diff --git a/include/wx/gdicmn.h b/include/wx/gdicmn.h -index e29a77627c..dc48cf9451 100644 +index 2f5f8ee99f..39e9317d40 100644 --- a/include/wx/gdicmn.h +++ b/include/wx/gdicmn.h @@ -38,6 +38,7 @@ class WXDLLIMPEXP_FWD_CORE wxRegion; @@ -80,7 +92,7 @@ index e29a77627c..dc48cf9451 100644 // --------------------------------------------------------------------------- // constants -@@ -1092,7 +1093,9 @@ extern int WXDLLIMPEXP_CORE wxDisplayDepth(); +@@ -1106,7 +1107,9 @@ extern int WXDLLIMPEXP_CORE wxDisplayDepth(); // get the display size extern void WXDLLIMPEXP_CORE wxDisplaySize(int *width, int *height); @@ -91,13 +103,13 @@ index e29a77627c..dc48cf9451 100644 extern wxSize WXDLLIMPEXP_CORE wxGetDisplaySizeMM(); extern wxSize WXDLLIMPEXP_CORE wxGetDisplayPPI(); diff --git a/include/wx/generic/grid.h b/include/wx/generic/grid.h -index d7a3890764..e4dee51d5a 100644 +index 1bd58bbf04..903cb81319 100644 --- a/include/wx/generic/grid.h +++ b/include/wx/generic/grid.h -@@ -2951,9 +2951,11 @@ private: - wxGridWindow* gridWindow); - +@@ -3029,9 +3029,11 @@ private: // Update the width/height of the column/row being drag-resized. + // Should be only called when m_dragRowOrCol != -1, i.e. dragging is + // actually in progress. + //BBS: add cursor mode for DoGridDragResize's paremeters void DoGridDragResize(const wxPoint& position, const wxGridOperations& oper, @@ -151,19 +163,10 @@ index 317a0ca96f..58014ec1d4 100644 void OSXStoreOpenURL(const wxString &url ) { m_getURL = url ; } #endif diff --git a/src/common/combocmn.cpp b/src/common/combocmn.cpp -index b61aac35bf..d12b745e8c 100644 +index 80408c6677..aa07caebdc 100644 --- a/src/common/combocmn.cpp +++ b/src/common/combocmn.cpp -@@ -2141,7 +2141,7 @@ void wxComboCtrlBase::CreatePopup() - #if !USES_GENERICTLW - m_winPopup = new wxComboPopupWindowBase2( this, wxNO_BORDER ); - #else -- int tlwFlags = wxNO_BORDER; -+ int tlwFlags = wxNO_BORDER | wxSTAY_ON_TOP; - #ifdef wxCC_GENERIC_TLW_IS_FRAME - tlwFlags |= wxFRAME_NO_TASKBAR; - #endif -@@ -2285,6 +2285,9 @@ void wxComboCtrlBase::ShowPopup() +@@ -2061,6 +2061,9 @@ void wxComboCtrlBase::ShowPopup() SetFocus(); @@ -173,14 +176,14 @@ index b61aac35bf..d12b745e8c 100644 // Space above and below int screenHeight; wxPoint scrPos; -@@ -2407,9 +2410,13 @@ void wxComboCtrlBase::ShowPopup() +@@ -2183,9 +2186,13 @@ void wxComboCtrlBase::ShowPopup() int showFlags = CanDeferShow; - if ( spaceBelow < szp.y ) + int anchorSideVertical = m_anchorSide & (wxUP | wxDOWN); + if (// Pop up as asked for by the library user. -+ (anchorSideVertical & wxUP) || ++ (anchorSideVertical & wxUP) || + // Automatic: Pop up if it does not fit down. + (anchorSideVertical == 0 && spaceBelow < szp.y )) { @@ -190,10 +193,10 @@ index b61aac35bf..d12b745e8c 100644 } diff --git a/src/common/datavcmn.cpp b/src/common/datavcmn.cpp -index 1f5fd4d66b..14ea2f8ef1 100644 +index 0a1e43ad51..6c492aedab 100644 --- a/src/common/datavcmn.cpp +++ b/src/common/datavcmn.cpp -@@ -1322,7 +1322,11 @@ wxDataViewItem wxDataViewCtrlBase::GetSelection() const +@@ -1334,7 +1334,11 @@ wxDataViewItem wxDataViewCtrlBase::GetSelection() const wxDataViewItemArray selections; GetSelections(selections); @@ -207,28 +210,27 @@ index 1f5fd4d66b..14ea2f8ef1 100644 namespace diff --git a/src/common/dcbufcmn.cpp b/src/common/dcbufcmn.cpp -index 74958fce10..59844f4526 100644 +index 9b1c1f3159..ef5865ed4b 100644 --- a/src/common/dcbufcmn.cpp +++ b/src/common/dcbufcmn.cpp -@@ -82,9 +82,15 @@ private: +@@ -83,9 +83,15 @@ private: const double scale = dc ? dc->GetContentScaleFactor() : 1.0; wxBitmap* const buffer = new wxBitmap; +#if __WXMSW__ - // we must always return a valid bitmap but creating a bitmap of - // size 0 would fail, so create a 1*1 bitmap in this case -- buffer->CreateScaled(wxMax(w, 1), wxMax(h, 1), -1, scale); ++ // we must always return a valid bitmap but creating a bitmap of ++ // size 0 would fail, so create a 1*1 bitmap in this case + buffer->Create(wxMax(w, 1), wxMax(h, 1), 24); +#else -+ // we must always return a valid bitmap but creating a bitmap of -+ // size 0 would fail, so create a 1*1 bitmap in this case -+ buffer->CreateScaled(wxMax(w, 1), wxMax(h, 1), -1, scale); + // we must always return a valid bitmap but creating a bitmap of + // size 0 would fail, so create a 1*1 bitmap in this case + buffer->CreateWithDIPSize(wxMax(w, 1), wxMax(h, 1), scale); +#endif return buffer; } diff --git a/src/common/gdicmn.cpp b/src/common/gdicmn.cpp -index 20442bbc73..9a24951ec7 100644 +index db8a01f961..162c1ce2dc 100644 --- a/src/common/gdicmn.cpp +++ b/src/common/gdicmn.cpp @@ -863,11 +863,25 @@ void wxDisplaySize(int *width, int *height) @@ -257,72 +259,57 @@ index 20442bbc73..9a24951ec7 100644 void wxClientDisplayRect(int *x, int *y, int *width, int *height) { const wxRect rect = wxGetClientDisplayRect(); -diff --git a/src/common/intl.cpp b/src/common/intl.cpp -index 0b0d8798f4..7072fab18a 100644 ---- a/src/common/intl.cpp -+++ b/src/common/intl.cpp -@@ -1628,6 +1628,12 @@ GetInfoFromLCID(LCID lcid, - { - str = buf; - -+//FIXME Vojtech: We forcefully set the locales for a decimal point to "C", but this -+// is not possible for the Win32 locales, therefore there is a discrepancy. -+// It looks like we live with the discrepancy for at least half a year, so we will -+// suppress the assert until we fix Slic3r to properly switch to "C" locales just -+// for file import / export. -+#if 0 - // As we get our decimal point separator from Win32 and not the - // CRT there is a possibility of mismatch between them and this - // can easily happen if the user code called setlocale() -@@ -1641,6 +1647,7 @@ GetInfoFromLCID(LCID lcid, - "Decimal separator mismatch -- did you use setlocale()?" - "If so, use wxLocale to change the locale instead." - ); -+#endif - } - break; - +diff --git a/src/common/image.cpp b/src/common/image.cpp +index 19fe34ec91..a449b60930 100644 +--- a/src/common/image.cpp ++++ b/src/common/image.cpp +@@ -390,11 +390,11 @@ wxImage wxImage::ShrinkBy( int xFactor , int yFactor ) const + unsigned char red = pixel[0] ; + unsigned char green = pixel[1] ; + unsigned char blue = pixel[2] ; +- unsigned char alpha = 255 ; +- if ( source_alpha ) +- alpha = *(source_alpha + y_offset + x * xFactor + x1) ; + if ( !hasMask || red != maskRed || green != maskGreen || blue != maskBlue ) + { ++ unsigned char alpha = 255 ; ++ if ( source_alpha ) ++ alpha = *(source_alpha + y_offset + x * xFactor + x1) ; + if ( alpha > 0 ) + { + avgRed += red ; diff --git a/src/generic/grid.cpp b/src/generic/grid.cpp -index 41fd4524cf..f4a15cb839 100644 +index ed3d988994..d71cda122d 100644 --- a/src/generic/grid.cpp +++ b/src/generic/grid.cpp -@@ -3824,7 +3824,8 @@ void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event, wxGridRowLabelWindo +@@ -4068,7 +4068,8 @@ void wxGrid::ProcessRowColLabelMouseEvent( const wxGridOperations &oper, wxMouse + { + if ( m_cursorMode == oper.GetCursorModeResize() ) { - case WXGRID_CURSOR_RESIZE_ROW: - { -- DoGridDragResize(event.GetPosition(), wxGridRowOperations(), gridWindow); -+ //BBS: add cursor mode for DoGridDragResize's paremeters -+ DoGridDragResize(event.GetPosition(), wxGridRowOperations(), gridWindow, WXGRID_CURSOR_RESIZE_ROW); - } - break; - -@@ -4166,7 +4167,8 @@ void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event, wxGridColLabelWindo - switch ( m_cursorMode ) +- DoGridDragResize(event.GetPosition(), oper, gridWindow); ++ //BBS: add cursor mode for DoGridDragResize's paremeters ++ DoGridDragResize(event.GetPosition(), oper, gridWindow, m_cursorMode); + } + else if ( m_cursorMode == oper.GetCursorModeSelect() && line >=0 ) { - case WXGRID_CURSOR_RESIZE_COL: -- DoGridDragResize(event.GetPosition(), wxGridColumnOperations(), gridWindow); -+ //BBS: add cursor mode for DoGridDragResize's paremeters -+ DoGridDragResize(event.GetPosition(), wxGridColumnOperations(), gridWindow, WXGRID_CURSOR_RESIZE_COL); - break; - - case WXGRID_CURSOR_SELECT_COL: -@@ -4708,11 +4710,13 @@ bool wxGrid::DoGridDragEvent(wxMouseEvent& event, - return DoGridCellDrag(event, coords, isFirstDrag); +@@ -4691,12 +4692,14 @@ bool wxGrid::DoGridDragEvent(wxMouseEvent& event, case WXGRID_CURSOR_RESIZE_ROW: -- DoGridDragResize(event.GetPosition(), wxGridRowOperations(), gridWindow); -+ //BBS: add cursor mode for DoGridDragResize's paremeters -+ DoGridDragResize(event.GetPosition(), wxGridRowOperations(), gridWindow, WXGRID_CURSOR_RESIZE_ROW); + if ( m_dragRowOrCol != -1 ) +- DoGridDragResize(event.GetPosition(), wxGridRowOperations(), gridWindow); ++ //BBS: add cursor mode for DoGridDragResize's paremeters ++ DoGridDragResize(event.GetPosition(), wxGridRowOperations(), gridWindow, WXGRID_CURSOR_RESIZE_ROW); break; case WXGRID_CURSOR_RESIZE_COL: -- DoGridDragResize(event.GetPosition(), wxGridColumnOperations(), gridWindow); -+ //BBS: add cursor mode for DoGridDragResize's paremeters -+ DoGridDragResize(event.GetPosition(), wxGridColumnOperations(), gridWindow, WXGRID_CURSOR_RESIZE_COL); + if ( m_dragRowOrCol != -1 ) +- DoGridDragResize(event.GetPosition(), wxGridColumnOperations(), gridWindow); ++ //BBS: add cursor mode for DoGridDragResize's paremeters ++ DoGridDragResize(event.GetPosition(), wxGridColumnOperations(), gridWindow, WXGRID_CURSOR_RESIZE_COL); break; default: -@@ -4803,6 +4807,8 @@ wxGrid::DoGridCellLeftDown(wxMouseEvent& event, +@@ -4791,6 +4794,8 @@ wxGrid::DoGridCellLeftDown(wxMouseEvent& event, case wxGridSelectCells: case wxGridSelectRowsOrColumns: // nothing to do in these cases @@ -331,7 +318,7 @@ index 41fd4524cf..f4a15cb839 100644 break; case wxGridSelectRows: -@@ -5044,9 +5050,11 @@ void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event, wxGridWindow *eventG +@@ -5049,9 +5054,11 @@ void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event, wxGridWindow *eventG } } @@ -342,9 +329,9 @@ index 41fd4524cf..f4a15cb839 100644 + wxGridWindow* gridWindow, + CursorMode mode) { - // Get the logical position from the physical one we're passed. - const wxPoint -@@ -5056,10 +5064,28 @@ void wxGrid::DoGridDragResize(const wxPoint& position, + wxCHECK_RET( m_dragRowOrCol != -1, + "shouldn't be called when not drag resizing" ); +@@ -5064,10 +5071,28 @@ void wxGrid::DoGridDragResize(const wxPoint& position, // orthogonal direction. const int linePos = oper.Dual().Select(logicalPos); @@ -375,7 +362,7 @@ index 41fd4524cf..f4a15cb839 100644 // TODO: generate RESIZING event, see #10754, if the size has changed. } -@@ -5082,7 +5108,8 @@ wxPoint wxGrid::GetPositionForResizeEvent(int width) const +@@ -5090,7 +5115,8 @@ wxPoint wxGrid::GetPositionForResizeEvent(int width) const void wxGrid::DoEndDragResizeRow(const wxMouseEvent& event, wxGridWindow* gridWindow) { @@ -383,9 +370,9 @@ index 41fd4524cf..f4a15cb839 100644 + //BBS: add cursor mode for DoGridDragResize's paremeters + DoGridDragResize(event.GetPosition(), wxGridRowOperations(), gridWindow, WXGRID_CURSOR_RESIZE_ROW); - SendGridSizeEvent(wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event); + SendGridSizeEvent(wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, event); -@@ -5091,7 +5118,8 @@ void wxGrid::DoEndDragResizeRow(const wxMouseEvent& event, wxGridWindow* gridWin +@@ -5099,7 +5125,8 @@ void wxGrid::DoEndDragResizeRow(const wxMouseEvent& event, wxGridWindow* gridWin void wxGrid::DoEndDragResizeCol(const wxMouseEvent& event, wxGridWindow* gridWindow) { @@ -393,9 +380,9 @@ index 41fd4524cf..f4a15cb839 100644 + //BBS: add cursor mode for DoGridDragResize's paremeters + DoGridDragResize(event.GetPosition(), wxGridColumnOperations(), gridWindow, WXGRID_CURSOR_RESIZE_COL); - SendGridSizeEvent(wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event); + SendGridSizeEvent(wxEVT_GRID_COL_SIZE, m_dragRowOrCol, event); -@@ -5105,9 +5133,10 @@ void wxGrid::DoHeaderStartDragResizeCol(int col) +@@ -5113,9 +5140,10 @@ void wxGrid::DoHeaderStartDragResizeCol(int col) void wxGrid::DoHeaderDragResizeCol(int width) { @@ -407,7 +394,7 @@ index 41fd4524cf..f4a15cb839 100644 } void wxGrid::DoHeaderEndDragResizeCol(int width) -@@ -5891,6 +5920,10 @@ void wxGrid::OnKeyDown( wxKeyEvent& event ) +@@ -6013,6 +6041,10 @@ void wxGrid::OnKeyDown( wxKeyEvent& event ) DisableCellEditControl(); MoveCursorDown( event.ShiftDown() ); @@ -419,7 +406,7 @@ index 41fd4524cf..f4a15cb839 100644 break; diff --git a/src/msw/bmpcbox.cpp b/src/msw/bmpcbox.cpp -index 0a2d167ad7..0aeba45ea9 100644 +index 011bd4f534..17e7f18740 100644 --- a/src/msw/bmpcbox.cpp +++ b/src/msw/bmpcbox.cpp @@ -156,13 +156,20 @@ void wxBitmapComboBox::RecreateControl() @@ -445,7 +432,7 @@ index 0a2d167ad7..0aeba45ea9 100644 for ( i = 0; i < numItems; i++ ) { diff --git a/src/msw/font.cpp b/src/msw/font.cpp -index 0bd240d79f..d38b1b00f5 100644 +index 434876939c..91d4603018 100644 --- a/src/msw/font.cpp +++ b/src/msw/font.cpp @@ -54,7 +54,7 @@ static const int PITCH_MASK = FIXED_PITCH | VARIABLE_PITCH; @@ -503,7 +490,7 @@ index 0bd240d79f..d38b1b00f5 100644 bool wxFont::Create(const wxNativeFontInfo& info, WXHFONT hFont) diff --git a/src/msw/menuitem.cpp b/src/msw/menuitem.cpp -index 9bb397d472..30af7154a7 100644 +index 0bd017a36a..3b98bf1678 100644 --- a/src/msw/menuitem.cpp +++ b/src/msw/menuitem.cpp @@ -368,6 +368,8 @@ void MenuDrawData::Init(wxWindow const* window) @@ -516,10 +503,10 @@ index 9bb397d472..30af7154a7 100644 else #endif // wxUSE_UXTHEME diff --git a/src/msw/window.cpp b/src/msw/window.cpp -index eadc2f5700..f64fea4446 100644 +index c529a4fa3b..7e547c64df 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp -@@ -4773,33 +4773,49 @@ static wxSize GetWindowDPI(HWND hwnd) +@@ -4809,33 +4809,49 @@ static wxSize GetWindowDPI(HWND hwnd) } /*extern*/ @@ -588,18 +575,10 @@ index eadc2f5700..f64fea4446 100644 return ::GetSystemMetrics(nIndex); diff --git a/src/osx/cocoa/dataview.mm b/src/osx/cocoa/dataview.mm -index 6ff0cc3088..4943f3ea38 100644 +index f188e61089..7b867002d1 100644 --- a/src/osx/cocoa/dataview.mm +++ b/src/osx/cocoa/dataview.mm -@@ -1734,12 +1734,22 @@ outlineView:(NSOutlineView*)outlineView - if ( !dvc->GetEventHandler()->ProcessEvent(eventDV) ) - [super keyDown:event]; - } -- else -+ //FIXME Vojtech's hack to get the accelerators assigned to the wxDataViewControl working. -+ else if (! implementation->DoHandleKeyEvent(event)) - { - [super keyDown:event]; // all other keys +@@ -1604,6 +1604,15 @@ outlineView:(NSOutlineView*)outlineView } } @@ -615,7 +594,17 @@ index 6ff0cc3088..4943f3ea38 100644 // // contextual menus // -@@ -2672,12 +2682,22 @@ void wxCocoaDataViewControl::DoSetIndent(int indent) +@@ -2006,7 +2015,8 @@ void wxCocoaDataViewControl::keyEvent(WX_NSEvent event, WXWidget slf, void *_cmd + if ( !dvc->GetEventHandler()->ProcessEvent(eventDV) ) + wxWidgetCocoaImpl::keyEvent(event, slf, _cmd); + } +- else ++ //FIXME Vojtech's hack to get the accelerators assigned to the wxDataViewControl working. ++ else if (! DoHandleKeyEvent(event)) + { + wxWidgetCocoaImpl::keyEvent(event, slf, _cmd); // all other keys + } +@@ -2540,12 +2550,22 @@ void wxCocoaDataViewControl::DoSetIndent(int indent) void wxCocoaDataViewControl::HitTest(const wxPoint& point, wxDataViewItem& item, wxDataViewColumn*& columnPtr) const { @@ -641,10 +630,10 @@ index 6ff0cc3088..4943f3ea38 100644 indexRow = [m_OutlineView rowAtPoint: nativePoint]; if ((indexColumn >= 0) && (indexRow >= 0)) diff --git a/src/osx/cocoa/settings.mm b/src/osx/cocoa/settings.mm -index de5f52860c..a9581174a4 100644 +index c819deeb0c..dc3c3b0b53 100644 --- a/src/osx/cocoa/settings.mm +++ b/src/osx/cocoa/settings.mm -@@ -224,7 +224,7 @@ wxFont wxSystemSettingsNative::GetFont(wxSystemFont index) +@@ -222,7 +222,7 @@ wxFont wxSystemSettingsNative::GetFont(wxSystemFont index) // ---------------------------------------------------------------------------- // Get a system metric, e.g. scrollbar size @@ -653,7 +642,7 @@ index de5f52860c..a9581174a4 100644 { int value; -@@ -259,11 +259,11 @@ int wxSystemSettingsNative::GetMetric(wxSystemMetric index, const wxWindow* WXUN +@@ -257,11 +257,11 @@ int wxSystemSettingsNative::GetMetric(wxSystemMetric index, const wxWindow* WXUN // TODO case wxSYS_WINDOWMIN_Y: case wxSYS_SCREEN_X: @@ -667,3 +656,28 @@ index de5f52860c..a9581174a4 100644 return value; // TODO case wxSYS_FRAMESIZE_X: +diff --git a/src/osx/cocoa/window.mm b/src/osx/cocoa/window.mm +index 635ea286d4..42ae67e27a 100644 +--- a/src/osx/cocoa/window.mm ++++ b/src/osx/cocoa/window.mm +@@ -191,6 +191,9 @@ NSRect wxOSXGetFrameForControl( wxWindowMac* window , const wxPoint& pos , const + - (BOOL)isEnabled; + - (void)setEnabled:(BOOL)flag; + ++- (BOOL)clipsToBounds; ++- (void)setClipsToBounds:(BOOL)clipsToBounds; ++ + - (void)setImage:(NSImage *)image; + - (void)setControlSize:(NSControlSize)size; + +@@ -2559,6 +2562,7 @@ wxWidgetImpl( peer, flags ) + if ( m_osxView ) + CFRetain(m_osxView); + [m_osxView release]; ++ m_osxView.clipsToBounds = YES; + } + + +-- +2.42.0.windows.2 + diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake index 82c0f9f865..65aacfaad5 100644 --- a/deps/wxWidgets/wxWidgets.cmake +++ b/deps/wxWidgets/wxWidgets.cmake @@ -1,4 +1,5 @@ -set(_wx_git_tag v3.1.5) +set(_wx_git_tag v3.2.1) +set(_wx_patch_name 0001-patch-v3.2.1-for-OrcaSlicer.patch) set(_wx_toolkit "") set(_wx_private_font "-DwxUSE_PRIVATE_FONTS=1") @@ -17,13 +18,13 @@ else () endif () if (MSVC) - set(_patch_cmd if not exist WXWIDGETS_PATCHED ( "${GIT_EXECUTABLE}" apply --verbose --ignore-space-change --whitespace=fix ${CMAKE_CURRENT_LIST_DIR}/0001-wxWidget-fix.patch && type nul > WXWIDGETS_PATCHED ) ) + set(_patch_cmd if not exist WXWIDGETS_PATCHED ( "${GIT_EXECUTABLE}" apply --verbose --ignore-space-change --whitespace=fix ${CMAKE_CURRENT_LIST_DIR}/${_wx_patch_name} && type nul > WXWIDGETS_PATCHED ) ) else () - set(_patch_cmd test -f WXWIDGETS_PATCHED || ${PATCH_CMD} ${CMAKE_CURRENT_LIST_DIR}/0001-wxWidget-fix.patch && touch WXWIDGETS_PATCHED) + set(_patch_cmd test -f WXWIDGETS_PATCHED || ${PATCH_CMD} ${CMAKE_CURRENT_LIST_DIR}/${_wx_patch_name} && touch WXWIDGETS_PATCHED) endif () if (CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(_patch_cmd ${PATCH_CMD} ${CMAKE_CURRENT_LIST_DIR}/0001-wxWidget-fix.patch) + set(_patch_cmd ${PATCH_CMD} ${CMAKE_CURRENT_LIST_DIR}/${_wx_patch_name}) endif () orcaslicer_add_cmake_project( @@ -31,7 +32,7 @@ orcaslicer_add_cmake_project( GIT_REPOSITORY "https://github.com/wxWidgets/wxWidgets" GIT_TAG ${_wx_git_tag} PATCH_COMMAND ${_patch_cmd} - DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG + DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG dep_NanoSVG CMAKE_ARGS -DwxBUILD_PRECOMP=ON ${_wx_toolkit} @@ -47,7 +48,9 @@ orcaslicer_add_cmake_project( -DwxUSE_WEBVIEW=ON ${_wx_edge} -DwxUSE_WEBVIEW_IE=OFF - -DwxUSE_REGEX=builtin + -DwxUSE_NANOSVG=sys + -DwxUSE_NANOSVG_EXTERNAL=ON + -DwxUSE_REGEX=OFF -DwxUSE_LIBXPM=builtin -DwxUSE_LIBSDL=OFF -DwxUSE_XTEST=OFF diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 71f6fe1f5f..8c9ccc1309 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -94,7 +94,10 @@ if (SLIC3R_GUI) # wrong libs for opengl in the link line and it does not link to it by himself. # libslic3r_gui will link to opengl anyway, so lets override wx list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX OpenGL) - + + if (UNIX AND NOT APPLE) + list(APPEND wxWidgets_LIBRARIES X11 wayland-client wayland-egl EGL) + endif () # list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc) message(STATUS "wx libs: ${wxWidgets_LIBRARIES}") diff --git a/src/OrcaSlicer.cpp b/src/OrcaSlicer.cpp index 66b02add2f..4cdc9a15cc 100644 --- a/src/OrcaSlicer.cpp +++ b/src/OrcaSlicer.cpp @@ -4744,7 +4744,6 @@ int CLI::run(int argc, char **argv) } } - ThumbnailsParams thumbnail_params; GLShaderProgram* shader = opengl_mgr.get_shader("thumbnail"); if (!shader) { BOOST_LOG_TRIVIAL(error) << boost::format("can not get shader for rendering thumbnail"); diff --git a/src/nanosvg/README.txt b/src/nanosvg/README.txt deleted file mode 100644 index cd38634bec..0000000000 --- a/src/nanosvg/README.txt +++ /dev/null @@ -1 +0,0 @@ -Upstream source: https://github.com/fltk/nanosvg/archive/abcd277ea45e9098bed752cf9c6875b533c0892f.zip \ No newline at end of file diff --git a/src/nanosvg/nanosvg.h b/src/nanosvg/nanosvg.h deleted file mode 100644 index 1a1a20cb37..0000000000 --- a/src/nanosvg/nanosvg.h +++ /dev/null @@ -1,3106 +0,0 @@ -/* - * Copyright (c) 2013-14 Mikko Mononen memon@inside.org - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example - * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/) - * - * Arc calculation code based on canvg (https://code.google.com/p/canvg/) - * - * Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html - * - */ - -#ifndef NANOSVG_H -#define NANOSVG_H - -#ifndef NANOSVG_CPLUSPLUS -#ifdef __cplusplus -extern "C" { -#endif -#endif - -// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes. -// -// The library suits well for anything from rendering scalable icons in your editor application to prototyping a game. -// -// NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request! -// -// The shapes in the SVG images are transformed by the viewBox and converted to specified units. -// That is, you should get the same looking data as your designed in your favorite app. -// -// NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose -// to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters. -// -// The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'. -// DPI (dots-per-inch) controls how the unit conversion is done. -// -// If you don't know or care about the units stuff, "px" and 96 should get you going. - - -/* Example Usage: - // Load SVG - NSVGimage* image; - image = nsvgParseFromFile("test.svg", "px", 96); - printf("size: %f x %f\n", image->width, image->height); - // Use... - for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) { - for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { - for (int i = 0; i < path->npts-1; i += 3) { - float* p = &path->pts[i*2]; - drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); - } - } - } - // Delete - nsvgDelete(image); -*/ - -enum NSVGpaintType { - NSVG_PAINT_UNDEF = -1, - NSVG_PAINT_NONE = 0, - NSVG_PAINT_COLOR = 1, - NSVG_PAINT_LINEAR_GRADIENT = 2, - NSVG_PAINT_RADIAL_GRADIENT = 3 -}; - -enum NSVGspreadType { - NSVG_SPREAD_PAD = 0, - NSVG_SPREAD_REFLECT = 1, - NSVG_SPREAD_REPEAT = 2 -}; - -enum NSVGlineJoin { - NSVG_JOIN_MITER = 0, - NSVG_JOIN_ROUND = 1, - NSVG_JOIN_BEVEL = 2 -}; - -enum NSVGlineCap { - NSVG_CAP_BUTT = 0, - NSVG_CAP_ROUND = 1, - NSVG_CAP_SQUARE = 2 -}; - -enum NSVGfillRule { - NSVG_FILLRULE_NONZERO = 0, - NSVG_FILLRULE_EVENODD = 1 -}; - -enum NSVGflags { - NSVG_FLAGS_VISIBLE = 0x01 -}; - -typedef struct NSVGgradientStop { - unsigned int color; - float offset; -} NSVGgradientStop; - -typedef struct NSVGgradient { - float xform[6]; - char spread; - float fx, fy; - int nstops; - NSVGgradientStop stops[1]; -} NSVGgradient; - -typedef struct NSVGpaint { - signed char type; - union { - unsigned int color; - NSVGgradient* gradient; - }; -} NSVGpaint; - -typedef struct NSVGpath -{ - float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ... - int npts; // Total number of bezier points. - char closed; // Flag indicating if shapes should be treated as closed. - float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. - struct NSVGpath* next; // Pointer to next path, or NULL if last element. -} NSVGpath; - -typedef struct NSVGshape -{ - char id[64]; // Optional 'id' attr of the shape or its group - NSVGpaint fill; // Fill paint - NSVGpaint stroke; // Stroke paint - float opacity; // Opacity of the shape. - float strokeWidth; // Stroke width (scaled). - float strokeDashOffset; // Stroke dash offset (scaled). - float strokeDashArray[8]; // Stroke dash array (scaled). - char strokeDashCount; // Number of dash values in dash array. - char strokeLineJoin; // Stroke join type. - char strokeLineCap; // Stroke cap type. - float miterLimit; // Miter limit - char fillRule; // Fill rule, see NSVGfillRule. - unsigned char flags; // Logical or of NSVG_FLAGS_* flags - float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. - char fillGradient[64]; // Optional 'id' of fill gradient - char strokeGradient[64]; // Optional 'id' of stroke gradient - float xform[6]; // Root transformation for fill/stroke gradient - NSVGpath* paths; // Linked list of paths in the image. - struct NSVGshape* next; // Pointer to next shape, or NULL if last element. -} NSVGshape; - -typedef struct NSVGimage -{ - float width; // Width of the image. - float height; // Height of the image. - NSVGshape* shapes; // Linked list of shapes in the image. -} NSVGimage; - -// Parses SVG file from a file, returns SVG image as paths. -NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi); - -// Parses SVG file from a null terminated string, returns SVG image as paths. -// Important note: changes the string. -NSVGimage* nsvgParse(char* input, const char* units, float dpi); - -// Duplicates a path. -NSVGpath* nsvgDuplicatePath(NSVGpath* p); - -// Deletes an image. -void nsvgDelete(NSVGimage* image); - -#ifndef NANOSVG_CPLUSPLUS -#ifdef __cplusplus -} -#endif -#endif - -#ifdef NANOSVG_IMPLEMENTATION - -#include -#include -#include -#include - -#define NSVG_PI (3.14159265358979323846264338327f) -#define NSVG_KAPPA90 (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs. - -#define NSVG_ALIGN_MIN 0 -#define NSVG_ALIGN_MID 1 -#define NSVG_ALIGN_MAX 2 -#define NSVG_ALIGN_NONE 0 -#define NSVG_ALIGN_MEET 1 -#define NSVG_ALIGN_SLICE 2 - -#define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0) -#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16)) - -#ifdef _MSC_VER - #pragma warning (disable: 4996) // Switch off security warnings - #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings - #ifdef __cplusplus - #define NSVG_INLINE inline - #else - #define NSVG_INLINE - #endif -#else - #define NSVG_INLINE inline -#endif - - -static int nsvg__isspace(char c) -{ - return strchr(" \t\n\v\f\r", c) != 0; -} - -static int nsvg__isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; } -static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; } - - -// Simple XML parser - -#define NSVG_XML_TAG 1 -#define NSVG_XML_CONTENT 2 -#define NSVG_XML_MAX_ATTRIBS 256 - -static void nsvg__parseContent(char* s, - void (*contentCb)(void* ud, const char* s), - void* ud) -{ - // Trim start white spaces - while (*s && nsvg__isspace(*s)) s++; - if (!*s) return; - - if (contentCb) - (*contentCb)(ud, s); -} - -static void nsvg__parseElement(char* s, - void (*startelCb)(void* ud, const char* el, const char** attr), - void (*endelCb)(void* ud, const char* el), - void* ud) -{ - const char* attr[NSVG_XML_MAX_ATTRIBS]; - int nattr = 0; - char* name; - int start = 0; - int end = 0; - char quote; - - // Skip white space after the '<' - while (*s && nsvg__isspace(*s)) s++; - - // Check if the tag is end tag - if (*s == '/') { - s++; - end = 1; - } else { - start = 1; - } - - // Skip comments, data and preprocessor stuff. - if (!*s || *s == '?' || *s == '!') - return; - - // Get tag name - name = s; - while (*s && !nsvg__isspace(*s)) s++; - if (*s) { *s++ = '\0'; } - - // Get attribs - while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) { - char* name = NULL; - char* value = NULL; - - // Skip white space before the attrib name - while (*s && nsvg__isspace(*s)) s++; - if (!*s) break; - if (*s == '/') { - end = 1; - break; - } - name = s; - // Find end of the attrib name. - while (*s && !nsvg__isspace(*s) && *s != '=') s++; - if (*s) { *s++ = '\0'; } - // Skip until the beginning of the value. - while (*s && *s != '\"' && *s != '\'') s++; - if (!*s) break; - quote = *s; - s++; - // Store value and find the end of it. - value = s; - while (*s && *s != quote) s++; - if (*s) { *s++ = '\0'; } - - // Store only well formed attributes - if (name && value) { - attr[nattr++] = name; - attr[nattr++] = value; - } - } - - // List terminator - attr[nattr++] = 0; - attr[nattr++] = 0; - - // Call callbacks. - if (start && startelCb) - (*startelCb)(ud, name, attr); - if (end && endelCb) - (*endelCb)(ud, name); -} - -int nsvg__parseXML(char* input, - void (*startelCb)(void* ud, const char* el, const char** attr), - void (*endelCb)(void* ud, const char* el), - void (*contentCb)(void* ud, const char* s), - void* ud) -{ - char* s = input; - char* mark = s; - int state = NSVG_XML_CONTENT; - while (*s) { - if (*s == '<' && state == NSVG_XML_CONTENT) { - // Start of a tag - *s++ = '\0'; - nsvg__parseContent(mark, contentCb, ud); - mark = s; - state = NSVG_XML_TAG; - } else if (*s == '>' && state == NSVG_XML_TAG) { - // Start of a content or new tag. - *s++ = '\0'; - nsvg__parseElement(mark, startelCb, endelCb, ud); - mark = s; - state = NSVG_XML_CONTENT; - } else { - s++; - } - } - - return 1; -} - - -/* Simple SVG parser. */ - -#define NSVG_MAX_ATTR 128 - -enum NSVGgradientUnits { - NSVG_USER_SPACE = 0, - NSVG_OBJECT_SPACE = 1 -}; - -#define NSVG_MAX_DASHES 8 - -enum NSVGunits { - NSVG_UNITS_USER, - NSVG_UNITS_PX, - NSVG_UNITS_PT, - NSVG_UNITS_PC, - NSVG_UNITS_MM, - NSVG_UNITS_CM, - NSVG_UNITS_IN, - NSVG_UNITS_PERCENT, - NSVG_UNITS_EM, - NSVG_UNITS_EX -}; - -typedef struct NSVGcoordinate { - float value; - int units; -} NSVGcoordinate; - -typedef struct NSVGlinearData { - NSVGcoordinate x1, y1, x2, y2; -} NSVGlinearData; - -typedef struct NSVGradialData { - NSVGcoordinate cx, cy, r, fx, fy; -} NSVGradialData; - -typedef struct NSVGgradientData -{ - char id[64]; - char ref[64]; - signed char type; - union { - NSVGlinearData linear; - NSVGradialData radial; - }; - char spread; - char units; - float xform[6]; - int nstops; - NSVGgradientStop* stops; - struct NSVGgradientData* next; -} NSVGgradientData; - -typedef struct NSVGattrib -{ - char id[64]; - float xform[6]; - unsigned int fillColor; - unsigned int strokeColor; - float opacity; - float fillOpacity; - float strokeOpacity; - char fillGradient[64]; - char strokeGradient[64]; - float strokeWidth; - float strokeDashOffset; - float strokeDashArray[NSVG_MAX_DASHES]; - int strokeDashCount; - char strokeLineJoin; - char strokeLineCap; - float miterLimit; - char fillRule; - float fontSize; - unsigned int stopColor; - float stopOpacity; - float stopOffset; - char hasFill; - char hasStroke; - char visible; -} NSVGattrib; - -typedef struct NSVGparser -{ - NSVGattrib attr[NSVG_MAX_ATTR]; - int attrHead; - float* pts; - int npts; - int cpts; - NSVGpath* plist; - NSVGimage* image; - NSVGgradientData* gradients; - NSVGshape* shapesTail; - float viewMinx, viewMiny, viewWidth, viewHeight; - int alignX, alignY, alignType; - float dpi; - char pathFlag; - char defsFlag; -} NSVGparser; - -static void nsvg__xformIdentity(float* t) -{ - t[0] = 1.0f; t[1] = 0.0f; - t[2] = 0.0f; t[3] = 1.0f; - t[4] = 0.0f; t[5] = 0.0f; -} - -static void nsvg__xformSetTranslation(float* t, float tx, float ty) -{ - t[0] = 1.0f; t[1] = 0.0f; - t[2] = 0.0f; t[3] = 1.0f; - t[4] = tx; t[5] = ty; -} - -static void nsvg__xformSetScale(float* t, float sx, float sy) -{ - t[0] = sx; t[1] = 0.0f; - t[2] = 0.0f; t[3] = sy; - t[4] = 0.0f; t[5] = 0.0f; -} - -static void nsvg__xformSetSkewX(float* t, float a) -{ - t[0] = 1.0f; t[1] = 0.0f; - t[2] = tanf(a); t[3] = 1.0f; - t[4] = 0.0f; t[5] = 0.0f; -} - -static void nsvg__xformSetSkewY(float* t, float a) -{ - t[0] = 1.0f; t[1] = tanf(a); - t[2] = 0.0f; t[3] = 1.0f; - t[4] = 0.0f; t[5] = 0.0f; -} - -static void nsvg__xformSetRotation(float* t, float a) -{ - float cs = cosf(a), sn = sinf(a); - t[0] = cs; t[1] = sn; - t[2] = -sn; t[3] = cs; - t[4] = 0.0f; t[5] = 0.0f; -} - -static void nsvg__xformMultiply(float* t, float* s) -{ - float t0 = t[0] * s[0] + t[1] * s[2]; - float t2 = t[2] * s[0] + t[3] * s[2]; - float t4 = t[4] * s[0] + t[5] * s[2] + s[4]; - t[1] = t[0] * s[1] + t[1] * s[3]; - t[3] = t[2] * s[1] + t[3] * s[3]; - t[5] = t[4] * s[1] + t[5] * s[3] + s[5]; - t[0] = t0; - t[2] = t2; - t[4] = t4; -} - -static void nsvg__xformInverse(float* inv, float* t) -{ - double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; - if (det > -1e-6 && det < 1e-6) { - nsvg__xformIdentity(t); - return; - } - invdet = 1.0 / det; - inv[0] = (float)(t[3] * invdet); - inv[2] = (float)(-t[2] * invdet); - inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); - inv[1] = (float)(-t[1] * invdet); - inv[3] = (float)(t[0] * invdet); - inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); -} - -static void nsvg__xformPremultiply(float* t, float* s) -{ - float s2[6]; - memcpy(s2, s, sizeof(float)*6); - nsvg__xformMultiply(s2, t); - memcpy(t, s2, sizeof(float)*6); -} - -static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t) -{ - *dx = x*t[0] + y*t[2] + t[4]; - *dy = x*t[1] + y*t[3] + t[5]; -} - -static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t) -{ - *dx = x*t[0] + y*t[2]; - *dy = x*t[1] + y*t[3]; -} - -#define NSVG_EPSILON (1e-12) - -static int nsvg__ptInBounds(float* pt, float* bounds) -{ - return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3]; -} - - -static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3) -{ - double it = 1.0-t; - return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3; -} - -static void nsvg__curveBounds(float* bounds, float* curve) -{ - int i, j, count; - double roots[2], a, b, c, b2ac, t, v; - float* v0 = &curve[0]; - float* v1 = &curve[2]; - float* v2 = &curve[4]; - float* v3 = &curve[6]; - - // Start the bounding box by end points - bounds[0] = nsvg__minf(v0[0], v3[0]); - bounds[1] = nsvg__minf(v0[1], v3[1]); - bounds[2] = nsvg__maxf(v0[0], v3[0]); - bounds[3] = nsvg__maxf(v0[1], v3[1]); - - // Bezier curve fits inside the convex hull of it's control points. - // If control points are inside the bounds, we're done. - if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds)) - return; - - // Add bezier curve inflection points in X and Y. - for (i = 0; i < 2; i++) { - a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i]; - b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i]; - c = 3.0 * v1[i] - 3.0 * v0[i]; - count = 0; - if (fabs(a) < NSVG_EPSILON) { - if (fabs(b) > NSVG_EPSILON) { - t = -c / b; - if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) - roots[count++] = t; - } - } else { - b2ac = b*b - 4.0*c*a; - if (b2ac > NSVG_EPSILON) { - t = (-b + sqrt(b2ac)) / (2.0 * a); - if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) - roots[count++] = t; - t = (-b - sqrt(b2ac)) / (2.0 * a); - if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) - roots[count++] = t; - } - } - for (j = 0; j < count; j++) { - v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]); - bounds[0+i] = nsvg__minf(bounds[0+i], (float)v); - bounds[2+i] = nsvg__maxf(bounds[2+i], (float)v); - } - } -} - -static NSVGparser* nsvg__createParser(void) -{ - NSVGparser* p; - p = (NSVGparser*)malloc(sizeof(NSVGparser)); - if (p == NULL) goto error; - memset(p, 0, sizeof(NSVGparser)); - - p->image = (NSVGimage*)malloc(sizeof(NSVGimage)); - if (p->image == NULL) goto error; - memset(p->image, 0, sizeof(NSVGimage)); - - // Init style - nsvg__xformIdentity(p->attr[0].xform); - memset(p->attr[0].id, 0, sizeof p->attr[0].id); - p->attr[0].fillColor = NSVG_RGB(0,0,0); - p->attr[0].strokeColor = NSVG_RGB(0,0,0); - p->attr[0].opacity = 1; - p->attr[0].fillOpacity = 1; - p->attr[0].strokeOpacity = 1; - p->attr[0].stopOpacity = 1; - p->attr[0].strokeWidth = 1; - p->attr[0].strokeLineJoin = NSVG_JOIN_MITER; - p->attr[0].strokeLineCap = NSVG_CAP_BUTT; - p->attr[0].miterLimit = 4; - p->attr[0].fillRule = NSVG_FILLRULE_NONZERO; - p->attr[0].hasFill = 1; - p->attr[0].visible = 1; - - return p; - -error: - if (p) { - if (p->image) free(p->image); - free(p); - } - return NULL; -} - -static void nsvg__deletePaths(NSVGpath* path) -{ - while (path) { - NSVGpath *next = path->next; - if (path->pts != NULL) - free(path->pts); - free(path); - path = next; - } -} - -static void nsvg__deletePaint(NSVGpaint* paint) -{ - if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT) - free(paint->gradient); -} - -static void nsvg__deleteGradientData(NSVGgradientData* grad) -{ - NSVGgradientData* next; - while (grad != NULL) { - next = grad->next; - free(grad->stops); - free(grad); - grad = next; - } -} - -static void nsvg__deleteParser(NSVGparser* p) -{ - if (p != NULL) { - nsvg__deletePaths(p->plist); - nsvg__deleteGradientData(p->gradients); - nsvgDelete(p->image); - free(p->pts); - free(p); - } -} - -static void nsvg__resetPath(NSVGparser* p) -{ - p->npts = 0; -} - -static void nsvg__addPoint(NSVGparser* p, float x, float y) -{ - if (p->npts+1 > p->cpts) { - p->cpts = p->cpts ? p->cpts*2 : 8; - p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float)); - if (!p->pts) return; - } - p->pts[p->npts*2+0] = x; - p->pts[p->npts*2+1] = y; - p->npts++; -} - -static void nsvg__moveTo(NSVGparser* p, float x, float y) -{ - if (p->npts > 0) { - p->pts[(p->npts-1)*2+0] = x; - p->pts[(p->npts-1)*2+1] = y; - } else { - nsvg__addPoint(p, x, y); - } -} - -static void nsvg__lineTo(NSVGparser* p, float x, float y) -{ - float px,py, dx,dy; - if (p->npts > 0) { - px = p->pts[(p->npts-1)*2+0]; - py = p->pts[(p->npts-1)*2+1]; - dx = x - px; - dy = y - py; - nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f); - nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f); - nsvg__addPoint(p, x, y); - } -} - -static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y) -{ - if (p->npts > 0) { - nsvg__addPoint(p, cpx1, cpy1); - nsvg__addPoint(p, cpx2, cpy2); - nsvg__addPoint(p, x, y); - } -} - -static NSVGattrib* nsvg__getAttr(NSVGparser* p) -{ - return &p->attr[p->attrHead]; -} - -static void nsvg__pushAttr(NSVGparser* p) -{ - if (p->attrHead < NSVG_MAX_ATTR-1) { - p->attrHead++; - memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib)); - } -} - -static void nsvg__popAttr(NSVGparser* p) -{ - if (p->attrHead > 0) - p->attrHead--; -} - -static float nsvg__actualOrigX(NSVGparser* p) -{ - return p->viewMinx; -} - -static float nsvg__actualOrigY(NSVGparser* p) -{ - return p->viewMiny; -} - -static float nsvg__actualWidth(NSVGparser* p) -{ - return p->viewWidth; -} - -static float nsvg__actualHeight(NSVGparser* p) -{ - return p->viewHeight; -} - -static float nsvg__actualLength(NSVGparser* p) -{ - float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p); - return sqrtf(w*w + h*h) / sqrtf(2.0f); -} - -static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length) -{ - NSVGattrib* attr = nsvg__getAttr(p); - switch (c.units) { - case NSVG_UNITS_USER: return c.value; - case NSVG_UNITS_PX: return c.value; - case NSVG_UNITS_PT: return c.value / 72.0f * p->dpi; - case NSVG_UNITS_PC: return c.value / 6.0f * p->dpi; - case NSVG_UNITS_MM: return c.value / 25.4f * p->dpi; - case NSVG_UNITS_CM: return c.value / 2.54f * p->dpi; - case NSVG_UNITS_IN: return c.value * p->dpi; - case NSVG_UNITS_EM: return c.value * attr->fontSize; - case NSVG_UNITS_EX: return c.value * attr->fontSize * 0.52f; // x-height of Helvetica. - case NSVG_UNITS_PERCENT: return orig + c.value / 100.0f * length; - default: return c.value; - } - return c.value; -} - -static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id) -{ - NSVGgradientData* grad = p->gradients; - if (id == NULL || *id == '\0') - return NULL; - while (grad != NULL) { - if (strcmp(grad->id, id) == 0) - return grad; - grad = grad->next; - } - return NULL; -} - -static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, float *xform, signed char* paintType) -{ - NSVGgradientData* data = NULL; - NSVGgradientData* ref = NULL; - NSVGgradientStop* stops = NULL; - NSVGgradient* grad; - float ox, oy, sw, sh, sl; - int nstops = 0; - int refIter; - - data = nsvg__findGradientData(p, id); - if (data == NULL) return NULL; - - // TODO: use ref to fill in all unset values too. - ref = data; - refIter = 0; - while (ref != NULL) { - NSVGgradientData* nextRef = NULL; - if (stops == NULL && ref->stops != NULL) { - stops = ref->stops; - nstops = ref->nstops; - break; - } - nextRef = nsvg__findGradientData(p, ref->ref); - if (nextRef == ref) break; // prevent infite loops on malformed data - ref = nextRef; - refIter++; - if (refIter > 32) break; // prevent infite loops on malformed data - } - if (stops == NULL) return NULL; - - grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1)); - if (grad == NULL) return NULL; - - // The shape width and height. - if (data->units == NSVG_OBJECT_SPACE) { - ox = localBounds[0]; - oy = localBounds[1]; - sw = localBounds[2] - localBounds[0]; - sh = localBounds[3] - localBounds[1]; - } else { - ox = nsvg__actualOrigX(p); - oy = nsvg__actualOrigY(p); - sw = nsvg__actualWidth(p); - sh = nsvg__actualHeight(p); - } - sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f); - - if (data->type == NSVG_PAINT_LINEAR_GRADIENT) { - float x1, y1, x2, y2, dx, dy; - x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw); - y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh); - x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw); - y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh); - // Calculate transform aligned to the line - dx = x2 - x1; - dy = y2 - y1; - grad->xform[0] = dy; grad->xform[1] = -dx; - grad->xform[2] = dx; grad->xform[3] = dy; - grad->xform[4] = x1; grad->xform[5] = y1; - } else { - float cx, cy, fx, fy, r; - cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw); - cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh); - fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw); - fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh); - r = nsvg__convertToPixels(p, data->radial.r, 0, sl); - // Calculate transform aligned to the circle - grad->xform[0] = r; grad->xform[1] = 0; - grad->xform[2] = 0; grad->xform[3] = r; - grad->xform[4] = cx; grad->xform[5] = cy; - grad->fx = fx / r; - grad->fy = fy / r; - } - - nsvg__xformMultiply(grad->xform, data->xform); - nsvg__xformMultiply(grad->xform, xform); - - grad->spread = data->spread; - memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop)); - grad->nstops = nstops; - - *paintType = data->type; - - return grad; -} - -static float nsvg__getAverageScale(float* t) -{ - float sx = sqrtf(t[0]*t[0] + t[2]*t[2]); - float sy = sqrtf(t[1]*t[1] + t[3]*t[3]); - return (sx + sy) * 0.5f; -} - -static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform) -{ - NSVGpath* path; - float curve[4*2], curveBounds[4]; - int i, first = 1; - for (path = shape->paths; path != NULL; path = path->next) { - nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform); - for (i = 0; i < path->npts-1; i += 3) { - nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform); - nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform); - nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform); - nsvg__curveBounds(curveBounds, curve); - if (first) { - bounds[0] = curveBounds[0]; - bounds[1] = curveBounds[1]; - bounds[2] = curveBounds[2]; - bounds[3] = curveBounds[3]; - first = 0; - } else { - bounds[0] = nsvg__minf(bounds[0], curveBounds[0]); - bounds[1] = nsvg__minf(bounds[1], curveBounds[1]); - bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]); - bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]); - } - curve[0] = curve[6]; - curve[1] = curve[7]; - } - } -} - -static void nsvg__addShape(NSVGparser* p) -{ - NSVGattrib* attr = nsvg__getAttr(p); - float scale = 1.0f; - NSVGshape* shape; - NSVGpath* path; - int i; - - if (p->plist == NULL) - return; - - shape = (NSVGshape*)malloc(sizeof(NSVGshape)); - if (shape == NULL) goto error; - memset(shape, 0, sizeof(NSVGshape)); - - memcpy(shape->id, attr->id, sizeof shape->id); - memcpy(shape->fillGradient, attr->fillGradient, sizeof shape->fillGradient); - memcpy(shape->strokeGradient, attr->strokeGradient, sizeof shape->strokeGradient); - memcpy(shape->xform, attr->xform, sizeof shape->xform); - scale = nsvg__getAverageScale(attr->xform); - shape->strokeWidth = attr->strokeWidth * scale; - shape->strokeDashOffset = attr->strokeDashOffset * scale; - shape->strokeDashCount = (char)attr->strokeDashCount; - for (i = 0; i < attr->strokeDashCount; i++) - shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale; - shape->strokeLineJoin = attr->strokeLineJoin; - shape->strokeLineCap = attr->strokeLineCap; - shape->miterLimit = attr->miterLimit; - shape->fillRule = attr->fillRule; - shape->opacity = attr->opacity; - - shape->paths = p->plist; - p->plist = NULL; - - // Calculate shape bounds - shape->bounds[0] = shape->paths->bounds[0]; - shape->bounds[1] = shape->paths->bounds[1]; - shape->bounds[2] = shape->paths->bounds[2]; - shape->bounds[3] = shape->paths->bounds[3]; - for (path = shape->paths->next; path != NULL; path = path->next) { - shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]); - shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]); - shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]); - shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]); - } - - // Set fill - if (attr->hasFill == 0) { - shape->fill.type = NSVG_PAINT_NONE; - } else if (attr->hasFill == 1) { - shape->fill.type = NSVG_PAINT_COLOR; - shape->fill.color = attr->fillColor; - shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24; - } else if (attr->hasFill == 2) { - shape->fill.type = NSVG_PAINT_UNDEF; - } - - // Set stroke - if (attr->hasStroke == 0) { - shape->stroke.type = NSVG_PAINT_NONE; - } else if (attr->hasStroke == 1) { - shape->stroke.type = NSVG_PAINT_COLOR; - shape->stroke.color = attr->strokeColor; - shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24; - } else if (attr->hasStroke == 2) { - shape->stroke.type = NSVG_PAINT_UNDEF; - } - - // Set flags - shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00); - - // Add to tail - if (p->image->shapes == NULL) - p->image->shapes = shape; - else - p->shapesTail->next = shape; - p->shapesTail = shape; - - return; - -error: - if (shape) free(shape); -} - -static void nsvg__addPath(NSVGparser* p, char closed) -{ - NSVGattrib* attr = nsvg__getAttr(p); - NSVGpath* path = NULL; - float bounds[4]; - float* curve; - int i; - - if (p->npts < 4) - return; - - if (closed) - nsvg__lineTo(p, p->pts[0], p->pts[1]); - - // Expect 1 + N*3 points (N = number of cubic bezier segments). - if ((p->npts % 3) != 1) - return; - - path = (NSVGpath*)malloc(sizeof(NSVGpath)); - if (path == NULL) goto error; - memset(path, 0, sizeof(NSVGpath)); - - path->pts = (float*)malloc(p->npts*2*sizeof(float)); - if (path->pts == NULL) goto error; - path->closed = closed; - path->npts = p->npts; - - // Transform path. - for (i = 0; i < p->npts; ++i) - nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], attr->xform); - - // Find bounds - for (i = 0; i < path->npts-1; i += 3) { - curve = &path->pts[i*2]; - nsvg__curveBounds(bounds, curve); - if (i == 0) { - path->bounds[0] = bounds[0]; - path->bounds[1] = bounds[1]; - path->bounds[2] = bounds[2]; - path->bounds[3] = bounds[3]; - } else { - path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]); - path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]); - path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]); - path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]); - } - } - - path->next = p->plist; - p->plist = path; - - return; - -error: - if (path != NULL) { - if (path->pts != NULL) free(path->pts); - free(path); - } -} - -// We roll our own string to float because the std library one uses locale and messes things up. -static double nsvg__atof(const char* s) -{ - char* cur = (char*)s; - char* end = NULL; - double res = 0.0, sign = 1.0; - double intPart = 0.0, fracPart = 0.0; - char hasIntPart = 0, hasFracPart = 0; - - // Parse optional sign - if (*cur == '+') { - cur++; - } else if (*cur == '-') { - sign = -1; - cur++; - } - - // Parse integer part - if (nsvg__isdigit(*cur)) { - // Parse digit sequence -#ifdef _MSC_VER - intPart = (double)_strtoi64(cur, &end, 10); -#else - intPart = (double)strtoll(cur, &end, 10); -#endif - if (cur != end) { - res = intPart; - hasIntPart = 1; - cur = end; - } - } - - // Parse fractional part. - if (*cur == '.') { - cur++; // Skip '.' - if (nsvg__isdigit(*cur)) { - // Parse digit sequence -#ifdef _MSC_VER - fracPart = (double)_strtoi64(cur, &end, 10); -#else - fracPart = (double)strtoll(cur, &end, 10); -#endif - if (cur != end) { - res += fracPart / pow(10.0, (double)(end - cur)); - hasFracPart = 1; - cur = end; - } - } - } - - // A valid number should have integer or fractional part. - if (!hasIntPart && !hasFracPart) - return 0.0; - - // Parse optional exponent - if (*cur == 'e' || *cur == 'E') { - double expPart = 0.0; - cur++; // skip 'E' - expPart = (double)strtol(cur, &end, 10); // Parse digit sequence with sign - if (cur != end) { - res *= pow(10.0, expPart); - } - } - - return res * sign; -} - - -static const char* nsvg__parseNumber(const char* s, char* it, const int size) -{ - const int last = size-1; - int i = 0; - - // sign - if (*s == '-' || *s == '+') { - if (i < last) it[i++] = *s; - s++; - } - // integer part - while (*s && nsvg__isdigit(*s)) { - if (i < last) it[i++] = *s; - s++; - } - if (*s == '.') { - // decimal point - if (i < last) it[i++] = *s; - s++; - // fraction part - while (*s && nsvg__isdigit(*s)) { - if (i < last) it[i++] = *s; - s++; - } - } - // exponent - if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) { - if (i < last) it[i++] = *s; - s++; - if (*s == '-' || *s == '+') { - if (i < last) it[i++] = *s; - s++; - } - while (*s && nsvg__isdigit(*s)) { - if (i < last) it[i++] = *s; - s++; - } - } - it[i] = '\0'; - - return s; -} - -static const char* nsvg__getNextPathItemWhenArcFlag(const char* s, char* it) -{ - it[0] = '\0'; - while (*s && (nsvg__isspace(*s) || *s == ',')) s++; - if (!*s) return s; - if (*s == '0' || *s == '1') { - it[0] = *s++; - it[1] = '\0'; - return s; - } - return s; -} - -static const char* nsvg__getNextPathItem(const char* s, char* it) -{ - it[0] = '\0'; - // Skip white spaces and commas - while (*s && (nsvg__isspace(*s) || *s == ',')) s++; - if (!*s) return s; - if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) { - s = nsvg__parseNumber(s, it, 64); - } else { - // Parse command - it[0] = *s++; - it[1] = '\0'; - return s; - } - - return s; -} - -static unsigned int nsvg__parseColorHex(const char* str) -{ - unsigned int r=0, g=0, b=0; - if (sscanf(str, "#%2x%2x%2x", &r, &g, &b) == 3 ) // 2 digit hex - return NSVG_RGB(r, g, b); - if (sscanf(str, "#%1x%1x%1x", &r, &g, &b) == 3 ) // 1 digit hex, e.g. #abc -> 0xccbbaa - return NSVG_RGB(r*17, g*17, b*17); // same effect as (r<<4|r), (g<<4|g), .. - return NSVG_RGB(128, 128, 128); -} - -// Parse rgb color. The pointer 'str' must point at "rgb(" (4+ characters). -// This function returns gray (rgb(128, 128, 128) == '#808080') on parse errors -// for backwards compatibility. Note: other image viewers return black instead. - -static unsigned int nsvg__parseColorRGB(const char* str) -{ - int i; - unsigned int rgbi[3]; - float rgbf[3]; - // try decimal integers first - if (sscanf(str, "rgb(%u, %u, %u)", &rgbi[0], &rgbi[1], &rgbi[2]) != 3) { - // integers failed, try percent values (float, locale independent) - const char delimiter[3] = {',', ',', ')'}; - str += 4; // skip "rgb(" - for (i = 0; i < 3; i++) { - while (*str && (nsvg__isspace(*str))) str++; // skip leading spaces - if (*str == '+') str++; // skip '+' (don't allow '-') - if (!*str) break; - rgbf[i] = nsvg__atof(str); - - // Note 1: it would be great if nsvg__atof() returned how many - // bytes it consumed but it doesn't. We need to skip the number, - // the '%' character, spaces, and the delimiter ',' or ')'. - - // Note 2: The following code does not allow values like "33.%", - // i.e. a decimal point w/o fractional part, but this is consistent - // with other image viewers, e.g. firefox, chrome, eog, gimp. - - while (*str && nsvg__isdigit(*str)) str++; // skip integer part - if (*str == '.') { - str++; - if (!nsvg__isdigit(*str)) break; // error: no digit after '.' - while (*str && nsvg__isdigit(*str)) str++; // skip fractional part - } - if (*str == '%') str++; else break; - while (nsvg__isspace(*str)) str++; - if (*str == delimiter[i]) str++; - else break; - } - if (i == 3) { - rgbi[0] = roundf(rgbf[0] * 2.55f); - rgbi[1] = roundf(rgbf[1] * 2.55f); - rgbi[2] = roundf(rgbf[2] * 2.55f); - } else { - rgbi[0] = rgbi[1] = rgbi[2] = 128; - } - } - // clip values as the CSS spec requires - for (i = 0; i < 3; i++) { - if (rgbi[i] > 255) rgbi[i] = 255; - } - return NSVG_RGB(rgbi[0], rgbi[1], rgbi[2]); -} - -typedef struct NSVGNamedColor { - const char* name; - unsigned int color; -} NSVGNamedColor; - -NSVGNamedColor nsvg__colors[] = { - - { "red", NSVG_RGB(255, 0, 0) }, - { "green", NSVG_RGB( 0, 128, 0) }, - { "blue", NSVG_RGB( 0, 0, 255) }, - { "yellow", NSVG_RGB(255, 255, 0) }, - { "cyan", NSVG_RGB( 0, 255, 255) }, - { "magenta", NSVG_RGB(255, 0, 255) }, - { "black", NSVG_RGB( 0, 0, 0) }, - { "grey", NSVG_RGB(128, 128, 128) }, - { "gray", NSVG_RGB(128, 128, 128) }, - { "white", NSVG_RGB(255, 255, 255) }, - -#ifdef NANOSVG_ALL_COLOR_KEYWORDS - { "aliceblue", NSVG_RGB(240, 248, 255) }, - { "antiquewhite", NSVG_RGB(250, 235, 215) }, - { "aqua", NSVG_RGB( 0, 255, 255) }, - { "aquamarine", NSVG_RGB(127, 255, 212) }, - { "azure", NSVG_RGB(240, 255, 255) }, - { "beige", NSVG_RGB(245, 245, 220) }, - { "bisque", NSVG_RGB(255, 228, 196) }, - { "blanchedalmond", NSVG_RGB(255, 235, 205) }, - { "blueviolet", NSVG_RGB(138, 43, 226) }, - { "brown", NSVG_RGB(165, 42, 42) }, - { "burlywood", NSVG_RGB(222, 184, 135) }, - { "cadetblue", NSVG_RGB( 95, 158, 160) }, - { "chartreuse", NSVG_RGB(127, 255, 0) }, - { "chocolate", NSVG_RGB(210, 105, 30) }, - { "coral", NSVG_RGB(255, 127, 80) }, - { "cornflowerblue", NSVG_RGB(100, 149, 237) }, - { "cornsilk", NSVG_RGB(255, 248, 220) }, - { "crimson", NSVG_RGB(220, 20, 60) }, - { "darkblue", NSVG_RGB( 0, 0, 139) }, - { "darkcyan", NSVG_RGB( 0, 139, 139) }, - { "darkgoldenrod", NSVG_RGB(184, 134, 11) }, - { "darkgray", NSVG_RGB(169, 169, 169) }, - { "darkgreen", NSVG_RGB( 0, 100, 0) }, - { "darkgrey", NSVG_RGB(169, 169, 169) }, - { "darkkhaki", NSVG_RGB(189, 183, 107) }, - { "darkmagenta", NSVG_RGB(139, 0, 139) }, - { "darkolivegreen", NSVG_RGB( 85, 107, 47) }, - { "darkorange", NSVG_RGB(255, 140, 0) }, - { "darkorchid", NSVG_RGB(153, 50, 204) }, - { "darkred", NSVG_RGB(139, 0, 0) }, - { "darksalmon", NSVG_RGB(233, 150, 122) }, - { "darkseagreen", NSVG_RGB(143, 188, 143) }, - { "darkslateblue", NSVG_RGB( 72, 61, 139) }, - { "darkslategray", NSVG_RGB( 47, 79, 79) }, - { "darkslategrey", NSVG_RGB( 47, 79, 79) }, - { "darkturquoise", NSVG_RGB( 0, 206, 209) }, - { "darkviolet", NSVG_RGB(148, 0, 211) }, - { "deeppink", NSVG_RGB(255, 20, 147) }, - { "deepskyblue", NSVG_RGB( 0, 191, 255) }, - { "dimgray", NSVG_RGB(105, 105, 105) }, - { "dimgrey", NSVG_RGB(105, 105, 105) }, - { "dodgerblue", NSVG_RGB( 30, 144, 255) }, - { "firebrick", NSVG_RGB(178, 34, 34) }, - { "floralwhite", NSVG_RGB(255, 250, 240) }, - { "forestgreen", NSVG_RGB( 34, 139, 34) }, - { "fuchsia", NSVG_RGB(255, 0, 255) }, - { "gainsboro", NSVG_RGB(220, 220, 220) }, - { "ghostwhite", NSVG_RGB(248, 248, 255) }, - { "gold", NSVG_RGB(255, 215, 0) }, - { "goldenrod", NSVG_RGB(218, 165, 32) }, - { "greenyellow", NSVG_RGB(173, 255, 47) }, - { "honeydew", NSVG_RGB(240, 255, 240) }, - { "hotpink", NSVG_RGB(255, 105, 180) }, - { "indianred", NSVG_RGB(205, 92, 92) }, - { "indigo", NSVG_RGB( 75, 0, 130) }, - { "ivory", NSVG_RGB(255, 255, 240) }, - { "khaki", NSVG_RGB(240, 230, 140) }, - { "lavender", NSVG_RGB(230, 230, 250) }, - { "lavenderblush", NSVG_RGB(255, 240, 245) }, - { "lawngreen", NSVG_RGB(124, 252, 0) }, - { "lemonchiffon", NSVG_RGB(255, 250, 205) }, - { "lightblue", NSVG_RGB(173, 216, 230) }, - { "lightcoral", NSVG_RGB(240, 128, 128) }, - { "lightcyan", NSVG_RGB(224, 255, 255) }, - { "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) }, - { "lightgray", NSVG_RGB(211, 211, 211) }, - { "lightgreen", NSVG_RGB(144, 238, 144) }, - { "lightgrey", NSVG_RGB(211, 211, 211) }, - { "lightpink", NSVG_RGB(255, 182, 193) }, - { "lightsalmon", NSVG_RGB(255, 160, 122) }, - { "lightseagreen", NSVG_RGB( 32, 178, 170) }, - { "lightskyblue", NSVG_RGB(135, 206, 250) }, - { "lightslategray", NSVG_RGB(119, 136, 153) }, - { "lightslategrey", NSVG_RGB(119, 136, 153) }, - { "lightsteelblue", NSVG_RGB(176, 196, 222) }, - { "lightyellow", NSVG_RGB(255, 255, 224) }, - { "lime", NSVG_RGB( 0, 255, 0) }, - { "limegreen", NSVG_RGB( 50, 205, 50) }, - { "linen", NSVG_RGB(250, 240, 230) }, - { "maroon", NSVG_RGB(128, 0, 0) }, - { "mediumaquamarine", NSVG_RGB(102, 205, 170) }, - { "mediumblue", NSVG_RGB( 0, 0, 205) }, - { "mediumorchid", NSVG_RGB(186, 85, 211) }, - { "mediumpurple", NSVG_RGB(147, 112, 219) }, - { "mediumseagreen", NSVG_RGB( 60, 179, 113) }, - { "mediumslateblue", NSVG_RGB(123, 104, 238) }, - { "mediumspringgreen", NSVG_RGB( 0, 250, 154) }, - { "mediumturquoise", NSVG_RGB( 72, 209, 204) }, - { "mediumvioletred", NSVG_RGB(199, 21, 133) }, - { "midnightblue", NSVG_RGB( 25, 25, 112) }, - { "mintcream", NSVG_RGB(245, 255, 250) }, - { "mistyrose", NSVG_RGB(255, 228, 225) }, - { "moccasin", NSVG_RGB(255, 228, 181) }, - { "navajowhite", NSVG_RGB(255, 222, 173) }, - { "navy", NSVG_RGB( 0, 0, 128) }, - { "oldlace", NSVG_RGB(253, 245, 230) }, - { "olive", NSVG_RGB(128, 128, 0) }, - { "olivedrab", NSVG_RGB(107, 142, 35) }, - { "orange", NSVG_RGB(255, 165, 0) }, - { "orangered", NSVG_RGB(255, 69, 0) }, - { "orchid", NSVG_RGB(218, 112, 214) }, - { "palegoldenrod", NSVG_RGB(238, 232, 170) }, - { "palegreen", NSVG_RGB(152, 251, 152) }, - { "paleturquoise", NSVG_RGB(175, 238, 238) }, - { "palevioletred", NSVG_RGB(219, 112, 147) }, - { "papayawhip", NSVG_RGB(255, 239, 213) }, - { "peachpuff", NSVG_RGB(255, 218, 185) }, - { "peru", NSVG_RGB(205, 133, 63) }, - { "pink", NSVG_RGB(255, 192, 203) }, - { "plum", NSVG_RGB(221, 160, 221) }, - { "powderblue", NSVG_RGB(176, 224, 230) }, - { "purple", NSVG_RGB(128, 0, 128) }, - { "rosybrown", NSVG_RGB(188, 143, 143) }, - { "royalblue", NSVG_RGB( 65, 105, 225) }, - { "saddlebrown", NSVG_RGB(139, 69, 19) }, - { "salmon", NSVG_RGB(250, 128, 114) }, - { "sandybrown", NSVG_RGB(244, 164, 96) }, - { "seagreen", NSVG_RGB( 46, 139, 87) }, - { "seashell", NSVG_RGB(255, 245, 238) }, - { "sienna", NSVG_RGB(160, 82, 45) }, - { "silver", NSVG_RGB(192, 192, 192) }, - { "skyblue", NSVG_RGB(135, 206, 235) }, - { "slateblue", NSVG_RGB(106, 90, 205) }, - { "slategray", NSVG_RGB(112, 128, 144) }, - { "slategrey", NSVG_RGB(112, 128, 144) }, - { "snow", NSVG_RGB(255, 250, 250) }, - { "springgreen", NSVG_RGB( 0, 255, 127) }, - { "steelblue", NSVG_RGB( 70, 130, 180) }, - { "tan", NSVG_RGB(210, 180, 140) }, - { "teal", NSVG_RGB( 0, 128, 128) }, - { "thistle", NSVG_RGB(216, 191, 216) }, - { "tomato", NSVG_RGB(255, 99, 71) }, - { "turquoise", NSVG_RGB( 64, 224, 208) }, - { "violet", NSVG_RGB(238, 130, 238) }, - { "wheat", NSVG_RGB(245, 222, 179) }, - { "whitesmoke", NSVG_RGB(245, 245, 245) }, - { "yellowgreen", NSVG_RGB(154, 205, 50) }, -#endif -}; - -static unsigned int nsvg__parseColorName(const char* str) -{ - int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor); - - for (i = 0; i < ncolors; i++) { - if (strcmp(nsvg__colors[i].name, str) == 0) { - return nsvg__colors[i].color; - } - } - - return NSVG_RGB(128, 128, 128); -} - -static unsigned int nsvg__parseColor(const char* str) -{ - size_t len = 0; - while(*str == ' ') ++str; - len = strlen(str); - if (len >= 1 && *str == '#') - return nsvg__parseColorHex(str); - else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(') - return nsvg__parseColorRGB(str); - return nsvg__parseColorName(str); -} - -static float nsvg__parseOpacity(const char* str) -{ - float val = nsvg__atof(str); - if (val < 0.0f) val = 0.0f; - if (val > 1.0f) val = 1.0f; - return val; -} - -static float nsvg__parseMiterLimit(const char* str) -{ - float val = nsvg__atof(str); - if (val < 0.0f) val = 0.0f; - return val; -} - -static int nsvg__parseUnits(const char* units) -{ - if (units[0] == 'p' && units[1] == 'x') - return NSVG_UNITS_PX; - else if (units[0] == 'p' && units[1] == 't') - return NSVG_UNITS_PT; - else if (units[0] == 'p' && units[1] == 'c') - return NSVG_UNITS_PC; - else if (units[0] == 'm' && units[1] == 'm') - return NSVG_UNITS_MM; - else if (units[0] == 'c' && units[1] == 'm') - return NSVG_UNITS_CM; - else if (units[0] == 'i' && units[1] == 'n') - return NSVG_UNITS_IN; - else if (units[0] == '%') - return NSVG_UNITS_PERCENT; - else if (units[0] == 'e' && units[1] == 'm') - return NSVG_UNITS_EM; - else if (units[0] == 'e' && units[1] == 'x') - return NSVG_UNITS_EX; - return NSVG_UNITS_USER; -} - -static int nsvg__isCoordinate(const char* s) -{ - // optional sign - if (*s == '-' || *s == '+') - s++; - // must have at least one digit, or start by a dot - return (nsvg__isdigit(*s) || *s == '.'); -} - -static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str) -{ - NSVGcoordinate coord = {0, NSVG_UNITS_USER}; - char buf[64]; - coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64)); - coord.value = nsvg__atof(buf); - return coord; -} - -static NSVGcoordinate nsvg__coord(float v, int units) -{ - NSVGcoordinate coord = {v, units}; - return coord; -} - -static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length) -{ - NSVGcoordinate coord = nsvg__parseCoordinateRaw(str); - return nsvg__convertToPixels(p, coord, orig, length); -} - -static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na) -{ - const char* end; - const char* ptr; - char it[64]; - - *na = 0; - ptr = str; - while (*ptr && *ptr != '(') ++ptr; - if (*ptr == 0) - return 1; - end = ptr; - while (*end && *end != ')') ++end; - if (*end == 0) - return 1; - - while (ptr < end) { - if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) { - if (*na >= maxNa) return 0; - ptr = nsvg__parseNumber(ptr, it, 64); - args[(*na)++] = (float)nsvg__atof(it); - } else { - ++ptr; - } - } - return (int)(end - str); -} - - -static int nsvg__parseMatrix(float* xform, const char* str) -{ - float t[6]; - int na = 0; - int len = nsvg__parseTransformArgs(str, t, 6, &na); - if (na != 6) return len; - memcpy(xform, t, sizeof(float)*6); - return len; -} - -static int nsvg__parseTranslate(float* xform, const char* str) -{ - float args[2]; - float t[6]; - int na = 0; - int len = nsvg__parseTransformArgs(str, args, 2, &na); - if (na == 1) args[1] = 0.0; - - nsvg__xformSetTranslation(t, args[0], args[1]); - memcpy(xform, t, sizeof(float)*6); - return len; -} - -static int nsvg__parseScale(float* xform, const char* str) -{ - float args[2]; - int na = 0; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 2, &na); - if (na == 1) args[1] = args[0]; - nsvg__xformSetScale(t, args[0], args[1]); - memcpy(xform, t, sizeof(float)*6); - return len; -} - -static int nsvg__parseSkewX(float* xform, const char* str) -{ - float args[1]; - int na = 0; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 1, &na); - nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI); - memcpy(xform, t, sizeof(float)*6); - return len; -} - -static int nsvg__parseSkewY(float* xform, const char* str) -{ - float args[1]; - int na = 0; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 1, &na); - nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI); - memcpy(xform, t, sizeof(float)*6); - return len; -} - -static int nsvg__parseRotate(float* xform, const char* str) -{ - float args[3]; - int na = 0; - float m[6]; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 3, &na); - if (na == 1) - args[1] = args[2] = 0.0f; - nsvg__xformIdentity(m); - - if (na > 1) { - nsvg__xformSetTranslation(t, -args[1], -args[2]); - nsvg__xformMultiply(m, t); - } - - nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI); - nsvg__xformMultiply(m, t); - - if (na > 1) { - nsvg__xformSetTranslation(t, args[1], args[2]); - nsvg__xformMultiply(m, t); - } - - memcpy(xform, m, sizeof(float)*6); - - return len; -} - -static void nsvg__parseTransform(float* xform, const char* str) -{ - float t[6]; - int len; - nsvg__xformIdentity(xform); - while (*str) - { - if (strncmp(str, "matrix", 6) == 0) - len = nsvg__parseMatrix(t, str); - else if (strncmp(str, "translate", 9) == 0) - len = nsvg__parseTranslate(t, str); - else if (strncmp(str, "scale", 5) == 0) - len = nsvg__parseScale(t, str); - else if (strncmp(str, "rotate", 6) == 0) - len = nsvg__parseRotate(t, str); - else if (strncmp(str, "skewX", 5) == 0) - len = nsvg__parseSkewX(t, str); - else if (strncmp(str, "skewY", 5) == 0) - len = nsvg__parseSkewY(t, str); - else{ - ++str; - continue; - } - if (len != 0) { - str += len; - } else { - ++str; - continue; - } - - nsvg__xformPremultiply(xform, t); - } -} - -static void nsvg__parseUrl(char* id, const char* str) -{ - int i = 0; - str += 4; // "url("; - if (*str && *str == '#') - str++; - while (i < 63 && *str && *str != ')') { - id[i] = *str++; - i++; - } - id[i] = '\0'; -} - -static char nsvg__parseLineCap(const char* str) -{ - if (strcmp(str, "butt") == 0) - return NSVG_CAP_BUTT; - else if (strcmp(str, "round") == 0) - return NSVG_CAP_ROUND; - else if (strcmp(str, "square") == 0) - return NSVG_CAP_SQUARE; - // TODO: handle inherit. - return NSVG_CAP_BUTT; -} - -static char nsvg__parseLineJoin(const char* str) -{ - if (strcmp(str, "miter") == 0) - return NSVG_JOIN_MITER; - else if (strcmp(str, "round") == 0) - return NSVG_JOIN_ROUND; - else if (strcmp(str, "bevel") == 0) - return NSVG_JOIN_BEVEL; - // TODO: handle inherit. - return NSVG_JOIN_MITER; -} - -static char nsvg__parseFillRule(const char* str) -{ - if (strcmp(str, "nonzero") == 0) - return NSVG_FILLRULE_NONZERO; - else if (strcmp(str, "evenodd") == 0) - return NSVG_FILLRULE_EVENODD; - // TODO: handle inherit. - return NSVG_FILLRULE_NONZERO; -} - -static const char* nsvg__getNextDashItem(const char* s, char* it) -{ - int n = 0; - it[0] = '\0'; - // Skip white spaces and commas - while (*s && (nsvg__isspace(*s) || *s == ',')) s++; - // Advance until whitespace, comma or end. - while (*s && (!nsvg__isspace(*s) && *s != ',')) { - if (n < 63) - it[n++] = *s; - s++; - } - it[n++] = '\0'; - return s; -} - -static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray) -{ - char item[64]; - int count = 0, i; - float sum = 0.0f; - - // Handle "none" - if (str[0] == 'n') - return 0; - - // Parse dashes - while (*str) { - str = nsvg__getNextDashItem(str, item); - if (!*item) break; - if (count < NSVG_MAX_DASHES) - strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p))); - } - - for (i = 0; i < count; i++) - sum += strokeDashArray[i]; - if (sum <= 1e-6f) - count = 0; - - return count; -} - -static void nsvg__parseStyle(NSVGparser* p, const char* str); - -static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) -{ - float xform[6]; - NSVGattrib* attr = nsvg__getAttr(p); - if (!attr) return 0; - - if (strcmp(name, "style") == 0) { - nsvg__parseStyle(p, value); - } else if (strcmp(name, "display") == 0) { - if (strcmp(value, "none") == 0) - attr->visible = 0; - // Don't reset ->visible on display:inline, one display:none hides the whole subtree - - } else if (strcmp(name, "fill") == 0) { - if (strcmp(value, "none") == 0) { - attr->hasFill = 0; - } else if (strncmp(value, "url(", 4) == 0) { - attr->hasFill = 2; - nsvg__parseUrl(attr->fillGradient, value); - } else { - attr->hasFill = 1; - attr->fillColor = nsvg__parseColor(value); - } - } else if (strcmp(name, "opacity") == 0) { - attr->opacity = nsvg__parseOpacity(value); - } else if (strcmp(name, "fill-opacity") == 0) { - attr->fillOpacity = nsvg__parseOpacity(value); - } else if (strcmp(name, "stroke") == 0) { - if (strcmp(value, "none") == 0) { - attr->hasStroke = 0; - } else if (strncmp(value, "url(", 4) == 0) { - attr->hasStroke = 2; - nsvg__parseUrl(attr->strokeGradient, value); - } else { - attr->hasStroke = 1; - attr->strokeColor = nsvg__parseColor(value); - } - } else if (strcmp(name, "stroke-width") == 0) { - attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); - } else if (strcmp(name, "stroke-dasharray") == 0) { - attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray); - } else if (strcmp(name, "stroke-dashoffset") == 0) { - attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); - } else if (strcmp(name, "stroke-opacity") == 0) { - attr->strokeOpacity = nsvg__parseOpacity(value); - } else if (strcmp(name, "stroke-linecap") == 0) { - attr->strokeLineCap = nsvg__parseLineCap(value); - } else if (strcmp(name, "stroke-linejoin") == 0) { - attr->strokeLineJoin = nsvg__parseLineJoin(value); - } else if (strcmp(name, "stroke-miterlimit") == 0) { - attr->miterLimit = nsvg__parseMiterLimit(value); - } else if (strcmp(name, "fill-rule") == 0) { - attr->fillRule = nsvg__parseFillRule(value); - } else if (strcmp(name, "font-size") == 0) { - attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); - } else if (strcmp(name, "transform") == 0) { - nsvg__parseTransform(xform, value); - nsvg__xformPremultiply(attr->xform, xform); - } else if (strcmp(name, "stop-color") == 0) { - attr->stopColor = nsvg__parseColor(value); - } else if (strcmp(name, "stop-opacity") == 0) { - attr->stopOpacity = nsvg__parseOpacity(value); - } else if (strcmp(name, "offset") == 0) { - attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f); - } else if (strcmp(name, "id") == 0) { - strncpy(attr->id, value, 63); - attr->id[63] = '\0'; - } else { - return 0; - } - return 1; -} - -static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* end) -{ - const char* str; - const char* val; - char name[512]; - char value[512]; - int n; - - str = start; - while (str < end && *str != ':') ++str; - - val = str; - - // Right Trim - while (str > start && (*str == ':' || nsvg__isspace(*str))) --str; - ++str; - - n = (int)(str - start); - if (n > 511) n = 511; - if (n) memcpy(name, start, n); - name[n] = 0; - - while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val; - - n = (int)(end - val); - if (n > 511) n = 511; - if (n) memcpy(value, val, n); - value[n] = 0; - - return nsvg__parseAttr(p, name, value); -} - -static void nsvg__parseStyle(NSVGparser* p, const char* str) -{ - const char* start; - const char* end; - - while (*str) { - // Left Trim - while(*str && nsvg__isspace(*str)) ++str; - start = str; - while(*str && *str != ';') ++str; - end = str; - - // Right Trim - while (end > start && (*end == ';' || nsvg__isspace(*end))) --end; - ++end; - - nsvg__parseNameValue(p, start, end); - if (*str) ++str; - } -} - -static void nsvg__parseAttribs(NSVGparser* p, const char** attr) -{ - int i; - for (i = 0; attr[i]; i += 2) - { - if (strcmp(attr[i], "style") == 0) - nsvg__parseStyle(p, attr[i + 1]); - else - nsvg__parseAttr(p, attr[i], attr[i + 1]); - } -} - -static int nsvg__getArgsPerElement(char cmd) -{ - switch (cmd) { - case 'v': - case 'V': - case 'h': - case 'H': - return 1; - case 'm': - case 'M': - case 'l': - case 'L': - case 't': - case 'T': - return 2; - case 'q': - case 'Q': - case 's': - case 'S': - return 4; - case 'c': - case 'C': - return 6; - case 'a': - case 'A': - return 7; - case 'z': - case 'Z': - return 0; - } - return -1; -} - -static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) -{ - if (rel) { - *cpx += args[0]; - *cpy += args[1]; - } else { - *cpx = args[0]; - *cpy = args[1]; - } - nsvg__moveTo(p, *cpx, *cpy); -} - -static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) -{ - if (rel) { - *cpx += args[0]; - *cpy += args[1]; - } else { - *cpx = args[0]; - *cpy = args[1]; - } - nsvg__lineTo(p, *cpx, *cpy); -} - -static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) -{ - if (rel) - *cpx += args[0]; - else - *cpx = args[0]; - nsvg__lineTo(p, *cpx, *cpy); -} - -static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) -{ - if (rel) - *cpy += args[0]; - else - *cpy = args[0]; - nsvg__lineTo(p, *cpx, *cpy); -} - -static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy, - float* cpx2, float* cpy2, float* args, int rel) -{ - float x2, y2, cx1, cy1, cx2, cy2; - - if (rel) { - cx1 = *cpx + args[0]; - cy1 = *cpy + args[1]; - cx2 = *cpx + args[2]; - cy2 = *cpy + args[3]; - x2 = *cpx + args[4]; - y2 = *cpy + args[5]; - } else { - cx1 = args[0]; - cy1 = args[1]; - cx2 = args[2]; - cy2 = args[3]; - x2 = args[4]; - y2 = args[5]; - } - - nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); - - *cpx2 = cx2; - *cpy2 = cy2; - *cpx = x2; - *cpy = y2; -} - -static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy, - float* cpx2, float* cpy2, float* args, int rel) -{ - float x1, y1, x2, y2, cx1, cy1, cx2, cy2; - - x1 = *cpx; - y1 = *cpy; - if (rel) { - cx2 = *cpx + args[0]; - cy2 = *cpy + args[1]; - x2 = *cpx + args[2]; - y2 = *cpy + args[3]; - } else { - cx2 = args[0]; - cy2 = args[1]; - x2 = args[2]; - y2 = args[3]; - } - - cx1 = 2*x1 - *cpx2; - cy1 = 2*y1 - *cpy2; - - nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); - - *cpx2 = cx2; - *cpy2 = cy2; - *cpx = x2; - *cpy = y2; -} - -static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy, - float* cpx2, float* cpy2, float* args, int rel) -{ - float x1, y1, x2, y2, cx, cy; - float cx1, cy1, cx2, cy2; - - x1 = *cpx; - y1 = *cpy; - if (rel) { - cx = *cpx + args[0]; - cy = *cpy + args[1]; - x2 = *cpx + args[2]; - y2 = *cpy + args[3]; - } else { - cx = args[0]; - cy = args[1]; - x2 = args[2]; - y2 = args[3]; - } - - // Convert to cubic bezier - cx1 = x1 + 2.0f/3.0f*(cx - x1); - cy1 = y1 + 2.0f/3.0f*(cy - y1); - cx2 = x2 + 2.0f/3.0f*(cx - x2); - cy2 = y2 + 2.0f/3.0f*(cy - y2); - - nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); - - *cpx2 = cx; - *cpy2 = cy; - *cpx = x2; - *cpy = y2; -} - -static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy, - float* cpx2, float* cpy2, float* args, int rel) -{ - float x1, y1, x2, y2, cx, cy; - float cx1, cy1, cx2, cy2; - - x1 = *cpx; - y1 = *cpy; - if (rel) { - x2 = *cpx + args[0]; - y2 = *cpy + args[1]; - } else { - x2 = args[0]; - y2 = args[1]; - } - - cx = 2*x1 - *cpx2; - cy = 2*y1 - *cpy2; - - // Convert to cubix bezier - cx1 = x1 + 2.0f/3.0f*(cx - x1); - cy1 = y1 + 2.0f/3.0f*(cy - y1); - cx2 = x2 + 2.0f/3.0f*(cx - x2); - cy2 = y2 + 2.0f/3.0f*(cy - y2); - - nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); - - *cpx2 = cx; - *cpy2 = cy; - *cpx = x2; - *cpy = y2; -} - -static float nsvg__sqr(float x) { return x*x; } -static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); } - -static float nsvg__vecrat(float ux, float uy, float vx, float vy) -{ - return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy)); -} - -static float nsvg__vecang(float ux, float uy, float vx, float vy) -{ - float r = nsvg__vecrat(ux,uy, vx,vy); - if (r < -1.0f) r = -1.0f; - if (r > 1.0f) r = 1.0f; - return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r); -} - -static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) -{ - // Ported from canvg (https://code.google.com/p/canvg/) - float rx, ry, rotx; - float x1, y1, x2, y2, cx, cy, dx, dy, d; - float x1p, y1p, cxp, cyp, s, sa, sb; - float ux, uy, vx, vy, a1, da; - float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6]; - float sinrx, cosrx; - int fa, fs; - int i, ndivs; - float hda, kappa; - - rx = fabsf(args[0]); // y radius - ry = fabsf(args[1]); // x radius - rotx = args[2] / 180.0f * NSVG_PI; // x rotation angle - fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc - fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction - x1 = *cpx; // start point - y1 = *cpy; - if (rel) { // end point - x2 = *cpx + args[5]; - y2 = *cpy + args[6]; - } else { - x2 = args[5]; - y2 = args[6]; - } - - dx = x1 - x2; - dy = y1 - y2; - d = sqrtf(dx*dx + dy*dy); - if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) { - // The arc degenerates to a line - nsvg__lineTo(p, x2, y2); - *cpx = x2; - *cpy = y2; - return; - } - - sinrx = sinf(rotx); - cosrx = cosf(rotx); - - // Convert to center point parameterization. - // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes - // 1) Compute x1', y1' - x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f; - y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f; - d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry); - if (d > 1) { - d = sqrtf(d); - rx *= d; - ry *= d; - } - // 2) Compute cx', cy' - s = 0.0f; - sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p); - sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p); - if (sa < 0.0f) sa = 0.0f; - if (sb > 0.0f) - s = sqrtf(sa / sb); - if (fa == fs) - s = -s; - cxp = s * rx * y1p / ry; - cyp = s * -ry * x1p / rx; - - // 3) Compute cx,cy from cx',cy' - cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp; - cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp; - - // 4) Calculate theta1, and delta theta. - ux = (x1p - cxp) / rx; - uy = (y1p - cyp) / ry; - vx = (-x1p - cxp) / rx; - vy = (-y1p - cyp) / ry; - a1 = nsvg__vecang(1.0f,0.0f, ux,uy); // Initial angle - da = nsvg__vecang(ux,uy, vx,vy); // Delta angle - -// if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI; -// if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0; - - if (fs == 0 && da > 0) - da -= 2 * NSVG_PI; - else if (fs == 1 && da < 0) - da += 2 * NSVG_PI; - - // Approximate the arc using cubic spline segments. - t[0] = cosrx; t[1] = sinrx; - t[2] = -sinrx; t[3] = cosrx; - t[4] = cx; t[5] = cy; - - // Split arc into max 90 degree segments. - // The loop assumes an iteration per end point (including start and end), this +1. - ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f); - hda = (da / (float)ndivs) / 2.0f; - // Fix for ticket #179: division by 0: avoid cotangens around 0 (infinite) - if ((hda < 1e-3f) && (hda > -1e-3f)) - hda *= 0.5f; - else - hda = (1.0f - cosf(hda)) / sinf(hda); - kappa = fabsf(4.0f / 3.0f * hda); - if (da < 0.0f) - kappa = -kappa; - - for (i = 0; i <= ndivs; i++) { - a = a1 + da * ((float)i/(float)ndivs); - dx = cosf(a); - dy = sinf(a); - nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position - nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent - if (i > 0) - nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y); - px = x; - py = y; - ptanx = tanx; - ptany = tany; - } - - *cpx = x2; - *cpy = y2; -} - -static void nsvg__parsePath(NSVGparser* p, const char** attr) -{ - const char* s = NULL; - char cmd = '\0'; - float args[10]; - int nargs; - int rargs = 0; - char initPoint; - float cpx, cpy, cpx2, cpy2; - const char* tmp[4]; - char closedFlag; - int i; - char item[64]; - - for (i = 0; attr[i]; i += 2) { - if (strcmp(attr[i], "d") == 0) { - s = attr[i + 1]; - } else { - tmp[0] = attr[i]; - tmp[1] = attr[i + 1]; - tmp[2] = 0; - tmp[3] = 0; - nsvg__parseAttribs(p, tmp); - } - } - - if (s) { - nsvg__resetPath(p); - cpx = 0; cpy = 0; - cpx2 = 0; cpy2 = 0; - initPoint = 0; - closedFlag = 0; - nargs = 0; - - while (*s) { - item[0] = '\0'; - if ((cmd == 'A' || cmd == 'a') && (nargs == 3 || nargs == 4)) - s = nsvg__getNextPathItemWhenArcFlag(s, item); - if (!*item) - s = nsvg__getNextPathItem(s, item); - if (!*item) break; - if (cmd != '\0' && nsvg__isCoordinate(item)) { - if (nargs < 10) - args[nargs++] = (float)nsvg__atof(item); - if (nargs >= rargs) { - switch (cmd) { - case 'm': - case 'M': - nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0); - // Moveto can be followed by multiple coordinate pairs, - // which should be treated as linetos. - cmd = (cmd == 'm') ? 'l' : 'L'; - rargs = nsvg__getArgsPerElement(cmd); - cpx2 = cpx; cpy2 = cpy; - initPoint = 1; - break; - case 'l': - case 'L': - nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0); - cpx2 = cpx; cpy2 = cpy; - break; - case 'H': - case 'h': - nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0); - cpx2 = cpx; cpy2 = cpy; - break; - case 'V': - case 'v': - nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0); - cpx2 = cpx; cpy2 = cpy; - break; - case 'C': - case 'c': - nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0); - break; - case 'S': - case 's': - nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0); - break; - case 'Q': - case 'q': - nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0); - break; - case 'T': - case 't': - nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0); - break; - case 'A': - case 'a': - nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0); - cpx2 = cpx; cpy2 = cpy; - break; - default: - if (nargs >= 2) { - cpx = args[nargs-2]; - cpy = args[nargs-1]; - cpx2 = cpx; cpy2 = cpy; - } - break; - } - nargs = 0; - } - } else { - cmd = item[0]; - if (cmd == 'M' || cmd == 'm') { - // Commit path. - if (p->npts > 0) - nsvg__addPath(p, closedFlag); - // Start new subpath. - nsvg__resetPath(p); - closedFlag = 0; - nargs = 0; - } else if (initPoint == 0) { - // Do not allow other commands until initial point has been set (moveTo called once). - cmd = '\0'; - } - if (cmd == 'Z' || cmd == 'z') { - closedFlag = 1; - // Commit path. - if (p->npts > 0) { - // Move current point to first point - cpx = p->pts[0]; - cpy = p->pts[1]; - cpx2 = cpx; cpy2 = cpy; - nsvg__addPath(p, closedFlag); - } - // Start new subpath. - nsvg__resetPath(p); - nsvg__moveTo(p, cpx, cpy); - closedFlag = 0; - nargs = 0; - } - rargs = nsvg__getArgsPerElement(cmd); - if (rargs == -1) { - // Command not recognized - cmd = '\0'; - rargs = 0; - } - } - } - // Commit path. - if (p->npts) - nsvg__addPath(p, closedFlag); - } - - nsvg__addShape(p); -} - -static void nsvg__parseRect(NSVGparser* p, const char** attr) -{ - float x = 0.0f; - float y = 0.0f; - float w = 0.0f; - float h = 0.0f; - float rx = -1.0f; // marks not set - float ry = -1.0f; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)); - if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)); - if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); - if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); - } - } - - if (rx < 0.0f && ry > 0.0f) rx = ry; - if (ry < 0.0f && rx > 0.0f) ry = rx; - if (rx < 0.0f) rx = 0.0f; - if (ry < 0.0f) ry = 0.0f; - if (rx > w/2.0f) rx = w/2.0f; - if (ry > h/2.0f) ry = h/2.0f; - - if (w != 0.0f && h != 0.0f) { - nsvg__resetPath(p); - - if (rx < 0.00001f || ry < 0.0001f) { - nsvg__moveTo(p, x, y); - nsvg__lineTo(p, x+w, y); - nsvg__lineTo(p, x+w, y+h); - nsvg__lineTo(p, x, y+h); - } else { - // Rounded rectangle - nsvg__moveTo(p, x+rx, y); - nsvg__lineTo(p, x+w-rx, y); - nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry); - nsvg__lineTo(p, x+w, y+h-ry); - nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h); - nsvg__lineTo(p, x+rx, y+h); - nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry); - nsvg__lineTo(p, x, y+ry); - nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y); - } - - nsvg__addPath(p, 1); - - nsvg__addShape(p); - } -} - -static void nsvg__parseCircle(NSVGparser* p, const char** attr) -{ - float cx = 0.0f; - float cy = 0.0f; - float r = 0.0f; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p))); - } - } - - if (r > 0.0f) { - nsvg__resetPath(p); - - nsvg__moveTo(p, cx+r, cy); - nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r); - nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy); - nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r); - nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy); - - nsvg__addPath(p, 1); - - nsvg__addShape(p); - } -} - -static void nsvg__parseEllipse(NSVGparser* p, const char** attr) -{ - float cx = 0.0f; - float cy = 0.0f; - float rx = 0.0f; - float ry = 0.0f; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); - if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); - } - } - - if (rx > 0.0f && ry > 0.0f) { - - nsvg__resetPath(p); - - nsvg__moveTo(p, cx+rx, cy); - nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry); - nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy); - nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry); - nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy); - - nsvg__addPath(p, 1); - - nsvg__addShape(p); - } -} - -static void nsvg__parseLine(NSVGparser* p, const char** attr) -{ - float x1 = 0.0; - float y1 = 0.0; - float x2 = 0.0; - float y2 = 0.0; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - } - } - - nsvg__resetPath(p); - - nsvg__moveTo(p, x1, y1); - nsvg__lineTo(p, x2, y2); - - nsvg__addPath(p, 0); - - nsvg__addShape(p); -} - -static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag) -{ - int i; - const char* s; - float args[2]; - int nargs, npts = 0; - char item[64]; - - nsvg__resetPath(p); - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "points") == 0) { - s = attr[i + 1]; - nargs = 0; - while (*s) { - s = nsvg__getNextPathItem(s, item); - args[nargs++] = (float)nsvg__atof(item); - if (nargs >= 2) { - if (npts == 0) - nsvg__moveTo(p, args[0], args[1]); - else - nsvg__lineTo(p, args[0], args[1]); - nargs = 0; - npts++; - } - } - } - } - } - - nsvg__addPath(p, (char)closeFlag); - - nsvg__addShape(p); -} - -static void nsvg__parseSVG(NSVGparser* p, const char** attr) -{ - int i; - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "width") == 0) { - p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); - } else if (strcmp(attr[i], "height") == 0) { - p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); - } else if (strcmp(attr[i], "viewBox") == 0) { - const char *s = attr[i + 1]; - char buf[64]; - s = nsvg__parseNumber(s, buf, 64); - p->viewMinx = nsvg__atof(buf); - while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; - if (!*s) return; - s = nsvg__parseNumber(s, buf, 64); - p->viewMiny = nsvg__atof(buf); - while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; - if (!*s) return; - s = nsvg__parseNumber(s, buf, 64); - p->viewWidth = nsvg__atof(buf); - while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; - if (!*s) return; - s = nsvg__parseNumber(s, buf, 64); - p->viewHeight = nsvg__atof(buf); - } else if (strcmp(attr[i], "preserveAspectRatio") == 0) { - if (strstr(attr[i + 1], "none") != 0) { - // No uniform scaling - p->alignType = NSVG_ALIGN_NONE; - } else { - // Parse X align - if (strstr(attr[i + 1], "xMin") != 0) - p->alignX = NSVG_ALIGN_MIN; - else if (strstr(attr[i + 1], "xMid") != 0) - p->alignX = NSVG_ALIGN_MID; - else if (strstr(attr[i + 1], "xMax") != 0) - p->alignX = NSVG_ALIGN_MAX; - // Parse X align - if (strstr(attr[i + 1], "yMin") != 0) - p->alignY = NSVG_ALIGN_MIN; - else if (strstr(attr[i + 1], "yMid") != 0) - p->alignY = NSVG_ALIGN_MID; - else if (strstr(attr[i + 1], "yMax") != 0) - p->alignY = NSVG_ALIGN_MAX; - // Parse meet/slice - p->alignType = NSVG_ALIGN_MEET; - if (strstr(attr[i + 1], "slice") != 0) - p->alignType = NSVG_ALIGN_SLICE; - } - } - } - } -} - -static void nsvg__parseGradient(NSVGparser* p, const char** attr, signed char type) -{ - int i; - NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData)); - if (grad == NULL) return; - memset(grad, 0, sizeof(NSVGgradientData)); - grad->units = NSVG_OBJECT_SPACE; - grad->type = type; - if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) { - grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); - grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); - grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT); - grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); - } else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) { - grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); - grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); - grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); - } - - nsvg__xformIdentity(grad->xform); - - for (i = 0; attr[i]; i += 2) { - if (strcmp(attr[i], "id") == 0) { - strncpy(grad->id, attr[i+1], 63); - grad->id[63] = '\0'; - } else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "gradientUnits") == 0) { - if (strcmp(attr[i+1], "objectBoundingBox") == 0) - grad->units = NSVG_OBJECT_SPACE; - else - grad->units = NSVG_USER_SPACE; - } else if (strcmp(attr[i], "gradientTransform") == 0) { - nsvg__parseTransform(grad->xform, attr[i + 1]); - } else if (strcmp(attr[i], "cx") == 0) { - grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "cy") == 0) { - grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "r") == 0) { - grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "fx") == 0) { - grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "fy") == 0) { - grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "x1") == 0) { - grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "y1") == 0) { - grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "x2") == 0) { - grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "y2") == 0) { - grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "spreadMethod") == 0) { - if (strcmp(attr[i+1], "pad") == 0) - grad->spread = NSVG_SPREAD_PAD; - else if (strcmp(attr[i+1], "reflect") == 0) - grad->spread = NSVG_SPREAD_REFLECT; - else if (strcmp(attr[i+1], "repeat") == 0) - grad->spread = NSVG_SPREAD_REPEAT; - } else if (strcmp(attr[i], "xlink:href") == 0) { - const char *href = attr[i+1]; - strncpy(grad->ref, href+1, 62); - grad->ref[62] = '\0'; - } - } - } - - grad->next = p->gradients; - p->gradients = grad; -} - -static void nsvg__parseGradientStop(NSVGparser* p, const char** attr) -{ - NSVGattrib* curAttr = nsvg__getAttr(p); - NSVGgradientData* grad; - NSVGgradientStop* stop; - int i, idx; - - curAttr->stopOffset = 0; - curAttr->stopColor = 0; - curAttr->stopOpacity = 1.0f; - - for (i = 0; attr[i]; i += 2) { - nsvg__parseAttr(p, attr[i], attr[i + 1]); - } - - // Add stop to the last gradient. - grad = p->gradients; - if (grad == NULL) return; - - grad->nstops++; - grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops); - if (grad->stops == NULL) return; - - // Insert - idx = grad->nstops-1; - for (i = 0; i < grad->nstops-1; i++) { - if (curAttr->stopOffset < grad->stops[i].offset) { - idx = i; - break; - } - } - if (idx != grad->nstops-1) { - for (i = grad->nstops-1; i > idx; i--) - grad->stops[i] = grad->stops[i-1]; - } - - stop = &grad->stops[idx]; - stop->color = curAttr->stopColor; - stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24; - stop->offset = curAttr->stopOffset; -} - -static void nsvg__startElement(void* ud, const char* el, const char** attr) -{ - NSVGparser* p = (NSVGparser*)ud; - - if (p->defsFlag) { - // Skip everything but gradients in defs - if (strcmp(el, "linearGradient") == 0) { - nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); - } else if (strcmp(el, "radialGradient") == 0) { - nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); - } else if (strcmp(el, "stop") == 0) { - nsvg__parseGradientStop(p, attr); - } - return; - } - - if (strcmp(el, "g") == 0) { - nsvg__pushAttr(p); - nsvg__parseAttribs(p, attr); - } else if (strcmp(el, "path") == 0) { - if (p->pathFlag) // Do not allow nested paths. - return; - nsvg__pushAttr(p); - nsvg__parsePath(p, attr); - nsvg__popAttr(p); - } else if (strcmp(el, "rect") == 0) { - nsvg__pushAttr(p); - nsvg__parseRect(p, attr); - nsvg__popAttr(p); - } else if (strcmp(el, "circle") == 0) { - nsvg__pushAttr(p); - nsvg__parseCircle(p, attr); - nsvg__popAttr(p); - } else if (strcmp(el, "ellipse") == 0) { - nsvg__pushAttr(p); - nsvg__parseEllipse(p, attr); - nsvg__popAttr(p); - } else if (strcmp(el, "line") == 0) { - nsvg__pushAttr(p); - nsvg__parseLine(p, attr); - nsvg__popAttr(p); - } else if (strcmp(el, "polyline") == 0) { - nsvg__pushAttr(p); - nsvg__parsePoly(p, attr, 0); - nsvg__popAttr(p); - } else if (strcmp(el, "polygon") == 0) { - nsvg__pushAttr(p); - nsvg__parsePoly(p, attr, 1); - nsvg__popAttr(p); - } else if (strcmp(el, "linearGradient") == 0) { - nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); - } else if (strcmp(el, "radialGradient") == 0) { - nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); - } else if (strcmp(el, "stop") == 0) { - nsvg__parseGradientStop(p, attr); - } else if (strcmp(el, "defs") == 0) { - p->defsFlag = 1; - } else if (strcmp(el, "svg") == 0) { - nsvg__parseSVG(p, attr); - } -} - -static void nsvg__endElement(void* ud, const char* el) -{ - NSVGparser* p = (NSVGparser*)ud; - - if (strcmp(el, "g") == 0) { - nsvg__popAttr(p); - } else if (strcmp(el, "path") == 0) { - p->pathFlag = 0; - } else if (strcmp(el, "defs") == 0) { - p->defsFlag = 0; - } -} - -static void nsvg__content(void* ud, const char* s) -{ - NSVG_NOTUSED(ud); - NSVG_NOTUSED(s); - // empty -} - -static void nsvg__imageBounds(NSVGparser* p, float* bounds) -{ - NSVGshape* shape; - shape = p->image->shapes; - if (shape == NULL) { - bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0; - return; - } - bounds[0] = shape->bounds[0]; - bounds[1] = shape->bounds[1]; - bounds[2] = shape->bounds[2]; - bounds[3] = shape->bounds[3]; - for (shape = shape->next; shape != NULL; shape = shape->next) { - bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]); - bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]); - bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]); - bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]); - } -} - -static float nsvg__viewAlign(float content, float container, int type) -{ - if (type == NSVG_ALIGN_MIN) - return 0; - else if (type == NSVG_ALIGN_MAX) - return container - content; - // mid - return (container - content) * 0.5f; -} - -static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy) -{ - float t[6]; - nsvg__xformSetTranslation(t, tx, ty); - nsvg__xformMultiply (grad->xform, t); - - nsvg__xformSetScale(t, sx, sy); - nsvg__xformMultiply (grad->xform, t); -} - -static void nsvg__scaleToViewbox(NSVGparser* p, const char* units) -{ - NSVGshape* shape; - NSVGpath* path; - float tx, ty, sx, sy, us, bounds[4], t[6], avgs; - int i; - float* pt; - - // Guess image size if not set completely. - nsvg__imageBounds(p, bounds); - - if (p->viewWidth == 0) { - if (p->image->width > 0) { - p->viewWidth = p->image->width; - } else { - p->viewMinx = bounds[0]; - p->viewWidth = bounds[2] - bounds[0]; - } - } - if (p->viewHeight == 0) { - if (p->image->height > 0) { - p->viewHeight = p->image->height; - } else { - p->viewMiny = bounds[1]; - p->viewHeight = bounds[3] - bounds[1]; - } - } - if (p->image->width == 0) - p->image->width = p->viewWidth; - if (p->image->height == 0) - p->image->height = p->viewHeight; - - tx = -p->viewMinx; - ty = -p->viewMiny; - sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0; - sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0; - // Unit scaling - us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f); - - // Fix aspect ratio - if (p->alignType == NSVG_ALIGN_MEET) { - // fit whole image into viewbox - sx = sy = nsvg__minf(sx, sy); - tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; - ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; - } else if (p->alignType == NSVG_ALIGN_SLICE) { - // fill whole viewbox with image - sx = sy = nsvg__maxf(sx, sy); - tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; - ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; - } - - // Transform - sx *= us; - sy *= us; - avgs = (sx+sy) / 2.0f; - for (shape = p->image->shapes; shape != NULL; shape = shape->next) { - shape->bounds[0] = (shape->bounds[0] + tx) * sx; - shape->bounds[1] = (shape->bounds[1] + ty) * sy; - shape->bounds[2] = (shape->bounds[2] + tx) * sx; - shape->bounds[3] = (shape->bounds[3] + ty) * sy; - for (path = shape->paths; path != NULL; path = path->next) { - path->bounds[0] = (path->bounds[0] + tx) * sx; - path->bounds[1] = (path->bounds[1] + ty) * sy; - path->bounds[2] = (path->bounds[2] + tx) * sx; - path->bounds[3] = (path->bounds[3] + ty) * sy; - for (i =0; i < path->npts; i++) { - pt = &path->pts[i*2]; - pt[0] = (pt[0] + tx) * sx; - pt[1] = (pt[1] + ty) * sy; - } - } - - if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) { - nsvg__scaleGradient(shape->fill.gradient, tx,ty, sx,sy); - memcpy(t, shape->fill.gradient->xform, sizeof(float)*6); - nsvg__xformInverse(shape->fill.gradient->xform, t); - } - if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) { - nsvg__scaleGradient(shape->stroke.gradient, tx,ty, sx,sy); - memcpy(t, shape->stroke.gradient->xform, sizeof(float)*6); - nsvg__xformInverse(shape->stroke.gradient->xform, t); - } - - shape->strokeWidth *= avgs; - shape->strokeDashOffset *= avgs; - for (i = 0; i < shape->strokeDashCount; i++) - shape->strokeDashArray[i] *= avgs; - } -} - -static void nsvg__createGradients(NSVGparser* p) -{ - NSVGshape* shape; - - for (shape = p->image->shapes; shape != NULL; shape = shape->next) { - if (shape->fill.type == NSVG_PAINT_UNDEF) { - if (shape->fillGradient[0] != '\0') { - float inv[6], localBounds[4]; - nsvg__xformInverse(inv, shape->xform); - nsvg__getLocalBounds(localBounds, shape, inv); - shape->fill.gradient = nsvg__createGradient(p, shape->fillGradient, localBounds, shape->xform, &shape->fill.type); - } - if (shape->fill.type == NSVG_PAINT_UNDEF) { - shape->fill.type = NSVG_PAINT_NONE; - } - } - if (shape->stroke.type == NSVG_PAINT_UNDEF) { - if (shape->strokeGradient[0] != '\0') { - float inv[6], localBounds[4]; - nsvg__xformInverse(inv, shape->xform); - nsvg__getLocalBounds(localBounds, shape, inv); - shape->stroke.gradient = nsvg__createGradient(p, shape->strokeGradient, localBounds, shape->xform, &shape->stroke.type); - } - if (shape->stroke.type == NSVG_PAINT_UNDEF) { - shape->stroke.type = NSVG_PAINT_NONE; - } - } - } -} - -NSVGimage* nsvgParse(char* input, const char* units, float dpi) -{ - NSVGparser* p; - NSVGimage* ret = 0; - - p = nsvg__createParser(); - if (p == NULL) { - return NULL; - } - p->dpi = dpi; - - nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p); - - // Create gradients after all definitions have been parsed - nsvg__createGradients(p); - - // Scale to viewBox - nsvg__scaleToViewbox(p, units); - - ret = p->image; - p->image = NULL; - - nsvg__deleteParser(p); - - return ret; -} - -NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi) -{ - FILE* fp = NULL; - size_t size; - char* data = NULL; - NSVGimage* image = NULL; - - fp = fopen(filename, "rb"); - if (!fp) goto error; - fseek(fp, 0, SEEK_END); - size = ftell(fp); - fseek(fp, 0, SEEK_SET); - data = (char*)malloc(size+1); - if (data == NULL) goto error; - if (fread(data, 1, size, fp) != size) goto error; - data[size] = '\0'; // Must be null terminated. - fclose(fp); - image = nsvgParse(data, units, dpi); - free(data); - - return image; - -error: - if (fp) fclose(fp); - if (data) free(data); - if (image) nsvgDelete(image); - return NULL; -} - -NSVGpath* nsvgDuplicatePath(NSVGpath* p) -{ - NSVGpath* res = NULL; - - if (p == NULL) - return NULL; - - res = (NSVGpath*)malloc(sizeof(NSVGpath)); - if (res == NULL) goto error; - memset(res, 0, sizeof(NSVGpath)); - - res->pts = (float*)malloc(p->npts*2*sizeof(float)); - if (res->pts == NULL) goto error; - memcpy(res->pts, p->pts, p->npts * sizeof(float) * 2); - res->npts = p->npts; - - memcpy(res->bounds, p->bounds, sizeof(p->bounds)); - - res->closed = p->closed; - - return res; - -error: - if (res != NULL) { - free(res->pts); - free(res); - } - return NULL; -} - -void nsvgDelete(NSVGimage* image) -{ - NSVGshape *snext, *shape; - if (image == NULL) return; - shape = image->shapes; - while (shape != NULL) { - snext = shape->next; - nsvg__deletePaths(shape->paths); - nsvg__deletePaint(&shape->fill); - nsvg__deletePaint(&shape->stroke); - free(shape); - shape = snext; - } - free(image); -} - -#endif // NANOSVG_IMPLEMENTATION - -#endif // NANOSVG_H diff --git a/src/nanosvg/nanosvgrast.h b/src/nanosvg/nanosvgrast.h deleted file mode 100644 index a83db27260..0000000000 --- a/src/nanosvg/nanosvgrast.h +++ /dev/null @@ -1,1482 +0,0 @@ -/* - * Copyright (c) 2013-14 Mikko Mononen memon@inside.org - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * The polygon rasterization is heavily based on stb_truetype rasterizer - * by Sean Barrett - http://nothings.org/ - * - */ - -/* Modified by FLTK to support non-square X,Y axes scaling. - * - * Added: nsvgRasterizeXY() -*/ - - -#ifndef NANOSVGRAST_H -#define NANOSVGRAST_H - -#include "nanosvg.h" - -#ifndef NANOSVGRAST_CPLUSPLUS -#ifdef __cplusplus -extern "C" { -#endif -#endif - -typedef struct NSVGrasterizer NSVGrasterizer; - -/* Example Usage: - // Load SVG - NSVGimage* image; - image = nsvgParseFromFile("test.svg", "px", 96); - - // Create rasterizer (can be used to render multiple images). - struct NSVGrasterizer* rast = nsvgCreateRasterizer(); - // Allocate memory for image - unsigned char* img = malloc(w*h*4); - // Rasterize - nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4); - - // For non-square X,Y scaling, use - nsvgRasterizeXY(rast, image, 0,0,1,1, img, w, h, w*4); -*/ - -// Allocated rasterizer context. -NSVGrasterizer* nsvgCreateRasterizer(void); - -// Rasterizes SVG image, returns RGBA image (non-premultiplied alpha) -// r - pointer to rasterizer context -// image - pointer to image to rasterize -// tx,ty - image offset (applied after scaling) -// scale - image scale (assumes square aspect ratio) -// dst - pointer to destination image data, 4 bytes per pixel (RGBA) -// w - width of the image to render -// h - height of the image to render -// stride - number of bytes per scaleline in the destination buffer -void nsvgRasterize(NSVGrasterizer* r, - NSVGimage* image, float tx, float ty, float scale, - unsigned char* dst, int w, int h, int stride); - -// As above, but allow X and Y axes to scale independently for non-square aspects -void nsvgRasterizeXY(NSVGrasterizer* r, - NSVGimage* image, float tx, float ty, - float sx, float sy, - unsigned char* dst, int w, int h, int stride); - -// Deletes rasterizer context. -void nsvgDeleteRasterizer(NSVGrasterizer*); - - -#ifndef NANOSVGRAST_CPLUSPLUS -#ifdef __cplusplus -} -#endif -#endif - -#ifdef NANOSVGRAST_IMPLEMENTATION - -#include -#include -#include - -#define NSVG__SUBSAMPLES 5 -#define NSVG__FIXSHIFT 10 -#define NSVG__FIX (1 << NSVG__FIXSHIFT) -#define NSVG__FIXMASK (NSVG__FIX-1) -#define NSVG__MEMPAGE_SIZE 1024 - -typedef struct NSVGedge { - float x0,y0, x1,y1; - int dir; - struct NSVGedge* next; -} NSVGedge; - -typedef struct NSVGpoint { - float x, y; - float dx, dy; - float len; - float dmx, dmy; - unsigned char flags; -} NSVGpoint; - -typedef struct NSVGactiveEdge { - int x,dx; - float ey; - int dir; - struct NSVGactiveEdge *next; -} NSVGactiveEdge; - -typedef struct NSVGmemPage { - unsigned char mem[NSVG__MEMPAGE_SIZE]; - int size; - struct NSVGmemPage* next; -} NSVGmemPage; - -typedef struct NSVGcachedPaint { - signed char type; - char spread; - float xform[6]; - unsigned int colors[256]; -} NSVGcachedPaint; - -struct NSVGrasterizer -{ - float px, py; - - float tessTol; - float distTol; - - NSVGedge* edges; - int nedges; - int cedges; - - NSVGpoint* points; - int npoints; - int cpoints; - - NSVGpoint* points2; - int npoints2; - int cpoints2; - - NSVGactiveEdge* freelist; - NSVGmemPage* pages; - NSVGmemPage* curpage; - - unsigned char* scanline; - int cscanline; - - unsigned char* bitmap; - int width, height, stride; -}; - -NSVGrasterizer* nsvgCreateRasterizer(void) -{ - NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer)); - if (r == NULL) goto error; - memset(r, 0, sizeof(NSVGrasterizer)); - - r->tessTol = 0.25f; - r->distTol = 0.01f; - - return r; - -error: - nsvgDeleteRasterizer(r); - return NULL; -} - -void nsvgDeleteRasterizer(NSVGrasterizer* r) -{ - NSVGmemPage* p; - - if (r == NULL) return; - - p = r->pages; - while (p != NULL) { - NSVGmemPage* next = p->next; - free(p); - p = next; - } - - if (r->edges) free(r->edges); - if (r->points) free(r->points); - if (r->points2) free(r->points2); - if (r->scanline) free(r->scanline); - - free(r); -} - -static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur) -{ - NSVGmemPage *newp; - - // If using existing chain, return the next page in chain - if (cur != NULL && cur->next != NULL) { - return cur->next; - } - - // Alloc new page - newp = (NSVGmemPage*)malloc(sizeof(NSVGmemPage)); - if (newp == NULL) return NULL; - memset(newp, 0, sizeof(NSVGmemPage)); - - // Add to linked list - if (cur != NULL) - cur->next = newp; - else - r->pages = newp; - - return newp; -} - -static void nsvg__resetPool(NSVGrasterizer* r) -{ - NSVGmemPage* p = r->pages; - while (p != NULL) { - p->size = 0; - p = p->next; - } - r->curpage = r->pages; -} - -static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size) -{ - unsigned char* buf; - if (size > NSVG__MEMPAGE_SIZE) return NULL; - if (r->curpage == NULL || r->curpage->size+size > NSVG__MEMPAGE_SIZE) { - r->curpage = nsvg__nextPage(r, r->curpage); - } - buf = &r->curpage->mem[r->curpage->size]; - r->curpage->size += size; - return buf; -} - -static int nsvg__ptEquals(float x1, float y1, float x2, float y2, float tol) -{ - float dx = x2 - x1; - float dy = y2 - y1; - return dx*dx + dy*dy < tol*tol; -} - -static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags) -{ - NSVGpoint* pt; - - if (r->npoints > 0) { - pt = &r->points[r->npoints-1]; - if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) { - pt->flags = (unsigned char)(pt->flags | flags); - return; - } - } - - if (r->npoints+1 > r->cpoints) { - r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; - r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); - if (r->points == NULL) return; - } - - pt = &r->points[r->npoints]; - pt->x = x; - pt->y = y; - pt->flags = (unsigned char)flags; - r->npoints++; -} - -static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt) -{ - if (r->npoints+1 > r->cpoints) { - r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; - r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); - if (r->points == NULL) return; - } - r->points[r->npoints] = pt; - r->npoints++; -} - -static void nsvg__duplicatePoints(NSVGrasterizer* r) -{ - if (r->npoints > r->cpoints2) { - r->cpoints2 = r->npoints; - r->points2 = (NSVGpoint*)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2); - if (r->points2 == NULL) return; - } - - memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints); - r->npoints2 = r->npoints; -} - -static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1) -{ - NSVGedge* e; - - // Skip horizontal edges - if (y0 == y1) - return; - - if (r->nedges+1 > r->cedges) { - r->cedges = r->cedges > 0 ? r->cedges * 2 : 64; - r->edges = (NSVGedge*)realloc(r->edges, sizeof(NSVGedge) * r->cedges); - if (r->edges == NULL) return; - } - - e = &r->edges[r->nedges]; - r->nedges++; - - if (y0 < y1) { - e->x0 = x0; - e->y0 = y0; - e->x1 = x1; - e->y1 = y1; - e->dir = 1; - } else { - e->x0 = x1; - e->y0 = y1; - e->x1 = x0; - e->y1 = y0; - e->dir = -1; - } -} - -static float nsvg__normalize(float *x, float* y) -{ - float d = sqrtf((*x)*(*x) + (*y)*(*y)); - if (d > 1e-6f) { - float id = 1.0f / d; - *x *= id; - *y *= id; - } - return d; -} - -static float nsvg__absf(float x) { return x < 0 ? -x : x; } - -static void nsvg__flattenCubicBez(NSVGrasterizer* r, - float x1, float y1, float x2, float y2, - float x3, float y3, float x4, float y4, - int level, int type) -{ - float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; - float dx,dy,d2,d3; - - if (level > 10) return; - - x12 = (x1+x2)*0.5f; - y12 = (y1+y2)*0.5f; - x23 = (x2+x3)*0.5f; - y23 = (y2+y3)*0.5f; - x34 = (x3+x4)*0.5f; - y34 = (y3+y4)*0.5f; - x123 = (x12+x23)*0.5f; - y123 = (y12+y23)*0.5f; - - dx = x4 - x1; - dy = y4 - y1; - d2 = nsvg__absf(((x2 - x4) * dy - (y2 - y4) * dx)); - d3 = nsvg__absf(((x3 - x4) * dy - (y3 - y4) * dx)); - - if ((d2 + d3)*(d2 + d3) < r->tessTol * (dx*dx + dy*dy)) { - nsvg__addPathPoint(r, x4, y4, type); - return; - } - - x234 = (x23+x34)*0.5f; - y234 = (y23+y34)*0.5f; - x1234 = (x123+x234)*0.5f; - y1234 = (y123+y234)*0.5f; - - nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0); - nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type); -} - -static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float sx, float sy) -{ - int i, j; - NSVGpath* path; - - for (path = shape->paths; path != NULL; path = path->next) { - r->npoints = 0; - // Flatten path - nsvg__addPathPoint(r, path->pts[0]*sx, path->pts[1]*sy, 0); - for (i = 0; i < path->npts-1; i += 3) { - float* p = &path->pts[i*2]; - nsvg__flattenCubicBez(r, p[0]*sx,p[1]*sy, p[2]*sx,p[3]*sy, p[4]*sx,p[5]*sy, p[6]*sx,p[7]*sy, 0, 0); - } - // Close path - nsvg__addPathPoint(r, path->pts[0]*sx, path->pts[1]*sy, 0); - // Build edges - for (i = 0, j = r->npoints-1; i < r->npoints; j = i++) - nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y); - } -} - -enum NSVGpointFlags -{ - NSVG_PT_CORNER = 0x01, - NSVG_PT_BEVEL = 0x02, - NSVG_PT_LEFT = 0x04 -}; - -static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) -{ - float w = lineWidth * 0.5f; - float dx = p1->x - p0->x; - float dy = p1->y - p0->y; - float len = nsvg__normalize(&dx, &dy); - float px = p0->x + dx*len*0.5f, py = p0->y + dy*len*0.5f; - float dlx = dy, dly = -dx; - float lx = px - dlx*w, ly = py - dly*w; - float rx = px + dlx*w, ry = py + dly*w; - left->x = lx; left->y = ly; - right->x = rx; right->y = ry; -} - -static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) -{ - float w = lineWidth * 0.5f; - float px = p->x, py = p->y; - float dlx = dy, dly = -dx; - float lx = px - dlx*w, ly = py - dly*w; - float rx = px + dlx*w, ry = py + dly*w; - - nsvg__addEdge(r, lx, ly, rx, ry); - - if (connect) { - nsvg__addEdge(r, left->x, left->y, lx, ly); - nsvg__addEdge(r, rx, ry, right->x, right->y); - } - left->x = lx; left->y = ly; - right->x = rx; right->y = ry; -} - -static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) -{ - float w = lineWidth * 0.5f; - float px = p->x - dx*w, py = p->y - dy*w; - float dlx = dy, dly = -dx; - float lx = px - dlx*w, ly = py - dly*w; - float rx = px + dlx*w, ry = py + dly*w; - - nsvg__addEdge(r, lx, ly, rx, ry); - - if (connect) { - nsvg__addEdge(r, left->x, left->y, lx, ly); - nsvg__addEdge(r, rx, ry, right->x, right->y); - } - left->x = lx; left->y = ly; - right->x = rx; right->y = ry; -} - -#ifndef NSVG_PI -#define NSVG_PI (3.14159265358979323846264338327f) -#endif - -static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int ncap, int connect) -{ - int i; - float w = lineWidth * 0.5f; - float px = p->x, py = p->y; - float dlx = dy, dly = -dx; - float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0; - - for (i = 0; i < ncap; i++) { - float a = (float)i/(float)(ncap-1)*NSVG_PI; - float ax = cosf(a) * w, ay = sinf(a) * w; - float x = px - dlx*ax - dx*ay; - float y = py - dly*ax - dy*ay; - - if (i > 0) - nsvg__addEdge(r, prevx, prevy, x, y); - - prevx = x; - prevy = y; - - if (i == 0) { - lx = x; ly = y; - } else if (i == ncap-1) { - rx = x; ry = y; - } - } - - if (connect) { - nsvg__addEdge(r, left->x, left->y, lx, ly); - nsvg__addEdge(r, rx, ry, right->x, right->y); - } - - left->x = lx; left->y = ly; - right->x = rx; right->y = ry; -} - -static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) -{ - float w = lineWidth * 0.5f; - float dlx0 = p0->dy, dly0 = -p0->dx; - float dlx1 = p1->dy, dly1 = -p1->dx; - float lx0 = p1->x - (dlx0 * w), ly0 = p1->y - (dly0 * w); - float rx0 = p1->x + (dlx0 * w), ry0 = p1->y + (dly0 * w); - float lx1 = p1->x - (dlx1 * w), ly1 = p1->y - (dly1 * w); - float rx1 = p1->x + (dlx1 * w), ry1 = p1->y + (dly1 * w); - - nsvg__addEdge(r, lx0, ly0, left->x, left->y); - nsvg__addEdge(r, lx1, ly1, lx0, ly0); - - nsvg__addEdge(r, right->x, right->y, rx0, ry0); - nsvg__addEdge(r, rx0, ry0, rx1, ry1); - - left->x = lx1; left->y = ly1; - right->x = rx1; right->y = ry1; -} - -static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) -{ - float w = lineWidth * 0.5f; - float dlx0 = p0->dy, dly0 = -p0->dx; - float dlx1 = p1->dy, dly1 = -p1->dx; - float lx0, rx0, lx1, rx1; - float ly0, ry0, ly1, ry1; - - if (p1->flags & NSVG_PT_LEFT) { - lx0 = lx1 = p1->x - p1->dmx * w; - ly0 = ly1 = p1->y - p1->dmy * w; - nsvg__addEdge(r, lx1, ly1, left->x, left->y); - - rx0 = p1->x + (dlx0 * w); - ry0 = p1->y + (dly0 * w); - rx1 = p1->x + (dlx1 * w); - ry1 = p1->y + (dly1 * w); - nsvg__addEdge(r, right->x, right->y, rx0, ry0); - nsvg__addEdge(r, rx0, ry0, rx1, ry1); - } else { - lx0 = p1->x - (dlx0 * w); - ly0 = p1->y - (dly0 * w); - lx1 = p1->x - (dlx1 * w); - ly1 = p1->y - (dly1 * w); - nsvg__addEdge(r, lx0, ly0, left->x, left->y); - nsvg__addEdge(r, lx1, ly1, lx0, ly0); - - rx0 = rx1 = p1->x + p1->dmx * w; - ry0 = ry1 = p1->y + p1->dmy * w; - nsvg__addEdge(r, right->x, right->y, rx1, ry1); - } - - left->x = lx1; left->y = ly1; - right->x = rx1; right->y = ry1; -} - -static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap) -{ - int i, n; - float w = lineWidth * 0.5f; - float dlx0 = p0->dy, dly0 = -p0->dx; - float dlx1 = p1->dy, dly1 = -p1->dx; - float a0 = atan2f(dly0, dlx0); - float a1 = atan2f(dly1, dlx1); - float da = a1 - a0; - float lx, ly, rx, ry; - - if (da < NSVG_PI) da += NSVG_PI*2; - if (da > NSVG_PI) da -= NSVG_PI*2; - - n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap); - if (n < 2) n = 2; - if (n > ncap) n = ncap; - - lx = left->x; - ly = left->y; - rx = right->x; - ry = right->y; - - for (i = 0; i < n; i++) { - float u = (float)i/(float)(n-1); - float a = a0 + u*da; - float ax = cosf(a) * w, ay = sinf(a) * w; - float lx1 = p1->x - ax, ly1 = p1->y - ay; - float rx1 = p1->x + ax, ry1 = p1->y + ay; - - nsvg__addEdge(r, lx1, ly1, lx, ly); - nsvg__addEdge(r, rx, ry, rx1, ry1); - - lx = lx1; ly = ly1; - rx = rx1; ry = ry1; - } - - left->x = lx; left->y = ly; - right->x = rx; right->y = ry; -} - -static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p1, float lineWidth) -{ - float w = lineWidth * 0.5f; - float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w); - float rx = p1->x + (p1->dmx * w), ry = p1->y + (p1->dmy * w); - - nsvg__addEdge(r, lx, ly, left->x, left->y); - nsvg__addEdge(r, right->x, right->y, rx, ry); - - left->x = lx; left->y = ly; - right->x = rx; right->y = ry; -} - -static int nsvg__curveDivs(float r, float arc, float tol) -{ - float da = acosf(r / (r + tol)) * 2.0f; - int divs = (int)ceilf(arc / da); - if (divs < 2) divs = 2; - return divs; -} - -static void nsvg__expandStroke(NSVGrasterizer* r, NSVGpoint* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth) -{ - int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol); // Calculate divisions per half circle. - NSVGpoint left = {0,0,0,0,0,0,0,0}, right = {0,0,0,0,0,0,0,0}, firstLeft = {0,0,0,0,0,0,0,0}, firstRight = {0,0,0,0,0,0,0,0}; - NSVGpoint* p0, *p1; - int j, s, e; - - // Build stroke edges - if (closed) { - // Looping - p0 = &points[npoints-1]; - p1 = &points[0]; - s = 0; - e = npoints; - } else { - // Add cap - p0 = &points[0]; - p1 = &points[1]; - s = 1; - e = npoints-1; - } - - if (closed) { - nsvg__initClosed(&left, &right, p0, p1, lineWidth); - firstLeft = left; - firstRight = right; - } else { - // Add cap - float dx = p1->x - p0->x; - float dy = p1->y - p0->y; - nsvg__normalize(&dx, &dy); - if (lineCap == NSVG_CAP_BUTT) - nsvg__buttCap(r, &left, &right, p0, dx, dy, lineWidth, 0); - else if (lineCap == NSVG_CAP_SQUARE) - nsvg__squareCap(r, &left, &right, p0, dx, dy, lineWidth, 0); - else if (lineCap == NSVG_CAP_ROUND) - nsvg__roundCap(r, &left, &right, p0, dx, dy, lineWidth, ncap, 0); - } - - for (j = s; j < e; ++j) { - if (p1->flags & NSVG_PT_CORNER) { - if (lineJoin == NSVG_JOIN_ROUND) - nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap); - else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL)) - nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth); - else - nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth); - } else { - nsvg__straightJoin(r, &left, &right, p1, lineWidth); - } - p0 = p1++; - } - - if (closed) { - // Loop it - nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y); - nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y); - } else { - // Add cap - float dx = p1->x - p0->x; - float dy = p1->y - p0->y; - nsvg__normalize(&dx, &dy); - if (lineCap == NSVG_CAP_BUTT) - nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); - else if (lineCap == NSVG_CAP_SQUARE) - nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); - else if (lineCap == NSVG_CAP_ROUND) - nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1); - } -} - -static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoin) -{ - int i, j; - NSVGpoint* p0, *p1; - - p0 = &r->points[r->npoints-1]; - p1 = &r->points[0]; - for (i = 0; i < r->npoints; i++) { - // Calculate segment direction and length - p0->dx = p1->x - p0->x; - p0->dy = p1->y - p0->y; - p0->len = nsvg__normalize(&p0->dx, &p0->dy); - // Advance - p0 = p1++; - } - - // calculate joins - p0 = &r->points[r->npoints-1]; - p1 = &r->points[0]; - for (j = 0; j < r->npoints; j++) { - float dlx0, dly0, dlx1, dly1, dmr2, cross; - dlx0 = p0->dy; - dly0 = -p0->dx; - dlx1 = p1->dy; - dly1 = -p1->dx; - // Calculate extrusions - p1->dmx = (dlx0 + dlx1) * 0.5f; - p1->dmy = (dly0 + dly1) * 0.5f; - dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy; - if (dmr2 > 0.000001f) { - float s2 = 1.0f / dmr2; - if (s2 > 600.0f) { - s2 = 600.0f; - } - p1->dmx *= s2; - p1->dmy *= s2; - } - - // Clear flags, but keep the corner. - p1->flags = (p1->flags & NSVG_PT_CORNER) ? NSVG_PT_CORNER : 0; - - // Keep track of left turns. - cross = p1->dx * p0->dy - p0->dx * p1->dy; - if (cross > 0.0f) - p1->flags |= NSVG_PT_LEFT; - - // Check to see if the corner needs to be beveled. - if (p1->flags & NSVG_PT_CORNER) { - if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) { - p1->flags |= NSVG_PT_BEVEL; - } - } - - p0 = p1++; - } -} - -static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float sx, float sy) -{ - int i, j, closed; - NSVGpath* path; - NSVGpoint* p0, *p1; - float miterLimit = shape->miterLimit; - int lineJoin = shape->strokeLineJoin; - int lineCap = shape->strokeLineCap; - const float sw = (sx + sy) / 2; // average scaling factor - const float lineWidth = shape->strokeWidth * sw; // FIXME (?) - - for (path = shape->paths; path != NULL; path = path->next) { - // Flatten path - r->npoints = 0; - nsvg__addPathPoint(r, path->pts[0]*sx, path->pts[1]*sy, NSVG_PT_CORNER); - for (i = 0; i < path->npts-1; i += 3) { - float* p = &path->pts[i*2]; - nsvg__flattenCubicBez(r, p[0]*sx,p[1]*sy, p[2]*sx,p[3]*sy, p[4]*sx,p[5]*sy, p[6]*sx,p[7]*sy, 0, NSVG_PT_CORNER); - } - if (r->npoints < 2) - continue; - - closed = path->closed; - - // If the first and last points are the same, remove the last, mark as closed path. - p0 = &r->points[r->npoints-1]; - p1 = &r->points[0]; - if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) { - r->npoints--; - p0 = &r->points[r->npoints-1]; - closed = 1; - } - - if (shape->strokeDashCount > 0) { - int idash = 0, dashState = 1; - float totalDist = 0, dashLen, allDashLen, dashOffset; - NSVGpoint cur; - - if (closed) - nsvg__appendPathPoint(r, r->points[0]); - - // Duplicate points -> points2. - nsvg__duplicatePoints(r); - - r->npoints = 0; - cur = r->points2[0]; - nsvg__appendPathPoint(r, cur); - - // Figure out dash offset. - allDashLen = 0; - for (j = 0; j < shape->strokeDashCount; j++) - allDashLen += shape->strokeDashArray[j]; - if (shape->strokeDashCount & 1) - allDashLen *= 2.0f; - // Find location inside pattern - dashOffset = fmodf(shape->strokeDashOffset, allDashLen); - if (dashOffset < 0.0f) - dashOffset += allDashLen; - - while (dashOffset > shape->strokeDashArray[idash]) { - dashOffset -= shape->strokeDashArray[idash]; - idash = (idash + 1) % shape->strokeDashCount; - } - dashLen = (shape->strokeDashArray[idash] - dashOffset) * sw; - - for (j = 1; j < r->npoints2; ) { - float dx = r->points2[j].x - cur.x; - float dy = r->points2[j].y - cur.y; - float dist = sqrtf(dx*dx + dy*dy); - - if ((totalDist + dist) > dashLen) { - // Calculate intermediate point - float d = (dashLen - totalDist) / dist; - float x = cur.x + dx * d; - float y = cur.y + dy * d; - nsvg__addPathPoint(r, x, y, NSVG_PT_CORNER); - - // Stroke - if (r->npoints > 1 && dashState) { - nsvg__prepareStroke(r, miterLimit, lineJoin); - nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); - } - // Advance dash pattern - dashState = !dashState; - idash = (idash+1) % shape->strokeDashCount; - dashLen = shape->strokeDashArray[idash] * sw; - // Restart - cur.x = x; - cur.y = y; - cur.flags = NSVG_PT_CORNER; - totalDist = 0.0f; - r->npoints = 0; - nsvg__appendPathPoint(r, cur); - } else { - totalDist += dist; - cur = r->points2[j]; - nsvg__appendPathPoint(r, cur); - j++; - } - } - // Stroke any leftover path - if (r->npoints > 1 && dashState) - nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); - } else { - nsvg__prepareStroke(r, miterLimit, lineJoin); - nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap, lineWidth); - } - } -} - -static int nsvg__cmpEdge(const void *p, const void *q) -{ - const NSVGedge* a = (const NSVGedge*)p; - const NSVGedge* b = (const NSVGedge*)q; - - if (a->y0 < b->y0) return -1; - if (a->y0 > b->y0) return 1; - return 0; -} - - -static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float startPoint) -{ - NSVGactiveEdge* z; - - if (r->freelist != NULL) { - // Restore from freelist. - z = r->freelist; - r->freelist = z->next; - } else { - // Alloc new edge. - z = (NSVGactiveEdge*)nsvg__alloc(r, sizeof(NSVGactiveEdge)); - if (z == NULL) return NULL; - } - - float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); -// STBTT_assert(e->y0 <= start_point); - // round dx down to avoid going too far - if (dxdy < 0) - z->dx = (int)(-floorf(NSVG__FIX * -dxdy)); - else - z->dx = (int)floorf(NSVG__FIX * dxdy); - z->x = (int)floorf(NSVG__FIX * (e->x0 + dxdy * (startPoint - e->y0))); -// z->x -= off_x * FIX; - z->ey = e->y1; - z->next = 0; - z->dir = e->dir; - - return z; -} - -static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z) -{ - z->next = r->freelist; - r->freelist = z; -} - -static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax) -{ - int i = x0 >> NSVG__FIXSHIFT; - int j = x1 >> NSVG__FIXSHIFT; - if (i < *xmin) *xmin = i; - if (j > *xmax) *xmax = j; - if (i < len && j >= 0) { - if (i == j) { - // x0,x1 are the same pixel, so compute combined coverage - scanline[i] = (unsigned char)(scanline[i] + ((x1 - x0) * maxWeight >> NSVG__FIXSHIFT)); - } else { - if (i >= 0) // add antialiasing for x0 - scanline[i] = (unsigned char)(scanline[i] + (((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT)); - else - i = -1; // clip - - if (j < len) // add antialiasing for x1 - scanline[j] = (unsigned char)(scanline[j] + (((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT)); - else - j = len; // clip - - for (++i; i < j; ++i) // fill pixels between x0 and x1 - scanline[i] = (unsigned char)(scanline[i] + maxWeight); - } - } -} - -// note: this routine clips fills that extend off the edges... ideally this -// wouldn't happen, but it could happen if the truetype glyph bounding boxes -// are wrong, or if the user supplies a too-small bitmap -static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule) -{ - // non-zero winding fill - int x0 = 0, w = 0; - - if (fillRule == NSVG_FILLRULE_NONZERO) { - // Non-zero - while (e != NULL) { - if (w == 0) { - // if we're currently at zero, we need to record the edge start point - x0 = e->x; w += e->dir; - } else { - int x1 = e->x; w += e->dir; - // if we went to zero, we need to draw - if (w == 0) - nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); - } - e = e->next; - } - } else if (fillRule == NSVG_FILLRULE_EVENODD) { - // Even-odd - while (e != NULL) { - if (w == 0) { - // if we're currently at zero, we need to record the edge start point - x0 = e->x; w = 1; - } else { - int x1 = e->x; w = 0; - nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); - } - e = e->next; - } - } -} - -static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); } - -static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) -{ - return ((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16) | ((unsigned int)a << 24); -} - -static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u) -{ - int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); - int r = (((c0) & 0xff)*(256-iu) + (((c1) & 0xff)*iu)) >> 8; - int g = (((c0>>8) & 0xff)*(256-iu) + (((c1>>8) & 0xff)*iu)) >> 8; - int b = (((c0>>16) & 0xff)*(256-iu) + (((c1>>16) & 0xff)*iu)) >> 8; - int a = (((c0>>24) & 0xff)*(256-iu) + (((c1>>24) & 0xff)*iu)) >> 8; - return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); -} - -static unsigned int nsvg__applyOpacity(unsigned int c, float u) -{ - int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); - int r = (c) & 0xff; - int g = (c>>8) & 0xff; - int b = (c>>16) & 0xff; - int a = (((c>>24) & 0xff)*iu) >> 8; - return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); -} - -static inline int nsvg__div255(int x) -{ - return ((x+1) * 257) >> 16; -} - -static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y, - float tx, float ty, float sx, float sy, NSVGcachedPaint* cache) -{ - - if (cache->type == NSVG_PAINT_COLOR) { - int i, cr, cg, cb, ca; - cr = cache->colors[0] & 0xff; - cg = (cache->colors[0] >> 8) & 0xff; - cb = (cache->colors[0] >> 16) & 0xff; - ca = (cache->colors[0] >> 24) & 0xff; - - for (i = 0; i < count; i++) { - int r,g,b; - int a = nsvg__div255((int)cover[0] * ca); - int ia = 255 - a; - // Premultiply - r = nsvg__div255(cr * a); - g = nsvg__div255(cg * a); - b = nsvg__div255(cb * a); - - // Blend over - r += nsvg__div255(ia * (int)dst[0]); - g += nsvg__div255(ia * (int)dst[1]); - b += nsvg__div255(ia * (int)dst[2]); - a += nsvg__div255(ia * (int)dst[3]); - - dst[0] = (unsigned char)r; - dst[1] = (unsigned char)g; - dst[2] = (unsigned char)b; - dst[3] = (unsigned char)a; - - cover++; - dst += 4; - } - } else if (cache->type == NSVG_PAINT_LINEAR_GRADIENT) { - // TODO: spread modes. - // TODO: plenty of opportunities to optimize. - float fx, fy, dx, gy; - float* t = cache->xform; - int i, cr, cg, cb, ca; - unsigned int c; - - fx = ((float)x - tx) / sx; - fy = ((float)y - ty) / sy; - dx = 1.0f / sx; - - for (i = 0; i < count; i++) { - int r,g,b,a,ia; - gy = fx*t[1] + fy*t[3] + t[5]; - c = cache->colors[(int)nsvg__clampf(gy*255.0f, 0, 255.0f)]; - cr = (c) & 0xff; - cg = (c >> 8) & 0xff; - cb = (c >> 16) & 0xff; - ca = (c >> 24) & 0xff; - - a = nsvg__div255((int)cover[0] * ca); - ia = 255 - a; - - // Premultiply - r = nsvg__div255(cr * a); - g = nsvg__div255(cg * a); - b = nsvg__div255(cb * a); - - // Blend over - r += nsvg__div255(ia * (int)dst[0]); - g += nsvg__div255(ia * (int)dst[1]); - b += nsvg__div255(ia * (int)dst[2]); - a += nsvg__div255(ia * (int)dst[3]); - - dst[0] = (unsigned char)r; - dst[1] = (unsigned char)g; - dst[2] = (unsigned char)b; - dst[3] = (unsigned char)a; - - cover++; - dst += 4; - fx += dx; - } - } else if (cache->type == NSVG_PAINT_RADIAL_GRADIENT) { - // TODO: spread modes. - // TODO: plenty of opportunities to optimize. - // TODO: focus (fx,fy) - float fx, fy, dx, gx, gy, gd; - float* t = cache->xform; - int i, cr, cg, cb, ca; - unsigned int c; - - fx = ((float)x - tx) / sx; - fy = ((float)y - ty) / sy; - dx = 1.0f / sx; - - for (i = 0; i < count; i++) { - int r,g,b,a,ia; - gx = fx*t[0] + fy*t[2] + t[4]; - gy = fx*t[1] + fy*t[3] + t[5]; - gd = sqrtf(gx*gx + gy*gy); - c = cache->colors[(int)nsvg__clampf(gd*255.0f, 0, 255.0f)]; - cr = (c) & 0xff; - cg = (c >> 8) & 0xff; - cb = (c >> 16) & 0xff; - ca = (c >> 24) & 0xff; - - a = nsvg__div255((int)cover[0] * ca); - ia = 255 - a; - - // Premultiply - r = nsvg__div255(cr * a); - g = nsvg__div255(cg * a); - b = nsvg__div255(cb * a); - - // Blend over - r += nsvg__div255(ia * (int)dst[0]); - g += nsvg__div255(ia * (int)dst[1]); - b += nsvg__div255(ia * (int)dst[2]); - a += nsvg__div255(ia * (int)dst[3]); - - dst[0] = (unsigned char)r; - dst[1] = (unsigned char)g; - dst[2] = (unsigned char)b; - dst[3] = (unsigned char)a; - - cover++; - dst += 4; - fx += dx; - } - } -} - -static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float sx, float sy, NSVGcachedPaint* cache, char fillRule) -{ - NSVGactiveEdge *active = NULL; - int y, s; - int e = 0; - int maxWeight = (255 / NSVG__SUBSAMPLES); // weight per vertical scanline - int xmin, xmax; - - for (y = 0; y < r->height; y++) { - memset(r->scanline, 0, r->width); - xmin = r->width; - xmax = 0; - for (s = 0; s < NSVG__SUBSAMPLES; ++s) { - // find center of pixel for this scanline - float scany = (float)(y*NSVG__SUBSAMPLES + s) + 0.5f; - NSVGactiveEdge **step = &active; - - // update all active edges; - // remove all active edges that terminate before the center of this scanline - while (*step) { - NSVGactiveEdge *z = *step; - if (z->ey <= scany) { - *step = z->next; // delete from list -// NSVG__assert(z->valid); - nsvg__freeActive(r, z); - } else { - z->x += z->dx; // advance to position for current scanline - step = &((*step)->next); // advance through list - } - } - - // resort the list if needed - for (;;) { - int changed = 0; - step = &active; - while (*step && (*step)->next) { - if ((*step)->x > (*step)->next->x) { - NSVGactiveEdge* t = *step; - NSVGactiveEdge* q = t->next; - t->next = q->next; - q->next = t; - *step = q; - changed = 1; - } - step = &(*step)->next; - } - if (!changed) break; - } - - // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline - while (e < r->nedges && r->edges[e].y0 <= scany) { - if (r->edges[e].y1 > scany) { - NSVGactiveEdge* z = nsvg__addActive(r, &r->edges[e], scany); - if (z == NULL) break; - // find insertion point - if (active == NULL) { - active = z; - } else if (z->x < active->x) { - // insert at front - z->next = active; - active = z; - } else { - // find thing to insert AFTER - NSVGactiveEdge* p = active; - while (p->next && p->next->x < z->x) - p = p->next; - // at this point, p->next->x is NOT < z->x - z->next = p->next; - p->next = z; - } - } - e++; - } - - // now process all active edges in non-zero fashion - if (active != NULL) - nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule); - } - // Blit - if (xmin < 0) xmin = 0; - if (xmax > r->width-1) xmax = r->width-1; - if (xmin <= xmax) { - nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, sx, sy, cache); - } - } - -} - -static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int stride) -{ - int x,y; - - // Unpremultiply - for (y = 0; y < h; y++) { - unsigned char *row = &image[y*stride]; - for (x = 0; x < w; x++) { - int r = row[0], g = row[1], b = row[2], a = row[3]; - if (a != 0) { - row[0] = (unsigned char)(r*255/a); - row[1] = (unsigned char)(g*255/a); - row[2] = (unsigned char)(b*255/a); - } - row += 4; - } - } - - // Defringe - for (y = 0; y < h; y++) { - unsigned char *row = &image[y*stride]; - for (x = 0; x < w; x++) { - int r = 0, g = 0, b = 0, a = row[3], n = 0; - if (a == 0) { - if (x-1 > 0 && row[-1] != 0) { - r += row[-4]; - g += row[-3]; - b += row[-2]; - n++; - } - if (x+1 < w && row[7] != 0) { - r += row[4]; - g += row[5]; - b += row[6]; - n++; - } - if (y-1 > 0 && row[-stride+3] != 0) { - r += row[-stride]; - g += row[-stride+1]; - b += row[-stride+2]; - n++; - } - if (y+1 < h && row[stride+3] != 0) { - r += row[stride]; - g += row[stride+1]; - b += row[stride+2]; - n++; - } - if (n > 0) { - row[0] = (unsigned char)(r/n); - row[1] = (unsigned char)(g/n); - row[2] = (unsigned char)(b/n); - } - } - row += 4; - } - } -} - - -static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opacity) -{ - int i, j; - NSVGgradient* grad; - - cache->type = paint->type; - - if (paint->type == NSVG_PAINT_COLOR) { - cache->colors[0] = nsvg__applyOpacity(paint->color, opacity); - return; - } - - grad = paint->gradient; - - cache->spread = grad->spread; - memcpy(cache->xform, grad->xform, sizeof(float)*6); - - if (grad->nstops == 0) { - for (i = 0; i < 256; i++) - cache->colors[i] = 0; - } if (grad->nstops == 1) { - for (i = 0; i < 256; i++) - cache->colors[i] = nsvg__applyOpacity(grad->stops[i].color, opacity); - } else { - unsigned int ca, cb = 0; - float ua, ub, du, u; - int ia, ib, count; - - ca = nsvg__applyOpacity(grad->stops[0].color, opacity); - ua = nsvg__clampf(grad->stops[0].offset, 0, 1); - ub = nsvg__clampf(grad->stops[grad->nstops-1].offset, ua, 1); - ia = (int)(ua * 255.0f); - ib = (int)(ub * 255.0f); - for (i = 0; i < ia; i++) { - cache->colors[i] = ca; - } - - for (i = 0; i < grad->nstops-1; i++) { - ca = nsvg__applyOpacity(grad->stops[i].color, opacity); - cb = nsvg__applyOpacity(grad->stops[i+1].color, opacity); - ua = nsvg__clampf(grad->stops[i].offset, 0, 1); - ub = nsvg__clampf(grad->stops[i+1].offset, 0, 1); - ia = (int)(ua * 255.0f); - ib = (int)(ub * 255.0f); - count = ib - ia; - if (count <= 0) continue; - u = 0; - du = 1.0f / (float)count; - for (j = 0; j < count; j++) { - cache->colors[ia+j] = nsvg__lerpRGBA(ca,cb,u); - u += du; - } - } - - for (i = ib; i < 256; i++) - cache->colors[i] = cb; - } - -} - -/* -static void dumpEdges(NSVGrasterizer* r, const char* name) -{ - float xmin = 0, xmax = 0, ymin = 0, ymax = 0; - NSVGedge *e = NULL; - int i; - if (r->nedges == 0) return; - FILE* fp = fopen(name, "w"); - if (fp == NULL) return; - - xmin = xmax = r->edges[0].x0; - ymin = ymax = r->edges[0].y0; - for (i = 0; i < r->nedges; i++) { - e = &r->edges[i]; - xmin = nsvg__minf(xmin, e->x0); - xmin = nsvg__minf(xmin, e->x1); - xmax = nsvg__maxf(xmax, e->x0); - xmax = nsvg__maxf(xmax, e->x1); - ymin = nsvg__minf(ymin, e->y0); - ymin = nsvg__minf(ymin, e->y1); - ymax = nsvg__maxf(ymax, e->y0); - ymax = nsvg__maxf(ymax, e->y1); - } - - fprintf(fp, "", xmin, ymin, (xmax - xmin), (ymax - ymin)); - - for (i = 0; i < r->nedges; i++) { - e = &r->edges[i]; - fprintf(fp ,"", e->x0,e->y0, e->x1,e->y1); - } - - for (i = 0; i < r->npoints; i++) { - if (i+1 < r->npoints) - fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y); - fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0"); - } - - fprintf(fp, ""); - fclose(fp); -} -*/ - -void nsvgRasterizeXY(NSVGrasterizer* r, - NSVGimage* image, float tx, float ty, - float sx, float sy, - unsigned char* dst, int w, int h, int stride) -{ - NSVGshape *shape = NULL; - NSVGedge *e = NULL; - NSVGcachedPaint cache; - int i; - - r->bitmap = dst; - r->width = w; - r->height = h; - r->stride = stride; - - if (w > r->cscanline) { - r->cscanline = w; - r->scanline = (unsigned char*)realloc(r->scanline, w); - if (r->scanline == NULL) return; - } - - for (i = 0; i < h; i++) - memset(&dst[i*stride], 0, w*4); - - for (shape = image->shapes; shape != NULL; shape = shape->next) { - if (!(shape->flags & NSVG_FLAGS_VISIBLE)) - continue; - - if (shape->fill.type != NSVG_PAINT_NONE) { - nsvg__resetPool(r); - r->freelist = NULL; - r->nedges = 0; - - nsvg__flattenShape(r, shape, sx, sy); - - // Scale and translate edges - for (i = 0; i < r->nedges; i++) { - e = &r->edges[i]; - e->x0 = tx + e->x0; - e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; - e->x1 = tx + e->x1; - e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; - } - - // Rasterize edges - if (r->nedges != 0) - qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); - - // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule - nsvg__initPaint(&cache, &shape->fill, shape->opacity); - - nsvg__rasterizeSortedEdges(r, tx,ty, sx, sy, &cache, shape->fillRule); - } - if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * sx) > 0.01f) { - nsvg__resetPool(r); - r->freelist = NULL; - r->nedges = 0; - - nsvg__flattenShapeStroke(r, shape, sx, sy); - -// dumpEdges(r, "edge.svg"); - - // Scale and translate edges - for (i = 0; i < r->nedges; i++) { - e = &r->edges[i]; - e->x0 = tx + e->x0; - e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; - e->x1 = tx + e->x1; - e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; - } - - // Rasterize edges - if (r->nedges != 0) - qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); - - // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule - nsvg__initPaint(&cache, &shape->stroke, shape->opacity); - - nsvg__rasterizeSortedEdges(r, tx,ty,sx, sy, &cache, NSVG_FILLRULE_NONZERO); - } - } - - nsvg__unpremultiplyAlpha(dst, w, h, stride); - - r->bitmap = NULL; - r->width = 0; - r->height = 0; - r->stride = 0; -} - -void nsvgRasterize(NSVGrasterizer* r, - NSVGimage* image, float tx, float ty, float scale, - unsigned char* dst, int w, int h, int stride) -{ - nsvgRasterizeXY(r,image, tx, ty, scale, scale, dst, w, h, stride); -} - -#endif // NANOSVGRAST_IMPLEMENTATION - -#endif // NANOSVGRAST_H diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 71a4334ab9..8b40dc1e47 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -496,6 +496,8 @@ set(SLIC3R_GUI_SOURCES Utils/CalibUtils.hpp ) +find_package(NanoSVG REQUIRED) + if (WIN32) list(APPEND SLIC3R_GUI_SOURCES GUI/dark_mode/dark_mode.hpp @@ -543,7 +545,7 @@ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SLIC3R_GUI_SOURCES}) encoding_check(libslic3r_gui) -target_link_libraries(libslic3r_gui libslic3r cereal::cereal imgui minilzo GLEW::GLEW OpenGL::GL hidapi ${wxWidgets_LIBRARIES} glfw libcurl OpenSSL::SSL OpenSSL::Crypto) +target_link_libraries(libslic3r_gui libslic3r cereal::cereal imgui minilzo GLEW::GLEW OpenGL::GL hidapi ${wxWidgets_LIBRARIES} glfw libcurl OpenSSL::SSL OpenSSL::Crypto NanoSVG::nanosvg NanoSVG::nanosvgrast) #target_link_libraries(libslic3r_gui libslic3r cereal imgui minilzo GLEW::GLEW OpenGL::GL hidapi libcurl OpenSSL::SSL OpenSSL::Crypto ${wxWidgets_LIBRARIES} glfw) if (MSVC) diff --git a/src/slic3r/GUI/AMSMaterialsSetting.cpp b/src/slic3r/GUI/AMSMaterialsSetting.cpp index 6e89a04d46..9fa8383d18 100644 --- a/src/slic3r/GUI/AMSMaterialsSetting.cpp +++ b/src/slic3r/GUI/AMSMaterialsSetting.cpp @@ -1084,7 +1084,7 @@ void AMSMaterialsSetting::on_dpi_changed(const wxRect &suggested_rect) m_input_nozzle_max->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(20))); m_input_nozzle_min->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(20))); //m_clr_picker->msw_rescale(); - degree->msw_rescale(); + degree->sys_color_changed(); bitmap_max_degree->SetBitmap(degree->bmp()); bitmap_min_degree->SetBitmap(degree->bmp()); m_button_reset->SetMinSize(AMS_MATERIALS_SETTING_BUTTON_SIZE); diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp index e8f21416af..de4aca579c 100644 --- a/src/slic3r/GUI/AboutDialog.cpp +++ b/src/slic3r/GUI/AboutDialog.cpp @@ -19,7 +19,7 @@ AboutDialogLogo::AboutDialogLogo(wxWindow* parent) { this->SetBackgroundColour(*wxWHITE); this->logo = ScalableBitmap(this, Slic3r::var("OrcaSlicer_192px.png"), wxBITMAP_TYPE_PNG); - this->SetMinSize(this->logo.GetBmpSize()); + this->SetMinSize(this->logo.GetSize()); this->Bind(wxEVT_PAINT, &AboutDialogLogo::onRepaint, this); } @@ -30,9 +30,9 @@ void AboutDialogLogo::onRepaint(wxEvent &event) dc.SetBackgroundMode(wxTRANSPARENT); wxSize size = this->GetSize(); - int logo_w = this->logo.GetBmpWidth(); - int logo_h = this->logo.GetBmpHeight(); - dc.DrawBitmap(this->logo.bmp(), (size.GetWidth() - logo_w)/2, (size.GetHeight() - logo_h)/2, true); + int logo_w = this->logo.GetWidth(); + int logo_h = this->logo.GetHeight(); + dc.DrawBitmap(this->logo.get_bitmap(), (size.GetWidth() - logo_w)/2, (size.GetHeight() - logo_h)/2, true); event.Skip(); } @@ -380,7 +380,7 @@ AboutDialog::AboutDialog() void AboutDialog::on_dpi_changed(const wxRect &suggested_rect) { - m_logo_bitmap.msw_rescale(); + m_logo_bitmap.sys_color_changed(); m_logo->SetBitmap(m_logo_bitmap.bmp()); const wxFont& font = GetFont(); diff --git a/src/slic3r/GUI/AmsMappingPopup.cpp b/src/slic3r/GUI/AmsMappingPopup.cpp index 1c4c186816..900316609c 100644 --- a/src/slic3r/GUI/AmsMappingPopup.cpp +++ b/src/slic3r/GUI/AmsMappingPopup.cpp @@ -187,7 +187,7 @@ void MaterialItem::doRender(wxDC &dc) auto acolor = m_ams_coloul; if (mcolor.Alpha() == 0 || acolor.Alpha() == 0) { - dc.DrawBitmap(m_transparent_mitem.bmp(), FromDIP(1), FromDIP(1)); + dc.DrawBitmap(m_transparent_mitem.get_bitmap(), FromDIP(1), FromDIP(1)); } if (!IsEnabled()) { @@ -247,10 +247,10 @@ void MaterialItem::doRender(wxDC &dc) //arrow if ( (acolor.Red() > 160 && acolor.Green() > 160 && acolor.Blue() > 160) && (acolor.Red() < 180 && acolor.Green() < 180 && acolor.Blue() < 180)) { - dc.DrawBitmap(m_arraw_bitmap_white.bmp(), GetSize().x - m_arraw_bitmap_white.GetBmpSize().x - FromDIP(7), GetSize().y - m_arraw_bitmap_white.GetBmpSize().y); + dc.DrawBitmap(m_arraw_bitmap_white.get_bitmap(), GetSize().x - m_arraw_bitmap_white.GetSize().x - FromDIP(7), GetSize().y - m_arraw_bitmap_white.GetSize().y); } else { - dc.DrawBitmap(m_arraw_bitmap_gray.bmp(), GetSize().x - m_arraw_bitmap_gray.GetBmpSize().x - FromDIP(7), GetSize().y - m_arraw_bitmap_gray.GetBmpSize().y); + dc.DrawBitmap(m_arraw_bitmap_gray.get_bitmap(), GetSize().x - m_arraw_bitmap_gray.GetSize().x - FromDIP(7), GetSize().y - m_arraw_bitmap_gray.GetSize().y); } @@ -677,7 +677,7 @@ void MappingItem::doRender(wxDC &dc) dc.SetBrush(wxBrush(m_coloul)); if (m_coloul.Alpha() == 0) { - dc.DrawBitmap( m_transparent_mapping_item.bmp(), 0, (GetSize().y - MAPPING_ITEM_REAL_SIZE.y) / 2); + dc.DrawBitmap( m_transparent_mapping_item.get_bitmap(), 0, (GetSize().y - MAPPING_ITEM_REAL_SIZE.y) / 2); } else { dc.DrawRectangle(0, (GetSize().y - MAPPING_ITEM_REAL_SIZE.y) / 2, MAPPING_ITEM_REAL_SIZE.x, MAPPING_ITEM_REAL_SIZE.y); @@ -1340,7 +1340,7 @@ void AmsReplaceMaterialDialog::update_machine_obj(MachineObject* obj) } else { label_txt->SetLabelText(_L("If there are two identical filaments in AMS, AMS filament backup will be enabled. \n(Currently supporting automatic supply of consumables with the same brand, material type, and color)")); - } + } label_txt->SetMinSize(wxSize(FromDIP(380), -1)); label_txt->SetMaxSize(wxSize(FromDIP(380), -1)); @@ -1494,7 +1494,7 @@ void AmsRMGroup::doRender(wxDC& dc) float startAngle = 0.0; float endAngle = 0.0; - dc.DrawBitmap(bitmap_bg.bmp(), wxPoint((size.x - bitmap_bg.GetBmpSize().x) / 2, (size.y - bitmap_bg.GetBmpSize().y) / 2)); + dc.DrawBitmap(bitmap_bg.get_bitmap(), wxPoint((size.x - bitmap_bg.GetSize().x) / 2, (size.y - bitmap_bg.GetSize().y) / 2)); for (auto iter = m_group_info.rbegin(); iter != m_group_info.rend(); ++iter) { std::string tray_name = iter->first; @@ -1575,7 +1575,7 @@ void AmsRMGroup::doRender(wxDC& dc) dc.DrawEllipticArc(x - center_mask_radius, y - center_mask_radius, center_mask_radius * 2, center_mask_radius * 2, 0, 360); //draw center icon - dc.DrawBitmap(bitmap_backup_tips_0.bmp(), wxPoint((size.x - bitmap_backup_tips_0.GetBmpSize().x) / 2, (size.y - bitmap_backup_tips_0.GetBmpSize().y) / 2)); + dc.DrawBitmap(bitmap_backup_tips_0.get_bitmap(), wxPoint((size.x - bitmap_backup_tips_0.GetSize().x) / 2, (size.y - bitmap_backup_tips_0.GetSize().y) / 2)); //dc.DrawBitmap(bitmap_backup_tips_1.bmp(), wxPoint((size.x - bitmap_backup_tips_1.GetBmpSize().x) / 2, (size.y - bitmap_backup_tips_1.GetBmpSize().y) / 2)); //draw material diff --git a/src/slic3r/GUI/Auxiliary.cpp b/src/slic3r/GUI/Auxiliary.cpp index c3554bfe5f..2049fa88de 100644 --- a/src/slic3r/GUI/Auxiliary.cpp +++ b/src/slic3r/GUI/Auxiliary.cpp @@ -226,7 +226,7 @@ void AuFile::PaintBackground(wxDC &dc) dc.SetPen(AUFILE_GREY200); dc.SetBrush(AUFILE_GREY200); dc.DrawRoundedRectangle(0, 0, size.x, size.y, AUFILE_ROUNDING); - dc.DrawBitmap(m_file_bitmap.bmp(), (size.x - m_file_bitmap.GetBmpWidth()) / 2, (size.y - m_file_bitmap.GetBmpHeight()) / 2); + dc.DrawBitmap(m_file_bitmap.get_bitmap(), (size.x - m_file_bitmap.GetWidth()) / 2, (size.y - m_file_bitmap.GetHeight()) / 2); } } @@ -257,7 +257,7 @@ void AuFile::PaintForeground(wxDC &dc) } if (m_type == MODEL_PICTURE) { - dc.DrawBitmap(m_file_edit_mask.bmp(), 0, size.y - m_file_edit_mask.GetBmpSize().y); + dc.DrawBitmap(m_file_edit_mask.get_bitmap(), 0, size.y - m_file_edit_mask.GetSize().y); } @@ -268,14 +268,14 @@ void AuFile::PaintForeground(wxDC &dc) auto sizet = dc.GetTextExtent(cover_text_left); auto pos = wxPoint(0, 0); pos.x = (size.x / 2 - sizet.x) / 2; - pos.y = (size.y - (m_file_edit_mask.GetBmpSize().y + sizet.y) / 2); + pos.y = (size.y - (m_file_edit_mask.GetSize().y + sizet.y) / 2); dc.DrawText(cover_text_left, pos); // right text sizet = dc.GetTextExtent(cover_text_right); pos = wxPoint(0, 0); pos.x = size.x / 2 + (size.x / 2 - sizet.x) / 2; - pos.y = (size.y - (m_file_edit_mask.GetBmpSize().y + sizet.y) / 2); + pos.y = (size.y - (m_file_edit_mask.GetSize().y + sizet.y) / 2); dc.DrawText(cover_text_right, pos); // Split @@ -283,7 +283,7 @@ void AuFile::PaintForeground(wxDC &dc) dc.SetBrush(*wxWHITE); pos = wxPoint(0, 0); pos.x = size.x / 2 - 1; - pos.y = size.y - FromDIP(24) - (m_file_edit_mask.GetBmpSize().y - FromDIP(24)) / 2; + pos.y = size.y - FromDIP(24) - (m_file_edit_mask.GetSize().y - FromDIP(24)) / 2; dc.DrawRectangle(pos.x, pos.y, 2, FromDIP(24)); } else { // right text @@ -297,7 +297,7 @@ void AuFile::PaintForeground(wxDC &dc) if (m_cover) { dc.SetTextForeground(*wxWHITE); - dc.DrawBitmap(m_file_cover.bmp(), size.x - m_file_cover.GetBmpSize().x, 0); + dc.DrawBitmap(m_file_cover.get_bitmap(), size.x - m_file_cover.GetSize().x, 0); dc.SetFont(Label::Body_12); auto sizet = dc.GetTextExtent(cover_text_cover); auto pos = wxPoint(0, 0); @@ -306,7 +306,7 @@ void AuFile::PaintForeground(wxDC &dc) dc.DrawText(cover_text_cover, pos); } - if (m_hover) { dc.DrawBitmap(m_file_delete.bmp(), size.x - m_file_delete.GetBmpSize().x - FromDIP(10), FromDIP(10)); } + if (m_hover) { dc.DrawBitmap(m_file_delete.get_bitmap(), size.x - m_file_delete.GetSize().x - FromDIP(10), FromDIP(10)); } } void AuFile::on_mouse_enter(wxMouseEvent &evt) @@ -421,7 +421,7 @@ void AuFile::on_mouse_left_up(wxMouseEvent &evt) auto pos = evt.GetPosition(); // set cover - auto mask_size = wxSize(GetSize().x, m_file_edit_mask.GetBmpSize().y); + auto mask_size = wxSize(GetSize().x, m_file_edit_mask.GetSize().y); auto cover_left = 0; auto cover_top = size.y - mask_size.y; auto cover_right = mask_size.x / 2; @@ -443,10 +443,10 @@ void AuFile::on_mouse_left_up(wxMouseEvent &evt) if (pos.x > rename_left && pos.x < rename_right && pos.y > rename_top && pos.y < rename_bottom) { on_set_rename(); return; } // close - auto close_left = size.x - m_file_delete.GetBmpSize().x - FromDIP(10); + auto close_left = size.x - m_file_delete.GetSize().x - FromDIP(10); auto close_top = FromDIP(10); auto close_right = size.x - FromDIP(10); - auto close_bottom = m_file_delete.GetBmpSize().y + FromDIP(10); + auto close_bottom = m_file_delete.GetSize().y + FromDIP(10); if (pos.x > close_left && pos.x < close_right && pos.y > close_top && pos.y < close_bottom) { on_set_delete(); return; } exit_rename_mode(); diff --git a/src/slic3r/GUI/BBLTopbar.cpp b/src/slic3r/GUI/BBLTopbar.cpp index 386dfea00f..3fa7ffe748 100644 --- a/src/slic3r/GUI/BBLTopbar.cpp +++ b/src/slic3r/GUI/BBLTopbar.cpp @@ -93,9 +93,7 @@ void BBLTopbarArt::DrawButton(wxDC& dc, wxWindow* wnd, const wxAuiToolBarItem& i int bmpX = 0, bmpY = 0; int textX = 0, textY = 0; - const wxBitmap& bmp = item.GetState() & wxAUI_BUTTON_STATE_DISABLED - ? item.GetDisabledBitmap() - : item.GetBitmap(); + const wxBitmap &bmp = item.GetCurrentBitmapFor(wnd); const wxSize bmpSize = bmp.IsOk() ? bmp.GetScaledSize() : wxSize(0, 0); diff --git a/src/slic3r/GUI/BitmapCache.cpp b/src/slic3r/GUI/BitmapCache.cpp index 080a0f7db6..9eab03ddf9 100644 --- a/src/slic3r/GUI/BitmapCache.cpp +++ b/src/slic3r/GUI/BitmapCache.cpp @@ -13,10 +13,8 @@ #include #endif /* __WXGTK2__ */ -#define NANOSVG_IMPLEMENTATION -#include "nanosvg/nanosvg.h" -#define NANOSVGRAST_IMPLEMENTATION -#include "nanosvg/nanosvgrast.h" +#include +#include namespace Slic3r { namespace GUI { @@ -60,7 +58,168 @@ static wxBitmap wxImage_to_wxBitmap_with_alpha(wxImage &&image, float scale = 1. #endif } -wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_t height) +wxBitmapBundle* BitmapCache::insert_bndl(const std::string& name, const std::vector& bmps) +{ + wxVector bitmaps; + + std::set scales = {1.0}; +#ifndef __linux__ + +#ifdef __APPLE__ + scales.emplace(m_scale); +#else + size_t disp_cnt = wxDisplay::GetCount(); + for (size_t disp = 0; disp < disp_cnt; ++disp) + scales.emplace(wxDisplay(disp).GetScaleFactor()); +#endif + +#endif // !__linux__ + + for (double scale : scales) { + size_t width = 0; + size_t height = 0; + for (const wxBitmapBundle* bmp_bndl : bmps) { +#ifdef __APPLE__ + wxSize size = bmp_bndl->GetDefaultSize(); +#else + wxSize size = bmp_bndl->GetPreferredBitmapSizeAtScale(scale); +#endif + width += size.GetWidth(); + height = std::max(height, size.GetHeight()); + } + + std::string bitmap_key = name + "," +float_to_string_decimal_point(scale); + +#ifdef __WXGTK2__ + // Broken alpha workaround + wxImage image(width, height); + image.InitAlpha(); + // Fill in with a white color. + memset(image.GetData(), 0x0ff, width * height * 3); + // Fill in with full transparency. + memset(image.GetAlpha(), 0, width * height); + size_t x = 0; + for (const wxBitmapBundle* bmp_bndl : bmps) { + wxBitmap bmp = bmp_bndl->GetBitmap(bmp_bndl->GetDefaultSize()); + if (bmp.GetWidth() > 0) { + if (bmp.GetDepth() == 32) { + wxAlphaPixelData data(bmp); + //FIXME The following method is missing from wxWidgets 3.1.1. + // It looks like the wxWidgets 3.0.3 called the wrapped bitmap's UseAlpha(). + //data.UseAlpha(); + if (data) { + for (int r = 0; r < bmp.GetHeight(); ++r) { + wxAlphaPixelData::Iterator src(data); + src.Offset(data, 0, r); + unsigned char* dst_pixels = image.GetData() + (x + r * width) * 3; + unsigned char* dst_alpha = image.GetAlpha() + x + r * width; + for (int c = 0; c < bmp.GetWidth(); ++c, ++src) { + *dst_pixels++ = src.Red(); + *dst_pixels++ = src.Green(); + *dst_pixels++ = src.Blue(); + *dst_alpha++ = src.Alpha(); + } + } + } + } + else if (bmp.GetDepth() == 24) { + wxNativePixelData data(bmp); + if (data) { + for (int r = 0; r < bmp.GetHeight(); ++r) { + wxNativePixelData::Iterator src(data); + src.Offset(data, 0, r); + unsigned char* dst_pixels = image.GetData() + (x + r * width) * 3; + unsigned char* dst_alpha = image.GetAlpha() + x + r * width; + for (int c = 0; c < bmp.GetWidth(); ++c, ++src) { + *dst_pixels++ = src.Red(); + *dst_pixels++ = src.Green(); + *dst_pixels++ = src.Blue(); + *dst_alpha++ = wxALPHA_OPAQUE; + } + } + } + } + } + x += bmp.GetScaledWidth(); + } + + bitmaps.push_back(* this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image)))); + +#else + + wxBitmap* bitmap = this->insert(bitmap_key, width, height, scale); + wxMemoryDC memDC; + memDC.SelectObject(*bitmap); + memDC.SetBackground(*wxTRANSPARENT_BRUSH); + memDC.Clear(); + size_t x = 0; + for (const wxBitmapBundle* bmp_bndl : bmps) { + wxBitmap bmp = bmp_bndl->GetBitmap(bmp_bndl->GetPreferredBitmapSizeAtScale(scale)); + + if (bmp.GetWidth() > 0) + memDC.DrawBitmap(bmp, x, 0, true); + // we should "move" with step equal to non-scaled width +#ifdef __APPLE__ + x += bmp.GetScaledWidth(); +#else + x += bmp.GetWidth(); +#endif + } + memDC.SelectObject(wxNullBitmap); + bitmaps.push_back(*bitmap); + +#endif + } + + return insert_bndl(name, bitmaps); +} + +wxBitmapBundle* BitmapCache::insert_bndl(const std::string &bitmap_key, const char* data, size_t width, size_t height) +{ + wxBitmapBundle* bndl = nullptr; + auto it = m_bndl_map.find(bitmap_key); + if (it == m_bndl_map.end()) { + bndl = new wxBitmapBundle(wxBitmapBundle::FromSVG(data, wxSize(width, height))); + m_bndl_map[bitmap_key] = bndl; + } + else { + bndl = it->second; + *bndl = wxBitmapBundle::FromSVG(data, wxSize(width, height)); + } + return bndl; +} + +wxBitmapBundle* BitmapCache::insert_bndl(const std::string& bitmap_key, const wxBitmapBundle& bmp) +{ + wxBitmapBundle* bndl = nullptr; + auto it = m_bndl_map.find(bitmap_key); + if (it == m_bndl_map.end()) { + bndl = new wxBitmapBundle(bmp); + m_bndl_map[bitmap_key] = bndl; + } + else { + bndl = it->second; + *bndl = wxBitmapBundle(bmp); + } + return bndl; +} + +wxBitmapBundle* BitmapCache::insert_bndl(const std::string& bitmap_key, const wxVector& bmps) +{ + wxBitmapBundle* bndl = nullptr; + auto it = m_bndl_map.find(bitmap_key); + if (it == m_bndl_map.end()) { + bndl = new wxBitmapBundle(wxBitmapBundle::FromBitmaps(bmps)); + m_bndl_map[bitmap_key] = bndl; + } + else { + bndl = it->second; + *bndl = wxBitmapBundle::FromBitmaps(bmps); + } + return bndl; +} + +wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_t height, double scale/* = -1.0*/) { wxBitmap *bitmap = nullptr; auto it = m_map.find(bitmap_key); @@ -76,7 +235,7 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_ // So, We need to let the Mac OS wxBitmap implementation // know that the image may already be scaled appropriately for Retina, // and thereby that it's not supposed to upscale it. - bitmap->CreateScaled(width, height, -1, m_scale); + bitmap->CreateScaled(width, height, -1, scale < 0.0 ? m_scale : scale); #endif m_map[bitmap_key] = bitmap; } else { @@ -105,110 +264,6 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp return bitmap; } -wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp, const wxBitmap &bmp2) -{ - // Copying the wxBitmaps is cheap as the bitmap's content is reference counted. - const wxBitmap bmps[2] = { bmp, bmp2 }; - return this->insert(bitmap_key, bmps, bmps + 2); -} - -wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3) -{ - // Copying the wxBitmaps is cheap as the bitmap's content is reference counted. - const wxBitmap bmps[3] = { bmp, bmp2, bmp3 }; - return this->insert(bitmap_key, bmps, bmps + 3); -} - -wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *begin, const wxBitmap *end) -{ - size_t width = 0; - size_t height = 0; - for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) { -#ifdef __APPLE__ - width += bmp->GetScaledWidth(); - height = std::max(height, bmp->GetScaledHeight()); -#else - width += bmp->GetWidth(); - height = std::max(height, bmp->GetHeight()); -#endif - } - -#ifdef __WXGTK2__ - // Broken alpha workaround - wxImage image(width, height); - image.InitAlpha(); - // Fill in with a white color. - memset(image.GetData(), 0x0ff, width * height * 3); - // Fill in with full transparency. - memset(image.GetAlpha(), 0, width * height); - size_t x = 0; - for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) { - if (bmp->GetWidth() > 0) { - if (bmp->GetDepth() == 32) { - wxAlphaPixelData data(*const_cast(bmp)); - //FIXME The following method is missing from wxWidgets 3.1.1. - // It looks like the wxWidgets 3.0.3 called the wrapped bitmap's UseAlpha(). - //data.UseAlpha(); - if (data) { - for (int r = 0; r < bmp->GetHeight(); ++ r) { - wxAlphaPixelData::Iterator src(data); - src.Offset(data, 0, r); - unsigned char *dst_pixels = image.GetData() + (x + r * width) * 3; - unsigned char *dst_alpha = image.GetAlpha() + x + r * width; - for (int c = 0; c < bmp->GetWidth(); ++ c, ++ src) { - *dst_pixels ++ = src.Red(); - *dst_pixels ++ = src.Green(); - *dst_pixels ++ = src.Blue(); - *dst_alpha ++ = src.Alpha(); - } - } - } - } else if (bmp->GetDepth() == 24) { - wxNativePixelData data(*const_cast(bmp)); - if (data) { - for (int r = 0; r < bmp->GetHeight(); ++ r) { - wxNativePixelData::Iterator src(data); - src.Offset(data, 0, r); - unsigned char *dst_pixels = image.GetData() + (x + r * width) * 3; - unsigned char *dst_alpha = image.GetAlpha() + x + r * width; - for (int c = 0; c < bmp->GetWidth(); ++ c, ++ src) { - *dst_pixels ++ = src.Red(); - *dst_pixels ++ = src.Green(); - *dst_pixels ++ = src.Blue(); - *dst_alpha ++ = wxALPHA_OPAQUE; - } - } - } - } - } - x += bmp->GetWidth(); - } - return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image))); - -#else - - wxBitmap *bitmap = this->insert(bitmap_key, width, height); - wxMemoryDC memDC; - memDC.SelectObject(*bitmap); - memDC.SetBackground(*wxTRANSPARENT_BRUSH); - memDC.Clear(); - size_t x = 0; - for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) { - if (bmp->GetWidth() > 0) - memDC.DrawBitmap(*bmp, x, 0, true); -#ifdef __APPLE__ - // we should "move" with step equal to non-scaled width - x += bmp->GetScaledWidth(); -#else - x += bmp->GetWidth(); -#endif - } - memDC.SelectObject(wxNullBitmap); - return bitmap; - -#endif -} - wxBitmap* BitmapCache::insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, const bool grayscale/* = false*/) { wxImage image(width, height); @@ -305,7 +360,102 @@ error: return NULL; } -wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_width, unsigned target_height, +void BitmapCache::nsvgGetDataFromFileWithReplace(const char* filename, std::string& data_str, const std::map& replaces) +{ + FILE* fp = NULL; + size_t size; + char* data = NULL; + + fp = boost::nowide::fopen(filename, "rb"); + if (!fp) goto error; + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + data = (char*)malloc(size + 1); + if (data == NULL) goto error; + if (fread(data, 1, size, fp) != size) goto error; + data[size] = '\0'; // Must be null terminated. + fclose(fp); + + data_str.assign(data); + for (auto val : replaces) + boost::replace_all(data_str, val.first, val.second); + + free(data); + return; + +error: + if (fp) fclose(fp); + if (data) free(data); + return; +} + +wxBitmapBundle* BitmapCache::from_svg(const std::string& bitmap_name, unsigned target_width, unsigned target_height, + const bool dark_mode, const std::string& new_color /*= ""*/) +{ + if (target_width == 0) + target_width = target_height; + std::string bitmap_key = bitmap_name + (target_height != 0 ? + "-h" + std::to_string(target_height) : + "-w" + std::to_string(target_width)) + + (dark_mode ? "-dm" : "") + + new_color; + + auto it = m_bndl_map.find(bitmap_key); + if (it != m_bndl_map.end()) + return it->second; + + // map of color replaces + //Orca: use replaces from load_svg function + std::map replaces; + replaces["\"#0x00AE42\""] = "\"#009688\""; + replaces["\"#00FF00\""] = "\"#52c7b8\""; + if (dark_mode) { + replaces["\"#262E30\""] = "\"#EFEFF0\""; + replaces["\"#323A3D\""] = "\"#B3B3B5\""; + replaces["\"#808080\""] = "\"#818183\""; + replaces["\"#CECECE\""] = "\"#54545B\""; + replaces["\"#6B6B6B\""] = "\"#818182\""; + replaces["\"#909090\""] = "\"#FFFFFF\""; + replaces["\"#00FF00\""] = "\"#FF0000\""; + replaces["\"#009688\""] = "\"#00675b\""; + } + + std::string str; + nsvgGetDataFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), str, replaces); + if (str.empty()) + return nullptr; + + return insert_bndl(bitmap_key, str.data(), target_width, target_height); +} + +wxBitmapBundle* BitmapCache::from_png(const std::string& bitmap_name, unsigned width, unsigned height) +{ + std::string bitmap_key = bitmap_name + (height != 0 ? + "-h" + std::to_string(height) : + "-w" + std::to_string(width)); + + auto it = m_bndl_map.find(bitmap_key); + if (it != m_bndl_map.end()) + return it->second; + + wxImage image; + if (!image.LoadFile(Slic3r::GUI::from_u8(Slic3r::var(bitmap_name + ".png")), wxBITMAP_TYPE_PNG) || + image.GetWidth() == 0 || image.GetHeight() == 0) + return nullptr; + + if (height != 0 && unsigned(image.GetHeight()) != height) + width = unsigned(0.5f + float(image.GetWidth()) * height / image.GetHeight()); + else if (width != 0 && unsigned(image.GetWidth()) != width) + height = unsigned(0.5f + float(image.GetHeight()) * width / image.GetWidth()); + + if (height != 0 && width != 0) + image.Rescale(width, height, wxIMAGE_QUALITY_BILINEAR); + + return this->insert_bndl(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image))); +} + +wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_width, unsigned target_height, const bool grayscale/* = false*/, const bool dark_mode/* = false*/, const std::string& new_color /*= ""*/, const float scale_in_center/* = 0*/) { std::string bitmap_key = bitmap_name + ( target_height !=0 ? @@ -322,7 +472,7 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_ // map of color replaces std::map replaces; -replaces["\"#0x00AE42\""] = "\"#009688\""; + replaces["\"#0x00AE42\""] = "\"#009688\""; replaces["\"#00FF00\""] = "\"#52c7b8\""; if (dark_mode) { replaces["\"#262E30\""] = "\"#EFEFF0\""; @@ -333,7 +483,7 @@ replaces["\"#0x00AE42\""] = "\"#009688\""; replaces["\"#6B6B6B\""] = "\"#818182\""; replaces["\"#909090\""] = "\"#FFFFFF\""; replaces["\"#00FF00\""] = "\"#FF0000\""; -replaces["\"#009688\""] = "\"#00675b\""; + replaces["\"#009688\""] = "\"#00675b\""; } //if (!new_color.empty()) // replaces["\"#ED6B21\""] = "\"" + new_color + "\""; @@ -386,9 +536,9 @@ replaces["\"#009688\""] = "\"#00675b\""; return this->insert_raw_rgba(bitmap_key, width, height, data.data(), grayscale); } - +/* //we make scaled solid bitmaps only for the cases, when its will be used with scaled SVG icon in one output bitmap -wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling/* = false*/, size_t border_width /*= 0*/, bool dark_mode/* = false*/) +wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling/* = false* /, size_t border_width /*= 0* /, bool dark_mode/* = false* /) { double scale = suppress_scaling ? 1.0f : m_scale; width *= scale; @@ -430,6 +580,89 @@ wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsi return wxImage_to_wxBitmap_with_alpha(std::move(image), scale); } +*/ +//we make scaled solid bitmaps only for the cases, when its will be used with scaled SVG icon in one output bitmap +wxBitmapBundle BitmapCache::mksolid(size_t width_in, size_t height_in, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, size_t border_width /*= 0*/, bool dark_mode/* = false*/) +{ + wxVector bitmaps; + + std::set scales = { 1.0 }; +#ifndef __linux__ + +#ifdef __APPLE__ + scales.emplace(m_scale); +#else + size_t disp_cnt = wxDisplay::GetCount(); + for (size_t disp = 0; disp < disp_cnt; ++disp) + scales.emplace(wxDisplay(disp).GetScaleFactor()); +#endif + +#endif // !__linux__ + + for (double scale : scales) { + size_t width = width_in * scale; + size_t height = height_in * scale; + + wxImage image(width, height); + image.InitAlpha(); + unsigned char* imgdata = image.GetData(); + unsigned char* imgalpha = image.GetAlpha(); + for (size_t i = 0; i < width * height; ++i) { + *imgdata++ = r; + *imgdata++ = g; + *imgdata++ = b; + *imgalpha++ = transparency; + } + + // Add border, make white/light spools easier to see + if (border_width > 0) { + + // Restrict to width of image + if (border_width > height) border_width = height - 1; + if (border_width > width) border_width = width - 1; + + auto px_data = (uint8_t*)image.GetData(); + auto a_data = (uint8_t*)image.GetAlpha(); + + for (size_t x = 0; x < width; ++x) { + for (size_t y = 0; y < height; ++y) { + if (x < border_width || y < border_width || + x >= (width - border_width) || y >= (height - border_width)) { + const size_t idx = (x + y * width); + const size_t idx_rgb = (x + y * width) * 3; + px_data[idx_rgb] = px_data[idx_rgb + 1] = px_data[idx_rgb + 2] = dark_mode ? 245u : 110u; + a_data[idx] = 255u; + } + } + } + } + + bitmaps.push_back(wxImage_to_wxBitmap_with_alpha(std::move(image), scale)); + } + return wxBitmapBundle::FromBitmaps(bitmaps); +} + +wxBitmapBundle* BitmapCache::mksolid_bndl(size_t width, size_t height, const std::string& color, size_t border_width, bool dark_mode) +{ + std::string bitmap_key = (color.empty() ? "empty" : color) + "-h" + std::to_string(height) + "-w" + std::to_string(width) + (dark_mode ? "-dm" : ""); + + wxBitmapBundle* bndl = nullptr; + auto it = m_bndl_map.find(bitmap_key); + if (it == m_bndl_map.end()) { + if (color.empty()) + bndl = new wxBitmapBundle(mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT, size_t(0))); + else { + ColorRGB rgb;// [3] + decode_color(color, rgb); + bndl = new wxBitmapBundle(mksolid(width, height, rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar(), wxALPHA_OPAQUE, border_width, dark_mode)); + } + m_bndl_map[bitmap_key] = bndl; + } + else + return it->second; + + return bndl; +} bool BitmapCache::parse_color(const std::string& scolor, unsigned char* rgb_out) { diff --git a/src/slic3r/GUI/BitmapCache.hpp b/src/slic3r/GUI/BitmapCache.hpp index ab9e457240..54062777b4 100644 --- a/src/slic3r/GUI/BitmapCache.hpp +++ b/src/slic3r/GUI/BitmapCache.hpp @@ -12,7 +12,8 @@ #include "libslic3r/Color.hpp" struct NSVGimage; -namespace Slic3r { namespace GUI { +namespace Slic3r { +namespace GUI { class BitmapCache { @@ -22,15 +23,23 @@ public: void clear(); double scale() { return m_scale; } + wxBitmapBundle* find_bndl(const std::string &name) { auto it = m_bndl_map.find(name); return (it == m_bndl_map.end()) ? nullptr : it->second; } + const wxBitmapBundle* find_bndl(const std::string &name) const { return const_cast(this)->find_bndl(name); } wxBitmap* find(const std::string &name) { auto it = m_map.find(name); return (it == m_map.end()) ? nullptr : it->second; } const wxBitmap* find(const std::string &name) const { return const_cast(this)->find(name); } - wxBitmap* insert(const std::string &name, size_t width, size_t height); + wxBitmapBundle* insert_bndl(const std::string& bitmap_key, const char* data, size_t width, size_t height); + wxBitmapBundle* insert_bndl(const std::string& bitmap_key, const wxBitmapBundle &bmp); + wxBitmapBundle* insert_bndl(const std::string& bitmap_key, const wxVector& bmps); + wxBitmapBundle* insert_bndl(const std::string& name, const std::vector& bmps); + wxBitmapBundle* insert_raw_rgba_bndl(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, const bool grayscale = false); + + wxBitmap* insert(const std::string &name, size_t width, size_t height, double scale = -1.0); wxBitmap* insert(const std::string &name, const wxBitmap &bmp); - wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2); - wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3); - wxBitmap* insert(const std::string &name, const std::vector &bmps) { return this->insert(name, &bmps.front(), &bmps.front() + bmps.size()); } - wxBitmap* insert(const std::string &name, const wxBitmap *begin, const wxBitmap *end); +// wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2); +// wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3); +// wxBitmap* insert(const std::string &name, const std::vector &bmps) { return this->insert(name, &bmps.front(), &bmps.front() + bmps.size()); } +// wxBitmap* insert(const std::string &name, const wxBitmap *begin, const wxBitmap *end); wxBitmap* insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, const bool grayscale = false); // BBS: support resize by fill border (scale_in_center) @@ -41,19 +50,28 @@ public: // And makes replases befor parsing // replace_map containes old_value->new_value static NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map& replaces); + // Gets a data from SVG file and makes replases + // replace_map containes old_value->new_value + static void nsvgGetDataFromFileWithReplace(const char* filename, std::string& data_str, const std::map& replaces); + wxBitmapBundle* from_svg(const std::string& bitmap_name, unsigned target_width, unsigned target_height, const bool dark_mode, const std::string& new_color = ""); + wxBitmapBundle* from_png(const std::string& bitmap_name, unsigned width, unsigned height); // Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width. wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false, const bool dark_mode = false, const std::string& new_color = "", const float scale_in_center = 0.f); - wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false); - wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3], bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE, suppress_scaling, border_width, dark_mode); } - wxBitmap mksolid(size_t width, size_t height, const ColorRGB& rgb, bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false) { return mksolid(width, height, rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar(), wxALPHA_OPAQUE, suppress_scaling, border_width, dark_mode); } - wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT); } +// wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false); +// wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3], bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE, suppress_scaling, border_width, dark_mode); } +// wxBitmap mksolid(size_t width, size_t height, const ColorRGB& rgb, bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false) { return mksolid(width, height, rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar(), wxALPHA_OPAQUE, suppress_scaling, border_width, dark_mode); } +// wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT, true, 0); } + wxBitmapBundle mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, size_t border_width = 0, bool dark_mode = false); + wxBitmapBundle* mksolid_bndl(size_t width, size_t height, const std::string& color = std::string(), size_t border_width = 0, bool dark_mode = false); + wxBitmapBundle* mkclear_bndl(size_t width, size_t height) { return mksolid_bndl(width, height); } static bool parse_color(const std::string& scolor, unsigned char* rgb_out); static bool parse_color4(const std::string& scolor, unsigned char* rgba_out); private: std::map m_map; + std::map m_bndl_map; double m_gs = 0.2; // value, used for image.ConvertToGreyscale(m_gs, m_gs, m_gs) double m_scale = 1.0; // value, used for correct scaling of SVG icons on Retina display }; diff --git a/src/slic3r/GUI/BitmapComboBox.cpp b/src/slic3r/GUI/BitmapComboBox.cpp index 36c15343d3..24a971eb88 100644 --- a/src/slic3r/GUI/BitmapComboBox.cpp +++ b/src/slic3r/GUI/BitmapComboBox.cpp @@ -54,17 +54,6 @@ using Slic3r::GUI::format_wxstr; namespace Slic3r { namespace GUI { -/* For PresetComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina - * (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean - * "please scale this to such and such" but rather - * "the wxImage is already sized for backing scale such and such". ) - * Unfortunately, the constructor changes the size of wxBitmap too. - * Thus We need to use unscaled size value for bitmaps that we use - * to avoid scaled size of control items. - * For this purpose control drawing methods and - * control size calculation methods (virtual) are overridden. - **/ - BitmapComboBox::BitmapComboBox(wxWindow* parent, wxWindowID id/* = wxID_ANY*/, const wxString& value/* = wxEmptyString*/, @@ -90,72 +79,6 @@ BitmapComboBox::~BitmapComboBox() { } -#ifdef __APPLE__ -bool BitmapComboBox::OnAddBitmap(const wxBitmap& bitmap) -{ - if (bitmap.IsOk()) - { - // we should use scaled! size values of bitmap - int width = (int)bitmap.GetScaledWidth(); - int height = (int)bitmap.GetScaledHeight(); - - if (m_usedImgSize.x < 0) - { - // If size not yet determined, get it from this image. - m_usedImgSize.x = width; - m_usedImgSize.y = height; - - // Adjust control size to vertically fit the bitmap - wxWindow* ctrl = GetControl(); - ctrl->InvalidateBestSize(); - wxSize newSz = ctrl->GetBestSize(); - wxSize sz = ctrl->GetSize(); - if (newSz.y > sz.y) - ctrl->SetSize(sz.x, newSz.y); - else - DetermineIndent(); - } - - wxCHECK_MSG(width == m_usedImgSize.x && height == m_usedImgSize.y, - false, - "you can only add images of same size"); - - return true; - } - - return false; -} - -void BitmapComboBox::OnDrawItem(wxDC& dc, - const wxRect& rect, - int item, - int flags) const -{ - const wxBitmap& bmp = *(static_cast(m_bitmaps[item])); - if (bmp.IsOk()) - { - // we should use scaled! size values of bitmap - wxCoord w = bmp.GetScaledWidth(); - wxCoord h = bmp.GetScaledHeight(); - - const int imgSpacingLeft = 4; - - // Draw the image centered - dc.DrawBitmap(bmp, - rect.x + (m_usedImgSize.x - w) / 2 + imgSpacingLeft, - rect.y + (rect.height - h) / 2, - true); - } - - wxString text = GetString(item); - if (!text.empty()) - dc.DrawText(text, - rect.x + m_imgAreaWidth + 1, - rect.y + (rect.height - dc.GetCharHeight()) / 2); -} -#endif - - #ifdef _WIN32 int BitmapComboBox::Append(const wxString& item) @@ -166,18 +89,11 @@ int BitmapComboBox::Append(const wxString& item) //2. But then set width to 0 value for no using of bitmap left and right spacing //3. Set this empty bitmap to the at list one item and BitmapCombobox will be recreated correct - wxBitmap bitmap(1, int(1.6 * wxGetApp().em_unit() + 1)); - { - // bitmap.SetWidth(0); is depricated now - // so, use next code - bitmap.UnShare();// AllocExclusive(); - bitmap.GetGDIImageData()->m_width = 0; - } - + wxBitmapBundle bitmap = *get_empty_bmp_bundle(1, 16); OnAddBitmap(bitmap); + const int n = wxComboBox::Append(item); - if (n != wxNOT_FOUND) - DoSetItemBitmap(n, bitmap); + return n; } diff --git a/src/slic3r/GUI/BitmapComboBox.hpp b/src/slic3r/GUI/BitmapComboBox.hpp index a77bf401d6..545213fc3c 100644 --- a/src/slic3r/GUI/BitmapComboBox.hpp +++ b/src/slic3r/GUI/BitmapComboBox.hpp @@ -29,28 +29,13 @@ BitmapComboBox(wxWindow* parent, #ifdef _WIN32 int Append(const wxString& item); #endif - int Append(const wxString& item, const wxBitmap& bitmap) + int Append(const wxString& item, const wxBitmapBundle& bitmap) { return wxBitmapComboBox::Append(item, bitmap); } protected: -#ifdef __APPLE__ -/* For PresetComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina - * (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean - * "please scale this to such and such" but rather - * "the wxImage is already sized for backing scale such and such". ) - * Unfortunately, the constructor changes the size of wxBitmap too. - * Thus We need to use unscaled size value for bitmaps that we use - * to avoid scaled size of control items. - * For this purpose control drawing methods and - * control size calculation methods (virtual) are overridden. - **/ -bool OnAddBitmap(const wxBitmap& bitmap) override; -void OnDrawItem(wxDC& dc, const wxRect& rect, int item, int flags) const override; -#endif - #ifdef _WIN32 bool MSWOnDraw(WXDRAWITEMSTRUCT* item) override; void DrawBackground_(wxDC& dc, const wxRect& rect, int WXUNUSED(item), int flags) const; diff --git a/src/slic3r/GUI/CalibrationPanel.cpp b/src/slic3r/GUI/CalibrationPanel.cpp index 88e107da84..465fc95e6f 100644 --- a/src/slic3r/GUI/CalibrationPanel.cpp +++ b/src/slic3r/GUI/CalibrationPanel.cpp @@ -102,9 +102,9 @@ void MObjectPanel::doRender(wxDC& dc) if (m_state == PrinterState::IN_LAN) { dwbitmap = m_printer_in_lan; } // dc.DrawCircle(left, size.y / 2, 3); - dc.DrawBitmap(dwbitmap.bmp(), wxPoint(left, (size.y - dwbitmap.GetBmpSize().y) / 2)); + dc.DrawBitmap(dwbitmap.get_bitmap(), wxPoint(left, (size.y - dwbitmap.GetSize().y) / 2)); - left += dwbitmap.GetBmpSize().x + 8; + left += dwbitmap.GetSize().x + 8; dc.SetFont(Label::Body_13); dc.SetBackgroundMode(wxTRANSPARENT); dc.SetTextForeground(StateColor::darkModeColorFor(SELECT_MACHINE_GREY900)); diff --git a/src/slic3r/GUI/CalibrationWizardPage.cpp b/src/slic3r/GUI/CalibrationWizardPage.cpp index f3ff77313b..648ce0871c 100644 --- a/src/slic3r/GUI/CalibrationWizardPage.cpp +++ b/src/slic3r/GUI/CalibrationWizardPage.cpp @@ -371,7 +371,7 @@ CaliPageCaption::CaliPageCaption(wxWindow* parent, CalibMode cali_mode, auto top_sizer = new wxBoxSizer(wxVERTICAL); auto caption_sizer = new wxBoxSizer(wxHORIZONTAL); m_prev_btn = new ScalableButton(this, wxID_ANY, "cali_page_caption_prev", - wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true, 30); + wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, 30); m_prev_btn->SetBackgroundColour(*wxWHITE); caption_sizer->Add(m_prev_btn, 0, wxALIGN_CENTER | wxRIGHT, FromDIP(10)); @@ -382,7 +382,7 @@ CaliPageCaption::CaliPageCaption(wxWindow* parent, CalibMode cali_mode, caption_sizer->Add(title_text, 0, wxALIGN_CENTER | wxRIGHT, FromDIP(10)); m_help_btn = new ScalableButton(this, wxID_ANY, "cali_page_caption_help", - wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true, 30); + wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, 30); m_help_btn->Hide(); m_help_btn->SetBackgroundColour(*wxWHITE); caption_sizer->Add(m_help_btn, 0, wxALIGN_CENTER); @@ -472,12 +472,12 @@ void CaliPageCaption::show_help_icon(bool show) void CaliPageCaption::on_sys_color_changed() { - m_prev_btn->msw_rescale(); + m_prev_btn->sys_color_changed(); } void CaliPageCaption::msw_rescale() { - m_prev_btn->msw_rescale(); + m_prev_btn->sys_color_changed(); } CaliPageStepGuide::CaliPageStepGuide(wxWindow* parent, wxArrayString steps, @@ -593,7 +593,7 @@ PAPageHelpPanel::PAPageHelpPanel(wxWindow* parent, bool ground_panel, wxWindowID wxBoxSizer* help_text_sizer = new wxBoxSizer(wxHORIZONTAL); auto help_text = new Label(this, _L("You could change the Flow Dynamics Calibration Factor in material editing")); help_text->SetFont(Label::Body_14); - m_help_btn = new ScalableButton(this, wxID_ANY, "cali_page_caption_help", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, false, 24); + m_help_btn = new ScalableButton(this, wxID_ANY, "cali_page_caption_help", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, 24); m_help_btn->SetBackgroundColour(m_help_btn->GetParent()->GetBackgroundColour()); help_text_sizer->Add(help_text, 0, wxALIGN_CENTER | wxLEFT, left_align_padding); help_text_sizer->Add(m_help_btn, 0, wxALIGN_CENTER | wxLEFT, FromDIP(8)); @@ -611,8 +611,8 @@ PAPageHelpPanel::PAPageHelpPanel(wxWindow* parent, bool ground_panel, wxWindowID void PAPageHelpPanel::msw_rescale() { - m_help_btn->msw_rescale(); - m_bmp.msw_rescale(); + m_help_btn->sys_color_changed(); + m_bmp.sys_color_changed(); m_img->SetBitmap(m_bmp.bmp()); } diff --git a/src/slic3r/GUI/CalibrationWizardPresetPage.cpp b/src/slic3r/GUI/CalibrationWizardPresetPage.cpp index 9e72a211ae..bf60b50aa3 100644 --- a/src/slic3r/GUI/CalibrationWizardPresetPage.cpp +++ b/src/slic3r/GUI/CalibrationWizardPresetPage.cpp @@ -448,7 +448,7 @@ CalibrationPresetPage::CalibrationPresetPage( void CalibrationPresetPage::msw_rescale() { CalibrationWizardPage::msw_rescale(); - m_ams_sync_button->msw_rescale(); + m_ams_sync_button->sys_color_changed(); m_virtual_tray_comboBox->msw_rescale(); for (auto& comboBox : m_filament_comboBox_list) { comboBox->msw_rescale(); @@ -458,7 +458,7 @@ void CalibrationPresetPage::msw_rescale() void CalibrationPresetPage::on_sys_color_changed() { CalibrationWizardPage::on_sys_color_changed(); - m_ams_sync_button->msw_rescale(); + m_ams_sync_button->sys_color_changed(); } void CalibrationPresetPage::create_selection_panel(wxWindow* parent) @@ -507,7 +507,7 @@ void CalibrationPresetPage::create_selection_panel(wxWindow* parent) filament_for_text->SetFont(Label::Head_14); filament_for_title_sizer->Add(filament_for_text, 0, wxALIGN_CENTER); filament_for_title_sizer->AddSpacer(FromDIP(25)); - m_ams_sync_button = new ScalableButton(parent, wxID_ANY, "ams_fila_sync", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, false, 18); + m_ams_sync_button = new ScalableButton(parent, wxID_ANY, "ams_fila_sync", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, 18); m_ams_sync_button->SetBackgroundColour(*wxWHITE); m_ams_sync_button->SetToolTip(_L("Synchronize filament list from AMS")); filament_for_title_sizer->Add(m_ams_sync_button, 0, wxALIGN_CENTER); diff --git a/src/slic3r/GUI/CameraPopup.cpp b/src/slic3r/GUI/CameraPopup.cpp index e7b5cf68dd..be63c1706a 100644 --- a/src/slic3r/GUI/CameraPopup.cpp +++ b/src/slic3r/GUI/CameraPopup.cpp @@ -470,8 +470,8 @@ CameraItem::CameraItem(wxWindow *parent, std::string normal, std::string hover) CameraItem::~CameraItem() {} void CameraItem::msw_rescale() { - m_bitmap_normal.msw_rescale(); - m_bitmap_hover.msw_rescale(); + m_bitmap_normal.sys_color_changed(); + m_bitmap_hover.sys_color_changed(); } void CameraItem::on_enter_win(wxMouseEvent &evt) @@ -519,9 +519,9 @@ void CameraItem::render(wxDC &dc) void CameraItem::doRender(wxDC &dc) { if (m_hover) { - dc.DrawBitmap(m_bitmap_hover.bmp(), wxPoint((GetSize().x - m_bitmap_hover.GetBmpSize().x) / 2, (GetSize().y - m_bitmap_hover.GetBmpSize().y) / 2)); + dc.DrawBitmap(m_bitmap_hover.get_bitmap(), wxPoint((GetSize().x - m_bitmap_hover.GetSize().x) / 2, (GetSize().y - m_bitmap_hover.GetSize().y) / 2)); } else { - dc.DrawBitmap(m_bitmap_normal.bmp(), wxPoint((GetSize().x - m_bitmap_normal.GetBmpSize().x) / 2, (GetSize().y - m_bitmap_normal.GetBmpSize().y) / 2)); + dc.DrawBitmap(m_bitmap_normal.get_bitmap(), wxPoint((GetSize().x - m_bitmap_normal.GetSize().x) / 2, (GetSize().y - m_bitmap_normal.GetSize().y) / 2)); } } diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index a34a7cd46e..f59530ed0d 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -1499,7 +1499,7 @@ ConfigWizardIndex::ConfigWizardIndex(wxWindow *parent) #ifndef __WXOSX__ SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX #endif //__WXOSX__ - SetMinSize(bg.bmp().GetSize()); + SetMinSize(bg.GetSize()); const wxSize size = GetTextExtent("m"); em_w = size.x; @@ -1626,8 +1626,8 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt) wxPaintDC dc(this); - const auto bullet_w = bullet_black.bmp().GetSize().GetWidth(); - const auto bullet_h = bullet_black.bmp().GetSize().GetHeight(); + const auto bullet_w = bullet_black.GetWidth(); + const auto bullet_h = bullet_black.GetHeight(); const int yoff_icon = bullet_h < em_h ? (em_h - bullet_h) / 2 : 0; const int yoff_text = bullet_h > em_h ? (bullet_h - em_h) / 2 : 0; const int yinc = item_height(); @@ -1640,10 +1640,10 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt) unsigned x = em_w/2 + item.indent * em_w; if (i == item_active || (item_hover >= 0 && i == (size_t)item_hover)) { - dc.DrawBitmap(bullet_blue.bmp(), x, y + yoff_icon, false); + dc.DrawBitmap(bullet_blue.get_bitmap(), x, y + yoff_icon, false); } - else if (i < item_active) { dc.DrawBitmap(bullet_black.bmp(), x, y + yoff_icon, false); } - else if (i > item_active) { dc.DrawBitmap(bullet_white.bmp(), x, y + yoff_icon, false); } + else if (i < item_active) { dc.DrawBitmap(bullet_black.get_bitmap(), x, y + yoff_icon, false); } + else if (i > item_active) { dc.DrawBitmap(bullet_white.get_bitmap(), x, y + yoff_icon, false); } x += + bullet_w + em_w/2; const auto text_size = dc.GetTextExtent(item.label); @@ -1655,9 +1655,9 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt) } //draw logo - if (int y = size.y - bg.GetBmpHeight(); y>=0) { - dc.DrawBitmap(bg.bmp(), 0, y, false); - index_width = std::max(index_width, bg.GetBmpWidth() + em_w / 2); + if (int y = size.y - bg.GetHeight(); y>=0) { + dc.DrawBitmap(bg.get_bitmap(), 0, y, false); + index_width = std::max(index_width, bg.GetWidth() + em_w / 2); } if (GetMinSize().x < index_width) { @@ -1689,12 +1689,12 @@ void ConfigWizardIndex::msw_rescale() em_w = size.x; em_h = size.y; - bg.msw_rescale(); - SetMinSize(bg.bmp().GetSize()); + bg.sys_color_changed(); + SetMinSize(bg.GetSize()); - bullet_black.msw_rescale(); - bullet_blue.msw_rescale(); - bullet_white.msw_rescale(); + bullet_black.sys_color_changed(); + bullet_blue.sys_color_changed(); + bullet_white.sys_color_changed(); Refresh(); } diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index 364d378b42..4b0a86703f 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -510,7 +510,7 @@ private: ssize_t item_hover; size_t last_page; - int item_height() const { return std::max(bullet_black.bmp().GetSize().GetHeight(), em_w) + em_w; } + int item_height() const { return std::max(bullet_black.GetHeight(), em_w) + em_w; } void on_paint(wxPaintEvent &evt); void on_mouse_move(wxMouseEvent &evt); diff --git a/src/slic3r/GUI/DragCanvas.cpp b/src/slic3r/GUI/DragCanvas.cpp index 38a827dca8..60e1da3bcd 100644 --- a/src/slic3r/GUI/DragCanvas.cpp +++ b/src/slic3r/GUI/DragCanvas.cpp @@ -46,8 +46,8 @@ void DragCanvas::set_shape_list(const std::vector& colors, const st m_dragshape_list.clear(); for (int i = 0; i < order.size(); i++) { - wxBitmap* bmp = get_extruder_color_icon(colors[order[i] - 1], std::to_string(order[i]), SHAPE_SIZE, SHAPE_SIZE); - DragShape* shape = new DragShape(*bmp, order[i]); + wxBitmap bmp = get_extruder_color_icon(colors[order[i] - 1], std::to_string(order[i]), SHAPE_SIZE, SHAPE_SIZE)->GetBitmapFor(m_parent); + DragShape* shape = new DragShape(bmp, order[i]); m_dragshape_list.push_back(shape); } diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp index 068a463246..5c74a5e0ad 100644 --- a/src/slic3r/GUI/ExtraRenderers.cpp +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -33,6 +33,15 @@ wxIMPLEMENT_DYNAMIC_CLASS(DataViewBitmapText, wxObject) IMPLEMENT_VARIANT_OBJECT(DataViewBitmapText) +static wxSize get_size(const wxBitmap& icon) +{ +#ifdef __WIN32__ + return icon.GetSize(); +#else + return icon.GetScaledSize(); +#endif +} + // --------------------------------------------------------- // BitmapTextRenderer // --------------------------------------------------------- @@ -124,11 +133,7 @@ bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) const wxBitmap& icon = m_value.GetBitmap(); if (icon.IsOk()) { -#ifdef __APPLE__ - wxSize icon_sz = icon.GetScaledSize(); -#else - wxSize icon_sz = icon.GetSize(); -#endif + wxSize icon_sz = get_size(icon); dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon_sz.y) / 2); xoffset = icon_sz.x + 4; } @@ -270,11 +275,12 @@ bool BitmapChoiceRenderer::Render(wxRect rect, wxDC* dc, int state) const wxBitmap& icon = m_value.GetBitmap(); if (icon.IsOk()) { - dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2); -// xoffset = icon.GetWidth() + 4; + wxSize icon_sz = get_size(icon); + dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon_sz.GetHeight()) / 2); +// xoffset = icon_sz.GetWidth() + 4; if (rect.height == 0) - rect.height = icon.GetHeight(); + rect.height = icon_sz.GetHeight(); } #ifdef _WIN32 @@ -305,7 +311,7 @@ wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelR if (can_create_editor_ctrl && !can_create_editor_ctrl()) return nullptr; - std::vector icons = get_extruder_color_icons(); + std::vector icons = get_extruder_color_icons(); if (icons.empty()) return nullptr; diff --git a/src/slic3r/GUI/ExtraRenderers.hpp b/src/slic3r/GUI/ExtraRenderers.hpp index ef16ddece9..b778df4c49 100644 --- a/src/slic3r/GUI/ExtraRenderers.hpp +++ b/src/slic3r/GUI/ExtraRenderers.hpp @@ -54,7 +54,7 @@ private: DECLARE_VARIANT_OBJECT(DataViewBitmapText) // ---------------------------------------------------------------------------- -// BitmapTextRenderer +// BitmapTextRenderer - an editable text box within a DataView item // ---------------------------------------------------------------------------- #if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING class BitmapTextRenderer : public wxDataViewRenderer @@ -126,7 +126,7 @@ private: // ---------------------------------------------------------------------------- -// BitmapChoiceRenderer +// BitmapChoiceRenderer - Creates an editable ComboBox within a DataView item // ---------------------------------------------------------------------------- class BitmapChoiceRenderer : public wxDataViewCustomRenderer diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 235ba99509..7939bb9acd 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -1127,7 +1127,7 @@ void Choice::BUILD() auto icon_name = "param_" + m_opt.enum_values[i]; if (boost::filesystem::exists(image_path / (icon_name + ".svg"))) { ScalableBitmap bm(temp, icon_name, 24); - temp->Append(_(el), bm.bmp()); + temp->Append(_(el), bm.get_bitmap()); } else { temp->Append(_(el)); } @@ -1529,7 +1529,7 @@ void Choice::msw_rescale() auto icon_name = "param_" + m_opt.enum_values[i]; if (boost::filesystem::exists(image_path / (icon_name + ".svg"))) { ScalableBitmap bm(window, icon_name, 24); - temp->SetItemBitmap(i, bm.bmp()); + temp->SetItemBitmap(i, bm.get_bitmap()); } ++i; } diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index dba3523491..c1137487e1 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -28,8 +28,8 @@ #define STB_DXT_IMPLEMENTATION #include "stb_dxt/stb_dxt.h" -#include "nanosvg/nanosvg.h" -#include "nanosvg/nanosvgrast.h" +#include +#include #include "libslic3r/Utils.hpp" #include "GUI_App.hpp" diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 8485bfdb88..09789f9910 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -369,7 +369,7 @@ public: // See https://github.com/wxWidgets/wxWidgets/blob/master/src/msw/font.cpp // void wxNativeFontInfo::SetFractionalPointSize(float pointSizeNew) wxNativeFontInfo nfi= *font.GetNativeFontInfo(); - float pointSizeNew = scale * font.GetPointSize(); + float pointSizeNew = wxDisplay(this).GetScaleFactor() * scale * font.GetPointSize(); nfi.lf.lfHeight = nfi.GetLogFontHeightAtPPI(pointSizeNew, get_dpi_for_window(this)); nfi.pointSize = pointSizeNew; font = wxFont(nfi); diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index ac95f30129..f083a7e481 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -261,14 +261,13 @@ std::map SettingsFactory::CATEGORY_ICON = // BBS: remove SLA categories }; -wxBitmap SettingsFactory::get_category_bitmap(const std::string& category_name, bool menu_bmp) +wxBitmapBundle* SettingsFactory::get_category_bitmap(const std::string& category_name) { if (CATEGORY_ICON.find(category_name) == CATEGORY_ICON.end()) - return wxNullBitmap; - return create_scaled_bitmap(CATEGORY_ICON.at(category_name)); + return get_bmp_bundle("empty"); + return get_bmp_bundle(CATEGORY_ICON.at(category_name)); } - //------------------------------------- // MenuFactory //------------------------------------- @@ -425,13 +424,14 @@ static void create_freq_settings_popupmenu(wxMenu* menu, const bool is_object_se } } -std::vector MenuFactory::get_volume_bitmaps() +std::vector MenuFactory::get_volume_bitmaps() { - std::vector volume_bmps; + std::vector volume_bmps; volume_bmps.reserve(ADD_VOLUME_MENU_ITEMS.size()); for (auto item : ADD_VOLUME_MENU_ITEMS){ if(!item.second.empty()){ - volume_bmps.push_back(create_scaled_bitmap(item.second)); + //volume_bmps.push_back(create_menu_bitmap(item.second)); + volume_bmps.push_back(get_bmp_bundle(item.second)); } } return volume_bmps; @@ -619,7 +619,7 @@ wxMenuItem* MenuFactory::append_menu_item_settings(wxMenu* menu_) // Add full settings list auto menu_item = new wxMenuItem(menu, wxID_ANY, menu_name); - menu_item->SetBitmap(create_scaled_bitmap("cog")); + menu_item->SetBitmap(*get_bmp_bundle("cog")); menu_item->SetSubMenu(create_settings_popupmenu(menu, is_object_settings, item)); return menu->Append(menu_item); @@ -768,7 +768,7 @@ void MenuFactory::append_menu_item_change_extruder(wxMenu* menu) if (sels.IsEmpty()) return; - std::vector icons = get_extruder_color_icons(true); + std::vector icons = get_extruder_color_icons(true); wxMenu* extruder_selection_menu = new wxMenu(); const wxString& name = sels.Count() == 1 ? names[0] : names[1]; @@ -789,7 +789,7 @@ void MenuFactory::append_menu_item_change_extruder(wxMenu* menu) if (icon_idx >= 0 && icon_idx < icons.size()) { append_menu_item( - extruder_selection_menu, wxID_ANY, item_name, "", [i](wxCommandEvent &) { obj_list()->set_extruder_for_selected_items(i); }, *icons[icon_idx], menu, + extruder_selection_menu, wxID_ANY, item_name, "", [i](wxCommandEvent &) { obj_list()->set_extruder_for_selected_items(i); }, icons[icon_idx], menu, [is_active_extruder]() { return !is_active_extruder; }, m_parent); } else { append_menu_item( @@ -1593,7 +1593,7 @@ void MenuFactory::append_menu_item_change_filament(wxMenu* menu) return; } - std::vector icons = get_extruder_color_icons(true); + std::vector icons = get_extruder_color_icons(true); if (icons.size() < filaments_cnt) { BOOST_LOG_TRIVIAL(warning) << boost::format("Warning: icons size %1%, filaments_cnt=%2%")%icons.size()%filaments_cnt; if (icons.size() <= 1) @@ -1633,8 +1633,9 @@ void MenuFactory::append_menu_item_change_filament(wxMenu* menu) const wxString& item_name = (i == 0 ? _L("Default") : wxString::Format(_L("Filament %d"), i)) + (is_active_extruder ? " (" + _L("current") + ")" : ""); + //OcraftyoneTODO: determine if nullptr in place of icon causes issues append_menu_item(extruder_selection_menu, wxID_ANY, item_name, "", - [i](wxCommandEvent&) { obj_list()->set_extruder_for_selected_items(i); }, i == 0 ? wxNullBitmap : *icons[i - 1], menu, + [i](wxCommandEvent&) { obj_list()->set_extruder_for_selected_items(i); }, i == 0 ? nullptr : icons[i - 1], menu, [is_active_extruder]() { return !is_active_extruder; }, m_parent); } menu->Append(wxID_ANY, name, extruder_selection_menu, _L("Change Filament")); @@ -1752,12 +1753,6 @@ void MenuFactory::update_default_menu() create_default_menu(); } -void MenuFactory::msw_rescale() -{ - for (MenuWithSeparators* menu : { &m_object_menu, &m_sla_object_menu, &m_part_menu, &m_default_menu }) - msw_rescale_menu(dynamic_cast(menu)); -} - #ifdef _WIN32 // For this class is used code from stackoverflow: // https://stackoverflow.com/questions/257288/is-it-possible-to-write-a-template-to-check-for-a-functions-existence @@ -1787,7 +1782,7 @@ static void update_menu_item_def_colors(T* item) void MenuFactory::sys_color_changed() { for (MenuWithSeparators* menu : { &m_object_menu, &m_sla_object_menu, &m_part_menu, &m_default_menu }) { - msw_rescale_menu(dynamic_cast(menu));// msw_rescale_menu updates just icons, so use it + sys_color_changed_menu(dynamic_cast(menu));// msw_rescale_menu updates just icons, so use it #ifdef _WIN32 // but under MSW we have to update item's bachground color for (wxMenuItem* item : menu->GetMenuItems()) @@ -1802,14 +1797,17 @@ void MenuFactory::sys_color_changed(wxMenuBar* menubar) #if 0 for (size_t id = 0; id < menubar->GetMenuCount(); id++) { wxMenu* menu = menubar->GetMenu(id); - msw_rescale_menu(menu); + sys_color_changed_menu(menu); +#ifndef __linux__ + menu->SetupBitmaps(); #ifdef _WIN32 // but under MSW we have to update item's bachground color for (wxMenuItem* item : menu->GetMenuItems()) update_menu_item_def_colors(item); #endif } - menubar->Refresh(); +// menubar->Refresh(); +#endif #endif } diff --git a/src/slic3r/GUI/GUI_Factories.hpp b/src/slic3r/GUI/GUI_Factories.hpp index 92a816538f..8f4c9d6d0d 100644 --- a/src/slic3r/GUI/GUI_Factories.hpp +++ b/src/slic3r/GUI/GUI_Factories.hpp @@ -36,7 +36,7 @@ struct SettingsFactory static std::map> OBJECT_CATEGORY_SETTINGS; static std::map> PART_CATEGORY_SETTINGS; - static wxBitmap get_category_bitmap(const std::string& category_name, bool menu_bmp = true); + static wxBitmapBundle* get_category_bitmap(const std::string& category_name); static Bundle get_bundle(const DynamicPrintConfig* config, bool is_object_settings, bool is_layer_settings = false); static std::vector get_options(bool is_part); //BBS: add api to get options for catogary @@ -48,7 +48,7 @@ class MenuFactory { public: static const std::vector> ADD_VOLUME_MENU_ITEMS; - static std::vector get_volume_bitmaps(); + static std::vector get_volume_bitmaps(); MenuFactory(); ~MenuFactory() = default; @@ -57,7 +57,6 @@ public: void update(); void update_object_menu(); void update_default_menu(); - void msw_rescale(); void sys_color_changed(); static void sys_color_changed(wxMenuBar* menu_bar); diff --git a/src/slic3r/GUI/GUI_ObjectLayers.cpp b/src/slic3r/GUI/GUI_ObjectLayers.cpp index 76dd268633..cd1ff8ca28 100644 --- a/src/slic3r/GUI/GUI_ObjectLayers.cpp +++ b/src/slic3r/GUI/GUI_ObjectLayers.cpp @@ -248,47 +248,14 @@ void ObjectLayers::UpdateAndShow(const bool show) void ObjectLayers::msw_rescale() { - m_bmp_delete.msw_rescale(); - m_bmp_add.msw_rescale(); - - m_grid_sizer->SetHGap(wxGetApp().em_unit()); - - // rescale edit-boxes - const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount(); - for (int i = 0; i < cells_cnt; ++i) { - const wxSizerItem* item = m_grid_sizer->GetItem(i); - if (item->IsWindow()) { - LayerRangeEditor* editor = dynamic_cast(item->GetWindow()); - if (editor != nullptr) - editor->msw_rescale(); - } - else if (item->IsSizer()) // case when we have editor with buttons - { - wxSizerItem* e_item = item->GetSizer()->GetItem(size_t(0)); // editor - if (e_item->IsWindow()) { - LayerRangeEditor* editor = dynamic_cast(e_item->GetWindow()); - if (editor != nullptr) - editor->msw_rescale(); - } - - if (item->GetSizer()->GetItemCount() > 2) // if there are Add/Del buttons - for (size_t btn : {2, 3}) { // del_btn, add_btn - wxSizerItem* b_item = item->GetSizer()->GetItem(btn); - if (b_item->IsWindow()) { - auto button = dynamic_cast(b_item->GetWindow()); - if (button != nullptr) - button->msw_rescale(); - } - } - } - } + //Orca: deleted what PS commented out m_grid_sizer->Layout(); } void ObjectLayers::sys_color_changed() { - m_bmp_delete.msw_rescale(); - m_bmp_add.msw_rescale(); + m_bmp_delete.sys_color_changed(); + m_bmp_add.sys_color_changed(); // rescale edit-boxes const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount(); @@ -300,7 +267,7 @@ void ObjectLayers::sys_color_changed() if (b_item && b_item->IsWindow()) { auto button = dynamic_cast(b_item->GetWindow()); if (button != nullptr) - button->msw_rescale(); + button->sys_color_changed(); } } } diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 4677536bd3..7e088b435d 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1359,12 +1359,11 @@ void ObjectList::extruder_editing() if (!item || !(m_objects_model->GetItemType(item) & (itVolume | itObject))) return; - const int column_width = GetColumn(colFilament)->GetWidth() + wxSystemSettings::GetMetric(wxSYS_VSCROLL_X) + 5; - - wxPoint pos = this->get_mouse_position_in_control(); - wxSize size = wxSize(column_width, -1); - pos.x = GetColumn(colName)->GetWidth() + GetColumn(colPrint)->GetWidth() + 5; - pos.y -= GetTextExtent("m").y; + wxRect rect = this->GetItemRect(item, GetColumn(colFilament)); + wxPoint pos = rect.GetPosition(); + pos.y -= 4; + wxSize size = rect.GetSize(); + size.SetWidth(size.GetWidth() + 8); apply_extruder_selector(&m_extruder_editor, this, "1", pos, size); @@ -3196,6 +3195,21 @@ bool ObjectList::can_merge_to_single_object() const return (*m_objects)[obj_idx]->volumes.size() > 1; } +wxPoint ObjectList::get_mouse_position_in_control() const +{ + wxPoint pt = wxGetMousePosition() - this->GetScreenPosition(); + +#ifdef __APPLE__ + // Workaround for OSX. From wxWidgets 3.1.6 Hittest doesn't respect to the header of wxDataViewCtrl + if (wxDataViewItem top_item = this->GetTopItem(); top_item.IsOk()) { + auto rect = this->GetItemRect(top_item, this->GetColumn(0)); + pt.y -= rect.y; + } +#endif // __APPLE__ + + return pt; +} + bool ObjectList::can_mesh_boolean() const { int obj_idx = get_selected_obj_idx(); @@ -5505,17 +5519,17 @@ void ObjectList::msw_rescale() GetColumn(colSinking)->SetWidth(3 * em); GetColumn(colEditing )->SetWidth( 3 * em); - // rescale/update existing items with bitmaps - m_objects_model->Rescale(); - Layout(); } void ObjectList::sys_color_changed() { wxGetApp().UpdateDVCDarkUI(this, true); - - msw_rescale(); + + // rescale/update existing items with bitmaps + m_objects_model->UpdateBitmaps(); + + Layout(); if (m_objects_model) { m_objects_model->sys_color_changed(); } } @@ -5552,6 +5566,12 @@ void GUI::ObjectList::OnStartEditing(wxDataViewEvent &event) // Here the last active column is forgotten, so when leaving the editing mode, the next mouse click will not enter the editing mode of the newly selected column. void ObjectList::OnEditingStarted(wxDataViewEvent &event) { + // Orca: Automatically show drop down on editing start and finish editing when the combobox is closed + if (event.GetColumn() == colFilament) { + ::ComboBox*c = static_cast<::ComboBox *>(event.GetDataViewColumn()->GetRenderer()->GetEditorCtrl()); + c->ToggleDropDown(); + c->Bind(wxEVT_COMBOBOX_CLOSEUP, [event](wxCommandEvent& evt){ event.GetDataViewColumn()->GetRenderer()->FinishEditing(); }); + } #ifdef __WXMSW__ m_last_selected_column = -1; #else diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 0a95ecb415..b270b11638 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -321,7 +321,7 @@ public: void delete_all_connectors_for_selection(); void delete_all_connectors_for_object(int obj_idx); - wxPoint get_mouse_position_in_control() const { return wxGetMousePosition() - this->GetScreenPosition(); } + wxPoint get_mouse_position_in_control() const; int get_selected_obj_idx() const; ModelConfig& get_item_config(const wxDataViewItem& item) const; diff --git a/src/slic3r/GUI/GUI_ObjectSettings.cpp b/src/slic3r/GUI/GUI_ObjectSettings.cpp index ff9521b584..b0b59a1ac2 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.cpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.cpp @@ -112,7 +112,7 @@ bool ObjectSettings::update_settings_list() btn->SetToolTip(_(L("Remove parameter"))); btn->SetBitmapFocus(m_bmp_delete_focus.bmp()); - btn->SetBitmapHover(m_bmp_delete_focus.bmp()); + btn->SetBitmapCurrent(m_bmp_delete_focus.bmp()); btn->Bind(wxEVT_BUTTON, [opt_key, config, this](wxEvent &event) { wxGetApp().plater()->take_snapshot(from_u8((boost::format("Delete Option %s") % opt_key).str()).ToStdString()); @@ -146,7 +146,7 @@ bool ObjectSettings::update_settings_list() return; ctrl->SetBitmap_(m_bmp_delete); ctrl->SetBitmapFocus(m_bmp_delete_focus.bmp()); - ctrl->SetBitmapHover(m_bmp_delete_focus.bmp()); + ctrl->SetBitmapCurrent(m_bmp_delete_focus.bmp()); }; const bool is_extruders_cat = cat.first == "Extruders"; @@ -415,21 +415,13 @@ void ObjectSettings::UpdateAndShow(const bool show) #endif } -void ObjectSettings::msw_rescale() -{ -#if !NEW_OBJECT_SETTING - m_bmp_delete.msw_rescale(); - m_bmp_delete_focus.msw_rescale(); - - for (auto group : m_og_settings) - group->msw_rescale(); -#endif -} - void ObjectSettings::sys_color_changed() { #if !NEW_OBJECT_SETTING - m_og->sys_color_changed(); + m_og->sys_color_changed(); // not in old msw_rescale. is it needed? + // moved from old msw_rescale + m_bmp_delete.sys_color_changed(); + m_bmp_delete_focus.sys_color_changed(); for (auto group : m_og_settings) group->sys_color_changed(); diff --git a/src/slic3r/GUI/GUI_ObjectSettings.hpp b/src/slic3r/GUI/GUI_ObjectSettings.hpp index 8903f8748b..0e50901b2d 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.hpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.hpp @@ -68,7 +68,6 @@ public: bool add_missed_options(ModelConfig *config_to, const DynamicPrintConfig &config_from); void update_config_values(ModelConfig *config); void UpdateAndShow(const bool show); - void msw_rescale(); void sys_color_changed(); }; diff --git a/src/slic3r/GUI/GUI_ObjectTable.cpp b/src/slic3r/GUI/GUI_ObjectTable.cpp index eda7c15b71..c23c871395 100644 --- a/src/slic3r/GUI/GUI_ObjectTable.cpp +++ b/src/slic3r/GUI/GUI_ObjectTable.cpp @@ -77,7 +77,7 @@ void GridCellIconRenderer::Draw(wxGrid& grid, table->m_icon_row_height = grid.GetRowSize(row); table->m_icon_col_width = grid.GetColSize(col); //} - wxBitmap& bitmap = table->get_undo_bitmap(); + wxBitmap bitmap = table->get_undo_bitmap().GetBitmapFor(dc.GetWindow()); int bitmap_width = bitmap.GetWidth(); int bitmap_height = bitmap.GetHeight(); int offset_x = (table->m_icon_col_width - bitmap_width)/2; @@ -125,7 +125,7 @@ GridCellIconRenderer *GridCellIconRenderer::Clone() const GridCellFilamentsEditor::GridCellFilamentsEditor(const wxArrayString& choices, bool allowOthers, - std::vector* bitmaps) + std::vector* bitmaps) : wxGridCellChoiceEditor(choices, allowOthers), m_icons(bitmaps) { } @@ -133,7 +133,7 @@ GridCellFilamentsEditor::GridCellFilamentsEditor(const wxArrayString& choices, GridCellFilamentsEditor::GridCellFilamentsEditor(size_t count, const wxString choices[], bool allowOthers, - std::vector* bitmaps) + std::vector* bitmaps) : wxGridCellChoiceEditor(count, choices, allowOthers), m_icons(bitmaps) { } @@ -159,13 +159,14 @@ void GridCellFilamentsEditor::Create(wxWindow* parent, if ( !m_allowOthers ) style |= wxCB_READONLY; ::ComboBox *bitmap_combo = new ComboBox(parent, id, wxEmptyString, - wxDefaultPosition, wxSize(((*m_icons)[0])->GetWidth() + 10, -1), 0, nullptr, CB_NO_DROP_ICON | CB_NO_TEXT | wxCB_READONLY); + wxDefaultPosition, wxSize(get_preferred_size(*((*m_icons)[0]), wxGetApp().mainframe).GetWidth() + 10, -1), + 0, nullptr, CB_NO_DROP_ICON | CB_NO_TEXT | wxCB_READONLY); //Unsure if (m_icons) { int array_count = m_choices.GetCount(); int icon_count = m_icons->size(); for (int i = 0; i < array_count; i++) { - wxBitmap* bitmap = (i < icon_count) ? (*m_icons)[i] : (*m_icons)[0]; + wxBitmapBundle* bitmap = (i < icon_count) ? (*m_icons)[i] : (*m_icons)[0]; bitmap_combo->Append(m_choices[i], *bitmap); } } @@ -239,6 +240,9 @@ void GridCellFilamentsEditor::BeginEdit(int row, int col, wxGrid* grid) Combo()->SetFocus(); + // Orca: Show dropdown on editing start + Combo()->ToggleDropDown(); + #ifdef __WXOSX_COCOA__ // This is a work around for the combobox being simply dismissed when a // choice is made in it under OS X. The bug is almost certainly due to a @@ -335,9 +339,9 @@ void GridCellFilamentsRenderer::Draw(wxGrid &grid, wxGridCellAttr &attr, wxDC &d ObjectGridTable::ObjectGridRow *grid_row = table->get_grid_row(row - 1); ConfigOptionInt & cur_option = dynamic_cast((*grid_row)[(ObjectGridTable::GridColType) col]); - wxBitmap *bitmap = table->get_color_bitmap((cur_option.value >= 1) ? cur_option.value - 1 : cur_option.value); - int bitmap_width = bitmap->GetWidth(); - int bitmap_height = bitmap->GetHeight(); + wxBitmapBundle *bitmap = table->get_color_bitmap((cur_option.value >= 1) ? cur_option.value - 1 : cur_option.value); + int bitmap_width = bitmap->GetBitmapFor(dc.GetWindow()).GetWidth(); + int bitmap_height = bitmap->GetBitmapFor(dc.GetWindow()).GetHeight(); int offset_x = grid_cell_border_width; int offset_y = (rect.height > bitmap_height) ? (rect.height - bitmap_height) / 2 : grid_cell_border_height; @@ -345,7 +349,7 @@ void GridCellFilamentsRenderer::Draw(wxGrid &grid, wxGridCellAttr &attr, wxDC &d dc.SetBrush(wxBrush(attr.GetBackgroundColour())); dc.DrawRectangle(rect); if ( grid_row->model_volume_type != ModelVolumeType::NEGATIVE_VOLUME) { - dc.DrawBitmap(*bitmap, wxPoint(rect.x + offset_x, rect.y + offset_y)); + dc.DrawBitmap(bitmap->GetBitmapFor(dc.GetWindow()), wxPoint(rect.x + offset_x, rect.y + offset_y));//TODO: determine if this way of getting bitmap works well } text_rect.x += bitmap_width + grid_cell_border_width * 2; @@ -431,6 +435,9 @@ void GridCellChoiceEditor::BeginEdit(int row, int col, wxGrid *grid) Combo()->SetFocus(); + // Orca: Show dropdown on editing start + Combo()->ToggleDropDown(); + #ifdef __WXOSX_COCOA__ // This is a work around for the combobox being simply dismissed when a // choice is made in it under OS X. The bug is almost certainly due to a @@ -518,16 +525,16 @@ void GridCellComboBoxRenderer::Draw(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc ObjectGridTable::ObjectGridRow *grid_row = table->get_grid_row(row - 1); ConfigOptionInt & cur_option = dynamic_cast((*grid_row)[(ObjectGridTable::GridColType) col]); - wxBitmap *bitmap = table->get_color_bitmap((cur_option.value >= 1) ? cur_option.value - 1 : cur_option.value); - int bitmap_width = bitmap->GetWidth(); - int bitmap_height = bitmap->GetHeight(); + wxBitmapBundle *bitmap = table->get_color_bitmap((cur_option.value >= 1) ? cur_option.value - 1 : cur_option.value); + int bitmap_width = bitmap->GetBitmapFor(dc.GetWindow()).GetWidth(); + int bitmap_height = bitmap->GetBitmapFor(dc.GetWindow()).GetHeight(); int offset_x = grid_cell_border_width; int offset_y = (rect.height > bitmap_height) ? (rect.height - bitmap_height) / 2 : grid_cell_border_height; dc.SetPen(*wxTRANSPARENT_PEN); dc.SetBrush(wxBrush(attr.GetBackgroundColour())); dc.DrawRectangle(rect); - dc.DrawBitmap(*bitmap, wxPoint(rect.x + offset_x, rect.y + offset_y)); + dc.DrawBitmap(bitmap->GetBitmapFor(dc.GetWindow()), wxPoint(rect.x + offset_x, rect.y + offset_y)); text_rect.x += bitmap_width + grid_cell_border_width * 2; text_rect.width -= (bitmap_width + grid_cell_border_width * 2); } @@ -2641,12 +2648,12 @@ void ObjectGridTable::OnCellValueChanged(int row, int col) } } -wxBitmap& ObjectGridTable::get_undo_bitmap(bool selected) +wxBitmapBundle& ObjectGridTable::get_undo_bitmap(bool selected) { return m_panel->m_undo_bitmap; } -wxBitmap* ObjectGridTable::get_color_bitmap(int color_index) +wxBitmapBundle* ObjectGridTable::get_color_bitmap(int color_index) { if (color_index < m_panel->m_color_bitmaps.size()) return m_panel->m_color_bitmaps[color_index]; diff --git a/src/slic3r/GUI/GUI_ObjectTable.hpp b/src/slic3r/GUI/GUI_ObjectTable.hpp index a21436fe0e..6aca182533 100644 --- a/src/slic3r/GUI/GUI_ObjectTable.hpp +++ b/src/slic3r/GUI/GUI_ObjectTable.hpp @@ -82,10 +82,10 @@ public: GridCellFilamentsEditor(size_t count = 0, const wxString choices[] = NULL, bool allowOthers = false, - std::vector* bitmaps = NULL); + std::vector* bitmaps = NULL); GridCellFilamentsEditor(const wxArrayString& choices, bool allowOthers = false, - std::vector* bitmaps = NULL); + std::vector* bitmaps = NULL); virtual void Create(wxWindow* parent, wxWindowID id, @@ -105,7 +105,7 @@ protected: ::ComboBox *Combo() const { return (::ComboBox *)m_control; } void OnComboCloseUp(wxCommandEvent& evt); - std::vector* m_icons; + std::vector* m_icons; wxDECLARE_NO_COPY_CLASS(GridCellFilamentsEditor); private: @@ -498,8 +498,8 @@ public: void update_filament_to_config(ModelConfig* config, std::string& key, ConfigOption& new_value, ConfigOption& ori_value, bool is_object); void update_volume_values_from_object(int row, int col); void update_value_to_object(Model* model, ObjectGridRow* grid_row, int col); - wxBitmap& get_undo_bitmap(bool selected = false); - wxBitmap* get_color_bitmap(int color_index); + wxBitmapBundle& get_undo_bitmap(bool selected = false); + wxBitmapBundle* get_color_bitmap(int color_index); bool OnCellLeftClick(int row, int col, ConfigOptionType &type); void OnSelectCell(int row, int col); void OnRangeSelected(int row, int col, int row_count, int col_count); @@ -610,10 +610,10 @@ private: int init_filaments_and_colors(); wxFloatingPointValidator m_float_validator; - wxBitmap m_undo_bitmap; - std::vector m_color_bitmaps; - ScalableBitmap m_bmp_reset; - ScalableBitmap m_bmp_reset_disable; + wxBitmapBundle m_undo_bitmap; + std::vector m_color_bitmaps; + wxBitmapBundle m_bmp_reset; + wxBitmapBundle m_bmp_reset_disable; private: wxDECLARE_ABSTRACT_CLASS(ObjectGrid); wxDECLARE_EVENT_TABLE(); diff --git a/src/slic3r/GUI/GUI_ObjectTableSettings.cpp b/src/slic3r/GUI/GUI_ObjectTableSettings.cpp index 7468d9c1b4..b3508c5aaa 100644 --- a/src/slic3r/GUI/GUI_ObjectTableSettings.cpp +++ b/src/slic3r/GUI/GUI_ObjectTableSettings.cpp @@ -173,7 +173,7 @@ bool ObjectTableSettings::update_settings_list(bool is_object, bool is_multiple_ btn->SetBitmapFocus(m_bmp_reset_focus.bmp()); - btn->SetBitmapHover(m_bmp_reset_focus.bmp()); + btn->SetBitmapHover(m_bmp_reset_focus.get_bitmap()); #ifdef __WINDOWS__ btn->SetBitmapDisabled(m_bmp_reset_disable.bmp()); @@ -236,7 +236,7 @@ bool ObjectTableSettings::update_settings_list(bool is_object, bool is_multiple_ return; ctrl->SetBitmap_(m_bmp_reset); ctrl->SetBitmapFocus(m_bmp_reset_focus.bmp()); - ctrl->SetBitmapHover(m_bmp_reset_focus.bmp()); + ctrl->SetBitmapHover(m_bmp_reset_focus.get_bitmap()); #ifdef __WINDOWS__ ctrl->SetBitmapDisabled(m_bmp_reset_disable.bmp()); #endif diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 7622f35522..835e718bd1 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -43,9 +43,8 @@ #include "GUI_App.hpp" #include "../Utils/MacDarkMode.hpp" - -#include "nanosvg/nanosvg.h" -#include "nanosvg/nanosvgrast.h" +#include +#include #include "OpenGLManager.hpp" #include "GUI_App.hpp" diff --git a/src/slic3r/GUI/ImageGrid.cpp b/src/slic3r/GUI/ImageGrid.cpp index 2ab6748f4e..9fef6720ce 100644 --- a/src/slic3r/GUI/ImageGrid.cpp +++ b/src/slic3r/GUI/ImageGrid.cpp @@ -508,10 +508,10 @@ void ImageGrid::render(wxDC& dc) if (!m_file_sys || m_file_sys->GetCount() == 0) { dc.DrawRectangle({ 0, 0, size.x, size.y }); if (!m_status_msg.IsEmpty()) { - auto si = m_status_icon.GetBmpSize(); + auto si = m_status_icon.GetSize(); auto st = dc.GetTextExtent(m_status_msg); auto rect = wxRect{0, 0, max(st.x, si.x), si.y + 26 + st.y}.CenterIn(wxRect({0, 0}, size)); - dc.DrawBitmap(m_status_icon.bmp(), rect.x + (rect.width - si.x) / 2, rect.y); + dc.DrawBitmap(m_status_icon.get_bitmap(), rect.x + (rect.width - si.x) / 2, rect.y); dc.SetTextForeground(wxColor(0x909090)); dc.DrawText(m_status_msg, rect.x + (rect.width - st.x) / 2, rect.GetBottom() - st.y); } @@ -602,7 +602,7 @@ void Slic3r::GUI::ImageGrid::renderContent1(wxDC &dc, wxPoint const &pt, int ind bool show_download_state_always = true; // Draw checked icon if (m_selecting && !show_download_state_always) - dc.DrawBitmap(selected ? m_checked_icon.bmp() : m_unchecked_icon.bmp(), pt + wxPoint{10, 10}); + dc.DrawBitmap(selected ? m_checked_icon.get_bitmap() : m_unchecked_icon.get_bitmap(), pt + wxPoint{10, m_content_rect.GetHeight() - m_checked_icon.GetHeight() - 10}); // can't handle alpha // dc.GradientFillLinear({pt.x, pt.y, m_border_size.GetWidth(), 60}, wxColour(0x6F, 0x6F, 0x6F, 0x99), wxColour(0x6F, 0x6F, 0x6F, 0), wxBOTTOM); else if (m_file_sys->GetGroupMode() == PrinterFileSystem::G_NONE) { @@ -653,7 +653,7 @@ void Slic3r::GUI::ImageGrid::renderContent1(wxDC &dc, wxPoint const &pt, int ind dc.DrawText(date, pt + wxPoint{24, 16}); } if (m_selecting && show_download_state_always) - dc.DrawBitmap(selected ? m_checked_icon.bmp() : m_unchecked_icon.bmp(), pt + wxPoint{10, 10}); + dc.DrawBitmap(selected ? m_checked_icon.get_bitmap() : m_unchecked_icon.get_bitmap(), pt + wxPoint{10, m_content_rect.GetHeight() - m_checked_icon.GetHeight() - 10}); } void Slic3r::GUI::ImageGrid::renderContent2(wxDC &dc, wxPoint const &pt, int index, bool hit) @@ -745,8 +745,8 @@ void Slic3r::GUI::ImageGrid::renderText2(wxDC &dc, wxString text, wxRect const & void Slic3r::GUI::ImageGrid::renderIconText(wxDC & dc, ScalableBitmap const & icon, wxString text, wxRect const & rect) { - dc.DrawBitmap(icon.bmp(), rect.x, rect.y + (rect.height - icon.GetBmpHeight()) / 2); - renderText2(dc, text, {rect.x + icon.GetBmpWidth() + 4, rect.y, rect.width - icon.GetBmpWidth() - 4, rect.height}); + dc.DrawBitmap(icon.get_bitmap(), rect.x, rect.y + (rect.height - icon.GetHeight()) / 2); + renderText2(dc, text, {rect.x + icon.GetWidth() + 4, rect.y, rect.width - icon.GetWidth() - 4, rect.height}); } }} diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 0f065ff28f..fcd7e5e9fb 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -155,7 +155,7 @@ wxWindow *KBShortcutsDialog::create_button(int id, wxString text) void KBShortcutsDialog::on_dpi_changed(const wxRect& suggested_rect) { - m_logo_bmp.msw_rescale(); + m_logo_bmp.sys_color_changed(); m_header_bitmap->SetBitmap(m_logo_bmp.bmp()); msw_buttons_rescale(this, em_unit(), { wxID_OK }); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index cda31f9790..8223ca0823 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1968,11 +1968,6 @@ void MainFrame::on_dpi_changed(const wxRect& suggested_rect) m_monitor->msw_rescale(); m_calibration->msw_rescale(); - // BBS -#if 0 - for (size_t id = 0; id < m_menubar->GetMenuCount(); id++) - msw_rescale_menu(m_menubar->GetMenu(id)); -#endif // Workarounds for correct Window rendering after rescale @@ -2014,7 +2009,7 @@ void MainFrame::on_sys_color_changed() #ifdef _MSW_DARK_MODE // update common mode sizer if (!wxGetApp().tabs_as_menu()) - dynamic_cast(m_tabpanel)->Rescale(); + dynamic_cast(m_tabpanel)->OnColorsChanged(); #endif #endif diff --git a/src/slic3r/GUI/MediaFilePanel.cpp b/src/slic3r/GUI/MediaFilePanel.cpp index dded21372b..4c85725d3f 100644 --- a/src/slic3r/GUI/MediaFilePanel.cpp +++ b/src/slic3r/GUI/MediaFilePanel.cpp @@ -354,9 +354,9 @@ void MediaFilePanel::SwitchStorage(bool external) void MediaFilePanel::Rescale() { - m_bmp_loading.msw_rescale(); - m_bmp_failed.msw_rescale(); - m_bmp_empty.msw_rescale(); + m_bmp_loading.sys_color_changed(); + m_bmp_failed.sys_color_changed(); + m_bmp_empty.sys_color_changed(); auto top_sizer = GetSizer()->GetItem((size_t) 0)->GetSizer(); top_sizer->SetMinSize({-1, 75 * em_unit(this) / 10}); diff --git a/src/slic3r/GUI/ModelMall.cpp b/src/slic3r/GUI/ModelMall.cpp index f14de1ebf0..83c05bf544 100644 --- a/src/slic3r/GUI/ModelMall.cpp +++ b/src/slic3r/GUI/ModelMall.cpp @@ -36,7 +36,7 @@ namespace GUI { wxBoxSizer* m_sizer_web_control = new wxBoxSizer(wxHORIZONTAL); - auto m_control_back = new ScalableButton(m_web_control_panel, wxID_ANY, "mall_control_back", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true); + auto m_control_back = new ScalableButton(m_web_control_panel, wxID_ANY, "mall_control_back", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER); m_control_back->SetBackgroundColour(*wxWHITE); m_control_back->SetSize(wxSize(FromDIP(25), FromDIP(30))); m_control_back->SetMinSize(wxSize(FromDIP(25), FromDIP(30))); @@ -47,7 +47,7 @@ namespace GUI { m_control_back->Bind(wxEVT_LEAVE_WINDOW, [this](auto& e) {SetCursor(wxCursor(wxCURSOR_ARROW));}); - auto m_control_forward = new ScalableButton(m_web_control_panel, wxID_ANY, "mall_control_forward", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true); + auto m_control_forward = new ScalableButton(m_web_control_panel, wxID_ANY, "mall_control_forward", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER); m_control_forward->SetBackgroundColour(*wxWHITE); m_control_forward->SetSize(wxSize(FromDIP(25), FromDIP(30))); m_control_forward->SetMinSize(wxSize(FromDIP(25), FromDIP(30))); @@ -57,7 +57,7 @@ namespace GUI { m_control_forward->Bind(wxEVT_ENTER_WINDOW, [this](auto& e) {SetCursor(wxCursor(wxCURSOR_HAND)); }); m_control_forward->Bind(wxEVT_LEAVE_WINDOW, [this](auto& e) {SetCursor(wxCursor(wxCURSOR_ARROW)); }); - auto m_control_refresh = new ScalableButton(m_web_control_panel, wxID_ANY, "mall_control_refresh", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true); + auto m_control_refresh = new ScalableButton(m_web_control_panel, wxID_ANY, "mall_control_refresh", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER); m_control_refresh->SetBackgroundColour(*wxWHITE); m_control_refresh->SetSize(wxSize(FromDIP(25), FromDIP(30))); m_control_refresh->SetMinSize(wxSize(FromDIP(25), FromDIP(30))); diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index ed323417aa..d685ad3a81 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -227,10 +227,10 @@ void MsgDialog::apply_style(long style) if (style & wxNO) add_button(wxID_NO, false,_L("No")); if (style & wxCANCEL) add_button(wxID_CANCEL, false, _L("Cancel")); - logo->SetBitmap( create_scaled_bitmap(style & wxAPPLY ? "completed" : + logo->SetBitmap( *get_bmp_bundle(style & wxAPPLY ? "completed" : style & wxICON_WARNING ? "obj_warning" : style & wxICON_INFORMATION ? "info" : - style & wxICON_QUESTION ? "question" : "OrcaSlicer", this, 64, style & wxICON_ERROR)); + style & wxICON_QUESTION ? "question" : "OrcaSlicer", 64)); } void MsgDialog::finalize() @@ -339,7 +339,7 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg, bool monospaced_ add_msg_content(this, content_sizer, msg, monospaced_font); // Use a small bitmap with monospaced font, as the error text will not be wrapped. - logo->SetBitmap(create_scaled_bitmap("OrcaSlicer_192px_grayscale.png", this, monospaced_font ? 48 : /*1*/84)); + logo->SetBitmap(*get_bmp_bundle("OrcaSlicer_192px_grayscale.png", monospaced_font ? 48 : /*1*/84)); SetMaxSize(wxSize(-1, CONTENT_MAX_HEIGHT*wxGetApp().em_unit())); diff --git a/src/slic3r/GUI/Notebook.cpp b/src/slic3r/GUI/Notebook.cpp index 84bbf9950a..aac7ee5af3 100644 --- a/src/slic3r/GUI/Notebook.cpp +++ b/src/slic3r/GUI/Notebook.cpp @@ -122,6 +122,8 @@ void ButtonsListCtrl::Rescale() { //m_mode_sizer->msw_rescale(); int em = em_unit(this); + // Orca: following is removed by PS in wx 3.16 refactor. + // doesn't seem to be doing anything rn so leaving it alone for (Button* btn : m_pageButtons) { //BBS btn->SetMinSize({(btn->GetLabel().empty() ? 40 : 132) * em / 10, 36 * em / 10}); @@ -137,6 +139,14 @@ void ButtonsListCtrl::Rescale() m_sizer->Layout(); } +void ButtonsListCtrl::OnColorsChanged() +{ + for (Button* btn : m_pageButtons) + btn->Rescale(); + + m_sizer->Layout(); +} + void ButtonsListCtrl::SetSelection(int sel) { if (m_selection == sel) diff --git a/src/slic3r/GUI/Notebook.hpp b/src/slic3r/GUI/Notebook.hpp index d75898a7cd..482a909fe1 100644 --- a/src/slic3r/GUI/Notebook.hpp +++ b/src/slic3r/GUI/Notebook.hpp @@ -23,6 +23,7 @@ public: void SetSelection(int sel); void UpdateMode(); void Rescale(); + void OnColorsChanged(); bool InsertPage(size_t n, const wxString &text, bool bSelect = false, const std::string &bmp_name = "", const std::string &inactive_bmp_name = ""); void RemovePage(size_t n); bool SetPageImage(size_t n, const std::string& bmp_name) const; @@ -261,6 +262,11 @@ public: GetBtnsListCtrl()->Rescale(); } + void OnColorsChanged() + { + GetBtnsListCtrl()->OnColorsChanged(); + } + void OnNavigationKey(wxNavigationKeyEvent& event) { if (event.IsWindowChange()) { diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index 2aef316b48..26349192d1 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.cpp +++ b/src/slic3r/GUI/OG_CustomCtrl.cpp @@ -28,12 +28,12 @@ static bool is_point_in_rect(const wxPoint& pt, const wxRect& rect) rect.GetTop() <= pt.y && pt.y <= rect.GetBottom(); } -static wxSize get_bitmap_size(const wxBitmap& bmp) +static wxSize get_bitmap_size(const wxBitmapBundle* bmp, wxWindow* parent) { -#ifdef __APPLE__ - return bmp.GetScaledSize(); +#ifndef __WIN32__ + return bmp->GetBitmapFor(parent).GetSize(); #else - return bmp.GetSize(); + return bmp->GetDefaultSize(); #endif } @@ -58,8 +58,8 @@ OG_CustomCtrl::OG_CustomCtrl( wxWindow* parent, m_v_gap2 = lround(0.8 * m_em_unit); m_h_gap = lround(0.2 * m_em_unit); - //m_bmp_mode_sz = get_bitmap_size(create_scaled_bitmap("mode_simple", this, wxOSX ? 10 : 12)); - m_bmp_blinking_sz = get_bitmap_size(create_scaled_bitmap("blank_16", this)); + //m_bmp_mode_sz = get_bitmap_size(get_bmp_bundle("mode_simple", wxOSX ? 10 : 12), this); + m_bmp_blinking_sz = get_bitmap_size(get_bmp_bundle("blank_16"), this); init_ctrl_lines();// from og.lines() @@ -572,8 +572,8 @@ void OG_CustomCtrl::msw_rescale() m_v_gap2 = lround(0.8 * m_em_unit); m_h_gap = lround(0.2 * m_em_unit); - //m_bmp_mode_sz = create_scaled_bitmap("mode_simple", this, wxOSX ? 10 : 12).GetSize(); - m_bmp_blinking_sz = create_scaled_bitmap("blank_16", this).GetSize(); + //m_bmp_mode_sz = get_bitmap_size(get_bmp_bundle("mode_simple", wxOSX ? 10 : 12), this); + m_bmp_blinking_sz = get_bitmap_size(get_bmp_bundle("blank_16"), this); m_max_win_width = 0; @@ -666,7 +666,7 @@ void OG_CustomCtrl::CtrlLine::msw_rescale() { // if we have a single option with no label, no sidetext if (draw_just_act_buttons) - height = get_bitmap_size(create_scaled_bitmap("empty")).GetHeight(); + height = get_bitmap_size(get_bmp_bundle("empty"), ctrl).GetHeight(); if (ctrl->opt_group->label_width != 0 && !og_line.label.IsEmpty()) { wxSize label_sz = ctrl->GetTextExtent(og_line.label); @@ -748,7 +748,7 @@ void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord h_pos, wxCoord v_pos) if (field && field->undo_bitmap()) //if (field) // BBS: new layout - draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap()->bmp(), field->undo_bitmap()->bmp(), field->blink()); + draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap()->get_bitmap(), field->undo_bitmap()->get_bitmap(), field->blink()); return; } @@ -802,7 +802,7 @@ void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord h_pos, wxCoord v_pos) auto draw_buttons = [&h_pos, &dc, &v_pos, this](Field* field, size_t bmp_rect_id = 0) { if (field && field->undo_to_sys_bitmap()) { - h_pos = draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap()->bmp(), field->undo_bitmap()->bmp(), field->blink(), bmp_rect_id); + h_pos = draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap()->get_bitmap(), field->undo_bitmap()->get_bitmap(), field->blink(), bmp_rect_id); } #ifndef DISABLE_BLINKING else if (field && !field->undo_to_sys_bitmap() && field->blink()) @@ -933,19 +933,19 @@ wxCoord OG_CustomCtrl::CtrlLine::draw_text(wxDC &dc, wxPoint pos, const wxString wxPoint OG_CustomCtrl::CtrlLine::draw_blinking_bmp(wxDC& dc, wxPoint pos, bool is_blinking) { - wxBitmap bmp_blinking = create_scaled_bitmap(is_blinking ? "blank_16" : "empty", ctrl); + wxBitmapBundle* bmp_blinking = get_bmp_bundle(is_blinking ? "blank_16" : "empty"); wxCoord h_pos = pos.x; - wxCoord v_pos = pos.y + lround((height - get_bitmap_size(bmp_blinking).GetHeight()) / 2); + wxCoord v_pos = pos.y + lround((height - get_bitmap_size(bmp_blinking, ctrl).GetHeight()) / 2); - dc.DrawBitmap(bmp_blinking, h_pos, v_pos); + dc.DrawBitmap(bmp_blinking->GetBitmapFor(ctrl), h_pos, v_pos); - int bmp_dim = get_bitmap_size(bmp_blinking).GetWidth(); + int bmp_dim = get_bitmap_size(bmp_blinking, ctrl).GetWidth(); h_pos += bmp_dim + ctrl->m_h_gap; return wxPoint(h_pos, v_pos); } -wxCoord OG_CustomCtrl::CtrlLine::draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmap& bmp_undo_to_sys, const wxBitmap& bmp_undo, bool is_blinking, size_t rect_id) +wxCoord OG_CustomCtrl::CtrlLine::draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmapBundle& bmp_undo_to_sys, const wxBitmapBundle& bmp_undo, bool is_blinking, size_t rect_id) { #ifndef DISABLE_BLINKING pos = draw_blinking_bmp(dc, pos, is_blinking); @@ -953,11 +953,11 @@ wxCoord OG_CustomCtrl::CtrlLine::draw_act_bmps(wxDC& dc, wxPoint pos, const wxBi if (ctrl->opt_group->split_multi_line) { // BBS const std::vector