IDWriteFactory::GetSystemFontCollection won't give variants of fonts - c++

I am trying to get all the available fonts of windows through IDWriteFactory::GetSystemFontCollection in c++. I followed all the Microsoft MSDN steps. Now I can successfully get all the major fonts, but it won't provide any "variants" fonts.
Eg, I get "Arial" but no "Arial Black" and "Arial Narrow", "Yu Gothic" but no "Yu Gothic Medium" and "Yu Gothic Light". Any one knows how to get all these fonts in C++? Thank you!
Below is my program:
void wmain()
{
IDWriteFactory* pDWriteFactory = NULL;
HRESULT hr = DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast<IUnknown**>(&pDWriteFactory)
);
IDWriteFontCollection* pFontCollection = NULL;
// Get the system font collection.
if (SUCCEEDED(hr))
{
hr = pDWriteFactory->GetSystemFontCollection(&pFontCollection);
}
UINT32 familyCount = 0;
// Get the number of font families in the collection.
if (SUCCEEDED(hr))
{
familyCount = pFontCollection->GetFontFamilyCount();
}
for (UINT32 i = 0; i < familyCount; ++i)
{
IDWriteFontFamily* pFontFamily = NULL;
// Get the font family.
if (SUCCEEDED(hr))
{
hr = pFontCollection->GetFontFamily(i, &pFontFamily);
}
IDWriteLocalizedStrings* pFamilyNames = NULL;
// Get a list of localized strings for the family name.
if (SUCCEEDED(hr))
{
hr = pFontFamily->GetFamilyNames(&pFamilyNames);
}
UINT32 index = 0;
BOOL exists = false;
wchar_t localeName[LOCALE_NAME_MAX_LENGTH];
if (SUCCEEDED(hr))
{
// Get the default locale for this user.
int defaultLocaleSuccess = GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH);
// If the default locale is returned, find that locale name, otherwise use "en-us".
if (defaultLocaleSuccess)
{
hr = pFamilyNames->FindLocaleName(localeName, &index, &exists);
}
if (SUCCEEDED(hr) && !exists) // if the above find did not find a match, retry with US English
{
hr = pFamilyNames->FindLocaleName(L"en-us", &index, &exists);
}
}
// If the specified locale doesn't exist, select the first on the list.
if (!exists)
index = 0;
UINT32 length = 0;
// Get the string length.
if (SUCCEEDED(hr))
{
hr = pFamilyNames->GetStringLength(index, &length);
}
// Allocate a string big enough to hold the name.
wchar_t* name = new (std::nothrow) wchar_t[length+1];
if (name == NULL)
{
hr = E_OUTOFMEMORY;
}
// Get the family name.
if (SUCCEEDED(hr))
{
hr = pFamilyNames->GetString(index, name, length+1);
}
if (SUCCEEDED(hr))
{
// Print out the family name.
wprintf(L"%s\n", name);
}
}
}

GDI had a limitation of four variants per distinct font family. Therefore, Arial Black and Arial Narrow had to be treated as separate font families, even though in reality they're just different variants of Arial.
DirectWrite does not have that limitation, so now there is just Arial, and Arial Black and Arial Narrow come up as variants of Arial. Try it: call GetFonts() on the IDWriteFontFamily for Arial and print the GetFaceNames()s of all the resultant IDWriteFonts.
Of course, this may be counterintuitive to someone working with a program that uses GDI font enumeration, or expects that the family name of Arial Black is Arial Black instead of Arial (for instance, in a file format). I'm not sure what the solution to that problem is...

Related

How to draw text with the default UI font in DirectWrite?

The CreateTextFormat method expects a fontFamilyName parameter. How do I create an IDWriteTextFormat that uses the default UI font?
Please note, that all code here is done without any checks (too many methods here return HRESULT, would blow this example up!).
For acquiring the system wide font you should use this:
(This is from another stackoverflow question!)
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(ncm);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
HFONT hFont = CreateFontIndirect(&(ncm.lfMessageFont)); //
for paint now use this:
HDC hdc = BeginPaint(...); //Creates a device context
SelectObject(hdc, hFont);
//Your font is now set for the current device context
//do something
DeleteObject(hFont); //Don't forget to do this at the end!
A bit changed from this question!
This solution is really raw and in my opinion ugly.
Alternative solution do get the IDWriteFont (looks ugly but is fine):
//just the same as above except the hfont, instead use
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(ncm);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
IDWriteFactory *dwriteFactory_;
DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast<IUnknown**>(&dwriteFactory_)
);
IDWriteGdiInterop* gdiInterop = NULL;
dwriteFactory_->GetGdiInterop(&gdiInterop);
IDWriteFont* sys_font = nullptr;
gdiInterop->CreateFontFromLOGFONT(&ncm.lfMessageFont, &sys_font); //Now we have it!
//The text format can now be aquired like this
//We need the font family of our font
IDWriteFontFamily* family = nullptr;
sys_font->GetFontFamily(&family);
//Now we have to get the "localized" name of our family
IDWriteLocalizedStrings* font_family_name = nullptr;
family->GetFamilyNames(&font_family_name);
UINT32 index = 0;
UINT32 length = 0;
BOOL exists = false;
font_family_name->FindLocaleName(L"en-us", &index, &exists);
font_family_name->GetStringLength(index, &length);
wchar_t* name = new wchar_t[length + 1];
font_family_name->GetString(index, name, length + 1);
wprintf(L"%s\n", name);
//Some user defined stuff
DWRITE_FONT_WEIGHT font_weight = DWRITE_FONT_WEIGHT_BLACK;
DWRITE_FONT_STYLE font_style = DWRITE_FONT_STYLE_ITALIC;
DWRITE_FONT_STRETCH font_stretch = DWRITE_FONT_STRETCH_EXPANDED;
IDWriteTextFormat* text_format = nullptr;
dwriteFactory_->CreateTextFormat(name, nullptr, font_weight, font_style, font_stretch, 10.0, L"en-us", &text_format);
Even without checks, code runs on my computer without any problems and gives me the same result as the first solution (Windows 10, Font family name is Segoe UI).
Sources:
General Microsoft DirectWrite API documentation
CreateIndirectFont Documentation
How to enumerate font families, Microsoft documentation

SaveWICTextureToFile and SwapChainPanel with DirectXTK

I'm trying to use the SaveWICTextureToFile method from DirectXTK to grab a screenshot in my Windows Store app (Windows 8.1). I'm using XAML with a SwapChainPanel element. Unfortunately, the method always saves a rectangle of the screen size filled with solid color rather than the current screen content. Every time I call the SaveWICTextureToFile method it saves a different color.
This is my code (simplified):
void DirectXPage::SaveButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
critical_section::scoped_lock lock(m_main->GetCriticalSection());
auto folder = Windows::Storage::ApplicationData::Current->TemporaryFolder;
WCHAR fname[_MAX_PATH];
wcscpy_s(fname, folder->Path->Data());
wcscat_s(fname, L"\\screenshot.png");
auto context = m_deviceResources->GetD3DDeviceContext();
auto swapChain = m_deviceResources->GetSwapChain();
ID3D11Texture2D* backBuffer = nullptr;
HRESULT hr = swapChain->GetBuffer(0, __uuidof(*backBuffer), (LPVOID*)&backBuffer);
if (SUCCEEDED(hr))
{
HRESULT hr = SaveWICTextureToFile(context, backBuffer, GUID_ContainerFormatPng, fname);
DX::ThrowIfFailed(hr);
// ... mode code for FileSavePicker etc.
}
}
What am I doing wrong?
Thanks,
Leszek

Can not reuse brush to draw both text and rectangle

