diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index c96468415..db493270a 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -21,6 +21,9 @@ #include #include #include +#if ENABLE_THUMBNAIL_GENERATOR +#include +#endif // ENABLE_THUMBNAIL_GENERATOR #include #include @@ -951,18 +954,15 @@ void GCode::_do_export(Print &print, FILE *file) _write_format(file, "; %s\n\n", Slic3r::header_slic3r_generated().c_str()); #if ENABLE_THUMBNAIL_GENERATOR - // Write thumbnail + // Write thumbnail using base64 encoding if ((thumbnail_data != nullptr) && thumbnail_data->is_valid()) { _write_format(file, "\n;\n; thumbnail begin %dx%d\n", thumbnail_data->width, thumbnail_data->height); size_t row_size = 4 * thumbnail_data->width; - for (int r = (int)thumbnail_data->height - 1; r >= 0 ; --r) + for (int r = (int)thumbnail_data->height - 1; r >= 0; --r) { - _write(file, "; "); - const void* data_ptr = thumbnail_data->pixels.data() + r * row_size; - ::fwrite((const void*)data_ptr, 1, row_size, file); - _write(file, "\n"); + _write_format(file, "; %s\n", boost::beast::detail::base64_encode((const std::uint8_t*)(thumbnail_data->pixels.data() + r * row_size), row_size).c_str()); } _write(file, "; thumbnail end\n;\n\n"); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index b5e70c0a1..0b2244d65 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -51,6 +51,10 @@ #include #endif // __WXMSW__ +#if ENABLE_THUMBNAIL_GENERATOR +#include +#endif // ENABLE_THUMBNAIL_GENERATOR + namespace Slic3r { namespace GUI { @@ -1082,6 +1086,88 @@ bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage return res; } +#if ENABLE_THUMBNAIL_GENERATOR +void GUI_App::gcode_thumbnails_debug() +{ + const std::string BEGIN_MASK = "; thumbnail begin"; + const std::string END_MASK = "; thumbnail end"; + std::string gcode_line; + bool reading_image = false; + unsigned int width = 0; + unsigned int height = 0; + + wxFileDialog dialog(GetTopWindow(), _(L("Select a gcode file:")), "", "", "G-code files (*.gcode)|*.gcode;*.GCODE;", wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (dialog.ShowModal() != wxID_OK) + return; + + std::string in_filename = into_u8(dialog.GetPath()); + std::string out_path = boost::filesystem::path(in_filename).remove_filename().append(L"thumbnail").string(); + + boost::nowide::ifstream file(in_filename.c_str()); + std::vector rows; + if (file.good()) + { + while (std::getline(file, gcode_line)) + { + if (file.good()) + { + if (boost::starts_with(gcode_line, BEGIN_MASK)) + { + reading_image = true; + gcode_line = gcode_line.substr(BEGIN_MASK.length() + 1); + std::string::size_type x_pos = gcode_line.find('x'); + std::string width_str = gcode_line.substr(0, x_pos); + width = (unsigned int)::atoi(width_str.c_str()); + std::string height_str = gcode_line.substr(x_pos + 1); + height = (unsigned int)::atoi(height_str.c_str()); + } + else if (reading_image && boost::starts_with(gcode_line, END_MASK)) + { + if ((unsigned int)rows.size() == height) + { + std::vector thumbnail(4 * width * height, 0); + for (unsigned int r = 0; r < (unsigned int)rows.size(); ++r) + { + std::string decoded_row = boost::beast::detail::base64_decode(rows[r]); + if ((unsigned int)decoded_row.length() == width * 4) + { + void* image_ptr = (void*)(thumbnail.data() + r * width * 4); + ::memcpy(image_ptr, (const void*)decoded_row.c_str(), width * 4); + } + } + + wxImage image(width, height); + image.InitAlpha(); + + for (unsigned int r = 0; r < height; ++r) + { + unsigned int rr = r * width; + for (unsigned int c = 0; c < width; ++c) + { + unsigned char* px = thumbnail.data() + 4 * (rr + c); + image.SetRGB((int)c, (int)r, px[0], px[1], px[2]); + image.SetAlpha((int)c, (int)r, px[3]); + } + } + + image.SaveFile(out_path + std::to_string(width) + "x" + std::to_string(height) + ".png", wxBITMAP_TYPE_PNG); + } + + reading_image = false; + width = 0; + height = 0; + rows.clear(); + } + else if (reading_image) + rows.push_back(gcode_line.substr(2)); + } + } + + file.close(); + } +} +#endif // ENABLE_THUMBNAIL_GENERATOR + void GUI_App::window_pos_save(wxTopLevelWindow* window, const std::string &name) { if (name.empty()) { return; } diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index c5ddc0152..f9f907e42 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -189,6 +189,11 @@ public: void open_web_page_localized(const std::string &http_address); bool run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage start_page = ConfigWizard::SP_WELCOME); +#if ENABLE_THUMBNAIL_GENERATOR + // temporary and debug only -> extract thumbnails from selected gcode and save them as png files + void gcode_thumbnails_debug(); +#endif // ENABLE_THUMBNAIL_GENERATOR + private: bool on_init_inner(); void window_pos_save(wxTopLevelWindow* window, const std::string &name); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 6f39db86d..56eda2c0d 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -682,6 +682,11 @@ void MainFrame::init_menubar() helpMenu->AppendSeparator(); append_menu_item(helpMenu, wxID_ANY, _(L("Keyboard Shortcuts")) + sep + "&?", _(L("Show the list of the keyboard shortcuts")), [this](wxCommandEvent&) { wxGetApp().keyboard_shortcuts(); }); +#if ENABLE_THUMBNAIL_GENERATOR + helpMenu->AppendSeparator(); + append_menu_item(helpMenu, wxID_ANY, _(L("DEBUG gcode thumbnails")), _(L("DEBUG ONLY - read the selected gcode file and generates png for the contained thumbnails")), + [this](wxCommandEvent&) { wxGetApp().gcode_thumbnails_debug(); }); +#endif // ENABLE_THUMBNAIL_GENERATOR } // menubar