WIC increases the size of TIFF image - c++

Scenario:
Load a TIFF image and extract the frames of tiff image and save it locally.
Combine the extracted frames to the output TIFF image.
When i try to combine the frames, the size of the output tiff image is increasing drastically. For example if my input size if 40 MB , the output is increased to 300 MB.
Below is the code which explains the scenario,
void readTiff()
{
HRESULT hr;
IWICBitmapFrameDecode *frameDecode = NULL;
IWICFormatConverter *formatConverter = NULL;
IWICBitmapEncoder *encoder = NULL;
IWICStream *pOutStream = NULL;
IWICBitmapFrameEncode *frameEncode = NULL;
IWICImagingFactory* m_pWICFactory;
hr = CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&m_pWICFactory)
);
IWICBitmapDecoder *pIDecoder = NULL;
hr = m_pWICFactory->CreateDecoderFromFilename(
L"D:\\test28\\Multitiff_files\\300dpiTIFF40MB_WATER.tif", // Image to be decoded
NULL, // Do not prefer a particular vendor
GENERIC_READ, // Desired read access to the file
WICDecodeMetadataCacheOnDemand, // Cache metadata when needed
&pIDecoder // Pointer to the decoder
);
UINT frameCount = 0;
pIDecoder->GetFrameCount(&frameCount);
for (int i = 0; i < frameCount; i++)
{
wchar_t temp[200];
int j = i;
swprintf_s(temp, 200, L"D:\\test28\\Multitiff_files\\out\\filename_png%d.jpeg", i);
if (SUCCEEDED(hr))
hr = m_pWICFactory->CreateStream(&pOutStream);
if (SUCCEEDED(hr))
hr = pOutStream->InitializeFromFilename(temp, GENERIC_WRITE);
if (SUCCEEDED(hr))
hr = m_pWICFactory->CreateEncoder(GUID_ContainerFormatJpeg, NULL, &encoder);
if (SUCCEEDED(hr))
hr = encoder->Initialize(pOutStream, WICBitmapEncoderNoCache);
hr = pIDecoder->GetFrame(i, &frameDecode);
if (SUCCEEDED(hr))
hr = m_pWICFactory->CreateFormatConverter(&formatConverter);
hr = formatConverter->Initialize(
frameDecode, // Source frame to convert
GUID_WICPixelFormat8bppIndexed, // The desired pixel format
WICBitmapDitherTypeNone, // The desired dither pattern
NULL, // The desired palette
0.f, // The desired alpha threshold
WICBitmapPaletteTypeMedianCut // Palette translation type
);
IPropertyBag2 *pPropertybag = NULL;
//Create a new frame..
hr = encoder->CreateNewFrame(&frameEncode, &pPropertybag);
//PROPBAG2 option = { 0 };
//option.pstrName = L"ImageQuality";
//VARIANT varValue;
//VariantInit(&varValue);
//varValue.vt = VT_R4;
//varValue.fltVal = 0.01f;
//hr = pPropertybag->Write(
// 1, // number of properties being set
// &option,
// &varValue);
WICPixelFormatGUID pixelFormat;
hr = frameEncode->Initialize(pPropertybag);
//pixelFormat = GUID_WICPixelFormat8bppIndexed;
frameDecode->GetPixelFormat(&pixelFormat);
hr = frameEncode->SetPixelFormat(&pixelFormat);
hr = frameEncode->WriteSource(formatConverter, NULL);
frameEncode->Commit();
encoder->Commit();
if (formatConverter)
formatConverter->Release();
if (frameDecode)
frameDecode->Release();
if (frameEncode)
frameEncode->Release();
if (pOutStream)
pOutStream->Release();
if (encoder)
encoder->Release();
}
if (m_pWICFactory)
m_pWICFactory->Release();
pIDecoder->Release();
}
void combineFile(int count)
{
HRESULT hr;
IWICImagingFactory *pFactory = NULL;
IWICStream *pInStream = NULL;
IWICBitmapDecoder *decoder = NULL;
IWICBitmapFrameDecode *frameDecode = NULL;
IWICFormatConverter *formatConverter = NULL;
IWICBitmapEncoder *encoder = NULL;
IWICStream *pOutStream = NULL;
IWICBitmapFrameEncode *frameEncode = NULL;
hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID*)&pFactory);
if (!SUCCEEDED(hr)) {
hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID*)&pFactory);
}
if (SUCCEEDED(hr))
hr = pFactory->CreateStream(&pOutStream);
if (SUCCEEDED(hr))
hr = pOutStream->InitializeFromFilename(L"D:\\test28\\Multitiff_files\\out\\out.tiff", GENERIC_WRITE);
if (SUCCEEDED(hr))
hr = pFactory->CreateEncoder(GUID_ContainerFormatWmp, NULL, &encoder);
if (SUCCEEDED(hr))
hr = encoder->Initialize(pOutStream, WICBitmapEncoderNoCache);
for (int i = 0; i < count; i++)
{
wchar_t temp[200];
swprintf_s(temp, 200, L"D:\\test28\\Multitiff_files\\out\\filename_png%d.jpeg", i);
if (SUCCEEDED(hr))
hr = pFactory->CreateStream(&pInStream);
if (SUCCEEDED(hr))
hr = pInStream->InitializeFromFilename(temp, GENERIC_READ);
if (SUCCEEDED(hr))
hr = pFactory->CreateDecoderFromStream(pInStream, NULL, WICDecodeMetadataCacheOnLoad, &decoder);
if (SUCCEEDED(hr))
hr = pFactory->CreateFormatConverter(&formatConverter);
hr = decoder->GetFrame(0, &frameDecode);
//hr = formatConverter->Initialize(
// frameDecode, // Source frame to convert
// GUID_WICPixelFormat8bppIndexed, // The desired pixel format
// WICBitmapDitherTypeNone, // The desired dither pattern
// NULL, // The desired palette
// 0.f, // The desired alpha threshold
// WICBitmapPaletteTypeMedianCut // Palette translation type
// );
WICPixelFormatGUID pixelFormat;
frameDecode->GetPixelFormat(&pixelFormat);
IPropertyBag2 *pPropertybag = NULL;
//Create a new frame..
hr = encoder->CreateNewFrame(&frameEncode, &pPropertybag);
PROPBAG2 option = { 0 };
option.pstrName = L"TiffCompressionMethod";
VARIANT varValue;
VariantInit(&varValue);
varValue.vt = VT_UI1;
varValue.bVal = WICTiffCompressionOption::WICTiffCompressionZIP;
hr = pPropertybag->Write(1, &option, &varValue);
hr = frameEncode->Initialize(pPropertybag);
hr = frameEncode->SetPixelFormat(&pixelFormat);
hr = frameEncode->WriteSource(formatConverter, NULL);
hr = frameEncode->Commit();
if (pInStream)
pInStream->Release();
if (decoder)
decoder->Release();
if (formatConverter)
formatConverter->Release();
if (frameEncode)
frameEncode->Release();
//if (frameDecode)
//frameDecode->Release();
}
encoder->Commit();
if (pFactory)
pFactory->Release();
if (encoder)
encoder->Release();
if (pOutStream)
pOutStream->Release();
}

