SoFunction
Updated on 2025-05-14

WPF realizes image stretching by pixel

The picture components in WPF themselves support different stretching effects. The details are as follows:

  • None, no stretching
  • Fill is completely filled (will deform)
  • Uniform is scaled equally, and will not deform
  • UniformToFill is scaled equally and fully populated. Will not deform, but the long part will be cut

However, if we want to implement chat bubble functions like QQ or WeChat, directly using the image component cannot meet the requirements.

We can observe the chat bubble on WeChat. Its width and height can be automatically adjusted according to the content we enter, and the background image will not be deformed.

Today we will use WPF to implement this function!

To achieve the undeformed stretching function, we can stretch for 1 pixel. In this way, the stretched pictures, except for the pixels in the stretched area, the other areas still retain the appearance of the original picture.

Here we mainly need to use the CroppedBitmap class, which is mainly used for cropping, and can crop BitmapImage.

For scenes like WeChat chat bubbles, it needs to support horizontal and vertical stretching effects. We can use CroppedBitmap to crop the original image into 9 images. When rendering, we render the 9 images to the corresponding positions respectively. The stretched area is the upper middle position, the lower middle position, the left middle position, the right middle position and the middle position in the 9 pictures. These pictures are cropped by 1 pixel so that there will be no stretched pictures.

