diff --git a/src/BambuStudio.cpp b/src/BambuStudio.cpp index f0ba74cd3..cafda36b0 100644 --- a/src/BambuStudio.cpp +++ b/src/BambuStudio.cpp @@ -1485,6 +1485,7 @@ int CLI::run(int argc, char **argv) bool first_file = true, is_bbl_3mf = false, need_arrange = true, has_thumbnails = false, up_config_to_date = false, normative_check = true, duplicate_single_object = false, use_first_fila_as_default = false, minimum_save = false, enable_timelapse = false; bool allow_rotations = true, skip_modified_gcodes = false, avoid_extrusion_cali_region = false, skip_useless_pick = false, allow_newer_file = false, current_is_multi_extruder = false, new_is_multi_extruder = false, allow_mix_temp = false; Semver file_version; + Slic3r::GUI::Camera::ViewAngleType camera_view = Slic3r::GUI::Camera::ViewAngleType::Iso; std::map orients_requirement; std::vector project_presets; std::string new_printer_name, current_printer_name, new_process_name, current_process_name, current_printer_system_name, current_process_system_name, new_process_system_name, new_printer_system_name, printer_model_id, current_printer_model, printer_model, new_default_process_name; @@ -1549,6 +1550,10 @@ int CLI::run(int argc, char **argv) if (allow_mix_temp_option) allow_mix_temp = allow_mix_temp_option->value; + ConfigOptionInt* camera_view_option = m_config.option("camera_view"); + if (camera_view_option) + camera_view = (Slic3r::GUI::Camera::ViewAngleType)(camera_view_option->value); + ConfigOptionBool* avoid_extrusion_cali_region_option = m_config.option("avoid_extrusion_cali_region"); if (avoid_extrusion_cali_region_option) avoid_extrusion_cali_region = avoid_extrusion_cali_region_option->value; @@ -5325,7 +5330,7 @@ int CLI::run(int argc, char **argv) std::string export_3mf_file, load_slice_data_dir, export_slice_data_dir, export_stls_dir; std::vector calibration_thumbnails; std::vector plate_object_count(partplate_list.get_plate_count(), 0); - int max_slicing_time_per_plate = 0, max_triangle_count_per_plate = 0, sliced_plate = -1; + int max_slicing_time_per_plate = 0, max_triangle_count_per_plate = 0, sliced_plate = -1, export_png = -1; std::vector plate_has_skips(partplate_list.get_plate_count(), false); std::vector> plate_skipped_objects(partplate_list.get_plate_count()); @@ -5537,7 +5542,16 @@ int CLI::run(int argc, char **argv) } */else if (opt_key == "export_3mf") { export_to_3mf = true; export_3mf_file = m_config.opt_string(opt_key); - }else if(opt_key=="no_check"){ + } + else if (opt_key == "export_png") { + export_png = m_config.option("export_png")->value; + if (m_actions.size() > 1) { + BOOST_LOG_TRIVIAL(error) << "should not set export_png with other actions together." << std::endl; + record_exit_reson(outfile_dir, CLI_INVALID_PARAMS, 0, cli_errors[CLI_INVALID_PARAMS], sliced_info); + flush_and_exit(CLI_INVALID_PARAMS); + } + } + else if(opt_key=="no_check"){ no_check = m_config.opt_bool(opt_key); //} else if (opt_key == "export_gcode" || opt_key == "export_sla" || opt_key == "slice") { } else if (opt_key == "normative_check") { @@ -7010,6 +7024,54 @@ int CLI::run(int argc, char **argv) for (int i = 0; i < plate_bboxes.size(); i++) delete plate_bboxes[i]; } + else if (export_png >= 0) + { + std::vector thumbnails; + PlateDataPtrs plate_data_list; + partplate_list.store_to_3mf_structure(plate_data_list); + if (!opengl_valid) + opengl_valid = init_opengl_and_colors(m_models[0], colors); + + if (opengl_valid) { + Model& model = m_models[0]; + p_opengl_mgr->bind_vao(); + p_opengl_mgr->bind_shader(shader); + for (int i = 0; i < partplate_list.get_plate_count(); i++) { + Slic3r::GUI::PartPlate* part_plate = partplate_list.get_plate(i); + PlateData* plate_data = plate_data_list[i]; + ThumbnailData* thumbnail_data = &plate_data->plate_thumbnail; + + if ((export_png != 0) && (export_png != (i + 1))) { + BOOST_LOG_TRIVIAL(info) << boost::format("Line %1%: export png, Skip plate %2%.") % __LINE__ % (i + 1); + } + else { + unsigned int thumbnail_width = 512, thumbnail_height = 512; + const ThumbnailsParams thumbnail_params = { {}, false, true, true, true, i }; + + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s png, need to generate") % (i + 1); + Slic3r::GUI::GLCanvas3D::render_thumbnail_framebuffer(p_opengl_mgr, *thumbnail_data, + thumbnail_width, thumbnail_height, thumbnail_params, + partplate_list, model.objects, glvolume_collection, colors_out, shader, Slic3r::GUI::Camera::EType::Ortho, camera_view); + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s png,finished rendering") % (i + 1); + + std::string pnf_file = "plate_" + std::to_string(i + 1) + "_" + std::to_string((int)camera_view)+".png"; + if (!outfile_dir.empty()) { + pnf_file = outfile_dir + "/" + pnf_file; + } + bool write_result = Slic3r::png::write_gl_rgba_to_file(pnf_file.c_str(), thumbnail_width, thumbnail_height, (const uint8_t*)thumbnail_data->pixels.data()); + if (write_result) + BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%'s png write to %2% success") % (i + 1) % pnf_file; + else { + BOOST_LOG_TRIVIAL(error) << boost::format("plate %1%'s png write to %2% failed") % (i + 1) % pnf_file; + record_exit_reson(outfile_dir, CLI_EXPORT_3MF_ERROR, 0, cli_errors[CLI_EXPORT_3MF_ERROR], sliced_info); + flush_and_exit(CLI_EXPORT_3MF_ERROR); + } + } + } + p_opengl_mgr->unbind_shader(); + p_opengl_mgr->unbind_vao(); + } + } if (plate_data_src.size() > 0) { diff --git a/src/libslic3r/PNGReadWrite.cpp b/src/libslic3r/PNGReadWrite.cpp index e4b7489b8..5b6e7c17e 100644 --- a/src/libslic3r/PNGReadWrite.cpp +++ b/src/libslic3r/PNGReadWrite.cpp @@ -185,7 +185,7 @@ bool decode_colored_png(const ReadBuf &in_buf, ImageColorscale &out_img) // Based on https://www.lemoda.net/c/write-png/ // png_color_type is PNG_COLOR_TYPE_RGB or PNG_COLOR_TYPE_GRAY //FIXME maybe better to use tdefl_write_image_to_png_file_in_memory() instead? -static bool write_rgb_or_gray_to_file(const char *file_name_utf8, size_t width, size_t height, int png_color_type, const uint8_t *data) +static bool write_rgb_or_gray_to_file(const char *file_name_utf8, size_t width, size_t height, int png_color_type, const uint8_t *data, bool flip = false) { bool result = false; @@ -235,10 +235,12 @@ static bool write_rgb_or_gray_to_file(const char *file_name_utf8, size_t width, int line_width = width; if (png_color_type == PNG_COLOR_TYPE_RGB) line_width *= 3; + else if (png_color_type == PNG_COLOR_TYPE_RGBA) + line_width *= 4; for (size_t y = 0; y < height; ++ y) { auto row = reinterpret_cast(::png_malloc(png_ptr, line_width)); row_pointers[y] = row; - memcpy(row, data + line_width * y, line_width); + memcpy(row, data + line_width * (flip ? (height - 1 - y) : y), line_width); } } @@ -262,6 +264,11 @@ fopen_failed: return result; } +bool write_gl_rgba_to_file(const char* file_name_utf8, size_t width, size_t height, const uint8_t* data_rgb) +{ + return write_rgb_or_gray_to_file(file_name_utf8, width, height, PNG_COLOR_TYPE_RGBA, data_rgb, true); +} + bool write_rgb_to_file(const char *file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb) { return write_rgb_or_gray_to_file(file_name_utf8, width, height, PNG_COLOR_TYPE_RGB, data_rgb); diff --git a/src/libslic3r/PNGReadWrite.hpp b/src/libslic3r/PNGReadWrite.hpp index 13b8a47a4..961832606 100644 --- a/src/libslic3r/PNGReadWrite.hpp +++ b/src/libslic3r/PNGReadWrite.hpp @@ -84,6 +84,7 @@ bool decode_colored_png(const ReadBuf &in_buf, ImageColorscale &out_img); +bool write_gl_rgba_to_file(const char* file_name_utf8, size_t width, size_t height, const uint8_t* data_rgb); // Down to earth function to store a packed RGB image to file. Mostly useful for debugging purposes. bool write_rgb_to_file(const char *file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb); bool write_rgb_to_file(const std::string &file_name_utf8, size_t width, size_t height, const uint8_t *data_rgb); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index e567a97f0..a6990ff25 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -7495,6 +7495,13 @@ CLIActionsConfigDef::CLIActionsConfigDef() def->cli_params = "option"; def->set_default_value(new ConfigOptionInt(0)); + def = this->add("export_png", coInt); + def->label = "Export png of plate"; + def->tooltip = "Export png of plate: 0-all plates, i-plate i, others-invalid"; + def->cli = "export-png"; + def->cli_params = "option"; + def->set_default_value(new ConfigOptionInt(-1)); + def = this->add("help", coBool); def->label = "Help"; def->tooltip = "Show command help."; @@ -7887,6 +7894,12 @@ CLIMiscConfigDef::CLIMiscConfigDef() def->tooltip = "Allow filaments with high/low temperature to be printed together"; def->cli_params = "option"; def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("camera_view", coInt); + def->label = "Camera view angle for exporting png"; + def->tooltip = "Camera view angle for exporting png: 0-Iso, 1-Top_Front, 2-Left, 3-Right, 10-Iso_1, 11-Iso_2, 12-Iso_3"; + def->cli_params = "angle"; + def->set_default_value(new ConfigOptionInt(0)); } const CLIActionsConfigDef cli_actions_config_def;