アルファ値を失わずに画像のコピペできた、.NET WPFのClipboard
Clipboardクラスにはクリップボードに画像をコピーするSetImageと、クリップボードから画像を取得するGetImageがあるけど、これを使うとピクセルのアルファ値が255(完全不透明)か、0(完全透明)に変換されてしまう
左半分が完全不透明、右半分が半透明
C# クリップボード 画像アルファ値
とかで検索して見つかる
クリップボードを用いるとBMPデータの透過色が変換されてしまう
https://social.msdn.microsoft.com/Forums/vstudio/ja-JP/0416af2a-a2f4-49b5-a085-f117f8576ddf/bmp?forum=vcgeneralja
[VB] クリップボードに送った透過画像が貼り付けると透過にならない。: ~Road To Beautiful Life ~
https://tomovertex.at.webry.info/201009/article_2.html
この辺を見るとアルファ値を失わずにコピペするには、
コピー
画像をPNG形式にエンコードしたものをMemoryStreamにSave(書き込む?)して、それをClipboardのSetDataで"PNG"を指定してコピーする
ペースト(クリップボードから取り出し)
ClipboardのGetDataで"PNG"を指定して取り出す、これの型はMemoryStreamなので、BitmapFrameのCteateに渡せば画像として取り出せる
作成環境
- Windows 10 Home
- Visual Studio Community 2019
- WPF
- .NET Core 5
- C#
画像をコピーするアプリ
MainWindow.xaml
<Window x:Class="_20210210_Clipboardへ画像コピー.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:_20210210_Clipboardへ画像コピー" mc:Ignorable="d" Title="MainWindow" Height="250" Width="400"> <Grid UseLayoutRounding="True"> <DockPanel> <Menu DockPanel.Dock="Top"> <MenuItem Header="copy" Click="MenuItem_Click"/> <MenuItem Header="copy(PNG)" Click="MenuItem_Click_1"/> </Menu> <Image x:Name="MyImage" Stretch="None"/> </DockPanel> </Grid> </Window>
MainWindow.xaml.cs
using System; using System.Windows; using System.Windows.Media.Imaging; namespace _20210210_Clipboardへ画像コピー { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { private BitmapSource MyBitmapSource; public MainWindow() { InitializeComponent(); //アプリに埋め込んだ画像ファイルを取り出す string name = "_20210210_Clipboardへ画像コピー.不透明と半透明.png"; var assembly = System.Reflection.Assembly.GetExecutingAssembly(); using var stream = assembly.GetManifestResourceStream(name); if (stream != null) MyBitmapSource = BitmapFrame.Create(stream); MyImage.Source = MyBitmapSource; } private void MenuItem_Click(object sender, RoutedEventArgs e) { //普通にセット Clipboard.SetImage(MyBitmapSource); } private void MenuItem_Click_1(object sender, RoutedEventArgs e) { //PNG形式でセット ClipboardToPngImage(MyBitmapSource); } private void ClipboardToPngImage(BitmapSource source) { //画像をPNGにエンコード PngBitmapEncoder pngEnc = new(); pngEnc.Frames.Add(BitmapFrame.Create(source)); //エンコードした画像をMemoryStreamにSava using var ms = new System.IO.MemoryStream(); pngEnc.Save(ms); //MemoryStreamをクリップボードにコピー Clipboard.SetData("PNG", ms); } } }
クリップボードの画像を貼り付ける(取り出す)アプリ
MainWindow.xaml
<Window x:Class="_20210210_ClipboardのPNG形式画像を貼り付け.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:_20210210_ClipboardのPNG形式画像を貼り付け" mc:Ignorable="d" Title="MainWindow" Height="250" Width="400"> <Grid UseLayoutRounding="True"> <DockPanel> <Menu DockPanel.Dock="Top"> <MenuItem Header="paste" Click="MenuItem_Click"/> </Menu> <Image x:Name="MyImage" Stretch="None"/> </DockPanel> </Grid> </Window>
MainWindow.xaml.cs
using System.Windows; using System.Windows.Media.Imaging; namespace _20210210_ClipboardのPNG形式画像を貼り付け { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void MenuItem_Click(object sender, RoutedEventArgs e) { BitmapSource source = GetImageFromClipboardWithPNG(); if (source == null) { MessageBox.Show("クリップボードに画像はなかった"); } else { MyImage.Source = source; } } /// <summary> /// クリップボードからBitmapSourceを取り出して返す、PNG(アルファ値保持)形式に対応 /// </summary> /// <returns></returns> private BitmapSource GetImageFromClipboardWithPNG() { BitmapSource source = null; //クリップボードにPNG形式のデータがあったら、それを使ってBitmapFrame作成して返す //なければ普通にClipboardのGetImage、それでもなければnullを返す using var ms = (System.IO.MemoryStream)Clipboard.GetData("PNG"); if (ms != null) { //source = BitmapFrame.Create(ms);//これだと取得できない source = BitmapFrame.Create(ms, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); } else if (Clipboard.ContainsImage()) { source = Clipboard.GetImage(); } return source; } } }
2021WPF/20210210_Clipboardへ画像コピー at master · gogowaten/2021WPF · GitHub
2021WPF/20210210_ClipboardのPNG形式画像を貼り付け at master · gogowaten/2021WPF · GitHub
それぞれのコピー形式でPaint.NETに貼り付けてみると
Paint.NETはクリップボードのPNG形式に対応しているので、アルファ値が正しく表示される
PNG形式にして、MemoryStreamにして、それをクリップボードにコピーするのはいいとして、受け取るほうがPNG形式のMemoryStreamに対応していないと、画像として取り出せないので貼り付けることができない
対応しているアプリは
- Paint.NET
- FireAlpaca
- Pixtack紫陽花
などがあった、最近のアプリなら対応している感じ
あと今思ったのは、コピーするときにビットマップ形式と今回のPNG形式の両方をコピーすることはできる?
できた↓
追記ここから
ビットマップ形式とPNG形式の両方コピーするのできた
クリップボードに複数の形式のデータをコピーする - .NET Tips(VB.NET, C#...)
https://dobon.net/vb/dotnet/system/clipboardmultidata.html
ここを見て
MainWindow.xaml
<Window x:Class="_20210210_ClipboardにSetImageとPNG形式.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:_20210210_ClipboardにSetImageとPNG形式" mc:Ignorable="d" Title="MainWindow" Height="250" Width="300"> <Grid> <DockPanel> <Menu DockPanel.Dock="Top"> <MenuItem Header="コピー" Click="MenuItem_Click"/> </Menu> <Image x:Name="MyImage" StretchDirection="DownOnly"/> </DockPanel> </Grid> </Window>
MainWindow.xaml.cs
using System.Windows; using System.Windows.Media.Imaging; namespace _20210210_ClipboardにSetImageとPNG形式 { public partial class MainWindow : Window { BitmapSource MyBitmapSource; public MainWindow() { InitializeComponent(); //アプリに埋め込んだ画像ファイルを取り出す string name = "_20210210_ClipboardにSetImageとPNG形式.不透明と半透明.png"; var assembly = System.Reflection.Assembly.GetExecutingAssembly(); using var stream = assembly.GetManifestResourceStream(name); if (stream != null) MyBitmapSource = BitmapFrame.Create(stream); MyImage.Source = MyBitmapSource; } private void MenuItem_Click(object sender, RoutedEventArgs e) { ClipboardSetImageWithPng(MyBitmapSource); } // クリップボードに複数の形式のデータをコピーする - .NET Tips(VB.NET, C#...) //https://dobon.net/vb/dotnet/system/clipboardmultidata.html /// <summary> /// BitmapSourceをPNG形式に変換したものと、そのままの形式の両方をクリップボードにコピーする /// </summary> /// <param name="source"></param> private void ClipboardSetImageWithPng(BitmapSource source) { //DataObjectに入れたいデータを入れて、それをクリップボードにセットする DataObject data = new(); //BitmapSource形式そのままでセット data.SetData(typeof(BitmapSource), source); //PNG形式にエンコードしたものをMemoryStreamして、それをセット //画像をPNGにエンコード PngBitmapEncoder pngEnc = new(); pngEnc.Frames.Add(BitmapFrame.Create(source)); //エンコードした画像をMemoryStreamにSava using var ms = new System.IO.MemoryStream(); pngEnc.Save(ms); data.SetData("PNG", ms); //クリップボードにセット Clipboard.SetDataObject(data, true); } } }
DataObjectにBitmapSourceそのままと、PNGに変換してMemoryStreamにしたのを両方入れて、それをクリップボードにSetDataObject
あとは受け取り側のアプリ問題で、クリップボードのPNG形式に対応したアプリならPNG形式を優先して取り出すはずだし、対応していないアプリならビットマップ形式の方を取り出すはず
テスト
アプリ起動して
コピーボタンでクリップボードにビットマップ形式とPNG形式両方がコピーされるので、画像アプリに貼り付けてみる
FireAlpaca
ファイル→クリップボードから新規作成を実行すると
PNG形式の方で貼り付けられたようで、右半分が半透明になっている、いいね!
Pixtack紫陽花
クリップボードから追加ボタンで
これも半透明になっている、追加で貼り付けて
2つの画像を重ねてみると半透明になっているのがわかる
クリップボードのPNG形式画像に対応していないアプリの場合
ペイント
メニューのクリップボード→貼り付けを実行すると
半透明が失われている画像が貼りけられたので、これはPNG形式はパスされて、残ったビットマップ形式の画像が貼り付けられたことになる、いいね!
ビットマップ形式優先ボタン
Pixtack紫陽花にはビットマップ形式を選んで貼り付けるボタンもある、これで貼り付けるとPNGよりビットマップ形式が優先されて貼り付けられた
このボタンはエクセルのグラフを貼り付ける用で作ったんだよねえ
関連記事
次回は翌日
前回のWPF記事は昨日
このアプリにつけたcopy(PNG)が今回の方法
8ヶ月後
1年以上前
今回のを利用した