Skip to content

Commit

Permalink
Implement cube mapping based on angle from center (Related to issue p…
Browse files Browse the repository at this point in the history
…rusa3d#8649). Move image loading to the main thread for reliability.
  • Loading branch information
Poikilos committed Dec 28, 2022
1 parent 1c3a0ec commit 5f0239a
Show file tree
Hide file tree
Showing 9 changed files with 252 additions and 96 deletions.
19 changes: 19 additions & 0 deletions src/libslic3r/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@

namespace Slic3r {

std::map<std::string, png::BackendPng> config_images;
png::BackendPng no_image; // A non-loaded image (IsOK() should always be false) for avoiding returning nullptr.

// Escape \n, \r and backslash
std::string escape_string_cstyle(const std::string &str)
{
Expand Down Expand Up @@ -460,6 +463,22 @@ void ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys
} else
my_opt->set(other_opt);
}
// ConfigOption *path_opt = this->option("fuzzy_skin_displacement_map", false); // tries to use deleted = operator?
// std::string path_opt = "/home/owner/Maps/wall/Bricks076A_1K-JPG/Bricks076A_1K_Displacement-512-RGBA.png"; // this->option("fuzzy_skin_displacement_map", false)->value;
// dynamic_cast<std::string>(*this->option("fuzzy_skin_displacement_map"))->value;
/*
if (path_opt != nullptr) {
std::string this_displacement_map_path = path_opt;
if (this_displacement_map_path != displacement_img.GetPath()) {
if (this_displacement_map_path != "") {
this->m_displacement_img.LoadFile(this_displacement_map_path);
}
else {
m_displacement_img.Destroy();
}
}
}
*/
}

// Are the two configs equal? Ignoring options not present in both configs.
Expand Down
46 changes: 46 additions & 0 deletions src/libslic3r/Config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "clonable_ptr.hpp"
#include "Exception.hpp"
#include "Point.hpp"
#include "PNGReadWrite.hpp" // ideally some kind of cache or interface (IBackendImage or something) would be used instead of BackendPng directly.

#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/trim.hpp>
Expand Down Expand Up @@ -81,6 +82,10 @@ extern bool unescape_strings_cstyle(const std::string &str, std::vector<

extern std::string escape_ampersand(const std::string& str);

extern std::map<std::string, png::BackendPng> config_images;
// ^ consider: std::map<std::string, shared_ptr<BackendPng>> config_images; // use `new` and pointers but let delete get called automatically
extern png::BackendPng no_image; // A non-loaded image (IsOK() should always be false) for avoiding returning nullptr.

namespace ConfigHelpers {
inline bool looks_like_enum_value(std::string value)
{
Expand Down Expand Up @@ -2065,9 +2070,50 @@ class ConfigBase : public ConfigOptionResolver

static size_t load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions);

/*!
Get a pointer to the displacement map. The return is always non-null.
If the param is false but the function was not properly called on the main thread
beforehand with true, an exception will be raised (It will appear in the GUI, but it
is an implementation error and should be fixed before release).
@param opt_key_str The string equivalent to the opt_key that will also be used as the
caching key in config_images.
@param main_thread If true, the call must be made outside of a multithreading context.
In threads should be called with false to avoid multithreading issues,
by forcing an exception if not already loaded.
*/
const png::BackendPng* opt_image(std::string opt_key_str, bool main_thread) const {
// ConfigOption* path_opt = this->option("fuzzy_skin_displacement_map", false);
// if (path_opt == nullptr) {
// return &no_image;
// }
// std::string path = dynamic_cast<std::string>(path_opt->get()); // incorrect?
const std::string& path = this->opt_string(opt_key_str).empty()
? std::string("") // print_config_def.get("fuzzy_skin_displacement_map")->get_default_value<ConfigOptionString>()->value
: this->opt_string(opt_key_str);
if (path == "") {
return &no_image;
}

auto pos = config_images.find(path); // std::map<string, png::BackendImage>::const_iterator pos =
if (pos == config_images.end()) {
if (!main_thread) {
throw ConfigurationError(opt_key_str + " has a new value."
" opt_image(\"" + opt_key_str + "\", true) must"
" be accessed within the main thread first to preload it"
" (This is a problem with implementation not a runtime error).");
// return nullptr;
}
config_images[path] = png::BackendPng();
config_images[path].LoadFile(path);
}
// else Some other function should clear config_images cache in case the same image file changed on storage.
return &config_images[path];
}
private:
// Set a configuration value from a string.
bool set_deserialize_raw(const t_config_option_key& opt_key_src, const std::string& value, ConfigSubstitutionContext& substitutions, bool append);
// png::BackendPng m_displacement_img; //!< This must be loaded from fuzzy_skin_displacement_map (see apply_config; Using a cached image accessed by the option key may be better).
};

// Configuration store with dynamic number of configuration values.
Expand Down
9 changes: 5 additions & 4 deletions src/libslic3r/LayerRegion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,17 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
&slices,
this->layer()->height,
this->flow(frPerimeter),
&region_config,
&this->layer()->object()->config(),
&region_config, // PrintRegionConfig*
// &this->layer()->object()->config(), // PrintObjectConfig*
this->layer()->object(), // PrintObject*
&print_config,
spiral_vase,
this->layer()->print_z,

// output:
&this->perimeters,
&this->thin_fills,
fill_surfaces,
this->layer()->print_z
fill_surfaces
);

