Saving wxImage to wxStringOutputStream fails

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
MatthewDP
Earned a small fee
Earned a small fee
Posts: 16
Joined: Thu Apr 21, 2005 6:19 am

Saving wxImage to wxStringOutputStream fails

Post by MatthewDP »

Windows 10
wxWidgets 3.1.3
MSVC 16.2.5 (Visual C++ 2019)
Preprocessor Definitions: WIN32;_WINDOWS;UNICODE;_UNICODE;_DEBUG;__WXDEBUG__;CMAKE_INTDIR="Debug";%(PreprocessorDefinitions)


I have a wxImage object generated from a bitmap.
When I save it to a file stream, everything works find. The file is created as a valid PNG, which I can view in an external application.
However, when I try to save it to a wxStringOutputStream, the stream is empty.

Here is the relevant code:

Code: Select all

//Save to file - works!
	wxImage myImage1 = myBitmap.ConvertToImage();
	myImage1.SaveFile(imgPath, wxBITMAP_TYPE_PNG);

	//Save to stream - results in an empty stream and string
	wxImage myImage2 = myBitmap.ConvertToImage();
	wxStringOutputStream ssImg;
	bool imgSaveResult = myImage2.SaveFile(ssImg, wxBITMAP_TYPE_PNG);
	std::string imgString = ssImg.GetString();
The imgString object is empty, and the ssImg stream doesn't seem to hold any information.
I have tried initializing the wxStringOutputStream with a wxString and also with various wxMBConv objects.
It would seem that the default MCConv is appropriate.

I also traced this into the wxImage file with the debugger. Both calls to SaveFile end up in the same function, leading me to believe that there is something amiss with my use of wxStringOutputStream.

Can anyone shed some light?

Thanks,
Matt.
Last edited by MatthewDP on Thu Mar 26, 2020 12:12 pm, edited 1 time in total.
Kvaz1r
Super wx Problem Solver
Super wx Problem Solver
Posts: 357
Joined: Tue Jun 07, 2016 1:07 pm

Re: Saving wxImage to wxStringOutputStream fails

Post by Kvaz1r »

If first call you use wxBITMAP_TYPE_PNG and in second one - MIME_png. It should be so?
MatthewDP
Earned a small fee
Earned a small fee
Posts: 16
Joined: Thu Apr 21, 2005 6:19 am

Re: Saving wxImage to wxStringOutputStream fails

Post by MatthewDP »

Kvaz1r,

You are correct. Both should be wxBITMAP_TYPE_PNG. I copied code where I was experimenting with the string based MIME type. It doesn't make any difference if you use wxBITMAP_TYPE_PNG or "image/png".

I have now corrected this in my original post.
Kvaz1r
Super wx Problem Solver
Super wx Problem Solver
Posts: 357
Joined: Tue Jun 07, 2016 1:07 pm

Re: Saving wxImage to wxStringOutputStream fails

Post by Kvaz1r »

It's indeed tricky.
Could you check maybe will work such variant:

Code: Select all

        wxString str;
        wxStringOutputStream ssImg(&str, wxConvLocal);
        if (img.SaveFile(ssImg, wxBITMAP_TYPE_PNG))
        {
            ///data loaded into str
        }
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: Saving wxImage to wxStringOutputStream fails

Post by PB »

What do you want to accomplish by saving a binary data to a string?

If I wanted to get in-memory representation of a PNG, I would use wxMemoryOutputStream. If I wanted a PNG stored as text, I would uuencode it first.

EDIT
I did some very quick debugging and IMO storing arbitrary binary data in wxStringOutputStream cannot work. There is always a conversion involved when writing to this stream (using wxMBConv passed in the stream ctor), and the conversion is doomed to fail as there are many sequences of input bytes that just cannot be converted to a valid Unicode. 8-bit locale-based convertors may work but they depend on locale and this would be very brittle approach. But I may be wrong...

EDIT2
Regarding the conversion: The wxStringOutputStream docs says to use wxConvISO8859_1 for arbitrary 8-bit data, i.e. something like this

Code: Select all

