I'm having a bit of a weird issue - in JPEGDEC\examples\epd_demo\epd_demo.ino, you've done dithering for the 565 pixel format that the jpeg.c code produces.
Looking through the header file, I saw there was a jpeg.setPixelType function, that I've tried passing (FOUR_BIT_DITHERED) to.
I see in jpeg.c, that it applies Floyd-Steinberg dithering to the output if the pixel type is of the correct type. I'm not calling it directly, but using jpeg.setPixelType(FOUR_BIT_DITHERED) , and then jpeg.decode(0, 0, 0); but had no success...
If you could spare a little while to have a look at the code for me, could you show me how to get the jpeg.c outputting that lovely FS dithering it's got in it?
Thank you lots!
Line 3101:
// Dither the 8-bit gray pixels into 1, 2, or 4-bit gray
static void JPEGDither(JPEGIMAGE *pJPEG, int iWidth, int iHeight)
/*
https://github.com/wizche/flip-pics
https://github.com/bitbank2/JPEGDEC
*/
#include <M5EPD.h>
#include <memory>
#include <stdexcept>
#include <SD.h>
#include <iostream>
#include <vector>
#include <sstream>
#include <string>
#include <JPEGDEC.h>
#define SLEEP_HOURS 1
bool dither = true;
JPEGDEC jpeg;
M5EPD_Canvas canvas(&M5.EPD);
const char *DATA_FILE = "/data.txt";
uint32_t lastCount;
//###############
// Callbacks for the jpeg decoding.
File myfile;
void * myOpen(const char *filename, int32_t *size) {
myfile = SD.open(filename);
*size = myfile.size();
return &myfile;
}
void myClose(void *handle) {
if (myfile) myfile.close();
}
int32_t myRead(JPEGFILE *handle, uint8_t *buffer, int32_t length) {
if (!myfile) return 0;
return myfile.read(buffer, length);
}
int32_t mySeek(JPEGFILE *handle, int32_t position) {
if (!myfile) return 0;
return myfile.seek(position);
}
//###############
bool has_suffix(const std::string &str, const std::string &suffix) {
return str.size() >= suffix.size() &&
str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}
bool is_jpg(const std::string &filename) {
return (has_suffix(filename, ".jpg") || has_suffix(filename, ".jpeg"));
}
bool is_valid_image(const std::string &filename) {
return is_jpg(filename);
}
std::vector<std::string> split(const std::string &s, char delimiter) {
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream(s);
while (std::getline(tokenStream, token, delimiter)) {
tokens.push_back(token);
}
return tokens;
}
void storeCountSD(uint32_t c) {
auto f = SD.open(DATA_FILE, "wb");
uint8_t buf[4];
buf[0] = c;
buf[1] = c >> 8;
buf[2] = c >> 16;
buf[3] = c >> 24;
auto bytes = f.write(&buf[0], 4);
f.close();
}
uint32_t getCountSD() {
uint32_t val;
if (SD.exists(DATA_FILE)) {
auto f = SD.open(DATA_FILE, "rb");
val = f.read();
f.close();
}
else {
val = 0;
}
return val;
}
void drawTempHumidityBattery() {
char batteryBuffer[20];
uint32_t vol = M5.getBatteryVoltage();
if (vol < 3300) {
vol = 3300;
}
else if (vol > 4350) {
vol = 4350;
}
float battery = (float)(vol - 3300) / (float)(4350 - 3300);
if (battery <= 0.01) {
battery = 0.01;
}
if (battery > 1) {
battery = 1;
}
sprintf(batteryBuffer, "%d%%", (int)(battery * 100));
char statusBuffer[256] = "CHARGING";
M5.SHT30.UpdateData();
float tem = M5.SHT30.GetTemperature();
float hum = M5.SHT30.GetRelHumidity();
sprintf(statusBuffer, "%2.2fC | %0.2f%% | %s", tem, hum, batteryBuffer);
canvas.drawRightString(statusBuffer, 960, 0, 1);
}
int JPEGDraw(JPEGDRAW *pDraw)
{
int x = pDraw->x;
int y = pDraw->y;
int w = pDraw->iWidth;
int h = pDraw->iHeight;
/* Serial.println("---");
Serial.println(x);
Serial.println(y);
Serial.println(w);
Serial.println(h);*/
uint32_t rgb = 0;
for (int i = 0; i < 4; i++) {
Serial.print(" ------ ");
Serial.print(i);
Serial.print(" - ");
rgb = pDraw->pPixels[i];
Serial.print(rgb & 0xff);
Serial.print(", ");
Serial.print((rgb >> 8) & 0xff);
Serial.print(", ");
Serial.print((rgb >> 16) & 0xff);
Serial.print(", ");
Serial.println((rgb >> 24) & 0xff);
}
//M5.EPD.WritePartGram4bpp(x, y, w, h, (uint8_t) pDraw->pPixels);
for (int16_t i = 0; i < w; i++){
for (int16_t j = 0; j < h; j++){
//canvas.drawPixel(x + i, y + j, 255 - ((pDraw->pPixels[i + j * w]) & 0xff));
// 0 - 255 brightness, amended to 16 shades in the function
// This currently produces a suitable greyscale that is sadly undithered. Concerningly, I'm inversing the value to be displayed correctly!
canvas.drawPixel(x + i, y + j, (255 - ((pDraw->pPixels[i + j * w]) & 0xff)) >> 1);
*/
} // for j
} // for i
return 1;
} /* JPEGDraw() */
void load_image() {
File root = SD.open("/");
if (!root) {
Serial.printf("Failed to open directory");
return;
}
if (!root.isDirectory()) {
Serial.printf("Not a directory");
return;
}
String lastFile;
lastCount = getCountSD();
uint32_t currentCount = 0;
while (true) {
File file = root.openNextFile();
if (!file) {
Serial.printf("Reached end of directory, restart!\n");
lastCount = 0;
currentCount = 0;
root.rewindDirectory();
continue;
}
if (is_valid_image(file.name())) {
currentCount++;
}
else {
continue;
}
if (currentCount > lastCount) {
if (is_jpg(file.name())) {
Serial.printf("Filename %s\n", file.name());
//canvas.drawJpgFile(SD, file.name(), 0, 0, 960, 540, 0, 0, JPEG_DIV_NONE);
myfile = file;
jpeg.open((const char *)file.name(), myOpen, myClose, myRead, mySeek, JPEGDraw);
jpeg.decode(0, 0, 0);
jpeg.close();
}
else {
Serial.printf("Something went wrong!\n");
break;
}
lastCount = currentCount;
//canvas.drawRightString(basename.c_str(), 960, 540, 1);
canvas.drawRightString(file.name(), 960, 540, 1);
break;
}
}
storeCountSD(lastCount);
drawTempHumidityBattery();
canvas.pushCanvas(0, 0, UPDATE_MODE_GC16);
}
void setup() {
M5.begin();
M5.EPD.SetRotation(0);
M5.EPD.Clear(1);
M5.RTC.begin();
// jpeg.setPixelType(FOUR_BIT_DITHERED); //<< this should do Floyd Steinberg dithering, but isn't.
canvas.createCanvas(960, 540);
canvas.setTextSize(2);
load_image();
}
void loop() {
int tick = 200;
while ((--tick) > 0) {
delay(500);
M5.update();
if (M5.BtnL.wasPressed()) {
Serial.println("Previous image.");
tick = 0;
uint32_t lastCount = getCountSD();
lastCount -= 2;
if ((lastCount) < 0) lastCount = 0;
storeCountSD(lastCount);
}
if (M5.BtnP.wasPressed()) {
Serial.println("Turning off.");
canvas.drawRightString("Off", 960, 540, 1);
canvas.pushCanvas(0, 0, UPDATE_MODE_GC16);
M5.shutdown();
}
if (M5.BtnR.wasPressed()) {
Serial.println("Next image.");
tick = 0;
}
}
load_image();
}
I'm sorry I didn't provide a dither example with the latest code; I'll see if I can add one soon.
To get dithered output you need to call decodeDither() instead of decode(). See the Wiki for more details.
Hi!
I love this utility you wrote. I've crossed it with a photo-frame app for the M5 Paper I've got. https://shop.m5stack.com/products/m5paper-esp32-development-kit-960x540-4-7-eink-display-235-ppi It's 960 x 540 and 16 shades. (4bit)
I'm having a bit of a weird issue - in JPEGDEC\examples\epd_demo\epd_demo.ino, you've done dithering for the 565 pixel format that the jpeg.c code produces.
Looking through the header file, I saw there was a jpeg.setPixelType function, that I've tried passing (FOUR_BIT_DITHERED) to. I see in jpeg.c, that it applies Floyd-Steinberg dithering to the output if the pixel type is of the correct type. I'm not calling it directly, but using jpeg.setPixelType(FOUR_BIT_DITHERED) , and then jpeg.decode(0, 0, 0); but had no success...
If you could spare a little while to have a look at the code for me, could you show me how to get the jpeg.c outputting that lovely FS dithering it's got in it?
Thank you lots!
Line 3101: