diff options
author | B Stack <bgstack15@gmail.com> | 2018-09-10 02:46:25 +0000 |
---|---|---|
committer | B Stack <bgstack15@gmail.com> | 2018-09-10 02:46:25 +0000 |
commit | 728d32e6da9ce66968f8eef47a59505d613e2c1b (patch) | |
tree | 0f0441755ff0e6d65e12222d4502c648bffd6a7c /wx+ | |
parent | 10.3 (diff) | |
parent | pull in latest 10.4 from upstream (diff) | |
download | FreeFileSync-728d32e6da9ce66968f8eef47a59505d613e2c1b.tar.gz FreeFileSync-728d32e6da9ce66968f8eef47a59505d613e2c1b.tar.bz2 FreeFileSync-728d32e6da9ce66968f8eef47a59505d613e2c1b.zip |
Merge branch '10.4' into 'master'10.4
pull in latest 10.4 from upstream
See merge request opensource-tracking/FreeFileSync!1
Diffstat (limited to 'wx+')
-rwxr-xr-x | wx+/choice_enum.h | 4 | ||||
-rwxr-xr-x | wx+/dc.h | 11 | ||||
-rwxr-xr-x | wx+/focus.h | 29 | ||||
-rwxr-xr-x | wx+/graph.cpp | 4 | ||||
-rwxr-xr-x | wx+/graph.h | 29 | ||||
-rwxr-xr-x | wx+/grid.cpp | 56 | ||||
-rwxr-xr-x | wx+/grid.h | 27 | ||||
-rwxr-xr-x | wx+/image_resources.cpp | 4 | ||||
-rwxr-xr-x | wx+/image_tools.cpp | 149 | ||||
-rwxr-xr-x | wx+/image_tools.h | 2 | ||||
-rwxr-xr-x | wx+/rtl.h | 5 |
11 files changed, 158 insertions, 162 deletions
diff --git a/wx+/choice_enum.h b/wx+/choice_enum.h index 97c40b68..2c424b9f 100755 --- a/wx+/choice_enum.h +++ b/wx+/choice_enum.h @@ -77,7 +77,9 @@ void setEnumVal(const EnumDescrList<Enum>& mapping, wxChoice& ctrl, Enum value) { selectedPos = it - mapping.descrList.begin(); - if (!it->second.second.empty()) + if (it->second.second.empty()) + ctrl.UnsetToolTip(); + else ctrl.SetToolTip(it->second.second); } } @@ -8,7 +8,6 @@ #define DC_H_4987123956832143243214 #include <unordered_map> -#include <zen/optional.h> #include <zen/basic_math.h> #include <wx/dcbuffer.h> //for macro: wxALWAYS_NATIVE_DOUBLE_BUFFER #include <wx/dcscreen.h> @@ -104,7 +103,7 @@ private: //associate "active" clipping area with each DC static std::unordered_map<wxDC*, wxRect>& refDcToAreaMap() { static std::unordered_map<wxDC*, wxRect> clippingAreas; return clippingAreas; } - Opt<wxRect> oldRect_; + std::optional<wxRect> oldRect_; wxDC& dc_; }; @@ -114,13 +113,13 @@ private: #endif #if wxALWAYS_NATIVE_DOUBLE_BUFFER -struct BufferedPaintDC : public wxPaintDC { BufferedPaintDC(wxWindow& wnd, Opt<wxBitmap>& buffer) : wxPaintDC(&wnd) {} }; +struct BufferedPaintDC : public wxPaintDC { BufferedPaintDC(wxWindow& wnd, std::optional<wxBitmap>& buffer) : wxPaintDC(&wnd) {} }; #else class BufferedPaintDC : public wxMemoryDC { public: - BufferedPaintDC(wxWindow& wnd, Opt<wxBitmap>& buffer) : buffer_(buffer), paintDc_(&wnd) + BufferedPaintDC(wxWindow& wnd, std::optional<wxBitmap>& buffer) : buffer_(buffer), paintDc_(&wnd) { const wxSize clientSize = wnd.GetClientSize(); if (clientSize.GetWidth() > 0 && clientSize.GetHeight() > 0) //wxBitmap asserts this!! width may be 0; test case "Grid::CornerWin": compare both sides, then change config @@ -134,7 +133,7 @@ public: SetLayoutDirection(wxLayout_RightToLeft); } else - buffer = NoValue(); + buffer = {}; } ~BufferedPaintDC() @@ -153,7 +152,7 @@ public: } private: - Opt<wxBitmap>& buffer_; + std::optional<wxBitmap>& buffer_; wxPaintDC paintDc_; }; #endif diff --git a/wx+/focus.h b/wx+/focus.h index cd99d010..e2daef79 100755 --- a/wx+/focus.h +++ b/wx+/focus.h @@ -44,22 +44,37 @@ Preserving input focus has to be more clever than: */ struct FocusPreserver { + FocusPreserver() + { + if (wxWindow* win = wxWindow::FindFocus()) + setFocus(win); + } + ~FocusPreserver() { //wxTopLevelWindow::IsActive() does NOT call Win32 ::GetActiveWindow()! //Instead it checks if ::GetFocus() is set somewhere inside the top level //Note: Both Win32 active and focus windows are *thread-local* values, while foreground window is global! https://blogs.msdn.microsoft.com/oldnewthing/20131016-00/?p=2913 - if (oldFocus_) - if (wxTopLevelWindow* topWin = getTopLevelWindow(oldFocus_)) - if (topWin->IsActive()) //Linux/macOS: already behaves just like ::GetForegroundWindow() on Windows! - oldFocus_->SetFocus(); + + if (oldFocusId_ != wxID_ANY) + if (wxWindow* oldFocusWin = wxWindow::FindWindowById(oldFocusId_)) + if (wxTopLevelWindow* topWin = getTopLevelWindow(oldFocusWin)) + if (topWin->IsActive()) //Linux/macOS: already behaves just like ::GetForegroundWindow() on Windows! + oldFocusWin->SetFocus(); } - wxWindow* getFocus() const { return oldFocus_; } - void setFocus(wxWindow* win) { oldFocus_ = win; } + wxWindowID getFocusId() const { return oldFocusId_; } + + void setFocus(wxWindow* win) + { + oldFocusId_ = win->GetId(); + assert(oldFocusId_ != wxID_ANY); + } private: - wxWindow* oldFocus_ = wxWindow::FindFocus(); + wxWindowID oldFocusId_ = wxID_ANY; + //don't store wxWindow* which may be dangling during ~FocusPreserver()! + //test: click on delete folder pair and immediately press F5 => focus window (= FP del button) is defer-deleted during sync }; } diff --git a/wx+/graph.cpp b/wx+/graph.cpp index b006cda0..38846523 100755 --- a/wx+/graph.cpp +++ b/wx+/graph.cpp @@ -387,8 +387,8 @@ std::vector<CurvePoint> SparseCurveData::getPoints(double minX, double maxX, con for (int i = posFrom; i <= posTo; ++i) { const double x = cvrtX.screenToReal(i); - Opt<CurvePoint> ptLe = getLessEq(x); - Opt<CurvePoint> ptGe = getGreaterEq(x); + std::optional<CurvePoint> ptLe = getLessEq(x); + std::optional<CurvePoint> ptGe = getGreaterEq(x); //both non-existent and invalid return values are mapped to out of expected range: => check on posLe/posGe NOT ptLe/ptGe in the following! const int posLe = ptLe ? cvrtX.realToScreenRound(ptLe->x) : i + 1; const int posGe = ptGe ? cvrtX.realToScreenRound(ptGe->x) : i - 1; diff --git a/wx+/graph.h b/wx+/graph.h index e0c2c12b..f1ae5d5a 100755 --- a/wx+/graph.h +++ b/wx+/graph.h @@ -14,7 +14,6 @@ #include <wx/settings.h> #include <wx/bitmap.h> #include <zen/string_tools.h> -#include <zen/optional.h> //elegant 2D graph as wxPanel specialization @@ -63,8 +62,8 @@ struct SparseCurveData : public CurveData { SparseCurveData(bool addSteps = false) : addSteps_(addSteps) {} //addSteps: add points to get a staircase effect or connect points via a direct line - virtual Opt<CurvePoint> getLessEq (double x) const = 0; - virtual Opt<CurvePoint> getGreaterEq(double x) const = 0; + virtual std::optional<CurvePoint> getLessEq (double x) const = 0; + virtual std::optional<CurvePoint> getGreaterEq(double x) const = 0; private: std::vector<CurvePoint> getPoints(double minX, double maxX, const wxSize& areaSizePx) const override; @@ -80,21 +79,21 @@ struct ArrayCurveData : public SparseCurveData private: std::pair<double, double> getRangeX() const override { const size_t sz = getSize(); return { 0.0, sz == 0 ? 0.0 : sz - 1.0}; } - Opt<CurvePoint> getLessEq(double x) const override + std::optional<CurvePoint> getLessEq(double x) const override { const size_t sz = getSize(); const size_t pos = std::min<ptrdiff_t>(std::floor(x), sz - 1); //[!] expect unsigned underflow if empty! if (pos < sz) return CurvePoint(pos, getValue(pos)); - return NoValue(); + return {}; } - Opt<CurvePoint> getGreaterEq(double x) const override + std::optional<CurvePoint> getGreaterEq(double x) const override { const size_t pos = std::max<ptrdiff_t>(std::ceil(x), 0); //[!] use std::max with signed type! if (pos < getSize()) return CurvePoint(pos, getValue(pos)); - return NoValue(); + return {}; } }; @@ -246,7 +245,7 @@ public: MainAttributes& setMinY(double newMinY) { minY = newMinY; return *this; } MainAttributes& setMaxY(double newMaxY) { maxY = newMaxY; return *this; } - MainAttributes& setAutoSize() { minX = maxX = minY = maxY = NoValue(); return *this; } + MainAttributes& setAutoSize() { minX = maxX = minY = maxY = {}; return *this; } MainAttributes& setLabelX(PosLabelX posX, int height = -1, std::shared_ptr<LabelFormatter> newLabelFmt = nullptr) { @@ -272,18 +271,18 @@ public: private: friend class Graph2D; - Opt<double> minX; //x-range to visualize - Opt<double> maxX; // + std::optional<double> minX; //x-range to visualize + std::optional<double> maxX; // - Opt<double> minY; //y-range to visualize - Opt<double> maxY; // + std::optional<double> minY; //y-range to visualize + std::optional<double> maxY; // PosLabelX labelposX = LABEL_X_BOTTOM; - Opt<int> xLabelHeight; + std::optional<int> xLabelHeight; std::shared_ptr<LabelFormatter> labelFmtX = std::make_shared<DecimalNumberFormatter>(); PosLabelY labelposY = LABEL_Y_LEFT; - Opt<int> yLabelWidth; + std::optional<int> yLabelWidth; std::shared_ptr<LabelFormatter> labelFmtY = std::make_shared<DecimalNumberFormatter>(); std::map<PosCorner, wxString> cornerTexts; @@ -337,7 +336,7 @@ private: MainAttributes attr_; //global attributes - Opt<wxBitmap> doubleBuffer_; + std::optional<wxBitmap> doubleBuffer_; using CurveList = std::vector<std::pair<std::shared_ptr<CurveData>, CurveAttributes>>; CurveList curves_; diff --git a/wx+/grid.cpp b/wx+/grid.cpp index 52ee6e6b..65311ffa 100755 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -310,23 +310,21 @@ public: protected: void setToolTip(const std::wstring& text) //proper fix for wxWindow { - wxToolTip* tt = GetToolTip(); - - const wxString oldText = tt ? tt->GetTip() : wxString(); - if (text != oldText) + if (text != GetToolTipText()) { if (text.empty()) - SetToolTip(nullptr); //wxGTK doesn't allow wxToolTip with empty text! + UnsetToolTip(); //wxGTK doesn't allow wxToolTip with empty text! else { - //wxWidgets bug: tooltip multiline property is defined by first tooltip text containing newlines or not (same is true for maximum width) + wxToolTip* tt = GetToolTip(); if (!tt) - SetToolTip(new wxToolTip(L"a b\n\ - a b")); //ugly, but working (on Windows) - tt = GetToolTip(); //should be bound by now - assert(tt); - if (tt) - tt->SetTip(text); + { + //wxWidgets bug: tooltip multiline property is defined by first tooltip text containing newlines or not (same is true for maximum width) + tt = new wxToolTip(L"a b\n\ + a b"); //ugly, but working (on Windows) + SetToolTip(tt); //pass ownership + } + tt->SetTip(text); } } } @@ -391,7 +389,7 @@ private: } Grid& parent_; - Opt<wxBitmap> doubleBuffer_; + std::optional<wxBitmap> doubleBuffer_; }; //---------------------------------------------------------------------------------------------------------------- @@ -689,12 +687,12 @@ private: activeResizing_.reset(); activeClickOrMove_.reset(); - if (Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) + if (std::optional<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) { if (action->wantResize) { if (!event.LeftDClick()) //double-clicks never seem to arrive here; why is this checked at all??? - if (Opt<int> colWidth = refParent().getColWidth(action->col)) + if (std::optional<int> colWidth = refParent().getColWidth(action->col)) activeResizing_ = std::make_unique<ColumnResizing>(*this, action->col, *colWidth, event.GetPosition().x); } else //a move or single click @@ -724,7 +722,7 @@ private: } else //notify single label click { - if (const Opt<ColumnType> colType = refParent().colToType(activeClickOrMove_->getColumnFrom())) + if (const std::optional<ColumnType> colType = refParent().colToType(activeClickOrMove_->getColumnFrom())) sendEventNow(GridLabelClickEvent(EVENT_GRID_COL_LABEL_MOUSE_LEFT, event, *colType)); } activeClickOrMove_.reset(); @@ -745,7 +743,7 @@ private: void onMouseLeftDouble(wxMouseEvent& event) override { - if (Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) + if (std::optional<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) if (action->wantResize) { //auto-size visible range on double-click @@ -790,7 +788,7 @@ private: } else { - if (const Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) + if (const std::optional<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) { highlightCol_ = action->col; @@ -801,7 +799,7 @@ private: } else { - highlightCol_ = NoValue(); + highlightCol_ = {}; SetCursor(*wxSTANDARD_CURSOR); } } @@ -823,7 +821,7 @@ private: void onLeaveWindow(wxMouseEvent& event) override { - highlightCol_ = NoValue(); //wxEVT_LEAVE_WINDOW does not respect mouse capture! -> however highlight_ is drawn unconditionally during move/resize! + highlightCol_ = {}; //wxEVT_LEAVE_WINDOW does not respect mouse capture! -> however highlight_ is drawn unconditionally during move/resize! Refresh(); event.Skip(); @@ -831,9 +829,9 @@ private: void onMouseRightDown(wxMouseEvent& event) override { - if (const Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) + if (const std::optional<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition())) { - if (const Opt<ColumnType> colType = refParent().colToType(action->col)) + if (const std::optional<ColumnType> colType = refParent().colToType(action->col)) sendEventNow(GridLabelClickEvent(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, event, *colType)); //notify right click else assert(false); } @@ -847,7 +845,7 @@ private: std::unique_ptr<ColumnResizing> activeResizing_; std::unique_ptr<ColumnMove> activeClickOrMove_; - Opt<size_t> highlightCol_; //column during mouse-over + std::optional<size_t> highlightCol_; //column during mouse-over }; //---------------------------------------------------------------------------------------------------------------- @@ -1080,8 +1078,8 @@ private: activeSelection_.reset(); } - highlight_.row = -1; - Refresh(); + highlight_.row = -1; + Refresh(); //event.Skip(); -> we DID handle it! } @@ -1146,8 +1144,8 @@ private: size_t getStartRow () const { return rowStart_; } size_t getCurrentRow () const { return rowCurrent_; } bool isPositiveSelect() const { return positiveSelect_; } //are we selecting or unselecting? - bool gridWasCleared () const { return gridWasCleared_; } - + bool gridWasCleared () const { return gridWasCleared_; } + const GridClickEvent& getFirstClick() const { return firstClick_; } void evalMousePos() @@ -1823,7 +1821,7 @@ wxWindow& Grid::getMainWin () { return *mainWin_; } const wxWindow& Grid::getMainWin() const { return *mainWin_; } -Opt<Grid::ColAction> Grid::clientPosToColumnAction(const wxPoint& pos) const +std::optional<Grid::ColAction> Grid::clientPosToColumnAction(const wxPoint& pos) const { const int absPosX = CalcUnscrolledPosition(pos).x; if (absPosX >= 0) @@ -1851,7 +1849,7 @@ Opt<Grid::ColAction> Grid::clientPosToColumnAction(const wxPoint& pos) const } } } - return NoValue(); + return {}; } @@ -12,7 +12,6 @@ #include <vector> #include <wx/scrolwin.h> #include <zen/basic_math.h> -#include <zen/optional.h> //a user-friendly, extensible and high-performance grid control @@ -52,13 +51,13 @@ struct GridSelectEvent : public wxCommandEvent { GridSelectEvent(size_t rowFirst, size_t rowLast, bool positive, const GridClickEvent* mouseClick) : wxCommandEvent(EVENT_GRID_SELECT_RANGE), rowFirst_(rowFirst), rowLast_(rowLast), positive_(positive), - mouseClick_(mouseClick ? *mouseClick : Opt<GridClickEvent>()) { assert(rowFirst <= rowLast); } + mouseClick_(mouseClick ? *mouseClick : std::optional<GridClickEvent>()) { assert(rowFirst <= rowLast); } GridSelectEvent* Clone() const override { return new GridSelectEvent(*this); } const size_t rowFirst_; //selected range: [rowFirst_, rowLast_) const size_t rowLast_; const bool positive_; //"false" when clearing selection! - const Opt<GridClickEvent> mouseClick_; //filled unless selection was performed via keyboard shortcuts + const std::optional<GridClickEvent> mouseClick_; //filled unless selection was performed via keyboard shortcuts }; struct GridLabelClickEvent : public wxMouseEvent @@ -299,12 +298,12 @@ private: int getColWidthsSum(int mainWinWidth) const; std::vector<int> getColStretchedWidths(int clientWidth) const; //final width = (normalized) (stretchedWidth + offset) - Opt<int> getColWidth(size_t col) const + std::optional<int> getColWidth(size_t col) const { const auto& widths = getColWidths(); if (col < widths.size()) return widths[col].width; - return NoValue(); + return {}; } void setColumnWidth(int width, size_t col, GridEventPolicy columnResizeEventPolicy, bool notifyAsync = false); @@ -321,7 +320,7 @@ private: bool wantResize = false; //"!wantResize" means "move" or "single click" size_t col = 0; }; - Opt<ColAction> clientPosToColumnAction(const wxPoint& pos) const; + std::optional<ColAction> clientPosToColumnAction(const wxPoint& pos) const; void moveColumn(size_t colFrom, size_t colTo); ptrdiff_t clientPosToMoveTargetColumn(const wxPoint& pos) const; //return < 0 on error @@ -363,18 +362,10 @@ private: template <class ColAttrReal> std::vector<ColAttrReal> makeConsistent(const std::vector<ColAttrReal>& attribs, const std::vector<ColAttrReal>& defaults) { - using ColTypeReal = decltype(ColAttrReal().type); - std::vector<ColAttrReal> output; - - std::set<ColTypeReal> usedTypes; //remove duplicates - auto appendUnique = [&](const std::vector<ColAttrReal>& attr) - { - std::copy_if(attr.begin(), attr.end(), std::back_inserter(output), - [&](const ColAttrReal& a) { return usedTypes.insert(a.type).second; }); - }; - appendUnique(attribs); - appendUnique(defaults); //make sure each type is existing! - + std::vector<ColAttrReal> output = attribs; + //make sure each type is existing! + output.insert(output.end(), defaults.begin(), defaults.end()); + removeDuplicates(output, [](const ColAttrReal& lhs, const ColAttrReal& rhs) { return lhs.type < rhs.type; }); return output; } diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp index bbeeff8b..d0449fe5 100755 --- a/wx+/image_resources.cpp +++ b/wx+/image_resources.cpp @@ -106,7 +106,7 @@ class DpiParallelScaler public: DpiParallelScaler(int hqScale) : hqScale_(hqScale) { assert(hqScale > 1); } - ~DpiParallelScaler() { threadGroup_ = zen::NoValue(); } //DpiParallelScaler must out-live threadGroup!!! + ~DpiParallelScaler() { threadGroup_ = {}; } //DpiParallelScaler must out-live threadGroup!!! void add(const wxString& name, const wxImage& img) { @@ -141,7 +141,7 @@ private: Protected<std::vector<std::pair<std::wstring, ImageHolder>>> result_; using TaskType = FunctionReturnTypeT<decltype(&getScalerTask)>; - Opt<ThreadGroup<TaskType>> threadGroup_{ ThreadGroup<TaskType>(std::max<int>(std::thread::hardware_concurrency(), 1), "xBRZ Scaler") }; + std::optional<ThreadGroup<TaskType>> threadGroup_{ ThreadGroup<TaskType>(std::max<int>(std::thread::hardware_concurrency(), 1), "xBRZ Scaler") }; //hardware_concurrency() == 0 if "not computable or well defined" }; diff --git a/wx+/image_tools.cpp b/wx+/image_tools.cpp index 0cb0e328..4748a590 100755 --- a/wx+/image_tools.cpp +++ b/wx+/image_tools.cpp @@ -14,38 +14,45 @@ using namespace zen; namespace { -void writeToImage(const wxImage& source, wxImage& target, const wxPoint& pos) +void writeToImage(wxImage& output, const wxImage& top, const wxPoint& pos) { - const int srcWidth = source.GetWidth (); - const int srcHeight = source.GetHeight(); - const int trgWidth = target.GetWidth (); + const int topWidth = top.GetWidth (); + const int topHeight = top.GetHeight(); + const int outWidth = output.GetWidth(); - if (srcWidth > 0 && srcHeight > 0) - { - assert(0 <= pos.x && pos.x + srcWidth <= trgWidth ); //draw area must be a - assert(0 <= pos.y && pos.y + srcHeight <= target.GetHeight()); //subset of target image! - assert(target.HasAlpha()); + assert(0 <= pos.x && pos.x + topWidth <= outWidth ); //draw area must be a + assert(0 <= pos.y && pos.y + topHeight <= output.GetHeight()); //subset of output image! + assert(top.HasAlpha() && output.HasAlpha()); - { - const unsigned char* sourcePtr = source.GetData(); - unsigned char* targetPtr = target.GetData() + 3 * (pos.x + pos.y * trgWidth); + //https://en.wikipedia.org/wiki/Alpha_compositing + const unsigned char* topRgb = top.GetData(); + const unsigned char* topAlpha = top.GetAlpha(); - for (int row = 0; row < srcHeight; ++row) - ::memcpy(targetPtr + 3 * row * trgWidth, sourcePtr + 3 * row * srcWidth, 3 * srcWidth); - } + for (int y = 0; y < topHeight; ++y) + { + unsigned char* outRgb = output.GetData () + 3 * (pos.x + (pos.y + y) * outWidth); + unsigned char* outAlpha = output.GetAlpha() + pos.x + (pos.y + y) * outWidth; - //handle alpha channel + for (int x = 0; x < topWidth; ++x) { - unsigned char* targetPtr = target.GetAlpha() + pos.x + pos.y * trgWidth; - if (source.HasAlpha()) + const int w1 = *topAlpha; //alpha-composition interpreted as weighted average + const int w2 = *outAlpha * (255 - w1) / 255; + const int wSum = w1 + w2; + + auto calcColor = [w1, w2, wSum](unsigned char colTop, unsigned char colBot) { - const unsigned char* sourcePtr = source.GetAlpha(); - for (int row = 0; row < srcHeight; ++row) - ::memcpy(targetPtr + row * trgWidth, sourcePtr + row * srcWidth, srcWidth); - } - else - for (int row = 0; row < srcHeight; ++row) - ::memset(targetPtr + row * trgWidth, wxIMAGE_ALPHA_OPAQUE, srcWidth); + return static_cast<unsigned char>(wSum == 0 ? 0 : (colTop * w1 + colBot * w2) / wSum); + }; + outRgb[0] = calcColor(topRgb[0], outRgb[0]); + outRgb[1] = calcColor(topRgb[1], outRgb[1]); + outRgb[2] = calcColor(topRgb[2], outRgb[2]); + + *outAlpha = static_cast<unsigned char>(wSum); + + topRgb += 3; + outRgb += 3; + ++topAlpha; + ++outAlpha; } } } @@ -64,16 +71,12 @@ wxImage zen::stackImages(const wxImage& img1, const wxImage& img2, ImageStackLay int width = std::max(img1Width, img2Width); int height = std::max(img1Height, img2Height); - switch (dir) - { - case ImageStackLayout::HORIZONTAL: - width = img1Width + gap + img2Width; - break; - case ImageStackLayout::VERTICAL: - height = img1Height + gap + img2Height; - break; - } + if (dir == ImageStackLayout::HORIZONTAL) + width = img1Width + gap + img2Width; + else + height = img1Height + gap + img2Height; + wxImage output(width, height); output.SetAlpha(); ::memset(output.GetAlpha(), wxIMAGE_ALPHA_TRANSPARENT, width * height); @@ -96,13 +99,13 @@ wxImage zen::stackImages(const wxImage& img1, const wxImage& img2, ImageStackLay switch (dir) { case ImageStackLayout::HORIZONTAL: - writeToImage(img1, output, wxPoint(0, calcPos(img1Height, height))); - writeToImage(img2, output, wxPoint(img1Width + gap, calcPos(img2Height, height))); + writeToImage(output, img1, wxPoint(0, calcPos(img1Height, height))); + writeToImage(output, img2, wxPoint(img1Width + gap, calcPos(img2Height, height))); break; case ImageStackLayout::VERTICAL: - writeToImage(img1, output, wxPoint(calcPos(img1Width, width), 0)); - writeToImage(img2, output, wxPoint(calcPos(img2Width, width), img1Height + gap)); + writeToImage(output, img1, wxPoint(calcPos(img1Width, width), 0)); + writeToImage(output, img2, wxPoint(calcPos(img2Width, width), img1Height + gap)); break; } return output; @@ -111,26 +114,6 @@ wxImage zen::stackImages(const wxImage& img1, const wxImage& img2, ImageStackLay namespace { -void calcAlphaForBlackWhiteImage(wxImage& image) //assume black text on white background -{ - assert(image.HasAlpha()); - if (unsigned char* alphaPtr = image.GetAlpha()) - { - const int pixelCount = image.GetWidth() * image.GetHeight(); - const unsigned char* dataPtr = image.GetData(); - for (int i = 0; i < pixelCount; ++ i) - { - const unsigned char r = *dataPtr++; - const unsigned char g = *dataPtr++; - const unsigned char b = *dataPtr++; - - //black(0,0,0) becomes fully opaque(255), while white(255,255,255) becomes transparent(0) - alphaPtr[i] = static_cast<unsigned char>((255 - r + 255 - g + 255 - b) / 3); //mixed mode arithmetics! - } - } -} - - std::vector<std::pair<wxString, wxSize>> getTextExtentInfo(const wxString& text, const wxFont& font) { wxMemoryDC dc; //the context used for bitmaps @@ -201,32 +184,39 @@ wxImage zen::createImageFromText(const wxString& text, const wxFont& font, const wxImage output(newBitmap.ConvertToImage()); output.SetAlpha(); - calcAlphaForBlackWhiteImage(output); - - //apply actual text color - unsigned char* dataPtr = output.GetData(); + unsigned char* rgb = output.GetData(); + unsigned char* alpha = output.GetAlpha(); const int pixelCount = output.GetWidth() * output.GetHeight(); - for (int i = 0; i < pixelCount; ++ i) + + for (int i = 0; i < pixelCount; ++i) { - *dataPtr++ = col.Red(); - *dataPtr++ = col.Green(); - *dataPtr++ = col.Blue(); + //black(0,0,0) becomes wxIMAGE_ALPHA_OPAQUE(255), while white(255,255,255) becomes wxIMAGE_ALPHA_TRANSPARENT(0) + *alpha++ = static_cast<unsigned char>((255 - rgb[0] + 255 - rgb[1] + 255 - rgb[2]) / 3); //mixed mode arithmetics! + + rgb[0] = col.Red (); // + rgb[1] = col.Green(); //apply actual text color + rgb[2] = col.Blue (); // + + rgb += 3; } return output; } -wxBitmap zen::layOver(const wxBitmap& background, const wxBitmap& foreground, int alignment) +wxBitmap zen::layOver(const wxBitmap& back, const wxBitmap& front, int alignment) { - if (!foreground.IsOk()) return background; + if (!front.IsOk()) return back; + + const int width = std::max(back.GetWidth(), front.GetWidth()); + const int height = std::max(back.GetHeight(), front.GetHeight()); - assert(foreground.HasAlpha() == background.HasAlpha()); //we don't support mixed-mode brittleness! + assert(front.HasAlpha() == back.HasAlpha()); //we don't support mixed-mode brittleness! const int offsetX = [&] { if (alignment & wxALIGN_RIGHT) - return background.GetWidth() - foreground.GetWidth(); + return back.GetWidth() - front.GetWidth(); if (alignment & wxALIGN_CENTER_HORIZONTAL) - return (background.GetWidth() - foreground.GetWidth()) / 2; + return (back.GetWidth() - front.GetWidth()) / 2; static_assert(wxALIGN_LEFT == 0); return 0; @@ -235,19 +225,22 @@ wxBitmap zen::layOver(const wxBitmap& background, const wxBitmap& foreground, i const int offsetY = [&] { if (alignment & wxALIGN_BOTTOM) - return background.GetHeight() - foreground.GetHeight(); + return back.GetHeight() - front.GetHeight(); if (alignment & wxALIGN_CENTER_VERTICAL) - return (background.GetHeight() - foreground.GetHeight()) / 2; + return (back.GetHeight() - front.GetHeight()) / 2; static_assert(wxALIGN_TOP == 0); return 0; }(); - wxBitmap output(background.ConvertToImage()); //attention: wxBitmap/wxImage use ref-counting without copy on write! - { - wxMemoryDC dc(output); - dc.DrawBitmap(foreground, offsetX, offsetY); - } + //can't use wxMemoryDC and wxDC::DrawBitmap(): no alpha channel support on wxGTK! + wxImage output(width, height); + output.SetAlpha(); + ::memset(output.GetAlpha(), wxIMAGE_ALPHA_TRANSPARENT, width * height); + + const wxPoint posBack(std::max(-offsetX, 0), std::max(-offsetY, 0)); + writeToImage(output, back .ConvertToImage(), posBack); + writeToImage(output, front.ConvertToImage(), posBack + wxPoint(offsetX, offsetY)); return output; } diff --git a/wx+/image_tools.h b/wx+/image_tools.h index ca82a031..06d7f0ba 100755 --- a/wx+/image_tools.h +++ b/wx+/image_tools.h @@ -34,7 +34,7 @@ wxImage stackImages(const wxImage& img1, const wxImage& img2, ImageStackLayout d wxImage createImageFromText(const wxString& text, const wxFont& font, const wxColor& col, ImageStackAlignment textAlign = ImageStackAlignment::LEFT); //CENTER/LEFT/RIGHT -wxBitmap layOver(const wxBitmap& background, const wxBitmap& foreground, int alignment = wxALIGN_CENTER); +wxBitmap layOver(const wxBitmap& back, const wxBitmap& front, int alignment = wxALIGN_CENTER); wxImage greyScale(const wxImage& img); //greyscale + brightness adaption wxBitmap greyScale(const wxBitmap& bmp); // @@ -7,7 +7,6 @@ #ifndef RTL_H_0183487180058718273432148 #define RTL_H_0183487180058718273432148 -#include <zen/optional.h> #include <wx/dcmemory.h> #include <wx/image.h> #include <wx/app.h> @@ -16,7 +15,7 @@ namespace zen { //functions supporting right-to-left GUI layout -void drawBitmapRtlMirror (wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, Opt<wxBitmap>& buffer); +void drawBitmapRtlMirror (wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, std::optional<wxBitmap>& buffer); void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment); //wxDC::DrawIcon DOES mirror by default -> implement RTL support when needed @@ -56,7 +55,7 @@ void drawBitmapAligned(wxDC& dc, const wxBitmap& image, const wxRect& rect, int inline -void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, Opt<wxBitmap>& buffer) +void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, std::optional<wxBitmap>& buffer) { if (dc.GetLayoutDirection() == wxLayout_RightToLeft) { |