One easy way to debug this kind of issue is to use ImageMagick which is installed on most Linux distros and is available for OSX and Windows. First use the identify utility within the suite with its verbose option to find out everything about your before and after images like this:
# Find out all we know about first image and put in file "1.txt"
identify -verbose image1.tif > 1.txt
# Find out all we know about second image and put in file "2.txt"
identify -verbose image2.tif > 2.txt
Now use your favourite file comparison tool to see the differences:
opendiff 1.txt 2.txt

Related

Why MediaFoundation InitializeSinkWriter (SetInputMediaType) only accepts WMV3 format?

Taken from the MSDN help pages, InitializeSinkWriter works fine so long as the video encoding and video input format is WMV3/RGB32, however if I change it to WMV1, MPEG2, etc. then SetInputMediaType fails.
AFAIK I have WMV1 installed as a codec according to Sherlock the Codec Detective program.
Here is the code that causes the issue:
(to find the problem code, search for "problem" in source comments, there is a lot of boiler plate code that is irrelevant)
// Format constants
const UINT32 VIDEO_WIDTH = 640;
const UINT32 VIDEO_HEIGHT = 480;
const UINT32 VIDEO_FPS = 30;
const UINT64 VIDEO_FRAME_DURATION = 10 * 1000 * 1000 / VIDEO_FPS;
const UINT32 VIDEO_BIT_RATE = 800000;
const GUID VIDEO_ENCODING_FORMAT = MFVideoFormat_WMV1 ; // problem here, must be WMV3
const GUID VIDEO_INPUT_FORMAT = MFVideoFormat_WMV3 ; // problem here if not wmv3 too
const UINT32 VIDEO_PELS = VIDEO_WIDTH * VIDEO_HEIGHT;
const UINT32 VIDEO_FRAME_COUNT = 20 * VIDEO_FPS;
HRESULT InitializeSinkWriter(IMFSinkWriter **ppWriter, DWORD *pStreamIndex)
{
*ppWriter = NULL;
*pStreamIndex = NULL;
IMFSinkWriter *pSinkWriter = NULL;
IMFMediaType *pMediaTypeOut = NULL;
IMFMediaType *pMediaTypeIn = NULL;
DWORD streamIndex;
HRESULT hr = MFCreateSinkWriterFromURL(L"output.wmv", NULL, NULL, &pSinkWriter);
// Set the output media type.
if (SUCCEEDED(hr))
{
hr = MFCreateMediaType(&pMediaTypeOut);
}
if (SUCCEEDED(hr))
{
hr = pMediaTypeOut->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
}
if (SUCCEEDED(hr))
{
hr = pMediaTypeOut->SetGUID(MF_MT_SUBTYPE, VIDEO_ENCODING_FORMAT);
}
if (SUCCEEDED(hr))
{
hr = pMediaTypeOut->SetUINT32(MF_MT_AVG_BITRATE, VIDEO_BIT_RATE);
}
if (SUCCEEDED(hr))
{
hr = pMediaTypeOut->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
}
if (SUCCEEDED(hr))
{
hr = MFSetAttributeSize(pMediaTypeOut, MF_MT_FRAME_SIZE, VIDEO_WIDTH, VIDEO_HEIGHT);
}
if (SUCCEEDED(hr))
{
hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_FRAME_RATE, VIDEO_FPS, 1);
}
if (SUCCEEDED(hr))
{
hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
}
if (SUCCEEDED(hr))
{
hr = pSinkWriter->AddStream(pMediaTypeOut, &streamIndex);
}
// Set the input media type.
if (SUCCEEDED(hr))
{
hr = MFCreateMediaType(&pMediaTypeIn);
}
if (SUCCEEDED(hr))
{
hr = pMediaTypeIn->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
}
if (SUCCEEDED(hr))
{
hr = pMediaTypeIn->SetGUID(MF_MT_SUBTYPE, VIDEO_INPUT_FORMAT);
}
if (SUCCEEDED(hr))
{
hr = pMediaTypeIn->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
}
if (SUCCEEDED(hr))
{
hr = MFSetAttributeSize(pMediaTypeIn, MF_MT_FRAME_SIZE, VIDEO_WIDTH, VIDEO_HEIGHT);
}
if (SUCCEEDED(hr))
{
hr = MFSetAttributeRatio(pMediaTypeIn, MF_MT_FRAME_RATE, VIDEO_FPS, 1);
}
if (SUCCEEDED(hr))
{
hr = MFSetAttributeRatio(pMediaTypeIn, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
}
if (SUCCEEDED(hr))
{
// Problem here! Codec issue with wmv1, mpeg, etc.
hr = pSinkWriter->SetInputMediaType(streamIndex, pMediaTypeIn, NULL);
}
else {
puts("setattributeratio failed");
}
// Tell the sink writer to start accepting data.
if (SUCCEEDED(hr))
{
hr = pSinkWriter->BeginWriting();
}
else {
puts("setinputmediatype failed"); // <-- HR result problem here
}
// Return the pointer to the caller.
if (SUCCEEDED(hr))
{
*ppWriter = pSinkWriter;
(*ppWriter)->AddRef();
*pStreamIndex = streamIndex;
}
else {
puts("beginwriting failed");
}
SafeRelease(&pSinkWriter);
SafeRelease(&pMediaTypeOut);
SafeRelease(&pMediaTypeIn);
return hr;
}
Initialize sink writer is called with this code:
void main()
{
DWORD streamidx = 0;
const WCHAR *SAMPLE_FILE = L"sample.wmv";
IMFSourceReader *pReader = NULL;
IMFSinkWriter *pWriter = NULL;
puts("Initializing...");
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hr))
{
hr = MFStartup(MF_VERSION);
if (SUCCEEDED(hr))
{
// problem here !
hr = InitializeSinkWriter(&pWriter, &streamidx);
if (SUCCEEDED(hr))
{
// more code would go here...
}
else {
puts("InitializeSinkWriter failed"); // this is called
}
SafeRelease(&pWriter);
MFShutdown();
}
CoUninitialize();
}
puts("Finished...");
}
This is a standard Windows 7 computer I am using, so, if it only accepts WMV3 as the encoder or input type, does it mean I have to install codecs? This seems absurd since popular formats like WMV1 and MPEG should already be installed, and Sherlock codec detective says they are
There is no support for codecs you are trying in Windows Media Foundation (even though some third party software could report availability of other codecs for other APIs).
See:
Supported Media Formats in Media Foundation - Video Codecs - Encoder column in the table under Video Codecs
Windows Media Video 9 Encoder - Output Fomats - there is no WMV1 there

