C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

C#でBitmapSourceをIconに変換

[トピック内 13 記事 (1 - 13 表示)]  << 0 >>

■86110 / inTopicNo.1)  C#でBitmapSourceをIconに変換
  
□投稿者/ きみのぶ (12回)-(2017/12/18(Mon) 21:20:42)

分類:[.NET 全般] 

C#でBitmapSourceをIconに変換するには、どうすればいいでしょうか?。

今のところBitmapSource⇒Bitmapに変換⇒Iconに変換の手順でコーディングしていますが、Iconに変換出来ません。

現在試しているコード
IconBitmap iconBitmap = (IconBitmap)this.listView1.SelectedItem;
BitmapSource bitmapSource = iconBitmap.BitmapSource;

//  BitmapSourceをBitmapに変換
int width = bitmapSource.PixelWidth;
int height = bitmapSource.PixelHeight;
int stride = width * ((bitmapSource.Format.BitsPerPixel + 7) / 8);  // 行の長さは色深度によらず8の倍数のため
IntPtr intPtr = IntPtr.Zero;
intPtr = Marshal.AllocCoTaskMem(height * stride);
bitmapSource.CopyPixels(new Int32Rect(0, 0, width, height), intPtr, height * stride, stride);
    using (var bitmap = new Bitmap(width, height, stride, 0, intPtr))
    {
        try
        {
            //  BitmapをIconに変換
            //System.Drawing.Icon icon = System.Drawing.Icon.FromHandle(bitmap.GetHicon());
            //System.Drawing.Icon icon = System.Drawing.Icon.FromHandle(IntPtr.Zero);
            IntPtr Hicon = bitmap.GetHicon();
            Icon newIcon = System.Drawing.Icon.FromHandle(Hicon);
        }
        catch (Exception exception)
        {
            MessageBox.Show(exception.Message, "BitmapをIconに変換失敗");
            return;
        }
        //  保存
     }
上記のコードでは、bitmap.GetHicon()メソッドで、パラメタが有効ではないという例外が発生します。

そもそも、BitmapSource⇒Bitmapに変換⇒Iconに変換の手順が間違いでしょうか?

引用返信 編集キー/
■86111 / inTopicNo.2)  Re[1]: C#でBitmapSourceをIconに変換
□投稿者/ 魔界の仮面弁士 (1513回)-(2017/12/18(Mon) 23:28:48)
No86110 (きみのぶ さん) に返信
> int width = bitmapSource.PixelWidth;
> int height = bitmapSource.PixelHeight;
縦横のサイズは、最大で 256×256 のはずですが、その点は大丈夫でしょうか。
(Icon フォーマットでは、幅と高さがそれぞれ 1 バイトの領域しかない)


> 今のところBitmapSource⇒Bitmapに変換⇒Iconに変換の手順でコーディングしていますが、Iconに変換出来ません。
逆変換の Icon ⇒ BitmapSource なら簡単なのですけれどね。
System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon メソッドが使えるので。


> IntPtr Hicon = bitmap.GetHicon();
HICON の解放のために DestroyIcon API を呼ぶ必要があったはずです。
https://msdn.microsoft.com/en-us/library/system.drawing.bitmap.gethicon.aspx
http://xptn.dtiblog.com/blog-entry-80.html
引用返信 編集キー/
■86115 / inTopicNo.3)  Re[2]: C#でBitmapSourceをIconに変換
□投稿者/ きみのぶ (13回)-(2017/12/19(Tue) 10:48:59)
No86111 (魔界の仮面弁士 さん) に返信
> ■No86110 (きみのぶ さん) に返信
>>int width = bitmapSource.PixelWidth;
>>int height = bitmapSource.PixelHeight;
> 縦横のサイズは、最大で 256×256 のはずですが、その点は大丈夫でしょうか。
> (Icon フォーマットでは、幅と高さがそれぞれ 1 バイトの領域しかない)
問題ありません。Iconに変換するBitmapSourceは、Iconから変換したものです。

