【发布时间】:2017-04-12 12:33:27
【问题描述】:
我们希望通过使用 SDL_Image 加载图像来创建 SDL 表面,如果尺寸超过限制,则调整表面大小。
我们需要这样做的原因是在 Raspbian SDL 上从表面创建纹理时抛出错误('纹理尺寸限制为 2048x2048')。虽然这是一个非常大的图像,但我们不希望用户关心图像大小,但我们希望为他们调整它的大小。虽然我们在 Windows 上没有遇到此限制,但我们正在尝试在 Windows 上开发解决方案,但在调整纹理大小时遇到了问题。
寻找解决方案有过类似的问题...:
- 2008 not SDL2 custom blitting
- 2010 use SDL_gfx
- 2008 can't be done use SDL_gfx, 2015 use SDL_BlitScaled, 2015 use SDL_RenderCopyEx
当前 SDL2 是否需要自定义 blitter 或 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 等主要图像文件格式),或者如果这是推荐的方法,请提供任何帮助!
【问题讨论】:
-
为了使用 blit 函数,您的硬件必须支持 GL_EXT_framebuffer_blit - 集成显卡和一些工作站 GPU 通常不支持。
-
源图像的像素格式是什么(每像素位数和掩码)?当源和目标表面格式的组合没有blitting实现时产生的错误;我建议为每个通道目标表面创建“正常”8 位(RGB 为 24 位,RGBA 为 32 位)并将其插入。
-
@bruno-ferreira 谢谢,你说得对,不支持扩展,所以我安装了显卡驱动程序并得到了它,但不幸的是,如果没有解决这个问题
-
@keltar 感谢您的评论为我指明了正确的方向,我将在答案中详细说明