GxEPD2_7C.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. // Display Library for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
  2. // Requires HW SPI and Adafruit_GFX. Caution: these e-papers require 3.3V supply AND data lines!
  3. //
  4. // based on Demo Example from Good Display: http://www.e-paper-display.com/download_list/downloadcategoryid=34&isMode=false.html
  5. //
  6. // Author: Jean-Marc Zingg
  7. //
  8. // Version: see library.properties
  9. //
  10. // Library: https://github.com/ZinggJM/GxEPD2
  11. #ifndef _GxEPD2_7C_H_
  12. #define _GxEPD2_7C_H_
  13. // uncomment next line to use class GFX of library GFX_Root instead of Adafruit_GFX
  14. //#include <GFX.h>
  15. #ifndef ENABLE_GxEPD2_GFX
  16. // default is off
  17. #define ENABLE_GxEPD2_GFX 0
  18. #endif
  19. #if ENABLE_GxEPD2_GFX
  20. #include "GxEPD2_GFX.h"
  21. #define GxEPD2_GFX_BASE_CLASS GxEPD2_GFX
  22. #elif defined(_GFX_H_)
  23. #define GxEPD2_GFX_BASE_CLASS GFX
  24. #else
  25. #include <Adafruit_GFX.h>
  26. #define GxEPD2_GFX_BASE_CLASS Adafruit_GFX
  27. #endif
  28. #include "GxEPD2_EPD.h"
  29. #include "epd3c/GxEPD2_565c.h"
  30. template<typename GxEPD2_Type, const uint16_t page_height>
  31. class GxEPD2_7C : public GxEPD2_GFX_BASE_CLASS
  32. {
  33. public:
  34. GxEPD2_Type epd2;
  35. #if ENABLE_GxEPD2_GFX
  36. GxEPD2_7C(GxEPD2_Type epd2_instance) : GxEPD2_GFX_BASE_CLASS(epd2, GxEPD2_Type::WIDTH, GxEPD2_Type::HEIGHT), epd2(epd2_instance)
  37. #else
  38. GxEPD2_7C(GxEPD2_Type epd2_instance) : GxEPD2_GFX_BASE_CLASS(GxEPD2_Type::WIDTH, GxEPD2_Type::HEIGHT), epd2(epd2_instance)
  39. #endif
  40. {
  41. _page_height = page_height;
  42. _pages = (HEIGHT / _page_height) + ((HEIGHT % _page_height) > 0);
  43. _mirror = false;
  44. _using_partial_mode = false;
  45. _current_page = 0;
  46. setFullWindow();
  47. }
  48. uint16_t pages()
  49. {
  50. return _pages;
  51. }
  52. uint16_t pageHeight()
  53. {
  54. return _page_height;
  55. }
  56. bool mirror(bool m)
  57. {
  58. _swap_ (_mirror, m);
  59. return m;
  60. }
  61. void drawPixel(int16_t x, int16_t y, uint16_t color)
  62. {
  63. if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) return;
  64. if (_mirror) x = width() - x - 1;
  65. // check rotation, move pixel around if necessary
  66. switch (getRotation())
  67. {
  68. case 1:
  69. _swap_(x, y);
  70. x = WIDTH - x - 1;
  71. break;
  72. case 2:
  73. x = WIDTH - x - 1;
  74. y = HEIGHT - y - 1;
  75. break;
  76. case 3:
  77. _swap_(x, y);
  78. y = HEIGHT - y - 1;
  79. break;
  80. }
  81. // transpose partial window to 0,0
  82. x -= _pw_x;
  83. y -= _pw_y;
  84. // clip to (partial) window
  85. if ((x < 0) || (x >= _pw_w) || (y < 0) || (y >= _pw_h)) return;
  86. // adjust for current page
  87. y -= _current_page * _page_height;
  88. // check if in current page
  89. if ((y < 0) || (y >= _page_height)) return;
  90. uint32_t i = x / 2 + uint32_t(y) * (_pw_w / 2);
  91. uint8_t pv = color7(color);
  92. if (x & 1) _pixel_buffer[i] = (_pixel_buffer[i] & 0xF0) | pv;
  93. else _pixel_buffer[i] = (_pixel_buffer[i] & 0x0F) | (pv << 4);
  94. }
  95. void init(uint32_t serial_diag_bitrate = 0) // = 0 : disabled
  96. {
  97. epd2.init(serial_diag_bitrate);
  98. _using_partial_mode = false;
  99. _current_page = 0;
  100. setFullWindow();
  101. }
  102. // init method with additional parameters:
  103. // initial false for re-init after processor deep sleep wake up, if display power supply was kept
  104. // only relevant for b/w displays with fast partial update
  105. // reset_duration = 20 is default; a value of 2 may help with "clever" reset circuit of newer boards from Waveshare
  106. // pulldown_rst_mode true for alternate RST handling to avoid feeding 5V through RST pin
  107. void init(uint32_t serial_diag_bitrate, bool initial, uint16_t reset_duration = 20, bool pulldown_rst_mode = false)
  108. {
  109. epd2.init(serial_diag_bitrate, initial, reset_duration, pulldown_rst_mode);
  110. _using_partial_mode = false;
  111. _current_page = 0;
  112. setFullWindow();
  113. }
  114. // init method with additional parameters:
  115. // SPIClass& spi: either SPI or alternate HW SPI channel
  116. // SPISettings spi_settings: e.g. for higher SPI speed selection
  117. void init(uint32_t serial_diag_bitrate, bool initial, uint16_t reset_duration, bool pulldown_rst_mode, SPIClass& spi, SPISettings spi_settings)
  118. {
  119. epd2.selectSPI(spi, spi_settings);
  120. epd2.init(serial_diag_bitrate, initial, reset_duration, pulldown_rst_mode);
  121. _using_partial_mode = false;
  122. _current_page = 0;
  123. setFullWindow();
  124. }
  125. void fillScreen(uint16_t color)
  126. {
  127. uint8_t pv = color7(color);
  128. uint8_t pv2 = pv | pv << 4;
  129. for (uint32_t x = 0; x < sizeof(_pixel_buffer); x++)
  130. {
  131. _pixel_buffer[x] = pv2;
  132. }
  133. }
  134. // display buffer content to screen, useful for full screen buffer
  135. void display(bool partial_update_mode = false)
  136. {
  137. epd2.writeNative(_pixel_buffer, 0, 0, 0, WIDTH, _page_height);
  138. epd2.refresh(partial_update_mode);
  139. if (!partial_update_mode) epd2.powerOff();
  140. }
  141. // display part of buffer content to screen, useful for full screen buffer
  142. // displayWindow, use parameters according to actual rotation.
  143. // x and w should be multiple of 8, for rotation 0 or 2,
  144. // y and h should be multiple of 8, for rotation 1 or 3,
  145. // else window is increased as needed,
  146. // this is an addressing limitation of the e-paper controllers
  147. void displayWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
  148. {
  149. x = gx_uint16_min(x, width());
  150. y = gx_uint16_min(y, height());
  151. w = gx_uint16_min(w, width() - x);
  152. h = gx_uint16_min(h, height() - y);
  153. _rotate(x, y, w, h);
  154. epd2.writeNativePart(_pixel_buffer, 0, x, y, WIDTH, _page_height, x, y, w, h);
  155. epd2.refresh(x, y, w, h);
  156. }
  157. void setFullWindow()
  158. {
  159. _using_partial_mode = false;
  160. _pw_x = 0;
  161. _pw_y = 0;
  162. _pw_w = WIDTH;
  163. _pw_h = HEIGHT;
  164. }
  165. // setPartialWindow, use parameters according to actual rotation.
  166. // x and w should be multiple of 8, for rotation 0 or 2,
  167. // y and h should be multiple of 8, for rotation 1 or 3,
  168. // else window is increased as needed,
  169. // this is an addressing limitation of the e-paper controllers
  170. void setPartialWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
  171. {
  172. if (!epd2.hasPartialUpdate) return;
  173. _pw_x = gx_uint16_min(x, width());
  174. _pw_y = gx_uint16_min(y, height());
  175. _pw_w = gx_uint16_min(w, width() - _pw_x);
  176. _pw_h = gx_uint16_min(h, height() - _pw_y);
  177. _rotate(_pw_x, _pw_y, _pw_w, _pw_h);
  178. _using_partial_mode = true;
  179. // make _pw_x, _pw_w multiple of 2
  180. _pw_w += _pw_x % 2;
  181. if (_pw_w % 2 > 0) _pw_w += 2 - _pw_w % 2;
  182. _pw_x -= _pw_x % 2;
  183. }
  184. void firstPage()
  185. {
  186. fillScreen(GxEPD_WHITE);
  187. _current_page = 0;
  188. _second_phase = false;
  189. epd2.setPaged(); // for GxEPD2_565c paged workaround
  190. }
  191. bool nextPage()
  192. {
  193. uint16_t page_ys = _current_page * _page_height;
  194. if (_using_partial_mode)
  195. {
  196. //Serial.print(" nextPage("); Serial.print(_pw_x); Serial.print(", "); Serial.print(_pw_y); Serial.print(", ");
  197. //Serial.print(_pw_w); Serial.print(", "); Serial.print(_pw_h); Serial.print(") P"); Serial.println(_current_page);
  198. uint16_t page_ye = _current_page < (_pages - 1) ? page_ys + _page_height : HEIGHT;
  199. uint16_t dest_ys = _pw_y + page_ys; // transposed
  200. uint16_t dest_ye = gx_uint16_min(_pw_y + _pw_h, _pw_y + page_ye);
  201. if (dest_ye > dest_ys)
  202. {
  203. //Serial.print("writeImage("); Serial.print(_pw_x); Serial.print(", "); Serial.print(dest_ys); Serial.print(", ");
  204. //Serial.print(_pw_w); Serial.print(", "); Serial.print(dest_ye - dest_ys); Serial.println(")");
  205. epd2.writeNative(_pixel_buffer, 0, _pw_x, dest_ys, _pw_w, dest_ye - dest_ys);
  206. }
  207. else
  208. {
  209. //Serial.print("writeImage("); Serial.print(_pw_x); Serial.print(", "); Serial.print(dest_ys); Serial.print(", ");
  210. //Serial.print(_pw_w); Serial.print(", "); Serial.print(dest_ye - dest_ys); Serial.print(") skipped ");
  211. //Serial.print(dest_ys); Serial.print(".."); Serial.println(dest_ye);
  212. }
  213. _current_page++;
  214. if (_current_page == _pages)
  215. {
  216. _current_page = 0;
  217. if (!_second_phase)
  218. {
  219. epd2.refresh(_pw_x, _pw_y, _pw_w, _pw_h);
  220. if (epd2.hasFastPartialUpdate)
  221. {
  222. _second_phase = true;
  223. return true;
  224. }
  225. }
  226. return false;
  227. }
  228. fillScreen(GxEPD_WHITE);
  229. return true;
  230. }
  231. else // full update
  232. {
  233. epd2.writeNative(_pixel_buffer, 0, 0, page_ys, WIDTH, gx_uint16_min(_page_height, HEIGHT - page_ys));
  234. _current_page++;
  235. if (_current_page == _pages)
  236. {
  237. _current_page = 0;
  238. if ((epd2.panel == GxEPD2::GDEW0154Z04) && (_pages > 1))
  239. {
  240. if (!_second_phase)
  241. {
  242. epd2.refresh(false); // full update after first phase
  243. _second_phase = true;
  244. fillScreen(GxEPD_WHITE);
  245. return true;
  246. }
  247. else epd2.refresh(true); // partial update after second phase
  248. } else epd2.refresh(false); // full update after only phase
  249. epd2.powerOff();
  250. return false;
  251. }
  252. fillScreen(GxEPD_WHITE);
  253. return true;
  254. }
  255. }
  256. // GxEPD style paged drawing; drawCallback() is called as many times as needed
  257. void drawPaged(void (*drawCallback)(const void*), const void* pv)
  258. {
  259. if (_using_partial_mode)
  260. {
  261. for (_current_page = 0; _current_page < _pages; _current_page++)
  262. {
  263. uint16_t page_ys = _current_page * _page_height;
  264. uint16_t page_ye = _current_page < (_pages - 1) ? page_ys + _page_height : HEIGHT;
  265. uint16_t dest_ys = _pw_y + page_ys; // transposed
  266. uint16_t dest_ye = gx_uint16_min(_pw_y + _pw_h, _pw_y + page_ye);
  267. if (dest_ye > dest_ys)
  268. {
  269. fillScreen(GxEPD_WHITE);
  270. drawCallback(pv);
  271. epd2.writeNative(_pixel_buffer, 0, _pw_x, dest_ys, _pw_w, dest_ye - dest_ys);
  272. }
  273. }
  274. epd2.refresh(_pw_x, _pw_y, _pw_w, _pw_h);
  275. }
  276. else // full update
  277. {
  278. epd2.setPaged(); // for GxEPD2_154c paged workaround
  279. for (_current_page = 0; _current_page < _pages; _current_page++)
  280. {
  281. uint16_t page_ys = _current_page * _page_height;
  282. fillScreen(GxEPD_WHITE);
  283. drawCallback(pv);
  284. epd2.writeNative(_pixel_buffer, 0, 0, page_ys, WIDTH, gx_uint16_min(_page_height, HEIGHT - page_ys));
  285. }
  286. epd2.refresh(false); // full update
  287. epd2.powerOff();
  288. }
  289. _current_page = 0;
  290. }
  291. void drawInvertedBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color)
  292. {
  293. // taken from Adafruit_GFX.cpp, modified
  294. int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte
  295. uint8_t byte = 0;
  296. for (int16_t j = 0; j < h; j++)
  297. {
  298. for (int16_t i = 0; i < w; i++ )
  299. {
  300. if (i & 7) byte <<= 1;
  301. else
  302. {
  303. #if defined(__AVR) || defined(ESP8266) || defined(ESP32)
  304. byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]);
  305. #else
  306. byte = bitmap[j * byteWidth + i / 8];
  307. #endif
  308. }
  309. if (!(byte & 0x80))
  310. {
  311. drawPixel(x + i, y + j, color);
  312. }
  313. }
  314. }
  315. }
  316. // Support for Bitmaps (Sprites) to Controller Buffer and to Screen
  317. void clearScreen(uint8_t value = 0xFF) // init controller memory and screen (default white)
  318. {
  319. epd2.clearScreen(value);
  320. }
  321. void writeScreenBuffer(uint8_t value = 0xFF) // init controller memory (default white)
  322. {
  323. epd2.writeScreenBuffer(value);
  324. }
  325. // write to controller memory, without screen refresh; x and w should be multiple of 8
  326. void writeImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false)
  327. {
  328. epd2.writeImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
  329. }
  330. void writeImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
  331. int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false)
  332. {
  333. epd2.writeImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
  334. }
  335. void writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  336. {
  337. epd2.writeImage(black, color, x, y, w, h, invert, mirror_y, pgm);
  338. }
  339. void writeImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h)
  340. {
  341. epd2.writeImage(black, color, x, y, w, h, false, false, false);
  342. }
  343. void writeImagePart(const uint8_t* black, const uint8_t* color, int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
  344. int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  345. {
  346. epd2.writeImagePart(black, color, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
  347. }
  348. void writeImagePart(const uint8_t* black, const uint8_t* color, int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
  349. int16_t x, int16_t y, int16_t w, int16_t h)
  350. {
  351. epd2.writeImagePart(black, color, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, false, false, false);
  352. }
  353. // write sprite of native data to controller memory, without screen refresh; x and w should be multiple of 8
  354. void writeNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  355. {
  356. epd2.writeNative(data1, data2, x, y, w, h, invert, mirror_y, pgm);
  357. }
  358. // write to controller memory, with screen refresh; x and w should be multiple of 8
  359. void drawImage(const uint8_t bitmap[], int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false)
  360. {
  361. epd2.drawImage(bitmap, x, y, w, h, invert, mirror_y, pgm);
  362. }
  363. void drawImagePart(const uint8_t bitmap[], int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
  364. int16_t x, int16_t y, int16_t w, int16_t h, bool invert = false, bool mirror_y = false, bool pgm = false)
  365. {
  366. epd2.drawImagePart(bitmap, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
  367. }
  368. void drawImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  369. {
  370. epd2.drawImage(black, color, x, y, w, h, invert, mirror_y, pgm);
  371. }
  372. void drawImage(const uint8_t* black, const uint8_t* color, int16_t x, int16_t y, int16_t w, int16_t h)
  373. {
  374. epd2.drawImage(black, color, x, y, w, h, false, false, false);
  375. }
  376. void drawImagePart(const uint8_t* black, const uint8_t* color, int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
  377. int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  378. {
  379. epd2.drawImagePart(black, color, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, invert, mirror_y, pgm);
  380. }
  381. void drawImagePart(const uint8_t* black, const uint8_t* color, int16_t x_part, int16_t y_part, int16_t w_bitmap, int16_t h_bitmap,
  382. int16_t x, int16_t y, int16_t w, int16_t h)
  383. {
  384. epd2.drawImagePart(black, color, x_part, y_part, w_bitmap, h_bitmap, x, y, w, h, false, false, false);
  385. }
  386. // write sprite of native data to controller memory, with screen refresh; x and w should be multiple of 8
  387. void drawNative(const uint8_t* data1, const uint8_t* data2, int16_t x, int16_t y, int16_t w, int16_t h, bool invert, bool mirror_y, bool pgm)
  388. {
  389. epd2.drawNative(data1, data2, x, y, w, h, invert, mirror_y, pgm);
  390. }
  391. void refresh(bool partial_update_mode = false) // screen refresh from controller memory to full screen
  392. {
  393. epd2.refresh(partial_update_mode);
  394. if (!partial_update_mode) epd2.powerOff();
  395. }
  396. void refresh(int16_t x, int16_t y, int16_t w, int16_t h) // screen refresh from controller memory, partial screen
  397. {
  398. epd2.refresh(x, y, w, h);
  399. }
  400. // turns off generation of panel driving voltages, avoids screen fading over time
  401. void powerOff()
  402. {
  403. epd2.powerOff();
  404. }
  405. // turns powerOff() and sets controller to deep sleep for minimum power use, ONLY if wakeable by RST (rst >= 0)
  406. void hibernate()
  407. {
  408. epd2.hibernate();
  409. }
  410. private:
  411. template <typename T> static inline void
  412. _swap_(T & a, T & b)
  413. {
  414. T t = a;
  415. a = b;
  416. b = t;
  417. };
  418. static inline uint16_t gx_uint16_min(uint16_t a, uint16_t b)
  419. {
  420. return (a < b ? a : b);
  421. };
  422. static inline uint16_t gx_uint16_max(uint16_t a, uint16_t b)
  423. {
  424. return (a > b ? a : b);
  425. };
  426. void _rotate(uint16_t& x, uint16_t& y, uint16_t& w, uint16_t& h)
  427. {
  428. switch (getRotation())
  429. {
  430. case 1:
  431. _swap_(x, y);
  432. _swap_(w, h);
  433. x = WIDTH - x - w;
  434. break;
  435. case 2:
  436. x = WIDTH - x - w;
  437. y = HEIGHT - y - h;
  438. break;
  439. case 3:
  440. _swap_(x, y);
  441. _swap_(w, h);
  442. y = HEIGHT - y - h;
  443. break;
  444. }
  445. }
  446. uint8_t color7(uint16_t color)
  447. {
  448. static uint16_t _prev_color = GxEPD_BLACK;
  449. static uint8_t _prev_color7 = 0x00; // black
  450. if (color == _prev_color) return _prev_color7;
  451. uint8_t cv7 = 0x00;
  452. switch (color)
  453. {
  454. case GxEPD_BLACK: cv7 = 0x00; break;
  455. case GxEPD_WHITE: cv7 = 0x01; break;
  456. case GxEPD_GREEN: cv7 = 0x02; break;
  457. case GxEPD_BLUE: cv7 = 0x03; break;
  458. case GxEPD_RED: cv7 = 0x04; break;
  459. case GxEPD_YELLOW: cv7 = 0x05; break;
  460. case GxEPD_ORANGE: cv7 = 0x06; break;
  461. default:
  462. {
  463. uint16_t red = color & 0xF800;
  464. uint16_t green = (color & 0x07E0) << 5;
  465. uint16_t blue = (color & 0x001F) << 11;
  466. if ((red < 0x8000) && (green < 0x8000) && (blue < 0x8000)) cv7 = 0x00; // black
  467. else if ((red >= 0x8000) && (green >= 0x8000) && (blue >= 0x8000)) cv7 = 0x01; // white
  468. else if ((red >= 0x8000) && (blue >= 0x8000)) cv7 = red > blue ? 0x04 : 0x03; // red, blue
  469. else if ((green >= 0x8000) && (blue >= 0x8000)) cv7 = green > blue ? 0x02 : 0x03; // green, blue
  470. else if ((red >= 0x8000) && (green >= 0x8000))
  471. {
  472. static const uint16_t y2o_lim = ((GxEPD_YELLOW - GxEPD_ORANGE) / 2 + (GxEPD_ORANGE & 0x07E0)) << 5;
  473. cv7 = green > y2o_lim ? 0x05 : 0x06; // yellow, orange
  474. }
  475. else if (red >= 0x8000) cv7 = 0x04; // red
  476. else if (green >= 0x8000) cv7 = 0x02; // green
  477. else cv7 = 0x03; // blue
  478. }
  479. }
  480. _prev_color = color;
  481. _prev_color7 = cv7;
  482. return cv7;
  483. }
  484. private:
  485. uint8_t _pixel_buffer[(GxEPD2_Type::WIDTH / 2) * page_height];
  486. bool _using_partial_mode, _second_phase, _mirror;
  487. uint16_t _width_bytes, _pixel_bytes;
  488. int16_t _current_page;
  489. uint16_t _pages, _page_height;
  490. uint16_t _pw_x, _pw_y, _pw_w, _pw_h;
  491. };
  492. #endif