0

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!

Acorn
  • 24,970
  • 5
  • 40
  • 69
Ohjurot
  • 47
  • 9
  • It looks like a file format problem. How about to check every HRESULT return from every method call? There are maybe some error results. – 9dan Aug 09 '20 at 14:47
  • Thanks for your comment! I did checked every single method call witch returned something with if(FAILED(hr = CALL(...)){... return hr;} and got no error. Is there any special error code I need to explicitly check? – Ohjurot Aug 09 '20 at 15:18
  • I have traced down the value of my HRESULT hr and it never takes another value than S_OK. – Ohjurot Aug 09 '20 at 15:37
  • 1
    Are you sure about your pitch/stride computation? Or post a small reproducing project. – Simon Mourier Aug 10 '20 at 06:36
  • Well I don't know. Maybe there is somthing wrong with this line. But it worked with PNG's and it is the same on the MSDN sample. BBP * width / 8 makes sense for me because it coresponds to the width in bytes (stride) of a scanline. I have no idea why they used the +7... but removing it is still producing the same output. The stride is also correct I vertified it (at least for my interpretation). Maybe the +7 is to ensure that with round off the calaculation will still result in the appropriate stride. I will start from scratch now a create an only image loading example.I will post it here soon – Ohjurot Aug 10 '20 at 13:12
  • looks like classic pitch/stride problem (most likely in your code of course) – Roman R. Aug 11 '20 at 11:46
  • Stupid mistake on my side here! In my code to check the target DXGI_FORMATE compatibility I forgot an inversion. So i always ran into the fallback code which resulted in no conversion. Looks like this was fine for PNGs but failed for JPEGs. – Ohjurot Aug 11 '20 at 13:00

1 Answers1

1

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)

Ohjurot
  • 47
  • 9