【ITニュース解説】Decoding iPhone HEIC Photos in the Browser With libheif WASM (and the One-Line Bug That Broke It)
2026年10月17日に「Dev.to」が公開したITニュース「Decoding iPhone HEIC Photos in the Browser With libheif WASM (and the One-Line Bug That Broke It)」について初心者にもわかりやすく解説しています。
ITニュース概要
iPhoneのHEIC写真をサーバーに送らず、ブラウザで直接デコードする方法を解説。ファイルの種類を拡張子でなく内容で判別し、ブラウザ機能が無理ならWebAssembly化したlibheifで処理する。多数の画像処理時はメモリ使用量を考慮し、ワーカー数を調整するのが重要だ。
ITニュース解説
iPhoneで撮影された写真は、iOS 11以降、HEIC(High Efficiency Image File Format)という効率的な形式で保存されるようになった。この形式は、従来のJPEGに比べてファイルサイズを小さく保ちつつ高画質を実現するため、スマートフォンのストレージ節約に貢献する。しかし、Windowsパソコンや古いシステム、ウェブサイトのアップロードフォームなど、多くの環境でHEICファイルはそのままでは扱いにくく、互換性の問題が課題となっている。
この課題に対し、これまでの一般的な解決策は、HEICファイルをオンラインの変換サービスにアップロードし、JPEGなどの広く互換性のある形式に変換するというものだった。しかし、この方法では個人写真がインターネット上のサーバーに送信されるため、プライバシーや通信負荷の懸念があった。本記事では、この変換処理をサーバーを介さず、ユーザーの「ブラウザ内」で完結させる画期的な方法を解説する。これは、プライバシーを守りながら、より高速で効率的な画像変換を実現するアプローチである。
ブラウザ内でのHEIC変換は、主に二つの技術を組み合わせることで実現される。一つは一部の最新ブラウザが持つネイティブなHEICデコード機能。もう一つは、幅広いブラウザで対応するため、WebAssembly(WASM)という技術でコンパイルされた画像処理ライブラリ「libheif」を使用する方法だ。
変換の最初のステップは、アップロードされたファイルが本当にHEIC形式であるかを正確に判断することだ。ファイル名の拡張子(例: .heicや.jpg)は容易に変更できるため信頼できない。そこで、ファイル自体の先頭部分である「ヘッダー」のバイト列を読み取り、4バイト目から8バイト目がftypという文字列であり、さらに8バイト目から12バイト目がheic、mif1、msf1といったHEIC形式を表すブランド文字列のいずれかであるかを確認する。この方法で拡張子に依存せず正確な形式を識別できる。
HEICと判別できたら、まずブラウザが持つ標準のデコード機能を試す。JavaScriptのcreateImageBitmap()関数を使用することで、Safari 17などの一部のモダンブラウザはHEICファイルを直接デコードできる。成功すれば外部ライブラリは不要で、最も効率的だ。多くのChromeやFirefoxではまだネイティブ対応していないため、この試みは失敗し、次のステップに進む。
ネイティブデコードが不可の場合、WebAssemblyでコンパイルされたlibheifライブラリを使用する。WebAssemblyは、ブラウザで高速に実行できるバイナリ形式のコードで、C++などで書かれたlibheifのような強力なライブラリをブラウザ上で動かすことを可能にする。libheifのWebAssemblyコードは約520KBあるため、必要な時にだけダウンロードする「遅延ロード」を採用し、初期表示速度への影響を避ける。ダウンロード失敗時も、その後の処理に影響しないよう適切にエラーを処理する。
HEICファイルは、メイン画像に加えサムネイルや深度マップなど複数の画像を含むことがある。メインとなるプライマリ画像の判別には、libheif-jsのis_primary()メソッドを使う想定だった。しかし、特定のバージョン(1.19.8)でこのメソッドにバグがあり、存在しない関数を呼び出すためエラーが発生した。解決策は、直接低レベルなlibheif関数を呼び出すことだ。これは、実際のファイルでテストすることの重要性を示す事例である。テスト用に作成された人工ファイルでは見つからないバグが、実際の環境で顕在化することは少なくない。
HEICデコードで生成される生の画像データ(RGBA)は、非常に大きくなる。例えば、48メガピクセル(MP)の写真はデコードするだけで約190メガバイト(MB)、64MPでは約256MBものメモリを消費する。そのため、デコード前に画像のサイズを確認し、例えば64MPを超える画像は処理を拒否するなど、メモリ消費を防ぐ対策が必要だ。また、デコード完了後やエラー時にも、使用したメモリやリソースを確実に解放し、メモリリークを防ぐことが重要である。HEICファイルに含まれる回転情報はlibheifが自動で適用するが、変換後のJPEGにEXIFの回転情報を正しく設定しないと、一部ビューアで画像が二重に回転する可能性がある。
画像デコードはCPU負荷が高く、メインスレッドで実行するとページが固まるなど、ユーザー体験を損ねる。そこで、ブラウザのメインスレッドとは独立してバックグラウンドで処理を行う「Web Workers」を活用する。しかし、同時に多くのワーカーを動かすと、特にメモリが限られたスマートフォンなどではメモリ不足でタブがクラッシュする恐れがある。そのため、CPUコア数だけでなく、デバイスのメモリ量やモバイル環境であるかに応じてワーカー数を調整する。例えば、モバイルやメモリ4GB以下の環境では、安定性のため同時処理を1枚に制限するといった設定が推奨される。これにより、処理速度を少し犠牲にしても、アプリケーション全体の安定性を保てる。
画像をCanvasに描画すると、EXIFなどのメタデータ(撮影日時、カメラモデル、GPS情報など)は失われる。プライバシー保護のためGPS情報は通常削除するが、ユーザーが求める撮影日やカメラ情報などは、変換後のJPEGファイルに再度書き戻す。
これらの工夫により、HEICファイルをサーバーにアップロードすることなく、ブラウザ内で安全かつ効率的にJPEG等へ変換するコンバーターが実現できる。ネットワークログでは、画像のアップロードはなく、ページやWebAssemblyコードの初回ダウンロード、少量の解析データのみが確認できる。このアプローチは、ユーザーのプライバシー保護とオフライン環境での利用可能性を高める、非常に強力な解決策である。