尝试呈现 SDL_Texture : Invalid texture 时出现 C++ SDL2 错误

标签 c++ textures render sdl-2

我正在尝试制作一个简单的游戏,当我尝试渲染我的 SDL_Texture 时,我遇到了一个莫名其妙的错误。我已正确设置所有内容,我能够使用 SDL_RenderClear 成功清除屏幕,并且我的纹理不为空,因此它应该已正确创建。但是当我尝试调用 render() 函数时出现错误,并且 SDL_GetError() 返回“Invalid texture”。

编辑:我现在已经按照要求创建了一个 MCVE,并且我对其进行了测试以验证它是否重现了错误。它应该在窗口中的路径“gfx/grid.bmp”中显示图像,但它却给了我错误。这是完整的 MCVE:

#include <SDL.h>
#include <string>
#include <iostream>
#undef main

class Texture{
private:
    SDL_Texture* texture;
    SDL_Renderer* renderer;
    int width;
    int height;
public:
    Texture(){
        texture = nullptr;
    }
    Texture (SDL_Renderer* ren, std::string path){
        texture = nullptr;
        SDL_Surface* surface = nullptr;
        surface = SDL_LoadBMP(path.c_str());
        if(surface == nullptr) return;
        renderer = ren;

        texture = SDL_CreateTextureFromSurface( renderer, surface );
        if(texture == 0) return;

        width = surface->w;
        height = surface->h;

        SDL_FreeSurface(surface);
        std::cout << "Texture '" << path << "' successfully loaded\n";
    }
    bool loaded(){
        return texture != 0;
    }
    bool render(int x = 0, int y = 0){
        SDL_Rect dest;
        dest.x = x;
        dest.y = y;
        dest.w = width;
        dest.h = height;
        return SDL_RenderCopy(renderer, texture, nullptr, &dest) == 0;
    }
    void unload(){
        if(loaded()){
            SDL_DestroyTexture(texture);
            texture = nullptr;
            renderer = nullptr;
            width = 0;
            height = 0;
        }
    }
    ~Texture(){
        unload();
    }
};

class CApp{
private:
    bool running = true;

    SDL_Window* window = nullptr;
    SDL_Renderer* renderer = nullptr;
    Texture graphic_grid;
    bool render = true;
public:
    int OnExecute();

    bool OnInit();
    void OnEvent(SDL_Event* event);
    void OnRender();
    void OnCleanup();
};

bool CApp::OnInit(){
    if(SDL_Init(SDL_INIT_EVERYTHING) < 0){
        return false;
    }
    if((window = SDL_CreateWindow("Simple Tic-Tac-Toe", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 600, 600, SDL_WINDOW_SHOWN)) == nullptr){
        return false;
    }
    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    if(renderer == nullptr){
        std::cout << "Renderer failed to load! Error: " << SDL_GetError();
        return false;
    }
    SDL_SetRenderDrawColor( renderer, 0xFF, 0xFF, 0xFF, 0xFF );

    graphic_grid = Texture(renderer, "gfx/grid.bmp");

    if(!graphic_grid.loaded()){
        std::cout << "Image failed to load! Error: " << SDL_GetError();
        return false;
    }

    std::cout << "Initialized fully\n";

    return true;
}

void CApp::OnEvent(SDL_Event* event){
    switch(event->type == SDL_QUIT){
        running = false;
    }
}

void CApp::OnRender(){
    if(!render) return;

    if(SDL_RenderClear(renderer) == 0){
        std::cout << "Successfully cleared screen\n";
    }

    if(!graphic_grid.loaded()){
        std::cout << "Texture not loaded!\n";
    }else{
        std::cout << "Texture not null!\n";
    }

    //This is the place where I get the "Invalid texture" error, everything else works fine
    if(!graphic_grid.render()){
        std::cout << "Failed to render image! Error: " << SDL_GetError() << '\n';
    }

    SDL_RenderPresent(renderer);

    render = false;
}

