canvas.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  4. *
  5. * Based on Sprinter and grbl.
  6. * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. *
  21. */
  22. #include "../../inc/MarlinConfig.h"
  23. #if HAS_GRAPHICAL_TFT
  24. #include "canvas.h"
  25. uint16_t Canvas::width, Canvas::height;
  26. uint16_t Canvas::startLine, Canvas::endLine;
  27. uint16_t Canvas::background_color;
  28. uint16_t *Canvas::buffer = TFT::buffer;
  29. void Canvas::instantiate(uint16_t x, uint16_t y, uint16_t width, uint16_t height) {
  30. Canvas::width = width;
  31. Canvas::height = height;
  32. startLine = 0;
  33. endLine = 0;
  34. // The TFT handles DMA within the given canvas rectangle
  35. // so whatever is drawn will be offset on the screen by x,y.
  36. tft.set_window(x, y, x + width - 1, y + height - 1);
  37. }
  38. void Canvas::next() {
  39. startLine = endLine;
  40. endLine = (TFT_BUFFER_WORDS) < width * (height - startLine) ? startLine + (TFT_BUFFER_WORDS) / width : height;
  41. }
  42. bool Canvas::toScreen() {
  43. tft.write_sequence(buffer, width * (endLine - startLine));
  44. return endLine == height;
  45. }
  46. void Canvas::setBackground(uint16_t color) {
  47. /* TODO: test and optimize performance */
  48. /*
  49. uint32_t count = (endLine - startLine) * width;
  50. uint16_t *pixel = buffer;
  51. while (count--)
  52. *pixel++ = color;
  53. */
  54. const uint32_t two_pixels = (((uint32_t )color) << 16) | color;
  55. uint32_t count = ((endLine - startLine) * width + 1) >> 1;
  56. uint32_t *pointer = (uint32_t *)buffer;
  57. while (count--) *pointer++ = two_pixels;
  58. background_color = color;
  59. }
  60. extern uint16_t gradient(uint16_t colorA, uint16_t colorB, uint16_t factor);
  61. void Canvas::addText(uint16_t x, uint16_t y, uint16_t color, uint16_t *string, uint16_t maxWidth) {
  62. if (endLine < y || startLine > y + getFontHeight()) return;
  63. if (maxWidth == 0) maxWidth = width - x;
  64. uint16_t colors[16];
  65. uint16_t stringWidth = 0;
  66. if (getFontType() == FONT_MARLIN_GLYPHS_2BPP) {
  67. for (uint8_t i = 0; i < 3; i++) {
  68. colors[i] = gradient(ENDIAN_COLOR(color), ENDIAN_COLOR(background_color), ((i+1) << 8) / 3);
  69. colors[i] = ENDIAN_COLOR(colors[i]);
  70. }
  71. }
  72. for (uint16_t i = 0 ; *(string + i) ; i++) {
  73. glyph_t *pGlyph = glyph(string + i);
  74. if (stringWidth + pGlyph->bbxWidth > maxWidth) break;
  75. switch (getFontType()) {
  76. case FONT_MARLIN_GLYPHS_1BPP:
  77. addImage(x + stringWidth + pGlyph->bbxOffsetX, y + getFontAscent() - pGlyph->bbxHeight - pGlyph->bbxOffsetY, pGlyph->bbxWidth, pGlyph->bbxHeight, GREYSCALE1, ((uint8_t *)pGlyph) + sizeof(glyph_t), &color);
  78. break;
  79. case FONT_MARLIN_GLYPHS_2BPP:
  80. addImage(x + stringWidth + pGlyph->bbxOffsetX, y + getFontAscent() - pGlyph->bbxHeight - pGlyph->bbxOffsetY, pGlyph->bbxWidth, pGlyph->bbxHeight, GREYSCALE2, ((uint8_t *)pGlyph) + sizeof(glyph_t), colors);
  81. break;
  82. }
  83. stringWidth += pGlyph->dWidth;
  84. }
  85. }
  86. void Canvas::addImage(int16_t x, int16_t y, MarlinImage image, uint16_t *colors) {
  87. uint16_t *data = (uint16_t *)images[image].data;
  88. if (!data) return;
  89. const uint16_t image_width = images[image].width,
  90. image_height = images[image].height;
  91. colorMode_t color_mode = images[image].colorMode;
  92. if (color_mode == HIGHCOLOR) {
  93. // HIGHCOLOR - 16 bits per pixel
  94. int16_t line = y;
  95. for (int16_t i = 0; i < image_height; i++, line++) {
  96. if (WITHIN(line, startLine, endLine - 1)) {
  97. uint16_t *pixel = buffer + x + (line - startLine) * width;
  98. uint16_t cx = x;
  99. for (int16_t j = 0; j < image_width; j++, cx++) {
  100. if (WITHIN(cx, 0, width - 1)) {
  101. uint16_t color = ENDIAN_COLOR(*data);
  102. if (color == 0x0001) color = COLOR_BACKGROUND;
  103. *pixel = color;
  104. }
  105. pixel++;
  106. data++;
  107. }
  108. }
  109. else
  110. data += image_width;
  111. }
  112. return;
  113. }
  114. #if ENABLED(COMPACT_MARLIN_BOOT_LOGO)
  115. // RLE16 HIGHCOLOR - 16 bits per pixel
  116. if (color_mode == RLE16) {
  117. uint8_t *bytedata = (uint8_t *)images[image].data;
  118. if (!bytedata) return;
  119. // Loop through the image data advancing the row and column as needed
  120. int16_t srcy = 0, srcx = 0, // Image data line / column index
  121. dsty = y, dstx = x; // Destination line / column index
  122. uint16_t color = 0; // Persist the last fetched color value
  123. bool done = false;
  124. while (!done) {
  125. uint8_t count = *bytedata++; // Get the count byte
  126. const bool uniq = bool(count & 0x80); // >= 128 is a distinct run; < 128 is a repeat run
  127. count = (count & 0x7F) + 1; // Actual count is 7-bit plus 1
  128. bool getcol = true; // Get at least one color word
  129. while (count--) { // Emit 'count' pixels
  130. if (getcol) {
  131. getcol = uniq; // Keep getting colors if not RLE
  132. const uint16_t msb = *bytedata++, // Color most-significant bits
  133. lsb = *bytedata++; // Color least-significant bits
  134. color = ENDIAN_COLOR((msb << 8) | lsb); // Color with proper endianness
  135. if (color == 0x0001) color = COLOR_BACKGROUND; // 0x0001 is "transparent"
  136. }
  137. if (WITHIN(dsty, startLine, endLine - 1)) { // Dest pixel Y within the segment?
  138. if (WITHIN(dstx, 0, width - 1)) { // Dest pixel X within the canvas?
  139. uint16_t * const pixel = buffer + dstx + (dsty - startLine) * width;
  140. *pixel = color; // Store the color in the pixel
  141. }
  142. }
  143. ++srcx; ++dstx; // Advance the pixel column
  144. if (srcx >= image_width) { // Past the right edge of the source image?
  145. ++srcy; ++dsty; // Advance to the next line
  146. srcx = 0; dstx = x; // May be shifted within the canvas, but usually not
  147. if (dsty >= endLine || srcy >= image_height) { // Done with the segment or the image?
  148. done = true; // Set a flag to end the loop...
  149. break; // ...and break out of while(count--)
  150. }
  151. }
  152. }
  153. }
  154. return;
  155. }
  156. #endif // COMPACT_MARLIN_BOOT_LOGO
  157. addImage(x, y, image_width, image_height, color_mode, (uint8_t *)data, colors);
  158. }
  159. void Canvas::addImage(int16_t x, int16_t y, uint8_t image_width, uint8_t image_height, colorMode_t color_mode, uint8_t *data, uint16_t *colors) {
  160. uint8_t bitsPerPixel;
  161. switch (color_mode) {
  162. case GREYSCALE1: bitsPerPixel = 1; break;
  163. case GREYSCALE2: bitsPerPixel = 2; break;
  164. case GREYSCALE4: bitsPerPixel = 4; break;
  165. default: return;
  166. }
  167. uint8_t mask = 0xFF >> (8 - bitsPerPixel),
  168. pixelsPerByte = 8 / bitsPerPixel;
  169. colors--;
  170. for (int16_t i = 0; i < image_height; i++) {
  171. const int16_t line = y + i;
  172. if (WITHIN(line, startLine, endLine - 1)) {
  173. uint16_t *pixel = buffer + x + (line - startLine) * width;
  174. uint8_t offset = 8 - bitsPerPixel;
  175. for (int16_t j = 0; j < image_width; j++) {
  176. if (offset > 8) {
  177. data++;
  178. offset = 8 - bitsPerPixel;
  179. }
  180. if (WITHIN(x + j, 0, width - 1)) {
  181. const uint8_t color = ((*data) >> offset) & mask;
  182. if (color) *pixel = *(colors + color);
  183. }
  184. pixel++;
  185. offset -= bitsPerPixel;
  186. }
  187. data++;
  188. }
  189. else
  190. data += (image_width + pixelsPerByte - 1) / pixelsPerByte;
  191. }
  192. }
  193. void Canvas::addRect(uint16_t x, uint16_t y, uint16_t rectangleWidth, uint16_t rectangleHeight, uint16_t color) {
  194. if (endLine < y || startLine > y + rectangleHeight) return;
  195. for (uint16_t i = 0; i < rectangleHeight; i++) {
  196. const uint16_t line = y + i;
  197. if (WITHIN(line, startLine, endLine - 1)) {
  198. uint16_t *pixel = buffer + x + (line - startLine) * width;
  199. if (i == 0 || i == rectangleHeight - 1) {
  200. for (uint16_t j = 0; j < rectangleWidth; j++) *pixel++ = color;
  201. }
  202. else {
  203. *pixel = color;
  204. pixel += rectangleWidth - 1;
  205. *pixel = color;
  206. }
  207. }
  208. }
  209. }
  210. void Canvas::addBar(uint16_t x, uint16_t y, uint16_t barWidth, uint16_t barHeight, uint16_t color) {
  211. if (endLine < y || startLine > y + barHeight) return;
  212. for (uint16_t i = 0; i < barHeight; i++) {
  213. const uint16_t line = y + i;
  214. if (WITHIN(line, startLine, endLine - 1)) {
  215. uint16_t *pixel = buffer + x + (line - startLine) * width;
  216. for (uint16_t j = 0; j < barWidth; j++) *pixel++ = color;
  217. }
  218. }
  219. }
  220. Canvas tftCanvas;
  221. #endif // HAS_GRAPHICAL_TFT