다음을 통해 공유


HEIF 확장 코덱

Important

일부 정보는 상용 출시되기 전에 실질적으로 수정될 수 있는 시험판 제품과 관련이 있습니다. Microsoft는 여기에 제공된 정보에 대해 어떠한 명시적이거나 묵시적인 보증도 하지 않습니다.

WIC를 통해 사용할 수 있는 HEIF(고효율 이미지 형식) 확장 코덱에 대한 정보입니다. Microsoft Store에서 확장 코덱을 다운로드하려면 HEIF 이미지 확장을 참조하세요.

2픽셀 형식 GUID를 사용하면 사진 뷰어에서 HEIF 이미지의 대체 표현을 검색할 수 있습니다.

  • 깊이 전용
    • GUID_WICPixelFormat8bppDepth
  • 게인 전용
    • GUID_WICPixelFormat8bppGain

이러한 형식에 대한 자세한 내용은 네이티브 픽셀 형식 개요를 참조하세요.

인코딩 옵션의 WICHeifCompressionOption 열거형을 사용하면 앱에서 HEIF 이미지 파일을 만들 때 사용할 압축 형식을 선택할 수 있습니다.

HEIF 확장 코덱 사용

WIC를 사용하는 앱이 있는 경우 픽셀 형식 GUID를 사용하여 이미지를 나타내는 방법을 요청할 수 있습니다. 앱은 IWICBitmapSourceTransform::GetClosestPixelFormat 메서드를 사용하여 WIC 디코더가 특정 픽셀 형식을 지원하는지 여부를 검사 수 있습니다.

그런 다음, 이미지를 특정 픽셀 형식을 사용하는 비트맵으로 디코딩하려면 앱에서 IWICBitmapSourceTransform::CopyPixels를 호출할 수 있습니다.

예를 들어 이미지가 GUID_WICPixelFormat24bppRGB 픽셀 형식을 사용하는 비트맵으로 변환되는 경우 비트맵에 각 픽셀에 대한 빨간색, 녹색 및 파랑 색 정보가 포함되어 있음을 의미합니다. (색 구성 요소당 8비트) GUID_WICPixelFormat32bppRGBA 각 픽셀에 알파(투명도) 정보를 추가합니다. GUID_WICPixelFormat8bppAlpha 픽셀 형식은 각 픽셀에 대해 8비트 알파 정보만 포함하는 비트맵에 사용됩니다.

HEIF에 대한 깊이 및 정보 얻기

HEIF(고효율 이미지 형식) 파일의 경우 GUID_WICPixelFormat8bppDepth 픽셀 형식은 관련이 있습니다. GUID_WICPixelFormat8bppAlpha 유사하게 작동합니다. 알파픽셀의 투명도를 지정하지만 깊이는 이미지 뷰어에서 픽셀의 상대 거리를 지정합니다.

GUID_WICPixelFormat8bppDepth 사용하는 비트맵에서 비트맵에는 픽셀당 깊이 정보가 포함된 8비트 값이 포함됩니다. 값이 0이면 이미지의 색 비트맵 표현에서 공동 배치된 픽셀이 뷰어와 가장 가까운 위치에 있음을 의미합니다. 값이 255이면 색상 비트맵의 공동 배치 픽셀이 뷰어에서 가장 멀리 위치합니다.

또한 HEIF의 경우 GUID_WICPixelFormat8bppGain이라는 픽셀 형식이 있습니다. 알파 및 깊이 에 대한 픽셀 형식과 마찬가지로 GUID_WICPixelFormat8bppGain 픽셀당 8비트 값을 제공합니다. 게인 정보의 목적은 RGB 형식 픽셀에 적용할 수 있는 밝기 게인을 지정하는 것입니다. 일반 RGB 이미지를 HDR(High Dynamic Range) 이미지로 효과적으로 변환합니다. 게인 정보는 있는 그대로 노출됩니다. 앱은 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 코덱은 모든 PC에서 사용할 수 없습니다. Microsoft Store에서 이러한 코덱을 다운로드할 수 있습니다. HEVC 또는 AV1이 설치되어 있는지 검사 위해 MFTEnumEx 함수를 사용하여 각각 MFVideoFormat_HEVC 및 MFVideoFormat_AV1 형식에 대한 비디오 인코더의 존재를 쿼리할 수 있습니다.

코드 예제

이러한 코드 예제에서는 WIL(Windows 구현 라이브러리)을 사용합니다. 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;
}