2

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;
}
Bjorninn
  • 4,027
  • 4
  • 27
  • 39
  • 1
    While I can't see it solving your problem and you've not supplied enough code to compile and test with (the header files are all missing), it's worth pointing out that you have a bunch of resource-leaks in your texture loading code. The accepted answer here is a good (albeit brief) start: http://stackoverflow.com/questions/11734553/how-are-gdi-objects-selected-and-destroyed-by-selectobject-function – enhzflep Aug 25 '15 at 02:14
  • Thanks for the pointer. I strongly suspected I had some leaks there. I´ll take a look at that link and try and fix those. (Sorry for the late reply! I've been OOO) – Bjorninn Sep 07 '15 at 10:24
  • You're welcome. There's no time limit on replies. Neither of us is paying the other. ;) – enhzflep Sep 07 '15 at 11:13

0 Answers0