2021/5/28: AVIF 対応のビルド番号判定追加及び,Build 19041 (2004), 19042 (20H2), 19043 (21H1) 向け hotfix KB5003214 で拡張子 `.hif` に対応したことによる拡張子の追加に対応
WIC (Windows Imaging Component) で WebP や HEIF (AVIC, HEIC, AVIF) に対応できますが,それの対応フォーマットに関するお話。
1. 初期からインストールされているもの (判定不要)
- BMP (.bmp, .dib, .rle)
- GIF (.gif)
- PNG (.png)
- JPEG (.jpg, .jpe, .jpeg, .jfif, .exif)
- TIFF (.tif, .tiff)
- アイコン (.ico)
- カーソル (.cur)
- DDS Texture format (.dds)
といったもの。これらは WIC が追加された XP SP3 (SP2 は要ダウンロード), Vista から使えます。なお,JPEG XR (旧 Windows Media Photo, HD Photo) は Vista の一部で使えないらしいので,古いソフトだと一応確認したほうがいいかもですね。
2. WebP や HEIF などの追加フォーマット
昨今では Microsoft Store に拡張機能としてこれらの機能が使えるようになっています。
- RAW Image Extension: https://www.microsoft.com/store/productId/9NCTDW2W1BH8
- WebP Image Extension: https://www.microsoft.com/store/productId/9PG2DK419DRG
- HEIF Image Extension: https://www.microsoft.com/store/productId/9PMMSR1CGPWG
- HEVC Video Extension: https://www.microsoft.com/store/productId/9NMZLZ57R3T7 または https://www.microsoft.com/store/productId/9N4WGH0Z6VHQ
- AV1 Video Extension: https://www.microsoft.com/store/productId/9MVZQVXJBQ9V
注意すべきは HEIF なんですよね。あくまでこれはコンテナー (MP4 のようなもの) なんですよね。一応拡張子として .heif
ってのもあります。ただ,中身のコーデックに応じて拡張子を変えることもできます。
- AVCI (H.264/AVC):
.avci
,.avcs
- HEIC (H.265/HEVC):
.heic
,.heics
- AVIF (AV1):
.avif
,.avifs
判定方法として better なのは,
CLSID_WICHeifDecoder
がアクティベートできるのかをまず確認。OK なら AVCI はデコード可能。HKEY_CLASSES_ROOT\ActivatableClasses\Package
にMicrosoft.HEVCVideoExtension_
から始まるキーが存在するかどうか。OK なら HEIC はデコード可能。HKEY_CLASSES_ROOT\ActivatableClasses\Package
にMicrosoft.AV1VideoExtension_
から始まるキーが存在するかどうか。OK なら AVIF はデコード可能。
といった感じになると思います。結構めんどくさいですが,この手順を踏めば拡張子の有効無効判定のオンオフを実装できるかと。
一応サンプルコード貼っておきますが,一部例外周りのコードそのままじゃ動かないので,本番コードでは書き換えてください。
public abstract class ImageFormatSupportDetector { private bool? _isSupported = null; public bool IsSupported { get { if (!this._isSupported.HasValue) { this._isSupported = this.GetValue(); } return this._isSupported.Value; } } public abstract string[] Extensions { get; } public abstract string FileType { get; } public abstract bool GetValue(); } public abstract class ClsidImageFormatSupportDetector : ImageFormatSupportDetector { public abstract Guid CLSID { get; } public override bool GetValue() { try { var decoderType = Type.GetTypeFromCLSID(CLSID); var decoder = Activator.CreateInstance(decoderType); return true; } catch (COMException ex) when (ex.Match(HResult.REGDB_E_CLASSNOTREG)) { return false; } } } public sealed class HEIFSupportDetector : ClsidImageFormatSupportDetector { private bool? _isHIFExtensionSupported; public bool IsHIFExtensionSupported { get { if (!this._isHIFExtensionSupported.HasValue) { if (Environment.OSVersion.Version.Build >= 21301) { this._isHIFExtensionSupported = true; } else if (Environment.OSVersion.Version.Build >= 19041) { const string targetHootfixId = "KB5003214"; const string query = "SELECT HotFixID FROM Win32_QuickFixEngineering"; bool supported = false; var searcher = new System.Management.ManagementObjectSearcher(query); foreach (var hotfix in searcher.Get()) { if (hotfix["HotFixID"].ToString() == targetHootfixId) { supported = true; break; } } this._isHIFExtensionSupported = supported; } else { this._isHIFExtensionSupported = false; } } return this._isHIFExtensionSupported.Value; } } private bool? _isHEVCSupported; public bool IsHEVCSupported { get { if (!this._isHEVCSupported.HasValue) { const string targetKeyName = "Microsoft.HEVCVideoExtension_"; this._isHEVCSupported = Registry.ClassesRoot .OpenSubKey("ActivatableClasses") .OpenSubKey("Package") .GetSubKeyNames() .Any(name => name.StartsWith(targetKeyName)); } return this._isHEVCSupported.Value; } } private bool? _isAV1Supported; public bool IsAV1Supported { get { if (!this._isAV1Supported.HasValue) { const string targetKeyName = "Microsoft.AV1VideoExtension_"; this._isAV1Supported = Registry.ClassesRoot .OpenSubKey("ActivatableClasses") .OpenSubKey("Package") .GetSubKeyNames() .Any(name => name.StartsWith(targetKeyName)); } return this._isAV1Supported.Value; } } public bool IsAVIFSupported { get { if (!this._isAVIFSupported.HasValue) { this._isAVIFSupported = Environment.OSVersion.Version.Build >= 18305 && this.IsAV1Supported; } return this._isAVIFSupported.Value; } } public override string[] Extensions { get { var extensions = new Collection<string>(); if (this.IsHIFExtensionSupported) { extensions.Add(".hif"); } extensions.Add(".heif"); extensions.Add(".heifs"); extensions.Add(".avci"); extensions.Add(".avcs"); if (this.IsHEVCSupported) { extensions.Add(".heic"); extensions.Add(".heics"); } if (this.IsAVIFSupported) { extensions.Add(".avif"); extensions.Add(".avifs"); } return extensions.ToArray(); } } public override string FileType { get { return this.IsAVIFSupported ? this.IsHEVCSupported ? "HEIF (AVCI, HEIC, AVIF)" : "HEIF (AVCI, AVIF)" : this.IsHEVCSupported ? "HEIF (AVCI, HEIC)" : "HEIF (AVCI)"; } } // CLSID_WICHeifDecoder public override Guid CLSID => new Guid(0xe9a4a80a, 0x44fe, 0x4de4, 0x89, 0x71, 0x71, 0x50, 0xb1, 0x0a, 0x51, 0x99); }
もっといい判定方法ってあるんですかね? あったら教えてください。
7/30 追記: AUMID を検索するのに,レジストリーを使った手法があるようなので,インストールされているかの有無の判定はとりあえずこれで悪くなさそうですね。API あったらそっち使ったほうがもっといい感じにかけるな,と思っていたんですけど。
参考資料
WIC GUIDs のページ。