首页 > 解决方案 > 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, &current_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, &current_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, &current_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, &current_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;
}

标签: cimageimage-processingsdl

解决方案


好的,所以我想通了。如果缩放小于 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;
}

推荐阅读