1. 利用DrawingVisual实现绘制拼接
2. 将DrawingVisual转为RenderTargetBitmap
3. 最后再通过复制像素转为WriteableBitmap
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace WpfApp17
{
class numberctrl:Decorator
{
int iComboCount = 0; //连击数字
int iNumImgCount = 0; //图像数量
int iComboFrameIndex = 0; //当前播放帧数
const int kComboFrameCount = 40; //需要播放的总帧数
WriteableBitmap numComboBitmap = null; //拼接后的图像
System.Timers.Timer timer = new System.Timers.Timer();
public numberctrl()
{
timer.Interval = 60;
timer.Elapsed += new System.Timers.ElapsedEventHandler(OnTimer);
timer.AutoReset = true;
timer.Enabled = false;
}
// 随着计时器迭代加帧数,重新绘制
private void OnTimer(object source, System.Timers.ElapsedEventArgs e)
{
System.Windows.Application.Current.Dispatcher.Invoke(() =>
{
iComboFrameIndex++;
InvalidateVisual();
if (iComboFrameIndex >= kComboFrameCount)
{
if (timer.Enabled)
{
timer.Stop();
}
}
});
}
// 外界通过该接口实现触发更新
public void UpdateCombo(int iNewComboCount)
{
iComboCount = iNewComboCount;
if (iComboCount == 0)
{
if (timer.Enabled)
{
timer.Stop();
}
InvalidateVisual();
}
else
{
if (!timer.Enabled)
{
timer.Start();
}
}
}
// 重写Decorator渲染函数
protected override void OnRender(DrawingContext dc)
{
base.OnRender(dc);
if (iComboCount > 0)
{
DoCartoon(dc);
}
}
// 获取图片
public BitmapImage GetBitmapCacheByNum(int num)
{
try
{
BitmapImage bmp;
num = num > 9 ? 9 : num;
num = num < 0 ? 0 : num;
string filePath = $"pack://application:,,,/WpfApp17;Component/Resources/{num}.png";
bmp = new BitmapImage(new Uri(filePath));
bmp.Freeze();
return bmp;
}
catch { }
return null;
}
// 将拼接好的图像进行渲染
private void DoCartoon(DrawingContext dc)
{
//获取图片列表
if (numComboBitmap == null)
{
List<BitmapImage> numBitmpList = new List<BitmapImage>();
if (iComboCount >= 100)
{
if (iComboCount > 999)
iComboCount = 999;
numBitmpList.Add(GetBitmapCacheByNum(iComboCount / 100));
numBitmpList.Add(GetBitmapCacheByNum((iComboCount % 100) / 10));
numBitmpList.Add(GetBitmapCacheByNum(iComboCount % 10));
}
else if (iComboCount >= 10)
{
numBitmpList.Add(GetBitmapCacheByNum(iComboCount / 10));
numBitmpList.Add(GetBitmapCacheByNum(iComboCount % 10));
}
else
{
numBitmpList.Add(GetBitmapCacheByNum(iComboCount % 10));
}
//拼接图片为单个
numComboBitmap = CreateNumberBitmap(numBitmpList);
iNumImgCount = numBitmpList.Count;
}
// 画位图
Rect combRect = getDrawRectByCombo(20, 20, iNumImgCount, iComboFrameIndex);
dc.DrawImage(numComboBitmap, combRect);
}
// list<BitmapImage> -> WriteableBitmap
private WriteableBitmap CreateNumberBitmap(List<BitmapImage> numBitmpList)
{
if (numBitmpList.Count == 0)
return null;
WriteableBitmap bitmpCache = null;
try
{
Rect bitmapRect = new Rect(0, 0, numBitmpList.ElementAt(0).Width * numBitmpList.Count, numBitmpList.ElementAt(0).Height);
bitmpCache = new WriteableBitmap((int)bitmapRect.Width, (int)bitmapRect.Height, 96, 96, PixelFormats.Pbgra32, null);
byte[] pixels = new byte[(int)bitmapRect.Width * (int)bitmapRect.Height * 4];
// images -> drawingvisual
DrawingVisual drawingVisual = new DrawingVisual();
{
DrawingContext dc = drawingVisual.RenderOpen();
double point = 0;
for (int i = 0; i < numBitmpList.Count; i++)
{
Rect drawRect = new Rect(point, 0, numBitmpList.ElementAt(i).Width, numBitmpList.ElementAt(i).Height);
dc.DrawImage(numBitmpList.ElementAt(i), drawRect);
int iOffX = 2;
if (i > 0)
{
iOffX = 5;
}
point += numBitmpList.ElementAt(i).Width - iOffX;
}
dc.Close();
dc = null;
}
// drawingvisual -> rendertargetbitmap
RenderTargetBitmap r = new RenderTargetBitmap((int)bitmapRect.Width, (int)bitmapRect.Height, 96, 96, PixelFormats.Pbgra32);
r.Render(drawingVisual);
int stride = (r.PixelWidth * r.Format.BitsPerPixel + 7) / 8;
r.CopyPixels(pixels, stride, 0);
r.Freeze();
// rendertargetbitmap -> WriteableBitmap
bitmpCache.Lock();
Int32Rect rect = new Int32Rect(0, 0, (int)bitmapRect.Width, (int)bitmapRect.Height);
bitmpCache.WritePixels(rect, pixels, stride, 0);
bitmpCache.AddDirtyRect(rect);
bitmpCache.Unlock();
}
catch (Exception e1)
{
}
return bitmpCache;
}
// 根据帧索引确定绘制区域
private Rect getDrawRectByCombo(int x, int y, int count, int iFrame)
{
// 确认每帧更改宽度
const double dMaxWidth = 35;
const double dMinWdith = 22;
double changeSpan = (dMaxWidth - dMinWdith) / kComboFrameCount;
// 帧数范围防御
double iIndex = (double)iFrame;
iIndex = iIndex > kComboFrameCount ? kComboFrameCount : iIndex;
iIndex = iIndex < 0 ? 0 : iIndex;
// 最大宽度与高度
double sourceWidth = dMaxWidth;
double sourceHeight = sourceWidth * 38 / 29;
// 当前帧数的宽度与高度
double desWidth = sourceWidth - changeSpan * iIndex;
double desHeight = desWidth * 38 / 29;
double offx = (sourceWidth - desWidth) / 2;
double offy = (sourceHeight - desHeight) / 2;
return new Rect(x + offx, y + offy, desWidth * count, desWidth * 38 / 29);
}
}
}