Page 1 of 1

How to Stretch Bitmap into Parent Panel (wxWidgets Custom)

Posted: Mon Sep 17, 2018 4:07 pm
by R_Irudezu
Hi, I'm kinda new here, please warn me if i do something wrong.

I set a wxButton and it loads bitmap,png formats to Panel at right. But image is overfitting. I want to stretc image to Panel when it's loaded by button.

[img]
qq.PNG
qq.PNG (4.36 KiB) Viewed 891 times
[/img]

Here is button's function, just in case:

Code: Select all

void rrFrame::testClick(wxCommandEvent& event)
{
    wxBitmap bmp;
    wxString strPath = wxT("img\\img1.png");

    bmp.LoadFile(strPath, wxBITMAP_TYPE_ANY);
    camFrame_wx->DrawBitmap(bmp, wxPoint(0, 0), false);
    //camFrame_wx is the variable name of 'Custom'
}
I suppose i need a stretch or fit function in constructor. How to do this?

Re: How to Stretch Bitmap into Parent Panel (wxWidgets Custom)

Posted: Mon Sep 17, 2018 5:21 pm
by PB
I think there are several issues with your code, the main is that you are probably not redrawing the panel when needed (i.e., in its paint event handler) and then lose the bitmap.

E.g., here is a simple unoptimized example of implementing a panel that has a bitmap filling it (without maintaining the bitmap proportions), please notice that it uses wxImage::Rescale() instead of supposedly (much) faster wxDC::StretchBlit() to achieve better quality of the scaled bitmap. Truth to be told scaled bitmaps, particularly those produced artificially (i.e., computer drawings) look bad when rescaled regardless.

Code: Select all

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

class BitmapPanel : public wxPanel
{
public:
    BitmapPanel(wxWindow* parent, const wxBitmap& bitmap = wxNullBitmap)
        : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE),
          m_bitmap(bitmap)
    {                
        SetBackgroundStyle(wxBG_STYLE_PAINT);
        Bind(wxEVT_PAINT, &BitmapPanel::OnPaint, this);                        
    }

    void SetBitmap(const wxBitmap& bitmap)
    {
        m_bitmap = bitmap;
        m_scaledBitmap = wxNullBitmap;
        Update(); Refresh();
    }

private:
    wxBitmap m_bitmap, m_scaledBitmap;

    static wxBitmap RescaleBitmap(const wxBitmap& bitmap, const wxSize& newSize)
    {        
        if ( !bitmap.IsOk() )
            return wxNullBitmap;
                
        const wxImage image = bitmap.ConvertToImage();

        return wxBitmap(image.Scale(newSize.GetWidth(), newSize.GetHeight(), wxIMAGE_QUALITY_HIGH));
    }

    void OnPaint(wxPaintEvent&) 
    { 
        wxPaintDC dc(this);
        
        dc.SetBackground(*wxBLACK_BRUSH);
        dc.Clear();

        if ( !m_bitmap.IsOk() )
            return; 

        const wxSize dcSize = dc.GetSize();
        
        if ( !m_scaledBitmap.IsOk() || m_scaledBitmap.GetSize() != dcSize )
            m_scaledBitmap = RescaleBitmap(m_bitmap, dcSize);
        
        dc.DrawBitmap(m_scaledBitmap, 0, 0, true);
    }       
};


class MyFrame : public wxFrame
{
public:
    MyFrame() : wxFrame(NULL, wxID_ANY, "Test")
    {
        const int borderAround = FromDIP(10);
        
        wxBoxSizer* bSizer = new wxBoxSizer(wxVERTICAL);

        wxButton* button = new wxButton(this, wxID_ANY, "Load image file...");
        bSizer->Add(button, wxSizerFlags().Expand().Border(wxALL, borderAround));
        button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MyFrame::OnLoadImageFile, this);

        m_bitmapPanel = new BitmapPanel(this);
        bSizer->Add(m_bitmapPanel, wxSizerFlags().Expand().Proportion(1).Border(wxALL, borderAround));
        
        SetSizer(bSizer);
    }	
private:
    BitmapPanel* m_bitmapPanel;
    
    void OnLoadImageFile(wxCommandEvent&)
    {
        static wxString fileName;
        wxImage image;

        fileName = wxFileSelector("Choose Image to Load", "", fileName, "",
            "Image files (*.jpg;*.png;*.tga;*.bmp;*.tiff;*.gif;*.ico;*.pcx)| *.jpg;*.png;*.tga;*.bmp;*.tiff;*.gif;*.ico;*.pcx",
            wxFD_OPEN | wxFD_FILE_MUST_EXIST, this);

        if ( fileName.empty() )
            return;
                
        if ( image.LoadFile(fileName) )
        {                                
            m_bitmapPanel->SetBitmap(wxBitmap(image));
            SetTitle(wxString::Format("Simple Image Viewer (%s: %d x %d pixels)", 
                wxFileName(fileName).GetFullName(), image.GetWidth(), image.GetHeight()));                
        }                
    }
};

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

        (new MyFrame())->Show();
        return true;
    }
}; wxIMPLEMENT_APP(MyApp);
bitmappanel.png
bitmappanel.png (38.67 KiB) Viewed 884 times
The simpler variant of OnPaint() using wxDC::StretchBlit could look like this

Code: Select all