void CApp::OnCleanup(){
    graphic_grid.unload();

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    renderer = nullptr;
    window = nullptr;

    SDL_Quit();
}

int CApp::OnExecute(){
    if(OnInit() == false){
        return -1;
    }

    SDL_Event event;
    while(running){

        while(SDL_PollEvent(&event)){
            OnEvent(&event);
        }

        OnRender();
    }

    OnCleanup();

    return 0;
}

int main(){
    CApp theApp;
    return theApp.OnExecute();
}

我四处搜索,找不到任何可以解释我的错误的内容。我能找到的最接近的东西是 this question ,但该错误是由渲染器在纹理之前被破坏引起的,这不是我的情况。

我真的希望有人能告诉我可能导致此错误的原因,我们将不胜感激!

最佳答案

你的问题是你正在这一行复制你的纹理

graphic_grid = Texture(renderer, "gfx/grid.bmp");

由于您没有定义复制赋值运算符或移动赋值,编译器会为您提供一个,它只是按原样复制成员。

一旦由 Texture(renderer, "gfx/grid.bmp"); 创建的临时文件被销毁,graphic_grid.texture 就不再有效。您可以通过删除 Texture 析构函数来测试它,您应该会发现一切都按预期工作(当然您实际上不想这样做,它不是修复)。

如果您的 Texture 不打算被复制,您将需要实现移动赋值或复制赋值构造函数,最好是前者(并且由于您需要赋值,您还应该提供一个移动/复制构造函数,以及其他两个移动/复制方法,按照五的规则,或者至少显式删除它们)。

请仔细阅读 0/3/5 的规则

Rule-of-Three becomes Rule-of-Five with C++11?

如果您使用的是 GCC,-Weffc++ 标志会警告您一些像这样的狡猾类设计。

就我个人而言,我尽量将句柄和指针包装到 RAII 类型中,只要每个成员都对自己负责,您通常不必费心使用复制和移动构造函数。 std::unique_ptr 对此非常有效,例如:

template<typename T, void(*destroy)(T*), typename Ptr = std::unique_ptr<T, decltype(destroy)>>
struct Handle : public Ptr
{
  Handle(T*&& p): Ptr{p, destroy}
  {
    if (!*this)
      throw std::runtime_error{SDL_GetError()};
  }
};

using Window_h  = Handle<SDL_Window, SDL_DestroyWindow>;
using Surface_h = Handle<SDL_Surface, SDL_FreeSurface>;
// And so on for other handles

可以如下使用

Window_h window{SDL_CreateWindow(
  "Hello world", 
  SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
  800, 600,
 0 
)};

如果你要像上面那样包装 SDL_Texture 那么你的 Texture 类将有一个隐式移动构造函数和移动赋值运算符,它们可以开箱即用(你会但是,必须摆脱 renderer 成员,或者以不同的方式处理它,例如将其设为 std::shared_ptr 这样它就不会与纹理一起被破坏,除非它引用计数为 0)。

您的代码还有其他一些奇怪的事情,例如过早地从构造函数返回。如果无法构造对象,则应抛出异常。

关于尝试呈现 SDL_Texture : Invalid texture 时出现 C++ SDL2 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25738096/

相关文章:

c++ - c++库文件中局部常量是如何存储的

c++ - 标题排除?

c++ - 在 C++ 中存储和修改多个列表

html - 是否可以在静态 html5 页面中呈现部分内容

Laravel livewire,呈现性能问题。如何管理数百个组件?

c++ - 使用 SIMD/SSE 的水平运行差异和条件更新?

c# - 使用 Texture2D 创建平滑的圆角矩形

java - 试图解压缩 BC1 纹理压缩的扰乱 block

java - GL_NEAREST 上的 Android OpenGL mag 过滤器,小纹理仍然模糊

c++ - 当轮廓上的第一个点偏离曲线时如何解释自由字形轮廓