午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

WPFのBitmapSourceVisualizer、画像の保存とクリップボードへコピーできるようにした

gogowaten.hatenablog.com

これの続き

 

  • dllの名前を変更した、MyVisualizerからBitmapSourceVisualizer
  • 画像を保存できるようにした
  • 画像をクリップボードへコピーできるようにした
  • 画像の表示方法を選択出るようにした、実寸とウィンドウに合わせる

 

f:id:gogowaten:20200116091958p:plain

これで十分かなあ

 

ダウンロード先

BitmapSourceVisualizer.zip

 

ギットハブ

github.com

 

 

画像ファイルとして保存

f:id:gogowaten:20200116093526p:plain

保存

保存形式はpngbmp

 

f:id:gogowaten:20200116093633p:plain

コピーで画像をクリップボードへコピー

 

表示方式、実寸

f:id:gogowaten:20200116094145p:plain

実寸だと元のままの大きさ、ウィンドウより画像が大きければスクロールバーが表示される

 

表示方式、ウィンドウに合わせる

f:id:gogowaten:20200116094318p:plain

画像がウィンドウより大きいときはウィンドウに収まるように縮小表示、アスペクト比は保ったまま

 

表示方式、ウィンドウに合わせる

f:id:gogowaten:20200116093711p:plain

 

画像がウィンドウより小さいときは引き伸ばして表示される、これもアスペクト比保持

拡大縮小はこれで代替できるかなあと

 

f:id:gogowaten:20200116094622p:plain

背景色を黒にする

 

 

using Microsoft.VisualStudio.DebuggerVisualizers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;


[assembly: System.Diagnostics.DebuggerVisualizer(
    typeof(BitmapSourceVisualizer.MyDialog),
    typeof(BitmapSourceVisualizer.MySource),
    Target = typeof(BitmapSource),
    Description = "BitmapSourceVisualizer")]
namespace BitmapSourceVisualizer
{
    // TODO: SomeType のインスタンスをデバッグするときに、このビジュアライザーを表示するために SomeType の定義に次のコードを追加します:
    // 
    //  [DebuggerVisualizer(typeof(MyDialog))]
    //  [Serializable]
    //  public class SomeType
    //  {
    //   ...
    //  }
    // 
    /// <summary>
    /// SomeType のビジュアライザーです。  
    /// </summary>
    public class MyDialog : DialogDebuggerVisualizer
    {
        private Form MyForm;
        private Bitmap OriginBitmap;
        private BitmapSource OriginBitmapSource;//
        private PictureBox MyPictureBox;

        protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
        {
            if (windowService == null)
                throw new ArgumentNullException("windowService");
            if (objectProvider == null)
                throw new ArgumentNullException("objectProvider");

            //BitmapSourceをBitmapへ変換
            var data = (MyProxy)objectProvider.GetObject();
            OriginBitmapSource = BitmapSource.Create(data.Width, data.Height, 96, 96, PixelFormats.Bgra32, null, data.Pixels, data.Stride);
            OriginBitmap = new Bitmap(data.Width, data.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            var bmpData = OriginBitmap.LockBits(new Rectangle(0, 0, OriginBitmap.Width, OriginBitmap.Height), ImageLockMode.ReadOnly, OriginBitmap.PixelFormat);
            OriginBitmapSource.CopyPixels(Int32Rect.Empty, bmpData.Scan0, bmpData.Height * bmpData.Stride, bmpData.Stride);
            OriginBitmap.UnlockBits(bmpData);

            //this.OriginBitmap = (Bitmap)objectProvider.GetObject();

            //Form作成表示
            using (MyForm = new Form())
            {
                //FormにボタンとかPictureBox追加
                AddToolStrip();
                MyForm.Text = "BitmapSourceVisualizer";
                MyForm.BackColor = System.Drawing.Color.White;
                MyForm.Width = Screen.PrimaryScreen.Bounds.Width / 2;
                MyForm.Height = Screen.PrimaryScreen.Bounds.Height / 2;
                windowService.ShowDialog(MyForm);
            }
            this.OriginBitmap.Dispose();
        }

        // TODO: ビジュアライザーをテストするために、次のコードをユーザーのコードに追加します:
        // 
        //    MyDialog.TestShowVisualizer(new SomeType());
        // 
        /// <summary>
        /// デバッガーの外部にホストすることにより、ビジュアライザーをテストします。
        /// </summary>
        /// <param name="objectToVisualize">ビジュアライザーに表示するオブジェクトです。</param>
        public static void TestShowVisualizer(object objectToVisualize)
        {
            VisualizerDevelopmentHost visualizerHost = new VisualizerDevelopmentHost(objectToVisualize, typeof(MyDialog), typeof(MySource));
            visualizerHost.ShowVisualizer();
        }


        private void AddToolStrip()
        {
            var toolStrip = new ToolStrip();
            //ts.SuspendLayout();

            ToolStripButton button;
            button = new ToolStripButton { Text = "保存(&S)" };
            button.Click += (s, e) => { SaveImage(OriginBitmap); };
            toolStrip.Items.Add(button);

            button = new ToolStripButton { Text = "コピー(&C)" };
            button.Click += (s, e) => { System.Windows.Forms.Clipboard.SetImage(OriginBitmap); };
            //button.Click += (s, e) => { System.Windows.Clipboard.SetImage(OriginBitmapSource); };//アルファ値が失われる
            //button.Click += (s, e) => { Image2Clipboard(); };
            toolStrip.Items.Add(button);

            //var b3 = new ToolStripButton { Text = "x2" };
            //b3.Click += (e, x) => { Scale2(); };
            //ts.Items.Add(b3);


            button = new ToolStripButton { Text = "ウィンドウに合わせる(&Z)" };
            button.Click += (e, x) => { MyPictureBox.SizeMode = PictureBoxSizeMode.Zoom; MyPictureBox.Dock = DockStyle.Fill; };
            toolStrip.Items.Add(button);

            button = new ToolStripButton { Text = "実寸(&A)" };
            button.Click += (o, e) => { MyPictureBox.SizeMode = PictureBoxSizeMode.AutoSize; MyPictureBox.Dock = DockStyle.None; };
            toolStrip.Items.Add(button);

            button = new ToolStripButton { Text = "背景黒(&B)" };
            button.Click += (s, e) => { MyForm.BackColor = System.Drawing.Color.Black; };
            toolStrip.Items.Add(button);

            button = new ToolStripButton { Text = "背景白(&H)" };
            button.Click += (s, e) => { MyForm.BackColor = System.Drawing.Color.White; };
            toolStrip.Items.Add(button);

            button = new ToolStripButton { Text = "閉じる(&W)" };
            button.Click += (s, e) => { MyForm.Close(); };
            toolStrip.Items.Add(button);


            //ts.ResumeLayout(false);
            //ts.PerformLayout();

            MyForm.Controls.Add(toolStrip);

            MyPictureBox = new PictureBox { SizeMode = PictureBoxSizeMode.AutoSize };
            var p = new Panel { AutoScroll = true, Dock = DockStyle.Fill };
            p.Controls.Add(MyPictureBox);
            MyForm.Controls.Add(p);
            MyPictureBox.Image = OriginBitmap;
            p.BringToFront();

        }

        //画像の保存
        private void SaveImage(Bitmap bmp)
        {
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "*.png|*.png|*.bmp|*.bmp";
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                if (dialog.FilterIndex == 1)
                {
                    bmp.Save(dialog.FileName, ImageFormat.Png);
                }
                else if (dialog.FilterIndex == 2)
                {
                    bmp.Save(dialog.FileName, ImageFormat.Bmp);
                }
            }
            dialog.Dispose();
        }

        //private void Scale2()
        //{
        //    Bitmap canvas = new Bitmap(OriginBitmap.Width * 2, OriginBitmap.Height * 2);
        //    Graphics g = Graphics.FromImage(canvas);
        //    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
        //    g.DrawImage(OriginBitmap, 0, 0, OriginBitmap.Width * 2, OriginBitmap.Height * 2);
        //    g.Dispose();
        //    MyPictureBox.Image = canvas;
        //}

        //private void Image2Clipboard()
        //{
        //    var data = new System.Windows.Forms.DataObject();
        //    using (var stream = new MemoryStream())
        //    {
        //        OriginBitmap.Save(stream, ImageFormat.Png);
        //        data.SetData("PNG", false, stream);
        //    }
        //    System.Windows.Forms.Clipboard.SetDataObject(data);

        //}



    }

