Allow wxBitmapBundle to specify its preferred bitmap size

Using bitmaps of preferred size avoids scaling and results in much
better appearance, so add methods allowing querying the bundle about the
bitmaps it supports and implement them in the various implementations.

This is not actually used anywhere yet, but will be soon.
This commit is contained in:
Vadim Zeitlin
2021-10-19 01:37:06 +01:00
parent 1cda648206
commit b20552116c
8 changed files with 203 additions and 0 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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 )

View File

@@ -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 )

View File

@@ -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,

View File

@@ -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();

View File

@@ -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]")