Skip to content

Commit

Permalink
Add drawLine function
Browse files Browse the repository at this point in the history
  • Loading branch information
RossAdrian committed Jul 30, 2024
1 parent 0a45e2b commit b221945
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 1 deletion.
4 changes: 4 additions & 0 deletions native/framebuffers.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
/**
* Core resource management and lowlevel drawing operations.
*/
#include "pyframebuffer.h"

#include <fcntl.h>
#include <linux/fb.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Expand Down
30 changes: 30 additions & 0 deletions native/module_pyfb.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,35 @@ static PyObject* pyfunc_pyfb_getResolution(PyObject* self, PyObject* args) {
return tuple;
}

/**
* Python wrapper for the pyfb_drawLine function.
*
* @param self The function
* @param args The arguments, expecting long of the fbnum
*
* @return Just 0
*/
static PyObject* pyfunc_pyfb_sdrawLine(PyObject* self, PyObject* args) {
unsigned char fbnum_c;
unsigned long int x1;
unsigned long int y1;
unsigned long int x2;
unsigned long int y2;
uint32_t color_val;

if(!PyArg_ParseTuple(args, "bkkkkI", &fbnum_c, &x1, &y1, &x2, &y2, &color_val)) {
PyErr_SetString(PyExc_TypeError, "Expecting arguments of type (byte, long, long, long, long, long)");
return NULL;
}

// now parse the color
struct pyfb_color color;
pyfb_initcolor_u32(&color, color_val);

// and invoke the target function
pyfb_sdrawLine((uint8_t)fbnum_c, x1, y1, x2, y2, &color);
}

// The module def

/**
Expand All @@ -199,6 +228,7 @@ static PyMethodDef pyfb_methods[] = {
{"pyfb_open", pyfunc_pyfb_open, METH_VARARGS, "Framebuffer open function"},
{"pyfb_close", pyfunc_pyfb_close, METH_VARARGS, "Framebuffer close function"},
{"pyfb_setPixel", pyfunc_pyfb_ssetPixel, METH_VARARGS, "Draw a pixel on the framebuffer"},
{"pyfb_drawLine", pyfunc_pyfb_sdrawLine, METH_VARARGS, "Draw a line on the framebuffer"},
{"pyfb_drawHorizontalLine", pyfunc_pyfb_sdrawHorizontalLine, METH_VARARGS, "Draw a horizontal line on the framebuffer"},
{"pyfb_drawVerticalLine", pyfunc_pyfb_sdrawVerticalLine, METH_VARARGS, "Draw a vertical line on the framebuffer"},
{"pyfb_flushBuffer", pyfunc_pyfb_flushBuffer, METH_VARARGS, "Flush the offscreen buffer to the framebuffer"},
Expand Down
122 changes: 122 additions & 0 deletions native/paint.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/**
* Higher level painting operation sources.
*/
#include "pyframebuffer.h"

#define ULI_TO_LI(x) ((unsigned long)(x))

static inline long int li_abs(long int x) {
if(x < 0) {
return (unsigned long int)(x * -1);
}

// else
return (unsigned long int)x;
}

void __APISTATUS_internal pyfb_drawLine(uint8_t fbnum,
unsigned long int x1,
unsigned long int y1,
unsigned long int x2,
unsigned long int y2,
const struct pyfb_color* color) {
if(y1 == y2) {
// draw a horizontal line
unsigned long int begin = x1 < x2 ? x1 : x2;
unsigned long int end = x1 < x2 ? x2 : x1;
unsigned long int len = end - begin + 1;
pyfb_drawHorizontalLine(fbnum, begin, y1, len, color);
// ready
return;
}

if(x1 == x2) {
// draw a vertical line
unsigned long int begin = y1 < y2 ? y1 : y2;
unsigned long int end = y1 < y2 ? y2 : y1;
unsigned long int len = end - begin + 1;
pyfb_drawVerticalLine(fbnum, x1, begin, len, color);
// ready
return;
}

// else if we get here, draw a line so
long int li_x1 = ULI_TO_LI(x1);
long int li_x2 = ULI_TO_LI(x2);
long int li_y1 = ULI_TO_LI(y1);
long int li_y2 = ULI_TO_LI(y2);

long int dx = li_abs(li_x2 - li_x1);
long int dy = li_abs(li_y2 - li_y1);
long int sx = (li_x1 < li_x2) ? 1 : -1;
long int sy = (li_y1 < li_y2) ? 1 : -1;
long int err = dx - dy;

while(1) {
pyfb_setPixel(fbnum, li_x1, li_y1, color);

if(li_x1 == li_x2 && li_y1 == li_y2) {
break;
}

long int e2 = 2 * err;
if(e2 > -dy) {
err -= dy;
li_x1 += sx;
}

if(e2 < dx) {
err += dx;
li_y1 += sy;
}
}
}

