# sdl_img compares images in layouts instead of one at a time

> A C image viewer on SDL2 with multi-image layouts from single through oct, vim style thumbnail mode, a music player inspired library view, GIF scrubbing with speed control and a non-interleaving move option for comparing numbered sequences. Rotation is the one place it warns you, because stb_image_write cannot save GIFs.

**rswinkle/sdl_img** — A simple image viewer based on SDL2 and stb_image

- Repository: https://github.com/rswinkle/sdl_img
- Stars: 24 · Forks: 5
- Language: C
- License: MIT
- Published: 2026-08-27 · Updated: 2026-08-27 · Language: en
- Canonical page: https://hysenlabs.com/projects/rswinkle-sdl-img

## Basic use is one argument, and the directory comes along for the ride

Three invocations cover the normal cases:

```
sdl_img
sdl_img image_name.jpg
sdl_img -f image_name.jpg  (to start in full screen mode)
```

Run with no arguments, the viewer opens in File Selection mode because no images were supplied. Run with a filename, it opens that image plus every file with a recognized image extension in the same directory. Navigation then moves through those files in alphabetical order, and the comparison used for that ordering lives in `src/string_compare.c`, which the README links to alongside a plain-language description of what strcmp does to a human.

On Windows the binary is `sdl_img.exe`, and the suggested route is to set it as the default application per file type through the right click menu. Navigation keys are context sensitive: left and right step through images, or pan when the view is zoomed in, and Ctrl plus a direction key steps images even while zoomed.

## The layouts are addressed with Ctrl and a number, and 0 returns to all at once

The distinguishing feature is a set of layouts rather than one image at a time:

- CTRL + 1 single image mode
- CTRL + 2 double image mode
- CTRL + 4 quad image mode
- CTRL + 8 eight image mode
- CTRL + U thumbnail mode
- CTRL + I library mode

Inside a layout the basic controls act on every image at once, unless one image has been selected by pressing a digit from 1 to n, in which case cycling, zooming and fit apply only to that image. Pressing 0 returns to operating on all of them.

Switching up from a lower layout loads images to the right of the last one currently viewed. Switching down shows the first n images of the higher layout, where n is the lower count. The stated exception: with an image selected, going to single mode shows that selected image rather than the first.

Thumbnail mode is described as vim inspired and library mode as music player inspired, which is the author's shorthand for what those interactions feel like.

## The relative offsets option exists because alphabetical sorting breaks sequence comparison

By default, pressing next in an n-image mode displays the n sequential images after the current one, or before the first. The README is direct that this is annoying when comparing a sequence of tuples that do not interleave when sorted, and gives the case: with files named abc01.jpg, abc02.jpg and on, plus xyz01.jpg, xyz02.jpg and on, all of the abc files sort first.

So comparing abcX with xyzX in two-image mode, every press of next moves from [abcX, xyzX] to [xyzX+1, xyzX+2], because the walk has run off the end of the abc block. The fix is a preferences toggle named Preserve relative offsets in multimode movement, which gives [abcX, xyzX] to [abcX+1, xyzX+1] instead.

The author flags two open questions about it directly: a better name or description, with Independent movement mode offered as a possibility, and whether it should become the default behavior. Neither has been decided in the README.

## GIF frames get a progress bar, playback speed control and pause on hover

Animated GIFs carry their own control set, and the progress bar is only displayed when a GIF is being viewed in single image mode:

- CTRL + +/- and CTRL + Mouse Wheel speed up or slow down an animated GIF
- P pauses and unpauses
- Mouse over the progress bar pauses
- Wheel over the progress bar scrolls through frames
- Click and drag on the progress bar selects or scrolls frames

The viewer is built on stb_image.h with the animated GIF API extension suggested by the library's author, which is what makes frame level navigation possible rather than just play and stop.

The slideshow feature is described as smart enough to wait long enough to finish any GIF being displayed even when that takes longer than the delay you specified. F1 through F10 start a slideshow with one to ten second delays, ESC ends it, and every other control still works during a slideshow: starting one does not force fullscreen, and it will run in double, quad or oct image mode, or on a single image selected inside those modes.

## Saving a rotated image warns you, because GIF output is not supported

Rotation covers most of the transform operations: L and R rotate the current image 90 degrees left or right, Ctrl plus L or R rotates by an arbitrary number of degrees through a popup GUI, and H and V flip the current image horizontally or vertically.

