英語で読む

次の方法で共有


HEIF 拡張コーデック

重要

一部の情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。

WIC を介して使用できる High Efficiency Image Format (HEIF) 拡張コーデックに関する情報。 Microsoft Store からこの拡張コーデックをダウンロードするには、「HEIF 画像拡張機能」を参照してください。

フォト ビューアーで HEIF 画像の代替表現を取得できるようにするピクセル形式 GUID は 2 つあります。

  • 深度のみ
    • GUID_WICPixelFormat8bppDepth
  • ゲインのみ
    • GUID_WICPixelFormat8bppGain

これらの形式の詳細については、「ネイティブ ピクセル形式の概要」を参照してください。

エンコード オプションの WICHeifCompressionOption 列挙型によって、アプリは HEIF 画像ファイルの作成時にどの圧縮形式を使用するかを選択できるようになります。

HEIF 拡張コーデックの使用

WIC を使用するアプリがある場合は、ピクセル形式 GUID を使用して、画像の表現方法を要求できます。 アプリは、IWICBitmapSourceTransform::GetClosestPixelFormat メソッドを使用して、WIC デコーダーが特定のピクセル形式をサポートしているかどうかを確認できます。

次に、特定のピクセル形式を使用するビットマップに画像をデコードするために、アプリは IWICBitmapSourceTransform::CopyPixels を呼び出すことができます。

たとえば、画像が GUID_WICPixelFormat24bppRGB ピクセル形式を使用するビットマップに変換される場合、ビットマップには各ピクセルの赤、緑、青の色情報が含まれることになります。 (カラー コンポーネントあたり 8 ビット。)GUID_WICPixelFormat32bppRGBA は、各ピクセルに "アルファ" (透明度) 情報を追加します。 ピクセル形式 GUID_WICPixelFormat8bppAlpha は、ピクセルごとに 8 ビットの "アルファ" 情報しか含まないビットマップに使用されます。

HEIF の深度とゲインの情報

High Efficiency Image Format (HEIF) ファイルには、ピクセル形式 GUID_WICPixelFormat8bppDepth が適切です。 これは、GUID_WICPixelFormat8bppAlpha と同様に機能します。 相違点は、"アルファ" はピクセルの透明度を指定しますが、"深度" は画像のビューアーからのピクセルの相対距離を指定するということです。

GUID_WICPixelFormat8bppDepth を使用するビットマップでは、ビットマップはピクセルごとに "深度" 情報を含む 8 ビットの値を持っています。 値 0 は、画像のカラー ビットマップ表現内の併配置されたピクセルがビューアーに最も近い位置にあることを意味し、値 255 は、カラー ビットマップ内の併配置されたピクセルがビューアーから最も離れた位置にあることを意味します。

HEIF には、GUID_WICPixelFormat8bppGain という名前のピクセル形式も存在します。 アルファと深度用のピクセル形式と同様に、GUID_WICPixelFormat8bppGain はピクセルごとに 8 ビットの値を提供します。 "ゲイン" 情報の目的は、RGB 形式のピクセルに適用できる明るさのゲインを指定し、通常の RGB 画像をハイ ダイナミック レンジ (HDR) 画像に効率的に変換することです。 ゲイン情報は、そのまま公開されます。 アプリでは、HEIF 画像の Exif または XMP メタデータを使用して、どのカメラ モデルがゲインを生成したかを判断できます。 現在、ゲイン情報は、最近の Apple iOS デバイスによって作成された HEIF ファイルにのみ含まれています。

画像には "既定" の深度やゲインの情報はありません。 IWICBitmapSourceTransform::CopyPixels を呼び出して、ゲインや深度の表現の情報を持たない画像の該当情報を要求すると、WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT エラーが発生します。

圧縮オプション

WICHeifCompressionOption 列挙型は、ビットマップを HEIF 形式にエンコードするときに HeifCompressionMethod という名前のオプションを指定するために使用されます。

TIFF と同様に、HEIF はさまざまな圧縮方法を使用できるコンテナーです。 既定の圧縮方法は、High-Efficiency Video Codec (HEVC) です。 HEVC で圧縮された HEIF ファイルは通常、ファイル拡張子 .heic を使用します。 WICHeifCompressionAV1 列挙値では、代わりに AV1 コーデックを使用するように指定することが可能になります。 圧縮に AV1 を使用する HEIF ファイルは、通常、ファイル拡張子 .avif を使用します。

