wxBitmapBundle API proposal

61 views
Skip to first unread message

Vadim Zeitlin

unread,
Sep 22, 2021, 7:07:44 PM9/22/21
to wx-dev
Hello again,

Here is the first draft of the API that I've half-implemented (for MSW
only, without SVG support yet) so far, in wx/bmpbndl.h file:

---------------------------------- >8 --------------------------------------
// ----------------------------------------------------------------------------
// wxBitmapBundle provides 1 or more versions of a bitmap, all bundled together
// ----------------------------------------------------------------------------

// This class has value semantics and can be copied cheaply.

class WXDLLIMPEXP_CORE wxBitmapBundle
{
public:
// Default ctor constructs an empty bundle which can't be used for
// anything, but can be assigned something later.
wxBitmapBundle();

// This conversion ctor from a single bitmap does the same thing as
// FromBitmap() and only exists for interoperability with the existing code
// using wxBitmap.
wxBitmapBundle(const wxBitmap& bitmap);

// Another conversion ctor from a single image: this one is needed to allow
// passing wxImage to the functions that used to take wxBitmap but now take
// wxBitmapBundle.
wxBitmapBundle(const wxImage& image);

// Default copy ctor and assignment operator and dtor would be ok, but need
// to be defined out of line, where wxBitmapBundleImpl is fully declared.

wxBitmapBundle(const wxBitmapBundle& other);
wxBitmapBundle& operator=(const wxBitmapBundle& other);

~wxBitmapBundle();


// Create from the given collection of bitmaps.
static wxBitmapBundle FromBitmaps(const wxVector<wxBitmap>& bitmaps);
static wxBitmapBundle FromBitmaps(const wxBitmap& bitmap1,
const wxBitmap& bitmap2);

// Create from a single bitmap (this is only useful for compatibility
// with the existing code).
static wxBitmapBundle FromBitmap(const wxBitmap& bitmap);
static wxBitmapBundle FromImage(const wxImage& image);

// Create from the SVG data (data is supposed to be in UTF-8 encoding).
static wxBitmapBundle FromSVG(const char* data);

// Create from the resources: all existing versions of the bitmap of the
// form name_2x or name@2x (and also using other factors) will be used.
static wxBitmapBundle FromResources(const wxString& name);


// Check if bitmap bundle is non-empty.
bool IsOk() const { return m_impl; }

// Get bitmap of the specified size, creating a new bitmap from the closest
// available size by rescaling it if necessary.
wxBitmap GetBitmap(const wxSize size) const;

// Get the primary, i.e. smallest, bitmap. Note that this function can
// return an invalid bitmap for the bundles not created from a collection
// of bitmaps and only exists for backwards compatibility, i.e. when a
// wxBitmap is needed by some legacy function.
wxBitmap GetPrimary() const;

private:
typedef wxObjectDataPtr<wxBitmapBundleImpl> wxBitmapBundleImplPtr;

// Private ctor used by static factory functions to create objects of this
// class.
explicit wxBitmapBundle(const wxBitmapBundleImplPtr& impl);

wxBitmapBundleImplPtr m_impl;
};

// This macro can be used to create a bundle from resources on the platforms
// that support it and from name_png and name_2x_png on the other ones.
#ifdef wxHAS_IMAGE_RESOURCES
#define wxBITMAP_BUNDLE_2(name) wxBitmapBundle::FromResources(#name)
#else
#define wxBITMAP_BUNDLE_2(name) \
wxBitmapBundle::FromBitmaps(wxBITMAP_PNG_FROM_DATA(name), \
wxBITMAP_PNG_FROM_DATA(name##_2x))
#endif
---------------------------------- >8 --------------------------------------

Using this class and I can compile the minimal sample with only minimal
changes. It still doesn't work 100% because I didn't implement updating the
toolbar bitmaps on DPI change in wxMSW, but this should be possible and
I'll try to do it soon.

In the meanwhile, please let me know if you have any strong feelings about
the header file and class names or the API.

Thanks,
VZ

Eric Jensen

unread,
Sep 22, 2021, 7:37:36 PM9/22/21
to Vadim Zeitlin
Hello Vadim,

Thursday, September 23, 2021, 1:07:42 AM, you wrote:

VZ> // Create from the given collection of bitmaps.
VZ> static wxBitmapBundle FromBitmaps(const wxVector<wxBitmap>& bitmaps);
VZ> static wxBitmapBundle FromBitmaps(const wxBitmap& bitmap1,
VZ> const wxBitmap& bitmap2);
I'd like an additional Add/Append method to add individual bitmaps
one-by-one.


VZ> // Create from the SVG data (data is supposed to be in UTF-8 encoding).
VZ> static wxBitmapBundle FromSVG(const char* data);
How about loading from file by passing a pathname or a wxInputStream?


VZ> // Get bitmap of the specified size, creating a new bitmap from the closest
VZ> // available size by rescaling it if necessary.
VZ> wxBitmap GetBitmap(const wxSize size) const;
I think scaling should always be done from the largest bitmap
available, not the nearest size. Maybe add wxImageResizeQuality
parameter?


VZ> // Get the primary, i.e. smallest, bitmap. Note that this function can
VZ> // return an invalid bitmap for the bundles not created from a collection
VZ> // of bitmaps and only exists for backwards compatibility, i.e. when a
VZ> // wxBitmap is needed by some legacy function.
VZ> wxBitmap GetPrimary() const;
What's the primary bitmap, if the only source is an SVG?

Regards,
Eric

Vadim Zeitlin

unread,
Sep 23, 2021, 8:33:54 AM9/23/21
to wx-...@googlegroups.com
On Thu, 23 Sep 2021 01:37:33 +0200 Eric Jensen wrote:

EJ> Hello Vadim,

Hi and thanks for your comments!

EJ> Thursday, September 23, 2021, 1:07:42 AM, you wrote:
EJ>
EJ> VZ> // Create from the given collection of bitmaps.
EJ> VZ> static wxBitmapBundle FromBitmaps(const wxVector<wxBitmap>& bitmaps);
EJ> VZ> static wxBitmapBundle FromBitmaps(const wxBitmap& bitmap1,
EJ> VZ> const wxBitmap& bitmap2);
EJ> I'd like an additional Add/Append method to add individual bitmaps
EJ> one-by-one.

I can't implement this with the current design because it's impossible to
add a bitmap to vector bundle, i.e. one created from SVG. I did think about
adding a different wxBitmapCollection class which would really just contain
a collection (vector) of bitmaps and so could have Add() method, and then
allow creating wxBitmapBundle from that, but finally decided not to do it
because this wxBitmapCollection just doesn't seem sufficiently different
from wxVector<wxBitmap> to be worth having.

EJ> VZ> // Create from the SVG data (data is supposed to be in UTF-8 encoding).
EJ> VZ> static wxBitmapBundle FromSVG(const char* data);
EJ> How about loading from file by passing a pathname or a wxInputStream?

This could be added later, yes. For now I've tried to keep the API as
small as possible.

EJ> VZ> // Get bitmap of the specified size, creating a new bitmap from the closest
EJ> VZ> // available size by rescaling it if necessary.
EJ> VZ> wxBitmap GetBitmap(const wxSize size) const;
EJ> I think scaling should always be done from the largest bitmap
EJ> available, not the nearest size. Maybe add wxImageResizeQuality
EJ> parameter?

I agree with you, it should be done from the nearest larger bitmap, but I
think it should be unconditional. I also hope that using the highest
quality shouldn't be a problem, so for now I would keep it simple. But we
can always extend it later, of course.

EJ> VZ> // Get the primary, i.e. smallest, bitmap. Note that this function can
EJ> VZ> // return an invalid bitmap for the bundles not created from a collection
EJ> VZ> // of bitmaps and only exists for backwards compatibility, i.e. when a
EJ> VZ> // wxBitmap is needed by some legacy function.
EJ> VZ> wxBitmap GetPrimary() const;
EJ> What's the primary bitmap, if the only source is an SVG?

As the comment above says, the function returns invalid/null bitmap in
this case. It's not ideal to have this function at all for this reason, but
I'm afraid it's going to be needed to keep the existing code working
without too many changes.

But if we could just use GetBitmap() everywhere, it would be even better,
of course, and if by the time this is fully implemented I see that we don't
really need GetPrimary(), I'd like to remove it completely.

Thanks again for your comments,
VZ

Eric Jensen

unread,
Sep 23, 2021, 8:54:44 AM9/23/21
to Vadim Zeitlin
Hello Vadim,

Thursday, September 23, 2021, 2:33:51 PM, you wrote:

VZ> On Thu, 23 Sep 2021 01:37:33 +0200 Eric Jensen wrote:

EJ>> Hello Vadim,

VZ> Hi and thanks for your comments!

EJ>> Thursday, September 23, 2021, 1:07:42 AM, you wrote:
EJ>>
EJ>> VZ> // Create from the given collection of bitmaps.
EJ>> VZ> static wxBitmapBundle FromBitmaps(const wxVector<wxBitmap>& bitmaps);
EJ>> VZ> static wxBitmapBundle FromBitmaps(const wxBitmap& bitmap1,
EJ>> VZ> const wxBitmap& bitmap2);
EJ>> I'd like an additional Add/Append method to add individual bitmaps
EJ>> one-by-one.

VZ> I can't implement this with the current design because it's impossible to
VZ> add a bitmap to vector bundle, i.e. one created from SVG.

I don't quite understand that. I thought wxBitmapBundle would contain
a list of "source" bitmaps, so it should be able to append one?

If the user adds a SVG source, all source bitmaps should be discarded.

Additionally, wxBitmapBundle would contain a list of "generated"
bitmaps as cache. If the user code requests a new size, the bitmap
would be generated from either a "source" bitmap or the SVG and added
to that list.

Two different list of bitmaps would be necessary because generating a
new bitmap should always use one of the "source" bitmaps.

BTW: When creating a bitmap from SVG, will the background always be
transparent, or can the user set a background color?

Eric




--

Stefan Csomor

unread,
Sep 23, 2021, 9:07:50 AM9/23/21
to wx-...@googlegroups.com
Hi Vadim

Thanks for your draft. I'm absolutely fine with the class name and the concept of it.

A few thoughts:

If we keep the idea of several 'resolutions' then I think we still should have a Device-Independent Size for a wxBitmapBundle. That way a wxBitmapBundle can be drawn onto a dc and the best fit would be selected for rendering.

I think there still should be an AddBitmap or something like that.

As I wrote in the discussion before, I think the API should have a constructor taking eg a wxBitmapRenderer, together with data, and eventually a Device-Independent size. wxSVGRenderer would be an example. Upon needing a certain pixel dimension this bitmap would be rendered once and then stored.

For macOS and iOS I would add a native 'getter' on the bitmap bundle, that returns the corresponding NSImage. As in places where wxBitmap was used before, wxBimapBundle will have to be used in macOS, otherwise automatic Retina Support would be lost.

Just my 0.02$

Best,

Stefan

Vadim Zeitlin

unread,
Sep 23, 2021, 10:13:46 AM9/23/21
to wx-...@googlegroups.com
On Thu, 23 Sep 2021 14:53:50 +0200 Eric Jensen wrote:

EJ> I don't quite understand that. I thought wxBitmapBundle would contain
EJ> a list of "source" bitmaps,

Yes, exactly, so e.g. FromResource() contains an object which just
contains the name of the resource initially and FromSVG() just contains the
internal SVG representation.

EJ> so it should be able to append one?

While I could see how could appending a bitmap to a resource-based object
be implemented, I don't see at all how would you do it with an SVG-based
one.

Also, and perhaps more importantly, I don't understand _why_ would you
want to do it?

EJ> If the user adds a SVG source, all source bitmaps should be discarded.

This seems very counter-intuitive to me, Add() should never remove the
existing bitmaps.

EJ> BTW: When creating a bitmap from SVG, will the background always be
EJ> transparent, or can the user set a background color?

I didn't think about it yet, but I guess it would be transparent, after
all you can always define a solid background in SVG itself, can't you?

Regards,
VZ

Vadim Zeitlin

unread,
Sep 23, 2021, 10:19:32 AM9/23/21
to wx-...@googlegroups.com
On Thu, 23 Sep 2021 13:07:46 +0000 Stefan Csomor wrote:

SC> Thanks for your draft. I'm absolutely fine with the class name and the
SC> concept of it.

Thanks for reviewing it!

SC> A few thoughts:
SC>
SC> If we keep the idea of several 'resolutions' then I think we still
SC> should have a Device-Independent Size for a wxBitmapBundle. That way a
SC> wxBitmapBundle can be drawn onto a dc and the best fit would be
SC> selected for rendering.

What would be this size for an SVG bitmap? I just don't think there is any
good way to define it.

SC> I think there still should be an AddBitmap or something like that.

I've just answered to Eric's message about the same request, but I'd like
to repeat my question here: why do you think we need it? When exactly would
it be useful?

SC> As I wrote in the discussion before, I think the API should have a
SC> constructor taking eg a wxBitmapRenderer, together with data, and
SC> eventually a Device-Independent size. wxSVGRenderer would be an
SC> example.

Sorry, I don't understand the idea with wxBitmapRenderer, what is it and
what does it do?

SC> Upon needing a certain pixel dimension this bitmap would be
SC> rendered once and then stored.

Yes, generated bitmaps are cached. BTW, there is currently no way to clear
this cache, which conceivably might be a problem (bitmaps are GDI resources
under MSW and their number is limited), but I think this could be addressed
later if necessary.

SC> For macOS and iOS I would add a native 'getter' on the bitmap bundle,
SC> that returns the corresponding NSImage. As in places where wxBitmap was
SC> used before, wxBimapBundle will have to be used in macOS, otherwise
SC> automatic Retina Support would be lost.

Yes, indeed, we're going to need some internal accessor. The idea behind
using FromXXX() functions rather than a class with virtual methods is that
these functions could be implemented in a platform-specific way and create
some wxBitmapBundleImplMac specific to wxMac there.

Thanks,
VZ

Alec Teal

unread,
Sep 23, 2021, 10:45:21 AM9/23/21
to wx-...@googlegroups.com

I apologise I replied to the individual:



-------- Forwarded Message --------
Subject: Re: [wx-dev] wxBitmapBundle API proposal
Date: Thu, 23 Sep 2021 15:40:37 +0100
From: Alec Teal <al...@unifiedmathematics.com>
To: Vadim Zeitlin <va...@wxwidgets.org>


Hi there,

I have 2 queries, one is trivial but I swear I mean it genuinely:

bitmap bundle - do you have any other ideas than "bundle"?

Secondly:

How do I (a user) use this? In terms of building it (there's no append it seems I need a vector?) what if I can only get a vector of const bitmaps? In terms of using it: what functions can I expect to exist which can take this (where before they'd have taken a..?) Finally in terms of memory management? Bitmaps are reference counted though so that's a minor concern.

I've always found the wxWidgets bitmap/image distinction useful overall (sometimes it's annoying - but that's my fault for doing something without acknowledging that distinction and I am glad of it)

With wxWidgets there are some areas where I wish virtual methods had been used, yet they have not but for no good reason, although too late to change a lot of stuff (and it'd take years before I saw it anyway given I still have to support 2.8 projects) - could that be done here?


The functions also offer to do a lot of work (getBitmap(wxSize)) - that's slightly worrying, also there is a name for this - mipmapping, this is used when you have say a really high resolution texture, but it's far more detailed than the surface it's being viewed on (3D - think games) and this'd be a waste of bandwidth and very noisy

Sampling and filtering images is also a difficult problem in its own right.

At the time of loading (but I get why here it's on demand) you use a filter and make a very good quality copy at a lower resolution - then use that. A related technique is anistropic filtering (which involves say filtering it so an NxN image is now NxN/4 - this'd be used when looking at it from a shallow angle)

Mipmaps are usually stored in powers of 2, so an NxN image can be stored with several levels in a 2N x 2N image - this can help with memory.

You may want some future room to implement filters?


It's also worth asking: could I not implement some virtual methods and return my own bitmap when asked for a size?


I hope these questions help.


Alec
// Create from the given collection of bitmaps.
static wxBitmapBundle FromBitmaps(const wxVector<wxBitmap>& bitmaps);
static wxBitmapBundle FromBitmaps(const wxBitmap& bitmap1,
const wxBitmap& bitmap2);

// Create from a single bitmap (this is only useful for compatibility
// with the existing code).
static wxBitmapBundle FromBitmap(const wxBitmap& bitmap);
static wxBitmapBundle FromImage(const wxImage& image);

// Create from the SVG data (data is supposed to be in UTF-8 encoding).
static wxBitmapBundle FromSVG(const char* data);

// Create from the resources: all existing versions of the bitmap of the
// form name_2x or name@2x (and also using other factors) will be used.
static wxBitmapBundle FromResources(const wxString& name);


// Check if bitmap bundle is non-empty.
bool IsOk() const { return m_impl; }

// Get bitmap of the specified size, creating a new bitmap from the closest
// available size by rescaling it if necessary.
wxBitmap GetBitmap(const wxSize size) const;

// Get the primary, i.e. smallest, bitmap. Note that this function can
// return an invalid bitmap for the bundles not created from a collection
// of bitmaps and only exists for backwards compatibility, i.e. when a
// wxBitmap is needed by some legacy function.

Vadim Zeitlin

unread,
Sep 23, 2021, 11:12:33 AM9/23/21
to wx-...@googlegroups.com
On Thu, 23 Sep 2021 15:45:12 +0100 Alec Teal wrote:

AT> I have 2 queries, one is trivial but I swear I mean it genuinely:
AT>
AT> bitmap bundle - do you have any other ideas than "bundle"?

You won't believe it (and you will be right, because I do exaggerate --
but only slightly), but I've spent almost as much time on this question as
on all the rest combined. I couldn't come up with anything significantly
better, however, and wxBitmapBundle is at least consistent with the
existing wxIconBundle. Although whether this is really an advantage or not
is another question -- after all these classes are similar but not at all
identical and have rather different APIs.

FWIW I've considered wxBitmapCollection, wxBitmapSet, wxBitmapSheaf[*],
wxBitmapSource, wxBitmapProvider, just wxBitmaps and more that I'm
forgetting. None of them has really managed to clearly express the idea
that this class contains different versions of the same bitmap in different
resolutions, so I kept the original "bundle". If anybody has any convincing
arguments in favour of another name, it's not too late to change it yet
(but it will be soon).

[*] This one would undoubtedly make wxWidgets API more attractive to
geometric algebraists, but I'm not sure concentrating on this group
is the wisest marketing strategy.


AT> Secondly:
AT>
AT> How do I (a user) use this?

The updated (but not yet pushed) toolbar sample shows this, but there it's
as simple as this:

- wxBitmap toolBarBitmaps[Tool_Max];
+ wxBitmapBundle toolBarBitmaps[Tool_Max];

- if ( GetDPIScaleFactor() >= 1.5 )
- {
- toolBarBitmaps[Tool_new] = wxBITMAP_PNG(new_2x);
- }
- else
- {
- toolBarBitmaps[Tool_new] = wxBITMAP_PNG(new);
- }
+ toolBarBitmaps[Tool_new] = wxBITMAP_BUNDLE_2(new);

(I've removed the lines concerning the other bitmaps because they're
identical anyhow). Or, compared with the original version, if you prefer:

- wxBitmap toolBarBitmaps[Tool_Max];
+ wxBitmapBundle toolBarBitmaps[Tool_Max];

-#if USE_XPM_BITMAPS
- toolBarBitmaps[Tool_new] = wxBitmap(new_xpm)
-#else // !USE_XPM_BITMAPS
- toolBarBitmaps[Tool_new] = wxBITMAP(new)
-#endif // USE_XPM_BITMAPS/!USE_XPM_BITMAPS
+ toolBarBitmaps[Tool_new] = wxBITMAP_BUNDLE_2(new);


wxBITMAP_BUNDLE_2() here is a macro which expands into
wxBitmapBundle::FromResources(name) for the platforms supporting bitmaps in
the resources and wxBitmapBundle::FromBitmaps(wxBITMAP_PNG(name),
wxBITMAP_PNG(name_2x)) for the other ones.

This is not the final form as we want to allow using more than 2 sizes in
portable code and this will probably pass by implementing FromResources()
for all platforms, using some new wxResources class that would be used like
this:

wxResources::Get().AddBitmap(new_png, sizeof(new_png));
wxResources::Get().AddBitmap(new_1_5x_png, sizeof(new_1_5_x_png));
wxResources::Get().AddBitmap(new_2x_png, sizeof(new_2x_png));

and then used just as

toolBarBitmaps[Tool_new] = wxBitmapBundle::FromResources("new");


AT> In terms of building it (there's no append it seems I need a vector?)

I'm still very surprised that the absence of Add() function is seen such a
problem by everybody and don't understand why. What's wrong with

wxVector<wxBitmap> bitmaps;
bitmaps.push_back(...);
bitmaps.push_back(...);
button->SetBitmap(wxBitmapBundle::FromBitmaps(bitmaps));

?

AT> what if I can only get a vector of const bitmaps?

This is perfectly fine.

AT> In terms of using it: what functions can I expect to exist which can
AT> take this (where before they'd have taken a..?)

Most (all?) functions taking wxBitmap will take wxBitmapBundle now. The
presence of implicit conversion ctor from wxBitmap in wxBitmapBundle should
ensure that most of the existing code will continue to compile and work
without changes.

AT> With wxWidgets there are some areas where I wish virtual methods had
AT> been used, yet they have not but for no good reason,

I feel that one of the worst problems of the original wx API was using too
many virtual methods, but this goes way beyond the bounds of the current
discussion.

AT> The functions also offer to do a lot of work (getBitmap(wxSize)) -
AT> that's slightly worrying, also there is a name for this - mipmapping,
AT> this is used when you have say a really high resolution texture, but
AT> it's far more detailed than the surface it's being viewed on (3D - think
AT> games) and this'd be a waste of bandwidth and very noisy
AT>
AT> Sampling and filtering images is also a difficult problem in its own right.
AT>
AT> At the time of loading (but I get why here it's on demand) you use a
AT> filter and make a very good quality copy at a lower resolution - then
AT> use that. A related technique is anistropic filtering (which involves
AT> say filtering it so an NxN image is now NxN/4 - this'd be used when
AT> looking at it from a shallow angle)
AT>
AT> Mipmaps are usually stored in powers of 2, so an NxN image can be stored
AT> with several levels in a 2N x 2N image - this can help with memory.
AT>
AT> You may want some future room to implement filters?

I think it's all much less complicated than this. First of all, the idea
is that you should provide all the bitmaps in the needed sizes or use
vector bitmaps. AFAIK nobody seriously expects being able to produce good
looking icons by scaling them, whatever the algorithm used. So GetBitmap()
should return one of the existing bitmaps when it's called with one of the
standard sizes (32x32, 64x64 etc). Of course, it's possible that the bundle
doesn't have a bitmap in this size and then another bitmap is rescaled, but
this is not supposed to be perfect or even desirable, it's just better than
nothing.

AT> It's also worth asking: could I not implement some virtual methods and
AT> return my own bitmap when asked for a size?

No, at least not initially. Allowing to this would strongly overlap with
the existing wxArtProvider functionality, so if we the possibility to do
it, it will probably take form of some wxBitmapBundle::FromArt() and then
you'd define your own custom art provider doing this.

But I don't think it's going to be exceedingly useful in practice because
we only need bitmaps in a few standard sizes, not any arbitrary dimensions.

Regards,
VZ

Stefan Csomor

unread,
Sep 23, 2021, 11:23:56 AM9/23/21
to wx-...@googlegroups.com
Hi

SC> A few thoughts:
SC>
SC> If we keep the idea of several 'resolutions' then I think we still
SC> should have a Device-Independent Size for a wxBitmapBundle. That way a
SC> wxBitmapBundle can be drawn onto a dc and the best fit would be
SC> selected for rendering.

What would be this size for an SVG bitmap? I just don't think there is any
good way to define it.

I think the only case for not having a clear Device-Independen-Size would be a SVG, I wouldn't want to cripple functionality just because SVG allows arbitrary scaling, apart from always having the possibility of having a '-1,-1' for 'any size', even when using a SVG, I'd think that you'd pass in a size eg when using it with a button etc. I know eg I have 32x32 DIP sized toolbar buttons so I'd pass this in, and everything could work without further manual intervention

SC> I think there still should be an AddBitmap or something like that.

I've just answered to Eric's message about the same request, but I'd like
to repeat my question here: why do you think we need it? When exactly would
it be useful?

Looking at the situation of adding several bitmaps by hand as we did in the unix-source-png case, this would be IMHO simpler, compared to having build a vector first ...

SC> As I wrote in the discussion before, I think the API should have a
SC> constructor taking eg a wxBitmapRenderer, together with data, and
SC> eventually a Device-Independent size. wxSVGRenderer would be an
SC> example.

Sorry, I don't understand the idea with wxBitmapRenderer, what is it and
what does it do?

virtual wxBitmap wxBitmapRenderer::CreateBitmap( const wxSize &sz, .... data ... ) = 0

a class that renders data into a bitmap of a certain size, an instance could be used for constructing a wxBitmapBundle, together with data. Any new size requested would be created by the renderer, a SVG Renderer would be one implementation

SC> Upon needing a certain pixel dimension this bitmap would be
SC> rendered once and then stored.

Yes, generated bitmaps are cached. BTW, there is currently no way to clear
this cache, which conceivably might be a problem (bitmaps are GDI resources
under MSW and their number is limited), but I think this could be addressed
later if necessary.

Could we have a concept of weak references to wxObjectRefData ? So the button would keep its wxBitmap instance, if it goes out of scope, the bitmap would have to be regenerated

SC> For macOS and iOS I would add a native 'getter' on the bitmap bundle,
SC> that returns the corresponding NSImage. As in places where wxBitmap was
SC> used before, wxBimapBundle will have to be used in macOS, otherwise
SC> automatic Retina Support would be lost.

Yes, indeed, we're going to need some internal accessor. The idea behind
using FromXXX() functions rather than a class with virtual methods is that
these functions could be implemented in a platform-specific way and create
some wxBitmapBundleImplMac specific to wxMac there.

for macOS this would not be dependent on the way it's instantiated, it would just iterate through the 'source' wxBitmaps and add them to the NSImage once

Best,

Stefan

Alec Teal

unread,
Sep 23, 2021, 11:30:26 AM9/23/21
to wx-...@googlegroups.com
What kind of timeframe is there before it is "a thing" and committed?

I'm not usually very active but I do read and follow (mostly) this list,
I piped up here because I felt new APIs (especially for high DPI
handling) is important.


As I understand it now:

You wont be generating images on the fly, you expect the bundle to be
loaded up with them - great - that sidesteps all the filtering crap.
Also it makes sense why there's no add. The reason I thought you might
want an add is to do something like:

for(...) {

    bundle->add(...);

}

use(bundle);

rather than actually adding them "during program execution"


How does wxWidgets iterate over the sizes in a bundle, or decide what to
ask if I ask for an image of say 3w wide and h/4 high (where w and h are
the "original" dimensions) ?

There's also lots of talk about svgs - svgs are rasterised - I'd urge
you towards a separate object (perhaps with a common base class there) -
because you can rasterise them on demand to anything you want.

I'd suggest the thing that wants to render the SVG has to have its own
bitmap (and if it is resized - manage its own bitmap) and ask the SVG to
rasterise into it on demand, this stops the collection "caching" (with
no policy, certainly no controllable one) every intermediate size as say
a window is resized.

I get the point though, I frequently see like x1, x2 and x4 versions of
icons you want to represent that.

Alec


BTW regarding the add thing, maybe a builder? I get myself a
"wxBitmapFolio [think of this? ;)]Builder" - I can add all the bitmaps I
want to that. Build it getting myself a [one of these] which can be used.

I'd really like some virtual methods, just to be able to control the
policy (IF NEEDED) or instantiate bitmaps on demand.

This is all said in the spirit of an ounce of preparation is worth a
pound of cure - someone eventually will have 100,000s of icons or
something or use this for thumbnails, and maybe then we'll have x1, x2,
x4, x8 and x16 versions of stuff - imagine the memory that'll take [RAM
has stopped scaling, we just use more sticks now]

Vadim Zeitlin

unread,
Sep 23, 2021, 11:35:06 AM9/23/21
to wx-...@googlegroups.com
On Thu, 23 Sep 2021 15:23:52 +0000 Stefan Csomor wrote:

SC> VZ> SC> A few thoughts:
SC> VZ> SC>
SC> VZ> SC> If we keep the idea of several 'resolutions' then I think we still
SC> VZ> SC> should have a Device-Independent Size for a wxBitmapBundle. That way a
SC> VZ> SC> wxBitmapBundle can be drawn onto a dc and the best fit would be
SC> VZ> SC> selected for rendering.
SC> VZ>
SC> VZ> What would be this size for an SVG bitmap? I just don't think there is any
SC> VZ> good way to define it.
SC>
SC> I think the only case for not having a clear Device-Independen-Size
SC> would be a SVG, I wouldn't want to cripple functionality just because
SC> SVG allows arbitrary scaling, apart from always having the possibility
SC> of having a '-1,-1' for 'any size', even when using a SVG, I'd think
SC> that you'd pass in a size eg when using it with a button etc. I know eg
SC> I have 32x32 DIP sized toolbar buttons so I'd pass this in, and
SC> everything could work without further manual intervention

But if you know the size, you can just use GetBitmap(32). The only reason
for having GetPrimary() or, what is the same thing, GetDefaultSize(), is
when you do _not_ know the size. But if you don't know it, I don't see how
can the class know any better.

Also, I think we should be thinking about SVG because in the future it
should become the standard for all icons, with bitmaps used only in the old
code or very infrequently for some very specific icons in the new one.

SC> VZ> SC> I think there still should be an AddBitmap or something like that.
SC> VZ>
SC> VZ> I've just answered to Eric's message about the same request, but I'd like
SC> VZ> to repeat my question here: why do you think we need it? When exactly would
SC> VZ> it be useful?
SC>
SC> Looking at the situation of adding several bitmaps by hand as we did in
SC> the unix-source-png case, this would be IMHO simpler, compared to
SC> having build a vector first ...

I've just given an example of using a vector in another email and IMO it's
almost equivalent. We could add FromBitmaps() overloads for 3, 4, ...
bitmaps too if you think it can be useful.

SC> VZ> SC> As I wrote in the discussion before, I think the API should have a
SC> VZ> SC> constructor taking eg a wxBitmapRenderer, together with data, and
SC> VZ> SC> eventually a Device-Independent size. wxSVGRenderer would be an
SC> VZ> SC> example.
SC> VZ>
SC> VZ> Sorry, I don't understand the idea with wxBitmapRenderer, what is it and
SC> VZ> what does it do?
SC>
SC> virtual wxBitmap wxBitmapRenderer::CreateBitmap( const wxSize &sz, .... data ... ) = 0
SC>
SC> a class that renders data into a bitmap of a certain size, an instance
SC> could be used for constructing a wxBitmapBundle, together with data.
SC> Any new size requested would be created by the renderer, a SVG Renderer
SC> would be one implementation

I see, thanks. We could do it like this, but I'd rather not do it right
now because I'm not sure it's really going to be that useful and, again,
this strongly overlaps with wxArtProvider, so I'd rather cooperate with it
instead.

Maybe we could have an SVG-based wxArtProvider however...

SC> VZ> SC> Upon needing a certain pixel dimension this bitmap would be
SC> VZ> SC> rendered once and then stored.
SC> VZ>
SC> VZ> Yes, generated bitmaps are cached. BTW, there is currently no way to clear
SC> VZ> this cache, which conceivably might be a problem (bitmaps are GDI resources
SC> VZ> under MSW and their number is limited), but I think this could be addressed
SC> VZ> later if necessary.
SC>
SC> Could we have a concept of weak references to wxObjectRefData ? So the
SC> button would keep its wxBitmap instance, if it goes out of scope, the
SC> bitmap would have to be regenerated

Yes, I've thought about it too. But I think we'll keep it like this for
now and see if it creates any problems.

SC> VZ> SC> For macOS and iOS I would add a native 'getter' on the bitmap bundle,
SC> VZ> SC> that returns the corresponding NSImage. As in places where wxBitmap was
SC> VZ> SC> used before, wxBimapBundle will have to be used in macOS, otherwise
SC> VZ> SC> automatic Retina Support would be lost.
SC> VZ>
SC> VZ> Yes, indeed, we're going to need some internal accessor. The idea behind
SC> VZ> using FromXXX() functions rather than a class with virtual methods is that
SC> VZ> these functions could be implemented in a platform-specific way and create
SC> VZ> some wxBitmapBundleImplMac specific to wxMac there.
SC>
SC> for macOS this would not be dependent on the way it's instantiated, it
SC> would just iterate through the 'source' wxBitmaps and add them to the
SC> NSImage once

Well, it will do whatever it needs to do -- Mac code can be completely
independent of the other platforms, for which I'd rather avoid
instantiating all the bitmaps immediately and only do it on demand because
it seems wasteful to create bitmaps that are never going to be used.

I'll try to create a draft PR a.s.a.p. so that you could see if you're
going to be able to provide a Mac implementation of this API, but I'm not
sure if I can manage it until the end of the week, I have too many other
tasks in parallel, unfortunately.

VZ

Vadim Zeitlin

unread,
Sep 23, 2021, 11:42:46 AM9/23/21
to wx-...@googlegroups.com
On Thu, 23 Sep 2021 16:30:05 +0100 Alec Teal wrote:

AT> What kind of timeframe is there before it is "a thing" and committed?

I hope to have a draft PR soon. Maybe this week-end, but more likely some
time during the next week. FWIW this is my highest wx-related priority
right now.

AT> I'm not usually very active but I do read and follow (mostly) this list,
AT> I piped up here because I felt new APIs (especially for high DPI
AT> handling) is important.

Yes, definitely.

AT> As I understand it now:
AT>
AT> You wont be generating images on the fly, you expect the bundle to be
AT> loaded up with them - great - that sidesteps all the filtering crap.

This depends on the kind of a bundle, this is why we have different
FromXXX() functions -- they actually create rather different kinds of
objects. FromBitmaps() stores bitmaps it already has, FromResources()
doesn't store any bitmaps originally as it will only load them from
resources when necessary. And FromSVG() will create them on the fly.

AT> How does wxWidgets iterate over the sizes in a bundle, or decide what to
AT> ask if I ask for an image of say 3w wide and h/4 high (where w and h are
AT> the "original" dimensions) ?

This again depends on the kind of the bundle. SVG bundle will just
rasterize the bitmap in the requested resolution, while the other ones will
rescale the best fitting bitmap (which means the next larger one, if any,
of the next smaller one otherwise).

AT> I get the point though, I frequently see like x1, x2 and x4 versions of
AT> icons you want to represent that.

Yes, this is not at all a general purpose class, it's specifically meant
to be used for the bitmaps used in software UI, i.e. all the small buttons,
icons etc.

AT> BTW regarding the add thing, maybe a builder? I get myself a
AT> "wxBitmapFolio [think of this? ;)]Builder" - I can add all the bitmaps I
AT> want to that. Build it getting myself a [one of these] which can be used.

Sorry, I can't say I really like it, there is still no indication that it
contains different versions of the same bitmap (it could build different
bitmaps too) and it also seems to imply that bitmaps are always built,
which is not the case -- sometimes (and most commonly, in fact), it just
returns the existing bitmaps.

AT> I'd really like some virtual methods, just to be able to control the
AT> policy (IF NEEDED) or instantiate bitmaps on demand.

This is totally incompatible with this class design as written, it's not
supposed to be used polymorphically at all. If we need to make its
behaviour customizable, it will be done via some other pluggable class with
virtual methods, but wxBitmapBundle (or whatever) itself will have the same
semantics as wxBitmap to make it easy to replace the former with the
latter.

Regards,
VZ

Eric Jensen

unread,
Sep 23, 2021, 12:26:12 PM9/23/21
to Vadim Zeitlin
Hello Vadim,

Thursday, September 23, 2021, 4:13:44 PM, you wrote:

VZ> On Thu, 23 Sep 2021 14:53:50 +0200 Eric Jensen wrote:

EJ>> I don't quite understand that. I thought wxBitmapBundle would contain
EJ>> a list of "source" bitmaps,

VZ> Yes, exactly, so e.g. FromResource() contains an object which just
VZ> contains the name of the resource initially and FromSVG() just contains the
VZ> internal SVG representation.

EJ>> so it should be able to append one?

VZ> While I could see how could appending a bitmap to a resource-based object
VZ> be implemented, I don't see at all how would you do it with an SVG-based
VZ> one.
Sorry, apparently i misunderstood the implementation. I thought a bitmap
bundle could have multiple "sources", all parallel to each other.


VZ> Also, and perhaps more importantly, I don't understand _why_ would you
VZ> want to do it?
Just like Stefan wrote, only to avoid having to create an additional
wxVector variable.


EJ>> If the user adds a SVG source, all source bitmaps should be discarded.
VZ> This seems very counter-intuitive to me, Add() should never remove the
VZ> existing bitmaps.
The idea was that - if an SVG source is available - the source bitmaps
become obsolete. However, in practice anyone who uses SVG won't add
any bitmaps anyway.


Regards,
Eric

Vadim Zeitlin

unread,
Sep 23, 2021, 12:31:48 PM9/23/21
to wx-...@googlegroups.com
On Thu, 23 Sep 2021 18:25:40 +0200 Eric Jensen wrote:

EJ> Sorry, apparently i misunderstood the implementation. I thought a bitmap
EJ> bundle could have multiple "sources", all parallel to each other.

No, just to confirm, in my proposal each bundle has at most one source (it
can also have 0 sources if it's empty...), but different bundles can have
sources of different kinds.

EJ> VZ> Also, and perhaps more importantly, I don't understand why would you
EJ> VZ> want to do it?
EJ> Just like Stefan wrote, only to avoid having to create an additional
EJ> wxVector variable.

The problem is that this minor convenience results in a major API
inconsistency as Add() won't do anything for some kinds of bundle. So I
really don't think it's worth it.

EJ> EJ>> If the user adds a SVG source, all source bitmaps should be discarded.
EJ> VZ> This seems very counter-intuitive to me, Add() should never remove the
EJ> VZ> existing bitmaps.
EJ> The idea was that - if an SVG source is available - the source bitmaps
EJ> become obsolete. However, in practice anyone who uses SVG won't add
EJ> any bitmaps anyway.

I agree, and this is why I think that supporting multiple sources in the
same bundle is not worth it. And people who really need such flexibility
will be (eventually) able to achieve it by using a custom wxArtProvider.

Regards,
VZ

Stefan Csomor

unread,
Sep 23, 2021, 12:40:01 PM9/23/21
to wx-...@googlegroups.com
Hi

SC> I think the only case for not having a clear Device-Independen-Size
SC> would be a SVG, I wouldn't want to cripple functionality just because
SC> SVG allows arbitrary scaling, apart from always having the possibility
SC> of having a '-1,-1' for 'any size', even when using a SVG, I'd think
SC> that you'd pass in a size eg when using it with a button etc. I know eg
SC> I have 32x32 DIP sized toolbar buttons so I'd pass this in, and
SC> everything could work without further manual intervention

But if you know the size, you can just use GetBitmap(32). The only reason
for having GetPrimary() or, what is the same thing, GetDefaultSize(), is
when you do _not_ know the size. But if you don't know it, I don't see how
can the class know any better.

because at the place of rendering within wx I will have to get the DIP-Size * DPIScaleFactor version, so I need the DIP-Size somewhere, of course I could ask the default bitmap if there is any, but having this at the wxBitmapBundle level still seems logical to me

Also, I think we should be thinking about SVG because in the future it
should become the standard for all icons, with bitmaps used only in the old
code or very infrequently for some very specific icons in the new one.

While SVG is surely great for High-DPI renderings, I don't think they will replace pngs in the near future. IMHO low res bitmaps can be made looking a lot better by hand than when automatically scaling down SVG. So while I absolutely agree that we should look at supporting SVG, I don't think it would be the most used situation soon.

SC> virtual wxBitmap wxBitmapRenderer::CreateBitmap( const wxSize &sz, .... data ... ) = 0
SC>
SC> a class that renders data into a bitmap of a certain size, an instance
SC> could be used for constructing a wxBitmapBundle, together with data.
SC> Any new size requested would be created by the renderer, a SVG Renderer
SC> would be one implementation

I see, thanks. We could do it like this, but I'd rather not do it right
now because I'm not sure it's really going to be that useful and, again,
this strongly overlaps with wxArtProvider, so I'd rather cooperate with it
instead.

Maybe we could have an SVG-based wxArtProvider however...

This might be an angle as well ....

I'll try to create a draft PR a.s.a.p. so that you could see if you're
going to be able to provide a Mac implementation of this API, but I'm not
sure if I can manage it until the end of the week, I have too many other
tasks in parallel, unfortunately.

Same here, no problem ...

Thanks,

Stefan

Vadim Zeitlin

unread,
Sep 23, 2021, 12:51:08 PM9/23/21
to wx-...@googlegroups.com
On Thu, 23 Sep 2021 16:39:58 +0000 Stefan Csomor wrote:

SC> VZ> SC> I think the only case for not having a clear Device-Independen-Size
SC> VZ> SC> would be a SVG, I wouldn't want to cripple functionality just because
SC> VZ> SC> SVG allows arbitrary scaling, apart from always having the possibility
SC> VZ> SC> of having a '-1,-1' for 'any size', even when using a SVG, I'd think
SC> VZ> SC> that you'd pass in a size eg when using it with a button etc. I know eg
SC> VZ> SC> I have 32x32 DIP sized toolbar buttons so I'd pass this in, and
SC> VZ> SC> everything could work without further manual intervention
SC> VZ>
SC> VZ> But if you know the size, you can just use GetBitmap(32). The only reason
SC> VZ> for having GetPrimary() or, what is the same thing, GetDefaultSize(), is
SC> VZ> when you do not know the size. But if you don't know it, I don't see how
SC> VZ> can the class know any better.
SC>
SC> because at the place of rendering within wx I will have to get the
SC> DIP-Size * DPIScaleFactor version, so I need the DIP-Size somewhere, of
SC> course I could ask the default bitmap if there is any, but having this
SC> at the wxBitmapBundle level still seems logical to me

This is important, so I'd like to decide something about it before even
the first draft. Conceptually I still believe that it's the use of the
bitmap which determines the size we need, e.g. for wxToolBar we need to
have a bitmap of GetToolBitmapSize() and for a button we need bitmap of
some standard size determined by the button itself and so on. But in
practice I guess we could have many places where we do use the size of the
bitmap passed in currently and which are going to be difficult to update --
without speaking of the existing application code, which we can't update at
all.

So I guess we do need to add some wxBitmapBundle::GetBaseSize()[*] which
would be implemented by simply returning the size of the smallest bitmap
for a bundle created from a bitmap collection and which would require
passing the size to return to FromSVG(), even if it has to be arbitrarily
chosen.

Please let me know if this corresponds to what you had in mind or,
especially, if it doesn't.

TIA!
VZ

[*] Naming is the bane of my existence... Should it be called
GetStandardSize()? GetDIPSize()? GetSizeInDIP()? GetPrimarySize()?
BTW, if we add this, we definitely don't need GetPrimary() itself,
as it becomes equivalent to GetBitmap(GetBaseSize()).

Eric Jensen

unread,
Sep 23, 2021, 1:18:12 PM9/23/21
to Vadim Zeitlin
Hello Vadim,

Thursday, September 23, 2021, 6:51:06 PM, you wrote:

VZ> [*] Naming is the bane of my existence... Should it be called
VZ> GetStandardSize()? GetDIPSize()? GetSizeInDIP()? GetPrimarySize()?
VZ> BTW, if we add this, we definitely don't need GetPrimary() itself,
VZ> as it becomes equivalent to GetBitmap(GetBaseSize()).

I would go for GetStandardSize() or GetDefaultSize()

I'm also wondering if it might make sense to pass a size to the
bundle "constructor" defining the default size.

The meaning would be "this is the size the bitmap should have when
rendered at 100%".

At least for the SVG version that would make sense.

For the bitmap based bundles, i'm not sure if it's ok to assume that
the smallest available bitmap is the "default" one.

Regards,
Eric

Stefan Csomor

unread,
Sep 23, 2021, 2:15:56 PM9/23/21
to wx-...@googlegroups.com
Hi
That's exactly what I had in mind. Thanks

[*] Naming is the bane of my existence... Should it be called
GetStandardSize()? GetDIPSize()? GetSizeInDIP()? GetPrimarySize()?
BTW, if we add this, we definitely don't need GetPrimary() itself,
as it becomes equivalent to GetBitmap(GetBaseSize()).

Oh, I'm not at all qualified,

GetDIPSize or GetSizeAs/InDIP would seem closest in the intention, but we don't have such a method name anywhere, only FromDIP and ToDIP
GetDefaultSize or GetStandardSize sound close as well, to me GetBaseSize or GetPrimarySize are not really clear

But as I said, in the name game I'm not a valuable asset __

Thanks,

Stefan


Vadim Zeitlin

unread,
Sep 23, 2021, 3:32:21 PM9/23/21
to wx-...@googlegroups.com
On Thu, 23 Sep 2021 19:17:40 +0200 Eric Jensen wrote:

EJ> Hello Vadim,
EJ>
EJ> Thursday, September 23, 2021, 6:51:06 PM, you wrote:
EJ>
EJ> VZ> [*] Naming is the bane of my existence... Should it be called
EJ> VZ> GetStandardSize()? GetDIPSize()? GetSizeInDIP()? GetPrimarySize()?
EJ> VZ> BTW, if we add this, we definitely don't need GetPrimary() itself,
EJ> VZ> as it becomes equivalent to GetBitmap(GetBaseSize()).
EJ>
EJ> I would go for GetStandardSize() or GetDefaultSize()

I don't know why do I like GetBaseSize(), perhaps because I think of 100%
DPI size as the baseline? But if nobody else likes it, let's use
GetDefaultSize() instead.

EJ> I'm also wondering if it might make sense to pass a size to the
EJ> bundle "constructor" defining the default size.

It will be passed to FromSVG() pseudo-ctor.

EJ> For the bitmap based bundles, i'm not sure if it's ok to assume that
EJ> the smallest available bitmap is the "default" one.

Why not? What's the point of ever providing bitmaps in less than the
default size, I'm pretty sure we're never going to have displays with 50%
DPI...

VZ

Stefan Csomor

unread,
Sep 24, 2021, 1:55:41 AM9/24/21
to wx-...@googlegroups.com
Hi

Sorry, I forgot to write down two things:

class WXDLLIMPEXP_CORE wxBitmapBundle
{
public:
(...)

// Create from the resources: all existing versions of the bitmap of the
// form name_2x or name@2x (and also using other factors) will be used.
static wxBitmapBundle FromResources(const wxString& name);

- would *name* include a type extension ? if not, I think we should have an additional wxBitmapType type here, at least on macOS we can have PNG or JPEG

- right now on macOS I have the wxBitmap::LoadFile loading all the representations automatically. Could we have a wxBitmapBundle::FromFile that would at least on platforms that support it right now keep this behavior, we might also call it from ::FromFiles( to make this clearer

Thanks,

Stefan

Vadim Zeitlin

unread,
Sep 24, 2021, 9:17:09 AM9/24/21
to wx-...@googlegroups.com
On Fri, 24 Sep 2021 05:55:37 +0000 Stefan Csomor wrote:

SC> - would name include a type extension ? if not, I think we should have
SC> an additional wxBitmapType type here, at least on macOS we can have PNG
SC> or JPEG

We could add this, I honestly didn't know anybody used JPEGs for program
icons, but why not. This could be done later, IMHO, however.

SC> - right now on macOS I have the wxBitmap::LoadFile loading all the
SC> representations automatically. Could we have a wxBitmapBundle::FromFile
SC> that would at least on platforms that support it right now keep this
SC> behavior, we might also call it from ::FromFiles( to make this clearer

FromFiles() will take wxFileName and load all files with the name
optionally followed by "@Nx" suffix, right?

I'll add this to the TODO list, thanks,
VZ
Reply all
Reply to author
Forward
0 new messages