mirror of
https://github.com/Hopiu/rpi-rgb-led-matrix.git
synced 2026-03-16 22:10:27 +00:00
o Add a way to choose timing relevant parameters (-w, -l, -t) before
each image. Fixes #255
This commit is contained in:
parent
3080a773e7
commit
5fdc3088e7
2 changed files with 94 additions and 40 deletions
|
|
@ -17,13 +17,14 @@ make led-image-viewer
|
|||
|
||||
The resulting binary has a couple of flags.
|
||||
```
|
||||
usage: ./led-image-viewer [options] <image> [<image> ...]
|
||||
usage: ./led-image-viewer [options] <image> [option] [<image> ...]
|
||||
Options:
|
||||
-C : Center images.
|
||||
-w<seconds> : If multiple images given: Wait time between in seconds (default: 1.5).
|
||||
-f : Forever cycle through the list of files on the command line.
|
||||
-t<seconds> : For gif animations: stop after this time.
|
||||
-l<loop-count> : For gif animations: number of loops through a full cycle.
|
||||
-s : If multiple images are given: shuffle.
|
||||
-L : Large display, in which each chain is 'folded down'
|
||||
in the middle in an U-arrangement to get more vertical space.
|
||||
-R<angle> : Rotate output; steps of 90 degrees
|
||||
|
|
@ -44,8 +45,12 @@ General LED matrix options:
|
|||
--led-slowdown-gpio=<0..2>: Slowdown GPIO. Needed for faster Pis and/or slower panels (Default: 1).
|
||||
--led-daemon : Make the process run in the background as daemon.
|
||||
--led-no-drop-privs : Don't drop privileges from 'root' after initializing the hardware.
|
||||
|
||||
Switch time between files: -w for static images; -t/-l for animations
|
||||
Animated gifs: If both -l and -t are given, whatever comes first determines duration.
|
||||
Animated gifs: If both -l and -t are given, whatever finishes first determines duration.
|
||||
|
||||
The -w, -t and -l options apply to the following images until a new instance of one of these options is seen.
|
||||
So you can choose different durations for different images.
|
||||
```
|
||||
|
||||
Then, you can run it with any common image format, including animated gifs:
|
||||
|
|
@ -58,10 +63,13 @@ sudo ./led-image-viewer -t5 animated.gif # Show an animated gif for 5 second
|
|||
sudo ./led-image-viewer -l2 animated.gif # Show an animated gif for 2 loops
|
||||
|
||||
sudo ./led-image-viewer -w3 foo.jpg bar.png # show two images, wait 3 seconds between. Stop.
|
||||
sudo ./led-image-viewer -w3 foo.jpg -w2 bar.png baz.png # show images, wait 3 seconds after the first, 2 seconds after the second and third. Stop.
|
||||
sudo ./led-image-viewer -f -w3 foo.jpg bar.png # show images, wait 3sec between, go back and loop forever
|
||||
|
||||
sudo ./led-image-viewer -f -w3 *.png *.jpg # Loop forever through a list of images
|
||||
|
||||
sudo ./led-image-viewer -f -s *.png # Loop forever but randomize (shuffle) each round.
|
||||
|
||||
# Show image.png and animated.gif in a loop. Show the static image for 3 seconds
|
||||
# while the animation is shown for 5 seconds (-t takes precendence for animated
|
||||
# images over -w)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,10 @@
|
|||
#include <sys/time.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <Magick++.h>
|
||||
#include <magick/image.h>
|
||||
|
||||
|
|
@ -45,6 +48,7 @@ static void InterruptHandler(int signo) {
|
|||
}
|
||||
|
||||
typedef int64_t tmillis_t;
|
||||
static const tmillis_t distant_future = (1LL<<40); // that is a while.
|
||||
static tmillis_t GetTimeInMillis() {
|
||||
struct timeval tp;
|
||||
gettimeofday(&tp, NULL);
|
||||
|
|
@ -177,7 +181,9 @@ void DisplayAnimation(const PreprocessedList &frames,
|
|||
}
|
||||
|
||||
static int usage(const char *progname) {
|
||||
fprintf(stderr, "usage: %s [options] <image> [<image> ...]\n", progname);
|
||||
fprintf(stderr, "usage: %s [options] <image> [option] [<image> ...]\n",
|
||||
progname);
|
||||
|
||||
fprintf(stderr, "Options:\n"
|
||||
"\t-C : Center images.\n"
|
||||
"\t-w<seconds> : If multiple images given: "
|
||||
|
|
@ -188,7 +194,7 @@ static int usage(const char *progname) {
|
|||
"For gif animations: stop after this time.\n"
|
||||
"\t-l<loop-count> : "
|
||||
"For gif animations: number of loops through a full cycle.\n"
|
||||
"\t-s : if multiple images are given: shuffle.\n"
|
||||
"\t-s : If multiple images are given: shuffle.\n"
|
||||
"\t-L : Large display, in which each chain is 'folded down'\n"
|
||||
"\t in the middle in an U-arrangement to get more vertical space.\n"
|
||||
"\t-R<angle> : Rotate output; steps of 90 degrees\n"
|
||||
|
|
@ -198,13 +204,30 @@ static int usage(const char *progname) {
|
|||
rgb_matrix::PrintMatrixFlags(stderr);
|
||||
|
||||
fprintf(stderr,
|
||||
"Switch time between files: "
|
||||
"\nSwitch time between files: "
|
||||
"-w for static images; -t/-l for animations\n"
|
||||
"Animated gifs: If both -l and -t are given, "
|
||||
"whatever comes first determines duration.\n");
|
||||
"whatever finishes first determines duration.\n");
|
||||
|
||||
fprintf(stderr, "\nThe -w, -t and -l options apply to the following images "
|
||||
"until a new instance of one of these options is seen.\n"
|
||||
"So you can choose different durations for different images.\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct ImageParams {
|
||||
ImageParams() : anim_duration_ms(distant_future), wait_ms(1500), loops(-1){}
|
||||
tmillis_t anim_duration_ms;
|
||||
tmillis_t wait_ms;
|
||||
int loops;
|
||||
};
|
||||
|
||||
struct FileInfo {
|
||||
ImageParams params; // Each file might have specific timing settings
|
||||
PreprocessedList frames; // For animations: possibly multiple frames.
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
Magick::InitializeMagick(*argv);
|
||||
|
||||
|
|
@ -219,23 +242,34 @@ int main(int argc, char *argv[]) {
|
|||
bool do_center = false;
|
||||
bool do_shuffle = false;
|
||||
bool large_display = false; // 64x64 made out of 4 in sequence.
|
||||
const tmillis_t distant_future = (1LL<<40); // that is a while.
|
||||
tmillis_t anim_duration_ms = distant_future;
|
||||
tmillis_t wait_ms = 1500;
|
||||
int loops = -1;
|
||||
int angle = -361;
|
||||
|
||||
// We remember ImageParams for each image, which will change whenever
|
||||
// there is a flag modifying them. This map keeps track of filenames
|
||||
// and their image params (also for unrelated elements of argv[], but doesn't
|
||||
// matter).
|
||||
// We map the pointer instad of the string of the argv parameter so that
|
||||
// we can have two times the same image on the commandline list with different
|
||||
// parameters.
|
||||
std::map<const void *, struct ImageParams> filename_params;
|
||||
|
||||
// Set defaults.
|
||||
ImageParams img_param;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
filename_params[argv[i]] = img_param;
|
||||
}
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "w:t:l:fr:c:P:LhCR:s")) != -1) {
|
||||
switch (opt) {
|
||||
case 'w':
|
||||
wait_ms = roundf(atof(optarg) * 1000.0f);
|
||||
img_param.wait_ms = roundf(atof(optarg) * 1000.0f);
|
||||
break;
|
||||
case 't':
|
||||
anim_duration_ms = roundf(atof(optarg) * 1000.0f);
|
||||
img_param.anim_duration_ms = roundf(atof(optarg) * 1000.0f);
|
||||
break;
|
||||
case 'l':
|
||||
loops = atoi(optarg);
|
||||
img_param.loops = atoi(optarg);
|
||||
break;
|
||||
case 'f':
|
||||
do_forever = true;
|
||||
|
|
@ -269,6 +303,12 @@ int main(int argc, char *argv[]) {
|
|||
default:
|
||||
return usage(argv[0]);
|
||||
}
|
||||
|
||||
// Starting from the current file, set all the remaining files to
|
||||
// the latest change.
|
||||
for (int i = optind; i < argc; ++i) {
|
||||
filename_params[argv[i]] = img_param;
|
||||
}
|
||||
}
|
||||
|
||||
const int filename_count = argc - optind;
|
||||
|
|
@ -277,17 +317,7 @@ int main(int argc, char *argv[]) {
|
|||
return usage(argv[0]);
|
||||
}
|
||||
|
||||
if (filename_count == 1) {
|
||||
wait_ms = distant_future;
|
||||
}
|
||||
else if (filename_count > 1 &&
|
||||
loops < 0 && anim_duration_ms == distant_future) {
|
||||
// More than one image but parameters for animations are default ? Set them
|
||||
// to default loop only once, otherwise the first animation would just run
|
||||
// forever, stopping all the images after it.
|
||||
loops = 1;
|
||||
}
|
||||
|
||||
// Prepare matrix
|
||||
RGBMatrix *matrix = CreateMatrixFromOptions(matrix_options, runtime_opt);
|
||||
if (matrix == NULL)
|
||||
return 1;
|
||||
|
|
@ -312,9 +342,9 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
fprintf(stderr, "Load images...\n");
|
||||
// Preparing all the images beforehand as the Pi might be too slow to
|
||||
// be quickly switching between these.
|
||||
std::vector<PreprocessedList> file_imgs;
|
||||
for (int imgarg = optind; imgarg < argc && !interrupt_received; ++imgarg) {
|
||||
// be quickly switching between these. So preprocess.
|
||||
std::vector<FileInfo*> file_imgs;
|
||||
for (int imgarg = optind; imgarg < argc; ++imgarg) {
|
||||
const char *filename = argv[imgarg];
|
||||
|
||||
std::vector<Magick::Image> image_sequence;
|
||||
|
|
@ -323,24 +353,38 @@ int main(int argc, char *argv[]) {
|
|||
continue;
|
||||
}
|
||||
|
||||
PreprocessedList frames;
|
||||
FileInfo *file_info = new FileInfo();
|
||||
file_info->params = filename_params[filename];
|
||||
// Convert to preprocessed frames.
|
||||
for (size_t i = 0; i < image_sequence.size(); ++i) {
|
||||
FrameCanvas *canvas = matrix->CreateFrameCanvas();
|
||||
frames.push_back(new PreprocessedFrame(image_sequence[i], do_center,
|
||||
canvas));
|
||||
file_info->frames.push_back(
|
||||
new PreprocessedFrame(image_sequence[i], do_center, canvas));
|
||||
}
|
||||
// The 'animation delay' of a single image is the time to the next image.
|
||||
if (frames.size() == 1)
|
||||
frames.back()->set_delay(wait_ms);
|
||||
|
||||
file_imgs.push_back(frames);
|
||||
if (file_info->frames.size() == 1) {
|
||||
file_info->frames.back()->set_delay(file_info->params.wait_ms);
|
||||
}
|
||||
file_imgs.push_back(file_info);
|
||||
}
|
||||
|
||||
// Some parameter sanity adjustments.
|
||||
if (file_imgs.empty()) {
|
||||
// e.g. if all files could not be interpreted as image.
|
||||
fprintf(stderr, "No image could be loaded.\n");
|
||||
return 1;
|
||||
} else if (file_imgs.size() == 1) {
|
||||
// Single image: show forever.
|
||||
file_imgs[0]->params.wait_ms = distant_future;
|
||||
} else {
|
||||
for (size_t i = 0; i < file_imgs.size(); ++i) {
|
||||
ImageParams ¶ms = file_imgs[i]->params;
|
||||
// Forever animation ? Set to loop only once, otherwise that animation
|
||||
// would just run forever, stopping all the images after it.
|
||||
if (params.loops < 0 && params.anim_duration_ms == distant_future) {
|
||||
params.loops = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Display.\n");
|
||||
|
|
@ -353,21 +397,23 @@ int main(int argc, char *argv[]) {
|
|||
std::random_shuffle(file_imgs.begin(), file_imgs.end());
|
||||
}
|
||||
for (size_t i = 0; i < file_imgs.size() && !interrupt_received; ++i) {
|
||||
const PreprocessedList frames = file_imgs[i];
|
||||
const FileInfo *file = file_imgs[i];
|
||||
|
||||
const tmillis_t duration = ((frames.size() == 1)
|
||||
? wait_ms
|
||||
: anim_duration_ms);
|
||||
DisplayAnimation(frames, duration , loops, matrix);
|
||||
const tmillis_t duration = ((file->frames.size() == 1)
|
||||
? file->params.wait_ms
|
||||
: file->params.anim_duration_ms);
|
||||
DisplayAnimation(file->frames, duration, file->params.loops, matrix);
|
||||
}
|
||||
} while (do_forever && !interrupt_received);
|
||||
|
||||
if (interrupt_received)
|
||||
if (interrupt_received) {
|
||||
fprintf(stderr, "Caught signal. Exiting.\n");
|
||||
}
|
||||
|
||||
// Animation finished. Shut down the RGB matrix.
|
||||
matrix->Clear();
|
||||
delete matrix;
|
||||
|
||||
// Leaking the FileInfos, but don't care at program end.
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue