WPF实现图片按像素拉伸

 更新时间:2024年11月04日 10:14:54   作者:冷眼Σ(-᷅_-᷄๑)  
这篇文章主要为大家详细介绍了WPF实现图片按像素拉伸的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

WPF中的图片组件,本身是支持不同的拉伸效果。具体如下:

  • None, 不做拉伸
  • Fill 完全填充(会变形)
  • Uniform 等比缩放,不会变形
  • UniformToFill 等比缩放,并完全填充。不会变形,但是长的部分会被裁剪

但是,如果我们要实现像QQ或者微信这样子的聊天气泡功能,直接使用图片组件就无法满足要求了。

我们可以观察下微信的聊天气泡,他的宽度和高度可以根据我们输入的内容自动调整,并且背景图片也不会存在变形的问题。

今天我们就用WPF来实现这个功能!

要实现不变形的拉伸功能,我们可以针对1个像素来进行拉伸,这样拉伸出来的图片,除了拉伸区域的像素都是一样的,其它区域还是保留了原来的图片的外观。

这里主要需要用到 CroppedBitmap类,该类主要用于裁剪,可以对 BitmapImage进行裁剪。

微信聊天气泡这种场景,它需要支持水平和垂直的方向的拉伸效果,我们可以利用 CroppedBitmap,将原始图片裁剪成9张图,渲染的时候,我们分别将9张图渲染到对应的位置。拉伸的区域就是9张图中的上面中间位置,下面中间位置,左边中间位置,右边中间位置以及最中间的位置。这几张图片,都按1个像素进行裁剪,这样就不会出现拉伸的图片了。

