LCDGFX LCD display driver  1.1.5
This library is developed to control SSD1306/SSD1325/SSD1327/SSD1331/SSD1351/IL9163/PCD8554 RGB i2c/spi LED displays
ssd1306_common.inl
1 /*
2  MIT License
3 
4  Copyright (c) 2016-2020, Alexey Dynda
5 
6  Permission is hereby granted, free of charge, to any person obtaining a copy
7  of this software and associated documentation files (the "Software"), to deal
8  in the Software without restriction, including without limitation the rights
9  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  copies of the Software, and to permit persons to whom the Software is
11  furnished to do so, subject to the following conditions:
12 
13  The above copyright notice and this permission notice shall be included in all
14  copies or substantial portions of the Software.
15 
16  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  SOFTWARE.
23 */
24 
28 
29 #include "lcd_hal/io.h"
30 #include <stdio.h>
31 
32 #if 0
33 
34 uint8_t ssd1306_printFixed(uint8_t xpos, uint8_t y, const char *ch, EFontStyle style)
35 {
36  uint8_t i, j=0;
37  uint8_t text_index = 0;
38  uint8_t page_offset = 0;
39  uint8_t x = xpos;
40  y >>= 3;
41  ssd1306_lcd.set_block(xpos, y, ssd1306_lcd.width - xpos);
42  for(;;)
43  {
44  uint8_t ldata;
45  if ( (x > ssd1306_lcd.width - s_fixedFont.h.width) || (ch[j] == '\0') )
46  {
47  x = xpos;
48  y++;
49  if (y >= (ssd1306_lcd.height >> 3))
50  {
51  break;
52  }
53  page_offset++;
54  if (page_offset == s_fixedFont.pages)
55  {
56  text_index = j;
57  page_offset = 0;
58  if (ch[j] == '\0')
59  {
60  break;
61  }
62  }
63  else
64  {
65  j = text_index;
66  }
67  ssd1306_intf.stop();
68  ssd1306_lcd.set_block(xpos, y, ssd1306_lcd.width - xpos);
69  }
70  uint16_t unicode;
71  do
72  {
73  unicode = ssd1306_unicode16FromUtf8(ch[j]);
74  j++;
75  } while ( unicode == SSD1306_MORE_CHARS_REQUIRED );
76  SCharInfo char_info;
77  ssd1306_getCharBitmap(unicode, &char_info);
78  ldata = 0;
79  x += char_info.width + char_info.spacing;
80  if (char_info.height > page_offset * 8)
81  {
82  char_info.glyph += page_offset * char_info.width;
83  for( i = char_info.width; i>0; i--)
84  {
85  uint8_t data;
86  if ( style == STYLE_NORMAL )
87  {
88  data = pgm_read_byte(&char_info.glyph[0]);
89  }
90  else if ( style == STYLE_BOLD )
91  {
92  uint8_t temp = pgm_read_byte(&char_info.glyph[0]);
93  data = temp | ldata;
94  ldata = temp;
95  }
96  else
97  {
98  uint8_t temp = pgm_read_byte(&char_info.glyph[1]);
99  data = (temp & 0xF0) | ldata;
100  ldata = (temp & 0x0F);
101  }
102  ssd1306_lcd.send_pixels1(data^s_ssd1306_invertByte);
103  char_info.glyph++;
104  }
105  }
106  else
107  {
108  char_info.spacing += char_info.width;
109  }
110  for (i = 0; i < char_info.spacing; i++)
111  ssd1306_lcd.send_pixels1(s_ssd1306_invertByte);
112  }
113  ssd1306_intf.stop();
114  return j;
115 }
116 
117 uint8_t ssd1306_printFixed_oldStyle(uint8_t xpos, uint8_t y, const char *ch, EFontStyle style)
118 {
119  uint8_t i, j=0;
120  uint8_t text_index = 0;
121  uint8_t page_offset = 0;
122  uint8_t x = xpos;
123  y >>= 3;
124  ssd1306_lcd.set_block(xpos, y, ssd1306_lcd.width - xpos);
125  for(;;)
126  {
127  uint8_t c;
128  uint8_t ldata;
129  uint16_t offset;
130  if( (x > ssd1306_lcd.width - s_fixedFont.h.width) || (ch[j] == '\0') )
131  {
132  x = xpos;
133  y++;
134  if (y >= (ssd1306_lcd.height >> 3))
135  {
136  break;
137  }
138  page_offset++;
139  if (page_offset == s_fixedFont.pages)
140  {
141  text_index = j;
142  page_offset = 0;
143  if (ch[j] == '\0')
144  {
145  break;
146  }
147  }
148  else
149  {
150  j = text_index;
151  }
152  ssd1306_intf.stop();
153  ssd1306_lcd.set_block(xpos, y, ssd1306_lcd.width - xpos);
154  }
155  c = ch[j];
156  if ( c >= s_fixedFont.h.ascii_offset )
157  {
158  c -= s_fixedFont.h.ascii_offset;
159  }
160  ldata = 0;
161  offset = (c * s_fixedFont.pages + page_offset) * s_fixedFont.h.width;
162  for( i=s_fixedFont.h.width; i>0; i--)
163  {
164  uint8_t data;
165  if ( style == STYLE_NORMAL )
166  {
167  data = pgm_read_byte(&s_fixedFont.primary_table[offset]);
168  }
169  else if ( style == STYLE_BOLD )
170  {
171  uint8_t temp = pgm_read_byte(&s_fixedFont.primary_table[offset]);
172  data = temp | ldata;
173  ldata = temp;
174  }
175  else
176  {
177  uint8_t temp = pgm_read_byte(&s_fixedFont.primary_table[offset + 1]);
178  data = (temp & 0xF0) | ldata;
179  ldata = (temp & 0x0F);
180  }
181  ssd1306_lcd.send_pixels1(data^s_ssd1306_invertByte);
182  offset++;
183  }
184  x += s_fixedFont.h.width;
185  j++;
186  }
187  ssd1306_intf.stop();
188  return j;
189 }
190 
191 uint8_t ssd1306_printFixed2x(uint8_t xpos, uint8_t y, const char ch[], EFontStyle style)
192 {
193  uint8_t i, j=0;
194  uint8_t text_index = 0;
195  uint8_t page_offset = 0;
196  uint8_t x = xpos;
197  y >>= 3;
198  ssd1306_lcd.set_block(xpos, y, ssd1306_lcd.width - xpos);
199  for(;;)
200  {
201  uint8_t c;
202  uint8_t ldata;
203  uint16_t offset;
204  if( (x > ssd1306_lcd.width - (s_fixedFont.h.width << 1)) || (ch[j] == '\0') )
205  {
206  x = xpos;
207  y++;
208  if (y >= (ssd1306_lcd.height >> 3))
209  {
210  break;
211  }
212  page_offset++;
213  if (page_offset == (s_fixedFont.pages << 1))
214  {
215  text_index = j;
216  page_offset = 0;
217  if (ch[j] == '\0')
218  {
219  break;
220  }
221  }
222  else
223  {
224  j = text_index;
225  }
226  ssd1306_intf.stop();
227  ssd1306_lcd.set_block(xpos, y, ssd1306_lcd.width - xpos);
228  }
229  c = ch[j];
230  if ( c >= 32 )
231  {
232  c -= 32;
233  }
234  ldata = 0;
235  offset = (c * s_fixedFont.pages + (page_offset >> 1)) * s_fixedFont.h.width;
236  for( i=s_fixedFont.h.width; i>0; i--)
237  {
238  uint8_t data;
239  if ( style == STYLE_NORMAL )
240  {
241  data = pgm_read_byte(&s_fixedFont.primary_table[offset]);
242  }
243  else if ( style == STYLE_BOLD )
244  {
245  uint8_t temp = pgm_read_byte(&s_fixedFont.primary_table[offset]);
246  data = temp | ldata;
247  ldata = temp;
248  }
249  else
250  {
251  uint8_t temp = pgm_read_byte(&s_fixedFont.primary_table[offset + 1]);
252  data = (temp & 0xF0) | ldata;
253  ldata = (temp & 0x0F);
254  }
255  if (page_offset & 1) data >>= 4;
256  data = ((data & 0x01) ? 0x03: 0x00) |
257  ((data & 0x02) ? 0x0C: 0x00) |
258  ((data & 0x04) ? 0x30: 0x00) |
259  ((data & 0x08) ? 0xC0: 0x00);
260  ssd1306_lcd.send_pixels1(data^s_ssd1306_invertByte);
261  ssd1306_lcd.send_pixels1(data^s_ssd1306_invertByte);
262  offset++;
263  }
264  x += (s_fixedFont.h.width << 1);
265  j++;
266  }
267  ssd1306_intf.stop();
268  return j;
269 }
270 
271 
272 uint8_t ssd1306_printFixedN(uint8_t xpos, uint8_t y, const char ch[], EFontStyle style, uint8_t factor)
273 {
274  uint8_t i, j=0;
275  uint8_t text_index = 0;
276  uint8_t page_offset = 0;
277  uint8_t x = xpos;
278  y >>= 3;
279  ssd1306_lcd.set_block(xpos, y, ssd1306_lcd.width - xpos);
280  for(;;)
281  {
282  uint8_t ldata;
283  if( (x > ssd1306_lcd.width - (s_fixedFont.h.width << factor)) || (ch[j] == '\0') )
284  {
285  x = xpos;
286  y++;
287  if (y >= (ssd1306_lcd.height >> 3))
288  {
289  break;
290  }
291  page_offset++;
292  if (page_offset == (s_fixedFont.pages << factor))
293  {
294  text_index = j;
295  page_offset = 0;
296  if (ch[j] == '\0')
297  {
298  break;
299  }
300  }
301  else
302  {
303  j = text_index;
304  }
305  ssd1306_intf.stop();
306  ssd1306_lcd.set_block(xpos, y, ssd1306_lcd.width - xpos);
307  }
308  uint16_t unicode;
309  do
310  {
311  unicode = ssd1306_unicode16FromUtf8(ch[j]);
312  j++;
313  } while ( unicode == SSD1306_MORE_CHARS_REQUIRED );
314  SCharInfo char_info;
315  ssd1306_getCharBitmap(unicode, &char_info);
316  ldata = 0;
317  x += ((char_info.width + char_info.spacing) << factor);
318  if (char_info.height > (page_offset >> factor) * 8)
319  {
320  char_info.glyph += (page_offset >> factor) * char_info.width;
321  for( i=char_info.width; i>0; i--)
322  {
323  uint8_t data;
324  if ( style == STYLE_NORMAL )
325  {
326  data = pgm_read_byte(char_info.glyph);
327  }
328  else if ( style == STYLE_BOLD )
329  {
330  uint8_t temp = pgm_read_byte(char_info.glyph);
331  data = temp | ldata;
332  ldata = temp;
333  }
334  else
335  {
336  uint8_t temp = pgm_read_byte(char_info.glyph+1);
337  data = (temp & 0xF0) | ldata;
338  ldata = (temp & 0x0F);
339  }
340  if ( factor > 0 )
341  {
342  uint8_t accum = 0;
343  uint8_t mask = ~((0xFF) << (1<<factor));
344  // N=0 -> right shift is always 0
345  // N=1 -> right shift goes through 0, 4
346  // N=2 -> right shift goes through 0, 2, 4, 6
347  // N=3 -> right shift goes through 0, 1, 2, 3, 4, 5, 6, 7
348  data >>= ((page_offset & ((1<<factor) - 1))<<(3-factor));
349  for (uint8_t idx = 0; idx < 1<<(3-factor); idx++)
350  {
351  accum |= (((data>>idx) & 0x01) ? (mask<<(idx<<factor)) : 0);
352  }
353  data = accum;
354  }
355  for (uint8_t z=(1<<factor); z>0; z--)
356  {
357  ssd1306_lcd.send_pixels1(data^s_ssd1306_invertByte);
358  }
359  char_info.glyph++;
360  }
361  }
362  else
363  {
364  char_info.spacing += char_info.width;
365  }
366  for (i = 0; i < (char_info.spacing << factor); i++)
367  ssd1306_lcd.send_pixels1(s_ssd1306_invertByte);
368  }
369  ssd1306_intf.stop();
370  return j;
371 }
372 
373 void ssd1306_putPixel_delayed(uint8_t x, uint8_t y, uint8_t complete)
374 {
375  static uint8_t lx = 0, ly = 0xFF;
376  static uint8_t pixels = 0;
377  if ((lx != x) || ((ly & 0xF8) != (y & 0xF8)) || (complete))
378  {
379  if (ly != 0xFF)
380  {
381  ssd1306_putPixels( lx, ly, pixels );
382  }
383  pixels = 0;
384  ly = 0xFF;
385  }
386  if ( !complete )
387  {
388  pixels |= (1 << (y & 0x07));
389  lx = x; ly = y;
390  }
391 }
392 
393 void ssd1306_drawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
394 {
395  lcduint_t dx = x1 > x2 ? (x1 - x2): (x2 - x1);
396  lcduint_t dy = y1 > y2 ? (y1 - y2): (y2 - y1);
397  lcduint_t err = 0;
398  if (dy > dx)
399  {
400  if (y1 > y2)
401  {
402  ssd1306_swap_data(x1, x2, uint8_t);
403  ssd1306_swap_data(y1, y2, uint8_t);
404  }
405  for(; y1<=y2; y1++)
406  {
407  err += dx;
408  if (err >= dy)
409  {
410  err -= dy;
411  x1 < x2 ? x1++: x1--;
412  }
413  ssd1306_putPixel_delayed( x1, y1, 0 );
414  }
415  ssd1306_putPixel_delayed( 0, 0, 1 );
416  }
417  else
418  {
419  if (x1 > x2)
420  {
421  ssd1306_swap_data(x1, x2, uint8_t);
422  ssd1306_swap_data(y1, y2, uint8_t);
423  }
424  for(; x1<=x2; x1++)
425  {
426  err += dy;
427  if (err >= dx)
428  {
429  err -= dx;
430  if (y1 < y2) y1++; else y1--;
431  }
432  ssd1306_putPixel( x1, y1 );
433  }
434  }
435 }
436 
437 void ssd1306_drawBufferFast(lcdint_t x, lcdint_t y, lcduint_t w, lcduint_t h, const uint8_t *buf)
438 {
439  uint8_t j;
440  ssd1306_lcd.set_block(x, y >> 3, w);
441  for(j=(h >> 3); j>0; j--)
442  {
443  ssd1306_lcd.send_pixels_buffer1(buf,w);
444  buf+=w;
445  ssd1306_lcd.next_page();
446  }
447  ssd1306_intf.stop();
448 }
449 
450 void gfx_drawMonoBitmap(lcdint_t x, lcdint_t y, lcduint_t w, lcduint_t h, const uint8_t *buf)
451 {
452  lcduint_t origin_width = w;
453  uint8_t offset = y & 0x07;
454  uint8_t complexFlag = 0;
455  uint8_t mainFlag = 1;
456  uint8_t max_pages;
457  uint8_t pages;
458  lcduint_t i, j;
459  if (y + (lcdint_t)h <= 0) return;
460  if (y >= (lcdint_t)ssd1306_lcd.height) return;
461  if (x + (lcdint_t)w <= 0) return;
462  if (x >= (lcdint_t)ssd1306_lcd.width) return;
463  if (y < 0)
464  {
465  buf += ((lcduint_t)((-y) + 7) >> 3) * w;
466  h += y;
467  y = 0;
468  complexFlag = 1;
469  }
470  if (x < 0)
471  {
472  buf += -x;
473  w += x;
474  x = 0;
475  }
476  max_pages = (lcduint_t)(h + 15 - offset) >> 3;
477  if ((lcduint_t)((lcduint_t)y + h) > (lcduint_t)ssd1306_lcd.height)
478  {
479  h = (lcduint_t)(ssd1306_lcd.height - (lcduint_t)y);
480  }
481  if ((lcduint_t)((lcduint_t)x + w) > (lcduint_t)ssd1306_lcd.width)
482  {
483  w = (lcduint_t)(ssd1306_lcd.width - (lcduint_t)x);
484  }
485  pages = ((y + h - 1) >> 3) - (y >> 3) + 1;
486 
487  ssd1306_lcd.set_block(x, y >> 3, w);
488  for(j=0; j < pages; j++)
489  {
490  if ( j == (lcduint_t)(max_pages - 1) ) mainFlag = !offset;
491  for( i=w; i > 0; i--)
492  {
493  uint8_t data = 0;
494  if ( mainFlag ) data |= (pgm_read_byte(buf) << offset);
495  if ( complexFlag ) data |= (pgm_read_byte(buf - origin_width) >> (8 - offset));
496  buf++;
497  ssd1306_lcd.send_pixels1(s_ssd1306_invertByte^data);
498  }
499  buf += origin_width - w;
500  complexFlag = offset;
501  ssd1306_lcd.next_page();
502  }
503  ssd1306_intf.stop();
504 }
505 
506 
507 void ssd1306_setFont6x8(const uint8_t * progmemFont)
508 {
509  s_font6x8 = progmemFont + 4;
510 }
511 
512 #endif
513 
515 //
516 // COMMON GRAPHICS
517 //
519 
520 template <class O, class I> void NanoDisplayOps<O, I>::putPixel(const NanoPoint &p)
521 {
522  this->putPixel(p.x, p.y);
523 }
524 
525 template <class O, class I> void NanoDisplayOps<O, I>::drawRect(lcdint_t x1, lcdint_t y1, lcdint_t x2, lcdint_t y2)
526 {
527  this->drawHLine(x1, y1, x2);
528  this->drawHLine(x1, y2, x2);
529  this->drawVLine(x1, y1, y2);
530  this->drawVLine(x2, y1, y2);
531 }
532 
533 template <class O, class I> void NanoDisplayOps<O, I>::drawRect(const NanoRect &rect)
534 {
535  this->drawRect(rect.p1.x, rect.p1.y, rect.p2.x, rect.p2.y);
536 }
537 
538 template <class O, class I> void NanoDisplayOps<O, I>::drawLine(lcdint_t x1, lcdint_t y1, lcdint_t x2, lcdint_t y2)
539 {
540  lcduint_t dx = x1 > x2 ? (x1 - x2) : (x2 - x1);
541  lcduint_t dy = y1 > y2 ? (y1 - y2) : (y2 - y1);
542  lcduint_t err = 0;
543  if ( dy > dx )
544  {
545  if ( y1 > y2 )
546  {
547  ssd1306_swap_data(x1, x2, lcdint_t);
548  ssd1306_swap_data(y1, y2, lcdint_t);
549  }
550  for ( ; y1 <= y2; y1++ )
551  {
552  err += dx;
553  if ( err >= dy )
554  {
555  err -= dy;
556  x1 < x2 ? x1++ : x1--;
557  }
558  this->putPixel(x1, y1);
559  }
560  }
561  else
562  {
563  if ( x1 > x2 )
564  {
565  ssd1306_swap_data(x1, x2, lcdint_t);
566  ssd1306_swap_data(y1, y2, lcdint_t);
567  }
568  for ( ; x1 <= x2; x1++ )
569  {
570  err += dy;
571  if ( err >= dx )
572  {
573  err -= dx;
574  if ( y1 < y2 )
575  y1++;
576  else
577  y1--;
578  }
579  this->putPixel(x1, y1);
580  }
581  }
582 }
583 
584 template <class O, class I> void NanoDisplayOps<O, I>::drawLine(const NanoRect &rect)
585 {
586  this->drawLine(rect.p1.x, rect.p1.y, rect.p2.x, rect.p2.y);
587 }
588 
589 template <class O, class I> void NanoDisplayOps<O, I>::fillRect(const NanoRect &rect)
590 {
591  this->fillRect(rect.p1.x, rect.p1.y, rect.p2.x, rect.p2.y);
592 }
593 
594 template <class O, class I> void NanoDisplayOps<O, I>::drawCircle(lcdint_t xc, lcdint_t yc, lcdint_t r, uint8_t options)
595 {
596  lcdint_t d = 3 - 2 * r;
597  lcdint_t x = 0;
598  lcdint_t y = r;
599  if (options & (2 + 4)) putPixel(xc, yc + r);
600  if (options & (1 + 8)) putPixel(xc, yc - r);
601  if (options & (1 + 2)) putPixel(xc + r, yc);
602  if (options & (4 + 8)) putPixel(xc - r, yc);
603  while ( y >= x )
604  {
605  x++;
606  if ( d > 0 )
607  {
608  y--;
609  d += -4 * y + 4;
610  }
611  d += 4 * x + 6;
612  if (options & (2)) putPixel(xc + x, yc + y);
613  if (options & (4)) putPixel(xc - x, yc + y);
614  if (options & (1)) putPixel(xc + x, yc - y);
615  if (options & (8)) putPixel(xc - x, yc - y);
616  if (options & (2)) putPixel(xc + y, yc + x);
617  if (options & (4)) putPixel(xc - y, yc + x);
618  if (options & (1)) putPixel(xc + y, yc - x);
619  if (options & (8)) putPixel(xc - y, yc - x);
620  }
621 }
622 
623 template <class O, class I>
625 {
626  this->m_fontStyle = style;
627  this->m_cursorX = xpos;
628  this->m_cursorY = y;
629  for ( ;; )
630  {
631  char c = pgm_read_byte(ch);
632  if ( !c )
633  break;
634  this->write(c);
635  ch++;
636  }
637 }
638 
639 template <class O, class I> void NanoDisplayOps<O, I>::write(const char *str)
640 {
641  while ( *str )
642  {
643  this->write(*str);
644  str++;
645  }
646 }
647 
648 template <class O, class I> void NanoDisplayOps<O, I>::print(int number)
649 {
650  char intStr[12];
651  snprintf(intStr, sizeof(intStr), "%i", number);
652  this->write(intStr);
653 }
654 
655 template <class O, class I> void NanoDisplayOps<O, I>::print(float number)
656 {
657  char intStr[16];
658  snprintf(intStr, sizeof(intStr), "%f", number);
659  this->write(intStr);
660 }
661 
662 template <class O, class I> void NanoDisplayOps<O, I>::print(char c)
663 {
664  const char intStr[2] = {c, '\0'};
665  this->write(intStr);
666 }
667 
668 #ifndef lcd_gfx_min
669 #define lcd_gfx_min(x, y) ((x) < (y) ? (x) : (y))
670 #endif
671 
672 #ifndef lcd_gfx_max
673 #define lcd_gfx_max(x, y) ((x) > (y) ? (x) : (y))
674 #endif
675 
676 template <class O, class I> static uint8_t getMaxScreenItems(NanoDisplayOps<O, I> &display, const SAppMenu *menu)
677 {
678  return (menu->height - 16) / display.getFont().getHeader().height;
679 }
680 
681 template <class O, class I>
682 static uint8_t calculateScrollPosition(NanoDisplayOps<O, I> &display, const SAppMenu *menu, uint8_t selection)
683 {
684  if ( selection < menu->scrollPosition )
685  {
686  return selection;
687  }
688  else if ( selection - menu->scrollPosition > getMaxScreenItems<O, I>(display, menu) - 1 )
689  {
690  return selection - getMaxScreenItems<O, I>(display, menu) + 1;
691  }
692  return menu->scrollPosition;
693 }
694 
695 template <class O, class I> static void drawMenuItem(NanoDisplayOps<O, I> &display, SAppMenu *menu, uint8_t index)
696 {
697  if ( index == menu->selection )
698  {
699  display.invertColors();
700  }
701  lcdint_t item_top = 8 + menu->top + (index - menu->scrollPosition) * display.getFont().getHeader().height;
702  display.printFixed(menu->left + 8, item_top, menu->items[index], STYLE_NORMAL);
703  if ( index == menu->selection )
704  {
705  display.invertColors();
706  }
707 }
708 
709 template <class O, class I> static void drawMenuItemSmooth(NanoDisplayOps<O, I> &display, SAppMenu *menu, uint8_t index)
710 {
711  if ( index == menu->selection )
712  {
713  display.invertColors();
714  }
715  lcdint_t item_top = 8 + menu->top + (index - menu->scrollPosition) * display.getFont().getHeader().height;
716  display.setColor(0x0000);
717  display.fillRect(menu->left + 8 + display.getFont().getTextSize(menu->items[index]), item_top,
718  menu->width + menu->left - 9, item_top + display.getFont().getHeader().height - 1);
719  display.setColor(0xFFFF);
720  display.printFixed(menu->left + 8, item_top, menu->items[index], STYLE_NORMAL);
721  if ( index == menu->selection )
722  {
723  display.invertColors();
724  }
725 }
726 
727 template <class O, class I>
728 void NanoDisplayOps<O, I>::createMenu(SAppMenu *menu, const char **items, uint8_t count, const NanoRect &rect)
729 {
730  menu->items = items;
731  menu->count = count;
732  menu->selection = 0;
733  menu->oldSelection = 0;
734  menu->scrollPosition = 0;
735  menu->top = rect.p1.y;
736  menu->left = rect.p1.x;
737  menu->width = rect.p2.x ? rect.width() : (this->width() - menu->left);
738  menu->height = rect.p2.y ? rect.height() : (this->height() - menu->top);
739 }
740 
741 template <class O, class I> void NanoDisplayOps<O, I>::showMenu(SAppMenu *menu)
742 {
743  drawRect(4 + menu->left, 4 + menu->top, menu->width + menu->left - 5, menu->height + menu->top - 5);
744  menu->scrollPosition = calculateScrollPosition<O, I>(*this, menu, menu->selection);
745  for ( uint8_t i = menu->scrollPosition;
746  i < lcd_gfx_min(menu->count, (menu->scrollPosition + getMaxScreenItems<O, I>(*this, menu))); i++ )
747  {
748  drawMenuItem<O, I>(*this, menu, i);
749  }
750  menu->oldSelection = menu->selection;
751 }
752 
753 template <class O, class I> void NanoDisplayOps<O, I>::showMenuSmooth(SAppMenu *menu)
754 {
755  drawRect(4 + menu->left, 4 + menu->top, menu->width + menu->left - 5, menu->height + menu->top - 5);
756  menu->scrollPosition = calculateScrollPosition<O, I>(*this, menu, menu->selection);
757  for ( uint8_t i = menu->scrollPosition;
758  i < lcd_gfx_min(menu->count, (menu->scrollPosition + getMaxScreenItems<O, I>(*this, menu))); i++ )
759  {
760  drawMenuItemSmooth<O, I>(*this, menu, i);
761  }
762  menu->oldSelection = menu->selection;
763 }
764 
765 template <class O, class I> void NanoDisplayOps<O, I>::updateMenu(SAppMenu *menu)
766 {
767  if ( menu->selection != menu->oldSelection )
768  {
769  uint8_t scrollPosition = calculateScrollPosition<O, I>(*this, menu, menu->selection);
770  if ( scrollPosition != menu->scrollPosition )
771  {
772  this->clear();
773  showMenu(menu);
774  }
775  else
776  {
777  drawMenuItem<O, I>(*this, menu, menu->oldSelection);
778  drawMenuItem<O, I>(*this, menu, menu->selection);
779  menu->oldSelection = menu->selection;
780  }
781  }
782 }
783 
784 template <class O, class I> void NanoDisplayOps<O, I>::updateMenuSmooth(SAppMenu *menu)
785 {
786  if ( menu->selection != menu->oldSelection )
787  {
788  uint8_t scrollPosition = calculateScrollPosition<O, I>(*this, menu, menu->selection);
789  if ( scrollPosition != menu->scrollPosition )
790  {
791  showMenuSmooth(menu);
792  }
793  else
794  {
795  drawMenuItemSmooth<O, I>(*this, menu, menu->oldSelection);
796  drawMenuItemSmooth<O, I>(*this, menu, menu->selection);
797  menu->oldSelection = menu->selection;
798  }
799  }
800 }
801 
802 template <class O, class I> uint8_t NanoDisplayOps<O, I>::menuSelection(const SAppMenu *menu)
803 {
804  return menu->selection;
805 }
806 
807 template <class O, class I> void NanoDisplayOps<O, I>::menuDown(SAppMenu *menu)
808 {
809  if ( menu->selection < menu->count - 1 )
810  {
811  menu->selection++;
812  }
813  else
814  {
815  menu->selection = 0;
816  }
817 }
818 
819 template <class O, class I> void NanoDisplayOps<O, I>::menuUp(SAppMenu *menu)
820 {
821  if ( menu->selection > 0 )
822  {
823  menu->selection--;
824  }
825  else
826  {
827  menu->selection = menu->count - 1;
828  }
829 }
830 
831 template <class O, class I> void NanoDisplayOps<O, I>::drawCanvas(lcdint_t x, lcdint_t y, NanoCanvasOps<1> &canvas)
832 {
833  this->drawBuffer1Fast(x, y, canvas.width(), canvas.height(), canvas.getData());
834 }
835 
836 template <class O, class I> void NanoDisplayOps<O, I>::drawCanvas(lcdint_t x, lcdint_t y, NanoCanvasOps<4> &canvas)
837 {
838  this->drawBuffer4(x, y, canvas.width(), canvas.height(), canvas.getData());
839 }
840 
841 template <class O, class I> void NanoDisplayOps<O, I>::drawCanvas(lcdint_t x, lcdint_t y, NanoCanvasOps<8> &canvas)
842 {
843  this->drawBuffer8(x, y, canvas.width(), canvas.height(), canvas.getData());
844 }
845 
846 template <class O, class I> void NanoDisplayOps<O, I>::drawCanvas(lcdint_t x, lcdint_t y, NanoCanvasOps<16> &canvas)
847 {
848  this->drawBuffer16(x, y, canvas.width(), canvas.height(), canvas.getData());
849 }
850 
851 template <class O, class I> void NanoDisplayOps<O, I>::drawProgressBar(int8_t progress)
852 {
853  lcduint_t height = 8;
854  lcduint_t width = 8;
855  char str[5] = "100%";
856  if ( progress < 100 )
857  {
858  str[0] = ' ';
859  str[1] = progress / 10 + '0';
860  str[2] = progress % 10 + '0';
861  str[3] = '%';
862  }
863  if ( this->m_font != nullptr )
864  {
865  width = this->getFont().getTextSize(str, &height);
866  }
867  lcdint_t middle = this->height() / 2;
868  lcdint_t progress_pos = 8 + (int16_t)(this->width() - 16) * progress / 100;
869  uint16_t color = this->m_color;
870  this->m_color = 0x0000;
871  this->fillRect(progress_pos, middle, this->width() - 8, middle + height);
872  this->m_color = color;
873  this->drawRect(progress_pos, middle, this->width() - 8, middle + height);
874  this->fillRect(8, middle, progress_pos, middle + height);
875  if ( this->m_font != nullptr )
876  {
877  this->printFixed(this->width() / 2 - width / 2, middle - height, str);
878  }
879 }
880 
881 template <class O, class I>
882 void NanoDisplayOps<O, I>::drawWindow(lcdint_t x, lcdint_t y, lcduint_t width, lcduint_t height, const char *caption,
883  bool blank)
884 {
885  if ( width == 0 )
886  {
887  width = this->width() - 8;
888  x = 4;
889  }
890  if ( height == 0 )
891  {
892  height = this->height() - 4;
893  y = 0;
894  }
895  if ( blank )
896  {
897  uint16_t color = this->m_color;
898  this->m_color = 0x0000;
899  this->fillRect(x, y, x + width - 1, y + height - 1);
900  this->m_color = color;
901  }
902  if ( caption )
903  {
904  y += this->getFont().getHeader().height / 2;
905  height -= this->getFont().getHeader().height / 2;
906  }
907  this->drawRect(x, y, x + width - 1, y + height - 1);
908  if ( caption )
909  {
910  lcduint_t theight;
911  lcduint_t twidth = this->getFont().getTextSize(caption, &theight);
912  this->printFixed(x + (width - twidth) / 2, y - theight / 2, caption);
913  }
914 }
lcdint_t left
left offset
Definition: canvas_types.h:167
uint8_t height
char height in pixels
Definition: canvas_types.h:144
uint8_t lcduint_t
Definition: canvas_types.h:79
void menuUp(SAppMenu *menu)
Definition: rect.h:42
lcdint_t height() const
Definition: rect.h:69
void drawRect(lcdint_t x1, lcdint_t y1, lcdint_t x2, lcdint_t y2)
NanoPoint p2
Definition: rect.h:48
int8_t lcdint_t
Definition: canvas_types.h:77
void write(const char *str)
void createMenu(SAppMenu *menu, const char **items, uint8_t count, const NanoRect &rect={})
lcduint_t width
width of menu
Definition: canvas_types.h:169
uint8_t * getData()
Definition: canvas.h:408
lcdint_t y
Definition: point.h:44
#define SSD1306_MORE_CHARS_REQUIRED
Definition: canvas_types.h:43
lcduint_t width()
Definition: canvas.h:414
lcduint_t height
height of menu
Definition: canvas_types.h:171
void putPixel(const NanoPoint &p)
lcduint_t height()
Definition: canvas.h:420
void drawCanvas(lcdint_t x, lcdint_t y, NanoCanvasOps< 1 > &canvas) __attribute__((noinline))
void drawCircle(lcdint_t xc, lcdint_t yc, lcdint_t r, uint8_t options=0x0F)
void print(int number)
void drawLine(lcdint_t x1, lcdint_t y1, lcdint_t x2, lcdint_t y2)
void updateMenuSmooth(SAppMenu *menu)
void updateMenu(SAppMenu *menu)
uint8_t selection
currently selected item. Internally updated.
Definition: canvas_types.h:159
void showMenuSmooth(SAppMenu *menu)
uint8_t scrollPosition
position of menu scrolling. Internally updated
Definition: canvas_types.h:163
#define lcd_gfx_min(a, b)
uint8_t count
count of menu items in the menu
Definition: canvas_types.h:157
uint8_t width
char width in pixels
Definition: canvas_types.h:143
void showMenu(SAppMenu *menu)
lcdint_t top
top offset
Definition: canvas_types.h:165
void fillRect(const NanoRect &rect)
const uint8_t * glyph
char data, located in progmem.
Definition: canvas_types.h:146
lcdint_t width() const
Definition: rect.h:51
void drawProgressBar(int8_t progress)
uint8_t menuSelection(const SAppMenu *menu)
void printFixedPgm(lcdint_t xpos, lcdint_t y, const char *ch, EFontStyle style=STYLE_NORMAL) __attribute__((noinline))
uint8_t spacing
additional spaces after char in pixels
Definition: canvas_types.h:145
void drawWindow(lcdint_t x, lcdint_t y, lcduint_t width, lcduint_t height, const char *caption, bool blank)
EFontStyle
Definition: canvas_types.h:88
NanoPoint p1
Definition: rect.h:45
lcdint_t x
Definition: point.h:42
#define ssd1306_swap_data(a, b, type)
Definition: io.h:114
uint8_t oldSelection
selected item, when last redraw operation was performed. Internally updated.
Definition: canvas_types.h:161
void menuDown(SAppMenu *menu)
const char ** items
list of menu items of the menu
Definition: canvas_types.h:155