Displaying text with DirectX 9 crashes after several calls - c++

I want to display text with DirectX 9 but after thousands of calls the application crashes. In the future the text should be changeable, but in the example below, the text is everytime the same. The stack trace gives not much information, the crash occurs in some other part of the program due an access violation.
The header:
(...)
class QD3DWidget;
class D3DText : public D3DPrimitive
{
public:
D3DText(IDirect3DDevice9 * device);
virtual ~D3DText();
virtual bool CreateBuffers();
virtual void ReleaseBuffers();
virtual void Draw();
protected:
IDirect3DDevice9 * Device();
private:
struct TextData {
Vector2 pos;
std::string text;
};
QD3DWidget * m_pQD3DWidget;
IDirect3DDevice9 * m_pD3DDevice;
ID3DXFont *font;
};
#endif
And the implementation:
(...)
D3DText::D3DText(IDirect3DDevice9 * device)
: D3DPrimitive(device),
m_pQD3DWidget(0),
m_pD3DDevice(device),
font(0)
{
}
D3DText::~D3DText()
{
ReleaseBuffers();
}
void D3DText::Draw()
{
if (font)
{
RECT fRectangle;
SetRect(&fRectangle, 0, 0, 1000, 440);
std::string message = "This is some generic message to\ndisplay on the screen";
font->DrawTextA(NULL, message.c_str(), -1, &fRectangle, DT_LEFT, D3DCOLOR_XRGB(0, 0, 0));
}
}
void D3DText::ReleaseBuffers()
{
if (font) {
font->Release(); font = 0;
}
}
bool D3DText::CreateBuffers()
{
font = 0;
HRESULT hr = D3DXCreateFont(m_pD3DDevice, 22, 0, FW_NORMAL, 1, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
ANTIALIASED_QUALITY, FF_DONTCARE, TEXT("Arial"), &font);
if (!SUCCEEDED(hr))
return false;
return true;
}
IDirect3DDevice9 * D3DText::Device()
{
return m_pD3DDevice;
}

Related

Load bitmap asynchronously Direct2D C++

I have a class that represents an gui-element, that has method to set an image on it's background:
class Element
{
public:
ID2D1Bitmap *image;
ID2D1DeviceContext *target;
int x, y, width, height;
Element(ID2D1DeviceContext *target, int x, int y, int width, int height)
{
image = nullptr;
this->target = target;
this->x = x; this->y = y; this->width = width; this->height = height;
}
void Render()
{
if(image)
target->DrawBitmap(image, D2D1::RectF(x, y, x + width, y + height));
}
void setBackgroundImage(const wchar_t* path)
{
if (!path || wcslen(path) == 0)
return;
IWICBitmapFrameDecode* d2dBmpSrc = nullptr;
IWICBitmapDecoder* d2dDecoder = nullptr;
d2dWICFactory->CreateDecoderFromFilename(path, NULL, GENERIC_READ,
WICDecodeMetadataCacheOnLoad, &d2dDecoder);
if (d2dDecoder)
{
d2dDecoder->GetFrame(0, &d2dBmpSrc);
if (d2dBmpSrc)
{
d2dWICFactory->CreateFormatConverter(&d2dConverter2);
d2dConverter2->Initialize(d2dBmpSrc, GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteTypeMedianCut);
ID2D1Bitmap *temp = nullptr;
tar->CreateBitmapFromWicBitmap(d2dConverter2, NULL, &temp);
if (temp)
{
D2D1_SIZE_F si = temp->GetSize();
tar->CreateBitmap(D2D1::SizeU(si.width, si.height), 0, 0, D2D1::BitmapProperties(
D2D1::PixelFormat(DXGI_FORMAT::DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE::D2D1_ALPHA_MODE_PREMULTIPLIED)
), &image);
image->CopyFromBitmap(0, temp, 0);
SafeRelease(&temp);
}
}
}
SafeRelease(&d2dDecoder);
SafeRelease(&d2dBmpSrc);
SafeRelease(&d2dConverter2);
}
~Element(){SafeRelease(&image);}
}*object[100] = {NULL};
int main()
{
ID2D1Factory *factory = nullptr;
D2D1CreateFactory(D2D1_FACTORY_TYPE::D2D1_FACTORY_TYPE_MULTI_THREADED, &factory);
ID2D1DeviceContext *target = ObtainDeviceContext(factory);
object[0] = new Element(target, 0, 0, 100, 100);
object[0]->setBackgroundImage(L"img.png");
for(;;) // But in reality here also is Windows message loop with Dispatch message
{
target->BeginDraw();
target->Clear(D2D1::ColorF(1, 1, 1));
for(int i = 0; i < 100 && object[i]; i++)
object[i]->Render();
target->EndDraw();
}
return 0;
}
All works fine, but the problem, is that loading an image is obviously hangs the program.
Unfortunately, my asynchronous c++ skills are almost empty. I tried to just change method to this:
void Element::setBackgroundImage(const wchar_t*path)
{
thread th(LoadImage(this, path)); th.detach();
}
And jsut bring all code from the method to global function, with additional first argument - LoadImage(Object*,const wchar_t*);
Unfortunately, it immediately crashes. Then I created global variable mutex mu and placed mu.lock() and mu.unlock() as first and last line in LoadImage correspond. Still crashes.
Maybe I also need to lock in Render, and probably on destructor? By the way, what will happen if destructor will try to release image variable at time when it is locked by another thread? It will not be free, and hence memory leak?
Can someone please explain at least general conception of using asynchronous c++ programming for my case? Not thread::join, I need the main thread be going.
Also I would appreciate if You explain how to properly make render loop running in asynchronous thread.
First off, your code is far from a minimal reproducible example. But then again, I've never touched Direct2D before, and I still managed to get it working.
Anyway, it doesn't matter what you do, you'll have to load the image before you're able to render it. Since you used browsers as an example in the comments, what they do is indeed fetch and load files on separate threads, but before they're loaded and in memory, they're rendering placeholders or nothing at all.
Loading the image in a separate thread and locking a mutex in the render function is the same as not using any threads at all (functionally, at least. The main thread will still block in render while the image is being loaded). Really, what you tried before, with this:
void Element::setBackgroundImage(const wchar_t*path)
{
thread th(LoadImage(this, path)); th.detach();
}
should work, (in my testing even with a D2D1_FACTORY_TYPE_SINGLE_THREADED factory), since you're not rendering the element if image == nullptr, which acts a sort of lock. Using a proper bool loaded state variable would be even better, but it still works.
You just misused the std::thread constructor syntax, since you're handing it the return value of LoadImage with the arguments this and path, which is almost certainly not a function pointer or lambda, given that your program is crashing (In simple words: You're calling the function instead of passing it in as an argument).
Creating a std::thread calling Element::setBackgroundImage properly:
class Element
{
public:
...
void asyncSetBackgroundImage(const wchar_t* path)
{
std::thread thread(
&Element::setBackgroundImage,
this,
path
);
thread.detach();
}
...
};
Also I would appreciate if You explain how to properly make render loop running in asynchronous thread.
It's really as simple as handling the Win32 window message loop on the main thread, while doing the rendering on a separate thread.
bool render_state = true;
std::thread render_thread(
[&]()
{
while (render_state)
{
target->BeginDraw();
target->Clear(D2D1::ColorF(1, 1, 1));
for(int i = 0; i < 100 && object[i]; i++)
object[i]->Render();
target->EndDraw();
}
}
);
render_thread.detach();
while (window.win32_loop_iteration())
{ }
render_state = false;
This is where you might want to use a mutex, to synchronize the start and stop of the loop, but aside from that, this is enough.
Also, a (mostly) minimally reproducible example; all that's missing is the WINDOW::Window class, which can create a Win32 window among other things:
#include <d2d1.h>
#include <d2d1_1.h>
#include <wincodec.h>
#include "window/window.hpp"
#include <thread>
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
IWICImagingFactory* d2dWICFactory = nullptr;
class Element
{
public:
ID2D1Bitmap *image;
ID2D1DeviceContext *target;
int x, y, width, height;
Element(ID2D1DeviceContext *target, int x, int y, int width, int height)
{
image = nullptr;
this->target = target;
this->x = x; this->y = y; this->width = width; this->height = height;
}
void Render()
{
if(image)
target->DrawBitmap(image, D2D1::RectF(x, y, x + width, y + height));
}
void setBackgroundImage(const wchar_t* path)
{
if (!path || wcslen(path) == 0)
return;
IWICBitmapFrameDecode* d2dBmpSrc = nullptr;
IWICBitmapDecoder* d2dDecoder = nullptr;
IWICFormatConverter* d2dConverter2 = nullptr;
d2dWICFactory->CreateDecoderFromFilename(path, NULL, GENERIC_READ,
WICDecodeMetadataCacheOnLoad, &d2dDecoder);
if (d2dDecoder)
{
d2dDecoder->GetFrame(0, &d2dBmpSrc);
if (d2dBmpSrc)
{
d2dWICFactory->CreateFormatConverter(&d2dConverter2);
d2dConverter2->Initialize(d2dBmpSrc, GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteTypeMedianCut);
ID2D1Bitmap *temp = nullptr;
target->CreateBitmapFromWicBitmap(d2dConverter2, NULL, &temp);
if (temp)
{
D2D1_SIZE_F si = temp->GetSize();
target->CreateBitmap(D2D1::SizeU(si.width, si.height), 0, 0, D2D1::BitmapProperties(
D2D1::PixelFormat(DXGI_FORMAT::DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE::D2D1_ALPHA_MODE_PREMULTIPLIED)
), &image);
image->CopyFromBitmap(0, temp, 0);
SafeRelease(&temp);
}
}
}
SafeRelease(&d2dDecoder);
SafeRelease(&d2dBmpSrc);
SafeRelease(&d2dConverter2);
}
void asyncSetBackgroundImage(const wchar_t* path)
{
std::thread thread(
&Element::setBackgroundImage,
this,
path
);
thread.detach();
}
~Element()
{
SafeRelease(&image);
}
};
Element* object[100] = { nullptr };
int main(
int argument_count,
char** arguments
)
{
CoInitialize(
NULL
);
ID2D1Factory1 *factory = nullptr;
D2D1CreateFactory(D2D1_FACTORY_TYPE::D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory);
ID2D1HwndRenderTarget* render_target = nullptr;
D2D1_RENDER_TARGET_PROPERTIES properties = {};
properties.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
properties.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT::DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE::D2D1_ALPHA_MODE_IGNORE);
properties.dpiX = 0;
properties.dpiY = 0;
properties.usage = D2D1_RENDER_TARGET_USAGE::D2D1_RENDER_TARGET_USAGE_NONE;
properties.minLevel = D2D1_FEATURE_LEVEL::D2D1_FEATURE_LEVEL_DEFAULT;
WINDOW::Window window = {};
window.create();
window.show();
D2D1_HWND_RENDER_TARGET_PROPERTIES hwnd_properties = {};
hwnd_properties.hwnd = window.win32_handle_get();
hwnd_properties.pixelSize.width = window.size_x_get();
hwnd_properties.pixelSize.height = window.size_y_get();
hwnd_properties.presentOptions = D2D1_PRESENT_OPTIONS::D2D1_PRESENT_OPTIONS_NONE;
factory->CreateHwndRenderTarget(
&properties,
&hwnd_properties,
&render_target
);
ID2D1DeviceContext *target = reinterpret_cast<ID2D1DeviceContext*>(render_target);
CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IWICImagingFactory),
reinterpret_cast<LPVOID*>(&d2dWICFactory)
);
object[0] = new Element(target, 0, 0, 100, 100);
object[0]->asyncSetBackgroundImage(L"img.png");
while (window.win32_loop_iteration())
{
target->BeginDraw();
target->Clear(D2D1::ColorF(1, 1, 1));
for(int i = 0; i < 100 && object[i]; i++)
object[i]->Render();
target->EndDraw();
}
return 0;
}

How to reset my DirectX 9 device?

Recently, I've found a bug in my 3D application that uses DirectX 9. The bug is that everytime I alt-tab or ctrl-alt-delete, the program freezes or seems to do nothing. I've looked around and found out that I need to reset my device by using IDirect3DDevice9::TestCooperativeLevel and IDirect3DDevice9::Reset. But what I'm struggling on at the moment is that (as far as I know) before I Reset the device, I need to release the resouce in D3DPOOL_MANAGE before I reset the device and reallocate the resource back to it after the Reset? How do I do this? Below is the code I've got for the reset of my device (in the IsDeviceLost() function).
DirectX.h
#pragma once
#pragma comment(lib, "d3d9.lib")
#include<d3d9.h>
#include<d3dx9math.h>
#include"Window.h"
#include"Vertex.h"
#include"Shader.h"
#define VERTEXFORMAT (D3DFVF_XYZ | D3DFVF_TEX1) //Flags for the Flexible Vertex Format (FVF)
class Shader;
class DirectX
{
public:
DirectX(std::string windowTitle, int x, int y, unsigned int windowWidth, unsigned int windowHeight, bool fullscreen);
virtual ~DirectX();
static LPDIRECT3D9 direct3D;
static LPDIRECT3DDEVICE9 device;
static IDirect3DVertexDeclaration9* vertexDec;
static ID3DXEffect* currentShaderEffect;
void CheckShaderVersion();
bool IsDeviceLost();
protected:
D3DPRESENT_PARAMETERS direct3DPresPara;
Window *window;
unsigned int width;
unsigned int height;
D3DXMATRIX projMatrix;
private:
void Initialize(bool fullscreenMode);
};
Direct.cpp
#include"DirectX.h"
LPDIRECT3D9 DirectX::direct3D = NULL;
LPDIRECT3DDEVICE9 DirectX::device = NULL;
IDirect3DVertexDeclaration9* DirectX::vertexDec = NULL;
ID3DXEffect* DirectX::currentShaderEffect = NULL;
DirectX::DirectX(std::string windowTitle, int x, int y, unsigned int windowWidth, unsigned int windowHeight, bool fullscreen)
{
width = windowWidth;
height = windowHeight;
window = new Window(windowTitle.c_str(), windowWidth, windowHeight, x, y, fullscreen);
Initialize(fullscreen);
D3DXMatrixPerspectiveFovLH( &projMatrix,
D3DXToRadian(45),
(float)width/(float)height,
1.0f,
15000.0f);
//device->SetTransform(D3DTS_PROJECTION, &projMatrix);
}
DirectX::~DirectX()
{
direct3D->Release();
device->Release();
vertexDec->Release();
delete vertexDec;
delete currentShaderEffect;
delete window;
}
void DirectX::Initialize(bool fullscreenMode)
{
direct3D = Direct3DCreate9(D3D_SDK_VERSION);
ZeroMemory(&direct3DPresPara, sizeof(direct3DPresPara));
switch(fullscreenMode)
{
case true:
direct3DPresPara.Windowed = false;
break;
case false:
direct3DPresPara.Windowed = true;
}
direct3DPresPara.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; //turns off VSync, comment this line of code to turn VSync back on
direct3DPresPara.SwapEffect = D3DSWAPEFFECT_DISCARD;
direct3DPresPara.hDeviceWindow = window->GetHandle();
direct3DPresPara.BackBufferFormat = D3DFMT_X8R8G8B8;
direct3DPresPara.BackBufferWidth = width;
direct3DPresPara.BackBufferHeight = height;
direct3DPresPara.EnableAutoDepthStencil = TRUE;
direct3DPresPara.AutoDepthStencilFormat = D3DFMT_D16;
direct3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window->GetHandle(), D3DCREATE_SOFTWARE_VERTEXPROCESSING, &direct3DPresPara, &device);
D3DVERTEXELEMENT9 vertexElement[] = { {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
{0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
D3DDECL_END()};
device->CreateVertexDeclaration(vertexElement, &vertexDec);
//currentShaderEffect = 0;
}
void DirectX::CheckShaderVersion()
{
D3DCAPS9 caps;
device->GetDeviceCaps(&caps);
bool stop = false;
int i = 6;
while(!stop)
{
if(caps.VertexShaderVersion < D3DVS_VERSION(i, 0))
{
i--;
}
else
{
std::cout << "you are using shader model " << i << std::endl;
stop = true;
}
}
//std::cout << caps.VertexShaderVersion << std::endl;
//std::cout << D3DVS_VERSION(3, 0) << std::endl;
}
bool DirectX::IsDeviceLost()
{
HRESULT result = device->TestCooperativeLevel();
if(result == D3DERR_DEVICELOST)
{
Sleep(20); //Sleep for a little bit then try again.
return true;
}
else if(result == D3DERR_DRIVERINTERNALERROR)
{
MessageBox(0, "Internal Driver Error. The program will now exit.", 0, 0);
PostQuitMessage(0);
return true;
}
else if(result == D3DERR_DEVICENOTRESET)
{
device->Reset(&direct3DPresPara);
return false;
}
else
{
return false;
}
}
Any help or advice will be greatly appreciated. Thanks
You don't need to release resources in D3DPOOL_MANAGED. You need to release resources in D3DPOOL_DEFAULT.
If you're using D3DX objects (ID3DXFont, ID3DXMesh, ID3DXSprite), they typically have "OnLostDevice" and "OnResetDevice" methods. You should call those methods in appropriate situations.
If an object does not have any methods for dealing with "DeviceLost" state, and they should be released, simply destroy the object before resetting the device, and reload it afterwards.
Please note that D3DPOOL_MANAGED objects are reloaded automatically by the driver and don't need any "help" from you to deal with reset/devicelost. YOu do need to take care about all other objects.
In addition to what I said/wrote, you should obviously read DirectX SDK documentation. They cover lost devices in their documentation, and DirectX examples normally survive device reset just fine.

NULL pointer, thought it seems to be initialized

I get
Debug assertion failed.
p!=0
and it points to:
_NoAddRefReleaseOnCComPtr<T>* operator->() const throw()
{
ATLASSERT(p!=NULL);
return (_NoAddRefReleaseOnCComPtr<T>*)p;
}
in 'atlcomcli.h'
From what I understand it means I have forgot to initialize a pointer somewhere, but all of them seem to be initialized.
When I use normal pointers instead of 'CComPtr', it throws 'Access Violation Reading Location' at 'font->DrawTextA' in 'D3DFont::Draw' in D3DFont.cpp
//D3DFont.h:
#include <D3DX10.h>
#include <atlbase.h>
#include <string>
class D3DFont
{
public:
D3DFont(void);
~D3DFont(void);
bool Create(ID3D10Device *device, std::string name, int width,
int height, int weight, int mipLevels, bool italic, BYTE charset,
BYTE quality, BYTE pitchAndFamily);
void Draw(LPD3DX10SPRITE sprite, std::string text, int charCount,
LPRECT rect, UINT format, D3DXCOLOR color);
private:
CComPtr<ID3DX10Font> font;
};
//D3DFont.cpp:
#include "D3DFont.h"
D3DFont::D3DFont(void){}
D3DFont::~D3DFont(void){}
bool D3DFont::Create( ID3D10Device *device, std::string name,
int width, int height, int weight, int mipLevels, bool italic,
BYTE charset, BYTE quality, BYTE pitchAndFamily )
{
D3DX10_FONT_DESC fd;
ZeroMemory(&fd, sizeof(D3DX10_FONT_DESC));
fd.Height = height;
fd.Width = width;
fd.Weight = weight;
fd.MipLevels = mipLevels;
fd.Italic = italic;
fd.CharSet = charset;
fd.Quality = quality;
fd.PitchAndFamily = pitchAndFamily;
strcpy_s(fd.FaceName, name.c_str());
// INITIALIZING FONT HERE
D3DX10CreateFontIndirect(device, &fd, &font);
return true;
}
void D3DFont::Draw( LPD3DX10SPRITE sprite, std::string text,
int charCount, LPRECT rect, UINT format, D3DXCOLOR color )
{
// ERROR HERE
font->DrawTextA(sprite, text.c_str(), charCount, rect, format, color);
}
And my use of above functions:
if( !font.Create(d3d.GetDevice(), "Impact", 0, 175, 0, 1, false,
OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE) )
{
MessageBox(0, "Could not create font.", "Error!", MB_OK | MB_ICONERROR);
}
// later on...
RECT r = {35, 50, 0, 0};
font.Draw(0, "Test", -1, &r, DT_NOCLIP, d3d.GetColorObj(1.0f, 1.0f, 0.0f, 1.0f));
What could I miss?
'D3DX10CreateFontIndirect' throws 0x8876086C
Can't find what does it mean, but some google threads are related to d3dDevice, so I guess it must be related to it. Will update when I will have more info.
Calling the D3DX10CreateFontIndirect doesn't actually guarantee that your pointer will be initialized.
Rule of thumb : ALWAYS check HRESULTs when using DirectX functions that initialize a pointer:
HRESULT hr = D3DX10CreateFontIndirect(device, &fd, &font);
if(FAILED(hr)){
//Get the last error, display a message, etc.
//Eventually propagate the error if the code can't continue
//with the font pointer uninitialized.
}
When your function returns E_FAIL, do not try to use the pointer afterward. There are great chances that the values of the parameters are simply incorrect (here, your device pointer might be null or your font description might be incorrect).

Where can I find the source code examples for "Introduction to 3D game programming with DirectX 9.0c"?

I have a book : "Introduction to 3D game programming with DirectX 9.0c– a shader approach" by Frank Luna.
The official site is dead and I can't seem to find 3 main files used for all the projects.
d3dApp.h
d3dApp.cpp
d3dUtil.h
Does someone know where can I get them?
All I have found was this :
http://www.d3dcoder.net/
http://www.d3dcoder.net/phpBB/
But there is no source there.
Also I've found some fragments
//A sample directX demo outputting some flashing color text
#include "d3dApp.h"
#include <tchar.h>
#include <crtdbg.h>
//Our application is derived from the D3DAPP class, making setup for a game
//or other program easier in the long run
class HelloD3DApp : public D3DApp
{
public:
HelloD3DApp(HINSTANCE hInstance, std::string winCaption, D3DDEVTYPE devType, DWORD requestedVP);
~HelloD3DApp();
bool checkDeviceCaps();
void onLostDevice();
void onresetDevice();
void updateScene(float dt);
void drawScene();
private:
ID3DXFont* mFont;
};
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance,
PSTR cmdLine, int showCmd)
{
// Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif
HelloD3DApp app(hInstance, "Hello Direct3D", D3DDEVTYPE_HAL, D3DCREATE_HARDWARE_VERTEXPROCESSING);
gd3dApp = &app;
return gd3dApp->run();
}
HelloD3DApp::HelloD3DApp(HINSTANCE hInstance, std::string winCaption, D3DDEVTYPE devType, DWORD requestedVP)
: D3DApp(hInstance, winCaption, devType, requestedVP)
{
srand(time_t(0));
if(!checkDeviceCaps())
{
MessageBox(0, "checkDeviceCaps() Failed", 0, 0);
PostQuitMessage(0);
}
LOGFONTA font;
font.lfHeight = 80;
font.lfWidth = 40;
font.lfEscapement = 0;
font.lfOrientation = 0;
font.lfWeight = FW_BOLD;
font.lfItalic = true;
font.lfUnderline = false;
font.lfStrikeOut = false;
font.lfCharSet = DEFAULT_CHARSET;
font.lfOutPrecision = OUT_DEFAULT_PRECIS;
font.lfClipPrecision = CLIP_CHARACTER_PRECIS;
font.lfQuality = DEFAULT_QUALITY;
font.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
_tcscpy(font.lfFaceName, _T("Times New Roman"));
HR(D3DXCreateFontIndirect(gd3dDevice, &font, &mFont));
}
HelloD3DApp::~HelloD3DApp()
{
ReleaseCOM(mFont);
}
bool HelloD3DApp::checkDeviceCaps()
{
// Nothing to check.
return true;
}
void HelloD3DApp::onLostDevice()
{
HR(mFont->OnLostDevice());
}
void HelloD3DApp::onresetDevice()
{
HR(mFont->onresetDevice());
}
void HelloD3DApp::updateScene(float dt)
{
}
void HelloD3DApp::drawScene()
{
HR(gd3dDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255, 255, 255), 1.0f, 0));
RECT formatRect;
GetClientRect(mhMainWnd, &formatRect);
HR(gd3dDevice->BeginScene());
mFont->DrawText(TEXT("Hello </DIC>!"), -1,
&formatRect, DT_CENTER | DT_VCENTER,
D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256));
HR(gd3dDevice->EndScene());
HR(gd3dDevice->Present(0, 0, 0, 0));
}
But these does not help me either.
d3dApp.h
d3dApp.cpp
d3dUtil.h
These are the same files as in the zip file.

Drawing a jpg in MFC

I've been trying to show a jpg image in MFC, but I can't get it drawn. I'm a complete MFC newbie an everything I got up until now is mostly adapted from things I found on the net. Currently I have this:
Picture.h:
#pragma once
#include <afxwin.h>
class Picture
{
public:
Picture();
bool load(LPCTSTR filePath);
bool draw( CDC* deviceContext
, CRect clientRect
, LPCRECT prcMFBounds);
CSize getSize(CDC* pDC);
private:
LPPICTURE m_picture;
};
Picture.cpp:
#include "Picture.h"
Picture::Picture()
: m_picture(0)
{
}
bool Picture::load(LPCTSTR szFile)
{
HANDLE hFile = CreateFile(szFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return false;
DWORD dwFileSize = GetFileSize(hFile, NULL);
if (dwFileSize == (DWORD)-1)
{
CloseHandle(hFile);
return false;
}
LPVOID pvData = NULL;
// alloc memory based on file size
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwFileSize);
if (hGlobal == NULL)
{
CloseHandle(hFile);
return false;
}
pvData = GlobalLock(hGlobal);
if (pvData == NULL)
{
GlobalUnlock(hGlobal);
CloseHandle(hFile);
return false;
}
DWORD dwBytesRead = 0;
// read file and store in global memory
bool bRead = ReadFile(hFile, pvData, dwFileSize, &dwBytesRead, NULL) != 0;
GlobalUnlock(hGlobal);
CloseHandle(hFile);
if (!bRead)
return false;
LPSTREAM pstm = NULL;
// create IStream* from global memory
HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pstm);
if (!(SUCCEEDED(hr)))
{
if (pstm != NULL)
pstm->Release();
return false;
}
else if (pstm == NULL)
return false;
// Create IPicture from image file
if (m_picture)
m_picture->Release();
hr = ::OleLoadPicture(pstm, dwFileSize, FALSE, IID_IPicture, (LPVOID *)&(m_picture));
if (!(SUCCEEDED(hr)))
{
pstm->Release();
return false;
}
else if (m_picture == NULL)
{
pstm->Release();
return false;
}
pstm->Release();
return true;
}
bool Picture::draw(CDC* deviceContext, CRect clientRect, LPCRECT prcMFBounds)
{
if (clientRect.IsRectNull())
{
CSize imageSize = getSize(deviceContext);
clientRect.right = imageSize.cx;
clientRect.bottom = imageSize.cy;
}
long pictureWidth = 0;
long pictureHeigth = 0;
m_picture->get_Width(&pictureWidth);
m_picture->get_Height(&pictureHeigth);
m_picture->Render( *deviceContext
, clientRect.left
, clientRect.top
, clientRect.Width()
, clientRect.Height()
, 0
, pictureHeigth
, pictureWidth
, -pictureHeigth
, prcMFBounds);
return true;
}
CSize Picture::getSize(CDC* deviceContext)
{
if (!m_picture)
return CSize(0,0);
LONG width, height; // HIMETRIC units
m_picture->get_Width(&width);
m_picture->get_Height(&height);
CSize size(width, height);
if (deviceContext==NULL)
{
CWindowDC dc(NULL);
dc.HIMETRICtoDP(&size); // convert to pixels
}
else
{
deviceContext->HIMETRICtoDP(&size);
}
return size;
}
PictureView.h:
#pragma once
#include <afxwin.h>
#include <string>
class Picture;
class PictureView : public CStatic
{
public:
PictureView(std::string path);
protected:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
private:
Picture* m_picture;
};
PictureView.cpp:
#include "PictureView.h"
#include "Picture.h"
PictureView::PictureView(std::string path)
{
m_picture = new Picture();
m_picture->load(path.c_str());
}
void PictureView::DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/)
{
CRect rect;
GetClientRect(&rect);
m_picture->draw(GetDC(), rect, rect);
}
Constructor call:
CWnd* GenericChildWidget::addImage(std::string path, int horizontalPos, int width, int verticalPos, int height, int childId)
{
PictureView* image = new PictureView(path);
image->Create("", SS_OWNERDRAW, CRect(horizontalPos, verticalPos, width + horizontalPos, height + verticalPos), this, childId);
return image;
}
The problem is that I can't get the DrawItem function to be called. What am I doing wrong?
Any help will be appreciated.
Here's an easy way to draw an image in an MFC dialog box:
link with gdiplus.dll
#include "atlimage.h>
#include "gdiplus.h>
using namespace Gdiplus
.
.
.
CImage ci;
ci.Load((CString)"D:\\Pictures\\mycat.jpg");
CDC *dc = AfxGetMainWnd()->GetDC();
HDC hdc = *dc;
ci.Draw(hdc,10,10);
Hope it works!
with the help of a colleague of mine I've found the answer. He told me it's not possible to have an ownerdrawn CStatic. So when I now inherit PictureView from CButton and make it BS_OWNERDRAW my image is rendered.
Personally I think this is an ugly solution but I'm so tired of this problem now that I don't really care that much. These are the changes I've made to make it work:
Constructor call:
CWnd* GenericChildWidget::addImage(std::string path, int horizontalPos, int width, int verticalPos, int height, int childId)
{
PictureView* image = new PictureView(path);
image->Create("", BS_OWNERDRAW | WS_CHILD | WS_VISIBLE, CRect(horizontalPos, verticalPos, width + horizontalPos, height + verticalPos), this, childId);
return image;
}
PictureView.h:
#pragma once
#include <afxwin.h>
#include <string>
class Picture;
class PictureView : public CButton
{
public:
PictureView(std::string path);
protected:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
private:
Picture* m_picture;
};
Thank's everybody for all the help.
I'm not sure that the GetDC in the call to m_picture->draw will necessarily refer to the same DC that's given in the CREATESTRUCT. What I've done is:
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
EDIT: Since I can't comment, hopefully an edit will suffice. I think your colleague is mistaken, as I just recently implemented a CStatic control using SS_OWNERDRAW. I'm guessing that the addition of WS_CHILD | WS_VISIBLE on the Create call is key.
I've never used MFC but a quick perusal of the CStatic::DrawItem documentation says that it must be created with the SS_OWNERDRAW style for DrawItem to get called. You haven't shown the Create line for your PictureView so perhaps it's that?