Capture a frame from video using directshow filters in C++

I have taken a code from the net to capture a frame from a video file and modified to capture all frames and store it as bmp images.
HRESULT GrabVideoBitmap(PCWSTR pszVideoFile)
{
IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEventEx *pEvent = NULL;
IBaseFilter *pGrabberF = NULL;
ISampleGrabber *pGrabber = NULL;
IBaseFilter *pSourceF = NULL;
IEnumPins *pEnum = NULL;
IPin *pPin = NULL;
IBaseFilter *pNullF = NULL;
long evCode;
wchar_t temp[10];
wchar_t framename[50] = IMAGE_FILE_PATH; // L"D:\\sampleframe";
BYTE *pBuffer = NULL;
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr))
return 0;
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pGraph));
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pControl));
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pEvent));
// Create the Sample Grabber filter.
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pGrabberF));
hr = pGraph->AddFilter(pGrabberF, L"Sample Grabber");
hr = pGrabberF->QueryInterface(IID_PPV_ARGS(&pGrabber));
// Displays the metadata of the file
DisplayFileInfo((wchar_t*)pszVideoFile); // to display video information
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(mt));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
hr = pGrabber->SetMediaType(&mt);
hr = pGraph->AddSourceFilter(pszVideoFile, L"Source", &pSourceF);
hr = pSourceF->EnumPins(&pEnum);
while (S_OK == pEnum->Next(1, &pPin, NULL))
{
hr = ConnectFilters(pGraph, pPin, pGrabberF);
SafeRelease(&pPin);
if (SUCCEEDED(hr))
{
break;
}
}
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pNullF));
hr = pGraph->AddFilter(pNullF, L"Null Filter");
hr = ConnectFilters(pGraph, pGrabberF, pNullF);
hr = pGrabber->SetOneShot(TRUE);
hr = pGrabber->SetBufferSamples(TRUE);
hr = pControl->Run();
hr = pEvent->WaitForCompletion(INFINITE, &evCode);
for (int i = 0; i < 10; i++)
{
// Find the required buffer size.
long cbBuffer;
hr = pGrabber->GetCurrentBuffer(&cbBuffer, NULL);
pBuffer = (BYTE*)CoTaskMemAlloc(cbBuffer);
hr = pGrabber->GetCurrentBuffer(&cbBuffer, (long*)pBuffer);
hr = pGrabber->GetConnectedMediaType(&mt);
// Examine the format block.
if ((mt.formattype == FORMAT_VideoInfo) &&
(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
(mt.pbFormat != NULL))
{
swprintf(temp, 5, L"%d", i);
wcscat_s(framename, temp);
wcscat_s(framename, L".bmp");
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)mt.pbFormat;
hr = WriteBitmap((PCWSTR)framename, &pVih->bmiHeader,
mt.cbFormat - SIZE_PREHEADER, pBuffer, cbBuffer);
wcscpy_s(framename, IMAGE_FILE_PATH);
}
else
{
// Invalid format.
hr = VFW_E_INVALIDMEDIATYPE;
}
FreeMediaType(mt);
}
done:
CoTaskMemFree(pBuffer);
SafeRelease(&pPin);
SafeRelease(&pEnum);
SafeRelease(&pNullF);
SafeRelease(&pSourceF);
SafeRelease(&pGrabber);
SafeRelease(&pGrabberF);
SafeRelease(&pControl);
SafeRelease(&pEvent);
SafeRelease(&pGraph);
return hr;
}
The input video file has 132 frames.
But only 68 images are generated.
Also last frame of the video is captured for the last 38 images.
I think the directshow graph is running continuously and WriteBitmap() is missing frames.
How to get the control in directX to capture one frame and write it to bmp file and capture the next frame and thus capture all the frames as bmp images.
Thanks
Arun
Your approach is wrong. Currently, you set the sample grabber to one shot and after that you wait for the graph completion. This way it only works for capturing a single frame. You need to capture the frames inside the ISampleGrabberCB callback of your pGrabber. You need to implement ISampleGrabberCB interface and use ISampleGrabber::SetCallback on your pGrabber filter to point it to your implementation. After that you can capture the frames inside either SampleCB or BufferCB methods. http://www.infognition.com/blog/2013/accessing_raw_video_in_directshow.html

