A small C-based PPM image generator + viewer that uses SDL2 to display images. The repository contains two main programs:
imgGen.c— a simple image generator that produces a PPM image (saved toimages/output.ppmby default).imgV.c— an image viewer that loads a PPM file (by defaultimages/output.ppm) and displays it in an SDL2 window.
This README explains how to set up the project, build the binaries, how the programs work, the PPM image format and how to use the tools.
- Requirements
- File structure
- Build / Setup (step-by-step)
- Build commands (manual)
- How it works (internals)
- What is PPM? (PPM format explained)
- Usage
- Troubleshooting
- Limitations & Notes
- License
- A C compiler (gcc/clang)
- SDL2 development libraries and headers
- On Linux/macOS:
SDL2development package andpkg-configorsdl2-config. - On Windows (MSYS2):
mingw-w64-x86_64-SDL2and development tooling.
- On Linux/macOS:
- make (optional) — the repository contains a
Makefileto simplify builds. - Basic POSIX utilities if building on Linux/macOS.
.gitignore— files to ignore.Makefile— helper to build the programs.imgGen.c— image generator (writes PPM toimages/output.ppm).imgV.c— image viewer (readsimages/output.ppmby default and displays with SDL2).images/— image outputs (the viewer expectsimages/output.ppmby default).README.md— this file.
Follow the steps for your platform.
-
Update package lists: sudo apt update
-
Install build and SDL2 development packages: sudo apt install build-essential pkg-config libsdl2-dev
-
Clone the repo (if you haven't already) and change into the project directory: git clone https://github.com/Jayesh-Dev21/img_viewer.git cd img_viewer
-
Build using make (recommended) or manual commands (below): make
-
Install Homebrew if needed: https://brew.sh
-
Install SDL2 and pkg-config: brew install sdl2 pkg-config
-
Clone and build: git clone https://github.com/Jayesh-Dev21/img_viewer.git cd img_viewer make
-
Install MSYS2 and update packages: https://www.msys2.org
-
Open an MSYS2 MinGW 64-bit shell and install packages: pacman -Syu pacman -S mingw-w64-x86_64-toolchain mingw-w64-x86_64-SDL2 mingw-w64-x86_64-pkg-config
-
Clone and build (from MinGW 64-bit shell): git clone https://github.com/Jayesh-Dev21/img_viewer.git cd img_viewer make
If you want to compile manually (instead of using the Makefile), here are example commands.
Using pkg-config:
-
Build viewer: gcc -o imgV imgV.c $(pkg-config --cflags --libs sdl2)
-
Build generator: gcc -o imgGen imgGen.c
Using sdl2-config (if pkg-config not available):
gcc -o imgV imgV.c sdl2-config --cflags --libs
Notes:
imgGen.ctypically doesn't require SDL2.imgV.cuses SDL2 and must be linked with SDL2 libraries. If your linker complains, ensure SDL2 dev libs are installed and pkg-config or sdl2-config is in PATH.
High-level summary of what each program does:
-
imgGen
- Generates a PPM-format image and writes it to
images/output.ppm. - The generator produces a simple image pattern (gradient/blocks) and writes the header and pixel bytes.
- Generates a PPM-format image and writes it to
-
imgV
- Loads the image file into memory (reads whole file).
- Parses a small header portion to obtain:
- PPM magic bytes (e.g.,
P6) - Image width
- Image height
- Maximum color value
- PPM magic bytes (e.g.,
- Copies the remaining file bytes into an in-memory pixel buffer.
- Creates an SDL2 window sized to the image dimensions (plus small margins).
- Renders the image by iterating over the pixel buffer and drawing pixels to the SDL window surface.
Important implementation note:
- The current loader in
imgV.cis a simple parser that reads width/height/maxval from fixed byte offsets (fixed-width numeric fields). It is not a full PPM parser that handles arbitrary whitespace or comments. For best results useimgGenor generate files matching the expected header layout, or updateimgV.cto use robust header parsing.
PPM (Portable Pixmap) is part of the Netpbm family (PBM/PGM/PPM). It is a simple image file format that stores RGB images in a straightforward, minimally formatted way. PPM is commonly used for learning and testing image I/O because the format is easy to implement.
Key points:
- Two main variants:
- ASCII/form (P3): pixel values are stored as human-readable ASCII numbers.
- Binary form (P6): pixel values are stored as binary bytes — much smaller and faster to read.
- This project uses P6 (binary) images.
PPM file structure (P6):
- Magic number:
P6followed by whitespace (this identifies the format as binary PPM). - Width and height: ASCII decimal integers (one or more whitespace characters between them).
- Maximum color value: ASCII decimal integer (max sample value per channel).
- A single whitespace character (usually newline).
- Binary pixel data: width * height * 3 samples (RGB) follow immediately after that whitespace.
Example (human-readable):
P6
# Optional comment lines starting with '#'
800 600
255
<binary pixel data follows>
Important details and rules:
- Comments: Any line beginning with
#is a comment and should be ignored by a parser. Comments can appear after the magic number and before the pixel data, and between header fields. - Whitespace: Header fields are separated by whitespace (spaces, tabs, CR, LF). Parsers should skip arbitrary whitespace.
- Pixel bytes per sample:
- If
maxval <= 255: each sample (R, G, B) is 1 byte (unsigned). - If
maxval > 255: each sample is 2 bytes, stored most-significant byte first (big-endian) — so each channel consumes two bytes.
- If
- Pixel order: pixels are stored left-to-right, top-to-bottom. For each pixel, the order is R, then G, then B.
- Total pixel bytes: width * height * 3 * (1 or 2 bytes per sample, depending on maxval).
Common pitfalls:
- Many simple viewers assume
maxvalis 255 (1 byte per channel). If a file uses a larger maxval (>255), the loader must read two-byte samples. - Parsers must skip comments and arbitrary whitespace. A parser that reads fixed offsets (e.g., reads width/height from fixed bytes) will fail on valid but formatted PPM files.
- Some tools produce P3 ASCII PPMs — binary viewers expecting P6 will not handle them.
Creating PPMs from other images:
- ImageMagick:
convert input.png ppm:output.ppm
(or use explicit
-compressflags to control output; ImageMagick typically outputs P6 binary PPMs) - Netpbm tools (pnmwrite, etc.) can also create PPM files.
After building:
-
Ensure
images/directory exists: mkdir -p images -
Run the generator: ./imgGen
This should produce images/output.ppm by default.
The viewer supports two optional positional command-line arguments:
Usage:
- ./imgV [filepath] [display_time_seconds]
Arguments:
- filepath (optional): path to the PPM file to open. If omitted, the program uses the default path:
images/output.ppm. - display_time_seconds (optional): how long (in seconds) the viewer will display the image before automatically closing. If omitted, the default is 10 seconds.
Notes:
./imgVrefers to the compiled ELF binary on Linux/macOS. On Windows, run the built executableimgV.exefrom a suitable shell.- The viewer will open the SDL window sized to the image. If you close the window manually, the program will exit early even if the timeout hasn't elapsed.
- The display time is an integer number of seconds. The viewer currently expects an integer value; non-integer or invalid values will fall back to the default behavior or may be ignored depending on the implementation.
Examples:
-
Use default image and default time (10s): ./imgV
-
Specify a different filepath and display for 10 seconds: ./imgV images/images.ppm 10
-
Specify a custom image and show it for 5 seconds: ./imgV images/output.ppm 5
If you pass only one argument and it's a number, the program may interpret it as the filepath (depending on implementation). The recommended form is to always pass the filepath first, then the display time.
- The viewer's loader in this repository expects header fields in fixed-width positions and will not tolerate arbitrary comments or variable whitespace. To use your own PPM files:
- Ensure they are P6 (binary) format.
- Use
maxval≤ 255 (so samples are one byte each) unless you updateimgV.cto support two-byte samples. - Keep the header simple (no comments) and write width/height/maxval in the short fixed-width format used by the generator, or modify
imgV.cto parse whitespace and comments properly.
To support arbitrary valid P6 files, update imgV.c to:
- Read tokens (skip whitespace), ignoring lines starting with
#, to extract magic, width, height, and maxval. - After reading maxval and the single whitespace that follows, read the binary pixel data accordingly.
If you need a quick generator that creates a PPM file in the format this viewer expects, here's an example Python script that creates a simple gradient image and writes the header in fixed-width fields so the viewer can parse it:
# save as images/make_compatible_ppm.py or run from repo root
import os
os.makedirs('images', exist_ok=True)
width = 200
height = 120
maxval = 255
# The viewer expects fixed-width ASCII fields (4 chars each) for width/height/maxval
# and pixel data starting after a short fixed header. We construct the file accordingly.
header = b"P6 " # 2 magic bytes plus a separator
header += f"{width:4d}".encode('ascii') # 4 bytes for width
header += f"{height:4d}".encode('ascii') # 4 bytes for height
header += f"{maxval:4d}".encode('ascii') # 4 bytes for maxval
# After this, pixel data starts. Build a simple gradient RGB pattern:
pixel_data = bytearray()
for y in range(height):
for x in range(width):
r = int((x / (width - 1)) * maxval)
g = int((y / (height - 1)) * maxval)
b = int(((x + y) / (width + height - 2)) * maxval)
pixel_data += bytes((r, g, b))
with open('images/output.ppm', 'wb') as f:
f.write(header)
f.write(pixel_data)
print("Wrote images/output.ppm")Run: python3 images/make_compatible_ppm.py Then: ./imgV
-
SDL2 link errors:
- Ensure SDL2 development package is installed.
- Use
pkg-config --cflags --libs sdl2orsdl2-config --cflags --libsin the compile command. - On Debian/Ubuntu:
sudo apt install libsdl2-dev. - On macOS:
brew install sdl2.
-
images/output.ppm not found:
- Ensure
imgGencreatedimages/output.ppm, or use the example Python script above to create one. - The viewer currently uses a hard-coded default path (
images/output.ppm) if no filepath is provided. To view a different path, pass it as the first argument or editimgV.c.
- Ensure
-
Incorrect image dimensions or garbage pixels:
- If your PPM header doesn't match the exact byte layout the viewer expects, parsing may fail. Use the included generator or the example Python script above to produce compatible output.
- Consider improving header parsing in
imgV.cto support standard PPM P6 files with arbitrary whitespace and comments.
- The current header parser in
imgV.cis simplistic and expects fields at fixed offsets. Standard PPM files may include variable whitespace and comments — the viewer may not parse them correctly. - Error handling is minimal; a corrupt or incompatible file may cause undefined behavior.
- The viewer draws pixels by iterating in software; for very large images performance may be limited by this approach.
- The project is intended as a small learning/demo project rather than a production-grade image viewer.
Specify a license for the repository (e.g., MIT). If you want me to add a LICENSE file, tell me which license to use and I can prepare it.
