Page 1 of 1

Smoothing image scaling

Posted: Tue Sep 03, 2019 2:37 pm
by raananb
To display a picture of 4000x3000 (Image) in a 800x600 display area, I use a scaled image which squeezes the picture into the display area:

wxImage* scaledImage = new wxImage(Image->Scale(800, 600));

in OnPaint():
wxBufferedPaintDC dc(this);
dc.DrawBitmap(wxBitmap(*scaledImage), wxPoint(0,0), false);

I implemented a slider so that the central part is enlarged as the slider is moved, which gives a zoom effect.

double ratio = (100.0 + slider->GetValue())/100.0; // ratio >= 1.0
scaledImage = new wxImage(Image->Scale(displayWidth*ratio, displayHeight*ratio));
pictureUpperLeftCorner.x = (displaySize.x - scaledImageSize.x)/2.
pictureUpperLeftCorner.y = (displaySize.y - scaledImageSize.y)/2.

pictureUpperLeftCorner has negative values

The corresponding dc expression is:

dc.DrawBitmap(wxBitmap(*scaledImage), pictureUpperLeftCorner, false);

The displayed image is exactly what I expect, so the geometry is OK.

I wonder what can be done to smooth the transition while the slider is moved.

Re: Smoothing image scaling

Posted: Tue Sep 03, 2019 4:02 pm
by doublemax
Scaling down a wxImage is a very slow operation. So is converting a wxImage to a wxBitmap. So both steps should be avoided if possible.

If the image is static (= the content changes rarely or never), the convert it into a wxGraphicsBitmap (only when the content changes) and use wxGraphicsContext to draw it.

Use wxGraphicsContext::Scale() to scale the drawing.
Use wxGraphicsContext::SetInterpolationQuality(wxINTERPOLATION_GOOD) to get better output quality.

Re: Smoothing image scaling

Posted: Tue Sep 03, 2019 8:51 pm
by raananb
Thanks for the tip.

wxWidget documentation is quite slim, and does not include a wxImage.

I scoured the internet for an example of how to use wxGraphicContext to do what my code does, but to no avail.

An example would be welcome.

Re: Smoothing image scaling

Posted: Tue Sep 03, 2019 9:27 pm
by doublemax

Code: Select all

#include "wx/dc.h"
#include "wx/dcbuffer.h"
#include "wx/graphics.h"
class wxGraphicsBitmapPanel : public wxWindow
{
public:
  wxGraphicsBitmapPanel( wxWindow *parent, int id = wxID_ANY ) 
     : wxWindow( parent, id, wxDefaultPosition, wxDefaultSize,  wxFULL_REPAINT_ON_RESIZE)
     , m_zoom(1.0f)
     , m_bitmap_width(0)
     , m_bitmap_height(0)
  {
    SetBackgroundStyle( wxBG_STYLE_PAINT );
    Bind(wxEVT_PAINT, &wxGraphicsBitmapPanel::OnPaint, this );
  }

  void SetBitmap( const wxBitmap &bitmap )
  {
    wxGraphicsContext *gc = wxGraphicsContext::Create(this);
    m_bitmap = gc->CreateBitmap( bitmap );

    // wxGraphicsBitmap has no method to retrieve bitmap dimensions,
    // so we have to save them
    m_bitmap_width = bitmap.GetWidth();
    m_bitmap_height = bitmap.GetHeight();
    delete gc;
  }

  void SetZoom( double zoom )
  {
    m_zoom = zoom;
    Refresh();
  }

  void OnPaint( wxPaintEvent &evt )
  {
    wxAutoBufferedPaintDC pdc(this);

    pdc.SetBrush( GetBackgroundColour() );
    pdc.SetPen( GetBackgroundColour() );
    pdc.DrawRectangle( wxRect(GetClientSize()) );

    if( !m_bitmap.IsNull() && m_bitmap_width > 0 && m_bitmap_height > 0 )
    {
      wxGraphicsContext *gc = wxGraphicsContext::Create( pdc );
      gc->DrawBitmap( m_bitmap, 0, 0, m_zoom * m_bitmap_width, m_zoom * m_bitmap_height );
      delete gc;
    }
  }

protected:
  double m_zoom;
  int m_bitmap_width, m_bitmap_height;
  wxGraphicsBitmap m_bitmap;
};
How to use:

Code: Select all

wxGraphicsBitmapPanel *gbp = new wxGraphicsBitmapPanel(this, wxID_ANY);
wxBitmap bmp("d:\\something.jpg", wxBITMAP_TYPE_ANY);
gbp->SetBitmap( bmp );
gbp->SetZoom( 0.3f );

Re: Smoothing image scaling

Posted: Mon Sep 09, 2019 11:59 am
by raananb
Thanks, this works fine.