The limitation is on the way back out. While rotation will rotate any image, the viewer only asks whether you are sure you want to save single frame images, because stb_image_write does not support GIFs at all, let alone animated ones. A file type is detected from the extension and written as that type where possible, with jpg as the default fallback.

So the transform operations are format agnostic in memory and the write path is not. Saving a rotated animated GIF is the case that the confirmation prompt exists for.

Playlist handling sits alongside this: S saves the current images to the current playlist, and Ctrl plus S removes them from it.

## Sorting and shuffling are single mode only, with four separate keys

Four sort orders plus a shuffle, all restricted to single image mode:

- M shuffles or mixes the images
- N sorts the images by file name
- CTRL+N sorts the images by file path
- Z sorts the images by size
- T sorts the images by last modified

The distinction between name and path is the useful one. Sorting by name groups everything called IMG_0001.jpg together regardless of folder, while sorting by path respects the directory structure, so shots from different cameras or dates stay in blocks.

File selection is separate from the list: O opens a new file, Ctrl plus O opens more, Delete deletes the current image and moves to the next in single mode, and Backspace removes the current image from the list and moves on without deleting it from disk. Home and End jump to the first and last image in the list, Space and Ctrl plus Space step forward and back, and A returns to actual size.

## The Makefile picks native instructions for Linux release builds

The build system targets three platforms through a PLAT variable defaulting to linux, with the alternatives cross_win for an MSYS2 clang ucrt64 cross compile environment and msys2 for a native MSYS2 mingw64 setup. The target binary is `sdl_img` except for cross_win, where it is `sdl_img.exe`.

The release configuration for linux is the line worth reading:

```
OPTS=-std=gnu99 -march=native -O2 -g -DNDEBUG
```

`c99` with `-march=native` and optimization is what a distribution wants and what a portable binary does not want, since the binary ends up tied to the instruction set of whatever machine ran the build. The msys2 and cross_win release lines use `-msse` instead of `-march=native` for exactly that reason. A sanitize configuration exists for linux and msys2 with address and undefined behavior sanitizers, but the Makefile notes they are not supported in the cross compile environment and are skipped there.

Dependencies come from pkg-config for sdl2 and libcurl, with the Lua headers under `lua-5.4.7/src` and vendored headers in `src/extlibs`. The version is set to 0.101.0 with a TODO noting it is duplicated in src/compile_constants.h.

## Conclusion

This suits anyone comparing batches of images rather than opening them one at a time, since the multi-image layouts and the preserve relative offsets preference exist for exactly the sequence comparison problem the README describes at length. People who just want to double click a JPEG and see it will find more here than they need, and the honest limitation is saving: rotation works on anything but writing a file does not, because stb_image_write does not support GIFs at all, so the viewer will warn before saving a single frame image. Check the Makefile before building, since the version string is duplicated in src/compile_constants.h and a release build uses -march=native, which produces binaries tied to the build machine's instruction set.

## FAQ

### Is SDL still being developed, and does that affect sdl_img?

This README targets SDL2, linked through pkg-config along with libcurl. The question of SDL's own development status is not addressed here, so what the repository documents is the SDL2 dependency and the two additional ones in the Makefile line.

### What is SDL and SDL2 in the context of sdl_img?

SDL is the windowing and input layer this C viewer is built on, and the build pulls its compile flags through pkg-config sdl2. Image decoding itself comes from stb_image.h, with the animated GIF API extension, so the two concerns are separate.

### How does sdl_img compare images across directories?

By default, next in an n-image mode walks sequential sorted positions, which breaks across separate name blocks. The preferences toggle Preserve relative offsets in multimode movement keeps matching items aligned, so [abcX, xyzX] advances to [abcX+1, xyzX+1].

### Can sdl_img save a rotated animated GIF?

No. Rotation applies to any image, but the viewer only prompts to confirm saving because stb_image_write does not support GIFs. The type is taken from the file extension where possible, with jpg as the default fallback.

## Sources

- [Official README](https://github.com/rswinkle/sdl_img#readme)
- [Project repository](https://github.com/rswinkle/sdl_img)
- [Release notes](https://github.com/rswinkle/sdl_img/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/rswinkle-sdl-img