void BitmapPanel::OnPaint(wxPaintEvent&) 
{ 
    wxPaintDC dc(this);
        
    dc.SetBackground(*wxBLACK_BRUSH);
    dc.Clear();

    if ( !m_bitmap.IsOk() )
        return; 
        
    wxMemoryDC mdc(m_bitmap);

    dc.StretchBlit(wxPoint(0, 0), dc.GetSize(), &mdc, wxPoint(0,0), mdc.GetSize(), wxCOPY, true);
}       

Re: How to Stretch Bitmap into Parent Panel (wxWidgets Custom)

Posted: Tue Sep 18, 2018 7:55 pm
by R_Irudezu
PB wrote:I think there are several issues with your code, the main is that you are probably not redrawing the panel when needed (i.e., in its paint event handler) and then lose the bitmap.

E.g., here is a simple unoptimized example of implementing a panel that has a bitmap filling it (without maintaining the bitmap proportions), please notice that it uses wxImage::Rescale() instead of supposedly (much) faster wxDC::StretchBlit() to achieve better quality of the scaled bitmap. Truth to be told scaled bitmaps, particularly those produced artificially (i.e., computer drawings) look bad when rescaled regardless.

Code: Select all

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

class BitmapPanel : public wxPanel
{
public:
    BitmapPanel(wxWindow* parent, const wxBitmap& bitmap = wxNullBitmap)
        : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE),
          m_bitmap(bitmap)
    {                
        SetBackgroundStyle(wxBG_STYLE_PAINT);
        Bind(wxEVT_PAINT, &BitmapPanel::OnPaint, this);                        
    }

    void SetBitmap(const wxBitmap& bitmap)
    {
        m_bitmap = bitmap;
        m_scaledBitmap = wxNullBitmap;
        Update(); Refresh();
    }

private:
    wxBitmap m_bitmap, m_scaledBitmap;

    static wxBitmap RescaleBitmap(const wxBitmap& bitmap, const wxSize& newSize)
    {        
        if ( !bitmap.IsOk() )
            return wxNullBitmap;
                
        const wxImage image = bitmap.ConvertToImage();

        return wxBitmap(image.Scale(newSize.GetWidth(), newSize.GetHeight(), wxIMAGE_QUALITY_HIGH));
    }

    void OnPaint(wxPaintEvent&) 
    { 
        wxPaintDC dc(this);
        
        dc.SetBackground(*wxBLACK_BRUSH);
        dc.Clear();

        if ( !m_bitmap.IsOk() )
            return; 

        const wxSize dcSize = dc.GetSize();
        
        if ( !m_scaledBitmap.IsOk() || m_scaledBitmap.GetSize() != dcSize )
            m_scaledBitmap = RescaleBitmap(m_bitmap, dcSize);
        
        dc.DrawBitmap(m_scaledBitmap, 0, 0, true);
    }       
};


class MyFrame : public wxFrame
{
public:
    MyFrame() : wxFrame(NULL, wxID_ANY, "Test")
    {
        const int borderAround = FromDIP(10);
        
        wxBoxSizer* bSizer = new wxBoxSizer(wxVERTICAL);

        wxButton* button = new wxButton(this, wxID_ANY, "Load image file...");
        bSizer->Add(button, wxSizerFlags().Expand().Border(wxALL, borderAround));
        button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MyFrame::OnLoadImageFile, this);

        m_bitmapPanel = new BitmapPanel(this);
        bSizer->Add(m_bitmapPanel, wxSizerFlags().Expand().Proportion(1).Border(wxALL, borderAround));
        
        SetSizer(bSizer);
    }	
private:
    BitmapPanel* m_bitmapPanel;
    
    void OnLoadImageFile(wxCommandEvent&)
    {
        static wxString fileName;
        wxImage image;

        fileName = wxFileSelector("Choose Image to Load", "", fileName, "",
            "Image files (*.jpg;*.png;*.tga;*.bmp;*.tiff;*.gif;*.ico;*.pcx)| *.jpg;*.png;*.tga;*.bmp;*.tiff;*.gif;*.ico;*.pcx",
            wxFD_OPEN | wxFD_FILE_MUST_EXIST, this);

        if ( fileName.empty() )
            return;
                
        if ( image.LoadFile(fileName) )
        {                                
            m_bitmapPanel->SetBitmap(wxBitmap(image));
            SetTitle(wxString::Format("Simple Image Viewer (%s: %d x %d pixels)", 
                wxFileName(fileName).GetFullName(), image.GetWidth(), image.GetHeight()));                
        }                
    }
};

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

        (new MyFrame())->Show();
        return true;
    }
}; wxIMPLEMENT_APP(MyApp);
bitmappanel.png
The simpler variant of OnPaint() using wxDC::StretchBlit could look like this

Code: Select all

void BitmapPanel::OnPaint(wxPaintEvent&) 
{ 
    wxPaintDC dc(this);
        
    dc.SetBackground(*wxBLACK_BRUSH);
    dc.Clear();

    if ( !m_bitmap.IsOk() )
        return; 
        
    wxMemoryDC mdc(m_bitmap);

    dc.StretchBlit(wxPoint(0, 0), dc.GetSize(), &mdc, wxPoint(0,0), mdc.GetSize(), wxCOPY, true);
}       

Thanks a lot! =D>