    public class MySource : VisualizerObjectSource
    {
        public override void GetData(object target, Stream outgoingData)
        {
            var source = (BitmapSource)target;
            var data = new MyProxy(source);
            base.GetData(data, outgoingData);

            ////BitmapSourceをBitmapへ変換
            //var data = new MyProxy((BitmapSource)target);
            //var source = BitmapSource.Create(data.Width, data.Height, 96, 96, PixelFormats.Bgra32, null, data.Pixels, data.Stride);
            //var bmp = new Bitmap(data.Width, data.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            //var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
            //source.CopyPixels(Int32Rect.Empty, bmpData.Scan0, bmpData.Height * bmpData.Stride, bmpData.Stride);
            //bmp.UnlockBits(bmpData);
            //base.GetData(bmp, outgoingData);
        }


    }


    /// <summary>
    /// BitmapSourceをシリアライズできる型に分解
    /// PixelFormatをBgra32に決め打ち方式用
    /// </summary>
    [Serializable]
    public class MyProxy
    {
        public int Width { get; private set; }
        public int Height { get; private set; }
        public int Stride { get; private set; }
        public byte[] Pixels { get; private set; }
        public MyProxy(BitmapSource source)
        {
            if (source.Format != PixelFormats.Bgra32)
            {
                source = new FormatConvertedBitmap(source, PixelFormats.Bgra32, null, 0);
            }
            Width = source.PixelWidth;
            Height = source.PixelHeight;
            Stride = Width * 4;// (Width * source.Format.BitsPerPixel + 7) / 8;
            Pixels = new byte[Height * Stride];
            source.CopyPixels(new Int32Rect(0, 0, Width, Height), Pixels, Stride, 0);
        }
    }

}

最初から作り直したけど、参照に追加したdllは前回と同じ

 

フィールド

f:id:gogowaten:20200116100723p:plain

 

 

f:id:gogowaten:20200116095759p:plain

Formにボタンを追加するところ、ボタンはToolStripButtonを使った

これを

f:id:gogowaten:20200116100214p:plain

Formを作るところで呼んでいる

 

 

f:id:gogowaten:20200116100439p:plain

保存ボタン作成部分

 

f:id:gogowaten:20200116100448p:plain

jpegも加えようとしたけど、なんかできなかったのでpngbmpだけ

 

f:id:gogowaten:20200116101201p:plain

クリップボードへコピー

WPFクリップボードはやっぱりおかしい、アルファ値が255になってしまうのでFormsのクリップボードを使っている、これもアルファ値が変化してしまう?みたいだけどWPFのよりはマシ、透明や半透明のピクセルがない普通(アルファ値255)の画像なら全く問題ない

 

表示方式の変更は

f:id:gogowaten:20200116101903p:plain

PictureBoxSizeModeとPictureBoxのDockプロパティを変更しているだけ

 

Formに追加

f:id:gogowaten:20200116102126p:plain

  1. 各ボタンをToolStripに追加してFormに追加
  2. PanelにPictureBoxを追加してFormに追加
  3. PictureBoxのImageプロパティに画像を指定
  4. Panelを最前面にするBringToFront

この順番が大切、2番3番が逆だと大きな画像を表示したときでもPanelにスクロールバーが表示されなかった

 

 

めんどくさかった

f:id:gogowaten:20200116103603p:plain

画像を保存するところのテストで、保存ダイアログを表示するところ

SaveFileDialog dialog = new SaveFileDialog();

でエラー、コードも単純なものだから間違っているとは思えないので、試しにReleaseでビルドしたdllを普通に使ってみたら問題なく保存ダイアログが表示されて画像保存もできた!

よくわからんけどこのエラーのおかげで、保存ダイアログを使った動作を確認するときには、毎回Releaseでビルド→dllをVisualizersフォルダにコピー→別のプロジェクトを開いて動作確認、っていうめんどくさいことになっていた

 

 

f:id:gogowaten:20200116110104j:plain

元の画像

f:id:gogowaten:20200116112547p:plain

 

 

f:id:gogowaten:20200116112603p:plain

切り抜き後の画像をクリップボードへコピーして

 

f:id:gogowaten:20200116112650p:plain

Pixtack紫陽花に貼り付けたところ、コピペできてる

 

f:id:gogowaten:20200116112838p:plain

切り抜き後の画像を保存

 

f:id:gogowaten:20200116112858p:plain

保存された画像

 

f:id:gogowaten:20200116113023p:plain

ウィンドウに合わせるで拡大縮小表示していても、保存やコピーされるのは元の実寸サイズ

 

 

関連記事

gogowaten.hatenablog.com

 

次のVerは1年後

gogowaten.hatenablog.com