適当に落書き帳みたいなのを作ってみた。(適当すぎるので注意w)

ソースおいとく

using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Brushes;
using Microsoft.Graphics.Canvas.Geometry;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Storage.Pickers;
using Windows.UI;
using Windows.UI.Input;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// 空白ページのアイテム テンプレートについては、http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 を参照してください

namespace TestWin2d2
{
    /// <summary>
    /// それ自体で使用できる空白ページまたはフレーム内に移動できる空白ページ。
    /// </summary>
    public sealed partial class MainPage : Page
    {
        // 描画対象のイメージ
        private CanvasRenderTarget backImage = null;
        // 初期イメージのサイズ
        private const int IMAGE_WIDTH = 1280;
        private const int IMAGE_HEIGHT = 720;
        private const int IMAGE_DPI = 96;
        // 線の太さ
        private const int STROKE_WIDTH = 5;

        // イメージの表示開始位置
        private Point offset;
        // 拡縮率
        private double rate = 1.0;
        // イメージ全体の矩形
        private Rect srcRect;
        // canvas上のイメージコピー先矩形
        private Rect dstRect;

        // 描画中フラグ
        private bool nowDrawing = false;
        // 描画開始位置座標(イメージ上での座標)
        private Vector2 startPoint;

        public MainPage()
        {
            this.InitializeComponent();

            // 初期イメージの作成
            var device = CanvasDevice.GetSharedDevice();
            backImage = new CanvasRenderTarget(device, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_DPI);
            using (var session = backImage.CreateDrawingSession())
            {
                session.Clear(Colors.White);
            }
            srcRect = new Rect(new Point(0, 0), backImage.Size);
            canvas.Invalidate();
        }

        /// <summary>
        /// 開くボタンをクリックしたときの処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void button_Click(object sender, RoutedEventArgs e)
        {
            // 開くダイアログを表示
            var picker = new FileOpenPicker();
            picker.FileTypeFilter.Add(".png");
            picker.FileTypeFilter.Add(".jpg");
            var file = await picker.PickSingleFileAsync();
            if (file == null)
            {
                return;
            }

            // イメージファイルを読み込む
            var device = CanvasDevice.GetSharedDevice();
            using (var stream = await file.OpenReadAsync())
            using (var image = await CanvasBitmap.LoadAsync(device, stream))
            {
                if (backImage != null)
                {
                    backImage.Dispose();
                    backImage = null;
                }
                // バックイメージを作成して読み込んだイメージをコピーする。
                backImage = new CanvasRenderTarget(device, (float)image.Size.Width, (float)image.Size.Height, IMAGE_DPI);
                using (var session = backImage.CreateDrawingSession())
                {
                    session.DrawImage(image, 0, 0);
                    CalcRateAndSize();
                }
                canvas.Invalidate();
            }
        }

        /// <summary>
        /// 保存ボタンをクリックしたときの処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void button1_Click(object sender, RoutedEventArgs e)
        {
            // 保存ダイアログ表示
            var picker = new FileSavePicker();
            picker.FileTypeChoices.Add("PNGファイル", new List<string>() { ".png" });
            var file = await picker.PickSaveFileAsync();
            if(file == null)
            {
                return;
            }
            // ファイルに保存
            using (var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite))
            {
                await backImage.SaveAsync(stream, CanvasBitmapFileFormat.Png);
            }
        }

        /// <summary>
        /// canvasの再描画イベント
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void canvas_Draw(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
        {
            var session = args.DrawingSession;

            session.Clear(Colors.Gray);
            if (backImage != null)
            {
                session.DrawImage(backImage, dstRect, srcRect);
            }
        }

        /// <summary>
        /// canvasのサイズ変更イベント
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void canvas_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if(backImage !=null)
            {
                // 拡縮率などの再計算
                CalcRateAndSize();
            }
        }

        /// <summary>
        /// ポインティングデバイスの移動イベント
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void canvas_PointerMoved(object sender, PointerRoutedEventArgs e)
        {
            if (nowDrawing)     // 描画中なら
            {
                PointerPoint pointerPoint = e.GetCurrentPoint(canvas);
                if (pointerPoint.Properties.IsLeftButtonPressed)
                {
                    Vector2 p2;

                    using (var session = backImage.CreateDrawingSession())
                    using (var brush = new CanvasSolidColorBrush(backImage.Device, Colors.Red))
                    using (var style = CreateStrokeStyle())
                    {
                        // 現在位置をイメージ上の座標に変換
                        p2 = dispPosToOffscreenPos(pointerPoint.Position);
                        float strokeWidth = (float)((double)STROKE_WIDTH * canvas.Dpi / backImage.Dpi / rate);

                        // 前回位置との間で線を引く
                        session.DrawLine(startPoint, p2, brush, strokeWidth, style);
                        canvas.Invalidate();
                    }
                    startPoint = p2;
                }
            }
        }

        /// <summary>
        /// ポインティングデバイスのプレスイベント
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void canvas_PointerPressed(object sender, PointerRoutedEventArgs e)
        {
            PointerPoint pointerPoint = e.GetCurrentPoint(canvas);
            if(pointerPoint.Properties.IsLeftButtonPressed)
            {
                // 描画中にする
                nowDrawing = true;
                // 現在位置をイメージ上の座標に変換して保存
                startPoint = dispPosToOffscreenPos(pointerPoint.Position);
            }
        }

        /// <summary>
        /// ポインティングデバイスのリリースイベント
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void canvas_PointerReleased(object sender, PointerRoutedEventArgs e)
        {
            if (nowDrawing)
            {
                PointerPoint pointerPoint = e.GetCurrentPoint(canvas);
                Vector2 p2;
                using (var session = backImage.CreateDrawingSession())
                using (var brush = new CanvasSolidColorBrush(backImage.Device, Colors.Red))
                using (var style = CreateStrokeStyle())
                {
                    // 現在位置をイメージ上の座標に変換
                    p2 = dispPosToOffscreenPos(pointerPoint.Position);
                    float strokeWidth = (float)((double)STROKE_WIDTH * canvas.Dpi / backImage.Dpi / rate);

                    // 前回位置との間で線を引く
                    session.DrawLine(startPoint, p2, brush, strokeWidth, style);
                    canvas.Invalidate();
                }
                nowDrawing = false;
            }
        }

        /// <summary>
        /// 画面上の座標からイメージ上の座標に変換
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        private Vector2 dispPosToOffscreenPos(Point p)
        {
            return new Vector2((float)((p.X - offset.X) / rate),
                (float)((p.Y - offset.Y) / rate));
        }

        /// <summary>
        /// Strokeのスタイルの生成
        /// </summary>
        /// <returns></returns>
        private CanvasStrokeStyle CreateStrokeStyle()
        {
            var style = new CanvasStrokeStyle();

            style.StartCap = CanvasCapStyle.Round;
            style.LineJoin = CanvasLineJoin.Round;
            style.EndCap = CanvasCapStyle.Round;

            return style;
        }

        /// <summary>
        /// 拡縮率などの再計算
        /// </summary>
        private void CalcRateAndSize()
        {
            double ratex = canvas.Size.Width / backImage.Size.Width;
            double ratey = canvas.Size.Height / backImage.Size.Height;
            rate = ratex < ratey ? ratex : ratey;

            double width = rate * backImage.Size.Width;
            double height = rate * backImage.Size.Height;
            offset.X = ((float)canvas.Size.Width - width) / 2;
            offset.Y = ((float)canvas.Size.Height - height) / 2;
            dstRect = new Rect(offset.X, offset.Y, width, height);
        }
    }
}