SDL2调整表面大小

时间:2016-11-28 17:31:51

标签: c++ sdl-2

我们希望通过使用SDL_Image加载图像来创建SDL表面,如果尺寸超过限制,则调整表面大小。

我们需要这样做的原因是在Raspbian SDL上抛出一个从表面创建纹理的错误('纹理尺寸限制为2048x2048 ')。虽然这是一个非常大的图像,但我们不希望用户关注图像大小,我们希望为它们调整大小。虽然我们没有在Windows上遇到这个限制,但我们正试图在Windows上开发解决方案,并且在调整纹理大小时会出现问题。

寻找解决方案有类似的问题......:

当前SDL2是否需要自定义阻击器或SDL_gfx(这些答案早于SDL2 2013发布日期)? SDLRenderCopyEx没有帮助,因为您需要生成出现问题的纹理。

所以我们尝试了一些可用的blitting函数,比如SDL_BlitScaled,下面是一个简单的程序来渲染一个没有缩放的2500x2500 PNG:

#include <SDL.h>
#include <SDL_image.h>
#include <sstream>
#include <string>

SDL_Texture * get_texture(
    SDL_Renderer * pRenderer,
    std::string image_filename) {
    SDL_Texture * result = NULL;

    SDL_Surface * pSurface = IMG_Load(image_filename.c_str());

    if (pSurface == NULL) {
        printf("Error image load: %s\n", IMG_GetError());
    }
    else {
        SDL_Texture * pTexture = SDL_CreateTextureFromSurface(pRenderer, pSurface);

        if (pTexture == NULL) {
            printf("Error image load: %s\n", SDL_GetError());
        }
        else {
            SDL_SetTextureBlendMode(
                pTexture,
                SDL_BLENDMODE_BLEND);

            result = pTexture;
        }

        SDL_FreeSurface(pSurface);
        pSurface = NULL;
    }

    return result;
}

int main(int argc, char* args[]) {
    SDL_Window * pWindow = NULL;
    SDL_Renderer * pRenderer = NULL;

    // set up
    SDL_Init(SDL_INIT_VIDEO);

    SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");

    SDL_Rect screenDimensions;

    screenDimensions.x = 0;
    screenDimensions.y = 0;

    screenDimensions.w = 640;
    screenDimensions.h = 480;

    pWindow = SDL_CreateWindow("Resize Test",
        SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED,
        screenDimensions.w, 
        screenDimensions.h,
        SDL_WINDOW_SHOWN);

    pRenderer = SDL_CreateRenderer(pWindow,
        -1,
        SDL_RENDERER_ACCELERATED);

    IMG_Init(IMG_INIT_PNG);

    // render
    SDL_SetRenderDrawColor(
        pRenderer,
        0,
        0,
        0,
        0);

    SDL_RenderClear(pRenderer);

    SDL_Texture * pTexture = get_texture(
        pRenderer,
        "2500x2500.png");

    if (pTexture != NULL) {
        SDL_RenderCopy(
            pRenderer,
            pTexture,
            NULL,
            &screenDimensions);

        SDL_DestroyTexture(pTexture);
        pTexture = NULL;
    }

    SDL_RenderPresent(pRenderer);

    // wait for quit
    bool quit = false;

    while (!quit)
    {
        // poll input for quit
        SDL_Event inputEvent;

        while (SDL_PollEvent(&inputEvent) != 0) {
            if ((inputEvent.type == SDL_KEYDOWN) &&
                (inputEvent.key.keysym.sym == 113)) {
                quit = true;
            }
        }
    }

    IMG_Quit();

    SDL_DestroyRenderer(pRenderer);

    pRenderer = NULL;

    SDL_DestroyWindow(pWindow);

    pWindow = NULL;

    return 0;
}

更改get_texture函数以识别限制并尝试创建新曲面:

