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  this->putPixel(x1, y1);
553  err += dx;
554  if ( err >= dy )
555  {
556  err -= dy;
557  x1 < x2 ? x1++ : x1--;
558  }
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  this->putPixel(x1, y1);
571  err += dy;
572  if ( err >= dx )
573  {
574  err -= dx;
575  if ( y1 < y2 )
576  y1++;
577  else
578  y1--;
579  }
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>::clearRect(lcdint_t x1, lcdint_t y1, lcdint_t x2, lcdint_t y2)
595 {
596  uint16_t old_color = this->getColor();
597  this->setColor(0x0000);
598  this->fillRect(x1, y1, x2, y2);
599  this->setColor(old_color);
600 }
601 
602 template <class O, class I> void NanoDisplayOps<O, I>::clearRect(const NanoRect &rect)
603 {
604  this->clearRect(rect.p1.x, rect.p1.y, rect.p2.x, rect.p2.y);
605 }
606 
607 template <class O, class I> void NanoDisplayOps<O, I>::drawCircle(lcdint_t xc, lcdint_t yc, lcdint_t r, uint8_t options)
608 {
609  lcdint_t d = 3 - 2 * r;
610  lcdint_t x = 0;
611  lcdint_t y = r;
612  if (options & (2 + 4)) putPixel(xc, yc + r);
613  if (options & (1 + 8)) putPixel(xc, yc - r);
614  if (options & (1 + 2)) putPixel(xc + r, yc);
615  if (options & (4 + 8)) putPixel(xc - r, yc);
616  while ( y >= x )
617  {
618  x++;
619  if ( d > 0 )
620  {
621  y--;
622  d += -4 * y + 4;
623  }
624  d += 4 * x + 6;
625  if (options & (2)) putPixel(xc + x, yc + y);
626  if (options & (4)) putPixel(xc - x, yc + y);
627  if (options & (1)) putPixel(xc + x, yc - y);
628  if (options & (8)) putPixel(xc - x, yc - y);
629  if (options & (2)) putPixel(xc + y, yc + x);
630  if (options & (4)) putPixel(xc - y, yc + x);
631  if (options & (1)) putPixel(xc + y, yc - x);
632  if (options & (8)) putPixel(xc - y, yc - x);
633  }
634 }
635 
636 template <class O, class I>
638 {
639  this->m_fontStyle = style;
640  this->m_cursorX = xpos;
641  this->m_cursorY = y;
642  for ( ;; )
643  {
644  char c = pgm_read_byte(ch);
645  if ( !c )
646  break;
647  this->write(c);
648  ch++;
649  }
650 }
651 
652 template <class O, class I> void NanoDisplayOps<O, I>::write(const char *str)
653 {
654  while ( *str )
655  {
656  this->write(*str);
657  str++;
658  }
659 }
660 
661 template <class O, class I> void NanoDisplayOps<O, I>::print(int number)
662 {
663  char intStr[12];
664  snprintf(intStr, sizeof(intStr), "%i", number);
665  this->write(intStr);
666 }
667 
668 template <class O, class I> void NanoDisplayOps<O, I>::print(float number)
669 {
670  char intStr[16];
671  snprintf(intStr, sizeof(intStr), "%f", number);
672  this->write(intStr);
673 }
674 
675 template <class O, class I> void NanoDisplayOps<O, I>::print(char c)
676 {
677  const char intStr[2] = {c, '\0'};
678  this->write(intStr);
679 }
680 
681 #ifndef lcd_gfx_min
682 #define lcd_gfx_min(x, y) ((x) < (y) ? (x) : (y))
683 #endif
684 
685 #ifndef lcd_gfx_max
686 #define lcd_gfx_max(x, y) ((x) > (y) ? (x) : (y))
687 #endif
688 
689 template <class O, class I> static uint8_t getMaxScreenItems(NanoDisplayOps<O, I> &display, const SAppMenu *menu)
690 {
691  return (menu->height - 16) / display.getFont().getHeader().height;
692 }
693 
694 template <class O, class I>
695 static uint8_t calculateScrollPosition(NanoDisplayOps<O, I> &display, const SAppMenu *menu, uint8_t selection)
696 {
697  if ( selection < menu->scrollPosition )
698  {
699  return selection;
700  }
701  else if ( selection - menu->scrollPosition > getMaxScreenItems<O, I>(display, menu) - 1 )
702  {
703  return selection - getMaxScreenItems<O, I>(display, menu) + 1;
704  }
705  return menu->scrollPosition;
706 }
707 
708 template <class O, class I> static void drawMenuItem(NanoDisplayOps<O, I> &display, SAppMenu *menu, uint8_t index)
709 {
710  if ( index == menu->selection )
711  {
712  display.invertColors();
713  }
714  lcdint_t item_top = 8 + menu->top + (index - menu->scrollPosition) * display.getFont().getHeader().height;
715  display.printFixed(menu->left + 8, item_top, menu->items[index], STYLE_NORMAL);
716  if ( index == menu->selection )
717  {
718  display.invertColors();
719  }
720 }
721 
722 template <class O, class I> static void drawMenuItemSmooth(NanoDisplayOps<O, I> &display, SAppMenu *menu, uint8_t index)
723 {
724  if ( index == menu->selection )
725  {
726  display.invertColors();
727  }
728  lcdint_t item_top = 8 + menu->top + (index - menu->scrollPosition) * display.getFont().getHeader().height;
729  display.setColor(0x0000);
730  display.fillRect(menu->left + 8 + display.getFont().getTextSize(menu->items[index]), item_top,
731  menu->width + menu->left - 9, item_top + display.getFont().getHeader().height - 1);
732  display.setColor(0xFFFF);
733  display.printFixed(menu->left + 8, item_top, menu->items[index], STYLE_NORMAL);
734  if ( index == menu->selection )
735  {
736  display.invertColors();
737  }
738 }
739 
740 template <class O, class I>
741 void NanoDisplayOps<O, I>::createMenu(SAppMenu *menu, const char **items, uint8_t count, const NanoRect &rect)
742 {
743  menu->items = items;
744  menu->count = count;
745  menu->selection = 0;
746  menu->oldSelection = 0;
747  menu->scrollPosition = 0;
748  menu->top = rect.p1.y;
749  menu->left = rect.p1.x;
750  menu->width = rect.p2.x ? rect.width() : (this->width() - menu->left);
751  menu->height = rect.p2.y ? rect.height() : (this->height() - menu->top);
752 }
753 
754 template <class O, class I> void NanoDisplayOps<O, I>::showMenu(SAppMenu *menu)
755 {
756  drawRect(4 + menu->left, 4 + menu->top, menu->width + menu->left - 5, menu->height + menu->top - 5);
757  menu->scrollPosition = calculateScrollPosition<O, I>(*this, menu, menu->selection);
758  for ( uint8_t i = menu->scrollPosition;
759  i < lcd_gfx_min(menu->count, (menu->scrollPosition + getMaxScreenItems<O, I>(*this, menu))); i++ )
760  {
761  drawMenuItem<O, I>(*this, menu, i);
762  }
763  menu->oldSelection = menu->selection;
764 }
765 
766 template <class O, class I> void NanoDisplayOps<O, I>::showMenuSmooth(SAppMenu *menu)
767 {
768  drawRect(4 + menu->left, 4 + menu->top, menu->width + menu->left - 5, menu->height + menu->top - 5);
769  menu->scrollPosition = calculateScrollPosition<O, I>(*this, menu, menu->selection);
770  for ( uint8_t i = menu->scrollPosition;
771  i < lcd_gfx_min(menu->count, (menu->scrollPosition + getMaxScreenItems<O, I>(*this, menu))); i++ )
772  {
773  drawMenuItemSmooth<O, I>(*this, menu, i);
774  }
775  menu->oldSelection = menu->selection;
776 }
777 
778 template <class O, class I> void NanoDisplayOps<O, I>::updateMenu(SAppMenu *menu)
779 {
780  if ( menu->selection != menu->oldSelection )
781  {
782  uint8_t scrollPosition = calculateScrollPosition<O, I>(*this, menu, menu->selection);
783  if ( scrollPosition != menu->scrollPosition )
784  {
785  this->clear();
786  showMenu(menu);
787  }
788  else
789  {
790  drawMenuItem<O, I>(*this, menu, menu->oldSelection);
791  drawMenuItem<O, I>(*this, menu, menu->selection);
792  menu->oldSelection = menu->selection;
793  }
794  }
795 }
796 
797 template <class O, class I> void NanoDisplayOps<O, I>::updateMenuSmooth(SAppMenu *menu)
798 {
799  if ( menu->selection != menu->oldSelection )
800  {
801  uint8_t scrollPosition = calculateScrollPosition<O, I>(*this, menu, menu->selection);
802  if ( scrollPosition != menu->scrollPosition )
803  {
804  showMenuSmooth(menu);
805  }
806  else
807  {
808  drawMenuItemSmooth<O, I>(*this, menu, menu->oldSelection);
809  drawMenuItemSmooth<O, I>(*this, menu, menu->selection);
810  menu->oldSelection = menu->selection;
811  }
812  }
813 }
814 
815 template <class O, class I> uint8_t NanoDisplayOps<O, I>::menuSelection(const SAppMenu *menu)
816 {
817  return menu->selection;
818 }
819 
820 template <class O, class I> void NanoDisplayOps<O, I>::menuDown(SAppMenu *menu)
821 {
822  if ( menu->selection < menu->count - 1 )
823  {
824  menu->selection++;
825  }
826  else
827  {
828  menu->selection = 0;
829  }
830 }
831 
832 template <class O, class I> void NanoDisplayOps<O, I>::menuUp(SAppMenu *menu)
833 {
834  if ( menu->selection > 0 )
835  {
836  menu->selection--;
837  }
838  else
839  {
840  menu->selection = menu->count - 1;
841  }
842 }
843 
844 template <class O, class I> void NanoDisplayOps<O, I>::drawCanvas(lcdint_t x, lcdint_t y, NanoCanvasOps<1> &canvas)
845 {
846  this->drawBuffer1Fast(x, y, canvas.width(), canvas.height(), canvas.getData());
847 }
848 
849 template <class O, class I> void NanoDisplayOps<O, I>::drawCanvas(lcdint_t x, lcdint_t y, NanoCanvasOps<4> &canvas)
850 {
851  this->drawBuffer4(x, y, canvas.width(), canvas.height(), canvas.getData());
852 }
853 
854 template <class O, class I> void NanoDisplayOps<O, I>::drawCanvas(lcdint_t x, lcdint_t y, NanoCanvasOps<8> &canvas)
855 {
856  this->drawBuffer8(x, y, canvas.width(), canvas.height(), canvas.getData());
857 }
858 
859 template <class O, class I> void NanoDisplayOps<O, I>::drawCanvas(lcdint_t x, lcdint_t y, NanoCanvasOps<16> &canvas)
860 {
861  this->drawBuffer16(x, y, canvas.width(), canvas.height(), canvas.getData());
862 }
863 
864 template <class O, class I> void NanoDisplayOps<O, I>::drawProgressBar(int8_t progress)
865 {
866  lcduint_t height = 8;
867  lcduint_t width = 8;
868  char str[5] = "100%";
869  if ( progress < 100 )
870  {
871  str[0] = ' ';
872  str[1] = progress / 10 + '0';
873  str[2] = progress % 10 + '0';
874  str[3] = '%';
875  }
876  if ( this->m_font != nullptr )
877  {
878  width = this->getFont().getTextSize(str, &height);
879  }
880  lcdint_t middle = this->height() / 2;
881  lcdint_t progress_pos = 8 + (int16_t)(this->width() - 16) * progress / 100;
882  uint16_t color = this->m_color;
883  this->m_color = 0x0000;
884  this->fillRect(progress_pos, middle, this->width() - 8, middle + height);
885  this->m_color = color;
886  this->drawRect(progress_pos, middle, this->width() - 8, middle + height);
887  this->fillRect(8, middle, progress_pos, middle + height);
888  if ( this->m_font != nullptr )
889  {
890  this->printFixed(this->width() / 2 - width / 2, middle - height, str);
891  }
892 }
893 
894 template <class O, class I>
895 void NanoDisplayOps<O, I>::drawWindow(lcdint_t x, lcdint_t y, lcduint_t width, lcduint_t height, const char *caption,
896  bool blank)
897 {
898  if ( width == 0 )
899  {
900  width = this->width() - 8;
901  x = 4;
902  }
903  if ( height == 0 )
904  {
905  height = this->height() - 4;
906  y = 0;
907  }
908  if ( blank )
909  {
910  uint16_t color = this->m_color;
911  this->m_color = 0x0000;
912  this->fillRect(x, y, x + width - 1, y + height - 1);
913  this->m_color = color;
914  }
915  if ( caption )
916  {
917  y += this->getFont().getHeader().height / 2;
918  height -= this->getFont().getHeader().height / 2;
919  }
920  this->drawRect(x, y, x + width - 1, y + height - 1);
921  if ( caption )
922  {
923  lcduint_t theight;
924  lcduint_t twidth = this->getFont().getTextSize(caption, &theight);
925  this->printFixed(x + (width - twidth) / 2, y - theight / 2, caption);
926  }
927 }
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
void clearRect(lcdint_t x1, lcdint_t y1, lcdint_t x2, lcdint_t y2)
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