SDL2调整表面大小

SDL2 resize a surface

本文关键字:表面 调整 SDL2      更新时间:2023-10-16

我们想通过加载带有sdl_image的图像来创建SDL表面,如果尺寸超过限制,则调整了表面的大小。

我们需要这样做的原因是在Raspbian SDL上引发了一个错误,从表面创建纹理('纹理尺寸限制为2048x2048 ')。尽管这是一个非常大的图像,但我们不希望用户关注图像大小,但我们希望为他们调整它。尽管我们尚未在Windows上遇到此限制,但我们正在尝试在Windows上开发解决方案,并在调整纹理的问题上遇到问题。

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

  • 2008不是SDL2自定义爆炸
  • 2010使用SDL_GFX
  • 2008无法完成使用SDL_GFX,2015年使用SDL_BLITSCALED,2015使用SDL_RenderCopyEx

当前SDL2是否需要自定义的blitter或SDL_GFX(这些答案Pre-pate SDL2的2013年版本)?Sdlrendercopyex无济于事,因为您需要生成纹理,这是我们问题发生的地方。

因此,我们尝试了一些可用的爆炸功能,例如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: %sn", IMG_GetError());
    }
    else {
        SDL_Texture * pTexture = SDL_CreateTextureFromSurface(pRenderer, pSurface);
        if (pTexture == NULL) {
            printf("Error image load: %sn", 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: %sn", 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: %sn", 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: %sn", SDL_GetError());
        }
        else {
            SDL_SetTextureBlendMode(
                pTexture,
                SDL_BLENDMODE_BLEND);
            result = pTexture;
        }
        SDL_FreeSurface(pSurface);
        pSurface = NULL;
    }
    return result;
}

SDL_BLITSCALITSCALED失败,错误'闪电组合不支持'其他变体具有类似的错误:

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

然后,我们尝试了一个非刻度打击...没有丢失错误,而只是显示白色(不是图像中的透明颜色或颜色)。

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

使用该漏油功能不起作用,我们然后使用与位图相同的映像(只需导出.png as .bmp),仍使用SDL_IMAGE加载文件,并且这两个功能都与SDL_BLITSCALCALED缩放量一起使用,符合预期的量表。P>

不确定这里出了什么问题(我们期望和需要支持。

tl; dr @kelter的评论将我指向正确的方向,另一个堆栈溢出问题为我提供了解决方案:如果您首先闪电到一个32BPP表面,然后将其粘贴到另一个32BPP表面。它适用于8和24位深度PNG,32位再次被看不见另一个堆栈溢出问题,建议在爆炸之前首先填充表面。

更新的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: %sn", 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);
            // create a 32 bits per pixel surface to Blit the image to first, before BlitScaled
            // https://stackoverflow.com/questions/33850453/sdl2-blit-scaled-from-a-palettized-8bpp-surface-gives-error-blit-combination/33944312
            SDL_Surface *p32BPPSurface = SDL_CreateRGBSurface(
                pSurface->flags,
                sourceDimensions.w,
                sourceDimensions.h,
                32,
                pSurface->format->Rmask,
                pSurface->format->Gmask,
                pSurface->format->Bmask,
                pSurface->format->Amask);
            if (SDL_BlitSurface(pSurface, NULL, p32BPPSurface, NULL) < 0) {
                printf("Error did not blit surface: %sn", SDL_GetError());
            }
            else {
                // create another 32 bits per pixel surface are the desired scale
                SDL_Surface *pScaleSurface = SDL_CreateRGBSurface(
                    p32BPPSurface->flags,
                    targetDimensions.w,
                    targetDimensions.h,
                    p32BPPSurface->format->BitsPerPixel,
                    p32BPPSurface->format->Rmask,
                    p32BPPSurface->format->Gmask,
                    p32BPPSurface->format->Bmask,
                    p32BPPSurface->format->Amask);
                // 32 bit per pixel surfaces (loaded from the original file) won't scale down with BlitScaled, suggestion to first fill the surface
                // 8 and 24 bit depth pngs did not require this
                // https://stackoverflow.com/questions/20587999/sdl-blitscaled-doesnt-work
                SDL_FillRect(pScaleSurface, &targetDimensions, SDL_MapRGBA(pScaleSurface->format, 255, 0, 0, 255));
                if (SDL_BlitScaled(p32BPPSurface, NULL, pScaleSurface, NULL) < 0) {
                    printf("Error did not scale surface: %sn", SDL_GetError());
                    SDL_FreeSurface(pScaleSurface);
                    pScaleSurface = NULL;
                }
                else {
                    SDL_FreeSurface(pSurface);
                    pSurface = pScaleSurface;
                    width = pSurface->w;
                    height = pSurface->h;
                }
            }
            SDL_FreeSurface(p32BPPSurface);
            p32BPPSurface = NULL;
        }
        SDL_Texture * pTexture = SDL_CreateTextureFromSurface(pRenderer, pSurface);
        if (pTexture == NULL) {
            printf("Error image load: %sn", SDL_GetError());
        }
        else {
            SDL_SetTextureBlendMode(
                pTexture,
                SDL_BLENDMODE_BLEND);
            result = pTexture;
        }
        SDL_FreeSurface(pSurface);
        pSurface = NULL;
    }
    return result;
}

@kelter的评论让我更仔细地看待表面像素格式,位图在24BPP上工作,PNG在8BPP上加载而不起作用。尝试将目标表面更改为24或32 BPP,但这无济于事。我们已经在自动检测的位深度生成了PNG,将其设置为8或24,并在表面上执行蓝色的表面,尽管它不适用于32。@petruza的问答。

update 有点快地写下此答案,原始解决方案处理的BMP和8和24位PNG,但32位PNG没有渲染。@retired的忍者回答有关blit_scaled的另一个问题的回答建议在调用功能之前填充表面并对其进行整理,还有另一个问题与在新表面上设置alpha有关,可能与此相关(特别是如果您需要透明度),但请填充固体颜色对我来说已经足够了...