How to Retain the Aspect Ratio of an Image While Resizing

Environment: VC6

I needed a way to display images full screen but I did not want the image to change its aspect ratio since that would distort its shape. If the image was very narrow it would expand to fill up the whole width of the screen making the image look too wide.

To fix this I wrote a function that will calculate the rectangle that will be needed. If you stretch the image to fit in the rectangle it will expand the image to fill as much of the screen as possible without changing the aspect ratio of the picture.

The function has three parameters. First is the rectangle that you want the image to fill. The second is the size of the picture. The last parameter tells the function whether you want the image to be centered in the rectangle or not.

The function returns a rectangle that you use to stretch the image.

CRect SizeRectWithConstantAspectRatio( CRect* rcScreen,
                                       CSize sizePicture,
                                       BOOL bCenter)
{
  CRect rect(rcScreen);
  double dWidth = rcScreen->Width();
  double dHeight = rcScreen->Height();
  double dAspectRatio = dWidth/dHeight;

  double dPictureWidth = sizePicture.cx;
  double dPictureHeight = sizePicture.cy;
  double dPictureAspectRatio = dPictureWidth/dPictureHeight;

  //If the aspect ratios are the same then the screen rectangle
  // will do, otherwise we need to calculate the new rectangle

  if (dPictureAspectRatio > dAspectRatio)
  {
    int nNewHeight = (int)(dWidth/dPictureWidth*dPictureHeight);
    int nCenteringFactor = (rcScreen->Height() - nNewHeight) / 2;
    rect.SetRect( 0,
                  nCenteringFactor,
                  (int)dWidth,
                  nNewHeight + nCenteringFactor);

  }
  else if (dPictureAspectRatio < dAspectRatio)
  {
    int nNewWidth =  (int)(dHeight/dPictureHeight*dPictureWidth);
    int nCenteringFactor = (rcScreen->Width() - nNewWidth) / 2;
    rect.SetRect( nCenteringFactor, 
                  0,
                  nNewWidth + nCenteringFactor,
                  (int)(dHeight));
  }

  return rect;
}

This is an example of how to use this function. I used the CPicture class from CodeGuru but you could use a normal CBitmap class and use CDC::StretchBlt for the same effect.

void CMyWnd::OnPaint() 
{
  CPaintDC dc(this); // device context for painting
  CString strSize;

  CPicture picture;
  picture.Load(m_strPicturePath);
  // Get Picture Dimentions In Pixels
  picture.UpdateSizeOnDC(&dc);

//Get the dimensions of the screen
  CRect rcScreen( 0, 0, 
                  GetSystemMetrics(SM_CXSCREEN),
                  GetSystemMetrics(SM_CYSCREEN));

  //Get dimensions of Image
  CSize sizePicture(picture.m_Width, picture.m_Height); 

  //Create a black background
  CBrush backBrush;
  backBrush.CreateSolidBrush(RGB(0,0,0));
  dc.FillRect(&rcScreen, &backBrush);

  CRect rcNewPictureRect = 
        SizeRectWithConstantAspectRatio(&rcScreen,
                                        sizePicture,
                                        TRUE);
  picture.Show(&dc, rcNewPictureRect);
}

Downloads

Download demo project - 29 Kb


