天天看點

Silverlight實用竅門系列:50.InkPresenter塗鴉闆的基本使用,以及将效果儲存為Png圖檔【附帶源碼執行個體】

    在Silverlight中我們有時候需要手工繪制線條或者直線等,在這裡我們認識一下InkPresenter控件,它将支援使用者使用滑鼠、手寫闆等工具來繪制圖形或者筆迹,用途為塗鴉、筆迹确認等等。

        InkPresenter是繼承于Canvas控件的支援所有的Canvas屬性,并且其内部還可以嵌套顯示其他控件。InkPresenter控件的顯 示分為三層:底層是InkPresenter的Background、中間層是InkPresenter的Children屬性的控件、最後才是 Strokes屬性中的筆畫層。

        對于Strokes屬性中的筆畫Stroke我們可以設定它的顔色、粗細、外邊框顔色等等屬性以獲得滿意的筆畫類型。下面我們來看看如何使用InkPresenter控件,首先我們來看Xaml代碼如下:

<Grid x:Name="LayoutRoot1" Background="White"> 

    <Canvas> 

        <Border BorderThickness="1" Margin="50 10 0 0" BorderBrush="CadetBlue"  

                HorizontalAlignment="Center" VerticalAlignment="Center"> 

            <InkPresenter x:Name="iPresenter" Height="500" Width="500" 

              MouseLeftButtonDown="iPresenter_MouseLeftButtonDown"  

              LostMouseCapture="iPresenter_LostMouseCapture"  

              MouseMove="iPresenter_MouseMove"  

              Background="Transparent" Opacity="1" > 

                <TextBox Width="138" Canvas.Left="58" Canvas.Top="105"></TextBox> 

            </InkPresenter> 

        </Border> 

        <Button Canvas.Left="560" Canvas.Top="11" Content="将塗鴉儲存為圖檔" Height="23" 

                Name="button1" Width="104" Click="button1_Click" /> 

        <Image Name="showIP" Width="400" Height="400" Canvas.Left="560" Canvas.Top="60"></Image> 

    </Canvas> 

</Grid> 

        然後我們來看看Xaml.cs代碼如下:

public partial class MainPage : UserControl 

    public MainPage() 

    { 

        InitializeComponent(); 

        SetPresenterClip(); 

    } 

    Stroke myStroke; 

    private void iPresenter_MouseLeftButtonDown(object sender, MouseEventArgs e) 

        //讓滑鼠捕獲資料 

        iPresenter.CaptureMouse(); 

        //收集筆觸資料點儲存值StylusPointCollection集合中 

        StylusPointCollection stylusPointCollection = new StylusPointCollection(); 

        stylusPointCollection.Add(e.StylusDevice.GetStylusPoints(iPresenter)); 

        //将資料點的結合儲存為一個筆畫 

        myStroke = new Stroke(stylusPointCollection); 

        //設定筆畫的繪畫效果,如顔色,大小等。 

        myStroke.DrawingAttributes.Color = Colors.Gray; 

        myStroke.DrawingAttributes.Width = 1; 

        myStroke.DrawingAttributes.Height = 1; 

        iPresenter.Strokes.Add(myStroke); 

    private void iPresenter_MouseMove(object sender, MouseEventArgs e) 

        //在滑鼠移動的過程中将資料點加入到筆畫中去。 

        if (myStroke != null) 

            myStroke.StylusPoints.Add(e.StylusDevice.GetStylusPoints(iPresenter)); 

    private void iPresenter_LostMouseCapture(object sender, MouseEventArgs e) 

        //将筆畫清空 

        myStroke = null; 

        iPresenter.ReleaseMouseCapture();//釋放滑鼠坐标 

    /// <summary> 

    /// 設定繪畫區域為InkPresenter的大小 

    /// </summary> 

    private void SetPresenterClip() 

        RectangleGeometry MyRectangleGeometry = new RectangleGeometry(); 

        MyRectangleGeometry.Rect = new Rect(0, 0, iPresenter.ActualWidth, iPresenter.ActualHeight); 

        //設定擷取繪畫内容的有效區域 

        iPresenter.Clip = MyRectangleGeometry; 

    private void button1_Click(object sender, RoutedEventArgs e) 

        //儲存InkPresenter塗鴉闆内繪畫的圖 

        WriteableBitmap _bitmap = new WriteableBitmap(iPresenter, null); 

        this.showIP.Source = _bitmap; 

        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> 

public class EditableImage 

    private int _width = 0; 

    private int _height = 0; 

    private bool _init = false; 

    private byte[] _buffer; 

    private int _rowLength; 

    /// 當圖檔錯誤時引發 

    public event EventHandler<EditableImageErrorEventArgs> ImageError; 

    /// 執行個體化 

    /// <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 

            return _height; 

                OnImageError("錯誤: 圖檔初始化後不可以改變高度"); 

                OnImageError("錯誤: 高度必須在 0 到 2047"); 

                _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) 

        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; 

            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; } 

        這是Png操作類:

/// PNG格式操作類 

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 }; 

    /// 編碼 

    /// <param name="data"></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; 

            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); 

                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; 

<a target="_blank" href="http://blog.51cto.com/attachment/201204/132445579.jpg"></a>

本文轉自程興亮 51CTO部落格,原文連結:http://blog.51cto.com/chengxingliang/826757

繼續閱讀