>
>
>>今のところBitmapSource⇒Bitmapに変換⇒Iconに変換の手順でコーディングしていますが、Iconに変換出来ません。
> 逆変換の Icon ⇒ BitmapSource なら簡単なのですけれどね。
> System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon メソッドが使えるので。
>
>
>>IntPtr Hicon = bitmap.GetHicon();
> HICON の解放のために DestroyIcon API を呼ぶ必要があったはずです。
> https://msdn.microsoft.com/en-us/library/system.drawing.bitmap.gethicon.aspx
> http://xptn.dtiblog.com/blog-entry-80.html
それはコーディングするつもりです。
引用返信 編集キー/
■86116 / inTopicNo.4)  Re[3]: C#でBitmapSourceをIconに変換
□投稿者/ Hongliang (583回)-(2017/12/19(Tue) 11:14:07)
2017/12/19(Tue) 11:14:32 編集(投稿者)

new Bitmapする際に、適切なPixelFormatを与えてください。
System.Windows.Media.PixelFormatからSystem.Drawing.Imaging.PixelFormatへの変換は確か標準ライブラリには存在しなかったので、自分でマッピングする必要があるでしょう。

// Bitmap::GetHiconする際だったか、Icon.FromHandleする際だったかにすごい勢いの画質劣化があったような記憶が…。
// 確か強制的に4bppに変換されたような…?
引用返信 編集キー/
■86117 / inTopicNo.5)  Re[4]: C#でBitmapSourceをIconに変換
□投稿者/ Hongliang (584回)-(2017/12/19(Tue) 11:44:07)
あ、本題とは関係ないですし多分動作には影響しませんが、
> int stride = width * ((bitmapSource.Format.BitsPerPixel + 7) / 8); // 行の長さは色深度によらず8の倍数のため
は色々間違えています。
BMP形式では1行のピクセルデータは4バイト単位である必要があります。
極端な例のために1bpp(2値画像ですね)を考えると、この画像の横幅が15ピクセルだとすると、単純には1行が15bitになります。
が、上記の4バイト単位であるという要請のために、1行を4バイトにしなければなりません。4バイト=32bitのうち、17bitは余りになります。

計算式の組み立て方としては、
・横幅とピクセル当たりのビット数から実際に使用するビット数を計算する
・32ビット単位に切り上げる
・バイト単位に直す

式に起こすと、
bits = width * bpp
rounded = floor((bits + 31) / 32) * 32
stride = rounded / 8
となり、一行にまとめると
stride = (((width * bpp) + 31) / 32) * 4
// /32と*4をまとめないのは、/32で切り捨て処理が一旦必要なため。
引用返信 編集キー/
■86118 / inTopicNo.6)  Re[3]: C#でBitmapSourceをIconに変換
□投稿者/ 魔界の仮面弁士 (1515回)-(2017/12/19(Tue) 12:21:32)
No86115 (きみのぶ さん) に返信
> Iconに変換するBitmapSourceは、Iconから変換したものです。

Format16bppGrayScale などといった、アイコン化できないフォーマットが
指定されているということは無いでしょうか。

たとえばBitmap を生成する際、ColorPalette が必要な色数、具体的には
System.Drawing.Imaging.PixelFormat.Format1bppIndexed
System.Drawing.Imaging.PixelFormat.Format4bppIndexed
System.Drawing.Imaging.PixelFormat.Format8bppIndexed
を指定して作成した場合は、GetHicon できないでしょうか。
引用返信 編集キー/
■86119 / inTopicNo.7)  Re[5]: C#でBitmapSourceをIconに変換
□投稿者/ きみのぶ (15回)-(2017/12/19(Tue) 15:28:08)
No86117 (Hongliang さん) に返信
> 式に起こすと、
> bits = width * bpp
> rounded = floor((bits + 31) / 32) * 32
> stride = rounded / 8
> となり、一行にまとめると
> stride = (((width * bpp) + 31) / 32) * 4
> // /32と*4をまとめないのは、/32で切り捨て処理が一旦必要なため。