Direct2D Loading and Drawing Bitmap

I'm trying to draw a bitmap to the screen; the code that calls to load the bitmap from a file works, but it seems the actual drawing part is causing some program crashes.
I followed a tutorial from MSDN, but the only change I made was instead of using ID2D1RenderTarget, I'm using a ID2D1DCRenderTarget.
Here's the Load method that works:
HRESULT LoadBitmapFromFile(
ID2D1DCRenderTarget *pRenderTarget,
IWICImagingFactory *pIWICFactory,
PCWSTR uri,
UINT destinationWidth,
UINT destinationHeight,
ID2D1Bitmap **ppBitmap
)
{
IWICBitmapDecoder *pDecoder = NULL;
IWICBitmapFrameDecode *pSource = NULL;
IWICStream *pStream = NULL;
IWICFormatConverter *pConverter = NULL;
IWICBitmapScaler *pScaler = NULL;
HRESULT hr = pIWICFactory->CreateDecoderFromFilename(
uri,
NULL,
GENERIC_READ,
WICDecodeMetadataCacheOnLoad,
&pDecoder
);
if (SUCCEEDED(hr))
{
// Create the initial frame.
hr = pDecoder->GetFrame(0, &pSource);
}
if (SUCCEEDED(hr))
{
// Convert the image format to 32bppPBGRA
// (DXGI_FORMAT_B8G8R8A8_UNORM + D2D1_ALPHA_MODE_PREMULTIPLIED).
hr = pIWICFactory->CreateFormatConverter(&pConverter);
}
if (SUCCEEDED(hr))
{
// If a new width or height was specified, create an
// IWICBitmapScaler and use it to resize the image.
if (destinationWidth != 0 || destinationHeight != 0)
{
UINT originalWidth, originalHeight;
hr = pSource->GetSize(&originalWidth, &originalHeight);
if (SUCCEEDED(hr))
{
if (destinationWidth == 0)
{
FLOAT scalar = static_cast<FLOAT>(destinationHeight) / static_cast<FLOAT>(originalHeight);
destinationWidth = static_cast<UINT>(scalar * static_cast<FLOAT>(originalWidth));
}
else if (destinationHeight == 0)
{
FLOAT scalar = static_cast<FLOAT>(destinationWidth) / static_cast<FLOAT>(originalWidth);
destinationHeight = static_cast<UINT>(scalar * static_cast<FLOAT>(originalHeight));
}
hr = pIWICFactory->CreateBitmapScaler(&pScaler);
if (SUCCEEDED(hr))
{
hr = pScaler->Initialize(
pSource,
destinationWidth,
destinationHeight,
WICBitmapInterpolationModeCubic
);
}
if (SUCCEEDED(hr))
{
hr = pConverter->Initialize(
pScaler,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
NULL,
0.f,
WICBitmapPaletteTypeMedianCut
);
}
}
}
else // Don't scale the image.
{
hr = pConverter->Initialize(
pSource,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
NULL,
0.f,
WICBitmapPaletteTypeMedianCut
);
}
}
if (SUCCEEDED(hr))
{
// Create a Direct2D bitmap from the WIC bitmap.
hr = pRenderTarget->CreateBitmapFromWicBitmap(
pConverter,
NULL,
ppBitmap
);
}
if (pDecoder) pDecoder->Release();
if (pSource) pSource->Release();
if (pStream) pStream->Release();
if (pScaler) pScaler->Release();
return hr;
}
And in between the BeginDraw() and EndDraw() for the render target, I have the following code to draw the bitmap. This is the part that crashes the program when I attempt to run it:
d2dg->pRT->BeginDraw();
// Create a WIC Factory
HRESULT hr = CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWICImagingFactory,
(LPVOID*)&wicFactory);
hr = LoadBitmapFromFile(d2dg->pRT, wicFactory, L"image1.png", 400, 400, &pBitmap);
D2D1_SIZE_F size = pBitmap->GetSize();
D2D1_POINT_2F upperLeftCorner = D2D1::Point2F(100.f, 10.f);
// Draw a bitmap.
d2dg->pRT->DrawBitmap(
pBitmap,
D2D1::RectF(
upperLeftCorner.x,
upperLeftCorner.y,
upperLeftCorner.x + size.width,
upperLeftCorner.y + size.height)
);
d2dg->pRT->EndDraw();
Found the issue to this: the bitmap I wanted to draw was still NULL.
This can be fixed by deleting the ID2D1Bitmap **ppBitmap parameter from LoadBitmapFromFile and just work with the pointer to &pBitmap for any references to a bitmap within the method.
That way, pBitmap gets manipulated and will not remain NULL.

