Getting a Bitmap BitsPerPixel from IWICBitmapSource, IWICBitmap, IWICBitmapDecoder, wherever

2.3k Views Asked by At

I think the subject says it all. But in some detail, I am loading, manipulating, then displaying a bitmap, and was doing it in GDI. Now I want to add some speed, because it happens repeatedly.

Many years ago, I used DirectX for other things, but everything I've seen says to get off of DirectX, go Direct2D. So time to throw away all that knowledge, and start over again. Truly the MS way ;-)

Well, in order to manipulate the bitmap, before it goes in for rendering, I see I can get it with the 'Lock()'. And a function on the interface will also tell me the size. But I also need to know the BBP, and stride.

Before anyone says "Why don't you use ::GetBBP()... DUH", I haven't been able to find anything remotely like that, after hours of searching the MSDN and other sources. And there are a lot of very confusing COM interfaces out there.

The only thing I can find is GetPixelFormat(), which returns a GUID, and then I get to write about 150 "if (...)" statements to compare it. Just so I can test it for three values and a reject if it's none of them (1,8,32) Hardly an efficient way to deal with this.

And GetPixelFormat() doesn't tell me the stride either.

Is there a way to do this?

(The bitmaps are also uncompressed, so I don't even need to run them through the IWICBitmapDecoder, but I have yet to unravel how to simply tell IWICBitmap "here's a blob, go use it as a bitmap of size x-y")

Thanks for any assistance.

-Scott

2

There are 2 best solutions below

2
Victoria Dassen On

Use IWICPixelFormatInfo.

You can get this interface from the IWICImagingFactory.CreateComponentInfo() method.

  • Pass the WICPixelFormatGUID value you get from IWICBitmap.GetPixelFormat().
  • Use QueryInterface( __uuidof(IWICPixelFormatInfo)) on the IWICComponentInfo interface pointer returned by the factory method above to get the IWICPixelFormatInfo interface pointer.
  • Then you can ask the IWICPixelFormatInfo pointer to GetBitsPerPixel()

Convert that to the number of bytes per pixel, and you are done.

A bit more convoluted than getting the read lock, but an alternative nonetheless.

Pseudocode

//Get the PixelFormatInfo for the bitmap's particular format
CLSID pixelFormat = bitmapSource.GetPixelFormat();
IWicComponentInfo componentInfo = WicFactory.CreateComponent(pixelFormat);
IWICPixelFormatInfo info = componentInfo as IWICPixelFormatInfo;

//Get the number of bytes per pixel (e.g. 32bpp -> 4 bytes)
UInt32 bitsPerPixel = info.GetBitsPerPixel();
Int32 bytesPerPixel = bitsPerPixel / 8;

//Calculate the format's stride
UInt32 width;
UInt32 height;
bitmapSource.GetSize(out width, out height);
Int32 stride = width * bytesPerPixel;
4
soddoff Baldrick On

The answer above is the correct way to do it, there is ABSOLUTELY no need to create the 50 line if statement. I must admit that is what I did first, but what if you get a new format? You wont have an answer. Doing it this way will always work.

Thanks to whoever posted this answer above for the solution, but the pseudo code calculates the stride a bit wrongly. The stride is essentially the number of Bytes required to encode all the bits. So if you have less than 8 bits left over, you need to get 1 more byte to manage them.

Here is the answer in C++, mostly copied from the WIC documentation. This documentation is excellent, but as pointed out there is no example of how to get an instance of the IWICPixelFormatInfo. That is the part that I couldn't get.

Getting BitsPerPixel and Stride:

First: get a IWICBitmapFrameDecode as the Bitmap source

// Initialize COM.
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);

IWICImagingFactory *piFactory = NULL;
IWICBitmapDecoder *piDecoder = NULL;
IWICBitmapFrameDecode *pIDecoderFrame  = NULL;
GUID *pPixelFormat = NULL; 

// Create the COM imaging factory.
if (SUCCEEDED(hr))
{
    hr = CoCreateInstance(CLSID_WICImagingFactory,
    NULL, CLSCTX_INPROC_SERVER,
    IID_PPV_ARGS(&piFactory));
}