Hongliang さん、またしてもアドバイスありがとうございます。
教えて頂いた通りにコーディングしてみます。
引用返信 編集キー/
■86120 / inTopicNo.8)  Re[4]: C#でBitmapSourceをIconに変換
□投稿者/ きみのぶ (16回)-(2017/12/19(Tue) 15:33:02)
No86116 (Hongliang さん) に返信
> 2017/12/19(Tue) 11:14:32 編集(投稿者)
> 
> new Bitmapする際に、適切なPixelFormatを与えてください。
> System.Windows.Media.PixelFormatからSystem.Drawing.Imaging.PixelFormatへの変換は確か標準ライブラリには存在しなかったので、自分でマッピングする必要があるでしょう。
> 
> // Bitmap::GetHiconする際だったか、Icon.FromHandleする際だったかにすごい勢いの画質劣化があったような記憶が…。
> // 確か強制的に4bppに変換されたような…?

var bitmap = new Bitmap(width, height, stride, System.Drawing.Imaging.PixelFormat.Format64bppPArgb, intPtr)
上記のようにPixelFormatを指定することで、Iconへの変換、Iconファイルとしての保存が出来るようになりました。
ただ、色劣化が酷く、元の画像とは似ても似つかないものになってしまいました。
どのPixelFormatが適当なのか、全て試してみます。
現在のところ、以下のようなコードとなっています。

try
            {
                IconBitmap iconBitmap = (IconBitmap)this.listView1.SelectedItem;
                BitmapSource bitmapSource = iconBitmap.BitmapSource;

                #region 過去コード
                /*
                // フォーマットが異なるならば変換
                if (bitmapSource.Format != PixelFormats.Bgr32)
                {
                    bitmapSource = new FormatConvertedBitmap(bitmapSource, PixelFormats.Bgr32, null, 0);
                    bitmapSource.Freeze();
                }

                // ピクセルデータをコピー
                int width = (int)bitmapSource.Width;
                int height = (int)bitmapSource.Height;
                int stride = width * 4;
                byte[] datas = new byte[stride * height];
                bitmapSource.CopyPixels(datas, stride, 0);

                // Bitmap へピクセルデータ書き出し
                Bitmap dest = new Bitmap(width, height, Imaging::PixelFormat.Format32bppArgb);
                Imaging::BitmapData destBits = null;
                try
                {
                    destBits = dest.LockBits(new System.Drawing.Rectangle(0, 0, width, height), Imaging::ImageLockMode.WriteOnly, Imaging::PixelFormat.Format32bppArgb);
                    Marshal.Copy(datas, 0, destBits.Scan0, datas.Length);
                }
                catch (Exception exception)
                {
                    MessageBox.Show(exception.Message, "変換失敗");
                    dest.Dispose();

                    dest = null;
                    throw;
                }
                finally
                {
                    if (dest != null && destBits != null)
                    {
                        dest.UnlockBits(destBits);
                    }
                }
                */
                #endregion
                
                
                //  BitmapSourceをBitmapに変換
                int width = bitmapSource.PixelWidth;
                int height = bitmapSource.PixelHeight;
                //int stride = width * ((bitmapSource.Format.BitsPerPixel + 7) / 8);  // 行の長さは色深度によらず8の倍数のため
                int stride = (((width * bitmapSource.Format.BitsPerPixel) + 31) / 32) * 4;
                IntPtr intPtr = IntPtr.Zero;
                intPtr = Marshal.AllocCoTaskMem(height * stride);
                bitmapSource.CopyPixels(new Int32Rect(0, 0, width, height), intPtr, height * stride, stride);
                using (var bitmap = new Bitmap(width, height, stride, System.Drawing.Imaging.PixelFormat.Format64bppPArgb, intPtr))
                {
                    try
                    {
                        //  BitmapをIconに変換
                        //System.Drawing.Icon icon = System.Drawing.Icon.FromHandle(bitmap.GetHicon());
                        //System.Drawing.Icon icon = System.Drawing.Icon.FromHandle(IntPtr.Zero);
                        IntPtr Hicon = bitmap.GetHicon();
                        Icon newIcon = System.Drawing.Icon.FromHandle(Hicon);

                        try
                        {
                            //  保存
                            SaveFileDialog dialog = new SaveFileDialog();
                            dialog.FileName = "新しいアイコン.ico";
                            dialog.InitialDirectory = System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                            dialog.Filter = "アイコンファイル(*.ico)|*.ico|すべてのファイル(*.*)|*.*";
                            dialog.FilterIndex = 0;
                            dialog.Title = "保存先のファイルを指定して下さい";
                            dialog.RestoreDirectory = true;
                            dialog.OverwritePrompt = true;
                            dialog.CheckPathExists = true;
                            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                            {
                                String newIconFile = dialog.FileName;
                                using (FileStream fileStream = new FileStream(newIconFile, FileMode.Create, FileAccess.Write))
                                {
                                    newIcon.Save(fileStream);
                                    fileStream.Close();
                                }
                            }
                        }
                        catch (Exception exception)
                        {
                            System.Windows.Forms.MessageBox.Show(exception.Message, "Iconの保存失敗");
                            return;
                        }

                        DestroyIcon(Hicon);
                    }
                    catch (Exception exception)
                    {
                        System.Windows.Forms.MessageBox.Show(exception.Message, "BitmapをIconに変換失敗");
                        return;
                    }
                }
            }
            catch (Exception exception)
            {
                System.Windows.Forms.MessageBox.Show(exception.Message, "変換失敗");
            }

