mirror of
https://git.sr.ht/~calebccff/pbsplash
synced 2026-01-12 20:48:40 -09:00
animated logo support
Drop the wavy circles and expect to be provided a series of SVGs to animate the logo.
This commit is contained in:
parent
1a6bb17cb1
commit
f8193eb745
3 changed files with 142 additions and 96 deletions
|
|
@ -29,6 +29,29 @@ struct col {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
void animate_frame(int frame, int w, int y_off, long dpi);
|
struct dpi_info {
|
||||||
|
long dpi;
|
||||||
|
int pixels_per_milli;
|
||||||
|
float logo_size_px;
|
||||||
|
int logo_size_max_mm;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct NSVGimage NSVGimage;
|
||||||
|
|
||||||
|
#define MAX_FRAMES 16
|
||||||
|
|
||||||
|
struct image_info {
|
||||||
|
char *path[MAX_FRAMES];
|
||||||
|
NSVGimage *image[MAX_FRAMES];
|
||||||
|
int num_frames;
|
||||||
|
float width;
|
||||||
|
float height;
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
};
|
||||||
|
|
||||||
|
void draw_svg(NSVGimage *image, int x, int y, int w, int h);
|
||||||
|
|
||||||
|
void animate_frame(int frame, int w, int y_off, long dpi, struct image_info *images);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -6,39 +6,22 @@
|
||||||
|
|
||||||
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
|
static void animate_logo(int frame, struct image_info *images)
|
||||||
#define n_circles 3
|
|
||||||
#define speed 2.5
|
|
||||||
|
|
||||||
static void circles_wave(int frame, int w, int y_off, long dpi)
|
|
||||||
{
|
{
|
||||||
unsigned int t_col = tfb_make_color(color.r, color.g, color.b);
|
static int step = 0;
|
||||||
int f = round(frame * speed);
|
|
||||||
|
|
||||||
int rad = MM_TO_PX(dpi, 1);
|
tfb_draw_pixel(0, 0, tfb_black);
|
||||||
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++) {
|
if (frame > 0 && (step + 1) * 4 < frame) {
|
||||||
int x = left + (i * dist);
|
step++;
|
||||||
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_circle(x, y, rad, t_col);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// tfb_draw_line(0, 0, 100, 500, tfb_red);
|
draw_svg(images->image[step % images->num_frames], images->x, images->y, images->width,
|
||||||
// tfb_draw_line(100, 0, 100, 500, tfb_green);
|
images->height);
|
||||||
// tfb_draw_line(200, 0, 100, 500, tfb_blue);
|
|
||||||
|
|
||||||
// tfb_fill_rect(400, 300, 200, 500, tfb_red);
|
|
||||||
// tfb_fill_rect(600, 300, 200, 500, tfb_green);
|
|
||||||
// tfb_fill_rect(800, 300, 200, 500, tfb_blue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void animate_frame(int frame, int w, int y_off, long dpi)
|
void animate_frame(int frame, int w, int y_off, long dpi,
|
||||||
|
struct image_info *images)
|
||||||
{
|
{
|
||||||
circles_wave(frame, w, y_off, dpi);
|
animate_logo(frame, images);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
176
src/pbsplash.c
176
src/pbsplash.c
|
|
@ -33,6 +33,8 @@ struct col bg = { .r = 0, .g = 0, .b = 0, .a = 255 };
|
||||||
|
|
||||||
static int screenWidth, screenHeight;
|
static int screenWidth, screenHeight;
|
||||||
|
|
||||||
|
static struct dpi_info dpi_info = { .logo_size_max_mm = LOGO_SIZE_MAX_MM };
|
||||||
|
|
||||||
#define zalloc(size) calloc(1, size)
|
#define zalloc(size) calloc(1, size)
|
||||||
|
|
||||||
#define LOG(fmt, ...) \
|
#define LOG(fmt, ...) \
|
||||||
|
|
@ -73,10 +75,10 @@ static void term(int signum)
|
||||||
terminate = 1;
|
terminate = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void draw_svg(NSVGimage *image, int x, int y, int w, int h)
|
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;
|
||||||
LOG("draw_svg: (%d, %d), %dx%d, %f\n", x, y, w, h, sz);
|
// LOG("draw_svg: (%d, %d), %dx%d, %f\n", x, y, w, h, sz);
|
||||||
NSVGrasterizer *rast = nsvgCreateRasterizer();
|
NSVGrasterizer *rast = nsvgCreateRasterizer();
|
||||||
unsigned char *img = zalloc(w * h * 4);
|
unsigned char *img = zalloc(w * h * 4);
|
||||||
struct col bg = { .r = 0, .g = 0, .b = 0, .a = 255 };
|
struct col bg = { .r = 0, .g = 0, .b = 0, .a = 255 };
|
||||||
|
|
@ -189,58 +191,56 @@ out:
|
||||||
return out_text;
|
return out_text;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct dpi_info {
|
struct dpi_info *calculate_dpi_info()
|
||||||
long dpi;
|
|
||||||
int pixels_per_milli;
|
|
||||||
float logo_size_px;
|
|
||||||
int logo_size_max_mm;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void calculate_dpi_info(struct dpi_info *dpi_info)
|
|
||||||
{
|
{
|
||||||
|
if (dpi_info.logo_size_px)
|
||||||
|
return &dpi_info;
|
||||||
|
|
||||||
int w_mm = tfb_screen_width_mm();
|
int w_mm = tfb_screen_width_mm();
|
||||||
int h_mm = tfb_screen_height_mm();
|
int h_mm = tfb_screen_height_mm();
|
||||||
|
|
||||||
if ((w_mm < 1 || h_mm < 1) && !dpi_info->dpi) {
|
if ((w_mm < 1 || h_mm < 1) && !dpi_info.dpi) {
|
||||||
fprintf(stderr, "ERROR!!!: Invalid screen size: %dmmx%dmm\n", w_mm, h_mm);
|
fprintf(stderr, "ERROR!!!: Invalid screen size: %dx%d\n", w_mm, h_mm);
|
||||||
|
|
||||||
// Assume a dpi of 300
|
// Assume a dpi of 300
|
||||||
// This should be readable everywhere
|
// This should be readable everywhere
|
||||||
// Except ridiculous HiDPI displays
|
// Except ridiculous HiDPI displays
|
||||||
// which honestly should expose their physical
|
// which honestly should expose their physical
|
||||||
// dimensions....
|
// dimensions....
|
||||||
dpi_info->dpi = 300;
|
dpi_info.dpi = 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If DPI is specified on cmdline then calculate display size from it
|
// If DPI is specified on cmdline then calculate display size from it
|
||||||
// otherwise calculate the dpi based on the display size.
|
// otherwise calculate the dpi based on the display size.
|
||||||
if (dpi_info->dpi > 0) {
|
if (dpi_info.dpi > 0) {
|
||||||
w_mm = screenWidth / (float)dpi_info->dpi * 25.4;
|
w_mm = screenWidth / (float)dpi_info.dpi * 25.4;
|
||||||
h_mm = screenHeight / (float)dpi_info->dpi * 25.4;
|
h_mm = screenHeight / (float)dpi_info.dpi * 25.4;
|
||||||
} else {
|
} else {
|
||||||
dpi_info->dpi = (float)screenWidth / (float)w_mm * 25.4;
|
dpi_info.dpi = (float)screenWidth / (float)w_mm * 25.4;
|
||||||
}
|
}
|
||||||
dpi_info->pixels_per_milli = (float)screenWidth / (float)w_mm;
|
dpi_info.pixels_per_milli = (float)screenWidth / (float)w_mm;
|
||||||
|
|
||||||
if (dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli > screenWidth)
|
if (dpi_info.logo_size_max_mm * dpi_info.pixels_per_milli > screenWidth)
|
||||||
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 : screenHeight) * 0.75f;
|
(float)(screenWidth < screenHeight ? screenWidth : 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_px =
|
||||||
dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli;
|
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_px =
|
||||||
dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli;
|
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,
|
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);
|
dpi_info.dpi, dpi_info.logo_size_px);
|
||||||
|
|
||||||
|
return &dpi_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct msg_info {
|
struct msg_info {
|
||||||
|
|
@ -331,35 +331,32 @@ static void show_messages(struct messages *msgs, const struct dpi_info *dpi_info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct image_info {
|
int load_image(const struct dpi_info *dpi_info, struct image_info *image_info, int i)
|
||||||
const char *path;
|
|
||||||
NSVGimage *image;
|
|
||||||
float width;
|
|
||||||
float height;
|
|
||||||
float x;
|
|
||||||
float y;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int load_image(const struct dpi_info *dpi_info, struct image_info *image_info)
|
|
||||||
{
|
{
|
||||||
int logo_size_px = dpi_info->logo_size_px;
|
int logo_size_px = dpi_info->logo_size_px;
|
||||||
|
|
||||||
image_info->image = nsvgParseFromFile(image_info->path, "", logo_size_px);
|
image_info->image[i] = nsvgParseFromFile(image_info->path[i], "", logo_size_px);
|
||||||
if (!image_info->image) {
|
if (!image_info->image[i] && i == 0) {
|
||||||
fprintf(stderr, "failed to load SVG image\n");
|
fprintf(stderr, "failed to load SVG image\n");
|
||||||
fprintf(stderr, " image path: %s\n", image_info->path);
|
fprintf(stderr, " image path: %s\n", image_info->path[i]);
|
||||||
|
return 1;
|
||||||
|
} else if (!image_info->image[i]) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For taller images make sure they don't get too wide
|
// For taller images make sure they don't get too wide
|
||||||
if (image_info->image->width < image_info->image->height * 1.1)
|
if (image_info->image[i]->width < image_info->image[i]->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)logo_size_px / (image_info->image->width > image_info->image->height ?
|
if (i > 0)
|
||||||
image_info->image->height :
|
return 0;
|
||||||
image_info->image->width);
|
|
||||||
image_info->width = image_info->image->width * sz + 0.5;
|
float sz =
|
||||||
image_info->height = image_info->image->height * sz + 0.5;
|
(float)logo_size_px / (image_info->image[i]->width > image_info->image[i]->height ?
|
||||||
|
image_info->image[i]->height :
|
||||||
|
image_info->image[i]->width);
|
||||||
|
image_info->width = image_info->image[i]->width * sz + 0.5;
|
||||||
|
image_info->height = image_info->image[i]->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) /
|
((float)(dpi_info->logo_size_max_mm * dpi_info->pixels_per_milli) /
|
||||||
|
|
@ -374,6 +371,46 @@ static int load_image(const struct dpi_info *dpi_info, struct image_info *image_
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* strdup and increment a 2 digit number in the string at offset */
|
||||||
|
static char *strdupinc(const char *s, int i)
|
||||||
|
{
|
||||||
|
char *n = NULL;
|
||||||
|
char *base = zalloc(strlen(s));
|
||||||
|
|
||||||
|
LOG("%d, %s\n", i, s);
|
||||||
|
|
||||||
|
n = strstr(s, "01");
|
||||||
|
|
||||||
|
memcpy(base, s, strlen(s));
|
||||||
|
int len = strlen(s) - strlen(n);
|
||||||
|
base[len] = '0' + (i / 10);
|
||||||
|
base[len + 1] = '1' + (i % 10);
|
||||||
|
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
int load_images(const struct dpi_info *dpi_info, struct image_info *image_info)
|
||||||
|
{
|
||||||
|
if (strlen(image_info->path[0]) < 6) {
|
||||||
|
fprintf(stderr, "invalid image path\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_FRAMES; i++) {
|
||||||
|
if (i > 0)
|
||||||
|
image_info->path[i] = strdupinc(image_info->path[0], i);
|
||||||
|
if (load_image(dpi_info, image_info, i)) {
|
||||||
|
free(image_info->path[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
image_info->num_frames++;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG("num_frames: %d\n", image_info->num_frames);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
@ -388,15 +425,9 @@ int main(int argc, char **argv)
|
||||||
.msg = NULL,
|
.msg = NULL,
|
||||||
.bottom_msg = NULL,
|
.bottom_msg = NULL,
|
||||||
};
|
};
|
||||||
struct dpi_info dpi_info = {
|
|
||||||
.dpi = 0,
|
|
||||||
.pixels_per_milli = 0,
|
|
||||||
.logo_size_px = 0,
|
|
||||||
.logo_size_max_mm = LOGO_SIZE_MAX_MM,
|
|
||||||
};
|
|
||||||
struct image_info image_info = {
|
struct image_info image_info = {
|
||||||
.path = NULL,
|
.path = {},
|
||||||
.image = NULL,
|
.image = {},
|
||||||
.width = 0,
|
.width = 0,
|
||||||
.height = 0,
|
.height = 0,
|
||||||
.x = 0,
|
.x = 0,
|
||||||
|
|
@ -426,7 +457,7 @@ int main(int argc, char **argv)
|
||||||
msgs.font_path = optarg;
|
msgs.font_path = optarg;
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
image_info.path = optarg;
|
image_info.path[0] = strdup(optarg);
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
message = malloc(strlen(optarg) + 1);
|
message = malloc(strlen(optarg) + 1);
|
||||||
|
|
@ -492,7 +523,7 @@ 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(0, "/dev/fb0", active_tty)) != 0) {
|
if ((rc = tfb_acquire_fb(TFB_FL_USE_DOUBLE_BUFFER, "/dev/fb0", active_tty)) != 0) {
|
||||||
fprintf(stderr, "tfb_acquire_fb() failed with error code: %d\n", rc);
|
fprintf(stderr, "tfb_acquire_fb() failed with error code: %d\n", rc);
|
||||||
rc = 1;
|
rc = 1;
|
||||||
return rc;
|
return rc;
|
||||||
|
|
@ -506,17 +537,18 @@ int main(int argc, char **argv)
|
||||||
screenWidth = (int)tfb_screen_width();
|
screenWidth = (int)tfb_screen_width();
|
||||||
screenHeight = (int)tfb_screen_height();
|
screenHeight = (int)tfb_screen_height();
|
||||||
|
|
||||||
calculate_dpi_info(&dpi_info);
|
struct dpi_info *dpi_info = calculate_dpi_info();
|
||||||
|
|
||||||
rc = load_image(&dpi_info, &image_info);
|
rc = load_images(dpi_info, &image_info);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
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(bg.r, bg.g, bg.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);
|
draw_svg(image_info.image[0], image_info.x, image_info.y, image_info.width,
|
||||||
|
image_info.height);
|
||||||
|
|
||||||
if (!message && !message_bottom)
|
if (!message && !message_bottom)
|
||||||
goto no_messages;
|
goto no_messages;
|
||||||
|
|
@ -534,7 +566,7 @@ int main(int argc, char **argv)
|
||||||
if (message)
|
if (message)
|
||||||
msgs.msg = &msg;
|
msgs.msg = &msg;
|
||||||
|
|
||||||
show_messages(&msgs, &dpi_info);
|
show_messages(&msgs, dpi_info);
|
||||||
|
|
||||||
no_messages:
|
no_messages:
|
||||||
/* This is necessary to copy the parts we draw once (like the logo) to the front buffer */
|
/* This is necessary to copy the parts we draw once (like the logo) to the front buffer */
|
||||||
|
|
@ -563,7 +595,8 @@ no_messages:
|
||||||
}
|
}
|
||||||
clock_gettime(CLOCK_REALTIME, &start);
|
clock_gettime(CLOCK_REALTIME, &start);
|
||||||
tick = timespec_to_double(timespec_sub(start, epoch)) * tickrate;
|
tick = timespec_to_double(timespec_sub(start, epoch)) * tickrate;
|
||||||
animate_frame(tick, screenWidth, animation_y, dpi_info.dpi);
|
animate_frame(tick, screenWidth, animation_y, dpi_info->dpi, &image_info);
|
||||||
|
tfb_flush_window();
|
||||||
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);
|
||||||
|
|
@ -581,17 +614,24 @@ out:
|
||||||
/* Currently DRM doesn't persist the framebuffer after exiting */
|
/* Currently DRM doesn't persist the framebuffer after exiting */
|
||||||
if (!use_drm) {
|
if (!use_drm) {
|
||||||
// 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[0]) {
|
||||||
ioctl(tty, KDSETMODE, KD_TEXT);
|
ioctl(tty, KDSETMODE, KD_TEXT);
|
||||||
draw_svg(image_info.image, image_info.x, image_info.y, image_info.width,
|
// Before we exit print the logo so it will persist
|
||||||
image_info.height);
|
if (image_info.image[0]) {
|
||||||
|
ioctl(tty, KDSETMODE, KD_TEXT);
|
||||||
|
draw_svg(image_info.image[0], 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
|
||||||
show_messages(&msgs, &dpi_info);
|
show_messages(&msgs, dpi_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsvgDelete(image_info.image);
|
for (int i = 0; i < image_info.num_frames; i++) {
|
||||||
|
nsvgDelete(image_info.image[i]);
|
||||||
|
free(image_info.path[i]);
|
||||||
|
}
|
||||||
nsvgDelete(msgs.font);
|
nsvgDelete(msgs.font);
|
||||||
free_message(msgs.msg);
|
free_message(msgs.msg);
|
||||||
free_message(msgs.bottom_msg);
|
free_message(msgs.bottom_msg);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue