Search code examples
c++windowsjpegwic

JPEG Decoding problems with WIC


I am currently trying to load images from disk into memory using WIC. I used the MSDN documentation here to write my code.

Everything works fine for loading PNG images. Loading JPEG images works without any error but does not produce the correct result! The interesting thing is, that when I convert the image from JPEG to PNG (using irfan view) the error persists.

Consider the following test image:

Original image

Converted image from memory (inspected with Image Watch for Visual Studio)

As you can see the image got shrunk in the x direction and all color information is gone. However, when you zoom in you can see that there is still some color present, but it doesn’t look like expected:

Zoom in with Image Watch for visual studio

(Problems persist when uploading the image as texture to the GPU)

I have striped out my WIC loading code omitting error handling and resource freeing for readability:

// CoInitialize(NULL) called in main

// WIC Factory
IWICImagingFactory* ptrFactory = NULL;
CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&ptrFactory));

// Open decoder
IWICBitmapDecoder* ptrDecoder = NULL;
ptrFactory->CreateDecoderFromFilename(fileName, NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &ptrDecoder)

// Get first frame
IWICBitmapFrameDecode* ptrFrameDecoder = NULL;
ptrDecoder->GetFrame(0, &ptrFrameDecoder));

// Read formate
WICPixelFormatGUID frameNativeFormat;
ptrFrameDecoder->GetPixelFormat(&frameNativeFormat);

// Computing target format selectConverterWic(...) and selectConverterDx(...) are just some very basic functions for selecting the right convert to formate by iterating over an std::array witch contains the mappings suggested by the MSDC article mentioned above
WICPixelFormatGUID targetFormat;
DXGI_FORMAT targetFormatDx;
if (!selectConverterDx(frameNativeFormat, &targetFormatDx)) {
    // Try to find WIC to WIC converter
    selectConverterWic(frameNativeFormat, &targetFormat)
    selectConverterDx(targetFormat, &targetFormatDx)
}else {
    memcpy(&targetFormat, &frameNativeFormat, sizeof(GUID));
}

// Get format info
IWICComponentInfo* ptrCmpInfo = NULL;
ptrFactory->CreateComponentInfo(targetFormat, &ptrCmpInfo);

// Get type
WICComponentType ptrCmpType;
ptrCmpInfo->GetComponentType(&ptrCmpType);


// Get info from type
IWICPixelFormatInfo* ptrFormatInfo = NULL;
ptrCmpInfo->QueryInterface(IID_PPV_ARGS(&ptrFormatInfo));

// Get BBP
UINT uiBitsPerPixel;
ptrFormatInfo->GetBitsPerPixel(&uiBitsPerPixel);

// ID3D12Device->CheckFeatureSupport(...) and fallback omitted

// Image size
UINT width, height;
ptrFrameDecoder->GetSize(&width, &height);

// Tempory memory allocation
UINT rowPitch = (width * uiBitsPerPixel + 7) / 8;
SIZE_T imageSize = (SIZE_T)rowPitch * (SIZE_T)height;
void* workMemory = malloc(imageSize);

// Check if direct copy is possible
if (memcmp(&frameNativeFormat, &targetFormat, sizeof(GUID)) == 0){
    ptrFrameDecoder->CopyPixels(NULL, rowPitch, (UINT)imageSize, (BYTE*)workMemory);
}else{
    // Format conversion (Got never hit by a jpeg file; I tried to force it but results weren't right asswell)
    IWICFormatConverter* ptrFormatConverter = NULL;
    ptrFactory->CreateFormatConverter(&ptrFormatConverter);
    ptrFormatConverter->Initialize(ptrFrameDecoder, targetFormat, WICBitmapDitherTypeErrorDiffusion, NULL, 0, WICBitmapPaletteTypeCustom);
    ptrFormatConverter->CopyPixels(NULL, rowPitch, (UINT)imageSize, (BYTE*)workMemory);
    
}

// I inspected workMemory and got the result you saw above
// Some more code for copying data to user supplied parameters

Thanks in advance for your help!


Solution

  • The code above is totally fine and works! The problem was in the omitted DirectX 12 Feature support check:

    ID3D12Device->CheckFeatureSupport(...)
    

    I forgot an inversion which resulted that the following code snipped was executed

    memcpy(&frameNativeFormat, &GUID_WICPixelFormat32bppRGBA, sizeof(GUID));
    targetFormatDx = DXGI_FORMAT_R8G8B8A8_UNORM;
    uiBitsPerPixel = 32;
    

    The second bug was that I override "frameNativeFormat" instead of "targetFormat" which resulted that no conversion was executed (At least for JPEGs).

    Fixing both issues gave me a decent texture loading algorithm (At least for Windows)