Adafruit_GFX.cpp (93575B)
1 /* 2 This is the core graphics library for all our displays, providing a common 3 set of graphics primitives (points, lines, circles, etc.). It needs to be 4 paired with a hardware-specific library for each display device we carry 5 (to handle the lower-level functions). 6 7 Adafruit invests time and resources providing this open source code, please 8 support Adafruit & open-source hardware by purchasing products from Adafruit! 9 10 Copyright (c) 2013 Adafruit Industries. All rights reserved. 11 12 Redistribution and use in source and binary forms, with or without 13 modification, are permitted provided that the following conditions are met: 14 15 - Redistributions of source code must retain the above copyright notice, 16 this list of conditions and the following disclaimer. 17 - Redistributions in binary form must reproduce the above copyright notice, 18 this list of conditions and the following disclaimer in the documentation 19 and/or other materials provided with the distribution. 20 21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include "Adafruit_GFX.h" 35 #include "glcdfont.c" 36 #ifdef __AVR__ 37 #include <avr/pgmspace.h> 38 #elif defined(ESP8266) || defined(ESP32) 39 #include <pgmspace.h> 40 #endif 41 42 // Many (but maybe not all) non-AVR board installs define macros 43 // for compatibility with existing PROGMEM-reading AVR code. 44 // Do our own checks and defines here for good measure... 45 46 #ifndef pgm_read_byte 47 #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) 48 #endif 49 #ifndef pgm_read_word 50 #define pgm_read_word(addr) (*(const unsigned short *)(addr)) 51 #endif 52 #ifndef pgm_read_dword 53 #define pgm_read_dword(addr) (*(const unsigned long *)(addr)) 54 #endif 55 56 // Pointers are a peculiar case...typically 16-bit on AVR boards, 57 // 32 bits elsewhere. Try to accommodate both... 58 59 #if !defined(__INT_MAX__) || (__INT_MAX__ > 0xFFFF) 60 #define pgm_read_pointer(addr) ((void *)pgm_read_dword(addr)) 61 #else 62 #define pgm_read_pointer(addr) ((void *)pgm_read_word(addr)) 63 #endif 64 65 inline GFXglyph *pgm_read_glyph_ptr(const GFXfont *gfxFont, uint8_t c) { 66 #ifdef __AVR__ 67 return &(((GFXglyph *)pgm_read_pointer(&gfxFont->glyph))[c]); 68 #else 69 // expression in __AVR__ section may generate "dereferencing type-punned 70 // pointer will break strict-aliasing rules" warning In fact, on other 71 // platforms (such as STM32) there is no need to do this pointer magic as 72 // program memory may be read in a usual way So expression may be simplified 73 return gfxFont->glyph + c; 74 #endif //__AVR__ 75 } 76 77 inline uint8_t *pgm_read_bitmap_ptr(const GFXfont *gfxFont) { 78 #ifdef __AVR__ 79 return (uint8_t *)pgm_read_pointer(&gfxFont->bitmap); 80 #else 81 // expression in __AVR__ section generates "dereferencing type-punned pointer 82 // will break strict-aliasing rules" warning In fact, on other platforms (such 83 // as STM32) there is no need to do this pointer magic as program memory may 84 // be read in a usual way So expression may be simplified 85 return gfxFont->bitmap; 86 #endif //__AVR__ 87 } 88 89 #ifndef min 90 #define min(a, b) (((a) < (b)) ? (a) : (b)) 91 #endif 92 93 #ifndef _swap_int16_t 94 #define _swap_int16_t(a, b) \ 95 { \ 96 int16_t t = a; \ 97 a = b; \ 98 b = t; \ 99 } 100 #endif 101 102 /**************************************************************************/ 103 /*! 104 @brief Instatiate a GFX context for graphics! Can only be done by a 105 superclass 106 @param w Display width, in pixels 107 @param h Display height, in pixels 108 */ 109 /**************************************************************************/ 110 Adafruit_GFX::Adafruit_GFX(int16_t w, int16_t h) : WIDTH(w), HEIGHT(h) { 111 _width = WIDTH; 112 _height = HEIGHT; 113 rotation = 0; 114 cursor_y = cursor_x = 0; 115 textsize_x = textsize_y = 1; 116 textcolor = textbgcolor = 0xFFFF; 117 wrap = true; 118 _cp437 = false; 119 gfxFont = NULL; 120 } 121 122 /**************************************************************************/ 123 /*! 124 @brief Write a line. Bresenham's algorithm - thx wikpedia 125 @param x0 Start point x coordinate 126 @param y0 Start point y coordinate 127 @param x1 End point x coordinate 128 @param y1 End point y coordinate 129 @param color 16-bit 5-6-5 Color to draw with 130 */ 131 /**************************************************************************/ 132 void Adafruit_GFX::writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, 133 uint16_t color) { 134 #if defined(ESP8266) 135 yield(); 136 #endif 137 int16_t steep = abs(y1 - y0) > abs(x1 - x0); 138 if (steep) { 139 _swap_int16_t(x0, y0); 140 _swap_int16_t(x1, y1); 141 } 142 143 if (x0 > x1) { 144 _swap_int16_t(x0, x1); 145 _swap_int16_t(y0, y1); 146 } 147 148 int16_t dx, dy; 149 dx = x1 - x0; 150 dy = abs(y1 - y0); 151 152 int16_t err = dx / 2; 153 int16_t ystep; 154 155 if (y0 < y1) { 156 ystep = 1; 157 } else { 158 ystep = -1; 159 } 160 161 for (; x0 <= x1; x0++) { 162 if (steep) { 163 writePixel(y0, x0, color); 164 } else { 165 writePixel(x0, y0, color); 166 } 167 err -= dy; 168 if (err < 0) { 169 y0 += ystep; 170 err += dx; 171 } 172 } 173 } 174 175 /**************************************************************************/ 176 /*! 177 @brief Start a display-writing routine, overwrite in subclasses. 178 */ 179 /**************************************************************************/ 180 void Adafruit_GFX::startWrite() {} 181 182 /**************************************************************************/ 183 /*! 184 @brief Write a pixel, overwrite in subclasses if startWrite is defined! 185 @param x x coordinate 186 @param y y coordinate 187 @param color 16-bit 5-6-5 Color to fill with 188 */ 189 /**************************************************************************/ 190 void Adafruit_GFX::writePixel(int16_t x, int16_t y, uint16_t color) { 191 drawPixel(x, y, color); 192 } 193 194 /**************************************************************************/ 195 /*! 196 @brief Write a perfectly vertical line, overwrite in subclasses if 197 startWrite is defined! 198 @param x Top-most x coordinate 199 @param y Top-most y coordinate 200 @param h Height in pixels 201 @param color 16-bit 5-6-5 Color to fill with 202 */ 203 /**************************************************************************/ 204 void Adafruit_GFX::writeFastVLine(int16_t x, int16_t y, int16_t h, 205 uint16_t color) { 206 // Overwrite in subclasses if startWrite is defined! 207 // Can be just writeLine(x, y, x, y+h-1, color); 208 // or writeFillRect(x, y, 1, h, color); 209 drawFastVLine(x, y, h, color); 210 } 211 212 /**************************************************************************/ 213 /*! 214 @brief Write a perfectly horizontal line, overwrite in subclasses if 215 startWrite is defined! 216 @param x Left-most x coordinate 217 @param y Left-most y coordinate 218 @param w Width in pixels 219 @param color 16-bit 5-6-5 Color to fill with 220 */ 221 /**************************************************************************/ 222 void Adafruit_GFX::writeFastHLine(int16_t x, int16_t y, int16_t w, 223 uint16_t color) { 224 // Overwrite in subclasses if startWrite is defined! 225 // Example: writeLine(x, y, x+w-1, y, color); 226 // or writeFillRect(x, y, w, 1, color); 227 drawFastHLine(x, y, w, color); 228 } 229 230 /**************************************************************************/ 231 /*! 232 @brief Write a rectangle completely with one color, overwrite in 233 subclasses if startWrite is defined! 234 @param x Top left corner x coordinate 235 @param y Top left corner y coordinate 236 @param w Width in pixels 237 @param h Height in pixels 238 @param color 16-bit 5-6-5 Color to fill with 239 */ 240 /**************************************************************************/ 241 void Adafruit_GFX::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, 242 uint16_t color) { 243 // Overwrite in subclasses if desired! 244 fillRect(x, y, w, h, color); 245 } 246 247 /**************************************************************************/ 248 /*! 249 @brief End a display-writing routine, overwrite in subclasses if 250 startWrite is defined! 251 */ 252 /**************************************************************************/ 253 void Adafruit_GFX::endWrite() {} 254 255 /**************************************************************************/ 256 /*! 257 @brief Draw a perfectly vertical line (this is often optimized in a 258 subclass!) 259 @param x Top-most x coordinate 260 @param y Top-most y coordinate 261 @param h Height in pixels 262 @param color 16-bit 5-6-5 Color to fill with 263 */ 264 /**************************************************************************/ 265 void Adafruit_GFX::drawFastVLine(int16_t x, int16_t y, int16_t h, 266 uint16_t color) { 267 startWrite(); 268 writeLine(x, y, x, y + h - 1, color); 269 endWrite(); 270 } 271 272 /**************************************************************************/ 273 /*! 274 @brief Draw a perfectly horizontal line (this is often optimized in a 275 subclass!) 276 @param x Left-most x coordinate 277 @param y Left-most y coordinate 278 @param w Width in pixels 279 @param color 16-bit 5-6-5 Color to fill with 280 */ 281 /**************************************************************************/ 282 void Adafruit_GFX::drawFastHLine(int16_t x, int16_t y, int16_t w, 283 uint16_t color) { 284 startWrite(); 285 writeLine(x, y, x + w - 1, y, color); 286 endWrite(); 287 } 288 289 /**************************************************************************/ 290 /*! 291 @brief Fill a rectangle completely with one color. Update in subclasses if 292 desired! 293 @param x Top left corner x coordinate 294 @param y Top left corner y coordinate 295 @param w Width in pixels 296 @param h Height in pixels 297 @param color 16-bit 5-6-5 Color to fill with 298 */ 299 /**************************************************************************/ 300 void Adafruit_GFX::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, 301 uint16_t color) { 302 startWrite(); 303 for (int16_t i = x; i < x + w; i++) { 304 writeFastVLine(i, y, h, color); 305 } 306 endWrite(); 307 } 308 309 /**************************************************************************/ 310 /*! 311 @brief Fill the screen completely with one color. Update in subclasses if 312 desired! 313 @param color 16-bit 5-6-5 Color to fill with 314 */ 315 /**************************************************************************/ 316 void Adafruit_GFX::fillScreen(uint16_t color) { 317 fillRect(0, 0, _width, _height, color); 318 } 319 320 /**************************************************************************/ 321 /*! 322 @brief Draw a line 323 @param x0 Start point x coordinate 324 @param y0 Start point y coordinate 325 @param x1 End point x coordinate 326 @param y1 End point y coordinate 327 @param color 16-bit 5-6-5 Color to draw with 328 */ 329 /**************************************************************************/ 330 void Adafruit_GFX::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, 331 uint16_t color) { 332 // Update in subclasses if desired! 333 if (x0 == x1) { 334 if (y0 > y1) 335 _swap_int16_t(y0, y1); 336 drawFastVLine(x0, y0, y1 - y0 + 1, color); 337 } else if (y0 == y1) { 338 if (x0 > x1) 339 _swap_int16_t(x0, x1); 340 drawFastHLine(x0, y0, x1 - x0 + 1, color); 341 } else { 342 startWrite(); 343 writeLine(x0, y0, x1, y1, color); 344 endWrite(); 345 } 346 } 347 348 /**************************************************************************/ 349 /*! 350 @brief Draw a circle outline 351 @param x0 Center-point x coordinate 352 @param y0 Center-point y coordinate 353 @param r Radius of circle 354 @param color 16-bit 5-6-5 Color to draw with 355 */ 356 /**************************************************************************/ 357 void Adafruit_GFX::drawCircle(int16_t x0, int16_t y0, int16_t r, 358 uint16_t color) { 359 #if defined(ESP8266) 360 yield(); 361 #endif 362 int16_t f = 1 - r; 363 int16_t ddF_x = 1; 364 int16_t ddF_y = -2 * r; 365 int16_t x = 0; 366 int16_t y = r; 367 368 startWrite(); 369 writePixel(x0, y0 + r, color); 370 writePixel(x0, y0 - r, color); 371 writePixel(x0 + r, y0, color); 372 writePixel(x0 - r, y0, color); 373 374 while (x < y) { 375 if (f >= 0) { 376 y--; 377 ddF_y += 2; 378 f += ddF_y; 379 } 380 x++; 381 ddF_x += 2; 382 f += ddF_x; 383 384 writePixel(x0 + x, y0 + y, color); 385 writePixel(x0 - x, y0 + y, color); 386 writePixel(x0 + x, y0 - y, color); 387 writePixel(x0 - x, y0 - y, color); 388 writePixel(x0 + y, y0 + x, color); 389 writePixel(x0 - y, y0 + x, color); 390 writePixel(x0 + y, y0 - x, color); 391 writePixel(x0 - y, y0 - x, color); 392 } 393 endWrite(); 394 } 395 396 /**************************************************************************/ 397 /*! 398 @brief Quarter-circle drawer, used to do circles and roundrects 399 @param x0 Center-point x coordinate 400 @param y0 Center-point y coordinate 401 @param r Radius of circle 402 @param cornername Mask bit #1 or bit #2 to indicate which quarters of 403 the circle we're doing 404 @param color 16-bit 5-6-5 Color to draw with 405 */ 406 /**************************************************************************/ 407 void Adafruit_GFX::drawCircleHelper(int16_t x0, int16_t y0, int16_t r, 408 uint8_t cornername, uint16_t color) { 409 int16_t f = 1 - r; 410 int16_t ddF_x = 1; 411 int16_t ddF_y = -2 * r; 412 int16_t x = 0; 413 int16_t y = r; 414 415 while (x < y) { 416 if (f >= 0) { 417 y--; 418 ddF_y += 2; 419 f += ddF_y; 420 } 421 x++; 422 ddF_x += 2; 423 f += ddF_x; 424 if (cornername & 0x4) { 425 writePixel(x0 + x, y0 + y, color); 426 writePixel(x0 + y, y0 + x, color); 427 } 428 if (cornername & 0x2) { 429 writePixel(x0 + x, y0 - y, color); 430 writePixel(x0 + y, y0 - x, color); 431 } 432 if (cornername & 0x8) { 433 writePixel(x0 - y, y0 + x, color); 434 writePixel(x0 - x, y0 + y, color); 435 } 436 if (cornername & 0x1) { 437 writePixel(x0 - y, y0 - x, color); 438 writePixel(x0 - x, y0 - y, color); 439 } 440 } 441 } 442 443 /**************************************************************************/ 444 /*! 445 @brief Draw a circle with filled color 446 @param x0 Center-point x coordinate 447 @param y0 Center-point y coordinate 448 @param r Radius of circle 449 @param color 16-bit 5-6-5 Color to fill with 450 */ 451 /**************************************************************************/ 452 void Adafruit_GFX::fillCircle(int16_t x0, int16_t y0, int16_t r, 453 uint16_t color) { 454 startWrite(); 455 writeFastVLine(x0, y0 - r, 2 * r + 1, color); 456 fillCircleHelper(x0, y0, r, 3, 0, color); 457 endWrite(); 458 } 459 460 /**************************************************************************/ 461 /*! 462 @brief Quarter-circle drawer with fill, used for circles and roundrects 463 @param x0 Center-point x coordinate 464 @param y0 Center-point y coordinate 465 @param r Radius of circle 466 @param corners Mask bits indicating which quarters we're doing 467 @param delta Offset from center-point, used for round-rects 468 @param color 16-bit 5-6-5 Color to fill with 469 */ 470 /**************************************************************************/ 471 void Adafruit_GFX::fillCircleHelper(int16_t x0, int16_t y0, int16_t r, 472 uint8_t corners, int16_t delta, 473 uint16_t color) { 474 475 int16_t f = 1 - r; 476 int16_t ddF_x = 1; 477 int16_t ddF_y = -2 * r; 478 int16_t x = 0; 479 int16_t y = r; 480 int16_t px = x; 481 int16_t py = y; 482 483 delta++; // Avoid some +1's in the loop 484 485 while (x < y) { 486 if (f >= 0) { 487 y--; 488 ddF_y += 2; 489 f += ddF_y; 490 } 491 x++; 492 ddF_x += 2; 493 f += ddF_x; 494 // These checks avoid double-drawing certain lines, important 495 // for the SSD1306 library which has an INVERT drawing mode. 496 if (x < (y + 1)) { 497 if (corners & 1) 498 writeFastVLine(x0 + x, y0 - y, 2 * y + delta, color); 499 if (corners & 2) 500 writeFastVLine(x0 - x, y0 - y, 2 * y + delta, color); 501 } 502 if (y != py) { 503 if (corners & 1) 504 writeFastVLine(x0 + py, y0 - px, 2 * px + delta, color); 505 if (corners & 2) 506 writeFastVLine(x0 - py, y0 - px, 2 * px + delta, color); 507 py = y; 508 } 509 px = x; 510 } 511 } 512 513 /**************************************************************************/ 514 /*! 515 @brief Draw a rectangle with no fill color 516 @param x Top left corner x coordinate 517 @param y Top left corner y coordinate 518 @param w Width in pixels 519 @param h Height in pixels 520 @param color 16-bit 5-6-5 Color to draw with 521 */ 522 /**************************************************************************/ 523 void Adafruit_GFX::drawRect(int16_t x, int16_t y, int16_t w, int16_t h, 524 uint16_t color) { 525 startWrite(); 526 writeFastHLine(x, y, w, color); 527 writeFastHLine(x, y + h - 1, w, color); 528 writeFastVLine(x, y, h, color); 529 writeFastVLine(x + w - 1, y, h, color); 530 endWrite(); 531 } 532 533 /**************************************************************************/ 534 /*! 535 @brief Draw a rounded rectangle with no fill color 536 @param x Top left corner x coordinate 537 @param y Top left corner y coordinate 538 @param w Width in pixels 539 @param h Height in pixels 540 @param r Radius of corner rounding 541 @param color 16-bit 5-6-5 Color to draw with 542 */ 543 /**************************************************************************/ 544 void Adafruit_GFX::drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, 545 int16_t r, uint16_t color) { 546 int16_t max_radius = ((w < h) ? w : h) / 2; // 1/2 minor axis 547 if (r > max_radius) 548 r = max_radius; 549 // smarter version 550 startWrite(); 551 writeFastHLine(x + r, y, w - 2 * r, color); // Top 552 writeFastHLine(x + r, y + h - 1, w - 2 * r, color); // Bottom 553 writeFastVLine(x, y + r, h - 2 * r, color); // Left 554 writeFastVLine(x + w - 1, y + r, h - 2 * r, color); // Right 555 // draw four corners 556 drawCircleHelper(x + r, y + r, r, 1, color); 557 drawCircleHelper(x + w - r - 1, y + r, r, 2, color); 558 drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color); 559 drawCircleHelper(x + r, y + h - r - 1, r, 8, color); 560 endWrite(); 561 } 562 563 /**************************************************************************/ 564 /*! 565 @brief Draw a rounded rectangle with fill color 566 @param x Top left corner x coordinate 567 @param y Top left corner y coordinate 568 @param w Width in pixels 569 @param h Height in pixels 570 @param r Radius of corner rounding 571 @param color 16-bit 5-6-5 Color to draw/fill with 572 */ 573 /**************************************************************************/ 574 void Adafruit_GFX::fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, 575 int16_t r, uint16_t color) { 576 int16_t max_radius = ((w < h) ? w : h) / 2; // 1/2 minor axis 577 if (r > max_radius) 578 r = max_radius; 579 // smarter version 580 startWrite(); 581 writeFillRect(x + r, y, w - 2 * r, h, color); 582 // draw four corners 583 fillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color); 584 fillCircleHelper(x + r, y + r, r, 2, h - 2 * r - 1, color); 585 endWrite(); 586 } 587 588 /**************************************************************************/ 589 /*! 590 @brief Draw a triangle with no fill color 591 @param x0 Vertex #0 x coordinate 592 @param y0 Vertex #0 y coordinate 593 @param x1 Vertex #1 x coordinate 594 @param y1 Vertex #1 y coordinate 595 @param x2 Vertex #2 x coordinate 596 @param y2 Vertex #2 y coordinate 597 @param color 16-bit 5-6-5 Color to draw with 598 */ 599 /**************************************************************************/ 600 void Adafruit_GFX::drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, 601 int16_t x2, int16_t y2, uint16_t color) { 602 drawLine(x0, y0, x1, y1, color); 603 drawLine(x1, y1, x2, y2, color); 604 drawLine(x2, y2, x0, y0, color); 605 } 606 607 /**************************************************************************/ 608 /*! 609 @brief Draw a triangle with color-fill 610 @param x0 Vertex #0 x coordinate 611 @param y0 Vertex #0 y coordinate 612 @param x1 Vertex #1 x coordinate 613 @param y1 Vertex #1 y coordinate 614 @param x2 Vertex #2 x coordinate 615 @param y2 Vertex #2 y coordinate 616 @param color 16-bit 5-6-5 Color to fill/draw with 617 */ 618 /**************************************************************************/ 619 void Adafruit_GFX::fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, 620 int16_t x2, int16_t y2, uint16_t color) { 621 622 int16_t a, b, y, last; 623 624 // Sort coordinates by Y order (y2 >= y1 >= y0) 625 if (y0 > y1) { 626 _swap_int16_t(y0, y1); 627 _swap_int16_t(x0, x1); 628 } 629 if (y1 > y2) { 630 _swap_int16_t(y2, y1); 631 _swap_int16_t(x2, x1); 632 } 633 if (y0 > y1) { 634 _swap_int16_t(y0, y1); 635 _swap_int16_t(x0, x1); 636 } 637 638 startWrite(); 639 if (y0 == y2) { // Handle awkward all-on-same-line case as its own thing 640 a = b = x0; 641 if (x1 < a) 642 a = x1; 643 else if (x1 > b) 644 b = x1; 645 if (x2 < a) 646 a = x2; 647 else if (x2 > b) 648 b = x2; 649 writeFastHLine(a, y0, b - a + 1, color); 650 endWrite(); 651 return; 652 } 653 654 int16_t dx01 = x1 - x0, dy01 = y1 - y0, dx02 = x2 - x0, dy02 = y2 - y0, 655 dx12 = x2 - x1, dy12 = y2 - y1; 656 int32_t sa = 0, sb = 0; 657 658 // For upper part of triangle, find scanline crossings for segments 659 // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1 660 // is included here (and second loop will be skipped, avoiding a /0 661 // error there), otherwise scanline y1 is skipped here and handled 662 // in the second loop...which also avoids a /0 error here if y0=y1 663 // (flat-topped triangle). 664 if (y1 == y2) 665 last = y1; // Include y1 scanline 666 else 667 last = y1 - 1; // Skip it 668 669 for (y = y0; y <= last; y++) { 670 a = x0 + sa / dy01; 671 b = x0 + sb / dy02; 672 sa += dx01; 673 sb += dx02; 674 /* longhand: 675 a = x0 + (x1 - x0) * (y - y0) / (y1 - y0); 676 b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); 677 */ 678 if (a > b) 679 _swap_int16_t(a, b); 680 writeFastHLine(a, y, b - a + 1, color); 681 } 682 683 // For lower part of triangle, find scanline crossings for segments 684 // 0-2 and 1-2. This loop is skipped if y1=y2. 685 sa = (int32_t)dx12 * (y - y1); 686 sb = (int32_t)dx02 * (y - y0); 687 for (; y <= y2; y++) { 688 a = x1 + sa / dy12; 689 b = x0 + sb / dy02; 690 sa += dx12; 691 sb += dx02; 692 /* longhand: 693 a = x1 + (x2 - x1) * (y - y1) / (y2 - y1); 694 b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); 695 */ 696 if (a > b) 697 _swap_int16_t(a, b); 698 writeFastHLine(a, y, b - a + 1, color); 699 } 700 endWrite(); 701 } 702 703 // BITMAP / XBITMAP / GRAYSCALE / RGB BITMAP FUNCTIONS --------------------- 704 705 /**************************************************************************/ 706 /*! 707 @brief Draw a PROGMEM-resident 1-bit image at the specified (x,y) 708 position, using the specified foreground color (unset bits are transparent). 709 @param x Top left corner x coordinate 710 @param y Top left corner y coordinate 711 @param bitmap byte array with monochrome bitmap 712 @param w Width of bitmap in pixels 713 @param h Height of bitmap in pixels 714 @param color 16-bit 5-6-5 Color to draw with 715 */ 716 /**************************************************************************/ 717 void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], 718 int16_t w, int16_t h, uint16_t color) { 719 720 int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte 721 uint8_t byte = 0; 722 723 startWrite(); 724 for (int16_t j = 0; j < h; j++, y++) { 725 for (int16_t i = 0; i < w; i++) { 726 if (i & 7) 727 byte <<= 1; 728 else 729 byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]); 730 if (byte & 0x80) 731 writePixel(x + i, y, color); 732 } 733 } 734 endWrite(); 735 } 736 737 /**************************************************************************/ 738 /*! 739 @brief Draw a PROGMEM-resident 1-bit image at the specified (x,y) 740 position, using the specified foreground (for set bits) and background (unset 741 bits) colors. 742 @param x Top left corner x coordinate 743 @param y Top left corner y coordinate 744 @param bitmap byte array with monochrome bitmap 745 @param w Width of bitmap in pixels 746 @param h Height of bitmap in pixels 747 @param color 16-bit 5-6-5 Color to draw pixels with 748 @param bg 16-bit 5-6-5 Color to draw background with 749 */ 750 /**************************************************************************/ 751 void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], 752 int16_t w, int16_t h, uint16_t color, 753 uint16_t bg) { 754 755 int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte 756 uint8_t byte = 0; 757 758 startWrite(); 759 for (int16_t j = 0; j < h; j++, y++) { 760 for (int16_t i = 0; i < w; i++) { 761 if (i & 7) 762 byte <<= 1; 763 else 764 byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]); 765 writePixel(x + i, y, (byte & 0x80) ? color : bg); 766 } 767 } 768 endWrite(); 769 } 770 771 /**************************************************************************/ 772 /*! 773 @brief Draw a RAM-resident 1-bit image at the specified (x,y) position, 774 using the specified foreground color (unset bits are transparent). 775 @param x Top left corner x coordinate 776 @param y Top left corner y coordinate 777 @param bitmap byte array with monochrome bitmap 778 @param w Width of bitmap in pixels 779 @param h Height of bitmap in pixels 780 @param color 16-bit 5-6-5 Color to draw with 781 */ 782 /**************************************************************************/ 783 void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, 784 int16_t h, uint16_t color) { 785 786 int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte 787 uint8_t byte = 0; 788 789 startWrite(); 790 for (int16_t j = 0; j < h; j++, y++) { 791 for (int16_t i = 0; i < w; i++) { 792 if (i & 7) 793 byte <<= 1; 794 else 795 byte = bitmap[j * byteWidth + i / 8]; 796 if (byte & 0x80) 797 writePixel(x + i, y, color); 798 } 799 } 800 endWrite(); 801 } 802 803 /**************************************************************************/ 804 /*! 805 @brief Draw a RAM-resident 1-bit image at the specified (x,y) position, 806 using the specified foreground (for set bits) and background (unset bits) 807 colors. 808 @param x Top left corner x coordinate 809 @param y Top left corner y coordinate 810 @param bitmap byte array with monochrome bitmap 811 @param w Width of bitmap in pixels 812 @param h Height of bitmap in pixels 813 @param color 16-bit 5-6-5 Color to draw pixels with 814 @param bg 16-bit 5-6-5 Color to draw background with 815 */ 816 /**************************************************************************/ 817 void Adafruit_GFX::drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, 818 int16_t h, uint16_t color, uint16_t bg) { 819 820 int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte 821 uint8_t byte = 0; 822 823 startWrite(); 824 for (int16_t j = 0; j < h; j++, y++) { 825 for (int16_t i = 0; i < w; i++) { 826 if (i & 7) 827 byte <<= 1; 828 else 829 byte = bitmap[j * byteWidth + i / 8]; 830 writePixel(x + i, y, (byte & 0x80) ? color : bg); 831 } 832 } 833 endWrite(); 834 } 835 836 /**************************************************************************/ 837 /*! 838 @brief Draw PROGMEM-resident XBitMap Files (*.xbm), exported from GIMP. 839 Usage: Export from GIMP to *.xbm, rename *.xbm to *.c and open in editor. 840 C Array can be directly used with this function. 841 There is no RAM-resident version of this function; if generating bitmaps 842 in RAM, use the format defined by drawBitmap() and call that instead. 843 @param x Top left corner x coordinate 844 @param y Top left corner y coordinate 845 @param bitmap byte array with monochrome bitmap 846 @param w Width of bitmap in pixels 847 @param h Height of bitmap in pixels 848 @param color 16-bit 5-6-5 Color to draw pixels with 849 */ 850 /**************************************************************************/ 851 void Adafruit_GFX::drawXBitmap(int16_t x, int16_t y, const uint8_t bitmap[], 852 int16_t w, int16_t h, uint16_t color) { 853 854 int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte 855 uint8_t byte = 0; 856 857 startWrite(); 858 for (int16_t j = 0; j < h; j++, y++) { 859 for (int16_t i = 0; i < w; i++) { 860 if (i & 7) 861 byte >>= 1; 862 else 863 byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]); 864 // Nearly identical to drawBitmap(), only the bit order 865 // is reversed here (left-to-right = LSB to MSB): 866 if (byte & 0x01) 867 writePixel(x + i, y, color); 868 } 869 } 870 endWrite(); 871 } 872 873 /**************************************************************************/ 874 /*! 875 @brief Draw a PROGMEM-resident 8-bit image (grayscale) at the specified 876 (x,y) pos. Specifically for 8-bit display devices such as IS31FL3731; no 877 color reduction/expansion is performed. 878 @param x Top left corner x coordinate 879 @param y Top left corner y coordinate 880 @param bitmap byte array with grayscale bitmap 881 @param w Width of bitmap in pixels 882 @param h Height of bitmap in pixels 883 */ 884 /**************************************************************************/ 885 void Adafruit_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, 886 const uint8_t bitmap[], int16_t w, 887 int16_t h) { 888 startWrite(); 889 for (int16_t j = 0; j < h; j++, y++) { 890 for (int16_t i = 0; i < w; i++) { 891 writePixel(x + i, y, (uint8_t)pgm_read_byte(&bitmap[j * w + i])); 892 } 893 } 894 endWrite(); 895 } 896 897 /**************************************************************************/ 898 /*! 899 @brief Draw a RAM-resident 8-bit image (grayscale) at the specified (x,y) 900 pos. Specifically for 8-bit display devices such as IS31FL3731; no color 901 reduction/expansion is performed. 902 @param x Top left corner x coordinate 903 @param y Top left corner y coordinate 904 @param bitmap byte array with grayscale bitmap 905 @param w Width of bitmap in pixels 906 @param h Height of bitmap in pixels 907 */ 908 /**************************************************************************/ 909 void Adafruit_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap, 910 int16_t w, int16_t h) { 911 startWrite(); 912 for (int16_t j = 0; j < h; j++, y++) { 913 for (int16_t i = 0; i < w; i++) { 914 writePixel(x + i, y, bitmap[j * w + i]); 915 } 916 } 917 endWrite(); 918 } 919 920 /**************************************************************************/ 921 /*! 922 @brief Draw a PROGMEM-resident 8-bit image (grayscale) with a 1-bit mask 923 (set bits = opaque, unset bits = clear) at the specified (x,y) position. 924 BOTH buffers (grayscale and mask) must be PROGMEM-resident. 925 Specifically for 8-bit display devices such as IS31FL3731; no color 926 reduction/expansion is performed. 927 @param x Top left corner x coordinate 928 @param y Top left corner y coordinate 929 @param bitmap byte array with grayscale bitmap 930 @param mask byte array with mask bitmap 931 @param w Width of bitmap in pixels 932 @param h Height of bitmap in pixels 933 */ 934 /**************************************************************************/ 935 void Adafruit_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, 936 const uint8_t bitmap[], 937 const uint8_t mask[], int16_t w, 938 int16_t h) { 939 int16_t bw = (w + 7) / 8; // Bitmask scanline pad = whole byte 940 uint8_t byte = 0; 941 startWrite(); 942 for (int16_t j = 0; j < h; j++, y++) { 943 for (int16_t i = 0; i < w; i++) { 944 if (i & 7) 945 byte <<= 1; 946 else 947 byte = pgm_read_byte(&mask[j * bw + i / 8]); 948 if (byte & 0x80) { 949 writePixel(x + i, y, (uint8_t)pgm_read_byte(&bitmap[j * w + i])); 950 } 951 } 952 } 953 endWrite(); 954 } 955 956 /**************************************************************************/ 957 /*! 958 @brief Draw a RAM-resident 8-bit image (grayscale) with a 1-bit mask 959 (set bits = opaque, unset bits = clear) at the specified (x,y) position. 960 BOTH buffers (grayscale and mask) must be RAM-residentt, no mix-and-match 961 Specifically for 8-bit display devices such as IS31FL3731; no color 962 reduction/expansion is performed. 963 @param x Top left corner x coordinate 964 @param y Top left corner y coordinate 965 @param bitmap byte array with grayscale bitmap 966 @param mask byte array with mask bitmap 967 @param w Width of bitmap in pixels 968 @param h Height of bitmap in pixels 969 */ 970 /**************************************************************************/ 971 void Adafruit_GFX::drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap, 972 uint8_t *mask, int16_t w, int16_t h) { 973 int16_t bw = (w + 7) / 8; // Bitmask scanline pad = whole byte 974 uint8_t byte = 0; 975 startWrite(); 976 for (int16_t j = 0; j < h; j++, y++) { 977 for (int16_t i = 0; i < w; i++) { 978 if (i & 7) 979 byte <<= 1; 980 else 981 byte = mask[j * bw + i / 8]; 982 if (byte & 0x80) { 983 writePixel(x + i, y, bitmap[j * w + i]); 984 } 985 } 986 } 987 endWrite(); 988 } 989 990 /**************************************************************************/ 991 /*! 992 @brief Draw a PROGMEM-resident 16-bit image (RGB 5/6/5) at the specified 993 (x,y) position. For 16-bit display devices; no color reduction performed. 994 @param x Top left corner x coordinate 995 @param y Top left corner y coordinate 996 @param bitmap byte array with 16-bit color bitmap 997 @param w Width of bitmap in pixels 998 @param h Height of bitmap in pixels 999 */ 1000 /**************************************************************************/ 1001 void Adafruit_GFX::drawRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[], 1002 int16_t w, int16_t h) { 1003 startWrite(); 1004 for (int16_t j = 0; j < h; j++, y++) { 1005 for (int16_t i = 0; i < w; i++) { 1006 writePixel(x + i, y, pgm_read_word(&bitmap[j * w + i])); 1007 } 1008 } 1009 endWrite(); 1010 } 1011 1012 /**************************************************************************/ 1013 /*! 1014 @brief Draw a RAM-resident 16-bit image (RGB 5/6/5) at the specified (x,y) 1015 position. For 16-bit display devices; no color reduction performed. 1016 @param x Top left corner x coordinate 1017 @param y Top left corner y coordinate 1018 @param bitmap byte array with 16-bit color bitmap 1019 @param w Width of bitmap in pixels 1020 @param h Height of bitmap in pixels 1021 */ 1022 /**************************************************************************/ 1023 void Adafruit_GFX::drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, 1024 int16_t w, int16_t h) { 1025 startWrite(); 1026 for (int16_t j = 0; j < h; j++, y++) { 1027 for (int16_t i = 0; i < w; i++) { 1028 writePixel(x + i, y, bitmap[j * w + i]); 1029 } 1030 } 1031 endWrite(); 1032 } 1033 1034 /**************************************************************************/ 1035 /*! 1036 @brief Draw a PROGMEM-resident 16-bit image (RGB 5/6/5) with a 1-bit mask 1037 (set bits = opaque, unset bits = clear) at the specified (x,y) position. BOTH 1038 buffers (color and mask) must be PROGMEM-resident. For 16-bit display 1039 devices; no color reduction performed. 1040 @param x Top left corner x coordinate 1041 @param y Top left corner y coordinate 1042 @param bitmap byte array with 16-bit color bitmap 1043 @param mask byte array with monochrome mask bitmap 1044 @param w Width of bitmap in pixels 1045 @param h Height of bitmap in pixels 1046 */ 1047 /**************************************************************************/ 1048 void Adafruit_GFX::drawRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[], 1049 const uint8_t mask[], int16_t w, int16_t h) { 1050 int16_t bw = (w + 7) / 8; // Bitmask scanline pad = whole byte 1051 uint8_t byte = 0; 1052 startWrite(); 1053 for (int16_t j = 0; j < h; j++, y++) { 1054 for (int16_t i = 0; i < w; i++) { 1055 if (i & 7) 1056 byte <<= 1; 1057 else 1058 byte = pgm_read_byte(&mask[j * bw + i / 8]); 1059 if (byte & 0x80) { 1060 writePixel(x + i, y, pgm_read_word(&bitmap[j * w + i])); 1061 } 1062 } 1063 } 1064 endWrite(); 1065 } 1066 1067 /**************************************************************************/ 1068 /*! 1069 @brief Draw a RAM-resident 16-bit image (RGB 5/6/5) with a 1-bit mask (set 1070 bits = opaque, unset bits = clear) at the specified (x,y) position. BOTH 1071 buffers (color and mask) must be RAM-resident. For 16-bit display devices; no 1072 color reduction performed. 1073 @param x Top left corner x coordinate 1074 @param y Top left corner y coordinate 1075 @param bitmap byte array with 16-bit color bitmap 1076 @param mask byte array with monochrome mask bitmap 1077 @param w Width of bitmap in pixels 1078 @param h Height of bitmap in pixels 1079 */ 1080 /**************************************************************************/ 1081 void Adafruit_GFX::drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, 1082 uint8_t *mask, int16_t w, int16_t h) { 1083 int16_t bw = (w + 7) / 8; // Bitmask scanline pad = whole byte 1084 uint8_t byte = 0; 1085 startWrite(); 1086 for (int16_t j = 0; j < h; j++, y++) { 1087 for (int16_t i = 0; i < w; i++) { 1088 if (i & 7) 1089 byte <<= 1; 1090 else 1091 byte = mask[j * bw + i / 8]; 1092 if (byte & 0x80) { 1093 writePixel(x + i, y, bitmap[j * w + i]); 1094 } 1095 } 1096 } 1097 endWrite(); 1098 } 1099 1100 // TEXT- AND CHARACTER-HANDLING FUNCTIONS ---------------------------------- 1101 1102 // Draw a character 1103 /**************************************************************************/ 1104 /*! 1105 @brief Draw a single character 1106 @param x Bottom left corner x coordinate 1107 @param y Bottom left corner y coordinate 1108 @param c The 8-bit font-indexed character (likely ascii) 1109 @param color 16-bit 5-6-5 Color to draw chraracter with 1110 @param bg 16-bit 5-6-5 Color to fill background with (if same as color, 1111 no background) 1112 @param size Font magnification level, 1 is 'original' size 1113 */ 1114 /**************************************************************************/ 1115 void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, 1116 uint16_t color, uint16_t bg, uint8_t size) { 1117 drawChar(x, y, c, color, bg, size, size); 1118 } 1119 1120 // Draw a character 1121 /**************************************************************************/ 1122 /*! 1123 @brief Draw a single character 1124 @param x Bottom left corner x coordinate 1125 @param y Bottom left corner y coordinate 1126 @param c The 8-bit font-indexed character (likely ascii) 1127 @param color 16-bit 5-6-5 Color to draw chraracter with 1128 @param bg 16-bit 5-6-5 Color to fill background with (if same as color, 1129 no background) 1130 @param size_x Font magnification level in X-axis, 1 is 'original' size 1131 @param size_y Font magnification level in Y-axis, 1 is 'original' size 1132 */ 1133 /**************************************************************************/ 1134 void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, 1135 uint16_t color, uint16_t bg, uint8_t size_x, 1136 uint8_t size_y) { 1137 1138 if (!gfxFont) { // 'Classic' built-in font 1139 1140 if ((x >= _width) || // Clip right 1141 (y >= _height) || // Clip bottom 1142 ((x + 6 * size_x - 1) < 0) || // Clip left 1143 ((y + 8 * size_y - 1) < 0)) // Clip top 1144 return; 1145 1146 if (!_cp437 && (c >= 176)) 1147 c++; // Handle 'classic' charset behavior 1148 1149 startWrite(); 1150 for (int8_t i = 0; i < 5; i++) { // Char bitmap = 5 columns 1151 uint8_t line = pgm_read_byte(&font[c * 5 + i]); 1152 for (int8_t j = 0; j < 8; j++, line >>= 1) { 1153 if (line & 1) { 1154 if (size_x == 1 && size_y == 1) 1155 writePixel(x + i, y + j, color); 1156 else 1157 writeFillRect(x + i * size_x, y + j * size_y, size_x, size_y, 1158 color); 1159 } else if (bg != color) { 1160 if (size_x == 1 && size_y == 1) 1161 writePixel(x + i, y + j, bg); 1162 else 1163 writeFillRect(x + i * size_x, y + j * size_y, size_x, size_y, bg); 1164 } 1165 } 1166 } 1167 if (bg != color) { // If opaque, draw vertical line for last column 1168 if (size_x == 1 && size_y == 1) 1169 writeFastVLine(x + 5, y, 8, bg); 1170 else 1171 writeFillRect(x + 5 * size_x, y, size_x, 8 * size_y, bg); 1172 } 1173 endWrite(); 1174 1175 } else { // Custom font 1176 1177 // Character is assumed previously filtered by write() to eliminate 1178 // newlines, returns, non-printable characters, etc. Calling 1179 // drawChar() directly with 'bad' characters of font may cause mayhem! 1180 1181 c -= (uint8_t)pgm_read_byte(&gfxFont->first); 1182 GFXglyph *glyph = pgm_read_glyph_ptr(gfxFont, c); 1183 uint8_t *bitmap = pgm_read_bitmap_ptr(gfxFont); 1184 1185 uint16_t bo = pgm_read_word(&glyph->bitmapOffset); 1186 uint8_t w = pgm_read_byte(&glyph->width), h = pgm_read_byte(&glyph->height); 1187 int8_t xo = pgm_read_byte(&glyph->xOffset), 1188 yo = pgm_read_byte(&glyph->yOffset); 1189 uint8_t xx, yy, bits = 0, bit = 0; 1190 int16_t xo16 = 0, yo16 = 0; 1191 1192 if (size_x > 1 || size_y > 1) { 1193 xo16 = xo; 1194 yo16 = yo; 1195 } 1196 1197 // Todo: Add character clipping here 1198 1199 // NOTE: THERE IS NO 'BACKGROUND' COLOR OPTION ON CUSTOM FONTS. 1200 // THIS IS ON PURPOSE AND BY DESIGN. The background color feature 1201 // has typically been used with the 'classic' font to overwrite old 1202 // screen contents with new data. This ONLY works because the 1203 // characters are a uniform size; it's not a sensible thing to do with 1204 // proportionally-spaced fonts with glyphs of varying sizes (and that 1205 // may overlap). To replace previously-drawn text when using a custom 1206 // font, use the getTextBounds() function to determine the smallest 1207 // rectangle encompassing a string, erase the area with fillRect(), 1208 // then draw new text. This WILL infortunately 'blink' the text, but 1209 // is unavoidable. Drawing 'background' pixels will NOT fix this, 1210 // only creates a new set of problems. Have an idea to work around 1211 // this (a canvas object type for MCUs that can afford the RAM and 1212 // displays supporting setAddrWindow() and pushColors()), but haven't 1213 // implemented this yet. 1214 1215 startWrite(); 1216 for (yy = 0; yy < h; yy++) { 1217 for (xx = 0; xx < w; xx++) { 1218 if (!(bit++ & 7)) { 1219 bits = pgm_read_byte(&bitmap[bo++]); 1220 } 1221 if (bits & 0x80) { 1222 if (size_x == 1 && size_y == 1) { 1223 writePixel(x + xo + xx, y + yo + yy, color); 1224 } else { 1225 writeFillRect(x + (xo16 + xx) * size_x, y + (yo16 + yy) * size_y, 1226 size_x, size_y, color); 1227 } 1228 } 1229 bits <<= 1; 1230 } 1231 } 1232 endWrite(); 1233 1234 } // End classic vs custom font 1235 } 1236 /**************************************************************************/ 1237 /*! 1238 @brief Print one byte/character of data, used to support print() 1239 @param c The 8-bit ascii character to write 1240 */ 1241 /**************************************************************************/ 1242 size_t Adafruit_GFX::write(uint8_t c) { 1243 if (!gfxFont) { // 'Classic' built-in font 1244 1245 if (c == '\n') { // Newline? 1246 cursor_x = 0; // Reset x to zero, 1247 cursor_y += textsize_y * 8; // advance y one line 1248 } else if (c != '\r') { // Ignore carriage returns 1249 if (wrap && ((cursor_x + textsize_x * 6) > _width)) { // Off right? 1250 cursor_x = 0; // Reset x to zero, 1251 cursor_y += textsize_y * 8; // advance y one line 1252 } 1253 drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize_x, 1254 textsize_y); 1255 cursor_x += textsize_x * 6; // Advance x one char 1256 } 1257 1258 } else { // Custom font 1259 1260 if (c == '\n') { 1261 cursor_x = 0; 1262 cursor_y += 1263 (int16_t)textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); 1264 } else if (c != '\r') { 1265 uint8_t first = pgm_read_byte(&gfxFont->first); 1266 if ((c >= first) && (c <= (uint8_t)pgm_read_byte(&gfxFont->last))) { 1267 GFXglyph *glyph = pgm_read_glyph_ptr(gfxFont, c - first); 1268 uint8_t w = pgm_read_byte(&glyph->width), 1269 h = pgm_read_byte(&glyph->height); 1270 if ((w > 0) && (h > 0)) { // Is there an associated bitmap? 1271 int16_t xo = (int8_t)pgm_read_byte(&glyph->xOffset); // sic 1272 if (wrap && ((cursor_x + textsize_x * (xo + w)) > _width)) { 1273 cursor_x = 0; 1274 cursor_y += (int16_t)textsize_y * 1275 (uint8_t)pgm_read_byte(&gfxFont->yAdvance); 1276 } 1277 drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize_x, 1278 textsize_y); 1279 } 1280 cursor_x += 1281 (uint8_t)pgm_read_byte(&glyph->xAdvance) * (int16_t)textsize_x; 1282 } 1283 } 1284 } 1285 return 1; 1286 } 1287 1288 /**************************************************************************/ 1289 /*! 1290 @brief Set text 'magnification' size. Each increase in s makes 1 pixel 1291 that much bigger. 1292 @param s Desired text size. 1 is default 6x8, 2 is 12x16, 3 is 18x24, etc 1293 */ 1294 /**************************************************************************/ 1295 void Adafruit_GFX::setTextSize(uint8_t s) { setTextSize(s, s); } 1296 1297 /**************************************************************************/ 1298 /*! 1299 @brief Set text 'magnification' size. Each increase in s makes 1 pixel 1300 that much bigger. 1301 @param s_x Desired text width magnification level in X-axis. 1 is default 1302 @param s_y Desired text width magnification level in Y-axis. 1 is default 1303 */ 1304 /**************************************************************************/ 1305 void Adafruit_GFX::setTextSize(uint8_t s_x, uint8_t s_y) { 1306 textsize_x = (s_x > 0) ? s_x : 1; 1307 textsize_y = (s_y > 0) ? s_y : 1; 1308 } 1309 1310 /**************************************************************************/ 1311 /*! 1312 @brief Set rotation setting for display 1313 @param x 0 thru 3 corresponding to 4 cardinal rotations 1314 */ 1315 /**************************************************************************/ 1316 void Adafruit_GFX::setRotation(uint8_t x) { 1317 rotation = (x & 3); 1318 switch (rotation) { 1319 case 0: 1320 case 2: 1321 _width = WIDTH; 1322 _height = HEIGHT; 1323 break; 1324 case 1: 1325 case 3: 1326 _width = HEIGHT; 1327 _height = WIDTH; 1328 break; 1329 } 1330 } 1331 1332 /**************************************************************************/ 1333 /*! 1334 @brief Set the font to display when print()ing, either custom or default 1335 @param f The GFXfont object, if NULL use built in 6x8 font 1336 */ 1337 /**************************************************************************/ 1338 void Adafruit_GFX::setFont(const GFXfont *f) { 1339 if (f) { // Font struct pointer passed in? 1340 if (!gfxFont) { // And no current font struct? 1341 // Switching from classic to new font behavior. 1342 // Move cursor pos down 6 pixels so it's on baseline. 1343 cursor_y += 6; 1344 } 1345 } else if (gfxFont) { // NULL passed. Current font struct defined? 1346 // Switching from new to classic font behavior. 1347 // Move cursor pos up 6 pixels so it's at top-left of char. 1348 cursor_y -= 6; 1349 } 1350 gfxFont = (GFXfont *)f; 1351 } 1352 1353 /**************************************************************************/ 1354 /*! 1355 @brief Helper to determine size of a character with current font/size. 1356 Broke this out as it's used by both the PROGMEM- and RAM-resident 1357 getTextBounds() functions. 1358 @param c The ASCII character in question 1359 @param x Pointer to x location of character. Value is modified by 1360 this function to advance to next character. 1361 @param y Pointer to y location of character. Value is modified by 1362 this function to advance to next character. 1363 @param minx Pointer to minimum X coordinate, passed in to AND returned 1364 by this function -- this is used to incrementally build a 1365 bounding rectangle for a string. 1366 @param miny Pointer to minimum Y coord, passed in AND returned. 1367 @param maxx Pointer to maximum X coord, passed in AND returned. 1368 @param maxy Pointer to maximum Y coord, passed in AND returned. 1369 */ 1370 /**************************************************************************/ 1371 void Adafruit_GFX::charBounds(unsigned char c, int16_t *x, int16_t *y, 1372 int16_t *minx, int16_t *miny, int16_t *maxx, 1373 int16_t *maxy) { 1374 1375 if (gfxFont) { 1376 1377 if (c == '\n') { // Newline? 1378 *x = 0; // Reset x to zero, advance y by one line 1379 *y += textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); 1380 } else if (c != '\r') { // Not a carriage return; is normal char 1381 uint8_t first = pgm_read_byte(&gfxFont->first), 1382 last = pgm_read_byte(&gfxFont->last); 1383 if ((c >= first) && (c <= last)) { // Char present in this font? 1384 GFXglyph *glyph = pgm_read_glyph_ptr(gfxFont, c - first); 1385 uint8_t gw = pgm_read_byte(&glyph->width), 1386 gh = pgm_read_byte(&glyph->height), 1387 xa = pgm_read_byte(&glyph->xAdvance); 1388 int8_t xo = pgm_read_byte(&glyph->xOffset), 1389 yo = pgm_read_byte(&glyph->yOffset); 1390 if (wrap && ((*x + (((int16_t)xo + gw) * textsize_x)) > _width)) { 1391 *x = 0; // Reset x to zero, advance y by one line 1392 *y += textsize_y * (uint8_t)pgm_read_byte(&gfxFont->yAdvance); 1393 } 1394 int16_t tsx = (int16_t)textsize_x, tsy = (int16_t)textsize_y, 1395 x1 = *x + xo * tsx, y1 = *y + yo * tsy, x2 = x1 + gw * tsx - 1, 1396 y2 = y1 + gh * tsy - 1; 1397 if (x1 < *minx) 1398 *minx = x1; 1399 if (y1 < *miny) 1400 *miny = y1; 1401 if (x2 > *maxx) 1402 *maxx = x2; 1403 if (y2 > *maxy) 1404 *maxy = y2; 1405 *x += xa * tsx; 1406 } 1407 } 1408 1409 } else { // Default font 1410 1411 if (c == '\n') { // Newline? 1412 *x = 0; // Reset x to zero, 1413 *y += textsize_y * 8; // advance y one line 1414 // min/max x/y unchaged -- that waits for next 'normal' character 1415 } else if (c != '\r') { // Normal char; ignore carriage returns 1416 if (wrap && ((*x + textsize_x * 6) > _width)) { // Off right? 1417 *x = 0; // Reset x to zero, 1418 *y += textsize_y * 8; // advance y one line 1419 } 1420 int x2 = *x + textsize_x * 6 - 1, // Lower-right pixel of char 1421 y2 = *y + textsize_y * 8 - 1; 1422 if (x2 > *maxx) 1423 *maxx = x2; // Track max x, y 1424 if (y2 > *maxy) 1425 *maxy = y2; 1426 if (*x < *minx) 1427 *minx = *x; // Track min x, y 1428 if (*y < *miny) 1429 *miny = *y; 1430 *x += textsize_x * 6; // Advance x one char 1431 } 1432 } 1433 } 1434 1435 /**************************************************************************/ 1436 /*! 1437 @brief Helper to determine size of a string with current font/size. 1438 Pass string and a cursor position, returns UL corner and W,H. 1439 @param str The ASCII string to measure 1440 @param x The current cursor X 1441 @param y The current cursor Y 1442 @param x1 The boundary X coordinate, returned by function 1443 @param y1 The boundary Y coordinate, returned by function 1444 @param w The boundary width, returned by function 1445 @param h The boundary height, returned by function 1446 */ 1447 /**************************************************************************/ 1448 void Adafruit_GFX::getTextBounds(const char *str, int16_t x, int16_t y, 1449 int16_t *x1, int16_t *y1, uint16_t *w, 1450 uint16_t *h) { 1451 1452 uint8_t c; // Current character 1453 int16_t minx = 0x7FFF, miny = 0x7FFF, maxx = -1, maxy = -1; // Bound rect 1454 // Bound rect is intentionally initialized inverted, so 1st char sets it 1455 1456 *x1 = x; // Initial position is value passed in 1457 *y1 = y; 1458 *w = *h = 0; // Initial size is zero 1459 1460 while ((c = *str++)) { 1461 // charBounds() modifies x/y to advance for each character, 1462 // and min/max x/y are updated to incrementally build bounding rect. 1463 charBounds(c, &x, &y, &minx, &miny, &maxx, &maxy); 1464 } 1465 1466 if (maxx >= minx) { // If legit string bounds were found... 1467 *x1 = minx; // Update x1 to least X coord, 1468 *w = maxx - minx + 1; // And w to bound rect width 1469 } 1470 if (maxy >= miny) { // Same for height 1471 *y1 = miny; 1472 *h = maxy - miny + 1; 1473 } 1474 } 1475 1476 /**************************************************************************/ 1477 /*! 1478 @brief Helper to determine size of a string with current font/size. Pass 1479 string and a cursor position, returns UL corner and W,H. 1480 @param str The ascii string to measure (as an arduino String() class) 1481 @param x The current cursor X 1482 @param y The current cursor Y 1483 @param x1 The boundary X coordinate, set by function 1484 @param y1 The boundary Y coordinate, set by function 1485 @param w The boundary width, set by function 1486 @param h The boundary height, set by function 1487 */ 1488 /**************************************************************************/ 1489 void Adafruit_GFX::getTextBounds(const String &str, int16_t x, int16_t y, 1490 int16_t *x1, int16_t *y1, uint16_t *w, 1491 uint16_t *h) { 1492 if (str.length() != 0) { 1493 getTextBounds(const_cast<char *>(str.c_str()), x, y, x1, y1, w, h); 1494 } 1495 } 1496 1497 /**************************************************************************/ 1498 /*! 1499 @brief Helper to determine size of a PROGMEM string with current 1500 font/size. Pass string and a cursor position, returns UL corner and W,H. 1501 @param str The flash-memory ascii string to measure 1502 @param x The current cursor X 1503 @param y The current cursor Y 1504 @param x1 The boundary X coordinate, set by function 1505 @param y1 The boundary Y coordinate, set by function 1506 @param w The boundary width, set by function 1507 @param h The boundary height, set by function 1508 */ 1509 /**************************************************************************/ 1510 void Adafruit_GFX::getTextBounds(const __FlashStringHelper *str, int16_t x, 1511 int16_t y, int16_t *x1, int16_t *y1, 1512 uint16_t *w, uint16_t *h) { 1513 uint8_t *s = (uint8_t *)str, c; 1514 1515 *x1 = x; 1516 *y1 = y; 1517 *w = *h = 0; 1518 1519 int16_t minx = _width, miny = _height, maxx = -1, maxy = -1; 1520 1521 while ((c = pgm_read_byte(s++))) 1522 charBounds(c, &x, &y, &minx, &miny, &maxx, &maxy); 1523 1524 if (maxx >= minx) { 1525 *x1 = minx; 1526 *w = maxx - minx + 1; 1527 } 1528 if (maxy >= miny) { 1529 *y1 = miny; 1530 *h = maxy - miny + 1; 1531 } 1532 } 1533 1534 /**************************************************************************/ 1535 /*! 1536 @brief Invert the display (ideally using built-in hardware command) 1537 @param i True if you want to invert, false to make 'normal' 1538 */ 1539 /**************************************************************************/ 1540 void Adafruit_GFX::invertDisplay(bool i) { 1541 // Do nothing, must be subclassed if supported by hardware 1542 (void)i; // disable -Wunused-parameter warning 1543 } 1544 1545 /***************************************************************************/ 1546 1547 /**************************************************************************/ 1548 /*! 1549 @brief Create a simple drawn button UI element 1550 */ 1551 /**************************************************************************/ 1552 Adafruit_GFX_Button::Adafruit_GFX_Button(void) { _gfx = 0; } 1553 1554 /**************************************************************************/ 1555 /*! 1556 @brief Initialize button with our desired color/size/settings 1557 @param gfx Pointer to our display so we can draw to it! 1558 @param x The X coordinate of the center of the button 1559 @param y The Y coordinate of the center of the button 1560 @param w Width of the buttton 1561 @param h Height of the buttton 1562 @param outline Color of the outline (16-bit 5-6-5 standard) 1563 @param fill Color of the button fill (16-bit 5-6-5 standard) 1564 @param textcolor Color of the button label (16-bit 5-6-5 standard) 1565 @param label Ascii string of the text inside the button 1566 @param textsize The font magnification of the label text 1567 */ 1568 /**************************************************************************/ 1569 // Classic initButton() function: pass center & size 1570 void Adafruit_GFX_Button::initButton(Adafruit_GFX *gfx, int16_t x, int16_t y, 1571 uint16_t w, uint16_t h, uint16_t outline, 1572 uint16_t fill, uint16_t textcolor, 1573 char *label, uint8_t textsize) { 1574 // Tweak arguments and pass to the newer initButtonUL() function... 1575 initButtonUL(gfx, x - (w / 2), y - (h / 2), w, h, outline, fill, textcolor, 1576 label, textsize); 1577 } 1578 1579 /**************************************************************************/ 1580 /*! 1581 @brief Initialize button with our desired color/size/settings 1582 @param gfx Pointer to our display so we can draw to it! 1583 @param x The X coordinate of the center of the button 1584 @param y The Y coordinate of the center of the button 1585 @param w Width of the buttton 1586 @param h Height of the buttton 1587 @param outline Color of the outline (16-bit 5-6-5 standard) 1588 @param fill Color of the button fill (16-bit 5-6-5 standard) 1589 @param textcolor Color of the button label (16-bit 5-6-5 standard) 1590 @param label Ascii string of the text inside the button 1591 @param textsize_x The font magnification in X-axis of the label text 1592 @param textsize_y The font magnification in Y-axis of the label text 1593 */ 1594 /**************************************************************************/ 1595 // Classic initButton() function: pass center & size 1596 void Adafruit_GFX_Button::initButton(Adafruit_GFX *gfx, int16_t x, int16_t y, 1597 uint16_t w, uint16_t h, uint16_t outline, 1598 uint16_t fill, uint16_t textcolor, 1599 char *label, uint8_t textsize_x, 1600 uint8_t textsize_y) { 1601 // Tweak arguments and pass to the newer initButtonUL() function... 1602 initButtonUL(gfx, x - (w / 2), y - (h / 2), w, h, outline, fill, textcolor, 1603 label, textsize_x, textsize_y); 1604 } 1605 1606 /**************************************************************************/ 1607 /*! 1608 @brief Initialize button with our desired color/size/settings, with 1609 upper-left coordinates 1610 @param gfx Pointer to our display so we can draw to it! 1611 @param x1 The X coordinate of the Upper-Left corner of the button 1612 @param y1 The Y coordinate of the Upper-Left corner of the button 1613 @param w Width of the buttton 1614 @param h Height of the buttton 1615 @param outline Color of the outline (16-bit 5-6-5 standard) 1616 @param fill Color of the button fill (16-bit 5-6-5 standard) 1617 @param textcolor Color of the button label (16-bit 5-6-5 standard) 1618 @param label Ascii string of the text inside the button 1619 @param textsize The font magnification of the label text 1620 */ 1621 /**************************************************************************/ 1622 void Adafruit_GFX_Button::initButtonUL(Adafruit_GFX *gfx, int16_t x1, 1623 int16_t y1, uint16_t w, uint16_t h, 1624 uint16_t outline, uint16_t fill, 1625 uint16_t textcolor, char *label, 1626 uint8_t textsize) { 1627 initButtonUL(gfx, x1, y1, w, h, outline, fill, textcolor, label, textsize, 1628 textsize); 1629 } 1630 1631 /**************************************************************************/ 1632 /*! 1633 @brief Initialize button with our desired color/size/settings, with 1634 upper-left coordinates 1635 @param gfx Pointer to our display so we can draw to it! 1636 @param x1 The X coordinate of the Upper-Left corner of the button 1637 @param y1 The Y coordinate of the Upper-Left corner of the button 1638 @param w Width of the buttton 1639 @param h Height of the buttton 1640 @param outline Color of the outline (16-bit 5-6-5 standard) 1641 @param fill Color of the button fill (16-bit 5-6-5 standard) 1642 @param textcolor Color of the button label (16-bit 5-6-5 standard) 1643 @param label Ascii string of the text inside the button 1644 @param textsize_x The font magnification in X-axis of the label text 1645 @param textsize_y The font magnification in Y-axis of the label text 1646 */ 1647 /**************************************************************************/ 1648 void Adafruit_GFX_Button::initButtonUL(Adafruit_GFX *gfx, int16_t x1, 1649 int16_t y1, uint16_t w, uint16_t h, 1650 uint16_t outline, uint16_t fill, 1651 uint16_t textcolor, char *label, 1652 uint8_t textsize_x, uint8_t textsize_y) { 1653 _x1 = x1; 1654 _y1 = y1; 1655 _w = w; 1656 _h = h; 1657 _outlinecolor = outline; 1658 _fillcolor = fill; 1659 _textcolor = textcolor; 1660 _textsize_x = textsize_x; 1661 _textsize_y = textsize_y; 1662 _gfx = gfx; 1663 strncpy(_label, label, 9); 1664 } 1665 1666 /**************************************************************************/ 1667 /*! 1668 @brief Draw the button on the screen 1669 @param inverted Whether to draw with fill/text swapped to indicate 1670 'pressed' 1671 */ 1672 /**************************************************************************/ 1673 void Adafruit_GFX_Button::drawButton(bool inverted) { 1674 uint16_t fill, outline, text; 1675 1676 if (!inverted) { 1677 fill = _fillcolor; 1678 outline = _outlinecolor; 1679 text = _textcolor; 1680 } else { 1681 fill = _textcolor; 1682 outline = _outlinecolor; 1683 text = _fillcolor; 1684 } 1685 1686 uint8_t r = min(_w, _h) / 4; // Corner radius 1687 _gfx->fillRoundRect(_x1, _y1, _w, _h, r, fill); 1688 _gfx->drawRoundRect(_x1, _y1, _w, _h, r, outline); 1689 1690 _gfx->setCursor(_x1 + (_w / 2) - (strlen(_label) * 3 * _textsize_x), 1691 _y1 + (_h / 2) - (4 * _textsize_y)); 1692 _gfx->setTextColor(text); 1693 _gfx->setTextSize(_textsize_x, _textsize_y); 1694 _gfx->print(_label); 1695 } 1696 1697 /**************************************************************************/ 1698 /*! 1699 @brief Helper to let us know if a coordinate is within the bounds of the 1700 button 1701 @param x The X coordinate to check 1702 @param y The Y coordinate to check 1703 @returns True if within button graphics outline 1704 */ 1705 /**************************************************************************/ 1706 bool Adafruit_GFX_Button::contains(int16_t x, int16_t y) { 1707 return ((x >= _x1) && (x < (int16_t)(_x1 + _w)) && (y >= _y1) && 1708 (y < (int16_t)(_y1 + _h))); 1709 } 1710 1711 /**************************************************************************/ 1712 /*! 1713 @brief Query whether the button was pressed since we last checked state 1714 @returns True if was not-pressed before, now is. 1715 */ 1716 /**************************************************************************/ 1717 bool Adafruit_GFX_Button::justPressed() { return (currstate && !laststate); } 1718 1719 /**************************************************************************/ 1720 /*! 1721 @brief Query whether the button was released since we last checked state 1722 @returns True if was pressed before, now is not. 1723 */ 1724 /**************************************************************************/ 1725 bool Adafruit_GFX_Button::justReleased() { return (!currstate && laststate); } 1726 1727 // ------------------------------------------------------------------------- 1728 1729 // GFXcanvas1, GFXcanvas8 and GFXcanvas16 (currently a WIP, don't get too 1730 // comfy with the implementation) provide 1-, 8- and 16-bit offscreen 1731 // canvases, the address of which can be passed to drawBitmap() or 1732 // pushColors() (the latter appears only in a couple of GFX-subclassed TFT 1733 // libraries at this time). This is here mostly to help with the recently- 1734 // added proportionally-spaced fonts; adds a way to refresh a section of the 1735 // screen without a massive flickering clear-and-redraw...but maybe you'll 1736 // find other uses too. VERY RAM-intensive, since the buffer is in MCU 1737 // memory and not the display driver...GXFcanvas1 might be minimally useful 1738 // on an Uno-class board, but this and the others are much more likely to 1739 // require at least a Mega or various recent ARM-type boards (recommended, 1740 // as the text+bitmap draw can be pokey). GFXcanvas1 requires 1 bit per 1741 // pixel (rounded up to nearest byte per scanline), GFXcanvas8 is 1 byte 1742 // per pixel (no scanline pad), and GFXcanvas16 uses 2 bytes per pixel (no 1743 // scanline pad). 1744 // NOT EXTENSIVELY TESTED YET. MAY CONTAIN WORST BUGS KNOWN TO HUMANKIND. 1745 1746 #ifdef __AVR__ 1747 // Bitmask tables of 0x80>>X and ~(0x80>>X), because X>>Y is slow on AVR 1748 const uint8_t PROGMEM GFXcanvas1::GFXsetBit[] = {0x80, 0x40, 0x20, 0x10, 1749 0x08, 0x04, 0x02, 0x01}; 1750 const uint8_t PROGMEM GFXcanvas1::GFXclrBit[] = {0x7F, 0xBF, 0xDF, 0xEF, 1751 0xF7, 0xFB, 0xFD, 0xFE}; 1752 #endif 1753 1754 /**************************************************************************/ 1755 /*! 1756 @brief Instatiate a GFX 1-bit canvas context for graphics 1757 @param w Display width, in pixels 1758 @param h Display height, in pixels 1759 */ 1760 /**************************************************************************/ 1761 GFXcanvas1::GFXcanvas1(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) { 1762 uint16_t bytes = ((w + 7) / 8) * h; 1763 if ((buffer = (uint8_t *)malloc(bytes))) { 1764 memset(buffer, 0, bytes); 1765 } 1766 } 1767 1768 /**************************************************************************/ 1769 /*! 1770 @brief Delete the canvas, free memory 1771 */ 1772 /**************************************************************************/ 1773 GFXcanvas1::~GFXcanvas1(void) { 1774 if (buffer) 1775 free(buffer); 1776 } 1777 1778 /**************************************************************************/ 1779 /*! 1780 @brief Draw a pixel to the canvas framebuffer 1781 @param x x coordinate 1782 @param y y coordinate 1783 @param color Binary (on or off) color to fill with 1784 */ 1785 /**************************************************************************/ 1786 void GFXcanvas1::drawPixel(int16_t x, int16_t y, uint16_t color) { 1787 if (buffer) { 1788 if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) 1789 return; 1790 1791 int16_t t; 1792 switch (rotation) { 1793 case 1: 1794 t = x; 1795 x = WIDTH - 1 - y; 1796 y = t; 1797 break; 1798 case 2: 1799 x = WIDTH - 1 - x; 1800 y = HEIGHT - 1 - y; 1801 break; 1802 case 3: 1803 t = x; 1804 x = y; 1805 y = HEIGHT - 1 - t; 1806 break; 1807 } 1808 1809 uint8_t *ptr = &buffer[(x / 8) + y * ((WIDTH + 7) / 8)]; 1810 #ifdef __AVR__ 1811 if (color) 1812 *ptr |= pgm_read_byte(&GFXsetBit[x & 7]); 1813 else 1814 *ptr &= pgm_read_byte(&GFXclrBit[x & 7]); 1815 #else 1816 if (color) 1817 *ptr |= 0x80 >> (x & 7); 1818 else 1819 *ptr &= ~(0x80 >> (x & 7)); 1820 #endif 1821 } 1822 } 1823 1824 /**********************************************************************/ 1825 /*! 1826 @brief Get the pixel color value at a given coordinate 1827 @param x x coordinate 1828 @param y y coordinate 1829 @returns The desired pixel's binary color value, either 0x1 (on) or 0x0 1830 (off) 1831 */ 1832 /**********************************************************************/ 1833 bool GFXcanvas1::getPixel(int16_t x, int16_t y) const { 1834 int16_t t; 1835 switch (rotation) { 1836 case 1: 1837 t = x; 1838 x = WIDTH - 1 - y; 1839 y = t; 1840 break; 1841 case 2: 1842 x = WIDTH - 1 - x; 1843 y = HEIGHT - 1 - y; 1844 break; 1845 case 3: 1846 t = x; 1847 x = y; 1848 y = HEIGHT - 1 - t; 1849 break; 1850 } 1851 return getRawPixel(x, y); 1852 } 1853 1854 /**********************************************************************/ 1855 /*! 1856 @brief Get the pixel color value at a given, unrotated coordinate. 1857 This method is intended for hardware drivers to get pixel value 1858 in physical coordinates. 1859 @param x x coordinate 1860 @param y y coordinate 1861 @returns The desired pixel's binary color value, either 0x1 (on) or 0x0 1862 (off) 1863 */ 1864 /**********************************************************************/ 1865 bool GFXcanvas1::getRawPixel(int16_t x, int16_t y) const { 1866 if ((x < 0) || (y < 0) || (x >= WIDTH) || (y >= HEIGHT)) 1867 return 0; 1868 if (this->getBuffer()) { 1869 uint8_t *buffer = this->getBuffer(); 1870 uint8_t *ptr = &buffer[(x / 8) + y * ((WIDTH + 7) / 8)]; 1871 1872 #ifdef __AVR__ 1873 return ((*ptr) & pgm_read_byte(&GFXsetBit[x & 7])) != 0; 1874 #else 1875 return ((*ptr) & (0x80 >> (x & 7))) != 0; 1876 #endif 1877 } 1878 return 0; 1879 } 1880 1881 /**************************************************************************/ 1882 /*! 1883 @brief Fill the framebuffer completely with one color 1884 @param color Binary (on or off) color to fill with 1885 */ 1886 /**************************************************************************/ 1887 void GFXcanvas1::fillScreen(uint16_t color) { 1888 if (buffer) { 1889 uint16_t bytes = ((WIDTH + 7) / 8) * HEIGHT; 1890 memset(buffer, color ? 0xFF : 0x00, bytes); 1891 } 1892 } 1893 1894 /**************************************************************************/ 1895 /*! 1896 @brief Speed optimized vertical line drawing 1897 @param x Line horizontal start point 1898 @param y Line vertical start point 1899 @param h Length of vertical line to be drawn, including first point 1900 @param color Color to fill with 1901 */ 1902 /**************************************************************************/ 1903 void GFXcanvas1::drawFastVLine(int16_t x, int16_t y, int16_t h, 1904 uint16_t color) { 1905 1906 if (h < 0) { // Convert negative heights to positive equivalent 1907 h *= -1; 1908 y -= h - 1; 1909 if (y < 0) { 1910 h += y; 1911 y = 0; 1912 } 1913 } 1914 1915 // Edge rejection (no-draw if totally off canvas) 1916 if ((x < 0) || (x >= width()) || (y >= height()) || ((y + h - 1) < 0)) { 1917 return; 1918 } 1919 1920 if (y < 0) { // Clip top 1921 h += y; 1922 y = 0; 1923 } 1924 if (y + h > height()) { // Clip bottom 1925 h = height() - y; 1926 } 1927 1928 if (getRotation() == 0) { 1929 drawFastRawVLine(x, y, h, color); 1930 } else if (getRotation() == 1) { 1931 int16_t t = x; 1932 x = WIDTH - 1 - y; 1933 y = t; 1934 x -= h - 1; 1935 drawFastRawHLine(x, y, h, color); 1936 } else if (getRotation() == 2) { 1937 x = WIDTH - 1 - x; 1938 y = HEIGHT - 1 - y; 1939 1940 y -= h - 1; 1941 drawFastRawVLine(x, y, h, color); 1942 } else if (getRotation() == 3) { 1943 int16_t t = x; 1944 x = y; 1945 y = HEIGHT - 1 - t; 1946 drawFastRawHLine(x, y, h, color); 1947 } 1948 } 1949 1950 /**************************************************************************/ 1951 /*! 1952 @brief Speed optimized horizontal line drawing 1953 @param x Line horizontal start point 1954 @param y Line vertical start point 1955 @param w Length of horizontal line to be drawn, including first point 1956 @param color Color to fill with 1957 */ 1958 /**************************************************************************/ 1959 void GFXcanvas1::drawFastHLine(int16_t x, int16_t y, int16_t w, 1960 uint16_t color) { 1961 if (w < 0) { // Convert negative widths to positive equivalent 1962 w *= -1; 1963 x -= w - 1; 1964 if (x < 0) { 1965 w += x; 1966 x = 0; 1967 } 1968 } 1969 1970 // Edge rejection (no-draw if totally off canvas) 1971 if ((y < 0) || (y >= height()) || (x >= width()) || ((x + w - 1) < 0)) { 1972 return; 1973 } 1974 1975 if (x < 0) { // Clip left 1976 w += x; 1977 x = 0; 1978 } 1979 if (x + w >= width()) { // Clip right 1980 w = width() - x; 1981 } 1982 1983 if (getRotation() == 0) { 1984 drawFastRawHLine(x, y, w, color); 1985 } else if (getRotation() == 1) { 1986 int16_t t = x; 1987 x = WIDTH - 1 - y; 1988 y = t; 1989 drawFastRawVLine(x, y, w, color); 1990 } else if (getRotation() == 2) { 1991 x = WIDTH - 1 - x; 1992 y = HEIGHT - 1 - y; 1993 1994 x -= w - 1; 1995 drawFastRawHLine(x, y, w, color); 1996 } else if (getRotation() == 3) { 1997 int16_t t = x; 1998 x = y; 1999 y = HEIGHT - 1 - t; 2000 y -= w - 1; 2001 drawFastRawVLine(x, y, w, color); 2002 } 2003 } 2004 2005 /**************************************************************************/ 2006 /*! 2007 @brief Speed optimized vertical line drawing into the raw canvas buffer 2008 @param x Line horizontal start point 2009 @param y Line vertical start point 2010 @param h length of vertical line to be drawn, including first point 2011 @param color Binary (on or off) color to fill with 2012 */ 2013 /**************************************************************************/ 2014 void GFXcanvas1::drawFastRawVLine(int16_t x, int16_t y, int16_t h, 2015 uint16_t color) { 2016 // x & y already in raw (rotation 0) coordinates, no need to transform. 2017 int16_t row_bytes = ((WIDTH + 7) / 8); 2018 uint8_t *buffer = this->getBuffer(); 2019 uint8_t *ptr = &buffer[(x / 8) + y * row_bytes]; 2020 2021 if (color > 0) { 2022 #ifdef __AVR__ 2023 uint8_t bit_mask = pgm_read_byte(&GFXsetBit[x & 7]); 2024 #else 2025 uint8_t bit_mask = (0x80 >> (x & 7)); 2026 #endif 2027 for (int16_t i = 0; i < h; i++) { 2028 *ptr |= bit_mask; 2029 ptr += row_bytes; 2030 } 2031 } else { 2032 #ifdef __AVR__ 2033 uint8_t bit_mask = pgm_read_byte(&GFXclrBit[x & 7]); 2034 #else 2035 uint8_t bit_mask = ~(0x80 >> (x & 7)); 2036 #endif 2037 for (int16_t i = 0; i < h; i++) { 2038 *ptr &= bit_mask; 2039 ptr += row_bytes; 2040 } 2041 } 2042 } 2043 2044 /**************************************************************************/ 2045 /*! 2046 @brief Speed optimized horizontal line drawing into the raw canvas buffer 2047 @param x Line horizontal start point 2048 @param y Line vertical start point 2049 @param w length of horizontal line to be drawn, including first point 2050 @param color Binary (on or off) color to fill with 2051 */ 2052 /**************************************************************************/ 2053 void GFXcanvas1::drawFastRawHLine(int16_t x, int16_t y, int16_t w, 2054 uint16_t color) { 2055 // x & y already in raw (rotation 0) coordinates, no need to transform. 2056 int16_t rowBytes = ((WIDTH + 7) / 8); 2057 uint8_t *buffer = this->getBuffer(); 2058 uint8_t *ptr = &buffer[(x / 8) + y * rowBytes]; 2059 size_t remainingWidthBits = w; 2060 2061 // check to see if first byte needs to be partially filled 2062 if ((x & 7) > 0) { 2063 // create bit mask for first byte 2064 uint8_t startByteBitMask = 0x00; 2065 for (int8_t i = (x & 7); ((i < 8) && (remainingWidthBits > 0)); i++) { 2066 #ifdef __AVR__ 2067 startByteBitMask |= pgm_read_byte(&GFXsetBit[i]); 2068 #else 2069 startByteBitMask |= (0x80 >> i); 2070 #endif 2071 remainingWidthBits--; 2072 } 2073 if (color > 0) { 2074 *ptr |= startByteBitMask; 2075 } else { 2076 *ptr &= ~startByteBitMask; 2077 } 2078 2079 ptr++; 2080 } 2081 2082 // do the next remainingWidthBits bits 2083 if (remainingWidthBits > 0) { 2084 size_t remainingWholeBytes = remainingWidthBits / 8; 2085 size_t lastByteBits = remainingWidthBits % 8; 2086 uint8_t wholeByteColor = color > 0 ? 0xFF : 0x00; 2087 2088 memset(ptr, wholeByteColor, remainingWholeBytes); 2089 2090 if (lastByteBits > 0) { 2091 uint8_t lastByteBitMask = 0x00; 2092 for (size_t i = 0; i < lastByteBits; i++) { 2093 #ifdef __AVR__ 2094 lastByteBitMask |= pgm_read_byte(&GFXsetBit[i]); 2095 #else 2096 lastByteBitMask |= (0x80 >> i); 2097 #endif 2098 } 2099 ptr += remainingWholeBytes; 2100 2101 if (color > 0) { 2102 *ptr |= lastByteBitMask; 2103 } else { 2104 *ptr &= ~lastByteBitMask; 2105 } 2106 } 2107 } 2108 } 2109 2110 /**************************************************************************/ 2111 /*! 2112 @brief Instatiate a GFX 8-bit canvas context for graphics 2113 @param w Display width, in pixels 2114 @param h Display height, in pixels 2115 */ 2116 /**************************************************************************/ 2117 GFXcanvas8::GFXcanvas8(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) { 2118 uint32_t bytes = w * h; 2119 if ((buffer = (uint8_t *)malloc(bytes))) { 2120 memset(buffer, 0, bytes); 2121 } 2122 } 2123 2124 /**************************************************************************/ 2125 /*! 2126 @brief Delete the canvas, free memory 2127 */ 2128 /**************************************************************************/ 2129 GFXcanvas8::~GFXcanvas8(void) { 2130 if (buffer) 2131 free(buffer); 2132 } 2133 2134 /**************************************************************************/ 2135 /*! 2136 @brief Draw a pixel to the canvas framebuffer 2137 @param x x coordinate 2138 @param y y coordinate 2139 @param color 8-bit Color to fill with. Only lower byte of uint16_t is used. 2140 */ 2141 /**************************************************************************/ 2142 void GFXcanvas8::drawPixel(int16_t x, int16_t y, uint16_t color) { 2143 if (buffer) { 2144 if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) 2145 return; 2146 2147 int16_t t; 2148 switch (rotation) { 2149 case 1: 2150 t = x; 2151 x = WIDTH - 1 - y; 2152 y = t; 2153 break; 2154 case 2: 2155 x = WIDTH - 1 - x; 2156 y = HEIGHT - 1 - y; 2157 break; 2158 case 3: 2159 t = x; 2160 x = y; 2161 y = HEIGHT - 1 - t; 2162 break; 2163 } 2164 2165 buffer[x + y * WIDTH] = color; 2166 } 2167 } 2168 2169 /**********************************************************************/ 2170 /*! 2171 @brief Get the pixel color value at a given coordinate 2172 @param x x coordinate 2173 @param y y coordinate 2174 @returns The desired pixel's 8-bit color value 2175 */ 2176 /**********************************************************************/ 2177 uint8_t GFXcanvas8::getPixel(int16_t x, int16_t y) const { 2178 int16_t t; 2179 switch (rotation) { 2180 case 1: 2181 t = x; 2182 x = WIDTH - 1 - y; 2183 y = t; 2184 break; 2185 case 2: 2186 x = WIDTH - 1 - x; 2187 y = HEIGHT - 1 - y; 2188 break; 2189 case 3: 2190 t = x; 2191 x = y; 2192 y = HEIGHT - 1 - t; 2193 break; 2194 } 2195 return getRawPixel(x, y); 2196 } 2197 2198 /**********************************************************************/ 2199 /*! 2200 @brief Get the pixel color value at a given, unrotated coordinate. 2201 This method is intended for hardware drivers to get pixel value 2202 in physical coordinates. 2203 @param x x coordinate 2204 @param y y coordinate 2205 @returns The desired pixel's 8-bit color value 2206 */ 2207 /**********************************************************************/ 2208 uint8_t GFXcanvas8::getRawPixel(int16_t x, int16_t y) const { 2209 if ((x < 0) || (y < 0) || (x >= WIDTH) || (y >= HEIGHT)) 2210 return 0; 2211 if (buffer) { 2212 return buffer[x + y * WIDTH]; 2213 } 2214 return 0; 2215 } 2216 2217 /**************************************************************************/ 2218 /*! 2219 @brief Fill the framebuffer completely with one color 2220 @param color 8-bit Color to fill with. Only lower byte of uint16_t is used. 2221 */ 2222 /**************************************************************************/ 2223 void GFXcanvas8::fillScreen(uint16_t color) { 2224 if (buffer) { 2225 memset(buffer, color, WIDTH * HEIGHT); 2226 } 2227 } 2228 2229 /**************************************************************************/ 2230 /*! 2231 @brief Speed optimized vertical line drawing 2232 @param x Line horizontal start point 2233 @param y Line vertical start point 2234 @param h Length of vertical line to be drawn, including first point 2235 @param color 8-bit Color to fill with. Only lower byte of uint16_t is 2236 used. 2237 */ 2238 /**************************************************************************/ 2239 void GFXcanvas8::drawFastVLine(int16_t x, int16_t y, int16_t h, 2240 uint16_t color) { 2241 if (h < 0) { // Convert negative heights to positive equivalent 2242 h *= -1; 2243 y -= h - 1; 2244 if (y < 0) { 2245 h += y; 2246 y = 0; 2247 } 2248 } 2249 2250 // Edge rejection (no-draw if totally off canvas) 2251 if ((x < 0) || (x >= width()) || (y >= height()) || ((y + h - 1) < 0)) { 2252 return; 2253 } 2254 2255 if (y < 0) { // Clip top 2256 h += y; 2257 y = 0; 2258 } 2259 if (y + h > height()) { // Clip bottom 2260 h = height() - y; 2261 } 2262 2263 if (getRotation() == 0) { 2264 drawFastRawVLine(x, y, h, color); 2265 } else if (getRotation() == 1) { 2266 int16_t t = x; 2267 x = WIDTH - 1 - y; 2268 y = t; 2269 x -= h - 1; 2270 drawFastRawHLine(x, y, h, color); 2271 } else if (getRotation() == 2) { 2272 x = WIDTH - 1 - x; 2273 y = HEIGHT - 1 - y; 2274 2275 y -= h - 1; 2276 drawFastRawVLine(x, y, h, color); 2277 } else if (getRotation() == 3) { 2278 int16_t t = x; 2279 x = y; 2280 y = HEIGHT - 1 - t; 2281 drawFastRawHLine(x, y, h, color); 2282 } 2283 } 2284 2285 /**************************************************************************/ 2286 /*! 2287 @brief Speed optimized horizontal line drawing 2288 @param x Line horizontal start point 2289 @param y Line vertical start point 2290 @param w Length of horizontal line to be drawn, including 1st point 2291 @param color 8-bit Color to fill with. Only lower byte of uint16_t is 2292 used. 2293 */ 2294 /**************************************************************************/ 2295 void GFXcanvas8::drawFastHLine(int16_t x, int16_t y, int16_t w, 2296 uint16_t color) { 2297 2298 if (w < 0) { // Convert negative widths to positive equivalent 2299 w *= -1; 2300 x -= w - 1; 2301 if (x < 0) { 2302 w += x; 2303 x = 0; 2304 } 2305 } 2306 2307 // Edge rejection (no-draw if totally off canvas) 2308 if ((y < 0) || (y >= height()) || (x >= width()) || ((x + w - 1) < 0)) { 2309 return; 2310 } 2311 2312 if (x < 0) { // Clip left 2313 w += x; 2314 x = 0; 2315 } 2316 if (x + w >= width()) { // Clip right 2317 w = width() - x; 2318 } 2319 2320 if (getRotation() == 0) { 2321 drawFastRawHLine(x, y, w, color); 2322 } else if (getRotation() == 1) { 2323 int16_t t = x; 2324 x = WIDTH - 1 - y; 2325 y = t; 2326 drawFastRawVLine(x, y, w, color); 2327 } else if (getRotation() == 2) { 2328 x = WIDTH - 1 - x; 2329 y = HEIGHT - 1 - y; 2330 2331 x -= w - 1; 2332 drawFastRawHLine(x, y, w, color); 2333 } else if (getRotation() == 3) { 2334 int16_t t = x; 2335 x = y; 2336 y = HEIGHT - 1 - t; 2337 y -= w - 1; 2338 drawFastRawVLine(x, y, w, color); 2339 } 2340 } 2341 2342 /**************************************************************************/ 2343 /*! 2344 @brief Speed optimized vertical line drawing into the raw canvas buffer 2345 @param x Line horizontal start point 2346 @param y Line vertical start point 2347 @param h length of vertical line to be drawn, including first point 2348 @param color 8-bit Color to fill with. Only lower byte of uint16_t is 2349 used. 2350 */ 2351 /**************************************************************************/ 2352 void GFXcanvas8::drawFastRawVLine(int16_t x, int16_t y, int16_t h, 2353 uint16_t color) { 2354 // x & y already in raw (rotation 0) coordinates, no need to transform. 2355 uint8_t *buffer_ptr = buffer + y * WIDTH + x; 2356 for (int16_t i = 0; i < h; i++) { 2357 (*buffer_ptr) = color; 2358 buffer_ptr += WIDTH; 2359 } 2360 } 2361 2362 /**************************************************************************/ 2363 /*! 2364 @brief Speed optimized horizontal line drawing into the raw canvas buffer 2365 @param x Line horizontal start point 2366 @param y Line vertical start point 2367 @param w length of horizontal line to be drawn, including first point 2368 @param color 8-bit Color to fill with. Only lower byte of uint16_t is 2369 used. 2370 */ 2371 /**************************************************************************/ 2372 void GFXcanvas8::drawFastRawHLine(int16_t x, int16_t y, int16_t w, 2373 uint16_t color) { 2374 // x & y already in raw (rotation 0) coordinates, no need to transform. 2375 memset(buffer + y * WIDTH + x, color, w); 2376 } 2377 2378 /**************************************************************************/ 2379 /*! 2380 @brief Instatiate a GFX 16-bit canvas context for graphics 2381 @param w Display width, in pixels 2382 @param h Display height, in pixels 2383 */ 2384 /**************************************************************************/ 2385 GFXcanvas16::GFXcanvas16(uint16_t w, uint16_t h) : Adafruit_GFX(w, h) { 2386 uint32_t bytes = w * h * 2; 2387 if ((buffer = (uint16_t *)malloc(bytes))) { 2388 memset(buffer, 0, bytes); 2389 } 2390 } 2391 2392 /**************************************************************************/ 2393 /*! 2394 @brief Delete the canvas, free memory 2395 */ 2396 /**************************************************************************/ 2397 GFXcanvas16::~GFXcanvas16(void) { 2398 if (buffer) 2399 free(buffer); 2400 } 2401 2402 /**************************************************************************/ 2403 /*! 2404 @brief Draw a pixel to the canvas framebuffer 2405 @param x x coordinate 2406 @param y y coordinate 2407 @param color 16-bit 5-6-5 Color to fill with 2408 */ 2409 /**************************************************************************/ 2410 void GFXcanvas16::drawPixel(int16_t x, int16_t y, uint16_t color) { 2411 if (buffer) { 2412 if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height)) 2413 return; 2414 2415 int16_t t; 2416 switch (rotation) { 2417 case 1: 2418 t = x; 2419 x = WIDTH - 1 - y; 2420 y = t; 2421 break; 2422 case 2: 2423 x = WIDTH - 1 - x; 2424 y = HEIGHT - 1 - y; 2425 break; 2426 case 3: 2427 t = x; 2428 x = y; 2429 y = HEIGHT - 1 - t; 2430 break; 2431 } 2432 2433 buffer[x + y * WIDTH] = color; 2434 } 2435 } 2436 2437 /**********************************************************************/ 2438 /*! 2439 @brief Get the pixel color value at a given coordinate 2440 @param x x coordinate 2441 @param y y coordinate 2442 @returns The desired pixel's 16-bit 5-6-5 color value 2443 */ 2444 /**********************************************************************/ 2445 uint16_t GFXcanvas16::getPixel(int16_t x, int16_t y) const { 2446 int16_t t; 2447 switch (rotation) { 2448 case 1: 2449 t = x; 2450 x = WIDTH - 1 - y; 2451 y = t; 2452 break; 2453 case 2: 2454 x = WIDTH - 1 - x; 2455 y = HEIGHT - 1 - y; 2456 break; 2457 case 3: 2458 t = x; 2459 x = y; 2460 y = HEIGHT - 1 - t; 2461 break; 2462 } 2463 return getRawPixel(x, y); 2464 } 2465 2466 /**********************************************************************/ 2467 /*! 2468 @brief Get the pixel color value at a given, unrotated coordinate. 2469 This method is intended for hardware drivers to get pixel value 2470 in physical coordinates. 2471 @param x x coordinate 2472 @param y y coordinate 2473 @returns The desired pixel's 16-bit 5-6-5 color value 2474 */ 2475 /**********************************************************************/ 2476 uint16_t GFXcanvas16::getRawPixel(int16_t x, int16_t y) const { 2477 if ((x < 0) || (y < 0) || (x >= WIDTH) || (y >= HEIGHT)) 2478 return 0; 2479 if (buffer) { 2480 return buffer[x + y * WIDTH]; 2481 } 2482 return 0; 2483 } 2484 2485 /**************************************************************************/ 2486 /*! 2487 @brief Fill the framebuffer completely with one color 2488 @param color 16-bit 5-6-5 Color to fill with 2489 */ 2490 /**************************************************************************/ 2491 void GFXcanvas16::fillScreen(uint16_t color) { 2492 if (buffer) { 2493 uint8_t hi = color >> 8, lo = color & 0xFF; 2494 if (hi == lo) { 2495 memset(buffer, lo, WIDTH * HEIGHT * 2); 2496 } else { 2497 uint32_t i, pixels = WIDTH * HEIGHT; 2498 for (i = 0; i < pixels; i++) 2499 buffer[i] = color; 2500 } 2501 } 2502 } 2503 2504 /**************************************************************************/ 2505 /*! 2506 @brief Reverses the "endian-ness" of each 16-bit pixel within the 2507 canvas; little-endian to big-endian, or big-endian to little. 2508 Most microcontrollers (such as SAMD) are little-endian, while 2509 most displays tend toward big-endianness. All the drawing 2510 functions (including RGB bitmap drawing) take care of this 2511 automatically, but some specialized code (usually involving 2512 DMA) can benefit from having pixel data already in the 2513 display-native order. Note that this does NOT convert to a 2514 SPECIFIC endian-ness, it just flips the bytes within each word. 2515 */ 2516 /**************************************************************************/ 2517 void GFXcanvas16::byteSwap(void) { 2518 if (buffer) { 2519 uint32_t i, pixels = WIDTH * HEIGHT; 2520 for (i = 0; i < pixels; i++) 2521 buffer[i] = __builtin_bswap16(buffer[i]); 2522 } 2523 } 2524 2525 /**************************************************************************/ 2526 /*! 2527 @brief Speed optimized vertical line drawing 2528 @param x Line horizontal start point 2529 @param y Line vertical start point 2530 @param h length of vertical line to be drawn, including first point 2531 @param color color 16-bit 5-6-5 Color to draw line with 2532 */ 2533 /**************************************************************************/ 2534 void GFXcanvas16::drawFastVLine(int16_t x, int16_t y, int16_t h, 2535 uint16_t color) { 2536 if (h < 0) { // Convert negative heights to positive equivalent 2537 h *= -1; 2538 y -= h - 1; 2539 if (y < 0) { 2540 h += y; 2541 y = 0; 2542 } 2543 } 2544 2545 // Edge rejection (no-draw if totally off canvas) 2546 if ((x < 0) || (x >= width()) || (y >= height()) || ((y + h - 1) < 0)) { 2547 return; 2548 } 2549 2550 if (y < 0) { // Clip top 2551 h += y; 2552 y = 0; 2553 } 2554 if (y + h > height()) { // Clip bottom 2555 h = height() - y; 2556 } 2557 2558 if (getRotation() == 0) { 2559 drawFastRawVLine(x, y, h, color); 2560 } else if (getRotation() == 1) { 2561 int16_t t = x; 2562 x = WIDTH - 1 - y; 2563 y = t; 2564 x -= h - 1; 2565 drawFastRawHLine(x, y, h, color); 2566 } else if (getRotation() == 2) { 2567 x = WIDTH - 1 - x; 2568 y = HEIGHT - 1 - y; 2569 2570 y -= h - 1; 2571 drawFastRawVLine(x, y, h, color); 2572 } else if (getRotation() == 3) { 2573 int16_t t = x; 2574 x = y; 2575 y = HEIGHT - 1 - t; 2576 drawFastRawHLine(x, y, h, color); 2577 } 2578 } 2579 2580 /**************************************************************************/ 2581 /*! 2582 @brief Speed optimized horizontal line drawing 2583 @param x Line horizontal start point 2584 @param y Line vertical start point 2585 @param w Length of horizontal line to be drawn, including 1st point 2586 @param color Color 16-bit 5-6-5 Color to draw line with 2587 */ 2588 /**************************************************************************/ 2589 void GFXcanvas16::drawFastHLine(int16_t x, int16_t y, int16_t w, 2590 uint16_t color) { 2591 if (w < 0) { // Convert negative widths to positive equivalent 2592 w *= -1; 2593 x -= w - 1; 2594 if (x < 0) { 2595 w += x; 2596 x = 0; 2597 } 2598 } 2599 2600 // Edge rejection (no-draw if totally off canvas) 2601 if ((y < 0) || (y >= height()) || (x >= width()) || ((x + w - 1) < 0)) { 2602 return; 2603 } 2604 2605 if (x < 0) { // Clip left 2606 w += x; 2607 x = 0; 2608 } 2609 if (x + w >= width()) { // Clip right 2610 w = width() - x; 2611 } 2612 2613 if (getRotation() == 0) { 2614 drawFastRawHLine(x, y, w, color); 2615 } else if (getRotation() == 1) { 2616 int16_t t = x; 2617 x = WIDTH - 1 - y; 2618 y = t; 2619 drawFastRawVLine(x, y, w, color); 2620 } else if (getRotation() == 2) { 2621 x = WIDTH - 1 - x; 2622 y = HEIGHT - 1 - y; 2623 2624 x -= w - 1; 2625 drawFastRawHLine(x, y, w, color); 2626 } else if (getRotation() == 3) { 2627 int16_t t = x; 2628 x = y; 2629 y = HEIGHT - 1 - t; 2630 y -= w - 1; 2631 drawFastRawVLine(x, y, w, color); 2632 } 2633 } 2634 2635 /**************************************************************************/ 2636 /*! 2637 @brief Speed optimized vertical line drawing into the raw canvas buffer 2638 @param x Line horizontal start point 2639 @param y Line vertical start point 2640 @param h length of vertical line to be drawn, including first point 2641 @param color color 16-bit 5-6-5 Color to draw line with 2642 */ 2643 /**************************************************************************/ 2644 void GFXcanvas16::drawFastRawVLine(int16_t x, int16_t y, int16_t h, 2645 uint16_t color) { 2646 // x & y already in raw (rotation 0) coordinates, no need to transform. 2647 uint16_t *buffer_ptr = buffer + y * WIDTH + x; 2648 for (int16_t i = 0; i < h; i++) { 2649 (*buffer_ptr) = color; 2650 buffer_ptr += WIDTH; 2651 } 2652 } 2653 2654 /**************************************************************************/ 2655 /*! 2656 @brief Speed optimized horizontal line drawing into the raw canvas buffer 2657 @param x Line horizontal start point 2658 @param y Line vertical start point 2659 @param w length of horizontal line to be drawn, including first point 2660 @param color color 16-bit 5-6-5 Color to draw line with 2661 */ 2662 /**************************************************************************/ 2663 void GFXcanvas16::drawFastRawHLine(int16_t x, int16_t y, int16_t w, 2664 uint16_t color) { 2665 // x & y already in raw (rotation 0) coordinates, no need to transform. 2666 uint32_t buffer_index = y * WIDTH + x; 2667 for (uint32_t i = buffer_index; i < buffer_index + w; i++) { 2668 buffer[i] = color; 2669 } 2670 }