Skip to content

Commit 103d29c

Browse files
authored
Add tests for line (#138)
* Add test for horizontal line * Add tests for vertical line * Add tests for diagonal line * Add test for single pixel line * Add test for line clipping * Add tests for custom clip window
1 parent 7848989 commit 103d29c

2 files changed

Lines changed: 360 additions & 3 deletions

File tree

tests/Makefile

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ SRCS = \
1717
../src/hagl_triangle.c \
1818
../src/rgb565.c
1919

20-
all: test_fill_polygon test_polygon test_fill_rectangle test_rectangle test_pixel
20+
all: test_fill_polygon test_polygon test_fill_rectangle test_rectangle test_pixel test_line
2121

2222
test_fill_polygon: test_fill_polygon.c $(SRCS)
2323
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
@@ -34,14 +34,18 @@ test_rectangle: test_rectangle.c $(SRCS)
3434
test_pixel: test_pixel.c $(SRCS)
3535
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
3636

37-
test: test_fill_polygon test_polygon test_fill_rectangle test_rectangle test_pixel
37+
test_line: test_line.c $(SRCS)
38+
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
39+
40+
test: test_fill_polygon test_polygon test_fill_rectangle test_rectangle test_pixel test_line
3841
./test_fill_polygon
3942
./test_polygon
4043
./test_fill_rectangle
4144
./test_rectangle
4245
./test_pixel
46+
./test_line
4347

4448
clean:
45-
rm -f test_fill_polygon test_polygon test_fill_rectangle test_rectangle test_pixel
49+
rm -f test_fill_polygon test_polygon test_fill_rectangle test_rectangle test_pixel test_line
4650

4751
.PHONY: all test clean

tests/test_line.c

Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
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+
27+
This file is part of the HAGL graphics library:
28+
https://github.com/tuupola/hagl
29+
30+
SPDX-License-Identifier: MIT
31+
32+
*/
33+
34+
#include <string.h>
35+
36+
#include "greatest.h"
37+
#include "crc32.h"
38+
#include "hagl.h"
39+
40+
static hagl_backend_t backend;
41+
42+
static uint32_t
43+
count_pixels(hagl_backend_t *backend, hagl_color_t color) {
44+
uint32_t count = 0;
45+
for (int16_t y = 0; y < backend->height; y++) {
46+
for (int16_t x = 0; x < backend->width; x++) {
47+
if (hagl_get_pixel(backend, x, y) == color) {
48+
count++;
49+
}
50+
}
51+
}
52+
return count;
53+
}
54+
55+
static void
56+
setup_callback(void *data) {
57+
memset(&backend, 0, sizeof(hagl_backend_t));
58+
hagl_hal_init(&backend);
59+
hagl_set_clip(&backend, 0, 0, backend.width - 1, backend.height - 1);
60+
hagl_clear(&backend);
61+
}
62+
63+
/*
64+
* Horizontal line:
65+
*
66+
* (10,50)-----(20,50)
67+
*/
68+
TEST
69+
test_draw_line_horizontal(void) {
70+
hagl_draw_line(&backend, 10, 50, 20, 50, 0xFFFF);
71+
72+
/* On line: endpoints */
73+
ASSERT_EQ(0xFFFF, hagl_get_pixel(&backend, 10, 50));
74+
ASSERT_EQ(0xFFFF, hagl_get_pixel(&backend, 20, 50));
75+
76+
/* On line: midpoint */
77+
ASSERT_EQ(0xFFFF, hagl_get_pixel(&backend, 15, 50));
78+
79+
/* Outside: above and below midpoint */
80+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 15, 49));
81+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 15, 51));
82+
83+
/* Outside: left and right of endpoints */
84+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 9, 50));
85+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 21, 50));
86+
87+
/* Total: 11 pixels */
88+
ASSERT_EQ(11, count_pixels(&backend, 0xFFFF));
89+
90+
PASS();
91+
}
92+
93+
TEST
94+
test_draw_line_horizontal_regression(void) {
95+
hagl_draw_line(&backend, 10, 50, 20, 50, 0xFFFF);
96+
97+
size_t size = backend.width * backend.height * (backend.depth / 8);
98+
uint32_t crc = crc32(backend.buffer, size);
99+
100+
ASSERT_EQ(0xDF4E4483, crc);
101+
PASS();
102+
}
103+
104+
/*
105+
* Vertical line:
106+
*
107+
* (50,10)
108+
* |
109+
* |
110+
* (50,20)
111+
*/
112+
TEST
113+
test_draw_line_vertical(void) {
114+
hagl_draw_line(&backend, 50, 10, 50, 20, 0xFFFF);
115+
116+
/* On line: endpoints */
117+
ASSERT_EQ(0xFFFF, hagl_get_pixel(&backend, 50, 10));
118+
ASSERT_EQ(0xFFFF, hagl_get_pixel(&backend, 50, 20));
119+
120+
/* On line: midpoint */
121+
ASSERT_EQ(0xFFFF, hagl_get_pixel(&backend, 50, 15));
122+
123+
/* Outside: left and right of midpoint */
124+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 49, 15));
125+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 51, 15));
126+
127+
/* Outside: above and below endpoints */
128+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 50, 9));
129+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 50, 21));
130+
131+
/* Total: 11 pixels */
132+
ASSERT_EQ(11, count_pixels(&backend, 0xFFFF));
133+
134+
PASS();
135+
}
136+
137+
TEST
138+
test_draw_line_vertical_regression(void) {
139+
hagl_draw_line(&backend, 50, 10, 50, 20, 0xFFFF);
140+
141+
size_t size = backend.width * backend.height * (backend.depth / 8);
142+
uint32_t crc = crc32(backend.buffer, size);
143+
144+
ASSERT_EQ(0x538BB775, crc);
145+
PASS();
146+
}
147+
148+
/*
149+
* Diagonal line (45 degrees):
150+
*
151+
* (10,10)
152+
* \
153+
* \
154+
* (20,20)
155+
*
156+
* Bresenham produces a staircase pattern with 21 pixels:
157+
* (10,10) (10,11) (11,11) (11,12) ... (19,20) (20,20)
158+
*/
159+
TEST
160+
test_draw_line_diagonal(void) {
161+
hagl_draw_line(&backend, 10, 10, 20, 20, 0xFFFF);
162+
163+
/* On line: endpoints */
164+
ASSERT_EQ(0xFFFF, hagl_get_pixel(&backend, 10, 10));
165+
ASSERT_EQ(0xFFFF, hagl_get_pixel(&backend, 20, 20));
166+
167+
/* On line: midpoint */
168+
ASSERT_EQ(0xFFFF, hagl_get_pixel(&backend, 15, 15));
169+
170+
/* On line: staircase neighbors of midpoint */
171+
ASSERT_EQ(0xFFFF, hagl_get_pixel(&backend, 14, 15));
172+
ASSERT_EQ(0xFFFF, hagl_get_pixel(&backend, 15, 16));
173+
174+
/* Outside: off the staircase */
175+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 16, 15));
176+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 15, 14));
177+
178+
/* Outside: beyond endpoints */
179+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 9, 9));
180+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 21, 21));
181+
182+
/* Total: 21 pixels (staircase pattern) */
183+
ASSERT_EQ(21, count_pixels(&backend, 0xFFFF));
184+
185+
PASS();
186+
}
187+
188+
TEST
189+
test_draw_line_diagonal_regression(void) {
190+
hagl_draw_line(&backend, 10, 10, 20, 20, 0xFFFF);
191+
192+
size_t size = backend.width * backend.height * (backend.depth / 8);
193+
uint32_t crc = crc32(backend.buffer, size);
194+
195+
ASSERT_EQ(0xAD9B84B1, crc);
196+
PASS();
197+
}
198+
199+
/*
200+
* Single pixel line:
201+
*
202+
* (50,50)
203+
*/
204+
TEST
205+
test_draw_line_single_pixel(void) {
206+
hagl_draw_line(&backend, 50, 50, 50, 50, 0xFFFF);
207+
208+
/* On line: the pixel itself */
209+
ASSERT_EQ(0xFFFF, hagl_get_pixel(&backend, 50, 50));
210+
211+
/* Outside: all four neighbors */
212+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 49, 50));
213+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 51, 50));
214+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 50, 49));
215+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 50, 51));
216+
217+
/* Total: 1 pixel */
218+
ASSERT_EQ(1, count_pixels(&backend, 0xFFFF));
219+
220+
PASS();
221+
}
222+
223+
/*
224+
* Line clipped by top-left corner of display:
225+
*/
226+
TEST
227+
test_draw_line_clip_top_left(void) {
228+
hagl_draw_line(&backend, -10, -10, 10, 10, 0xFFFF);
229+
230+
/* On line: clipped start at origin */
231+
ASSERT_EQ(0xFFFF, hagl_get_pixel(&backend, 0, 0));
232+
233+
/* On line: visible endpoint */
234+
ASSERT_EQ(0xFFFF, hagl_get_pixel(&backend, 10, 10));
235+
236+
/* On line: midpoint of visible portion */
237+
ASSERT_EQ(0xFFFF, hagl_get_pixel(&backend, 5, 5));
238+
239+
/* Outside: beyond visible endpoint */
240+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 11, 11));
241+
242+
/* Outside: off the staircase */
243+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 1, 0));
244+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 0, 2));
245+
246+
/* Total: 21 pixels (staircase from origin to (10,10)) */
247+
ASSERT_EQ(21, count_pixels(&backend, 0xFFFF));
248+
249+
PASS();
250+
}
251+
252+
TEST
253+
test_draw_line_clip_top_left_regression(void) {
254+
hagl_draw_line(&backend, -10, -10, 10, 10, 0xFFFF);
255+
256+
size_t size = backend.width * backend.height * (backend.depth / 8);
257+
uint32_t crc = crc32(backend.buffer, size);
258+
259+
ASSERT_EQ(0xC93AD1B1, crc);
260+
PASS();
261+
}
262+
263+
/*
264+
* Line entirely outside the display:
265+
*
266+
* (-30,-30)
267+
* \
268+
* \
269+
* (-10,-10)
270+
* .(0,0) display starts here
271+
*/
272+
TEST
273+
test_draw_line_clip_outside(void) {
274+
hagl_draw_line(&backend, -30, -30, -10, -10, 0xFFFF);
275+
276+
ASSERT_EQ(0, count_pixels(&backend, 0xFFFF));
277+
PASS();
278+
}
279+
280+
/*
281+
* Line clipped by a custom clip window:
282+
*
283+
* (40,40)
284+
* \
285+
* (50,50)-------(100,50)
286+
* | |
287+
* | |
288+
* (50,100)------(100,100)
289+
* \
290+
* (110,110)
291+
*/
292+
TEST
293+
test_draw_line_custom_clip(void) {
294+
hagl_set_clip(&backend, 50, 50, 100, 100);
295+
hagl_draw_line(&backend, 40, 40, 110, 110, 0xFFFF);
296+
297+
/* On line: clip window corners */
298+
ASSERT_EQ(0xFFFF, hagl_get_pixel(&backend, 50, 50));
299+
ASSERT_EQ(0xFFFF, hagl_get_pixel(&backend, 100, 100));
300+
301+
/* On line: midpoint of visible portion */
302+
ASSERT_EQ(0xFFFF, hagl_get_pixel(&backend, 75, 75));
303+
304+
/* Outside: before clip window */
305+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 49, 49));
306+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 40, 40));
307+
308+
/* Outside: after clip window */
309+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 101, 101));
310+
ASSERT_EQ(0x0000, hagl_get_pixel(&backend, 110, 110));
311+
312+
/* Total: 101 pixels (staircase from (50,50) to (100,100)) */
313+
ASSERT_EQ(101, count_pixels(&backend, 0xFFFF));
314+
315+
PASS();
316+
}
317+
318+
TEST
319+
test_draw_line_custom_clip_regression(void) {
320+
hagl_set_clip(&backend, 50, 50, 100, 100);
321+
hagl_draw_line(&backend, 40, 40, 110, 110, 0xFFFF);
322+
323+
size_t size = backend.width * backend.height * (backend.depth / 8);
324+
uint32_t crc = crc32(backend.buffer, size);
325+
326+
ASSERT_EQ(0x5BC8B87A, crc);
327+
PASS();
328+
}
329+
330+
SUITE(line_suite) {
331+
SET_SETUP(setup_callback, NULL);
332+
RUN_TEST(test_draw_line_horizontal);
333+
RUN_TEST(test_draw_line_horizontal_regression);
334+
RUN_TEST(test_draw_line_vertical);
335+
RUN_TEST(test_draw_line_vertical_regression);
336+
RUN_TEST(test_draw_line_diagonal);
337+
RUN_TEST(test_draw_line_diagonal_regression);
338+
RUN_TEST(test_draw_line_single_pixel);
339+
RUN_TEST(test_draw_line_clip_top_left);
340+
RUN_TEST(test_draw_line_clip_top_left_regression);
341+
RUN_TEST(test_draw_line_clip_outside);
342+
RUN_TEST(test_draw_line_custom_clip);
343+
RUN_TEST(test_draw_line_custom_clip_regression);
344+
}
345+
346+
GREATEST_MAIN_DEFS();
347+
348+
int
349+
main(int argc, char **argv) {
350+
GREATEST_MAIN_BEGIN();
351+
RUN_SUITE(line_suite);
352+
GREATEST_MAIN_END();
353+
}

0 commit comments

Comments
 (0)