File: ARCHITECTURE.md

package info (click to toggle)
pngtools 1.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 928 kB
  • sloc: ansic: 871; sh: 684; python: 558; makefile: 32
file content (192 lines) | stat: -rw-r--r-- 8,146 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# pngtools Architecture

## Overview

pngtools is a suite of four command-line utilities for inspecting and
manipulating PNG image files. It was modelled on libtiff's tifftools
suite and is written in C against the libpng library. The project dates
from 2001 and uses GNU Autotools for its build system.

## Tools

### pnginfo (pnginfo.c, ~440 lines)

The primary tool. Reads PNG files via libpng and displays metadata:
image dimensions, bit depth, colour type, interlacing, compression,
resolution, and embedded text chunks. Has three modes:

- Default: display PNG metadata with descriptive labels
- `-t`: use tiffinfo-compatible label names
- `-d`: dump the image bitmap as hex pixel triples
- `-D`: verify bitmap extraction without displaying it

The bitmap dump includes run-length compression for zero-valued
pixels, printing them once and then showing a repeat count.

### pngchunks (pngchunks.c, ~190 lines)

Lists the raw chunk structure of a PNG file. Unlike pnginfo, this tool
does **not** use libpng -- it memory-maps the file with `mmap()` and
manually walks the PNG chunk linked list by parsing the 8-byte chunk
headers (4-byte length + 4-byte type). For IHDR chunks it also parses
and displays the IHDR payload fields. Each chunk's four-character name
is decoded to show its case-encoded properties (critical/ancillary,
public/private, etc.). All pointer advances are bounds-checked against
the mmap'd region to prevent segfaults on malformed input.

### pngchunkdesc (pngchunkdesc.c, ~48 lines)

A stdin/stdout filter that reads four-character PNG chunk names and
decodes the case information embedded in each letter. Uses the shared
`chunk_meanings` lookup table from `chunk_meanings.h`.

### pngcp (pngcp.c + pngread.c + pngwrite.c + inflateraster.c, ~350 lines total)

Copies a PNG file while optionally changing bit depth (`-d`) and/or
the number of samples per pixel (`-s`). The pipeline is:

```
readimage() -> inflateraster() -> writeimage()
```

- **pngread.c / readimage()**: Opens the input PNG via libpng,
  expands palettes and sub-byte samples, returns the raw raster buffer
  along with width, height, bitdepth, and channel count.

- **inflateraster.c / inflateraster()**: Transforms the raster to the
  target bit depth and/or channel count. Processes all pixels in a
  single pass, handling both bitdepth scaling and channel mapping
  together. Supports multi-byte samples (e.g. 16-bit) using
  big-endian byte order. Channel mapping supports gray-to-RGB
  expansion, alpha addition/removal, and direct channel copying.

- **pngwrite.c / writeimage()**: Writes the output PNG via libpng.
  Derives the PNG colour type from the channel count (1=gray,
  2=gray+alpha, 3=RGB, 4=RGBA).

## Header Files

- **pngcp.h**: Declares the three pngcp helper functions
  (`readimage`, `writeimage`, `inflateraster`). Uses `png_uint_32`
  for width/height to match the libpng API.

## Build System

GNU Autotools (autoconf + automake):

- **configure.ac**: Checks for a C compiler, libpng (`png_read_image`
  symbol), libm (`atan` symbol), and `png.h`.
- **Makefile.am**: Builds four binaries. pnginfo and pngchunks are
  standalone; pngcp links pngread.c, pngwrite.c, and inflateraster.c.
  pngcp additionally links libm. `EXTRA_DIST` includes the test
  suite, test images, stestr config, and test requirements so that
  `make dist` tarballs contain everything needed to run tests.
- **man/**: DocBook SGML man page sources (`man/*.sgml.in`), built
  to man pages via `docbook2man`. The `.sgml.in` templates use
  `@PACKAGE_VERSION@` which `configure` substitutes from
  `AC_INIT` to generate the `.sgml` files. Each file contains
  `<refmiscinfo>` elements in `<refmeta>` that set the man page
  footer (source, version, manual section).

Build steps: `aclocal && autoconf && automake --add-missing &&
autoreconf && ./configure && make`

## Code Quality

- **clang-format**: Enforces consistent formatting using the GNU
  base style. Configuration in `.clang-format`. Run
  `scripts/check-format.sh fix` to auto-format, or
  `scripts/check-format.sh` to check without modifying.

- **cppcheck**: Static analysis for warnings and style issues.
  Runs with `--enable=warning,style` in both CI and pre-commit.

- **shellcheck**: Lints shell scripts in `scripts/`.

- **actionlint**: Validates GitHub Actions workflow YAML files.

- **CodeQL**: GitHub's semantic code analysis for security
  vulnerabilities and code quality. Runs as a separate CI
  workflow (`.github/workflows/codeql.yml`) with the
  `security-and-quality` query suite. Also runs weekly on a
  schedule to catch newly discovered vulnerability patterns.

## CI

Three GitHub Actions workflows:

**CI** (`.github/workflows/c.yml`): runs actionlint, shellcheck,
clang-format, and cppcheck checks first, then builds on Ubuntu
with libpng-dev and docbook-utils. Runs the full autotools chain,
configure, make, and make install to a staging directory. Then
sets up a Python venv, installs test dependencies, generates test
images, and runs the full test suite via stestr.

**CodeQL** (`.github/workflows/codeql.yml`): runs on push, PR,
and weekly schedule. Performs deep semantic security and quality
analysis of the C source code.

**Release** (`.github/workflows/release.yml`): manually triggered
via `workflow_dispatch`. Takes a version input (must match
`configure.ac`), builds and tests the project, creates a `make
dist` tarball, creates a Sigstore-signed git tag via gitsign,
publishes a GitHub Release with the tarball attached, and attests
the tarball provenance with `actions/attest-build-provenance`.

Five pre-commit hooks run automatically before each commit:
1. **actionlint** -- validates workflow YAML
2. **shellcheck** -- lints shell scripts
3. **clang-format** -- checks source formatting
4. **cppcheck** -- static analysis
5. **build-and-test** -- full build and test cycle

## Test Data

Five sample PNG files covering different configurations:

| File                      | Dimensions | Bit Depth | Colour Type |
|---------------------------|------------|-----------|-------------|
| sample.png                | 640x480    | 8         | RGB         |
| input.png                 | 256x256    | 8         | RGB         |
| foursamplesperpixel.png   | 32x32      | 8         | RGBA        |
| multibytesample.png       | 32x32      | 16        | Grayscale   |
| grayscale.png             | 32x32      | 4         | Grayscale   |

Additional generated test images in `testdata/` (created by
`tests/generate_test_images.py`):

| File                   | Dimensions | Bit Depth | Colour Type        |
|------------------------|------------|-----------|--------------------|
| paletted.png           | 32x32      | 8         | Paletted           |
| interlaced.png         | 32x32      | 8         | RGB (Adam7)        |
| with_text.png          | 32x32      | 8         | RGB + tEXt chunks  |
| with_transparency.png  | 32x32      | 8         | Paletted + tRNS    |

## Test Suite

55 automated tests using Python testtools + stestr, organised into
four test modules matching the four tools:

- `tests/test_pnginfo.py` -- metadata, tiff mode, bitmap dump, errors
- `tests/test_pngchunks.py` -- chunk listing, IHDR parsing, errors
- `tests/test_pngchunkdesc.py` -- case-bit decoding
- `tests/test_pngcp.py` -- copy, bitdepth/channel changes, errors

Tests run the compiled binaries via subprocess and assert on exit
codes and stdout/stderr content. See README.md for how to run them.

## Known Bugs and Issues

- **Issue #3**: No release tags exist on the GitHub repository.
  Downstream packagers have requested at least a `0.4` and `1.0`
  tag. This is a release management task, not a code bug.

## Dependencies

- **libpng** (required): PNG reading/writing for pnginfo, pngcp
- **libm** (required): math functions for pngcp (pow in inflateraster)
- **docbook-utils** (optional): man page generation from SGML sources
- **clang-format** (development): code formatting enforcement
- **cppcheck** (development): static analysis
- **shellcheck** (development): shell script linting
- **actionlint** (development): GitHub Actions workflow validation