zoukankan      html  css  js  c++  java
  • 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(
    "图片保存成功!");
    }
    }

    }
    }

            对于将InkPresenter中绘画出来的图片保存为Png图片得处理,我们在这里借鉴了园子中永恒的记忆兄弟的将元素转为Png图片的方法,在这里贴出两个辅助转Png格式的类。

        /// <summary>
    /// 编辑图片
    /// </summary>
    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; }
    }
    }

    }

            这是Png操作类:

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

            最后我们来看看运行的效果如下,如需源码请点击 SLInkPresenter.zip下载:

  • 相关阅读:
    大数据分析谨慎对待
    大数据时代下,市场研究何去何从
    大数据时代下,市场研究何去何从
    浅谈数据挖掘与数据分析?
    浅谈数据挖掘与数据分析?
    大数据的七种商业化模式
    大数据的七种商业化模式
    栈的应用---编译器左右符号的语法匹配
    Python 对Twitter tweet的元素 (Word, Screen Name, Hash Tag)的频率分析
    东华软件笔试
  • 原文地址:https://www.cnblogs.com/chengxingliang/p/2118404.html
Copyright © 2011-2022 走看看