// Create the decoder.
if (SUCCEEDED(hr))
{
    hr = piFactory->CreateDecoderFromFilename(L"test.tif", NULL, GENERIC_READ,
        WICDecodeMetadataCacheOnDemand, //For JPEG lossless decoding/encoding.
        &piDecoder);
}

// Retrieve the First frame from the image (tif might have more than 1)
if (SUCCEEDED(hr))
{
   hr = pIDecoder->GetFrame(0, &pIDecoderFrame);
}

Next get the pixel format and the BitsPerPixel

//////////////////////////////////////////////////////////////////////////////////
//// IMPORTANT PART OF THE ANSWER
//////////////////////////////////////////////////////////////////////////////////

// Get the Pixel Format
IDecoderFrame.GetPixelFormat(&pPixelFormat);

// Now get a POINTER to an instance of the Pixel Format    
IWICComponentInfo *pIComponentInfo = NULL;
if (SUCCEEDED(hr))
{
    hr = piFactory->CreateComponentInfo(pPixelFormat, &pIComponentInfo);
}

// Get IWICPixelFormatInfo from IWICComponentInfo
IWICPixelFormatInfo *pIPixelFormatInfo;

hr = pIComponentInfo->QueryInterface(__uuidof(IWICPixelFormatInfo), reinterpret_cast<void**>(&pIPixelFormatInfo));

// Now get the Bits Per Pixel
UInt32 bitsPerPixel;
if (SUCCEEDED(hr))
{
   hr = pIPixelFormatInfo.GetBitsPerPixel(&bitsPerPixel);
}

You have two choices:

//Manually Calculate the Stride from the Bits Per Pixel.
UINT width;
UINT height;
if (SUCCEEDED(hr))
{
   hr = IDecoderFrame.GetSize(&width, &height);
}
float totalPixels = bitsPerPixel * width + 7; // +7 forces to next byte if needed
UINT stride = totalPixels / 8;

// ... now do something with stride-You can stop here if you dont need the bitmap

//////////////////////////////////////////////////////////////////////////////////

Or, if you want to use the image, you can create it...

// Alternative is to get a lock, by you need to actually create the bitmap
// by calling CreateBitmapFromSource.  IF you do want to create the bitmap,
// then by all means create one.  


IWICBitmap *pIBitmap = NULL;
IWICBitmapLock *pILock = NULL;


WICRect rcLock = { 0, 0, width, height }; // from GetSize earlier

// Create the bitmap from the image frame.
if (SUCCEEDED(hr))
{
   hr = m_pIWICFactory->CreateBitmapFromSource(
      pIDecoderFrame,          // Create a bitmap from the image frame
      WICBitmapCacheOnDemand,  // Cache metadata when needed
      &pIBitmap);              // Pointer to the bitmap
}

if (SUCCEEDED(hr))
{
   hr = pIBitmap->Lock(&rcLock, WICBitmapLockWrite, &pILock);   
}

// Now get the stride from the lock.
piLock.GetStride(&stride);

/// END OF ANSWER
/////////////////////////////////////////////////////////////////////////////////

Dont forget to tidy up...

SafeRelease(&pILock);
...

UPDATE: 2020. For Thomas.

I actually was writing Delphi code when I answer this so I was translating back to c++ :

Given this Pascal header:

function CreateComponentInfo(const clsidComponent: TGUID;
      out ppIInfo: IWICComponentInfo): HRESULT; stdcall;

Probably translates to:

HRESULT CreateComponentInfo(pGUID *clsidComponent, pIWICComponentInfo *ppIInfo);

I suspect either I have not referenced the pointer correctly in the example above.

Here is the working Delphi Code (object Pascal)

// from the already created frame interface
var lPixelFormat: TGUID;
iFrame.GetPixelFormat(lPixelFormat);   
if WCIUnSuccessful(lImagingFactory.CreateComponentInfo(lPixelFormat,
  iComponentInfo)) then
  exit;
iPixelformatInfo := iComponentInfo as IWICPixelFormatInfo;
if WCIUnSuccessful(iPixelformatInfo.GetBitsPerPixel(result.BitsPerPixel))
then
  exit;
    

So the call can take a GUID (if you get the pointer right)