diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 283a089634..9907dd70d6 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1998,12 +1998,15 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato print.config().nozzle_temperature_initial_layer.get_at(0)); file.write("; CONFIG_BLOCK_END\n\n"); } else { - if (m_gcode_thumbnail_format != GCodeThumbnailsFormat::BTT_TFT) + if (m_gcode_thumbnail_format != GCodeThumbnailsFormat::BTT_TFT) { + auto thumbnaim_fmt = m_gcode_thumbnail_format; + // Orca: if the thumbnail format is ColPic, we write PNG in the beginning of gcode file and ColPic in the end of gcode file. + if(m_gcode_thumbnail_format == GCodeThumbnailsFormat::ColPic) + thumbnaim_fmt = GCodeThumbnailsFormat::PNG; GCodeThumbnails::export_thumbnails_to_file( thumbnail_cb, print.get_plate_index(), print.full_print_config().option("thumbnails")->values, - m_gcode_thumbnail_format, - [&file](const char *sz) { file.write(sz); }, - [&print]() { print.throw_if_canceled(); }); + thumbnaim_fmt, [&file](const char* sz) { file.write(sz); }, [&print]() { print.throw_if_canceled(); }); + } } } @@ -2646,6 +2649,11 @@ this->placeholder_parser().set("z_offset", new ConfigOptionFloat(m_config.z_offs GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder) .c_str()); file.write("\n"); + if (m_gcode_thumbnail_format == GCodeThumbnailsFormat::ColPic) + GCodeThumbnails::export_thumbnails_to_file( + thumbnail_cb, print.get_plate_index(), print.full_print_config().option("thumbnails")->values, + m_gcode_thumbnail_format, [&file](const char* sz) { file.write(sz); }, [&print]() { print.throw_if_canceled(); }); + file.write("; CONFIG_BLOCK_START\n"); std::string full_config; append_full_config(print, full_config); diff --git a/src/libslic3r/GCode/Thumbnails.cpp b/src/libslic3r/GCode/Thumbnails.cpp index 22788967ec..3d1ae9eec4 100644 --- a/src/libslic3r/GCode/Thumbnails.cpp +++ b/src/libslic3r/GCode/Thumbnails.cpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace Slic3r::GCodeThumbnails { @@ -37,6 +38,12 @@ struct CompressedBIQU : CompressedImageBuffer std::string_view tag() const override { return "thumbnail_BIQU"sv; } }; +struct CompressedColPic : CompressedImageBuffer +{ + ~CompressedColPic() override { free(data); } + std::string_view tag() const override { return "thumbnail_QIDI"sv; } +}; + std::unique_ptr compress_thumbnail_png(const ThumbnailData &data) { auto out = std::make_unique(); @@ -113,6 +120,64 @@ std::unique_ptr compress_thumbnail_qoi(const ThumbnailDat return out; } +int ColPic_EncodeStr(unsigned short* fromcolor16, int picw, int pich, unsigned char* outputdata, int outputmaxtsize, int colorsmax); + +std::unique_ptr compress_thumbnail_colpic(const ThumbnailData &data) +{ + const int MAX_SIZE = 512; + int width = int(data.width); + int height = int(data.height); + + // Orca: cap data size to MAX_SIZE while maintaining aspect ratio + if (width > MAX_SIZE || height > MAX_SIZE) { + double aspectRatio = static_cast(width) / height; + if (aspectRatio > 1.0) { + width = MAX_SIZE; + height = static_cast(MAX_SIZE / aspectRatio); + } else { + height = MAX_SIZE; + width = static_cast(MAX_SIZE * aspectRatio); + } + } + + std::vector color16_buf(width * height); + std::vector output_buf(height * width * 10); + + std::vector rgba_pixels(data.pixels.size() * 4); + size_t row_size = width * 4; + for (size_t y = 0; y < height; ++y) + memcpy(rgba_pixels.data() + y * row_size, data.pixels.data() + y * row_size, row_size); + const unsigned char* pixels; + pixels = (const unsigned char*) rgba_pixels.data(); + int r = 0, g = 0, b = 0, a = 0, rgb = 0; + int time = width * height - 1; + for (int row = 0; row < height; ++row) { + int rr = row * width; + for (int col = 0; col < width; ++col) { + const int pix_idx = 4 * (rr + width - col - 1); + r = int(pixels[pix_idx]) >> 3; + g = int(pixels[pix_idx + 1]) >> 2; + b = int(pixels[pix_idx + 2]) >> 3; + a = int(pixels[pix_idx + 3]); + if (a == 0) { + r = 239 >> 3; + g = 243 >> 2; + b = 247 >> 3; + } + rgb = (r << 11) | (g << 5) | b; + color16_buf[time--] = rgb; + } + } + + ColPic_EncodeStr(color16_buf.data(), width, height, output_buf.data(), output_buf.size(), 1024); + + auto out = std::make_unique(); + out->size = output_buf.size(); + out->data = malloc(out->size); + ::memcpy(out->data, output_buf.data(), out->size); + return out; +} + std::unique_ptr compress_thumbnail_btt_tft(const ThumbnailData &data) { // Take vector of RGBA pixels and flip the image vertically @@ -133,10 +198,10 @@ std::unique_ptr compress_thumbnail_btt_tft(const Thumbnai std::stringstream out_data; typedef struct {unsigned char r, g, b, a;} pixel; pixel px; - for (int ypos = 0; ypos < data.height; ypos++) { + for (unsigned int ypos = 0; ypos < data.height; ypos++) { std::stringstream line; line << ";"; - for (int xpos = 0; xpos < row_size; xpos+=4) { + for (unsigned int xpos = 0; xpos < row_size; xpos+=4) { px.r = rgba_pixels[ypos * row_size + xpos]; px.g = rgba_pixels[ypos * row_size + xpos + 1]; px.b = rgba_pixels[ypos * row_size + xpos + 2]; @@ -190,7 +255,278 @@ std::unique_ptr compress_thumbnail(const ThumbnailData &d return compress_thumbnail_qoi(data); case GCodeThumbnailsFormat::BTT_TFT: return compress_thumbnail_btt_tft(data); + case GCodeThumbnailsFormat::ColPic: + return compress_thumbnail_colpic(data); } } +typedef struct +{ + unsigned short colo16; + unsigned char A0; + unsigned char A1; + unsigned char A2; + unsigned char res0; + unsigned short res1; + unsigned int qty; +} U16HEAD; +typedef struct +{ + unsigned char encodever; + unsigned char res0; + unsigned short oncelistqty; + unsigned int PicW; + unsigned int PicH; + unsigned int mark; + unsigned int ListDataSize; + unsigned int ColorDataSize; + unsigned int res1; + unsigned int res2; +} ColPicHead3; + +static void colmemmove(unsigned char* dec, unsigned char* src, int lenth) +{ + if (src < dec) { + dec += lenth - 1; + src += lenth - 1; + while (lenth > 0) { + *(dec--) = *(src--); + lenth--; + } + } else { + while (lenth > 0) { + *(dec++) = *(src++); + lenth--; + } + } +} +static void colmemcpy(unsigned char* dec, unsigned char* src, int lenth) +{ + while (lenth > 0) { + *(dec++) = *(src++); + lenth--; + } +} +static void colmemset(unsigned char* dec, unsigned char val, int lenth) +{ + while (lenth > 0) { + *(dec++) = val; + lenth--; + } +} + +static void ADList0(unsigned short val, U16HEAD* listu16, int* listqty, int maxqty) +{ + unsigned char A0; + unsigned char A1; + unsigned char A2; + int qty = *listqty; + if (qty >= maxqty) + return; + for (int i = 0; i < qty; i++) { + if (listu16[i].colo16 == val) { + listu16[i].qty++; + return; + } + } + A0 = (unsigned char) (val >> 11); + A1 = (unsigned char) ((val << 5) >> 10); + A2 = (unsigned char) ((val << 11) >> 11); + U16HEAD* a = &listu16[qty]; + a->colo16 = val; + a->A0 = A0; + a->A1 = A1; + a->A2 = A2; + a->qty = 1; + *listqty = qty + 1; +} + +static int Byte8bitEncode( + unsigned short* fromcolor16, unsigned short* listu16, int listqty, int dotsqty, unsigned char* outputdata, int decMaxBytesize) +{ + unsigned char tid, sid; + int dots = 0; + int srcindex = 0; + int decindex = 0; + int lastid = 0; + int temp = 0; + while (dotsqty > 0) { + dots = 1; + for (int i = 0; i < (dotsqty - 1); i++) { + if (fromcolor16[srcindex + i] != fromcolor16[srcindex + i + 1]) + break; + dots++; + if (dots == 255) + break; + } + temp = 0; + for (int i = 0; i < listqty; i++) { + if (listu16[i] == fromcolor16[srcindex]) { + temp = i; + break; + } + } + tid = (unsigned char) (temp % 32); + sid = (unsigned char) (temp / 32); + if (lastid != sid) { + if (decindex >= decMaxBytesize) + goto IL_END; + outputdata[decindex] = 7; + outputdata[decindex] <<= 5; + outputdata[decindex] += sid; + decindex++; + lastid = sid; + } + if (dots <= 6) { + if (decindex >= decMaxBytesize) + goto IL_END; + outputdata[decindex] = (unsigned char) dots; + outputdata[decindex] <<= 5; + outputdata[decindex] += tid; + decindex++; + } else { + if (decindex >= decMaxBytesize) + goto IL_END; + outputdata[decindex] = 0; + outputdata[decindex] += tid; + decindex++; + if (decindex >= decMaxBytesize) + goto IL_END; + outputdata[decindex] = (unsigned char) dots; + decindex++; + } + srcindex += dots; + dotsqty -= dots; + } +IL_END: + return decindex; +} + +static int ColPicEncode(unsigned short* fromcolor16, int picw, int pich, unsigned char* outputdata, int outputmaxtsize, int colorsmax) +{ + U16HEAD l0; + int cha0, cha1, cha2, fid, minval; + ColPicHead3* Head0 = nullptr; + U16HEAD Listu16[1024]; + int ListQty = 0; + int enqty = 0; + int dotsqty = picw * pich; + if (colorsmax > 1024) + colorsmax = 1024; + for (int i = 0; i < dotsqty; i++) { + int ch = (int) fromcolor16[i]; + ADList0(ch, Listu16, &ListQty, 1024); + } + + for (int index = 1; index < ListQty; index++) { + l0 = Listu16[index]; + for (int i = 0; i < index; i++) { + if (l0.qty >= Listu16[i].qty) { + colmemmove((unsigned char*) &Listu16[i + 1], (unsigned char*) &Listu16[i], (index - i) * sizeof(U16HEAD)); + colmemcpy((unsigned char*) &Listu16[i], (unsigned char*) &l0, sizeof(U16HEAD)); + break; + } + } + } + while (ListQty > colorsmax) { + l0 = Listu16[ListQty - 1]; + minval = 255; + fid = -1; + for (int i = 0; i < colorsmax; i++) { + cha0 = Listu16[i].A0 - l0.A0; + if (cha0 < 0) + cha0 = 0 - cha0; + cha1 = Listu16[i].A1 - l0.A1; + if (cha1 < 0) + cha1 = 0 - cha1; + cha2 = Listu16[i].A2 - l0.A2; + if (cha2 < 0) + cha2 = 0 - cha2; + int chall = cha0 + cha1 + cha2; + if (chall < minval) { + minval = chall; + fid = i; + } + } + for (int i = 0; i < dotsqty; i++) { + if (fromcolor16[i] == l0.colo16) + fromcolor16[i] = Listu16[fid].colo16; + } + ListQty = ListQty - 1; + } + Head0 = ((ColPicHead3*) outputdata); + colmemset(outputdata, 0, sizeof(ColPicHead3)); + Head0->encodever = 3; + Head0->oncelistqty = 0; + Head0->mark = 0x05DDC33C; + Head0->ListDataSize = ListQty * 2; + for (int i = 0; i < ListQty; i++) { + unsigned short* l0 = (unsigned short*) &outputdata[sizeof(ColPicHead3)]; + l0[i] = Listu16[i].colo16; + } + enqty = Byte8bitEncode(fromcolor16, (unsigned short*) &outputdata[sizeof(ColPicHead3)], Head0->ListDataSize >> 1, dotsqty, + &outputdata[sizeof(ColPicHead3) + Head0->ListDataSize], + outputmaxtsize - sizeof(ColPicHead3) - Head0->ListDataSize); + Head0->ColorDataSize = enqty; + Head0->PicW = picw; + Head0->PicH = pich; + return sizeof(ColPicHead3) + Head0->ListDataSize + Head0->ColorDataSize; +} + +int ColPic_EncodeStr(unsigned short* fromcolor16, int picw, int pich, unsigned char* outputdata, int outputmaxtsize, int colorsmax) +{ + int qty = 0; + int temp = 0; + int strindex = 0; + int hexindex = 0; + unsigned char TempBytes[4]; + qty = ColPicEncode(fromcolor16, picw, pich, outputdata, outputmaxtsize, colorsmax); + if (qty == 0) + return 0; + temp = 3 - (qty % 3); + while (temp > 0) { + outputdata[qty] = 0; + qty++; + temp--; + } + if ((qty * 4 / 3) >= outputmaxtsize) + return 0; + hexindex = qty; + strindex = (qty * 4 / 3); + while (hexindex > 0) { + hexindex -= 3; + strindex -= 4; + + TempBytes[0] = (unsigned char) (outputdata[hexindex] >> 2); + TempBytes[1] = (unsigned char) (outputdata[hexindex] & 3); + TempBytes[1] <<= 4; + TempBytes[1] += ((unsigned char) (outputdata[hexindex + 1] >> 4)); + TempBytes[2] = (unsigned char) (outputdata[hexindex + 1] & 15); + TempBytes[2] <<= 2; + TempBytes[2] += ((unsigned char) (outputdata[hexindex + 2] >> 6)); + TempBytes[3] = (unsigned char) (outputdata[hexindex + 2] & 63); + + TempBytes[0] += 48; + if (TempBytes[0] == (unsigned char) '\\') + TempBytes[0] = 126; + TempBytes[0 + 1] += 48; + if (TempBytes[0 + 1] == (unsigned char) '\\') + TempBytes[0 + 1] = 126; + TempBytes[0 + 2] += 48; + if (TempBytes[0 + 2] == (unsigned char) '\\') + TempBytes[0 + 2] = 126; + TempBytes[0 + 3] += 48; + if (TempBytes[0 + 3] == (unsigned char) '\\') + TempBytes[0 + 3] = 126; + + outputdata[strindex] = TempBytes[0]; + outputdata[strindex + 1] = TempBytes[1]; + outputdata[strindex + 2] = TempBytes[2]; + outputdata[strindex + 3] = TempBytes[3]; + } + qty = qty * 4 / 3; + outputdata[qty] = 0; + return qty; +} + } // namespace Slic3r::GCodeThumbnails \ No newline at end of file diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index f4b76a3884..0d6e3f1bcb 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -7,6 +7,7 @@ #include "../Point.hpp" #include "../PrintConfig.hpp" +#include "PrintConfig.hpp" #include "ThumbnailData.hpp" #include @@ -52,7 +53,15 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, output((char *) compressed->data); if (i == (thumbnails.size() - 1)) output("; bigtree thumbnail end\r\n\r\n"); - } else { + } + else if (format == GCodeThumbnailsFormat::ColPic) { + if (i == 0) { + output((boost::format("\n\n;gimage:%s\n\n") % reinterpret_cast(compressed->data)).str().c_str()); + } else { + output((boost::format("\n\n;simage:%s\n\n") % reinterpret_cast(compressed->data)).str().c_str()); + } + } + else { output("; THUMBNAIL_BLOCK_START\n"); std::string encoded; encoded.resize(boost::beast::detail::base64::encoded_size(compressed->size)); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index f2844f8ce9..c7281cdd3b 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -368,7 +368,8 @@ static const t_config_enum_values s_keys_map_GCodeThumbnailsFormat = { { "PNG", int(GCodeThumbnailsFormat::PNG) }, { "JPG", int(GCodeThumbnailsFormat::JPG) }, { "QOI", int(GCodeThumbnailsFormat::QOI) }, - { "BTT_TFT", int(GCodeThumbnailsFormat::BTT_TFT) } + { "BTT_TFT", int(GCodeThumbnailsFormat::BTT_TFT) }, + { "ColPic", int(GCodeThumbnailsFormat::ColPic) } }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(GCodeThumbnailsFormat) @@ -4390,7 +4391,13 @@ def = this->add("filament_loading_speed", coFloats); def->enum_values.push_back("PNG"); def->enum_values.push_back("JPG"); def->enum_values.push_back("QOI"); - def->enum_values.push_back("BTT TFT"); + def->enum_values.push_back("BTT_TFT"); + def->enum_values.push_back("ColPic"); + def->enum_labels.push_back("PNG"); + def->enum_labels.push_back("JPG"); + def->enum_labels.push_back("QOI"); + def->enum_labels.push_back("BTT TT"); + def->enum_labels.push_back("ColPic"); def->set_default_value(new ConfigOptionEnum(GCodeThumbnailsFormat::PNG)); def = this->add("use_relative_e_distances", coBool); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 9be2e4a31a..8d63e543ae 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -272,7 +272,7 @@ enum RetractLiftEnforceType { }; enum class GCodeThumbnailsFormat { - PNG, JPG, QOI, BTT_TFT + PNG, JPG, QOI, BTT_TFT, ColPic }; static std::string bed_type_to_gcode_string(const BedType type) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index d982656deb..30568b78fc 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3408,8 +3408,8 @@ void TabPrinter::build_fff() optgroup->append_single_option_line("disable_m73"); option = optgroup->get_option("thumbnails"); option.opt.full_width = true; - optgroup->append_single_option_line(option); - optgroup->append_single_option_line("thumbnails_format"); + optgroup->append_single_option_line(option, "thumbnails"); + optgroup->append_single_option_line("thumbnails_format", "thumbnails"); optgroup->append_single_option_line("use_relative_e_distances"); optgroup->append_single_option_line("use_firmware_retraction"); // optgroup->append_single_option_line("spaghetti_detector");