2024年4月7日日曜日

C# Formアプリでheic画像表示

Formアプリは時代遅りつつあり、WPFではサポートされてもFormではダメなものがあります。

PictuerBoxのheic対応もそのひとつで、次のようなコードでheicファイルを表示しようとすると、メモリ不足のエラーが発生します。

pictureBox1.Image = Image.FromFile(mediaItem.FilePath);

Webでheic表示関連を検索すると、Magick.NETを使う例が見つかりますが、Microsoftのライブラリだけで行う方法はまだ見つかりませんでした。Magick.NETは優れたものだと思いますが、dllをアプリに含めると、小さいなアプリでは本体よりMagick.NETの方が大きくなってしまいます。これはあまりうれしくないので、Microsoftのライブラリだけで行う方法をいくつか試してみました。

なお、前提としてWindows 10, 11にHEIF 画像拡張をインストールしてあるものとします。HEIF 画像拡張がインストールされていない場合はMicrosoft Store から ダウンロードできます。

方法1
BitmapImage
BitmapEncoderを使用する。
今のところ、これが一番良好な結果になっています。
次のようなステップで行います。

System.Windows.Media.Imaging.BitmapImageをファイルから作る。BmpBitmapEncoderでMemoryStreamに書き出す。
MemoryStreamからSystem.Drawint.Imageを作る。
pictureBox1.Imageにセットする。

コードは次のようになります。

            BitmapImage bImage = new BitmapImage();
            bImage.BeginInit();
            bImage.CacheOption = BitmapCacheOption.None;
            bImage.DecodePixelHeight = height;
            bImage.DecodePixelWidth = width;
            bImage.UriSource = new Uri(FilePath, UriKind.Absolute);
            bImage.EndInit();
            using (MemoryStream ms = new MemoryStream())
            {
                BitmapEncoder enc = new BmpBitmapEncoder();
                enc.Frames.Add(BitmapFrame.Create(bImage));
                enc.Save(ms);
                pictureBox1.Image = Image.FromStream(ms);
            }

ひとつの静止画を扱うなら、これで十分でしょう。イメージのサイズを設定できるので、メモリ消費も必要最低限に抑えられ、スピードもこれ以上は難しいでしょう。

また、書き出し先をファイルにすればフォーマット変換に使えます。

System.Windows.Media.Imagingのための参照追加 は次のとおりです。

プロジェクトの参照右クリック⇒参照の追加⇒アッセンブリ
PresentationCoreをチェック⇒OK

方法2
BitmapDecoder
BitmapEncoderを使用する。

BitmapDecoderの使い方はよくわかっていませんが、複数のFrameを持ち、Animationなども扱えます。複雑な画像表示を行えるようですが、ひとつの静止画だけを扱うなら方法1で十分そうです。もしかしたら有用かもしれないので紹介しておきます。

ステップは方法1のBitmapImageがSystem.Windows.Media.Imaging.BitmapDecoderに置き換わっただけです。

コードは次のようになります。

            BitmapDecoder uriBitmap = BitmapDecoder.Create(
                new Uri(FilePath, UriKind.Absolute),
      BitmapCreateOptions.None,
                BitmapCacheOption.Default);
            using (MemoryStream ms = new MemoryStream())
           {
               BitmapEncoder enc = new BmpBitmapEncoder();
               enc.Frames.Add(uriBitmap.Frames[0]);
               enc.Save(ms);
               pictureBox1.Image = Image.FromStream(ms);
           }

調べた限りでは、この流れでは画像サイズを指定できません。そのため、高解像度画像ではメモリ消費が多くなり、スピードも少し遅くなります。

方法3
 AxWMPLib.AxWindowsMediaPlayerを使用する。

MediaPlayerは静止画も表示でき、HEIF 画像拡張インストールされていればheicフォーマットも表示できます。ちなみにHEVCビデオも再生できます。静止画を扱う場合は uiMode="none"が良いでしょう。

メリットとしては、まずは静止画も動画も同じインタフェイスで扱えることです。また、対応していないフォーマットや壊れたファイルのエラー対応もMediaPlayerまかせにできます。

これまでのFormアプリと同じ要領で使え、次の要領でコントロールにファイルのパスを設定するだけなので、お手軽といえます。

(AxWMPLib.AxWindowsMediaPlayer)player.URL = filePath;

VisualStudioでMediaPlayerを使う方法は次のリンクを参照してください。
Microsoft Visual Studio で Windows メディア プレーヤー コントロールを使用する
https://draft.blogger.com/blog/post/edit/6538117324271148932/209217070553378409#

デメリットとしては、方法1,2に比べると遅いこと、メモリ消費量も特に高解像度のheicの場合は多くなります。

HEIFコーディックの有無チェック 
Magick.NETと違い、コーディックがないとエラーになります。
以下の要領でHEIFコーディックの有無がチェックできます。 

bool HeifDllExists = Registry.GetValue(@"HKEY_CLASSES_ROOT\CLSID\{E9A4A80A-44FE-4DE4-8971-7150B10A5199}\InprocServer32", null, null) != null;

参照:HEIF Format Overview
https://learn.microsoft.com/ja-jp/previous-versions/windows/desktop/legacy/mt846532(v=vs.85)




2024年2月25日日曜日

Excel VBA 備忘録

ときどきExcelのVBAを書きますが、普段使わない言語だとなにかとつまづきます。
自分自身のための備忘録ですが、きっと初心者ならだれもつまづくところだと思います。

VariantとObject
    Variant
     すべての変数。
     Integerなどの値のみを持つデータ型とオブジェクト型のどちらも代入可能。
    Object
     データとプロパティー、メソッドがカプセル化されたもの。
       オブジェクト型のみ代入可能。

代入時にSetが必要な場合、不要な場合
   Setが必要な場合
        Objectに代入する場合で、値だけでなくObjectのプロパティーの設定が行われる。
 Setが不要な場合
     Integerなどのデータ型に値のみ代入する場合。

コンパイルエラー:プロパティーの使い方が不正です
    Setを付けずにObjectへ代入しようとした場合。

コンパイルエラー:型が一致しません
 オブジェクトにIntegerなどObject以外の値を代入しようとした場合。

実行時エラー :オブジェクトが必要です
    値がNothingのオブジェクトのプロパティーにアクセスした場合。

Sub呼び出しでCallが必要な場合
 引数を()でくくる場合。