君達はHD Photoを忘れてないか
最近はJPEG XR(Wikipedia)とも呼ばれているらしいHD Photo.
君達はその存在を忘れてはいないだろうか。
僕は忘れていました・・・。
.NET Framework 3.0からサポートされているため、我々はそれをプログラミングすることが出来る。
GDI+ではなくWPFでの扱いになっているようで、WPF素人の僕は何箇所か躓きましたが、何とか動いたので以下にメモしておきます。
HD Photoだけじゃなく、他のフォーマットも扱い方は同じみたいだ。これに気付くのに時間がかかってしまった。
C#すごい久しぶりに触ったなぁ・・・。
まずは要所を抜粋したもの。
public static BitmapSource Decode( string uri ) { // WmpBitmapDecoder を使うとHD Photo以外を受け付けなくなる var decoder = BitmapDecoder.Create( new Uri( uri ), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default ); return decoder.Frames[ 0 ]; } public static void Encode( BitmapSource source, string outUri, float quality = 0.8f ) { using ( var stream = new FileStream( outUri, FileMode.OpenOrCreate ) ) { var encoder = new WmpBitmapEncoder(); encoder.ImageQualityLevel = quality; encoder.Frames.Add( BitmapFrame.Create( source ) ); encoder.Save( stream ); } }
JPEGだとかPNGのような他のものもBitmap(En|De)coderを継承しているため、HD Photoに限らずWPFで画像を扱う際は似たような感じになるみたいです。
Decodeメソッド内のコメントにあるように、(当たり前ですが)WmpBitmapDecoderを使うとHD Photo以外の画像を開けない(例外が発生する)ので、今回は親クラスであるBitmapDecoderを使用しました(このようにするとフォーマットを自動で判別してくれるらしい)。
EncodeのほうはHD Photoでの書き出しに特化しています。クォリティは適当に0.8f。ちなみにオプショナル引数はC#4.0で追加された新しい仕様であります。
VistaはHD Photoにもともと対応しているので、保存した画像は標準のViewerで確認できます。
ファイルの拡張子は*.hdpか*.wdpらしいですが、僕の環境では*.hdpを画像ファイルとして認識してくれない・・・。
最後に、一応すべてのプログラムを掲載しておきます。おまけでXAMLも。
<Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" WindowStyle="ThreeDBorderWindow" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Height="480" Width="640" HorizontalAlignment="Left" VerticalAlignment="Top"> <Grid Name="main"> <ScrollViewer Name="scrollViewer1" CanContentScroll="True" HorizontalScrollBarVisibility="Visible" HorizontalContentAlignment="Left" VerticalContentAlignment="Top" Margin="0,41,0,0" Background="#FF524F4F"> <Image HorizontalAlignment="Stretch" Name="image1" Stretch="None" VerticalAlignment="Stretch" StretchDirection="Both" /> </ScrollViewer> <Button Content="Open" Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="button_Open" VerticalAlignment="Top" Width="75" Click="button_Open_Click" /> <Button Content="Convert" Height="23" HorizontalAlignment="Left" Margin="93,12,0,0" Name="button_Convert" VerticalAlignment="Top" Width="75" Click="button_Convert_Click" /> </Grid> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.IO; using WinForms = System.Windows.Forms; using System.Drawing.Imaging; namespace WpfApplication1 { /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window { private WinForms.OpenFileDialog openFileDialog = new WinForms.OpenFileDialog(); private WinForms.SaveFileDialog saveFileDialog = new WinForms.SaveFileDialog { AddExtension = true, Filter = "HD Photo ファイル|*.wdp;.*hdp;" }; public Window1() { InitializeComponent(); } private void button_Open_Click( object sender, RoutedEventArgs e ) { if ( this.openFileDialog.ShowDialog() == WinForms.DialogResult.OK ) this.image1.Source = JPEG_XR.Decode( this.openFileDialog.FileName ); } private void button_Convert_Click( object sender, RoutedEventArgs e ) { if ( this.image1.Source == null ) return; if ( this.saveFileDialog.ShowDialog() == WinForms.DialogResult.OK ) { JPEG_XR.Encode( (BitmapSource)this.image1.Source, this.saveFileDialog.FileName ); } } } public static class JPEG_XR { public static System.Windows.Controls.Image Convert( BitmapSource source ) { var img = new System.Windows.Controls.Image() { Source = source, Stretch = System.Windows.Media.Stretch.None }; return img; } public static BitmapSource Decode( string uri ) { // WmpBitmapDecoder を使うとHD Photo以外を受け付けなくなる var decoder = BitmapDecoder.Create( new Uri( uri ), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default ); return decoder.Frames[ 0 ]; } public static void Encode( BitmapSource source, string outUri, float quality = 0.8f ) { using ( var stream = new FileStream( outUri, FileMode.OpenOrCreate ) ) { var encoder = new WmpBitmapEncoder(); encoder.ImageQualityLevel = quality; encoder.Frames.Add( BitmapFrame.Create( source ) ); encoder.Save( stream ); } } } }