So what I'm trying to accomplish is getting a handle to a window by name (e.g. Slack) and copying that windows pixel information into an OpenGL texture.
I've been basically learning C++ and OpenGL as I've been trying to accomplish this. Before this I had mostly worked with Java.
I managed to load a picture of a cute kitten into a texture and display that on a quad.
I've also found a HWND handle to a specific window.
The problem I have is loading the pixel information from the window and displaying it on a quad. The end goal is to use the texture somewhere else but to make sure that I'm getting the texture I'm displaying it on a quad.
The Problem
Below in WindowTextureFactory there are two functions I managed to puzzle together from several sources. I'm sure that's the source of my error.
The first one, CreateVideoTexture(...)
causes the following error:
Exception thrown at 0x0F61615B (ucrtbased.dll) in program.exe: 0xC0000005:
Access violation reading location 0x0518FFF0.
When I use the data a little later in TextureLoader
in this line
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
While the second one, CreateVideoTextureAlt(...)
, seemingly does nothing. I simply get a black quad.
So what am I doing wrong?
I've included a lot of code and I might be going into to much detail. I've spent a lot of time getting to where I am right now and I want to make sure I'm not making a mistake somewhere.
- WindowManager -
To start there's the WindowManager that finds the HWND to a specific window. I've hard-coded "spotify" as a window name to begin with.
#include "WindowManager.h"
HWND mHwnd;
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam);
WindowManager::WindowManager()
{
EnumWindows(EnumWindowsProc, NULL);
}
SIZE WindowManager::getSizeForHwnd(HWND hwnd)
{
RECT rect;
SIZE size;
BOOL success = GetWindowRect(hwnd, &rect);
if (!success)
{
int errorCode = GetLastError();
std::cout << "Call failed to GetWindowRect() with error code: " + std::to_string(errorCode) << std::endl;
}
size.cx = rect.right - rect.left;
size.cy = rect.bottom - rect.top;
std::cout << "Size: " << size.cx << "x" << size.cy << std::endl;
return size;
}
HWND WindowManager::getHwnd()
{
return mHwnd;
}
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
char class_name[80];
char title[80];
GetWindowText(hwnd, title, sizeof(title));
std::string x = title;
std::transform(x.begin(), x.end(), x.begin(), ::tolower);
if (!x.compare("spotify"))
{
mHwnd = hwnd;
}
return TRUE;
}
Using this class like so
WindowManager wManager;
HWND hwnd = wManager.getHwnd();
SIZE size = wManager.getSizeForHwnd(hwnd);
I now have a handle to the Spotify window. The SIZE
value changes accordingly if I resize the Spotify window and run the program again.
- WindowTextureFactory -
#include "WindowTextureFactory.h"
WindowTextureFactory::WindowTextureFactory() { }
WindowTextureFactory::~WindowTextureFactory() { }
unsigned char* WindowTextureFactory::CreateVideoTexture(HWND hwnd, SIZE bmpSize)
{
if (!bmpSize.cx || !bmpSize.cy)
{
std::cout << "Error creating window texture in CreateVideoTexture(...)" << std::endl;
return nullptr;
}
BITMAPINFO bmi;
auto& hdr = bmi.bmiHeader;
hdr.biSize = sizeof(bmi.bmiHeader);
hdr.biWidth = bmpSize.cx;
hdr.biHeight = -bmpSize.cy;
hdr.biPlanes = 1;
hdr.biBitCount = 32;
hdr.biCompression = BI_RGB;
hdr.biSizeImage = 0;
hdr.biXPelsPerMeter = 0;
hdr.biYPelsPerMeter = 0;
hdr.biClrUsed = 0;
hdr.biClrImportant = 0;
unsigned char* bitmapBits;
HDC hdc = GetWindowDC(hwnd);
HDC hBmpDc = CreateCompatibleDC(hdc);
BITMAP bm;
HBITMAP hBmp = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**) &bitmapBits, nullptr, 0);
SelectObject(hBmpDc, hBmp);
BOOL success = BitBlt(hBmpDc, 0, 0, bmpSize.cx, bmpSize.cy, hdc, 0, 0, SRCCOPY);
if (!success)
{
std::cout << "BitBlt failed" << std::endl;
}
return bitmapBits;
}
unsigned char* WindowTextureFactory::CreateVideoTextureAlt(HWND hwnd, SIZE bmpSize)
{
HDC hdc = GetDC(hwnd);
HDC hBmpDc = CreateCompatibleDC(hdc);
BITMAP bmp;
HBITMAP hBmp = CreateCompatibleBitmap(hBmpDc, bmpSize.cx, bmpSize.cy);
GetObject(hBmp, sizeof(BITMAP), &bmp);
SelectObject(hBmpDc, hBmp);
BOOL success = BitBlt(hBmpDc, 0, 0, bmpSize.cx, bmpSize.cy, hdc, 0, 0, SRCCOPY);
if (!success)
{
std::cout << "BitBlt failed" << std::endl;
}
return (unsigned char*) bmp.bmBits;
}
- TextureLoader -
This class a slightly changed version of another one from in2gpu.com's OpenGL guide. It worked perfectly when loading an external .bmp file.
#include "TextureLoader.h"
TextureLoader::TextureLoader() { }
TextureLoader::~TextureLoader() { }
unsigned int TextureLoader::LoadTexture(unsigned char* data, unsigned int width, unsigned int height)
{
//create the OpenGL texture
unsigned int gl_texture_object = 0;
glGenTextures(1, &gl_texture_object);
glBindTexture(GL_TEXTURE_2D, gl_texture_object);
//filtering
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
float maxAnisotropy;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, maxAnisotropy);
//when we work with textures of sizes not divisible by 4 we have to use the line reader
//which loads the textures in OpenGL so as it can work with a 1 alligned memory (default is 4)
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
//Generates texture
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
//eliminates the array from the RAM
delete data;
//creates the mipmap hierarchy
glGenerateMipmap(GL_TEXTURE_2D);
//returns the texture object
return gl_texture_object;
}
- Fragment & Vertex Shaders -
Vertex_Shader_T.glsl
#version 450 core
layout(location = 0) in vec3 vert;
layout(location = 1) in vec2 vertTexCoord;
out vec2 fragTexCoord;
void main()
{
fragTexCoord = vertTexCoord;
gl_Position = vec4(vert, 1);
}
Fragment_Shader_T.glsl
#version 450 core
uniform sampler2D tex; // texture
in vec2 fragTexCoord; // texture coord
layout(location = 0) out vec4 finalColor; // pixel color output
void main()
{
finalColor = texture(tex, fragTexCoord);
}
- main -
#pragma once
#include "Core\Shader_Loader.h"
#include "Core\GameModels.h"
#include "Core\TextureLoader.h"
#include "Core\WindowManager.h"
#include "Core\WindowTextureFactory.h"
Models::GameModels* gameModels;
GLuint program;
void renderScene(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.6f, 0.6f, 0.6f, 1.0f);
glBindVertexArray(gameModels->GetModel("quad"));
glUseProgram(program);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glutSwapBuffers();
}
void closeCallback()
{
std::cout << "GLUT:\t Finished" << std::endl;
glutLeaveMainLoop();
}
void Init()
{
glEnable(GL_DEPTH_TEST);
gameModels = new Models::GameModels();
gameModels->CreateQuadModel("quad");
// load and compile shaders
Core::Shader_Loader shaderLoader;
program = shaderLoader.CreateProgram("Shaders\\Vertex_Shader_T.glsl", "Shaders\\Fragment_Shader_T.glsl");
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
void texturize()
{
Core::TextureLoader textureLoader;
WindowTextureFactory factory;
WindowManager wManager;
HWND hwnd = wManager.getHwnd();
SIZE size = wManager.getSizeForHwnd(hwnd);
unsigned char* data = factory.CreateVideoTexture(hwnd, size);
unsigned int texture = textureLoader.LoadTexture(data, size.cx, size.cy);
GLint loc = glGetUniformLocation(program, "tex");
if (loc != -1)
{
glUniform1f(loc, 0.432f);
}
glUniform1i(texture, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(500, 500);
glutInitWindowSize(800, 600);
glutCreateWindow("OpenGL Window");
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);
glewInit();
if (glewIsSupported("GL_VERSION_4_5"))
{
std::cout << " OpenGL Version is 4.5\n ";
}
else
{
std::cout << "OpenGL 4.5 not supported\n ";
}
Init();
// Callbacks
glutDisplayFunc(renderScene);
glutCloseFunc(closeCallback);
texturize();
glutMainLoop();
delete gameModels;
glDeleteProgram(program);
return 0;
}