WPF實(shí)現(xiàn)圖片按像素拉伸
WPF中的圖片組件,本身是支持不同的拉伸效果。具體如下:
- None, 不做拉伸
- Fill 完全填充(會(huì)變形)
- Uniform 等比縮放,不會(huì)變形
- UniformToFill 等比縮放,并完全填充。不會(huì)變形,但是長(zhǎng)的部分會(huì)被裁剪
但是,如果我們要實(shí)現(xiàn)像QQ或者微信這樣子的聊天氣泡功能,直接使用圖片組件就無(wú)法滿足要求了。
我們可以觀察下微信的聊天氣泡,他的寬度和高度可以根據(jù)我們輸入的內(nèi)容自動(dòng)調(diào)整,并且背景圖片也不會(huì)存在變形的問(wèn)題。
今天我們就用WPF來(lái)實(shí)現(xiàn)這個(gè)功能!
要實(shí)現(xiàn)不變形的拉伸功能,我們可以針對(duì)1個(gè)像素來(lái)進(jìn)行拉伸,這樣拉伸出來(lái)的圖片,除了拉伸區(qū)域的像素都是一樣的,其它區(qū)域還是保留了原來(lái)的圖片的外觀。
這里主要需要用到 CroppedBitmap類,該類主要用于裁剪,可以對(duì) BitmapImage進(jìn)行裁剪。
微信聊天氣泡這種場(chǎng)景,它需要支持水平和垂直的方向的拉伸效果,我們可以利用 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è)量布局大小,這里要記得重寫(xiě)下。這個(gè)版本不支持不同尺寸的分辨率,可以通過(guò)計(jì)算縮放比來(lái)實(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#使用Mutex簡(jiǎn)單實(shí)現(xiàn)程序單實(shí)例運(yùn)行的方法
這篇文章主要介紹了C#使用Mutex簡(jiǎn)單實(shí)現(xiàn)程序單實(shí)例運(yùn)行的方法,涉及C#實(shí)現(xiàn)單實(shí)例程序運(yùn)行的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09
C#使用RestSharp實(shí)現(xiàn)封裝常用的http請(qǐng)求方法
這篇文章主要為大家詳細(xì)介紹了C#如何使用RestSharp實(shí)現(xiàn)封裝常用的http請(qǐng)求方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2024-02-02
C#/VB.NET實(shí)現(xiàn)PPT或PPTX轉(zhuǎn)換為圖像功能
由于大多數(shù)便攜式設(shè)備支持瀏覽圖片而不支持瀏覽PowerPoint 文件,所以相比較而言,圖像對(duì)于用戶而言更加友好。本文將利用C#/VB.NET實(shí)現(xiàn)PPT或PPTX轉(zhuǎn)換為圖像功能,需要的可以參考一下2022-08-08
C#多線程異步執(zhí)行和跨線程訪問(wèn)控件Helper
這篇文章介紹了C#多線程異步執(zhí)行和跨線程訪問(wèn)控件Helper,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04
C# winfrom 模擬ftp文件管理實(shí)現(xiàn)代碼
從網(wǎng)上找到的非常好用的模擬ftp管理代碼,整理了一下,希望對(duì)需要的人有幫助2014-01-01
C#實(shí)現(xiàn)動(dòng)態(tài)加載dll的方法
這篇文章主要介紹了C#實(shí)現(xiàn)動(dòng)態(tài)加載dll的方法,涉及針對(duì)動(dòng)態(tài)鏈接庫(kù)的靈活操作技巧,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-12-12
使用mutex實(shí)現(xiàn)應(yīng)用程序單實(shí)例運(yùn)行代碼分享
本文主要介紹了使用Mutex實(shí)現(xiàn)應(yīng)用程序單實(shí)例運(yùn)行的方法,實(shí)現(xiàn)原理是在程序啟動(dòng)時(shí),請(qǐng)求一個(gè)互斥體,如果能獲取對(duì)指定互斥的訪問(wèn)權(quán),就繼續(xù)運(yùn)行程序,否則就退出程序2014-01-01