引用返信 編集キー/
■86123 / inTopicNo.9)  Re[5]: C#でBitmapSourceをIconに変換
□投稿者/ Hongliang (585回)-(2017/12/19(Tue) 16:13:42)
PixelFormatとは関係なかったはずです。元が8bppIndexだろうが容赦なく4bppに変換されたはず。
// というか元が4bppでさえ無駄に減色処理してたような。
なので、まともな色のIconオブジェクトを手に入れるなら、GetHiconからは無理ということになります。
ico形式での保存はSystem.DrawingでもSystem.Windows.Media.Imagingでもサポートされてないので、自前または何らかの外部ライブラリを使用してico形式のデータを作成し、それをnew Iconすることになるのではないかと。
引用返信 編集キー/
■86124 / inTopicNo.10)  Re[6]: C#でBitmapSourceをIconに変換
□投稿者/ 魔界の仮面弁士 (1516回)-(2017/12/19(Tue) 16:58:10)
No86123 (Hongliang さん) に補足
> // というか元が4bppでさえ無駄に減色処理してたような。

Icon にしても Cursor にしても、.NET 以前に
Win32 や GDI+ 側の API サポートが貧弱なようで…。
https://msdn.microsoft.com/en-us/library/ms997538.aspx


> 自前または何らかの外部ライブラリを使用してico形式のデータを作成し、
これはどうでしょう。

https://www.codeproject.com/Articles/16178/IconLib
https://www.nuget.org/packages/IconLib.Unofficial/


System.Drawing.Bitmap png = new System.Drawing.Bitmap(pngFile);
System.Drawing.IconLib.MultiIcon multiIcon = new System.Drawing.IconLib.MultiIcon();
System.Drawing.IconLib.SingleIcon singleIcon = multiIcon.Add("ICON");
System.Drawing.IconLib.IconImage icon = singleIcon.Add(png);
System.Drawing.Icon result = icon.Icon;
引用返信 編集キー/
■86125 / inTopicNo.11)  Re[6]: C#でBitmapSourceをIconに変換
□投稿者/ きみのぶ (18回)-(2017/12/19(Tue) 17:24:00)
No86123 (Hongliang さん) に返信