关键代码:

        //根据裁剪区域,获取裁剪后的图片。ImageSource指的是原始图片
        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>
        /// 获取水平偏移像素值
        /// </summary>
        /// <returns></returns>
        private int GetHorizontalOffset()
        {
            return (int)(HorizontalPrecent * ImageSource.Width / 100);
        }
 
        /// <summary>
        /// 获取垂直位置的偏移像素值
        /// </summary>
        /// <returns></returns>
        private int GetVerticalOffset()
        {
            return (int)(VerticalPrecent * ImageSource.Height / 100);
        }
 
        /// <summary>
        /// 获取水平偏移像素值
        /// </summary>
        /// <returns></returns>
        private int GetStretchHeight()
        {
            return (int)(RenderSize.Height - ImageSource.Height);
        }
 
        /// <summary>
        /// 获取垂直位置的偏移像素值
        /// </summary>
        /// <returns></returns>
        private int GetStretchWidth()
        {
            return (int)(RenderSize.Width - ImageSource.Width);
        }
 
        /// <summary>
        /// 水平拉伸是否可用
        /// </summary>
        private bool IsHorizontalStretchEnabled
        {
            get
            {
                if (HorizontalPrecent > 0 && HorizontalPrecent < 100)
                {
                    return true;
                }
                return false;
            }
        }
 
        /// <summary>
        /// 按水平方向进行裁剪的偏移量
        /// </summary>
        public double HorizontalPrecent { get; set; }
 
        /// <summary>
        /// 按垂直方向进行裁剪的偏移量
        /// </summary>
        public double VerticalPrecent { get; set; }
 
        //绘制水平+垂直拉伸的方法
        protected override void OnRender(DrawingContext drawingContext)
        {
            //这个需要9张图
            //左上,左中,左下,右上,右中,右下,水平中,垂直中
            var horizontalOffset = GetHorizontalOffset();
            var verticalOffset = GetVerticalOffset();
            var leftTop = GetCroppedBitmap(0, 0, horizontalOffset, verticalOffset);
            var leftBottom = GetCroppedBitmap(0, verticalOffset + 1, horizontalOffset, ImageSource.Height - verticalOffset - 1);
            var rightTop = GetCroppedBitmap(horizontalOffset + 1, 0, ImageSource.Width - horizontalOffset - 1, ImageSource.Height - verticalOffset - 1);
            var rightBottom = GetCroppedBitmap(horizontalOffset + 1, verticalOffset + 1, ImageSource.Width - horizontalOffset - 1, ImageSource.Height - verticalOffset - 1);
            //最中间的
            var center = GetCroppedBitmap(horizontalOffset, verticalOffset, 1, 1);
            var leftCenter = GetCroppedBitmap(0, verticalOffset + 1, horizontalOffset, 1);
            var rightCenter = GetCroppedBitmap(horizontalOffset + 1, verticalOffset + 1, ImageSource.Width - horizontalOffset - 1, 1);
            var topCenter = GetCroppedBitmap(horizontalOffset + 1, 0, 1, verticalOffset);
            var bottomCenter = GetCroppedBitmap(horizontalOffset + 1, verticalOffset + 1, 1, ImageSource.Height - verticalOffset - 1);
            //------------------------------- 上面的逻辑是切图,下面的逻辑是绘制 -----------------------------------
            var stretchHeight = GetStretchHeight();
            if (stretchHeight < 0) stretchHeight = 0;
            var stretchWidth = GetStretchWidth();
            if (stretchWidth < 0) stretchWidth = 0;
            drawingContext.DrawImage(leftTop, new Rect(0, 0, horizontalOffset, verticalOffset));
            drawingContext.DrawImage(rightTop, new Rect(horizontalOffset + stretchWidth, 0, ImageSource.Width - horizontalOffset - 1, ImageSource.Height - verticalOffset));
            //绘制水平方向的拉伸像素
            if (stretchHeight > 0)
            {
                drawingContext.DrawImage(leftCenter, new Rect(0, verticalOffset, horizontalOffset, stretchHeight));
                drawingContext.DrawImage(rightCenter, new Rect(horizontalOffset + stretchWidth, verticalOffset, ImageSource.Width - horizontalOffset - 1, stretchHeight));
            }
            //绘制垂直方向的拉伸像素
            if (stretchWidth > 0)
            {
                drawingContext.DrawImage(topCenter, new Rect(horizontalOffset, 0, stretchWidth, verticalOffset));
                drawingContext.DrawImage(bottomCenter, new Rect(horizontalOffset, verticalOffset + stretchHeight, stretchWidth, ImageSource.Height - verticalOffset - 1));
            }
            //绘制中间拉伸的像素
            if (stretchHeight > 0 && stretchWidth > 0)
            {
                drawingContext.DrawImage(center, new Rect(horizontalOffset, verticalOffset, stretchWidth, stretchHeight));
            }
 
            drawingContext.DrawImage(leftBottom, new Rect(0, verticalOffset + stretchHeight, horizontalOffset, ImageSource.Height - verticalOffset - 1));
            drawingContext.DrawImage(rightBottom, new Rect(horizontalOffset + stretchWidth, verticalOffset + stretchHeight, ImageSource.Width - horizontalOffset - 1, ImageSource.Height - verticalOffset - 1));
        }
 
        //仅支持水平方向拉伸
        protected override void OnRender(DrawingContext drawingContext)
        {
            var horizontalOffset = GetHorizontalOffset();
            var left = GetCroppedBitmap(0, 0, horizontalOffset, ImageSource.Height);
            var center = GetCroppedBitmap(horizontalOffset, 0, 1, ImageSource.Height);
            var right = GetCroppedBitmap(horizontalOffset + 1, 0, ImageSource.Width - horizontalOffset - 1, ImageSource.Height);
            drawingContext.DrawImage(left, new Rect(0, 0, horizontalOffset, ImageSource.Height));
            var stretchWidth = GetStretchWidth();
            if (stretchWidth > 0)
            {
                drawingContext.DrawImage(center, new Rect(horizontalOffset, 0, stretchWidth, ImageSource.Height));
            }
            else
            {
                stretchWidth = 0;
            }
            drawingContext.DrawImage(right, new Rect(horizontalOffset + stretchWidth, 0, ImageSource.Width - horizontalOffset - 1, ImageSource.Height));
        }
 
        //仅支持垂直方向拉伸
        protected override void OnRender(DrawingContext drawingContext)
        {
            var verticalOffset = GetVerticalOffset();
            var top = GetCroppedBitmap(0, 0, ImageSource.Width, verticalOffset);
            var center = GetCroppedBitmap(0, verticalOffset, ImageSource.Width, 1);
            var bottom = GetCroppedBitmap(0, verticalOffset + 1, ImageSource.Width, ImageSource.Height - verticalOffset - 1);
            drawingContext.DrawImage(top, new Rect(0, 0, ImageSource.Width, verticalOffset));
            var stretchHeight = GetStretchHeight();
            if (stretchHeight > 0)
            {
                drawingContext.DrawImage(center, new Rect(0, verticalOffset, ImageSource.Width, stretchHeight));
            }
            else
            {
                stretchHeight = 0;
            }
            drawingContext.DrawImage(bottom, new Rect(0, verticalOffset + stretchHeight, ImageSource.Width, ImageSource.Height - verticalOffset - 1));
        }
 
        //测量布局大小,这里要记得重写下。这个版本不支持不同尺寸的分辨率,可以通过计算缩放比来实现
        private Size MeasureCore(Size size, ImageSource imgSource)
        {
            if (imgSource == null) return size;
 
            Size naturalSize;
 
            if (IsHorizontalStretchEnabled && IsVerticalStretchEnabled)
            {
                naturalSize = new Size(size.Width, size.Height);
            }
            else if (IsHorizontalStretchEnabled)
            {
                naturalSize = new Size(size.Width, imgSource.Height);
            }
            else if (IsVerticalStretchEnabled)
            {
                naturalSize = new Size(imgSource.Width, size.Height);
            }
            else
            {
                return size;
            }
 
            return naturalSize;
        }

以上代码就可以实现水平拉伸,垂直拉伸或者水平+垂直拉伸的功能了。目前的测试代码还不支持不同分辨率的图片,demo中的计算是使用了ImageSource的宽高。如果需要支持任意分辨率,可以按渲染的宽高和图片的实际宽高做个比例缩放运算即可。

到此这篇关于WPF实现图片按像素拉伸的文章就介绍到这了,更多相关WPF图片按像素拉伸内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

最新评论