WIP: vendor tfblib + clang-format

We only need a subset of it, to iterate faster and integrate features
like in-line rotation let's vendor it for now.

Signed-off-by: Caleb Connolly <caleb@connolly.tech>
This commit is contained in:
Caleb Connolly 2023-07-28 20:15:06 +01:00
parent 760ad79c92
commit 01af13950c
No known key found for this signature in database
GPG key ID: 7930459FB9303217
13 changed files with 3279 additions and 1833 deletions

View file

@ -1,10 +1,67 @@
BasedOnStyle: LLVM
IndentWidth: 8
UseTab: Always
BreakBeforeBraces: Linux
# SPDX-License-Identifier: GPL-2.0
#
# clang-format configuration file. Intended for clang-format >= 11.
#
# For more information, see:
#
# Documentation/process/clang-format.rst
# https://clang.llvm.org/docs/ClangFormat.html
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
#
---
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AlignConsecutiveMacros:
Enabled: true
AcrossEmptyLines: true
AcrossComments: false
IndentCaseLabels: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 100
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
IndentWidth: 8
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
UseTab: Always
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false

File diff suppressed because it is too large Load diff

View file

@ -58,19 +58,14 @@ NSVGrasterizer* nsvgCreateRasterizer();
// w - width of the image to render
// h - height of the image to render
// stride - number of bytes per scaleline in the destination buffer
void nsvgRasterize(NSVGrasterizer* r,
NSVGimage* image, float tx, float ty, float scale,
void nsvgRasterize(NSVGrasterizer *r, NSVGimage *image, float tx, float ty, float scale,
unsigned char *dst, int w, int h, int stride);
// Deletes rasterizer context.
void nsvgDeleteRasterizer(NSVGrasterizer *);
void nsvgRasterizeText(NSVGrasterizer* r,
const NSVGimage* font, float tx, float ty, float scale,
unsigned char* dst, int w, int h, int stride,
const char* text);
void nsvgRasterizeText(NSVGrasterizer *r, const NSVGimage *font, float tx, float ty, float scale,
unsigned char *dst, int w, int h, int stride, const char *text);
#ifndef NANOSVGRAST_CPLUSPLUS
#ifdef __cplusplus
@ -124,8 +119,7 @@ typedef struct NSVGcachedPaint {
unsigned int colors[256];
} NSVGcachedPaint;
struct NSVGrasterizer
{
struct NSVGrasterizer {
float px, py;
float tessTol;
@ -157,7 +151,8 @@ struct NSVGrasterizer
NSVGrasterizer *nsvgCreateRasterizer()
{
NSVGrasterizer *r = (NSVGrasterizer *)malloc(sizeof(NSVGrasterizer));
if (r == NULL) goto error;
if (r == NULL)
goto error;
memset(r, 0, sizeof(NSVGrasterizer));
r->tessTol = 0.25f;
@ -174,7 +169,8 @@ void nsvgDeleteRasterizer(NSVGrasterizer* r)
{
NSVGmemPage *p;
if (r == NULL) return;
if (r == NULL)
return;
p = r->pages;
while (p != NULL) {
@ -183,10 +179,14 @@ void nsvgDeleteRasterizer(NSVGrasterizer* r)
p = next;
}
if (r->edges) free(r->edges);
if (r->points) free(r->points);
if (r->points2) free(r->points2);
if (r->scanline) free(r->scanline);
if (r->edges)
free(r->edges);
if (r->points)
free(r->points);
if (r->points2)
free(r->points2);
if (r->scanline)
free(r->scanline);
free(r);
}
@ -202,7 +202,8 @@ static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur)
// Alloc new page
newp = (NSVGmemPage *)malloc(sizeof(NSVGmemPage));
if (newp == NULL) return NULL;
if (newp == NULL)
return NULL;
memset(newp, 0, sizeof(NSVGmemPage));
// Add to linked list
@ -227,7 +228,8 @@ static void nsvg__resetPool(NSVGrasterizer* r)
static unsigned char *nsvg__alloc(NSVGrasterizer *r, int size)
{
unsigned char *buf;
if (size > NSVG__MEMPAGE_SIZE) return NULL;
if (size > NSVG__MEMPAGE_SIZE)
return NULL;
if (r->curpage == NULL || r->curpage->size + size > NSVG__MEMPAGE_SIZE) {
r->curpage = nsvg__nextPage(r, r->curpage);
}
@ -258,7 +260,8 @@ static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags)
if (r->npoints + 1 > r->cpoints) {
r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64;
r->points = (NSVGpoint *)realloc(r->points, sizeof(NSVGpoint) * r->cpoints);
if (r->points == NULL) return;
if (r->points == NULL)
return;
}
pt = &r->points[r->npoints];
@ -273,7 +276,8 @@ static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt)
if (r->npoints + 1 > r->cpoints) {
r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64;
r->points = (NSVGpoint *)realloc(r->points, sizeof(NSVGpoint) * r->cpoints);
if (r->points == NULL) return;
if (r->points == NULL)
return;
}
r->points[r->npoints] = pt;
r->npoints++;
@ -284,7 +288,8 @@ static void nsvg__duplicatePoints(NSVGrasterizer* r)
if (r->npoints > r->cpoints2) {
r->cpoints2 = r->npoints;
r->points2 = (NSVGpoint *)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2);
if (r->points2 == NULL) return;
if (r->points2 == NULL)
return;
}
memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints);
@ -302,7 +307,8 @@ static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float
if (r->nedges + 1 > r->cedges) {
r->cedges = r->cedges > 0 ? r->cedges * 2 : 64;
r->edges = (NSVGedge *)realloc(r->edges, sizeof(NSVGedge) * r->cedges);
if (r->edges == NULL) return;
if (r->edges == NULL)
return;
}
e = &r->edges[r->nedges];
@ -334,17 +340,19 @@ static float nsvg__normalize(float *x, float* y)
return d;
}
static float nsvg__absf(float x) { return x < 0 ? -x : x; }
static float nsvg__absf(float x)
{
return x < 0 ? -x : x;
}
static void nsvg__flattenCubicBez(NSVGrasterizer* r,
float x1, float y1, float x2, float y2,
float x3, float y3, float x4, float y4,
int level, int type)
static void nsvg__flattenCubicBez(NSVGrasterizer *r, float x1, float y1, float x2, float y2,
float x3, float y3, float x4, float y4, int level, int type)
{
float x12, y12, x23, y23, x34, y34, x123, y123, x234, y234, x1234, y1234;
float dx, dy, d2, d3;
if (level > 10) return;
if (level > 10)
return;
x12 = (x1 + x2) * 0.5f;
y12 = (y1 + y2) * 0.5f;
@ -385,24 +393,23 @@ static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale)
nsvg__addPathPoint(r, path->pts[0] * scale, path->pts[1] * scale, 0);
for (i = 0; i < path->npts - 1; i += 3) {
float *p = &path->pts[i * 2];
nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, 0);
nsvg__flattenCubicBez(r, p[0] * scale, p[1] * scale, p[2] * scale,
p[3] * scale, p[4] * scale, p[5] * scale,
p[6] * scale, p[7] * scale, 0, 0);
}
// Close path
nsvg__addPathPoint(r, path->pts[0] * scale, path->pts[1] * scale, 0);
// Build edges
for (i = 0, j = r->npoints - 1; i < r->npoints; j = i++)
nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y);
nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x,
r->points[i].y);
}
}
enum NSVGpointFlags
{
NSVG_PT_CORNER = 0x01,
NSVG_PT_BEVEL = 0x02,
NSVG_PT_LEFT = 0x04
};
enum NSVGpointFlags { NSVG_PT_CORNER = 0x01, NSVG_PT_BEVEL = 0x02, NSVG_PT_LEFT = 0x04 };
static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth)
static void nsvg__initClosed(NSVGpoint *left, NSVGpoint *right, NSVGpoint *p0, NSVGpoint *p1,
float lineWidth)
{
float w = lineWidth * 0.5f;
float dx = p1->x - p0->x;
@ -412,11 +419,14 @@ static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, N
float dlx = dy, dly = -dx;
float lx = px - dlx * w, ly = py - dly * w;
float rx = px + dlx * w, ry = py + dly * w;
left->x = lx; left->y = ly;
right->x = rx; right->y = ry;
left->x = lx;
left->y = ly;
right->x = rx;
right->y = ry;
}
static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect)
static void nsvg__buttCap(NSVGrasterizer *r, NSVGpoint *left, NSVGpoint *right, NSVGpoint *p,
float dx, float dy, float lineWidth, int connect)
{
float w = lineWidth * 0.5f;
float px = p->x, py = p->y;
@ -430,11 +440,14 @@ static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right,
nsvg__addEdge(r, left->x, left->y, lx, ly);
nsvg__addEdge(r, rx, ry, right->x, right->y);
}
left->x = lx; left->y = ly;
right->x = rx; right->y = ry;
left->x = lx;
left->y = ly;
right->x = rx;
right->y = ry;
}
static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect)
static void nsvg__squareCap(NSVGrasterizer *r, NSVGpoint *left, NSVGpoint *right, NSVGpoint *p,
float dx, float dy, float lineWidth, int connect)
{
float w = lineWidth * 0.5f;
float px = p->x - dx * w, py = p->y - dy * w;
@ -448,15 +461,18 @@ static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right
nsvg__addEdge(r, left->x, left->y, lx, ly);
nsvg__addEdge(r, rx, ry, right->x, right->y);
}
left->x = lx; left->y = ly;
right->x = rx; right->y = ry;
left->x = lx;
left->y = ly;
right->x = rx;
right->y = ry;
}
#ifndef NSVG_PI
#define NSVG_PI (3.14159265358979323846264338327f)
#endif
static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int ncap, int connect)
static void nsvg__roundCap(NSVGrasterizer *r, NSVGpoint *left, NSVGpoint *right, NSVGpoint *p,
float dx, float dy, float lineWidth, int ncap, int connect)
{
int i;
float w = lineWidth * 0.5f;
@ -477,9 +493,11 @@ static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right,
prevy = y;
if (i == 0) {
lx = x; ly = y;
lx = x;
ly = y;
} else if (i == ncap - 1) {
rx = x; ry = y;
rx = x;
ry = y;
}
}
@ -488,11 +506,14 @@ static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right,
nsvg__addEdge(r, rx, ry, right->x, right->y);
}
left->x = lx; left->y = ly;
right->x = rx; right->y = ry;
left->x = lx;
left->y = ly;
right->x = rx;
right->y = ry;
}
static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth)
static void nsvg__bevelJoin(NSVGrasterizer *r, NSVGpoint *left, NSVGpoint *right, NSVGpoint *p0,
NSVGpoint *p1, float lineWidth)
{
float w = lineWidth * 0.5f;
float dlx0 = p0->dy, dly0 = -p0->dx;
@ -508,11 +529,14 @@ static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right
nsvg__addEdge(r, right->x, right->y, rx0, ry0);
nsvg__addEdge(r, rx0, ry0, rx1, ry1);
left->x = lx1; left->y = ly1;
right->x = rx1; right->y = ry1;
left->x = lx1;
left->y = ly1;
right->x = rx1;
right->y = ry1;
}
static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth)
static void nsvg__miterJoin(NSVGrasterizer *r, NSVGpoint *left, NSVGpoint *right, NSVGpoint *p0,
NSVGpoint *p1, float lineWidth)
{
float w = lineWidth * 0.5f;
float dlx0 = p0->dy, dly0 = -p0->dx;
@ -544,11 +568,14 @@ static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right
nsvg__addEdge(r, right->x, right->y, rx1, ry1);
}
left->x = lx1; left->y = ly1;
right->x = rx1; right->y = ry1;
left->x = lx1;
left->y = ly1;
right->x = rx1;
right->y = ry1;
}
static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap)
static void nsvg__roundJoin(NSVGrasterizer *r, NSVGpoint *left, NSVGpoint *right, NSVGpoint *p0,
NSVGpoint *p1, float lineWidth, int ncap)
{
int i, n;
float w = lineWidth * 0.5f;
@ -559,12 +586,16 @@ static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right
float da = a1 - a0;
float lx, ly, rx, ry;
if (da < NSVG_PI) da += NSVG_PI*2;
if (da > NSVG_PI) da -= NSVG_PI*2;
if (da < NSVG_PI)
da += NSVG_PI * 2;
if (da > NSVG_PI)
da -= NSVG_PI * 2;
n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap);
if (n < 2) n = 2;
if (n > ncap) n = ncap;
if (n < 2)
n = 2;
if (n > ncap)
n = ncap;
lx = left->x;
ly = left->y;
@ -581,15 +612,20 @@ static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right
nsvg__addEdge(r, lx1, ly1, lx, ly);
nsvg__addEdge(r, rx, ry, rx1, ry1);
lx = lx1; ly = ly1;
rx = rx1; ry = ry1;
lx = lx1;
ly = ly1;
rx = rx1;
ry = ry1;
}
left->x = lx; left->y = ly;
right->x = rx; right->y = ry;
left->x = lx;
left->y = ly;
right->x = rx;
right->y = ry;
}
static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p1, float lineWidth)
static void nsvg__straightJoin(NSVGrasterizer *r, NSVGpoint *left, NSVGpoint *right, NSVGpoint *p1,
float lineWidth)
{
float w = lineWidth * 0.5f;
float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w);
@ -598,22 +634,28 @@ static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* ri
nsvg__addEdge(r, lx, ly, left->x, left->y);
nsvg__addEdge(r, right->x, right->y, rx, ry);
left->x = lx; left->y = ly;
right->x = rx; right->y = ry;
left->x = lx;
left->y = ly;
right->x = rx;
right->y = ry;
}
static int nsvg__curveDivs(float r, float arc, float tol)
{
float da = acosf(r / (r + tol)) * 2.0f;
int divs = (int)ceilf(arc / da);
if (divs < 2) divs = 2;
if (divs < 2)
divs = 2;
return divs;
}
static void nsvg__expandStroke(NSVGrasterizer* r, NSVGpoint* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth)
static void nsvg__expandStroke(NSVGrasterizer *r, NSVGpoint *points, int npoints, int closed,
int lineJoin, int lineCap, float lineWidth)
{
int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol); // Calculate divisions per half circle.
NSVGpoint left = {0,0,0,0,0,0,0,0}, right = {0,0,0,0,0,0,0,0}, firstLeft = {0,0,0,0,0,0,0,0}, firstRight = {0,0,0,0,0,0,0,0};
int ncap = nsvg__curveDivs(lineWidth * 0.5f, NSVG_PI,
r->tessTol); // Calculate divisions per half circle.
NSVGpoint left = { 0, 0, 0, 0, 0, 0, 0, 0 }, right = { 0, 0, 0, 0, 0, 0, 0, 0 },
firstLeft = { 0, 0, 0, 0, 0, 0, 0, 0 }, firstRight = { 0, 0, 0, 0, 0, 0, 0, 0 };
NSVGpoint *p0, *p1;
int j, s, e;
@ -729,7 +771,8 @@ static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoi
// Check to see if the corner needs to be beveled.
if (p1->flags & NSVG_PT_CORNER) {
if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) {
if ((dmr2 * miterLimit * miterLimit) < 1.0f ||
lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) {
p1->flags |= NSVG_PT_BEVEL;
}
}
@ -754,7 +797,9 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
nsvg__addPathPoint(r, path->pts[0] * scale, path->pts[1] * scale, NSVG_PT_CORNER);
for (i = 0; i < path->npts - 1; i += 3) {
float *p = &path->pts[i * 2];
nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, NSVG_PT_CORNER);
nsvg__flattenCubicBez(r, p[0] * scale, p[1] * scale, p[2] * scale,
p[3] * scale, p[4] * scale, p[5] * scale,
p[6] * scale, p[7] * scale, 0, NSVG_PT_CORNER);
}
if (r->npoints < 2)
continue;
@ -817,7 +862,8 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
// Stroke
if (r->npoints > 1 && dashState) {
nsvg__prepareStroke(r, miterLimit, lineJoin);
nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth);
nsvg__expandStroke(r, r->points, r->npoints, 0,
lineJoin, lineCap, lineWidth);
}
// Advance dash pattern
dashState = !dashState;
@ -839,10 +885,12 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
}
// Stroke any leftover path
if (r->npoints > 1 && dashState)
nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth);
nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap,
lineWidth);
} else {
nsvg__prepareStroke(r, miterLimit, lineJoin);
nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap, lineWidth);
nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap,
lineWidth);
}
}
}
@ -852,12 +900,13 @@ static int nsvg__cmpEdge(const void *p, const void *q)
const NSVGedge *a = (const NSVGedge *)p;
const NSVGedge *b = (const NSVGedge *)q;
if (a->y0 < b->y0) return -1;
if (a->y0 > b->y0) return 1;
if (a->y0 < b->y0)
return -1;
if (a->y0 > b->y0)
return 1;
return 0;
}
static NSVGactiveEdge *nsvg__addActive(NSVGrasterizer *r, NSVGedge *e, float startPoint)
{
NSVGactiveEdge *z;
@ -869,7 +918,8 @@ static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float sta
} else {
// Alloc new edge.
z = (NSVGactiveEdge *)nsvg__alloc(r, sizeof(NSVGactiveEdge));
if (z == NULL) return NULL;
if (z == NULL)
return NULL;
}
float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
@ -894,24 +944,33 @@ static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z)
r->freelist = z;
}
static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax)
static void nsvg__fillScanline(unsigned char *scanline, int len, int x0, int x1, int maxWeight,
int *xmin, int *xmax)
{
int i = x0 >> NSVG__FIXSHIFT;
int j = x1 >> NSVG__FIXSHIFT;
if (i < *xmin) *xmin = i;
if (j > *xmax) *xmax = j;
if (i < *xmin)
*xmin = i;
if (j > *xmax)
*xmax = j;
if (i < len && j >= 0) {
if (i == j) {
// x0,x1 are the same pixel, so compute combined coverage
scanline[i] = (unsigned char)(scanline[i] + ((x1 - x0) * maxWeight >> NSVG__FIXSHIFT));
scanline[i] = (unsigned char)(scanline[i] +
((x1 - x0) * maxWeight >> NSVG__FIXSHIFT));
} else {
if (i >= 0) // add antialiasing for x0
scanline[i] = (unsigned char)(scanline[i] + (((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT));
scanline[i] = (unsigned char)(scanline[i] +
(((NSVG__FIX - (x0 & NSVG__FIXMASK)) *
maxWeight) >>
NSVG__FIXSHIFT));
else
i = -1; // clip
if (j < len) // add antialiasing for x1
scanline[j] = (unsigned char)(scanline[j] + (((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT));
scanline[j] = (unsigned char)(scanline[j] +
(((x1 & NSVG__FIXMASK) * maxWeight) >>
NSVG__FIXSHIFT));
else
j = len; // clip
@ -924,7 +983,8 @@ static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1,
// note: this routine clips fills that extend off the edges... ideally this
// wouldn't happen, but it could happen if the truetype glyph bounding boxes
// are wrong, or if the user supplies a too-small bitmap
static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule)
static void nsvg__fillActiveEdges(unsigned char *scanline, int len, NSVGactiveEdge *e,
int maxWeight, int *xmin, int *xmax, char fillRule)
{
// non-zero winding fill
int x0 = 0, w = 0;
@ -933,13 +993,17 @@ static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEd
// Non-zero
while (e != NULL) {
if (w == 0) {
// if we're currently at zero, we need to record the edge start point
x0 = e->x; w += e->dir;
// if we're currently at zero, we need to record the edge start
// point
x0 = e->x;
w += e->dir;
} else {
int x1 = e->x; w += e->dir;
int x1 = e->x;
w += e->dir;
// if we went to zero, we need to draw
if (w == 0)
nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax);
nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin,
xmax);
}
e = e->next;
}
@ -947,10 +1011,13 @@ static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEd
// Even-odd
while (e != NULL) {
if (w == 0) {
// if we're currently at zero, we need to record the edge start point
x0 = e->x; w = 1;
// if we're currently at zero, we need to record the edge start
// point
x0 = e->x;
w = 1;
} else {
int x1 = e->x; w = 0;
int x1 = e->x;
w = 0;
nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax);
}
e = e->next;
@ -958,7 +1025,10 @@ static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEd
}
}
static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); }
static float nsvg__clampf(float a, float mn, float mx)
{
return a < mn ? mn : (a > mx ? mx : a);
}
static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
@ -1118,7 +1188,8 @@ static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* co
}
}
static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule)
static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale,
NSVGcachedPaint *cache, char fillRule)
{
NSVGactiveEdge *active = NULL;
int y, s;
@ -1164,14 +1235,17 @@ static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, fl
}
step = &(*step)->next;
}
if (!changed) break;
if (!changed)
break;
}
// insert all edges that start before the center of this scanline -- omit ones that also end on this scanline
// insert all edges that start before the center of this scanline -- omit
// ones that also end on this scanline
while (e < r->nedges && r->edges[e].y0 <= scany) {
if (r->edges[e].y1 > scany) {
NSVGactiveEdge *z = nsvg__addActive(r, &r->edges[e], scany);
if (z == NULL) break;
if (z == NULL)
break;
// find insertion point
if (active == NULL) {
active = z;
@ -1194,16 +1268,19 @@ static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, fl
// now process all active edges in non-zero fashion
if (active != NULL)
nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule);
nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight,
&xmin, &xmax, fillRule);
}
// Blit
if (xmin < 0) xmin = 0;
if (xmax > r->width-1) xmax = r->width-1;
if (xmin < 0)
xmin = 0;
if (xmax > r->width - 1)
xmax = r->width - 1;
if (xmin <= xmax) {
nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache);
nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin * 4, xmax - xmin + 1,
&r->scanline[xmin], xmin, y, tx, ty, scale, cache);
}
}
}
static void nsvg__unpremultiplyAlpha(unsigned char *image, int w, int h, int stride)
@ -1265,7 +1342,6 @@ static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int str
}
}
static void nsvg__initPaint(NSVGcachedPaint *cache, NSVGpaint *paint, float opacity)
{
int i, j;
@ -1286,7 +1362,8 @@ static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opac
if (grad->nstops == 0) {
for (i = 0; i < 256; i++)
cache->colors[i] = 0;
} if (grad->nstops == 1) {
}
if (grad->nstops == 1) {
for (i = 0; i < 256; i++)
cache->colors[i] = nsvg__applyOpacity(grad->stops[i].color, opacity);
} else {
@ -1311,7 +1388,8 @@ static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opac
ia = (int)(ua * 255.0f);
ib = (int)(ub * 255.0f);
count = ib - ia;
if (count <= 0) continue;
if (count <= 0)
continue;
u = 0;
du = 1.0f / (float)count;
for (j = 0; j < count; j++) {
@ -1323,7 +1401,6 @@ static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opac
for (i = ib; i < 256; i++)
cache->colors[i] = cb;
}
}
/*
@ -1350,17 +1427,21 @@ static void dumpEdges(NSVGrasterizer* r, const char* name)
ymax = nsvg__maxf(ymax, e->y1);
}
fprintf(fp, "<svg viewBox=\"%f %f %f %f\" xmlns=\"http://www.w3.org/2000/svg\">", xmin, ymin, (xmax - xmin), (ymax - ymin));
fprintf(fp, "<svg viewBox=\"%f %f %f %f\" xmlns=\"http://www.w3.org/2000/svg\">", xmin,
ymin, (xmax - xmin), (ymax - ymin));
for (i = 0; i < r->nedges; i++) {
e = &r->edges[i];
fprintf(fp ,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke:#000;\" />", e->x0,e->y0, e->x1,e->y1);
fprintf(fp ,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke:#000;\"
/>", e->x0,e->y0, e->x1,e->y1);
}
for (i = 0; i < r->npoints; i++) {
if (i+1 < r->npoints)
fprintf(fp ,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke:#f00;\" />", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y);
fprintf(fp ,"<circle cx=\"%f\" cy=\"%f\" r=\"1\" style=\"fill:%s;\" />", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0");
fprintf(fp ,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\"
style=\"stroke:#f00;\" />", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y);
fprintf(fp ,"<circle cx=\"%f\" cy=\"%f\" r=\"1\" style=\"fill:%s;\" />",
r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0");
}
fprintf(fp, "</svg>");
@ -1368,10 +1449,8 @@ static void dumpEdges(NSVGrasterizer* r, const char* name)
}
*/
void nsvgRasterizeText(NSVGrasterizer* r,
const NSVGimage* font, float tx, float ty, float scale,
unsigned char* dst, int w, int h, int stride,
const char* text)
void nsvgRasterizeText(NSVGrasterizer *r, const NSVGimage *font, float tx, float ty, float scale,
unsigned char *dst, int w, int h, int stride, const char *text)
{
NSVGshape *shape = NULL;
NSVGedge *e = NULL;
@ -1442,7 +1521,8 @@ void nsvgRasterizeText(NSVGrasterizer* r,
// Rasterize edges
qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
// now, traverse the scanlines and find the intersections on each scanline,
// use non-zero rule
nsvg__initPaint(&cache, &shape->fill, shape->opacity);
nsvg__rasterizeSortedEdges(r, tx, ty, scale, &cache, shape->fillRule);
@ -1468,7 +1548,8 @@ void nsvgRasterizeText(NSVGrasterizer* r,
// Rasterize edges
qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
// now, traverse the scanlines and find the intersections on each scanline,
// use non-zero rule
nsvg__initPaint(&cache, &shape->stroke, shape->opacity);
nsvg__rasterizeSortedEdges(r, tx, ty, scale, &cache, NSVG_FILLRULE_NONZERO);
@ -1486,8 +1567,7 @@ void nsvgRasterizeText(NSVGrasterizer* r,
r->stride = 0;
}
void nsvgRasterize(NSVGrasterizer* r,
NSVGimage* image, float tx, float ty, float scale,
void nsvgRasterize(NSVGrasterizer *r, NSVGimage *image, float tx, float ty, float scale,
unsigned char *dst, int w, int h, int stride)
{
NSVGshape *shape = NULL;
@ -1503,7 +1583,8 @@ void nsvgRasterize(NSVGrasterizer* r,
if (w > r->cscanline) {
r->cscanline = w;
r->scanline = (unsigned char *)realloc(r->scanline, w);
if (r->scanline == NULL) return;
if (r->scanline == NULL)
return;
}
for (i = 0; i < h; i++)
@ -1532,7 +1613,8 @@ void nsvgRasterize(NSVGrasterizer* r,
// Rasterize edges
qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
// now, traverse the scanlines and find the intersections on each scanline,
// use non-zero rule
nsvg__initPaint(&cache, &shape->fill, shape->opacity);
nsvg__rasterizeSortedEdges(r, tx, ty, scale, &cache, shape->fillRule);
@ -1558,7 +1640,8 @@ void nsvgRasterize(NSVGrasterizer* r,
// Rasterize edges
qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge);
// now, traverse the scanlines and find the intersections on each scanline, use non-zero rule
// now, traverse the scanlines and find the intersections on each scanline,
// use non-zero rule
nsvg__initPaint(&cache, &shape->stroke, shape->opacity);
nsvg__rasterizeSortedEdges(r, tx, ty, scale, &cache, NSVG_FILLRULE_NONZERO);

View file

@ -3,6 +3,23 @@
#define MM_TO_PX(dpi, mm) (dpi / 25.4) * (mm)
#define ARRAY_SIZE(a) ((int)(sizeof(a) / sizeof(a[0])))
#define INT_ABS(x) ((x) > 0 ? (x) : (-(x)))
#define MIN(x, y) \
({ \
__typeof__(x) _x = (x); \
__typeof__(y) _y = (y); \
_x <= _y ? _x : _y; \
})
#define MAX(x, y) \
({ \
__typeof__(x) _x = (x); \
__typeof__(y) _y = (y); \
_x > _y ? _x : _y; \
})
struct col {
union {
unsigned int rgba;

571
include/tfblib.h Normal file
View file

@ -0,0 +1,571 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file tfblib.h
* @brief Tfblib's main header file
*/
#pragma once
#define _TFBLIB_H_
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "pbsplash.h"
/// Convenience macro used to shorten the signatures. Undefined at the end.
#define u8 uint8_t
/// Convenience macro used to shorten the signatures. Undefined at the end.
#define u32 uint32_t
/*
* ----------------------------------------------------------------------------
*
* Initialization/setup functions and definitions
*
* ----------------------------------------------------------------------------
*/
/**
* \addtogroup flags Flags
* @{
*/
/**
* Do NOT put TTY in graphics mode.
*
* Passing this flag to tfb_acquire_fb() will
* allow to use the framebuffer and to see stdout on TTY as well. That usually
* is undesirable because the text written to TTY will overwrite the graphics.
*/
#define TFB_FL_NO_TTY_KD_GRAPHICS (1 << 0)
/**
* Do NOT write directly onto the framebuffer.
*
* Passing this flag to tfb_acquire_fb() will make it allocate a regular memory
* buffer where all the writes (while drawing) will be directed to. The changes
* will appear on-screen only after manually called tfb_flush_rect() or
* tfb_flush_rect(). This flag is useful for applications needing to clean and
* redraw the whole screen (or part of it) very often (e.g. games) in order to
* avoid the annoying flicker effect.
*/
#define TFB_FL_USE_DOUBLE_BUFFER (1 << 1)
/** @} */
/**
* Opens and maps the framebuffer device in the current address space
*
* A successful call to tfb_acquire_fb() is mandatory before calling any drawing
* functions, including the tfb_clear_* and tfb_flush_* functions.
*
* @param[in] flags One or more among: #TFB_FL_NO_TTY_KD_GRAPHICS,
* #TFB_FL_USE_DOUBLE_BUFFER.
*
* @param[in] fb_device The framebuffer device file. Can be NULL.
* Defaults to /dev/fb0.
*
* @param[in] tty_device The tty device file to use for setting tty in
* graphics mode. Can be NULL. Defaults to /dev/tty.
*
* @return #TFB_SUCCESS in case of success or one of the
* following errors:
* #TFB_ERR_OPEN_FB,
* #TFB_ERR_IOCTL_FB,
* #TFB_ERR_UNSUPPORTED_VIDEO_MODE,
* #TFB_ERR_TTY_GRAPHIC_MODE,
* #TFB_ERR_MMAP_FB,
* #TFB_ERR_OUT_OF_MEMORY.
*
* \note This function does not affect the kb mode. tfb_set_kb_raw_mode() can
* be called before or after tfb_acquire_fb().
*/
int tfb_acquire_fb(u32 flags, const char *fb_device, const char *tty_device);
/**
* Release the framebuffer device
*
* \note The function **must** be called before exiting, otherwise the TTY
* will remain in graphics mode and be unusable.
*
* \note This function does not affect the kb mode. If tfb_set_kb_raw_mode()
* has been used, tfb_restore_kb_mode() must be called to restore the
* kb mode to its original value.
*/
void tfb_release_fb(void);
/**
* Limit the drawing to a window at (x, y) having size (w, h)
*
* In case the application does not want to use the whole screen, it can call
* this function to get the coordinate system shifted by (+x, +y) and everything
* outside of it just cut off. Using windows smaller than then screen could
* improve application's performance.
*
* @param[in] x X coordinate of the window, in pixels
* @param[in] y Y coordinate of the window, in pixels
* @param[in] w Width of the window, in pixels
* @param[in] h Height of the window, in pixels
*
* @return #TFB_SUCCESS in case of success or #TFB_ERR_INVALID_WINDOW.
*/
int tfb_set_window(u32 x, u32 y, u32 w, u32 h);
/**
* Limit the drawing to a window having size (w, h) at the center of the screen
*
* tfb_set_center_window_size() is a wrapper of tfb_set_window() which just
* calculates the (x, y) coordinates of the window in order it to be at the
* center of the screen.
*
* @param[in] w Width of the window, in pixels
* @param[in] h Height of the window, in pixels
*
* @return #TFB_SUCCESS in case of success or #TFB_ERR_INVALID_WINDOW.
*/
int tfb_set_center_window_size(u32 w, u32 h);
/*
* ----------------------------------------------------------------------------
*
* Text-related functions and definitions
*
* ----------------------------------------------------------------------------
*/
/**
* \addtogroup flags Flags
* @{
*/
/**
* When passed to the 'w' param of tfb_set_font_by_size(), means that any font
* width is acceptable.
*/
#define TFB_FONT_ANY_WIDTH 0
/**
* When passed to the 'h' param of tfb_set_font_by_size(), means that any font
* height is acceptable.
*/
#define TFB_FONT_ANY_HEIGHT 0
/** @} */
/**
* Opaque font type
*/
typedef void *tfb_font_t;
/**
* Font info structure
*
* tfb_iterate_over_fonts() passes a pointer to a struct tfb_font_info * for
* each statically embedded font in the library to the callback function.
*/
struct tfb_font_info {
const char *name; /**< Font's file name */
u32 width; /**< Font's character width in pixels */
u32 height; /**< Font's character height in pixels */
u32 psf_version; /**< PSF version: either 1 or 2 */
tfb_font_t font_id; /**< An opaque identifier of the font */
};
/**
* Callback type accepted by tfb_iterate_over_fonts().
*/
typedef bool (*tfb_font_iter_func)(struct tfb_font_info *cb, void *user_arg);
/**
* Iterate over the fonts embedded in the library.
*
* tfb_iterate_over_fonts() calls 'cb' once for each embedded font passing to
* it a pointer a struct tfb_font_info structure and the user_arg until either
* the font list is over or the callback returned false.
*
* @param[in] cb An user callback function
* @param[in] user_arg An arbitrary user pointer that will be passed to the
* callback function.
*/
void tfb_iterate_over_fonts(tfb_font_iter_func cb, void *user_arg);
/**
* Set the font used by the functions for drawing text
*
* @param[in] font_id An opaque identifier provided by the library either
* as a member of struct tfb_font_info, or returned as
* an out parameter by tfb_dyn_load_font().
*
* @return #TFB_SUCCESS in case of success or
* #TFB_ERR_INVALID_FONT_ID otherwise.
*/
int tfb_set_current_font(tfb_font_t font_id);
/**
* Load dynamically a PSF font file
*
* @param[in] file File path
* @param[in,out] font_id Address of a tfb_font_t variable that will
* be set by the function in case of success.
*
* @return #TFB_SUCCESS in case of success or one of the
* following errors:
* #TFB_ERR_READ_FONT_FILE_FAILED,
* #TFB_ERR_OUT_OF_MEMORY.
*/
int tfb_dyn_load_font(const char *file, tfb_font_t *font_id);
/**
* Unload a dynamically-loaded font
*
* @param[in] font_id Opaque pointer returned by tfb_dyn_load_font()
*
* @return #TFB_SUCCESS in case of success or
* #TFB_ERR_NOT_A_DYN_LOADED_FONT if the caller passed
* to it the font_id of an embedded font.
*/
int tfb_dyn_unload_font(tfb_font_t font_id);
/**
* Select the first font matching the given (w, h) criteria
*
* The tfb_set_font_by_size() function iterates over the fonts embedded in the
* library and sets the first font having width = w and height = h.
*
* @param[in] w Desired width of the font.
* The caller may pass #TFB_FONT_ANY_WIDTH to tell the
* function that any font width is acceptable.
*
* @param[in] h Desired height of the font.
* The caller may pass #TFB_FONT_ANY_HEIGHT to tell the
* function that any font width is acceptable.
*
* @return #TFB_SUCCESS in case a font matching the given criteria has
* been found or #TFB_ERR_FONT_NOT_FOUND otherwise.
*/
int tfb_set_font_by_size(int w, int h);
/**
* Get current font's width
*
* @return the width (in pixels) of the current font or 0 in case there
* is no currently selected font.
*/
int tfb_get_curr_font_width(void);
/**
* Get current font's height
*
* @return the height (in pixels) of the current font or 0 in case there
* is no currently selected font.
*/
int tfb_get_curr_font_height(void);
/*
* ----------------------------------------------------------------------------
*
* Drawing functions
*
* ----------------------------------------------------------------------------
*/
/**
* Value for 1 degree (of 360) of hue, when passed to tfb_make_color_hsv()
*/
#define TFB_HUE_DEGREE 256
/**
* Get a representation of the RGB color (r, g, b) for the current video mode
*
* @param[in] r Red color component [0, 255]
* @param[in] g Green color component [0, 255]
* @param[in] b Blue color component [0, 255]
*
* @return A framebuffer-specific representation of the RGB color
* passed using the r, g, b parameters.
*/
inline u32 tfb_make_color(u8 r, u8 g, u8 b);
/**
* Get a representation of the HSV color (h, s, v) for the current video mode
*
* @param[in] h Hue [0, 360 * #TFB_HUE_DEGREE]
* @param[in] s Saturation [0, 255]
* @param[in] v Value (Brightness) [0, 255]
*
* @return A framebuffer-specific representation of the HSV color
* passed using the h, s, v parameters.
*
* \note 1 degree of hue is #TFB_HUE_DEGREE, not simply 1. This is necessary
* in order to increase the precision of the internal integer-only
* computations.
*/
u32 tfb_make_color_hsv(u32 h, u8 s, u8 v);
/**
* Set the color of the pixel at (x, y) to 'color'
*
* @param[in] x Window-relative X coordinate of the pixel
* @param[in] y Window-relative Y coordinate of the pixel
* @param[in] color A color returned by tfb_make_color()
*
* \note By default, the library uses as "window" the whole screen, therefore
* by default the point (x, y) corresponds to the pixel at (x, y) on the
* screen. But, after calling tfb_set_window() the origin of the
* coordinate system gets shifted.
*/
inline void tfb_draw_pixel(int x, int y, u32 color);
/**
* Draw a horizonal line on-screen
*
* @param[in] x Window-relative X coordinate of line's first point
* @param[in] y Window-relative Y coordinate of line's first point
* @param[in] len Length of the line, in pixels
* @param[in] color Color of the line. See tfb_make_color().
*
* Calling tfb_draw_hline(x, y, len, color) is equivalent to calling:
* tfb_draw_line(x, y, x + len, y, color)
*
* The only difference between the two functions is in the implementation: given
* the simpler task of tfb_draw_hline(), it can be implemented in much more
* efficient way.
*/
void tfb_draw_hline(int x, int y, int len, u32 color);
/**
* Draw a vertical line on-screen
*
* @param[in] x Window-relative X coordinate of line's first point
* @param[in] y Window-relative Y coordinate of line's first point
* @param[in] len Length of the line, in pixels
* @param[in] color Color of the line. See tfb_make_color().
*
* Calling tfb_draw_vline(x, y, len, color) is equivalent to calling:
* tfb_draw_line(x, y, x, y + len, color)
*
* The only difference between the two functions is in the implementation: given
* the simpler task of tfb_draw_vline(), it can be implemented in much more
* efficient way.
*/
void tfb_draw_vline(int x, int y, int len, u32 color);
/**
* Draw a line on-screen
*
* @param[in] x0 Window-relative X coordinate of line's first point
* @param[in] y0 Window-relative Y coordinate of line's first point
* @param[in] x1 Window-relative X coordinate of line's second point
* @param[in] y1 Window-relative Y coordinate of line's second point
* @param[in] color Color of the line. See tfb_make_color().
*/
void tfb_draw_line(int x0, int y0, int x1, int y1, u32 color);
/**
* Draw an empty rectangle on-screen
*
* @param[in] x Window-relative X coordinate of rect's top-left corner
* @param[in] y Window-relative Y coordinate of rect's top-left corner
* @param[in] w Width of the rectangle
* @param[in] h Height of the rectangle
* @param[in] color Color of the rectangle
*/
void tfb_draw_rect(int x, int y, int w, int h, u32 color);
/**
* Draw filled rectangle on-screen
*
* @param[in] x Window-relative X coordinate of rect's top-left corner
* @param[in] y Window-relative Y coordinate of rect's top-left corner
* @param[in] w Width of the rectangle
* @param[in] h Height of the rectangle
* @param[in] color Color of the rectangle
*/
void tfb_fill_rect(int x, int y, int w, int h, u32 color);
/**
* Draw an empty circle on-screen
*
* @param[in] cx X coordinate of circle's center
* @param[in] cy Y coordinate of circle's center
* @param[in] r Circle's radius
* @param[in] color Circle's color
*/
void tfb_draw_circle(int cx, int cy, int r, u32 color);
/**
* Draw a filled circle on-screen
*
* @param[in] cx X coordinate of circle's center
* @param[in] cy Y coordinate of circle's center
* @param[in] r Circle's radius
* @param[in] color Circle's color
*/
void tfb_fill_circle(int cx, int cy, int r, u32 color);
/**
* Blit a 32-bit RGBA buffer to the screen at the specified coordinates.
*
* @param[in] buf The buffer
* @param[in] x Window-relative X coordinate of the top-left corner of
* the buffer
* @param[in] y Window-relative Y coordinate of the top-left corner of
* the buffer
* @param[in] w Width of the buffer
* @param[in] h Height of the buffer
* @param[in] bg Background color. Pixels with this color are not
* blitted to the screen.
* @param[in] vflip If true, the buffer is flipped vertically
*/
void blit_buf(unsigned char *buf, int x, int y, int w, int h, struct col bg, bool vflip);
/**
* Set all the pixels of the screen to the supplied color
*
* @param[in] color The color. See tfb_make_color().
*/
void tfb_clear_screen(u32 color);
/**
* Set all the pixels of the current window to the supplied color
*
* @param[in] color The color. See tfb_make_color().
*
* \note Unless tfb_set_window() has been called, the current window is by
* default large as the whole screen.
*/
void tfb_clear_win(u32 color);
/**
* Get screen's physical width in mm
*
* @return the width of the screen in mm
*/
u32 tfb_screen_width_mm(void);
/**
* Get screen's physical height in mm
*
* @return the height of the screen in mm
*/
u32 tfb_screen_height_mm(void);
/**
* Get the screen's rotation as a multiple of 90 degrees
* 1: 90 degrees
* 2: 180 degrees
* 3: 270 degrees
*/
int tfb_get_rotation(void);
/**
* Flush a given region to the actual framebuffer
*
* @param[in] x Window-relative X coordinate of region's position
* @param[in] y Window-relative Y coordinate of region's position
* @param[in] w Width of the region (in pixels)
* @param[in] h Height of the region (in pixels)
*
* In case tfb_acquire_fb() has been called with #TFB_FL_USE_DOUBLE_BUFFER,
* this function copies the pixels in the specified region to actual
* framebuffer. By default double buffering is not used and this function has no
* effect.
*/
void tfb_flush_rect(int x, int y, int w, int h);
/**
* Flush the current window to the actual framebuffer
*
* A shortcut for tfb_flush_rect(0, 0, tfb_win_width(), tfb_win_height()).
*
* @see tfb_flush_rect
* @see tfb_set_window
*/
void tfb_flush_window(void);
/**
* Flush the framebuffer, causing it to update. This is different
* to tfb_flush_window() as it doesn't deal with double_buffering,
* rather it handles the case where the framebuffer has to be "ACTIVATED".
*
* @return #TFB_SUCCESS on success or #TFB_ERR_FB_FLUSH_IOCTL_FAILED
* on failure.
*
* @see tfb_flush_window
*/
int tfb_flush_fb(void);
/* Essential variables */
extern void *__fb_buffer;
extern void *__fb_real_buffer;
extern int __fb_screen_w;
extern int __fb_screen_h;
extern size_t __fb_size;
extern size_t __fb_pitch;
extern size_t __fb_pitch_div4; /* see the comment in drawing.c */
/* Window-related variables */
extern int __fb_win_w;
extern int __fb_win_h;
extern int __fb_off_x;
extern int __fb_off_y;
extern int __fb_win_end_x;
extern int __fb_win_end_y;
/* Color-related variables */
extern u32 __fb_r_mask;
extern u32 __fb_g_mask;
extern u32 __fb_b_mask;
extern u8 __fb_r_mask_size;
extern u8 __fb_g_mask_size;
extern u8 __fb_b_mask_size;
extern u8 __fb_r_pos;
extern u8 __fb_g_pos;
extern u8 __fb_b_pos;
extern uint32_t tfb_red;
extern uint32_t tfb_blue;
extern uint32_t tfb_white;
extern uint32_t tfb_gray;
extern uint32_t tfb_black;
inline u32 tfb_make_color(u8 r, u8 g, u8 b)
{
return ((r << __fb_r_pos) & __fb_r_mask) | ((g << __fb_g_pos) & __fb_g_mask) |
((b << __fb_b_pos) & __fb_b_mask);
}
inline void tfb_draw_pixel(int x, int y, u32 color)
{
x += __fb_off_x;
y += __fb_off_y;
if ((u32)x < (u32)__fb_win_end_x && (u32)y < (u32)__fb_win_end_y)
((volatile u32 *)__fb_buffer)[x + y * __fb_pitch_div4] = color;
}
inline u32 tfb_screen_width(void)
{
return __fb_screen_w;
}
inline u32 tfb_screen_height(void)
{
return __fb_screen_h;
}
inline u32 tfb_win_width(void)
{
return __fb_win_w;
}
inline u32 tfb_win_height(void)
{
return __fb_win_h;
}
/* undef the the convenience types defined above */
#undef u8
#undef u32

View file

@ -2,7 +2,6 @@ project('pbsplash', 'c')
cc = meson.get_compiler('c')
deps = [
dependency('tfblib'),
cc.find_library('m', required : false)
]

View file

@ -18,15 +18,14 @@ static void circles_wave(int frame, int w, int y_off, long dpi)
int rad = MM_TO_PX(dpi, 1);
int dist = rad * 3.5;
int amplitude = rad * 1;
int left = ((float)w / 2) - (dist * (n_circles - 1) / 2.0);
for (unsigned int i = 0; i < n_circles; i++) {
int x = left + (i * dist);
double offset = sin(f / 60.0 * PI + i);
int y = y_off + offset * amplitude;
tfb_fill_rect(x - rad - 3, y_off - amplitude - rad - 3,
rad * 2 + 6, amplitude * 2 + rad * 2 + 6,
tfb_black);
tfb_fill_rect(x - rad - 3, y_off - amplitude - rad - 3, rad * 2 + 6,
amplitude * 2 + rad * 2 + 6, tfb_black);
tfb_fill_circle(x, y, rad, t_col);
}
}

308
src/drawing.c Normal file
View file

@ -0,0 +1,308 @@
/* SPDX-License-Identifier: BSD-2-Clause */
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pbsplash.h"
#include "tfblib.h"
extern inline uint32_t tfb_make_color(uint8_t red, uint8_t green, uint8_t blue);
extern inline void tfb_draw_pixel(int x, int y, uint32_t color);
extern inline uint32_t tfb_screen_width(void);
extern inline uint32_t tfb_screen_height(void);
extern inline uint32_t tfb_win_width(void);
extern inline uint32_t tfb_win_height(void);
void *__fb_buffer;
void *__fb_real_buffer;
size_t __fb_size;
size_t __fb_pitch;
size_t __fb_pitch_div4; /*
* Used in tfb_draw_pixel* to save a (x << 2) operation.
* If we had to use __fb_pitch, we'd had to write:
* *(uint32_t *)(__fb_buffer + (x << 2) + y * __fb_pitch)
* which clearly requires an additional shift operation
* that we can skip by using __fb_pitch_div4 + an early
* cast to uint32_t.
*/
int __fb_screen_w;
int __fb_screen_h;
int __fb_win_w;
int __fb_win_h;
int __fb_off_x;
int __fb_off_y;
int __fb_win_end_x;
int __fb_win_end_y;
uint32_t __fb_r_mask;
uint32_t __fb_g_mask;
uint32_t __fb_b_mask;
uint8_t __fb_r_mask_size;
uint8_t __fb_g_mask_size;
uint8_t __fb_b_mask_size;
uint8_t __fb_r_pos;
uint8_t __fb_g_pos;
uint8_t __fb_b_pos;
int tfb_set_center_window_size(uint32_t w, uint32_t h)
{
return tfb_set_window(__fb_screen_w / 2 - w / 2, __fb_screen_h / 2 - h / 2, w, h);
}
void tfb_clear_screen(uint32_t color)
{
if (__fb_pitch == (uint32_t)4 * __fb_screen_w) {
memset(__fb_buffer, color, __fb_size >> 2);
return;
}
for (int y = 0; y < __fb_screen_h; y++)
tfb_draw_hline(0, y, __fb_screen_w, color);
}
void tfb_clear_win(uint32_t color)
{
tfb_fill_rect(0, 0, __fb_win_w, __fb_win_h, color);
}
void tfb_draw_hline(int x, int y, int len, uint32_t color)
{
if (x < 0) {
len += x;
x = 0;
}
x += __fb_off_x;
y += __fb_off_y;
if (len < 0 || y < __fb_off_y || y >= __fb_win_end_y)
return;
len = MIN(len, MAX(0, (int)__fb_win_end_x - x));
memset(__fb_buffer + y * __fb_pitch + (x << 2), color, len);
}
void tfb_draw_vline(int x, int y, int len, uint32_t color)
{
int yend;
if (y < 0) {
len += y;
y = 0;
}
x += __fb_off_x;
y += __fb_off_y;
if (len < 0 || x < __fb_off_x || x >= __fb_win_end_x)
return;
yend = MIN(y + len, __fb_win_end_y);
volatile uint32_t *buf = ((volatile uint32_t *)__fb_buffer) + y * __fb_pitch_div4 + x;
for (; y < yend; y++, buf += __fb_pitch_div4)
*buf = color;
}
void tfb_fill_rect(int x, int y, int w, int h, uint32_t color)
{
uint32_t yend;
void *dest;
if (w < 0) {
x += w;
w = -w;
}
if (h < 0) {
y += h;
h = -h;
}
x += __fb_off_x;
y += __fb_off_y;
if (x < 0) {
w += x;
x = 0;
}
if (y < 0) {
h += y;
y = 0;
}
if (w < 0 || h < 0)
return;
w = MIN(w, MAX(0, (int)__fb_win_end_x - x));
yend = MIN(y + h, __fb_win_end_y);
dest = __fb_buffer + y * __fb_pitch + (x << 2);
for (uint32_t cy = y; cy < yend; cy++, dest += __fb_pitch)
memset(dest, color, w);
}
void tfb_draw_rect(int x, int y, int w, int h, uint32_t color)
{
tfb_draw_hline(x, y, w, color);
tfb_draw_vline(x, y, h, color);
tfb_draw_vline(x + w - 1, y, h, color);
tfb_draw_hline(x, y + h - 1, w, color);
}
static void midpoint_line(int x, int y, int x1, int y1, uint32_t color, bool swap_xy)
{
const int dx = INT_ABS(x1 - x);
const int dy = INT_ABS(y1 - y);
const int sx = x1 > x ? 1 : -1;
const int sy = y1 > y ? 1 : -1;
const int incE = dy << 1;
const int incNE = (dy - dx) << 1;
const int inc_d[2] = { incNE, incE };
const int inc_y[2] = { sy, 0 };
int d = (dy << 1) - dx;
if (swap_xy) {
tfb_draw_pixel(y, x, color);
while (x != x1) {
x += sx;
y += inc_y[d <= 0];
d += inc_d[d <= 0];
tfb_draw_pixel(y, x, color);
}
} else {
tfb_draw_pixel(x, y, color);
while (x != x1) {
x += sx;
y += inc_y[d <= 0];
d += inc_d[d <= 0];
tfb_draw_pixel(x, y, color);
}
}
}
void tfb_draw_line(int x0, int y0, int x1, int y1, uint32_t color)
{
if (INT_ABS(y1 - y0) <= INT_ABS(x1 - x0))
midpoint_line(x0, y0, x1, y1, color, false);
else
midpoint_line(y0, x0, y1, x1, color, true);
}
/*
* Based on the pseudocode in:
* https://sites.google.com/site/johnkennedyshome/home/downloadable-papers/bcircle.pdf
*
* Written by John Kennedy, Mathematics Department, Santa Monica College.
*/
void tfb_draw_circle(int cx, int cy, int r, uint32_t color)
{
int x = r;
int y = 0;
int xch = 1 - 2 * r;
int ych = 1;
int rerr = 0;
while (x >= y) {
tfb_draw_pixel(cx + x, cy + y, color);
tfb_draw_pixel(cx - x, cy + y, color);
tfb_draw_pixel(cx - x, cy - y, color);
tfb_draw_pixel(cx + x, cy - y, color);
tfb_draw_pixel(cx + y, cy + x, color);
tfb_draw_pixel(cx - y, cy + x, color);
tfb_draw_pixel(cx - y, cy - x, color);
tfb_draw_pixel(cx + y, cy - x, color);
y++;
rerr += ych;
ych += 2;
if (2 * rerr + xch > 0) {
x--;
rerr += xch;
xch += 2;
}
}
}
/*
* Simple algorithm for drawing a filled circle which just scans the whole
* 2R x 2R square containing the circle.
*/
void tfb_fill_circle(int cx, int cy, int r, uint32_t color)
{
const int r2 = r * r + r;
for (int y = -r; y <= r; y++)
for (int x = -r; x <= r; x++)
if (x * x + y * y <= r2)
tfb_draw_pixel(cx + x, cy + y, color);
}
#define DEBUGRENDER 0
/* x and y are expected to be relative to the screen rotation, however the buffer
* width and height won't be, we need to handle rotating the buffer here.
*/
void blit_buf(unsigned char *buf, int x, int y, int w, int h, struct col bg, bool vflip)
{
struct col prev_col = { .r = 0, .g = 0, .b = 0, .a = 0 };
unsigned int col = tfb_make_color(bg.r, bg.g, bg.b);
int tmp, rot = tfb_get_rotation();
if (vflip)
rot = (rot + 2) % 4;
switch (rot) {
case 1:
tmp = w;
w = h;
h = tmp;
case 0:
default:
break;
}
for (size_t i = 0; i < w; i++) {
for (size_t j = 0; j < h; j++) {
#if DEBUGRENDER == 1
if (i == 0 || i == w - 1 || j == 0 || j == h - 1) {
tfb_draw_pixel(x + i, y + h - j, tfb_red);
continue;
}
#endif
struct col rgba = *(struct col *)(buf + (j * w + i) * 4);
if (rgba.a == 0 || rgba.rgba == bg.rgba)
continue;
// Alpha blending
if (rgba.a != 255) {
rgba.r = (rgba.r * rgba.a + bg.r * (255 - rgba.a)) >> 8;
rgba.g = (rgba.g * rgba.a + bg.g * (255 - rgba.a)) >> 8;
rgba.b = (rgba.b * rgba.a + bg.b * (255 - rgba.a)) >> 8;
}
// No need to generate the colour again if it's the same as the previous one
if (rgba.rgba != prev_col.rgba) {
prev_col.rgba = rgba.rgba;
col = tfb_make_color(rgba.r, rgba.g, rgba.b);
}
if (vflip)
tfb_draw_pixel(x + i, y + h - j, col);
else
tfb_draw_pixel(x + i, y + j, col);
}
}
}

261
src/fb.c Normal file
View file

@ -0,0 +1,261 @@
/* SPDX-License-Identifier: BSD-2-Clause */
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <linux/kd.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <termios.h>
#include <unistd.h>
#include "pbsplash.h"
#include "tfblib.h"
#define DEFAULT_FB_DEVICE "/dev/fb0"
#define DEFAULT_TTY_DEVICE "/dev/tty"
struct fb_var_screeninfo __fbi;
#define ROTATE_SWAP_XY (__fbi.rotate == 1 || __fbi.rotate == 3)
int __tfb_ttyfd = -1;
static int fbfd = -1;
static void tfb_init_colors(void);
int tfb_set_window(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
if (x + w > (uint32_t)__fb_screen_w) {
fprintf(stderr, "tfb_set_window: window exceeds screen width\n");
}
if (y + h > (uint32_t)__fb_screen_h) {
fprintf(stderr, "tfb_set_window: window exceeds screen height\n");
}
__fb_off_x = __fbi.xoffset + x;
__fb_off_y = __fbi.yoffset + y;
__fb_win_w = w;
__fb_win_h = h;
__fb_win_end_x = __fb_off_x + __fb_win_w;
__fb_win_end_y = __fb_off_y + __fb_win_h;
return 0;
}
int tfb_acquire_fb(uint32_t flags, const char *fb_device, const char *tty_device)
{
static struct fb_fix_screeninfo fb_fixinfo;
if (!fb_device)
fb_device = DEFAULT_FB_DEVICE;
if (!tty_device)
tty_device = DEFAULT_TTY_DEVICE;
fbfd = open(fb_device, O_RDWR);
if (fbfd < 0) {
perror("Couldn't open framebuffer device");
return -1;
}
if (ioctl(fbfd, FBIOGET_FSCREENINFO, &fb_fixinfo) != 0) {
perror("Couldn't get fb fixed info");
close(fbfd);
return -1;
}
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &__fbi) != 0) {
perror("Couldn't get fb vscreen info");
close(fbfd);
return -1;
}
__fb_pitch = fb_fixinfo.line_length;
__fb_size = __fb_pitch * __fbi.yres;
__fb_pitch_div4 = __fb_pitch >> 2;
if (__fbi.bits_per_pixel != 32) {
fprintf(stderr, "Unsupported framebuffer format: %u\n", __fbi.bits_per_pixel);
close(fbfd);
return -1;
}
if (__fbi.red.msb_right || __fbi.green.msb_right || __fbi.blue.msb_right) {
fprintf(stderr, "Sanity check failed for RGB masks: %u %u %u\n",
__fbi.red.msb_right, __fbi.green.msb_right, __fbi.blue.msb_right);
close(fbfd);
return -1;
}
__tfb_ttyfd = open(tty_device, O_RDWR);
if (__tfb_ttyfd < 0) {
perror("Couldn't open tty device");
close(fbfd);
return -1;
}
if (!(flags & TFB_FL_NO_TTY_KD_GRAPHICS)) {
if (ioctl(__tfb_ttyfd, KDSETMODE, KD_GRAPHICS) != 0) {
perror("Couldn't set tty to graphics mode");
close(fbfd);
ioctl(__tfb_ttyfd, KDSETMODE, KD_TEXT);
close(__tfb_ttyfd);
return -1;
}
}
__fb_real_buffer = mmap(NULL, __fb_size, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
if (__fb_real_buffer == MAP_FAILED) {
perror("Couldn't mmap framebuffer");
close(fbfd);
close(__tfb_ttyfd);
return -1;
}
if (flags & TFB_FL_USE_DOUBLE_BUFFER) {
__fb_buffer = malloc(__fb_size);
if (!__fb_buffer) {
perror("Couldn't allocate double buffer");
tfb_release_fb();
return -1;
}
} else {
__fb_buffer = __fb_real_buffer;
}
__fb_screen_w = ROTATE_SWAP_XY ? __fbi.yres : __fbi.xres;
__fb_screen_h = ROTATE_SWAP_XY ? __fbi.xres : __fbi.yres;
__fb_r_pos = __fbi.red.offset;
__fb_r_mask_size = __fbi.red.length;
__fb_r_mask = ((1 << __fb_r_mask_size) - 1) << __fb_r_pos;
__fb_g_pos = __fbi.green.offset;
__fb_g_mask_size = __fbi.green.length;
__fb_g_mask = ((1 << __fb_g_mask_size) - 1) << __fb_g_pos;
__fb_b_pos = __fbi.blue.offset;
__fb_b_mask_size = __fbi.blue.length;
__fb_b_mask = ((1 << __fb_b_mask_size) - 1) << __fb_b_pos;
tfb_set_window(0, 0, __fb_screen_w, __fb_screen_h);
tfb_init_colors();
return 0;
}
void tfb_release_fb(void)
{
if (__fb_real_buffer)
munmap(__fb_real_buffer, __fb_size);
if (__fb_buffer != __fb_real_buffer)
free(__fb_buffer);
if (__tfb_ttyfd != -1) {
ioctl(__tfb_ttyfd, KDSETMODE, KD_TEXT);
close(__tfb_ttyfd);
}
if (fbfd != -1)
close(fbfd);
}
int tfb_get_rotation(void)
{
return __fbi.rotate;
}
void tfb_flush_rect(int x, int y, int w, int h)
{
int yend;
if (__fb_buffer == __fb_real_buffer)
return;
x += __fb_off_x;
y += __fb_off_y;
if (x < 0) {
w += x;
x = 0;
}
if (y < 0) {
h += y;
y = 0;
}
if (w < 0 || h < 0)
return;
w = MIN(w, MAX(0, __fb_win_end_x - x));
yend = MIN(y + h, __fb_win_end_y);
size_t offset = y * __fb_pitch + (__fb_off_x << 2);
void *dest = __fb_real_buffer + offset;
void *src = __fb_buffer + offset;
uint32_t rect_pitch = w << 2;
for (int cy = y; cy < yend; cy++, src += __fb_pitch, dest += __fb_pitch)
memcpy(dest, src, rect_pitch);
}
void tfb_flush_window(void)
{
tfb_flush_rect(0, 0, __fb_win_w, __fb_win_h);
}
int tfb_flush_fb(void)
{
__fbi.activate |= FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
if (ioctl(fbfd, FBIOPUT_VSCREENINFO, &__fbi) < 0) {
perror("Couldn't flush framebuffer");
return -1;
}
return 0;
}
uint32_t tfb_screen_width_mm(void)
{
return ROTATE_SWAP_XY ? __fbi.height : __fbi.width;
}
uint32_t tfb_screen_height_mm(void)
{
return ROTATE_SWAP_XY ? __fbi.width : __fbi.height;
}
/*
* ----------------------------------------------------------------------------
*
* Colors
*
* ----------------------------------------------------------------------------
*/
uint32_t tfb_red;
uint32_t tfb_blue;
uint32_t tfb_white;
uint32_t tfb_gray;
uint32_t tfb_black;
static void tfb_init_colors(void)
{
tfb_red = tfb_make_color(255, 0, 0);
tfb_blue = tfb_make_color(0, 0, 255);
tfb_white = tfb_make_color(255, 255, 255);
tfb_gray = tfb_make_color(128, 128, 128);
tfb_black = tfb_make_color(0, 0, 0);
}

View file

@ -3,6 +3,8 @@ src = [
'nanosvg.c',
'timespec.c',
'pbsplash.c',
'fb.c',
'drawing.c',
]
executable('pbsplash', src,

View file

@ -1,23 +1,23 @@
#include <errno.h>
#include <fcntl.h>
#include <linux/kd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <tfblib/tfb_colors.h>
#include <tfblib/tfblib.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/kd.h>
#include <fcntl.h>
#include <tfblib/tfblib.h>
#include <tfblib/tfb_colors.h>
#include <string.h>
#include <math.h>
#include <string.h>
#define NANOSVG_ALL_COLOR_KEYWORDS // Include full list of color keywords.
#include "nanosvg.h"
#include "nanosvgrast.h"
#include "timespec.h"
#include "pbsplash.h"
#include "timespec.h"
#define MSG_MAX_LEN 4096
#define DEFAULT_FONT_PATH "/usr/share/pbsplash/OpenSans-Regular.svg"
@ -28,12 +28,10 @@
#define PT_TO_MM 0.38f
#define TTY_PATH_LEN 11
#define DEBUGRENDER 0
volatile sig_atomic_t terminate = 0;
bool debug = false;
struct col background_color = { .r = 0, .g = 0, .b = 0, .a = 255 };
struct col bg = { .r = 0, .g = 0, .b = 0, .a = 255 };
static int screenWidth, screenHeight;
@ -74,55 +72,6 @@ static void term(int signum)
terminate = 1;
}
static void blit_buf(unsigned char *buf, int x, int y, int w, int h, bool vflip,
bool redraw)
{
struct col prev_col = { .r = 0, .g = 0, .b = 0, .a = 0 };
unsigned int col = tfb_make_color(
background_color.r, background_color.g, background_color.b);
for (size_t i = 0; i < w; i++) {
for (size_t j = 0; j < h; j++) {
#if DEBUGRENDER == 1
if (i == 0 || i == w - 1 || j == 0 || j == h - 1) {
tfb_draw_pixel(x + i, y + h - j, tfb_red);
continue;
}
#endif
struct col rgba =
*(struct col *)(buf + (j * w + i) * 4);
if (rgba.a == 0 || rgba.rgba == background_color.rgba)
continue;
// Alpha blending
if (rgba.a != 255) {
rgba.r =
(rgba.r * rgba.a +
background_color.r * (255 - rgba.a)) >>
8;
rgba.g =
(rgba.g * rgba.a +
background_color.g * (255 - rgba.a)) >>
8;
rgba.b =
(rgba.b * rgba.a +
background_color.b * (255 - rgba.a)) >>
8;
}
// No need to generate the colour again if it's the same as the previous one
if (rgba.rgba != prev_col.rgba) {
prev_col.rgba = rgba.rgba;
col = tfb_make_color(rgba.r, rgba.g, rgba.b);
}
if (vflip)
tfb_draw_pixel(x + i, y + h - j, col);
else
tfb_draw_pixel(x + i, y + j, col);
}
}
}
static void draw_svg(NSVGimage *image, int x, int y, int w, int h)
{
float sz = (int)((float)w / (float)image->width * 100.f) / 100.f;
@ -131,25 +80,24 @@ static void draw_svg(NSVGimage *image, int x, int y, int w, int h)
unsigned char *img = zalloc(w * h * 4);
nsvgRasterize(rast, image, 0, 0, sz, img, w, h, w * 4);
blit_buf(img, x, y, w, h, false, false);
blit_buf(img, x, y, w, h, false);
free(img);
nsvgDeleteRasterizer(rast);
}
static void draw_text(const NSVGimage *font, const char *text, int x, int y, int width,
int height, float scale, unsigned int tfb_col)
static void draw_text(const NSVGimage *font, const char *text, int x, int y, int width, int height,
float scale, unsigned int tfb_col)
{
LOG("text '%s': fontsz=%f, x=%d, y=%d, dimensions: %d x %d\n", text,
scale, x, y, width, height);
LOG("text '%s': fontsz=%f, x=%d, y=%d, dimensions: %d x %d\n", text, scale, x, y, width,
height);
NSVGshape **shapes = nsvgGetTextShapes(font, text, strlen(text));
unsigned char *img = zalloc(width * height * 4);
NSVGrasterizer *rast = nsvgCreateRasterizer();
nsvgRasterizeText(rast, font, 0, 0, scale, img, width, height,
width * 4, text);
nsvgRasterizeText(rast, font, 0, 0, scale, img, width, height, width * 4, text);
blit_buf(img, x, y, width, height, true, false);
blit_buf(img, x, y, width, height, true);
free(img);
free(shapes);
@ -274,21 +222,21 @@ static void calculate_dpi_info(struct dpi_info *dpi_info)
dpi_info->logo_size_max_mm = (screenWidth * 0.75f) / dpi_info->pixels_per_milli;
dpi_info->logo_size_px =
(float)(screenWidth < screenHeight ? screenWidth :
screenHeight) *
0.75f;
(float)(screenWidth < screenHeight ? screenWidth : screenHeight) * 0.75f;
if (w_mm > 0 && h_mm > 0) {
if (w_mm < h_mm) {
if (w_mm > dpi_info->logo_size_max_mm * 1.2f)
dpi_info->logo_size_px = dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli;
dpi_info->logo_size_px =
dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli;
} else {
if (h_mm > dpi_info->logo_size_max_mm * 1.2f)
dpi_info->logo_size_px = dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli;
dpi_info->logo_size_px =
dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli;
}
}
printf("%dx%d @ %dx%dmm, dpi=%ld, logo_size_px=%f\n", screenWidth,
screenHeight, w_mm, h_mm, dpi_info->dpi, dpi_info->logo_size_px);
printf("%dx%d @ %dx%dmm, dpi=%ld, logo_size_px=%f\n", screenWidth, screenHeight, w_mm, h_mm,
dpi_info->dpi, dpi_info->logo_size_px);
}
struct msg_info {
@ -301,12 +249,13 @@ struct msg_info {
float fontsz;
};
static void load_message(struct msg_info *msg_info, const struct dpi_info *dpi_info, float font_size_pt, const NSVGimage *font)
static void load_message(struct msg_info *msg_info, const struct dpi_info *dpi_info,
float font_size_pt, const NSVGimage *font)
{
msg_info->fontsz = (font_size_pt * PT_TO_MM) /
(font->fontAscent - font->fontDescent) *
msg_info->fontsz = (font_size_pt * PT_TO_MM) / (font->fontAscent - font->fontDescent) *
dpi_info->pixels_per_milli;
msg_info->message = getTextDimensions(font, msg_info->src_message, msg_info->fontsz, &msg_info->width, &msg_info->height);
msg_info->message = getTextDimensions(font, msg_info->src_message, msg_info->fontsz,
&msg_info->width, &msg_info->height);
msg_info->x = (screenWidth - msg_info->width) / 2;
// Y coordinate is set later
}
@ -346,14 +295,16 @@ static void show_messages(struct messages *msgs, const struct dpi_info *dpi_info
font_failed = true;
fprintf(stderr, "failed to load SVG font, can't render messages\n");
fprintf(stderr, " font_path: %s\n", msgs->font_path);
fprintf(stderr, "msg: %s\n\nbottom_message: %s\n", msgs->msg->src_message, msgs->bottom_msg->src_message);
fprintf(stderr, "msg: %s\n\nbottom_message: %s\n", msgs->msg->src_message,
msgs->bottom_msg->src_message);
return;
}
if (msgs->bottom_msg) {
if (!msgs->bottom_msg->message) {
load_message(msgs->bottom_msg, dpi_info, msgs->font_size_b_pt, msgs->font);
msgs->bottom_msg->y = screenHeight - msgs->bottom_msg->height - MM_TO_PX(dpi_info->dpi, B_MESSAGE_OFFSET_MM);
msgs->bottom_msg->y = screenHeight - msgs->bottom_msg->height -
MM_TO_PX(dpi_info->dpi, B_MESSAGE_OFFSET_MM);
}
show_message(msgs->bottom_msg, msgs->font);
}
@ -362,9 +313,15 @@ static void show_messages(struct messages *msgs, const struct dpi_info *dpi_info
if (!msgs->msg->message) {
load_message(msgs->msg, dpi_info, msgs->font_size_pt, msgs->font);
if (msgs->bottom_msg)
msgs->msg->y = msgs->bottom_msg->y - msgs->msg->height - (MM_TO_PX(dpi_info->dpi, msgs->font_size_b_pt * PT_TO_MM) * 0.6);
msgs->msg->y =
msgs->bottom_msg->y - msgs->msg->height -
(MM_TO_PX(dpi_info->dpi, msgs->font_size_b_pt * PT_TO_MM) *
0.6);
else
msgs->msg->y = screenHeight - msgs->msg->height - (MM_TO_PX(dpi_info->dpi, msgs->font_size_pt * PT_TO_MM) * 2);
msgs->msg->y =
screenHeight - msgs->msg->height -
(MM_TO_PX(dpi_info->dpi, msgs->font_size_pt * PT_TO_MM) *
2);
}
show_message(msgs->msg, msgs->font);
}
@ -394,14 +351,15 @@ static int load_image(const struct dpi_info *dpi_info, struct image_info *image_
if (image_info->image->width < image_info->image->height * 1.1)
logo_size_px = MM_TO_PX(dpi_info->dpi, 25);
float sz =
(float)logo_size_px /
(image_info->image->width > image_info->image->height ? image_info->image->height : image_info->image->width);
float sz = (float)logo_size_px / (image_info->image->width > image_info->image->height ?
image_info->image->height :
image_info->image->width);
image_info->width = image_info->image->width * sz + 0.5;
image_info->height = image_info->image->height * sz + 0.5;
if (image_info->width > (dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli)) {
float scalefactor =
((float)(dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli) / image_info->width);
((float)(dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli) /
image_info->width);
// printf("Got scale factor: %f\n", scalefactor);
image_info->width = dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli;
image_info->height *= scalefactor;
@ -476,24 +434,21 @@ int main(int argc, char **argv)
case 'o':
msgs.font_size_b_pt = strtof(optarg, &end);
if (end == optarg) {
fprintf(stderr, "Invalid font size: %s\n",
optarg);
fprintf(stderr, "Invalid font size: %s\n", optarg);
return usage();
}
break;
case 'p':
msgs.font_size_pt = strtof(optarg, &end);
if (end == optarg) {
fprintf(stderr, "Invalid font size: %s\n",
optarg);
fprintf(stderr, "Invalid font size: %s\n", optarg);
return usage();
}
break;
case 'q':
dpi_info.logo_size_max_mm = strtof(optarg, &end);
if (end == optarg) {
fprintf(stderr, "Invalid max logo size: %s\n",
optarg);
fprintf(stderr, "Invalid max logo size: %s\n", optarg);
return usage();
}
break;
@ -525,10 +480,9 @@ int main(int argc, char **argv)
LOG("active tty: '%s'\n", active_tty);
if ((rc = tfb_acquire_fb(/*TFB_FL_NO_TTY_KD_GRAPHICS */ 0, "/dev/fb0",
active_tty)) != TFB_SUCCESS) {
fprintf(stderr, "tfb_acquire_fb() failed with error code: %d\n",
rc);
if ((rc = tfb_acquire_fb(/*TFB_FL_NO_TTY_KD_GRAPHICS */ 0, "/dev/fb0", active_tty)) !=
TFB_SUCCESS) {
fprintf(stderr, "tfb_acquire_fb() failed with error code: %d\n", rc);
rc = 1;
return rc;
}
@ -544,8 +498,7 @@ int main(int argc, char **argv)
float animation_y = image_info.y + image_info.height + MM_TO_PX(dpi_info.dpi, 5);
tfb_clear_screen(tfb_make_color(background_color.r, background_color.g,
background_color.b));
tfb_clear_screen(tfb_make_color(bg.r, bg.g, bg.b));
draw_svg(image_info.image, image_info.x, image_info.y, image_info.width, image_info.height);
@ -607,7 +560,8 @@ out:
// Before we exit print the logo so it will persist
if (image_info.image) {
ioctl(tty, KDSETMODE, KD_TEXT);
draw_svg(image_info.image, image_info.x, image_info.y, image_info.width, image_info.height);
draw_svg(image_info.image, image_info.x, image_info.y, image_info.width,
image_info.height);
}
// Draw the messages again so they will persist

View file

@ -56,11 +56,11 @@
* is normalised according to the rules in timespec_normalise().
*/
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
#include <sys/time.h>
#include <time.h>
#include <inttypes.h>
#include "timespec.h"
@ -117,22 +117,19 @@ struct timespec timespec_mod(struct timespec ts1, struct timespec ts2)
/* If ts2 is zero, just return ts1
*/
if (ts2.tv_sec == 0 && ts2.tv_nsec == 0)
{
if (ts2.tv_sec == 0 && ts2.tv_nsec == 0) {
return ts1;
}
/* If inputs are negative, flip and record sign
*/
if (ts1.tv_sec < 0 || ts1.tv_nsec < 0)
{
if (ts1.tv_sec < 0 || ts1.tv_nsec < 0) {
neg1 = true;
ts1.tv_sec = -ts1.tv_sec;
ts1.tv_nsec = -ts1.tv_nsec;
}
if (ts2.tv_sec < 0 || ts2.tv_nsec < 0)
{
if (ts2.tv_sec < 0 || ts2.tv_nsec < 0) {
neg2 = true;
ts2.tv_sec = -ts2.tv_sec;
ts2.tv_nsec = -ts2.tv_nsec;
@ -140,13 +137,11 @@ struct timespec timespec_mod(struct timespec ts1, struct timespec ts2)
/* Shift ts2 until it is larger than ts1 or is about to overflow
*/
while ((ts2.tv_sec < (LONG_MAX >> 1)) && timespec_ge(ts1, ts2))
{
while ((ts2.tv_sec < (LONG_MAX >> 1)) && timespec_ge(ts1, ts2)) {
i++;
ts2.tv_nsec <<= 1;
ts2.tv_sec <<= 1;
if (ts2.tv_nsec > NSEC_PER_SEC)
{
if (ts2.tv_nsec > NSEC_PER_SEC) {
ts2.tv_nsec -= NSEC_PER_SEC;
ts2.tv_sec++;
}
@ -154,21 +149,17 @@ struct timespec timespec_mod(struct timespec ts1, struct timespec ts2)
/* Division by repeated subtraction
*/
while (i >= 0)
{
if (timespec_ge(ts1, ts2))
{
while (i >= 0) {
if (timespec_ge(ts1, ts2)) {
ts1 = timespec_sub(ts1, ts2);
}
if (i == 0)
{
if (i == 0) {
break;
}
i--;
if (ts2.tv_sec & 1)
{
if (ts2.tv_sec & 1) {
ts2.tv_nsec += NSEC_PER_SEC;
}
ts2.tv_nsec >>= 1;
@ -177,15 +168,13 @@ struct timespec timespec_mod(struct timespec ts1, struct timespec ts2)
/* If signs differ and result is nonzero, subtract once more to cross zero
*/
if (neg1 ^ neg2 && (ts1.tv_sec != 0 || ts1.tv_nsec != 0))
{
if (neg1 ^ neg2 && (ts1.tv_sec != 0 || ts1.tv_nsec != 0)) {
ts1 = timespec_sub(ts1, ts2);
}
/* Restore sign
*/
if (neg1)
{
if (neg1) {
ts1.tv_sec = -ts1.tv_sec;
ts1.tv_nsec = -ts1.tv_nsec;
}
@ -196,7 +185,8 @@ struct timespec timespec_mod(struct timespec ts1, struct timespec ts2)
/** \fn struct timespec timespec_min(struct timespec ts1, struct timespec ts2)
* \brief Return the lesser one of the two given timespec values.
*/
struct timespec timespec_min(struct timespec ts1, struct timespec ts2) {
struct timespec timespec_min(struct timespec ts1, struct timespec ts2)
{
if (timespec_le(ts1, ts2)) {
return ts1;
} else {
@ -207,7 +197,8 @@ struct timespec timespec_min(struct timespec ts1, struct timespec ts2) {
/** \fn struct timespec timespec_max(struct timespec ts1, struct timespec ts2)
* \brief Return the greater one of the two given timespec values.
*/
struct timespec timespec_max(struct timespec ts1, struct timespec ts2) {
struct timespec timespec_max(struct timespec ts1, struct timespec ts2)
{
if (timespec_ge(ts1, ts2)) {
return ts1;
} else {
@ -218,7 +209,8 @@ struct timespec timespec_max(struct timespec ts1, struct timespec ts2) {
/** \fn struct timespec timespec_clamp(struct timespec ts, struct timespec min, struct timespec max)
* \brief Clamp the value of TS between MIN and MAX.
*/
struct timespec timespec_clamp(struct timespec ts, struct timespec min, struct timespec max) {
struct timespec timespec_clamp(struct timespec ts, struct timespec min, struct timespec max)
{
if (timespec_gt(ts, max)) {
return max;
}
@ -236,16 +228,12 @@ int timespec_cmp(struct timespec ts1, struct timespec ts2)
ts1 = timespec_normalise(ts1);
ts2 = timespec_normalise(ts2);
if(ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec == ts2.tv_nsec)
{
if (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec == ts2.tv_nsec) {
return 0;
}
else if((ts1.tv_sec > ts2.tv_sec)
|| (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec > ts2.tv_nsec))
{
} else if ((ts1.tv_sec > ts2.tv_sec) ||
(ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec > ts2.tv_nsec)) {
return 1;
}
else {
} else {
return -1;
}
}
@ -280,7 +268,8 @@ bool timespec_ge(struct timespec ts1, struct timespec ts2)
ts1 = timespec_normalise(ts1);
ts2 = timespec_normalise(ts2);
return (ts1.tv_sec > ts2.tv_sec || (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec >= ts2.tv_nsec));
return (ts1.tv_sec > ts2.tv_sec ||
(ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec >= ts2.tv_nsec));
}
/** \fn bool timespec_lt(struct timespec ts1, struct timespec ts2)
@ -302,7 +291,8 @@ bool timespec_le(struct timespec ts1, struct timespec ts2)
ts1 = timespec_normalise(ts1);
ts2 = timespec_normalise(ts2);
return (ts1.tv_sec < ts2.tv_sec || (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec <= ts2.tv_nsec));
return (ts1.tv_sec < ts2.tv_sec ||
(ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec <= ts2.tv_nsec));
}
/** \fn struct timespec timespec_from_double(double s)
@ -331,10 +321,7 @@ double timespec_to_double(struct timespec ts)
*/
struct timespec timespec_from_timeval(struct timeval tv)
{
struct timespec ts = {
.tv_sec = tv.tv_sec,
.tv_nsec = tv.tv_usec * 1000
};
struct timespec ts = { .tv_sec = tv.tv_sec, .tv_nsec = tv.tv_usec * 1000 };
return timespec_normalise(ts);
}
@ -389,20 +376,17 @@ long timespec_to_ms(struct timespec ts)
*/
struct timespec timespec_normalise(struct timespec ts)
{
while(ts.tv_nsec >= NSEC_PER_SEC)
{
while (ts.tv_nsec >= NSEC_PER_SEC) {
++(ts.tv_sec);
ts.tv_nsec -= NSEC_PER_SEC;
}
while(ts.tv_nsec <= -NSEC_PER_SEC)
{
while (ts.tv_nsec <= -NSEC_PER_SEC) {
--(ts.tv_sec);
ts.tv_nsec += NSEC_PER_SEC;
}
if(ts.tv_nsec < 0)
{
if (ts.tv_nsec < 0) {
/* Negative nanoseconds isn't valid according to POSIX.
* Decrement tv_sec and roll tv_nsec over.
*/
@ -424,129 +408,150 @@ struct timespec timespec_now()
#ifdef TEST
#include <stdio.h>
#define TEST_NORMALISE(ts_sec, ts_nsec, expect_sec, expect_nsec) { \
#define TEST_NORMALISE(ts_sec, ts_nsec, expect_sec, expect_nsec) \
{ \
struct timespec in = { .tv_sec = ts_sec, .tv_nsec = ts_nsec }; \
struct timespec got = timespec_normalise(in); \
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \
{ \
printf("%s:%d: timespec_normalise({%ld, %ld}) returned wrong values\n", __FILE__, __LINE__, \
(long)(ts_sec), (long)(ts_nsec)); \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \
if (got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) { \
printf("%s:%d: timespec_normalise({%ld, %ld}) returned wrong values\n", \
__FILE__, __LINE__, (long)(ts_sec), (long)(ts_nsec)); \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), \
(long)(expect_nsec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), \
(long)(got.tv_nsec)); \
++result; \
} \
}
#define TEST_BINOP(func, ts1_sec, ts1_nsec, ts2_sec, ts2_nsec, expect_sec, expect_nsec) { \
#define TEST_BINOP(func, ts1_sec, ts1_nsec, ts2_sec, ts2_nsec, expect_sec, expect_nsec) \
{ \
struct timespec ts1 = { .tv_sec = ts1_sec, .tv_nsec = ts1_nsec }; \
struct timespec ts2 = { .tv_sec = ts2_sec, .tv_nsec = ts2_nsec }; \
struct timespec got = func(ts1, ts2); \
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \
{ \
if (got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) { \
printf(#func "({%ld, %ld}, {%ld, %ld}) returned wrong values\n", \
(long)(ts1_sec), (long)(ts1_nsec), (long)(ts2_sec), (long)(ts2_nsec)); \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \
(long)(ts1_sec), (long)(ts1_nsec), (long)(ts2_sec), \
(long)(ts2_nsec)); \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), \
(long)(expect_nsec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), \
(long)(got.tv_nsec)); \
++result; \
} \
}
#define TEST_TRINOP(func, ts1_sec, ts1_nsec, ts2_sec, ts2_nsec, ts3_sec, ts3_nsec, expect_sec, expect_nsec) { \
#define TEST_TRINOP(func, ts1_sec, ts1_nsec, ts2_sec, ts2_nsec, ts3_sec, ts3_nsec, expect_sec, \
expect_nsec) \
{ \
struct timespec ts1 = { .tv_sec = ts1_sec, .tv_nsec = ts1_nsec }; \
struct timespec ts2 = { .tv_sec = ts2_sec, .tv_nsec = ts2_nsec }; \
struct timespec ts3 = { .tv_sec = ts3_sec, .tv_nsec = ts3_nsec }; \
struct timespec got = func(ts1, ts2, ts3); \
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \
{ \
printf(#func "({%ld, %ld}, {%ld, %ld}, {%ld, %ld}) returned wrong values\n", \
(long)(ts1_sec), (long)(ts1_nsec), \
(long)(ts2_sec), (long)(ts2_nsec), \
(long)(ts3_sec), (long)(ts3_nsec)); \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \
if (got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) { \
printf(#func \
"({%ld, %ld}, {%ld, %ld}, {%ld, %ld}) returned wrong values\n", \
(long)(ts1_sec), (long)(ts1_nsec), (long)(ts2_sec), \
(long)(ts2_nsec), (long)(ts3_sec), (long)(ts3_nsec)); \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), \
(long)(expect_nsec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), \
(long)(got.tv_nsec)); \
++result; \
} \
}
#define TEST_TEST_FUNC(func, ts1_sec, ts1_nsec, ts2_sec, ts2_nsec, expect) { \
#define TEST_TEST_FUNC(func, ts1_sec, ts1_nsec, ts2_sec, ts2_nsec, expect) \
{ \
struct timespec ts1 = { .tv_sec = ts1_sec, .tv_nsec = ts1_nsec }; \
struct timespec ts2 = { .tv_sec = ts2_sec, .tv_nsec = ts2_nsec }; \
int got = func(ts1, ts2); \
if(got != expect) \
{ \
printf("%s:%d: " #func "({%ld, %ld}, {%ld, %ld}) returned %d, expected %s\n", __FILE__, __LINE__, \
(long)(ts1_sec), (long)(ts1_nsec), (long)(ts2_sec), (long)(ts2_nsec), \
got, #expect); \
if (got != expect) { \
printf("%s:%d: " #func \
"({%ld, %ld}, {%ld, %ld}) returned %d, expected %s\n", \
__FILE__, __LINE__, (long)(ts1_sec), (long)(ts1_nsec), \
(long)(ts2_sec), (long)(ts2_nsec), got, #expect); \
++result; \
} \
}
#define TEST_FROM_DOUBLE(d_secs, expect_sec, expect_nsec) { \
#define TEST_FROM_DOUBLE(d_secs, expect_sec, expect_nsec) \
{ \
struct timespec got = timespec_from_double(d_secs); \
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \
{ \
printf("%s:%d: timespec_from_double(%f) returned wrong values\n", __FILE__, __LINE__, (double)(d_secs)); \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \
if (got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) { \
printf("%s:%d: timespec_from_double(%f) returned wrong values\n", \
__FILE__, __LINE__, (double)(d_secs)); \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), \
(long)(expect_nsec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), \
(long)(got.tv_nsec)); \
++result; \
} \
}
#define TEST_TO_DOUBLE(ts_sec, ts_nsec, expect) { \
#define TEST_TO_DOUBLE(ts_sec, ts_nsec, expect) \
{ \
struct timespec ts = { .tv_sec = ts_sec, .tv_nsec = ts_nsec }; \
double got = timespec_to_double(ts); \
if (got != expect) { \
printf("%s:%d: timespec_to_double({%ld, %ld}) returned wrong value\n", __FILE__, __LINE__, \
(long)(ts_sec), (long)(ts_nsec)); \
printf("%s:%d: timespec_to_double({%ld, %ld}) returned wrong value\n", \
__FILE__, __LINE__, (long)(ts_sec), (long)(ts_nsec)); \
printf(" Expected: %f\n", (double)(expect)); \
printf(" Got: %f\n", got); \
++result; \
} \
}
#define TEST_FROM_TIMEVAL(in_sec, in_usec, expect_sec, expect_nsec) { \
#define TEST_FROM_TIMEVAL(in_sec, in_usec, expect_sec, expect_nsec) \
{ \
struct timeval tv = { .tv_sec = in_sec, .tv_usec = in_usec }; \
struct timespec got = timespec_from_timeval(tv); \
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \
{ \
printf("%s:%d: timespec_from_timeval({%ld, %ld}) returned wrong values\n", __FILE__, __LINE__, \
(long)(in_sec), (long)(in_usec)); \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \
if (got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) { \
printf("%s:%d: timespec_from_timeval({%ld, %ld}) returned wrong values\n", \
__FILE__, __LINE__, (long)(in_sec), (long)(in_usec)); \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), \
(long)(expect_nsec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), \
(long)(got.tv_nsec)); \
++result; \
} \
}
#define TEST_TO_TIMEVAL(ts_sec, ts_nsec, expect_sec, expect_usec) { \
#define TEST_TO_TIMEVAL(ts_sec, ts_nsec, expect_sec, expect_usec) \
{ \
struct timespec ts = { .tv_sec = ts_sec, .tv_nsec = ts_nsec }; \
struct timeval got = timespec_to_timeval(ts); \
if(got.tv_sec != expect_sec || got.tv_usec != expect_usec) \
{ \
printf("%s:%d: timespec_to_timeval({%ld, %ld}) returned wrong values\n", __FILE__, __LINE__, \
(long)(ts_sec), (long)(ts_nsec)); \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_usec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_usec)); \
if (got.tv_sec != expect_sec || got.tv_usec != expect_usec) { \
printf("%s:%d: timespec_to_timeval({%ld, %ld}) returned wrong values\n", \
__FILE__, __LINE__, (long)(ts_sec), (long)(ts_nsec)); \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), \
(long)(expect_usec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), \
(long)(got.tv_usec)); \
++result; \
} \
}
#define TEST_FROM_MS(msecs, expect_sec, expect_nsec) { \
#define TEST_FROM_MS(msecs, expect_sec, expect_nsec) \
{ \
struct timespec got = timespec_from_ms(msecs); \
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \
{ \
printf("%s:%d: timespec_from_ms(%ld) returned wrong values\n", __FILE__, __LINE__, (long)(msecs)); \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \
if (got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) { \
printf("%s:%d: timespec_from_ms(%ld) returned wrong values\n", __FILE__, \
__LINE__, (long)(msecs)); \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), \
(long)(expect_nsec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), \
(long)(got.tv_nsec)); \
++result; \
} \
}
#define TEST_TO_MS(ts_sec, ts_nsec, expect) { \
#define TEST_TO_MS(ts_sec, ts_nsec, expect) \
{ \
struct timespec ts = { .tv_sec = ts_sec, .tv_nsec = ts_nsec }; \
long got = timespec_to_ms(ts); \
if (got != expect) { \
printf("%s:%d: timespec_to_ms({%ld, %ld}) returned wrong value\n", __FILE__, __LINE__, \
(long)(ts_sec), (long)(ts_nsec)); \
printf("%s:%d: timespec_to_ms({%ld, %ld}) returned wrong value\n", \
__FILE__, __LINE__, (long)(ts_sec), (long)(ts_nsec)); \
printf(" Expected: %ld\n", (long)(expect)); \
printf(" Got: %ld\n", got); \
++result; \
@ -974,11 +979,9 @@ int main()
TEST_NORMALISE(-1, 500000000, -1, 500000000);
TEST_NORMALISE(-1, 499999999, -1, 499999999);
if(result > 0)
{
if (result > 0) {
printf("%d tests failed\n", result);
}
else{
} else {
printf("All tests passed\n");
}