From eb54c65e2497029ba2aa805e5494140c1d6a908d Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 16 Oct 2021 16:56:31 +0200 Subject: [PATCH 1/6] Separate GTK 2/3 version of BitmapProviderDefault No real changes, just move the trivial GTK 2 stab out of the way. --- src/gtk/image_gtk.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/gtk/image_gtk.cpp b/src/gtk/image_gtk.cpp index 348637df82..9f370500ef 100644 --- a/src/gtk/image_gtk.cpp +++ b/src/gtk/image_gtk.cpp @@ -15,23 +15,20 @@ namespace { + +#ifdef __WXGTK3__ + // Default provider for HiDPI common case struct BitmapProviderDefault: wxGtkImage::BitmapProvider { -#ifdef __WXGTK3__ BitmapProviderDefault(wxWindow* win) : m_win(win) { } virtual wxBitmap Get() const wxOVERRIDE; virtual void Set(const wxBitmap& bitmap) wxOVERRIDE; wxWindow* const m_win; wxBitmap m_bitmap; wxBitmap m_bitmapDisabled; -#else - BitmapProviderDefault(wxWindow*) { } - virtual wxBitmap Get() const wxOVERRIDE { return wxBitmap(); } -#endif }; -#ifdef __WXGTK3__ wxBitmap BitmapProviderDefault::Get() const { return (m_win == NULL || m_win->IsEnabled()) ? m_bitmap : m_bitmapDisabled; @@ -48,7 +45,18 @@ void BitmapProviderDefault::Set(const wxBitmap& bitmap) m_bitmapDisabled = bitmap.CreateDisabled(); } } -#endif // __WXGTK3__ + +#else // !__WXGTK3__ + +// Trivial version for GTK < 3 which doesn't provide any high DPI support. +struct BitmapProviderDefault: wxGtkImage::BitmapProvider +{ + BitmapProviderDefault(wxWindow*) { } + virtual wxBitmap Get() const wxOVERRIDE { return wxBitmap(); } +}; + +#endif // __WXGTK3__/!__WXGTK3__ + } // namespace extern "C" { From 8f1be368e9ca5ec1e8d682511354dab8a91101d4 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 16 Oct 2021 17:01:48 +0200 Subject: [PATCH 2/6] Add some comments to wxGtkImage Document which pointers can, and can't, be null and when exactly is the stored bitmap valid, as this is far from being immediately obvious. No real changes. --- include/wx/gtk/private/image.h | 4 ++++ src/gtk/image_gtk.cpp | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/include/wx/gtk/private/image.h b/include/wx/gtk/private/image.h index c346cd7919..ce664c6d86 100644 --- a/include/wx/gtk/private/image.h +++ b/include/wx/gtk/private/image.h @@ -23,9 +23,13 @@ public: static GtkWidget* New(wxWindow* win = NULL); void Set(const wxBitmap& bitmap); + // This pointer is never null and is owned by this class. BitmapProvider* m_provider; wxDECLARE_NO_COPY_CLASS(wxGtkImage); + + // This class is constructed by New() and destroyed by its GObject + // finalizer, so neither its ctor nor dtor can ever be used. wxGtkImage() wxMEMBER_DELETE; ~wxGtkImage() wxMEMBER_DELETE; }; diff --git a/src/gtk/image_gtk.cpp b/src/gtk/image_gtk.cpp index 9f370500ef..29d9297766 100644 --- a/src/gtk/image_gtk.cpp +++ b/src/gtk/image_gtk.cpp @@ -24,8 +24,17 @@ struct BitmapProviderDefault: wxGtkImage::BitmapProvider BitmapProviderDefault(wxWindow* win) : m_win(win) { } virtual wxBitmap Get() const wxOVERRIDE; virtual void Set(const wxBitmap& bitmap) wxOVERRIDE; + + // This pointer can be null if there is no associated window. wxWindow* const m_win; + + // The bitmap stored here is only valid if it uses scale factor > 1, + // otherwise we leave it invalid to indicate the drawing the bitmap should + // be left to GtkImage itself. wxBitmap m_bitmap; + + // This bitmap is only valid if m_bitmap is and if we have the associated + // window (as otherwise it would never be used at all). wxBitmap m_bitmapDisabled; }; From 77e3983091653d246c8e15923a498f2692884e8a Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 16 Oct 2021 17:06:44 +0200 Subject: [PATCH 3/6] Create disabled bitmap in BitmapProviderDefault on demand There is no need to always do it in advance when we might never actually need it. --- src/gtk/image_gtk.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/gtk/image_gtk.cpp b/src/gtk/image_gtk.cpp index 29d9297766..eea80ff277 100644 --- a/src/gtk/image_gtk.cpp +++ b/src/gtk/image_gtk.cpp @@ -33,25 +33,33 @@ struct BitmapProviderDefault: wxGtkImage::BitmapProvider // be left to GtkImage itself. wxBitmap m_bitmap; - // This bitmap is only valid if m_bitmap is and if we have the associated - // window (as otherwise it would never be used at all). - wxBitmap m_bitmapDisabled; + // This bitmap is created on demand from m_bitmap when necessary (and is + // mutable because this is done in const Get()). + mutable wxBitmap m_bitmapDisabled; }; wxBitmap BitmapProviderDefault::Get() const { - return (m_win == NULL || m_win->IsEnabled()) ? m_bitmap : m_bitmapDisabled; + if ( m_win && !m_win->IsEnabled() ) + { + if ( !m_bitmapDisabled.IsOk() && m_bitmap.IsOk() ) + m_bitmapDisabled = m_bitmap.CreateDisabled(); + + return m_bitmapDisabled; + } + + return m_bitmap; } void BitmapProviderDefault::Set(const wxBitmap& bitmap) { m_bitmap.UnRef(); + + // Ensure it's recreated if needed later. m_bitmapDisabled.UnRef(); if (bitmap.IsOk() && bitmap.GetScaleFactor() > 1) { m_bitmap = bitmap; - if (m_win) - m_bitmapDisabled = bitmap.CreateDisabled(); } } From 399b0ff9ae70adf9e3e1339cbebfc03d29c53ecb Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 16 Oct 2021 19:56:01 +0200 Subject: [PATCH 4/6] Use wxBitmapBundle instead of bitmap scale factor in wxGtkImage Minimal changes to the code to allow using bigger bitmaps from wxBitmapBundle in high DPI instead of having to create individual bitmaps with scale factor greater than 1. Notice that this is not actually used anywhere in wxGTK yet, but will be soon. --- include/wx/gtk/private/image.h | 19 ++++++++- src/gtk/image_gtk.cpp | 78 ++++++++++++++++++++-------------- src/gtk/toolbar.cpp | 7 +++ 3 files changed, 69 insertions(+), 35 deletions(-) diff --git a/include/wx/gtk/private/image.h b/include/wx/gtk/private/image.h index ce664c6d86..24baebfdd3 100644 --- a/include/wx/gtk/private/image.h +++ b/include/wx/gtk/private/image.h @@ -5,6 +5,9 @@ // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// +#include "wx/bmpbndl.h" +#include "wx/math.h" + // Class that can be used in place of GtkImage, to allow drawing of alternate // bitmaps, such as HiDPI or disabled. @@ -14,14 +17,26 @@ public: struct BitmapProvider { virtual ~BitmapProvider() { } + + virtual double GetScale() const = 0; virtual wxBitmap Get() const = 0; - virtual void Set(const wxBitmap&) { } + virtual void Set(const wxBitmapBundle&) { } + + // Simple helpers used in implementation. + bool IsScaled() const { return !wxIsSameDouble(GetScale(), 1); } + wxBitmap GetAtScale(const wxBitmapBundle& b) const + { + return b.GetBitmap(b.GetDefaultSize()*GetScale()); + } }; static GType Type(); static GtkWidget* New(BitmapProvider* provider); static GtkWidget* New(wxWindow* win = NULL); - void Set(const wxBitmap& bitmap); + + // Use bitmaps from the given bundle, the logical bitmap size is the + // default size of the bundle. + void Set(const wxBitmapBundle& bitmapBundle); // This pointer is never null and is owned by this class. BitmapProvider* m_provider; diff --git a/src/gtk/image_gtk.cpp b/src/gtk/image_gtk.cpp index eea80ff277..98ef9a8589 100644 --- a/src/gtk/image_gtk.cpp +++ b/src/gtk/image_gtk.cpp @@ -7,7 +7,6 @@ #include "wx/wxprec.h" -#include "wx/bitmap.h" #include "wx/window.h" #include "wx/gtk/private/wrapgtk.h" @@ -22,45 +21,49 @@ namespace struct BitmapProviderDefault: wxGtkImage::BitmapProvider { BitmapProviderDefault(wxWindow* win) : m_win(win) { } + + virtual double GetScale() const wxOVERRIDE; virtual wxBitmap Get() const wxOVERRIDE; - virtual void Set(const wxBitmap& bitmap) wxOVERRIDE; + virtual void Set(const wxBitmapBundle& bitmap) wxOVERRIDE; + // This pointer can be null if there is no associated window. wxWindow* const m_win; - // The bitmap stored here is only valid if it uses scale factor > 1, - // otherwise we leave it invalid to indicate the drawing the bitmap should - // be left to GtkImage itself. - wxBitmap m_bitmap; + // All the bitmaps we use. + wxBitmapBundle m_bitmapBundle; - // This bitmap is created on demand from m_bitmap when necessary (and is - // mutable because this is done in const Get()). + // This bitmap is created on demand from m_bitmapBundle when necessary (and + // is mutable because this is done in const Get()). mutable wxBitmap m_bitmapDisabled; }; +double BitmapProviderDefault::GetScale() const +{ + return m_win ? m_win->GetDPIScaleFactor() : 1.0; +} + wxBitmap BitmapProviderDefault::Get() const { if ( m_win && !m_win->IsEnabled() ) { - if ( !m_bitmapDisabled.IsOk() && m_bitmap.IsOk() ) - m_bitmapDisabled = m_bitmap.CreateDisabled(); + if ( !m_bitmapDisabled.IsOk() && m_bitmapBundle.IsOk() ) + m_bitmapDisabled = GetAtScale(m_bitmapBundle).CreateDisabled(); return m_bitmapDisabled; } - return m_bitmap; + // We currently don't return the bitmap we use from here when scale is 1, + // as then we can just let GtkImage draw the bitmap it has. + return IsScaled() ? GetAtScale(m_bitmapBundle) : wxBitmap(); } -void BitmapProviderDefault::Set(const wxBitmap& bitmap) +void BitmapProviderDefault::Set(const wxBitmapBundle& bitmapBundle) { - m_bitmap.UnRef(); + m_bitmapBundle = bitmapBundle; // Ensure it's recreated if needed later. m_bitmapDisabled.UnRef(); - if (bitmap.IsOk() && bitmap.GetScaleFactor() > 1) - { - m_bitmap = bitmap; - } } #else // !__WXGTK3__ @@ -69,6 +72,7 @@ void BitmapProviderDefault::Set(const wxBitmap& bitmap) struct BitmapProviderDefault: wxGtkImage::BitmapProvider { BitmapProviderDefault(wxWindow*) { } + virtual double GetScale() const wxOVERRIDE { return 1.0; } virtual wxBitmap Get() const wxOVERRIDE { return wxBitmap(); } }; @@ -110,27 +114,20 @@ GtkWidget* wxGtkImage::New(wxWindow* win) return New(new BitmapProviderDefault(win)); } -void wxGtkImage::Set(const wxBitmap& bitmap) +void wxGtkImage::Set(const wxBitmapBundle& bitmapBundle) { - m_provider->Set(bitmap); + m_provider->Set(bitmapBundle); + + // Always set the default bitmap to use the correct size, even if we draw a + // different bitmap below. + wxBitmap bitmap = bitmapBundle.GetBitmap(wxDefaultSize); GdkPixbuf* pixbuf = NULL; - GdkPixbuf* pixbufNew = NULL; if (bitmap.IsOk()) { - if (bitmap.GetScaleFactor() <= 1) - pixbuf = bitmap.GetPixbuf(); - else - { - // Placeholder pixbuf for correct size - pixbufNew = - pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, false, 8, - int(bitmap.GetScaledWidth()), int(bitmap.GetScaledHeight())); - } + pixbuf = bitmap.GetPixbuf(); } gtk_image_set_from_pixbuf(GTK_IMAGE(this), pixbuf); - if (pixbufNew) - g_object_unref(pixbufNew); } static GtkWidgetClass* wxGtkImageParentClass; @@ -144,9 +141,13 @@ static gboolean wxGtkImageDraw(GtkWidget* widget, GdkEventExpose* event) #endif { wxGtkImage* image = WX_GTK_IMAGE(widget); + const wxBitmap bitmap(image->m_provider->Get()); if (!bitmap.IsOk()) { + // Just let GtkImage draw the bitmap in standard DPI. Arguably, we + // should still be drawing it ourselves even in this case just for + // consistency, but for now keep the original behaviour. #ifdef __WXGTK3__ return wxGtkImageParentClass->draw(widget, cr); #else @@ -154,13 +155,24 @@ static gboolean wxGtkImageDraw(GtkWidget* widget, GdkEventExpose* event) #endif } + const double scaleFactor = image->m_provider->GetScale(); + GtkAllocation alloc; gtk_widget_get_allocation(widget, &alloc); - int x = (alloc.width - int(bitmap.GetScaledWidth() )) / 2; - int y = (alloc.height - int(bitmap.GetScaledHeight())) / 2; + int x = (alloc.width - int(bitmap.GetWidth() /scaleFactor)) / 2; + int y = (alloc.height - int(bitmap.GetHeight()/scaleFactor)) / 2; #ifdef __WXGTK3__ gtk_render_background(gtk_widget_get_style_context(widget), cr, 0, 0, alloc.width, alloc.height); + + if (!wxIsSameDouble(scaleFactor, 1)) + { + cairo_translate(cr, x, y); + const double scale = 1 / scaleFactor; + cairo_scale(cr, scale, scale); + x = 0; + y = 0; + } bitmap.Draw(cr, x, y); #else x += alloc.x; diff --git a/src/gtk/toolbar.cpp b/src/gtk/toolbar.cpp index 9429a5fb33..fdb45ed270 100644 --- a/src/gtk/toolbar.cpp +++ b/src/gtk/toolbar.cpp @@ -176,10 +176,17 @@ namespace struct BitmapProvider: wxGtkImage::BitmapProvider { BitmapProvider(wxToolBarTool* tool) : m_tool(tool) { } + + virtual double GetScale() const wxOVERRIDE; virtual wxBitmap Get() const wxOVERRIDE; wxToolBarTool* const m_tool; }; +double BitmapProvider::GetScale() const +{ + return m_tool->GetToolBar()->GetDPIScaleFactor(); +} + wxBitmap BitmapProvider::Get() const { #ifdef __WXGTK3__ From 0f5c2851f40facef6fe8e7f603df2cc6a90250a1 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 16 Oct 2021 20:01:28 +0200 Subject: [PATCH 5/6] Add wxToolBarTool::Get{Normal,Disabled}BitmapBundle() accessors The existing variants returning wxBitmap are insufficient for non-MSW ports where the toolbar bitmap size is unavailable otherwise, as GetToolBitmapSize() value doesn't really correspond to it (which is a problem on its own, but there is not much that can be done about it by now). Having these functions allows to retrieve the actually used bitmap size by using wxBitmapBundle::GetDefaultSize(). --- include/wx/tbarbase.h | 3 +++ interface/wx/toolbar.h | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/include/wx/tbarbase.h b/include/wx/tbarbase.h index c489d4c068..b6addcc4cf 100644 --- a/include/wx/tbarbase.h +++ b/include/wx/tbarbase.h @@ -146,6 +146,9 @@ public: { return m_kind == wxITEM_CHECK || m_kind == wxITEM_RADIO; } // attributes + wxBitmapBundle GetNormalBitmapBundle() const { return m_bmpNormal; } + wxBitmapBundle GetDisabledBitmapBundle() const { return m_bmpDisabled; } + wxBitmap GetNormalBitmap(const wxSize& size = wxDefaultSize) const { return m_bmpNormal.GetBitmap(size); } wxBitmap GetDisabledBitmap(const wxSize& size = wxDefaultSize) const diff --git a/interface/wx/toolbar.h b/interface/wx/toolbar.h index 35474cf509..19cf6a4506 100644 --- a/interface/wx/toolbar.h +++ b/interface/wx/toolbar.h @@ -112,6 +112,26 @@ public: bool IsToggled() const; bool CanBeToggled() const; + /** + Return the bundle containing normal tool bitmaps. + + This bundle may be invalid if the tool doesn't show a bitmap. + + @since 3.1.6 + */ + wxBitmapBundle GetNormalBitmapBundle() const; + + /** + Return the bundle containing disabled tool bitmaps. + + This bundle may be invalid if the tool doesn't show a bitmap or doesn't + have a specific disabled bitmap creates one automatically from the + normal bitmap. + + @since 3.1.6 + */ + wxBitmapBundle GetDisabledBitmapBundle() const; + wxBitmap GetNormalBitmap() const; wxBitmap GetDisabledBitmap() const; From 3333356624cf2fd8d2ad1d89690c51d7a88076af Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 16 Oct 2021 20:05:51 +0200 Subject: [PATCH 6/6] Really support wxBitmapBundle in wxGTK wxToolBar Use the appropriately sized bitmap instead of always using the (scaled version of the) default one, as was the case since 97f6c85d9b (Add first version of wxBitmapBundle and use it in wxToolBar, 2021-09-24). With this change, toolbar bitmaps work correctly in 100% and 200% DPI scaling. --- src/gtk/toolbar.cpp | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/gtk/toolbar.cpp b/src/gtk/toolbar.cpp index fdb45ed270..e2f1c121ae 100644 --- a/src/gtk/toolbar.cpp +++ b/src/gtk/toolbar.cpp @@ -190,29 +190,27 @@ double BitmapProvider::GetScale() const wxBitmap BitmapProvider::Get() const { #ifdef __WXGTK3__ - wxBitmap bitmap(m_tool->GetNormalBitmap()); - if (m_tool->IsEnabled()) + if (!m_tool->IsEnabled()) { - if (bitmap.IsOk() && bitmap.GetScaleFactor() <= 1) - bitmap.UnRef(); - } - else - { - wxBitmap disabled(m_tool->GetDisabledBitmap()); + wxBitmap disabled(GetAtScale(m_tool->GetDisabledBitmapBundle())); // if no disabled bitmap and normal bitmap is scaled - if (!disabled.IsOk() && bitmap.IsOk() && bitmap.GetScaleFactor() > 1) + if (!disabled.IsOk() && IsScaled()) { // make scaled disabled bitmap from normal one - disabled = bitmap.CreateDisabled(); + wxBitmap bitmap(GetAtScale(m_tool->GetNormalBitmapBundle())); + if (bitmap.IsOk()) + disabled = bitmap.CreateDisabled(); } - bitmap = disabled; + return disabled; } + + if (IsScaled()) + return GetAtScale(m_tool->GetNormalBitmapBundle()); #else - wxBitmap bitmap; if (!m_tool->IsEnabled()) - bitmap = m_tool->GetDisabledBitmap(); + return m_tool->GetDisabledBitmap(); #endif - return bitmap; + return wxBitmap(); } } // namespace