INTRODUCTION AND RELEVANT INFORMATION:
I am trying to learn to print with XPS Document API.
For a simple start, I have decided to draw a rectangle, and some text below it.
After going through the official examples I was able to achieve my goal.
PROBLEM:
Basically, I have concatenated 2 code examples provided from the above link. Now I wanted to polish the code, mainly to use single brush to draw both rectangle and the text.
After rewriting the code, I get the following error:
First-chance exception at 0x7555D3CF in XPS printing.exe: Microsoft C++ exception: SplException::THResultException at memory location 0x002CEF9C.
If there is a handler for this exception, the program may be safely continued.
SSCCEE:
Below is the function I rewrote. I have marked the crashing point with the appropriate comments.
void XPS_TEST()
{
IXpsOMObjectFactory *xpsFactory;
HRESULT hr = S_OK;
// Init COM for this thread if it hasn't
// been initialized, yet.
hr = CoInitializeEx(0, COINIT_MULTITHREADED);
hr = CoCreateInstance(
__uuidof(XpsOMObjectFactory),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IXpsOMObjectFactory),
reinterpret_cast<LPVOID*>(&xpsFactory));
if (SUCCEEDED(hr))
{
// Declare the variables used in this section.
IOpcPartUri *opcPartUri = NULL;
IXpsOMPackage *xpsPackage = NULL;
IXpsOMDocumentSequence *xpsFDS = NULL;
IXpsOMDocumentCollection *fixedDocuments = NULL;
IXpsOMDocument *xpsFD = NULL;
IXpsOMPage *xpsPage = NULL;
IXpsOMPageReferenceCollection *pageRefs = NULL;
IXpsOMPageReference *xpsPageRef = NULL;
// test size of the document
XPS_SIZE pageSize = { 200, 200 };
// Create the package.
hr = xpsFactory->CreatePackage(&xpsPackage);
// Create the URI for the fixed document sequence part and then
// create the fixed document sequence
hr = xpsFactory->CreatePartUri(
L"/FixedDocumentSequence.fdseq", &opcPartUri);
hr = xpsFactory->CreateDocumentSequence(opcPartUri, &xpsFDS);
// Release this URI to reuse the interface pointer.
if (NULL != opcPartUri) { opcPartUri->Release(); opcPartUri = NULL; }
// Create the URI for the document part and then create the document.
hr = xpsFactory->CreatePartUri(
L"/Documents/1/FixedDocument.fdoc", &opcPartUri);
hr = xpsFactory->CreateDocument(opcPartUri, &xpsFD);
// Release this URI to reuse the interface pointer.
if (NULL != opcPartUri) { opcPartUri->Release(); opcPartUri = NULL; }
// Create a blank page.
hr = xpsFactory->CreatePartUri(
L"/Documents/1/Pages/1.fpage", &opcPartUri);
hr = xpsFactory->CreatePage(
&pageSize, // Page size
L"en-US", // Page language
opcPartUri, // Page part name
&xpsPage);
// Release this URI to reuse the interface pointer.
if (NULL != opcPartUri) { opcPartUri->Release(); opcPartUri = NULL; }
// Create a page reference for the page.
hr = xpsFactory->CreatePageReference(&pageSize, &xpsPageRef);
// Add the fixed document sequence to the package.
hr = xpsPackage->SetDocumentSequence(xpsFDS);
// Get the document collection of the fixed document sequence
// and then add the document to the collection.
hr = xpsFDS->GetDocuments(&fixedDocuments);
hr = fixedDocuments->Append(xpsFD);
// Get the page reference collection from the document
// and add the page reference and blank page.
hr = xpsFD->GetPageReferences(&pageRefs);
hr = pageRefs->Append(xpsPageRef);
hr = xpsPageRef->SetPage(xpsPage);
//======================== draw rectangle ====================//
XPS_COLOR xpsColor;
IXpsOMSolidColorBrush *xpsFillBrush = NULL;
// the brush I want to reuse !!
IXpsOMSolidColorBrush *xpsStrokeBrush = NULL;
// Set the fill brush color to RED.
xpsColor.colorType = XPS_COLOR_TYPE_SRGB;
xpsColor.value.sRGB.alpha = 0xFF;
xpsColor.value.sRGB.red = 0xFF;
xpsColor.value.sRGB.green = 0x00;
xpsColor.value.sRGB.blue = 0x00;
// Use the object factory to create the brush.
hr = xpsFactory->CreateSolidColorBrush(
&xpsColor,
NULL, // color profile resource
&xpsFillBrush);
// The color profile resource parameter is NULL because
// this color type does not use a color profile resource.
// Set the stroke brush color to BLACK.
xpsColor.colorType = XPS_COLOR_TYPE_SRGB;
xpsColor.value.sRGB.alpha = 0xFF;
xpsColor.value.sRGB.red = 0x00;
xpsColor.value.sRGB.green = 0x00;
xpsColor.value.sRGB.blue = 0x00;
// Use the object factory to create the brush.
hr = xpsFactory->CreateSolidColorBrush(
&xpsColor,
NULL, // This color type does not use a color profile resource.
&xpsStrokeBrush);
// test rectangle
XPS_RECT rect = { 0, 0, 200, 20 };
IXpsOMGeometryFigure *rectFigure;
IXpsOMGeometry *imageRectGeometry;
IXpsOMGeometryFigureCollection *geomFigureCollection;
// Define the start point and create an empty figure.
XPS_POINT origin = { rect.x, rect.y };
hr = xpsFactory->CreateGeometryFigure(&origin, &rectFigure);
// Define the segments of the geometry figure.
// First, define the type of each segment.
XPS_SEGMENT_TYPE segmentTypes[3] =
{
XPS_SEGMENT_TYPE_LINE, // each segment is a straight line
XPS_SEGMENT_TYPE_LINE,
XPS_SEGMENT_TYPE_LINE
};
// Define the x and y coordinates of each corner of the figure
// the start point has already been defined so only the
// remaining three corners need to be defined.
FLOAT segmentData[6] =
{
rect.x, (rect.y + rect.height),
(rect.x + rect.width), (rect.y + rect.height),
(rect.x + rect.width), rect.y
};
// Describe if the segments are stroked (that is if the segment lines
// should be drawn as a line).
BOOL segmentStrokes[3] =
{
TRUE, TRUE, TRUE // Yes, draw each of the segment lines.
};
// Add the segment data to the figure.
hr = rectFigure->SetSegments(
3,
6,
segmentTypes,
segmentData,
segmentStrokes);
// Set the closed and filled properties of the figure.
hr = rectFigure->SetIsClosed(TRUE);
hr = rectFigure->SetIsFilled(TRUE);
// Create the geometry object.
hr = xpsFactory->CreateGeometry(&imageRectGeometry);
// Get a pointer to the figure collection interface of the geometry...
hr = imageRectGeometry->GetFigures(&geomFigureCollection);
// ...and then add the figure created above to this geometry.
hr = geomFigureCollection->Append(rectFigure);
// If not needed for anything else, release the rectangle figure.
rectFigure->Release();
// when done adding figures, release the figure collection.
geomFigureCollection->Release();
IXpsOMPath *rectPath = NULL;
IXpsOMVisualCollection *pageVisuals = NULL;
// Create the new path object.
hr = xpsFactory->CreatePath(&rectPath);
// Add the geometry to the path.
// imageRectGeometry is initialized outside of this example.
hr = rectPath->SetGeometryLocal(imageRectGeometry);
// Set the short description of the path to provide
// a textual description of the object for accessibility.
hr = rectPath->SetAccessibilityShortDescription(L"Red Rectangle");
// Set the fill and stroke brushes to use the brushes
// created in the first section.
hr = rectPath->SetFillBrushLocal(xpsFillBrush);
hr = rectPath->SetStrokeBrushLocal(xpsStrokeBrush);
// Get the visual collection of this page and add this path to it.
hr = xpsPage->GetVisuals(&pageVisuals);
hr = pageVisuals->Append(rectPath);
// If not needed for anything else, release the rectangle path.
rectPath->Release();
// When finished with the brushes, release the interface pointers.
if (NULL != xpsFillBrush) xpsFillBrush->Release();
//******************** I have commented out below code, ****************//
//******************** because I plan to use the brush to draw text ****//
//if (NULL != xpsStrokeBrush) xpsStrokeBrush->Release();
// When done with the geometry interface, release it.
imageRectGeometry->Release();
//========================= draw text =====================//
GUID fontNameGuid;
WCHAR guidString[128] = { 0 };
WCHAR uriString[256] = { 0 };
IStream *fontStream = NULL;
IOpcPartUri *fontUri = NULL;
IXpsOMFontResource *fontResource = NULL;
// Create font stream.
hr = xpsFactory->CreateReadOnlyStreamOnFile(
// I have hardcoded Arial here, just for testing
L"C:\\Windows\\Fonts\\Arial.ttf",
&fontStream);
// Create new obfuscated part name for this resource using a GUID.
hr = CoCreateGuid(&fontNameGuid);
hr = StringFromGUID2(
fontNameGuid,
guidString,
ARRAYSIZE(guidString));
// Create a URI string for this font resource that will place
// the font part in the /Resources/Fonts folder of the package.
wcscpy_s(uriString, ARRAYSIZE(uriString), L"/Resources/Fonts/");
// Create the part name using the GUID string as the name and
// ".odttf" as the extension GUID string start and ends with
// curly braces so they are removed.
wcsncat_s(uriString, ARRAYSIZE(uriString),
guidString + 1, wcslen(guidString) - 2);
wcscat_s(uriString, ARRAYSIZE(uriString), L".odttf");
// Create the font URI interface.
hr = xpsFactory->CreatePartUri(
uriString,
&fontUri);
// Create the font resource.
hr = xpsFactory->CreateFontResource(
fontStream,
XPS_FONT_EMBEDDING_OBFUSCATED,
fontUri,
FALSE, // isObfSourceStream
&fontResource);
if (NULL != fontUri) fontUri->Release();
LPCWSTR unicodeString = L"Test string";
// move test string below our rectangle
origin.y += 30.0f;
FLOAT fontEmSize = 7.56f;
IXpsOMGlyphsEditor *glyphsEditor = NULL;
IXpsOMGlyphs *xpsGlyphs = NULL;
// Create a new Glyphs object and set its properties.
hr = xpsFactory->CreateGlyphs(fontResource, &xpsGlyphs);
hr = xpsGlyphs->SetOrigin(&origin);
hr = xpsGlyphs->SetFontRenderingEmSize(fontEmSize);
//*************** I GET A CRASH BELOW !!!! ***************//
hr = xpsGlyphs->SetFillBrushLocal(xpsStrokeBrush); // <<---
// Some properties are inter-dependent so they
// must be changed by using a GlyphsEditor.
hr = xpsGlyphs->GetGlyphsEditor(&glyphsEditor);
hr = glyphsEditor->SetUnicodeString(unicodeString);
hr = glyphsEditor->ApplyEdits();
// Add the new Glyphs object to the page
hr = pageVisuals->Append(xpsGlyphs);
// Release interface pointers.
if (NULL != xpsGlyphs) xpsGlyphs->Release();
if (NULL != glyphsEditor) glyphsEditor->Release();
if (NULL != pageVisuals) pageVisuals->Release();
//******************** Releasing the brush here *******//
if (NULL != xpsStrokeBrush) xpsStrokeBrush->Release();
//========================= write to file ====================//
hr = xpsPackage->WriteToFile(
L"C:\\Users\\Smiljkovic\\Desktop\\xpsTest.xps",
NULL, // LPSECURITY_ATTRIBUTES
FILE_ATTRIBUTE_NORMAL,
FALSE); // Optimize Markup Size
//========================== cleanup ==================//
// Release interface pointer
if (NULL != xpsPage) xpsPage->Release();
if (NULL != pageRefs) pageRefs->Release();
if (NULL != fixedDocuments) fixedDocuments->Release();
if (NULL != xpsPageRef) xpsPageRef->Release();
if (NULL != xpsFD) xpsFD->Release();
if (NULL != xpsFDS) xpsFDS->Release();
if (NULL != xpsPackage) xpsPackage->Release();
xpsFactory->Release();
}
// Uninitialize COM when finished
CoUninitialize();
}
QUESTION:
How can I use the same brush ( xpsStrokeBrush from the above example ) for drawing both the text and the rectangle outline?
per SetStrokeBrushLocal documentation:
After you call SetStrokeBrushLocal, the stroke brush lookup key is released and GetStrokeBrushLookup returns a NULL pointer in the lookup parameter.
You could use Clone on the brush before using it.
But, if you plan on re-using brushes, then use the CreateDictionary, SetDictionaryLocal and then Append your brush there; which will let you use SetFillBrushLookup.

Embedding Window Media Player in mfc

I am embedding a window media player activex control in a dialog based mfc application. Now all play/pause button is getting disabled. I want to enable them.
As I have put put_uiMode(_T("mini"));
and when writingthese lines
hr = m_spWMPPlayer->put_fullScreen(VARIANT_TRUE)
hr = m_spWMPPlayer->get_fullScreen(&b); // returned b = VARIANT_FALSE
value of b is coming FALSE.
What could be the error? Any one knows this?
Thank You
BOOL CLuminPlayerDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
CComPtr<IWMPPlayer> m_spWMPPlayer;
CComPtr<IWMPRemoteMediaServices> m_spServices;
CComPtr<IWMPControls> m_spControls;
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
HRESULT hr = NULL;
if(SUCCEEDED(hr))
{
hr = CoCreateInstance(__uuidof(WindowsMediaPlayer), 0, CLSCTX_INPROC_SERVER, IID_IOleObject, (void**)&m_spServices);
if(SUCCEEDED(hr))
{
BSTR str = NULL;
VARIANT_BOOL b ;
hr = m_spServices->QueryInterface(__uuidof(IWMPPlayer), (void**)&m_spWMPPlayer);
if(SUCCEEDED(hr))
{
hr = m_spWMPPlayer->get_enabled(&b); // returned b= VARIANT_TRUE
hr = m_spWMPPlayer->get_enableContextMenu(&b); //returned b = VARIANT_TRUE
hr = m_spWMPPlayer->get_uiMode(&str); // returned str = L"full"
hr = m_spWMPPlayer->put_uiMode(_T("mini")); //hr = S_OK
hr = m_spWMPPlayer->get_uiMode(&str); // str = L"mini"
hr = m_spWMPPlayer->put_fullScreen(VARIANT_TRUE);
hr = m_spWMPPlayer->get_fullScreen(&b); // returned b = VARIANT_FALSE
hr = m_spWMPPlayer->put_URL(_T("C:\\Visual Studio 2012\\Projects\\Lumin-Player\\Debug\\abc.mp4")); //returned hr = S_OK
hr = m_spServices->QueryInterface(__uuidof(IWMPControls), (void**)&m_spControls); // returned hr = S_OK
if(SUCCEEDED(hr))
{
hr = m_spControls->play(); //returned hr = S_OK
}
}
}
}
return TRUE; // return TRUE unless you set the focus to a control
}
MSDN says:
For full-screen mode to work properly when embedding the Windows Media Player control, the video display area must have a height and width of at least one pixel. If the BSTR specified in IWMPPlayer::put_uiMode is set to "mini" or "full", the height of the control itself must be 65 pixels or greater to accommodate the video display area in addition to the user interface.
This assumes that the player is already properly initialized as ActiveX control. In your code, you simply create a COM object without doing any ActiveX Control initialization. Presumably the player detects this and reports error.
Your hr in respective put_fullScreen call should have 0xC00D0FD2 NS_E_WMPOCX_NO_ACTIVE_CORE "The requested method or property is not available because the Windows Media Player ActiveX control has not been properly activated." to indicate the problem.

C++ Direct2D ID2D1HwndRenderTarget's parent IUnknown class' _vfptr variable becomes null

I have a Direct2D app that I am making, and I am writing a Direct2D library that makes using Direct2D easier for me as well. I'll post the exact problematic code if I need to, but my main issue is that I have an ID2D1HwndRenderTarget in the definition of one class, I extend that class with another class, in the child class I have a method that calls a method of the parent class that initializes the Render Target, and then in turn calls the load method of the child class. However, as soon as the program reaches the load content method of the child class, the __vfptr variable (I don't have a clue what that is) in the IUnknown portion of the ID2D1HwndRenderTarget is now null. The only reason I figured this out is in some other code I was getting an access violation error when using the render target to create a ID2D1Bitmap from an IWicBitmapSource. I don't understand how this happens because after initializing the Render Target, that _vfptr variable becomes null as soon as the method with the initialization code returns. Can anyone explain why this may be happening? My relevant code is below.
This code is called once to create the hwnd render target and the offscreen render target. This is in a dll project.
GameBase.cpp
HRESULT GameBase::Initialize(HINSTANCE hInst, HWND winHandle, struct DX2DInitOptions options)
{
this->mainRenderTarget = NULL;
this->offscreenRendTarget = NULL;
this->factory = NULL;
HRESULT result;
D2D1_FACTORY_OPTIONS factOptions;
D2D1_FACTORY_TYPE factType;
if(options.enableDebugging)
factOptions.debugLevel = D2D1_DEBUG_LEVEL::D2D1_DEBUG_LEVEL_ERROR;
else
factOptions.debugLevel = D2D1_DEBUG_LEVEL::D2D1_DEBUG_LEVEL_NONE;
if(options.singleThreadedApp)
factType = D2D1_FACTORY_TYPE_SINGLE_THREADED;
else
factType = D2D1_FACTORY_TYPE_MULTI_THREADED;
result = D2D1CreateFactory(factType, factOptions, &this->factory);
if(FAILED(result))
{
OutputDebugString(L"Failed to create a Direct 2D Factory!");
return result;
}
this->instance = hInst;
this->hwnd = winHandle;
D2D1_SIZE_U size = D2D1::SizeU(options.winWidth, options.winHeight);
this->width = options.winWidth;
this->height = options.winHeight;
result = factory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(winHandle, size), &this->mainRenderTarget);
if(FAILED(result))
{
OutputDebugString(L"Failed to create a render target to draw to the window with!");
return result;
}
result = this->mainRenderTarget->CreateCompatibleRenderTarget(&this->offscreenRendTarget);
if(FAILED(result))
{
OutputDebugString(L"Failed to create an offscreen render target from the main render target.");
return result;
}
return LoadContent();
}
After the call to LoadContent, at no point in time do I change the value of the mainRenderTarget.
DX2DImage.cpp
HRESULT DX2DImageLoader::LoadFromResource(LPCWSTR resourceName, LPCWSTR resourceType, HMODULE progModule, DX2DImage* image)
{
if(!this->isInit)
{
OutputDebugStringA("You must call InitializeImageLoader before using this image loader!");
return E_FAIL;
}
IWICBitmapDecoder *decoder = NULL;
IWICBitmapFrameDecode *source = NULL;
IWICStream *stream = NULL;
IWICFormatConverter *converter = NULL;
HRSRC imageResHandle = NULL;
HGLOBAL imageResDataHandle = NULL;
void *imageFile = NULL;
DWORD imageFileSize = 0;
HRESULT result;
//Find the image.
imageResHandle = FindResource(progModule, resourceName, resourceType);
if(!imageResHandle)
{
OutputDebugStringA("Failed to get a handle to the resource!");
return E_FAIL;
}
//Load the data handle of the image.
imageResDataHandle = LoadResource(progModule, imageResHandle);
if(!imageResDataHandle)
{
OutputDebugStringA("Failed to load the image from the module!");
return E_FAIL;
}
//Lock and retrieve the image.
imageFile = LockResource(imageResDataHandle);
if(!imageFile)
{
OutputDebugStringA("Failed to lock the image in the module!");
return E_FAIL;
}
//Get the size of the image.
imageFileSize = SizeofResource(progModule, imageResHandle);
if(!imageFileSize)
{
OutputDebugStringA("Failed to retrieve the size of the image in the module!");
return E_FAIL;
}
//Create a stream that will read the image data.
result = this->factory->CreateStream(&stream);
if(FAILED(result))
{
OutputDebugStringA("Failed to create an IWICStream!");
return result;
}
//Open a stream to the image.
result = stream->InitializeFromMemory(reinterpret_cast<BYTE*>(imageFile), imageFileSize);
if(FAILED(result))
{
OutputDebugStringA("Failed to initialize the stream!");
return result;
}
//Create a decoder from the stream
result = this->factory->CreateDecoderFromStream(stream, NULL, WICDecodeMetadataCacheOnDemand, &decoder);
if(FAILED(result))
{
OutputDebugStringA("Failed to create a decoder from the stream!");
return result;
}
//Get the first frame from the image.
result = decoder->GetFrame(0, &source);
if(FAILED(result))
{
OutputDebugStringA("Failed to get the first frame from the decoder!");
return result;
}
//Create a format converter to convert image to 32bppPBGRA
result = this->factory->CreateFormatConverter(&converter);
if(FAILED(result))
{
OutputDebugStringA("Failed to create a format converter!");
return result;
}
//Convert the image to the new format.
result = converter->Initialize(source, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0.0f, WICBitmapPaletteTypeMedianCut);
if(FAILED(result))
{
OutputDebugStringA("Failed to convert the image to the correct format!");
return result;
}
//Create the Direct2D Bitmap from the Wic Bitmap.
result = this->renderTarget->CreateBitmapFromWicBitmap(converter, NULL, &image->bitmap);
if(FAILED(result))
{
OutputDebugStringA("Failed to create a Direct 2D Bitmap from a WIC Bitmap!");
return result;
}
image->width = static_cast<UINT>(image->bitmap->GetSize().width);
image->height = static_cast<UINT>(image->bitmap->GetSize().height);
SafeRelease(&source);
SafeRelease(&converter);
SafeRelease(&decoder);
SafeRelease(&stream);
return S_OK;
}
The Access Violation exception occurs on the line
result = this->renderTarget->CreateBitmapFromWicBitmap(converter, NULL, &image->bitmap);
where image->bitmap is a currently NULL (Like it's supposed to be) ID2D1Bitmap.
Here, the renderTarget variable is the same mainRenderTarget variable from GameBase.cpp above. When I debug the line, all the parents of the RenderTarget are not null, however once I get to the IUnknown interface under it all, the _vfptr thing is null. This is not the case with the converter variable, this variable, or image variable.
I don't have enough code to debug your code, but from what I see I suspect the call to converter->Initialize(...) be invalid since MSDN say:
If you do not have a predefined palette, you must first create one. Use
InitializeFromBitmap to create the palette object, then pass it in along
with your other parameters.
dither, pIPalette, alphaThresholdPercent, and paletteTranslate are used to
mitigate color loss when converting to a reduced bit-depth format. For
conversions that do not need these settings, the following parameters values
should be used: dither set to WICBitmapDitherTypeNone, pIPalette set to NULL,
alphaThresholdPercent set to 0.0f, and paletteTranslate set to
WICBitmapPaletteTypeCustom.
And in your code you do not provide a valid pallete(you used NULL) and your paletteTranslate is not WICBitmapPaletteTypeCustom