HEIF は、画像を再圧縮せずに、回転やトリミングなどの特定の編集を試みます。 WICHeifCompressionNone 列挙値を使用して、画像が再圧縮されないことを強制できます。 画像を再圧縮する必要がある場合、WICHeifCompressionNone が指定されているなら、Commit 操作は WINCODEC_ERR_UNSUPPORTEDOPERATION を返します。

HEVC および AV1 コーデックは、すべての PC で使用できるとは限りません。 これらのコーデックは、Microsoft Store からダウンロードできます。 HEVC または AV1 がインストールされているかどうかを確認するには、MFTEnumEx 関数を使用して、それぞれ MFVideoFormat_HEVC 形式と MFVideoFormat_AV1 形式のビデオ エンコーダーが存在するかどうかのクエリを実行します。

コード例

以下の例では、Windows Implementation Libraries (WIL) を使用します。 WIL をインストールするための便利な方法は、Visual Studio で、[プロジェクト]>[NuGet パッケージの管理...]>[参照] とクリックし、検索ボックスに「Microsoft.Windows.ImplementationLibrary」と入力するか貼り付けます。検索結果の項目を選択し、[インストール] をクリックしてそのプロジェクトにパッケージをインストールします。

例 1: 深度

この例では、深度情報が使用可能かどうかを確認する方法と、それを取得する方法を示します。

using namespace wil;

bool IsPixelFormatSupported(_In_ IWICBitmapSourceTransform* bitmap, 
    REFWICPixelFormatGUID proposedPixelFormat)
{
    WICPixelFormatGUID closestPixelFormat = proposedPixelFormat;
    if (SUCCEEDED(bitmap->GetClosestPixelFormat(&closestPixelFormat)) &&
        closestPixelFormat == proposedPixelFormat)
    {
        return true;
    }
    return false;
}

bool IsDepthAvailable(_In_ IWICBitmapSourceTransform* bitmap)
{
    return IsPixelFormatSupported(bitmap, GUID_WICPixelFormat8bppDepth);
}

HRESULT GetDepthBitmap(_In_ IWICBitmapFrameDecode* frame, unique_cotaskmem_ptr<BYTE[]>& bitmap)
{
    bitmap.reset();

    com_ptr_nothrow<IWICBitmapSourceTransform> frameTransform;
    RETURN_IF_FAILED(com_query_to_nothrow(frame, &frameTransform));

    // Check whether depth information is available for this image. If there's no depth
    // information, then just return an empty bitmap.
    if (IsDepthAvailable(frameTransform.get()))
    {
        UINT width;
        UINT height;
        RETURN_IF_FAILED(frame->GetSize(&width, &height));

        size_t bitmapSize;
        RETURN_IF_FAILED(SizeTMult(width, height, &bitmapSize));

        bitmap = make_unique_cotaskmem_nothrow<BYTE[]>(bitmapSize);
        RETURN_IF_NULL_ALLOC(bitmap);

        WICPixelFormatGUID pixelFormat = GUID_WICPixelFormat8bppDepth;
        RETURN_IF_FAILED(frameTransform->CopyPixels(nullptr, width, height,
            &pixelFormat, WICBitmapTransformRotate0, width, bitmapSize, bitmap.get()));
    }
    return S_OK;
}

例 2: ゲイン

この例では、"ゲイン" 情報が使用可能かどうかを確認する方法と、それを取得する方法を示します。 この例では、例 1 で示した IsPixelFormatSupported 関数を再利用します。

using namespace wil;

bool IsGainAvailable(_In_ IWICBitmapSourceTransform* bitmap)
{
    return IsPixelFormatSupported(bitmap, GUID_WICPixelFormat8bppGain);
}

HRESULT GetGainBitmap(_In_ IWICBitmapFrameDecode* frame, unique_cotaskmem_ptr<BYTE[]>& bitmap)
{
    bitmap.reset();

    com_ptr_nothrow<IWICBitmapSourceTransform> frameTransform;
    RETURN_IF_FAILED(com_query_to_nothrow(frame, &frameTransform));

    // Check whether gain information is available for this image. If there's no gain
    // information, then just return an empty bitmap.
    if (IsGainAvailable(frameTransform.get()))
    {
        UINT width;
        UINT height;
        RETURN_IF_FAILED(frame->GetSize(&width, &height));

        size_t bitmapSize;
        RETURN_IF_FAILED(SizeTMult(width, height, &bitmapSize));

        bitmap = make_unique_cotaskmem_nothrow<BYTE[]>(bitmapSize);
        RETURN_IF_NULL_ALLOC(bitmap);

        WICPixelFormatGUID pixelFormat = GUID_WICPixelFormat8bppGain;
        RETURN_IF_FAILED(frameTransform->CopyPixels(nullptr, width, height,
            &pixelFormat, WICBitmapTransformRotate0, width, bitmapSize, bitmap.get()));
    }
    return S_OK;
}

例 3: AV1 圧縮を使用する必要があることの指定

この例では、AV1 圧縮を使用して HEIF ファイルをエンコードし、.avif ファイルを作成する方法を示します。

using namespace wil;

HRESULT EncodeSourceAsAvif(IWICBitmapSource* source, IWICStream* outputStream, IWICImagingFactory* factory)
{
    com_ptr_nothrow<IWICBitmapEncoder> encoder;
    RETURN_IF_FAILED(factory->CreateEncoder(GUID_ContainerFormatHeif, nullptr, &encoder));
    RETURN_IF_FAILED(encoder->Initialize(outputStream, WICBitmapEncoderCacheInMemory));

    com_ptr_nothrow<IWICBitmapFrameEncode> outputFrame;
    com_ptr_nothrow<IPropertyBag2> encoderOptions;
    RETURN_IF_FAILED(encoder->CreateNewFrame(&outputFrame, &encoderOptions));

    // Configure the output frame to compress the source image using the AV1 encoder.
    PROPBAG2 option{};
    option.pstrName = L"HeifCompressionMethod";

    VARIANT propertyValue{};
    propertyValue.vt = VT_UI1;
    propertyValue.bVal = static_cast<BYTE>(WICHeifCompressionAV1);

    RETURN_IF_FAILED(encoderOptions->Write(1, &option, &propertyValue));
    RETURN_IF_FAILED(outputFrame->Initialize(encoderOptions.get()));
    RETURN_IF_FAILED(outputFrame->WriteSource(source, nullptr));

    // Do the actual encoding of the source image. If the AV1 encoder is missing,
    // then this call fails.
    RETURN_IF_FAILED(outputFrame->Commit());
    RETURN_IF_FAILED(encoder->Commit());
    return S_OK;
}

例 4: 圧縮を許可しないことの指定

この例では、HEIF コンテナーに画像を保存し、回転とトリミングを画像に適用しつつ、画像が圧縮されないことを強制する方法を示します。 ソースが既に HEVC または AV1 形式の場合、これは成功します。 しかし、ソースが生のビットマップまたは他の形式であり、エンコードが許可されていない場合、関数はエラーを返します。

using namespace wil;

// Rotate and optionally crop the source image, and produce a HEIF image file as output.
// If the input parameter allowEncoding is false, and the image needs to be encoded,
// then this function will return WINCODEC_ERR_UNSUPPORTEDOPERATION.
HRESULT RotateAndCropSource(
    IWICBitmapSource* source,
    IWICStream* outputStream,
    IWICImagingFactory* factory,
    bool allowCompression,
    WICBitmapTransformOptions transformOptions,
    WICRect* cropRectangle = nullptr)
{
    com_ptr_nothrow<IWICBitmapEncoder> encoder;
    RETURN_IF_FAILED(factory->CreateEncoder(GUID_ContainerFormatHeif, NULL, &encoder));
    RETURN_IF_FAILED(encoder->Initialize(outputStream, WICBitmapEncoderCacheInMemory));

    com_ptr_nothrow<IWICBitmapFrameEncode> outputFrame;
    com_ptr_nothrow<IPropertyBag2> encoderOptions;
    RETURN_IF_FAILED(encoder->CreateNewFrame(&outputFrame, &encoderOptions));

    VARIANT propertyValue{};

    if (!allowCompression)
    {
        // Specify that compression of the image is not allowed.
        PROPBAG2 option{};
        option.pstrName = L"HeifCompressionMethod";

        propertyValue.vt = VT_UI1;
        propertyValue.bVal = static_cast<BYTE>(WICHeifCompressionNone);
        RETURN_IF_FAILED(encoderOptions->Write(1, &option, &propertyValue));
    }

    if (transformOptions != WICBitmapTransformRotate0)
    {
        PROPBAG2 option{};
        option.pstrName = L"BitmapTransform";

        propertyValue.vt = VT_UI1;
        propertyValue.bVal = static_cast<BYTE>(transformOptions);
        RETURN_IF_FAILED(encoderOptions->Write(1, &option, &propertyValue));
    }

    RETURN_IF_FAILED(outputFrame->Initialize(encoderOptions.get()));
    RETURN_IF_FAILED(outputFrame->WriteSource(source, cropRectangle));

    // Generate the HEIF file. If the image needs to be compressed, this call fails.
    RETURN_IF_FAILED(outputFrame->Commit());
    RETURN_IF_FAILED(encoder->Commit());
    return S_OK;
}