Resize UIImage with aspect ratio?
Asked Answered
D

2

37

I'm using this code to resize an image on the iPhone:

CGRect screenRect = CGRectMake(0, 0, 320.0, 480.0);
UIGraphicsBeginImageContext(screenRect.size);
[value drawInRect:screenRect blendMode:kCGBlendModePlusDarker alpha:1];
UIImage *tmpValue = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Which is working great, as long as the aspect ratio of the image matches that of the new resized image. I'd like to modify this so that it keeps the correct aspect ratio and just puts a black background anywhere the image doesn't show up. So I would still end up with a 320x480 image but with black on the top and bottom or sides, depending on the original image size.

Is there an easy way to do this similar to what I'm doing? Thanks!

Drury answered 9/11, 2009 at 19:13 Comment(1)
Here's an article that keeps the aspect ratio: #7645954Jemmie
G
56

After you set your screen rect, do something like the following to decide what rect to draw the image in:

float hfactor = value.bounds.size.width / screenRect.size.width;
float vfactor = value.bounds.size.height / screenRect.size.height;

float factor = fmax(hfactor, vfactor);

// Divide the size by the greater of the vertical or horizontal shrinkage factor
float newWidth = value.bounds.size.width / factor;
float newHeight = value.bounds.size.height / factor;

// Then figure out if you need to offset it to center vertically or horizontally
float leftOffset = (screenRect.size.width - newWidth) / 2;
float topOffset = (screenRect.size.height - newHeight) / 2;

CGRect newRect = CGRectMake(leftOffset, topOffset, newWidth, newHeight);

If you don't want to enlarge images smaller than the screenRect, make sure factor is greater than or equal to one (e.g. factor = fmax(factor, 1)).

To get the black background, you would probably just want to set the context color to black and call fillRect before drawing the image.

Graphitize answered 9/11, 2009 at 19:29 Comment(7)
value is the variable Cory is using to store the source image.Graphitize
@Frank Schmitt, can I check how I can set the context to black for this case?Trestle
I was getting "Implicit declaration of function 'max'" thrown by the compiler, even though I remembered to include math.h (I'm using XCode 4.0) Changing: float factor = max( … to float factor = MAX( … all caps) resolved the issue. FWIW the reason I needed to rescale a UIImage (as opposed to UIImageView) was because I was setting the image in a default styled UITableCell. This helped me avoid needing to implement a custom UITableCell. Thanks to Frank for a GREAT solution!Kilo
Just made an edit to use fmax, which is already in whatever precompiled headers Apple includes. The difference with MAX() is that it treats everything as an integer, whereas Core Graphics uses CGFloats (doubles) natively for all coordinate values. So on Retina screens your method might be off by half a point :)Graphitize
Can you explain the logic as in why we need the factor variable here to get the newWidth and height hereEncapsulate
@AshishPisey In order to preserve the image proportions, you need to divide the width and the height by the same amount. This code picks that amount (factor) based on whether it needs to be shrunk more in the horizontal (hFactor) or vertical (vFactor) direction to fit in the screen rectangle.Graphitize
@iOS_Ramesh value is the UIImage that you're resizing.Graphitize
C
0

I know this is very old, but thanks for that post -- it redirected me from attempting to use scale to drawing the image. In case it is of benefit to anyone, I made an extension class I'll throw in here. It allows you to resize an image like this:

      UIImage imgNew = img.Fit(40.0f, 40.0f);

I don't need a fit option, but it could easily be extended to support Fill as well.

using CoreGraphics;
using System;
using UIKit;

namespace SomeApp.iOS.Extensions
{
  public static class UIImageExtensions
  {
    public static CGSize Fit(this CGSize sizeImage,
      CGSize sizeTarget)
    {
      CGSize ret;
      float fw;
      float fh;
      float f;

      fw = (float) (sizeTarget.Width / sizeImage.Width);
      fh = (float) (sizeTarget.Height / sizeImage.Height);
      f = Math.Min(fw, fh);
      ret = new CGSize
      {
        Width = sizeImage.Width * f,
        Height = sizeImage.Height * f
      };
      return ret;
    }

    public static UIImage Fit(this UIImage image,
      float width,
      float height,
      bool opaque = false,
      float scale = 1.0f)
    {
      UIImage ret;

      ret = image.Fit(new CGSize(width, height),
        opaque,
        scale);
      return ret;
    }

    public static UIImage Fit(this UIImage image,
      CGSize sizeTarget,
      bool opaque = false,
      float scale = 1.0f)
    {
      CGSize sizeNewImage;
      CGSize size;
      UIImage ret;

      size = image.Size;
      sizeNewImage = size.Fit(sizeTarget);
      UIGraphics.BeginImageContextWithOptions(sizeNewImage,
        opaque,
        1.0f);
      using (CGContext context = UIGraphics.GetCurrentContext())
      {
        context.ScaleCTM(1, -1);
        context.TranslateCTM(0, -sizeNewImage.Height);
        context.DrawImage(new CGRect(CGPoint.Empty, sizeNewImage),
          image.CGImage);
        ret = UIGraphics.GetImageFromCurrentImageContext();
      }
      UIGraphics.EndImageContext();
      return ret;
    }
  }
}

As per the post above, it starts a new context for an image, then for that image it figures out aspect and then paints into the image. If you haven't done any Swift xcode dev time, UIGraphics is a bit backwards to most systems I work with but not bad. One issue is that bitmaps by default paint bottom to top. To get around that,

        context.ScaleCTM(1, -1);
        context.TranslateCTM(0, -sizeNewImage.Height);

Changes the orientation of drawing to the more common top-left to bottom-right... but then you need to move the origin as well hence the TranslateCTM.

Hopefully, it saves someone some time.

Cheers

Copt answered 11/9, 2019 at 23:45 Comment(3)
What language is this? It looks like C# but uses iOS SDK classes.Sporocyte
@Sporocyte With MAUI, you can delineate code blocks and in platform specific areas for iOS/Mac access UIKit and Android, Java / Google Play, etc.. Sometimes the inter-ops are fairly straightforward if you are familiar with Android SDK / Swift -- sometimes, achieving same behavior can be quite tricky.Copt
Ah, ok. Thanks for the clarification. I've never seen this.Sporocyte

© 2022 - 2024 — McMap. All rights reserved.