Hongliang 様
何度もアドバイスありがとうございます。

> PixelFormatとは関係なかったはずです。元が8bppIndexだろうが容赦なく4bppに変換されたはず。
> // というか元が4bppでさえ無駄に減色処理してたような。
PixelFormatの全ての値を試してみましたが、どれも色劣化を起こし、元のアイコンと同じ物は生成出来ませんでした。


> なので、まともな色のIconオブジェクトを手に入れるなら、GetHiconからは無理ということになります。
そうなのですか。。。

> ico形式での保存はSystem.DrawingでもSystem.Windows.Media.Imagingでもサポートされてないので、自前または何らかの外部ライブラリを使用してico形式のデータを作成し、それをnew Iconすることになるのではないかと。
他の方が、外部ライブラリを紹介してくれたので、それを試してみます。
引用返信 編集キー/
■86126 / inTopicNo.12)  Re[7]: C#でBitmapSourceをIconに変換
□投稿者/ きみのぶ (19回)-(2017/12/19(Tue) 17:27:03)
No86124 (魔界の仮面弁士 さん) に返信
> ■No86123 (Hongliang さん) に補足
>>// というか元が4bppでさえ無駄に減色処理してたような。
>
> Icon にしても Cursor にしても、.NET 以前に
> Win32 や GDI+ 側の API サポートが貧弱なようで…。
> https://msdn.microsoft.com/en-us/library/ms997538.aspx
>
>
>>自前または何らかの外部ライブラリを使用してico形式のデータを作成し、
> これはどうでしょう。
>
> https://www.codeproject.com/Articles/16178/IconLib
> https://www.nuget.org/packages/IconLib.Unofficial/
ご紹介くださりありがとうございます。早速試してみます。
ドキュメントが英語なので、理解するのに時間がかかりますが。


>
>
> System.Drawing.Bitmap png = new System.Drawing.Bitmap(pngFile);
> System.Drawing.IconLib.MultiIcon multiIcon = new System.Drawing.IconLib.MultiIcon();
> System.Drawing.IconLib.SingleIcon singleIcon = multiIcon.Add("ICON");
> System.Drawing.IconLib.IconImage icon = singleIcon.Add(png);
> System.Drawing.Icon result = icon.Icon;
引用返信 編集キー/
■86127 / inTopicNo.13)  Re[8]: C#でBitmapSourceをIconに変換
□投稿者/ きみのぶ (20回)-(2017/12/19(Tue) 18:54:30)
No86126 (きみのぶ さん) に返信
> ■No86124 (魔界の仮面弁士 さん) に返信
>>■No86123 (Hongliang さん) に補足
> >>// というか元が4bppでさえ無駄に減色処理してたような。
>>
>>Icon にしても Cursor にしても、.NET 以前に
>>Win32 や GDI+ 側の API サポートが貧弱なようで…。
>>https://msdn.microsoft.com/en-us/library/ms997538.aspx
>>
>>
> >>自前または何らかの外部ライブラリを使用してico形式のデータを作成し、
>>これはどうでしょう。
>>
>>https://www.codeproject.com/Articles/16178/IconLib
>>https://www.nuget.org/packages/IconLib.Unofficial/
> ご紹介くださりありがとうございます。早速試してみます。
> ドキュメントが英語なので、理解するのに時間がかかりますが。
>
>
>>
>>
>>System.Drawing.Bitmap png = new System.Drawing.Bitmap(pngFile);
>>System.Drawing.IconLib.MultiIcon multiIcon = new System.Drawing.IconLib.MultiIcon();
>>System.Drawing.IconLib.SingleIcon singleIcon = multiIcon.Add("ICON");
>>System.Drawing.IconLib.IconImage icon = singleIcon.Add(png);
>>System.Drawing.Icon result = icon.Icon;
出来ました。
ろくにドキュメントも読まずに、上記のコードを記述する事で、狙い通りの綺麗なアイコンに保存する事が出来ました。
本当にありがとうございました。
解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