c - SDL2 图像缩放与抗锯齿
问题描述
所以我编写了这个程序,我用它来使用 SDL2 显示图像。老实说,我在几年前将它从 SDL1 移植到了 SDL2。我仍然使用这个程序,但我注意到缩放图像会导致非常可怕的锯齿。我正在使用 SDL2_gfx rotozoom 来缩放图像,并且启用了平滑标志,并且我也尝试过没有平滑的情况,它实际上看起来完全一样,所以我认为它不起作用。我在这里和互联网上的其他地方阅读了有关移植到 SDL2 的各种帖子,但至少对我来说,这个程序有点难以移植。我已经尝试将其转换为新的渲染器模型,但没有明显的方法可以在不同的位置渲染事物。哦,我仍然使用这个程序的主要原因是因为我使用 SDL_threads 来预加载下一个图像,这对 IMO 非常有用。
所以基本上我想要的是一种在显示图像之前平滑缩放图像的方法,任何帮助将不胜感激。
编辑:我已经更新了程序以使用纹理,但是缩放时的图像质量仍然很糟糕......请帮忙。
这是程序:
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_thread.h>
#include <SDL2/SDL2_rotozoom.h>
#include <GL/gl.h>
#define MAX_BUF 256
#define FPS_LIMIT 10 // in FPS
#define FPS_DELAY 1000/FPS_LIMIT // in ms
#define ZOOM_LIMIT 10
#define ZOOM(sw,iw) (((double) (sw) / (iw)) > ZOOM_LIMIT ? ZOOM_LIMIT : ((double) (sw) / (iw)))
static SDL_Window * window;
static SDL_Renderer * renderer;
static SDL_Texture * next_texture;
static SDL_DisplayMode dmode;
static SDL_Rect source_rect;
int load_next_image (void * data)
{
char * name = (char *) data;
SDL_Surface * temp = IMG_Load (name); // unfree
if (NULL == temp)
{
fprintf (stderr, "Failed to load image %s\n", name);
return 1;
}
source_rect.w = temp->w;
source_rect.h = temp->h;
next_texture = SDL_CreateTextureFromSurface(renderer, temp); // unfree
SDL_FreeSurface (temp); // free
if (NULL == next_texture)
{
fprintf (stderr, "Failed to convert image %s\n", name);
return 1;
}
return 0;
}
void draw_image (SDL_Texture * texture, SDL_Rect * dest, double zoom)
{
SDL_RenderClear(renderer);
SDL_Rect temp;
temp.x = dest->x;
temp.y = dest->y;
temp.w = dest->w * zoom;
temp.h = dest->h * zoom;
SDL_RenderCopyEx(renderer, texture, NULL, &temp, 0.0, NULL, SDL_FLIP_NONE);
SDL_RenderPresent(renderer);
SDL_UpdateWindowSurface (window);
}
int selector (const struct dirent * dir)
{
if (0 == strcmp (".", dir->d_name) || 0 == strcmp ("..", dir->d_name)) // add more as needed
{
return 0;
}
return 1;
}
int main (int argc, char * argv[])
{
// check arg
if (2 != argc)
{
fprintf (stderr, "Usage: %s /path/to/file.jpg\n", argv[0]);
return 1;
}
// get file and directory names
struct dirent ** dir;
char dir_name[MAX_BUF];
char * first_file = strrchr (argv[1], '/') + 1;
unsigned int length = strlen (argv[1]) - strlen (first_file);
strncpy (dir_name, argv[1], length);
dir_name[length] = 0;
int num_files = scandir (dir_name, &dir, selector, alphasort); // unfree
if (num_files < 1)
{
fprintf (stderr, "Failed to open directory %s\n", dir_name);
return 1;
}
// find first file index
int i;
int first_index = 0;
for (i = 0; i < num_files; i++)
{
if (0 == strcmp (first_file, dir[i]->d_name))
{
first_index = i;
break;
}
}
// init SDL
if (0 != SDL_Init (SDL_INIT_VIDEO))
{
fprintf (stderr, "Cannot initialize SDL: %s\n", SDL_GetError());
return 1;
}
// Settings
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 16);
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
glEnable(GL_MULTISAMPLE);
// create window and renderer
SDL_GetDesktopDisplayMode (0, &dmode);
window = SDL_CreateWindow (first_file, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, dmode.w, dmode.h, SDL_WINDOW_OPENGL); // unfree
if (NULL == window)
{
fprintf (stderr, "Cannot create window: %s\n", SDL_GetError());
SDL_Quit();
return 1;
}
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); //unfree
if (NULL == renderer)
{
fprintf (stderr, "Cannot create renderer: %s\n", SDL_GetError());
return 1;
}
// load current image
SDL_Texture * current_texture = NULL;
int status = load_next_image (argv[1]); // unfree unfree
SDL_Rect current_rect;
if (0 == status)
{
current_texture = next_texture;
current_rect = source_rect;
}
double zoom = 1.0;
int prev_x = 0;
int prev_y = 0;
int x[2] = {};
int y[2] = {};
unsigned int wide = 0;
// file list loop
for (i = first_index + 1; i <= num_files + first_index; i++)
{
// preload next image if not last
SDL_Thread * thread = NULL;
if (i != num_files + first_index)
{
strcat (dir_name, dir[i % num_files]->d_name);
thread = SDL_CreateThread (load_next_image, "lnit", dir_name); // unfree unfree
}
// handle current image
if (0 == status)
{
if (current_rect.w > current_rect.h)
{
wide = 1;
}
else
{
wide = 0;
}
current_rect.x = x[wide];
current_rect.y = y[wide];
draw_image (current_texture, ¤t_rect, zoom);
unsigned int go = 1;
// input loop
while (1 == go)
{
unsigned int frame_limit = SDL_GetTicks() + FPS_DELAY;
SDL_Event event;
while (SDL_PollEvent (&event))
{
switch (event.type)
{
case SDL_QUIT:
go = 0;
i = num_files + first_index;
break;
case SDL_KEYDOWN:
go = 0;
if (SDLK_ESCAPE == event.key.keysym.sym)
{
i = num_files + first_index;
}
break;
case SDL_MOUSEBUTTONDOWN:
switch (event.button.button)
{
case SDL_BUTTON_LEFT:
prev_x = event.button.x;
prev_y = event.button.y;
break;
case SDL_BUTTON_RIGHT:
if (1.0 == zoom)
{
zoom = ZOOM(dmode.w, current_rect.w);
x[wide] = (dmode.w / 2) - ((event.button.x - x[wide]) * zoom);
y[wide] = (dmode.h / 2) - ((event.button.y - y[wide]) * zoom);
current_rect.x = x[wide];
current_rect.y = y[wide];
draw_image (current_texture, ¤t_rect, zoom);
}
else
{
x[wide] = (dmode.w / 2) - ((event.button.x - x[wide]) / zoom);
y[wide] = (dmode.h / 2) - ((event.button.y - y[wide]) / zoom);
zoom = 1.0;
current_rect.x = x[wide];
current_rect.y = y[wide];
draw_image (current_texture, ¤t_rect, zoom);
}
break;
}
break;
case SDL_MOUSEBUTTONUP:
if (SDL_BUTTON_LEFT == event.button.button)
{
x[wide] += event.button.x - prev_x;
y[wide] += event.button.y - prev_y;
current_rect.x = x[wide];
current_rect.y = y[wide];
draw_image (current_texture, ¤t_rect, zoom);
}
break;
case SDL_WINDOWEVENT:
if (SDL_WINDOWEVENT_EXPOSED == event.window.event)
{
SDL_UpdateWindowSurface (window);
}
break;
}
}
// delay
unsigned int ticks = SDL_GetTicks();
if (frame_limit < ticks)
{
continue;
}
if (frame_limit > ticks + FPS_DELAY)
{
SDL_Delay (FPS_DELAY);
}
else
{
SDL_Delay (frame_limit - ticks);
}
}
SDL_DestroyTexture (current_texture); // free
}
// switch to next image if not last
if (i != num_files + first_index)
{
SDL_WaitThread (thread, &status);
dir_name[length] = 0;
if (0 == status)
{
SDL_SetWindowTitle (window, dir[i % num_files]->d_name);
current_texture = next_texture;
current_rect = source_rect;
}
}
}
// free next image if not last
if (0 == status && i != num_files + first_index + 1)
{
SDL_DestroyTexture (current_texture); // free
}
SDL_DestroyWindow (window); // free
SDL_DestroyRenderer (renderer); // free
SDL_Quit();
free (dir); // free
return 0;
}
解决方案
好的,所以我想通了。如果缩放小于 1.0,您应该使用 shrinkSurface() 而不是 rotozoomSurface(),因为这实际上是导致问题的原因。shrinkSurface() 使用平均方法来缩小图像,而 rotozoomSurface() 使用线性插值。它需要更多的挖掘,但我很高兴我弄明白了。再次感谢。
int load_next_image (void * data)
{
char * name = (char *) data;
SDL_Surface * temp = IMG_Load (name); // unfree
if (NULL == temp)
{
fprintf (stderr, "Failed to load image %s\n", name);
return 1;
}
next_image = SDL_ConvertSurface (temp, screen->format, 0); // unfree
SDL_FreeSurface (temp); // free
if (NULL == next_image)
{
fprintf (stderr, "Failed to convert image %s\n", name);
return 1;
}
double zoom = ZOOM(dmode.w, next_image->w);
if (zoom < 1)
{
next_zoomed = shrinkSurface (next_image, next_image->w / dmode.w , next_image->w / dmode.w); // unfree
}
else if (zoom > 1)
{
next_zoomed = rotozoomSurface (next_image, 0.0, zoom, SMOOTHING_ON); // unfree
}
else
{
next_zoomed = next_image;
}
if (NULL == next_zoomed)
{
fprintf (stderr, "Failed to zoom image %s\n", name);
return 1;
}
return 0;
}
推荐阅读
- c# - .NET Core 3 使用 WinUI
- python - 如何使用 Python 3 脚本在浏览器中获取所有存储的 cookie?
- maple - 使用分段 MAPLE 2018 时,如何在列表元素之间插入“和”(代替逗号)
- pandas - 按包含多个字符串的标题过滤 Pandas 数据框列
- c# - 如何删除列表中的项目列表?
- javascript - 如何在hybris中自动保存?
- swift - 带有聚合运算符的数组过滤器
- vb.net - 我如何从变量中进行拆分
- visual-studio - Visual Studio 负载测试 - 在指定时间运行测试组合
- wmi - C++/CX WMI PropertyData 数组类型