Silverlight实现对图片的涂鸦、绘制矩形、圆形、直线、文本,并且能够移动
经理让找找Silverlight实现画图的功能,找了老半天,根据前辈人的经验和思路自己汇总了一个画图工具,基本上实现对图片的涂鸦、绘制图形、添加文本的功能,希望能对大家开发新的图片编辑器有所帮助,下面所以下我的思路,并奉上我的代码。
首先我想到使用InkPresenter控件,实现图形的编辑,然后将InkPresenters上的控件进行保存为Pngs图片。
我们需要监听InkPresenter控件的三个事件, MouseLeftButtonUp、MouseMove 、MouseLeftButtonDown,为了让我的代码显得比较整齐,我用枚举类型来告诉InkPresenter我要干什么
我将我要画得每一个元素以枚举类型描述出来
由于代码比较简单,我就不做详细解说了,直接给代码附上:
页面代码:
<UserControl x:Class="PicDraw.ImageEdit"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
>
<Grid ShowGridLines="False" Margin="0" >
<Grid.RowDefinitions >
<RowDefinition Height="35"/>
<RowDefinition Height="600"/>
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Canvas Grid.ColumnSpan="2" Grid.Row="0" Grid.Column="0" Width="1150" HorizontalAlignment="Left" Background="#0054e3" >
<TextBlock Margin="10 5 0 0" Height="35" FontSize="16" Foreground="White" Text="画图工具" ></TextBlock>
</Canvas>
<StackPanel VerticalAlignment="Top" Background="#e5eff8" Width="150" Height="600" Grid.Row="1" Grid.Column="0">
<Grid Margin="10 0 0 0">
<Grid.RowDefinitions>
<RowDefinition Height="400"></RowDefinition>
<RowDefinition Height="200"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<TextBlock Width="150">工具栏</TextBlock>
<Grid OpacityMask="#FFEF1C1C" ShowGridLines="False">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Button Name="saveBTN" Click="saveBTN_Click" Grid.Row="0" Grid.Column="0"
Margin="5" >
保存
</Button>
<Button Name="openBTN" Click="openBTN_Click" Grid.Row="0" Grid.Column="1"
Margin="5" >
打开
</Button>
<Button
Name="eraserBtn"
Grid.Row="4"
Grid.Column="0"
Margin="5" Click="toolBTN_Clik" >
橡皮擦
</Button>
<Button Name="bushBtn" Grid.Row="1" Grid.Column="1" Margin="5" Click="toolBTN_Clik">
画笔
</Button>
<Button Name="txtBtn" Grid.Row="1" Grid.Column="0" Margin="5" Click="toolBTN_Clik">
文本
</Button>
<Button Name="BorderBtn" Grid.Row="2" Grid.Column="0" Margin="5" Click="toolBTN_Clik">
矩形
</Button>
<Button Name="circleBtn" Grid.Row="2" Grid.Column="1" Margin="5" Click="toolBTN_Clik"
>画圆</Button>
<Button Grid.Row="3" Grid.Column="0" Margin="5" Name="lineBtn" Click="toolBTN_Clik">划线
</Button>
<Button Name="arrowsBtn" Grid.Row="3" Grid.Column="1" Margin="5" Click="toolBTN_Clik" >箭头
</Button>
</Grid>
</StackPanel>
<StackPanel Grid.Row="1" Opacity="5">
<TextBlock Width="150">颜色</TextBlock>
<ListBox Name="colorLBX" FlowDirection="LeftToRight" UseLayoutRounding="True" TabNavigation="Local" SelectionChanged="ListBox_SelectionChanged">
<ListBoxItem Background="White">White</ListBoxItem>
<ListBoxItem Background="Red">Red</ListBoxItem>
<ListBoxItem Background="Green">Green</ListBoxItem>
<ListBoxItem Background="Blue" >Blue</ListBoxItem>
<ListBoxItem Background="Black" Foreground="White" >Black</ListBoxItem>
<ListBoxItem></ListBoxItem>
</ListBox>
<TextBlock Name="lineWidthTBK">线宽为:5</TextBlock>
<Slider Name="lineWidthSD" Width="100" Minimum="0" Value="5" Maximum="10" ValueChanged="lineWidthSD_ValueChanged" SmallChange="1" />
</StackPanel>
</Grid>
</StackPanel>
<Canvas
ScrollViewer.HorizontalScrollBarVisibility="Visible"
ScrollViewer.VerticalScrollBarVisibility="Visible"
Name="cnsDesignerContainer"
Grid.Row="1" Grid.Column="1"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Width="1000"
Height="600"
Background="#dcdcdc">
<InkPresenter x:Name="inkPresenter" MouseLeftButtonDown="inkPresenter_MouseLeftButtonDown"
MouseLeftButtonUp="inkPresenter_MouseLeftButtonUp"
MouseMove="inkPresenter_MouseMove"
Cursor="Stylus" Canvas.ZIndex="-1" Background="Transparent">
<Image Name="test"
MaxHeight="600"
Canvas.ZIndex="-1"
MaxWidth="1000"
Source="/PicDraw;component/Images/Penguins.jpg">
</Image>
</InkPresenter>
</Canvas>
</Grid>
</UserControl>
后台代码实现:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;
using System.Windows.Media.Imaging;
using System.Windows.Ink;
using System.IO;
namespace PicDraw
{
public partial class ImageEdit : UserControl
{
/// <summary>
/// 起始点
/// </summary>
private Ellipse ellipse = new Ellipse();
// 在涂鸦板上描绘的笔划
private System.Windows.Ink.Stroke _newStroke;
// 在涂鸦板上描绘的笔划的颜色
private System.Windows.Media.Color _currentColor = Colors.Red;
private double lineWidth = 5d;
private SharpType sharpType = SharpType.brush;
// 当前是否正在 InkPresenter 上捕获鼠标
private bool _isCapture = false;
private bool _isStrart = true;
private double _startX = 0, _startY = 0;
bool trackingMouseMove = false;
Point mousePosition;
public ImageEdit()
{
InitializeComponent();
this.inkPresenter.Height = test.Height;
this.inkPresenter.Width = test.Width;
}
#region 工具栏
/// <summary>
/// 颜色选择器
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selectItem = colorLBX.SelectedItem as ListBoxItem;
var bu = selectItem.Background as SolidColorBrush;
_currentColor = bu.Color;
}
/// <summary>
/// 保存
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void saveBTN_Click(object sender, RoutedEventArgs e)
{
//保存InkPresenter涂鸦板内绘画的图
WriteableBitmap _bitmap = new WriteableBitmap(inkPresenter, null);
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "PNG Files (*.png)|*.png|All Files (*.*)|*.*";
sfd.DefaultExt = ".png";
sfd.FilterIndex = 1;
if ((bool)sfd.ShowDialog())
{
using (Stream fs = sfd.OpenFile())
{
int width = _bitmap.PixelWidth;
int height = _bitmap.PixelHeight;
EditableImage ei = new EditableImage(width, height);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
int pixel = _bitmap.Pixels[(i * width) + j];
ei.SetPixel(j, i,
(byte)((pixel >> 16) & 0xFF),
(byte)((pixel >> 8) & 0xFF),
(byte)(pixel & 0xFF),
(byte)((pixel >> 24) & 0xFF)
);
}
}
//获取流
Stream png = ei.GetStream();
int len = (int)png.Length;
byte[] bytes = new byte[len];
png.Read(bytes, 0, len);
fs.Write(bytes, 0, len);
MessageBox.Show("图片保存成功!");
}
}
}
/// <summary>
/// 工具栏
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void toolBTN_Clik(object sender, RoutedEventArgs e)
{
_startX = 0;
_startY = 0;
_isStrart = true;
var btn = sender as Button;
var name = btn.Name;
if (name.Equals("eraserBtn"))
{
inkPresenter.Cursor = Cursors.Eraser;
this.sharpType = SharpType.eraser;
return;
}
if (name.Equals("bushBtn"))
{
this.sharpType = SharpType.brush;
return;
}
if (name.Equals("txtBtn"))
{
this.sharpType = SharpType.text;
return;
}
if (name.Equals("BorderBtn"))
{
this.sharpType = SharpType.border;
return;
}
if (name.Equals("circleBtn"))
{
this.sharpType = SharpType.ellipse;
return;
}
if (name.Equals("arrowsBtn"))
{
inkPresenter.Cursor = Cursors.Arrow;
this.sharpType = SharpType.pointer;
return;
}
if (name.Equals("lineBtn"))
{
this.sharpType = SharpType.line;
return;
}
}
#endregion
#region 涂鸦板的操作
private void inkPresenter_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
inkPresenter.CaptureMouse();
_isCapture = true;
switch (sharpType)
{
case SharpType.border: BorderStroke(e, false); break;
case SharpType.brush: BrushStroke(e); break;
case SharpType.ellipse: EllipseStroke(e, false); break;
case SharpType.line: LineStroke(e, false); break;
case SharpType.text: TextStroke(e, false); break;
}
}
private void inkPresenter_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
inkPresenter.ReleaseMouseCapture();
_newStroke = null;
_isCapture = false;
}
private void inkPresenter_MouseMove(object sender, MouseEventArgs e)
{
if (_isCapture)
{
switch (sharpType)
{
case SharpType.brush: BrushStroke(e); break;
//case SharpType.border: BorderStroke(e, true); break;
//case SharpType.ellipse: EllipseStroke(e, true); break;
//case SharpType.line: LineStroke(e, true); break;
}
}
}
#region 操作板
private void TextStroke(MouseEventArgs e, bool isMove)
{
Point p = e.GetPosition(inkPresenter);
if (_isStrart)
{
TextBox txtBox = new TextBox();
txtBox.Height = 30;
txtBox.Width = 200;
txtBox.Text = "请输入字体";
//字体颜色
txtBox.Foreground = new SolidColorBrush(_currentColor);
txtBox.BorderBrush = null;
txtBox.Background = null;
txtBox.MouseLeftButtonDown += new MouseButtonEventHandler(OnMouseDown);
txtBox.MouseMove += new MouseEventHandler(OnMouseMove);
txtBox.MouseLeftButtonUp += new MouseButtonEventHandler(OnMouseUp);
txtBox.TextChanged += null;
Canvas.SetTop(txtBox, p.Y);
Canvas.SetLeft(txtBox, p.X);
Canvas.SetZIndex(txtBox, 1);
inkPresenter.Children.Add(txtBox);
//<TextBox Background="{x:Null}" BorderBrush="{x:Null}" FontWeight="Bold"></TextBox>
}
}
#region 划线操作
private Line startLine = null;
/// <summary>
/// 划线处理事件
/// </summary>
/// <param name="e"></param>
/// <param name="isMove">是否为移动鼠标,</param>
private void LineStroke(MouseEventArgs e, bool isMove)
{
var point = e.GetPosition(this.inkPresenter);
//如果刚开始则话一个点
if (_isStrart)
{
_startX = point.X;
_startY = point.Y;
DrawPoint(_startX, _startY);
}
else
{
DrawOneLine(_startX, _startY, point.X, point.Y, isMove);
}
_isStrart = !_isStrart;
}
/// <summary>
/// 画一个点
/// </summary>
/// <param name="pX1"></param>
/// <param name="pY1"></param>
private void DrawPoint(double pX1, double pY1)
{
if (inkPresenter.Children.Contains(ellipse))
{
inkPresenter.Children.Remove(ellipse);
}
ellipse.Stroke = new SolidColorBrush(_currentColor);//动态设置Stroke属性的方法。
ellipse.StrokeThickness = 2;
ellipse.Width = 4;
ellipse.Height = 4;
Canvas.SetLeft(ellipse, pX1);
Canvas.SetTop(ellipse, pY1);
Canvas.SetZIndex(ellipse, 1);
inkPresenter.Children.Add(ellipse);
}
/// <summary>
/// 画一根线
/// </summary>
/// <param name="pX1"></param>
/// <param name="pY1"></param>
/// <param name="pX2"></param>
/// <param name="pY2"></param>
/// <returns></returns>
private void DrawOneLine(double pX1, double pY1, double pX2, double pY2, bool isMove)
{
inkPresenter.Children.Remove(startLine);
if (isMove)
{
startLine = new Line();
startLine.X1 = pX1;
startLine.Y1 = pY1;
startLine.X2 = pX2;
startLine.Y2 = pY2;
startLine.StrokeThickness = lineWidth;
startLine.Stroke = new SolidColorBrush(_currentColor);
Canvas.SetZIndex(startLine, 1);
inkPresenter.Children.Add(startLine);
return;
}
else
{
Line lastLine = new Line();
lastLine.X1 = pX1;
lastLine.Y1 = pY1;
lastLine.X2 = pX2;
lastLine.Y2 = pY2;
lastLine.Stroke = new SolidColorBrush(_currentColor);
lastLine.StrokeThickness = lineWidth;
lastLine.MouseLeftButtonDown += new MouseButtonEventHandler(OnMouseDown);
lastLine.MouseMove += new MouseEventHandler(OnMouseMove);
lastLine.MouseLeftButtonUp += new MouseButtonEventHandler(OnMouseUp);
//移除起始线
Canvas.SetZIndex(lastLine, 1);
//清空startLine的内容
startLine = null;
inkPresenter.Children.Add(lastLine);
return;
}
}
#endregion
#region 移动控件
void OnMouseDown(object sender, MouseButtonEventArgs e)
{
if (sharpType == SharpType.pointer)
{
FrameworkElement element = sender as FrameworkElement;//当前元件
mousePosition = e.GetPosition(null);//鼠标位置
trackingMouseMove = true;
if (null != element)
{
element.CaptureMouse();//设置鼠标捕获
element.Cursor = Cursors.Hand;
}
}
}
void OnMouseMove(object sender, MouseEventArgs e)
{
if (sharpType == SharpType.pointer)
{
FrameworkElement element = sender as FrameworkElement;
if (trackingMouseMove)
{
//计算位置
double deltaV = e.GetPosition(null).Y - mousePosition.Y;
double deltaH = e.GetPosition(null).X - mousePosition.X;
double newTop = deltaV + (double)element.GetValue(Canvas.TopProperty);
double newLeft = deltaH + (double)element.GetValue(Canvas.LeftProperty);
//GetValue();SetValue();
element.SetValue(Canvas.TopProperty, newTop);
element.SetValue(Canvas.LeftProperty, newLeft);
mousePosition = e.GetPosition(null);
}
}
}
void OnMouseUp(object sender, MouseButtonEventArgs e)
{
if (sharpType == SharpType.pointer)
{
FrameworkElement element = sender as FrameworkElement;
trackingMouseMove = false;
element.ReleaseMouseCapture();//释放鼠标
mousePosition.X = mousePosition.Y = 0;
element.Cursor = null;
}
if (sharpType == SharpType.eraser)
{
inkPresenter.Children.Remove(sender as UIElement);
}
}
#endregion
#region 画圆
private void EllipseStroke(MouseEventArgs e, bool isMove)
{
Point p = e.GetPosition(this.inkPresenter);
if (this._isStrart)
{
_startX = p.X;//设置起点坐标
_startY = p.Y;
DrawPoint(p.X, p.Y);
}
else
{
DrawCrile(_startX,_startY, p.X, p.Y,isMove);
}
_isStrart = !_isStrart;
}
private void DrawCrile(double pX1, double pY1, double pX2, double pY2,bool isMove)
{
if (isMove)
{ }
else
{
Ellipse e = new Ellipse();
e.Height = System.Math.Sqrt(System.Math.Abs(pX1 - pX2) * System.Math.Abs(pX1 - pX2) + System.Math.Abs(pY1 - pY2) * System.Math.Abs(pY1 - pY2)) * 2;
e.Width = e.Height;
e.Stroke = new SolidColorBrush(_currentColor);
e.StrokeThickness = this.lineWidth;
e.MouseLeftButtonDown += new MouseButtonEventHandler(OnMouseDown);
e.MouseMove += new MouseEventHandler(OnMouseMove);
e.MouseLeftButtonUp += new MouseButtonEventHandler(OnMouseUp);
Canvas.SetZIndex(e, 10);
Canvas.SetTop(e, pY1 - e.Height / 2);
Canvas.SetLeft(e, pX1 - e.Height / 2);
inkPresenter.Children.Add(e);
}
}
#endregion
#region 矩形
private void BorderStroke(MouseEventArgs e, bool isMove)
{
Point p = e.GetPosition(this.inkPresenter);
if (_isStrart)
{
_startX = p.X;//设置起点坐标
_startY = p.Y;
DrawPoint(p.X, p.Y);
}
else
{
DrawBord(_startX, _startY, p.X, p.Y,isMove);
}
_isStrart = !_isStrart;
}
private void DrawBord(double _startX, double _startY, double pX2, double pY2, bool isMove)
{
if (isMove)
{ }
else
{
Border b = new Border();
b.Width = System.Math.Abs(pX2 - _startX);
b.Height = System.Math.Abs(pY2 - _startY);
b.BorderThickness = new Thickness(lineWidth);
//动态设置矩形的颜色
b.BorderBrush = new SolidColorBrush(_currentColor);
b.MouseLeftButtonDown += new MouseButtonEventHandler(OnMouseDown);
b.MouseMove += new MouseEventHandler(OnMouseMove);
b.MouseLeftButtonUp += new MouseButtonEventHandler(OnMouseUp);
Canvas.SetLeft(b, _startX > pX2 ? pX2 : _startX);
Canvas.SetTop(b, _startY > pY2 ? pY2 : _startY);
Canvas.SetZIndex(b, 1000);
this.inkPresenter.Children.Add(b);
}
}
#endregion
private void BrushStroke(MouseEventArgs e)
{
if (_newStroke != null)
{
_newStroke.StylusPoints.Add(e.StylusDevice.GetStylusPoints(inkPresenter));
}
_newStroke = new System.Windows.Ink.Stroke();
_newStroke.DrawingAttributes.Width = lineWidth;
_newStroke.DrawingAttributes.Height = lineWidth;
_newStroke.DrawingAttributes.Color = _currentColor;
_newStroke.StylusPoints.Add(e.StylusDevice.GetStylusPoints(inkPresenter));
inkPresenter.Strokes.Add(_newStroke);
}
#endregion
private void lineWidthSD_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (lineWidthTBK != null)
{
lineWidthTBK.Text = "线宽为:" + lineWidthSD.Value;
this.lineWidth = lineWidthSD.Value;
}
}
#endregion
private void openBTN_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog o = new OpenFileDialog() {
Multiselect=false,
Filter="图片文件(*.jpg,*.gif,*.bmp,*.png)|*.jpg;*.gif;*.bmp;*.png"
};
if (o.ShowDialog() == true)
{
using (Stream stream = o.File.OpenRead())
{
//获取图片流信息并完成与Image控件的绑定
BitmapImage image = new BitmapImage();
image.SetSource(stream);
inkPresenter.Children.Clear();
Image img = new Image();
Canvas.SetLeft(img, 0);
Canvas.SetTop(img, 0);
Canvas.SetZIndex(img, -1);
img.Source = image;
img.MaxWidth = 1000;
img.MaxHeight = 600;
inkPresenter.Children.Add(img);
stream.Close();
}
}
}
}
}
图片转为Png
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;
namespace PicDraw
{
public class EditableImage
{
private int _width = 0;
private int _height = 0;
private bool _init = false;
private byte[] _buffer;
private int _rowLength;
/// <summary>
/// 当图片错误时引发
/// </summary>
public event EventHandler<EditableImageErrorEventArgs> ImageError;
/// <summary>
/// 实例化
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
public EditableImage(int width, int height)
{
this.Width = width;
this.Height = height;
}
public int Width
{
get
{
return _width;
}
set
{
if (_init)
{
OnImageError("错误: 图片初始化后不可以改变宽度");
}
else if ((value <= 0) || (value > 2047))
{
OnImageError("错误: 宽度必须在 0 到 2047");
}
else
{
_width = value;
}
}
}
public int Height
{
get
{
return _height;
}
set
{
if (_init)
{
OnImageError("错误: 图片初始化后不可以改变高度");
}
else if ((value <= 0) || (value > 2047))
{
OnImageError("错误: 高度必须在 0 到 2047");
}
else
{
_height = value;
}
}
}
public void SetPixel(int col, int row, Color color)
{
SetPixel(col, row, color.R, color.G, color.B, color.A);
}
public void SetPixel(int col, int row, byte red, byte green, byte blue, byte alpha)
{
if (!_init)
{
_rowLength = _width * 4 + 1;
_buffer = new byte[_rowLength * _height];
// Initialize
for (int idx = 0; idx < _height; idx++)
{
_buffer[idx * _rowLength] = 0; // Filter bit
}
_init = true;
}
if ((col > _width) || (col < 0))
{
OnImageError("Error: Column must be greater than 0 and less than the Width");
}
else if ((row > _height) || (row < 0))
{
OnImageError("Error: Row must be greater than 0 and less than the Height");
}
// Set the pixel
int start = _rowLength * row + col * 4 + 1;
_buffer[start] = red;
_buffer[start + 1] = green;
_buffer[start + 2] = blue;
_buffer[start + 3] = alpha;
}
public Color GetPixel(int col, int row)
{
if ((col > _width) || (col < 0))
{
OnImageError("Error: Column must be greater than 0 and less than the Width");
}
else if ((row > _height) || (row < 0))
{
OnImageError("Error: Row must be greater than 0 and less than the Height");
}
Color color = new Color();
int _base = _rowLength * row + col + 1;
color.R = _buffer[_base];
color.G = _buffer[_base + 1];
color.B = _buffer[_base + 2];
color.A = _buffer[_base + 3];
return color;
}
public Stream GetStream()
{
Stream stream;
if (!_init)
{
OnImageError("Error: Image has not been initialized");
stream = null;
}
else
{
stream = PngEncoder.Encode(_buffer, _width, _height);
}
return stream;
}
private void OnImageError(string msg)
{
if (null != ImageError)
{
EditableImageErrorEventArgs args = new EditableImageErrorEventArgs();
args.ErrorMessage = msg;
ImageError(this, args);
}
}
public class EditableImageErrorEventArgs : EventArgs
{
private string _errorMessage = string.Empty;
public string ErrorMessage
{
get { return _errorMessage; }
set { _errorMessage = value; }
}
}
}
/// <summary>
/// PNG格式操作类
/// </summary>
public class PngEncoder
{
private const int _ADLER32_BASE = 65521;
private const int _MAXBLOCK = 0xFFFF;
private static byte[] _HEADER = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
private static byte[] _IHDR = { (byte)'I', (byte)'H', (byte)'D', (byte)'R' };
private static byte[] _GAMA = { (byte)'g', (byte)'A', (byte)'M', (byte)'A' };
private static byte[] _IDAT = { (byte)'I', (byte)'D', (byte)'A', (byte)'T' };
private static byte[] _IEND = { (byte)'I', (byte)'E', (byte)'N', (byte)'D' };
private static byte[] _4BYTEDATA = { 0, 0, 0, 0 };
private static byte[] _ARGB = { 0, 0, 0, 0, 0, 0, 0, 0, 8, 6, 0, 0, 0 };
/// <summary>
/// 编码
/// </summary>
/// <param name="data"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
public static Stream Encode(byte[] data, int width, int height)
{
MemoryStream ms = new MemoryStream();
byte[] size;
// Write PNG header
ms.Write(_HEADER, 0, _HEADER.Length);
// Write IHDR
// Width: 4 bytes
// Height: 4 bytes
// Bit depth: 1 byte
// Color type: 1 byte
// Compression method: 1 byte
// Filter method: 1 byte
// Interlace method: 1 byte
size = BitConverter.GetBytes(width);
_ARGB[0] = size[3]; _ARGB[1] = size[2]; _ARGB[2] = size[1]; _ARGB[3] = size[0];
size = BitConverter.GetBytes(height);
_ARGB[4] = size[3]; _ARGB[5] = size[2]; _ARGB[6] = size[1]; _ARGB[7] = size[0];
// Write IHDR chunk
WriteChunk(ms, _IHDR, _ARGB);
// Set gamma = 1
size = BitConverter.GetBytes(1 * 100000);
_4BYTEDATA[0] = size[3]; _4BYTEDATA[1] = size[2]; _4BYTEDATA[2] = size[1]; _4BYTEDATA[3] = size[0];
// Write gAMA chunk
WriteChunk(ms, _GAMA, _4BYTEDATA);
// Write IDAT chunk
uint widthLength = (uint)(width * 4) + 1;
uint dcSize = widthLength * (uint)height;
// First part of ZLIB header is 78 1101 1010 (DA) 0000 00001 (01)
// ZLIB info
//
// CMF Byte: 78
// CINFO = 7 (32K window size)
// CM = 8 = (deflate compression)
// FLG Byte: DA
// FLEVEL = 3 (bits 6 and 7 - ignored but signifies max compression)
// FDICT = 0 (bit 5, 0 - no preset dictionary)
// FCHCK = 26 (bits 0-4 - ensure CMF*256+FLG / 31 has no remainder)
// Compressed data
// FLAGS: 0 or 1
// 00000 00 (no compression) X (X=1 for last block, 0=not the last block)
// LEN = length in bytes (equal to ((width*4)+1)*height
// NLEN = one's compliment of LEN
// Example: 1111 1011 1111 1111 (FB), 0000 0100 0000 0000 (40)
// Data for each line: 0 [RGBA] [RGBA] [RGBA] ...
// ADLER32
uint adler = ComputeAdler32(data);
MemoryStream comp = new MemoryStream();
// 64K的块数计算
uint rowsPerBlock = _MAXBLOCK / widthLength;
uint blockSize = rowsPerBlock * widthLength;
uint blockCount;
ushort length;
uint remainder = dcSize;
if ((dcSize % blockSize) == 0)
{
blockCount = dcSize / blockSize;
}
else
{
blockCount = (dcSize / blockSize) + 1;
}
// 头部
comp.WriteByte(0x78);
comp.WriteByte(0xDA);
for (uint blocks = 0; blocks < blockCount; blocks++)
{
// 长度
length = (ushort)((remainder < blockSize) ? remainder : blockSize);
if (length == remainder)
{
comp.WriteByte(0x01);
}
else
{
comp.WriteByte(0x00);
}
comp.Write(BitConverter.GetBytes(length), 0, 2);
comp.Write(BitConverter.GetBytes((ushort)~length), 0, 2);
// Write 块
comp.Write(data, (int)(blocks * blockSize), length);
//下一块
remainder -= blockSize;
}
WriteReversedBuffer(comp, BitConverter.GetBytes(adler));
comp.Seek(0, SeekOrigin.Begin);
byte[] dat = new byte[comp.Length];
comp.Read(dat, 0, (int)comp.Length);
WriteChunk(ms, _IDAT, dat);
// Write IEND chunk
WriteChunk(ms, _IEND, new byte[0]);
// Reset stream
ms.Seek(0, SeekOrigin.Begin);
return ms;
}
private static void WriteReversedBuffer(Stream stream, byte[] data)
{
int size = data.Length;
byte[] reorder = new byte[size];
for (int idx = 0; idx < size; idx++)
{
reorder[idx] = data[size - idx - 1];
}
stream.Write(reorder, 0, size);
}
private static void WriteChunk(Stream stream, byte[] type, byte[] data)
{
int idx;
int size = type.Length;
byte[] buffer = new byte[type.Length + data.Length];
// 初始化缓冲
for (idx = 0; idx < type.Length; idx++)
{
buffer[idx] = type[idx];
}
for (idx = 0; idx < data.Length; idx++)
{
buffer[idx + size] = data[idx];
}
WriteReversedBuffer(stream, BitConverter.GetBytes(data.Length));
// Write 类型和数据
stream.Write(buffer, 0, buffer.Length); // Should always be 4 bytes
// 计算和书写的CRC
WriteReversedBuffer(stream, BitConverter.GetBytes(GetCRC(buffer)));
}
private static uint[] _crcTable = new uint[256];
private static bool _crcTableComputed = false;
private static void MakeCRCTable()
{
uint c;
for (int n = 0; n < 256; n++)
{
c = (uint)n;
for (int k = 0; k < 8; k++)
{
if ((c & (0x00000001)) > 0)
c = 0xEDB88320 ^ (c >> 1);
else
c = c >> 1;
}
_crcTable[n] = c;
}
_crcTableComputed = true;
}
private static uint UpdateCRC(uint crc, byte[] buf, int len)
{
uint c = crc;
if (!_crcTableComputed)
{
MakeCRCTable();
}
for (int n = 0; n < len; n++)
{
c = _crcTable[(c ^ buf[n]) & 0xFF] ^ (c >> 8);
}
return c;
}
//返回的字节的CRC缓冲区
private static uint GetCRC(byte[] buf)
{
return UpdateCRC(0xFFFFFFFF, buf, buf.Length) ^ 0xFFFFFFFF;
}
private static uint ComputeAdler32(byte[] buf)
{
uint s1 = 1;
uint s2 = 0;
int length = buf.Length;
for (int idx = 0; idx < length; idx++)
{
s1 = (s1 + (uint)buf[idx]) % _ADLER32_BASE;
s2 = (s2 + s1) % _ADLER32_BASE;
}
return (s2 << 16) + s1;
}
}
}
呵呵:简单的画图工具实现,里面还有较多的Bug,希望大家指出来,我们共同探讨一下,例如:在移动控件的时候没做边界值判断等等。