WPF實(shí)現(xiàn)圖片按像素拉伸
WPF中的圖片組件,本身是支持不同的拉伸效果。具體如下:
- None, 不做拉伸
- Fill 完全填充(會(huì)變形)
- Uniform 等比縮放,不會(huì)變形
- UniformToFill 等比縮放,并完全填充。不會(huì)變形,但是長的部分會(huì)被裁剪
但是,如果我們要實(shí)現(xiàn)像QQ或者微信這樣子的聊天氣泡功能,直接使用圖片組件就無法滿足要求了。
我們可以觀察下微信的聊天氣泡,他的寬度和高度可以根據(jù)我們輸入的內(nèi)容自動(dòng)調(diào)整,并且背景圖片也不會(huì)存在變形的問題。
今天我們就用WPF來實(shí)現(xiàn)這個(gè)功能!
要實(shí)現(xiàn)不變形的拉伸功能,我們可以針對(duì)1個(gè)像素來進(jìn)行拉伸,這樣拉伸出來的圖片,除了拉伸區(qū)域的像素都是一樣的,其它區(qū)域還是保留了原來的圖片的外觀。
這里主要需要用到 CroppedBitmap類,該類主要用于裁剪,可以對(duì) BitmapImage進(jìn)行裁剪。
微信聊天氣泡這種場景,它需要支持水平和垂直的方向的拉伸效果,我們可以利用 CroppedBitmap,將原始圖片裁剪成9張圖,渲染的時(shí)候,我們分別將9張圖渲染到對(duì)應(yīng)的位置。拉伸的區(qū)域就是9張圖中的上面中間位置,下面中間位置,左邊中間位置,右邊中間位置以及最中間的位置。這幾張圖片,都按1個(gè)像素進(jìn)行裁剪,這樣就不會(huì)出現(xiàn)拉伸的圖片了。
關(guān)鍵代碼:
//根據(jù)裁剪區(qū)域,獲取裁剪后的圖片。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>
/// 按水平方向進(jìn)行裁剪的偏移量
/// </summary>
public double HorizontalPrecent { get; set; }
/// <summary>
/// 按垂直方向進(jìn)行裁剪的偏移量
/// </summary>
public double VerticalPrecent { get; set; }
//繪制水平+垂直拉伸的方法
protected override void OnRender(DrawingContext drawingContext)
{
//這個(gè)需要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));
}
//測(cè)量布局大小,這里要記得重寫下。這個(gè)版本不支持不同尺寸的分辨率,可以通過計(jì)算縮放比來實(shí)現(xiàn)
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;
}
以上代碼就可以實(shí)現(xiàn)水平拉伸,垂直拉伸或者水平+垂直拉伸的功能了。目前的測(cè)試代碼還不支持不同分辨率的圖片,demo中的計(jì)算是使用了ImageSource的寬高。如果需要支持任意分辨率,可以按渲染的寬高和圖片的實(shí)際寬高做個(gè)比例縮放運(yùn)算即可。
到此這篇關(guān)于WPF實(shí)現(xiàn)圖片按像素拉伸的文章就介紹到這了,更多相關(guān)WPF圖片按像素拉伸內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c# EPPlus秘籍之Excel實(shí)現(xiàn)圖表導(dǎo)出
這篇文章主要為大家介紹了c# EPPlus秘籍之Excel實(shí)現(xiàn)圖表導(dǎo)出示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
C#的path.GetFullPath 獲取上級(jí)目錄實(shí)現(xiàn)方法
這篇文章主要介紹了C#的path.GetFullPath 獲取上級(jí)目錄實(shí)現(xiàn)方法,包含了具體的C#實(shí)現(xiàn)方法以及ASP.net與ASP等的方法對(duì)比,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-10-10
C#中圖片.BYTE[]和base64string的轉(zhuǎn)換方法
下面小編就為大家?guī)硪黄狢#中圖片.BYTE[]和base64string的轉(zhuǎn)換方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-02-02
C# Web應(yīng)用調(diào)試開啟外部訪問步驟解析
本文主要介紹了C# Web應(yīng)用調(diào)試開啟外部訪問的實(shí)現(xiàn)過程與方法。具有一定的參考價(jià)值,下面跟著小編一起來看下吧2017-01-01

