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:
Caleb Connolly 2023-06-07 18:55:07 +01:00 committed by Caleb Connolly
parent 1a6bb17cb1
commit f8193eb745
No known key found for this signature in database
GPG key ID: 0583312B195F64B6
3 changed files with 142 additions and 96 deletions

View file

@ -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

View file

@ -6,39 +6,22 @@
struct col color = { .r = 255, .g = 255, .b = 255, .a = 255 };
#define PI 3.1415926535897932384626433832795
#define n_circles 3
#define speed 2.5
static void circles_wave(int frame, int w, int y_off, long dpi)
static void animate_logo(int frame, struct image_info *images)
{
unsigned int t_col = tfb_make_color(color.r, color.g, color.b);
int f = round(frame * speed);
static int step = 0;
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);
tfb_draw_pixel(0, 0, tfb_black);
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_circle(x, y, rad, t_col);
if (frame > 0 && (step + 1) * 4 < frame) {
step++;
}
// tfb_draw_line(0, 0, 100, 500, tfb_red);
// tfb_draw_line(100, 0, 100, 500, tfb_green);
// 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);
draw_svg(images->image[step % images->num_frames], images->x, images->y, images->width,
images->height);
}
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);
}

View file

@ -33,6 +33,8 @@ struct col bg = { .r = 0, .g = 0, .b = 0, .a = 255 };
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 LOG(fmt, ...) \
@ -73,10 +75,10 @@ static void term(int signum)
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;
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();
unsigned char *img = zalloc(w * h * 4);
struct col bg = { .r = 0, .g = 0, .b = 0, .a = 255 };
@ -189,58 +191,56 @@ out:
return out_text;
}
struct 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)
struct dpi_info *calculate_dpi_info()
{
if (dpi_info.logo_size_px)
return &dpi_info;
int w_mm = tfb_screen_width_mm();
int h_mm = tfb_screen_height_mm();
if ((w_mm < 1 || h_mm < 1) && !dpi_info->dpi) {
fprintf(stderr, "ERROR!!!: Invalid screen size: %dmmx%dmm\n", w_mm, h_mm);
if ((w_mm < 1 || h_mm < 1) && !dpi_info.dpi) {
fprintf(stderr, "ERROR!!!: Invalid screen size: %dx%d\n", w_mm, h_mm);
// Assume a dpi of 300
// This should be readable everywhere
// Except ridiculous HiDPI displays
// which honestly should expose their physical
// dimensions....
dpi_info->dpi = 300;
dpi_info.dpi = 300;
}
// If DPI is specified on cmdline then calculate display size from it
// otherwise calculate the dpi based on the display size.
if (dpi_info->dpi > 0) {
w_mm = screenWidth / (float)dpi_info->dpi * 25.4;
h_mm = screenHeight / (float)dpi_info->dpi * 25.4;
if (dpi_info.dpi > 0) {
w_mm = screenWidth / (float)dpi_info.dpi * 25.4;
h_mm = screenHeight / (float)dpi_info.dpi * 25.4;
} 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)
dpi_info->logo_size_max_mm = (screenWidth * 0.75f) / dpi_info->pixels_per_milli;
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_px =
dpi_info.logo_size_px =
(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;
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;
} 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;
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;
}
}
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 {
@ -331,35 +331,32 @@ static void show_messages(struct messages *msgs, const struct dpi_info *dpi_info
}
}
struct image_info {
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 load_image(const struct dpi_info *dpi_info, struct image_info *image_info, int i)
{
int logo_size_px = dpi_info->logo_size_px;
image_info->image = nsvgParseFromFile(image_info->path, "", logo_size_px);
if (!image_info->image) {
image_info->image[i] = nsvgParseFromFile(image_info->path[i], "", logo_size_px);
if (!image_info->image[i] && i == 0) {
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;
}
// 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);
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 (i > 0)
return 0;
float sz =
(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)) {
float scalefactor =
((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;
}
/* 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 rc = 0;
@ -388,15 +425,9 @@ int main(int argc, char **argv)
.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 = {
.path = NULL,
.image = NULL,
.path = {},
.image = {},
.width = 0,
.height = 0,
.x = 0,
@ -426,7 +457,7 @@ int main(int argc, char **argv)
msgs.font_path = optarg;
break;
case 's':
image_info.path = optarg;
image_info.path[0] = strdup(optarg);
break;
case 'm':
message = malloc(strlen(optarg) + 1);
@ -492,7 +523,7 @@ int main(int argc, char **argv)
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);
rc = 1;
return rc;
@ -506,17 +537,18 @@ int main(int argc, char **argv)
screenWidth = (int)tfb_screen_width();
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)
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));
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)
goto no_messages;
@ -534,7 +566,7 @@ int main(int argc, char **argv)
if (message)
msgs.msg = &msg;
show_messages(&msgs, &dpi_info);
show_messages(&msgs, dpi_info);
no_messages:
/* 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);
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();
clock_gettime(CLOCK_REALTIME, &end);
diff = timespec_sub(end, start);
@ -581,17 +614,24 @@ out:
/* Currently DRM doesn't persist the framebuffer after exiting */
if (!use_drm) {
// Before we exit print the logo so it will persist
if (image_info.image) {
if (image_info.image[0]) {
ioctl(tty, KDSETMODE, KD_TEXT);
draw_svg(image_info.image, image_info.x, image_info.y, image_info.width,
image_info.height);
// Before we exit print the logo so it will persist
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
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);
free_message(msgs.msg);
free_message(msgs.bottom_msg);