@@ -310,6 +310,119 @@ void MatrixPanel_FPGA_SPI::drawFrameRGB888(const uint8_t *data, size_t length) {
310310 do_drawFrameRGB888_ (data, length);
311311}
312312
313+ void MatrixPanel_FPGA_SPI::do_drawRectRGB888_ (int16_t x, int16_t y, int16_t w,
314+ int16_t h, const uint8_t *data,
315+ size_t length) {
316+ if (!initialized) {
317+ ESP_LOGI (" drawRectRGB888()" ,
318+ " Tried to set output brightness before begin()" );
319+ return ;
320+ }
321+ if (data == nullptr ) {
322+ ESP_LOGE (" MatrixPanel_FPGA_SPI:drawRectRGB888" ,
323+ " Invalid data passed to drawRectRGB888 nullptr! (length=%d)" ,
324+ length);
325+ return ;
326+ }
327+ if (x < 0 || y < 0 || w <= 0 || h <= 0 ) {
328+ ESP_LOGE (" MatrixPanel_FPGA_SPI:drawRectRGB888" ,
329+ " Invalid rect params x=%d y=%d w=%d h=%d" , x, y, w, h);
330+ return ;
331+ }
332+ const size_t expected_len = static_cast <size_t >(w) *
333+ static_cast <size_t >(h) * 3 ;
334+ if (length != expected_len) {
335+ ESP_LOGE (" MatrixPanel_FPGA_SPI:drawRectRGB888" ,
336+ " Invalid data length=%d expected=%d" , length, expected_len);
337+ return ;
338+ }
339+ SpiLockGuard spi_lock (this );
340+ if (!spi_lock.locked ())
341+ return ;
342+ if (!wait_for_fpga_resetstatus_ ())
343+ return ;
344+
345+ uint8_t header[7 ];
346+ uint8_t header_len = 0 ;
347+ header[header_len++] = ' X' ; // Command byte
348+ if (PIXELS_PER_ROW <= 0xff ) {
349+ header[header_len++] = static_cast <uint8_t >(x);
350+ } else {
351+ header[header_len++] = static_cast <uint8_t >((x >> 8 ) & 0xFF );
352+ header[header_len++] = static_cast <uint8_t >(x & 0xFF );
353+ }
354+ header[header_len++] = static_cast <uint8_t >(y);
355+ if (PIXELS_PER_ROW <= 0xff ) {
356+ header[header_len++] = static_cast <uint8_t >(w);
357+ } else {
358+ header[header_len++] = static_cast <uint8_t >((w >> 8 ) & 0xFF );
359+ header[header_len++] = static_cast <uint8_t >(w & 0xFF );
360+ }
361+ header[header_len++] = static_cast <uint8_t >(h);
362+
363+ spi_transaction_t t = {
364+ .length = (size_t )(8 * header_len), // bits
365+ .tx_buffer = header,
366+ };
367+ esp_err_t err = spi_device_transmit (spi_bus, &t);
368+ if (err != ESP_OK ) {
369+ ESP_LOGE (" MatrixPanel_FPGA_SPI:drawRectRGB888" ,
370+ " SPI transmit failed: %s" , esp_err_to_name (err));
371+ return ;
372+ }
373+
374+ const size_t chunk_bytes =
375+ std::min (static_cast <size_t >(SPI_MAX_DMA_LEN ), length);
376+ uint8_t *buf =
377+ static_cast <uint8_t *>(heap_caps_malloc (chunk_bytes, MALLOC_CAP_DMA ));
378+ if (buf == nullptr ) {
379+ ESP_LOGE (" MatrixPanel_FPGA_SPI:drawRectRGB888" ,
380+ " DMA alloc failed for rect chunk (%u bytes)" ,
381+ static_cast <unsigned >(chunk_bytes));
382+ return ;
383+ }
384+
385+ size_t offset = 0 ;
386+ while (offset < length) {
387+ const size_t chunk = std::min (length - offset, chunk_bytes);
388+ memcpy (buf, data + offset, chunk);
389+ spi_transaction_t t2 = {
390+ .length = static_cast <size_t >(chunk * 8 ), // bits
391+ .tx_buffer = buf,
392+ };
393+ esp_err_t err2 = spi_device_transmit (spi_bus, &t2);
394+ if (err2 != ESP_OK ) {
395+ ESP_LOGE (" MatrixPanel_FPGA_SPI:drawRectRGB888" ,
396+ " SPI transmit failed: %s" , esp_err_to_name (err2));
397+ break ;
398+ }
399+ offset += chunk;
400+ }
401+
402+ heap_caps_free (buf);
403+ wait_for_fpga_busy_clear_ ();
404+ }
405+
406+ void MatrixPanel_FPGA_SPI::drawRectRGB888 (int16_t x, int16_t y, int16_t w,
407+ int16_t h, const uint8_t *data,
408+ size_t length) {
409+ if (use_worker_) {
410+ if (!tx_q_ || !tx_task_)
411+ return ;
412+ Job j;
413+ j.op = Op::DRAW_RECT ;
414+ j.x = x;
415+ j.y = static_cast <uint8_t >(y);
416+ j.w = w;
417+ j.h = h;
418+ j.data = data;
419+ j.length = length;
420+ (void )xQueueSend (tx_q_, &j, 0 );
421+ return ;
422+ }
423+ do_drawRectRGB888_ (x, y, w, h, data, length);
424+ }
425+
313426void MatrixPanel_FPGA_SPI::do_drawRowRGB888_ (const uint8_t y,
314427 const uint8_t *data,
315428 size_t length) {
@@ -730,6 +843,11 @@ void MatrixPanel_FPGA_SPI::run_test_graphic(uint32_t delay_ms) {
730843 // - blue center band via fillRect
731844 // - yellow left column, magenta right column for edge accents
732845 // - opposing diagonals (orange/white) to cover drawPixelRGB888
846+ // - centered drawRectRGB888 rect: size=max(4, width/5)xmax(4, height/3)
847+ // BLACK GRADIANT RED
848+ // .............GRADIANT..............
849+ // .............GRADIANT..............
850+ // TEAL/GREEN GRADIANT YELLOW
733851 // - brightness pushed to 255 and the frame swapped to display the pattern
734852 clearScreen ();
735853 delay_if_needed ();
@@ -781,6 +899,34 @@ void MatrixPanel_FPGA_SPI::run_test_graphic(uint32_t delay_ms) {
781899 wait_for_worker_idle ();
782900 delay_if_needed ();
783901
902+ // Buffered rectangle: gradient checker to exercise drawRectRGB888.
903+ const int rect_w = std::max (4 , width / 5 );
904+ const int rect_h = std::max (4 , height / 3 );
905+ const int rect_x = std::max (0 , (width - rect_w) / 2 );
906+ const int rect_y = std::max (0 , (height - rect_h) / 2 );
907+ const int rect_w_denom = std::max (1 , rect_w - 1 );
908+ const int rect_h_denom = std::max (1 , rect_h - 1 );
909+ std::vector<uint8_t > rect_buf (
910+ static_cast <size_t >(rect_w) * static_cast <size_t >(rect_h) * 3 );
911+ for (int y = 0 ; y < rect_h; ++y) {
912+ for (int x = 0 ; x < rect_w; ++x) {
913+ const size_t idx =
914+ (static_cast <size_t >(y) * rect_w + x) * 3 ;
915+ const uint8_t r = static_cast <uint8_t >(
916+ (x * 255 ) / rect_w_denom);
917+ const uint8_t g = static_cast <uint8_t >(
918+ (y * 255 ) / rect_h_denom);
919+ const uint8_t b = ((x + y) & 1 ) ? 0x20 : 0xA0 ;
920+ rect_buf[idx] = r;
921+ rect_buf[idx + 1 ] = g;
922+ rect_buf[idx + 2 ] = b;
923+ }
924+ }
925+ drawRectRGB888 (rect_x, rect_y, rect_w, rect_h, rect_buf.data (),
926+ rect_buf.size ());
927+ wait_for_worker_idle ();
928+ delay_if_needed ();
929+
784930 // Diagonals: orange from top-left and white from top-right.
785931 const int diag_length = std::min (width, height);
786932 const int diag_step = std::max (1 , diag_length / 16 );
0 commit comments