共用方式為


編碼概觀

重要

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

編碼器會將影像數據寫入數據流。 編碼器可以在將影像圖元寫入數據流之前,先以數種方式壓縮、加密和改變影像圖元。 使用某些編碼器會產生取捨,例如 JPEG,這會取捨色彩資訊以取得更好的壓縮。 其他編碼器不會產生這類損失,例如位圖(BMP)。 由於許多編解碼器使用專屬技術來達到更佳的壓縮和影像逼真度,因此影像編碼方式的詳細數據會由編解碼器開發人員決定。

IWICBitmapEncoder

IWICBitmapEncoder 是將影像編碼成目標格式的主要介面,用來將影像的元件串行化,例如縮圖 (SetThumbnail) 和畫面格 (CreateNewFrame), 到圖像檔。

串行化發生的方式和時機會留給編解碼器開發人員。 目標檔格式內的每個個別數據區塊都應該能夠設定與順序無關,但同樣地,這是編解碼器開發人員的決定。 不過,呼叫 Commit 方法之後,就不應該允許對映像進行變更,而且應該關閉數據流。

IWICBitmapFrameEncode

IWICBitmapFrameEncode 是用來編碼影像個別畫面格的介面。 它提供設定個別畫面圖像處理元件的方法,例如縮圖和畫面格,以及影像尺寸、DPI 和像素格式。

個別畫面可能會使用框架特定元數據進行編碼,因此IWICBitmapFrameEncode可透過 GetMetadataQueryWriter 方法存取元數據寫入器。

框架的 Commit 方法會認可個別框架的所有變更,並指出該畫面的變更不應再接受。

編碼範例 (TIFF)

在下列範例中,標記影像檔格式 (TIFF) 影像會使用 IWICBitmapEncoder IWICBitmapFrameEncode 編碼。 TIFF 輸出是使用 WICTiffCompressionOption 自定義,而且點陣圖框架會使用指定的選項初始化。 使用 WritePixels 建立映像之後,會透過 Commit認可畫面,並使用 Commit 儲存映射。

IWICImagingFactory *piFactory = NULL;
IWICBitmapEncoder *piEncoder = NULL;
IWICBitmapFrameEncode *piBitmapFrame = NULL;
IPropertyBag2 *pPropertybag = NULL;

IWICStream *piStream = NULL;
UINT uiWidth = 640;
UINT uiHeight = 480;

HRESULT hr = CoCreateInstance(
                CLSID_WICImagingFactory,
                NULL,
                CLSCTX_INPROC_SERVER,
                IID_IWICImagingFactory,
                (LPVOID*) &piFactory);

if (SUCCEEDED(hr))
{
    hr = piFactory->CreateStream(&piStream);
}

if (SUCCEEDED(hr))
{
    hr = piStream->InitializeFromFilename(L"output.tif", GENERIC_WRITE);
}

if (SUCCEEDED(hr))
{
   hr = piFactory->CreateEncoder(GUID_ContainerFormatTiff, NULL, &piEncoder);
}

if (SUCCEEDED(hr))
{
    hr = piEncoder->Initialize(piStream, WICBitmapEncoderNoCache);
}

if (SUCCEEDED(hr))
{
    hr = piEncoder->CreateNewFrame(&piBitmapFrame, &pPropertybag);
}

if (SUCCEEDED(hr))
{        
    // This is how you customize the TIFF output.
    PROPBAG2 option = { 0 };
    option.pstrName = L"TiffCompressionMethod";
    VARIANT varValue;    
    VariantInit(&varValue);
    varValue.vt = VT_UI1;
    varValue.bVal = WICTiffCompressionZIP;      
    hr = pPropertybag->Write(1, &option, &varValue);        
    if (SUCCEEDED(hr))
    {
        hr = piBitmapFrame->Initialize(pPropertybag);
    }
}

if (SUCCEEDED(hr))
{
    hr = piBitmapFrame->SetSize(uiWidth, uiHeight);
}

WICPixelFormatGUID formatGUID = GUID_WICPixelFormat24bppBGR;
if (SUCCEEDED(hr))
{
    hr = piBitmapFrame->SetPixelFormat(&formatGUID);
}

if (SUCCEEDED(hr))
{
    // We're expecting to write out 24bppRGB. Fail if the encoder cannot do it.
    hr = IsEqualGUID(formatGUID, GUID_WICPixelFormat24bppBGR) ? S_OK : E_FAIL;
}

if (SUCCEEDED(hr))
{
    UINT cbStride = (uiWidth * 24 + 7)/8/***WICGetStride***/;
    UINT cbBufferSize = uiHeight * cbStride;

    BYTE *pbBuffer = new BYTE[cbBufferSize];

    if (pbBuffer != NULL)
    {
        for (UINT i = 0; i < cbBufferSize; i++)
        {
            pbBuffer[i] = static_cast<BYTE>(rand());
        }

        hr = piBitmapFrame->WritePixels(uiHeight, cbStride, cbBufferSize, pbBuffer);

        delete[] pbBuffer;
    }
    else
    {
        hr = E_OUTOFMEMORY;
    }
}

if (SUCCEEDED(hr))
{
    hr = piBitmapFrame->Commit();
}    

if (SUCCEEDED(hr))
{
    hr = piEncoder->Commit();
}

if (piFactory)
    piFactory->Release();

if (piEncoder)
    piEncoder->Release();

if (piBitmapFrame)
    piBitmapFrame->Release();

if (pPropertybag)
    pPropertybag->Release();

if (piStream)
    piStream->Release();

return hr;

編碼器選項使用方式

