From f78db924624e49615637a7eab07cc69076b155a9 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 17 Dec 2021 23:59:19 +0000 Subject: [PATCH] Avoid bitmap scaling in wxArtProvider::GetBitmapBundle() Instead of immediately constructing the bitmap with the requested size, possibly by downscaling a higher-resolution bitmap, and then potentially having to upscale it if we actually need a bitmap of a bigger size, just retrieve the bitmap in the actually needed size from wxArtProvider when needed. This makes bitmaps obtained from wxArtProvider::GetBitmapBundle() look good, rather than fuzzy and ugly, in high DPI if they're actually available in the appropriate size. --- include/wx/artprov.h | 9 ++--- interface/wx/artprov.h | 4 ++- src/common/artprov.cpp | 79 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 84 insertions(+), 8 deletions(-) diff --git a/include/wx/artprov.h b/include/wx/artprov.h index b8cbd199b1..dd067d5144 100644 --- a/include/wx/artprov.h +++ b/include/wx/artprov.h @@ -234,14 +234,11 @@ protected: return wxNullBitmap; } - // Derived classes must override CreateBitmapBundle if they provide - // a bundle that cannot be represented through an ordinary bitmap. + // Default implementation creates a wxBitmapBundle which returns the + // specified art resource in whichever size it is being asked for. virtual wxBitmapBundle CreateBitmapBundle(const wxArtID& id, const wxArtClient& client, - const wxSize& size) - { - return wxBitmapBundle(CreateBitmap(id, client, size)); - } + const wxSize& size); virtual wxIconBundle CreateIconBundle(const wxArtID& WXUNUSED(id), const wxArtClient& WXUNUSED(client)) diff --git a/interface/wx/artprov.h b/interface/wx/artprov.h index 8f0c1ff0d7..e8b0931783 100644 --- a/interface/wx/artprov.h +++ b/interface/wx/artprov.h @@ -307,7 +307,9 @@ public: @param client wxArtClient identifier of the client (i.e. who is asking for the bitmap). @param size - Default size for the returned bundle. + Default size for the returned bundle, i.e. the size of the bitmap + in normal DPI (this implies that wxWindow::FromDIP() must @e not be + used with it). @return If any of the registered providers recognizes the ID in its CreateBitmapBundle(), this bundle is returned. Otherwise, if any of diff --git a/src/common/artprov.cpp b/src/common/artprov.cpp index 66327070fb..0d7aa76e54 100644 --- a/src/common/artprov.cpp +++ b/src/common/artprov.cpp @@ -138,6 +138,77 @@ wxArtProviderCache::ConstructHashID(const wxArtID& id, wxString::Format(wxT("%d-%d"), size.x, size.y); } +// ---------------------------------------------------------------------------- +// wxBitmapBundleImplArt: uses art provider to get the bitmaps +// ---------------------------------------------------------------------------- + +namespace +{ + +class wxBitmapBundleImplArt : public wxBitmapBundleImpl +{ +public: + wxBitmapBundleImplArt(const wxArtID& id, + const wxArtClient& client, + const wxSize& size) + : m_artId(id), + m_artClient(client), + m_sizeDefault(GetValidSize(id, client, size)) + { + } + + virtual wxSize GetDefaultSize() const wxOVERRIDE + { + return m_sizeDefault; + } + + virtual wxSize GetPreferredSizeAtScale(double scale) const wxOVERRIDE + { + // We have no preferred sizes. + return m_sizeDefault*scale; + } + + virtual wxBitmap GetBitmap(const wxSize& size) wxOVERRIDE + { + return wxArtProvider::GetBitmap(m_artId, m_artClient, size); + } + +private: + static wxSize GetValidSize(const wxArtID& id, + const wxArtClient& client, + const wxSize& size) + { + // If valid size is provided, just use it. + if ( size != wxDefaultSize ) + return size; + + // Otherwise, try to get the size we'd use without creating a bitmap + // immediately. + const wxSize sizeHint = wxArtProvider::GetSizeHint(client); + if ( sizeHint != wxDefaultSize ) + return sizeHint; + + // If we really have to, do create a bitmap just to get its size. Note + // we need the size in logical pixels here, it will be scaled later if + // necessary, so use GetScaledSize() and not GetSize(). + const wxBitmap bitmap = wxArtProvider::GetBitmap(id, client); + if ( bitmap.IsOk() ) + return bitmap.GetScaledSize(); + + // We really need some default size, so just return this hardcoded + // value if all else fails -- what else can we do. + return wxSize(16, 16); + } + + const wxArtID m_artId; + const wxArtClient m_artClient; + const wxSize m_sizeDefault; + + wxDECLARE_NO_COPY_CLASS(wxBitmapBundleImplArt); +}; + +} // anonymous namespace + // ============================================================================ // wxArtProvider class // ============================================================================ @@ -305,7 +376,7 @@ wxBitmapBundle wxArtProvider::GetBitmapBundle(const wxArtID& id, wxCHECK_MSG( sm_providers, wxNullBitmap, wxT("no wxArtProvider exists") ); - wxString hashId = wxArtProviderCache::ConstructHashID(id, client); + wxString hashId = wxArtProviderCache::ConstructHashID(id, client, size); wxBitmapBundle bitmapbundle; // (DoGetIconBundle(id, client)); @@ -325,6 +396,12 @@ wxBitmapBundle wxArtProvider::GetBitmapBundle(const wxArtID& id, return bitmapbundle; } +wxBitmapBundle wxArtProvider::CreateBitmapBundle(const wxArtID& id, + const wxArtClient& client, + const wxSize& size) +{ + return wxBitmapBundle::FromImpl(new wxBitmapBundleImplArt(id, client, size)); +} /*static*/ wxIconBundle wxArtProvider::GetIconBundle(const wxArtID& id, const wxArtClient& client)