Writing IWICBtmap to file - incorrect resolution

I have the following code where I read an image (JPG for test), specify a clipping rectangle area and write the "clipped" area out to a file.
My test image is 320 x 240 pixels # 300dpi. When I read it in all indications say that it is that size but when I write it out the result image is 102 x 76 and looking a the file properties I see no H/V resolution.
Now 320/102 = 3.1372 and 300/96 = 3.125 so is there something with screen resolution vs image?
This entire subject of writing out a IWICBitmap has been a boxing match from the beginning. Why is it this hard?
Thanks a bunch
IWICImagingFactory *pImageFactory = GfxAgent::WICImagingFactory::GetInstance().GetFactory();
D2D1_SIZE_U sizeFrame = D2D1::SizeU(imageRect.Width(), imageRect.Height());
CComPtr<IWICBitmap> pWICBitmap;
hr = pImageFactory->CreateBitmap(imageRect.Width(), imageRect.Height(),
GUID_WICPixelFormat32bppPBGRA,
WICBitmapCacheOnLoad,
&pWICBitmap
);
D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties();
rtProps.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED);
rtProps.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
rtProps.usage = D2D1_RENDER_TARGET_USAGE_NONE;
// define the render target
CComPtr<ID2D1RenderTarget> pRenderTarget = 0;
hr = m_pDirect2dFactory->CreateWicBitmapRenderTarget(pWICBitmap, rtProps, &pRenderTarget);
CComPtr<ID2D1Bitmap> imageS;
hr = GfxAgent::ImageUtilities::LoadImageFromFile(pRenderTarget, m_imgPath, 0, 0, 0, &imageS, &resX, &resY);
if (hr != S_OK)
{
}
// get format of image we just read
D2D1_PIXEL_FORMAT fmt = imageS->GetPixelFormat();
CComPtr<ID2D1Bitmap> imageD;
D2D1_SIZE_U bitmapPixelSize = D2D1::SizeU(imageRect.Width(), imageRect.Height());
// create destination image of "clipped" source image
hr = pRenderTarget->CreateBitmap(bitmapPixelSize, D2D1::BitmapProperties(
D2D1::PixelFormat(fmt.format, fmt.alphaMode),
(float)resX, (float)resY), &imageD);
D2D1_POINT_2U topleft = D2D1::Point2U(0, 0);
D2D1_RECT_U srcRect = D2D1::RectU(imageRect.left, imageRect.top, imageRect.right, imageRect.bottom);
// get the "clipped" source
hr = imageD->CopyFromBitmap(&topleft, imageS, &srcRect);
if (hr != S_OK)
{
}
CComPtr<IWICBitmapEncoder> pEncoder;
CComPtr<IWICBitmapFrameEncode> pFrame;
CComPtr<IWICStream> pStream;
WICPixelFormatGUID format = GUID_WICPixelFormat32bppPBGRA;
// draw the "clipped" image into the render target (WIC image)
if (SUCCEEDED(hr)) {
pRenderTarget->BeginDraw();
pRenderTarget->Clear();
pRenderTarget->DrawBitmap(imageD);
hr = pRenderTarget->EndDraw();
}
// now proceed to write the "clipped" image to a file
if (SUCCEEDED(hr)) {
hr = pImageFactory->CreateStream(&pStream);
}
if (SUCCEEDED(hr)) {
hr = pStream->InitializeFromFilename(MultiByteToUnicode(szNewFileName).c_str(), GENERIC_WRITE);
}
if (SUCCEEDED(hr)) {
hr = pImageFactory->CreateEncoder(GUID_ContainerFormatJpeg, NULL, &pEncoder);
}
if (SUCCEEDED(hr)) {
hr = pEncoder->Initialize(pStream, WICBitmapEncoderNoCache);
}
if (SUCCEEDED(hr)) {
hr = pEncoder->CreateNewFrame(&pFrame, NULL);
}
if (SUCCEEDED(hr)) {
hr = pFrame->Initialize(NULL);
}
if (SUCCEEDED(hr)) {
hr = pFrame->SetSize((UINT)imageD->GetSize().width, (UINT)imageD->GetSize().height);
}
if (SUCCEEDED(hr)) {
hr = pFrame->SetPixelFormat(&format);
}
if (SUCCEEDED(hr)) {
hr = pFrame->WriteSource(pWICBitmap, NULL);
}
if (SUCCEEDED(hr)) {
hr = pFrame->Commit();
}
if (SUCCEEDED(hr)) {
hr = pEncoder->Commit();
}
More info
D2D1_POINT_2U topleft = D2D1::Point2U(0, 0);
D2D1_RECT_U srcRect = D2D1::RectU(imageRect.left, imageRect.top, imageRect.right, imageRect.bottom);
// get the "clipped" source
hr = imageD->CopyFromBitmap(&topleft, imageS, &srcRect);
if (hr != S_OK)
{
}
UINT wD, hD;
wD = (UINT)imageD->GetSize().width;
hD = (UINT)imageD->GetSize().height;
The imageRect is correct LTRB = 0,0,320,240
but after the copy wD = 102 and hD = 76
Why?
Here is some more info
CComPtr<ID2D1Bitmap> imageD;
D2D1_SIZE_U bitmapPixelSize = D2D1::SizeU(imageRect.Width(), imageRect.Height());
// create destination image of "clipped" source image
hr = pRenderTarget->CreateBitmap(bitmapPixelSize, D2D1::BitmapProperties(
D2D1::PixelFormat(fmt.format, fmt.alphaMode),
(float)resX, (float)resY), &imageD);
UINT wD, hD;
wD = (UINT)imageD->GetSize().width;
hD = (UINT)imageD->GetSize().height;
The bitmapPixelSize is correct 320 x 240, resX and Y are 300
format= DXGI_FORMAT_B8G8R8A8_UNORM
alphaMode D2D1_ALPHA_MODE_PREMULTIPLIED
wD = 102 and hD = 76 - Why?
Latest code
IWICImagingFactory *pImageFactory = GfxAgent::WICImagingFactory::GetInstance().GetFactory();
D2D1_SIZE_U sizeFrame = D2D1::SizeU(imageRect.Width(), imageRect.Height());
CComPtr<IWICBitmap> pWICBitmap;
hr = pImageFactory->CreateBitmap(imageRect.Width(), imageRect.Height(),
GUID_WICPixelFormat32bppPBGRA,
WICBitmapCacheOnLoad,
&pWICBitmap
);
// sanity check
UINT wicW, wicH;
pWICBitmap->GetSize(&wicW, &wicH);
D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties();
rtProps.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED);
rtProps.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
rtProps.usage = D2D1_RENDER_TARGET_USAGE_NONE;
// define the render target
CComPtr<ID2D1RenderTarget> pRenderTarget = 0;
hr = m_pDirect2dFactory->CreateWicBitmapRenderTarget(pWICBitmap, rtProps, &pRenderTarget);
CComPtr<ID2D1Bitmap> imageS;
hr = GfxAgent::ImageUtilities::LoadImageFromFile(pRenderTarget, m_imgPath, 0, 0, 0, &imageS, &resX, &resY);
if (hr != S_OK)
{
}
// set new image resolution same as source
pWICBitmap->SetResolution(resX, resY);
// get format of image we just read
D2D1_PIXEL_FORMAT fmt = imageS->GetPixelFormat();
CComPtr<ID2D1Bitmap> imageD;
D2D1_SIZE_U bitmapPixelSize = D2D1::SizeU(imageRect.Width(), imageRect.Height());
// create destination image of "clipped" source image
hr = pRenderTarget->CreateBitmap(bitmapPixelSize, D2D1::BitmapProperties(
D2D1::PixelFormat(fmt.format, fmt.alphaMode),
(float)resX, (float)resY), &imageD);
D2D1_POINT_2U topleft = D2D1::Point2U(0, 0);
D2D1_RECT_U srcRect = D2D1::RectU(imageRect.left, imageRect.top, imageRect.right, imageRect.bottom);
// get the "clipped" source
hr = imageD->CopyFromBitmap(&topleft, imageS, &srcRect);
if (hr != S_OK)
{
}
// just a sanity check (pixels NOT DIPS)
D2D1_SIZE_U sourcePixelSize = imageS->GetPixelSize();
D2D1_SIZE_U destPixelSize = imageD->GetPixelSize();
CComPtr<IWICBitmapEncoder> pEncoder;
CComPtr<IWICBitmapFrameEncode> pFrame;
CComPtr<IWICStream> pStream;
WICPixelFormatGUID format = GUID_WICPixelFormat32bppPBGRA;
// draw the "clipped" image into the render target (WIC image)
if (SUCCEEDED(hr)) {
pRenderTarget->BeginDraw();
pRenderTarget->Clear();
pRenderTarget->DrawBitmap(imageD);
hr = pRenderTarget->EndDraw();
}
// now proceed to write the "clipped" image to a file
if (SUCCEEDED(hr)) {
hr = pImageFactory->CreateStream(&pStream);
}
if (SUCCEEDED(hr)) {
hr = pStream->InitializeFromFilename(MultiByteToUnicode(szNewFileName).c_str(), GENERIC_WRITE);
}
if (SUCCEEDED(hr)) {
hr = pImageFactory->CreateEncoder(GUID_ContainerFormatJpeg, NULL, &pEncoder);
}
if (SUCCEEDED(hr)) {
hr = pEncoder->Initialize(pStream, WICBitmapEncoderNoCache);
}
if (SUCCEEDED(hr)) {
hr = pEncoder->CreateNewFrame(&pFrame, NULL);
}
if (SUCCEEDED(hr)) {
hr = pFrame->Initialize(NULL);
}
if (SUCCEEDED(hr)) {
hr = pFrame->SetSize(destPixelSize.width, destPixelSize.height);
}
if (SUCCEEDED(hr)) {
hr = pFrame->SetPixelFormat(&format);
}
if (SUCCEEDED(hr)) {
hr = pFrame->WriteSource(pWICBitmap, NULL);
}
if (SUCCEEDED(hr)) {
hr = pFrame->Commit();
}
if (SUCCEEDED(hr)) {
hr = pEncoder->Commit();
}
ID2D1Bitmap::GetSize gets you DIPs:
Returns the size, in device-independent pixels (DIPs), of the bitmap.
A DIP is 1/96 of an inch. To retrieve the size in device pixels, use the ID2D1Bitmap::GetPixelSize method.
In your case the size is 320 px * 96 dpi/px / 300 dpi = 102.4 device-independent pixels. The same along Y axis.
Knowing what the original image's resolution is when I create the render target from the WICBitmap I used the following properties
D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties();
rtProps.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED);
rtProps.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
rtProps.usage = D2D1_RENDER_TARGET_USAGE_NONE;
rtProps.dpiX = (float)m_img.GetResolutionX();
rtProps.dpiY = (float)m_img.GetResolutionY();
the key was using the resolution of the original image in the create, attempting to set them later did nothing. Roman gave me the "hint" - Thanks

Direct2D ID2D1RenderTarget::CreateBitmapFromWicBitmap fails with no error

I just try to add LoadBitmapFromFile function from sample (from SDK) to my MFC project. So my app fails during runtime on line, no errors, no warnings.
hr = pRenderTarget->CreateBitmapFromWicBitmap(pConverter, NULL, ppBitmap);
When I debugging it and goes over this line I receive empty window and debug session stops. How to find out what the error is? What could be the cause?
HRESULT DemoApp::LoadBitmapFromFile(
ID2D1RenderTarget *pRenderTarget,
IWICImagingFactory *pIWICFactory,
PCWSTR uri,
UINT destinationWidth,
UINT destinationHeight,
ID2D1Bitmap **ppBitmap)
{
HRESULT hr = S_OK;
IWICBitmapDecoder *pDecoder = NULL;
IWICBitmapFrameDecode *pSource = NULL;
IWICStream *pStream = NULL;
IWICFormatConverter *pConverter = NULL;
IWICBitmapScaler *pScaler = NULL;
hr = pIWICFactory->CreateDecoderFromFilename(
uri,
NULL,
GENERIC_READ,
WICDecodeMetadataCacheOnLoad,
&pDecoder
);
if (SUCCEEDED(hr))
{
// Create the initial frame.
hr = pDecoder->GetFrame(0, &pSource);
}
if (SUCCEEDED(hr))
{
// Convert the image format to 32bppPBGRA
// (DXGI_FORMAT_B8G8R8A8_UNORM + D2D1_ALPHA_MODE_PREMULTIPLIED).
hr = pIWICFactory->CreateFormatConverter(&pConverter);
}
if (SUCCEEDED(hr))
{
// If a new width or height was specified, create an
// IWICBitmapScaler and use it to resize the image.
if (destinationWidth != 0 || destinationHeight != 0)
{
UINT originalWidth, originalHeight;
hr = pSource->GetSize(&originalWidth, &originalHeight);
if (SUCCEEDED(hr))
{
if (destinationWidth == 0)
{
FLOAT scalar = static_cast<FLOAT>(destinationHeight) / static_cast<FLOAT>(originalHeight);
destinationWidth = static_cast<UINT>(scalar * static_cast<FLOAT>(originalWidth));
}
else if (destinationHeight == 0)
{
FLOAT scalar = static_cast<FLOAT>(destinationWidth) / static_cast<FLOAT>(originalWidth);
destinationHeight = static_cast<UINT>(scalar * static_cast<FLOAT>(originalHeight));
}
hr = pIWICFactory->CreateBitmapScaler(&pScaler);
if (SUCCEEDED(hr))
{
hr = pScaler->Initialize(
pSource,
destinationWidth,
destinationHeight,
WICBitmapInterpolationModeCubic
);
}
if (SUCCEEDED(hr))
{
hr = pConverter->Initialize(
pScaler,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
NULL,
0.f,
WICBitmapPaletteTypeMedianCut
);
}
}
}
else // Don't scale the image.
{
hr = pConverter->Initialize(
pSource,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
NULL,
0.f,
WICBitmapPaletteTypeMedianCut
);
}
}
if (SUCCEEDED(hr))
{
// Create a Direct2D bitmap from the WIC bitmap.
hr = pRenderTarget->CreateBitmapFromWicBitmap(pConverter, NULL, ppBitmap);
}
SafeRelease(&pDecoder);
SafeRelease(&pSource);
SafeRelease(&pStream);
SafeRelease(&pConverter);
SafeRelease(&pScaler);
return hr;
}