Comments

  • An other way to do this...

    Posted by Legacy on 09/15/2003 12:00am

    Originally posted by: Mattias

    This is how I solved this...
    
    

    /// <summary>
    /// Resizes the image with or without retained aspectratio.
    /// </summary>
    /// <param name="orgWidth"></param>
    /// <param name="orgHeight"></param>
    /// <param name="width"></param>
    /// <param name="height"></param>
    /// <param name="stretch"></param>
    private void GetImageMeasures(int orgWidth, int orgHeight, ref int width, ref int height, bool stretch){

    if (stretch == true || (orgHeight == orgWidth && width == height))
    return;

    // Get the aspectratio
    double ar = ((double)orgWidth / (double)orgHeight); // the correct aspectratio
    double newAr = ((double)width / (double)height); // the new maby incorrect aspectratio

    double f = Math.Max(ar, newAr);
    if (f == ar){
    // fit to width.
    height = (int) (width / ar);
    } else{
    // fit to height
    width = (int) (height * ar);
    }
    }

    /Mattias

    Reply
  • Helpful

    Posted by Legacy on 06/26/2002 12:00am

    Originally posted by: Ramaprasad Potturi

    This is very handy. thanks

    Reply
  • algorithm looks good, but would like more formatting in code

    Posted by Legacy on 01/02/2002 12:00am

    Originally posted by: Kelly Grant

    I have a pet peeve with your formatting in the calulation. You do a " x / y * z" type of calculation, depending on the compiler's rules of precedence to do the right thing. I think programmers do this to test each other's knowledge, or just show their own cleverness. However, in 18 years of programming, I've learned that clarity is extremely important, especially for "clever" code. The algorithm that looks so straight-forward when you write it can look completely foreign in a couple of years. It can turn a lot of code into "write-only" that may need to be rewritten later on just so the programmer can figure out what they were doing in the first place. I have found that a text representation of the calculation in comments makes reading the code a much easier task later on.

    I have a practical reason for this as well. I am translating your code into Ada, and my compiler's precedence may be different than VC6. I don't use VC6 enough to know its rules that well, so it would really provide a service to anyone who translates code to explicitly state precedence with a few parens.

    All that being said, thanks for writing the code, it looks good! :-)

    Kelly

    Reply
  • Works Fine

    Posted by Legacy on 12/23/2001 12:00am

    Originally posted by: Sikander Rafiq

    Thanks Michael Hatton. Your Demo works fine. Good Effort.

    Reply
  • Centering

    Posted by Legacy on 12/21/2001 12:00am

    Originally posted by: Michael Hatton

    Even though the function I posted has you pass in bCenter, I negelected to give the version that allows you to choose whether you want to center or not.  
    
    

    All you have to do is set nCenteringFactor to 0 if you do not want it centered. Here is a version that does that.

    CRect CMyWnd::SizeRectWithAspectRatio(CRect* rcScreen, CSize sizePicture, BOOL bCenter)
    {
    CRect rect(rcScreen);
    double dWidth = rcScreen->Width();
    double dHeight = rcScreen->Height();
    double dAspectRatio = dWidth/dHeight;

    double dPictureWidth = sizePicture.cx;
    double dPictureHeight = sizePicture.cy;
    double dPictureAspectRatio = dPictureWidth/dPictureHeight;

    if (dPictureAspectRatio > dAspectRatio)
    {
    int nNewHeight = (int)(dWidth/dPictureWidth*dPictureHeight);
    int nCenteringFactor = 0;
    if (bCenter)
    nCenteringFactor = (rcScreen->Height() - nNewHeight) / 2;
    rect.SetRect(0, nCenteringFactor, (int)dWidth, nNewHeight + nCenteringFactor);

    }
    else if (dPictureAspectRatio < dAspectRatio)
    {
    int nNewWidth = (int)(dHeight/dPictureHeight*dPictureWidth);
    int nCenteringFactor = 0;
    if (bCenter)
    nCenteringFactor = (rcScreen->Width() - nNewWidth) / 2;
    rect.SetRect(nCenteringFactor, 0, nNewWidth + nCenteringFactor, (int)(dHeight));
    }

    return rect;
    }

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • The explosion in mobile devices and applications has generated a great deal of interest in APIs. Today's businesses are under increased pressure to make it easy to build apps, supply tools to help developers work more quickly, and deploy operational analytics so they can track users, developers, application performance, and more. Apigee Edge provides comprehensive API delivery tools and both operational and business-level analytics in an integrated platform. It is available as on-premise software or through …

  • Live Event Date: September 10, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild". This loop of continuous delivery and continuous feedback is …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds