Related
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;
}
I'm writing a program to rapidly capture images from one window, modify them, and output them to another window using Xlib with C++. I have this working via XGetImage:
#include <iostream>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
const int TEST_SIZE = 512;
int main()
{
// Open default display
Display *display = XOpenDisplay(nullptr);
int screen = DefaultScreen(display);
Window rootWin = RootWindow(display, screen);
GC graphicsContext = DefaultGC(display, screen);
// Create new window and subscribe to events
long blackPixel = BlackPixel(display, screen);
long whitePixel = WhitePixel(display, screen);
Window newWin = XCreateSimpleWindow(display, rootWin, 0, 0, TEST_SIZE, TEST_SIZE, 1, blackPixel, whitePixel);
XMapWindow(display, newWin);
XSelectInput(display, newWin, ExposureMask | KeyPressMask);
// Main event loop for new window
XImage *image;
XEvent event;
bool exposed = false;
bool killWindow = false;
while (!killWindow)
{
// Handle pending events
if (XPending(display) > 0)
{
XNextEvent(display, &event);
if (event.type == Expose)
{
exposed = true;
} else if (event.type == NoExpose)
{
exposed = false;
} else if (event.type == KeyPress)
{
killWindow = true;
}
}
// Capture the original image
image = XGetImage(display, rootWin, 0, 0, TEST_SIZE, TEST_SIZE, AllPlanes, ZPixmap);
// Modify the image
if (image->data != nullptr)
{
long pixel = 0;
for (int x = 0; x < image->width; x++)
{
for (int y = 0; y < image->height; y++)
{
// Invert the color of each pixel
pixel = XGetPixel(image, x, y);
XPutPixel(image, x, y, ~pixel);
}
}
}
// Output the modified image
if (exposed && killWindow == false)
{
XPutImage(display, newWin, graphicsContext, image, 0, 0, 0, 0, TEST_SIZE, TEST_SIZE);
}
XDestroyImage(image);
}
// Goodbye
XCloseDisplay(display);
}
It generates output like this: https://streamable.com/hovg9
I'm happy to have gotten this far, but from what I've read the performance isn't going to scale very well because it has to allocate space for a new image at every frame. In fact, without the call to XDestroyImage at the end of the loop this program fills up all 16GB of memory on my machine in a matter of seconds!
It seems the recommended approach here is to to set up a shared memory space where X can write the contents of each frame and my program can subsequently read and modify them without the need for any extra allocation. Since the call to XShmGetImage blocks and waits for IPC I believe this means I won't have to worry about any concurrency issues in the shared space.
I've attempted to implement the XShmGetImage approach with the following code:
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/extensions/XShm.h>
#include <X11/Xutil.h>
const int TEST_SIZE = 512;
int main()
{
// Open default display
Display *display = XOpenDisplay(nullptr);
int screen = DefaultScreen(display);
Window rootWin = RootWindow(display, screen);
GC graphicsContext = DefaultGC(display, screen);
// Create new window and subscribe to events
long blackPixel = BlackPixel(display, screen);
long whitePixel = WhitePixel(display, screen);
Window newWin = XCreateSimpleWindow(display, rootWin, 0, 0, TEST_SIZE, TEST_SIZE, 1, blackPixel, whitePixel);
XMapWindow(display, newWin);
XSelectInput(display, newWin, ExposureMask | KeyPressMask);
// Allocate shared memory for image capturing
Visual *visual = DefaultVisual(display, 0);
XShmSegmentInfo shminfo;
int depth = DefaultDepth(display, screen);
XImage *image = XShmCreateImage(display, visual, depth, ZPixmap, nullptr, &shminfo, TEST_SIZE, TEST_SIZE);
shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line * image->height, IPC_CREAT | 0666);
shmat(shminfo.shmid, nullptr, 0);
shminfo.shmaddr = image->data;
shminfo.readOnly = False;
XShmAttach(display, &shminfo);
// Main event loop for new window
XEvent event;
bool exposed = false;
bool killWindow = false;
while (!killWindow)
{
// Handle pending events
if (XPending(display) > 0)
{
XNextEvent(display, &event);
if (event.type == Expose)
{
exposed = true;
} else if (event.type == NoExpose)
{
exposed = false;
} else if (event.type == KeyPress)
{
killWindow = true;
}
}
// Capture the original image
XShmGetImage(display, rootWin, image, 0, 0, AllPlanes);
// Modify the image
if (image->data != nullptr) // NEVER TRUE. DATA IS ALWAYS NULL!
{
long pixel = 0;
for (int x = 0; x < image->width; x++)
{
for (int y = 0; y < image->height; y++)
{
// Invert the color of each pixel
pixel = XGetPixel(image, x, y);
XPutPixel(image, x, y, ~pixel);
}
}
}
// Output the modified image
if (exposed && killWindow == false)
{
XShmPutImage(display, newWin, graphicsContext, image, 0, 0, 0, 0, 512, 512, false);
}
}
// Goodbye
XFree(image);
XCloseDisplay(display);
}
Somehow, the images are still being captured and sent to the new window, but the data pointer within the XImage is always null. I can't modify any of the contents and any usage of the XGetPixel and XPutPixel macros will fail. Notice in this video that none of the colors are being inverted like before: https://streamable.com/dckyv
This doesn't make any sense to me. Clearly the data is still being transferred between the windows, but where is it in the XImage structure? How can I access it via code?
It turns out I wasn't reading the signature for shmat correctly. It doesn't have a void return type, it returns a void pointer. This needs to be assigned directly to the XImage's data pointer in order for the data pointer to do anything.
In the call to XShmPutImage the shared memory location is referenced internally rather than the XImage's data pointer which is why this was still working even without utilizing the return value from shmat.
Here's the working code, along with a few other additions which I made for error handling and teardown:
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/extensions/XShm.h>
#include <X11/Xutil.h>
const int TEST_SIZE = 512;
static int handleXError(Display *display, XErrorEvent *event)
{
printf("XErrorEvent triggered!\n");
printf("error_code: %d", event->error_code);
printf("minor_code: %d", event->minor_code);
printf("request_code: %d", event->request_code);
printf("resourceid: %lu", event->resourceid);
printf("serial: %d", event->error_code);
printf("type: %d", event->type);
return 0;
}
int main()
{
// Open default display
XSetErrorHandler(handleXError);
Display *display = XOpenDisplay(nullptr);
int screen = DefaultScreen(display);
Window rootWin = RootWindow(display, screen);
GC graphicsContext = DefaultGC(display, screen);
// Create new window and subscribe to events
long blackPixel = BlackPixel(display, screen);
long whitePixel = WhitePixel(display, screen);
Window newWin = XCreateSimpleWindow(display, rootWin, 0, 0, TEST_SIZE, TEST_SIZE, 1, blackPixel, whitePixel);
XMapWindow(display, newWin);
XSelectInput(display, newWin, ExposureMask | KeyPressMask);
// Allocate shared memory for image capturing
XShmSegmentInfo shminfo;
Visual *visual = DefaultVisual(display, screen);
int depth = DefaultDepth(display, screen);
XImage *image = XShmCreateImage(display, visual, depth, ZPixmap, nullptr, &shminfo, TEST_SIZE, TEST_SIZE);
shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line * image->height, IPC_CREAT | 0777);
image->data = (char*)shmat(shminfo.shmid, 0, 0);
shminfo.shmaddr = image->data;
shminfo.readOnly = False;
XShmAttach(display, &shminfo);
XSync(display, false);
shmctl(shminfo.shmid, IPC_RMID, 0);
// Main event loop for new window
XEvent event;
bool exposed = false;
bool killWindow = false;
while (!killWindow)
{
// Handle pending events
if (XPending(display) > 0)
{
XNextEvent(display, &event);
if (event.type == Expose)
{
exposed = true;
} else if (event.type == NoExpose)
{
exposed = false;
} else if (event.type == KeyPress)
{
killWindow = true;
}
}
// Capture the original image
XShmGetImage(display, rootWin, image, 0, 0, AllPlanes);
// Modify the image
if(image->data != nullptr) // NEVER TRUE. DATA IS ALWAYS NULL!
{
long pixel = 0;
for (int x = 0; x < image->width; x++)
{
for (int y = 0; y < image->height; y++)
{
// Invert the color of each pixel
pixel = XGetPixel(image, x, y);
XPutPixel(image, x, y, ~pixel);
}
}
}
// Output the modified image
if (exposed && killWindow == false)
{
XShmPutImage(display, newWin, graphicsContext, image, 0, 0, 0, 0, 512, 512, false);
}
}
// Goodbye
XShmDetach(display, &shminfo);
XDestroyImage(image);
shmdt(shminfo.shmaddr);
XCloseDisplay(display);
}
I wrote a bit of code, based on NeHe's first tutorial, which creates a window and initializes gl context. this worked exactly as it should in vc++. Then I've tried to replicate the same code in eclipse c++ environment using cygwin compiler and the problems began.
The window compiles without any errors (A number of warnings but no errors), the exe opens a win32 window as it should, all of the functionality that I've coded into the window works too (e.g. full screen mode, change resolution) the only problem is that instead of refreshing the background of the window with red colour as it should I'm getting a black square and that's it.
To me this looks like an opengl initialization problem. Been trying to solve this problem for two days but I can't find any solution I hope somebody can see what I'm doing wrong.
Below are the code extracts.
Initial method
#include <iostream>
#include <windows.h>
#include <gl/glew.h>
#include <string>
#define GLEW_STATIC
using namespace std;
#include <glWindow.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
MSG msg;
GLboolean Exit = FALSE;
glWindow screen("Dark Light", 640, 480, 16);
while(!Exit){
if (screen.LastError() == errNone && screen.WndState() != glExit){
// Is There A Message Waiting?
if (PeekMessage(&msg, screen.Handles().Window(), 0, 0, PM_REMOVE)){
// Have We Received A Quit Message?
if (msg.message == WM_QUIT){
screen.WndState() = glExit;
}
// If Not, Deal With Window Messages
else{
TranslateMessage(&msg); // Translate The Message
DispatchMessage(&msg); // Dispatch The Message
}
}
// If There Are No Messages
else{
if (screen.WndState() == glActive){
if (screen.KeysState(VK_ESCAPE)){
screen.WndState() = glExit;
}
else{
// Draw The Scene
screen.Draw();
}
}
if (screen.KeysState(VK_F1)){
screen.KeysState(VK_F1) = FALSE;
screen.ToggleFullscreen();
}
if (screen.KeysState(VK_SPACE)){
screen.KeysState(VK_SPACE) = FALSE;
screen.SetResolution(800, 600);
}
}
}
else{
Exit = true;
}
}
// Shutdown
return 0;
}
glWindow.h
#ifndef GLWINDOW_H
#define GLWINDOW_H
#include <string>
using namespace std;
enum glWndState
{
glActive = 0, glPaused = 1, glExit = 2
};
enum glWndErrors
{
errNone = 0, errCreateWC = 1, errCreateWnd = 2, errCreateDC = 3, errMatchPixelFormat = 4,
errSetPixelFormat = 5, errCreateRC = 6, errActivateRC = 7, errInitGL = 8, errChangeRC = 9,
errReleaseRC = 10, errReleaseDC = 11, errDestroyWnd = 12, errDestroyWC = 13, errGoToFullscreen = 14,
errGoToWindowed = 15, errGetInstance = 16
};
class glWndSettings
{
private:
GLsizei _width;
GLsizei _height;
GLboolean _fullscreen;
GLint _bits;
PIXELFORMATDESCRIPTOR _pfd;
DEVMODE _screenSettings;
public:
glWndSettings(GLsizei width, GLsizei height, GLint bits);
glWndErrors glSetStyle(HWND hWnd);
glWndErrors glSetStyle(HWND hWnd, GLboolean fullscreen, GLboolean save = TRUE);
glWndErrors glSetResolution(HWND hWnd, GLsizei width, GLsizei height);
GLsizei& Width();
GLsizei& Height();
GLboolean& Fullscreen();
GLint& Bits();
PIXELFORMATDESCRIPTOR& PixelFormatDescription();
};
class glWndHandles
{
private:
string _className;
HINSTANCE _hInstance;
WNDCLASS _wc;
HWND _hWnd;
HDC _hDC;
HGLRC _hRC;
GLuint _pixelFormat;
public:
glWndHandles(HICON icon, string title, WNDPROC wndProc);
~glWndHandles();
glWndErrors glDefWindow(PIXELFORMATDESCRIPTOR pfd);
HINSTANCE& Instance();
WNDCLASS& WinClass();
HWND& Window();
HDC& DeviceContext();
HGLRC& RenderContext();
};
class glWndFPS
{
GLint _framesCounter;
GLint _fps;
public:
glWndFPS();
GLvoid NewFrame();
GLvoid ResetFrames();
GLint FPS();
};
class glWindow
{
glWndHandles _handles;
glWndSettings _settings;
glWndFPS _fps;
glWndErrors _error;
glWndState _state;
bool _keys[256];
string _title;
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
public:
glWindow(string title, int width, int height, int bits);
~glWindow();
int InitGL();
GLvoid Draw();
GLvoid DisplayFPS();
GLvoid SetTitle(string title);
GLvoid SetResolution(GLsizei width, GLsizei height);
GLvoid SetFullscreen(GLboolean fullscreen);
GLvoid ToggleFullscreen();
glWndHandles& Handles();
glWndSettings& Settings();
glWndFPS& fpsInfo();
glWndErrors& LastError();
glWndState& WndState();
bool& KeysState(int key);
GLvoid ReSizeGLScene(GLsizei width, GLsizei height);
};
#endif /* GLWINDOW_H_ */
glWindow.cpp
#include <stdio.h>
#include <windows.h>
#include <GL/glew.h>
#include <glm/gtc/matrix_transform.hpp>
using namespace std;
#include "glWindow.h"
glWndSettings::glWndSettings(GLsizei width, GLsizei height, GLint bits){
_fullscreen = FALSE;
_width = width;
_height = height;
_bits = bits;
_pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); // Size Of This Pixel Format Descriptor
_pfd.nVersion = 1; // Version Number
_pfd.dwFlags = PFD_DRAW_TO_WINDOW | // Format Must Support Window
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
PFD_DOUBLEBUFFER; // Must Support Double Buffering
_pfd.iPixelType = PFD_TYPE_RGBA; // Request An RGBA Format
_pfd.cColorBits = bits; // Select Our Color Depth
_pfd.cRedBits = 0; _pfd.cRedShift = 0;
_pfd.cGreenBits = 0; _pfd.cGreenShift = 0;
_pfd.cBlueBits = 0; _pfd.cBlueShift = 0; // Color Bits Ignored
_pfd.cAlphaBits = 0; _pfd.cAlphaShift = 0; // No Alpha Buffer
_pfd.cAccumBits = 0;
_pfd.cAccumRedBits = 0;
_pfd.cAccumGreenBits = 0;
_pfd.cAccumBlueBits = 0;
_pfd.cAccumAlphaBits = 0;
_pfd.cDepthBits = 16; // 16Bit Z-Buffer (Depth Buffer)
_pfd.cStencilBits = 0; // No Stencil Buffer
_pfd.cAuxBuffers = 0; // No Auxiliary Buffer
_pfd.iLayerType = PFD_MAIN_PLANE; // Main Drawing Layer
_pfd.bReserved = 0; // Reserved
_pfd.dwLayerMask = 0;
_pfd.dwVisibleMask = 0;
_pfd.dwDamageMask = 0; // Layer Masks Ignored
}
glWndErrors glWndSettings::glSetStyle(HWND hWnd){
GLboolean fullscreen = !_fullscreen;
return glSetStyle(hWnd, fullscreen);
}
glWndErrors glWndSettings::glSetStyle(HWND hWnd, GLboolean fullscreen, GLboolean save){
DWORD dwExStyle = 0;
DWORD dwStyle = 0;
if (save)
_fullscreen = fullscreen;
if (fullscreen){
dwExStyle = WS_EX_APPWINDOW;
dwStyle = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
memset(&_screenSettings, 0, sizeof(_screenSettings)); // Makes Sure Memory's Cleared
_screenSettings.dmSize = sizeof(_screenSettings); // Size Of The Devmode Structure
_screenSettings.dmPelsWidth = _width; // Selected Screen Width
_screenSettings.dmPelsHeight = _height; // Selected Screen Height
_screenSettings.dmBitsPerPel = _bits; // Selected Bits Per Pixel
_screenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
// Try To Set Selected Mode And Get Results. NOTE: CDS_FULLSCREEN Gets Rid Of Start Bar.
if (ChangeDisplaySettings(&_screenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL){
return errGoToFullscreen;
}
while(ShowCursor(FALSE) >= 0);
}
else{
dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
if (ChangeDisplaySettings(NULL, 0) != DISP_CHANGE_SUCCESSFUL){
return errGoToWindowed;
}
while(ShowCursor(TRUE) < 0);
}
RECT windowRect;
windowRect.left=(long)0; // Set Left Value To 0
windowRect.right=(long)_width; // Set Right Value To Requested Width
windowRect.top=(long)0; // Set Top Value To 0
windowRect.bottom=(long)_height; // Set Bottom Value To Requested Height
AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle); // Adjust Window To True Requested Size
ShowWindow(hWnd, SW_HIDE);
DWORD oldExStyle = SetWindowLongPtr(hWnd, GWL_EXSTYLE, dwExStyle);
DWORD oldStyle = SetWindowLongPtr(hWnd, GWL_STYLE, dwStyle);
SetWindowPos(hWnd, HWND_TOP, 0, 0, windowRect.right, windowRect.bottom, SWP_NOZORDER);
ShowWindow(hWnd, SW_SHOW);
return errNone;
}
glWndErrors glWndSettings::glSetResolution(HWND hWnd, GLsizei width, GLsizei height){
_width = width; _height = height;
glSetStyle(hWnd, _fullscreen);
return errNone;
}
GLsizei& glWndSettings::Width(){
return _width;
}
GLsizei& glWndSettings::Height(){
return _height;
}
GLboolean& glWndSettings::Fullscreen(){
return _fullscreen;
}
GLint& glWndSettings::Bits(){
return _bits;
}
PIXELFORMATDESCRIPTOR& glWndSettings::PixelFormatDescription(){
return _pfd;
}
glWndHandles::glWndHandles(HICON icon, string title, WNDPROC wndProc){
_hInstance = NULL;
_hWnd = NULL;
_hDC = NULL;
_hRC = NULL;
_pixelFormat = NULL;
_className = title;
_wc.style = CS_HREDRAW | CS_VREDRAW | // Redraw On Size
CS_OWNDC; // Own DC For Window.
_wc.lpfnWndProc = wndProc; // WndProc Handles Messages
_wc.cbClsExtra = NULL; // No Extra Window Data
_wc.cbWndExtra = NULL; // No Extra Window Data
_wc.hInstance = NULL; // Set The Instance
_wc.hIcon = icon; // Load The Default Icon
_wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Load The Arrow Pointer
_wc.hbrBackground = NULL; // No Background Required For GL
_wc.lpszMenuName = NULL; // We Don't Want A Menu
_wc.lpszClassName = _className.c_str(); // Set The Class Name
}
glWndHandles::~glWndHandles(){
// Are We Able To Release The DC And RC Contexts?
if (_hRC){
if (!wglMakeCurrent(NULL, NULL))
MessageBox(NULL, "Release Of DC And RC Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
// Are We Able To Delete The RC?
if (!wglDeleteContext(_hRC))
MessageBox(NULL, "Release Rendering Context Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
}
// Are We Able To Release The DC
if (_hDC && !ReleaseDC(_hWnd, _hDC))
MessageBox(NULL, "Release Device Context Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
// Are We Able To Destroy The Window?
if (_hWnd && !DestroyWindow(_hWnd))
MessageBox(NULL, "Could Not Release hWnd.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
// Are We Able To Unregister Class
if (!UnregisterClass(_className.c_str(), _hInstance))
MessageBox(NULL, "Could Not Unregister Class.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
}
glWndErrors glWndHandles::glDefWindow(PIXELFORMATDESCRIPTOR pfd){
if ((_hInstance = GetModuleHandle(NULL))){
_wc.hInstance = _hInstance;
}
else{
MessageBox(NULL, "Failed To Get Window's Instance.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
return errGetInstance;
}
if (!RegisterClass(&_wc)){
MessageBox(NULL, "Failed To Register The Window Class.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
return errCreateWC;
}
if (!(_hWnd=CreateWindowEx( NULL, // Extended Style For The Window
_wc.lpszClassName, // Class Name
_className.c_str(), // Window Title
NULL, // Style For The Window
0, 0, 300, 300, // Window's Position and Size
NULL, // No Parent Window
NULL, // No Menu
_hInstance, // Instance
NULL))){
MessageBox(NULL, "Window Creation Error.", "ERROR",MB_OK|MB_ICONEXCLAMATION);
return errCreateWnd;
}
//Get Window's Device Context
if (!(_hDC = GetDC(_hWnd))){
MessageBox(NULL, "Can't Create A GL Device Context.", "ERROR",MB_OK|MB_ICONEXCLAMATION);
return errCreateDC;
}
// Did Windows Find A Matching Pixel Format?
if (!(_pixelFormat = ChoosePixelFormat(_hDC, &pfd))){
MessageBox(NULL, "Can't Find A Suitable PixelFormat.", "ERROR",MB_OK|MB_ICONEXCLAMATION);
return errMatchPixelFormat;
}
// Are We Able To Set The Pixel Format?
if(!SetPixelFormat(_hDC, _pixelFormat, &pfd)){
MessageBox(NULL, "Can't Set The PixelFormat.", "ERROR",MB_OK|MB_ICONEXCLAMATION);
return errSetPixelFormat;
}
if (!(_hRC=wglCreateContext(_hDC))){
MessageBox(NULL, "Can't Create A GL Rendering Context.", "ERROR",MB_OK|MB_ICONEXCLAMATION);
return errCreateRC;
}
if(!wglMakeCurrent(_hDC,_hRC)){
MessageBox(NULL, "Can't Activate The GL Rendering Context.", "ERROR",MB_OK|MB_ICONEXCLAMATION);
return errActivateRC;
}
// GLenum err = glewInit();
//
// if (err != GLEW_OK){
// MessageBox(NULL, (char*)glewGetErrorString(err), "ERROR", MB_OK|MB_ICONEXCLAMATION);
// return errInitGL;
// }
return errNone;
}
HINSTANCE& glWndHandles::Instance(){
return _hInstance;
}
WNDCLASS& glWndHandles::WinClass(){
return _wc;
}
HDC& glWndHandles::DeviceContext(){
return _hDC;
}
HGLRC& glWndHandles::RenderContext(){
return _hRC;
}
HWND& glWndHandles::Window(){
return _hWnd;
}
glWndFPS::glWndFPS(){
_framesCounter = 0; _fps = 0;
}
GLvoid glWndFPS::NewFrame(){
_framesCounter++;
}
GLvoid glWndFPS::ResetFrames(){
_fps = _framesCounter; _framesCounter = 0;
}
GLint glWndFPS::FPS(){
return _fps;
}
glWindow* owner;
LRESULT CALLBACK glWindow::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
// Check For Windows Messages
switch (uMsg){
// Watch For Window Activate Message
case WM_ACTIVATE:{
// Check Minimization State
if (!HIWORD(wParam)){
// Program Is Active
owner->_state = glActive;
}
else{
// Program Is No Longer Active
owner->_state = glPaused;
}
// Return To The Message Loop
return 0;
}
// Intercept System Commands
case WM_SYSCOMMAND:{
// Check System Calls
switch (wParam){
// Screensaver Trying To Start?
case SC_SCREENSAVE:
// Monitor Trying To Enter Powersave?
case SC_MONITORPOWER:
// Prevent From Happening
return 0;
}
break;
}
// Did We Receive A Close Message?
case WM_CLOSE:{
// Send A Quit Message
PostQuitMessage(0);
return 0;
}
// Is A Key Being Held Down?
case WM_KEYDOWN:{
// If So, Mark It As TRUE
owner->_keys[wParam] = TRUE;
return 0;
}
// Has A Key Been Released?
case WM_KEYUP:{
// If So, Mark It As FALSE
owner->_keys[wParam] = FALSE;
return 0;
}
// Resize The OpenGL Window
case WM_SIZE:{
if (owner->Settings().Fullscreen() && IsWindowVisible(hWnd)){
if (wParam == SIZE_MINIMIZED)
owner->Settings().glSetStyle(hWnd, FALSE, FALSE);
else if (wParam == SIZE_RESTORED)
owner->Settings().glSetStyle(hWnd, TRUE, FALSE);
}
// LoWord=Width, HiWord=Height
owner->ReSizeGLScene(LOWORD(lParam), HIWORD(lParam));
return 0;
}
case WM_KILLFOCUS:{
if (IsWindowVisible(hWnd) && owner->Settings().Fullscreen())
ShowWindow(hWnd, SW_MINIMIZE);
return 0;
}
case WM_TIMER:{
if (wParam == 1)
owner->DisplayFPS();
return 0;
}
}
// Pass All Unhandled Messages To DefWindowProc
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
glWindow::glWindow(string title, int width, int height, int bits)
:_settings(width, height, bits), _handles(LoadIcon(NULL, IDI_WINLOGO), title, (WNDPROC)WndProc)
{
for (int i = 0; i < 256; i++){
_keys[i] = false;
}
_error = errNone; _state = glActive; owner = this;
_title = title;
if ((_error = _handles.glDefWindow(_settings.PixelFormatDescription())) != errNone){
return;
}
_settings.glSetStyle(_handles.Window(), false);
SetForegroundWindow(_handles.Window()); // Slightly Higher Priority
SetFocus(_handles.Window()); // Sets Keyboard Focus To The Window
// Initialize Our Newly Created GL Window
if (!InitGL()){
MessageBox(NULL, "Initialization Failed.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
_error = errInitGL;
return;
}
SetTimer(_handles.Window(), 1, 1000, NULL);
}
glWindow::~glWindow(void){
if (_settings.Fullscreen()){
ChangeDisplaySettings(NULL,0); // If So Switch Back To The Desktop
ShowCursor(TRUE); // Show Mouse Pointer
}
}
int glWindow::InitGL(){
glShadeModel(GL_SMOOTH);
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1.0f); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LEQUAL); // The Type Of Depth Test To Do
// Really Nice Perspective Calculations
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
return TRUE; // Initialization Went OK
}
GLvoid glWindow::Draw(){
//Clear The Screen And The Depth Buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); // Reset The Current Modelview Matrix
SwapBuffers(_handles.DeviceContext());
fpsInfo().NewFrame();
}
glWndErrors& glWindow::LastError(){
return _error;
}
glWndState& glWindow::WndState(){
return _state;
}
glWndHandles& glWindow::Handles(){
return _handles;
}
glWndSettings& glWindow::Settings(){
return _settings;
}
glWndFPS& glWindow::fpsInfo(){
return _fps;
}
bool& glWindow::KeysState(int key){
return _keys[key];
}
GLvoid glWindow::DisplayFPS(){
fpsInfo().ResetFrames();
char fps[100];
sprintf(fps, "%d", fpsInfo().FPS());
SetTitle(_title + " : ");
SetTimer(_handles.Window(), 1, 1000, NULL);
}
GLvoid glWindow::SetTitle(string title){
SetWindowText(_handles.Window(), title.c_str());
}
GLvoid glWindow::SetResolution(GLsizei width, GLsizei height){
_settings.glSetResolution(_handles.Window(), width, height);
}
GLvoid glWindow::SetFullscreen(GLboolean fullscreen){
_settings.glSetStyle(_handles.Window(), fullscreen);
}
GLvoid glWindow::ToggleFullscreen(){
_settings.glSetStyle(_handles.Window());
}
GLvoid glWindow::ReSizeGLScene(GLsizei width, GLsizei height){
if (height == 0 && width == 0){
return;
}
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
// Calculate The Aspect Ratio Of The Window
glm::perspective(45.0f, (GLfloat)width/(GLfloat)height, 0.1f, 100.0f);
glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
glLoadIdentity(); // Reset The Modelview Matrix
}
Compiler output
g++ -ID:/DarkLight/hello/header -ID:/DarkLight/externals/include -O0 -g3 -Wall -c -fmessage-length=0 -o "src\\glWindow.o" "..\\src\\glWindow.cpp"
g++ -ID:/DarkLight/hello/header -ID:/DarkLight/externals/include -O0 -g3 -Wall -c -fmessage-length=0 -o "src\\hello.o" "..\\src\\hello.cpp"
g++ -mwindows -o hello.exe "src\\hello.o" "src\\glWindow.o" -lgl -lglew32 -lglu32 -lopengl32 -lgdi32
How did you compile the Cygwin build? Specifically which libraries did you link?
Cygwin can be compiled for the native GUI system (Win32) or to make use of X11. But OpenGL goes through two very different stacks between the two cases. In case of Win32 it goes through WGL and in case of X11 it goes through GLX. Now if you accidently linked the GLX based OpenGL API (-lGL) then all the OpenGL symbols are there, so things seem to work superficially, but there's no output. You have to link against opengl32.lib, i.e. -lopengl32 so that the WGL based bindings are used.
I'm writing a simple window class which is failing to return a display. Here's the short version:
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
class WindowImpl
{
public:
WindowImpl()
{
open = true;
}
WindowImpl(float width, float height)
{
if(!create(width, height))
{
fprintf(stderr, "Could not open display\n");
exit(1);
}
open = true;
}
~WindowImpl()
{
XCloseDisplay(display);
};
bool create(float width, float height)
{
display = XOpenDisplay(NULL);
if(display == NULL)
return false;
int displayID = DefaultScreen(display);
window = XCreateSimpleWindow(display, RootWindow(display, displayID), 10, 10, width, height, 1, BlackPixel(display, displayID), WhitePixel(display, displayID));
XMapWindow(display, window);
return true;
}
bool isOpen()
{
return open;
}
void close()
{
open == false;
}
private:
Display* display;
Window window;
bool open;
};
int main()
{
WindowImpl myWindow(1920, 1080);
char* cmd;
while(myWindow.isOpen())
{
if(gets(cmd) == "close")
myWindow.close();
}
return 0;
}
WindowImpl::create fails, XOpenDisplay is returning NULL but I'm not sure why. Hopefully someone could shed some light on the problem here.
Edit: Changing WindowImpl::create to return true and false instead of 0 and 1 causes it to go through but the window still doesn't open;
For clarification:
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
Display* display;
Window window;
XEvent event;
char* message = "Hello";
int screenSize;
display = XOpenDisplay(NULL);
if(display == NULL)
{
fprintf(stderr, "Cannot open display\n");
exit(1);
}
screenSize = DefaultScreen(display);
window = XCreateSimpleWindow(display, RootWindow(display, screenSize), 10, 10, 1920, 1080, 1, BlackPixel(display, screenSize), WhitePixel(display, screenSize));
XSelectInput(display, window, ExposureMask | KeyPressMask);
XMapWindow(display, window);
KeySym keysym = XK_Escape;
KeyCode keycode = XKeysymToKeycode(display, keysym);
while(true)
{
XNextEvent(display, &event);
if(event.type == KeyPress && event.xkey.keycode == keycode)
break;
}
XCloseDisplay(display);
return 0;
}
Compiles and runs just fine.
When you create a windows, you need to let the X server to handle output buffer. Normally this is done when you enter into the window event loop, i.e. calling any function that has EVENT in its name.
Once you are not dealing with events in your code, another way to flush the output buffer is calling XFlush, as said in its manual:
The XFlush function flushes the output buffer. Most client applications need not use this function because the output buffer is automatically flushed as needed by calls to XPending, XNextEvent, and XWindowEvent. Events generated by the server may be enqueued into the library's event queue.
The XSync function flushes the output buffer and then waits until all requests have been received and processed by the X server.
So, to solve your issue, I suggest to put this line of code:
window = XCreateSimpleWindow(display, RootWindow(display, displayID), 10, 10, width, height, 1, BlackPixel(display, displayID), WhitePixel(display, displayID));
XMapWindow(display, window);
XFlush(display); // <------------
return true;
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?