Compartir a través de


Códec de extensión HEIF

Importante

Parte de la información hace referencia a un producto de versión preliminar que puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.

Información sobre el códec de extensión HEIF (Formato de imagen de alta eficiencia) disponible a través de WIC. Para descargar el códec de extensión de Microsoft Store, consulte Extensión de imagen HEIF.

Dos GUID de formato de píxel permiten a los visores de fotos recuperar representaciones alternativas de una imagen HEIF.

  • Solo profundidad
    • GUID_WICPixelFormat8bppDepth
  • Solo ganancia
    • GUID_WICPixelFormat8bppGain

Para obtener más información sobre esos formatos, consulte Introducción a los formatos de píxel nativos.

La enumeración WICHeifCompressionOption de opciones de codificación permite a las aplicaciones elegir qué formato de compresión debe usarse al crear un archivo de imagen HEIF.

Uso del códec de extensión HEIF

Si tiene una aplicación que usa WIC, puede usar un GUID de formato de píxel para solicitar cómo se representa la imagen. La aplicación puede comprobar si un descodificador WIC admite un formato de píxel específico mediante el método IWICBitmapSourceTransform::GetClosestPixelFormat.

Posteriormente, para descodificar una imagen en un mapa de bits que usa un formato de píxel específico, la aplicación puede llamar a IWICBitmapSourceTransform::CopyPixels.

Por ejemplo, si una imagen se convierte en un mapa de bits que usa el formato de píxel GUID_WICPixelFormat24bppRGB, significa que el mapa de bits contiene información de color rojo, verde y azul para cada píxel. (8 bits por componente de color). GUID_WICPixelFormat32bppRGBA agrega información de alfa (transparencia) a cada píxel. El formato de píxel GUID_WICPixelFormat8bppAlpha se usa para mapas de bits que contienen solo información de alfa de 8 bits para cada píxel.

Información de profundidad y ganancia para HEIF

Para los archivos HEIF (Formato de imagen de alta eficiencia), el formato de píxel GUID_WICPixelFormat8bppDepth es relevante. Funciona de forma similar a GUID_WICPixelFormat8bppAlpha. Excepto que mientras que alfa especifica la transparencia de píxeles, la profundidad especifica la distancia relativa de píxeles desde el visor de la imagen.

En un mapa de bits que usa GUID_WICPixelFormat8bppDepth, el mapa de bits contiene un valor de 8 bits con información de profundidad por píxel. Un valor de 0 significa que el píxel ubicado conjuntamente en la representación de mapa de bits de color de la imagen se encuentra más cercano al visor, mientras que un valor de 255 significa que el píxel ubicado conjuntamente en el mapa de bits de color se encuentra más lejos del visor.

También para HEIF, hay un formato de píxel denominado GUID_WICPixelFormat8bppGain. De forma similar a los formatos de píxel para alfa y profundidad, GUID_WICPixelFormat8bppGain proporciona un valor de 8 bits por píxel. El objetivo de la información de ganancia es especificar una ganancia de brillo que se puede aplicar a los píxeles de formato RGB: convertir eficazmente una imagen RGB normal en una imagen HDR (Alto rango dinámico). La información de ganancia se expone tal como está. La aplicación puede usar los metadatos Exif o XMP en la imagen HEIF para determinar qué modelo de cámara ha generado la ganancia. Actualmente, la información de ganancia solo se incluye en archivos HEIF creados por los dispositivos iOS de Apple recientes.

No hay ninguna información de ganancia o profundidad predeterminada para las imágenes. Al llamar a IWICBitmapSourceTransform::CopyPixels solicitando una representación de ganancia o profundidad de una imagen que no tenga esa información, se producirá un error de tipo WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT.

Opciones de compresión

La enumeración WICHeifCompressionOption se usa para especificar una opción denominada HeifCompressionMethod al codificar un mapa de bits en formato HEIF.

HEIF es un contenedor, similar a TIFF, que puede usar diferentes métodos de compresión. El método de compresión predeterminado es HEVC (Códec de vídeo de alta eficiencia). Los archivos HEIF comprimidos con HEVC suelen usar la extensión de archivo .heic. Con el valor de enumeración WICHeifCompressionAV1, será posible especificar que alternativamente se debe usar el códec AV1. Los archivos HEIF que usan AV1 para la compresión suelen usar la extensión de archivo .avif.

HEIF intentará realizar ciertas modificaciones, como la rotación y el recorte, sin volver a comprimir la imagen. Puede usar el valor de enumeración WICHeifCompressionNone para exigir que la imagen no se vuelva a comprimir. Si es necesario volver a comprimir la imagen, la operación Commit devolverá WINCODEC_ERR_UNSUPPORTEDOPERATION si se ha especificado WICHeifCompressionNone.

Es posible que los códecs HEVC y AV1 no estén disponibles en todos los equipos. Puede descargar los códecs desde Microsoft Store. Para comprobar si HEVC o AV1 están instalados, puede usar la función MFTEnumEx para consultar la presencia de un codificador de vídeo para los formatos MFVideoFormat_HEVC y MFVideoFormat_AV1, respectivamente.

Ejemplos de código

En estos ejemplos de código se usan las bibliotecas de implementación de Windows (WIL). Una forma de instalar WIL es ir a Visual Studio, hacer clic en Proyecto>Administrar paquetes de NuGet...>Examinar, escriba o pegue Microsoft.Windows.ImplementationLibrary en el cuadro de búsqueda, seleccione el elemento en los resultados de la búsqueda y luego haga clic en Instalar para instalar el paquete para ese proyecto.

Ejemplo 1: Profundidad

En este ejemplo se muestra cómo comprobar si la información de profundidad está disponible y cómo recuperarla.

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;
}

Ejemplo 2: Ganancia

En este ejemplo se muestra cómo comprobar si la información de ganancia está disponible y cómo recuperarla. En este ejemplo se reutiliza la función IsPixelFormatSupported que se muestra en el ejemplo 1.

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;
}

Ejemplo 3: Especificación para usar la compresión AV1

En este ejemplo se muestra cómo usar la compresión AV1 para codificar un archivo HEIF, creando un archivo .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;
}

Ejemplo 4: Especificación para no permitir la compresión

En este ejemplo se muestra cómo guardar una imagen en un contenedor HEIF y aplicar rotación y recorte a la imagen, pero exigir que la imagen no se comprima. Si el origen ya está en formato HEVC o AV1, la acción se realizará correctamente. Pero si el origen es un mapa de bits sin formato o en algún otro formato, la función devolverá un error si no se permite la codificación.

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;
}