C++/WinRTでUWPその8　ファイルピッカーと画像の表示

# 1. 今回やること
・ファイル操作の基本をやります。
・C++だと、[filesystem](https://cpprefjp.github.io/reference/filesystem.html)や[fstream](https://cpprefjp.github.io/reference/fstream.html)もあるので最悪WinRTのAPIを使わなくてもなんとかなります。
・でも、ファイルやフォルダを選ぶときにテキストボックスに手打ちで指定させるのでは無く、ダイアログを使いたいでしょ？
・MFCではCFileDialog等を使用しましたが、UWPではファイルピッカーを使用してファイルを指定します。
・[ファイル、フォルダー、およびライブラリ](https://docs.microsoft.com/ja-jp/windows/uwp/files/)にわかりやすく書いてあるので良く読んでください。

・やることはファイルピッカーで指定した画像ファイルの表示をやります。
<br>

# 2. 雛型の作成
・名前はなぜか「ocr_1」にして、見た目はこんな感じにしました。~~次にやることがバレますね。~~

```xml:MainPage.xaml（変更後）
<Page
    x:Class="ocr_1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ocr_1"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid RequestedTheme="Default">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="1*" />

            <RowDefinition Height="5*" />
        </Grid.RowDefinitions>
        <Button x:Name="button1" Click="button1_Click" Content="クリックして画像を選べ" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FontSize="24" Margin="5,5,5,5"/>
        <Image x:Name="pic1" Grid.Row="1" Grid.Column="0"/>
        <TextBox x:Name="res1" Grid.Row="1" Grid.Column="1" Background="LightGray"/>

    </Grid>
</Page>
```
![1-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/ff4b1486-650c-e67e-7c80-6a2c5a8d9401.png)
<br>
# 3. ファイルピッカーの実装
・[ピッカーでファイルやフォルダーを開く](https://docs.microsoft.com/ja-jp/windows/uwp/files/quickstart-using-file-and-folder-pickers)や[github](https://github.com/microsoft/Windows-universal-samples/tree/master/Samples/FilePicker/cppwinrt)のサンプルを見ながら実装していきましょう。
・まずはクリックして画像を選んでその画像のパスを右のTextBoxへ表示させます。

```C++:MainPage.cpp（変更後）
#include "pch.h"
#include "MainPage.h"
#include "MainPage.g.cpp"

#include <winrt/Windows.Storage.h>
#include <winrt/Windows.Storage.Pickers.h>
#include <winrt/Windows.Foundation.h>

using namespace winrt;
using namespace Windows::UI::Xaml;


namespace winrt::ocr_1::implementation
{
    MainPage::MainPage()
    {
        InitializeComponent();
    }
}


winrt::Windows::Foundation::IAsyncAction winrt::ocr_1::implementation::MainPage::button1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    winrt::Windows::Storage::Pickers::FileOpenPicker picPicker;
    picPicker.SuggestedStartLocation(winrt::Windows::Storage::Pickers::PickerLocationId::Desktop);
    picPicker.FileTypeFilter().ReplaceAll({ L".jpg", L".jpeg", L".png", L".bmp" });

    auto picfile = co_await picPicker.PickSingleFileAsync();

    if (picfile == nullptr)
        res1().Text(L"選んで無いよ！");
    else
        res1().Text(picfile.Path());    
}

```

・MainPage.cppのみ載せます。
・まずは前回やったようにbutton1_ClickのイベントハンドラをIAsyncAction型へ変更してください。前回も書きましたが、WinRT APIは非同期関数が多いです。なので、使うときはほぼIAsync～型となると考えてください。
・ファイルピッカーを使用するために#include ＜winrt/Windows.Storage.Pickers.h＞します。
・また、ファイルピッカーはStrageFile型を返すので、#include ＜winrt/Windows.Storage.h＞も必要です。
・使い方は大丈夫ですよね。最初に開くフォルダをSuggestedStartLocation()で指定して、FileTypeFilter()で開くファイルの拡張子を指定します。FileTypeFilter()で指定しないとエラーを起こすので注意してください。
・時間のかかりそうな関数は～Async()となるので、「co_await」を使用して非同期に実行します。
・キャンセルだとnullptrを返すのでそのチェックを行っています。
・[github](https://github.com/microsoft/Windows-universal-samples/tree/master/Samples/FilePicker/cppwinrt)のサンプルではViewMode()の指定もしていますが、今回は指定していません。何故かはを知りたいときは両方やってみてください。
<br>

# 4. 画像の表示
・MainPage.xamlでpic1と指定した箇所へ画像を表示します。
・画像はSoftwareBitmapとして内部へ保存するので、MainPage.hへメンバを追加します。SoftwareBitmapは#include ＜winrt/Windows.Graphics.Imaging.h＞しないと使えないので忘れずに追加します。

```C++:MainPage.h（変更後）

#pragma once

#include "MainPage.g.h"
#include <winrt/Windows.Graphics.Imaging.h>

namespace winrt::ocr_1::implementation
{
    struct MainPage : MainPageT<MainPage>
    {
        MainPage();
        winrt::Windows::Foundation::IAsyncAction button1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
    
    private:
        winrt::Windows::Graphics::Imaging::SoftwareBitmap showpic{ nullptr };
    
    };
}

namespace winrt::ocr_1::factory_implementation
{
    struct MainPage : MainPageT<MainPage, implementation::MainPage>
    {
    };
}

```

```C++:MainPage.h（変更後）
#include "pch.h"
#include "MainPage.h"
#include "MainPage.g.cpp"


#include <winrt/Windows.Storage.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Windows.Storage.Pickers.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.UI.Xaml.Media.Imaging.h>


using namespace winrt;
using namespace Windows::UI::Xaml;


namespace winrt::ocr_1::implementation
{
    MainPage::MainPage()
    {
        InitializeComponent();
    }
}

winrt::Windows::Foundation::IAsyncAction winrt::ocr_1::implementation::MainPage::button1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    winrt::Windows::Storage::Pickers::FileOpenPicker picPicker;
    picPicker.SuggestedStartLocation(winrt::Windows::Storage::Pickers::PickerLocationId::Desktop);
    picPicker.FileTypeFilter().ReplaceAll({ L".jpg", L".jpeg", L".png", L".bmp" });

    auto picfile = co_await picPicker.PickSingleFileAsync();

    if (picfile == nullptr)
        res1().Text(L"選んで無いよ！");
    else
    {
        res1().Text(picfile.Path());

        auto picstream = co_await picfile.OpenAsync(winrt::Windows::Storage::FileAccessMode::Read);
        auto picdecoder = co_await winrt::Windows::Graphics::Imaging::BitmapDecoder::CreateAsync(picstream);

        showpic = co_await picdecoder.GetSoftwareBitmapAsync(winrt::Windows::Graphics::Imaging::BitmapPixelFormat::Bgra8, winrt::Windows::Graphics::Imaging::BitmapAlphaMode::Premultiplied);

        auto picsource = winrt::Windows::UI::Xaml::Media::Imaging::WriteableBitmap(showpic.PixelWidth(), showpic.PixelHeight());

        showpic.CopyToBuffer(picsource.PixelBuffer());
        pic1().Source(picsource);
    }
}
```

・名前空間が深くて面倒くさい・・・~~using namespace使えって？~~
・さっき書いた以降のこんな感じに書くと画像が表示できます。最近ずっとOpenCV使って楽してたから表示するだけでも面倒です。
・画像を表示するために、いろいろ必要なので#include ＜winrt/Windows.Storage.Streams.h＞と#include ＜winrt/Windows.UI.Xaml.Media.Imaging.h＞が必要です。そのあと色々こねくり回すと画像が表示できます。[詳しくはこちらで](https://docs.microsoft.com/ja-jp/windows/uwp/audio-video-camera/imaging)。
・でも、ウインドウの大きさに合わせてちゃんと画像の大きさも変えてくれるので、そこは評価できます。そんなのをMFCのOnDrawでやろうとしたら・・・
・今回の画像は[pixabay](https://pixabay.com/ja/)さんから使用させていただきました。
![1-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/11a021fc-3aed-2ba1-984b-18b9697c444e.png)


<br>



次はOCR機能を追加します。

[もくじへ](https://qiita.com/lilac0011/items/f53f7401ea7da31e3a0b)