diff --git a/docs/doxygen/mainpages/const_cpp.h b/docs/doxygen/mainpages/const_cpp.h index 0c9fdd9b8b..cf3bacf7ab 100644 --- a/docs/doxygen/mainpages/const_cpp.h +++ b/docs/doxygen/mainpages/const_cpp.h @@ -167,6 +167,9 @@ Currently the following symbols exist: have an efficient (CPU-specific) implementation. Notice that the functions themselves are always available but can be prohibitively slow to use when implemented in a generic way, using a critical section.} +@itemdef{wxHAS_BITMAP_SCALE_FACTOR, Defined in @c wx/bitmap.h if bitmaps + actually use scale factor under the current platform, see + wxBitmap::SetScaleFactor().} @itemdef{wxHAS_BITMAPTOGGLEBUTTON, Defined in @c wx/tglbtn.h if wxBitmapToggleButton class is available in addition to wxToggleButton.} @itemdef{wxHAS_CONFIG_TEMPLATE_RW, Defined if the currently used compiler diff --git a/include/wx/bitmap.h b/include/wx/bitmap.h index e4cb25302f..6b31c71c37 100644 --- a/include/wx/bitmap.h +++ b/include/wx/bitmap.h @@ -177,8 +177,7 @@ public: virtual bool Create(int width, int height, int depth = wxBITMAP_SCREEN_DEPTH) = 0; virtual bool Create(const wxSize& sz, int depth = wxBITMAP_SCREEN_DEPTH) = 0; - virtual bool CreateScaled(int w, int h, int d, double logicalScale) - { return Create(wxRound(w*logicalScale), wxRound(h*logicalScale), d); } + virtual bool CreateScaled(int w, int h, int d, double logicalScale); virtual int GetHeight() const = 0; virtual int GetWidth() const = 0; @@ -188,11 +187,11 @@ public: { return wxSize(GetWidth(), GetHeight()); } // support for scaled bitmaps - virtual double GetScaleFactor() const { return 1.0; } - virtual double GetScaledWidth() const { return GetWidth() / GetScaleFactor(); } - virtual double GetScaledHeight() const { return GetHeight() / GetScaleFactor(); } - virtual wxSize GetScaledSize() const - { return wxSize(wxRound(GetScaledWidth()), wxRound(GetScaledHeight())); } + virtual void SetScaleFactor(double scale); + virtual double GetScaleFactor() const; + virtual double GetScaledWidth() const; + virtual double GetScaledHeight() const; + virtual wxSize GetScaledSize() const; #if wxUSE_IMAGE virtual wxImage ConvertToImage() const = 0; diff --git a/include/wx/bmpbndl.h b/include/wx/bmpbndl.h index 6609e649a0..e55ba08f44 100644 --- a/include/wx/bmpbndl.h +++ b/include/wx/bmpbndl.h @@ -149,12 +149,7 @@ wxBitmapBundle wxBitmapBundle::FromBitmaps(const wxBitmap& bitmap1, /* static */ inline wxBitmapBundle wxBitmapBundle::FromBitmap(const wxBitmap& bitmap) { - if ( !bitmap.IsOk() ) - return wxBitmapBundle(); - - wxVector bitmaps; - bitmaps.push_back(bitmap); - return FromBitmaps(bitmaps); + return wxBitmapBundle(bitmap); } /* static */ inline diff --git a/include/wx/gtk/bitmap.h b/include/wx/gtk/bitmap.h index 2f17a24e5c..2a6f9c62f5 100644 --- a/include/wx/gtk/bitmap.h +++ b/include/wx/gtk/bitmap.h @@ -17,6 +17,10 @@ typedef struct _GdkPixbuf GdkPixbuf; class WXDLLIMPEXP_FWD_CORE wxPixelDataBase; class WXDLLIMPEXP_FWD_CORE wxCursor; +#ifdef __WXGTK3__ + #define wxHAS_BITMAP_SCALE_FACTOR +#endif + //----------------------------------------------------------------------------- // wxMask //----------------------------------------------------------------------------- @@ -85,8 +89,9 @@ public: { return Create(sz.GetWidth(), sz.GetHeight(), depth); } bool Create(int width, int height, const wxDC& WXUNUSED(dc)) { return Create(width,height); } -#ifdef __WXGTK3__ +#ifdef wxHAS_BITMAP_SCALE_FACTOR virtual bool CreateScaled(int w, int h, int depth, double scale) wxOVERRIDE; + virtual void SetScaleFactor(double scale) wxOVERRIDE; virtual double GetScaleFactor() const wxOVERRIDE; #endif diff --git a/include/wx/msw/bitmap.h b/include/wx/msw/bitmap.h index 4d661ff13c..21ebdec47f 100644 --- a/include/wx/msw/bitmap.h +++ b/include/wx/msw/bitmap.h @@ -158,8 +158,7 @@ public: virtual bool Create(int width, int height, const wxDC& dc); virtual bool Create(const void* data, wxBitmapType type, int width, int height, int depth = 1); - virtual bool CreateScaled(int w, int h, int d, double logicalScale) - { return Create(wxRound(w*logicalScale), wxRound(h*logicalScale), d); } + virtual bool CreateScaled(int w, int h, int d, double logicalScale); virtual bool LoadFile(const wxString& name, wxBitmapType type = wxBITMAP_DEFAULT_TYPE); virtual bool SaveFile(const wxString& name, wxBitmapType type, const wxPalette *cmap = NULL) const; @@ -185,12 +184,12 @@ public: void UseAlpha(bool use = true); void ResetAlpha() { UseAlpha(false); } - // support for scaled bitmaps - virtual double GetScaleFactor() const { return 1.0; } - virtual double GetScaledWidth() const { return GetWidth() / GetScaleFactor(); } - virtual double GetScaledHeight() const { return GetHeight() / GetScaleFactor(); } - virtual wxSize GetScaledSize() const - { return wxSize(wxRound(GetScaledWidth()), wxRound(GetScaledHeight())); } + // provide stabs of scaled bitmaps functions, they are trivial here + virtual void SetScaleFactor(double scale); + virtual double GetScaleFactor() const; + virtual double GetScaledWidth() const; + virtual double GetScaledHeight() const; + virtual wxSize GetScaledSize() const; // implementation only from now on // ------------------------------- diff --git a/include/wx/osx/bitmap.h b/include/wx/osx/bitmap.h index bae0e68537..4f85b77fcb 100644 --- a/include/wx/osx/bitmap.h +++ b/include/wx/osx/bitmap.h @@ -13,6 +13,8 @@ #include "wx/palette.h" +#define wxHAS_BITMAP_SCALE_FACTOR + // Bitmap class WXDLLIMPEXP_FWD_CORE wxBitmap; class wxBitmapRefData ; @@ -239,6 +241,7 @@ public: void EndRawAccess(); #endif + void SetScaleFactor(double scale) wxOVERRIDE; double GetScaleFactor() const wxOVERRIDE; void SetSelectedInto(wxDC *dc); diff --git a/interface/wx/bitmap.h b/interface/wx/bitmap.h index 734605e350..c45a34a583 100644 --- a/interface/wx/bitmap.h +++ b/interface/wx/bitmap.h @@ -402,6 +402,15 @@ public: */ static void CleanUpHandlers(); + /** + Returns disabled (dimmed) version of the bitmap. + + This method is not available when wxUSE_IMAGE == 0. + + @since 2.9.0 + */ + wxBitmap ConvertToDisabled(unsigned char brightness = 255) const; + /** Creates an image from a platform-dependent bitmap. This preserves mask information so that bitmaps and images can be converted back @@ -460,7 +469,7 @@ public: @param depth The number of bits used to represent each bitmap pixel. @param logicalScale - Scale factor used by the bitmap + Scale factor used by the bitmap, see SetScaleFactor(). @return @true if the creation was successful. @@ -542,7 +551,7 @@ public: /** Gets the height of the bitmap in pixels. - @see GetWidth(), GetSize() + @see GetWidth(), GetSize(), GetScaledHeight() */ virtual int GetHeight() const; @@ -568,28 +577,75 @@ public: */ virtual wxBitmap GetSubBitmap(const wxRect& rect) const; + /** + Returns the scale factor of this bitmap. + + Scale factor is 1 by default, but can be greater to indicate that the + size of bitmap in logical, DPI-independent pixels is smaller than its + actual size in physical pixels. Bitmaps with scale factor greater than + 1 must be used in high DPI to appear sharp on the screen. + + Note that the scale factor is only used in the ports where logical + pixels are not the same as physical ones, such as wxOSX or wxGTK3, and + this function always returns 1 under the other platforms. + + @see SetScaleFactor(), GetScaledWidth(), GetScaledHeight(), GetScaledSize() + + @since 2.9.5 + */ + virtual double GetScaleFactor() const; + + /** + Returns the scaled height of the bitmap. + + See GetScaledSize() for more information. + + @see GetScaledWidth(), GetHeight() + + @since 2.9.5 + */ + virtual double GetScaledHeight() const; + + /** + Returns the scaled size of the bitmap. + + The scaled size of the bitmap is its size in pixels, as returned by + GetSize(), divided by its scale factor, as returned by + GetScaleFactor(), and so is the same as the normal size for bitmaps + with the default scale factor of 1 and always less than the physical + size for the higher resolution bitmaps supposed to be used on high DPI + screens. + + @see GetScaledWidth(), GetScaledHeight(), GetSize() + + @since 2.9.5 + */ + virtual wxSize GetScaledSize() const; + + /** + Returns the scaled width of the bitmap. + + See GetScaledSize() for more information. + + @see GetScaledHeight(), GetWidth() + + @since 2.9.5 + */ + virtual double GetScaledWidth() const; + /** Returns the size of the bitmap in pixels. @since 2.9.0 - @see GetHeight(), GetWidth() + @see GetHeight(), GetWidth(), GetScaledSize() */ wxSize GetSize() const; - /** - Returns disabled (dimmed) version of the bitmap. - - This method is not available when wxUSE_IMAGE == 0. - - @since 2.9.0 - */ - wxBitmap ConvertToDisabled(unsigned char brightness = 255) const; - /** Gets the width of the bitmap in pixels. - @see GetHeight(), GetSize() + @see GetHeight(), GetSize(), GetScaledWidth() */ virtual int GetWidth() const; @@ -741,6 +797,38 @@ public: */ virtual void SetHeight(int height); + /** + Sets the bitmap scale factor. + + This doesn't change the bitmap actual size or its contents, but changes + its scale factor, so that it appears in a smaller size when it is drawn + on screen: e.g. setting @a scale to 2 means that the bitmap will be + twice smaller (in each direction) when drawn on screen in the ports in + which logical and physical pixels differ (i.e. wxOSX and wxGTK3, but + not wxMSW). + + When creating a new bitmap, CreateScaled() can be used to specify the + correct scale factor from the beginning. + + Note that this method exists in all ports, but simply does nothing in + those of them that don't use logical pixel scaling. The preprocessor + symbol @c wxHAS_BITMAP_SCALE_FACTOR can be tested to determine whether + the scale factor is really supported, e.g. + + @code + bitmap.SetScaleFactor(2); + + // In the other ports scale factor is always 1, so the assert would + // fail there. + #ifdef wxHAS_BITMAP_SCALE_FACTOR + wxASSERT( bitmap.GetScaleFactor() == 2 ); + #endif + @endcode + + @since 3.1.6 + */ + virtual void SetScaleFactor(double scale); + /** Sets the mask for this bitmap. diff --git a/src/common/bmpbase.cpp b/src/common/bmpbase.cpp index 7634981844..1136d1f1d2 100644 --- a/src/common/bmpbase.cpp +++ b/src/common/bmpbase.cpp @@ -196,6 +196,39 @@ public: wxIMPLEMENT_DYNAMIC_CLASS(wxBitmapBaseModule, wxModule); +// ---------------------------------------------------------------------------- +// Trivial implementations of scale-factor related functions +// ---------------------------------------------------------------------------- + +bool wxBitmapBase::CreateScaled(int w, int h, int d, double logicalScale) +{ + return Create(wxRound(w*logicalScale), wxRound(h*logicalScale), d); +} + +void wxBitmapBase::SetScaleFactor(double WXUNUSED(scale)) +{ +} + +double wxBitmapBase::GetScaleFactor() const +{ + return 1.0; +} + +double wxBitmapBase::GetScaledWidth() const +{ + return GetWidth() / GetScaleFactor(); +} + +double wxBitmapBase::GetScaledHeight() const +{ + return GetHeight() / GetScaleFactor(); +} + +wxSize wxBitmapBase::GetScaledSize() const +{ + return wxSize(wxRound(GetScaledWidth()), wxRound(GetScaledHeight())); +} + #endif // wxUSE_BITMAP_BASE // ---------------------------------------------------------------------------- diff --git a/src/common/bmpbndl.cpp b/src/common/bmpbndl.cpp index becd229c9c..af6702d08a 100644 --- a/src/common/bmpbndl.cpp +++ b/src/common/bmpbndl.cpp @@ -110,6 +110,11 @@ private: // Note that this vector is never empty. Entries m_entries; + // The size of the bitmap at the default size. + // + // Note that it may be different from the size of the first entry if we + // only have high resolution bitmap and no bitmap for 100% DPI. + wxSize m_sizeDefault; // Common implementation of all ctors. void Init(const wxBitmap* bitmaps, size_t n); @@ -141,6 +146,13 @@ void wxBitmapBundleImplSet::Init(const wxBitmap* bitmaps, size_t n) std::sort(m_entries.begin(), m_entries.end(), BitmapSizeComparator()); + // This is not normally the case, but it could happen that even the + // smallest bitmap has scale factor > 1, so use its scaled size (this can + // notably be the case when there is only a single high resolution bitmap + // provided, e.g. in the code predating wxBitmapBundle introduction but now + // using it due to implicit conversion to it from wxBitmap). + m_sizeDefault = m_entries[0].bitmap.GetScaledSize(); + // Should we check that all bitmaps really have unique sizes here? For now, // don't bother with this, but we might want to do it later if it really // turns out to be a problem in practice. @@ -152,8 +164,7 @@ void wxBitmapBundleImplSet::Init(const wxBitmap* bitmaps, size_t n) wxSize wxBitmapBundleImplSet::GetDefaultSize() const { - // Default size is the size of the smallest bitmap in the bundle. - return m_entries[0].bitmap.GetSize(); + return m_sizeDefault; } wxSize wxBitmapBundleImplSet::GetPreferredSizeAtScale(double scale) const @@ -384,7 +395,16 @@ wxBitmap wxBitmapBundle::GetBitmap(const wxSize& size) const if ( !m_impl ) return wxBitmap(); - return m_impl->GetBitmap(size == wxDefaultSize ? GetDefaultSize() : size); + const wxSize sizeDef = GetDefaultSize(); + + wxBitmap bmp = m_impl->GetBitmap(size == wxDefaultSize ? sizeDef : size); + + // Ensure that the returned bitmap uses the scale factor such that it takes + // the same space, in logical pixels, as the bitmap in the default size. + if ( size != wxDefaultSize ) + bmp.SetScaleFactor(static_cast(size.y)/sizeDef.y); + + return bmp; } // ============================================================================ diff --git a/src/gtk/bitmap.cpp b/src/gtk/bitmap.cpp index 658480e5ff..c6b229e211 100644 --- a/src/gtk/bitmap.cpp +++ b/src/gtk/bitmap.cpp @@ -1002,6 +1002,13 @@ bool wxBitmap::CreateScaled(int w, int h, int depth, double scale) return true; } +void wxBitmap::SetScaleFactor(double scale) +{ + wxCHECK_RET(m_refData, "invalid bitmap"); + + M_BMPDATA->m_scaleFactor = scale; +} + double wxBitmap::GetScaleFactor() const { wxCHECK_MSG(m_refData, -1, "invalid bitmap"); diff --git a/src/gtk/image_gtk.cpp b/src/gtk/image_gtk.cpp index 98ef9a8589..518b313f99 100644 --- a/src/gtk/image_gtk.cpp +++ b/src/gtk/image_gtk.cpp @@ -155,24 +155,13 @@ 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.GetWidth() /scaleFactor)) / 2; - int y = (alloc.height - int(bitmap.GetHeight()/scaleFactor)) / 2; + int x = (alloc.width - int(bitmap.GetScaledWidth() )) / 2; + int y = (alloc.height - int(bitmap.GetScaledHeight())) / 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/msw/bitmap.cpp b/src/msw/bitmap.cpp index 30b2d439a6..c63dd61089 100644 --- a/src/msw/bitmap.cpp +++ b/src/msw/bitmap.cpp @@ -745,6 +745,11 @@ bool wxBitmap::Create(int width, int height, const wxDC& dc) return false; } +bool wxBitmap::CreateScaled(int w, int h, int d, double logicalScale) +{ + return Create(wxRound(w*logicalScale), wxRound(h*logicalScale), d); +} + bool wxBitmap::DoCreate(int w, int h, int d, WXHDC hdc) { UnRef(); @@ -1357,6 +1362,39 @@ bool wxBitmap::InitFromHBITMAP(WXHBITMAP bmp, int width, int height, int depth) return IsOk(); } +// ---------------------------------------------------------------------------- +// scale factor-related functions +// ---------------------------------------------------------------------------- + +// Note: currently we don't use scale factor at all and don't even store it +// because this seems useless, but we define these functions out of line here +// and not inline in the header to make it possible to change this later +// without breaking ABI if necessary. + +void wxBitmap::SetScaleFactor(double WXUNUSED(scale)) +{ +} + +double wxBitmap::GetScaleFactor() const +{ + return 1.0; +} + +double wxBitmap::GetScaledWidth() const +{ + return GetWidth(); +} + +double wxBitmap::GetScaledHeight() const +{ + return GetHeight(); +} + +wxSize wxBitmap::GetScaledSize() const +{ + return GetSize(); +} + // ---------------------------------------------------------------------------- // raw bitmap access support // ---------------------------------------------------------------------------- diff --git a/src/osx/core/bitmap.cpp b/src/osx/core/bitmap.cpp index d7211a50e4..2f82e5d9a2 100644 --- a/src/osx/core/bitmap.cpp +++ b/src/osx/core/bitmap.cpp @@ -75,6 +75,8 @@ public: int GetBytesPerRow() const; bool HasAlpha() const; WXImage GetImage() const; + + void SetScaleFactor(double scale) { m_scaleFactor = scale; } double GetScaleFactor() const { return m_scaleFactor; } const void *GetRawAccess() const; @@ -1389,6 +1391,13 @@ int wxBitmap::GetWidth() const return GetBitmapData()->GetWidth() ; } +void wxBitmap::SetScaleFactor(double scale) +{ + wxCHECK_RET( IsOk(), wxT("invalid bitmap") ); + + return GetBitmapData()->SetScaleFactor(scale) ; +} + double wxBitmap::GetScaleFactor() const { wxCHECK_MSG( IsOk(), -1, wxT("invalid bitmap") ); diff --git a/tests/graphics/bmpbundle.cpp b/tests/graphics/bmpbundle.cpp index b14a25148c..b08fafc26a 100644 --- a/tests/graphics/bmpbundle.cpp +++ b/tests/graphics/bmpbundle.cpp @@ -68,6 +68,40 @@ TEST_CASE("BitmapBundle::GetPreferredSize", "[bmpbundle]") CHECK( b.GetPreferredSizeAtScale(3 ) == bigger ); } +#ifdef wxHAS_BITMAP_SCALE_FACTOR + +TEST_CASE("BitmapBundle::Scaled", "[bmpbundle]") +{ + // Adding a bitmap with scale factor > 1 should create the bundle using the + // scaled size as default size. + wxBitmap scaled2x(64, 64); + scaled2x.SetScaleFactor(2); + CHECK( scaled2x.GetScaledSize() == wxSize(32, 32) ); + + wxBitmapBundle b(scaled2x); + CHECK( b.GetDefaultSize() == wxSize(32, 32) ); + + // Retrieving this bitmap back from the bundle should preserve its scale. + scaled2x = b.GetBitmap(wxSize(64, 64)); + CHECK( scaled2x.GetSize() == wxSize(64, 64) ); + CHECK( scaled2x.GetScaleFactor() == 2 ); + + // And retrieving the bitmap from the bundle should set scale factor for it + // even if it hadn't originally been added with it. + b = wxBitmapBundle::FromBitmaps(wxBitmap(32, 32), wxBitmap(64, 64)); + scaled2x = b.GetBitmap(wxSize(64, 64)); + CHECK( scaled2x.GetSize() == wxSize(64, 64) ); + CHECK( scaled2x.GetScaleFactor() == 2 ); + + // Using scaled bitmaps when there is more than one of them is a bad idea + // in general, as only physical size matters, but the default size should + // still be the scaled size of the smallest one. + b = wxBitmapBundle::FromBitmaps(scaled2x, wxBitmap(64, 64)); + CHECK( b.GetDefaultSize() == wxSize(32, 32) ); +} + +#endif // wxHAS_BITMAP_SCALE_FACTOR + #ifdef wxHAS_SVG TEST_CASE("BitmapBundle::FromSVG", "[bmpbundle][svg]")