// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*- // // Example how to display an image, including animated images using // ImageMagick. For a full utility that does a few more things, have a look // at the led-image-viewer in ../utils // // Showing an image is not so complicated, essentially just copy all the // pixels to the canvas. How to get the pixels ? In this example we're using // the graphicsmagick library as universal image loader library that // can also deal with animated images. // You can of course do your own image loading or use some other library. // // 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 // make image-example #include "led-matrix.h" #include #include #include #include #include #include #include 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; // Given the filename, load the image and scale to the size of the // matrix. // // If this is an animated image, the resutlting vector will contain multiple. 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 an image to a Canvas. Note, the RGBMatrix is implementing the Canvas // interface as well as the FrameCanvas we use in the double-buffering of the // animted image. void CopyImageToCanvas(const Magick::Image &image, Canvas *canvas) { const int offset_x = 0, offset_y = 0; // If you want to move the image. // Copy all the pixels to the canvas. for (size_t y = 0; y < image.rows(); ++y) { for (size_t x = 0; x < image.columns(); ++x) { const Magick::Color &c = image.pixelColor(x, y); if (c.alphaQuantum() < 256) { canvas->SetPixel(x + offset_x, y + offset_y, ScaleQuantumToChar(c.redQuantum()), ScaleQuantumToChar(c.greenQuantum()), ScaleQuantumToChar(c.blueQuantum())); } } } } // An animated image has to constantly swap to the next frame. // We're using double-buffering and fill an offscreen buffer first, then show. void ShowAnimatedImage(const ImageVector &images, RGBMatrix *matrix) { FrameCanvas *offscreen_canvas = matrix->CreateFrameCanvas(); while (!interrupt_received) { for (const auto &image : images) { if (interrupt_received) break; CopyImageToCanvas(image, offscreen_canvas); offscreen_canvas = matrix->SwapOnVSync(offscreen_canvas); usleep(image.animationDelay() * 10000); // 1/100s converted to usec } } } int usage(const char *progname) { fprintf(stderr, "Usage: %s [led-matrix-options] \n", progname); rgb_matrix::PrintMatrixFlags(stderr); return 1; } int main(int argc, char *argv[]) { Magick::InitializeMagick(*argv); // Initialize the RGB matrix with RGBMatrix::Options matrix_options; rgb_matrix::RuntimeOptions runtime_opt; if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv, &matrix_options, &runtime_opt)) { return usage(argv[0]); } if (argc != 2) return usage(argv[0]); const char *filename = argv[1]; signal(SIGTERM, InterruptHandler); signal(SIGINT, InterruptHandler); RGBMatrix *matrix = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt); if (matrix == NULL) return 1; ImageVector images = LoadImageAndScaleImage(filename, matrix->width(), matrix->height()); switch (images.size()) { case 0: // failed to load image. break; case 1: // Simple example: one image to show CopyImageToCanvas(images[0], matrix); while (!interrupt_received) sleep(1000); // Until Ctrl-C is pressed break; default: // More than one image: this is an animation. ShowAnimatedImage(images, matrix); break; } matrix->Clear(); delete matrix; return 0; }