Page 1 of 1

Saving wxImage to wxStringOutputStream fails

Posted: Thu Mar 26, 2020 11:47 am
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.

Re: Saving wxImage to wxStringOutputStream fails

Posted: Thu Mar 26, 2020 12:01 pm
by Kvaz1r
If first call you use wxBITMAP_TYPE_PNG and in second one - MIME_png. It should be so?

Re: Saving wxImage to wxStringOutputStream fails

Posted: Thu Mar 26, 2020 12:13 pm
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.

Re: Saving wxImage to wxStringOutputStream fails

Posted: Thu Mar 26, 2020 1:22 pm
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
        }

Re: Saving wxImage to wxStringOutputStream fails

Posted: Thu Mar 26, 2020 1:24 pm
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);

Re: Saving wxImage to wxStringOutputStream fails

Posted: Thu Mar 26, 2020 5:23 pm
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.

Re: Saving wxImage to wxStringOutputStream fails

Posted: Thu Mar 26, 2020 5:32 pm
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.

Re: Saving wxImage to wxStringOutputStream fails

Posted: Thu Mar 26, 2020 8:37 pm
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 1231 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

Re: Saving wxImage to wxStringOutputStream fails

Posted: Fri Mar 27, 2020 9:49 am
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!