230 lines
6.2 KiB
C++
230 lines
6.2 KiB
C++
/**
|
|
* Marlin 3D Printer Firmware
|
|
* Copyright (c) 2022 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
|
*
|
|
* Based on Sprinter and grbl.
|
|
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* DWIN G-code thumbnail preview
|
|
* Author: Miguel A. Risco-Castillo
|
|
* version: 3.3.2
|
|
* Date: 2023/06/18
|
|
*/
|
|
|
|
#include "../../../inc/MarlinConfigPre.h"
|
|
|
|
#if ALL(DWIN_LCD_PROUI, HAS_GCODE_PREVIEW)
|
|
|
|
#include "gcode_preview.h"
|
|
|
|
#include "../../../core/types.h"
|
|
#include "../../marlinui.h"
|
|
#include "../../../sd/cardreader.h"
|
|
#include "../../../MarlinCore.h" // for wait_for_user
|
|
#include "dwin.h"
|
|
#include "dwin_popup.h"
|
|
#include "base64.hpp"
|
|
|
|
#define THUMBWIDTH 230
|
|
#define THUMBHEIGHT 180
|
|
|
|
Preview preview;
|
|
|
|
typedef struct {
|
|
char name[13] = ""; // 8.3 + null
|
|
uint32_t thumbstart = 0;
|
|
int thumbsize = 0;
|
|
int thumbheight = 0, thumbwidth = 0;
|
|
float time = 0;
|
|
float filament = 0;
|
|
float layer = 0;
|
|
float width = 0, height = 0, length = 0;
|
|
|
|
void setname(const char * const fn) {
|
|
const uint8_t len = _MIN(sizeof(name) - 1, strlen(fn));
|
|
memcpy(name, fn, len);
|
|
name[len] = '\0';
|
|
}
|
|
|
|
void clear() {
|
|
fileprop.name[0] = '\0';
|
|
fileprop.thumbstart = 0;
|
|
fileprop.thumbsize = 0;
|
|
fileprop.thumbheight = fileprop.thumbwidth = 0;
|
|
fileprop.time = 0;
|
|
fileprop.filament = 0;
|
|
fileprop.layer = 0;
|
|
fileprop.height = fileprop.width = fileprop.length = 0;
|
|
}
|
|
|
|
} fileprop_t;
|
|
|
|
fileprop_t fileprop;
|
|
|
|
void getValue(const char * const buf, PGM_P const key, float &value) {
|
|
if (value != 0.0f) return;
|
|
|
|
char *posptr = strstr_P(buf, key);
|
|
if (posptr == nullptr) return;
|
|
|
|
char num[10] = "";
|
|
for (uint8_t i = 0; i < sizeof(num);) {
|
|
const char c = *posptr;
|
|
if (ISEOL(c) || c == '\0') {
|
|
num[i] = '\0';
|
|
value = atof(num);
|
|
break;
|
|
}
|
|
if (WITHIN(c, '0', '9') || c == '.') num[i++] = c;
|
|
posptr++;
|
|
}
|
|
}
|
|
|
|
bool Preview::hasPreview() {
|
|
const char * const tbstart = PSTR("; thumbnail begin " STRINGIFY(THUMBWIDTH) "x" STRINGIFY(THUMBHEIGHT));
|
|
char *posptr = nullptr;
|
|
uint32_t indx = 0;
|
|
float tmp = 0;
|
|
|
|
fileprop.clear();
|
|
fileprop.setname(card.filename);
|
|
|
|
card.openFileRead(fileprop.name);
|
|
|
|
char buf[256];
|
|
uint8_t nbyte = 1;
|
|
while (!fileprop.thumbstart && nbyte > 0 && indx < 4 * sizeof(buf)) {
|
|
nbyte = card.read(buf, sizeof(buf) - 1);
|
|
if (nbyte > 0) {
|
|
buf[nbyte] = '\0';
|
|
getValue(buf, PSTR(";TIME:"), fileprop.time);
|
|
getValue(buf, PSTR(";Filament used:"), fileprop.filament);
|
|
getValue(buf, PSTR(";Layer height:"), fileprop.layer);
|
|
getValue(buf, PSTR(";MINX:"), tmp);
|
|
getValue(buf, PSTR(";MAXX:"), fileprop.width);
|
|
fileprop.width -= tmp;
|
|
tmp = 0;
|
|
getValue(buf, PSTR(";MINY:"), tmp);
|
|
getValue(buf, PSTR(";MAXY:"), fileprop.length);
|
|
fileprop.length -= tmp;
|
|
tmp = 0;
|
|
getValue(buf, PSTR(";MINZ:"), tmp);
|
|
getValue(buf, PSTR(";MAXZ:"), fileprop.height);
|
|
fileprop.height -= tmp;
|
|
posptr = strstr_P(buf, tbstart);
|
|
if (posptr != nullptr) {
|
|
fileprop.thumbstart = indx + (posptr - &buf[0]);
|
|
}
|
|
else {
|
|
indx += _MAX(10, nbyte - (signed)strlen_P(tbstart));
|
|
card.setIndex(indx);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!fileprop.thumbstart) {
|
|
card.closefile();
|
|
LCD_MESSAGE_F("Thumbnail not found");
|
|
return false;
|
|
}
|
|
|
|
// Get the size of the thumbnail
|
|
card.setIndex(fileprop.thumbstart + strlen_P(tbstart));
|
|
for (uint8_t i = 0; i < 16; i++) {
|
|
const char c = card.get();
|
|
if (ISEOL(c)) { buf[i] = '\0'; break; }
|
|
buf[i] = c;
|
|
}
|
|
fileprop.thumbsize = atoi(buf);
|
|
|
|
// Exit if there isn't a thumbnail
|
|
if (!fileprop.thumbsize) {
|
|
card.closefile();
|
|
LCD_MESSAGE_F("Invalid Thumbnail Size");
|
|
return false;
|
|
}
|
|
|
|
uint8_t buf64[fileprop.thumbsize];
|
|
uint16_t nread = 0;
|
|
while (nread < fileprop.thumbsize) {
|
|
const uint8_t c = card.get();
|
|
if (!ISEOL(c) && c != ';' && c != ' ')
|
|
buf64[nread++] = c;
|
|
}
|
|
card.closefile();
|
|
buf64[nread] = '\0';
|
|
|
|
uint8_t thumbdata[3 + 3 * (fileprop.thumbsize / 4)]; // Reserve space for the JPEG thumbnail
|
|
fileprop.thumbsize = decode_base64(buf64, thumbdata);
|
|
DWINUI::writeToSRAM(0x00, fileprop.thumbsize, thumbdata);
|
|
|
|
fileprop.thumbwidth = THUMBWIDTH;
|
|
fileprop.thumbheight = THUMBHEIGHT;
|
|
|
|
return true;
|
|
}
|
|
|
|
void Preview::drawFromSD() {
|
|
if (!hasPreview()) {
|
|
hmiFlag.select_flag = 1;
|
|
wait_for_user = false;
|
|
return;
|
|
}
|
|
|
|
MString<45> buf;
|
|
dwinDrawRectangle(1, hmiData.colorBackground, 0, 0, DWIN_WIDTH, STATUS_Y - 1);
|
|
if (fileprop.time) {
|
|
buf.setf(F("Estimated time: %i:%02i"), (uint16_t)fileprop.time / 3600, ((uint16_t)fileprop.time % 3600) / 60);
|
|
DWINUI::drawString(20, 10, &buf);
|
|
}
|
|
if (fileprop.filament) {
|
|
buf.set(F("Filament used: "), p_float_t(fileprop.filament, 2), F(" m"));
|
|
DWINUI::drawString(20, 30, &buf);
|
|
}
|
|
if (fileprop.layer) {
|
|
buf.set(F("Layer height: "), p_float_t(fileprop.layer, 2), F(" mm"));
|
|
DWINUI::drawString(20, 50, &buf);
|
|
}
|
|
if (fileprop.width) {
|
|
buf.set(F("Volume: "), p_float_t(fileprop.width, 1), 'x', p_float_t(fileprop.length, 1), 'x', p_float_t(fileprop.height, 1), F(" mm"));
|
|
DWINUI::drawString(20, 70, &buf);
|
|
}
|
|
DWINUI::drawButton(BTN_Print, 26, 290);
|
|
DWINUI::drawButton(BTN_Cancel, 146, 290);
|
|
show();
|
|
drawSelectHighlight(true, 290);
|
|
dwinUpdateLCD();
|
|
}
|
|
|
|
void Preview::invalidate() {
|
|
fileprop.thumbsize = 0;
|
|
}
|
|
|
|
bool Preview::valid() {
|
|
return !!fileprop.thumbsize;
|
|
}
|
|
|
|
void Preview::show() {
|
|
const uint8_t xpos = ((DWIN_WIDTH) - fileprop.thumbwidth) / 2,
|
|
ypos = (205 - fileprop.thumbheight) / 2 + 87;
|
|
dwinIconShow(xpos, ypos, 0x00);
|
|
}
|
|
|
|
#endif // DWIN_LCD_PROUI && HAS_GCODE_PREVIEW
|