2016-11-28 47 views
0

我们希望通过装载SDL_Image图像,以创建一个SDL表面,如果尺寸超过限制调整表面。SDL2调整表面

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

寻找一个解决方案也出现了类似的问题...:

是一个自定义的阻击器或必要SDL_gfx当前SDL2(那些回答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: %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) 

随着该位图传输功能不能正常工作,我们再与相同的图像作为位图(仅仅出口巴纽为.BMP),还是加载与SDL_Image和这两个功能的文件,SDL_BlitScaled比例为工作试过预计

不知道发生了什么事情错在这里(我们期望和需要像png格式的主要图像文件格式的支持),或者如果这是推荐的方法,任何帮助表示赞赏!

+1

为了使用blit功能,您的硬件必须支持GL_EXT_framebuffer_blit--集成显卡和一些工作站GPU通常不支持的功能。 –

+1

什么是源图像的像素格式(每像素和掩模的位数)?所述错误在源和目标表面格式的组合没有blitting实现时产生;我建议为每个通道的目标表面(RGB为24位,RGBA为32)创建“正常”8位,并将其传送至其中。 – keltar

+0

@ bruno-ferreira谢谢,你是对的,扩展不支持,所以我安装了显卡的驱动程序,并得到它,但不幸的是,如果没有解决这个问题 – Paddy

回答

1

TL; DR从@kelter的评论指出我朝着正确的方向和another stack overflow question给我一个解决方案:它的工作原理,如果你第一次BLIT到32bpp的表面,然后BlitScaled另一个32bpp的表面。这对于8位和24位深度PNG是有效的,32位不可见again another stack overflow question建议在blitting之前首先填充表面。

更新的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); 

      // 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: %s\n", 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: %s\n", 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: %s\n", SDL_GetError()); 
     } 
     else { 
      SDL_SetTextureBlendMode(
       pTexture, 
       SDL_BLENDMODE_BLEND); 

      result = pTexture; 
     } 

     SDL_FreeSurface(pSurface); 
     pSurface = NULL; 
    } 

    return result; 
} 

从@kelter的评论让我在表面的像素格式更密切地关注,位图在24 bpp的工作了,PNG图片都被加载在8bpp的,而不是工作。试图改变目标表面为24或32 bpp,但没有帮助。我们使用自动检测位深度生成了png,将其设置为8或24,并且使用相同的每像素位执行BlitScaled工作,尽管它不适用于32位。使用谷歌搜索转换错误导致来自@Petruza的问题和答案。

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