君達は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で追加された新しい仕様であります。


VistaHD 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 );
			}
		}
	}
}