SDL_Texture * get_texture(
    SDL_Renderer * pRenderer,
    std::string image_filename) {
    SDL_Texture * result = NULL;

    SDL_Surface * pSurface = IMG_Load(image_filename.c_str());

    if (pSurface == NULL) {
        printf("Error image load: %s\n", IMG_GetError());
    }
    else {
        const int limit = 1024;
        int width = pSurface->w;
        int height = pSurface->h;

        if ((width > limit) ||
            (height > limit)) {
            SDL_Rect sourceDimensions;
            sourceDimensions.x = 0;
            sourceDimensions.y = 0;
            sourceDimensions.w = width;
            sourceDimensions.h = height;

            float scale = (float)limit / (float)width;
            float scaleH = (float)limit / (float)height;

            if (scaleH < scale) {
                scale = scaleH;
            }

            SDL_Rect targetDimensions;
            targetDimensions.x = 0;
            targetDimensions.y = 0;
            targetDimensions.w = (int)(width * scale);
            targetDimensions.h = (int)(height * scale);

            SDL_Surface *pScaleSurface = SDL_CreateRGBSurface(
                pSurface->flags,
                targetDimensions.w,
                targetDimensions.h,
                pSurface->format->BitsPerPixel,
                pSurface->format->Rmask,
                pSurface->format->Gmask,
                pSurface->format->Bmask,
                pSurface->format->Amask);

            if (SDL_BlitScaled(pSurface, NULL, pScaleSurface, &targetDimensions) < 0) {
                printf("Error did not scale surface: %s\n", SDL_GetError());

                SDL_FreeSurface(pScaleSurface);
                pScaleSurface = NULL;
            }
            else {
                SDL_FreeSurface(pSurface);

                pSurface = pScaleSurface;
                width = pSurface->w;
                height = pSurface->h;
            }
        }

        SDL_Texture * pTexture = SDL_CreateTextureFromSurface(pRenderer, pSurface);

        if (pTexture == NULL) {
            printf("Error image load: %s\n", SDL_GetError());
        }
        else {
            SDL_SetTextureBlendMode(
                pTexture,
                SDL_BLENDMODE_BLEND);

            result = pTexture;
        }

        SDL_FreeSurface(pSurface);
        pSurface = NULL;
    }

    return result;
}

SDL_BlitScaled失败并显示错误'不支持Blit组合'其他变体也有类似的错误:

SDL_BlitScaled(pSurface, NULL, pScaleSurface, NULL)
SDL_BlitScaled(pSurface, &sourceDimensions, pScaleSurface, &targetDimensions)
SDL_LowerBlitScaled(pSurface, &sourceDimensions, pScaleSurface, &targetDimensions) // from the wiki this is the call SDL_BlitScaled makes internally

然后我们尝试了一个非缩放的blit ...它没有抛出错误但只显示白色(不是图像中的清晰颜色或颜色)。

SDL_BlitSurface(pSurface, &targetDimensions, pScaleSurface, &targetDimensions)

由于blitting函数不起作用,我们尝试使用与位图相同的图像(只是将.png导出为.bmp),仍然使用SDL_Image加载文件,这两个函数都按预期使用SDL_BlitScaled缩放

不确定这里出了什么问题(我们希望并需要支持主要的图片文件格式,例如.png),或者如果这是推荐的方法,那么任何帮助都会受到赞赏!

1 个答案:

答案 0 :(得分:1)

TL; DR 来自@kelter的评论向我指出了正确的方向,而another stack overflow question给了我一个解决方案:如果你首先Blit到32bpp表面然后BlitScaled到另一个32bpp表面。这适用于8位和24位深度png,32位不可见again another stack overflow question建议首先在blitting之前填充表面。

更新的get_texture函数:

NTE_KEYSET_NOTDEF

来自@kelter的评论让我更仔细地观察表面像素格式,位图工作在24bpp,pngs正在加载8bpp并且无法正常工作。尝试将目标表面更改为24或32 bpp,但这没有帮助。我们已经生成了具有自动检测位深度的png,将其设置为8或24并且在具有相同的每像素位数的表面上执行BlitScaled,尽管它不适用于32.搜索blit转换错误导致来自@Petruza的问答。

更新写这个答案有点快,原始解决方案处理了bmp和8位和24位png但32位png没有渲染。 @Retired Ninja回答另一个关于Blit_Scaled的问题,建议在调用函数之前填充表面并对其进行排序,another question与新表面上的alpha设置有关,可能与此相关(特别是如果你需要透明度)但是填充对我来说,纯色是足够的......现在。