WPF實(shí)現(xiàn)圖片按像素拉伸
WPF中的圖片組件,本身是支持不同的拉伸效果。具體如下:
- None, 不做拉伸
- Fill 完全填充(會(huì)變形)
- Uniform 等比縮放,不會(huì)變形
- UniformToFill 等比縮放,并完全填充。不會(huì)變形,但是長(zhǎng)的部分會(huì)被裁剪
但是,如果我們要實(shí)現(xiàn)像QQ或者微信這樣子的聊天氣泡功能,直接使用圖片組件就無(wú)法滿(mǎn)足要求了。
我們可以觀(guān)察下微信的聊天氣泡,他的寬度和高度可以根據(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)的圖片的外觀(guān)。
這里主要需要用到 CroppedBitmap類(lèi),該類(lèi)主要用于裁剪,可以對(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-09C#使用RestSharp實(shí)現(xiàn)封裝常用的http請(qǐng)求方法
這篇文章主要為大家詳細(xì)介紹了C#如何使用RestSharp實(shí)現(xiàn)封裝常用的http請(qǐng)求方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2024-02-02C#/VB.NET實(shí)現(xiàn)PPT或PPTX轉(zhuǎn)換為圖像功能
由于大多數(shù)便攜式設(shè)備支持瀏覽圖片而不支持瀏覽PowerPoint 文件,所以相比較而言,圖像對(duì)于用戶(hù)而言更加友好。本文將利用C#/VB.NET實(shí)現(xiàn)PPT或PPTX轉(zhuǎn)換為圖像功能,需要的可以參考一下2022-08-08C#多線(xiàn)程異步執(zhí)行和跨線(xiàn)程訪(fǎng)問(wèn)控件Helper
這篇文章介紹了C#多線(xiàn)程異步執(zhí)行和跨線(xiàn)程訪(fǎng)問(wèn)控件Helper,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04C# winfrom 模擬ftp文件管理實(shí)現(xiàn)代碼
從網(wǎng)上找到的非常好用的模擬ftp管理代碼,整理了一下,希望對(duì)需要的人有幫助2014-01-01C#實(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ì)指定互斥的訪(fǎng)問(wèn)權(quán),就繼續(xù)運(yùn)行程序,否則就退出程序2014-01-01