Key Code:

        //Get the cropped image according to the cropped area.  ImageSource refers to the original image        private ImageSource GetCroppedBitmap(double x, double y, double width, double height)
        {
            return new CroppedBitmap(ImageSource, new Int32Rect((int)x, (int)y, (int)width, (int)height));
        }
 
        /// <summary>
        /// Get the horizontal offset pixel value        /// </summary>
        /// <returns></returns>
        private int GetHorizontalOffset()
        {
            return (int)(HorizontalPrecent *  / 100);
        }
 
        /// <summary>
        /// Get the offset pixel value of the vertical position        /// </summary>
        /// <returns></returns>
        private int GetVerticalOffset()
        {
            return (int)(VerticalPrecent *  / 100);
        }
 
        /// <summary>
        /// Get the horizontal offset pixel value        /// </summary>
        /// <returns></returns>
        private int GetStretchHeight()
        {
            return (int)( - );
        }
 
        /// <summary>
        /// Get the offset pixel value of the vertical position        /// </summary>
        /// <returns></returns>
        private int GetStretchWidth()
        {
            return (int)( - );
        }
 
        /// <summary>
        /// Is horizontal stretching available        /// </summary>
        private bool IsHorizontalStretchEnabled
        {
            get
            {
                if (HorizontalPrecent > 0 && HorizontalPrecent < 100)
                {
                    return true;
                }
                return false;
            }
        }
 
        /// <summary>
        /// Offset for cropping horizontally        /// </summary>
        public double HorizontalPrecent { get; set; }
 
        /// <summary>
        /// Offset for cropping in vertical direction        /// </summary>
        public double VerticalPrecent { get; set; }
 
        // Method of drawing horizontal + vertical stretching        protected override void OnRender(DrawingContext drawingContext)
        {
            //This requires 9 pictures            //Upper left, middle left, lower left, upper right, middle right, lower right, middle horizontal, center vertical            var horizontalOffset = GetHorizontalOffset();
            var verticalOffset = GetVerticalOffset();
            var leftTop = GetCroppedBitmap(0, 0, horizontalOffset, verticalOffset);
            var leftBottom = GetCroppedBitmap(0, verticalOffset + 1, horizontalOffset,  - verticalOffset - 1);
            var rightTop = GetCroppedBitmap(horizontalOffset + 1, 0,  - horizontalOffset - 1,  - verticalOffset - 1);
            var rightBottom = GetCroppedBitmap(horizontalOffset + 1, verticalOffset + 1,  - horizontalOffset - 1,  - verticalOffset - 1);
            //The middle            var center = GetCroppedBitmap(horizontalOffset, verticalOffset, 1, 1);
            var leftCenter = GetCroppedBitmap(0, verticalOffset + 1, horizontalOffset, 1);
            var rightCenter = GetCroppedBitmap(horizontalOffset + 1, verticalOffset + 1,  - horizontalOffset - 1, 1);
            var topCenter = GetCroppedBitmap(horizontalOffset + 1, 0, 1, verticalOffset);
            var bottomCenter = GetCroppedBitmap(horizontalOffset + 1, verticalOffset + 1, 1,  - verticalOffset - 1);
            //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------            var stretchHeight = GetStretchHeight();
            if (stretchHeight < 0) stretchHeight = 0;
            var stretchWidth = GetStretchWidth();
            if (stretchWidth < 0) stretchWidth = 0;
            (leftTop, new Rect(0, 0, horizontalOffset, verticalOffset));
            (rightTop, new Rect(horizontalOffset + stretchWidth, 0,  - horizontalOffset - 1,  - verticalOffset));
            //Draw the horizontal stretched pixel            if (stretchHeight > 0)
            {
                (leftCenter, new Rect(0, verticalOffset, horizontalOffset, stretchHeight));
                (rightCenter, new Rect(horizontalOffset + stretchWidth, verticalOffset,  - horizontalOffset - 1, stretchHeight));
            }
            //Draw the vertically stretched pixel            if (stretchWidth > 0)
            {
                (topCenter, new Rect(horizontalOffset, 0, stretchWidth, verticalOffset));
                (bottomCenter, new Rect(horizontalOffset, verticalOffset + stretchHeight, stretchWidth,  - verticalOffset - 1));
            }
            //Draw the pixels stretched in the middle            if (stretchHeight > 0 && stretchWidth > 0)
            {
                (center, new Rect(horizontalOffset, verticalOffset, stretchWidth, stretchHeight));
            }
 
            (leftBottom, new Rect(0, verticalOffset + stretchHeight, horizontalOffset,  - verticalOffset - 1));
            (rightBottom, new Rect(horizontalOffset + stretchWidth, verticalOffset + stretchHeight,  - horizontalOffset - 1,  - verticalOffset - 1));
        }
 
        //Only support horizontal stretching        protected override void OnRender(DrawingContext drawingContext)
        {
            var horizontalOffset = GetHorizontalOffset();
            var left = GetCroppedBitmap(0, 0, horizontalOffset, );
            var center = GetCroppedBitmap(horizontalOffset, 0, 1, );
            var right = GetCroppedBitmap(horizontalOffset + 1, 0,  - horizontalOffset - 1, );
            (left, new Rect(0, 0, horizontalOffset, ));
            var stretchWidth = GetStretchWidth();
            if (stretchWidth > 0)
            {
                (center, new Rect(horizontalOffset, 0, stretchWidth, ));
            }
            else
            {
                stretchWidth = 0;
            }
            (right, new Rect(horizontalOffset + stretchWidth, 0,  - horizontalOffset - 1, ));
        }
 
        //Only support vertical stretching        protected override void OnRender(DrawingContext drawingContext)
        {
            var verticalOffset = GetVerticalOffset();
            var top = GetCroppedBitmap(0, 0, , verticalOffset);
            var center = GetCroppedBitmap(0, verticalOffset, , 1);
            var bottom = GetCroppedBitmap(0, verticalOffset + 1, ,  - verticalOffset - 1);
            (top, new Rect(0, 0, , verticalOffset));
            var stretchHeight = GetStretchHeight();
            if (stretchHeight > 0)
            {
                (center, new Rect(0, verticalOffset, , stretchHeight));
            }
            else
            {
                stretchHeight = 0;
            }
            (bottom, new Rect(0, verticalOffset + stretchHeight, ,  - verticalOffset - 1));
        }
 
        //Measure the layout size, remember to rewrite it here.  This version does not support resolutions of different sizes, and can be achieved by calculating the scaling ratio        private Size MeasureCore(Size size, ImageSource imgSource)
        {
            if (imgSource == null) return size;
 
            Size naturalSize;
 
            if (IsHorizontalStretchEnabled && IsVerticalStretchEnabled)
            {
                naturalSize = new Size(, );
            }
            else if (IsHorizontalStretchEnabled)
            {
                naturalSize = new Size(, );
            }
            else if (IsVerticalStretchEnabled)
            {
                naturalSize = new Size(, );
            }
            else
            {
                return size;
            }
 
            return naturalSize;
        }

The above code can realize the functions of horizontal stretching, vertical stretching or horizontal + vertical stretching. The current test code does not support images with different resolutions. The calculation in the demo uses the width and height of the ImageSource. If you need to support any resolution, you can scale the rendered width and height and the actual width and height of the picture.

This is the end of this article about WPF's image stretching by pixel. For more related content on WPF image stretching by pixel, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!