2016-11-28 79 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有關(可能與此相關)(特別是如果您需要透明度),但填充純色對我來說已經夠了......現在。