├── .github └── stale.yml ├── README.md ├── TFT_ILI9486.ino ├── benchmark.txt ├── component.mk ├── examples ├── Benchmark │ └── Benchmark.ino ├── CP_1251_cyrillic │ ├── CP_1251_cyrillic.ino │ └── CP_1251_cyrillic.jpg ├── CP_1252_latin │ ├── CP_1252_latin.ino │ └── CP_1252_latin.jpg ├── CP_1253_greek │ ├── CP_1253_greek.ino │ └── CP_1253_greek.jpg └── Old_English_Text │ ├── Old_English_Text.ino │ └── Old_English_Text.jpg ├── images ├── GLCD Font Creator.jpg ├── MHS-3.5inch Display.png ├── RPi 3.5inch Pins.JPG ├── SAM_0205.jpg ├── SPI DIsplay.JPG ├── Waveshare RPi 3.5.jpg ├── arduino.jpg ├── tested displays.png ├── wall_e.bmp ├── wallpaper1.jpg ├── wallpaper2.jpg └── wallpaper3.jpg ├── keywords.txt ├── library.properties └── src ├── fonts ├── Baskerville_Old_Face.h ├── Courier_New.h ├── Garamond.h ├── Garamond_cyrillic.h ├── Garamond_greek.h ├── Monotype_Corsiva.h ├── Old_English_Text_MT.h ├── Script_MT_Bold.h ├── Times_New_Roman.h └── misc.h ├── ili9486.cpp └── ili9486.h /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32-TFT-Library-ILI9486 2 | A library for 3.5 inch RPi LCD (A) 320x480 display from Waveshare, for LCD (B) comment line 61 and uncomment line 62 in tft.cpp 3 | ![Display](https://github.com/schreibfaul1/ESP32-TFT-Library-ILI9486/blob/master/images/tested%20displays.png) 4 | 5 | Create new fonts with MikroElektronika GLCD Font Creator and insert the new font in fonts.h 6 | You can also display bitmaps, touchpadcontroller XPT2046 is included 7 | Examplecodes: 8 | 9 | ```` c++ 10 | #include "Arduino.h" 11 | #include "ili9486.h" 12 | 13 | // GPIOs for SPI 14 | #define SPI_MOSI 23 15 | #define SPI_MISO 19 16 | #define SPI_SCK 18 17 | 18 | // GPIOs for TFT/TP 19 | #define TFT_CS 22 20 | #define TFT_DC 5 21 | #define TP_CS 13 22 | #define TP_IRQ 12 23 | 24 | TFT tft; 25 | 26 | //------------------------------------------------------------------------------------- 27 | void setup() { 28 | 29 | tft.begin(TFT_CS, TFT_DC, VSPI, SPI_MOSI, SPI_MISO, SPI_SCK); 30 | tft.setRotation(1); //landscape 31 | tft.fillScreen(TFT_BLACK); 32 | tft.setFont(Times_New_Roman66x53); 33 | tft.setTextColor(TFT_CYAN); 34 | tft.setCursor(20,30); 35 | tft.print("Hello World!"); 36 | } 37 | //------------------------------------------------------------------------------------- 38 | void loop(void) { 39 | for(uint8_t rotation=0; rotation<4; rotation++) { 40 | tft.setRotation(rotation); 41 | tft.fillScreen(TFT_BLACK); 42 | tft.setCursor(20,30); 43 | tft.print("Hello World!"); 44 | delay(3000); 45 | } 46 | } 47 | //------------------------------------------------------------------------------------- 48 | 49 | ```` 50 | Display a bitmap or jpg file, thanks to Bodmer for his great work - JPEG Decoder Library 51 | https://github.com/Bodmer/JPEGDecoder 52 | ```` c++ 53 | #include "Arduino.h" 54 | #include "SPI.h" 55 | #include "SD_MMC.h" 56 | #include "FS.h" 57 | #include "ili9486.h" 58 | 59 | // GPIOs for SPI 60 | #define SPI_MOSI 23 61 | #define SPI_MISO 19 62 | #define SPI_SCK 18 63 | 64 | // GPIOs for TFT/TP 65 | #define TFT_CS 22 66 | #define TFT_DC 5 67 | #define TP_CS 13 68 | #define TP_IRQ 12 69 | 70 | TFT tft; 71 | 72 | //------------------------------------------------------------------------------------- 73 | void setup() { 74 | pinMode(2, INPUT_PULLUP); 75 | if(!SD_MMC.begin("/sd", true)){ 76 | log_e("SD Card Mount Failed"); 77 | while(1){}; // endless loop 78 | } 79 | tft.setFrequency(40000000); 80 | tft.begin(TFT_CS, TFT_DC, VSPI, SPI_MOSI, SPI_MISO, SPI_SCK); 81 | } 82 | //------------------------------------------------------------------------------------- 83 | void loop(void) { 84 | tft.setRotation(0); //portait 85 | tft.drawBmpFile(SD_MMC, "/wall_e.bmp", 0, 0); 86 | delay(2000); 87 | tft.setRotation(3); //landscape 88 | tft.drawJpgFile(SD_MMC,"/wallpaper1.jpg", 0,0); 89 | delay(2000); 90 | tft.drawJpgFile(SD_MMC,"/arduino.jpg", 100,50); 91 | delay(2000); 92 | } 93 | //------------------------------------------------------------------------------------- 94 | ```` 95 | Use the touchpad 96 | ```` c++ 97 | #include "Arduino.h" 98 | #include "ili9486.h" 99 | 100 | // GPIOs for SPI 101 | #define SPI_MOSI 23 102 | #define SPI_MISO 19 103 | #define SPI_SCK 18 104 | 105 | // GPIOs for TFT/TP 106 | #define TFT_CS 22 107 | #define TFT_DC 5 108 | #define TP_CS 13 109 | #define TP_IRQ 12 110 | 111 | TFT tft; 112 | TP tp(TP_CS, TP_IRQ); 113 | uint16_t tp_x, tp_y; 114 | 115 | //------------------------------------------------------------------------------------- 116 | void setup() { 117 | tft.begin(TFT_CS, TFT_DC, VSPI, SPI_MOSI, SPI_MISO, SPI_SCK); 118 | tft.setFrequency(40000000); 119 | tft.setRotation(1); //landscape 120 | tp.setRotation(1); 121 | tft.fillScreen(TFT_BLACK); 122 | tft.setTextColor(TFT_GREENYELLOW); 123 | tft.setTextSize(4); 124 | } 125 | //------------------------------------------------------------------------------------- 126 | void loop(void) { 127 | tp.loop(); 128 | } 129 | //------------------------------------------------------------------------------------- 130 | // Events from TouchPad 131 | void tp_pressed(uint16_t x, uint16_t y){ 132 | tp_x=x; tp_y=y; 133 | } 134 | void tp_released(){ 135 | tft.fillRect(100, 100, 120, 50, TFT_BLACK); 136 | tft.setCursor(100, 100); 137 | tft.print("PosX="); tft.println(tp_x); 138 | tft.print("PosY="); tft.println(tp_y); 139 | } 140 | //------------------------------------------------------------------------------------- 141 | ```` 142 | 143 | 144 | -------------------------------------------------------------------------------- /TFT_ILI9486.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "SPI.h" 3 | #include "tft.h" 4 | 5 | #define TFT_CS 22 6 | #define TFT_DC 21 7 | #define SPI_MOSI 23 8 | #define SPI_MISO 19 9 | #define SPI_SCK 18 10 | 11 | 12 | #define min(X, Y) (((X) < (Y)) ? (X) : (Y)) 13 | 14 | TFT tft; 15 | 16 | 17 | void testText(); // function prototyp 18 | 19 | void setup() { 20 | Serial.begin(115200); 21 | SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI); 22 | tft.begin(TFT_CS, TFT_DC, SPI_MOSI, SPI_MISO, SPI_SCK); 23 | tft.setRotation(1); 24 | tft.fillScreen(TFT_BLACK); 25 | } 26 | 27 | //------------------------------------------------------------------------------------- 28 | void loop(void) { 29 | for(uint8_t rotation=0; rotation<4; rotation++) { 30 | tft.setRotation(rotation); 31 | testText(); 32 | delay(3000); 33 | } 34 | } 35 | //------------------------------------------------------------------------------------- 36 | 37 | void testText() { 38 | tft.fillScreen(TFT_BLACK); 39 | tft.setCursor(0, 0); 40 | tft.setTextColor(TFT_WHITE); tft.setTextSize(1); 41 | tft.println("Hello World!"); 42 | tft.setTextColor(TFT_YELLOW); tft.setTextSize(2); 43 | tft.println(1234.56); 44 | tft.setTextColor(TFT_RED); tft.setTextSize(3); 45 | tft.println(0xDEADBEEF, HEX); 46 | tft.println(); 47 | tft.setTextColor(TFT_GREEN); 48 | tft.setTextSize(6); 49 | tft.println("Groop"); 50 | tft.setTextSize(2); 51 | tft.println("I implore thee,"); 52 | tft.setTextSize(1); 53 | tft.println("my foonting turlingdromes."); 54 | tft.println("And hooptiously drangle me"); 55 | tft.println("with crinkly bindlewurdles,"); 56 | tft.println("Or I will rend thee"); 57 | tft.println("in the gobberwarts"); 58 | tft.println("with my blurglecruncheon,"); 59 | tft.println("see if I don't!"); 60 | } 61 | -------------------------------------------------------------------------------- /benchmark.txt: -------------------------------------------------------------------------------- 1 | 2 | RPi LCD(A) SPI 26.6MHz (80MHz/3) 3 | 4 | Benchmark Time (microseconds) 5 | Screen fill 545578 6 | Text 75752 7 | Lines 477363 8 | Horiz/Vert Lines 47781 9 | Rectangles (outline) 28114 10 | Rectangles (filled) 1330747 11 | Circles (filled) 299565 12 | Circles (outline) 398110 13 | Triangles (outline) 98622 14 | Triangles (filled) 497232 15 | Rounded rects (outline) 126946 16 | Rounded rects (filled) 1485030 17 | 18 | -------------------------------------------- 19 | 20 | RPi LCD(B) SPI 40MHz (80MHz/2) 21 | 22 | Benchmark Time (microseconds) 23 | Screen fill 389912 24 | Text 64342 25 | Lines 401130 26 | Horiz/Vert Lines 35013 27 | Rectangles (outline) 20797 28 | Rectangles (filled) 951325 29 | Circles (filled) 241763 30 | Circles (outline) 335487 31 | Triangles (outline) 82751 32 | Triangles (filled) 369945 33 | Rounded rects (outline) 104712 34 | Rounded rects (filled) 1070470 35 | 36 | -------------------------------------------- 37 | 38 | MHS-3.5inch Display 80MHz 39 | 40 | Benchmark Time (microseconds) 41 | Screen fill 240469 42 | Text 74525 43 | Lines 431731 44 | Horiz/Vert Lines 159632 45 | Rectangles (outline) 52956 46 | Rectangles (filled) 619145 47 | Circles (filled) 383955 48 | Circles (outline) 424709 49 | Triangles (outline) 140530 50 | Triangles (filled) 265034 51 | Rounded rects (outline) 122529 52 | Rounded rects (filled) 695859 53 | -------------------------------------------------------------------------------- /component.mk: -------------------------------------------------------------------------------- 1 | COMPONENT_ADD_INCLUDEDIRS := src 2 | COMPONENT_SRCDIRS := src 3 | CXXFLAGS += -fno-rtti 4 | -------------------------------------------------------------------------------- /examples/Benchmark/Benchmark.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "ili9486.h" 3 | 4 | // GPIOs for SPI 5 | #define SPI_MOSI 23 6 | #define SPI_MISO 19 7 | #define SPI_SCK 18 8 | 9 | // GPIOs for TFT/TP 10 | #define TFT_CS 22 11 | #define TFT_DC 5 12 | #define TP_CS 13 13 | #define TP_IRQ 12 14 | 15 | TFT tft; 16 | 17 | 18 | // function prototypes 19 | unsigned long testFillScreen(); 20 | unsigned long testText(); 21 | unsigned long testLines(uint16_t color); 22 | unsigned long testFastLines(uint16_t color1, uint16_t color2); 23 | unsigned long testRects(uint16_t color); 24 | unsigned long testFilledRects(uint16_t color1, uint16_t color2); 25 | unsigned long testCircles(uint8_t radius, uint16_t color); 26 | unsigned long testFilledCircles(uint8_t radius, uint16_t color); 27 | unsigned long testTriangles(); 28 | unsigned long testFilledTriangles(); 29 | unsigned long testRoundRects(); 30 | unsigned long testFilledRoundRects(); 31 | 32 | void setup() { 33 | pinMode(TP_CS, OUTPUT); 34 | digitalWrite(TP_CS, HIGH); // set Touchpad inactive 35 | Serial.begin(115200); 36 | tft.begin(TFT_CS, TFT_DC, VSPI, SPI_MOSI, SPI_MISO, SPI_SCK); 37 | tft.setFrequency(40000000); // 20000000, 27000000, 40000000, 80000000 38 | tft.setRotation(1); 39 | tft.fillScreen(TFT_BLACK); 40 | 41 | Serial.println(F("Benchmark Time (microseconds)")); 42 | delay(10); 43 | Serial.print(F("Screen fill ")); 44 | Serial.println(testFillScreen()); 45 | delay(500); 46 | 47 | Serial.print(F("Text ")); 48 | Serial.println(testText()); 49 | delay(3000); 50 | 51 | Serial.print(F("Lines ")); 52 | Serial.println(testLines(TFT_CYAN)); 53 | delay(500); 54 | 55 | Serial.print(F("Horiz/Vert Lines ")); 56 | Serial.println(testFastLines(TFT_RED, TFT_BLUE)); 57 | delay(500); 58 | 59 | Serial.print(F("Rectangles (outline) ")); 60 | Serial.println(testRects(TFT_GREEN)); 61 | delay(500); 62 | 63 | Serial.print(F("Rectangles (filled) ")); 64 | Serial.println(testFilledRects(TFT_YELLOW, TFT_MAGENTA)); 65 | delay(500); 66 | 67 | Serial.print(F("Circles (filled) ")); 68 | Serial.println(testFilledCircles(10, TFT_MAGENTA)); 69 | 70 | Serial.print(F("Circles (outline) ")); 71 | Serial.println(testCircles(10, TFT_WHITE)); 72 | delay(500); 73 | 74 | Serial.print(F("Triangles (outline) ")); 75 | Serial.println(testTriangles()); 76 | delay(500); 77 | 78 | Serial.print(F("Triangles (filled) ")); 79 | Serial.println(testFilledTriangles()); 80 | delay(500); 81 | 82 | Serial.print(F("Rounded rects (outline) ")); 83 | Serial.println(testRoundRects()); 84 | delay(500); 85 | 86 | Serial.print(F("Rounded rects (filled) ")); 87 | Serial.println(testFilledRoundRects()); 88 | delay(500); 89 | 90 | Serial.println(F("Done!")); 91 | } 92 | 93 | //------------------------------------------------------------------------------------- 94 | void loop(void) { 95 | for (uint8_t rotation = 0; rotation < 4; rotation++) { 96 | tft.setRotation(rotation); 97 | testText(); 98 | delay(3000); 99 | } 100 | } 101 | //------------------------------------------------------------------------------------- 102 | 103 | unsigned long testFillScreen() { 104 | unsigned long start = micros(); 105 | tft.fillScreen(TFT_BLACK); 106 | tft.fillScreen(TFT_RED); 107 | tft.fillScreen(TFT_GREEN); 108 | tft.fillScreen(TFT_BLUE); 109 | tft.fillScreen(TFT_BLACK); 110 | return micros() - start; 111 | } 112 | 113 | unsigned long testText() { 114 | tft.fillScreen(TFT_BLACK); 115 | unsigned long start = micros(); 116 | tft.setCursor(10, 10); 117 | tft.setTextColor(TFT_WHITE); 118 | tft.setTextSize(1); 119 | tft.println("Hello World!"); 120 | tft.setTextColor(TFT_YELLOW); 121 | tft.setTextSize(2); 122 | tft.println(1234.56); 123 | tft.setTextColor(TFT_RED); 124 | tft.setTextSize(3); 125 | tft.println(0xDEADBEEF, HEX); 126 | tft.println(); 127 | tft.setTextColor(TFT_GREEN); 128 | tft.setTextSize(6); 129 | tft.println("Groop"); 130 | tft.setTextSize(3); 131 | tft.print("Oh freddled gruntbuggly thy micturations are to me "); 132 | tft.print("As plurdled gabbleblotchits on a lurgid bee. Groop I implore thee, "); 133 | tft.print("my foonting turlingdromes. And hooptiously drangle me with crinkly bindlewurdles, "); 134 | tft.print("Or I will rend thee in the gobberwarts with my blurglecruncheon, see if I dont!"); 135 | return micros() - start; 136 | } 137 | 138 | unsigned long testLines(uint16_t color) { 139 | unsigned long start, t; 140 | int x1, y1, x2, y2, w = tft.width(), h = tft.height(); 141 | 142 | tft.fillScreen(TFT_BLACK); 143 | yield(); 144 | 145 | x1 = y1 = 0; 146 | y2 = h - 1; 147 | start = micros(); 148 | for (x2 = 0; x2 < w; x2 += 6) tft.drawLine(x1, y1, x2, y2, color); 149 | x2 = w - 1; 150 | for (y2 = 0; y2 < h; y2 += 6) tft.drawLine(x1, y1, x2, y2, color); 151 | t = micros() - start; // fillScreen doesn't count against timing 152 | 153 | yield(); 154 | tft.fillScreen(TFT_BLACK); 155 | yield(); 156 | 157 | x1 = w - 1; 158 | y1 = 0; 159 | y2 = h - 1; 160 | start = micros(); 161 | for (x2 = 0; x2 < w; x2 += 6) tft.drawLine(x1, y1, x2, y2, color); 162 | x2 = 0; 163 | for (y2 = 0; y2 < h; y2 += 6) tft.drawLine(x1, y1, x2, y2, color); 164 | t += micros() - start; 165 | 166 | yield(); 167 | tft.fillScreen(TFT_BLACK); 168 | yield(); 169 | 170 | x1 = 0; 171 | y1 = h - 1; 172 | y2 = 0; 173 | start = micros(); 174 | for (x2 = 0; x2 < w; x2 += 6) tft.drawLine(x1, y1, x2, y2, color); 175 | x2 = w - 1; 176 | for (y2 = 0; y2 < h; y2 += 6) tft.drawLine(x1, y1, x2, y2, color); 177 | t += micros() - start; 178 | 179 | yield(); 180 | tft.fillScreen(TFT_BLACK); 181 | yield(); 182 | 183 | x1 = w - 1; 184 | y1 = h - 1; 185 | y2 = 0; 186 | start = micros(); 187 | for (x2 = 0; x2 < w; x2 += 6) tft.drawLine(x1, y1, x2, y2, color); 188 | x2 = 0; 189 | for (y2 = 0; y2 < h; y2 += 6) tft.drawLine(x1, y1, x2, y2, color); 190 | 191 | yield(); 192 | return micros() - start; 193 | } 194 | 195 | unsigned long testFastLines(uint16_t color1, uint16_t color2) { 196 | unsigned long start; 197 | int x, y, w = tft.width(), h = tft.height(); 198 | 199 | tft.fillScreen(TFT_BLACK); 200 | start = micros(); 201 | for (y = 0; y < h; y += 5) tft.drawFastHLine(0, y, w, color1); 202 | for (x = 0; x < w; x += 5) tft.drawFastVLine(x, 0, h, color2); 203 | 204 | return micros() - start; 205 | } 206 | 207 | unsigned long testRects(uint16_t color) { 208 | unsigned long start; 209 | int n, i, i2, cx = tft.width() / 2, cy = tft.height() / 2; 210 | 211 | tft.fillScreen(TFT_BLACK); 212 | n = min(tft.width(), tft.height()); 213 | start = micros(); 214 | for (i = 2; i < n; i += 6) { 215 | i2 = i / 2; 216 | tft.drawRect(cx - i2, cy - i2, i, i, color); 217 | } 218 | 219 | return micros() - start; 220 | } 221 | 222 | unsigned long testFilledRects(uint16_t color1, uint16_t color2) { 223 | unsigned long start, t = 0; 224 | int n, i, i2, cx = tft.width() / 2 - 1, cy = tft.height() / 2 - 1; 225 | 226 | tft.fillScreen(TFT_BLACK); 227 | n = min(tft.width(), tft.height()); 228 | for (i = n; i > 0; i -= 6) { 229 | i2 = i / 2; 230 | start = micros(); 231 | tft.fillRect(cx - i2, cy - i2, i, i, color1); 232 | t += micros() - start; 233 | // Outlines are not included in timing results 234 | tft.drawRect(cx - i2, cy - i2, i, i, color2); 235 | } 236 | 237 | return t; 238 | } 239 | 240 | unsigned long testFilledCircles(uint8_t radius, uint16_t color) { 241 | unsigned long start; 242 | int x, y, w = tft.width(), h = tft.height(), r2 = radius * 2; 243 | 244 | tft.fillScreen(TFT_BLACK); 245 | start = micros(); 246 | for (x = radius; x < w; x += r2) { 247 | for (y = radius; y < h; y += r2) { 248 | tft.fillCircle(x, y, radius, color); 249 | } 250 | } 251 | 252 | return micros() - start; 253 | } 254 | 255 | unsigned long testCircles(uint8_t radius, uint16_t color) { 256 | unsigned long start; 257 | int x, y, r2 = radius * 2, w = tft.width() + radius, h = tft.height() + radius; 258 | 259 | // Screen is not cleared for this one -- this is 260 | // intentional and does not affect the reported time. 261 | start = micros(); 262 | for (x = 0; x < w; x += r2) { 263 | for (y = 0; y < h; y += r2) { 264 | tft.drawCircle(x, y, radius, color); 265 | } 266 | } 267 | 268 | return micros() - start; 269 | } 270 | 271 | unsigned long testTriangles() { 272 | unsigned long start; 273 | int n, i, cx = tft.width() / 2 - 1, cy = tft.height() / 2 - 1; 274 | 275 | tft.fillScreen(TFT_BLACK); 276 | n = min(cx, cy); 277 | start = micros(); 278 | for (i = 0; i < n; i += 5) { 279 | tft.drawTriangle(cx, cy - i, // peak 280 | cx - i, cy + i, // bottom left 281 | cx + i, cy + i, // bottom right 282 | tft.color565(i, i, i)); 283 | } 284 | 285 | return micros() - start; 286 | } 287 | 288 | unsigned long testFilledTriangles() { 289 | unsigned long start, t = 0; 290 | int i, cx = tft.width() / 2 - 1, cy = tft.height() / 2 - 1; 291 | 292 | tft.fillScreen(TFT_BLACK); 293 | start = micros(); 294 | for (i = min(cx, cy); i > 10; i -= 5) { 295 | start = micros(); 296 | tft.fillTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i, tft.color565(0, i * 10, i * 10)); 297 | t += micros() - start; 298 | tft.drawTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i, tft.color565(i * 10, i * 10, 0)); 299 | yield(); 300 | } 301 | 302 | return t; 303 | } 304 | 305 | unsigned long testRoundRects() { 306 | unsigned long start; 307 | int w, i, i2, cx = tft.width() / 2 - 1, cy = tft.height() / 2 - 1; 308 | 309 | tft.fillScreen(TFT_BLACK); 310 | w = min(tft.width(), tft.height()); 311 | start = micros(); 312 | for (i = 0; i < w; i += 6) { 313 | i2 = i / 2; 314 | tft.drawRoundRect(cx - i2, cy - i2, i, i, i / 8, tft.color565(i, 0, 0)); 315 | } 316 | 317 | return micros() - start; 318 | } 319 | 320 | unsigned long testFilledRoundRects() { 321 | unsigned long start; 322 | int i, i2, cx = tft.width() / 2 - 1, cy = tft.height() / 2 - 1; 323 | 324 | tft.fillScreen(TFT_BLACK); 325 | start = micros(); 326 | for (i = min(tft.width(), tft.height()); i > 20; i -= 6) { 327 | i2 = i / 2; 328 | tft.fillRoundRect(cx - i2, cy - i2, i, i, i / 8, tft.color565(0, i, 0)); 329 | yield(); 330 | } 331 | 332 | return micros() - start; 333 | } 334 | -------------------------------------------------------------------------------- /examples/CP_1251_cyrillic/CP_1251_cyrillic.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "ili9486.h" 3 | 4 | // GPIOs for SPI 5 | #define SPI_MOSI 23 6 | #define SPI_MISO 19 7 | #define SPI_SCK 18 8 | 9 | // GPIOs for TFT/TP 10 | #define TFT_CS 22 11 | #define TFT_DC 5 12 | #define TP_CS 13 13 | #define TP_IRQ 12 14 | 15 | TFT tft; 16 | 17 | void setup() { 18 | String txt=""; 19 | pinMode(TP_CS, OUTPUT); 20 | digitalWrite(TP_CS, HIGH); // disable the touchpad 21 | tft.begin(TFT_CS, TFT_DC, VSPI, SPI_MOSI, SPI_MISO, SPI_SCK); 22 | tft.fillScreen(TFT_BLACK); 23 | tft.setRotation(1); 24 | tft.setCursor(0, 0); 25 | tft.setFont(Times_New_Roman38x31); 26 | tft.setTextColor(TFT_WHITE); 27 | txt="Парус – a poem by M.Lermontow\n"; 28 | tft.println(txt); 29 | tft.setTextColor(TFT_YELLOW); 30 | txt= "Белеет парус одинокий\n"; 31 | txt+="В тумане моря голубом!..\n"; 32 | txt+="Что ищет он в стране далекой?,\n"; 33 | txt+="Что кинул он в краю родном?..\n"; 34 | tft.println(txt); 35 | } 36 | //------------------------------------------------------------------------------------- 37 | void loop(void) { 38 | delay(2000); 39 | } 40 | -------------------------------------------------------------------------------- /examples/CP_1251_cyrillic/CP_1251_cyrillic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schreibfaul1/ESP32-TFT-Library-ILI9486/34f9a726d5c598fb53209780fca335d7f14cd1b5/examples/CP_1251_cyrillic/CP_1251_cyrillic.jpg -------------------------------------------------------------------------------- /examples/CP_1252_latin/CP_1252_latin.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "ili9486.h" 3 | 4 | // GPIOs for SPI 5 | #define SPI_MOSI 23 6 | #define SPI_MISO 19 7 | #define SPI_SCK 18 8 | 9 | // GPIOs for TFT/TP 10 | #define TFT_CS 22 11 | #define TFT_DC 5 12 | #define TP_CS 13 13 | #define TP_IRQ 12 14 | 15 | TFT tft; 16 | 17 | void setup() { 18 | String txt=""; 19 | pinMode(TP_CS, OUTPUT); 20 | digitalWrite(TP_CS, HIGH); // disable the touchpad 21 | tft.begin(TFT_CS, TFT_DC, VSPI, SPI_MOSI, SPI_MISO, SPI_SCK); 22 | tft.fillScreen(TFT_BLACK); 23 | tft.setRotation(1); 24 | tft.setCursor(0, 0); 25 | tft.setFont(Times_New_Roman34x27); 26 | tft.setTextColor(TFT_YELLOW); 27 | txt= " Hør mig Vætter,\n Hør mig Thurser,\n"; 28 | txt+=" Hør mig alfer og dværge.\n"; 29 | txt+=" Hør mig Aser,\n Hør mig Vaner,\n"; 30 | txt+=" Hør mig disedøtre og muspelsønner.\n"; 31 | txt+=" På dette sted mrker vi marken,\n"; 32 | txt+=" På dette sted ære vi guder,\n"; 33 | txt+=" På dette sted holder vi blot."; 34 | tft.print(txt); 35 | } 36 | //------------------------------------------------------------------------------------- 37 | void loop(void) { 38 | delay(2000); 39 | } 40 | -------------------------------------------------------------------------------- /examples/CP_1252_latin/CP_1252_latin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schreibfaul1/ESP32-TFT-Library-ILI9486/34f9a726d5c598fb53209780fca335d7f14cd1b5/examples/CP_1252_latin/CP_1252_latin.jpg -------------------------------------------------------------------------------- /examples/CP_1253_greek/CP_1253_greek.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "ili9486.h" 3 | 4 | // GPIOs for SPI 5 | #define SPI_MOSI 23 6 | #define SPI_MISO 19 7 | #define SPI_SCK 18 8 | 9 | // GPIOs for TFT/TP 10 | #define TFT_CS 22 11 | #define TFT_DC 5 12 | #define TP_CS 13 13 | #define TP_IRQ 12 14 | 15 | TFT tft; 16 | 17 | void setup() { 18 | String txt=""; 19 | pinMode(TP_CS, OUTPUT); 20 | digitalWrite(TP_CS, HIGH); // disable the touchpad 21 | tft.begin(TFT_CS, TFT_DC, VSPI, SPI_MOSI, SPI_MISO, SPI_SCK); 22 | tft.fillScreen(TFT_BLACK); 23 | tft.setRotation(1); 24 | tft.setCursor(0, 0); 25 | tft.setFont(Times_New_Roman43x35); 26 | tft.setTextColor(TFT_YELLOW); 27 | txt="Ο Παπάς πρώτα τα γένια του ευλογεί."; 28 | tft.print(txt); 29 | tft.setTextColor(TFT_WHITE); 30 | txt=" The pastor blesses his own beard first\n"; 31 | tft.println(txt); 32 | tft.setTextColor(TFT_YELLOW); 33 | txt="Ο τρελός είδε τον μεθυσμένο και φοβήθηκε."; 34 | tft.print(txt); 35 | tft.setTextColor(TFT_WHITE); 36 | txt= " A drunk is more dangerous than a madman"; 37 | tft.print(txt); 38 | } 39 | //------------------------------------------------------------------------------------- 40 | void loop(void) { 41 | delay(2000); 42 | } 43 | -------------------------------------------------------------------------------- /examples/CP_1253_greek/CP_1253_greek.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schreibfaul1/ESP32-TFT-Library-ILI9486/34f9a726d5c598fb53209780fca335d7f14cd1b5/examples/CP_1253_greek/CP_1253_greek.jpg -------------------------------------------------------------------------------- /examples/Old_English_Text/Old_English_Text.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "ili9486.h" 3 | 4 | // GPIOs for SPI 5 | #define SPI_MOSI 23 6 | #define SPI_MISO 19 7 | #define SPI_SCK 18 8 | 9 | // GPIOs for TFT/TP 10 | #define TFT_CS 22 11 | #define TFT_DC 5 12 | #define TP_CS 13 13 | #define TP_IRQ 12 14 | 15 | TFT tft; 16 | 17 | void setup() { 18 | String txt=""; 19 | pinMode(TP_CS, OUTPUT); 20 | digitalWrite(TP_CS, HIGH); // disable the touchpad 21 | tft.begin(TFT_CS, TFT_DC, VSPI, SPI_MOSI, SPI_MISO, SPI_SCK); 22 | tft.fillScreen(TFT_BLACK); 23 | tft.setRotation(1); 24 | tft.setCursor(0, 0); 25 | tft.setFont(Old_English_Text_MT32x32); // uncomment font in ili9486.h 26 | tft.setTextColor(TFT_WHITE); 27 | txt= "O rose, thou art sick!\nThe invisible worm,\n"; 28 | txt+="That flies in the nightn\nIn the howling storm.\n"; 29 | txt+="Has found out thy bed\nOf crimson joy,\n"; 30 | txt+="And his dark secret love\n,Does thy life destroy"; 31 | tft.print(txt); 32 | } 33 | //------------------------------------------------------------------------------------- 34 | void loop(void) { 35 | delay(2000); 36 | } 37 | -------------------------------------------------------------------------------- /examples/Old_English_Text/Old_English_Text.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schreibfaul1/ESP32-TFT-Library-ILI9486/34f9a726d5c598fb53209780fca335d7f14cd1b5/examples/Old_English_Text/Old_English_Text.jpg -------------------------------------------------------------------------------- /images/GLCD Font Creator.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schreibfaul1/ESP32-TFT-Library-ILI9486/34f9a726d5c598fb53209780fca335d7f14cd1b5/images/GLCD Font Creator.jpg -------------------------------------------------------------------------------- /images/MHS-3.5inch Display.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schreibfaul1/ESP32-TFT-Library-ILI9486/34f9a726d5c598fb53209780fca335d7f14cd1b5/images/MHS-3.5inch Display.png -------------------------------------------------------------------------------- /images/RPi 3.5inch Pins.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schreibfaul1/ESP32-TFT-Library-ILI9486/34f9a726d5c598fb53209780fca335d7f14cd1b5/images/RPi 3.5inch Pins.JPG -------------------------------------------------------------------------------- /images/SAM_0205.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schreibfaul1/ESP32-TFT-Library-ILI9486/34f9a726d5c598fb53209780fca335d7f14cd1b5/images/SAM_0205.jpg -------------------------------------------------------------------------------- /images/SPI DIsplay.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schreibfaul1/ESP32-TFT-Library-ILI9486/34f9a726d5c598fb53209780fca335d7f14cd1b5/images/SPI DIsplay.JPG -------------------------------------------------------------------------------- /images/Waveshare RPi 3.5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schreibfaul1/ESP32-TFT-Library-ILI9486/34f9a726d5c598fb53209780fca335d7f14cd1b5/images/Waveshare RPi 3.5.jpg -------------------------------------------------------------------------------- /images/arduino.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schreibfaul1/ESP32-TFT-Library-ILI9486/34f9a726d5c598fb53209780fca335d7f14cd1b5/images/arduino.jpg -------------------------------------------------------------------------------- /images/tested displays.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schreibfaul1/ESP32-TFT-Library-ILI9486/34f9a726d5c598fb53209780fca335d7f14cd1b5/images/tested displays.png -------------------------------------------------------------------------------- /images/wall_e.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schreibfaul1/ESP32-TFT-Library-ILI9486/34f9a726d5c598fb53209780fca335d7f14cd1b5/images/wall_e.bmp -------------------------------------------------------------------------------- /images/wallpaper1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schreibfaul1/ESP32-TFT-Library-ILI9486/34f9a726d5c598fb53209780fca335d7f14cd1b5/images/wallpaper1.jpg -------------------------------------------------------------------------------- /images/wallpaper2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schreibfaul1/ESP32-TFT-Library-ILI9486/34f9a726d5c598fb53209780fca335d7f14cd1b5/images/wallpaper2.jpg -------------------------------------------------------------------------------- /images/wallpaper3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schreibfaul1/ESP32-TFT-Library-ILI9486/34f9a726d5c598fb53209780fca335d7f14cd1b5/images/wallpaper3.jpg -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Ultrasound 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | TFT KEYWORD1 10 | TP KEYWORD1 11 | 12 | ####################################### 13 | # Methods and Functions (KEYWORD2) 14 | ####################################### 15 | 16 | begin KEYWORD2 17 | setRotation KEYWORD2 18 | setFrequency KEYWORD2 19 | setCursor KEYWORD2 20 | invertDisplay KEYWORD2 21 | write KEYWORD2 22 | drawLine KEYWORD2 23 | drawFastVLine KEYWORD2 24 | drawFastHLine KEYWORD2 25 | drawRect KEYWORD2 26 | fillRect KEYWORD2 27 | drawRoundRect KEYWORD2 28 | fillRoundRect KEYWORD2 29 | fillScreen KEYWORD2 30 | drawTriangle KEYWORD2 31 | fillTriangle KEYWORD2 32 | drawCircle KEYWORD2 33 | fillCircle KEYWORD2 34 | setBrigthness KEYWORD2 35 | drawBmpFile KEYWORD2 36 | drawJpgFile KEYWORD2 37 | color565 KEYWORD2 38 | writeText KEYWORD2 39 | setTextColor KEYWORD2 40 | setFont KEYWORD2 41 | setTextSize KEYWORD2 42 | setTextOrientation KEYWORD2 43 | height KEYWORD2 44 | width KEYWORD2 45 | getRotation KEYWORD2 46 | 47 | ####################################### 48 | # Constants (LITERAL1) 49 | ####################################### 50 | 51 | TFT_AQUAMARINE LITERAL1 52 | TFT_BEIGE LITERAL1 53 | TFT_BLACK LITERAL1 54 | TFT_BLUE LITERAL1 55 | TFT_BROWN LITERAL1 56 | TFT_CHOCOLATE LITERAL1 57 | TFT_CORNSILK LITERAL1 58 | TFT_CYAN LITERAL1 59 | TFT_DARKGREEN LITERAL1 60 | TFT_DARKGREY LITERAL1 61 | TFT_DARKCYAN LITERAL1 62 | TFT_DEEPSKYBLUE LITERAL1 63 | TFT_GRAY LITERAL1 64 | TFT_GREEN LITERAL1 65 | TFT_GREENYELLOW LITERAL1 66 | TFT_GOLD LITERAL1 67 | TFT_HOTPINK LITERAL1 68 | TFT_LAVENDER LITERAL1 69 | TFT_LAWNGREEN LITERAL1 70 | TFT_LIGHTBLUE LITERAL1 71 | TFT_LIGHTCYAN LITERAL1 72 | TFT_LIGHTGREY LITERAL1 73 | TFT_LIGHTGREEN LITERAL1 74 | TFT_LIGHTYELLOW LITERAL1 75 | TFT_LIME LITERAL1 76 | TFT_MAGENTA LITERAL1 77 | TFT_MAROON LITERAL1 78 | TFT_MEDIUMVIOLETRED LITERAL1 79 | TFT_NAVY LITERAL1 80 | TFT_OLIVE LITERAL1 81 | TFT_ORANGE LITERAL1 82 | TFT_PINK LITERAL1 83 | TFT_PURPLE LITERAL1 84 | TFT_RED LITERAL1 85 | TFT_SANDYBROWN LITERAL1 86 | TFT_TURQUOISE LITERAL1 87 | TFT_VIOLET LITERAL1 88 | TFT_WHITE LITERAL1 89 | TFT_YELLOW LITERAL1 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESP32-TFT-Library-ILI9486 2 | version=0.0.0 3 | author=schreibfaul1 4 | maintainer=schreibfaul1 5 | sentence=A library for 3.5 inch RPi LCD (A) 320x480 display from Waveshare. 6 | paragraph=Create new fonts with MikroElektronika GLCD Font Creator and insert the new font in fonts.h. You can also display bitmaps. Touchpadcontroller XPT2046 is included. 7 | category=Display 8 | url=https://github.com/schreibfaul1/ESP32-TFT-Library-ILI9486 9 | architectures=ESP32, esp32 10 | includes=tft.h 11 | -------------------------------------------------------------------------------- /src/ili9486.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * tft.cpp 3 | * 4 | * Created on: May 28,2018 5 | * Updated on: May 05,2025 6 | * Author: Wolle (schreibfaul1) 7 | * 8 | */ 9 | 10 | 11 | 12 | #include "Arduino.h" 13 | #include "ili9486.h" 14 | 15 | #define TFT_MAX_PIXELS_AT_ONCE 32 16 | 17 | JPEGDecoder JpegDec; 18 | SPIClass* SPItransfer; 19 | 20 | void TFT::init() { 21 | if(tft_info) tft_info("init ILI9486\n"); 22 | startWrite(); 23 | //Driving ability Setting 24 | writeCommand(0x11); // Sleep out, also SW reset 25 | delay(120); 26 | 27 | writeCommand(0x3A); // Interface Pixel Format 28 | spi_TFT->write16(0x55); 29 | 30 | writeCommand(0xC2); // Power Control 3 (For Normal Mode) 31 | spi_TFT->write16(0x44); 32 | 33 | writeCommand(0xC5); // VCOM Control 34 | spi_TFT->write16(0x00); 35 | spi_TFT->write16(0x00); 36 | spi_TFT->write16(0x00); 37 | spi_TFT->write16(0x00); 38 | 39 | writeCommand(0xE0); // PGAMCTRL(Positive Gamma Control) 40 | spi_TFT->write16(0x0F); 41 | spi_TFT->write16(0x1F); 42 | spi_TFT->write16(0x1C); 43 | spi_TFT->write16(0x0C); 44 | spi_TFT->write16(0x0F); 45 | spi_TFT->write16(0x08); 46 | spi_TFT->write16(0x48); 47 | spi_TFT->write16(0x98); 48 | spi_TFT->write16(0x37); 49 | spi_TFT->write16(0x0A); 50 | spi_TFT->write16(0x13); 51 | spi_TFT->write16(0x04); 52 | spi_TFT->write16(0x11); 53 | spi_TFT->write16(0x0D); 54 | spi_TFT->write16(0x00); 55 | 56 | writeCommand(0xE1); // NGAMCTRL (Negative Gamma Correction) 57 | spi_TFT->write16(0x0F); 58 | spi_TFT->write16(0x32); 59 | spi_TFT->write16(0x2E); 60 | spi_TFT->write16(0x0B); 61 | spi_TFT->write16(0x0D); 62 | spi_TFT->write16(0x05); 63 | spi_TFT->write16(0x47); 64 | spi_TFT->write16(0x75); 65 | spi_TFT->write16(0x37); 66 | spi_TFT->write16(0x06); 67 | spi_TFT->write16(0x10); 68 | spi_TFT->write16(0x03); 69 | spi_TFT->write16(0x24); 70 | spi_TFT->write16(0x20); 71 | spi_TFT->write16(0x00); 72 | 73 | writeCommand(0x20); // Display Inversion OFF RPi LCD (A) 74 | // writeCommand(0x21); // Display Inversion ON RPi LCD (B) 75 | 76 | writeCommand(0x36); // Memory Access Control 77 | spi_TFT->write16(0x48); 78 | 79 | writeCommand(0x29); // Display ON 80 | delay(150); 81 | endWrite(); 82 | } 83 | 84 | // Pass 8-bit (each) R,G,B, get back 16-bit packed color 85 | uint16_t TFT::color565(uint8_t r, uint8_t g, uint8_t b) { 86 | return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3); 87 | } 88 | 89 | TFT::TFT() { 90 | _freq = 20000000; 91 | _height = TFT_HEIGHT; 92 | _width = TFT_WIDTH; 93 | _rotation=0; 94 | } 95 | 96 | void TFT::setFrequency(uint32_t f){ 97 | if(f>80000000) f=80000000; 98 | _freq=f; // overwrite default 99 | } 100 | 101 | void TFT::startWrite(void){ 102 | spi_TFT->beginTransaction(SPISettings(_freq, MSBFIRST, SPI_MODE0)); 103 | TFT_CS_LOW(); 104 | } 105 | 106 | void TFT::endWrite(void){ 107 | TFT_CS_HIGH(); 108 | spi_TFT->endTransaction(); 109 | } 110 | 111 | void TFT::writeCommand(uint16_t cmd){ 112 | TFT_DC_LOW(); 113 | spi_TFT->write16(cmd); 114 | TFT_DC_HIGH(); 115 | } 116 | // Return the size of the display (per current rotation) 117 | int16_t TFT::width(void) const { 118 | return _width; 119 | } 120 | int16_t TFT::height(void) const { 121 | return _height; 122 | } 123 | uint8_t TFT::getRotation(void) const{ 124 | return _rotation; 125 | } 126 | 127 | //---------------------------------------------------------------------------------------------------------------------- 128 | 129 | void TFT::begin(uint8_t CS, uint8_t DC, uint8_t spi, uint8_t mosi, uint8_t miso, uint8_t sclk) { 130 | 131 | spi_TFT = new SPIClass(spi); 132 | spi_TFT->begin(sclk, miso, mosi, -1); 133 | spi_TFT->setFrequency(_freq); 134 | SPItransfer = spi_TFT; 135 | 136 | TFT_SPI = SPISettings(_freq, MSBFIRST, SPI_MODE0); 137 | 138 | 139 | String info=""; 140 | TFT_CS = CS; TFT_DC = DC; 141 | 142 | pinMode(TFT_DC, OUTPUT); 143 | digitalWrite(TFT_DC, LOW); 144 | pinMode(TFT_CS, OUTPUT); 145 | digitalWrite(TFT_CS, HIGH); 146 | 147 | // log_i("DC=%d, CS=%d, MISO=%d, MOSI=%d, SCK=%d", TFT_DC, TFT_CS, TFT_MISO, TFT_MOSI, TFT_SCK); 148 | spi_TFT->begin(TFT_SCK, TFT_MISO, TFT_MOSI, -1); 149 | 150 | init(); // 151 | } 152 | //---------------------------------------------------------------------------------------------------------------------- 153 | 154 | void TFT::setRotation(uint8_t m) { 155 | _rotation = m % 4; // can't be higher than 3 156 | startWrite(); 157 | writeCommand(ILI9486_MADCTL); 158 | 159 | switch (_rotation) { 160 | case 0: 161 | spi_TFT->write16(MADCTL_MX |MADCTL_BGR); 162 | _width = TFT_WIDTH; 163 | _height = TFT_HEIGHT; 164 | break; 165 | case 1: 166 | spi_TFT->write16(MADCTL_MV | MADCTL_BGR); 167 | _width = TFT_HEIGHT; 168 | _height = TFT_WIDTH; 169 | break; 170 | case 2: 171 | spi_TFT->write16(MADCTL_MY | MADCTL_BGR); 172 | _width = TFT_WIDTH; 173 | _height = TFT_HEIGHT; 174 | break; 175 | case 3: 176 | spi_TFT->write16(MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR); 177 | _width = TFT_HEIGHT; 178 | _height = TFT_WIDTH; 179 | break; 180 | } 181 | endWrite(); 182 | } 183 | 184 | 185 | /* 186 | * Transaction API 187 | * */ 188 | 189 | void TFT::setBrigthness(uint8_t br){ 190 | startWrite(); 191 | writeCommand(ILI9486_CDBVAL); 192 | spi_TFT->write16(0); 193 | writeCommand(ILI9486_WDBVAL); 194 | spi_TFT->write16(128); 195 | endWrite(); 196 | } 197 | 198 | void TFT::setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { 199 | writeCommand(ILI9486_CASET); // Column addr set 200 | spi_TFT->write16(x >> 8); 201 | spi_TFT->write16(x & 0xFF); // XSTART 202 | w=x+w-1; 203 | spi_TFT->write16(w >> 8); 204 | spi_TFT->write16(w & 0xFF); // XEND 205 | 206 | writeCommand(ILI9486_PASET); // Row addr set 207 | spi_TFT->write16(y >> 8); 208 | spi_TFT->write16(y & 0xFF); // YSTART 209 | h=y+h-1; 210 | spi_TFT->write16(h >> 8); 211 | spi_TFT->write16(h & 0xFF); // YEND 212 | } 213 | 214 | void TFT::startBitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { 215 | startWrite(); 216 | writeCommand(ILI9486_MADCTL); 217 | if(_rotation==0){spi_TFT->write16(MADCTL_MX | MADCTL_MY | MADCTL_ML | MADCTL_BGR);} 218 | if(_rotation==1){spi_TFT->write16(MADCTL_MH | MADCTL_MV | MADCTL_MX | MADCTL_BGR);} 219 | if(_rotation==2){spi_TFT->write16(MADCTL_MH | MADCTL_BGR);} 220 | if(_rotation==3){spi_TFT->write16(MADCTL_MV | MADCTL_MY | MADCTL_BGR);} 221 | 222 | setAddrWindow(x, _height - y - h ,w ,h); 223 | writeCommand(ILI9486_RAMWR); 224 | endWrite(); 225 | } 226 | 227 | void TFT::endBitmap() { 228 | setRotation(_rotation); 229 | } 230 | 231 | void TFT::startJpeg() { 232 | startWrite(); 233 | writeCommand(ILI9486_MADCTL); 234 | if(_rotation==0){spi_TFT->write16(MADCTL_MH | MADCTL_MX |MADCTL_BGR);} 235 | if(_rotation==1){spi_TFT->write16(MADCTL_MV | MADCTL_BGR);} 236 | if(_rotation==2){spi_TFT->write16(MADCTL_MY | MADCTL_BGR);} 237 | if(_rotation==3){spi_TFT->write16(MADCTL_MV | MADCTL_MY | MADCTL_MX |MADCTL_BGR);} 238 | endWrite(); 239 | } 240 | 241 | void TFT::endJpeg() { 242 | setRotation(_rotation); 243 | } 244 | 245 | void TFT::pushColor(uint16_t color) { 246 | startWrite(); 247 | spi_TFT->write16(color); 248 | endWrite(); 249 | } 250 | 251 | void TFT::writePixel(uint16_t color){ 252 | spi_TFT->write16(color); 253 | } 254 | 255 | void TFT::writePixels(uint16_t * colors, uint32_t len){ 256 | spi_TFT->writePixels((uint8_t*)colors , len * 2); 257 | } 258 | 259 | void TFT::writeColor(uint16_t color, uint32_t len){ 260 | static uint16_t temp[TFT_MAX_PIXELS_AT_ONCE]; 261 | size_t blen = (len > TFT_MAX_PIXELS_AT_ONCE)?TFT_MAX_PIXELS_AT_ONCE:len; 262 | uint16_t tlen = 0; 263 | 264 | for (uint32_t t=0; tblen)?blen:len; 270 | writePixels(temp, tlen); 271 | len -= tlen; 272 | } 273 | } 274 | 275 | void TFT::writePixel(int16_t x, int16_t y, uint16_t color) { 276 | if((x < 0) ||(x >= _width) || (y < 0) || (y >= _height)) return; 277 | setAddrWindow(x,y,1,1); 278 | writeCommand(ILI9486_RAMWR); 279 | writePixel(color); 280 | } 281 | 282 | void TFT::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color){ 283 | if((x >= _width) || (y >= _height)) return; 284 | 285 | int16_t x2 = x + w - 1, y2 = y + h - 1; 286 | if((x2 < 0) || (y2 < 0)) return; 287 | 288 | // Clip left/top 289 | if(x < 0) { 290 | x = 0; 291 | w = x2 + 1; 292 | } 293 | if(y < 0) { 294 | y = 0; 295 | h = y2 + 1; 296 | } 297 | 298 | // Clip right/bottom 299 | if(x2 >= _width) w = _width - x; 300 | if(y2 >= _height) h = _height - y; 301 | 302 | int32_t len = (int32_t)w * h; 303 | setAddrWindow(x, y, w, h); 304 | writeCommand(ILI9486_RAMWR); 305 | writeColor(color, len); 306 | } 307 | 308 | void TFT::writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color){ 309 | writeFillRect(x, y, 1, h, color); 310 | } 311 | 312 | void TFT::writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color){ 313 | writeFillRect(x, y, w, 1, color); 314 | } 315 | 316 | 317 | void TFT::drawPixel(int16_t x, int16_t y, uint16_t color){ 318 | startWrite(); 319 | writePixel(x, y, color); 320 | endWrite(); 321 | } 322 | 323 | void TFT::drawFastVLine(int16_t x, int16_t y, 324 | int16_t h, uint16_t color) { 325 | startWrite(); 326 | writeFastVLine(x, y, h, color); 327 | endWrite(); 328 | } 329 | 330 | void TFT::drawFastHLine(int16_t x, int16_t y, 331 | int16_t w, uint16_t color) { 332 | startWrite(); 333 | writeFastHLine(x, y, w, color); 334 | endWrite(); 335 | } 336 | /*******************************************************************************/ 337 | void TFT::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color) { 338 | // Bresenham's algorithm - thx wikipedia - speed enhanced by Bodmer to use 339 | // an eficient FastH/V Line draw routine for line segments of 2 pixels or more 340 | int16_t t; 341 | boolean steep = abs(y1 - y0) > abs(x1 - x0); 342 | if (steep) { 343 | t=x0; x0=y0; y0=t; // swap (x0, y0); 344 | t=x1; x1=y1; y1=t; // swap(x1, y1); 345 | } 346 | if (x0 > x1) { 347 | t=x0; x0=x1; x1=t; // swap(x0, x1); 348 | t=y0; y0=y1; y1=t; // swap(y0, y1); 349 | } 350 | int16_t dx = x1 - x0, dy = abs(y1 - y0);; 351 | int16_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0; 352 | 353 | if (y0 < y1) ystep = 1; 354 | startWrite(); 355 | // Split into steep and not steep for FastH/V separation 356 | if (steep) { 357 | for (; x0 <= x1; x0++) { 358 | dlen++; 359 | err -= dy; 360 | if (err < 0) { 361 | err += dx; 362 | if (dlen == 1) writePixel(y0, xs, color); 363 | else writeFastVLine(y0, xs, dlen, color); 364 | dlen = 0; y0 += ystep; xs = x0 + 1; 365 | } 366 | } 367 | if (dlen) writeFastVLine(y0, xs, dlen, color); 368 | } 369 | else { 370 | for (; x0 <= x1; x0++) { 371 | dlen++; 372 | err -= dy; 373 | if (err < 0) { 374 | err += dx; 375 | if (dlen == 1) writePixel(xs, y0, color); 376 | else writeFastHLine(xs, y0, dlen, color); 377 | dlen = 0; y0 += ystep; xs = x0 + 1; 378 | } 379 | } 380 | if (dlen) writeFastHLine(xs, y0, dlen, color); 381 | } 382 | endWrite(); 383 | } 384 | 385 | void TFT::fillScreen(uint16_t color) { 386 | // Update in subclasses if desired! 387 | fillRect(0, 0, width(), height(), color); 388 | } 389 | void TFT::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, 390 | uint16_t color) { 391 | startWrite(); 392 | writeFillRect(x,y,w,h,color); 393 | endWrite(); 394 | } 395 | /*******************************************************************************/ 396 | void TFT::drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, 397 | int16_t x2, int16_t y2, uint16_t color) { 398 | drawLine(x0, y0, x1, y1, color); 399 | drawLine(x1, y1, x2, y2, color); 400 | drawLine(x2, y2, x0, y0, color); 401 | } 402 | /*******************************************************************************/ 403 | void TFT::fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, 404 | int16_t x2, int16_t y2, uint16_t color) { 405 | 406 | int16_t a, b, y, last; 407 | 408 | // Sort coordinates by Y order (y2 >= y1 >= y0) 409 | if (y0 > y1) { 410 | _swap_int16_t(y0, y1); 411 | _swap_int16_t(x0, x1); 412 | } 413 | if (y1 > y2) { 414 | _swap_int16_t(y2, y1); 415 | _swap_int16_t(x2, x1); 416 | } 417 | if (y0 > y1) { 418 | _swap_int16_t(y0, y1); 419 | _swap_int16_t(x0, x1); 420 | } 421 | startWrite(); 422 | if (y0 == y2) { // Handle awkward all-on-same-line case as its own thing 423 | a = b = x0; 424 | if (x1 < a) 425 | a = x1; 426 | else if (x1 > b) 427 | b = x1; 428 | if (x2 < a) 429 | a = x2; 430 | else if (x2 > b) 431 | b = x2; 432 | writeFastHLine(a, y0, b - a + 1, color); 433 | endWrite(); 434 | return; 435 | } 436 | int16_t dx01 = x1 - x0, dy01 = y1 - y0, dx02 = x2 - x0, dy02 = y2 - y0, 437 | dx12 = x2 - x1, dy12 = y2 - y1; 438 | int32_t sa = 0, sb = 0; 439 | 440 | // For upper part of triangle, find scanline crossings for segments 441 | // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1 442 | // is included here (and second loop will be skipped, avoiding a /0 443 | // error there), otherwise scanline y1 is skipped here and handled 444 | // in the second loop...which also avoids a /0 error here if y0=y1 445 | // (flat-topped triangle). 446 | if (y1 == y2) 447 | last = y1; // Include y1 scanline 448 | else 449 | last = y1 - 1; // Skip it 450 | 451 | for (y = y0; y <= last; y++) { 452 | a = x0 + sa / dy01; 453 | b = x0 + sb / dy02; 454 | sa += dx01; 455 | sb += dx02; 456 | /* longhand: 457 | a = x0 + (x1 - x0) * (y - y0) / (y1 - y0); 458 | b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); 459 | */ 460 | if (a > b) 461 | _swap_int16_t(a, b); 462 | writeFastHLine(a, y, b - a + 1, color); 463 | } 464 | 465 | // For lower part of triangle, find scanline crossings for segments 466 | // 0-2 and 1-2. This loop is skipped if y1=y2. 467 | sa = (int32_t)dx12 * (y - y1); 468 | sb = (int32_t)dx02 * (y - y0); 469 | for (; y <= y2; y++) { 470 | a = x1 + sa / dy12; 471 | b = x0 + sb / dy02; 472 | sa += dx12; 473 | sb += dx02; 474 | /* longhand: 475 | a = x1 + (x2 - x1) * (y - y1) / (y2 - y1); 476 | b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); 477 | */ 478 | if (a > b) 479 | _swap_int16_t(a, b); 480 | writeFastHLine(a, y, b - a + 1, color); 481 | } 482 | endWrite(); 483 | } 484 | void TFT::drawRect(int16_t Xpos, int16_t Ypos, uint16_t Width, uint16_t Height, uint16_t Color) 485 | { 486 | if(Width<1 || Height<1) return; 487 | startWrite(); 488 | writeFastHLine(Xpos, Ypos, Width, Color); 489 | writeFastHLine(Xpos, Ypos + Height-1, Width, Color); 490 | writeFastVLine(Xpos, Ypos, Height, Color); 491 | writeFastVLine(Xpos + Width-1, Ypos, Height, Color); 492 | endWrite(); 493 | } 494 | 495 | void TFT::drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color) { 496 | // smarter version 497 | startWrite(); 498 | writeFastHLine(x + r, y, w - 2 * r, color); // Top 499 | writeFastHLine(x + r, y + h - 1, w - 2 * r, color); // Bottom 500 | writeFastVLine(x, y + r, h - 2 * r, color); // Left 501 | writeFastVLine(x + w - 1, y + r, h - 2 * r, color); // Right 502 | // draw four corners 503 | drawCircleHelper(x + r, y + r, r, 1, color); 504 | drawCircleHelper(x + w - r - 1, y + r, r, 2, color); 505 | drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color); 506 | drawCircleHelper(x + r, y + h - r - 1, r, 8, color); 507 | endWrite(); 508 | } 509 | /*******************************************************************************/ 510 | void TFT::fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color) { 511 | // smarter version 512 | startWrite(); 513 | writeFillRect(x + r, y, w - 2 * r, h, color); 514 | 515 | // draw four corners 516 | fillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color); 517 | fillCircleHelper(x + r, y + r, r, 2, h - 2 * r - 1, color); 518 | endWrite(); 519 | } 520 | /*******************************************************************************/ 521 | void TFT::drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color) { 522 | int16_t f = 1 - r; 523 | int16_t ddF_x = 1; 524 | int16_t ddF_y = -2 * r; 525 | int16_t x = 0; 526 | int16_t y = r; 527 | 528 | startWrite(); 529 | writePixel(x0, y0 + r, color); 530 | writePixel(x0, y0 - r, color); 531 | writePixel(x0 + r, y0, color); 532 | writePixel(x0 - r, y0, color); 533 | 534 | while (x < y) { 535 | if (f >= 0) { 536 | y--; 537 | ddF_y += 2; 538 | f += ddF_y; 539 | } 540 | x++; 541 | ddF_x += 2; 542 | f += ddF_x; 543 | 544 | writePixel(x0 + x, y0 + y, color); 545 | writePixel(x0 - x, y0 + y, color); 546 | writePixel(x0 + x, y0 - y, color); 547 | writePixel(x0 - x, y0 - y, color); 548 | writePixel(x0 + y, y0 + x, color); 549 | writePixel(x0 - y, y0 + x, color); 550 | writePixel(x0 + y, y0 - x, color); 551 | writePixel(x0 - y, y0 - x, color); 552 | } 553 | endWrite(); 554 | } 555 | /*******************************************************************************/ 556 | void TFT::fillCircle(int16_t Xm, //specify x position. 557 | int16_t Ym, //specify y position. 558 | uint16_t r, //specify the radius of the circle. 559 | uint16_t color) //specify the color of the circle. 560 | { 561 | signed int f = 1 - r, ddF_x = 1, ddF_y = 0 - (2 * r), x = 0, y = r; 562 | startWrite(); 563 | writeFastVLine(Xm, Ym - r, 2 * r, color); 564 | 565 | while (x < y) { 566 | if (f >= 0) { 567 | y--; 568 | ddF_y += 2; 569 | f += ddF_y; 570 | } 571 | 572 | x++; 573 | ddF_x += 2; 574 | f += ddF_x; 575 | 576 | writeFastVLine(Xm + x, Ym - y, 2 * y, color); 577 | writeFastVLine(Xm - x, Ym - y, 2 * y, color); 578 | writeFastVLine(Xm + y, Ym - x, 2 * x, color); 579 | writeFastVLine(Xm - y, Ym - x, 2 * x, color); 580 | } 581 | endWrite(); 582 | } 583 | /*******************************************************************************/ 584 | void TFT::drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint16_t color) { 585 | int16_t f = 1 - r; 586 | int16_t ddF_x = 1; 587 | int16_t ddF_y = -2 * r; 588 | int16_t x = 0; 589 | int16_t y = r; 590 | 591 | while (x < y) { 592 | if (f >= 0) { 593 | y--; 594 | ddF_y += 2; 595 | f += ddF_y; 596 | } 597 | x++; 598 | ddF_x += 2; 599 | f += ddF_x; 600 | if (cornername & 0x4) { 601 | writePixel(x0 + x, y0 + y, color); 602 | writePixel(x0 + y, y0 + x, color); 603 | } 604 | if (cornername & 0x2) { 605 | writePixel(x0 + x, y0 - y, color); 606 | writePixel(x0 + y, y0 - x, color); 607 | } 608 | if (cornername & 0x8) { 609 | writePixel(x0 - y, y0 + x, color); 610 | writePixel(x0 - x, y0 + y, color); 611 | } 612 | if (cornername & 0x1) { 613 | writePixel(x0 - y, y0 - x, color); 614 | writePixel(x0 - x, y0 - y, color); 615 | } 616 | } 617 | } 618 | /*******************************************************************************/ 619 | void TFT::fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, uint16_t color) { 620 | 621 | int16_t f = 1 - r; 622 | int16_t ddF_x = 1; 623 | int16_t ddF_y = -2 * r; 624 | int16_t x = 0; 625 | int16_t y = r; 626 | 627 | while (x < y) { 628 | if (f >= 0) { 629 | y--; 630 | ddF_y += 2; 631 | f += ddF_y; 632 | } 633 | x++; 634 | ddF_x += 2; 635 | f += ddF_x; 636 | 637 | if (cornername & 0x1) { 638 | writeFastVLine(x0 + x, y0 - y, 2 * y + 1 + delta, color); 639 | writeFastVLine(x0 + y, y0 - x, 2 * x + 1 + delta, color); 640 | } 641 | if (cornername & 0x2) { 642 | writeFastVLine(x0 - x, y0 - y, 2 * y + 1 + delta, color); 643 | writeFastVLine(x0 - y, y0 - x, 2 * x + 1 + delta, color); 644 | } 645 | } 646 | } 647 | /*******************************************************************************/ 648 | bool TFT::setCursor(uint16_t x, uint16_t y) { 649 | 650 | if (x >= width()|| y >= height()) { 651 | return false; 652 | } 653 | writeCommand(0x2A); 654 | spi_TFT->write(x >> 8); 655 | spi_TFT->write(x & 0xFF); 656 | writeCommand(ILI9486_RAMWR); //Column Start 657 | writeCommand(0x2B); 658 | spi_TFT->write(y >> 8); 659 | spi_TFT->write(y & 0xFF); 660 | writeCommand(ILI9486_RAMWR); //Row Start 661 | _curX=x; 662 | _curY=y; 663 | _f_curPos=true; //curPos is updated 664 | return true; 665 | } 666 | /*******************************************************************************/ 667 | size_t TFT::writeText(const uint8_t *str, int16_t maxHeight) { // a pointer to string 668 | 669 | int16_t sHeight = height(); 670 | int16_t sWidth = width(); 671 | if(maxHeight > 0){ 672 | sHeight = maxHeight; 673 | } 674 | 675 | uint16_t len=0; 676 | while(str[len]!=0)len++; // determine length of text 677 | 678 | static int16_t xC=64; 679 | static int16_t tmp_curX=0; 680 | static int16_t tmp_curY=0; 681 | 682 | if(_f_curPos==true){tmp_curX=_curX; tmp_curY=_curY; _f_curPos=false;} //new CursorValues? 683 | 684 | boolean f_wrap=false; 685 | uint16_t color=_textcolor; 686 | int16_t Xpos=tmp_curX; 687 | int16_t Ypos=tmp_curY; 688 | int16_t Ypos0 = Ypos; 689 | int16_t Xpos0 = Xpos; 690 | uint16_t font_char=0; 691 | int16_t i=0; 692 | uint16_t j=0; 693 | int a=0; 694 | uint16_t font_height = _font[6]; 695 | startWrite(); 696 | 697 | while(i != len) { //until string ends 698 | int strw=0; 699 | //------------------------------------------------------------------ word wrap 700 | a=i+1 ; 701 | if(str[i] == 32) { // space 702 | strw=font_height/4; // erstes Leerzeichen 703 | uint16_t fi=8; 704 | fi=fi + (str[i] - 32) * 4; 705 | strw=strw + _font[fi] +1; 706 | while((str[a] != 32) && (a < len)) { 707 | fi=8; 708 | if((_f_utf8)&&(str[a]>=0xC2)){ //next char is UTF-8 709 | uint16_t ch=str[a]; ch<<=8; ch+=str[a+1]; 710 | if((ch<0xD4B0)) { // char is in range, is not a armenian char or higher 711 | xC=(str[a]-0xC2)*64; a++; fi+=(str[a]+xC-32)*4; // UTF-8 decoding 712 | } 713 | else { 714 | fi+=(str[a] - 32) * 4; 715 | } 716 | } 717 | else { 718 | fi+=(str[a] - 32) * 4; 719 | } 720 | strw=strw + _font[fi] +1; 721 | a++; 722 | if(str[a]=='\n') break; // text defined word wrap recognised 723 | } 724 | if(_textorientation == 0) { 725 | if((Xpos + strw) >= sWidth) f_wrap = true; 726 | } 727 | else{ 728 | if((Ypos+strw) >= sHeight) f_wrap=true; 729 | } 730 | } 731 | //------------------------------------------------------------------ word wrap end 732 | 733 | font_char = str[i]; //die ersten 32 ASCII-Zeichen sind nicht im Zeichensatz enthalten 734 | if((str[i]==32) && (f_wrap==true)) { font_char='\n'; f_wrap=false; } 735 | if(font_char>=32) // it is a printable char 736 | { 737 | if(_f_utf8) { 738 | if((font_char>=0xC2)&&(font_char<0xd5)) { 739 | if((font_char==0xd4)&&(str[i+1]>0xAF)) {} // do nothing, it is a armenian character or higher 740 | else { 741 | xC=(font_char-0xC2)*64; i++; font_char = str[i]+xC; // UTF-8 decoding 742 | } 743 | } 744 | if((font_char==0xE2)&&(str[i+1]==0x80)) { // general punctuation, three bytes 745 | i+=2; 746 | font_char=32; // set blank 747 | } 748 | } 749 | font_char-=32; 750 | uint16_t font_index = 8; // begins at position 8 ever 751 | font_index = font_index + font_char * 4; 752 | uint16_t char_width = _font[font_index]; 753 | uint16_t space; 754 | if(font_char==0) space=font_height/4; else space=0; //correct spacewidth is 1 755 | if(_textorientation==0) { 756 | if((Xpos+char_width+space)>=sWidth){Xpos=_curX; Ypos+=font_height; Xpos0=Xpos; Ypos0=Ypos;} 757 | if((Ypos+font_height)>=sHeight){tmp_curX=Xpos; tmp_curY=Ypos; endWrite(); return i;} 758 | } 759 | else { 760 | if((Ypos+char_width+space)>sHeight){Ypos=_curY; Xpos-=font_height; Xpos0=Xpos; Ypos0=Ypos;} 761 | if((Xpos-font_height)<0){tmp_curX=Xpos; tmp_curY=Ypos; endWrite(); return i;} 762 | } 763 | uint16_t char_bytes = (char_width - 1) / 8 + 1; //number of bytes for a character 764 | uint32_t font_offset; 765 | font_offset = _font[font_index + 3]; //MSB 766 | font_offset <<= 8; // shift left 8 times 767 | font_offset += _font[font_index + 2]; 768 | font_offset <<= 8; 769 | font_offset += _font[font_index + 1]; //LSB 770 | //ab font_offset stehen die Infos für das Zeichen 771 | int16_t n = 0; 772 | for (uint16_t k = 0; k < font_height; k++) { 773 | for (int16_t m = 0; m < char_bytes; m++) { 774 | uint16_t chTemp = (_font[font_offset + n]); 775 | n++; 776 | if (_textorientation == 0) { 777 | for (j = 0; j < 8; j++) { 778 | if (chTemp & 0x01) { 779 | writePixel(Xpos, Ypos, color); 780 | } 781 | chTemp >>= 1; 782 | Xpos++; 783 | if ((Xpos - Xpos0) == char_width) { 784 | Xpos = Xpos0; Ypos++; break; 785 | } 786 | } 787 | } 788 | else { 789 | for (j = 0; j < 8; j++) { 790 | if (chTemp & 0x01) writePixel(Xpos, Ypos, color); 791 | chTemp >>= 1; 792 | Ypos++; 793 | 794 | if ((Ypos - Ypos0) == char_width) { 795 | Ypos = Ypos0; Xpos--; break; 796 | } 797 | } 798 | } 799 | } 800 | } 801 | if (_textorientation == 0) { 802 | Ypos = Ypos0; Xpos0 = Xpos0 + char_width + 1 + space; Xpos = Xpos0; 803 | } 804 | else { 805 | Xpos = Xpos0; Ypos0 = Ypos0 + char_width + 1 + space; Ypos = Ypos0; 806 | } 807 | } // end if(font_char>=0) 808 | else { // das ist ein Steuerzeichen 809 | //if(str[i]==10) { //CRLF 810 | if(font_char==10){ 811 | if(_textorientation==0){ 812 | {Xpos=_curX; Ypos+=font_height; Xpos0=Xpos; Ypos0=Ypos;} 813 | if((Ypos+font_height)>sHeight){tmp_curX=Xpos; tmp_curY=Ypos; endWrite(); return i;} 814 | } 815 | else{ 816 | {Ypos=_curY; Xpos-=font_height; Xpos0=Xpos; Ypos0=Ypos;} 817 | if((Ypos+font_height)>sHeight){tmp_curX=Xpos; tmp_curY=Ypos; endWrite(); return i;} 818 | } 819 | } 820 | } 821 | i++; 822 | } // end while 823 | tmp_curX=Xpos; 824 | tmp_curY=Ypos; 825 | 826 | endWrite(); 827 | return i; 828 | } 829 | /*******************************************************************************/ 830 | size_t TFT::write(uint8_t character) { 831 | /*Code to display letter when given the ASCII code for it*/ 832 | return 0; 833 | } 834 | size_t TFT::write(const uint8_t *buffer, size_t size){ 835 | if(_f_cp1251){writeText(UTF8toCp1251(buffer)); return 0;} 836 | if(_f_cp1252){writeText(UTF8toCp1252(buffer)); return 0;} 837 | if(_f_cp1253){writeText(UTF8toCp1253(buffer)); return 0;} 838 | writeText(buffer); return 0; 839 | } 840 | /*******************************************************************************/ 841 | const uint8_t* TFT::UTF8toCp1251(const uint8_t* str){ //cyrillic 842 | uint16_t i=0, j=0; 843 | boolean k=false; 844 | while((str[i]!=0)&&(i<1024)){ 845 | if(str[i]==0xD0){ 846 | if((str[i+1]>=0x90)&&(str[i+1]<=0xBF)){ 847 | buf[j]=str[i+1]+0x30; k=true; 848 | } 849 | if(str[i+1]==0x81){ 850 | buf[j]=0xA8; k=true; 851 | } 852 | } 853 | if(str[i]==0xD1){ 854 | if((str[i+1]>=0x80)&&(str[i+1]<=0x8F)){ 855 | buf[j]=str[i+1]+0x70; k=true; 856 | } 857 | if(str[i+1]==0x91){ 858 | buf[j]=0xB8; k=true; 859 | } 860 | } 861 | if(k==false){ 862 | buf[j]=str[i]; 863 | i++; j++; 864 | } 865 | else{ 866 | k=false; 867 | i+=2; j++; 868 | } 869 | } 870 | buf[j]=0; 871 | return (buf); 872 | } 873 | 874 | const uint8_t* TFT::UTF8toCp1252(const uint8_t* str){ //WinLatin1 875 | uint16_t i=0, j=0; 876 | while((str[i]!=0)&&(i<1024)){ 877 | if(str[i]<127){ 878 | buf[j]=str[i]; 879 | i++; j++; 880 | } 881 | else if(str[i]>=192 && str[i]<=195){ // 0xC0, 0xC1, 0xC2, 0xC3 882 | buf[j]=(str[i]-192)*64+(str[i+1]-128); 883 | i+=2; j++; 884 | } 885 | else{ 886 | buf[j]=str[i]; 887 | i++; j++; 888 | } 889 | } 890 | buf[j]=0; 891 | return (buf); 892 | } 893 | 894 | const uint8_t* TFT::UTF8toCp1253(const uint8_t* str){ //Greek 895 | uint16_t i=0, j=0; 896 | while((str[i]!=0)&&(i<1024)){ 897 | if(str[i]<184){ 898 | buf[j]=str[i]; 899 | i++; j++; 900 | } 901 | else if(str[i]==206){ // 0xCE 902 | if((str[i+1]>=136)&&(str[i+1]<=191)){ //0xCE88..0xCEBF 903 | buf[j]=str[i+1]+48; 904 | i+=2; j++; 905 | } 906 | else{ 907 | buf[j]=str[i]; 908 | i+=2; j++; 909 | } 910 | } 911 | else if(str[i]==207){ // 0xCF 912 | if((str[i+1]>=128)&&(str[i+1]<=142)){ //0xCF88..0xCF8E 913 | buf[j]=str[i+1]+112; 914 | i+=2; j++; 915 | } 916 | else{ 917 | buf[j]=str[i]; 918 | i+=2; j++; 919 | } 920 | } 921 | else{ 922 | buf[j]=str[i]; 923 | i++; j++; 924 | } 925 | } 926 | buf[j]=0; 927 | return (buf); 928 | } 929 | /******************************************************************************************************************* 930 | B I T M A P S 931 | *******************************************************************************************************************/ 932 | 933 | #define bmpRead32(d,o) (d[o] | (uint16_t)(d[(o)+1]) << 8 | (uint32_t)(d[(o)+2]) << 16 | (uint32_t)(d[(o)+3]) << 24) 934 | #define bmpRead16(d,o) (d[o] | (uint16_t)(d[(o)+1]) << 8) 935 | 936 | #define bmpColor8(c) (((uint16_t)(((uint8_t*)(c))[0] & 0xE0) << 8) | ((uint16_t)(((uint8_t*)(c))[0] & 0x1C) << 6) | ((((uint8_t*)(c))[0] & 0x3) << 3)) 937 | #define bmpColor16(c) ((((uint8_t*)(c))[0] | ((uint16_t)((uint8_t*)(c))[1]) << 8)) 938 | #define bmpColor24(c) (((uint16_t)(((uint8_t*)(c))[2] & 0xF8) << 8) | ((uint16_t)(((uint8_t*)(c))[1] & 0xFC) << 3) | ((((uint8_t*)(c))[0] & 0xF8) >> 3)) 939 | #define bmpColor32(c) (((uint16_t)(((uint8_t*)(c))[3] & 0xF8) << 8) | ((uint16_t)(((uint8_t*)(c))[2] & 0xFC) << 3) | ((((uint8_t*)(c))[1] & 0xF8) >> 3)) 940 | 941 | void TFT::bmpSkipPixels(fs::File &file, uint8_t bitsPerPixel, size_t len){ 942 | size_t bytesToSkip = (len * bitsPerPixel) / 8; 943 | file.seek(bytesToSkip, SeekCur); 944 | } 945 | 946 | void TFT::bmpAddPixels(fs::File &file, uint8_t bitsPerPixel, size_t len){ 947 | size_t bytesPerTransaction = bitsPerPixel * 4; 948 | uint8_t transBuf[bytesPerTransaction]; 949 | uint16_t pixBuf[32]; 950 | uint8_t * tBuf; 951 | uint8_t pixIndex = 0; 952 | size_t wIndex = 0, pixNow, bytesNow; 953 | while(wIndex < len){ 954 | pixNow = len - wIndex; 955 | if(pixNow > 32){ 956 | pixNow = 32; 957 | } 958 | bytesNow = (pixNow * bitsPerPixel) / 8; 959 | file.read(transBuf, bytesNow); 960 | tBuf = transBuf; 961 | 962 | for(pixIndex=0; pixIndex < pixNow; pixIndex++){ 963 | if(bitsPerPixel == 32){ 964 | pixBuf[pixIndex] = (bmpColor32(tBuf)); 965 | tBuf+=4; 966 | } else if(bitsPerPixel == 24){ 967 | pixBuf[pixIndex] = (bmpColor24(tBuf)); 968 | tBuf+=3; 969 | } else if(bitsPerPixel == 16){ 970 | pixBuf[pixIndex] = (bmpColor16(tBuf)); 971 | tBuf+=2; 972 | } else if(bitsPerPixel == 8){ 973 | pixBuf[pixIndex] = (bmpColor8(tBuf)); 974 | tBuf+=1; 975 | } else if(bitsPerPixel == 4){ 976 | uint16_t g = tBuf[0] & 0xF; 977 | if(pixIndex & 1){ 978 | tBuf+=1; 979 | } else { 980 | g = tBuf[0] >> 4; 981 | } 982 | pixBuf[pixIndex] = ((g << 12) | (g << 7) | (g << 1)); 983 | } 984 | } 985 | startWrite(); 986 | writePixels(pixBuf, pixNow); 987 | endWrite(); 988 | wIndex += pixNow; 989 | } 990 | } 991 | 992 | boolean TFT::drawBmpFile(fs::FS &fs, const char * path, uint16_t x, uint16_t y, uint16_t maxWidth, uint16_t maxHeight, uint16_t offX, uint16_t offY){ 993 | if((x + maxWidth) > width() || (y + maxHeight) > height()){ 994 | log_e("Bad dimensions given"); 995 | return false; 996 | } 997 | 998 | if(!maxWidth){ 999 | maxWidth = width() - x; 1000 | } 1001 | if(!maxHeight){ 1002 | maxHeight = height() - y; 1003 | } 1004 | //log_e("maxWidth=%i, maxHeight=%i", maxWidth, maxHeight); 1005 | File file = fs.open(path); 1006 | if(!file){ 1007 | if(tft_info) tft_info("Failed to open file for reading\n"); 1008 | return false; 1009 | } 1010 | size_t headerLen = 0x22; 1011 | size_t fileSize = file.size(); 1012 | uint8_t headerBuf[headerLen]; 1013 | if(fileSize < headerLen || file.read(headerBuf, headerLen) < headerLen){ 1014 | log_e("Failed to read the file's header"); 1015 | file.close(); 1016 | return false; 1017 | } 1018 | 1019 | if(headerBuf[0] != 'B' || headerBuf[1] != 'M'){ 1020 | log_e("Wrong file format"); 1021 | file.close(); 1022 | return false; 1023 | } 1024 | 1025 | //size_t bmpSize = bmpRead32(headerBuf, 0x02); 1026 | uint32_t dataOffset = bmpRead32(headerBuf, 0x0A); 1027 | int32_t bmpWidthI = bmpRead32(headerBuf, 0x12); 1028 | int32_t bmpHeightI = bmpRead32(headerBuf, 0x16); 1029 | uint16_t bitsPerPixel = bmpRead16(headerBuf, 0x1C); 1030 | 1031 | size_t bmpWidth = abs(bmpWidthI); 1032 | size_t bmpHeight = abs(bmpHeightI); 1033 | 1034 | if(offX >= bmpWidth || offY >= bmpHeight){ 1035 | log_e("Offset Outside of bitmap size"); 1036 | file.close(); 1037 | return false; 1038 | } 1039 | 1040 | size_t bmpMaxWidth = bmpWidth - offX; 1041 | size_t bmpMaxHeight = bmpHeight - offY; 1042 | size_t outWidth = (bmpMaxWidth > maxWidth)?maxWidth:bmpMaxWidth; 1043 | size_t outHeight = (bmpMaxHeight > maxHeight)?maxHeight:bmpMaxHeight; 1044 | size_t ovfWidth = bmpMaxWidth - outWidth; 1045 | size_t ovfHeight = bmpMaxHeight - outHeight; 1046 | 1047 | file.seek(dataOffset); 1048 | startBitmap(x, y, outWidth, outHeight); 1049 | 1050 | if(ovfHeight){ 1051 | bmpSkipPixels(file, bitsPerPixel, ovfHeight * bmpWidth); 1052 | } 1053 | if(!offX && !ovfWidth){ 1054 | bmpAddPixels(file, bitsPerPixel, outWidth * outHeight); 1055 | } else { 1056 | size_t ih; 1057 | for(ih=0;ih width() || (y + maxHeight) > height()){ 1079 | log_e("Bad dimensions given"); 1080 | return false; 1081 | } 1082 | //log_e("maxWidth=%i, maxHeight=%i", maxWidth, maxHeight); 1083 | File file = fs.open(path); 1084 | if(!file){ 1085 | if(tft_info) tft_info("Failed to open file for reading\n"); 1086 | return false; 1087 | } 1088 | JpegDec.decodeSdFile(file); 1089 | 1090 | // log_i("Width: %i, Height: %i,", JpegDec.width, JpegDec.height); 1091 | // log_i("Components: %i, Scan type %i", JpegDec.comps, JpegDec.scanType); 1092 | // log_i("MCU / row: %i, MCU / col: %i", JpegDec.MCUSPerRow, JpegDec.MCUSPerCol); 1093 | // log_i("MCU width: %i, MCU height: %i", JpegDec.MCUWidth, JpegDec.MCUHeight); 1094 | 1095 | renderJPEG(x, y, maxWidth, maxHeight); 1096 | 1097 | return true; 1098 | } 1099 | 1100 | void TFT::renderJPEG(int xpos, int ypos, uint16_t maxWidth, uint16_t maxHeight) { 1101 | // retrieve infomration about the image 1102 | uint16_t *pImg; 1103 | uint16_t mcu_w = JpegDec.MCUWidth; 1104 | uint16_t mcu_h = JpegDec.MCUHeight; 1105 | uint32_t max_x = JpegDec.width; 1106 | uint32_t max_y = JpegDec.height; 1107 | 1108 | maxWidth = (uint)(maxWidth / 16) * 16; // must be a multiple of 16 (MCU blocksize) 1109 | //log_i("maxWidth %d", maxWidth ); 1110 | maxHeight = (uint)(maxHeight / 16) * 16; // must be a multiple of 8 1111 | 1112 | if(maxWidth > 0 && maxWidth < max_x) max_x = maxWidth; // overwrite 1113 | if(maxHeight > 0 && maxHeight < max_y) max_y = maxHeight; // overwrite 1114 | 1115 | 1116 | // Jpeg images are draw as a set of image block (tiles) called Minimum Coding Units (MCUs) 1117 | // Typically these MCUs are 16x16 pixel blocks 1118 | // Determine the width and height of the right and bottom edge image blocks 1119 | uint32_t min_w = minimum(mcu_w, max_x % mcu_w); 1120 | uint32_t min_h = minimum(mcu_h, max_y % mcu_h); 1121 | 1122 | // save the current image block size 1123 | uint32_t win_w = mcu_w; 1124 | uint32_t win_h = mcu_h; 1125 | 1126 | // record the current time so we can measure how long it takes to draw an image 1127 | uint32_t drawTime = millis(); 1128 | 1129 | // save the coordinate of the right and bottom edges to assist image cropping to the screen size 1130 | max_x += xpos; 1131 | max_y += ypos; 1132 | 1133 | // read each MCU block until there are no more 1134 | startJpeg(); 1135 | 1136 | while( JpegDec.read()){ 1137 | // save a pointer to the image block 1138 | pImg = JpegDec.pImage; 1139 | 1140 | // calculate where the image block should be drawn on the screen 1141 | int16_t mcu_x = JpegDec.MCUx * mcu_w + xpos; 1142 | int16_t mcu_y = JpegDec.MCUy * mcu_h + ypos; 1143 | 1144 | // check if the image block size needs to be changed for the right and bottom edges 1145 | if (mcu_x + mcu_w <= max_x) win_w = mcu_w; 1146 | else win_w = min_w; 1147 | if (mcu_y + mcu_h <= max_y) win_h = mcu_h; 1148 | else win_h = min_h; 1149 | 1150 | //log_i("mcu_x=%i, mcu_y=%i", mcu_x, mcu_y); 1151 | 1152 | // calculate how many pixels must be drawn 1153 | uint32_t mcu_pixels = win_w * win_h; 1154 | 1155 | // draw image block if it will fit on the screen 1156 | if ( ( mcu_x + win_w) <= width() && ( mcu_y + win_h) <= height()) { 1157 | // open a window onto the screen to paint the pixels into 1158 | startWrite(); 1159 | setAddrWindow(mcu_x, mcu_y, win_w , win_h); 1160 | writeCommand(ILI9486_RAMWR); 1161 | // push all the image block pixels to the screen 1162 | while (mcu_pixels--) writePixel(*pImg++); // Send to TFT 16 bits at a time 1163 | endWrite(); 1164 | } 1165 | 1166 | // stop drawing blocks if the bottom of the screen has been reached 1167 | // the abort function will close the file 1168 | else if ( ( mcu_y + win_h) >= height()) JpegDec.abort(); 1169 | } 1170 | 1171 | // calculate how long it took to draw the image 1172 | drawTime = millis() - drawTime; // Calculate the time it took 1173 | 1174 | // print the results to the serial port 1175 | // log_i("Total render time was: %ims",drawTime); 1176 | endJpeg(); 1177 | } 1178 | 1179 | // JPEGDecoder thx to Bodmer https://github.com/Bodmer/JPEGDecoder 1180 | JPEGDecoder::JPEGDecoder(){ // @suppress("Class members should be properly initialized") 1181 | mcu_x = 0 ; 1182 | mcu_y = 0 ; 1183 | is_available = 0; 1184 | thisPtr = this; 1185 | } 1186 | 1187 | JPEGDecoder::~JPEGDecoder(){ 1188 | if (pImage) delete pImage; 1189 | pImage = NULL; 1190 | } 1191 | 1192 | uint8_t JPEGDecoder::pjpeg_callback(uint8_t* pBuf, uint8_t buf_size, uint8_t *pBytes_actually_read, void *pCallback_data) { 1193 | JPEGDecoder *thisPtr = JpegDec.thisPtr ; 1194 | thisPtr->pjpeg_need_bytes_callback(pBuf, buf_size, pBytes_actually_read, pCallback_data); 1195 | return 0; 1196 | } 1197 | 1198 | uint8_t JPEGDecoder::pjpeg_need_bytes_callback(uint8_t* pBuf, uint8_t buf_size, uint8_t *pBytes_actually_read, void *pCallback_data) { 1199 | uint n; 1200 | n = jpg_min(g_nInFileSize - g_nInFileOfs, buf_size); 1201 | if (jpg_source == JPEG_ARRAY) { // We are handling an array 1202 | for (int i = 0; i < n; i++) { 1203 | pBuf[i] = pgm_read_byte(jpg_data++); 1204 | //Serial.println(pBuf[i],HEX); 1205 | } 1206 | } 1207 | if (jpg_source == JPEG_SD_FILE) g_pInFileSd.read(pBuf,n); // else we are handling a file 1208 | *pBytes_actually_read = (uint8_t)(n); 1209 | g_nInFileOfs += n; 1210 | return 0; 1211 | } 1212 | 1213 | int JPEGDecoder::decode_mcu(void) { 1214 | status = JpegDec.pjpeg_decode_mcu(); 1215 | if (status) { 1216 | is_available = 0 ; 1217 | if (status != JpegDec.PJPG_NO_MORE_BLOCKS) { 1218 | log_e("pjpeg_decode_mcu() failed with status %i", status); 1219 | return -1; 1220 | } 1221 | } 1222 | return 1; 1223 | } 1224 | 1225 | int JPEGDecoder::read(void){ 1226 | int y, x; 1227 | uint16_t *pDst_row; 1228 | if(is_available == 0 || mcu_y >= image_info.m_MCUSPerCol) { 1229 | abort(); 1230 | return 0; 1231 | } 1232 | // Copy MCU's pixel blocks into the destination bitmap. 1233 | pDst_row = pImage; 1234 | for (y = 0; y < image_info.m_MCUHeight; y += 8) { 1235 | const int by_limit = jpg_min(8, image_info.m_height - (mcu_y * image_info.m_MCUHeight + y)); 1236 | for (x = 0; x < image_info.m_MCUWidth; x += 8) { 1237 | uint16_t *pDst_block = pDst_row + x; 1238 | // Compute source byte offset of the block in the decoder's MCU buffer. 1239 | uint src_ofs = (x * 8U) + (y * 16U); 1240 | const uint8_t *pSrcR = image_info.m_pMCUBufR + src_ofs; 1241 | const uint8_t *pSrcG = image_info.m_pMCUBufG + src_ofs; 1242 | const uint8_t *pSrcB = image_info.m_pMCUBufB + src_ofs; 1243 | const int bx_limit = jpg_min(8, image_info.m_width - (mcu_x * image_info.m_MCUWidth + x)); 1244 | if (image_info.m_scanType == PJPG_GRAYSCALE) { 1245 | int bx, by; 1246 | for (by = 0; by < by_limit; by++) { 1247 | uint16_t *pDst = pDst_block; 1248 | for (bx = 0; bx < bx_limit; bx++) { 1249 | *pDst++ = (*pSrcR & 0xF8) << 8 | (*pSrcR & 0xFC) <<3 | *pSrcR >> 3; 1250 | pSrcR++; 1251 | } 1252 | pSrcR += (8 - bx_limit); 1253 | pDst_block += row_pitch; 1254 | } 1255 | } 1256 | else { 1257 | int bx, by; 1258 | for (by = 0; by < by_limit; by++) { 1259 | uint16_t *pDst = pDst_block; 1260 | 1261 | for (bx = 0; bx < bx_limit; bx++) { 1262 | *pDst++ = (*pSrcR & 0xF8) << 8 | (*pSrcG & 0xFC) <<3 | *pSrcB >> 3; 1263 | pSrcR++; pSrcG++; pSrcB++; 1264 | } 1265 | pSrcR += (8 - bx_limit); 1266 | pSrcG += (8 - bx_limit); 1267 | pSrcB += (8 - bx_limit); 1268 | 1269 | pDst_block += row_pitch; 1270 | } 1271 | } 1272 | } 1273 | pDst_row += (row_pitch * 8); 1274 | } 1275 | MCUx = mcu_x; 1276 | MCUy = mcu_y; 1277 | 1278 | mcu_x++; 1279 | if (mcu_x == image_info.m_MCUSPerRow) { 1280 | mcu_x = 0; 1281 | mcu_y++; 1282 | } 1283 | if(decode_mcu()==-1) is_available = 0 ; 1284 | return 1; 1285 | } 1286 | 1287 | int JPEGDecoder::readSwappedBytes(void) { 1288 | int y, x; 1289 | uint16_t *pDst_row; 1290 | 1291 | if(is_available == 0 || mcu_y >= image_info.m_MCUSPerCol) { 1292 | abort(); 1293 | return 0; 1294 | } 1295 | // Copy MCU's pixel blocks into the destination bitmap. 1296 | pDst_row = pImage; 1297 | for (y = 0; y < image_info.m_MCUHeight; y += 8) { 1298 | const int by_limit = jpg_min(8, image_info.m_height - (mcu_y * image_info.m_MCUHeight + y)); 1299 | 1300 | for (x = 0; x < image_info.m_MCUWidth; x += 8) { 1301 | uint16_t *pDst_block = pDst_row + x; 1302 | // Compute source byte offset of the block in the decoder's MCU buffer. 1303 | uint src_ofs = (x * 8U) + (y * 16U); 1304 | const uint8_t *pSrcR = image_info.m_pMCUBufR + src_ofs; 1305 | const uint8_t *pSrcG = image_info.m_pMCUBufG + src_ofs; 1306 | const uint8_t *pSrcB = image_info.m_pMCUBufB + src_ofs; 1307 | const int bx_limit = jpg_min(8, image_info.m_width - (mcu_x * image_info.m_MCUWidth + x)); 1308 | 1309 | if (image_info.m_scanType == PJPG_GRAYSCALE) { 1310 | int bx, by; 1311 | for (by = 0; by < by_limit; by++) { 1312 | uint16_t *pDst = pDst_block; 1313 | for (bx = 0; bx < bx_limit; bx++) { 1314 | *pDst++ = (*pSrcR & 0xF8) | (*pSrcR & 0xE0) >> 5 | (*pSrcR & 0xF8) << 5 | (*pSrcR & 0x1C) << 11; 1315 | pSrcR++; 1316 | } 1317 | } 1318 | } 1319 | else { 1320 | int bx, by; 1321 | for (by = 0; by < by_limit; by++) { 1322 | uint16_t *pDst = pDst_block; 1323 | 1324 | for (bx = 0; bx < bx_limit; bx++) { 1325 | *pDst++ = (*pSrcR & 0xF8) | (*pSrcG & 0xE0) >> 5 | (*pSrcB & 0xF8) << 5 | (*pSrcG & 0x1C) << 11; 1326 | pSrcR++; pSrcG++; pSrcB++; 1327 | } 1328 | pSrcR += (8 - bx_limit); 1329 | pSrcG += (8 - bx_limit); 1330 | pSrcB += (8 - bx_limit); 1331 | pDst_block += row_pitch; 1332 | } 1333 | } 1334 | } 1335 | pDst_row += (row_pitch * 8); 1336 | } 1337 | MCUx = mcu_x; 1338 | MCUy = mcu_y; 1339 | 1340 | mcu_x++; 1341 | if (mcu_x == image_info.m_MCUSPerRow) { 1342 | mcu_x = 0; 1343 | mcu_y++; 1344 | } 1345 | if(decode_mcu()==-1) is_available = 0 ; 1346 | return 1; 1347 | } 1348 | 1349 | int JPEGDecoder::decodeSdFile(File jpgFile) { // This is for the SD library 1350 | g_pInFileSd = jpgFile; 1351 | jpg_source = JPEG_SD_FILE; // Flag to indicate a SD file 1352 | 1353 | if (!g_pInFileSd) { 1354 | return -1; 1355 | } 1356 | g_nInFileOfs = 0; 1357 | g_nInFileSize = g_pInFileSd.size(); 1358 | 1359 | return decodeCommon(); 1360 | } 1361 | 1362 | int JPEGDecoder::decodeArray(const uint8_t array[], uint32_t array_size) { 1363 | jpg_source = JPEG_ARRAY; // We are not processing a file, use arrays 1364 | g_nInFileOfs = 0; 1365 | jpg_data = (uint8_t *)array; 1366 | g_nInFileSize = array_size; 1367 | return decodeCommon(); 1368 | } 1369 | 1370 | int JPEGDecoder::decodeCommon(void) { 1371 | status = JpegDec.pjpeg_decode_init(&image_info, pjpeg_callback, NULL, 0); 1372 | if (status) { 1373 | log_e("pjpeg_decode_init() failed with status %i", status); 1374 | if (status == JpegDec.PJPG_UNSUPPORTED_MODE) { 1375 | log_e("Progressive JPEG files are not supported."); 1376 | } 1377 | return -1; 1378 | } 1379 | decoded_width = image_info.m_width; 1380 | decoded_height = image_info.m_height; 1381 | row_pitch = image_info.m_MCUWidth; 1382 | pImage = new uint16_t[image_info.m_MCUWidth * image_info.m_MCUHeight]; 1383 | if (!pImage) { 1384 | log_e("Memory Allocation Failure"); 1385 | return -1; 1386 | } 1387 | // memset(pImage , 0 , sizeof(*pImage)); 1388 | 1389 | row_blocks_per_mcu = image_info.m_MCUWidth >> 3; 1390 | col_blocks_per_mcu = image_info.m_MCUHeight >> 3; 1391 | is_available = 1 ; 1392 | width = decoded_width; 1393 | height = decoded_height; 1394 | comps = 1; 1395 | MCUSPerRow = image_info.m_MCUSPerRow; 1396 | MCUSPerCol = image_info.m_MCUSPerCol; 1397 | scanType = image_info.m_scanType; 1398 | MCUWidth = image_info.m_MCUWidth; 1399 | MCUHeight = image_info.m_MCUHeight; 1400 | return decode_mcu(); 1401 | } 1402 | 1403 | void JPEGDecoder::abort(void) { 1404 | mcu_x = 0 ; 1405 | mcu_y = 0 ; 1406 | is_available = 0; 1407 | if(pImage) delete pImage; 1408 | pImage = NULL; 1409 | if (jpg_source == JPEG_SD_FILE) if (g_pInFileSd) g_pInFileSd.close(); 1410 | } 1411 | // JPEGDecoder picojpeg 1412 | int16_t JPEGDecoder::replicateSignBit16(int8_t n){ 1413 | switch (n){ 1414 | case 0: return 0x0000; 1415 | case 1: return 0x8000; 1416 | case 2: return 0xC000; 1417 | case 3: return 0xE000; 1418 | case 4: return 0xF000; 1419 | case 5: return 0xF800; 1420 | case 6: return 0xFC00; 1421 | case 7: return 0xFE00; 1422 | case 8: return 0xFF00; 1423 | case 9: return 0xFF80; 1424 | case 10: return 0xFFC0; 1425 | case 11: return 0xFFE0; 1426 | case 12: return 0xFFF0; 1427 | case 13: return 0xFFF8; 1428 | case 14: return 0xFFFC; 1429 | case 15: return 0xFFFE; 1430 | default: return 0xFFFF; 1431 | } 1432 | } 1433 | 1434 | //------------------------------------------------------------------------------ 1435 | void JPEGDecoder::fillInBuf(void){ 1436 | unsigned char status; 1437 | // Reserve a few bytes at the beginning of the buffer for putting back ("stuffing") chars. 1438 | gInBufOfs = 4; 1439 | gInBufLeft = 0; 1440 | status = (*g_pNeedBytesCallback)(gInBuf + gInBufOfs, PJPG_MAX_IN_BUF_SIZE - gInBufOfs, &gInBufLeft, g_pCallback_data); 1441 | if (status){ 1442 | // The user provided need bytes callback has indicated an error, so record the error and continue trying to decode. 1443 | // The highest level pjpeg entrypoints will catch the error and return the non-zero status. 1444 | gCallbackStatus = status; 1445 | } 1446 | } 1447 | //------------------------------------------------------------------------------ 1448 | 1449 | uint16_t JPEGDecoder::getBits(uint8_t numBits, uint8_t FFCheck){ 1450 | uint8_t origBits = numBits; 1451 | uint16_t ret = gBitBuf; 1452 | if (numBits > 8) { 1453 | numBits -= 8; 1454 | gBitBuf <<= gBitsLeft; 1455 | gBitBuf |= getOctet(FFCheck); 1456 | gBitBuf <<= (8 - gBitsLeft); 1457 | ret = (ret & 0xFF00) | (gBitBuf >> 8); 1458 | } 1459 | if (gBitsLeft < numBits){ 1460 | gBitBuf <<= gBitsLeft; 1461 | gBitBuf |= getOctet(FFCheck); 1462 | gBitBuf <<= (numBits - gBitsLeft); 1463 | gBitsLeft = 8 - (numBits - gBitsLeft); 1464 | } 1465 | else{ 1466 | gBitsLeft = (uint8_t)(gBitsLeft - numBits); 1467 | gBitBuf <<= numBits; 1468 | } 1469 | return ret >> (16 - origBits); 1470 | } 1471 | //------------------------------------------------------------------------------ 1472 | 1473 | uint16_t JPEGDecoder::getExtendTest(uint8_t i){ 1474 | switch (i){ 1475 | case 0: return 0; 1476 | case 1: return 0x0001; 1477 | case 2: return 0x0002; 1478 | case 3: return 0x0004; 1479 | case 4: return 0x0008; 1480 | case 5: return 0x0010; 1481 | case 6: return 0x0020; 1482 | case 7: return 0x0040; 1483 | case 8: return 0x0080; 1484 | case 9: return 0x0100; 1485 | case 10: return 0x0200; 1486 | case 11: return 0x0400; 1487 | case 12: return 0x0800; 1488 | case 13: return 0x1000; 1489 | case 14: return 0x2000; 1490 | case 15: return 0x4000; 1491 | default: return 0; 1492 | } 1493 | } 1494 | //------------------------------------------------------------------------------ 1495 | int16_t JPEGDecoder::getExtendOffset(uint8_t i){ 1496 | 1497 | #pragma GCC diagnostic push 1498 | #pragma GCC diagnostic ignored "-Wshift-negative-value" 1499 | 1500 | switch (i){ 1501 | case 0: return 0; 1502 | case 1: return ((-1)<<1) + 1; 1503 | case 2: return ((-1)<<2) + 1; 1504 | case 3: return ((-1)<<3) + 1; 1505 | case 4: return ((-1)<<4) + 1; 1506 | case 5: return ((-1)<<5) + 1; 1507 | case 6: return ((-1)<<6) + 1; 1508 | case 7: return ((-1)<<7) + 1; 1509 | case 8: return ((-1)<<8) + 1; 1510 | case 9: return ((-1)<<9) + 1; 1511 | case 10: return ((-1)<<10) + 1; 1512 | case 11: return ((-1)<<11) + 1; 1513 | case 12: return ((-1)<<12) + 1; 1514 | case 13: return ((-1)<<13) + 1; 1515 | case 14: return ((-1)<<14) + 1; 1516 | case 15: return ((-1)<<15) + 1; 1517 | default: return 0; 1518 | } 1519 | 1520 | #pragma GCC diagnostic pop 1521 | 1522 | } 1523 | //------------------------------------------------------------------------------ 1524 | 1525 | void JPEGDecoder::huffCreate(const uint8_t* pBits, HuffTable* pHuffTable){ 1526 | uint8_t i = 0, j=0; 1527 | uint16_t code = 0; 1528 | 1529 | for ( ; ; ){ 1530 | uint8_t num = pBits[i]; 1531 | if (!num){ 1532 | pHuffTable->mMinCode[i] = 0x0000; 1533 | pHuffTable->mMaxCode[i] = 0xFFFF; 1534 | pHuffTable->mValPtr[i] = 0; 1535 | } 1536 | else{ 1537 | pHuffTable->mMinCode[i] = code; 1538 | pHuffTable->mMaxCode[i] = code + num - 1; 1539 | pHuffTable->mValPtr[i] = j; 1540 | j = (uint8_t)(j + num); 1541 | code = (uint16_t)(code + num); 1542 | } 1543 | code <<= 1; 1544 | i++; 1545 | if (i > 15) break; 1546 | } 1547 | } 1548 | //------------------------------------------------------------------------------ 1549 | JPEGDecoder::HuffTable* JPEGDecoder::getHuffTable(uint8_t index){ 1550 | // 0-1 = DC 1551 | // 2-3 = AC 1552 | switch (index){ 1553 | case 0: return &gHuffTab0; 1554 | case 1: return &gHuffTab1; 1555 | case 2: return &gHuffTab2; 1556 | case 3: return &gHuffTab3; 1557 | default: return 0; 1558 | } 1559 | } 1560 | //------------------------------------------------------------------------------ 1561 | uint8_t* JPEGDecoder::getHuffVal(uint8_t index){ 1562 | // 0-1 = DC 1563 | // 2-3 = AC 1564 | switch (index){ 1565 | case 0: return gHuffVal0; 1566 | case 1: return gHuffVal1; 1567 | case 2: return gHuffVal2; 1568 | case 3: return gHuffVal3; 1569 | default: return 0; 1570 | } 1571 | } 1572 | //------------------------------------------------------------------------------ 1573 | uint8_t JPEGDecoder::readDHTMarker(void){ 1574 | uint8_t bits[16]; 1575 | uint16_t left = getBits1(16); 1576 | if (left < 2) return PJPG_BAD_DHT_MARKER; 1577 | left -= 2; 1578 | while(left){ 1579 | uint8_t i, tableIndex, index; 1580 | uint8_t* pHuffVal; 1581 | HuffTable* pHuffTable; 1582 | uint16_t count, totalRead; 1583 | index = (uint8_t)getBits1(8); 1584 | 1585 | if(((index & 0xF) > 1) || ((index & 0xF0) > 0x10)) return PJPG_BAD_DHT_INDEX; 1586 | 1587 | tableIndex = ((index >> 3) & 2) + (index & 1); 1588 | pHuffTable = getHuffTable(tableIndex); 1589 | pHuffVal = getHuffVal(tableIndex); 1590 | gValidHuffTables |= (1 << tableIndex); 1591 | count = 0; 1592 | for(i = 0; i <= 15; i++){ 1593 | uint8_t n = (uint8_t)getBits1(8); 1594 | bits[i] = n; 1595 | count = (uint16_t)(count + n); 1596 | } 1597 | if(count > getMaxHuffCodes(tableIndex)) return PJPG_BAD_DHT_COUNTS; 1598 | for(i = 0; i < count; i++) pHuffVal[i] = (uint8_t)getBits1(8); 1599 | totalRead = 1 + 16 + count; 1600 | if (left < totalRead) return PJPG_BAD_DHT_MARKER; 1601 | left = (uint16_t)(left - totalRead); 1602 | huffCreate(bits, pHuffTable); 1603 | } 1604 | return 0; 1605 | } 1606 | //------------------------------------------------------------------------------ 1607 | //void JPEGDecoder::createWinogradQuant(int16_t* pQuant); 1608 | uint8_t JPEGDecoder::readDQTMarker(void){ 1609 | uint16_t left = getBits1(16); 1610 | if (left < 2) return PJPG_BAD_DQT_MARKER; 1611 | left -= 2; 1612 | while (left){ 1613 | uint8_t i; 1614 | uint8_t n = (uint8_t)getBits1(8); 1615 | uint8_t prec = n >> 4; 1616 | uint16_t totalRead; 1617 | 1618 | n &= 0x0F; 1619 | if (n > 1) return PJPG_BAD_DQT_TABLE; 1620 | gValidQuantTables |= (n ? 2 : 1); 1621 | // read quantization entries, in zag order 1622 | for (i = 0; i < 64; i++){ 1623 | uint16_t temp = getBits1(8); 1624 | if(prec) temp = (temp << 8) + getBits1(8); 1625 | if (n) gQuant1[i] = (int16_t)temp; 1626 | else 1627 | gQuant0[i] = (int16_t)temp; 1628 | } 1629 | createWinogradQuant(n ? gQuant1 : gQuant0); 1630 | totalRead = 64 + 1; 1631 | if(prec) totalRead += 64; 1632 | if(left < totalRead) return PJPG_BAD_DQT_LENGTH; 1633 | left = (uint16_t)(left - totalRead); 1634 | } 1635 | return 0; 1636 | } 1637 | //------------------------------------------------------------------------------ 1638 | uint8_t JPEGDecoder::readSOFMarker(void){ 1639 | uint8_t i; 1640 | uint16_t left = getBits1(16); 1641 | 1642 | if (getBits1(8) != 8) return PJPG_BAD_PRECISION; 1643 | gImageYSize = getBits1(16); 1644 | if((!gImageYSize) || (gImageYSize > PJPG_MAX_HEIGHT)) return PJPG_BAD_HEIGHT; 1645 | gImageXSize = getBits1(16); 1646 | if((!gImageXSize) || (gImageXSize > PJPG_MAX_WIDTH)) return PJPG_BAD_WIDTH; 1647 | gCompsInFrame = (uint8_t)getBits1(8); 1648 | if(gCompsInFrame > 3) return PJPG_TOO_MANY_COMPONENTS; 1649 | if (left != (gCompsInFrame + gCompsInFrame + gCompsInFrame + 8)) return PJPG_BAD_SOF_LENGTH; 1650 | for (i = 0; i < gCompsInFrame; i++){ 1651 | gCompIdent[i] = (uint8_t)getBits1(8); 1652 | gCompHSamp[i] = (uint8_t)getBits1(4); 1653 | gCompVSamp[i] = (uint8_t)getBits1(4); 1654 | gCompQuant[i] = (uint8_t)getBits1(8); 1655 | if(gCompQuant[i] > 1) return PJPG_UNSUPPORTED_QUANT_TABLE; 1656 | } 1657 | return 0; 1658 | } 1659 | //------------------------------------------------------------------------------ 1660 | // Used to skip unrecognized markers. 1661 | uint8_t JPEGDecoder::skipVariableMarker(void){ 1662 | uint16_t left = getBits1(16); 1663 | if (left < 2) return PJPG_BAD_VARIABLE_MARKER; 1664 | left -= 2; 1665 | while(left){getBits1(8); left--;} 1666 | return 0; 1667 | } 1668 | //------------------------------------------------------------------------------ 1669 | // Read a define restart interval (DRI) marker. 1670 | uint8_t JPEGDecoder::readDRIMarker(void){ 1671 | if (getBits1(16) != 4) return PJPG_BAD_DRI_LENGTH; 1672 | gRestartInterval = getBits1(16); 1673 | return 0; 1674 | } 1675 | //------------------------------------------------------------------------------ 1676 | // Read a start of scan (SOS) marker. 1677 | uint8_t JPEGDecoder::readSOSMarker(void){ 1678 | uint8_t i; 1679 | uint16_t left = getBits1(16); 1680 | uint8_t successive_high __attribute__((unused)); 1681 | uint8_t successive_low __attribute__((unused)); 1682 | uint8_t spectral_end __attribute__((unused)); 1683 | uint8_t spectral_start __attribute__((unused)); 1684 | 1685 | gCompsInScan = (uint8_t)getBits1(8); 1686 | left -= 3; 1687 | if((left!=(gCompsInScan+gCompsInScan+3))||(gCompsInScan<1)||(gCompsInScan>PJPG_MAXCOMPSINSCAN)) return PJPG_BAD_SOS_LENGTH; 1688 | for (i = 0; i < gCompsInScan; i++){ 1689 | uint8_t cc = (uint8_t)getBits1(8); 1690 | uint8_t c = (uint8_t)getBits1(8); 1691 | uint8_t ci; 1692 | left -= 2; 1693 | for(ci = 0; ci < gCompsInFrame; ci++) if(cc == gCompIdent[ci]) break; 1694 | if(ci >= gCompsInFrame) return PJPG_BAD_SOS_COMP_ID; 1695 | gCompList[i] = ci; 1696 | gCompDCTab[ci] = (c >> 4) & 15; 1697 | gCompACTab[ci] = (c & 15); 1698 | } 1699 | spectral_start = (uint8_t)getBits1(8); 1700 | spectral_end = (uint8_t)getBits1(8); 1701 | successive_high = (uint8_t)getBits1(4); 1702 | successive_low = (uint8_t)getBits1(4); 1703 | left -= 3; 1704 | while(left){getBits1(8); left--;} 1705 | return 0; 1706 | 1707 | } 1708 | //------------------------------------------------------------------------------ 1709 | uint8_t JPEGDecoder::nextMarker(void){ 1710 | uint8_t c; 1711 | uint8_t bytes = 0; 1712 | 1713 | do{ 1714 | do{ 1715 | bytes++; 1716 | c = (uint8_t)getBits1(8); 1717 | } while (c != 0xFF); 1718 | do{ 1719 | c = (uint8_t)getBits1(8); 1720 | } while (c == 0xFF); 1721 | } while (c == 0); 1722 | 1723 | // If bytes > 0 here, there where extra bytes before the marker (not good). 1724 | return c; 1725 | } 1726 | //------------------------------------------------------------------------------ 1727 | // Process markers. Returns when an SOFx, SOI, EOI, or SOS marker is 1728 | // encountered. 1729 | uint8_t JPEGDecoder::processMarkers(uint8_t* pMarker){ 1730 | for( ; ; ){ 1731 | uint8_t c = nextMarker(); 1732 | 1733 | switch (c){ 1734 | case M_SOF0: 1735 | case M_SOF1: 1736 | case M_SOF2: 1737 | case M_SOF3: 1738 | case M_SOF5: 1739 | case M_SOF6: 1740 | case M_SOF7: 1741 | // case M_JPG: 1742 | case M_SOF9: 1743 | case M_SOF10: 1744 | case M_SOF11: 1745 | case M_SOF13: 1746 | case M_SOF14: 1747 | case M_SOF15: 1748 | case M_SOI: 1749 | case M_EOI: 1750 | case M_SOS:{*pMarker = c; return 0;} 1751 | case M_DHT:{readDHTMarker(); break;} 1752 | // Sorry, no arithmetic support at this time. Dumb patents! 1753 | case M_DAC:{return PJPG_NO_ARITHMITIC_SUPPORT;} 1754 | case M_DQT:{readDQTMarker(); break;} 1755 | case M_DRI:{readDRIMarker(); break;} 1756 | //case M_APP0: /* no need to read the JFIF marker */ 1757 | case M_JPG: 1758 | case M_RST0: /* no parameters */ 1759 | case M_RST1: 1760 | case M_RST2: 1761 | case M_RST3: 1762 | case M_RST4: 1763 | case M_RST5: 1764 | case M_RST6: 1765 | case M_RST7: 1766 | case M_TEM:{return PJPG_UNEXPECTED_MARKER;} 1767 | default: /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */ 1768 | { 1769 | skipVariableMarker(); 1770 | break; 1771 | } 1772 | } 1773 | } 1774 | return 0; 1775 | } 1776 | //------------------------------------------------------------------------------ 1777 | // Finds the start of image (SOI) marker. 1778 | uint8_t JPEGDecoder::locateSOIMarker(void){ 1779 | uint16_t bytesleft; 1780 | uint8_t lastchar = (uint8_t)getBits1(8); 1781 | uint8_t thischar = (uint8_t)getBits1(8); 1782 | /* ok if it's a normal JPEG file without a special header */ 1783 | if ((lastchar == 0xFF) && (thischar == M_SOI)) return 0; 1784 | bytesleft = 4096; //512; 1785 | for( ; ; ){ 1786 | if (--bytesleft == 0) return PJPG_NOT_JPEG; 1787 | lastchar = thischar; 1788 | thischar = (uint8_t)getBits1(8); 1789 | if(lastchar == 0xFF){ 1790 | if(thischar == M_SOI) break; 1791 | else if (thischar == M_EOI) return PJPG_NOT_JPEG; //getBits1 will keep returning M_EOI if we read past the end 1792 | } 1793 | } 1794 | /* Check the next character after marker: if it's not 0xFF, it can't 1795 | be the start of the next marker, so the file is bad */ 1796 | thischar = (uint8_t)((gBitBuf >> 8) & 0xFF); 1797 | if (thischar != 0xFF) return PJPG_NOT_JPEG; 1798 | return 0; 1799 | } 1800 | //------------------------------------------------------------------------------ 1801 | // Find a start of frame (SOF) marker. 1802 | uint8_t JPEGDecoder::locateSOFMarker(void){ 1803 | uint8_t c; 1804 | uint8_t status = locateSOIMarker(); 1805 | if(status) return status; 1806 | status = processMarkers(&c); 1807 | if(status) return status; 1808 | switch(c){ 1809 | case M_SOF2:{ 1810 | // Progressive JPEG - not supported by JPEGDecoder (would require too 1811 | // much memory, or too many IDCT's for embedded systems). 1812 | return PJPG_UNSUPPORTED_MODE; 1813 | } 1814 | case M_SOF0:{ /* baseline DCT */ 1815 | status = readSOFMarker(); 1816 | if (status) return status; 1817 | break; 1818 | } 1819 | case M_SOF9:{return PJPG_NO_ARITHMITIC_SUPPORT;} 1820 | case M_SOF1: /* extended sequential DCT */ 1821 | default:{ 1822 | return PJPG_UNSUPPORTED_MARKER; 1823 | } 1824 | } 1825 | return 0; 1826 | } 1827 | //------------------------------------------------------------------------------ 1828 | // Find a start of scan (SOS) marker. 1829 | uint8_t JPEGDecoder::locateSOSMarker(uint8_t* pFoundEOI){ 1830 | uint8_t c; 1831 | uint8_t status; 1832 | 1833 | *pFoundEOI = 0; 1834 | status = processMarkers(&c); 1835 | if(status) return status; 1836 | if(c == M_EOI){ 1837 | *pFoundEOI = 1; 1838 | return 0; 1839 | } 1840 | else if (c != M_SOS) return PJPG_UNEXPECTED_MARKER; 1841 | return readSOSMarker(); 1842 | } 1843 | //------------------------------------------------------------------------------ 1844 | uint8_t JPEGDecoder::init(void){ 1845 | gImageXSize = 0; 1846 | gImageYSize = 0; 1847 | gCompsInFrame = 0; 1848 | gRestartInterval = 0; 1849 | gCompsInScan = 0; 1850 | gValidHuffTables = 0; 1851 | gValidQuantTables = 0; 1852 | gTemFlag = 0; 1853 | gInBufOfs = 0; 1854 | gInBufLeft = 0; 1855 | gBitBuf = 0; 1856 | gBitsLeft = 8; 1857 | 1858 | getBits1(8); 1859 | getBits1(8); 1860 | 1861 | return 0; 1862 | } 1863 | //------------------------------------------------------------------------------ 1864 | // This method throws back into the stream any bytes that where read 1865 | // into the bit buffer during initial marker scanning. 1866 | void JPEGDecoder::fixInBuffer(void){ 1867 | /* In case any 0xFF's where pulled into the buffer during marker scanning */ 1868 | if(gBitsLeft > 0) stuffChar((uint8_t)gBitBuf); 1869 | stuffChar((uint8_t)(gBitBuf >> 8)); 1870 | gBitsLeft = 8; 1871 | getBits2(8); 1872 | getBits2(8); 1873 | } 1874 | //------------------------------------------------------------------------------ 1875 | // Restart interval processing. 1876 | uint8_t JPEGDecoder::processRestart(void){ 1877 | // Let's scan a little bit to find the marker, but not _too_ far. 1878 | // 1536 is a "fudge factor" that determines how much to scan. 1879 | uint16_t i; 1880 | uint8_t c = 0; 1881 | for(i = 1536; i > 0; i--) if (getChar() == 0xFF) break; 1882 | if(i==0) return PJPG_BAD_RESTART_MARKER; 1883 | for( ; i > 0; i--) if ((c = getChar()) != 0xFF) break; 1884 | if(i==0) return PJPG_BAD_RESTART_MARKER; 1885 | // Is it the expected marker? If not, something bad happened. 1886 | if(c != (gNextRestartNum + M_RST0)) return PJPG_BAD_RESTART_MARKER; 1887 | // Reset each component's DC prediction values. 1888 | gLastDC[0] = 0; 1889 | gLastDC[1] = 0; 1890 | gLastDC[2] = 0; 1891 | gRestartsLeft = gRestartInterval; 1892 | gNextRestartNum = (gNextRestartNum + 1) & 7; 1893 | // Get the bit buffer going again 1894 | gBitsLeft = 8; 1895 | getBits2(8); 1896 | getBits2(8); 1897 | return 0; 1898 | } 1899 | //------------------------------------------------------------------------------ 1900 | // FIXME: findEOI() is not actually called at the end of the image 1901 | // (it's optional, and probably not needed on embedded devices) 1902 | uint8_t JPEGDecoder::findEOI(void){ 1903 | uint8_t c; 1904 | uint8_t status; 1905 | // Prime the bit buffer 1906 | gBitsLeft = 8; 1907 | getBits1(8); 1908 | getBits1(8); 1909 | 1910 | // The next marker _should_ be EOI 1911 | status = processMarkers(&c); 1912 | if(status) return status; 1913 | else if(gCallbackStatus) return gCallbackStatus; 1914 | //gTotalBytesRead -= in_buf_left; 1915 | if(c!=M_EOI) return PJPG_UNEXPECTED_MARKER; 1916 | return 0; 1917 | } 1918 | //------------------------------------------------------------------------------ 1919 | uint8_t JPEGDecoder::checkHuffTables(void){ 1920 | uint8_t i; 1921 | for(i = 0; i < gCompsInScan; i++){ 1922 | uint8_t compDCTab = gCompDCTab[gCompList[i]]; 1923 | uint8_t compACTab = gCompACTab[gCompList[i]] + 2; 1924 | if(((gValidHuffTables & (1 << compDCTab)) == 0)||((gValidHuffTables & (1 << compACTab)) == 0)) 1925 | return PJPG_UNDEFINED_HUFF_TABLE; 1926 | } 1927 | return 0; 1928 | } 1929 | //------------------------------------------------------------------------------ 1930 | uint8_t JPEGDecoder::checkQuantTables(void){ 1931 | uint8_t i; 1932 | for(i=0; i> ((gMaxMCUXSize == 8) ? 3 : 4); 2016 | gMaxMCUSPerCol = (gImageYSize + (gMaxMCUYSize - 1)) >> ((gMaxMCUYSize == 8) ? 3 : 4); 2017 | gNumMCUSRemaining = gMaxMCUSPerRow * gMaxMCUSPerCol; 2018 | return 0; 2019 | } 2020 | //---------------------------------------------------------------------------- 2021 | // Winograd IDCT: 5 multiplies per row/col, up to 80 muls for the 2D IDCT 2022 | #define PJPG_DCT_SCALE (1U << PJPG_DCT_SCALE_BITS) 2023 | //#define PJPG_DESCALE(x) PJPG_ARITH_SHIFT_RIGHT_N_16(((x) + (1U << (PJPG_DCT_SCALE_BITS - 1))), PJPG_DCT_SCALE_BITS) 2024 | #define PJPG_WFIX(x) ((x) * PJPG_DCT_SCALE + 0.5f) 2025 | 2026 | // Multiply quantization matrix by the Winograd IDCT scale factors 2027 | void JPEGDecoder::createWinogradQuant(int16_t* pQuant){ 2028 | uint8_t i; 2029 | for(i=0; i<64; i++){ 2030 | long x = pQuant[i]; 2031 | x *= gWinogradQuant[i]; 2032 | pQuant[i]=(int16_t)((x+(1<<(PJPG_WINOGRAD_QUANT_SCALE_BITS-PJPG_DCT_SCALE_BITS-1)))>>(PJPG_WINOGRAD_QUANT_SCALE_BITS-PJPG_DCT_SCALE_BITS)); 2033 | } 2034 | } 2035 | 2036 | void JPEGDecoder::idctRows(void){ 2037 | uint8_t i; 2038 | int16_t* pSrc = gCoeffBuf; 2039 | for(i=0; i<8; i++){ 2040 | if((pSrc[1] | pSrc[2] | pSrc[3] | pSrc[4] | pSrc[5] | pSrc[6] | pSrc[7]) == 0){ 2041 | // Short circuit the 1D IDCT if only the DC component is non-zero 2042 | int16_t src0 = *pSrc; 2043 | *(pSrc+1) = src0; 2044 | *(pSrc+2) = src0; 2045 | *(pSrc+3) = src0; 2046 | *(pSrc+4) = src0; 2047 | *(pSrc+5) = src0; 2048 | *(pSrc+6) = src0; 2049 | *(pSrc+7) = src0; 2050 | } 2051 | else{ 2052 | int16_t src4 = *(pSrc+5); 2053 | int16_t src7 = *(pSrc+3); 2054 | int16_t x4 = src4 - src7; 2055 | int16_t x7 = src4 + src7; 2056 | int16_t src5 = *(pSrc+1); 2057 | int16_t src6 = *(pSrc+7); 2058 | int16_t x5 = src5 + src6; 2059 | int16_t x6 = src5 - src6; 2060 | int16_t tmp1 = imul_b5(x4 - x6); 2061 | int16_t stg26 = imul_b4(x6) - tmp1; 2062 | int16_t x24 = tmp1 - imul_b2(x4); 2063 | int16_t x15 = x5 - x7; 2064 | int16_t x17 = x5 + x7; 2065 | int16_t tmp2 = stg26 - x17; 2066 | int16_t tmp3 = imul_b1_b3(x15) - tmp2; 2067 | int16_t x44 = tmp3 + x24; 2068 | int16_t src0 = *(pSrc+0); 2069 | int16_t src1 = *(pSrc+4); 2070 | int16_t x30 = src0 + src1; 2071 | int16_t x31 = src0 - src1; 2072 | int16_t src2 = *(pSrc+2); 2073 | int16_t src3 = *(pSrc+6); 2074 | int16_t x12 = src2 - src3; 2075 | int16_t x13 = src2 + src3; 2076 | int16_t x32 = imul_b1_b3(x12) - x13; 2077 | int16_t x40 = x30 + x13; 2078 | int16_t x43 = x30 - x13; 2079 | int16_t x41 = x31 + x32; 2080 | int16_t x42 = x31 - x32; 2081 | 2082 | *(pSrc+0) = x40 + x17; 2083 | *(pSrc+1) = x41 + tmp2; 2084 | *(pSrc+2) = x42 + tmp3; 2085 | *(pSrc+3) = x43 - x44; 2086 | *(pSrc+4) = x43 + x44; 2087 | *(pSrc+5) = x42 - tmp3; 2088 | *(pSrc+6) = x41 - tmp2; 2089 | *(pSrc+7) = x40 - x17; 2090 | } 2091 | pSrc += 8; 2092 | } 2093 | } 2094 | 2095 | void JPEGDecoder::idctCols(void){ 2096 | uint8_t i; 2097 | int16_t* pSrc = gCoeffBuf; 2098 | for(i=0; i<8; i++){ 2099 | if((pSrc[1*8] | pSrc[2*8] | pSrc[3*8] | pSrc[4*8] | pSrc[5*8] | pSrc[6*8] | pSrc[7*8]) == 0){ 2100 | // Short circuit the 1D IDCT if only the DC component is non-zero 2101 | uint8_t c = clamp(PJPG_DESCALE(*pSrc) + 128); 2102 | *(pSrc+0*8) = c; 2103 | *(pSrc+1*8) = c; 2104 | *(pSrc+2*8) = c; 2105 | *(pSrc+3*8) = c; 2106 | *(pSrc+4*8) = c; 2107 | *(pSrc+5*8) = c; 2108 | *(pSrc+6*8) = c; 2109 | *(pSrc+7*8) = c; 2110 | } 2111 | else{ 2112 | int16_t src4 = *(pSrc+5*8); 2113 | int16_t src7 = *(pSrc+3*8); 2114 | int16_t x4 = src4 - src7; 2115 | int16_t x7 = src4 + src7; 2116 | int16_t src5 = *(pSrc+1*8); 2117 | int16_t src6 = *(pSrc+7*8); 2118 | int16_t x5 = src5 + src6; 2119 | int16_t x6 = src5 - src6; 2120 | int16_t tmp1 = imul_b5(x4 - x6); 2121 | int16_t stg26 = imul_b4(x6) - tmp1; 2122 | int16_t x24 = tmp1 - imul_b2(x4); 2123 | int16_t x15 = x5 - x7; 2124 | int16_t x17 = x5 + x7; 2125 | int16_t tmp2 = stg26 - x17; 2126 | int16_t tmp3 = imul_b1_b3(x15) - tmp2; 2127 | int16_t x44 = tmp3 + x24; 2128 | int16_t src0 = *(pSrc+0*8); 2129 | int16_t src1 = *(pSrc+4*8); 2130 | int16_t x30 = src0 + src1; 2131 | int16_t x31 = src0 - src1; 2132 | int16_t src2 = *(pSrc+2*8); 2133 | int16_t src3 = *(pSrc+6*8); 2134 | int16_t x12 = src2 - src3; 2135 | int16_t x13 = src2 + src3; 2136 | int16_t x32 = imul_b1_b3(x12) - x13; 2137 | int16_t x40 = x30 + x13; 2138 | int16_t x43 = x30 - x13; 2139 | int16_t x41 = x31 + x32; 2140 | int16_t x42 = x31 - x32; 2141 | // descale, convert to unsigned and clamp to 8-bit 2142 | *(pSrc+0*8) = clamp(PJPG_DESCALE(x40 + x17) + 128); 2143 | *(pSrc+1*8) = clamp(PJPG_DESCALE(x41 + tmp2) + 128); 2144 | *(pSrc+2*8) = clamp(PJPG_DESCALE(x42 + tmp3) + 128); 2145 | *(pSrc+3*8) = clamp(PJPG_DESCALE(x43 - x44) + 128); 2146 | *(pSrc+4*8) = clamp(PJPG_DESCALE(x43 + x44) + 128); 2147 | *(pSrc+5*8) = clamp(PJPG_DESCALE(x42 - tmp3) + 128); 2148 | *(pSrc+6*8) = clamp(PJPG_DESCALE(x41 - tmp2) + 128); 2149 | *(pSrc+7*8) = clamp(PJPG_DESCALE(x40 - x17) + 128); 2150 | } 2151 | pSrc++; 2152 | } 2153 | } 2154 | /*----------------------------------------------------------------------------*/ 2155 | // Cb upsample and accumulate, 4x4 to 8x8 2156 | void JPEGDecoder::upsampleCb(uint8_t srcOfs, uint8_t dstOfs){ 2157 | // Cb - affects G and B 2158 | uint8_t x, y; 2159 | int16_t* pSrc = gCoeffBuf + srcOfs; 2160 | uint8_t* pDstG = gMCUBufG + dstOfs; 2161 | uint8_t* pDstB = gMCUBufB + dstOfs; 2162 | for(y=0; y<4; y++){ 2163 | for(x=0; x<4; x++){ 2164 | uint8_t cb = (uint8_t)*pSrc++; 2165 | int16_t cbG, cbB; 2166 | cbG = ((cb * 88U) >> 8U) - 44U; 2167 | pDstG[0] = subAndClamp(pDstG[0], cbG); 2168 | pDstG[1] = subAndClamp(pDstG[1], cbG); 2169 | pDstG[8] = subAndClamp(pDstG[8], cbG); 2170 | pDstG[9] = subAndClamp(pDstG[9], cbG); 2171 | cbB = (cb + ((cb * 198U) >> 8U)) - 227U; 2172 | pDstB[0] = addAndClamp(pDstB[0], cbB); 2173 | pDstB[1] = addAndClamp(pDstB[1], cbB); 2174 | pDstB[8] = addAndClamp(pDstB[8], cbB); 2175 | pDstB[9] = addAndClamp(pDstB[9], cbB); 2176 | pDstG += 2; 2177 | pDstB += 2; 2178 | } 2179 | pSrc = pSrc - 4 + 8; 2180 | pDstG = pDstG - 8 + 16; 2181 | pDstB = pDstB - 8 + 16; 2182 | } 2183 | } 2184 | /*----------------------------------------------------------------------------*/ 2185 | // Cb upsample and accumulate, 4x8 to 8x8 2186 | void JPEGDecoder::upsampleCbH(uint8_t srcOfs, uint8_t dstOfs){ 2187 | // Cb - affects G and B 2188 | uint8_t x, y; 2189 | int16_t* pSrc = gCoeffBuf + srcOfs; 2190 | uint8_t* pDstG = gMCUBufG + dstOfs; 2191 | uint8_t* pDstB = gMCUBufB + dstOfs; 2192 | for(y=0; y<8; y++){ 2193 | for(x=0; x<4; x++){ 2194 | uint8_t cb = (uint8_t)*pSrc++; 2195 | int16_t cbG, cbB; 2196 | cbG = ((cb * 88U) >> 8U) - 44U; 2197 | pDstG[0] = subAndClamp(pDstG[0], cbG); 2198 | pDstG[1] = subAndClamp(pDstG[1], cbG); 2199 | cbB = (cb + ((cb * 198U) >> 8U)) - 227U; 2200 | pDstB[0] = addAndClamp(pDstB[0], cbB); 2201 | pDstB[1] = addAndClamp(pDstB[1], cbB); 2202 | pDstG += 2; 2203 | pDstB += 2; 2204 | } 2205 | pSrc = pSrc - 4 + 8; 2206 | } 2207 | } 2208 | /*----------------------------------------------------------------------------*/ 2209 | // Cb upsample and accumulate, 8x4 to 8x8 2210 | void JPEGDecoder::upsampleCbV(uint8_t srcOfs, uint8_t dstOfs){ 2211 | // Cb - affects G and B 2212 | uint8_t x, y; 2213 | int16_t* pSrc = gCoeffBuf + srcOfs; 2214 | uint8_t* pDstG = gMCUBufG + dstOfs; 2215 | uint8_t* pDstB = gMCUBufB + dstOfs; 2216 | for(y=0; y<4; y++){ 2217 | for(x=0; x<8; x++){ 2218 | uint8_t cb = (uint8_t)*pSrc++; 2219 | int16_t cbG, cbB; 2220 | cbG = ((cb * 88U) >> 8U) - 44U; 2221 | pDstG[0] = subAndClamp(pDstG[0], cbG); 2222 | pDstG[8] = subAndClamp(pDstG[8], cbG); 2223 | cbB = (cb + ((cb * 198U) >> 8U)) - 227U; 2224 | pDstB[0] = addAndClamp(pDstB[0], cbB); 2225 | pDstB[8] = addAndClamp(pDstB[8], cbB); 2226 | ++pDstG; 2227 | ++pDstB; 2228 | } 2229 | pDstG = pDstG - 8 + 16; 2230 | pDstB = pDstB - 8 + 16; 2231 | } 2232 | } 2233 | /*----------------------------------------------------------------------------*/ 2234 | // Cr upsample and accumulate, 4x4 to 8x8 2235 | void JPEGDecoder::upsampleCr(uint8_t srcOfs, uint8_t dstOfs){ 2236 | // Cr - affects R and G 2237 | uint8_t x, y; 2238 | int16_t* pSrc = gCoeffBuf + srcOfs; 2239 | uint8_t* pDstR = gMCUBufR + dstOfs; 2240 | uint8_t* pDstG = gMCUBufG + dstOfs; 2241 | for(y=0; y<4; y++){ 2242 | for(x=0; x<4; x++){ 2243 | uint8_t cr = (uint8_t)*pSrc++; 2244 | int16_t crR, crG; 2245 | crR = (cr + ((cr * 103U) >> 8U)) - 179; 2246 | pDstR[0] = addAndClamp(pDstR[0], crR); 2247 | pDstR[1] = addAndClamp(pDstR[1], crR); 2248 | pDstR[8] = addAndClamp(pDstR[8], crR); 2249 | pDstR[9] = addAndClamp(pDstR[9], crR); 2250 | crG = ((cr * 183U) >> 8U) - 91; 2251 | pDstG[0] = subAndClamp(pDstG[0], crG); 2252 | pDstG[1] = subAndClamp(pDstG[1], crG); 2253 | pDstG[8] = subAndClamp(pDstG[8], crG); 2254 | pDstG[9] = subAndClamp(pDstG[9], crG); 2255 | pDstR += 2; 2256 | pDstG += 2; 2257 | } 2258 | pSrc = pSrc - 4 + 8; 2259 | pDstR = pDstR - 8 + 16; 2260 | pDstG = pDstG - 8 + 16; 2261 | } 2262 | } 2263 | /*----------------------------------------------------------------------------*/ 2264 | // Cr upsample and accumulate, 4x8 to 8x8 2265 | void JPEGDecoder::upsampleCrH(uint8_t srcOfs, uint8_t dstOfs){ 2266 | // Cr - affects R and G 2267 | uint8_t x, y; 2268 | int16_t* pSrc = gCoeffBuf + srcOfs; 2269 | uint8_t* pDstR = gMCUBufR + dstOfs; 2270 | uint8_t* pDstG = gMCUBufG + dstOfs; 2271 | for(y=0; y<8; y++){ 2272 | for(x=0; x<4; x++){ 2273 | uint8_t cr = (uint8_t)*pSrc++; 2274 | int16_t crR, crG; 2275 | crR = (cr + ((cr * 103U) >> 8U)) - 179; 2276 | pDstR[0] = addAndClamp(pDstR[0], crR); 2277 | pDstR[1] = addAndClamp(pDstR[1], crR); 2278 | crG = ((cr * 183U) >> 8U) - 91; 2279 | pDstG[0] = subAndClamp(pDstG[0], crG); 2280 | pDstG[1] = subAndClamp(pDstG[1], crG); 2281 | pDstR += 2; 2282 | pDstG += 2; 2283 | } 2284 | pSrc = pSrc - 4 + 8; 2285 | } 2286 | } 2287 | /*----------------------------------------------------------------------------*/ 2288 | // Cr upsample and accumulate, 8x4 to 8x8 2289 | void JPEGDecoder::upsampleCrV(uint8_t srcOfs, uint8_t dstOfs){ 2290 | // Cr - affects R and G 2291 | uint8_t x, y; 2292 | int16_t* pSrc = gCoeffBuf + srcOfs; 2293 | uint8_t* pDstR = gMCUBufR + dstOfs; 2294 | uint8_t* pDstG = gMCUBufG + dstOfs; 2295 | for(y=0; y<4; y++){ 2296 | for(x=0; x<8; x++){ 2297 | uint8_t cr = (uint8_t)*pSrc++; 2298 | int16_t crR, crG; 2299 | crR = (cr + ((cr * 103U) >> 8U)) - 179; 2300 | pDstR[0] = addAndClamp(pDstR[0], crR); 2301 | pDstR[8] = addAndClamp(pDstR[8], crR); 2302 | crG = ((cr * 183U) >> 8U) - 91; 2303 | pDstG[0] = subAndClamp(pDstG[0], crG); 2304 | pDstG[8] = subAndClamp(pDstG[8], crG); 2305 | ++pDstR; 2306 | ++pDstG; 2307 | } 2308 | pDstR = pDstR - 8 + 16; 2309 | pDstG = pDstG - 8 + 16; 2310 | } 2311 | } 2312 | /*----------------------------------------------------------------------------*/ 2313 | // Convert Y to RGB 2314 | void JPEGDecoder::copyY(uint8_t dstOfs){ 2315 | uint8_t i; 2316 | uint8_t* pRDst = gMCUBufR + dstOfs; 2317 | uint8_t* pGDst = gMCUBufG + dstOfs; 2318 | uint8_t* pBDst = gMCUBufB + dstOfs; 2319 | int16_t* pSrc = gCoeffBuf; 2320 | for(i=64; i>0; i--){ 2321 | uint8_t c = (uint8_t)*pSrc++; 2322 | *pRDst++ = c; 2323 | *pGDst++ = c; 2324 | *pBDst++ = c; 2325 | } 2326 | } 2327 | /*----------------------------------------------------------------------------*/ 2328 | // Cb convert to RGB and accumulate 2329 | void JPEGDecoder::convertCb(uint8_t dstOfs){ 2330 | uint8_t i; 2331 | uint8_t* pDstG = gMCUBufG + dstOfs; 2332 | uint8_t* pDstB = gMCUBufB + dstOfs; 2333 | int16_t* pSrc = gCoeffBuf; 2334 | for(i=64; i>0; i--){ 2335 | uint8_t cb = (uint8_t)*pSrc++; 2336 | int16_t cbG, cbB; 2337 | cbG = ((cb * 88U) >> 8U) - 44U; 2338 | pDstG[0] = subAndClamp(pDstG[0], cbG); 2339 | cbB = (cb + ((cb * 198U) >> 8U)) - 227U; 2340 | pDstB[0] = addAndClamp(pDstB[0], cbB); 2341 | ++pDstG; 2342 | ++pDstB; 2343 | } 2344 | } 2345 | /*----------------------------------------------------------------------------*/ 2346 | // Cr convert to RGB and accumulate 2347 | void JPEGDecoder::convertCr(uint8_t dstOfs){ 2348 | uint8_t i; 2349 | uint8_t* pDstR = gMCUBufR + dstOfs; 2350 | uint8_t* pDstG = gMCUBufG + dstOfs; 2351 | int16_t* pSrc = gCoeffBuf; 2352 | for(i=64; i>0; i--){ 2353 | uint8_t cr = (uint8_t)*pSrc++; 2354 | int16_t crR, crG; 2355 | crR = (cr + ((cr * 103U) >> 8U)) - 179; 2356 | pDstR[0] = addAndClamp(pDstR[0], crR); 2357 | crG = ((cr * 183U) >> 8U) - 91; 2358 | pDstG[0] = subAndClamp(pDstG[0], crG); 2359 | ++pDstR; 2360 | ++pDstG; 2361 | } 2362 | } 2363 | /*----------------------------------------------------------------------------*/ 2364 | void JPEGDecoder::transformBlock(uint8_t mcuBlock){ 2365 | idctRows(); 2366 | idctCols(); 2367 | switch (gScanType){ 2368 | case PJPG_GRAYSCALE:{copyY(0); break;} // MCU size: 1, 1 block per MCU 2369 | case PJPG_YH1V1:{ // MCU size: 8x8, 3 blocks per MCU 2370 | switch(mcuBlock){ 2371 | case 0:{copyY(0); break;} 2372 | case 1:{convertCb(0); break;} 2373 | case 2:{convertCr(0); break;} 2374 | } break; 2375 | } 2376 | case PJPG_YH1V2:{ // MCU size: 8x16, 4 blocks per MCU 2377 | switch(mcuBlock){ 2378 | case 0:{copyY(0); break;} 2379 | case 1:{copyY(128); break;} 2380 | case 2:{upsampleCbV(0, 0); upsampleCbV(4*8, 128); break;} 2381 | case 3:{upsampleCrV(0, 0); upsampleCrV(4*8, 128); break;} 2382 | } break; 2383 | } 2384 | case PJPG_YH2V1:{ // MCU size: 16x8, 4 blocks per MCU 2385 | switch(mcuBlock){ 2386 | case 0:{copyY(0); break;} 2387 | case 1:{copyY(64); break;} 2388 | case 2:{upsampleCbH(0, 0); upsampleCbH(4, 64); break;} 2389 | case 3:{upsampleCrH(0, 0); upsampleCrH(4, 64); break;} 2390 | } break; 2391 | } 2392 | case PJPG_YH2V2:{ // MCU size: 16x16, 6 blocks per MCU 2393 | switch (mcuBlock){ 2394 | case 0:{copyY(0); break;} 2395 | case 1:{copyY(64); break;} 2396 | case 2:{copyY(128); break;} 2397 | case 3:{copyY(192); break;} 2398 | case 4:{upsampleCb(0, 0); upsampleCb(4, 64); upsampleCb(4*8, 128); upsampleCb(4+4*8, 192); break;} 2399 | case 5:{upsampleCr(0, 0); upsampleCr(4, 64); upsampleCr(4*8, 128); upsampleCr(4+4*8, 192); break;} 2400 | } break; 2401 | } 2402 | } 2403 | } 2404 | //------------------------------------------------------------------------------ 2405 | void JPEGDecoder::transformBlockReduce(uint8_t mcuBlock){ 2406 | uint8_t c = clamp(PJPG_DESCALE(gCoeffBuf[0]) + 128); 2407 | int16_t cbG, cbB, crR, crG; 2408 | switch(gScanType){ 2409 | case PJPG_GRAYSCALE:{gMCUBufR[0] = c; break;} // MCU size: 1, 1 block per MCU 2410 | case PJPG_YH1V1:{ // MCU size: 8x8, 3 blocks per MCU 2411 | switch(mcuBlock){ 2412 | case 0:{gMCUBufR[0] = c; gMCUBufG[0] = c; gMCUBufB[0] = c; break;} 2413 | case 1:{cbG = ((c * 88U) >> 8U) - 44U; 2414 | gMCUBufG[0] = subAndClamp(gMCUBufG[0], cbG); 2415 | cbB = (c + ((c * 198U) >> 8U)) - 227U; 2416 | gMCUBufB[0] = addAndClamp(gMCUBufB[0], cbB); 2417 | break;} 2418 | case 2:{crR = (c + ((c * 103U) >> 8U)) - 179; 2419 | gMCUBufR[0] = addAndClamp(gMCUBufR[0], crR); 2420 | crG = ((c * 183U) >> 8U) - 91; 2421 | gMCUBufG[0] = subAndClamp(gMCUBufG[0], crG); 2422 | break;} 2423 | } break; 2424 | } 2425 | case PJPG_YH1V2:{ // MCU size: 8x16, 4 blocks per MCU 2426 | switch(mcuBlock){ 2427 | case 0:{gMCUBufR[0] = c; gMCUBufG[0] = c; gMCUBufB[0] = c; break;} 2428 | case 1:{gMCUBufR[128] = c; gMCUBufG[128] = c; gMCUBufB[128] = c; break;} 2429 | case 2:{cbG = ((c * 88U) >> 8U) - 44U; 2430 | gMCUBufG[0] = subAndClamp(gMCUBufG[0], cbG); 2431 | gMCUBufG[128] = subAndClamp(gMCUBufG[128], cbG); 2432 | cbB = (c + ((c * 198U) >> 8U)) - 227U; 2433 | gMCUBufB[0] = addAndClamp(gMCUBufB[0], cbB); 2434 | gMCUBufB[128] = addAndClamp(gMCUBufB[128], cbB); 2435 | break;} 2436 | case 3:{crR = (c + ((c * 103U) >> 8U)) - 179; 2437 | gMCUBufR[0] = addAndClamp(gMCUBufR[0], crR); 2438 | gMCUBufR[128] = addAndClamp(gMCUBufR[128], crR); 2439 | crG = ((c * 183U) >> 8U) - 91; 2440 | gMCUBufG[0] = subAndClamp(gMCUBufG[0], crG); 2441 | gMCUBufG[128] = subAndClamp(gMCUBufG[128], crG); 2442 | break;} 2443 | } break; 2444 | } 2445 | case PJPG_YH2V1:{ // MCU size: 16x8, 4 blocks per MCU 2446 | switch(mcuBlock){ 2447 | case 0:{gMCUBufR[0] = c; gMCUBufG[0] = c; gMCUBufB[0] = c; break;} 2448 | case 1:{gMCUBufR[64] = c; gMCUBufG[64] = c; gMCUBufB[64] = c; break;} 2449 | case 2:{cbG = ((c * 88U) >> 8U) - 44U; 2450 | gMCUBufG[0] = subAndClamp(gMCUBufG[0], cbG); 2451 | gMCUBufG[64] = subAndClamp(gMCUBufG[64], cbG); 2452 | cbB = (c + ((c * 198U) >> 8U)) - 227U; 2453 | gMCUBufB[0] = addAndClamp(gMCUBufB[0], cbB); 2454 | gMCUBufB[64] = addAndClamp(gMCUBufB[64], cbB); 2455 | break;} 2456 | case 3:{crR = (c + ((c * 103U) >> 8U)) - 179; 2457 | gMCUBufR[0] = addAndClamp(gMCUBufR[0], crR); 2458 | gMCUBufR[64] = addAndClamp(gMCUBufR[64], crR); 2459 | crG = ((c * 183U) >> 8U) - 91; 2460 | gMCUBufG[0] = subAndClamp(gMCUBufG[0], crG); 2461 | gMCUBufG[64] = subAndClamp(gMCUBufG[64], crG); 2462 | break;} 2463 | } break; 2464 | } 2465 | case PJPG_YH2V2:{ // MCU size: 16x16, 6 blocks per MCU 2466 | switch(mcuBlock){ 2467 | case 0:{gMCUBufR[0] = c; gMCUBufG[0] = c; gMCUBufB[0] = c; break;} 2468 | case 1:{gMCUBufR[64] = c; gMCUBufG[64] = c; gMCUBufB[64] = c; break;} 2469 | case 2:{gMCUBufR[128] = c; gMCUBufG[128] = c; gMCUBufB[128] = c; break;} 2470 | case 3:{gMCUBufR[192] = c; gMCUBufG[192] = c; gMCUBufB[192] = c; break;} 2471 | case 4:{cbG = ((c * 88U) >> 8U) - 44U; 2472 | gMCUBufG[0] = subAndClamp(gMCUBufG[0], cbG); 2473 | gMCUBufG[64] = subAndClamp(gMCUBufG[64], cbG); 2474 | gMCUBufG[128] = subAndClamp(gMCUBufG[128], cbG); 2475 | gMCUBufG[192] = subAndClamp(gMCUBufG[192], cbG); 2476 | cbB = (c + ((c * 198U) >> 8U)) - 227U; 2477 | gMCUBufB[0] = addAndClamp(gMCUBufB[0], cbB); 2478 | gMCUBufB[64] = addAndClamp(gMCUBufB[64], cbB); 2479 | gMCUBufB[128] = addAndClamp(gMCUBufB[128], cbB); 2480 | gMCUBufB[192] = addAndClamp(gMCUBufB[192], cbB); 2481 | break;} 2482 | case 5:{crR = (c + ((c * 103U) >> 8U)) - 179; 2483 | gMCUBufR[0] = addAndClamp(gMCUBufR[0], crR); 2484 | gMCUBufR[64] = addAndClamp(gMCUBufR[64], crR); 2485 | gMCUBufR[128] = addAndClamp(gMCUBufR[128], crR); 2486 | gMCUBufR[192] = addAndClamp(gMCUBufR[192], crR); 2487 | crG = ((c * 183U) >> 8U) - 91; 2488 | gMCUBufG[0] = subAndClamp(gMCUBufG[0], crG); 2489 | gMCUBufG[64] = subAndClamp(gMCUBufG[64], crG); 2490 | gMCUBufG[128] = subAndClamp(gMCUBufG[128], crG); 2491 | gMCUBufG[192] = subAndClamp(gMCUBufG[192], crG); 2492 | break;} 2493 | } break; 2494 | } 2495 | } 2496 | } 2497 | //------------------------------------------------------------------------------ 2498 | uint8_t JPEGDecoder::huffDecode(const HuffTable* pHuffTable, const uint8_t* pHuffVal){ 2499 | uint8_t i = 0; uint8_t j; uint16_t code = getBit(); 2500 | 2501 | // This func only reads a bit at a time, which on modern CPU's is not terribly efficient. 2502 | // But on microcontrollers without strong integer shifting support this seems like a 2503 | // more reasonable approach. 2504 | for ( ; ; ){ 2505 | uint16_t maxCode; 2506 | if (i == 16) return 0; 2507 | 2508 | maxCode = pHuffTable->mMaxCode[i]; 2509 | if ((code <= maxCode) && (maxCode != 0xFFFF)) break; 2510 | 2511 | i++; 2512 | code <<= 1; 2513 | code |= getBit(); 2514 | } 2515 | 2516 | j = pHuffTable->mValPtr[i]; 2517 | j = (uint8_t)(j + (code - pHuffTable->mMinCode[i])); 2518 | return pHuffVal[j]; 2519 | } 2520 | //------------------------------------------------------------------------------ 2521 | uint8_t JPEGDecoder::decodeNextMCU(void){ 2522 | uint8_t status; 2523 | uint8_t mcuBlock; 2524 | if(gRestartInterval){ 2525 | if(gRestartsLeft == 0){ 2526 | status = processRestart(); 2527 | if(status) return status; 2528 | } 2529 | gRestartsLeft--; 2530 | } 2531 | for(mcuBlock = 0; mcuBlock < gMaxBlocksPerMCU; mcuBlock++){ 2532 | uint8_t componentID = gMCUOrg[mcuBlock]; 2533 | uint8_t compQuant = gCompQuant[componentID]; 2534 | uint8_t compDCTab = gCompDCTab[componentID]; 2535 | uint8_t numExtraBits, compACTab, k; 2536 | const int16_t* pQ = compQuant ? gQuant1 : gQuant0; 2537 | uint16_t r, dc; 2538 | uint8_t s = huffDecode(compDCTab ? &gHuffTab1 : &gHuffTab0, compDCTab ? gHuffVal1 : gHuffVal0); 2539 | r = 0; 2540 | numExtraBits = s & 0xF; 2541 | if(numExtraBits) r = getBits2(numExtraBits); 2542 | dc = huffExtend(r, s); 2543 | dc = dc + gLastDC[componentID]; 2544 | gLastDC[componentID] = dc; 2545 | gCoeffBuf[0] = dc * pQ[0]; 2546 | compACTab = gCompACTab[componentID]; 2547 | if(gReduce){ // Decode, but throw out the AC coefficients in reduce mode. 2548 | for(k=1; k<64; k++){ 2549 | s=huffDecode(compACTab ? &gHuffTab3 : &gHuffTab2, compACTab ? gHuffVal3 : gHuffVal2); 2550 | numExtraBits = s & 0xF; 2551 | if(numExtraBits) getBits2(numExtraBits); 2552 | r = s >> 4; 2553 | s &= 15; 2554 | if(s){ 2555 | if(r){ 2556 | if((k+r)>63) return PJPG_DECODE_ERROR; 2557 | k=(uint8_t)(k + r); 2558 | } 2559 | } 2560 | else{ 2561 | if(r==15){ 2562 | if((k+16)>64) return PJPG_DECODE_ERROR; 2563 | k+=(16-1); // - 1 because the loop counter is k 2564 | } 2565 | else break; 2566 | } 2567 | } 2568 | transformBlockReduce(mcuBlock); 2569 | } 2570 | else{ // Decode and dequantize AC coefficients 2571 | for(k=1; k<64; k++){ 2572 | uint16_t extraBits; 2573 | s=huffDecode(compACTab ? &gHuffTab3 : &gHuffTab2, compACTab ? gHuffVal3 : gHuffVal2); 2574 | extraBits = 0; 2575 | numExtraBits = s & 0xF; 2576 | if(numExtraBits) extraBits=getBits2(numExtraBits); 2577 | r=s>>4; 2578 | s&=15; 2579 | if(s){ 2580 | int16_t ac; 2581 | if(r){ 2582 | if((k+r)>63) return PJPG_DECODE_ERROR; 2583 | while(r){ 2584 | gCoeffBuf[ZAG[k++]] = 0; 2585 | r--; 2586 | } 2587 | } 2588 | ac=huffExtend(extraBits, s); 2589 | gCoeffBuf[ZAG[k]] = ac * pQ[k]; 2590 | } 2591 | else{ 2592 | if(r==15){ 2593 | if((k+16)>64) return PJPG_DECODE_ERROR; 2594 | for(r=16; r>0; r--) gCoeffBuf[ZAG[k++]] = 0; 2595 | k--; // - 1 because the loop counter is k 2596 | } 2597 | else break; 2598 | } 2599 | } 2600 | while(k<64) gCoeffBuf[ZAG[k++]] = 0; 2601 | transformBlock(mcuBlock); 2602 | } 2603 | } 2604 | return 0; 2605 | } 2606 | //------------------------------------------------------------------------------ 2607 | uint8_t JPEGDecoder::pjpeg_decode_mcu(){ 2608 | uint8_t status; 2609 | if(gCallbackStatus) return gCallbackStatus; 2610 | if(!gNumMCUSRemaining) return PJPG_NO_MORE_BLOCKS; 2611 | status=decodeNextMCU(); 2612 | if((status)||(gCallbackStatus)) return gCallbackStatus ? gCallbackStatus : status; 2613 | gNumMCUSRemaining--; 2614 | return 0; 2615 | } 2616 | //------------------------------------------------------------------------------ 2617 | uint8_t JPEGDecoder::pjpeg_decode_init(pjpeg_image_info_t* pInfo, pjpeg_need_bytes_callback_t pNeed_bytes_callback, void *pCallback_data, unsigned char reduce){ 2618 | uint8_t status; 2619 | pInfo->m_width = 0; pInfo->m_height = 0; pInfo->m_comps = 0; 2620 | pInfo->m_MCUSPerRow = 0; pInfo->m_MCUSPerCol = 0; 2621 | pInfo->m_scanType = PJPG_GRAYSCALE; 2622 | pInfo->m_MCUWidth = 0; pInfo->m_MCUHeight = 0; 2623 | pInfo->m_pMCUBufR = (unsigned char*)0; pInfo->m_pMCUBufG = (unsigned char*)0; pInfo->m_pMCUBufB = (unsigned char*)0; 2624 | g_pNeedBytesCallback = pNeed_bytes_callback; 2625 | g_pCallback_data = pCallback_data; 2626 | gCallbackStatus = 0; 2627 | gReduce = reduce; 2628 | status = init(); 2629 | if((status)||(gCallbackStatus)) return gCallbackStatus ? gCallbackStatus : status; 2630 | status = locateSOFMarker(); 2631 | if((status)||(gCallbackStatus)) return gCallbackStatus ? gCallbackStatus : status; 2632 | status = initFrame(); 2633 | if((status)||(gCallbackStatus)) return gCallbackStatus ? gCallbackStatus : status; 2634 | status = initScan(); 2635 | if((status)||(gCallbackStatus)) return gCallbackStatus ? gCallbackStatus : status; 2636 | pInfo->m_width = gImageXSize; pInfo->m_height = gImageYSize; pInfo->m_comps = gCompsInFrame; 2637 | pInfo->m_scanType = gScanType; 2638 | pInfo->m_MCUSPerRow = gMaxMCUSPerRow; pInfo->m_MCUSPerCol = gMaxMCUSPerCol; 2639 | pInfo->m_MCUWidth = gMaxMCUXSize; pInfo->m_MCUHeight = gMaxMCUYSize; 2640 | pInfo->m_pMCUBufR = gMCUBufR; pInfo->m_pMCUBufG = gMCUBufG; pInfo->m_pMCUBufB = gMCUBufB; 2641 | return 0; 2642 | } 2643 | 2644 | /*******************************************************************************/ 2645 | 2646 | // Code für Touchpad mit XPT2046 2647 | TP::TP(uint8_t CS, uint8_t IRQ){ 2648 | TP_CS=CS; 2649 | TP_IRQ=IRQ; 2650 | pinMode(TP_CS, OUTPUT); 2651 | digitalWrite(TP_CS, HIGH); // prevent blockage of the SPI bus 2652 | pinMode(TP_IRQ, INPUT); 2653 | TP_SPI=SPISettings(100000, MSBFIRST, SPI_MODE0); //slower speed 2654 | xFaktor=float(Xmax-Xmin)/TFT_WIDTH; 2655 | yFaktor=float(Ymax-Ymin)/TFT_HEIGHT; 2656 | _rotation=0; 2657 | } 2658 | 2659 | uint16_t TP::TP_Send(uint8_t set_val){ 2660 | uint16_t get_val; 2661 | SPItransfer->beginTransaction(TP_SPI); // Prevent other SPI users 2662 | digitalWrite(TP_CS, 0); 2663 | SPItransfer->write(set_val); 2664 | get_val=SPItransfer->transfer16(0); 2665 | digitalWrite(TP_CS, 1); 2666 | SPItransfer->endTransaction(); // Allow other SPI users 2667 | return get_val >> 4; 2668 | } 2669 | 2670 | void TP::loop(){ 2671 | if(!digitalRead(TP_IRQ)){ 2672 | read_TP(x,y); //erste Messung auslassen 2673 | if(f_loop){ 2674 | f_loop=false; 2675 | if(read_TP(x, y)) {if(tp_pressed) tp_pressed(x, y);} //zweite Messung lesen 2676 | } 2677 | } else { 2678 | if(f_loop==false){if(tp_released) tp_released();} 2679 | f_loop=true; 2680 | } 2681 | } 2682 | void TP::setRotation(uint8_t m){ 2683 | _rotation=m; 2684 | } 2685 | 2686 | bool TP::read_TP(uint16_t& x, uint16_t& y){ 2687 | uint16_t _y[3]; 2688 | uint16_t _x[3]; 2689 | uint16_t tmpxy; 2690 | 2691 | uint8_t i; 2692 | if (digitalRead(TP_IRQ)) return false; 2693 | for(i=0; i<3; i++){ 2694 | x = TP_Send(0xD0); //x 2695 | 2696 | if((xXmax)) return false; //außerhalb des Displays 2697 | x=Xmax-x; 2698 | _x[i]=x/xFaktor; 2699 | 2700 | y= TP_Send(0x90); //y 2701 | if((yYmax)) return false; //außerhalb des Displays 2702 | y=Ymax-y; 2703 | _y[i]=y/yFaktor; 2704 | 2705 | } 2706 | x=(_x[0]+_x[1]+_x[2])/3; // Mittelwert bilden 2707 | y=(_y[0]+_y[1]+_y[2])/3; 2708 | 2709 | //log_i("TP X org=%i",x); 2710 | //log_i("TP Y org=%i",y); 2711 | 2712 | if(_rotation==1){tmpxy=x; x=y; y=TFT_WIDTH-tmpxy; if(x>TFT_HEIGHT-1) x=0; if(y>TFT_WIDTH-1)y=0;} 2713 | if(_rotation==2){x=TFT_WIDTH-x; y=TFT_HEIGHT-y; if(x>TFT_WIDTH-1) x=0; if(y>TFT_HEIGHT-1) y=0;} 2714 | if(_rotation==3){tmpxy=y; y=x; x=TFT_HEIGHT-tmpxy; if(x>TFT_HEIGHT-1) x=0; if(y>TFT_WIDTH-1) y=0;} 2715 | //log_i("TP X=%i",x); 2716 | //log_i("TP Y=%i",y); 2717 | return true; 2718 | } 2719 | 2720 | -------------------------------------------------------------------------------- /src/ili9486.h: -------------------------------------------------------------------------------- 1 | /* 2 | * tft.h 3 | * 4 | * Created on: May 28,2018 5 | * Updated on: May 05,2025 6 | * Author: Wolle (schreibfaul1) 7 | * 8 | */ 9 | 10 | 11 | #pragma once 12 | 13 | #include "Arduino.h" 14 | #include "FS.h" 15 | #include "SPI.h" 16 | #include "SD.h" 17 | 18 | //exclude all not needed fonts (because c++ indexer will be slow) 19 | //#include "fonts/Garamond.h" // default, do not comment this line 20 | //#include "fonts/Baskerville_Old_Face.h" 21 | //#include "fonts/Courier_New.h" 22 | //#include "fonts/Monotype_Corsiva.h" 23 | #include "fonts/misc.h" 24 | //#include "fonts/Old_English_Text_MT.h" 25 | //#include "fonts/Script_MT_Bold.h" 26 | //#include "fonts/Garamond_cyrillic.h" 27 | //#include "fonts/Garamond_greek.h" 28 | #include "fonts/Times_New_Roman.h" // latin, greek, cyrillic with all extensions 29 | 30 | extern __attribute__((weak)) void tft_info(const char*); 31 | extern __attribute__((weak)) void tp_pressed(uint16_t x, uint16_t y); 32 | extern __attribute__((weak)) void tp_released(); 33 | 34 | #define TFT_WIDTH 320 35 | #define TFT_HEIGHT 480 36 | 37 | #define ILI9486_INVOFF 0x20 // Display Inversion OFF 38 | #define ILI9486_INVON 0x21 // Display Inversion ON 39 | #define ILI9486_CASET 0x2A // Display On 40 | #define ILI9486_PASET 0x2B // Page Address Set 41 | #define ILI9486_RAMWR 0x2C // Memory Write 42 | #define ILI9486_MADCTL 0x36 // Memory Data Access Control 43 | #define MADCTL_MY 0x80 // Bit 7 Parameter MADCTL 44 | #define MADCTL_MX 0x40 // Bit 6 Parameter MADCTL 45 | #define MADCTL_MV 0x20 // Bit 5 Parameter MADCTL 46 | #define MADCTL_ML 0x10 // Bit 4 Parameter MADCTL 47 | #define MADCTL_BGR 0x08 // Bit 3 Parameter MADCTL 48 | #define MADCTL_MH 0x04 // Bit 2 Parameter MADCTL 49 | #define ILI9486_WDBVAL 0x51 // Write Display Brightness Value 50 | #define ILI9486_CDBVAL 0x53 // Write Control Display Value 51 | 52 | // RGB565 Color definitions 53 | #define TFT_AQUAMARINE 0x7FFA // 127, 255, 212 54 | #define TFT_BEIGE 0xF7BB // 245, 245, 220 55 | #define TFT_BLACK 0x0000 // 0, 0, 0 56 | #define TFT_BLUE 0x001F // 0, 0, 255 57 | #define TFT_BROWN 0xA145 // 165, 42, 42 58 | #define TFT_CHOCOLATE 0xD343 // 210, 105, 30 59 | #define TFT_CORNSILK 0xFFDB // 255, 248, 220 60 | #define TFT_CYAN 0x07FF // 0, 255, 255 61 | #define TFT_DARKGREEN 0x0320 // 0, 100, 0 62 | #define TFT_DARKGREY 0xAD55 // 169, 169, 169 63 | #define TFT_DARKCYAN 0x0451 // 0, 139, 139 64 | #define TFT_DEEPSKYBLUE 0x05FF // 0, 191, 255 65 | #define TFT_GRAY 0x8410 // 128, 128, 128 66 | #define TFT_GREEN 0x0400 // 0, 128, 0 67 | #define TFT_GREENYELLOW 0xAFE5 // 173, 255, 47 68 | #define TFT_GOLD 0xFEA0 // 255, 215, 0 69 | #define TFT_HOTPINK 0xFB56 // 255, 105, 180 70 | #define TFT_LAVENDER 0xE73F // 230, 230, 250 71 | #define TFT_LAWNGREEN 0x7FE0 // 124, 252, 0 72 | #define TFT_LIGHTBLUE 0xAEDC // 173, 216, 230 73 | #define TFT_LIGHTCYAN 0xE7FF // 224, 255, 255 74 | #define TFT_LIGHTGREY 0xD69A // 211, 211, 211 75 | #define TFT_LIGHTGREEN 0x9772 // 144, 238, 144 76 | #define TFT_LIGHTYELLOW 0xFFFC // 255, 255, 224 77 | #define TFT_LIME 0x07E0 // 0. 255, 0 78 | #define TFT_MAGENTA 0xF81F // 255, 0, 255 79 | #define TFT_MAROON 0x7800 // 128, 0, 0 80 | #define TFT_MEDIUMVIOLETRED 0xC0B0 // 199, 21, 133 81 | #define TFT_NAVY 0x000F // 0, 0, 128 82 | #define TFT_OLIVE 0x7BE0 // 128, 128, 0 83 | #define TFT_ORANGE 0xFD20 // 255, 165, 0 84 | #define TFT_PINK 0xFE19 // 255, 192, 203 85 | #define TFT_PURPLE 0x780F // 128, 0, 128 86 | #define TFT_RED 0xF800 // 255, 0, 0 87 | #define TFT_SANDYBROWN 0xF52C // 244, 164, 96 88 | #define TFT_TURQUOISE 0x471A // 64, 224, 208 89 | #define TFT_VIOLET 0x801F // 128, 0, 255 90 | #define TFT_WHITE 0xFFFF // 255, 255, 255 91 | #define TFT_YELLOW 0xFFE0 // 255, 255, 0 92 | 93 | //----------------------------------------------------------------------------------------------------------------------- 94 | class TFT : public Print { 95 | 96 | public: 97 | TFT(); 98 | virtual ~TFT(){} 99 | 100 | void begin(uint8_t CS, uint8_t DC, uint8_t spi, uint8_t mosi, uint8_t miso, uint8_t sclk); 101 | 102 | void setRotation(uint8_t r); 103 | void setFrequency(uint32_t f); 104 | bool setCursor(uint16_t x, uint16_t y); 105 | void invertDisplay(boolean i); 106 | // void scrollTo(uint16_t y); 107 | 108 | virtual size_t write(uint8_t); 109 | virtual size_t write(const uint8_t *buffer, size_t size); 110 | 111 | // Recommended Non-Transaction 112 | void drawLine(int16_t Xpos0, int16_t Ypos0, int16_t Xpos1, int16_t Ypos1, uint16_t color); 113 | void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); 114 | void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); 115 | void drawRect(int16_t Xpos, int16_t Ypos, uint16_t Width, uint16_t Height, uint16_t Color); 116 | void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); 117 | void drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color); 118 | void fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color); 119 | void fillScreen(uint16_t color); 120 | void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color); 121 | void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color); 122 | void drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color); 123 | void fillCircle(int16_t Xm, int16_t Ym, uint16_t r, uint16_t color); 124 | void setBrigthness(uint8_t br); 125 | boolean drawBmpFile(fs::FS &fs, const char * path, uint16_t x=0, uint16_t y=0, uint16_t maxWidth=0, uint16_t maxHeight=0, uint16_t offX=0, uint16_t offY=0); 126 | boolean drawJpgFile(fs::FS &fs, const char * path, uint16_t x=0, uint16_t y=0, uint16_t maxWidth=0, uint16_t maxHeight=0, uint16_t offX=0, uint16_t offY=0); 127 | uint16_t color565(uint8_t r, uint8_t g, uint8_t b); 128 | size_t writeText(const uint8_t *str, int16_t maxHeight = -1); 129 | 130 | inline void setTextColor(uint16_t color){_textcolor=color;} 131 | inline void setFont(const uint16_t* font){_font=font; 132 | #ifdef TIMES_NEW_ROMAN_H_ 133 | if((_font==Times_New_Roman15x14)|| 134 | (_font==Times_New_Roman21x17)|| 135 | (_font==Times_New_Roman27x21)|| 136 | (_font==Times_New_Roman34x27)|| 137 | (_font==Times_New_Roman38x31)|| 138 | (_font==Times_New_Roman43x35)|| 139 | (_font==Times_New_Roman56x46)|| 140 | (_font==Times_New_Roman66x53)){ 141 | 142 | _f_utf8=true; _f_cp1251=false; _f_cp1252=false; _f_cp1253=false;// font can handle UTF-8 143 | } 144 | else _f_utf8=false; 145 | #endif //TIMES_NEW_ROMAN_H_ 146 | #ifdef GARAMOND_H_ 147 | if((_font==Garamond15x18)|| 148 | (_font==Garamond17x21)|| 149 | (_font==Garamond19x24)|| 150 | (_font==Garamond27x33)|| 151 | (_font==Garamond34x42)|| 152 | (_font==Garamond44x54)|| 153 | (_font==Garamond88x108)){ 154 | _f_utf8=false; _f_cp1251=false; _f_cp1252=true; _f_cp1253=false;// font can handle CP1252 155 | } 156 | else _f_cp1252=false; 157 | #endif //GARAMOND_H_ 158 | #ifdef GARAMOND_CYRILLIC_H_ 159 | if((_font==Garamond18x18cyrillic)|| 160 | (_font==Garamond21x21cyrillic)|| 161 | (_font==Garamond23x24cyrillic)|| 162 | (_font==Garamond32x33cyrillic)|| 163 | (_font==Garamond41x42cyrillic)|| 164 | (_font==Garamond53x54cyrillic)|| 165 | (_font==Garamond107x108cyrillic)){ 166 | _f_utf8=false; _f_cp1251=true; _f_cp1252=false; _f_cp1253=false;// font can handle CP1251 167 | } 168 | else _f_cp1251=false; 169 | #endif //GARAMOND_CYRILLIC_H_ 170 | #ifdef GARAMOND_GREEK_H_ 171 | if((_font==Garamond15x13greek)|| 172 | (_font==Garamond17x16greek)|| 173 | (_font==Garamond19x17greek)|| 174 | (_font==Garamond27x25greek)|| 175 | (_font==Garamond35x31greek)|| 176 | (_font==Garamond44x41greek)|| 177 | (_font==Garamond85x80greek)){ 178 | _f_utf8=false; _f_cp1251=false; _f_cp1252=false; _f_cp1253=true;// font can handle CP1253 179 | } 180 | else _f_cp1253=false; 181 | #endif //GARAMOND_GREEK_H_ 182 | 183 | } 184 | inline void setTextSize(uint8_t size){ 185 | #ifdef TIMES_NEW_ROMAN_H_ 186 | if(size==1) _font=Times_New_Roman15x14; 187 | if(size==2) _font=Times_New_Roman21x17; 188 | if(size==3) _font=Times_New_Roman27x21; 189 | if(size==4) _font=Times_New_Roman34x27; 190 | if(size==5) _font=Times_New_Roman38x31; 191 | if(size==6) _font=Times_New_Roman43x35; 192 | if(size==7) _font=Times_New_Roman56x46; 193 | if(size==8) _font=Times_New_Roman66x53; 194 | #else 195 | if(size==1) _font=Garamond15x18; 196 | if(size==2) _font=Garamond17x21; 197 | if(size==3) _font=Garamond19x24; 198 | if(size==4) _font=Garamond27x33; 199 | if(size==5) _font=Garamond34x42; 200 | if(size==6) _font=Garamond44x54; 201 | if(size==7) _font=Garamond88x108; 202 | #endif 203 | } 204 | 205 | inline void setTextOrientation(uint8_t orientation=0){_textorientation=orientation;} //0 h other v 206 | // inline void setUTF8decoder(boolean UTF8){if(UTF8==true) _f_utf8=true; else _f_utf8=false;} // obsolete, will be set automatically 207 | int16_t height(void) const; 208 | int16_t width(void) const; 209 | uint8_t getRotation(void) const; 210 | 211 | 212 | private: 213 | SPIClass* spi_TFT = nullptr; 214 | SPISettings TFT_SPI; 215 | uint32_t _freq; 216 | uint16_t _height; 217 | uint16_t _width; 218 | uint8_t _rotation; 219 | uint16_t _curX=0; 220 | int16_t _curY=0; 221 | uint16_t _textcolor = TFT_BLACK; 222 | uint8_t _textorientation=0; 223 | boolean _f_utf8=false; 224 | boolean _f_cp1251=false; 225 | boolean _f_cp1252=false; 226 | boolean _f_cp1253=false; 227 | const uint16_t * _font = Times_New_Roman15x14; 228 | boolean _f_curPos=false; 229 | uint8_t TFT_DC = 21; /* Data or Command */ 230 | uint8_t TFT_CS = 22; /* SPI Chip select */ 231 | //uint8_t TFT_BL = 17; /* BackLight */ 232 | uint8_t TFT_SCK = 18; 233 | uint8_t TFT_MISO= 19; 234 | uint8_t TFT_MOSI= 23; 235 | uint8_t buf[1024]; 236 | 237 | inline int minimum(int a, int b){if(a < b) return a; else return b;} 238 | inline void TFT_DC_HIGH() {gpio_set_level((gpio_num_t)TFT_DC, 1);} 239 | inline void TFT_DC_LOW() {gpio_set_level((gpio_num_t)TFT_DC, 0);} 240 | inline void TFT_CS_HIGH() {gpio_set_level((gpio_num_t)TFT_CS, 1);} 241 | inline void TFT_CS_LOW() {gpio_set_level((gpio_num_t)TFT_CS, 0);} 242 | inline void _swap_int16_t(int16_t &a, int16_t &b) { int16_t t = a; a = b; b = t; } 243 | void init(); 244 | void writeCommand(uint16_t cmd); 245 | const uint8_t* UTF8toCp1251(const uint8_t* str); 246 | const uint8_t* UTF8toCp1252(const uint8_t* str); 247 | const uint8_t* UTF8toCp1253(const uint8_t* str); 248 | 249 | 250 | // Transaction API not used by GFX 251 | void setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h); 252 | void writePixel(uint16_t color); 253 | void writePixels(uint16_t * colors, uint32_t len); 254 | void writeColor(uint16_t color, uint32_t len); 255 | void pushColor(uint16_t color); 256 | 257 | // Required Non-Transaction 258 | void drawPixel(int16_t x, int16_t y, uint16_t color); 259 | 260 | // Transaction API 261 | void startWrite(void); 262 | void endWrite(void); 263 | void writePixel(int16_t x, int16_t y, uint16_t color); 264 | void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); 265 | void writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); 266 | void writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); 267 | 268 | void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, uint16_t color); 269 | void drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint16_t color); 270 | void startBitmap(uint16_t x, uint16_t y, uint16_t w, uint16_t h); 271 | void endBitmap(); 272 | void startJpeg(); 273 | void endJpeg(); 274 | void bmpSkipPixels(fs::File &file, uint8_t bitsPerPixel, size_t len); 275 | void bmpAddPixels(fs::File &file, uint8_t bitsPerPixel, size_t len); 276 | void drawBitmap(int16_t x, int16_t y, int16_t w, int16_t h, const uint16_t *pcolors); 277 | void renderJPEG(int xpos, int ypos, uint16_t maxWidth, uint16_t maxHeight); 278 | }; 279 | 280 | //----------------------------------------------------------------------------------------------------------------------- 281 | class JPEGDecoder { 282 | 283 | private: 284 | 285 | typedef enum { // Scan types 286 | PJPG_GRAYSCALE, 287 | PJPG_YH1V1, 288 | PJPG_YH2V1, 289 | PJPG_YH1V2, 290 | PJPG_YH2V2 291 | } pjpeg_scan_type_t; 292 | 293 | typedef enum 294 | { 295 | M_SOF0 = 0xC0, M_SOF1 = 0xC1, M_SOF2 = 0xC2, M_SOF3 = 0xC3, M_SOF5 = 0xC5, 296 | M_SOF6 = 0xC6, M_SOF7 = 0xC7, M_JPG = 0xC8, M_SOF9 = 0xC9, M_SOF10 = 0xCA, 297 | M_SOF11 = 0xCB, M_SOF13 = 0xCD, M_SOF14 = 0xCE, M_SOF15 = 0xCF, M_DHT = 0xC4, 298 | M_RST0 = 0xD0, M_RST1 = 0xD1, M_RST2 = 0xD2, M_RST3 = 0xD3, M_RST4 = 0xD4, 299 | M_RST5 = 0xD5, M_RST6 = 0xD6, M_RST7 = 0xD7, M_SOI = 0xD8, M_EOI = 0xD9, 300 | M_SOS = 0xDA, M_DQT = 0xDB, M_DNL = 0xDC, M_DRI = 0xDD, M_DHP = 0xDE, 301 | M_EXP = 0xDF, M_APP0 = 0xE0, M_APP15 = 0xEF, M_JPG0 = 0xF0, M_JPG13 = 0xFD, 302 | M_COM = 0xFE, M_TEM = 0x01, M_ERROR = 0x100,RST0 = 0xD0, M_DAC = 0xCC 303 | } JPEG_MARKER; 304 | 305 | typedef struct{ 306 | int m_width; // Image resolution 307 | int m_height; 308 | int m_comps; // Number of components (1 or 3) 309 | int m_MCUSPerRow; // Total number of minimum coded units (MCU's) per row/col. 310 | int m_MCUSPerCol; 311 | pjpeg_scan_type_t m_scanType; // Scan type 312 | int m_MCUWidth; // MCU width/height in pixels (each is either 8 or 16 depending on the scan type) 313 | int m_MCUHeight; 314 | uint8_t *m_pMCUBufR; 315 | uint8_t *m_pMCUBufG; 316 | uint8_t *m_pMCUBufB; 317 | } pjpeg_image_info_t; 318 | 319 | typedef struct HufftableT{ 320 | uint16_t mMinCode[16]; 321 | uint16_t mMaxCode[16]; 322 | uint8_t mValPtr[16]; 323 | }HuffTable; 324 | 325 | typedef unsigned char (*pjpeg_need_bytes_callback_t) 326 | (unsigned char* pBuf, unsigned char buf_size, unsigned char *pBytes_actually_read, void *pCallback_data); 327 | 328 | pjpeg_scan_type_t scanType, gScanType; 329 | pjpeg_image_info_t image_info; 330 | pjpeg_need_bytes_callback_t g_pNeedBytesCallback; 331 | HuffTable gHuffTab0, gHuffTab1, gHuffTab2, gHuffTab3; 332 | 333 | File g_pInFileSd; 334 | 335 | const uint8_t JPEG_ARRAY = 0; 336 | const uint8_t JPEG_FS_FILE=1; 337 | const uint8_t JPEG_SD_FILE=2; 338 | const uint16_t PJPG_MAX_WIDTH = 16384; 339 | const uint16_t PJPG_MAX_HEIGHT = 16384; 340 | const uint8_t PJPG_MAXCOMPSINSCAN = 3; 341 | const uint8_t PJPG_DCT_SCALE_BITS = 7; 342 | const uint8_t PJPG_WINOGRAD_QUANT_SCALE_BITS = 10; 343 | 344 | static const uint16_t PJPG_MAX_IN_BUF_SIZE = 256; 345 | 346 | const uint8_t 347 | PJPG_NO_MORE_BLOCKS = 1, PJPG_TOO_MANY_COMPONENTS = 11, 348 | PJPG_BAD_DHT_COUNTS = 2, PJPG_BAD_VARIABLE_MARKER = 12, 349 | PJPG_BAD_DHT_INDEX = 3, PJPG_W_EXTRA_BYTES_BEFORE_MARKER = 16, 350 | PJPG_BAD_DHT_MARKER = 4, PJPG_NO_ARITHMITIC_SUPPORT = 17, 351 | PJPG_BAD_DQT_MARKER = 5, PJPG_UNEXPECTED_MARKER = 18, 352 | PJPG_BAD_DQT_TABLE = 6, PJPG_UNSUPPORTED_MARKER = 20, 353 | PJPG_BAD_PRECISION = 7, PJPG_UNDEFINED_QUANT_TABLE = 23, 354 | PJPG_BAD_HEIGHT = 8, PJPG_UNDEFINED_HUFF_TABLE = 24, 355 | PJPG_BAD_WIDTH = 9, PJPG_UNSUPPORTED_COLORSPACE = 26, 356 | PJPG_BAD_SOF_LENGTH = 10, PJPG_UNSUPPORTED_SAMP_FACTORS = 27, 357 | PJPG_BAD_DRI_LENGTH = 13, PJPG_BAD_RESTART_MARKER = 29, 358 | PJPG_BAD_SOS_LENGTH = 14, PJPG_BAD_SOS_SPECTRAL = 31, 359 | PJPG_BAD_SOS_COMP_ID = 15, PJPG_BAD_SOS_SUCCESSIVE = 32, 360 | PJPG_NOT_JPEG = 19, PJPG_STREAM_READ_ERROR = 33, 361 | PJPG_BAD_DQT_LENGTH = 21, PJPG_UNSUPPORTED_COMP_IDENT = 35, 362 | PJPG_TOO_MANY_BLOCKS = 22, PJPG_UNSUPPORTED_QUANT_TABLE = 36, 363 | PJPG_NOT_SINGLE_SCAN = 25, 364 | PJPG_DECODE_ERROR = 28, 365 | PJPG_ASSERTION_ERROR = 30, 366 | PJPG_NOTENOUGHMEM = 34, 367 | PJPG_UNSUPPORTED_MODE= 37; // picojpeg doesn't support progressive JPEG's 368 | 369 | const uint8_t ZAG[64] = { 370 | 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 371 | 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 372 | 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 373 | 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63}; 374 | 375 | const uint8_t gWinogradQuant[64] = { 376 | 128, 178, 178, 167, 246, 167, 151, 232, 232, 151, 128, 209, 219, 209, 128, 101, 377 | 178, 197, 197, 178, 101, 69, 139, 167, 177, 167, 139, 69, 35, 96, 131, 151, 378 | 151, 131, 96, 35, 49, 91, 118, 128, 118, 91, 49, 46, 81, 101, 101, 81, 379 | 46, 42, 69, 79, 69, 42, 35, 54, 54, 35, 28, 37, 28, 19, 19, 10}; 380 | 381 | uint8_t status=0; 382 | uint8_t jpg_source=0; 383 | uint8_t gHuffVal0[16], gHuffVal1[16]; 384 | uint8_t gHuffVal2[256], gHuffVal3[256]; 385 | uint8_t gCompsInScan; 386 | uint8_t gValidHuffTables; 387 | uint8_t gValidQuantTables; 388 | uint8_t gTemFlag; 389 | uint8_t gInBuf[PJPG_MAX_IN_BUF_SIZE]; 390 | uint8_t gInBufOfs; 391 | uint8_t gInBufLeft; 392 | uint8_t gBitsLeft; 393 | uint8_t gCompsInFrame; 394 | uint8_t gMaxBlocksPerMCU; 395 | uint8_t gMaxMCUXSize; 396 | uint8_t gMaxMCUYSize; 397 | uint8_t gCompList [3]; 398 | uint8_t gCompDCTab[3]; // 0,1 399 | uint8_t gCompACTab[3]; // 0,1 400 | uint8_t gCompIdent[3]; 401 | uint8_t gCompHSamp[3]; 402 | uint8_t gCompVSamp[3]; 403 | uint8_t gCompQuant[3]; 404 | uint8_t gCallbackStatus; 405 | uint8_t gReduce; 406 | uint8_t gMCUOrg[6]; 407 | uint8_t gMCUBufR[256]; 408 | uint8_t gMCUBufG[256]; 409 | uint8_t gMCUBufB[256]; 410 | int16_t is_available=0; 411 | int16_t mcu_x; 412 | int16_t mcu_y; 413 | int16_t gCoeffBuf[8*8]; 414 | int16_t gQuant0[8*8]; 415 | int16_t gQuant1[8*8]; 416 | int16_t gLastDC[3]; 417 | uint16_t g_nInFileSize=0; 418 | uint16_t g_nInFileOfs=0; 419 | uint16_t row_pitch=0; 420 | uint16_t decoded_width=0, decoded_height=0; 421 | uint16_t row_blocks_per_mcu=0, col_blocks_per_mcu=0; 422 | uint16_t gBitBuf; 423 | uint16_t gImageXSize, gImageYSize; 424 | uint16_t gRestartInterval; 425 | uint16_t gNextRestartNum; 426 | uint16_t gRestartsLeft; 427 | uint8_t* jpg_data; 428 | uint16_t gMaxMCUSPerRow; 429 | uint16_t gMaxMCUSPerCol; 430 | uint16_t gNumMCUSRemaining; 431 | 432 | void *g_pCallback_data; 433 | 434 | JPEGDecoder *thisPtr; 435 | int comps=0; 436 | int MCUSPerRow=0; 437 | int MCUSPerCol=0; 438 | 439 | public: 440 | uint16_t *pImage=0; 441 | int width=0; 442 | int height=0; 443 | int MCUWidth=0; 444 | int MCUHeight=0; 445 | int MCUx=0; 446 | int MCUy=0; 447 | 448 | JPEGDecoder(); 449 | ~JPEGDecoder(); 450 | int read(void); 451 | int decodeSdFile (File g_pInFile); 452 | void abort(void); 453 | // Initializes the decompressor. Returns 0 on success, or one of the above error codes on failure. pNeed_bytes_callback will be called 454 | // to fill the decompressor's internal input buffer. If reduce is 1, only the first pixel of each block will be decoded. This mode is 455 | // much faster because it skips the AC dequantization, IDCT and chroma upsampling of every image pixel.Not thread safe. 456 | uint8_t pjpeg_decode_init(pjpeg_image_info_t *pInfo, pjpeg_need_bytes_callback_t pNeed_bytes_callback, void *pCallback_data, unsigned char reduce); 457 | // Decompresses the file's next MCU. Returns 0 on success, PJPG_NO_MORE_BLOCKS if no more blocks are available, or an error code. 458 | // Must be called a total of m_MCUSPerRow*m_MCUSPerCol times to completely decompress the image. Not thread safe. 459 | uint8_t pjpeg_decode_mcu(); 460 | 461 | private: 462 | int available(void); 463 | int readSwappedBytes(void); 464 | int decodeArray(const uint8_t array[], uint32_t array_size); 465 | int decode_mcu(void); 466 | int decodeCommon(void); 467 | static uint8_t pjpeg_callback(unsigned char* pBuf, unsigned char buf_size, unsigned char *pBytes_actually_read, void *pCallback_data); 468 | uint8_t pjpeg_need_bytes_callback(unsigned char* pBuf, unsigned char buf_size, unsigned char *pBytes_actually_read, void *pCallback_data); 469 | 470 | int16_t replicateSignBit16(int8_t n); 471 | uint16_t getBits(uint8_t numBits, uint8_t FFCheck); 472 | uint16_t getExtendTest(uint8_t i); 473 | int16_t getExtendOffset(uint8_t i); 474 | HuffTable* getHuffTable(uint8_t index); 475 | uint8_t* getHuffVal(uint8_t index); 476 | uint8_t readDHTMarker(void); 477 | uint8_t readDQTMarker(void); 478 | uint8_t locateSOIMarker(void); 479 | uint8_t locateSOFMarker(void); 480 | uint8_t locateSOSMarker(uint8_t* pFoundEOI); 481 | uint8_t init(void); 482 | uint8_t processRestart(void); 483 | uint8_t findEOI(void); 484 | uint8_t checkHuffTables(void); 485 | uint8_t checkQuantTables(void); 486 | uint8_t initScan(void); 487 | uint8_t initFrame(void); 488 | uint8_t readSOFMarker(void); 489 | uint8_t skipVariableMarker(void); 490 | uint8_t readDRIMarker(void); 491 | uint8_t readSOSMarker(void); 492 | uint8_t nextMarker(void); 493 | uint8_t processMarkers(uint8_t* pMarker); 494 | uint8_t huffDecode(const HuffTable* pHuffTable, const uint8_t* pHuffVal); 495 | uint8_t decodeNextMCU(void); 496 | void fillInBuf(void); 497 | void fixInBuffer(void); 498 | void idctRows(void); 499 | void idctCols(void); 500 | void createWinogradQuant(int16_t* pQuant); 501 | void huffCreate(const uint8_t* pBits, HuffTable* pHuffTable); 502 | void upsampleCb(uint8_t srcOfs, uint8_t dstOfs); 503 | void upsampleCbH(uint8_t srcOfs, uint8_t dstOfs); 504 | void upsampleCbV(uint8_t srcOfs, uint8_t dstOfs); 505 | void upsampleCr(uint8_t srcOfs, uint8_t dstOfs); 506 | void upsampleCrH(uint8_t srcOfs, uint8_t dstOfs); 507 | void upsampleCrV(uint8_t srcOfs, uint8_t dstOfs); 508 | void copyY(uint8_t dstOfs); 509 | void convertCb(uint8_t dstOfs); 510 | void convertCr(uint8_t dstOfs); 511 | void transformBlock(uint8_t mcuBlock); 512 | void transformBlockReduce(uint8_t mcuBlock); 513 | 514 | inline int jpg_min(int a, int b){ 515 | if(a < b) return a; else return b; 516 | } 517 | inline int16_t arithmeticRightShiftN16(int16_t x, int8_t n){ 518 | int16_t r = (uint16_t)x >> (uint8_t)n; 519 | if (x < 0) r |= replicateSignBit16(n); 520 | return r; 521 | } 522 | inline int32_t arithmeticRightShift8L(long x){ 523 | int32_t r = (unsigned long)x >> 8U; 524 | if (x < 0) r |= ~(~(unsigned long)0U >> 8U); 525 | return r; 526 | } 527 | inline uint8_t getOctet(uint8_t FFCheck){ 528 | uint8_t c = getChar(); 529 | if ((FFCheck) && (c == 0xFF)){ uint8_t n = getChar(); 530 | if (n){ stuffChar(n); stuffChar(0xFF);} 531 | } return c; 532 | } 533 | inline uint16_t getBits1(uint8_t numBits){ 534 | return getBits(numBits, 0); 535 | } 536 | inline uint16_t getBits2(uint8_t numBits){ 537 | return getBits(numBits, 1); 538 | } 539 | inline uint8_t getChar(void){ 540 | if (!gInBufLeft){ fillInBuf(); if (!gInBufLeft){ 541 | gTemFlag = ~gTemFlag; return gTemFlag ? 0xFF : 0xD9;}} 542 | gInBufLeft--; return gInBuf[gInBufOfs++]; 543 | } 544 | inline void stuffChar(uint8_t i){ 545 | gInBufOfs--; gInBuf[gInBufOfs] = i; gInBufLeft++; 546 | } 547 | inline uint8_t getBit(void){ 548 | uint8_t ret = 0; 549 | if (gBitBuf & 0x8000) ret = 1; 550 | if (!gBitsLeft){gBitBuf |= getOctet(1); gBitsLeft += 8;} 551 | gBitsLeft--; gBitBuf <<= 1; 552 | return ret; 553 | } 554 | inline int16_t huffExtend(uint16_t x, uint8_t s){ 555 | return ((x < getExtendTest(s)) ? ((int16_t)x + getExtendOffset(s)) : (int16_t)x); 556 | } 557 | inline uint16_t getMaxHuffCodes(uint8_t index){ 558 | return (index < 2) ? 12 : 255; 559 | } 560 | inline int16_t imul_b1_b3(int16_t w){ // 1/cos(4*pi/16)[362, 256+106] 561 | long x = (w * 362L); x += 128L; return (int16_t)(arithmeticRightShift8L(x)); 562 | } 563 | inline int16_t imul_b2(int16_t w){ // 1/cos(6*pi/16)[669, 256+256+157] 564 | long x = (w * 669L); x += 128L; return (int16_t)(arithmeticRightShift8L(x)); 565 | } 566 | inline int16_t imul_b4(int16_t w){ // 1/cos(2*pi/16)[277, 256+21] 567 | long x = (w * 277L); x += 128L; return (int16_t)(arithmeticRightShift8L(x)); 568 | } 569 | inline int16_t imul_b5(int16_t w){ // 1/(cos(2*pi/16) + cos(6*pi/16))[196, 196] 570 | long x = (w * 196L); x += 128L; return (int16_t)(arithmeticRightShift8L(x)); 571 | } 572 | inline uint8_t clamp(int16_t s){ 573 | if ((uint16_t)s > 255U){ if (s < 0) return 0; else if (s > 255) return 255;} 574 | return (uint8_t)s; 575 | } 576 | inline uint8_t addAndClamp(uint8_t a, int16_t b){ 577 | b = a + b; if ((uint16_t)b > 255U){if (b < 0) return 0;else if (b > 255) return 255;} 578 | return (uint8_t)b; 579 | } 580 | inline uint8_t subAndClamp(uint8_t a, int16_t b){ 581 | b = a - b; if ((uint16_t)b > 255U) {if (b < 0) return 0; else if (b > 255) return 255;} 582 | return (uint8_t)b; 583 | } 584 | inline int16_t PJPG_DESCALE(int16_t x){ 585 | return arithmeticRightShiftN16(x + (1U << (PJPG_DCT_SCALE_BITS - 1)), PJPG_DCT_SCALE_BITS); 586 | } 587 | }; 588 | 589 | //----------------------------------------------------------------------------------------------------------------------- 590 | class TP { 591 | 592 | //Kalibrierung 593 | //x,y | Ux,Uy 0 ,0 | 1922,1930 594 | //x,y | Ux,Uy 320,0 | 140,1930 595 | //x,y | Ux,Uy 0 ,480 | 1922,125 596 | //x,y | Ux,Uy 320,480 | 140,125 597 | //daraus ergib sich für x: (1922-140)/320 = 5,5687mV pro Pixel 598 | // und für y: (1930-125)/480 = 3,7604mV pro Pixel 599 | 600 | public: 601 | 602 | TP(uint8_t TP_CS, uint8_t TP_IRQ); 603 | void loop(); 604 | void setRotation(uint8_t m); 605 | private: 606 | SPISettings TP_SPI; 607 | uint8_t TP_CS, TP_IRQ; 608 | uint16_t x=0, y=0; 609 | uint8_t _rotation; 610 | boolean f_loop=false; 611 | //const uint8_t TP_Dummy=0x80; //nur Startbit für XPT2046 612 | float xFaktor; 613 | float yFaktor; 614 | const uint16_t Xmax=1922; 615 | const uint16_t Xmin=140; 616 | const uint16_t Ymax=1930; 617 | const uint16_t Ymin=125; 618 | 619 | protected: 620 | uint16_t TP_Send(uint8_t set_val); 621 | bool read_TP(uint16_t& x, uint16_t& y); 622 | }; 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | --------------------------------------------------------------------------------