Png support start
This commit is contained in:
parent
615f3b939c
commit
cb5c74138b
|
@ -109,18 +109,26 @@ char *months[] = {
|
|||
|
||||
// Out UI elements data
|
||||
textElement elements[] = {
|
||||
{50, 130, &Roboto_Light_160, currencyAbbr, 0}, {390, 80, &Roboto_Light_40, date, 0},
|
||||
{190, 185, &Roboto_Light_40, fromToDate, 0}, {570, 140, &Roboto_Light_40, "Current price:", 0},
|
||||
{790, 190, &Roboto_Light_40, current, 1}, {630, 275, &Roboto_Light_40, "Minimum:", 0},
|
||||
{790, 320, &Roboto_Light_40, minimum, 1}, {625, 420, &Roboto_Light_40, "Maximum:", 0},
|
||||
{50, 130, &Roboto_Light_160, currencyAbbr, 0},
|
||||
{390, 80, &Roboto_Light_40, date, 0},
|
||||
{190, 185, &Roboto_Light_40, fromToDate, 0},
|
||||
{570, 140, &Roboto_Light_40, "Current price:", 0},
|
||||
{790, 190, &Roboto_Light_40, current, 1},
|
||||
{630, 275, &Roboto_Light_40, "Minimum:", 0},
|
||||
{790, 320, &Roboto_Light_40, minimum, 1},
|
||||
{625, 420, &Roboto_Light_40, "Maximum:", 0},
|
||||
{790, 466, &Roboto_Light_40, maximum, 1},
|
||||
|
||||
{18, 570, &Roboto_Light_36, dates, 0}, {122, 570, &Roboto_Light_36, dates + 8, 0},
|
||||
{227, 570, &Roboto_Light_36, dates + 16, 0}, {342, 570, &Roboto_Light_36, dates + 24, 0},
|
||||
{18, 570, &Roboto_Light_36, dates, 0},
|
||||
{122, 570, &Roboto_Light_36, dates + 8, 0},
|
||||
{227, 570, &Roboto_Light_36, dates + 16, 0},
|
||||
{342, 570, &Roboto_Light_36, dates + 24, 0},
|
||||
{466, 570, &Roboto_Light_36, dates + 32, 0},
|
||||
|
||||
{450, 240, &Roboto_Light_36, prices, 0}, {450, 322, &Roboto_Light_36, prices + 16, 0},
|
||||
{450, 401, &Roboto_Light_36, prices + 32, 0}, {450, 483, &Roboto_Light_36, prices + 48, 0},
|
||||
{450, 240, &Roboto_Light_36, prices, 0},
|
||||
{450, 322, &Roboto_Light_36, prices + 16, 0},
|
||||
{450, 401, &Roboto_Light_36, prices + 32, 0},
|
||||
{450, 483, &Roboto_Light_36, prices + 48, 0},
|
||||
};
|
||||
|
||||
// Our functions declared below setup and loop
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<mxfile host="app.diagrams.net" modified="2020-09-14T10:51:16.904Z" agent="5.0 (Macintosh)" etag="wPpNYQGGCjD756OMAaPR" version="13.6.10" type="device"><diagram id="oV9ivTpqu0Y3tAoBFJ3n" name="Page-1">7VxLc6M4EP41Pk4KEM9jHk4ys5spV3lrZ/a0xRgZ2GDECjk2++tXgHgJh8HBjkyGHFKo1RJS96dutVp4Bm43+wdsR94TcmAwUyRnPwN3M0WxVJ3+TwlJTtBMkBNc7Ds5Sa4IS/8/yIgSo259B8YNRoJQQPyoSVyhMIQr0qDZGKNdk22NguZbI9uFLcJyZQdt6jffIV5ONRWjoj9C3/WKN8u6ldds7IKZzST2bAftaiQwn4FbjBDJnzb7Wxiksivkkre7f6W2HBiGIenTIPQ2853+p6w+atZquX1Wf3uUPjHtxCQpJgwdOn9WRJh4yEWhHcwr6g1G29CBaa8SLVU8vyMUUaJMif9AQhKmTHtLECV5ZBOwWrj3yfe0+ZWmseJfWdEqind71ntWSGqFBcT+BhKIC1pIcJJ3phbFvDO5KFadZaWkXuK7y8WRyuBVKTNSjLZ4BTtEW6DVxi4kHXxKiQW6hiCio8EJbYdhYBP/pTkOm6HZLfkqhdMHpvMj9G9M+hetfyBS/6zfFzvYsjfNFD2gw71x/Bf66KaPn8PniA4GXnlFJX1Xrb6rySqKXmvEAy8IqFFPAUYtZZQSVwHa0sHf7DyfwGVkZ+LeUTfThBObAsQE7ru11pYyawA0NW/CvJRcGO1dZfNLmlez96Z0LsUo08o818pUeq5Mc+DKzJpeY2wnNYYI+SGJaz0vUkINisWOKSmgybl1jl82zC5++pCPoIJiOZUB6AQTOkWj0xoDOhVLBDrVCZ2i0SlLY4AnKPz3G/k1qZtflc0u/vPAH0ybeuHwB/oo4G8dCX+enz9y4OGvmF3854E/U1F3SLNMYgI3RwQ0rME4whmFM1OKJjqcKQ/BJpt0cpsE+rpkY6BNGoYAeUKAcARoIrySJnFeSe/2GrqqdPGfadPUx2tkKQ5/FY/ABajqpbkAs4+E53F0hFNOuWse+QfmeS9YQTLg9lKWaAVZfRT0tDpGQSn3WBVUrpiLUVCRE+3W0FdIdgg/3wY+negRumq2G6vWgMFF6EC01ooYaNr4nH7jk29o+iRZxWbZtD4Ld5kulrjXiq2BSf93m94guFmjkHyKMzhcUwYr2ld1fB8p75s7YcP8mYHI39FtNy5qRgsUJHSNcSH+oWmMyiBqVjO1Kd6NKYLPJ82GPSxKH8IcGqMwh4olFgBAnhAwOD8zCAFgyh+LhwAQkqLTuIMJ/cgcAsd/ptMgbcKneHyqY8CnZorAp+CYUjIa+FSHwDPHeoVPSxsNQIcmeYcFlUafoPLzxnb7Xdz8efCknSICO9hJPsp3DynPPKGbp0WPaPKSZ/BlMX84rJasevG1q/bOJx7EHy2eVo2Li6d75a6uHXuNtz75++H++8WfMTUGe+YldL5ZuOt9ytaQ9gcc/qiWL7+9E58fAL2yOn98iaB7WL5X9O+iZMzfktSF556B2pIx9V3ZZpC+wPND96IE2IpBxAtQ7wPSpXNvkx5WrcLt1SiNSOvasHgj0k7eO3Dth3k+6YJEJ/P2V7TkistGDWTzAgud6/Rb4kxSdhz7q9nZDm5mpwtkwSufiNRkrR0QdUE77pyl/emH0tS0zKswHz9r1XHCwl/EaHWUB+qtjk512KIe2l+/GSGDkw8fBiAyp9fym/+jASJxHUnvDJD27mIAQCy9ARDj1wUIf4exdLPHAgRwACmd0HsB5NDu6c0AGZq/nvDRMiDcfk7WT4UPWqx+4SNnr34mBcz/Bw==</diagram></mxfile>
|
|
@ -3,9 +3,9 @@
|
|||
#include "../libs/TJpeg/TJpg_Decoder.h"
|
||||
#include "pgmspace.h"
|
||||
|
||||
#define RED(a) ((((a)&0xf800) >> 11) << 3)
|
||||
#define RED(a) ((((a)&0xf800) >> 11) << 3)
|
||||
#define GREEN(a) ((((a)&0x07e0) >> 5) << 2)
|
||||
#define BLUE(a) (((a)&0x001f) << 3)
|
||||
#define BLUE(a) (((a)&0x001f) << 3)
|
||||
|
||||
Image *_imagePtrJpeg = nullptr;
|
||||
|
||||
|
@ -27,6 +27,8 @@ bool Image::drawImage(const char *path, int x, int y, bool dither, bool invert)
|
|||
return drawBitmapFromWeb(path, x, y, dither, invert);
|
||||
if (strstr(path, ".jpg") != NULL || strstr(path, ".jpeg") != NULL)
|
||||
return drawJpegFromWeb(path, x, y, dither, invert);
|
||||
if (strstr(path, ".png") != NULL)
|
||||
return drawJpegFromWeb(path, x, y, dither, invert);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -34,6 +36,8 @@ bool Image::drawImage(const char *path, int x, int y, bool dither, bool invert)
|
|||
return drawBitmapFromSd(path, x, y, dither, invert);
|
||||
if (strstr(path, ".jpg") != NULL || strstr(path, ".jpeg") != NULL)
|
||||
return drawJpegFromSd(path, x, y, dither, invert);
|
||||
if (strstr(path, ".png") != NULL)
|
||||
return drawJpegFromSd(path, x, y, dither, invert);
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
class Image : virtual public NetworkClient, virtual public Adafruit_GFX
|
||||
{
|
||||
public:
|
||||
public:
|
||||
Image(int16_t w, int16_t h);
|
||||
|
||||
virtual void drawPixel(int16_t x, int16_t y, uint16_t color) = 0;
|
||||
|
@ -47,8 +47,13 @@ class Image : virtual public NetworkClient, virtual public Adafruit_GFX
|
|||
bool drawJpegFromWeb(const char *url, int x, int y, bool dither = 0, bool invert = 0);
|
||||
bool drawJpegFromWeb(WiFiClient *s, int x, int y, int32_t len, bool dither = 0, bool invert = 0);
|
||||
|
||||
bool drawPngFromSd(const char *fileName, int x, int y, bool dither = 0, bool invert = 0);
|
||||
bool drawPngFromSd(SdFile *p, int x, int y, bool dither = 0, bool invert = 0);
|
||||
|
||||
private:
|
||||
bool drawPngFromWeb(const char *url, int x, int y, bool dither = 0, bool invert = 0);
|
||||
bool drawPngFromWeb(WiFiClient *s, int x, int y, int32_t len, bool dither = 0, bool invert = 0);
|
||||
|
||||
private:
|
||||
virtual void startWrite(void) = 0;
|
||||
virtual void writePixel(int16_t x, int16_t y, uint16_t color) = 0;
|
||||
virtual void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) = 0;
|
||||
|
@ -92,5 +97,4 @@ class Image : virtual public NetworkClient, virtual public Adafruit_GFX
|
|||
// -------------------------------------------
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -1 +1,23 @@
|
|||
#include "Image.h"
|
||||
|
||||
#include "../libs/pngle/pngle.h"
|
||||
|
||||
bool Image::drawPngFromSd(const char *fileName, int x, int y, bool dither, bool invert)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Image::drawPngFromSd(SdFile *p, int x, int y, bool dither, bool invert)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Image::drawPngFromWeb(const char *url, int x, int y, bool dither, bool invert)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Image::drawPngFromWeb(WiFiClient *s, int x, int y, int32_t len, bool dither, bool invert)
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2019 kikuchan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,112 @@
|
|||
Pngle
|
||||
=====
|
||||
|
||||
This is a stream based portable **PNG** **L**oader for **E**mbedding, **Pngle**.
|
||||
|
||||
## Background
|
||||
|
||||
Basically PNG is a memory consuming format from an embedded system perspective, especially on decoding, it mandatory require 32KiB working memory for a sliding window of Deflate, and a scanline buffer (its size depend on image width & format) to reconstruct the image, at least.
|
||||
Unfortunately, due to the memory requirements, we could say traditional Arduino boards, that uses ATmega328 or ATmega32U4 for example, lack of ability to decode standard PNG images.
|
||||
|
||||
Today, we have variety of SoC boards for embedded systems or Arduino that have enough memory to decode PNG images, but we need to be concerned about memory consumption sometimes.
|
||||
While there are many other PNG decoders, some of them always require the full size of frame-buffer, some of them don't but do require using complicated APIs instead, and some of them are hard to use in Arduino because of deep dependency.
|
||||
This is why I reinvent the wheel for my own.
|
||||
|
||||
## Features & Restrictions
|
||||
|
||||
- **All standard types of PNG files are supported**
|
||||
- Interlaced files are also supported
|
||||
- Tested with PngSuite
|
||||
- Easy to use
|
||||
- **Simple API**
|
||||
- Feed PNG binary data with `pngle_feed` API (in the same way as `write` syscall)
|
||||
- A super simple single callback interface is used to draw an image
|
||||
- You need to render pixel-by-pixel (it's neither line-by-line nor frame-by-frame)
|
||||
- Pixel values are always normalized to 8bits/ch x 4ch for convenience (even if they are indexed, grayscaled, ... or 16bits/ch)
|
||||
- Drawing x and y values are passed via the interface, so...
|
||||
- You can roughly resize an image on-the-fly by adjusting drawing x and y values
|
||||
- You can draw an interlaced image as soon as possible (x and y values occur in Adam7 sequence)
|
||||
- Easy to embed
|
||||
- **Reasonably small memory footprint** on runtime
|
||||
- Theoretical minimum scanline buffer (depends on width & format) + decompression working memory for Deflate (~43KiB) + α
|
||||
- **No frame-buffer required**
|
||||
- It simply renders pixel-by-pixel instead, mentioned above
|
||||
- If you prefer off-screen canvas, you can allocate the canvas by yourself and draw pixels to it
|
||||
- **Less dependency**
|
||||
- It only requires `miniz` (don't worry, battery included!)
|
||||
- **Portable**
|
||||
- Written in C99 with `stdint.h` (but not for `miniz` yet...)
|
||||
- MIT License
|
||||
- **Transparency support**
|
||||
- A value of transparency is always available as 4th channel of pixel
|
||||
- tRNS chunk is also supported
|
||||
- If you need full featured alpha-blending, you can implement it easily (as long as you could know its background source color value, and how to blend them)
|
||||
- **Gamma correction support** (gAMA chunk only)
|
||||
- You can activate the feature by calling `pngle_set_display_gamma` API with display gamma value (Typically 2.2)
|
||||
- It require additional memory (depends on image depth, 64KiB at most), math library (libm), and floating point arithmetic to generate gamma lookup table
|
||||
- You can remove the feature by defining `PNGLE_NO_GAMMA_CORRECTION` in case of emergency
|
||||
|
||||
## Usage & How it works
|
||||
|
||||
1. Allocate Pngle object by calling `pngle_new()`
|
||||
2. Setup draw callback by calling `pngle_set_draw_callback()`
|
||||
3. Feed PNG binary data by calling `pngle_feed()` until exhausted
|
||||
4. During the feeding, callback function `on_draw()` (for example) is called repeatedly
|
||||
5. In the `on_draw()` function, put the pixel on a screen (or wherever you want)
|
||||
6. Finally, you'll get an image
|
||||
|
||||
## Examples
|
||||
|
||||
### Generic C
|
||||
```c
|
||||
void on_draw(pngle_t *pngle, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint8_t rgba[4])
|
||||
{
|
||||
uint8_t r = rgba[0]; // 0 - 255
|
||||
uint8_t g = rgba[1]; // 0 - 255
|
||||
uint8_t b = rgba[2]; // 0 - 255
|
||||
uint8_t a = rgba[3]; // 0: fully transparent, 255: fully opaque
|
||||
|
||||
if (a) printf("put pixel at (%d, %d) with color #%02x%02x%02x\n", x, y, r, g, b);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
pngle_t *pngle = pngle_new();
|
||||
|
||||
pngle_set_draw_callback(pngle, on_draw);
|
||||
|
||||
// Feed data to pngle
|
||||
char buf[1024];
|
||||
int remain = 0;
|
||||
int len;
|
||||
while ((len = read(STDIN_FILENO, buf + remain, sizeof(buf) - remain)) > 0) {
|
||||
int fed = pngle_feed(pngle, buf, remain + len);
|
||||
if (fed < 0) errx(1, "%s", pngle_error(pngle));
|
||||
|
||||
remain = remain + len - fed;
|
||||
if (remain > 0) memmove(buf, buf + fed, remain);
|
||||
}
|
||||
|
||||
pngle_destroy(pngle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Arduino (for M5Stack)
|
||||
|
||||
See [examples/m5stack-png.ino](examples/m5stack-png.ino)
|
||||
|
||||
## API
|
||||
|
||||
See [pngle.h](pngle.h)
|
||||
|
||||
|
||||
## Author
|
||||
|
||||
kikuchan / @kikuchan98
|
||||
|
||||
|
||||
## License
|
||||
|
||||
MIT License
|
|
@ -0,0 +1,114 @@
|
|||
#include <M5Stack.h>
|
||||
#include <HTTPClient.h>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "pngle.h"
|
||||
|
||||
#define WIFI_SSID "XXXXXXXX"
|
||||
#define WIFI_PASS "XXXXXXXX"
|
||||
|
||||
void cls()
|
||||
{
|
||||
M5.Lcd.fillScreen(TFT_BLACK);
|
||||
|
||||
M5.Lcd.setCursor(0, 0);
|
||||
M5.Lcd.setTextColor(WHITE);
|
||||
M5.Lcd.setTextSize(1);
|
||||
}
|
||||
|
||||
// ===================================================
|
||||
// pngle example for m5stack
|
||||
// ===================================================
|
||||
|
||||
double g_scale = 1.0;
|
||||
void pngle_on_draw(pngle_t *pngle, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint8_t rgba[4])
|
||||
{
|
||||
uint16_t color = (rgba[0] << 8 & 0xf800) | (rgba[1] << 3 & 0x07e0) | (rgba[2] >> 3 & 0x001f);
|
||||
|
||||
if (rgba[3]) {
|
||||
x = ceil(x * g_scale);
|
||||
y = ceil(y * g_scale);
|
||||
w = ceil(w * g_scale);
|
||||
h = ceil(h * g_scale);
|
||||
M5.Lcd.fillRect(x, y, w, h, color);
|
||||
}
|
||||
}
|
||||
|
||||
void load_png(const char *url, double scale = 1.0)
|
||||
{
|
||||
HTTPClient http;
|
||||
|
||||
http.begin(url);
|
||||
|
||||
int httpCode = http.GET();
|
||||
if (httpCode != HTTP_CODE_OK) {
|
||||
M5.Lcd.printf("HTTP ERROR: %d\n", httpCode);
|
||||
http.end();
|
||||
return ;
|
||||
}
|
||||
|
||||
WiFiClient *stream = http.getStreamPtr();
|
||||
|
||||
pngle_t *pngle = pngle_new();
|
||||
pngle_set_draw_callback(pngle, pngle_on_draw);
|
||||
g_scale = scale; // XXX:
|
||||
|
||||
uint8_t buf[2048];
|
||||
int remain = 0;
|
||||
while (http.connected()) {
|
||||
size_t size = stream->available();
|
||||
if (!size) { delay(1); continue; }
|
||||
|
||||
if (size > sizeof(buf) - remain) {
|
||||
size = sizeof(buf) - remain;
|
||||
}
|
||||
|
||||
int len = stream->readBytes(buf + remain, size);
|
||||
if (len > 0) {
|
||||
int fed = pngle_feed(pngle, buf, remain + len);
|
||||
if (fed < 0) {
|
||||
cls();
|
||||
M5.Lcd.printf("ERROR: %s\n", pngle_error(pngle));
|
||||
break;
|
||||
}
|
||||
|
||||
remain = remain + len - fed;
|
||||
if (remain > 0) memmove(buf, buf + fed, remain);
|
||||
} else {
|
||||
delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
pngle_destroy(pngle);
|
||||
|
||||
http.end();
|
||||
}
|
||||
// ===================================================
|
||||
|
||||
void setup()
|
||||
{
|
||||
M5.begin();
|
||||
|
||||
M5.Lcd.printf("Welcome.\n");
|
||||
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(100);
|
||||
}
|
||||
|
||||
M5.Lcd.printf("WiFi connected.\n");
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
M5.update();
|
||||
|
||||
if (M5.BtnA.wasReleased()) {
|
||||
load_png("https://raw.githubusercontent.com/kikuchan/pngle/master/tests/pngsuite/PngSuite.png");
|
||||
} else if (M5.BtnB.wasReleased()) {
|
||||
load_png("https://raw.githubusercontent.com/kikuchan/pngle/master/tests/pngsuite/tbrn2c08.png", 7);
|
||||
} else if (M5.BtnC.wasReleased()) {
|
||||
load_png("https://avatars3.githubusercontent.com/u/17420673?s=240");
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,2 @@
|
|||
#define MINIZ_HEADER_FILE_ONLY
|
||||
#include "miniz.c"
|
|
@ -0,0 +1,874 @@
|
|||
/*-
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2019 kikuchan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "miniz.h"
|
||||
#include "pngle.h"
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifdef PNGLE_DEBUG
|
||||
#define debug_printf(...) fprintf(stderr, __VA_ARGS__)
|
||||
#else
|
||||
#define debug_printf(...) ((void)0)
|
||||
#endif
|
||||
|
||||
#define PNGLE_ERROR(s) (pngle->error = (s), pngle->state = PNGLE_STATE_ERROR, -1)
|
||||
#define PNGLE_CALLOC(a, b, name) (debug_printf("[pngle] Allocating %zu bytes for %s\n", (size_t)(a) * (size_t)(b), (name)), calloc((size_t)(a), (size_t)(b)))
|
||||
|
||||
#define PNGLE_UNUSED(x) (void)(x)
|
||||
|
||||
typedef enum {
|
||||
PNGLE_STATE_ERROR = -2,
|
||||
PNGLE_STATE_EOF = -1,
|
||||
PNGLE_STATE_INITIAL = 0,
|
||||
|
||||
PNGLE_STATE_FIND_CHUNK_HEADER,
|
||||
PNGLE_STATE_HANDLE_CHUNK,
|
||||
PNGLE_STATE_CRC,
|
||||
} pngle_state_t;
|
||||
|
||||
typedef enum {
|
||||
// Supported chunks
|
||||
// Filter chunk names by following command to (re)generate hex constants;
|
||||
// % perl -ne 'chomp; s/.*\s*\/\/\s*//; print "\tPNGLE_CHUNK_$_ = 0x" . unpack("H*") . "UL, // $_\n";'
|
||||
PNGLE_CHUNK_IHDR = 0x49484452UL, // IHDR
|
||||
PNGLE_CHUNK_PLTE = 0x504c5445UL, // PLTE
|
||||
PNGLE_CHUNK_IDAT = 0x49444154UL, // IDAT
|
||||
PNGLE_CHUNK_IEND = 0x49454e44UL, // IEND
|
||||
PNGLE_CHUNK_tRNS = 0x74524e53UL, // tRNS
|
||||
PNGLE_CHUNK_gAMA = 0x67414d41UL, // gAMA
|
||||
} pngle_chunk_t;
|
||||
|
||||
// typedef struct _pngle_t pngle_t; // declared in pngle.h
|
||||
struct _pngle_t {
|
||||
pngle_ihdr_t hdr;
|
||||
|
||||
uint_fast8_t channels; // 0 indicates IHDR hasn't been processed yet
|
||||
|
||||
// PLTE chunk
|
||||
size_t n_palettes;
|
||||
uint8_t *palette;
|
||||
|
||||
// tRNS chunk
|
||||
size_t n_trans_palettes;
|
||||
uint8_t *trans_palette;
|
||||
|
||||
// parser state (reset on every chunk header)
|
||||
pngle_state_t state;
|
||||
uint32_t chunk_type;
|
||||
uint32_t chunk_remain;
|
||||
mz_ulong crc32;
|
||||
|
||||
// decompression state (reset on IHDR)
|
||||
tinfl_decompressor inflator; // 11000 bytes
|
||||
uint8_t lz_buf[TINFL_LZ_DICT_SIZE]; // 32768 bytes
|
||||
uint8_t *next_out; // NULL indicates IDAT hasn't been processed yet
|
||||
size_t avail_out;
|
||||
|
||||
// scanline decoder (reset on every set_interlace_pass() call)
|
||||
uint8_t *scanline_ringbuf;
|
||||
size_t scanline_ringbuf_size;
|
||||
size_t scanline_ringbuf_cidx;
|
||||
int_fast8_t scanline_remain_bytes_to_render;
|
||||
int_fast8_t filter_type;
|
||||
uint32_t drawing_x;
|
||||
uint32_t drawing_y;
|
||||
|
||||
// interlace
|
||||
uint_fast8_t interlace_pass;
|
||||
|
||||
const char *error;
|
||||
|
||||
#ifndef PNGLE_NO_GAMMA_CORRECTION
|
||||
uint8_t *gamma_table;
|
||||
double display_gamma;
|
||||
#endif
|
||||
|
||||
pngle_init_callback_t init_callback;
|
||||
pngle_draw_callback_t draw_callback;
|
||||
pngle_done_callback_t done_callback;
|
||||
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
// magic
|
||||
static const uint8_t png_sig[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
|
||||
static uint32_t interlace_off_x[8] = { 0, 0, 4, 0, 2, 0, 1, 0 };
|
||||
static uint32_t interlace_off_y[8] = { 0, 0, 0, 4, 0, 2, 0, 1 };
|
||||
static uint32_t interlace_div_x[8] = { 1, 8, 8, 4, 4, 2, 2, 1 };
|
||||
static uint32_t interlace_div_y[8] = { 1, 8, 8, 8, 4, 4, 2, 2 };
|
||||
|
||||
|
||||
static inline uint8_t read_uint8(const uint8_t *p)
|
||||
{
|
||||
return *p;
|
||||
}
|
||||
|
||||
static inline uint32_t read_uint32(const uint8_t *p)
|
||||
{
|
||||
return (p[0] << 24)
|
||||
| (p[1] << 16)
|
||||
| (p[2] << 8)
|
||||
| (p[3] << 0)
|
||||
;
|
||||
}
|
||||
|
||||
static inline uint32_t U32_CLAMP_ADD(uint32_t a, uint32_t b, uint32_t top)
|
||||
{
|
||||
uint32_t v = a + b;
|
||||
if (v < a) return top; // uint32 overflow
|
||||
if (v > top) return top; // clamp
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
void pngle_reset(pngle_t *pngle)
|
||||
{
|
||||
if (!pngle) return ;
|
||||
|
||||
pngle->state = PNGLE_STATE_INITIAL;
|
||||
pngle->error = "No error";
|
||||
|
||||
if (pngle->scanline_ringbuf) free(pngle->scanline_ringbuf);
|
||||
if (pngle->palette) free(pngle->palette);
|
||||
if (pngle->trans_palette) free(pngle->trans_palette);
|
||||
#ifndef PNGLE_NO_GAMMA_CORRECTION
|
||||
if (pngle->gamma_table) free(pngle->gamma_table);
|
||||
#endif
|
||||
|
||||
pngle->scanline_ringbuf = NULL;
|
||||
pngle->palette = NULL;
|
||||
pngle->trans_palette = NULL;
|
||||
#ifndef PNGLE_NO_GAMMA_CORRECTION
|
||||
pngle->gamma_table = NULL;
|
||||
#endif
|
||||
|
||||
pngle->channels = 0; // indicates IHDR hasn't been processed yet
|
||||
pngle->next_out = NULL; // indicates IDAT hasn't been processed yet
|
||||
|
||||
// clear them just in case...
|
||||
memset(&pngle->hdr, 0, sizeof(pngle->hdr));
|
||||
pngle->n_palettes = 0;
|
||||
pngle->n_trans_palettes = 0;
|
||||
|
||||
tinfl_init(&pngle->inflator);
|
||||
}
|
||||
|
||||
pngle_t *pngle_new()
|
||||
{
|
||||
pngle_t *pngle = (pngle_t *)PNGLE_CALLOC(1, sizeof(pngle_t), "pngle_t");
|
||||
if (!pngle) return NULL;
|
||||
|
||||
pngle_reset(pngle);
|
||||
|
||||
return pngle;
|
||||
}
|
||||
|
||||
void pngle_destroy(pngle_t *pngle)
|
||||
{
|
||||
if (pngle) {
|
||||
pngle_reset(pngle);
|
||||
free(pngle);
|
||||
}
|
||||
}
|
||||
|
||||
const char *pngle_error(pngle_t *pngle)
|
||||
{
|
||||
if (!pngle) return "Uninitialized";
|
||||
return pngle->error;
|
||||
}
|
||||
|
||||
uint32_t pngle_get_width(pngle_t *pngle)
|
||||
{
|
||||
if (!pngle) return 0;
|
||||
return pngle->hdr.width;
|
||||
}
|
||||
|
||||
uint32_t pngle_get_height(pngle_t *pngle)
|
||||
{
|
||||
if (!pngle) return 0;
|
||||
return pngle->hdr.height;
|
||||
}
|
||||
|
||||
pngle_ihdr_t *pngle_get_ihdr(pngle_t *pngle)
|
||||
{
|
||||
if (!pngle) return NULL;
|
||||
if (pngle->channels == 0) return NULL;
|
||||
return &pngle->hdr;
|
||||
}
|
||||
|
||||
|
||||
static int is_trans_color(pngle_t *pngle, uint16_t *value, size_t n)
|
||||
{
|
||||
if (pngle->n_trans_palettes != 1) return 0; // false (none or indexed)
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (value[i] != (pngle->trans_palette[i * 2 + 0] * 0x100 + pngle->trans_palette[i * 2 + 1])) return 0; // false
|
||||
}
|
||||
return 1; // true
|
||||
}
|
||||
|
||||
static inline void scanline_ringbuf_push(pngle_t *pngle, uint8_t value)
|
||||
{
|
||||
pngle->scanline_ringbuf[pngle->scanline_ringbuf_cidx] = value;
|
||||
pngle->scanline_ringbuf_cidx = (pngle->scanline_ringbuf_cidx + 1) % pngle->scanline_ringbuf_size;
|
||||
}
|
||||
|
||||
static inline uint16_t get_value(pngle_t *pngle, size_t *ridx, int *bitcount, int depth)
|
||||
{
|
||||
uint16_t v;
|
||||
|
||||
switch (depth) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
if (*bitcount >= 8) {
|
||||
*bitcount = 0;
|
||||
*ridx = (*ridx + 1) % pngle->scanline_ringbuf_size;
|
||||
}
|
||||
*bitcount += depth;
|
||||
uint8_t mask = ((1UL << depth) - 1);
|
||||
uint8_t shift = (8 - *bitcount);
|
||||
return (pngle->scanline_ringbuf[*ridx] >> shift) & mask;
|
||||
|
||||
case 8:
|
||||
v = pngle->scanline_ringbuf[*ridx];
|
||||
*ridx = (*ridx + 1) % pngle->scanline_ringbuf_size;
|
||||
return v;
|
||||
|
||||
case 16:
|
||||
v = pngle->scanline_ringbuf[*ridx];
|
||||
*ridx = (*ridx + 1) % pngle->scanline_ringbuf_size;
|
||||
|
||||
v = v * 0x100 + pngle->scanline_ringbuf[*ridx];
|
||||
*ridx = (*ridx + 1) % pngle->scanline_ringbuf_size;
|
||||
return v;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pngle_draw_pixels(pngle_t *pngle, size_t scanline_ringbuf_xidx)
|
||||
{
|
||||
uint16_t v[4]; // MAX_CHANNELS
|
||||
int bitcount = 0;
|
||||
uint8_t pixel_depth = (pngle->hdr.color_type & 1) ? 8 : pngle->hdr.depth;
|
||||
uint16_t maxval = (1UL << pixel_depth) - 1;
|
||||
|
||||
int n_pixels = pngle->hdr.depth == 16 ? 1 : (8 / pngle->hdr.depth);
|
||||
|
||||
for (; n_pixels-- > 0 && pngle->drawing_x < pngle->hdr.width; pngle->drawing_x = U32_CLAMP_ADD(pngle->drawing_x, interlace_div_x[pngle->interlace_pass], pngle->hdr.width)) {
|
||||
for (uint_fast8_t c = 0; c < pngle->channels; c++) {
|
||||
v[c] = get_value(pngle, &scanline_ringbuf_xidx, &bitcount, pngle->hdr.depth);
|
||||
}
|
||||
|
||||
// color type: 0000 0111
|
||||
// ^-- indexed color (palette)
|
||||
// ^--- Color
|
||||
// ^---- Alpha channel
|
||||
|
||||
if (pngle->hdr.color_type & 2) {
|
||||
// color
|
||||
if (pngle->hdr.color_type & 1) {
|
||||
// indexed color: type 3
|
||||
|
||||
// lookup palette info
|
||||
uint16_t pidx = v[0];
|
||||
if (pidx >= pngle->n_palettes) return PNGLE_ERROR("Color index is out of range");
|
||||
|
||||
v[0] = pngle->palette[pidx * 3 + 0];
|
||||
v[1] = pngle->palette[pidx * 3 + 1];
|
||||
v[2] = pngle->palette[pidx * 3 + 2];
|
||||
|
||||
// tRNS as an indexed alpha value table (for color type 3)
|
||||
v[3] = pidx < pngle->n_trans_palettes ? pngle->trans_palette[pidx] : maxval;
|
||||
} else {
|
||||
// true color: 2, and 6
|
||||
v[3] = (pngle->hdr.color_type & 4) ? v[3] : is_trans_color(pngle, v, 3) ? 0 : maxval;
|
||||
}
|
||||
} else {
|
||||
// alpha, tRNS, or opaque
|
||||
v[3] = (pngle->hdr.color_type & 4) ? v[1] : is_trans_color(pngle, v, 1) ? 0 : maxval;
|
||||
|
||||
// monochrome
|
||||
v[1] = v[2] = v[0];
|
||||
}
|
||||
|
||||
if (pngle->draw_callback) {
|
||||
uint8_t rgba[4] = {
|
||||
(v[0] * 255 + maxval / 2) / maxval,
|
||||
(v[1] * 255 + maxval / 2) / maxval,
|
||||
(v[2] * 255 + maxval / 2) / maxval,
|
||||
(v[3] * 255 + maxval / 2) / maxval
|
||||
};
|
||||
|
||||
#ifndef PNGLE_NO_GAMMA_CORRECTION
|
||||
if (pngle->gamma_table) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
rgba[i] = pngle->gamma_table[v[i]];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
pngle->draw_callback(pngle, pngle->drawing_x, pngle->drawing_y
|
||||
, MIN(interlace_div_x[pngle->interlace_pass] - interlace_off_x[pngle->interlace_pass], pngle->hdr.width - pngle->drawing_x)
|
||||
, MIN(interlace_div_y[pngle->interlace_pass] - interlace_off_y[pngle->interlace_pass], pngle->hdr.height - pngle->drawing_y)
|
||||
, rgba
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int paeth(int a, int b, int c)
|
||||
{
|
||||
int p = a + b - c;
|
||||
int pa = abs(p - a);
|
||||
int pb = abs(p - b);
|
||||
int pc = abs(p - c);
|
||||
|
||||
if (pa <= pb && pa <= pc) return a;
|
||||
if (pb <= pc) return b;
|
||||
return c;
|
||||
}
|
||||
|
||||
static int set_interlace_pass(pngle_t *pngle, uint_fast8_t pass)
|
||||
{
|
||||
pngle->interlace_pass = pass;
|
||||
|
||||
uint_fast8_t bytes_per_pixel = (pngle->channels * pngle->hdr.depth + 7) / 8; // 1 if depth <= 8
|
||||
size_t scanline_pixels = (pngle->hdr.width - interlace_off_x[pngle->interlace_pass] + interlace_div_x[pngle->interlace_pass] - 1) / interlace_div_x[pngle->interlace_pass];
|
||||
size_t scanline_stride = (scanline_pixels * pngle->channels * pngle->hdr.depth + 7) / 8;
|
||||
|
||||
pngle->scanline_ringbuf_size = scanline_stride + bytes_per_pixel * 2; // 2 rooms for c/x and a
|
||||
|
||||
if (pngle->scanline_ringbuf) free(pngle->scanline_ringbuf);
|
||||
if ((pngle->scanline_ringbuf = PNGLE_CALLOC(pngle->scanline_ringbuf_size, 1, "scanline ringbuf")) == NULL) return PNGLE_ERROR("Insufficient memory");
|
||||
|
||||
pngle->drawing_x = interlace_off_x[pngle->interlace_pass];
|
||||
pngle->drawing_y = interlace_off_y[pngle->interlace_pass];
|
||||
pngle->filter_type = -1;
|
||||
|
||||
pngle->scanline_ringbuf_cidx = 0;
|
||||
pngle->scanline_remain_bytes_to_render = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_gamma_table(pngle_t *pngle, uint32_t png_gamma)
|
||||
{
|
||||
#ifndef PNGLE_NO_GAMMA_CORRECTION
|
||||
if (pngle->gamma_table) free(pngle->gamma_table);
|
||||
|
||||
if (pngle->display_gamma <= 0) return 0; // disable gamma correction
|
||||
if (png_gamma == 0) return 0;
|
||||
|
||||
uint8_t pixel_depth = (pngle->hdr.color_type & 1) ? 8 : pngle->hdr.depth;
|
||||
uint16_t maxval = (1UL << pixel_depth) - 1;
|
||||
|
||||
pngle->gamma_table = PNGLE_CALLOC(1, maxval + 1, "gamma table");
|
||||
if (!pngle->gamma_table) return PNGLE_ERROR("Insufficient memory");
|
||||
|
||||
for (int i = 0; i < maxval + 1; i++) {
|
||||
pngle->gamma_table[i] = (uint8_t)floor(pow(i / (double)maxval, 100000.0 / png_gamma / pngle->display_gamma) * 255.0 + 0.5);
|
||||
}
|
||||
debug_printf("[pngle] gamma value = %d\n", png_gamma);
|
||||
#else
|
||||
PNGLE_UNUSED(pngle);
|
||||
PNGLE_UNUSED(png_gamma);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int pngle_on_data(pngle_t *pngle, const uint8_t *p, int len)
|
||||
{
|
||||
const uint8_t *ep = p + len;
|
||||
|
||||
uint_fast8_t bytes_per_pixel = (pngle->channels * pngle->hdr.depth + 7) / 8; // 1 if depth <= 8
|
||||
|
||||
while (p < ep) {
|
||||
if (pngle->drawing_x >= pngle->hdr.width) {
|
||||
// New row
|
||||
pngle->drawing_x = interlace_off_x[pngle->interlace_pass];
|
||||
pngle->drawing_y = U32_CLAMP_ADD(pngle->drawing_y, interlace_div_y[pngle->interlace_pass], pngle->hdr.height);
|
||||
pngle->filter_type = -1; // Indicate new line
|
||||
}
|
||||
|
||||
if (pngle->drawing_x >= pngle->hdr.width || pngle->drawing_y >= pngle->hdr.height) {
|
||||
if (pngle->interlace_pass == 0 || pngle->interlace_pass >= 7) return len; // Do nothing further
|
||||
|
||||
// Interlace: Next pass
|
||||
if (set_interlace_pass(pngle, pngle->interlace_pass + 1) < 0) return -1;
|
||||
debug_printf("[pngle] interlace pass changed to: %d\n", pngle->interlace_pass);
|
||||
|
||||
continue; // This is required because "No filter type bytes are present in an empty pass".
|
||||
}
|
||||
|
||||
if (pngle->filter_type < 0) {
|
||||
if (*p > 4) {
|
||||
debug_printf("[pngle] Invalid filter type is found; 0x%02x\n", *p);
|
||||
return PNGLE_ERROR("Invalid filter type is found");
|
||||
}
|
||||
|
||||
pngle->filter_type = (int_fast8_t)*p++; // 0 - 4
|
||||
|
||||
// push sentinel bytes for new line
|
||||
for (uint_fast8_t i = 0; i < bytes_per_pixel; i++) {
|
||||
scanline_ringbuf_push(pngle, 0);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t cidx = pngle->scanline_ringbuf_cidx;
|
||||
size_t bidx = (pngle->scanline_ringbuf_cidx + bytes_per_pixel) % pngle->scanline_ringbuf_size;
|
||||
size_t aidx = (pngle->scanline_ringbuf_cidx + pngle->scanline_ringbuf_size - bytes_per_pixel) % pngle->scanline_ringbuf_size;
|
||||
// debug_printf("[pngle] cidx = %zd, bidx = %zd, aidx = %zd\n", cidx, bidx, aidx);
|
||||
|
||||
uint8_t c = pngle->scanline_ringbuf[cidx]; // left-up
|
||||
uint8_t b = pngle->scanline_ringbuf[bidx]; // up
|
||||
uint8_t a = pngle->scanline_ringbuf[aidx]; // left
|
||||
uint8_t x = *p++; // target
|
||||
// debug_printf("[pngle] c = 0x%02x, b = 0x%02x, a = 0x%02x, x = 0x%02x\n", c, b, a, x);
|
||||
|
||||
// Reverse the filter
|
||||
switch (pngle->filter_type) {
|
||||
case 0: break; // None
|
||||
case 1: x += a; break; // Sub
|
||||
case 2: x += b; break; // Up
|
||||
case 3: x += (a + b) / 2; break; // Average
|
||||
case 4: x += paeth(a, b, c); break; // Paeth
|
||||
}
|
||||
|
||||
scanline_ringbuf_push(pngle, x); // updates scanline_ringbuf_cidx
|
||||
|
||||
if (pngle->scanline_remain_bytes_to_render < 0) pngle->scanline_remain_bytes_to_render = bytes_per_pixel;
|
||||
if (--pngle->scanline_remain_bytes_to_render == 0) {
|
||||
size_t xidx = (pngle->scanline_ringbuf_cidx + pngle->scanline_ringbuf_size - bytes_per_pixel) % pngle->scanline_ringbuf_size;
|
||||
|
||||
if (pngle_draw_pixels(pngle, xidx) < 0) return -1;
|
||||
|
||||
pngle->scanline_remain_bytes_to_render = -1; // reset
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static int pngle_handle_chunk(pngle_t *pngle, const uint8_t *buf, size_t len)
|
||||
{
|
||||
size_t consume = 0;
|
||||
|
||||
switch (pngle->chunk_type) {
|
||||
case PNGLE_CHUNK_IHDR:
|
||||
// parse IHDR
|
||||
consume = 13;
|
||||
if (len < consume) return 0;
|
||||
|
||||
debug_printf("[pngle] Parse IHDR\n");
|
||||
|
||||
pngle->hdr.width = read_uint32(buf + 0);
|
||||
pngle->hdr.height = read_uint32(buf + 4);
|
||||
pngle->hdr.depth = read_uint8 (buf + 8);
|
||||
pngle->hdr.color_type = read_uint8 (buf + 9);
|
||||
pngle->hdr.compression = read_uint8 (buf + 10);
|
||||
pngle->hdr.filter = read_uint8 (buf + 11);
|
||||
pngle->hdr.interlace = read_uint8 (buf + 12);
|
||||
|
||||
|
||||
debug_printf("[pngle] width : %d\n", pngle->hdr.width );
|
||||
debug_printf("[pngle] height : %d\n", pngle->hdr.height );
|
||||
debug_printf("[pngle] depth : %d\n", pngle->hdr.depth );
|
||||
debug_printf("[pngle] color_type : %d\n", pngle->hdr.color_type );
|
||||
debug_printf("[pngle] compression: %d\n", pngle->hdr.compression);
|
||||
debug_printf("[pngle] filter : %d\n", pngle->hdr.filter );
|
||||
debug_printf("[pngle] interlace : %d\n", pngle->hdr.interlace );
|
||||
|
||||
/*
|
||||
Color Allowed Interpretation channels
|
||||
Type Bit Depths
|
||||
|
||||
0 1,2,4,8,16 Each pixel is a grayscale sample. 1 channels (Brightness)
|
||||
|
||||
2 8,16 Each pixel is an R,G,B triple. 3 channels (R, G, B)
|
||||
|
||||
3 1,2,4,8 Each pixel is a palette index; 1 channels (palette info)
|
||||
a PLTE chunk must appear.
|
||||
|
||||
4 8,16 Each pixel is a grayscale sample, 2 channels (Brightness, Alpha)
|
||||
followed by an alpha sample.
|
||||
|
||||
6 8,16 Each pixel is an R,G,B triple, 4 channels (R, G, B, Alpha)
|
||||
followed by an alpha sample.
|
||||
*/
|
||||
// 111
|
||||
// ^-- indexed color (palette)
|
||||
// ^--- Color
|
||||
// ^---- Alpha channel
|
||||
|
||||
switch (pngle->hdr.color_type) {
|
||||
case 0: pngle->channels = 1; if (pngle->hdr.depth != 1 && pngle->hdr.depth != 2 && pngle->hdr.depth != 4 && pngle->hdr.depth != 8 && pngle->hdr.depth != 16) return PNGLE_ERROR("Invalid bit depth"); break; // grayscale
|
||||
case 2: pngle->channels = 3; if ( pngle->hdr.depth != 8 && pngle->hdr.depth != 16) return PNGLE_ERROR("Invalid bit depth"); break; // truecolor
|
||||
case 3: pngle->channels = 1; if (pngle->hdr.depth != 1 && pngle->hdr.depth != 2 && pngle->hdr.depth != 4 && pngle->hdr.depth != 8 ) return PNGLE_ERROR("Invalid bit depth"); break; // indexed color
|
||||
case 4: pngle->channels = 2; if ( pngle->hdr.depth != 8 && pngle->hdr.depth != 16) return PNGLE_ERROR("Invalid bit depth"); break; // grayscale + alpha
|
||||
case 6: pngle->channels = 4; if ( pngle->hdr.depth != 8 && pngle->hdr.depth != 16) return PNGLE_ERROR("Invalid bit depth"); break; // truecolor + alpha
|
||||
default:
|
||||
return PNGLE_ERROR("Incorrect IHDR info");
|
||||
}
|
||||
|
||||
if (pngle->hdr.compression != 0) return PNGLE_ERROR("Unsupported compression type in IHDR");
|
||||
if (pngle->hdr.filter != 0) return PNGLE_ERROR("Unsupported filter type in IHDR");
|
||||
|
||||
// interlace
|
||||
if (set_interlace_pass(pngle, pngle->hdr.interlace ? 1 : 0) < 0) return -1;
|
||||
|
||||
// callback
|
||||
if (pngle->init_callback) pngle->init_callback(pngle, pngle->hdr.width, pngle->hdr.height);
|
||||
|
||||
break;
|
||||
|
||||
case PNGLE_CHUNK_IDAT:
|
||||
// parse & decode IDAT chunk
|
||||
if (len < 1) return 0;
|
||||
|
||||
debug_printf("[pngle] Reading IDAT (len %zd / chunk remain %u)\n", len, pngle->chunk_remain);
|
||||
|
||||
size_t in_bytes = len;
|
||||
size_t out_bytes = pngle->avail_out;
|
||||
|
||||
//debug_printf("[pngle] in_bytes %zd, out_bytes %zd, next_out %p\n", in_bytes, out_bytes, pngle->next_out);
|
||||
|
||||
// XXX: tinfl_decompress always requires (next_out - lz_buf + avail_out) == TINFL_LZ_DICT_SIZE
|
||||
tinfl_status status = tinfl_decompress(&pngle->inflator, (const mz_uint8 *)buf, &in_bytes, pngle->lz_buf, (mz_uint8 *)pngle->next_out, &out_bytes, TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_PARSE_ZLIB_HEADER);
|
||||
|
||||
//debug_printf("[pngle] tinfl_decompress\n");
|
||||
//debug_printf("[pngle] => in_bytes %zd, out_bytes %zd, next_out %p, status %d\n", in_bytes, out_bytes, pngle->next_out, status);
|
||||
|
||||
if (status < TINFL_STATUS_DONE) {
|
||||
// Decompression failed.
|
||||
debug_printf("[pngle] tinfl_decompress() failed with status %d!\n", status);
|
||||
return PNGLE_ERROR("Failed to decompress the IDAT stream");
|
||||
}
|
||||
|
||||
pngle->next_out += out_bytes;
|
||||
pngle->avail_out -= out_bytes;
|
||||
|
||||
// debug_printf("[pngle] => avail_out %zd, next_out %p\n", pngle->avail_out, pngle->next_out);
|
||||
|
||||
if (status == TINFL_STATUS_DONE || pngle->avail_out == 0) {
|
||||
// Output buffer is full, or decompression is done, so write buffer to output file.
|
||||
// XXX: This is the only chance to process the buffer.
|
||||
uint8_t *read_ptr = pngle->lz_buf;
|
||||
size_t n = TINFL_LZ_DICT_SIZE - (size_t)pngle->avail_out;
|
||||
|
||||
// pngle_on_data() usually returns n, otherwise -1 on error
|
||||
if (pngle_on_data(pngle, read_ptr, n) < 0) return -1;
|
||||
|
||||
// XXX: tinfl_decompress always requires (next_out - lz_buf + avail_out) == TINFL_LZ_DICT_SIZE
|
||||
pngle->next_out = pngle->lz_buf;
|
||||
pngle->avail_out = TINFL_LZ_DICT_SIZE;
|
||||
}
|
||||
|
||||
consume = in_bytes;
|
||||
break;
|
||||
|
||||
case PNGLE_CHUNK_PLTE:
|
||||
consume = 3;
|
||||
if (len < consume) return 0;
|
||||
|
||||
memcpy(pngle->palette + pngle->n_palettes * 3, buf, 3);
|
||||
|
||||
debug_printf("[pngle] PLTE[%zd]: (%d, %d, %d)\n"
|
||||
, pngle->n_palettes
|
||||
, pngle->palette[pngle->n_palettes * 3 + 0]
|
||||
, pngle->palette[pngle->n_palettes * 3 + 1]
|
||||
, pngle->palette[pngle->n_palettes * 3 + 2]
|
||||
);
|
||||
|
||||
pngle->n_palettes++;
|
||||
|
||||
break;
|
||||
|
||||
case PNGLE_CHUNK_IEND:
|
||||
consume = 0;
|
||||
break;
|
||||
|
||||
case PNGLE_CHUNK_tRNS:
|
||||
switch (pngle->hdr.color_type) {
|
||||
case 3: consume = 1; break;
|
||||
case 0: consume = 2 * 1; break;
|
||||
case 2: consume = 2 * 3; break;
|
||||
default:
|
||||
return PNGLE_ERROR("tRNS chunk is prohibited on the color type");
|
||||
}
|
||||
if (len < consume) return 0;
|
||||
|
||||
memcpy(pngle->trans_palette + pngle->n_trans_palettes, buf, consume);
|
||||
|
||||
pngle->n_trans_palettes++;
|
||||
|
||||
break;
|
||||
|
||||
case PNGLE_CHUNK_gAMA:
|
||||
consume = 4;
|
||||
if (len < consume) return 0;
|
||||
|
||||
if (setup_gamma_table(pngle, read_uint32(buf)) < 0) return -1;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
// unknown chunk
|
||||
consume = len;
|
||||
|
||||
debug_printf("[pngle] Unknown chunk; %zd bytes discarded\n", consume);
|
||||
break;
|
||||
}
|
||||
|
||||
return consume;
|
||||
}
|
||||
|
||||
static int pngle_feed_internal(pngle_t *pngle, const uint8_t *buf, size_t len)
|
||||
{
|
||||
if (!pngle) return -1;
|
||||
|
||||
switch (pngle->state) {
|
||||
case PNGLE_STATE_ERROR:
|
||||
return -1;
|
||||
|
||||
case PNGLE_STATE_EOF:
|
||||
return len;
|
||||
|
||||
case PNGLE_STATE_INITIAL:
|
||||
// find PNG header
|
||||
if (len < sizeof(png_sig)) return 0;
|
||||
|
||||
if (memcmp(png_sig, buf, sizeof(png_sig))) return PNGLE_ERROR("Incorrect PNG signature");
|
||||
|
||||
debug_printf("[pngle] PNG signature found\n");
|
||||
|
||||
pngle->state = PNGLE_STATE_FIND_CHUNK_HEADER;
|
||||
return sizeof(png_sig);
|
||||
|
||||
case PNGLE_STATE_FIND_CHUNK_HEADER:
|
||||
if (len < 8) return 0;
|
||||
|
||||
pngle->chunk_remain = read_uint32(buf);
|
||||
pngle->chunk_type = read_uint32(buf + 4);
|
||||
|
||||
pngle->crc32 = mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)(buf + 4), 4);
|
||||
|
||||
debug_printf("[pngle] Chunk '%.4s' len %u\n", buf + 4, pngle->chunk_remain);
|
||||
|
||||
pngle->state = PNGLE_STATE_HANDLE_CHUNK;
|
||||
|
||||
// initialize & sanity check
|
||||
switch (pngle->chunk_type) {
|
||||
case PNGLE_CHUNK_IHDR:
|
||||
if (pngle->chunk_remain != 13) return PNGLE_ERROR("Invalid IHDR chunk size");
|
||||
if (pngle->channels != 0) return PNGLE_ERROR("Multiple IHDR chunks are not allowed");
|
||||
break;
|
||||
|
||||
case PNGLE_CHUNK_IDAT:
|
||||
if (pngle->chunk_remain <= 0) return PNGLE_ERROR("Invalid IDAT chunk size");
|
||||
if (pngle->channels == 0) return PNGLE_ERROR("No IHDR chunk is found");
|
||||
if (pngle->hdr.color_type == 3 && pngle->palette == NULL) return PNGLE_ERROR("No PLTE chunk is found");
|
||||
|
||||
if (pngle->next_out == NULL) {
|
||||
// Very first IDAT
|
||||
pngle->next_out = pngle->lz_buf;
|
||||
pngle->avail_out = TINFL_LZ_DICT_SIZE;
|
||||
}
|
||||
break;
|
||||
|
||||
case PNGLE_CHUNK_PLTE:
|
||||
if (pngle->chunk_remain <= 0) return PNGLE_ERROR("Invalid PLTE chunk size");
|
||||
if (pngle->channels == 0) return PNGLE_ERROR("No IHDR chunk is found");
|
||||
if (pngle->palette) return PNGLE_ERROR("Too many PLTE chunk");
|
||||
|
||||
switch (pngle->hdr.color_type) {
|
||||
case 3: // indexed color
|
||||
break;
|
||||
case 2: // truecolor
|
||||
case 6: // truecolor + alpha
|
||||
// suggested palettes
|
||||
break;
|
||||
default:
|
||||
return PNGLE_ERROR("PLTE chunk is prohibited on the color type");
|
||||
}
|
||||
|
||||
if (pngle->chunk_remain % 3) return PNGLE_ERROR("Invalid PLTE chunk size");
|
||||
if (pngle->chunk_remain / 3 > MIN(256, (1UL << pngle->hdr.depth))) return PNGLE_ERROR("Too many palettes in PLTE");
|
||||
if ((pngle->palette = PNGLE_CALLOC(pngle->chunk_remain / 3, 3, "palette")) == NULL) return PNGLE_ERROR("Insufficient memory");
|
||||
pngle->n_palettes = 0;
|
||||
break;
|
||||
|
||||
case PNGLE_CHUNK_IEND:
|
||||
if (pngle->next_out == NULL) return PNGLE_ERROR("No IDAT chunk is found");
|
||||
if (pngle->chunk_remain > 0) return PNGLE_ERROR("Invalid IEND chunk size");
|
||||
break;
|
||||
|
||||
case PNGLE_CHUNK_tRNS:
|
||||
if (pngle->chunk_remain <= 0) return PNGLE_ERROR("Invalid tRNS chunk size");
|
||||
if (pngle->channels == 0) return PNGLE_ERROR("No IHDR chunk is found");
|
||||
if (pngle->trans_palette) return PNGLE_ERROR("Too many tRNS chunk");
|
||||
|
||||
switch (pngle->hdr.color_type) {
|
||||
case 3: // indexed color
|
||||
if (pngle->chunk_remain > (1UL << pngle->hdr.depth)) return PNGLE_ERROR("Too many palettes in tRNS");
|
||||
break;
|
||||
case 0: // grayscale
|
||||
if (pngle->chunk_remain != 2) return PNGLE_ERROR("Invalid tRNS chunk size");
|
||||
break;
|
||||
case 2: // truecolor
|
||||
if (pngle->chunk_remain != 6) return PNGLE_ERROR("Invalid tRNS chunk size");
|
||||
break;
|
||||
|
||||
default:
|
||||
return PNGLE_ERROR("tRNS chunk is prohibited on the color type");
|
||||
}
|
||||
if ((pngle->trans_palette = PNGLE_CALLOC(pngle->chunk_remain, 1, "trans palette")) == NULL) return PNGLE_ERROR("Insufficient memory");
|
||||
pngle->n_trans_palettes = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 8;
|
||||
|
||||
case PNGLE_STATE_HANDLE_CHUNK:
|
||||
len = MIN(len, pngle->chunk_remain);
|
||||
|
||||
int consumed = pngle_handle_chunk(pngle, buf, len);
|
||||
|
||||
if (consumed > 0) {
|
||||
if (pngle->chunk_remain < (uint32_t)consumed) return PNGLE_ERROR("Chunk data has been consumed too much");
|
||||
|
||||
pngle->chunk_remain -= consumed;
|
||||
pngle->crc32 = mz_crc32(pngle->crc32, (const mz_uint8 *)buf, consumed);
|
||||
}
|
||||
if (pngle->chunk_remain <= 0) pngle->state = PNGLE_STATE_CRC;
|
||||
|
||||
return consumed;
|
||||
|
||||
case PNGLE_STATE_CRC:
|
||||
if (len < 4) return 0;
|
||||
|
||||
uint32_t crc32 = read_uint32(buf);
|
||||
|
||||
if (crc32 != pngle->crc32) {
|
||||
debug_printf("[pngle] CRC: %08x vs %08x => NG\n", crc32, (uint32_t)pngle->crc32);
|
||||
return PNGLE_ERROR("CRC mismatch");
|
||||
}
|
||||
|
||||
debug_printf("[pngle] CRC: %08x vs %08x => OK\n", crc32, (uint32_t)pngle->crc32);
|
||||
pngle->state = PNGLE_STATE_FIND_CHUNK_HEADER;
|
||||
|
||||
// XXX:
|
||||
if (pngle->chunk_type == PNGLE_CHUNK_IEND) {
|
||||
pngle->state = PNGLE_STATE_EOF;
|
||||
if (pngle->done_callback) pngle->done_callback(pngle);
|
||||
debug_printf("[pngle] DONE\n");
|
||||
}
|
||||
|
||||
return 4;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return PNGLE_ERROR("Invalid state");
|
||||
}
|
||||
|
||||
int pngle_feed(pngle_t *pngle, const void *buf, size_t len)
|
||||
{
|
||||
size_t pos = 0;
|
||||
pngle_state_t last_state = pngle->state;
|
||||
|
||||
while (pos < len) {
|
||||
int r = pngle_feed_internal(pngle, (const uint8_t *)buf + pos, len - pos);
|
||||
if (r < 0) return r; // error
|
||||
|
||||
if (r == 0 && last_state == pngle->state) break;
|
||||
last_state = pngle->state;
|
||||
|
||||
pos += r;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
void pngle_set_display_gamma(pngle_t *pngle, double display_gamma)
|
||||
{
|
||||
if (!pngle) return ;
|
||||
#ifndef PNGLE_NO_GAMMA_CORRECTION
|
||||
pngle->display_gamma = display_gamma;
|
||||
#else
|
||||
PNGLE_UNUSED(display_gamma);
|
||||
#endif
|
||||
}
|
||||
|
||||
void pngle_set_init_callback(pngle_t *pngle, pngle_init_callback_t callback)
|
||||
{
|
||||
if (!pngle) return ;
|
||||
pngle->init_callback = callback;
|
||||
}
|
||||
|
||||
void pngle_set_draw_callback(pngle_t *pngle, pngle_draw_callback_t callback)
|
||||
{
|
||||
if (!pngle) return ;
|
||||
pngle->draw_callback = callback;
|
||||
}
|
||||
|
||||
void pngle_set_done_callback(pngle_t *pngle, pngle_done_callback_t callback)
|
||||
{
|
||||
if (!pngle) return ;
|
||||
pngle->done_callback = callback;
|
||||
}
|
||||
|
||||
void pngle_set_user_data(pngle_t *pngle, void *user_data)
|
||||
{
|
||||
if (!pngle) return ;
|
||||
pngle->user_data = user_data;
|
||||
}
|
||||
|
||||
void *pngle_get_user_data(pngle_t *pngle)
|
||||
{
|
||||
if (!pngle) return NULL;
|
||||
return pngle->user_data;
|
||||
}
|
||||
|
||||
/* vim: set ts=4 sw=4 noexpandtab: */
|
|
@ -0,0 +1,86 @@
|
|||
/*-
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2019 kikuchan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __PNGLE_H__
|
||||
#define __PNGLE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Main Pngle object
|
||||
typedef struct _pngle_t pngle_t;
|
||||
|
||||
// Callback signatures
|
||||
typedef void (*pngle_init_callback_t)(pngle_t *pngle, uint32_t w, uint32_t h);
|
||||
typedef void (*pngle_draw_callback_t)(pngle_t *pngle, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint8_t rgba[4]);
|
||||
typedef void (*pngle_done_callback_t)(pngle_t *pngle);
|
||||
|
||||
// ----------------
|
||||
// Basic interfaces
|
||||
// ----------------
|
||||
pngle_t *pngle_new();
|
||||
void pngle_destroy(pngle_t *pngle);
|
||||
void pngle_reset(pngle_t *pngle); // clear its internal state (not applied to pngle_set_* functions)
|
||||
const char *pngle_error(pngle_t *pngle);
|
||||
int pngle_feed(pngle_t *pngle, const void *buf, size_t len); // returns -1: On error, 0: Need more data, n: n bytes eaten
|
||||
|
||||
uint32_t pngle_get_width(pngle_t *pngle);
|
||||
uint32_t pngle_get_height(pngle_t *pngle);
|
||||
|
||||
void pngle_set_init_callback(pngle_t *png, pngle_init_callback_t callback);
|
||||
void pngle_set_draw_callback(pngle_t *png, pngle_draw_callback_t callback);
|
||||
void pngle_set_done_callback(pngle_t *png, pngle_done_callback_t callback);
|
||||
|
||||
void pngle_set_display_gamma(pngle_t *pngle, double display_gamma); // enables gamma correction by specifying display gamma, typically 2.2. No effect when gAMA chunk is missing
|
||||
|
||||
void pngle_set_user_data(pngle_t *pngle, void *user_data);
|
||||
void *pngle_get_user_data(pngle_t *pngle);
|
||||
|
||||
|
||||
// ----------------
|
||||
// Debug interfaces
|
||||
// ----------------
|
||||
|
||||
typedef struct _pngle_ihdr_t {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint8_t depth;
|
||||
uint8_t color_type;
|
||||
uint8_t compression;
|
||||
uint8_t filter;
|
||||
uint8_t interlace;
|
||||
} pngle_ihdr_t;
|
||||
|
||||
// Get IHDR information
|
||||
pngle_ihdr_t *pngle_get_ihdr(pngle_t *pngle);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __PNGLE_H__ */
|
|
@ -1 +1,19 @@
|
|||
#include "Inkplate.h"
|
||||
#include "generatedUI.h"
|
||||
|
||||
Inkplate display(INKPLATE_3BIT);
|
||||
|
||||
void setup()
|
||||
{
|
||||
display.begin();
|
||||
mainDraw();
|
||||
int x[] = {600, 700, 800, 700, 600}, y[] = {200, 100, 400, 500, 300};
|
||||
int x2[] = {600, 700, 800, 700, 600}, y2[] = {400, 300, 600, 600, 500};
|
||||
display.drawPolygon(x, y, 5, 0);
|
||||
display.fillPolygon(x2, y2, 5, 0);
|
||||
display.display();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
}
|
|
@ -0,0 +1,300 @@
|
|||
#include "Arduino.h"
|
||||
#include "Inkplate.h"
|
||||
#include "../res/Fonts/FreeSansBold24pt7b.h"
|
||||
|
||||
extern Inkplate display;
|
||||
|
||||
String text0_content = "Hello there!";
|
||||
int text0_cursor_x = 15;
|
||||
int text0_cursor_y = 33;
|
||||
const GFXfont *text0_font = &FreeSansBold24pt7b;
|
||||
|
||||
int pixel0_x = 17;
|
||||
int pixel0_y = 56;
|
||||
int pixel0_color = 0;
|
||||
|
||||
int line0_start_x = 17;
|
||||
int line0_start_y = 75;
|
||||
int line0_end_x = 171;
|
||||
int line0_end_y = 72;
|
||||
int line0_color = 0;
|
||||
int line0_thickness = 1;
|
||||
int line0_gradient = 0;
|
||||
|
||||
int rect0_a_x = 18;
|
||||
int rect0_a_y = 91;
|
||||
int rect0_b_x = 170;
|
||||
int rect0_b_y = 136;
|
||||
int rect0_fill = -1;
|
||||
int rect0_radius = -1;
|
||||
int rect0_color = 0;
|
||||
|
||||
int rect1_a_x = 20;
|
||||
int rect1_a_y = 153;
|
||||
int rect1_b_x = 171;
|
||||
int rect1_b_y = 201;
|
||||
int rect1_fill = -1;
|
||||
int rect1_radius = 15;
|
||||
int rect1_color = 0;
|
||||
|
||||
int rect2_a_x = 22;
|
||||
int rect2_a_y = 220;
|
||||
int rect2_b_x = 174;
|
||||
int rect2_b_y = 269;
|
||||
int rect2_fill = 1;
|
||||
int rect2_radius = -1;
|
||||
int rect2_color = 3;
|
||||
|
||||
int rect3_a_x = 22;
|
||||
int rect3_a_y = 290;
|
||||
int rect3_b_x = 175;
|
||||
int rect3_b_y = 336;
|
||||
int rect3_fill = 1;
|
||||
int rect3_radius = 20;
|
||||
int rect3_color = 2;
|
||||
|
||||
int line1_start_x = 197;
|
||||
int line1_start_y = 10;
|
||||
int line1_end_x = 200;
|
||||
int line1_end_y = 334;
|
||||
int line1_color = 4;
|
||||
int line1_thickness = 5;
|
||||
int line1_gradient = 0;
|
||||
|
||||
int line2_start_x = 222;
|
||||
int line2_start_y = 12;
|
||||
int line2_end_x = 228;
|
||||
int line2_end_y = 410;
|
||||
int line2_color = 0;
|
||||
int line2_thickness = 10;
|
||||
int line2_gradient = 7;
|
||||
|
||||
int circle0_center_x = 110;
|
||||
int circle0_center_y = 455;
|
||||
int circle0_fill = -1;
|
||||
int circle0_radius = 100;
|
||||
int circle0_color = 0;
|
||||
|
||||
int circle1_center_x = 109;
|
||||
int circle1_center_y = 454;
|
||||
int circle1_fill = 1;
|
||||
int circle1_radius = 50;
|
||||
int circle1_color = 3;
|
||||
|
||||
int triangle0_a_x = 226;
|
||||
int triangle0_a_y = 424;
|
||||
int triangle0_b_x = 361;
|
||||
int triangle0_b_y = 491;
|
||||
int triangle0_c_x = 228;
|
||||
int triangle0_c_y = 581;
|
||||
int triangle0_fill = 1;
|
||||
int triangle0_radius = -1;
|
||||
int triangle0_color = 4;
|
||||
|
||||
int triangle1_a_x = 257;
|
||||
int triangle1_a_y = 409;
|
||||
int triangle1_b_x = 374;
|
||||
int triangle1_b_y = 479;
|
||||
int triangle1_c_x = 252;
|
||||
int triangle1_c_y = 194;
|
||||
int triangle1_fill = -1;
|
||||
int triangle1_radius = -1;
|
||||
int triangle1_color = 0;
|
||||
|
||||
int digital_clock0_h = 9;
|
||||
int digital_clock0_m = 41;
|
||||
int digital_clock0_location_x = 248;
|
||||
int digital_clock0_location_y = 12;
|
||||
int digital_clock0_size = 64;
|
||||
int digital_clock0_bitmask[] = {119, 48, 93, 121, 58, 107, 111, 49, 127, 59};
|
||||
int digital_clock0_triangleX[] = {83, 101, 108, 101, 108, 277, 101, 108, 277, 257, 277, 108, 257, 277, 286, 76, 60, 98, 60, 98, 80, 80, 39, 60, 80, 39, 55, 31, 55, 73, 31, 73, 52, 31, 9, 52, 9, 52, 20, 61, 86, 80, 86, 80, 233, 233, 227, 80, 233, 227, 252, 260, 292, 305, 305, 260, 240, 305, 281, 240, 240, 281, 260, 259, 234, 276, 234, 276, 256, 256, 214, 234, 214, 256, 237, 38, 27, 60, 38, 60, 207, 207, 38, 212, 212, 207, 230};
|
||||
int digital_clock0_triangleY[] = {30, 13, 60, 13, 60, 14, 13, 60, 14, 57, 14, 60, 57, 14, 29, 36, 47, 61, 47, 61, 198, 198, 201, 47, 198, 201, 219, 252, 232, 253, 252, 253, 390, 252, 406, 390, 406, 390, 416, 227, 202, 249, 202, 249, 203, 203, 247, 249, 203, 247, 224, 60, 35, 49, 49, 60, 200, 50, 201, 200, 200, 201, 220, 231, 252, 252, 252, 252, 403, 403, 390, 252, 390, 403, 415, 439, 424, 392, 439, 392, 394, 394, 439, 439, 439, 394, 424};
|
||||
int digital_clock0_maxX = 310;
|
||||
int digital_clock0_maxY = 440;
|
||||
|
||||
int widget1_h = 9;
|
||||
int widget1_m = 41;
|
||||
int widget1_center_x = 290;
|
||||
int widget1_center_y = 126;
|
||||
int widget1_size = 64;
|
||||
int widget1_r0 = (double)widget1_size / 2 * 0.55;
|
||||
int widget1_r1 = (double)widget1_size / 2 * 0.65;
|
||||
int widget1_r2 = (double)widget1_size / 2 * 0.9;
|
||||
int widget1_r3 = (double)widget1_size / 2 * 1.0;
|
||||
|
||||
int widget2_h = 9;
|
||||
int widget2_m = 41;
|
||||
int widget2_center_x = 386;
|
||||
int widget2_center_y = 231;
|
||||
int widget2_size = 151;
|
||||
int widget2_r0 = (double)widget2_size / 2 * 0.55;
|
||||
int widget2_r1 = (double)widget2_size / 2 * 0.65;
|
||||
int widget2_r2 = (double)widget2_size / 2 * 0.9;
|
||||
int widget2_r3 = (double)widget2_size / 2 * 1.0;
|
||||
|
||||
void mainDraw()
|
||||
{
|
||||
display.setFont(text0_font);
|
||||
display.setTextColor(0, 7);
|
||||
display.setTextSize(1);
|
||||
display.setCursor(text0_cursor_x, text0_cursor_y);
|
||||
display.print(text0_content);
|
||||
|
||||
display.drawPixel(pixel0_x, pixel0_y, pixel0_color);
|
||||
|
||||
if (line0_gradient <= line0_color && line0_thickness == 1)
|
||||
display.drawLine(line0_start_x, line0_start_y, line0_end_x, line0_end_y, line0_color);
|
||||
else if (line0_gradient <= line0_color && line0_thickness != 1)
|
||||
display.drawThickLine(line0_start_x, line0_start_y, line0_end_x, line0_end_y, line0_color, line0_thickness);
|
||||
else if (line0_gradient > line0_color && line0_thickness == 1)
|
||||
display.drawGradientLine(line0_start_x, line0_start_y, line0_end_x, line0_end_y, line0_color, line0_gradient, 1);
|
||||
else if (line0_gradient > line0_color && line0_thickness != 1)
|
||||
display.drawGradientLine(line0_start_x, line0_start_y, line0_end_x, line0_end_y, line0_color, line0_gradient, line0_thickness);
|
||||
|
||||
if (rect0_radius != -1 && rect0_fill != -1)
|
||||
display.fillRoundRect(rect0_a_x, rect0_a_y, rect0_b_x - rect0_a_x, rect0_b_y - rect0_a_y, rect0_radius, rect0_color);
|
||||
else if (rect0_radius != -1 && rect0_fill == -1)
|
||||
display.drawRoundRect(rect0_a_x, rect0_a_y, rect0_b_x - rect0_a_x, rect0_b_y - rect0_a_y, rect0_radius, rect0_color);
|
||||
else if (rect0_radius == -1 && rect0_fill != -1)
|
||||
display.fillRect(rect0_a_x, rect0_a_y, rect0_b_x - rect0_a_x, rect0_b_y - rect0_a_y, rect0_color);
|
||||
else if (rect0_radius == -1 && rect0_fill == -1)
|
||||
display.drawRect(rect0_a_x, rect0_a_y, rect0_b_x - rect0_a_x, rect0_b_y - rect0_a_y, rect0_color);
|
||||
|
||||
if (rect1_radius != -1 && rect1_fill != -1)
|
||||
display.fillRoundRect(rect1_a_x, rect1_a_y, rect1_b_x - rect1_a_x, rect1_b_y - rect1_a_y, rect1_radius, rect1_color);
|
||||
else if (rect1_radius != -1 && rect1_fill == -1)
|
||||
display.drawRoundRect(rect1_a_x, rect1_a_y, rect1_b_x - rect1_a_x, rect1_b_y - rect1_a_y, rect1_radius, rect1_color);
|
||||
else if (rect1_radius == -1 && rect1_fill != -1)
|
||||
display.fillRect(rect1_a_x, rect1_a_y, rect1_b_x - rect1_a_x, rect1_b_y - rect1_a_y, rect1_color);
|
||||
else if (rect1_radius == -1 && rect1_fill == -1)
|
||||
display.drawRect(rect1_a_x, rect1_a_y, rect1_b_x - rect1_a_x, rect1_b_y - rect1_a_y, rect1_color);
|
||||
|
||||
if (rect2_radius != -1 && rect2_fill != -1)
|
||||
display.fillRoundRect(rect2_a_x, rect2_a_y, rect2_b_x - rect2_a_x, rect2_b_y - rect2_a_y, rect2_radius, rect2_color);
|
||||
else if (rect2_radius != -1 && rect2_fill == -1)
|
||||
display.drawRoundRect(rect2_a_x, rect2_a_y, rect2_b_x - rect2_a_x, rect2_b_y - rect2_a_y, rect2_radius, rect2_color);
|
||||
else if (rect2_radius == -1 && rect2_fill != -1)
|
||||
display.fillRect(rect2_a_x, rect2_a_y, rect2_b_x - rect2_a_x, rect2_b_y - rect2_a_y, rect2_color);
|
||||
else if (rect2_radius == -1 && rect2_fill == -1)
|
||||
display.drawRect(rect2_a_x, rect2_a_y, rect2_b_x - rect2_a_x, rect2_b_y - rect2_a_y, rect2_color);
|
||||
|
||||
if (rect3_radius != -1 && rect3_fill != -1)
|
||||
display.fillRoundRect(rect3_a_x, rect3_a_y, rect3_b_x - rect3_a_x, rect3_b_y - rect3_a_y, rect3_radius, rect3_color);
|
||||
else if (rect3_radius != -1 && rect3_fill == -1)
|
||||
display.drawRoundRect(rect3_a_x, rect3_a_y, rect3_b_x - rect3_a_x, rect3_b_y - rect3_a_y, rect3_radius, rect3_color);
|
||||
else if (rect3_radius == -1 && rect3_fill != -1)
|
||||
display.fillRect(rect3_a_x, rect3_a_y, rect3_b_x - rect3_a_x, rect3_b_y - rect3_a_y, rect3_color);
|
||||
else if (rect3_radius == -1 && rect3_fill == -1)
|
||||
display.drawRect(rect3_a_x, rect3_a_y, rect3_b_x - rect3_a_x, rect3_b_y - rect3_a_y, rect3_color);
|
||||
|
||||
if (line1_gradient <= line1_color && line1_thickness == 1)
|
||||
display.drawLine(line1_start_x, line1_start_y, line1_end_x, line1_end_y, line1_color);
|
||||
else if (line1_gradient <= line1_color && line1_thickness != 1)
|
||||
display.drawThickLine(line1_start_x, line1_start_y, line1_end_x, line1_end_y, line1_color, line1_thickness);
|
||||
else if (line1_gradient > line1_color && line1_thickness == 1)
|
||||
display.drawGradientLine(line1_start_x, line1_start_y, line1_end_x, line1_end_y, line1_color, line1_gradient, 1);
|
||||
else if (line1_gradient > line1_color && line1_thickness != 1)
|
||||
display.drawGradientLine(line1_start_x, line1_start_y, line1_end_x, line1_end_y, line1_color, line1_gradient, line1_thickness);
|
||||
|
||||
if (line2_gradient <= line2_color && line2_thickness == 1)
|
||||
display.drawLine(line2_start_x, line2_start_y, line2_end_x, line2_end_y, line2_color);
|
||||
else if (line2_gradient <= line2_color && line2_thickness != 1)
|
||||
display.drawThickLine(line2_start_x, line2_start_y, line2_end_x, line2_end_y, line2_color, line2_thickness);
|
||||
else if (line2_gradient > line2_color && line2_thickness == 1)
|
||||
display.drawGradientLine(line2_start_x, line2_start_y, line2_end_x, line2_end_y, line2_color, line2_gradient, 1);
|
||||
else if (line2_gradient > line2_color && line2_thickness != 1)
|
||||
display.drawGradientLine(line2_start_x, line2_start_y, line2_end_x, line2_end_y, line2_color, line2_gradient, line2_thickness);
|
||||
|
||||
if (circle0_fill != -1)
|
||||
display.fillCircle(circle0_center_x, circle0_center_y, circle0_radius, circle0_color);
|
||||
else
|
||||
display.drawCircle(circle0_center_x, circle0_center_y, circle0_radius, circle0_color);
|
||||
|
||||
if (circle1_fill != -1)
|
||||
display.fillCircle(circle1_center_x, circle1_center_y, circle1_radius, circle1_color);
|
||||
else
|
||||
display.drawCircle(circle1_center_x, circle1_center_y, circle1_radius, circle1_color);
|
||||
|
||||
if (triangle0_fill != -1)
|
||||
display.fillTriangle(triangle0_a_x, triangle0_a_y, triangle0_b_x, triangle0_b_y, triangle0_c_x, triangle0_c_y, triangle0_color);
|
||||
else
|
||||
display.drawTriangle(triangle0_a_x, triangle0_a_y, triangle0_b_x, triangle0_b_y, triangle0_c_x, triangle0_c_y, triangle0_color);
|
||||
|
||||
if (triangle1_fill != -1)
|
||||
display.fillTriangle(triangle1_a_x, triangle1_a_y, triangle1_b_x, triangle1_b_y, triangle1_c_x, triangle1_c_y, triangle1_color);
|
||||
else
|
||||
display.drawTriangle(triangle1_a_x, triangle1_a_y, triangle1_b_x, triangle1_b_y, triangle1_c_x, triangle1_c_y, triangle1_color);
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
for (int j = 0; j < sizeof(digital_clock0_triangleX) / sizeof(digital_clock0_triangleX[0]); j += 3)
|
||||
{
|
||||
int temp[4] = {digital_clock0_h / 10 % 10, digital_clock0_h % 10, digital_clock0_m / 10 % 10, digital_clock0_m % 10};
|
||||
int b = digital_clock0_bitmask[temp[i]];
|
||||
if (b & (1 << ((j - 1) / (3 * 4))))
|
||||
{
|
||||
display.fillTriangle(
|
||||
(int)((float)i * (float)digital_clock0_maxX / (float)digital_clock0_maxY * (float)digital_clock0_size * 1.1 + (float)digital_clock0_location_x + (float)digital_clock0_maxX / (float)digital_clock0_maxY * (float)digital_clock0_size * (float)digital_clock0_triangleX[j + 0] / (float)digital_clock0_maxX),
|
||||
(int)((float)digital_clock0_location_y + (float)digital_clock0_size * (float)digital_clock0_triangleY[j + 0] / (float)digital_clock0_maxY),
|
||||
|
||||
(int)((float)i * (float)digital_clock0_maxX / (float)digital_clock0_maxY * (float)digital_clock0_size * 1.1 + (float)digital_clock0_location_x + (float)digital_clock0_maxX / (float)digital_clock0_maxY * (float)digital_clock0_size * (float)digital_clock0_triangleX[j + 1] / (float)digital_clock0_maxX),
|
||||
(int)((float)digital_clock0_location_y + (float)digital_clock0_size * (float)digital_clock0_triangleY[j + 1] / (float)digital_clock0_maxY),
|
||||
|
||||
(int)((float)i * (float)digital_clock0_maxX / (float)digital_clock0_maxY * (float)digital_clock0_size * 1.1 + (float)digital_clock0_location_x + (float)digital_clock0_maxX / (float)digital_clock0_maxY * (float)digital_clock0_size * (float)digital_clock0_triangleX[j + 2] / (float)digital_clock0_maxX),
|
||||
(int)((float)digital_clock0_location_y + (float)digital_clock0_size * (float)digital_clock0_triangleY[j + 2] / (float)digital_clock0_maxY),
|
||||
|
||||
0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int digital_clock0_r = 0.05 * (float)digital_clock0_size;
|
||||
|
||||
display.fillCircle((int)((float)digital_clock0_location_x + 4.0 * (float)digital_clock0_maxX / (float)digital_clock0_maxY * (float)digital_clock0_size * 1.075 / 2.0), (int)((float)digital_clock0_location_y + (float)digital_clock0_size * 0.4), digital_clock0_r, 0);
|
||||
display.fillCircle((int)((float)digital_clock0_location_x + 4.0 * (float)digital_clock0_maxX / (float)digital_clock0_maxY * (float)digital_clock0_size * 1.075 / 2.0), (int)((float)digital_clock0_location_y + (float)digital_clock0_size * 0.6), digital_clock0_r, 0);
|
||||
for (int i = 0; i < 60; ++i)
|
||||
{
|
||||
if (i % 5 == 0)
|
||||
display.drawThickLine(widget1_center_x + widget1_r1 * cos((double)i / 60.0 * 2.0 * 3.14159265),
|
||||
widget1_center_y + widget1_r1 * sin((double)i / 60.0 * 2.0 * 3.14159265),
|
||||
widget1_center_x + widget1_r3 * cos((double)i / 60.0 * 2.0 * 3.14159265),
|
||||
widget1_center_y + widget1_r3 * sin((double)i / 60.0 * 2.0 * 3.14159265), 0, 3);
|
||||
else if (widget1_size > 150)
|
||||
display.drawLine(widget1_center_x + widget1_r1 * cos((double)i / 60.0 * 2.0 * 3.14159265),
|
||||
widget1_center_y + widget1_r1 * sin((double)i / 60.0 * 2.0 * 3.14159265),
|
||||
widget1_center_x + widget1_r2 * cos((double)i / 60.0 * 2.0 * 3.14159265),
|
||||
widget1_center_y + widget1_r2 * sin((double)i / 60.0 * 2.0 * 3.14159265), 2);
|
||||
}
|
||||
display.drawThickLine(widget1_center_x,
|
||||
widget1_center_y,
|
||||
widget1_center_x + widget1_r0 * cos((double)(widget1_h - 3.0 + widget1_m / 60.0) / 12.0 * 2.0 * 3.14159265),
|
||||
widget1_center_y + widget1_r0 * sin((double)(widget1_h - 3.0 + widget1_m / 60.0) / 12.0 * 2.0 * 3.14159265), 2, 2);
|
||||
|
||||
display.drawThickLine(widget1_center_x,
|
||||
widget1_center_y,
|
||||
widget1_center_x + widget1_r2 * cos((double)(widget1_m - 15.0) / 60.0 * 2.0 * 3.14159265),
|
||||
widget1_center_y + widget1_r2 * sin((double)(widget1_m - 15.0) / 60.0 * 2.0 * 3.14159265), 2, 2);
|
||||
|
||||
for (int i = 0; i < 60; ++i)
|
||||
{
|
||||
if (i % 5 == 0)
|
||||
display.drawThickLine(widget2_center_x + widget2_r1 * cos((double)i / 60.0 * 2.0 * 3.14159265),
|
||||
widget2_center_y + widget2_r1 * sin((double)i / 60.0 * 2.0 * 3.14159265),
|
||||
widget2_center_x + widget2_r3 * cos((double)i / 60.0 * 2.0 * 3.14159265),
|
||||
widget2_center_y + widget2_r3 * sin((double)i / 60.0 * 2.0 * 3.14159265), 0, 3);
|
||||
else if (widget2_size > 150)
|
||||
display.drawLine(widget2_center_x + widget2_r1 * cos((double)i / 60.0 * 2.0 * 3.14159265),
|
||||
widget2_center_y + widget2_r1 * sin((double)i / 60.0 * 2.0 * 3.14159265),
|
||||
widget2_center_x + widget2_r2 * cos((double)i / 60.0 * 2.0 * 3.14159265),
|
||||
widget2_center_y + widget2_r2 * sin((double)i / 60.0 * 2.0 * 3.14159265), 2);
|
||||
}
|
||||
display.drawThickLine(widget2_center_x,
|
||||
widget2_center_y,
|
||||
widget2_center_x + widget2_r0 * cos((double)(widget2_h - 3.0 + widget2_m / 60.0) / 12.0 * 2.0 * 3.14159265),
|
||||
widget2_center_y + widget2_r0 * sin((double)(widget2_h - 3.0 + widget2_m / 60.0) / 12.0 * 2.0 * 3.14159265), 2, 2);
|
||||
|
||||
display.drawThickLine(widget2_center_x,
|
||||
widget2_center_y,
|
||||
widget2_center_x + widget2_r2 * cos((double)(widget2_m - 15.0) / 60.0 * 2.0 * 3.14159265),
|
||||
widget2_center_y + widget2_r2 * sin((double)(widget2_m - 15.0) / 60.0 * 2.0 * 3.14159265), 2, 2);
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
#include "Inkplate.h"
|
||||
#include "SdFat.h"
|
||||
|
||||
const int n = 10;
|
||||
const int n = 500;
|
||||
|
||||
Inkplate display(INKPLATE_1BIT);
|
||||
|
||||
|
|
Loading…
Reference in New Issue