2

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

SpacemanScott
  • 953
  • 9
  • 21
  • Yep, 150 ifs is what you're going to need to write. Use IWICBitmapSource::CopyPixels to put your blob to the bitmap. – vt. Sep 12 '14 at 14:51
  • Thanks @vt. You gotta wonder about the guys at MS who did this. Since it's a GUID, you can't even use a switch statement in C++ to test it. And on the other side, they had to have written the same massive number of if's to put it together. Even GTK+ has a simple call to get this info... really straightforward. – SpacemanScott Sep 16 '14 at 13:20
  • We can get width, height and total byte length with IWICBitmap GetSize, Lock method. Stride = length / height, isn't it? I think we don't need 150 ifs because we can't handle 150 different pixel formats. Why don't just check against a few known formats? – 9dan Oct 02 '14 at 17:02
  • @9dan No, you cannot get the total byte length. Only the width and height. see IWICBitmapSource and it's child IWICBitmap. And copy pixels expects you to tell it the stride. So you are left trying to assemble these pieces to accomplish anything. So it simply reinforces my previous statement.. "you have to wonder about the guys at MS who did this."
    Anyway, a few 'if' statements is the path I am taking, since there is only a subset that I know are going to be dealt with.
    – SpacemanScott Oct 03 '14 at 18:43
  • @SpacemanScott Oh, Stride property is provided by IWICBitmapLock interface! http://msdn.microsoft.com/en-us/library/windows/desktop/ee690161(v=vs.85).aspx For using Lock method, you could look at my recent post, http://stackoverflow.com/questions/26153633/windows-stretchblt-api-performance – 9dan Oct 03 '14 at 19:06

2 Answers2

6

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)

Andreas
  • 9,245
  • 9
  • 49
  • 97
  • When I cast the IWICComponentInfo pointer to a IWICPixelFormatInfo pointer something seems to go haywire. Does it require more than a simple type cast? I always get 0 as the result when calling GetBitsPerPixel() with it, and it crashes when I try to call, say, GetChannelCount(). I can confirm the IWICComponentInfo says it is of the type WICPixelFormat, and when I call GetFriendlyName on it it gives me something nice like "24bpp BGR 10". – Thomas Jun 07 '19 at 22:32
  • I must admit that it is some time since I worked on this. Just a cursory glance though says that you cant generally cast interfaces - only if you are using interface inheritence - I could be wrong, but I dont think the two interfaces are compatibile in this case. So the cast between interfaces would generally fail. I forget, but is there are method in the api for converting between these two intefaces? – soddoff Baldrick Jun 09 '19 at 00:07
  • hr = piFactory->CreateComponentInfo(pPixelFormat, &pIComponentInfo); This line doesn't work - CreateComponentInfo can't take in a GUID. – Thomas Jun 09 '20 at 01:31
  • @Thomas: The code was missing a call to `QueryInterface`. You can't cast `IWICComponentInfo` to `IWICPixelFormatInfo`. You have to get the `IWICPixelFormatInfo` pointer from `IWICComponentInfo` using `QueryInterface`. I have fixed the code in the answer. – Andreas Nov 01 '20 at 12:39
4

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;
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219