InkCanvas にてストロークと背景の合成画像をリサイズしてJPG保存する


サンプルコードまで飛びたい方はこちらのリンクをクリック

前提条件および動作確認した環境

  • C#
  • .NET 4.0
  • WPF
  • VisualStudio 2019

なぜ .NET 4.0 なのか、というのは推し測ってほしい。

確認する内容

  • 後述の BEFORE 画像(640x480)を画面読み込み時に InkCanvas(640x480) に背景画像として設定する
  • InkCanvas に書き込み終わった後 Save ボタンをクリックする
  • 同クリック後、リサイズされた「背景画像とストロークの合成画像」(320x240) が C:\Temp\test.jpg として保存される

上記をサンプルコードで示す。処理内容に粗はあることを了承願いたい。

BEFORE

AFTER

サンプル

xaml

<Window x:Class="ImageReflect.MainWindow"
        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"
        xmlns:local="clr-namespace:ImageReflect"
        mc:Ignorable="d"
        Title="MainWindow" Width="640" Height="600" >
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="640" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="480"/>
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid Grid.Row="0">
            <InkCanvas
                Name="MainCanvas" />
        </Grid>
        <Grid Grid.Row="1">
            <Button
                Name="SaveButton"
                Content="Save"
                FontSize="24px"
                Click="onClickSave" />
        </Grid>
    </Grid>
</Window>

デバッグウィンドウ表示時のイメージ

xaml.cs

{ソリューションディレクトリ}/Resource/Image/sea.jpg を用意しておく。

using System;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace ImageReflect
{
  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();
      this.initializeMainCanvas();
    }

    public void initializeMainCanvas()
    {
      var imageBrush = new ImageBrush();
      imageBrush.ImageSource = new BitmapImage(new Uri("pack://application:,,,/Resource/Image/sea.jpg"));
      this.MainCanvas.Background = imageBrush;
    }

    #region UI event handler

    // Save ボタンクリック時の event handler
    private void onClickSave(object sender, RoutedEventArgs e)
    {
      // レンダリングオブジェクトの描画先を作成する
      var drawingVisual = new DrawingVisual();
      var drawingContext = drawingVisual.RenderOpen();

      // InkCanvas 原寸大の描画「領域(rectangle)」を定義する
      var rect = new Rect(0, 0, this.MainCanvas.ActualWidth, this.MainCanvas.ActualHeight);
      // レンダリングオブジェクトに、背景画像を描画する
      drawingContext.DrawRectangle(this.MainCanvas.Background, null, rect);
      // レンダリングオブジェクトに、追加でストローク情報を描画する
      this.MainCanvas.Strokes.Draw(drawingContext);
      // レンダリングオブジェクト情報をフラッシュする
      drawingContext.Close();

      // 描画先をビットマップにする(96dpi)
      var renderTargetBitmap = new RenderTargetBitmap(
        (int)rect.Width, (int)rect.Height, 96d, 96d, PixelFormats.Default
      );
      renderTargetBitmap.Render(drawingVisual);

      // 描画先を 幅・高さ共に0.5倍へ変換する
      var scaledBitmap = new TransformedBitmap(renderTargetBitmap,
        new ScaleTransform(0.5d, 0.5d));

      // ここまでの描画情報をエンコーダーのフレームに追加する
      var encoder = new JpegBitmapEncoder();
      encoder.Frames.Add(BitmapFrame.Create(scaledBitmap));

      // C:\Temp\test.jpg へ JPG として出力
      using (var fileStream = File.Create(@"C:\Temp\test.jpg"))
      {
        encoder.Save(fileStream);
      }
    }

    #endregion UI event handler
  }
}

その他

まだ把握しきれていないが、RenderBitmap 周りでメモリ枯渇(or リーク) の話がちらほら見えるので

サンプルコードはあくまで参考としてほしい。

参考にしたページ

サンプルに使った画像