Skip to content

Commit 12f6437

Browse files
schodetdlech
authored andcommitted
pbio/image: Implement line drawing.
Implement line drawing in images. Refs: pybricks/support#2154
1 parent 3b3c775 commit 12f6437

File tree

2 files changed

+199
-9
lines changed

2 files changed

+199
-9
lines changed

lib/pbio/src/image/image.c

Lines changed: 198 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,142 @@ void pbio_image_draw_vline(pbio_image_t *image, int x, int y, int l,
262262
}
263263
}
264264

265+
/**
266+
* Draw a line with a flat slope (less or equal to 1).
267+
* @param [in,out] image Image to draw into.
268+
* @param [in] x1 X coordinate of the first end.
269+
* @param [in] y1 Y coordinate of the first end.
270+
* @param [in] x2 X coordinate of the second end.
271+
* @param [in] y2 Y coordinate of the second end.
272+
* @param [in] value Pixel value.
273+
*
274+
* This is an internal function, x2 must be greater or equal to x1.
275+
*
276+
* Clipping: drawing is clipped to image dimensions.
277+
*/
278+
static void pbio_image_draw_line_flat(pbio_image_t *image, int x1, int y1,
279+
int x2, int y2, uint8_t value) {
280+
int dx = x2 - x1;
281+
int dy = y2 - y1;
282+
int ydir = 1;
283+
int x, y, err;
284+
285+
// Fall back to horizontal line, much faster.
286+
if (dy == 0) {
287+
pbio_image_draw_hline(image, x1, y1, dx + 1, value);
288+
return;
289+
}
290+
291+
// Clipping, X out of image.
292+
if (x1 >= image->width || x2 < 0) {
293+
return;
294+
}
295+
296+
// Check Y direction.
297+
if (dy < 0) {
298+
dy = -dy;
299+
ydir = -1;
300+
}
301+
302+
// Error is scaled by 2 * dx, offset with one half to look at mid point.
303+
err = -dx;
304+
y = y1;
305+
306+
// Skip pixels left of image.
307+
if (x1 < 0) {
308+
err += -x1 * dy * 2;
309+
int yskip = (err + dx * 2) / (dx * 2);
310+
err -= yskip * dx * 2;
311+
y += yskip * ydir;
312+
x1 = 0;
313+
}
314+
315+
// Skip pixels right of image.
316+
if (x2 >= image->width) {
317+
x2 = image->width - 1;
318+
}
319+
320+
// Draw.
321+
x = x1;
322+
do {
323+
pbio_image_draw_pixel(image, x, y, value);
324+
err += dy * 2;
325+
if (err >= 0) {
326+
err -= dx * 2;
327+
y += ydir;
328+
}
329+
x++;
330+
} while (x <= x2);
331+
}
332+
333+
/**
334+
* Draw a line with a steep slope (more than 1).
335+
* @param [in,out] image Image to draw into.
336+
* @param [in] x1 X coordinate of the first end.
337+
* @param [in] y1 Y coordinate of the first end.
338+
* @param [in] x2 X coordinate of the second end.
339+
* @param [in] y2 Y coordinate of the second end.
340+
* @param [in] value Pixel value.
341+
*
342+
* This is an internal function, y2 must be greater or equal to y1.
343+
*
344+
* Clipping: drawing is clipped to image dimensions.
345+
*/
346+
static void pbio_image_draw_line_steep(pbio_image_t *image, int x1, int y1,
347+
int x2, int y2, uint8_t value) {
348+
int dx = x2 - x1;
349+
int dy = y2 - y1;
350+
int xdir = 1;
351+
int x, y, err;
352+
353+
// Fall back to vertical line, much faster.
354+
if (dx == 0) {
355+
pbio_image_draw_vline(image, x1, y1, dy + 1, value);
356+
return;
357+
}
358+
359+
// Clipping, Y out of image.
360+
if (y1 >= image->height || y2 < 0) {
361+
return;
362+
}
363+
364+
// Check X direction.
365+
if (dx < 0) {
366+
dx = -dx;
367+
xdir = -1;
368+
}
369+
370+
// Error is scaled by 2 * dy, offset with one half to look at mid point.
371+
err = -dy;
372+
x = x1;
373+
374+
// Skip pixels above image.
375+
if (y1 < 0) {
376+
err += -y1 * dx * 2;
377+
int xskip = (err + dy * 2) / (dy * 2);
378+
err -= xskip * dy * 2;
379+
x += xskip * xdir;
380+
y1 = 0;
381+
}
382+
383+
// Skip pixels bellow image.
384+
if (y2 >= image->height) {
385+
y2 = image->height - 1;
386+
}
387+
388+
// Draw.
389+
y = y1;
390+
do {
391+
pbio_image_draw_pixel(image, x, y, value);
392+
err += dx * 2;
393+
if (err >= 0) {
394+
err -= dy * 2;
395+
x += xdir;
396+
}
397+
y++;
398+
} while (y <= y2);
399+
}
400+
265401
/**
266402
* Draw a line.
267403
* @param [in,out] image Image to draw into.
@@ -275,7 +411,26 @@ void pbio_image_draw_vline(pbio_image_t *image, int x, int y, int l,
275411
*/
276412
void pbio_image_draw_line(pbio_image_t *image, int x1, int y1, int x2, int y2,
277413
uint8_t value) {
278-
// TODO
414+
int dx = x2 - x1;
415+
int dy = y2 - y1;
416+
int abs_dx = dx < 0 ? -dx : dx;
417+
int abs_dy = dy < 0 ? -dy : dy;
418+
419+
if (abs_dx >= abs_dy) {
420+
// Flat slope, X always increasing.
421+
if (dx > 0) {
422+
pbio_image_draw_line_flat(image, x1, y1, x2, y2, value);
423+
} else {
424+
pbio_image_draw_line_flat(image, x2, y2, x1, y1, value);
425+
}
426+
} else {
427+
// Steep slope, Y always increasing.
428+
if (dy > 0) {
429+
pbio_image_draw_line_steep(image, x1, y1, x2, y2, value);
430+
} else {
431+
pbio_image_draw_line_steep(image, x2, y2, x1, y1, value);
432+
}
433+
}
279434
}
280435

281436
/**
@@ -289,7 +444,7 @@ void pbio_image_draw_line(pbio_image_t *image, int x1, int y1, int x2, int y2,
289444
* @param [in] value Pixel value.
290445
*
291446
* When line thickness is odd, pixels are centered on the line. When even,
292-
* line is thicker on one side.
447+
* line is thicker on left side, when looking from first end to second end.
293448
*
294449
* Clipping: drawing is clipped to image dimensions.
295450
*/
@@ -299,7 +454,47 @@ void pbio_image_draw_thick_line(pbio_image_t *image, int x1, int y1, int x2,
299454
if (thickness <= 0) {
300455
return;
301456
}
302-
// TODO
457+
458+
// Fall back to regular line.
459+
if (thickness <= 1) {
460+
pbio_image_draw_line(image, x1, y1, x2, y2, value);
461+
return;
462+
}
463+
464+
int dx = x2 - x1;
465+
int dy = y2 - y1;
466+
int abs_dx = dx < 0 ? -dx : dx;
467+
int abs_dy = dy < 0 ? -dy : dy;
468+
int i;
469+
int offset = thickness / 2;
470+
471+
if (abs_dx >= abs_dy) {
472+
// Flat slope, X always increasing.
473+
if (dx > 0) {
474+
for (i = 0; i < thickness; i++) {
475+
pbio_image_draw_line_flat(image, x1, y1 + i - offset,
476+
x2, y2 + i - offset, value);
477+
}
478+
} else {
479+
for (i = 0; i < thickness; i++) {
480+
pbio_image_draw_line_flat(image, x2, y2 - i + offset,
481+
x1, y1 - i + offset, value);
482+
}
483+
}
484+
} else {
485+
// Steep slope, Y always increasing.
486+
if (dy > 0) {
487+
for (i = 0; i < thickness; i++) {
488+
pbio_image_draw_line_steep(image, x1 - i + offset, y1,
489+
x2 - i + offset, y2, value);
490+
}
491+
} else {
492+
for (i = 0; i < thickness; i++) {
493+
pbio_image_draw_line_steep(image, x2 + i - offset, y2,
494+
x1 + i - offset, y1, value);
495+
}
496+
}
497+
}
303498
}
304499

305500
/**

pybricks/parameters/pb_type_image.c

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -274,12 +274,7 @@ static mp_obj_t pb_type_Image_draw_line(size_t n_args, const mp_obj_t *pos_args,
274274
mp_int_t width = pb_obj_get_int(width_in);
275275
int color = get_color(color_in);
276276

277-
mp_raise_NotImplementedError(MP_ERROR_TEXT("draw_line is not implemented yet"));
278-
if (width <= 1) {
279-
pbio_image_draw_line(&self->image, x1, y1, x2, y2, color);
280-
} else {
281-
pbio_image_draw_thick_line(&self->image, x1, y1, x2, y2, width, color);
282-
}
277+
pbio_image_draw_thick_line(&self->image, x1, y1, x2, y2, width, color);
283278

284279
if (self->is_display) {
285280
pbdrv_display_update();

0 commit comments

Comments
 (0)