WIP: vendor tfblib + clang-format

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

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

View file

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

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

571
include/tfblib.h Normal file
View file

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

View file

@ -27,7 +27,7 @@
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to <http://unlicense.org/>
*/
*/
#ifndef DAN_TIMESPEC_H
#define DAN_TIMESPEC_H

View file

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

View file

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

308
src/drawing.c Normal file
View file

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

261
src/fb.c Normal file
View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff