mirror of
https://git.sr.ht/~calebccff/pbsplash
synced 2026-01-12 20:48:40 -09:00
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:
parent
760ad79c92
commit
01af13950c
13 changed files with 3279 additions and 1833 deletions
|
|
@ -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
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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
571
include/tfblib.h
Normal 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
|
||||||
|
|
@ -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)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
308
src/drawing.c
Normal 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
261
src/fb.c
Normal 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);
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
156
src/pbsplash.c
156
src/pbsplash.c
|
|
@ -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
|
||||||
|
|
|
||||||
223
src/timespec.c
223
src/timespec.c
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue