locked
Image custom aspect RRS feed

  • Question

  • User312712 posted

    I am trying to modify the aspect of an image

    Assuming that the container of the image has a size ratio of 1:3 I want the image to take all the available width space and not be deformed or cropped if it doesn't fit the ratio. That's where my pan-able image comes into play, to be able to move the image around.

    AspectFill => Crop AspectFit => Width not 100% Fill => Stretch

    I also tried to find a workaround with AspectFit + WidthRequest but couldn't make it work.

    Thursday, April 27, 2017 2:10 PM

Answers

  • User312712 posted

    I was able to get what I was looking for. The image ratio is kept and it fits all the available space without being cropped.

    class ImageFit : Image {
        protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint) {
            SizeRequest sizeRequest = base.OnMeasure(double.PositiveInfinity, double.PositiveInfinity);
    
            var innerRatio = sizeRequest.Request.Width / sizeRequest.Request.Height;
    
            // Width needs to be adjusted
            if (double.IsInfinity(heightConstraint)) {
                // Height needs to be adjusted
                if (double.IsInfinity(widthConstraint)) {
                    widthConstraint = sizeRequest.Request.Width;
                    heightConstraint = sizeRequest.Request.Height;
                } else {
                    // Adjust height
                    heightConstraint = widthConstraint * sizeRequest.Request.Height / sizeRequest.Request.Width;
                }
            } else if (double.IsInfinity(widthConstraint)) {
                // Adjust width
                widthConstraint = heightConstraint * sizeRequest.Request.Width / sizeRequest.Request.Height;
            } else {
                // strech the image to make it fit while conserving it's ratio
                var outerRatio = widthConstraint / heightConstraint;
    
                var ratioFactor = (innerRatio >= outerRatio) ?
                    (widthConstraint / sizeRequest.Request.Width) :
                    (heightConstraint / sizeRequest.Request.Height);
    
                widthConstraint = sizeRequest.Request.Width * ratioFactor;
                heightConstraint = sizeRequest.Request.Height * ratioFactor;
            }
            sizeRequest = new SizeRequest(new Size(widthConstraint, heightConstraint));
            return sizeRequest;
        }
    }
    

    Xaml

    <ScrollView>
        <renderer:ImageFit x:Name="image" Source="{Binding ImgSource}" />
    </ScrollView>
    

    Thanks for the help @ClintStLaurent

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Friday, April 28, 2017 10:31 AM

All replies

  • User180523 posted

    I am trying to modify the aspect of an image Before going down this route... There are several built-in aspect ratio settings in Xamarin.Forms. Are none of them what you need:Fill, FillAspect, and so on. Do you have some really weird need to have the aspect stretch is a way 99% of the world hasn't stumbled across yet?

    Thursday, April 27, 2017 2:20 PM
  • User312712 posted

    Assuming that the container of the image has a size ratio of 1:3 I want the image to take all the available width space and not be deformed or cropped if it doesn't fit the ratio. That's where my pan-able image comes into play, to be able to move the image around.

    AspectFill => Crop AspectFit => Width not 100% Fill => Stretch

    I also tried to find a workaround with AspectFit + WidthRequest but couldn't make it work.

    Thursday, April 27, 2017 3:28 PM
  • User180523 posted

    Ah, interesting. This is why I ask questions: To learn things.

    So you want it to fit width, not crop and be pan-able. An Image in a ScrollView won't do this, without the need for custom renderer? Hmm, something to play with in free time.

    Thursday, April 27, 2017 3:36 PM
  • User312712 posted

    Placing the Image in a ScrollView causes OnMeasure to be called. Small steps. I'll continue to fiddle with it.

    Thursday, April 27, 2017 4:49 PM
  • User312712 posted

    I was able to get what I was looking for. The image ratio is kept and it fits all the available space without being cropped.

    class ImageFit : Image {
        protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint) {
            SizeRequest sizeRequest = base.OnMeasure(double.PositiveInfinity, double.PositiveInfinity);
    
            var innerRatio = sizeRequest.Request.Width / sizeRequest.Request.Height;
    
            // Width needs to be adjusted
            if (double.IsInfinity(heightConstraint)) {
                // Height needs to be adjusted
                if (double.IsInfinity(widthConstraint)) {
                    widthConstraint = sizeRequest.Request.Width;
                    heightConstraint = sizeRequest.Request.Height;
                } else {
                    // Adjust height
                    heightConstraint = widthConstraint * sizeRequest.Request.Height / sizeRequest.Request.Width;
                }
            } else if (double.IsInfinity(widthConstraint)) {
                // Adjust width
                widthConstraint = heightConstraint * sizeRequest.Request.Width / sizeRequest.Request.Height;
            } else {
                // strech the image to make it fit while conserving it's ratio
                var outerRatio = widthConstraint / heightConstraint;
    
                var ratioFactor = (innerRatio >= outerRatio) ?
                    (widthConstraint / sizeRequest.Request.Width) :
                    (heightConstraint / sizeRequest.Request.Height);
    
                widthConstraint = sizeRequest.Request.Width * ratioFactor;
                heightConstraint = sizeRequest.Request.Height * ratioFactor;
            }
            sizeRequest = new SizeRequest(new Size(widthConstraint, heightConstraint));
            return sizeRequest;
        }
    }
    

    Xaml

    <ScrollView>
        <renderer:ImageFit x:Name="image" Source="{Binding ImgSource}" />
    </ScrollView>
    

    Thanks for the help @ClintStLaurent

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Friday, April 28, 2017 10:31 AM
  • User180523 posted

    @TheoBenvenuti said: I was able to get what I was looking for. The image ratio is kept and it fits all the available space without being cropped.

    Very nice. Thanks for sharing - That's why it is a coding community !

    Friday, April 28, 2017 11:14 AM
  • User375962 posted

    Thanx! One little adjustment for the case that the Image is contained within a parent that is initially not Visible:

    class ImageFit : Image {
        protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint) {
            SizeRequest sizeRequest = base.OnMeasure(double.PositiveInfinity, double.PositiveInfinity);
    
            var innerRatio = sizeRequest.Request.Width / sizeRequest.Request.Height;
    
            if (double.IsNaN(innerRatio))
                return sizeRequest;
    
            // Width needs to be adjusted
            if (double.IsInfinity(heightConstraint)) {
                // Height needs to be adjusted
                if (double.IsInfinity(widthConstraint)) {
                    widthConstraint = sizeRequest.Request.Width;
                    heightConstraint = sizeRequest.Request.Height;
                } else {
                    // Adjust height
                    heightConstraint = widthConstraint * sizeRequest.Request.Height / sizeRequest.Request.Width;
                }
            } else if (double.IsInfinity(widthConstraint)) {
                // Adjust width
                widthConstraint = heightConstraint * sizeRequest.Request.Width / sizeRequest.Request.Height;
            } else {
                // strech the image to make it fit while conserving it's ratio
                var outerRatio = widthConstraint / heightConstraint;
    
                var ratioFactor = (innerRatio >= outerRatio) ?
                    (widthConstraint / sizeRequest.Request.Width) :
                    (heightConstraint / sizeRequest.Request.Height);
    
                widthConstraint = sizeRequest.Request.Width * ratioFactor;
                heightConstraint = sizeRequest.Request.Height * ratioFactor;
            }
            sizeRequest = new SizeRequest(new Size(widthConstraint, heightConstraint));
            return sizeRequest;
        }
    }
    
    Tuesday, October 2, 2018 9:07 AM
  • User384926 posted

    Thank you so much, i was searching a lot for a solution. Your code works perfect for me.

    Friday, August 16, 2019 5:59 PM