if (this->layer()->lower_layer != nullptr)
Expand Down
44 changes: 25 additions & 19 deletions src/libslic3r/PNGReadWrite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ static void png_read_callback(png_struct *png_ptr,
}


bool BackendPng::IsOk() {
bool BackendPng::IsOk() const {
//if (this->image_path == "") {
if (this->m_pixel_size < 1) {
return false;
Expand All @@ -88,7 +88,7 @@ bool BackendPng::IsOk() {
return true;
}

bool BackendPng::dump() {
bool BackendPng::dump() const {
std::cerr<<"[BackendImage] \"" << this->GetPath() << "\" (OK:" << (this->IsOk()?"true":"false") << ") dump:" <<std::endl;
if (!this->IsOk()) return false;
for (size_t y=0; y<this->GetHeight(); y++) {
Expand All @@ -110,8 +110,7 @@ bool BackendPng::dump() {
return true;
}

std::string BackendPng::GetPath()
{
std::string BackendPng::GetPath() const {
return this->image_path;
}

Expand All @@ -134,20 +133,24 @@ bool BackendPng::reinitialize(bool force) {
png_destroy_read_struct(&png, nullptr, nullptr);
this->png = nullptr;
}
/*
if (info) {
std::cerr << "[reinitialize] Error: Unexpected png info will be deleted." << std::endl;
delete this->info;
this->info = nullptr;
}
// Don't do this. The compiler says:
// - "invalid use of incomplete type ‘struct png_info_def’" "png.h:484:16: note: forward declaration of ‘struct png_info_def’"
// - "neither the destructor nor the class-specific ‘operator delete’ will be called, even if they are declared when the class is defined"
*/
this->image_path = "";
this->m_pixel_size = 0; // cause IsOk() to return false.
this->error_shown = false;
this->m_color = false;
return true;
}

void BackendPng::Destroy()
{
void BackendPng::Destroy() {
this->reinitialize(true);
}

Expand Down Expand Up @@ -338,8 +341,7 @@ bool BackendPng::load_png_stream(const ReadBuf &in_buf, std::string optional_sta
return this->load_png_stream(stream, optional_stated_path, next_busy);
}

bool BackendPng::LoadFile(std::string path)
{
bool BackendPng::LoadFile(std::string path) {
if (this->image_path == path) {
// another thread must have already loaded it.
return true;
Expand All @@ -358,7 +360,9 @@ bool BackendPng::LoadFile(std::string path)
return false; // Another thread already failed to load the image.
}
if (total_delay >= delay_timeout) {
std::cerr << "[BackendImage::LoadFile] waiting for other thread(s) timed out." << std::endl;
std::cerr << "[BackendImage::LoadFile] waiting for other thread(s) timed out."
" To avoid this, implement caching (for example, see config_images)"
" and use the main thread only (for example, see image_opt)." << std::endl;
// FIXME: Find a way to avoid IsOK() is false after this if the image was still loading in another thread and will have succeeded.
break;
}
Expand Down Expand Up @@ -391,17 +395,15 @@ bool BackendPng::LoadFile(std::string path)
}


size_t BackendPng::GetWidth()
{
size_t BackendPng::GetWidth() const {
return this->cols;
}

size_t BackendPng::GetHeight()
{
size_t BackendPng::GetHeight() const {
return this->rows;
}

bool BackendPng::clamp(size_t& x, size_t& y) {
bool BackendPng::clamp(size_t& x, size_t& y) const {
bool was_in_bounds = true;
if (x >= this->GetWidth()) {
was_in_bounds = false;
Expand All @@ -414,7 +416,7 @@ bool BackendPng::clamp(size_t& x, size_t& y) {
return was_in_bounds;
}

std::string BackendPng::get_type_message(png_byte color_type) {
std::string BackendPng::get_type_message(png_byte color_type) const {
std::string type_msg = "";
if (color_type == PNG_COLOR_TYPE_PALETTE) {
type_msg = " with indexed color";
Expand All @@ -432,25 +434,29 @@ std::string BackendPng::get_type_message(png_byte color_type) {
return type_msg;
}

uint8_t BackendPng::GetRed(size_t x, size_t y) {
uint8_t BackendPng::GetRed(size_t x, size_t y) const {
// PNG stores each pixel in RGBA order unless png_set_bgr is called,
// or png_set_swap_alpha is called to move A to beginning
// (See <http://www.libpng.org/pub/png/libpng-1.2.5-manual.html>).
this->clamp(x, y);
return buf[y * this->m_stride + x * this->m_pixel_size];
}

uint8_t BackendPng::GetGreen(size_t x, size_t y) {
uint8_t BackendPng::GetGreen(size_t x, size_t y) const {
this->clamp(x, y);
if (!this->m_color)
return buf[y * this->m_stride + x * this->m_pixel_size];
return buf[y * this->m_stride + x * this->m_pixel_size + 1];
}

uint8_t BackendPng::GetBlue(size_t x, size_t y) {
uint8_t BackendPng::GetBlue(size_t x, size_t y) const {
this->clamp(x, y);
if (!this->m_color)
return buf[y * this->m_stride + x * this->m_pixel_size];
return buf[y * this->m_stride + x * this->m_pixel_size + 2];
}

uint8_t BackendPng::GetLuma(size_t x, size_t y) {
uint8_t BackendPng::GetLuma(size_t x, size_t y) const {
this->clamp(x, y);
if (this->m_pixel_size < 3) { // GRAY or GRAY_ALPHA
return buf[y * this->m_stride + x * this->m_pixel_size];
Expand Down
36 changes: 20 additions & 16 deletions src/libslic3r/PNGReadWrite.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ struct ReadBuf { const void *buf = nullptr; const size_t sz = 0; };

bool is_png(const ReadBuf &pngbuf);

/// Implement a drop-in replacement, in most or all use cases, for wxImage but for backend use.
/// This class is based on the decode_png and elements from Image implementations, both from PNGReadWrite.*.
/*!
Implement a drop-in replacement, in most or all use cases, for wxImage but for backend use.
This class is based on the decode_png and elements from Image implementations, both from PNGReadWrite.*.
If this class is extended, it should imitate wxImage as much as possible for consistency between GUI and
CLI code. Otherwise an interface should be created and this would be the backend implementation of that.
*/
class BackendPng {
private:
png_struct *png = nullptr;
Expand All @@ -59,29 +63,29 @@ class BackendPng {
bool load_png_file(std::string path);
bool load_png_stream(IStream &in_buf, std::string optional_stated_path, bool next_busy);
bool load_png_stream(const ReadBuf& in_buff, std::string optional_stated_path, bool next_busy);
bool clamp(size_t& x, size_t& y);
std::string get_type_message(png_byte color_type);
bool dump();
bool clamp(size_t& x, size_t& y) const;
std::string get_type_message(png_byte color_type) const;
bool dump() const;
public:
BackendPng() = default;
BackendPng(const BackendPng&) = delete;
BackendPng(BackendPng&&) = delete;
BackendPng& operator=(const BackendPng&) = delete;
BackendPng& operator=(BackendPng&&) = delete;
// (BackendPng&&) = delete;
// BackendPng& operator=(const BackendPng&) = delete;
// BackendPng& operator=(BackendPng&&) = delete;
void Destroy();
~BackendPng()
{
this->Destroy();
}
bool IsOk();
std::string GetPath();
bool IsOk() const;
std::string GetPath() const;
bool LoadFile(std::string path);
size_t GetWidth();
size_t GetHeight();
uint8_t GetRed(size_t x, size_t y);
uint8_t GetGreen(size_t x, size_t y);
uint8_t GetBlue(size_t x, size_t y);
uint8_t GetLuma(size_t x, size_t y);
size_t GetWidth() const;
size_t GetHeight() const;
uint8_t GetRed(size_t x, size_t y) const;
uint8_t GetGreen(size_t x, size_t y) const;
uint8_t GetBlue(size_t x, size_t y) const;
uint8_t GetLuma(size_t x, size_t y) const;
};

template<class Img> bool decode_png(const ReadBuf &in_buf, Img &out_img)
Expand Down
Loading

0 comments on commit 5f0239a

Please sign in to comment.