Open shanerooni opened 4 years ago
The simplest case of course is to just offset whatever you want to show (e.g. text) in the other region; but I can see how it would be conceptually simpler to just think of multiple regions to be 'windows' on the big canvas.
If you want to think in terms of 'windows', I suggest to make a delegating canvas. A class that implements the canvas interface and for instance retuns a particular size it has (width() and height() impelementation).
Then in SetPixel() it just delegates the call to the underlying canvas (e.g. the matrix canvas), and can apply an offset to it.
A c++ implementation would roughly look like this
class WindowCanvas : public rgb_matrix::Canvas {
public:
WindowCanvas(rgb_matrix::Canvas *delegatee,
int width, int height,
int offset_x, int offset_y)
: delegatee_(delegatee), width_(width), height_(height),
offset_x_(offset_x), offset_y_(offset_y) {}
virtual int width() const { return width_; }
virtual int height() const { return height_; }
virtual void SetPixel(int x, int y, uint8_t r, uint8_t g, uint8_t b) {
if (x < 0 || x > width_ || y < 0 || y > height_) return; // do clipping
delegatee_->SetPixel(x + offset_x_, y + offset_y_, r, g, b);
}
virtual void Clear() { delegatee_->Clear(); }
virtual void Fill(uint8_t r, uint8_t g, uint8_t b) {
delegatee_->Fill(r, g, b);
}
private:
rgb_matrix::Canvas *const delegatee_;
const int width_;
const int height_;
const int offset_x_;
const int offset_y_;
};
Now, you can use that; say the frame canvas from your is rgbmatrix_canvas
You now can do
Canvas *left_window = new WindowCanvas(rgbmatrix_window, 32, 32, 0, 0);
Canvas *right_window = new WindowCanvas(rgbmatrix_wqindow, 96, 32, 32, 0);
And can regard each canvas as independent 'window' that also does clipping properly.
--led-multiplexing
, and why you need it for some panels is described in the README.
HI, I'm trying the "windows" approach and made some changes to the text-scroller example, but I got stuck when trying to swap the left_window on vsync:
left_window = canvas->SwapOnVSync(left_window);
The functions expects a FrameCanvas.
Any help would be appreciated, Thanks
Yes, you have to swap the whole FrameCanvas. This is the underlying hardware buffer.
So what you would do is to change all your Windows for the next animation step, and then swap the underlying canvas.
Thank you very much. I’ll will give it a try tomorrow!
Hi, implemented a draft of the textscroller example with "windows":
RGBMatrix *canvas = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt); if (canvas == NULL) return 1;
const bool all_extreme_colors = (matrix_options.brightness == 100) && FullSaturation(color) && FullSaturation(bg_color) && FullSaturation(outline_color); if (all_extreme_colors) canvas->SetPWMBits(1);
signal(SIGTERM, InterruptHandler); signal(SIGINT, InterruptHandler);`
printf("CTRL-C for exit.\n");
// Create a new canvas to be used with led_matrix_swap_on_vsync FrameCanvas offscreen_canvas = canvas->CreateFrameCanvas(); Canvas left_window = new WindowCanvas(offscreen_canvas, 32, 32, 0, 0);
const int scroll_direction = (speed >= 0) ? -1 : 1; speed = fabs(speed); int delay_speed_usec = 1000000; if (speed > 0) { delay_speed_usec = 1000000 / speed / font.CharacterWidth('W'); }
if (!xorigin_configured) { if (speed == 0) { // There would be no scrolling, so text would never appear. Move to front. x_orig = with_outline ? 1 : 0; } else { x_orig = scroll_direction < 0 ? canvas->width() : 0; } }
int x = x_orig; int y = y_orig; int length = 0;
struct timespec next_frame = {0, 0};
uint frame_counter = 0; while (!interrupt_received && loops != 0) { ++frame_counter; left_window->Fill(bg_color.r, bg_color.g, bg_color.b); const bool draw_on_frame = (blink_on <= 0) || (frame_counter % (blink_on + blink_off) < (uint)blink_on);
if (draw_on_frame) {
if (outline_font) {
// The outline font, we need to write with a negative (-2) text-spacing,
// as we want to have the same letter pitch as the regular text that
// we then write on top.
rgb_matrix::DrawText(left_window, *outline_font,
x - 1, y + font.baseline(),
outline_color, NULL,
line.c_str(), letter_spacing - 2);
}
// length = holds how many pixels our text takes up
length = rgb_matrix::DrawText(left_window, font,
x, y + font.baseline(),
color, NULL,
line.c_str(), letter_spacing);
}
x += scroll_direction;
if ((scroll_direction < 0 && x + length < 0) ||
(scroll_direction > 0 && x > canvas->width())) {
x = x_orig + ((scroll_direction > 0) ? -length : 0);
if (loops > 0) --loops;
}
// Make sure render-time delays are not influencing scroll-time
if (speed > 0) {
if (next_frame.tv_sec == 0) {
// First time. Start timer, but don't wait.
clock_gettime(CLOCK_MONOTONIC, &next_frame);
} else {
add_micros(&next_frame, delay_speed_usec);
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_frame, NULL);
}
}
// Swap the offscreen_canvas with canvas on vsync, avoids flickering
offscreen_canvas = canvas->SwapOnVSync(offscreen_canvas);
if (speed <= 0) pause(); // Nothing to scroll.
}
// Finished. Shut down the RGB matrix. canvas->Clear(); delete canvas;
return 0; }
but there's a lot of flickering. Any idea?
thanks,
The finished class:
class WindowCanvas : public rgb_matrix::Canvas {
public:
virtual ~WindowCanvas () {}
WindowCanvas (rgb_matrix::Canvas *delegatee,int width, int height,int offset_x, int offset_y): delegatee_(delegatee), width_(width), height_(height),offset_x_(offset_x), offset_y_(offset_y) {}
virtual int width() const {
return width_;
}
virtual int height() const {
return height_;
}
// Set pixel at coordinate (x,y) with given color. Pixel (0,0) is the
// top left corner.
// Each color is 8 bit (24bpp), 0 black, 255 brightest.
virtual void SetPixel(int x, int y, uint8_t r, uint8_t g, uint8_t b) {
if (x < 0 || x > width_ || y < 0 || y > height_) return; // do clipping
delegatee_->SetPixel(x + offset_x_, y + offset_y_, r, g, b);
}
// Clear screen to be all black.
virtual void Clear() {
delegatee_->Clear();
return;
}
// Fill screen with given 24bpp color.
// note: this will fill all screen since its not limited to the canvas size
virtual void Fill(uint8_t r, uint8_t g, uint8_t b) {
delegatee_->Fill(r, g, b);
return;
}
private:
rgb_matrix::Canvas *const delegatee_;
const int width_;
const int height_;
const int offset_x_;
const int offset_y_;
};
I'm fairly new to the pi 4, adafruit hat, and have only done some html5+js+css programming. i'm trying to make a large 3x4 matrix of 32x64 panels to display a huge finish line stopwatch for a kids running series where i'm a volunteer.
i've done well to get my 2 test displays to work well while waiting for the other 12 displays to arrive. the learning curve has been steep, but the examples have been great. there are a few questions i have, but the multiple canvi is the main issue.
first, i can see how a canvas is declared, and have had no issues manipulating it the way i want to display or scroll text, run the stopwatch, etc. however, i believe i'll have extra space on the display to show other information. am i able to, in the case of my 32x128 display, create two canvi and define them so that one is - let's say - 32x32 on the far left, and the other is 32x96 on the right?
here are a few other questions somewhat unrelated, but i didn't want to open a bunch of strings just to have some basic questions answered. i know you're busy, so feel free to only address the multi-canvas issue if possible.
for my particular panels to work, i have to use --led-multiplexing=1. i have no idea why this works, and i only found out because someone on amazon that bought the same panels put it in their review. would you be able to briefly explain why i need this tag? (otherwise whatever is on my display is repeated and garbled)
i will be out in a cross-country field for the running series. am i able to connect my phone via bluetooth or wifi to the pi and send it commands (start, stop, reset, scrolling messages, etc.). i was going to try to create a simple app with the pre-loaded commands that i need for easy access. my android phone has hotspot if that is required, but i won't be able to connect the phone and pi to the same "home" wifi like i have set up in my home office.
the bdf font has been giving me some headaches. is there a way to scale fonts or easily create my own? i'm thinking when i have the full 3x4 matrix, i will need fonts that are at least ~70 pixels high. i can't find any existing fonts that large, but it seems like a waste of time to painstakingly create my own custom-sized font.
i sincerely appreciate any help you are able to provide.