閱讀英文

共用方式為


HEIF 擴充功能編解碼器

重要

某些資訊與發行前版本產品有關,在發行前版本產品可能經過大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。

透過 WIC 取得的高效能影像格式 (HEIF) 延伸模組編解碼器的相關信息。 若要從 Microsoft Store 下載延伸模組編解碼器,請參閱 HEIF 映像延伸模組

兩個像素格式 GUID 可讓相片查看器擷取 HEIF 影像的替代表示法。

  • 僅限深度
    • GUID_WICPixelFormat8bppDepth
  • 僅取得
    • GUID_WICPixelFormat8bppGain

如需這些格式的詳細資訊,請參閱 原生圖元格式概觀

編碼 選項的 WICHeifCompressionOption 列舉 可讓應用程式選擇建立 HEIF 圖像檔時要使用的壓縮格式。

使用 HEIF 擴充功能編解碼器

如果您有使用 WIC 的應用程式,您可以使用圖元格式 GUID 來要求影像的呈現方式。 您的應用程式可以使用 IWICBitmapSourceTransform::GetClosestPixelFormat 方法來檢查 WIC 譯碼器是否支援特定圖元格式

然後,若要將影像譯碼為使用特定圖元格式的點陣圖,您的應用程式可以呼叫 IWICBitmapSourceTransform::CopyPixels

例如,如果影像轉換成使用GUID_WICPixelFormat24bppRGB像素格式的點陣圖,則表示位圖包含每個圖元的紅色、綠色和藍色資訊。 (每個色彩元件 8 位。GUID_WICPixelFormat32bppRGBA會將Alpha(透明度)資訊新增至每個圖元。 像素格式 GUID_WICPixelFormat8bppAlpha 用於只包含每個圖元 8 位 Alpha 資訊的點陣圖。

HEIF 的深度和取得資訊

針對高效影像格式 (HEIF) 檔案,圖元格式 GUID_WICPixelFormat8bppDepth 相關。 其運作方式與 GUID_WICPixelFormat8bppAlpha類似。 除了Alpha指定像素的透明度之外深度會指定影像查看器的像素相對距離。

在使用 GUID_WICPixelFormat8bppDepth 的點陣圖中,位圖包含 8 位值,其中包含 每個圖元的深度 資訊。 值為 0 表示影像色彩點陣圖表示中的共置圖元最接近查看器;和值為 255 表示色彩點圖中的共置圖元離查看器最遠。

此外,對於 HEIF,還有名為 GUID_WICPixelFormat8bppGain 的圖元格式。 與Alpha和深度的像素格式類似, GUID_WICPixelFormat8bppGain 提供每個像素8位的值。 取得資訊的目的是指定可套用至 RGB 格式圖元的亮度增益:有效地將一般 RGB 影像轉換成高動態範圍 (HDR) 影像。 取得資訊會依目前情況公開。 您的應用程式可以使用 HEIF 影像中的 Exif 或 XMP 元數據來判斷哪個相機模型已產生收益。 目前,取得資訊只會包含在最近 Apple iOS 裝置所建立的 HEIF 檔案中。

影像沒有 預設 深度或取得資訊。 呼叫 IWICBitmapSourceTransform::CopyPixels ,要求取得或深度表示沒有該資訊的影像會導致 WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT 錯誤。

壓縮選項

WICHeifCompressionOption 列舉可用來指定將點陣圖編碼成 HEIF 格式時,名為 HeifCompressionMethod 的選項。

HEIF 是一個容器,類似於 TIFF,可使用不同的壓縮方法。 默認壓縮方法是高效視訊編解碼器 (HEVC)。 使用 HEVC 壓縮的 HEIF 檔案通常會使用 .heic 擴展名。 使用 WICHeifCompressionAV1 列舉值,就可以指定應該改用 AV1 編解碼器。 使用AV1進行壓縮的HEIF檔案通常會使用 .avif 擴展名。

HEIF 會嘗試執行某些編輯,例如旋轉和裁剪,而不需重新壓縮影像。 您可以使用 WICHeifCompressionNone 列舉值來強制取消壓縮映像。 如果需要重新壓縮映像,則如果已指定 WICHeifCompressionNone認可作業將會傳回WINCODEC_ERR_UNSUPPORTEDOPERATION

HEVC 和 AV1 編解碼器可能無法在所有電腦上使用。 您可以從 Microsoft Store 下載這些編解碼器。 若要檢查是否已安裝 HEVC 或 AV1,您可以使用 MFTEnumEx 函式來查詢視訊編碼器是否分別MFVideoFormat_HEVC和MFVideoFormat_AV1格式

程式碼範例

這些程式代碼範例使用 Windows 實作連結庫 (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;
}