update
This commit is contained in:
252
matrix/imageScreen/image_split_boot.cc
Normal file
252
matrix/imageScreen/image_split_boot.cc
Normal file
@@ -0,0 +1,252 @@
|
||||
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
|
||||
//
|
||||
// Example how to display an image with split reveal animation - BOOT VERSION
|
||||
// Runs once at boot and exits
|
||||
//
|
||||
// This requires an external dependency, so install these first before you
|
||||
// can call `make image-example`
|
||||
// sudo apt-get update
|
||||
// sudo apt-get install libgraphicsmagick++-dev libwebp-dev -y
|
||||
|
||||
/*
|
||||
pour compiler:
|
||||
g++ -I/var/www/moduleair_pro_4g/matrix/include -L/var/www/moduleair_pro_4g/matrix/lib /var/www/moduleair_pro_4g/matrix/imageScreen/image_split_boot.cc -o /var/www/moduleair_pro_4g/matrix/imageScreen/image_split_boot -lrgbmatrix `GraphicsMagick++-config --cppflags --libs`
|
||||
|
||||
pour lancer:
|
||||
sudo /var/www/moduleair_pro_4g/matrix/imageScreen/image_split_boot /var/www/moduleair_pro_4g/matrix/imageScreen/ModuleAir128x64.png
|
||||
*/
|
||||
|
||||
#include "led-matrix.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <exception>
|
||||
#include <Magick++.h>
|
||||
#include <magick/image.h>
|
||||
|
||||
using rgb_matrix::Canvas;
|
||||
using rgb_matrix::RGBMatrix;
|
||||
using rgb_matrix::FrameCanvas;
|
||||
|
||||
// Make sure we can exit gracefully when Ctrl-C is pressed.
|
||||
volatile bool interrupt_received = false;
|
||||
static void InterruptHandler(int signo) {
|
||||
interrupt_received = true;
|
||||
}
|
||||
|
||||
using ImageVector = std::vector<Magick::Image>;
|
||||
|
||||
// Given the filename, load the image and scale to the size of the matrix.
|
||||
static ImageVector LoadImageAndScaleImage(const char *filename,
|
||||
int target_width,
|
||||
int target_height) {
|
||||
ImageVector result;
|
||||
|
||||
ImageVector frames;
|
||||
try {
|
||||
readImages(&frames, filename);
|
||||
} catch (std::exception &e) {
|
||||
if (e.what())
|
||||
fprintf(stderr, "%s\n", e.what());
|
||||
return result;
|
||||
}
|
||||
|
||||
if (frames.empty()) {
|
||||
fprintf(stderr, "No image found.");
|
||||
return result;
|
||||
}
|
||||
|
||||
// Animated images have partial frames that need to be put together
|
||||
if (frames.size() > 1) {
|
||||
Magick::coalesceImages(&result, frames.begin(), frames.end());
|
||||
} else {
|
||||
result.push_back(frames[0]); // just a single still image.
|
||||
}
|
||||
|
||||
for (Magick::Image &image : result) {
|
||||
image.scale(Magick::Geometry(target_width, target_height));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Copy image with split reveal animation effect
|
||||
void CopyImageToCanvasWithSplitReveal(const Magick::Image &image, Canvas *canvas, float progress) {
|
||||
const int matrix_height = canvas->height();
|
||||
const int matrix_width = canvas->width();
|
||||
const int half_height = matrix_height / 2;
|
||||
|
||||
// Calculate how many rows of each half to show based on progress (0.0 to 1.0)
|
||||
int top_rows_to_show = (int)(half_height * progress);
|
||||
int bottom_rows_to_show = (int)(half_height * progress);
|
||||
|
||||
// Clear the canvas first
|
||||
canvas->Clear();
|
||||
|
||||
// Draw top half (sliding down from above)
|
||||
for (int row = 0; row < top_rows_to_show && row < half_height; ++row) {
|
||||
// Calculate source row in the original image
|
||||
int src_row = row;
|
||||
if (src_row < (int)image.rows()) {
|
||||
for (size_t x = 0; x < image.columns() && x < matrix_width; ++x) {
|
||||
const Magick::Color &c = image.pixelColor(x, src_row);
|
||||
if (c.alphaQuantum() < 256) {
|
||||
canvas->SetPixel(x, row,
|
||||
ScaleQuantumToChar(c.redQuantum()),
|
||||
ScaleQuantumToChar(c.greenQuantum()),
|
||||
ScaleQuantumToChar(c.blueQuantum()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw bottom half (sliding up from below)
|
||||
for (int row = 0; row < bottom_rows_to_show && row < half_height; ++row) {
|
||||
// Calculate destination row (starting from bottom)
|
||||
int dest_row = matrix_height - 1 - row;
|
||||
// Calculate source row in the original image (from bottom half)
|
||||
int src_row = (int)image.rows() - 1 - row;
|
||||
|
||||
if (src_row >= half_height && src_row < (int)image.rows() && dest_row >= half_height) {
|
||||
for (size_t x = 0; x < image.columns() && x < matrix_width; ++x) {
|
||||
const Magick::Color &c = image.pixelColor(x, src_row);
|
||||
if (c.alphaQuantum() < 256) {
|
||||
canvas->SetPixel(x, dest_row,
|
||||
ScaleQuantumToChar(c.redQuantum()),
|
||||
ScaleQuantumToChar(c.greenQuantum()),
|
||||
ScaleQuantumToChar(c.blueQuantum()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show image with split reveal animation - BOOT VERSION (runs once and exits)
|
||||
void ShowBootSplitRevealAnimation(const Magick::Image &image, RGBMatrix *matrix) {
|
||||
const int animation_duration_ms = 3000; // 3 seconds animation
|
||||
const int display_duration_ms = 5000; // 5 seconds display
|
||||
const int fps = 60; // 60 frames per second for smooth animation
|
||||
const int frame_delay_ms = 1000 / fps;
|
||||
const int total_frames = animation_duration_ms / frame_delay_ms;
|
||||
|
||||
FrameCanvas *offscreen_canvas = matrix->CreateFrameCanvas();
|
||||
|
||||
printf("Starting boot split reveal animation...\n");
|
||||
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
|
||||
// Animation phase
|
||||
for (int frame = 0; frame <= total_frames && !interrupt_received; ++frame) {
|
||||
// Calculate progress (0.0 to 1.0)
|
||||
float progress = (float)frame / total_frames;
|
||||
|
||||
// Apply easing function for smoother animation (ease-out)
|
||||
progress = 1.0f - (1.0f - progress) * (1.0f - progress);
|
||||
|
||||
// Render frame with current progress
|
||||
CopyImageToCanvasWithSplitReveal(image, offscreen_canvas, progress);
|
||||
offscreen_canvas = matrix->SwapOnVSync(offscreen_canvas);
|
||||
|
||||
// Maintain consistent frame rate
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(frame_delay_ms));
|
||||
}
|
||||
|
||||
auto animation_end = std::chrono::steady_clock::now();
|
||||
auto animation_duration = std::chrono::duration_cast<std::chrono::milliseconds>(animation_end - start_time);
|
||||
printf("Boot animation completed in %ld ms\n", animation_duration.count());
|
||||
|
||||
// Display final complete image
|
||||
printf("Displaying boot image for %d seconds...\n", display_duration_ms / 1000);
|
||||
auto display_start = std::chrono::steady_clock::now();
|
||||
|
||||
while (!interrupt_received) {
|
||||
auto current_time = std::chrono::steady_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(current_time - display_start);
|
||||
|
||||
if (elapsed.count() >= display_duration_ms) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Keep refreshing the display
|
||||
offscreen_canvas = matrix->SwapOnVSync(offscreen_canvas);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
|
||||
auto total_end = std::chrono::steady_clock::now();
|
||||
auto total_duration = std::chrono::duration_cast<std::chrono::milliseconds>(total_end - start_time);
|
||||
printf("Boot display completed. Total time: %ld ms (%ds animation + %ds display)\n",
|
||||
total_duration.count(), animation_duration_ms / 1000, display_duration_ms / 1000);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
Magick::InitializeMagick(*argv);
|
||||
|
||||
// Check if a filename was provided
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "Usage: %s <image-filename>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
const char *filename = argv[1];
|
||||
|
||||
// Initialize with hardcoded defaults for your specific hardware
|
||||
RGBMatrix::Options defaults;
|
||||
defaults.hardware_mapping = "moduleair_pinout";
|
||||
defaults.rows = 64;
|
||||
defaults.cols = 128;
|
||||
defaults.chain_length = 1;
|
||||
defaults.parallel = 1;
|
||||
defaults.row_address_type = 3;
|
||||
defaults.show_refresh_rate = false; // Disable refresh rate display for boot
|
||||
defaults.brightness = 100;
|
||||
defaults.pwm_bits = 1;
|
||||
defaults.panel_type = "FM6126A";
|
||||
defaults.disable_hardware_pulsing = false;
|
||||
|
||||
// Set up runtime options
|
||||
rgb_matrix::RuntimeOptions runtime_opt;
|
||||
runtime_opt.gpio_slowdown = 4; // Adjust if needed for your Pi model
|
||||
runtime_opt.daemon = 0;
|
||||
runtime_opt.drop_privileges = 0; // Set to 1 if not running as root
|
||||
|
||||
// Handle Ctrl+C to exit gracefully
|
||||
signal(SIGTERM, InterruptHandler);
|
||||
signal(SIGINT, InterruptHandler);
|
||||
|
||||
// Create the matrix with our defaults
|
||||
RGBMatrix *matrix = RGBMatrix::CreateFromOptions(defaults, runtime_opt);
|
||||
if (matrix == NULL) {
|
||||
fprintf(stderr, "Failed to create matrix\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load and process the image
|
||||
ImageVector images = LoadImageAndScaleImage(filename,
|
||||
matrix->width(),
|
||||
matrix->height());
|
||||
|
||||
// Display the image with split reveal animation (once only)
|
||||
switch (images.size()) {
|
||||
case 0: // failed to load image
|
||||
fprintf(stderr, "Failed to load image\n");
|
||||
break;
|
||||
case 1: // single image - show with split reveal animation
|
||||
ShowBootSplitRevealAnimation(images[0], matrix);
|
||||
break;
|
||||
default: // animated image - just show first frame with split reveal
|
||||
printf("Animated image detected, using first frame for boot animation\n");
|
||||
ShowBootSplitRevealAnimation(images[0], matrix);
|
||||
break;
|
||||
}
|
||||
|
||||
// Clean up and exit
|
||||
printf("Boot animation finished. Cleaning up and exiting...\n");
|
||||
matrix->Clear();
|
||||
delete matrix;
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user