void __APISTATUS_internal pyfb_sdrawLine(uint8_t fbnum,
unsigned long int x1,
unsigned long int y1,
unsigned long int x2,
unsigned long int y2,
const struct pyfb_color* color) {
// first check if fbnum is valid
if(fbnum >= MAX_FRAMEBUFFERS) {
PyErr_SetString(PyExc_ValueError, "The framebuffer number is not valid");
return;
}

// Ok, then lock
pyfb_fblock(fbnum);

// next test if the device is really in use
if(!pyfb_fbused(fbnum)) {
// this framebuffer is not in use, so ignore
PyErr_SetString(PyExc_IOError, "The framebuffer is not opened");
pyfb_fbunlock(fbnum);
return;
}

// check if all values are valid
struct pyfb_videomode_info vinfo;
pyfb_vinfo(fbnum, &vinfo);

unsigned long int xres = vinfo.vinfo.xres;
unsigned long int yres = vinfo.vinfo.yres;

if(x1 >= xres || y1 >= yres) {
PyErr_SetString(PyExc_ValueError, "The x1y1 coordinate is not on the screen");
pyfb_fbunlock(fbnum);
return;
}

if(x2 >= xres || y2 >= yres) {
PyErr_SetString(PyExc_ValueError, "The x2y2 coordinate is not on the screen");
pyfb_fbunlock(fbnum);
return;
}

// all is valid, so draw the line
pyfb_drawLine(fbnum, x1, y1, x2, y2, color);

// ready, so return
pyfb_fbunlock(fbnum);
}
47 changes: 46 additions & 1 deletion native/pyframebuffer.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/**
* Main header of the native sources.
*/
#ifndef _pyframebuffer_included
#define _pyframebuffer_included

Expand Down Expand Up @@ -314,7 +317,7 @@ extern void __APISTATUS_internal pyfb_drawHorizontalLine(uint8_t fbnum,
const struct pyfb_color* color);

/**
* Paints a exactly vertical line. This function is secure because before painting,
* Paints a exactly vertical line. This function is secure, because before painting,
* it validates the arguments.
*
* @param fbnum The number of the target framebuffer
Expand Down Expand Up @@ -351,6 +354,48 @@ extern void __APISTATUS_internal pyfb_drawVerticalLine(uint8_t fbnum,
unsigned long int len,
const struct pyfb_color* color);

/**
* Draws a line from one Point to another. This function is the insecure way because due to
* performance increase, the arguments will not be checked. Please make sure the arguments
* are correct, because it may cause a memory error if the data is invalid. Also note that this
* function is marked as __APISTATUS_internal. Means this is an internal API and should not be
* accessible from outside.
*
* This function by it self does not handle the locking of the framebuffer. The caller must
* care of locking the framebuffer before calling this function.
*
* @param fbnum The number of the framebuffer
* @param x1 The x1 coordinate
* @param y1 The y1 coordinate
* @param x2 The x2 coordinate
* @param y2 The y2 coordinate
* @param color The color value
*/
extern void __APISTATUS_internal pyfb_drawLine(uint8_t fbnum,
unsigned long int x1,
unsigned long int y1,
unsigned long int x2,
unsigned long int y2,
const struct pyfb_color* color);

/**
* Draws a line. This function is secure, because before painting,
* it validates the arguments.
*
* @param fbnum The number of the framebuffer
* @param x1 The x1 coordinate
* @param y1 The y1 coordinate
* @param x2 The x2 coordinate
* @param y2 The y2 coordinate
* @param color The color value
*/
extern void __APISTATUS_internal pyfb_sdrawLine(uint8_t fbnum,
unsigned long int x1,
unsigned long int y1,
unsigned long int x2,
unsigned long int y2,
const struct pyfb_color* color);

/**
* Paints the content of the offscreen buffer to the framebuffer. This function must be callen
* because this is the only operation that is required to paint the content of the offscreen
Expand Down
14 changes: 14 additions & 0 deletions pyframebuffer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,20 @@ def drawPixel(self, x, y, color):
color = getColorValue(color)
fb.pyfb_setPixel(self.fbnum, x, y, color)

def drawLine(self, x1, y1, x2, y2, color):
"""
Draws a line from the Point (x1 | y1) to
(x2 | y2).
@param x1 The x1 coordinate
@param y1 The y1 coordinate
@param x2 The x2 coordinate
@param y2 The y2 coordinate
@param color The color value or object
"""
color = getColorValue(color)
fb.pyfb_drawLine(self.fbnum, x1, y1, x2, y2, color)

def drawHorizontalLine(self, x, y, len, color):
"""
Draws a horizontal line on the offscreen buffer.
Expand Down

0 comments on commit b221945

Please sign in to comment.