How to Stretch Bitmap into Parent Panel (wxWidgets Custom) Topic is solved

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
User avatar
R_Irudezu
In need of some credit
In need of some credit
Posts: 2
Joined: Mon Sep 17, 2018 4:02 pm

How to Stretch Bitmap into Parent Panel (wxWidgets Custom)

Post by R_Irudezu » Mon Sep 17, 2018 4:07 pm

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 802 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?

PB
Part Of The Furniture
Part Of The Furniture
Posts: 2511
Joined: Sun Jan 03, 2010 5:45 pm

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

Post by PB » Mon Sep 17, 2018 5:21 pm

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 795 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);
}       

User avatar
R_Irudezu
In need of some credit
In need of some credit
Posts: 2
Joined: Mon Sep 17, 2018 4:02 pm

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

Post by R_Irudezu » Tue Sep 18, 2018 7:55 pm

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>

Post Reply