Skip to content

Commit 38ec67c

Browse files
authored
Save the test output to an image file (#166)
1 parent 20f645d commit 38ec67c

19 files changed

+1980
-19
lines changed

.github/workflows/ccpp.yml

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,22 @@ name: C/C++ CI
22

33
on:
44
push:
5-
branches: [ master ]
5+
branches: [master]
66
pull_request:
7-
branches: [ master ]
7+
branches: [master]
88

99
jobs:
1010
build:
11-
1211
runs-on: ubuntu-latest
1312

1413
steps:
15-
- uses: actions/checkout@v4
16-
- name: make test
17-
run: make -C tests test
14+
- uses: actions/checkout@v4
15+
- name: make test
16+
run: make -C tests test
17+
- name: Upload test images
18+
uses: actions/upload-artifact@v4
19+
if: always()
20+
with:
21+
name: test-images
22+
path: tests/output/
23+
retention-days: 7

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,6 @@ modules.order
5050
Module.symvers
5151
Mkfile.old
5252
dkms.conf
53+
54+
# Test image output
55+
tests/output/

tests/Makefile

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,49 +20,49 @@ SRCS = \
2020

2121
all: test_fill_polygon test_polygon test_fill_rectangle test_rectangle test_pixel test_line test_hline test_vline test_circle test_ellipse test_fill_ellipse test_clip test_blit test_fontx test_char
2222

23-
test_fill_polygon: test_fill_polygon.c $(SRCS)
23+
test_fill_polygon: test_fill_polygon.c save_image.c $(SRCS)
2424
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
2525

26-
test_polygon: test_polygon.c $(SRCS)
26+
test_polygon: test_polygon.c save_image.c $(SRCS)
2727
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
2828

29-
test_fill_rectangle: test_fill_rectangle.c $(SRCS)
29+
test_fill_rectangle: test_fill_rectangle.c save_image.c $(SRCS)
3030
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
3131

32-
test_rectangle: test_rectangle.c $(SRCS)
32+
test_rectangle: test_rectangle.c save_image.c $(SRCS)
3333
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
3434

35-
test_pixel: test_pixel.c $(SRCS)
35+
test_pixel: test_pixel.c save_image.c $(SRCS)
3636
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
3737

38-
test_line: test_line.c $(SRCS)
38+
test_line: test_line.c save_image.c $(SRCS)
3939
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
4040

41-
test_hline: test_hline.c $(SRCS)
41+
test_hline: test_hline.c save_image.c $(SRCS)
4242
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
4343

44-
test_vline: test_vline.c $(SRCS)
44+
test_vline: test_vline.c save_image.c $(SRCS)
4545
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
4646

47-
test_circle: test_circle.c $(SRCS)
47+
test_circle: test_circle.c save_image.c $(SRCS)
4848
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
4949

50-
test_ellipse: test_ellipse.c $(SRCS)
50+
test_ellipse: test_ellipse.c save_image.c $(SRCS)
5151
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
5252

53-
test_fill_ellipse: test_fill_ellipse.c $(SRCS)
53+
test_fill_ellipse: test_fill_ellipse.c save_image.c $(SRCS)
5454
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
5555

5656
test_clip: test_clip.c $(SRCS)
5757
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
5858

59-
test_blit: test_blit.c $(SRCS)
59+
test_blit: test_blit.c save_image.c $(SRCS)
6060
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
6161

6262
test_fontx: test_fontx.c ../src/fontx.c
6363
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
6464

65-
test_char: test_char.c ../src/hagl_char.c ../src/fontx.c $(SRCS)
65+
test_char: test_char.c save_image.c ../src/hagl_char.c ../src/fontx.c $(SRCS)
6666
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
6767

6868
test: test_fill_polygon test_polygon test_fill_rectangle test_rectangle test_pixel test_line test_hline test_vline test_circle test_ellipse test_fill_ellipse test_clip test_blit test_fontx test_char
@@ -84,5 +84,6 @@ test: test_fill_polygon test_polygon test_fill_rectangle test_rectangle test_pix
8484

8585
clean:
8686
rm -f test_fill_polygon test_polygon test_fill_rectangle test_rectangle test_pixel test_line test_hline test_vline test_circle test_ellipse test_fill_ellipse test_clip test_blit test_fontx test_char
87+
rm -rf output
8788

8889
.PHONY: all test clean

tests/save_image.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
3+
MIT License
4+
5+
Copyright (c) 2026 Mika Tuupola
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in all
15+
copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
SOFTWARE.
24+
25+
-cut-
26+
stb_image_write implementation unit. Exactly one translation unit must
27+
define STB_IMAGE_WRITE_IMPLEMENTATION before including the header.
28+
29+
This file is part of the HAGL graphics library:
30+
https://github.com/tuupola/hagl
31+
32+
SPDX-License-Identifier: MIT
33+
34+
*/
35+
36+
#define STB_IMAGE_WRITE_IMPLEMENTATION
37+
#include "stb_image_write.h"

tests/save_image.h

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
3+
MIT License
4+
5+
Copyright (c) 2026 Mika Tuupola
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in all
15+
copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
SOFTWARE.
24+
25+
-cut-
26+
Helper for saving hagl_bitmap_t framebuffers as PNG files via
27+
stb_image_write. Used by the test suite for visual debugging.
28+
29+
This file is part of the HAGL graphics library:
30+
https://github.com/tuupola/hagl
31+
32+
SPDX-License-Identifier: MIT
33+
34+
*/
35+
36+
#ifndef SAVE_IMAGE_H
37+
#define SAVE_IMAGE_H
38+
39+
#include <stdint.h>
40+
#include <string.h>
41+
#include <sys/stat.h>
42+
43+
#include "hagl/bitmap.h"
44+
#include "stb_image_write.h"
45+
46+
/*
47+
* Convert an RGB565 framebuffer to RGB888 and write it as a PNG file.
48+
* Creates the parent directory if it does not exist.
49+
*/
50+
static void save_image(const hagl_bitmap_t *bitmap, const char *filename) {
51+
uint32_t pixel_count = bitmap->width * bitmap->height;
52+
uint8_t rgb[pixel_count * 3];
53+
uint16_t *src = (uint16_t *)bitmap->buffer;
54+
55+
for (uint32_t i = 0; i < pixel_count; i++) {
56+
uint16_t pixel = src[i];
57+
uint8_t r5 = (pixel >> 11) & 0x1F;
58+
uint8_t g6 = (pixel >> 5) & 0x3F;
59+
uint8_t b5 = pixel & 0x1F;
60+
61+
rgb[i * 3 + 0] = (r5 * 527 + 23) >> 6;
62+
rgb[i * 3 + 1] = (g6 * 259 + 33) >> 6;
63+
rgb[i * 3 + 2] = (b5 * 527 + 23) >> 6;
64+
}
65+
66+
/* Extract directory from filename and create it if needed. */
67+
char dir[256];
68+
const char *last_slash = NULL;
69+
for (const char *p = filename; *p; p++) {
70+
if (*p == '/') {
71+
last_slash = p;
72+
}
73+
}
74+
if (last_slash) {
75+
size_t len = last_slash - filename;
76+
if (len < sizeof(dir)) {
77+
memcpy(dir, filename, len);
78+
dir[len] = '\0';
79+
mkdir(dir, 0755);
80+
}
81+
}
82+
83+
stbi_write_png(filename, bitmap->width, bitmap->height, 3, rgb, bitmap->width * 3);
84+
}
85+
86+
#endif /* SAVE_IMAGE_H */

0 commit comments

Comments
 (0)