C++ SDL2 Error when trying to render SDL_Texture: Invalid texture

佐手、 提交于 2019-12-08 00:23:31

问题


I'm trying to make a simple game and when I try rendering my SDL_Texture, I get an inexplicable error. I've set up everything right, I'm able to successfully clear the screen with SDL_RenderClear, and my texture isn't null, so it should have been created properly. But when I try calling the render() function I get an error, and SDL_GetError() returns "Invalid texture".

Edit: I have now created an MCVE as requested, and I tested it to verify that it reproduces the error. It should display the image at the path "gfx/grid.bmp" in the window, but instead it gives me the error. Here is the full 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();
}

I have searched around and can't find anything that explains what my error could be. The closest thing I could find was this question, but that error was caused by the renderer being destroyed before the texture, which isn't my case.

I really hope someone can tell me what might be causing this error, any suggestions are appreciated!


回答1:


Your problem is that you are copying your Texture in this line

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

Since you do not define a copy assignment operator or a move assignment, the compiler provides one for you, which simply copies the members as-is.

As soon as the temporary created by Texture(renderer, "gfx/grid.bmp"); is destroyed, graphic_grid.texture is no longer valid. You can test this by getting rid of your Texture destructor, you should find everything works as expected (of course you don't actually want to do this, it isn't a fix).

You will need to implement either a move assignment or copy assignment constructor, preferably the former if your Textures are not meant to be copied (and since you need the assignment you should also provide a move/copy constructor too, along with the other two move/copy methods as per the rule of five, or at least explicitly delete them).

Please read up on the rule of 0/3/5

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

If you are using GCC the -Weffc++ flag will warn you about some parts of dodgy class design like this.

Personally I try to wrap handles and pointers into RAII types as much as possible, as long as each member is responsible for itself you usually don't have to faff around with copy and move constructors. std::unique_ptr works quite well for this, e.g.:

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

Can be used as follows

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

If you were to wrap SDL_Texture as above then your Texture class would have an implicit move constructor and move assignment operator that would just work out of the box (you would have to get rid of the renderer member however, or handle it differently, for instance make it a std::shared_ptr so it isn't destroyed along with the texture unless its reference count is 0).

There's a few other odd things about your code, such as prematurely returning from a constructor. If an object cannot be constructed you should throw an exception.




回答2:


I don't know which constructor you are calling to create object of your Texture class, I think there is some issue with the surface you are creating. I have tried your code with some changes and it is working fine,

void CApp::OnRender(){

    SDL_Window* pWindow;
    SDL_Renderer* render;
    SDL_Surface* surface;

    SDL_Init(SDL_INIT_VIDEO);
    // Create wndow 
    pWindow = SDL_CreateWindow( 
        "Dummy", 
        100, 
        100, 
        640, 480, 
        SDL_WINDOW_SHOWN); 
    // Create renderer 
    render = SDL_CreateRenderer(pWindow, -1, 0); 
    // Create Surface
    surface = SDL_GetWindowSurface(pWindow);

    // Creating object of class texture
    Texture graphic_grid(render, surface);

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

    if(!graphic_grid.loaded()){
        std::cout << "Texture not loaded!\n";
    }else{
        std::cout << "Texture not null!\n";
    }
    if(!graphic_grid.render()){
        std::cout << "Failed to render image! Error: " << SDL_GetError() << '\n';
    }
    for(int i = 0; i < 9; i++){
        if(grid[i] == 0) continue;
        int x = i%3*200;
        int y = i/3*200;
        if(grid[i] == 1){
            graphic_x.render(x, y);
        }else{
            graphic_o.render(x, y);
        }
    }

    SDL_RenderPresent(render);

    render = false;
}


来源:https://stackoverflow.com/questions/25738096/c-sdl2-error-when-trying-to-render-sdl-texture-invalid-texture

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!