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 # SPDX-License-Identifier: GPL-2.0
IndentWidth: 8 #
UseTab: Always # clang-format configuration file. Intended for clang-format >= 11.
BreakBeforeBraces: Linux #
# 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 AllowShortIfStatementsOnASingleLine: false
AlignConsecutiveMacros: AllowShortLoopsOnASingleLine: false
Enabled: true AlwaysBreakAfterDefinitionReturnType: None
AcrossEmptyLines: true AlwaysBreakAfterReturnType: None
AcrossComments: false AlwaysBreakBeforeMultilineStrings: false
IndentCaseLabels: 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 // w - width of the image to render
// h - height of the image to render // h - height of the image to render
// stride - number of bytes per scaleline in the destination buffer // stride - number of bytes per scaleline in the destination buffer
void nsvgRasterize(NSVGrasterizer* r, void nsvgRasterize(NSVGrasterizer *r, NSVGimage *image, float tx, float ty, float scale,
NSVGimage* image, float tx, float ty, float scale,
unsigned char *dst, int w, int h, int stride); unsigned char *dst, int w, int h, int stride);
// Deletes rasterizer context. // Deletes rasterizer context.
void nsvgDeleteRasterizer(NSVGrasterizer *); void nsvgDeleteRasterizer(NSVGrasterizer *);
void nsvgRasterizeText(NSVGrasterizer *r, const NSVGimage *font, float tx, float ty, float scale,
void nsvgRasterizeText(NSVGrasterizer* r, unsigned char *dst, int w, int h, int stride, const char *text);
const NSVGimage* font, float tx, float ty, float scale,
unsigned char* dst, int w, int h, int stride,
const char* text);
#ifndef NANOSVGRAST_CPLUSPLUS #ifndef NANOSVGRAST_CPLUSPLUS
#ifdef __cplusplus #ifdef __cplusplus
@ -124,8 +119,7 @@ typedef struct NSVGcachedPaint {
unsigned int colors[256]; unsigned int colors[256];
} NSVGcachedPaint; } NSVGcachedPaint;
struct NSVGrasterizer struct NSVGrasterizer {
{
float px, py; float px, py;
float tessTol; float tessTol;
@ -157,7 +151,8 @@ struct NSVGrasterizer
NSVGrasterizer *nsvgCreateRasterizer() NSVGrasterizer *nsvgCreateRasterizer()
{ {
NSVGrasterizer *r = (NSVGrasterizer *)malloc(sizeof(NSVGrasterizer)); NSVGrasterizer *r = (NSVGrasterizer *)malloc(sizeof(NSVGrasterizer));
if (r == NULL) goto error; if (r == NULL)
goto error;
memset(r, 0, sizeof(NSVGrasterizer)); memset(r, 0, sizeof(NSVGrasterizer));
r->tessTol = 0.25f; r->tessTol = 0.25f;
@ -174,7 +169,8 @@ void nsvgDeleteRasterizer(NSVGrasterizer* r)
{ {
NSVGmemPage *p; NSVGmemPage *p;
if (r == NULL) return; if (r == NULL)
return;
p = r->pages; p = r->pages;
while (p != NULL) { while (p != NULL) {
@ -183,10 +179,14 @@ void nsvgDeleteRasterizer(NSVGrasterizer* r)
p = next; p = next;
} }
if (r->edges) free(r->edges); if (r->edges)
if (r->points) free(r->points); free(r->edges);
if (r->points2) free(r->points2); if (r->points)
if (r->scanline) free(r->scanline); free(r->points);
if (r->points2)
free(r->points2);
if (r->scanline)
free(r->scanline);
free(r); free(r);
} }
@ -202,7 +202,8 @@ static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur)
// Alloc new page // Alloc new page
newp = (NSVGmemPage *)malloc(sizeof(NSVGmemPage)); newp = (NSVGmemPage *)malloc(sizeof(NSVGmemPage));
if (newp == NULL) return NULL; if (newp == NULL)
return NULL;
memset(newp, 0, sizeof(NSVGmemPage)); memset(newp, 0, sizeof(NSVGmemPage));
// Add to linked list // Add to linked list
@ -227,7 +228,8 @@ static void nsvg__resetPool(NSVGrasterizer* r)
static unsigned char *nsvg__alloc(NSVGrasterizer *r, int size) static unsigned char *nsvg__alloc(NSVGrasterizer *r, int size)
{ {
unsigned char *buf; 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) { if (r->curpage == NULL || r->curpage->size + size > NSVG__MEMPAGE_SIZE) {
r->curpage = nsvg__nextPage(r, r->curpage); 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) { if (r->npoints + 1 > r->cpoints) {
r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64;
r->points = (NSVGpoint *)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); 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]; pt = &r->points[r->npoints];
@ -273,7 +276,8 @@ static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt)
if (r->npoints + 1 > r->cpoints) { if (r->npoints + 1 > r->cpoints) {
r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64;
r->points = (NSVGpoint *)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); 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->points[r->npoints] = pt;
r->npoints++; r->npoints++;
@ -284,7 +288,8 @@ static void nsvg__duplicatePoints(NSVGrasterizer* r)
if (r->npoints > r->cpoints2) { if (r->npoints > r->cpoints2) {
r->cpoints2 = r->npoints; r->cpoints2 = r->npoints;
r->points2 = (NSVGpoint *)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2); 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); 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) { if (r->nedges + 1 > r->cedges) {
r->cedges = r->cedges > 0 ? r->cedges * 2 : 64; r->cedges = r->cedges > 0 ? r->cedges * 2 : 64;
r->edges = (NSVGedge *)realloc(r->edges, sizeof(NSVGedge) * r->cedges); 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]; e = &r->edges[r->nedges];
@ -334,17 +340,19 @@ static float nsvg__normalize(float *x, float* y)
return d; 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, static void nsvg__flattenCubicBez(NSVGrasterizer *r, float x1, float y1, float x2, float y2,
float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, int level, int type)
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 x12, y12, x23, y23, x34, y34, x123, y123, x234, y234, x1234, y1234;
float dx, dy, d2, d3; float dx, dy, d2, d3;
if (level > 10) return; if (level > 10)
return;
x12 = (x1 + x2) * 0.5f; x12 = (x1 + x2) * 0.5f;
y12 = (y1 + y2) * 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); nsvg__addPathPoint(r, path->pts[0] * scale, path->pts[1] * scale, 0);
for (i = 0; i < path->npts - 1; i += 3) { for (i = 0; i < path->npts - 1; i += 3) {
float *p = &path->pts[i * 2]; 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 // Close path
nsvg__addPathPoint(r, path->pts[0] * scale, path->pts[1] * scale, 0); nsvg__addPathPoint(r, path->pts[0] * scale, path->pts[1] * scale, 0);
// Build edges // Build edges
for (i = 0, j = r->npoints - 1; i < r->npoints; j = i++) 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 enum NSVGpointFlags { NSVG_PT_CORNER = 0x01, NSVG_PT_BEVEL = 0x02, NSVG_PT_LEFT = 0x04 };
{
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 w = lineWidth * 0.5f;
float dx = p1->x - p0->x; 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 dlx = dy, dly = -dx;
float lx = px - dlx * w, ly = py - dly * w; float lx = px - dlx * w, ly = py - dly * w;
float rx = px + dlx * w, ry = py + dly * w; float rx = px + dlx * w, ry = py + dly * w;
left->x = lx; left->y = ly; left->x = lx;
right->x = rx; right->y = ry; 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 w = lineWidth * 0.5f;
float px = p->x, py = p->y; 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, left->x, left->y, lx, ly);
nsvg__addEdge(r, rx, ry, right->x, right->y); nsvg__addEdge(r, rx, ry, right->x, right->y);
} }
left->x = lx; left->y = ly; left->x = lx;
right->x = rx; right->y = ry; 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 w = lineWidth * 0.5f;
float px = p->x - dx * w, py = p->y - dy * w; 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, left->x, left->y, lx, ly);
nsvg__addEdge(r, rx, ry, right->x, right->y); nsvg__addEdge(r, rx, ry, right->x, right->y);
} }
left->x = lx; left->y = ly; left->x = lx;
right->x = rx; right->y = ry; left->y = ly;
right->x = rx;
right->y = ry;
} }
#ifndef NSVG_PI #ifndef NSVG_PI
#define NSVG_PI (3.14159265358979323846264338327f) #define NSVG_PI (3.14159265358979323846264338327f)
#endif #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; int i;
float w = lineWidth * 0.5f; float w = lineWidth * 0.5f;
@ -477,9 +493,11 @@ static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right,
prevy = y; prevy = y;
if (i == 0) { if (i == 0) {
lx = x; ly = y; lx = x;
ly = y;
} else if (i == ncap - 1) { } 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); nsvg__addEdge(r, rx, ry, right->x, right->y);
} }
left->x = lx; left->y = ly; left->x = lx;
right->x = rx; right->y = ry; 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 w = lineWidth * 0.5f;
float dlx0 = p0->dy, dly0 = -p0->dx; 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, right->x, right->y, rx0, ry0);
nsvg__addEdge(r, rx0, ry0, rx1, ry1); nsvg__addEdge(r, rx0, ry0, rx1, ry1);
left->x = lx1; left->y = ly1; left->x = lx1;
right->x = rx1; right->y = ry1; 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 w = lineWidth * 0.5f;
float dlx0 = p0->dy, dly0 = -p0->dx; 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); nsvg__addEdge(r, right->x, right->y, rx1, ry1);
} }
left->x = lx1; left->y = ly1; left->x = lx1;
right->x = rx1; right->y = ry1; 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; int i, n;
float w = lineWidth * 0.5f; float w = lineWidth * 0.5f;
@ -559,12 +586,16 @@ static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right
float da = a1 - a0; float da = a1 - a0;
float lx, ly, rx, ry; float lx, ly, rx, ry;
if (da < NSVG_PI) da += NSVG_PI*2; if (da < NSVG_PI)
if (da > NSVG_PI) da -= NSVG_PI*2; da += NSVG_PI * 2;
if (da > NSVG_PI)
da -= NSVG_PI * 2;
n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap); n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap);
if (n < 2) n = 2; if (n < 2)
if (n > ncap) n = ncap; n = 2;
if (n > ncap)
n = ncap;
lx = left->x; lx = left->x;
ly = left->y; 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, lx1, ly1, lx, ly);
nsvg__addEdge(r, rx, ry, rx1, ry1); nsvg__addEdge(r, rx, ry, rx1, ry1);
lx = lx1; ly = ly1; lx = lx1;
rx = rx1; ry = ry1; ly = ly1;
rx = rx1;
ry = ry1;
} }
left->x = lx; left->y = ly; left->x = lx;
right->x = rx; right->y = ry; 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 w = lineWidth * 0.5f;
float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w); 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, lx, ly, left->x, left->y);
nsvg__addEdge(r, right->x, right->y, rx, ry); nsvg__addEdge(r, right->x, right->y, rx, ry);
left->x = lx; left->y = ly; left->x = lx;
right->x = rx; right->y = ry; left->y = ly;
right->x = rx;
right->y = ry;
} }
static int nsvg__curveDivs(float r, float arc, float tol) static int nsvg__curveDivs(float r, float arc, float tol)
{ {
float da = acosf(r / (r + tol)) * 2.0f; float da = acosf(r / (r + tol)) * 2.0f;
int divs = (int)ceilf(arc / da); int divs = (int)ceilf(arc / da);
if (divs < 2) divs = 2; if (divs < 2)
divs = 2;
return divs; 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. int ncap = nsvg__curveDivs(lineWidth * 0.5f, NSVG_PI,
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}; 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; NSVGpoint *p0, *p1;
int j, s, e; 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. // Check to see if the corner needs to be beveled.
if (p1->flags & NSVG_PT_CORNER) { 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; 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); nsvg__addPathPoint(r, path->pts[0] * scale, path->pts[1] * scale, NSVG_PT_CORNER);
for (i = 0; i < path->npts - 1; i += 3) { for (i = 0; i < path->npts - 1; i += 3) {
float *p = &path->pts[i * 2]; 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) if (r->npoints < 2)
continue; continue;
@ -817,7 +862,8 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
// Stroke // Stroke
if (r->npoints > 1 && dashState) { if (r->npoints > 1 && dashState) {
nsvg__prepareStroke(r, miterLimit, lineJoin); 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 // Advance dash pattern
dashState = !dashState; dashState = !dashState;
@ -839,10 +885,12 @@ static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float
} }
// Stroke any leftover path // Stroke any leftover path
if (r->npoints > 1 && dashState) 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 { } else {
nsvg__prepareStroke(r, miterLimit, lineJoin); 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 *a = (const NSVGedge *)p;
const NSVGedge *b = (const NSVGedge *)q; const NSVGedge *b = (const NSVGedge *)q;
if (a->y0 < b->y0) return -1; if (a->y0 < b->y0)
if (a->y0 > b->y0) return 1; return -1;
if (a->y0 > b->y0)
return 1;
return 0; return 0;
} }
static NSVGactiveEdge *nsvg__addActive(NSVGrasterizer *r, NSVGedge *e, float startPoint) static NSVGactiveEdge *nsvg__addActive(NSVGrasterizer *r, NSVGedge *e, float startPoint)
{ {
NSVGactiveEdge *z; NSVGactiveEdge *z;
@ -869,7 +918,8 @@ static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float sta
} else { } else {
// Alloc new edge. // Alloc new edge.
z = (NSVGactiveEdge *)nsvg__alloc(r, sizeof(NSVGactiveEdge)); 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); 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; 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 i = x0 >> NSVG__FIXSHIFT;
int j = x1 >> NSVG__FIXSHIFT; int j = x1 >> NSVG__FIXSHIFT;
if (i < *xmin) *xmin = i; if (i < *xmin)
if (j > *xmax) *xmax = j; *xmin = i;
if (j > *xmax)
*xmax = j;
if (i < len && j >= 0) { if (i < len && j >= 0) {
if (i == j) { if (i == j) {
// x0,x1 are the same pixel, so compute combined coverage // 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 { } else {
if (i >= 0) // add antialiasing for x0 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 else
i = -1; // clip i = -1; // clip
if (j < len) // add antialiasing for x1 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 else
j = len; // clip 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 // 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 // wouldn't happen, but it could happen if the truetype glyph bounding boxes
// are wrong, or if the user supplies a too-small bitmap // 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 // non-zero winding fill
int x0 = 0, w = 0; int x0 = 0, w = 0;
@ -933,13 +993,17 @@ static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEd
// Non-zero // Non-zero
while (e != NULL) { while (e != NULL) {
if (w == 0) { if (w == 0) {
// if we're currently at zero, we need to record the edge start point // if we're currently at zero, we need to record the edge start
x0 = e->x; w += e->dir; // point
x0 = e->x;
w += e->dir;
} else { } 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 we went to zero, we need to draw
if (w == 0) if (w == 0)
nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin,
xmax);
} }
e = e->next; e = e->next;
} }
@ -947,10 +1011,13 @@ static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEd
// Even-odd // Even-odd
while (e != NULL) { while (e != NULL) {
if (w == 0) { if (w == 0) {
// if we're currently at zero, we need to record the edge start point // if we're currently at zero, we need to record the edge start
x0 = e->x; w = 1; // point
x0 = e->x;
w = 1;
} else { } else {
int x1 = e->x; w = 0; int x1 = e->x;
w = 0;
nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax);
} }
e = e->next; 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) 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; NSVGactiveEdge *active = NULL;
int y, s; int y, s;
@ -1164,14 +1235,17 @@ static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, fl
} }
step = &(*step)->next; 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) { while (e < r->nedges && r->edges[e].y0 <= scany) {
if (r->edges[e].y1 > scany) { if (r->edges[e].y1 > scany) {
NSVGactiveEdge *z = nsvg__addActive(r, &r->edges[e], scany); NSVGactiveEdge *z = nsvg__addActive(r, &r->edges[e], scany);
if (z == NULL) break; if (z == NULL)
break;
// find insertion point // find insertion point
if (active == NULL) { if (active == NULL) {
active = z; 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 // now process all active edges in non-zero fashion
if (active != NULL) 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 // Blit
if (xmin < 0) xmin = 0; if (xmin < 0)
if (xmax > r->width-1) xmax = r->width-1; xmin = 0;
if (xmax > r->width - 1)
xmax = r->width - 1;
if (xmin <= xmax) { 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) 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) static void nsvg__initPaint(NSVGcachedPaint *cache, NSVGpaint *paint, float opacity)
{ {
int i, j; int i, j;
@ -1286,7 +1362,8 @@ static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opac
if (grad->nstops == 0) { if (grad->nstops == 0) {
for (i = 0; i < 256; i++) for (i = 0; i < 256; i++)
cache->colors[i] = 0; cache->colors[i] = 0;
} if (grad->nstops == 1) { }
if (grad->nstops == 1) {
for (i = 0; i < 256; i++) for (i = 0; i < 256; i++)
cache->colors[i] = nsvg__applyOpacity(grad->stops[i].color, opacity); cache->colors[i] = nsvg__applyOpacity(grad->stops[i].color, opacity);
} else { } else {
@ -1311,7 +1388,8 @@ static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opac
ia = (int)(ua * 255.0f); ia = (int)(ua * 255.0f);
ib = (int)(ub * 255.0f); ib = (int)(ub * 255.0f);
count = ib - ia; count = ib - ia;
if (count <= 0) continue; if (count <= 0)
continue;
u = 0; u = 0;
du = 1.0f / (float)count; du = 1.0f / (float)count;
for (j = 0; j < count; j++) { 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++) for (i = ib; i < 256; i++)
cache->colors[i] = cb; cache->colors[i] = cb;
} }
} }
/* /*
@ -1350,17 +1427,21 @@ static void dumpEdges(NSVGrasterizer* r, const char* name)
ymax = nsvg__maxf(ymax, e->y1); 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++) { for (i = 0; i < r->nedges; i++) {
e = &r->edges[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++) { for (i = 0; i < r->npoints; i++) {
if (i+1 < r->npoints) 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 ,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\"
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"); 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>"); fprintf(fp, "</svg>");
@ -1368,10 +1449,8 @@ static void dumpEdges(NSVGrasterizer* r, const char* name)
} }
*/ */
void nsvgRasterizeText(NSVGrasterizer* r, void nsvgRasterizeText(NSVGrasterizer *r, const NSVGimage *font, float tx, float ty, float scale,
const NSVGimage* font, float tx, float ty, float scale, unsigned char *dst, int w, int h, int stride, const char *text)
unsigned char* dst, int w, int h, int stride,
const char* text)
{ {
NSVGshape *shape = NULL; NSVGshape *shape = NULL;
NSVGedge *e = NULL; NSVGedge *e = NULL;
@ -1442,7 +1521,8 @@ void nsvgRasterizeText(NSVGrasterizer* r,
// Rasterize edges // Rasterize edges
qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); 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__initPaint(&cache, &shape->fill, shape->opacity);
nsvg__rasterizeSortedEdges(r, tx, ty, scale, &cache, shape->fillRule); nsvg__rasterizeSortedEdges(r, tx, ty, scale, &cache, shape->fillRule);
@ -1468,7 +1548,8 @@ void nsvgRasterizeText(NSVGrasterizer* r,
// Rasterize edges // Rasterize edges
qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); 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__initPaint(&cache, &shape->stroke, shape->opacity);
nsvg__rasterizeSortedEdges(r, tx, ty, scale, &cache, NSVG_FILLRULE_NONZERO); nsvg__rasterizeSortedEdges(r, tx, ty, scale, &cache, NSVG_FILLRULE_NONZERO);
@ -1486,8 +1567,7 @@ void nsvgRasterizeText(NSVGrasterizer* r,
r->stride = 0; r->stride = 0;
} }
void nsvgRasterize(NSVGrasterizer* r, void nsvgRasterize(NSVGrasterizer *r, NSVGimage *image, float tx, float ty, float scale,
NSVGimage* image, float tx, float ty, float scale,
unsigned char *dst, int w, int h, int stride) unsigned char *dst, int w, int h, int stride)
{ {
NSVGshape *shape = NULL; NSVGshape *shape = NULL;
@ -1503,7 +1583,8 @@ void nsvgRasterize(NSVGrasterizer* r,
if (w > r->cscanline) { if (w > r->cscanline) {
r->cscanline = w; r->cscanline = w;
r->scanline = (unsigned char *)realloc(r->scanline, 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++) for (i = 0; i < h; i++)
@ -1532,7 +1613,8 @@ void nsvgRasterize(NSVGrasterizer* r,
// Rasterize edges // Rasterize edges
qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); 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__initPaint(&cache, &shape->fill, shape->opacity);
nsvg__rasterizeSortedEdges(r, tx, ty, scale, &cache, shape->fillRule); nsvg__rasterizeSortedEdges(r, tx, ty, scale, &cache, shape->fillRule);
@ -1558,7 +1640,8 @@ void nsvgRasterize(NSVGrasterizer* r,
// Rasterize edges // Rasterize edges
qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); 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__initPaint(&cache, &shape->stroke, shape->opacity);
nsvg__rasterizeSortedEdges(r, tx, ty, scale, &cache, NSVG_FILLRULE_NONZERO); 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 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 { struct col {
union { union {
unsigned int rgba; 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') cc = meson.get_compiler('c')
deps = [ deps = [
dependency('tfblib'),
cc.find_library('m', required : false) 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 rad = MM_TO_PX(dpi, 1);
int dist = rad * 3.5; int dist = rad * 3.5;
int amplitude = rad * 1; int amplitude = rad * 1;
int left = ((float)w / 2) - (dist * (n_circles - 1) / 2.0); int left = ((float)w / 2) - (dist * (n_circles - 1) / 2.0);
for (unsigned int i = 0; i < n_circles; i++) { for (unsigned int i = 0; i < n_circles; i++) {
int x = left + (i * dist); int x = left + (i * dist);
double offset = sin(f / 60.0 * PI + i); double offset = sin(f / 60.0 * PI + i);
int y = y_off + offset * amplitude; int y = y_off + offset * amplitude;
tfb_fill_rect(x - rad - 3, y_off - amplitude - rad - 3, tfb_fill_rect(x - rad - 3, y_off - amplitude - rad - 3, rad * 2 + 6,
rad * 2 + 6, amplitude * 2 + rad * 2 + 6, amplitude * 2 + rad * 2 + 6, tfb_black);
tfb_black);
tfb_fill_circle(x, y, rad, t_col); 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', 'nanosvg.c',
'timespec.c', 'timespec.c',
'pbsplash.c', 'pbsplash.c',
'fb.c',
'drawing.c',
] ]
executable('pbsplash', src, executable('pbsplash', src,

View file

@ -1,23 +1,23 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <linux/kd.h>
#include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.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 <time.h>
#include <unistd.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 <math.h>
#include <string.h>
#define NANOSVG_ALL_COLOR_KEYWORDS // Include full list of color keywords. #define NANOSVG_ALL_COLOR_KEYWORDS // Include full list of color keywords.
#include "nanosvg.h" #include "nanosvg.h"
#include "nanosvgrast.h" #include "nanosvgrast.h"
#include "timespec.h"
#include "pbsplash.h" #include "pbsplash.h"
#include "timespec.h"
#define MSG_MAX_LEN 4096 #define MSG_MAX_LEN 4096
#define DEFAULT_FONT_PATH "/usr/share/pbsplash/OpenSans-Regular.svg" #define DEFAULT_FONT_PATH "/usr/share/pbsplash/OpenSans-Regular.svg"
@ -28,12 +28,10 @@
#define PT_TO_MM 0.38f #define PT_TO_MM 0.38f
#define TTY_PATH_LEN 11 #define TTY_PATH_LEN 11
#define DEBUGRENDER 0
volatile sig_atomic_t terminate = 0; volatile sig_atomic_t terminate = 0;
bool debug = false; 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; static int screenWidth, screenHeight;
@ -74,55 +72,6 @@ static void term(int signum)
terminate = 1; 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) 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; 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); unsigned char *img = zalloc(w * h * 4);
nsvgRasterize(rast, image, 0, 0, sz, img, w, h, w * 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); free(img);
nsvgDeleteRasterizer(rast); nsvgDeleteRasterizer(rast);
} }
static void draw_text(const NSVGimage *font, const char *text, int x, int y, int width, static void draw_text(const NSVGimage *font, const char *text, int x, int y, int width, int height,
int height, float scale, unsigned int tfb_col) float scale, unsigned int tfb_col)
{ {
LOG("text '%s': fontsz=%f, x=%d, y=%d, dimensions: %d x %d\n", text, LOG("text '%s': fontsz=%f, x=%d, y=%d, dimensions: %d x %d\n", text, scale, x, y, width,
scale, x, y, width, height); height);
NSVGshape **shapes = nsvgGetTextShapes(font, text, strlen(text)); NSVGshape **shapes = nsvgGetTextShapes(font, text, strlen(text));
unsigned char *img = zalloc(width * height * 4); unsigned char *img = zalloc(width * height * 4);
NSVGrasterizer *rast = nsvgCreateRasterizer(); NSVGrasterizer *rast = nsvgCreateRasterizer();
nsvgRasterizeText(rast, font, 0, 0, scale, img, width, height, nsvgRasterizeText(rast, font, 0, 0, scale, img, width, height, width * 4, text);
width * 4, text);
blit_buf(img, x, y, width, height, true, false); blit_buf(img, x, y, width, height, true);
free(img); free(img);
free(shapes); 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_max_mm = (screenWidth * 0.75f) / dpi_info->pixels_per_milli;
dpi_info->logo_size_px = dpi_info->logo_size_px =
(float)(screenWidth < screenHeight ? screenWidth : (float)(screenWidth < screenHeight ? screenWidth : screenHeight) * 0.75f;
screenHeight) *
0.75f;
if (w_mm > 0 && h_mm > 0) { if (w_mm > 0 && h_mm > 0) {
if (w_mm < h_mm) { if (w_mm < h_mm) {
if (w_mm > dpi_info->logo_size_max_mm * 1.2f) 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 { } else {
if (h_mm > dpi_info->logo_size_max_mm * 1.2f) 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, printf("%dx%d @ %dx%dmm, dpi=%ld, logo_size_px=%f\n", screenWidth, screenHeight, w_mm, h_mm,
screenHeight, w_mm, h_mm, dpi_info->dpi, dpi_info->logo_size_px); dpi_info->dpi, dpi_info->logo_size_px);
} }
struct msg_info { struct msg_info {
@ -301,12 +249,13 @@ struct msg_info {
float fontsz; 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) / msg_info->fontsz = (font_size_pt * PT_TO_MM) / (font->fontAscent - font->fontDescent) *
(font->fontAscent - font->fontDescent) *
dpi_info->pixels_per_milli; 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; msg_info->x = (screenWidth - msg_info->width) / 2;
// Y coordinate is set later // 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; font_failed = true;
fprintf(stderr, "failed to load SVG font, can't render messages\n"); fprintf(stderr, "failed to load SVG font, can't render messages\n");
fprintf(stderr, " font_path: %s\n", msgs->font_path); 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; return;
} }
if (msgs->bottom_msg) { if (msgs->bottom_msg) {
if (!msgs->bottom_msg->message) { if (!msgs->bottom_msg->message) {
load_message(msgs->bottom_msg, dpi_info, msgs->font_size_b_pt, msgs->font); 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); 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) { if (!msgs->msg->message) {
load_message(msgs->msg, dpi_info, msgs->font_size_pt, msgs->font); load_message(msgs->msg, dpi_info, msgs->font_size_pt, msgs->font);
if (msgs->bottom_msg) 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 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); 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) if (image_info->image->width < image_info->image->height * 1.1)
logo_size_px = MM_TO_PX(dpi_info->dpi, 25); logo_size_px = MM_TO_PX(dpi_info->dpi, 25);
float sz = float sz = (float)logo_size_px / (image_info->image->width > image_info->image->height ?
(float)logo_size_px / image_info->image->height :
(image_info->image->width > image_info->image->height ? image_info->image->height : image_info->image->width); image_info->image->width);
image_info->width = image_info->image->width * sz + 0.5; image_info->width = image_info->image->width * sz + 0.5;
image_info->height = image_info->image->height * 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)) { if (image_info->width > (dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli)) {
float scalefactor = 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); // printf("Got scale factor: %f\n", scalefactor);
image_info->width = dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli; image_info->width = dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli;
image_info->height *= scalefactor; image_info->height *= scalefactor;
@ -476,24 +434,21 @@ int main(int argc, char **argv)
case 'o': case 'o':
msgs.font_size_b_pt = strtof(optarg, &end); msgs.font_size_b_pt = strtof(optarg, &end);
if (end == optarg) { if (end == optarg) {
fprintf(stderr, "Invalid font size: %s\n", fprintf(stderr, "Invalid font size: %s\n", optarg);
optarg);
return usage(); return usage();
} }
break; break;
case 'p': case 'p':
msgs.font_size_pt = strtof(optarg, &end); msgs.font_size_pt = strtof(optarg, &end);
if (end == optarg) { if (end == optarg) {
fprintf(stderr, "Invalid font size: %s\n", fprintf(stderr, "Invalid font size: %s\n", optarg);
optarg);
return usage(); return usage();
} }
break; break;
case 'q': case 'q':
dpi_info.logo_size_max_mm = strtof(optarg, &end); dpi_info.logo_size_max_mm = strtof(optarg, &end);
if (end == optarg) { if (end == optarg) {
fprintf(stderr, "Invalid max logo size: %s\n", fprintf(stderr, "Invalid max logo size: %s\n", optarg);
optarg);
return usage(); return usage();
} }
break; break;
@ -525,10 +480,9 @@ int main(int argc, char **argv)
LOG("active tty: '%s'\n", active_tty); LOG("active tty: '%s'\n", active_tty);
if ((rc = tfb_acquire_fb(/*TFB_FL_NO_TTY_KD_GRAPHICS */ 0, "/dev/fb0", if ((rc = tfb_acquire_fb(/*TFB_FL_NO_TTY_KD_GRAPHICS */ 0, "/dev/fb0", active_tty)) !=
active_tty)) != TFB_SUCCESS) { TFB_SUCCESS) {
fprintf(stderr, "tfb_acquire_fb() failed with error code: %d\n", fprintf(stderr, "tfb_acquire_fb() failed with error code: %d\n", rc);
rc);
rc = 1; rc = 1;
return rc; 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); 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, tfb_clear_screen(tfb_make_color(bg.r, bg.g, bg.b));
background_color.b));
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);
@ -607,7 +560,8 @@ out:
// Before we exit print the logo so it will persist // Before we exit print the logo so it will persist
if (image_info.image) { if (image_info.image) {
ioctl(tty, KDSETMODE, KD_TEXT); 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 // Draw the messages again so they will persist

View file

@ -56,11 +56,11 @@
* is normalised according to the rules in timespec_normalise(). * is normalised according to the rules in timespec_normalise().
*/ */
#include <inttypes.h>
#include <limits.h> #include <limits.h>
#include <stdbool.h> #include <stdbool.h>
#include <sys/time.h> #include <sys/time.h>
#include <time.h> #include <time.h>
#include <inttypes.h>
#include "timespec.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 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; return ts1;
} }
/* If inputs are negative, flip and record sign /* 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; neg1 = true;
ts1.tv_sec = -ts1.tv_sec; ts1.tv_sec = -ts1.tv_sec;
ts1.tv_nsec = -ts1.tv_nsec; 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; neg2 = true;
ts2.tv_sec = -ts2.tv_sec; ts2.tv_sec = -ts2.tv_sec;
ts2.tv_nsec = -ts2.tv_nsec; 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 /* 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++; i++;
ts2.tv_nsec <<= 1; ts2.tv_nsec <<= 1;
ts2.tv_sec <<= 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_nsec -= NSEC_PER_SEC;
ts2.tv_sec++; ts2.tv_sec++;
} }
@ -154,21 +149,17 @@ struct timespec timespec_mod(struct timespec ts1, struct timespec ts2)
/* Division by repeated subtraction /* Division by repeated subtraction
*/ */
while (i >= 0) while (i >= 0) {
{ if (timespec_ge(ts1, ts2)) {
if (timespec_ge(ts1, ts2))
{
ts1 = timespec_sub(ts1, ts2); ts1 = timespec_sub(ts1, ts2);
} }
if (i == 0) if (i == 0) {
{
break; break;
} }
i--; i--;
if (ts2.tv_sec & 1) if (ts2.tv_sec & 1) {
{
ts2.tv_nsec += NSEC_PER_SEC; ts2.tv_nsec += NSEC_PER_SEC;
} }
ts2.tv_nsec >>= 1; 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 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); ts1 = timespec_sub(ts1, ts2);
} }
/* Restore sign /* Restore sign
*/ */
if (neg1) if (neg1) {
{
ts1.tv_sec = -ts1.tv_sec; ts1.tv_sec = -ts1.tv_sec;
ts1.tv_nsec = -ts1.tv_nsec; 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) /** \fn struct timespec timespec_min(struct timespec ts1, struct timespec ts2)
* \brief Return the lesser one of the two given timespec values. * \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)) { if (timespec_le(ts1, ts2)) {
return ts1; return ts1;
} else { } 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) /** \fn struct timespec timespec_max(struct timespec ts1, struct timespec ts2)
* \brief Return the greater one of the two given timespec values. * \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)) { if (timespec_ge(ts1, ts2)) {
return ts1; return ts1;
} else { } 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) /** \fn struct timespec timespec_clamp(struct timespec ts, struct timespec min, struct timespec max)
* \brief Clamp the value of TS between MIN and 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)) { if (timespec_gt(ts, max)) {
return max; return max;
} }
@ -236,16 +228,12 @@ int timespec_cmp(struct timespec ts1, struct timespec ts2)
ts1 = timespec_normalise(ts1); ts1 = timespec_normalise(ts1);
ts2 = timespec_normalise(ts2); 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; return 0;
} } else if ((ts1.tv_sec > ts2.tv_sec) ||
else if((ts1.tv_sec > ts2.tv_sec) (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec > ts2.tv_nsec)) {
|| (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec > ts2.tv_nsec))
{
return 1; return 1;
} } else {
else {
return -1; return -1;
} }
} }
@ -280,7 +268,8 @@ bool timespec_ge(struct timespec ts1, struct timespec ts2)
ts1 = timespec_normalise(ts1); ts1 = timespec_normalise(ts1);
ts2 = timespec_normalise(ts2); 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) /** \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); ts1 = timespec_normalise(ts1);
ts2 = timespec_normalise(ts2); 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) /** \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 timespec_from_timeval(struct timeval tv)
{ {
struct timespec ts = { struct timespec ts = { .tv_sec = tv.tv_sec, .tv_nsec = tv.tv_usec * 1000 };
.tv_sec = tv.tv_sec,
.tv_nsec = tv.tv_usec * 1000
};
return timespec_normalise(ts); return timespec_normalise(ts);
} }
@ -389,20 +376,17 @@ long timespec_to_ms(struct timespec ts)
*/ */
struct timespec timespec_normalise(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_sec);
ts.tv_nsec -= NSEC_PER_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_sec);
ts.tv_nsec += NSEC_PER_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. /* Negative nanoseconds isn't valid according to POSIX.
* Decrement tv_sec and roll tv_nsec over. * Decrement tv_sec and roll tv_nsec over.
*/ */
@ -424,129 +408,150 @@ struct timespec timespec_now()
#ifdef TEST #ifdef TEST
#include <stdio.h> #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 in = { .tv_sec = ts_sec, .tv_nsec = ts_nsec }; \
struct timespec got = timespec_normalise(in); \ struct timespec got = timespec_normalise(in); \
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \ if (got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) { \
{ \ printf("%s:%d: timespec_normalise({%ld, %ld}) returned wrong values\n", \
printf("%s:%d: timespec_normalise({%ld, %ld}) returned wrong values\n", __FILE__, __LINE__, \ __FILE__, __LINE__, (long)(ts_sec), (long)(ts_nsec)); \
(long)(ts_sec), (long)(ts_nsec)); \ printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \ (long)(expect_nsec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \ printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), \
(long)(got.tv_nsec)); \
++result; \ ++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 ts1 = { .tv_sec = ts1_sec, .tv_nsec = ts1_nsec }; \
struct timespec ts2 = { .tv_sec = ts2_sec, .tv_nsec = ts2_nsec }; \ struct timespec ts2 = { .tv_sec = ts2_sec, .tv_nsec = ts2_nsec }; \
struct timespec got = func(ts1, ts2); \ 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", \ printf(#func "({%ld, %ld}, {%ld, %ld}) returned wrong values\n", \
(long)(ts1_sec), (long)(ts1_nsec), (long)(ts2_sec), (long)(ts2_nsec)); \ (long)(ts1_sec), (long)(ts1_nsec), (long)(ts2_sec), \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \ (long)(ts2_nsec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_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; \ ++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 ts1 = { .tv_sec = ts1_sec, .tv_nsec = ts1_nsec }; \
struct timespec ts2 = { .tv_sec = ts2_sec, .tv_nsec = ts2_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 ts3 = { .tv_sec = ts3_sec, .tv_nsec = ts3_nsec }; \
struct timespec got = func(ts1, ts2, ts3); \ struct timespec got = func(ts1, ts2, ts3); \
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \ if (got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) { \
{ \ printf(#func \
printf(#func "({%ld, %ld}, {%ld, %ld}, {%ld, %ld}) returned wrong values\n", \ "({%ld, %ld}, {%ld, %ld}, {%ld, %ld}) returned wrong values\n", \
(long)(ts1_sec), (long)(ts1_nsec), \ (long)(ts1_sec), (long)(ts1_nsec), (long)(ts2_sec), \
(long)(ts2_sec), (long)(ts2_nsec), \ (long)(ts2_nsec), (long)(ts3_sec), (long)(ts3_nsec)); \
(long)(ts3_sec), (long)(ts3_nsec)); \ printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \ (long)(expect_nsec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \ printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), \
(long)(got.tv_nsec)); \
++result; \ ++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 ts1 = { .tv_sec = ts1_sec, .tv_nsec = ts1_nsec }; \
struct timespec ts2 = { .tv_sec = ts2_sec, .tv_nsec = ts2_nsec }; \ struct timespec ts2 = { .tv_sec = ts2_sec, .tv_nsec = ts2_nsec }; \
int got = func(ts1, ts2); \ int got = func(ts1, ts2); \
if(got != expect) \ if (got != expect) { \
{ \ printf("%s:%d: " #func \
printf("%s:%d: " #func "({%ld, %ld}, {%ld, %ld}) returned %d, expected %s\n", __FILE__, __LINE__, \ "({%ld, %ld}, {%ld, %ld}) returned %d, expected %s\n", \
(long)(ts1_sec), (long)(ts1_nsec), (long)(ts2_sec), (long)(ts2_nsec), \ __FILE__, __LINE__, (long)(ts1_sec), (long)(ts1_nsec), \
got, #expect); \ (long)(ts2_sec), (long)(ts2_nsec), got, #expect); \
++result; \ ++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); \ struct timespec got = timespec_from_double(d_secs); \
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \ if (got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) { \
{ \ printf("%s:%d: timespec_from_double(%f) returned wrong values\n", \
printf("%s:%d: timespec_from_double(%f) returned wrong values\n", __FILE__, __LINE__, (double)(d_secs)); \ __FILE__, __LINE__, (double)(d_secs)); \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \ printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \ (long)(expect_nsec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), \
(long)(got.tv_nsec)); \
++result; \ ++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 }; \ struct timespec ts = { .tv_sec = ts_sec, .tv_nsec = ts_nsec }; \
double got = timespec_to_double(ts); \ double got = timespec_to_double(ts); \
if (got != expect) { \ if (got != expect) { \
printf("%s:%d: timespec_to_double({%ld, %ld}) returned wrong value\n", __FILE__, __LINE__, \ printf("%s:%d: timespec_to_double({%ld, %ld}) returned wrong value\n", \
(long)(ts_sec), (long)(ts_nsec)); \ __FILE__, __LINE__, (long)(ts_sec), (long)(ts_nsec)); \
printf(" Expected: %f\n", (double)(expect)); \ printf(" Expected: %f\n", (double)(expect)); \
printf(" Got: %f\n", got); \ printf(" Got: %f\n", got); \
++result; \ ++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 timeval tv = { .tv_sec = in_sec, .tv_usec = in_usec }; \
struct timespec got = timespec_from_timeval(tv); \ struct timespec got = timespec_from_timeval(tv); \
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \ if (got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) { \
{ \ printf("%s:%d: timespec_from_timeval({%ld, %ld}) returned wrong values\n", \
printf("%s:%d: timespec_from_timeval({%ld, %ld}) returned wrong values\n", __FILE__, __LINE__, \ __FILE__, __LINE__, (long)(in_sec), (long)(in_usec)); \
(long)(in_sec), (long)(in_usec)); \ printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \ (long)(expect_nsec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \ printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), \
(long)(got.tv_nsec)); \
++result; \ ++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 timespec ts = { .tv_sec = ts_sec, .tv_nsec = ts_nsec }; \
struct timeval got = timespec_to_timeval(ts); \ struct timeval got = timespec_to_timeval(ts); \
if(got.tv_sec != expect_sec || got.tv_usec != expect_usec) \ if (got.tv_sec != expect_sec || got.tv_usec != expect_usec) { \
{ \ printf("%s:%d: timespec_to_timeval({%ld, %ld}) returned wrong values\n", \
printf("%s:%d: timespec_to_timeval({%ld, %ld}) returned wrong values\n", __FILE__, __LINE__, \ __FILE__, __LINE__, (long)(ts_sec), (long)(ts_nsec)); \
(long)(ts_sec), (long)(ts_nsec)); \ printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_usec)); \ (long)(expect_usec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_usec)); \ printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), \
(long)(got.tv_usec)); \
++result; \ ++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); \ struct timespec got = timespec_from_ms(msecs); \
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \ if (got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) { \
{ \ printf("%s:%d: timespec_from_ms(%ld) returned wrong values\n", __FILE__, \
printf("%s:%d: timespec_from_ms(%ld) returned wrong values\n", __FILE__, __LINE__, (long)(msecs)); \ __LINE__, (long)(msecs)); \
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \ printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \ (long)(expect_nsec)); \
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), \
(long)(got.tv_nsec)); \
++result; \ ++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 }; \ struct timespec ts = { .tv_sec = ts_sec, .tv_nsec = ts_nsec }; \
long got = timespec_to_ms(ts); \ long got = timespec_to_ms(ts); \
if (got != expect) { \ if (got != expect) { \
printf("%s:%d: timespec_to_ms({%ld, %ld}) returned wrong value\n", __FILE__, __LINE__, \ printf("%s:%d: timespec_to_ms({%ld, %ld}) returned wrong value\n", \
(long)(ts_sec), (long)(ts_nsec)); \ __FILE__, __LINE__, (long)(ts_sec), (long)(ts_nsec)); \
printf(" Expected: %ld\n", (long)(expect)); \ printf(" Expected: %ld\n", (long)(expect)); \
printf(" Got: %ld\n", got); \ printf(" Got: %ld\n", got); \
++result; \ ++result; \
@ -974,11 +979,9 @@ int main()
TEST_NORMALISE(-1, 500000000, -1, 500000000); TEST_NORMALISE(-1, 500000000, -1, 500000000);
TEST_NORMALISE(-1, 499999999, -1, 499999999); TEST_NORMALISE(-1, 499999999, -1, 499999999);
if(result > 0) if (result > 0) {
{
printf("%d tests failed\n", result); printf("%d tests failed\n", result);
} } else {
else{
printf("All tests passed\n"); printf("All tests passed\n");
} }