Загрузка текстуры окна из HWND в текстуру OpenGL

Итак, что я пытаюсь сделать, так это получить дескриптор окна по имени (например, Slack) и скопировать эту информацию о пикселях Windows в текстуру OpenGL.

Я в основном изучал C++ и OpenGL, когда пытался это сделать. До этого я в основном работал с Java.
Мне удалось загрузить изображение милого котенка в текстуру и отобразить его на квадроцикле.
Я также нашел дескриптор HWND для определенного окна.

Моя проблема заключается в загрузке информации о пикселях из окна и отображении ее на квадроцикле. Конечная цель состоит в том, чтобы использовать текстуру в другом месте, но чтобы убедиться, что я получаю текстуру, я показываю ее на четырехугольнике.


Проблема
Ниже в WindowTextureFactory есть две функции, которые мне удалось собрать вместе из нескольких источников. Я уверен, что это источник моей ошибки. Первый, CreateVideoTexture(...), вызывает следующую ошибку:

Exception thrown at 0x0F61615B (ucrtbased.dll) in program.exe: 0xC0000005:
Access violation reading location 0x0518FFF0.

Когда я использую данные немного позже в TextureLoader в этой строке
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);

А второй, CreateVideoTextureAlt(...), вроде бы ничего не делает. Я просто получаю черный квадрат.

Так что я делаю неправильно?


Я включил много кода и, возможно, буду вдаваться в подробности. Я потратил много времени на то, чтобы добраться туда, где я сейчас, и я хочу убедиться, что я не ошибаюсь.

- Менеджер окон -

Для начала есть WindowManager, который находит HWND для определенного окна. Для начала я жестко закодировал «spotify» в качестве имени окна.

#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;
}

Использование этого класса так

WindowManager wManager;
HWND hwnd = wManager.getHwnd();
SIZE size = wManager.getSizeForHwnd(hwnd);

Теперь у меня есть дескриптор окна Spotify. Значение SIZE изменится соответственно, если я изменю размер окна Spotify и снова запущу программу.

- Фабрика текстур окна -

#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;
}

- Загрузчик текстур -

Этот класс представляет собой слегка измененную версию другого класса из руководства по OpenGL от in2gpu.com. Он отлично работал при загрузке внешнего файла .bmp.

#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;
}

- Фрагментные и вершинные шейдеры -

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);
}

- главный -

#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;
}

person Bjorninn    schedule 24.08.2015    source источник
comment
Хотя я не вижу, чтобы это решило вашу проблему, и вы не предоставили достаточно кода для компиляции и тестирования (все файлы заголовков отсутствуют), стоит отметить, что у вас есть куча утечек ресурсов в вашем коде загрузки текстуры. . Принятый ответ здесь является хорошим (хотя и кратким) началом: stackoverflow.com/questions/11734553/   -  person enhzflep    schedule 25.08.2015
comment
Спасибо за указатель. Я сильно подозревал, что у меня там какие-то утечки. Я посмотрю на эту ссылку и попытаюсь исправить их. (Извините за поздний ответ! Я был ООО)   -  person Bjorninn    schedule 07.09.2015
comment
Пожалуйста. Время ответов не ограничено. Никто из нас не платит другому. ;)   -  person enhzflep    schedule 07.09.2015