はじめに
Qiitaやブログなどで画像リサイズや拡張子変更するのですが、
毎回1枚1枚指定するので面倒になってきたので、
まとめて同じ拡張子と同じQualityに指定して変換するツールを
Magick.NET使ってWinUI3で実装していきます!
(下記で使って以降、最近、WinUI3にはまっている)
Magick.NET: .NET Framework や .NET (Core) アプリケーションから ImageMagick
の機能を簡単に利用するためのライブラリです。Magick.NET は ImageMagick
のラッパーとして機能します。
ImageMagick:
画像の読み込み、編集、保存など様々な操作を行うためのソフトウェア本体です。
(
※ImageMagickをサーバー等で利用する場合は脆弱性が修正されているかを確認の上で使用検討をおすすめします。
下記より
)
今回作る成果物
こんな感じのアプリを作成する。
主な機能は
- 画面
OSのダークモードの設定で背景色が変わります。
対象
-
OS Windows
-
WinUI3使用
プロジェクトの作成
今回は、プロジェクト名は ImageConverter で作成する。
下記のように
ファイル情報を取得するFilePicker、
出力する拡張子のSelectBox、
品質を指定するQualityのInputのUIで実装する。
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid x:Name="AppTitleBar" Margin="0,0,0,8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Source="Assets/StoreLogo.png" Width="16" Height="16" Margin="8,8,8,8" VerticalAlignment="Center"/>
<TextBlock x:Name="AppTitleTextBlock" Text="ImageConverter" Grid.Column="1" Style="{StaticResource CaptionTextBlockStyle}" VerticalAlignment="Center"/>
</Grid>
<StackPanel Grid.Row="1" Orientation="Vertical" Margin="8">
<Button x:Name="PickFilesButton" Content="Open Multiple Files" Click="PickFilesButton_Click" Margin="0,10,0,10"/>
<TextBlock x:Name="PickFilesOutputTextBlock" TextWrapping="Wrap" Margin="0,10,0,10" />
<StackPanel Orientation="Horizontal" Margin="0,10,0,10">
<ComboBox x:Name="ExtComboBox" Header="Output Extensions" PlaceholderText="Pick an extension" Width="200" Margin="0,0,8,0"/>
<NumberBox x:Name="QualityNum" Header="Output Quality:" Value="100" Minimum="1" Maximum="100" SpinButtonPlacementMode="Inline"/>
</StackPanel>
<Button Style="{StaticResource AccentButtonStyle}" x:Name="ConvertButton" Content="Convert Files" Click="ConvertButton_Click"/>
</StackPanel>
</Grid>
初期化時にタイトルバーの設定、出力する拡張子のSelectBoxを設定する
Dictionary<MagickFormat, string> exts = new Dictionary<MagickFormat, string>();
IReadOnlyList<StorageFile> files = new List<StorageFile>();
public MainWindow()
{
this.InitializeComponent();
ExtendsContentIntoTitleBar = true;
SetTitleBar(AppTitleBar);
List<ComboBoxItem> items = new List<ComboBoxItem>();
exts.Add(MagickFormat.Jpeg, "jpeg");
exts.Add(MagickFormat.WebP, "webp");
exts.Add(MagickFormat.Png, "png");
foreach (var ext in exts)
{
ComboBoxItem item = new ComboBoxItem();
item.Content = ext.Value;
item.Tag = ext.Key;
items.Add(item);
}
ExtComboBox.ItemsSource = items;
ExtComboBox.SelectedIndex = 0;
}
FilePickerのボタン押下イベント処理を追加する。
ユーザーのDLフォルダーをデフォルトのOpen先に指定し、拡張子をフィルターする。
private async void PickFilesButton_Click(object sender, RoutedEventArgs e)
{
PickFilesOutputTextBlock.Text = "";
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
WinRT.Interop.InitializeWithWindow.Initialize(openPicker, hWnd);
openPicker.ViewMode = PickerViewMode.List;
openPicker.SuggestedStartLocation = PickerLocationId.Downloads;
openPicker.FileTypeFilter.Add(".jpg");
openPicker.FileTypeFilter.Add(".jpeg");
openPicker.FileTypeFilter.Add(".png");
openPicker.FileTypeFilter.Add(".webp");
openPicker.FileTypeFilter.Add(".heic");
files = await openPicker.PickMultipleFilesAsync();
if (files.Count > 0)
{
StringBuilder output = new StringBuilder("Picked files:");
foreach (StorageFile file in files)
{
output.Append(file.Name + ",");
}
PickFilesOutputTextBlock.Text = output.ToString();
}
else
{
PickFilesOutputTextBlock.Text = "Operation cancelled.";
}
}
Convertのボタン押下イベント処理を追加する。
成果物の出力先をユーザーのDLフォルダーのimageconverter_yyyyMMddhhmmssにして、作成する。
Magick.NETを使用して指定された出力拡張子と品質でConvertする。
private async void ConvertButton_Click(object sender, RoutedEventArgs e)
{
if (files.Count > 0)
{
try
{
string downloadsPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + @"\Downloads";
string newFolderPath = Path.Combine(downloadsPath, "imageconverter_" + DateTime.Now.ToString("yyyyMMddhhmmss"));
if (!Directory.Exists(newFolderPath))
{
Directory.CreateDirectory(newFolderPath);
}
var selectedItemTag = (ExtComboBox.SelectedItem as FrameworkElement)?.Tag;
foreach (StorageFile file in files)
{
string newExtPath = Path.ChangeExtension(file.Path, "." + ExtComboBox.SelectionBoxItem);
string outputPath = Path.Combine(newFolderPath, Path.GetFileName(newExtPath));
using (MagickImage image = new MagickImage(file.Path))
{
image.Format = (MagickFormat)selectedItemTag!;
image.Quality = (uint)QualityNum.Value;
image.Write(outputPath);
}
}
ContentDialog dialog = new ContentDialog
{
Title = "Success:Open " + newFolderPath,
CloseButtonText = "OK",
XamlRoot = this.Content.XamlRoot
};
await dialog.ShowAsync();
PickFilesOutputTextBlock.Text = "";
}
catch (Exception ex)
{
ContentDialog dialog = new ContentDialog
{
Title = ex.Message.ToString(),
CloseButtonText = "OK",
XamlRoot = this.Content.XamlRoot
};
await dialog.ShowAsync();
}
}
else
{
ContentDialog dialog = new ContentDialog
{
Title = "Choose Files",
CloseButtonText = "OK",
XamlRoot = this.Content.XamlRoot
};
await dialog.ShowAsync();
PickFilesButton.Focus(FocusState.Programmatic);
}
}
完成!
今回の成果物ソース
ソース
まとめ
Magick.NET(ImageMagick)で遊んでみたが、
様々な拡張子のサポートがあり、操作も簡単で扱いやすいライブラリだった。
特にHEIC拡張子をサポートしているライブラリは少ないので
脆弱性などを理解したうえで、今後も注目していきたいと思う。