From d86c1a8c466ba1539eb61b9faa66ead8b26371eb Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 2 Jun 2022 01:08:21 +0100 Subject: [PATCH] Add wxBitmapBundleImpl::GetIndexToUpscale() Ensure that wxBitmapBundleImplSet and wxBitmapBundleImplRC use the same logic for actually selecting the bitmap to upscale, and not just for deciding the size that it must have, too. No real changes, but this should make impossible for these functions to diverge once again -- and also make it simpler to reuse the same logic in any other wxBitmapBundleImpl-derived classes in the future. --- include/wx/bmpbndl.h | 14 +++++++--- interface/wx/bmpbndl.h | 46 ++++++++++++++++++++++++++++--- src/common/bmpbndl.cpp | 61 ++++++++++++++++++++---------------------- src/msw/bmpbndl.cpp | 15 +++-------- 4 files changed, 85 insertions(+), 51 deletions(-) diff --git a/include/wx/bmpbndl.h b/include/wx/bmpbndl.h index 3bf8e0b88d..1b137e588a 100644 --- a/include/wx/bmpbndl.h +++ b/include/wx/bmpbndl.h @@ -230,10 +230,16 @@ protected: // If this function is used, GetNextAvailableScale() must be overridden! wxSize DoGetPreferredSize(double scale) const; - // Override this function if DoGetPreferredSize() is used: it can use the - // provided parameter as an internal index, it's guaranteed to be 0 when - // calling this function for the first time. When there are no more scales, - // return 0. + // Helper for implementing GetBitmap(): if we need to upscale a bitmap, + // uses GetNextAvailableScale() to find the index of the best bitmap to + // use, where "best" is defined as "using scale which is a divisor of the + // given one", as upscaling by an integer factor is strongly preferable. + size_t GetIndexToUpscale(const wxSize& size) const; + + // Override this function if DoGetPreferredSize() or GetIndexToUpscale() is + // used: it can use the provided parameter as an internal index, it's + // guaranteed to be 0 when calling this function for the first time. When + // there are no more scales, return 0. // // This function is not pure virtual because it doesn't need to be // implemented if DoGetPreferredSize() is never used, but it will assert if diff --git a/interface/wx/bmpbndl.h b/interface/wx/bmpbndl.h index 795dd649f8..959919c3c4 100644 --- a/interface/wx/bmpbndl.h +++ b/interface/wx/bmpbndl.h @@ -505,16 +505,42 @@ protected: used. Typically this function is used in the derived classes implementation - to forward GetPreferredBitmapSizeAtScale() to it, e.g. + to forward GetPreferredBitmapSizeAtScale() to it and when this is done, + GetBitmap() may also use GetIndexToUpscale() to choose the bitmap to + upscale if necessary: @code class MyCustomBitmapBundleImpl : public wxBitmapBundleImpl { public: + wxSize GetDefaultSize() const + { + return wxSize(32, 32); + } + wxSize GetPreferredBitmapSizeAtScale(double scale) const wxOVERRIDE { return DoGetPreferredSize(scale); } + wxBitmap GetBitmap(const wxSize& size) wxOVERRIDE + { + // For consistency with GetNextAvailableScale(), we must have + // bitmap variants for 32, 48 and 64px sizes. + const wxSize availableSizes[] = { 32, 48, 64 }; + if ( size.y <= 64 ) + { + ... get the bitmap from somewhere ... + } + else + { + size_t n = GetIndexToUpscale(size); + bitmap = ... get bitmap for availableSizes[n] ...; + wxBitmap::Rescale(bitmap, size); + } + + return bitmap; + } + protected: double GetNextAvailableScale(size_t& i) const wxOVERRIDE { @@ -535,12 +561,26 @@ protected: */ wxSize DoGetPreferredSize(double scale) const; + /** + Return the index of the available scale most suitable to be upscaled to + the given size. + + See DoGetPreferredSize() for an example of using this function. + + @param size The required size, typically the same one as passed to + GetBitmap() + + @since 3.1.7 + */ + size_t GetIndexToUpscale(const wxSize& size) const; + /** Return information about the available bitmaps. Overriding this function is optional and only needs to be done if - DoGetPreferredSize() is called. If you do override it, this function - must return the next available scale or 0.0 if there are no more. + either DoGetPreferredSize() or GetIndexToUpscale() are called. If you + do override it, this function must return the next available scale or + 0.0 if there are no more. The returned scales must be in ascending order and the first returned scale, for the initial @a i value of 0, should be 1. The function must diff --git a/src/common/bmpbndl.cpp b/src/common/bmpbndl.cpp index 8af75ec2a9..e73cff491a 100644 --- a/src/common/bmpbndl.cpp +++ b/src/common/bmpbndl.cpp @@ -298,41 +298,14 @@ wxBitmap wxBitmapBundleImplSet::GetBitmap(const wxSize& size) // We only get here if the requested size is larger than the size of all // the bitmaps we have, in which case we have no choice but to upscale one - // of the bitmaps, so find the largest available non-generated bitmap which - // can be scaled using an integer factor or, failing that, just the largest - // non-generated bitmap. - const Entry* entryToRescale = NULL; - const Entry* entryLastNotGen = NULL; - for ( size_t i = 0; i < n; ++i ) - { - const Entry& entry = m_entries[i]; - if ( entry.generated ) - continue; + // of the bitmaps, so find the most appropriate one for doing it. + const size_t i = GetIndexToUpscale(size); - entryLastNotGen = &entry; + const Entry entryNew(m_entries[i], size); - const double - scale = static_cast(size.y) / entry.bitmap.GetSize().y; - if ( scale == wxRound(scale) ) - entryToRescale = &entry; - } + m_entries.push_back(entryNew); - if ( !entryToRescale ) - entryToRescale = entryLastNotGen; - - if ( entryToRescale ) - { - const Entry entryNew(*entryToRescale, size); - - m_entries.push_back(entryNew); - - return entryNew.bitmap; - } - - // We should have at least one non-generated bitmap. - wxFAIL_MSG( wxS("unreachable") ); - - return wxBitmap(); + return entryNew.bitmap; } #ifdef __WXOSX__ @@ -787,6 +760,30 @@ wxBitmapBundleImpl::DoGetPreferredSize(double scaleTarget) const return GetDefaultSize()*scaleBest; } +size_t wxBitmapBundleImpl::GetIndexToUpscale(const wxSize& size) const +{ + // Our best hope is to find a scale dividing the given one evenly. + size_t indexBest = (size_t)-1; + + // In the worst case, we will use the largest index, as it should hopefully + // result in the least bad results. + size_t indexLast = 0; + + const wxSize sizeDef = GetDefaultSize(); + for ( size_t i = 0;; indexLast = i) + { + const double scaleThis = GetNextAvailableScale(i); + if ( scaleThis == 0.0 ) + break; + + const double scale = size.y / (sizeDef.y*scaleThis); + if (wxRound(scale) == scale) + indexBest = indexLast; + } + + return indexBest != (size_t)-1 ? indexBest : indexLast; +} + wxBitmapBundleImpl::~wxBitmapBundleImpl() { #ifdef __WXOSX__ diff --git a/src/msw/bmpbndl.cpp b/src/msw/bmpbndl.cpp index e9eaf87a09..11bea81577 100644 --- a/src/msw/bmpbndl.cpp +++ b/src/msw/bmpbndl.cpp @@ -285,20 +285,11 @@ wxBitmap wxBitmapBundleImplRC::GetBitmap(const wxSize& size) // We have to upscale some bitmap because we don't have any bitmaps larger // than the requested size. Try to find one which can be upscaled using an // integer factor. - const ResourceInfo* infoToRescale = NULL; - for ( size_t i = 0; i < m_resourceInfos.size(); ++i ) - { - const ResourceInfo& info = m_resourceInfos[i]; + const size_t i = GetIndexToUpscale(size); - const double scale = size.y / sizeDef.y*info.scale; - if ( scale == wxRound(scale) ) - infoToRescale = &info; - } + const ResourceInfo& info = m_resourceInfos[i]; - if ( !infoToRescale ) - infoToRescale = &m_resourceInfos.back(); - - return AddBitmap(*infoToRescale, sizeDef*infoToRescale->scale, size); + return AddBitmap(info, sizeDef*info.scale, size); } // ============================================================================