http://archive.cnblogs.com/a/2270389/
经理让找找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,希望大家指出来,我们共同探讨一下,例如:在移动控件的时候没做边界值判断等等。