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
|
||||||
1686
include/nanosvg.h
1686
include/nanosvg.h
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -3,13 +3,30 @@
|
||||||
|
|
||||||
#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;
|
||||||
struct {
|
struct {
|
||||||
unsigned char r, g, b, a;
|
unsigned char r, g, b, a;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
void animate_frame(int frame, int w, int y_off, long dpi);
|
void animate_frame(int frame, int w, int y_off, long dpi);
|
||||||
|
|
|
||||||
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
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
* OTHER DEALINGS IN THE SOFTWARE.
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*
|
*
|
||||||
* For more information, please refer to <http://unlicense.org/>
|
* For more information, please refer to <http://unlicense.org/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef DAN_TIMESPEC_H
|
#ifndef DAN_TIMESPEC_H
|
||||||
#define DAN_TIMESPEC_H
|
#define DAN_TIMESPEC_H
|
||||||
|
|
@ -48,7 +48,7 @@ struct timespec timespec_min(struct timespec ts1, struct timespec ts2);
|
||||||
struct timespec timespec_max(struct timespec ts1, struct timespec ts2);
|
struct timespec timespec_max(struct timespec ts1, struct timespec ts2);
|
||||||
struct timespec timespec_clamp(struct timespec ts1, struct timespec min, struct timespec max);
|
struct timespec timespec_clamp(struct timespec ts1, struct timespec min, struct timespec max);
|
||||||
|
|
||||||
int timespec_cmp(struct timespec ts1, struct timespec ts2);
|
int timespec_cmp(struct timespec ts1, struct timespec ts2);
|
||||||
bool timespec_eq(struct timespec ts1, struct timespec ts2);
|
bool timespec_eq(struct timespec ts1, struct timespec ts2);
|
||||||
bool timespec_gt(struct timespec ts1, struct timespec ts2);
|
bool timespec_gt(struct timespec ts1, struct timespec ts2);
|
||||||
bool timespec_ge(struct timespec ts1, struct timespec ts2);
|
bool timespec_ge(struct timespec ts1, struct timespec ts2);
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,11 @@
|
||||||
#include <tfblib/tfb_colors.h>
|
#include <tfblib/tfb_colors.h>
|
||||||
#include <tfblib/tfblib.h>
|
#include <tfblib/tfblib.h>
|
||||||
|
|
||||||
struct col color = {.r = 255, .g = 255, .b = 255, .a = 255};
|
struct col color = { .r = 255, .g = 255, .b = 255, .a = 255 };
|
||||||
|
|
||||||
#define PI 3.1415926535897932384626433832795
|
#define PI 3.1415926535897932384626433832795
|
||||||
#define n_circles 3
|
#define n_circles 3
|
||||||
#define speed 2.5
|
#define speed 2.5
|
||||||
|
|
||||||
static void circles_wave(int frame, int w, int y_off, long dpi)
|
static void circles_wave(int frame, int w, int y_off, long dpi)
|
||||||
{
|
{
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
192
src/pbsplash.c
192
src/pbsplash.c
|
|
@ -1,48 +1,46 @@
|
||||||
#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"
|
||||||
#define LOGO_SIZE_MAX_MM 45
|
#define LOGO_SIZE_MAX_MM 45
|
||||||
#define FONT_SIZE_PT 9
|
#define FONT_SIZE_PT 9
|
||||||
#define FONT_SIZE_B_PT 6
|
#define FONT_SIZE_B_PT 6
|
||||||
#define B_MESSAGE_OFFSET_MM 3
|
#define B_MESSAGE_OFFSET_MM 3
|
||||||
#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;
|
||||||
|
|
||||||
#define zalloc(size) calloc(1, size)
|
#define zalloc(size) calloc(1, size)
|
||||||
|
|
||||||
#define LOG(fmt, ...) \
|
#define LOG(fmt, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (debug) \
|
if (debug) \
|
||||||
printf(fmt, ##__VA_ARGS__); \
|
printf(fmt, ##__VA_ARGS__); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
static int usage()
|
static int usage()
|
||||||
|
|
@ -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);
|
||||||
|
|
@ -170,7 +118,7 @@ static inline float getShapeWidth(const NSVGimage *font, const NSVGshape *shape)
|
||||||
* based on the font size and the font SVG file.
|
* based on the font size and the font SVG file.
|
||||||
*/
|
*/
|
||||||
static const char *getTextDimensions(const NSVGimage *font, const char *text, float scale,
|
static const char *getTextDimensions(const NSVGimage *font, const char *text, float scale,
|
||||||
int *width, int *height)
|
int *width, int *height)
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j;
|
||||||
int fontHeight = (font->fontAscent - font->fontDescent) * scale;
|
int fontHeight = (font->fontAscent - font->fontDescent) * scale;
|
||||||
|
|
@ -198,7 +146,7 @@ static const char *getTextDimensions(const NSVGimage *font, const char *text, fl
|
||||||
i--;
|
i--;
|
||||||
if (i < 1) {
|
if (i < 1) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"ERROR: Text is too long to fit on screen!");
|
"ERROR: Text is too long to fit on screen!");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -210,7 +158,7 @@ static const char *getTextDimensions(const NSVGimage *font, const char *text, fl
|
||||||
if (i <= 0) {
|
if (i <= 0) {
|
||||||
line_has_space = false;
|
line_has_space = false;
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"ERROR: Text is too long to fit on screen!");
|
"ERROR: Text is too long to fit on screen!");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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->message = getTextDimensions(font, msg_info->src_message, msg_info->fontsz, &msg_info->width, &msg_info->height);
|
&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
|
||||||
}
|
}
|
||||||
|
|
@ -331,7 +280,7 @@ struct messages {
|
||||||
static inline void show_message(const struct msg_info *msg_info, const NSVGimage *font)
|
static inline void show_message(const struct msg_info *msg_info, const NSVGimage *font)
|
||||||
{
|
{
|
||||||
draw_text(font, msg_info->message, msg_info->x, msg_info->y, msg_info->width,
|
draw_text(font, msg_info->message, msg_info->x, msg_info->y, msg_info->width,
|
||||||
msg_info->height, msg_info->fontsz, tfb_gray);
|
msg_info->height, msg_info->fontsz, tfb_gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show_messages(struct messages *msgs, const struct dpi_info *dpi_info)
|
static void show_messages(struct messages *msgs, const struct dpi_info *dpi_info)
|
||||||
|
|
@ -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,15 +351,16 @@ 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) /
|
||||||
//printf("Got scale factor: %f\n", scalefactor);
|
image_info->width);
|
||||||
|
// printf("Got scale factor: %f\n", scalefactor);
|
||||||
image_info->width = dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli;
|
image_info->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);
|
||||||
|
|
||||||
|
|
@ -593,7 +546,7 @@ no_messages:
|
||||||
tfb_flush_fb();
|
tfb_flush_fb();
|
||||||
clock_gettime(CLOCK_REALTIME, &end);
|
clock_gettime(CLOCK_REALTIME, &end);
|
||||||
diff = timespec_sub(end, start);
|
diff = timespec_sub(end, start);
|
||||||
//printf("%05d: %09ld\n", tick, diff.tv_nsec);
|
// printf("%05d: %09ld\n", tick, diff.tv_nsec);
|
||||||
if (diff.tv_nsec < 1000000000 / target_fps) {
|
if (diff.tv_nsec < 1000000000 / target_fps) {
|
||||||
struct timespec sleep_time = {
|
struct timespec sleep_time = {
|
||||||
.tv_sec = 0,
|
.tv_sec = 0,
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
1267
src/timespec.c
1267
src/timespec.c
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue