diff --git a/include/pbsplash.h b/include/pbsplash.h index c122f0b..cae5d25 100644 --- a/include/pbsplash.h +++ b/include/pbsplash.h @@ -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 diff --git a/src/animate.c b/src/animate.c index b80f2d1..e1f043e 100644 --- a/src/animate.c +++ b/src/animate.c @@ -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); } diff --git a/src/pbsplash.c b/src/pbsplash.c index fa5cb01..916c029 100644 --- a/src/pbsplash.c +++ b/src/pbsplash.c @@ -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);