Using SDL2: Viewports
Tags: cpp, sdl
Published: 2019-01-05 16:00:36

When rendering a scene or world not everything will be inside the boundaries of the window. Only a partial region can be rendered and displayed to the user at a time. In SDL (and generally in GC) this region is also known as the viewport. Anything within the viewport is rendered and everything outside is ignored. Many games have a character that can move and perform operations within in a world. Depending on where the character moves, the viewport must move with it to keep the character in focus.

As an example to demonstrate viewports in SDL, we will have a program that can zoom and move around in a world that is basically an image of a house and tree.

In previous posts, we have the `Appplication` class that handles window events, updating entities and lastly rendering. However, unlike in the previous installments, we will have an extra method named `handle_zoom()`. This method handles the user zoom when the event `SDL_KEYDOWN` is detected.

```cpp
void Application::loop()
{
    bool keep_window_open = true;
    while(keep_window_open)
    {
        while(SDL_PollEvent(&m_window_event) > 0)
        {
            switch(m_window_event.type)
            {
                case SDL_QUIT:
                    keep_window_open = false;
                    break;
                
                case SDL_KEYDOWN:
                    handle_zoom();
            }
        }

        update(1.0/60.0);
        draw();
    }
}
```

Because I decided on using transparency for the image's background, I had to use the SDL2 image extension library for opening PNG images. In the constructor, the image library is initialized and the image we will use is loaded into memory.

```cpp
Application::Application()
{
    // ...

    m_render_viewport = { 0, 0, 680, 480 };

    IMG_Init(IMG_INIT_PNG);

    SDL_Surface *image_surface = IMG_Load("world.png");
    m_image = SDL_CreateTextureFromSurface(m_window_renderer, image_surface);
    SDL_FreeSurface(image_surface);
}
```

In past articles when using an image, we loaded the image's pixel data into an `SDL_Surface *`, converted it into an `SDL_Texture`, and finally, queried for the texture's width and height. We do the first two here, but not the last. `m_render_viewport` takes care of what part of the image gets rendered onto the screen.

Now finally, I can show the method that makes all the magic happens:

```cpp
void Application::handle_zoom()
{
    unsigned char const *keys = SDL_GetKeyboardState(nullptr);
    if(keys[SDL_SCANCODE_UP])
    {
        m_render_viewport.w += 5;
        m_render_viewport.h += 5;
    } else if(keys[SDL_SCANCODE_DOWN])
    {
        m_render_viewport.w -= 5;
        m_render_viewport.h -= 5;
    }
    else if(keys[SDL_SCANCODE_W])
    {
        m_render_viewport.y += 20;
        if(m_render_viewport.y > 0)
            m_render_viewport.y = 0;
    } else if(keys[SDL_SCANCODE_S])
    {
        m_render_viewport.y -= 20;
        if(m_render_viewport.y < -720)
            m_render_viewport.y = -720;
    } else if(keys[SDL_SCANCODE_A])
    {
        m_render_viewport.x += 20;
    } else if(keys[SDL_SCANCODE_D])
    {
        m_render_viewport.x -= 20;
        if(m_render_viewport.x < -1280)
            m_render_viewport.x = -1280;
    }
}
```

This merely manipulates the viewports location and dimensions. In the `Application::draw()` method, we set the viewport and render.

```cpp
void Application::draw()
{
    SDL_SetRenderDrawColor(m_window_renderer, 255, 255, 255, 255);
    SDL_RenderClear(m_window_renderer);
    SDL_RenderCopy(m_window_renderer, m_image, nullptr, nullptr);
    SDL_RenderSetViewport(m_window_renderer, &m_render_viewport);
    SDL_RenderPresent(m_window_renderer);
}
```

Compile and run.

![](https://thepracticaldev.s3.amazonaws.com/i/7w774dxyv94tzlzzmwk6.png)

Of course, you can move about and zoom in and out.

## What's next

Nothing is explicitly scheduled for the next post, but you can think it as a surprise.

Github repository: https://github.com/Noah11012/sdl2-tutorial-code