wxStringOutputStream sos(nullptr, wxConvISO8859_1);
MatthewDP
Earned a small fee
Earned a small fee
Posts: 16
Joined: Thu Apr 21, 2005 6:19 am

Re: Saving wxImage to wxStringOutputStream fails

Post by MatthewDP »

In regards to Kvaz1r's suggestion to use wxConvLocal, I do get a single line of text which is the beginning to a PNG string. It is:

Code: Select all

‰PNG\r\n\x1a\n
With regards to PB's suggestion of:

Code: Select all

 wxStringOutputStream sos(nullptr, wxConvISO8859_1);
the value of GetString() is still empty.
MatthewDP
Earned a small fee
Earned a small fee
Posts: 16
Joined: Thu Apr 21, 2005 6:19 am

Re: Saving wxImage to wxStringOutputStream fails

Post by MatthewDP »

PB -

With regards to your questions:
What do you want to accomplish by saving a binary data to a string?
If I wanted to get in-memory representation of a PNG, I would use wxMemoryOutputStream. If I wanted a PNG stored as text, I would uuencode it first.
I sometimes need to save out an image to file as a .PNG file, and I sometimes need to embed the image into an XML document. This goes in as text, which starts out as

Code: Select all

data:image/png;base64,

followed by a lot of text.

Up to this point, I just dumped the image as a file to disk every time, and then (if I needed it), read it back in as a binary stream and chunked it into PNG embeddable text. But with a bunch of images, using the disk causes a small lag I'd like to eliminate. That's my interest in doing it all in memory.

So if I can accomplish this using wxMemoryOutputStream, I'd be happy to use that. But it's not clear to me how.

Thanks for your help,
Matt.
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: Saving wxImage to wxStringOutputStream fails

Post by PB »

Firstly, I can store PNG in a wxString like this (wxWidgets 3.1.3, MSW)

Code: Select all

#include <wx/wx.h>
#include <wx/sstream.h>

class MyApp : public wxApp
{
public:
    bool OnInit() override
    {        
        wxInitAllImageHandlers();

        wxImage img(128, 128);        
        wxString imgString;
        wxStringOutputStream sos(&imgString, wxConvISO8859_1);

        img.SetRGB(img.GetSize(), 255, 0, 0); // all red
        img.SaveFile(sos, wxBITMAP_TYPE_PNG);
        
        return false;
    }
}; wxIMPLEMENT_APP(MyApp);
Obviously the resulting string cannot be fully used by a normal string code, as it has embedded nulls. But when inspecting it in MSVS it looks OK
imgstring.png
imgstring.png (16.09 KiB) Viewed 1215 times
You can get the memory representation of the raw 8-bit data from a wxString with wxString::To8BitData().

The (untested) code below shows one of the ways how to get a PNG data as a void pointer using wxMemoryOutputStream and how to Base64 encode it for saving in a text file

Code: Select all

#include <wx/wx.h>
#include <wx/base64.h> 
#include <wx/mstream.h>

class MyApp : public wxApp
{
public:
    bool OnInit() override
    {        
        wxInitAllImageHandlers();

        wxImage img(128, 128);
        wxMemoryOutputStream mos;

        img.SetRGB(img.GetSize(), 255, 0, 0); // all red
        
        if ( img.SaveFile(mos, wxBITMAP_TYPE_PNG) )
        {
            const wxStreamBuffer* buff = mos.GetOutputStreamBuffer();
            const wxString encoded = wxBase64Encode(buff->GetBufferStart(), buff->GetBufferSize());
            
            wxLogDebug(encoded);
        }
        
        return false;
    }
}; wxIMPLEMENT_APP(MyApp);
BTW, if you want to create a bitmap from an in-memory stored PNG, there is a convenient wxBitmap::NewFromPNGData().

For loading/storing wxImage from/to memory see also code in viewtopic.php?f=1&t=45149&p=187023#p187023
MatthewDP
Earned a small fee
Earned a small fee
Posts: 16
Joined: Thu Apr 21, 2005 6:19 am

Re: Saving wxImage to wxStringOutputStream fails

Post by MatthewDP »

Well, that's officially awesome.

The code works as advertised. I've got my PNG in a string, as it were, and displaying.

Thanks PB!
Post Reply