From 78da0eed687a514a51dd2f31a87079836d3488c2 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 1 Jun 2022 18:35:44 +0100 Subject: [PATCH] Refactor wxBitmapBundleImplSet::GetPreferredBitmapSizeAtScale() Extract the logic determining the scale to use in a reusable DoGetPreferredSize() function to allow reusing it in other places. There are no real changes here, this commit just moved the existing code to the new function, but because it also changed it from using wxSize to double, even git --color-moved doesn't show it as an actual move. --- include/wx/bmpbndl.h | 8 ++++ interface/wx/bmpbndl.h | 52 ++++++++++++++++++++- src/common/bmpbndl.cpp | 103 ++++++++++++++++++++++++++--------------- 3 files changed, 124 insertions(+), 39 deletions(-) diff --git a/include/wx/bmpbndl.h b/include/wx/bmpbndl.h index 248dd63274..db07bea47f 100644 --- a/include/wx/bmpbndl.h +++ b/include/wx/bmpbndl.h @@ -224,6 +224,14 @@ wxBitmapBundle wxBitmapBundle::FromImage(const wxImage& image) class WXDLLIMPEXP_CORE wxBitmapBundleImpl : public wxRefCounter { protected: + // Standard implementation of GetPreferredBitmapSizeAtScale(): choose the + // scale closest to the given one from the available bitmap scales. + // + // Note that scales *must* be sorted in increasing scale order and there + // must be at least 1 of them. + wxSize + DoGetPreferredSize(double scale, size_t n, const double *availableScales) const; + virtual ~wxBitmapBundleImpl(); public: diff --git a/interface/wx/bmpbndl.h b/interface/wx/bmpbndl.h index 901b56d20b..06ce457999 100644 --- a/interface/wx/bmpbndl.h +++ b/interface/wx/bmpbndl.h @@ -442,7 +442,8 @@ public: return GetDefaultSize()*scale; ... otherwise, an existing bitmap of the size closest to the - one above would need to be found and its size returned ... + one above would need to be found and its size returned, + possibly by letting DoGetPreferredSize() choose it ... } wxBitmap GetBitmap(const wxSize& size) wxOVERRIDE @@ -488,6 +489,55 @@ public: on demand and cache it. */ virtual wxBitmap GetBitmap(const wxSize& size) = 0; + +protected: + /** + Helper for implementing GetPreferredBitmapSizeAtScale() in the derived + classes. + + This function implements the standard algorithm used inside wxWidgets + itself and tries to find the scale closest to the given one, while also + trying to choose one of the available scales, to avoid actually + rescaling the bitmaps. + + Typically this function is used in the derived classes implementation, + e.g. + @code + class MyCustomBitmapBundleImpl : public wxBitmapBundleImpl + { + public: + wxSize GetPreferredBitmapSizeAtScale(double scale) const wxOVERRIDE + { + std::vector vec; + vec.push_back(1); + + // Note that the vector must be sorted, so we must add this + // scale before adding 2 below. + if ( MyIntermediateBitmapIsAvailable() ) + vec.push_back(1.5); + + vec.push_back(2); + + return DoGetPreferredSize(scale, vec.size(), vec.data()); + } + + ... + }; + @endcode + + @param scale The required scale, typically the same one as passed to + GetPreferredBitmapSizeAtScale(). + @param n The number of elements in @a availableScales, must be strictly + positive (i.e. there must always be at least one available scale). + @param availableScales The scales in which bitmaps are available, i.e. + scales such that GetBitmap() wouldn't need to scale the bitmap if + it were called with them. This array @e must be in sorted order, + with 1 being its first element. + + @since 3.1.7 + */ + wxSize + DoGetPreferredSize(double scale, size_t n, const double *availableScales) const; }; /** diff --git a/src/common/bmpbndl.cpp b/src/common/bmpbndl.cpp index 51317d3d9d..18c713ad65 100644 --- a/src/common/bmpbndl.cpp +++ b/src/common/bmpbndl.cpp @@ -237,50 +237,16 @@ wxSize wxBitmapBundleImplSet::GetDefaultSize() const wxSize wxBitmapBundleImplSet::GetPreferredBitmapSizeAtScale(double scale) const { - // Target size is the ideal size we'd like the bitmap to have at this scale. - const wxSize sizeTarget = GetDefaultSize()*scale; + const double baseY = GetDefaultSize().y; const size_t n = m_entries.size(); + wxVector scales(n); for ( size_t i = 0; i < n; ++i ) { - const wxSize sizeThis = m_entries[i].bitmap.GetSize(); - - // Keep looking for the exact match which we still can hope to find - // while the current bitmap is smaller. - if ( sizeThis.y < sizeTarget.y ) - continue; - - // If we've found the exact match, just return it. - if ( sizeThis.y == sizeTarget.y ) - return sizeThis; - - // We've found the closest bigger bitmap. - - // If there is no smaller bitmap, we have no choice but to use this one. - if ( i == 0 ) - return sizeThis; - - // Decide whether we should use this one or the previous smaller one - // depending on which of them is closer to the target size, breaking - // the tie in favour of the bigger size. - const wxSize sizeLast = m_entries[i - 1].bitmap.GetSize(); - - return sizeThis.y - sizeTarget.y <= sizeTarget.y - sizeLast.y - ? sizeThis - : sizeLast; - + scales[i] = m_entries[i].bitmap.GetSize().y / baseY; } - // We only get here if the target size is bigger than all the available - // sizes, in which case we have no choice but to use the biggest bitmap. - const wxSize sizeMax = m_entries.back().bitmap.GetSize(); - - // But check how far is it from the requested scale: if it's more than 1.5 - // times smaller, we should still scale it, notably to ensure that bitmaps - // of standard size are scaled when 2x DPI scaling is used. - return static_cast(sizeTarget.y) / sizeMax.y >= 1.5 - ? sizeTarget - : sizeMax; + return DoGetPreferredSize(scale, n, &scales[0]); } wxBitmap wxBitmapBundleImplSet::GetBitmap(const wxSize& size) @@ -715,6 +681,67 @@ wxBitmapBundle::CreateImageList(wxWindow* win, // wxBitmapBundleImpl implementation // ============================================================================ +wxSize +wxBitmapBundleImpl::DoGetPreferredSize(double scaleTarget, + size_t n, + const double* availableScales) const +{ + wxCHECK_MSG( n > 0, wxSize(), wxS("must have at least one scale") ); + + double scaleBest = 0.0; + + for ( size_t i = 0; i < n; ++i ) + { + const double scaleThis = availableScales[i]; + + // Keep looking for the exact match which we still can hope to find + // while the current bitmap is smaller. + if ( scaleThis < scaleTarget ) + continue; + + // If we've found the exact match, just return it. + if ( scaleThis == scaleTarget ) + { + scaleBest = scaleThis; + break; + } + + // We've found the closest bigger bitmap. + + // If there is no smaller bitmap, we have no choice but to use this one. + if ( i == 0 ) + { + scaleBest = scaleThis; + break; + } + + // Decide whether we should use this one or the previous smaller one + // depending on which of them is closer to the target size, breaking + // the tie in favour of the bigger size. + const double scaleLast = availableScales[i - 1]; + + scaleBest = scaleThis - scaleTarget <= scaleTarget - scaleLast + ? scaleThis + : scaleLast; + break; + } + + if ( scaleBest == 0.0 ) + { + // We only get here if the target scale is bigger than all the + // available scales, in which case we have no choice but to use the + // biggest bitmap. + const double scaleMax = availableScales[n - 1]; + + // But check how far is it from the requested scale: if it's more than + // 1.5 times smaller, we should still scale it, notably to ensure that + // bitmaps of standard size are scaled when 2x DPI scaling is used. + scaleBest = scaleTarget / scaleMax >= 1.5 ? scaleTarget : scaleMax; + } + + return GetDefaultSize()*scaleBest; +} + wxBitmapBundleImpl::~wxBitmapBundleImpl() { #ifdef __WXOSX__