不同格式的不同編碼器需要針對影像編碼方式公開不同的選項。 Windows 映射元件 (WIC) 提供一致的機制,以表達編碼選項是否為必要,同時仍可讓應用程式使用多個編碼器,而不需要瞭解特定格式。 這可藉由在 CreateNewFrame 方法和 Initialize 方法提供 IPropertyBag 參數來完成。

元件處理站提供簡單的建立點來建立編碼器選項屬性包。 如果編解碼器需要提供簡單、直覺和非衝突的編碼器選項集,就可以使用此服務。 映像處理屬性包必須在建立期間初始化,且所有與編解碼器相關的編碼器選項。 針對標準集合中的編碼器選項,值範圍將會在 Write 上強制執行。 如需更進階的需求,編解碼器應該撰寫自己的屬性包實作。

應用程式會在建立畫面期間獲得編碼器選項包,而且必須在初始化編碼器畫面之前設定任何值。 針對UI驅動應用程式,它可以為標準編碼器選項提供固定UI,並為其餘選項提供進階檢視。 您可以透過 Write 方法一次進行一個變更,而且任何錯誤都會透過 IErrorLog 報告。 如果變更造成串聯效果,UI 應用程式應該一律重新讀取並顯示所有選項。 應用程式應該準備好處理編解碼器失敗的框架初始化,而編解碼器只會透過其屬性包提供最少的錯誤報告。

編碼器選項

應用程式可能會預期會遇到下列一組編碼器選項。 編碼器選項會反映編碼器和基礎容器格式的功能,因此本質上與編解碼器無關。 可能的話,應該將新選項正規化,以便套用至出現的新編解碼器。

屬性名稱 VARTYPE 適用的編解碼器
BitmapTransform VT_UI1 WICBitmapTransformOptions JPEG、HEIF
CompressionQuality VT_R4 0-1.0 TIFF
HeifCompressionMethod WICHeifCompressionOption various HEIF
ImageQuality VT_R4 0-1.0 JPEG、HDPhoto、HEIF
無損 VT_BOOL TRUEFALSE HDPhoto

ImageQualty 為 0.0 表示最低的可能逼真度轉譯,1.0 表示最高的精確度,這也可能代表視編解碼器而遺失。

CompressionQuality 為 0.0 表示最不有效率的壓縮配置可用,通常會產生快速編碼但較大的輸出。 值為 1.0 表示最有效率的配置可用,通常需要更多的時間來編碼,但會產生較小的輸出。 視編解碼器的功能而定,此範圍可能會對應到一組離散可用的壓縮方法。

無遺失表示編解碼器會將影像編碼為無遺失,且不會遺失影像數據。 如果已啟用 Lossless,則會忽略 ImageQuality。

除了上述一般編碼器選項之外,WIC 提供的編解碼器也支援下列選項。 如果編解碼器需要支援與這些提供編解碼器使用方式一致的選項,建議您這麼做。

屬性名稱 VARTYPE 適用的編解碼器
InterlaceOption VT_BOOL 開啟/關閉 PNG
FilterOption VT_UI1 WICPngFilterOption PNG
TiffCompressionMethod VT_UI1 WICTiffCompressionOption TIFF
明亮度 VT_UI4/VT_ARRAY 64 個專案 (DCT) JPEG
色度 VT_UI4/VT_ARRAY 64 個專案 (DCT) JPEG
JpegYCrCbSubsampling VT_UI1 WICJpegYCrCbSubsamplingOption JPEG
SuppressApp0 VT_BOOL JPEG
EnableV5Header32bppBGRA VT_BOOL 開啟/關閉 BMP

使用 VT_EMPTY 表示預設的 *未設定* 。 如果已設定其他屬性但不受支援,則編碼器應該忽略它們;這可讓應用程式在想要可能或可能不存在的功能時撰寫較少的邏輯程序代碼。

編碼器選項範例

在上述 TIFF 編碼範例,會設定特定的編碼器選項。 PROPBAG2 結構的 pstrName 成員會設定為適當的屬性名稱,且 VARIANT 會設定為對應的 VARTYPE 和所需的值,在此案例中,WICTiffCompressionOption 列舉的成員

if (SUCCEEDED(hr))
{
    hr = piEncoder->CreateNewFrame(&piBitmapFrame, &pPropertybag);
}

if (SUCCEEDED(hr))
{        
    // This is how you customize the TIFF output.
    PROPBAG2 option = { 0 };
    option.pstrName = L"TiffCompressionMethod";
    VARIANT varValue;    
    VariantInit(&varValue);
    varValue.vt = VT_UI1;
    varValue.bVal = WICTiffCompressionZIP;      
    hr = pPropertybag->Write(1, &option, &varValue);        
    if (SUCCEEDED(hr))
    {
        hr = piBitmapFrame->Initialize(pPropertybag);
    }
}

若要使用預設編碼器選項,只要使用建立框架時傳回的屬性包初始化點圖框架即可。

if (SUCCEEDED(hr))
{
    hr = piEncoder->CreateNewFrame(&piBitmapFrame, &pPropertybag);
}

if (SUCCEEDED(hr))
{        
    // Accept the default encoder options.
    if (SUCCEEDED(hr))
    {
        hr = piBitmapFrame->Initialize(pPropertybag);
    }
}

當未考慮任何編碼器選項時,也可以排除屬性包。

if (SUCCEEDED(hr))
{
    hr = piEncoder->CreateNewFrame(&piBitmapFrame, 0);
}

if (SUCCEEDED(hr))
{        
    // No encoder options.
    if (SUCCEEDED(hr))
    {
        hr = piBitmapFrame->Initialize(0);
    }
}