diff --git a/include/wx/bmpbndl.h b/include/wx/bmpbndl.h index f5db5ab140..4024e3154c 100644 --- a/include/wx/bmpbndl.h +++ b/include/wx/bmpbndl.h @@ -15,6 +15,7 @@ #include "wx/vector.h" class wxBitmapBundleImpl; +class WXDLLIMPEXP_FWD_CORE wxWindow; // It should be possible to implement SVG rasterizing without raw bitmap // support using wxDC::DrawSpline(), but currently we don't do it and so @@ -96,6 +97,12 @@ public: // default DPI, i.e. 100% scaling. Returns invalid size for empty bundle. wxSize GetDefaultSize() const; + // Get preferred size, i.e. usually the closest size in which a bitmap is + // available to the ideal size determined from the default size and the DPI + // scaling, for the given window. + wxSize GetPreferredSizeFor(wxWindow* window) const; + wxSize GetPreferredSizeAtScale(double scale) const; + // Get bitmap of the specified size, creating a new bitmap from the closest // available size by rescaling it if necessary. // @@ -177,6 +184,11 @@ public: // Must always return a valid size. virtual wxSize GetDefaultSize() const = 0; + // Return the preferred size that should be used at the given scale. + // + // Must always return a valid size. + virtual wxSize GetPreferredSizeAtScale(double scale) const = 0; + // Retrieve the bitmap of exactly the given size. // // Note that this function is non-const because it may generate the bitmap diff --git a/interface/wx/bmpbndl.h b/interface/wx/bmpbndl.h index c17273ef04..c9392657fa 100644 --- a/interface/wx/bmpbndl.h +++ b/interface/wx/bmpbndl.h @@ -252,6 +252,31 @@ public: */ wxSize GetDefaultSize() const; + /** + Get the size that would be best to use for this bundle at the given DPI + scaling factor. + + For bundles containing some number of the fixed-size bitmaps, this + function returns the size of an existing bitmap closest to the ideal + size at the given scale, i.e. GetDefaultSize() multiplied by @a scale. + + Passing a size returned by this function to GetBitmap() ensures that + bitmap doesn't need to be rescaled, which typically significantly + lowers its quality. + */ + wxSize GetPreferredSizeAtScale(double scale) const; + + /** + Get the size that would be best to use for this bundle at the DPI + scaling factor used by the given window. + + This is just a convenient wrapper for GetPreferredSizeAtScale() calling + that function with the result of wxWindow::GetDPIScaleFactor(). + + @param window Non-null and fully created window. + */ + wxSize GetPreferredSizeFor(wxWindow* window) const; + /** Get bitmap of the specified size, creating a new bitmap from the closest available size by rescaling it if necessary. @@ -285,6 +310,16 @@ public: ... determine the minimum/default size for bitmap to use ... } + wxSize GetPreferredSizeAtScale(double scale) const wxOVERRIDE + { + // If it's ok to scale the bitmap, just use the standard size + // at the given scale: + return GetDefaultSize()*scale; + + ... otherwise, an existing bitmap of the size closest to the + one above would need to be found and its size returned ... + } + wxBitmap GetBitmap(const wxSize& size) wxOVERRIDE { ... get the bitmap of the requested size from somewhere and @@ -314,6 +349,13 @@ public: */ virtual wxSize GetDefaultSize() const = 0; + /** + Return the preferred size that should be used at the given scale. + + Must always return a valid size. + */ + virtual wxSize GetPreferredSizeAtScale(double scale) const = 0; + /** Retrieve the bitmap of exactly the given size. diff --git a/samples/toolbar/toolbar.cpp b/samples/toolbar/toolbar.cpp index 9222ace1bb..41c5fb73a3 100644 --- a/samples/toolbar/toolbar.cpp +++ b/samples/toolbar/toolbar.cpp @@ -520,6 +520,13 @@ void MyFrame::PopulateToolbar(wxToolBarBase* toolBar) return m_sizeDef; } + wxSize GetPreferredSizeAtScale(double scale) const wxOVERRIDE + { + // We just scale the bitmap to fit the requested size, so + // we don't really have any preferences. + return m_sizeDef*scale; + } + wxBitmap GetBitmap(const wxSize& size) wxOVERRIDE { // In this simple implementation we don't bother caching diff --git a/src/common/bmpbndl.cpp b/src/common/bmpbndl.cpp index 0628b5ecd0..401a74b523 100644 --- a/src/common/bmpbndl.cpp +++ b/src/common/bmpbndl.cpp @@ -23,6 +23,7 @@ #include "wx/bmpbndl.h" #include "wx/icon.h" +#include "wx/window.h" #include "wx/private/bmpbndl.h" @@ -64,6 +65,7 @@ public: } virtual wxSize GetDefaultSize() const wxOVERRIDE; + virtual wxSize GetPreferredSizeAtScale(double scale) const wxOVERRIDE; virtual wxBitmap GetBitmap(const wxSize& size) wxOVERRIDE; #ifdef __WXOSX__ @@ -157,6 +159,47 @@ wxSize wxBitmapBundleImplSet::GetDefaultSize() const return m_entries[0].bitmap.GetSize(); } +wxSize wxBitmapBundleImplSet::GetPreferredSizeAtScale(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 size_t n = m_entries.size(); + 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; + + } + + // 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. + return m_entries.back().bitmap.GetSize(); +} + wxBitmap wxBitmapBundleImplSet::GetBitmap(const wxSize& size) { // We use linear search instead if binary one because it's simpler and the @@ -326,6 +369,21 @@ wxSize wxBitmapBundle::GetDefaultSize() const return m_impl->GetDefaultSize(); } +wxSize wxBitmapBundle::GetPreferredSizeFor(wxWindow* window) const +{ + wxCHECK_MSG( window, wxDefaultSize, "window must be valid" ); + + return GetPreferredSizeAtScale(window->GetDPIScaleFactor()); +} + +wxSize wxBitmapBundle::GetPreferredSizeAtScale(double scale) const +{ + if ( !m_impl ) + return wxDefaultSize; + + return m_impl->GetPreferredSizeAtScale(scale); +} + wxBitmap wxBitmapBundle::GetBitmap(const wxSize& size) const { if ( !m_impl ) diff --git a/src/generic/bmpsvg.cpp b/src/generic/bmpsvg.cpp index 8d1b4fc1bc..1c34e5215b 100644 --- a/src/generic/bmpsvg.cpp +++ b/src/generic/bmpsvg.cpp @@ -93,6 +93,7 @@ public: } virtual wxSize GetDefaultSize() const wxOVERRIDE; + virtual wxSize GetPreferredSizeAtScale(double scale) const wxOVERRIDE; virtual wxBitmap GetBitmap(const wxSize& size) wxOVERRIDE; private: @@ -126,6 +127,12 @@ wxSize wxBitmapBundleImplSVG::GetDefaultSize() const return m_sizeDef; } +wxSize wxBitmapBundleImplSVG::GetPreferredSizeAtScale(double scale) const +{ + // We consider that we can render at any scale. + return m_sizeDef*scale; +} + wxBitmap wxBitmapBundleImplSVG::GetBitmap(const wxSize& size) { if ( !m_cachedBitmap.IsOk() || m_cachedBitmap.GetSize() != size ) diff --git a/src/msw/bmpbndl.cpp b/src/msw/bmpbndl.cpp index f4c82894da..4380df8f14 100644 --- a/src/msw/bmpbndl.cpp +++ b/src/msw/bmpbndl.cpp @@ -138,6 +138,7 @@ public: const wxBitmap& bitmap); virtual wxSize GetDefaultSize() const wxOVERRIDE; + virtual wxSize GetPreferredSizeAtScale(double scale) const wxOVERRIDE; virtual wxBitmap GetBitmap(const wxSize& size) wxOVERRIDE; private: @@ -185,6 +186,52 @@ wxSize wxBitmapBundleImplRC::GetDefaultSize() const return m_bitmaps[0].GetSize(); } +wxSize wxBitmapBundleImplRC::GetPreferredSizeAtScale(double scale) const +{ + // Optimistically assume we're going to use this exact scale by default. + double scalePreferred = scale; + + for ( size_t i = 0; ; ++i ) + { + if ( i == m_resourceInfos.size() ) + { + // The requested scale is bigger than anything we have, so use the + // biggest available one. + scalePreferred = m_resourceInfos[i - 1].scale; + break; + } + + const double scaleThis = m_resourceInfos[i].scale; + + // Keep looking for the exact match which we still can hope to find + // while the current scale is smaller. + if ( scaleThis < scale ) + continue; + + // If we've found the exact match, just use it. + if ( scaleThis == scale ) + break; + + // We've found the closest bigger scale. + + // If there is no smaller one, we have no choice but to use this one. + if ( i == 0 ) + break; + + // Decide whether we should use this one or the previous smaller one + // depending on which of them is closer to the target scale, breaking + // the tie in favour of the bigger one. + const double scaleLast = m_resourceInfos[i - 1].scale; + + scalePreferred = scaleThis - scale <= scale - scaleLast + ? scaleThis + : scaleLast; + break; + } + + return GetDefaultSize()*scalePreferred; +} + wxBitmap wxBitmapBundleImplRC::AddBitmap(const ResourceInfo& info, const wxSize& sizeExpected, diff --git a/src/osx/core/bmpbndl.mm b/src/osx/core/bmpbndl.mm index 7fdb89a78a..10663232de 100644 --- a/src/osx/core/bmpbndl.mm +++ b/src/osx/core/bmpbndl.mm @@ -46,6 +46,7 @@ public: ~wxOSXImageBundleImpl(); virtual wxSize GetDefaultSize() const wxOVERRIDE; + virtual wxSize GetPreferredSizeAtScale(double scale) const wxOVERRIDE; virtual wxBitmap GetBitmap(const wxSize& size) wxOVERRIDE; virtual WXImage OSXGetImage() const wxOVERRIDE; @@ -76,6 +77,14 @@ wxSize wxOSXImageBundleImpl::GetDefaultSize() const return wxSize(sz.width, sz.height); } +wxSize wxOSXImageBundleImpl::GetPreferredSizeAtScale(double scale) const +{ + // The system always performs scaling, as the scaling factor is integer and + // so it doesn't make sense to round it up or down, hence we should use the + // theoretical best size for given scale. + return GetDefaultSize()*scale; +} + wxBitmap wxOSXImageBundleImpl::GetBitmap(const wxSize& size) { return wxBitmap(); diff --git a/tests/graphics/bmpbundle.cpp b/tests/graphics/bmpbundle.cpp index aaddfc546a..b14a25148c 100644 --- a/tests/graphics/bmpbundle.cpp +++ b/tests/graphics/bmpbundle.cpp @@ -47,6 +47,27 @@ TEST_CASE("BitmapBundle::FromBitmaps", "[bmpbundle]") CHECK( b.GetBitmap(wxSize(24, 24)).GetSize() == wxSize(24, 24) ); } +TEST_CASE("BitmapBundle::GetPreferredSize", "[bmpbundle]") +{ + CHECK( wxBitmapBundle().GetPreferredSizeAtScale(1) == wxDefaultSize ); + + const wxSize normal(32, 32); + const wxSize bigger(64, 64); + + const wxBitmapBundle + b = wxBitmapBundle::FromBitmaps(wxBitmap(normal), wxBitmap(bigger)); + + CHECK( b.GetPreferredSizeAtScale(0 ) == normal ); + CHECK( b.GetPreferredSizeAtScale(1 ) == normal ); + CHECK( b.GetPreferredSizeAtScale(1.25) == normal ); + CHECK( b.GetPreferredSizeAtScale(1.4 ) == normal ); + CHECK( b.GetPreferredSizeAtScale(1.5 ) == bigger ); + CHECK( b.GetPreferredSizeAtScale(1.75) == bigger ); + CHECK( b.GetPreferredSizeAtScale(2 ) == bigger ); + CHECK( b.GetPreferredSizeAtScale(2.5 ) == bigger ); + CHECK( b.GetPreferredSizeAtScale(3 ) == bigger ); +} + #ifdef wxHAS_SVG TEST_CASE("BitmapBundle::FromSVG", "[bmpbundle][svg]")