mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-20 00:49:05 +08:00
GLTexture can now load ideamaker texture files
This commit is contained in:
parent
7a295af0ca
commit
6e5bfbd069
@ -374,7 +374,7 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
|
||||
else if (boost::algorithm::iends_with(m_texture_filename, ".png")) {
|
||||
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
|
||||
if (temp_texture->get_id() == 0 || temp_texture->get_source() != m_texture_filename) {
|
||||
if (!temp_texture->load_from_file(m_texture_filename, false, GLTexture::ECompressionType::None, false)) {
|
||||
if (!temp_texture->load_from_png_file(m_texture_filename, false, GLTexture::ECompressionType::None, false)) {
|
||||
render_default(bottom, false);
|
||||
return;
|
||||
}
|
||||
@ -382,7 +382,7 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
|
||||
}
|
||||
|
||||
// starts generating the main texture, compression will run asynchronously
|
||||
if (!texture->load_from_file(m_texture_filename, true, GLTexture::ECompressionType::MultiThreaded, true)) {
|
||||
if (!texture->load_from_png_file(m_texture_filename, true, GLTexture::ECompressionType::MultiThreaded, true)) {
|
||||
render_default(bottom, false);
|
||||
return;
|
||||
}
|
||||
|
@ -12,6 +12,9 @@
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/beast/core/detail/base64.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
@ -20,8 +23,10 @@
|
||||
#define STB_DXT_IMPLEMENTATION
|
||||
#include "stb_dxt/stb_dxt.h"
|
||||
|
||||
#include "nanosvg/nanosvg.h"
|
||||
#include "nanosvg/nanosvgrast.h"
|
||||
#include <nanosvg/nanosvg.h>
|
||||
#include <nanosvg/nanosvgrast.h>
|
||||
|
||||
#include <png.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
@ -119,17 +124,14 @@ void GLTexture::Compressor::compress()
|
||||
|
||||
GLTexture::Quad_UVs GLTexture::FullTextureUVs = { { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 0.0f, 0.0f } };
|
||||
|
||||
bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy)
|
||||
bool GLTexture::load_from_png_file(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy)
|
||||
{
|
||||
reset();
|
||||
|
||||
if (!boost::filesystem::exists(filename))
|
||||
return false;
|
||||
|
||||
if (boost::algorithm::iends_with(filename, ".png"))
|
||||
return load_from_png(filename, use_mipmaps, compression_type, apply_anisotropy);
|
||||
else
|
||||
return false;
|
||||
return boost::algorithm::iends_with(filename, ".png") ? load_from_png_internal(filename, use_mipmaps, compression_type, apply_anisotropy) : false;
|
||||
}
|
||||
|
||||
bool GLTexture::load_from_svg_file(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px)
|
||||
@ -139,10 +141,51 @@ bool GLTexture::load_from_svg_file(const std::string& filename, bool use_mipmaps
|
||||
if (!boost::filesystem::exists(filename))
|
||||
return false;
|
||||
|
||||
if (boost::algorithm::iends_with(filename, ".svg"))
|
||||
return load_from_svg(filename, use_mipmaps, compress, apply_anisotropy, max_size_px);
|
||||
else
|
||||
return boost::algorithm::iends_with(filename, ".svg") ? load_from_svg_internal(filename, use_mipmaps, compress, apply_anisotropy, max_size_px) : false;
|
||||
}
|
||||
|
||||
bool GLTexture::load_from_ideamaker_texture_file(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy)
|
||||
{
|
||||
reset();
|
||||
|
||||
if (!boost::filesystem::exists(filename))
|
||||
return false;
|
||||
|
||||
if (boost::algorithm::iends_with(filename, ".texture")) {
|
||||
boost::property_tree::ptree root;
|
||||
boost::property_tree::read_json(filename, root); // << FIXME for utf8 files
|
||||
|
||||
//http://www.cochoy.fr/boost-property-tree/
|
||||
boost::optional<std::string> id = root.get_optional<std::string>("header.texture_id");
|
||||
boost::optional<std::string> name = root.get_optional<std::string>("header.texture_name");
|
||||
boost::optional<std::string> image_data = root.get_optional<std::string>("image_data");
|
||||
boost::optional<std::string> border_color = root.get_optional<std::string>("settings.texture_border_color");
|
||||
boost::optional<float> repeat_x = root.get_optional<float>("settings.texture_repeat_x");
|
||||
boost::optional<float> repeat_y = root.get_optional<float>("settings.texture_repeat_y");
|
||||
boost::optional<float> rotation_z = root.get_optional<float>("settings.texture_rotation_z");
|
||||
boost::optional<float> translation_x = root.get_optional<float>("settings.texture_translation_x");
|
||||
boost::optional<float> translation_y = root.get_optional<float>("settings.texture_translation_y");
|
||||
boost::optional<std::string> wrapping = root.get_optional<std::string>("settings.texture_wrapping");
|
||||
boost::optional<std::string> version = root.get_optional<std::string>("version");
|
||||
|
||||
m_source = filename;
|
||||
|
||||
// TODO: do something with data other than image_data
|
||||
|
||||
if (image_data.has_value()) {
|
||||
const std::string src = image_data.value();
|
||||
std::string decoded;
|
||||
decoded.resize(boost::beast::detail::base64::decoded_size(src.length()));
|
||||
std::pair<std::size_t, std::size_t> res = boost::beast::detail::base64::decode((void*)decoded.data(), src.data(), src.length());
|
||||
std::vector<unsigned char> src_data(decoded.length());
|
||||
::memcpy((void*)src_data.data(), (const void*)decoded.data(), decoded.length());
|
||||
bool ret = load_from_png_buffer(src_data, true, GLTexture::ECompressionType::SingleThreaded, true);
|
||||
if (!ret)
|
||||
reset();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::string>& filenames, const std::vector<std::pair<int, bool>>& states, unsigned int sprite_size_px, bool compress)
|
||||
@ -298,6 +341,207 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLTexture::load_from_png_buffer(const std::vector<unsigned char>& png_buffer, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy)
|
||||
{
|
||||
#define DEBUG_OUTPUT 0
|
||||
#if DEBUG_OUTPUT
|
||||
wxString out_file = m_source + ".png";
|
||||
#endif // DEBUG_OUTPUT
|
||||
|
||||
reset();
|
||||
|
||||
const bool compression_enabled = (compression_type != ECompressionType::None) && GLEW_EXT_texture_compression_s3tc;
|
||||
|
||||
// for reference, see: http://pulsarengine.com/2009/01/reading-png-images-from-memory/
|
||||
static const size_t PngSignatureLength = 8;
|
||||
|
||||
// check PNG signature
|
||||
if (png_buffer.size() < PngSignatureLength) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Found invalid data while loading png from memory";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::array<unsigned char, PngSignatureLength> png_signature;
|
||||
::memcpy((void*)png_signature.data(), (const void*)png_buffer.data(), PngSignatureLength);
|
||||
|
||||
if (!png_check_sig(png_signature.data(), PngSignatureLength)) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Found invalid signature while loading png from memory";
|
||||
return false;
|
||||
}
|
||||
|
||||
// create PNG file read struct (memory is allocated by libpng)
|
||||
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||
if (png_ptr == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to create read struct while loading png from memory";
|
||||
return false;
|
||||
}
|
||||
|
||||
// create PNG image data info struct (memory is allocated by libpng)
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (info_ptr == nullptr) {
|
||||
// libpng must free file info struct memory before we bail
|
||||
png_destroy_read_struct(&png_ptr, nullptr, nullptr);
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to create info struct while loading png from memory";
|
||||
return false;
|
||||
}
|
||||
|
||||
// helper class to read png data from memory
|
||||
class PngRead
|
||||
{
|
||||
const std::vector<unsigned char>& m_data;
|
||||
size_t m_pos{ PngSignatureLength }; // skip signature
|
||||
|
||||
public:
|
||||
explicit PngRead(const std::vector<unsigned char>& data) : m_data(data) {}
|
||||
void read(void* dest, size_t num_bytes) {
|
||||
::memcpy(dest, (const void*)&m_data[m_pos], num_bytes);
|
||||
m_pos += num_bytes;
|
||||
}
|
||||
};
|
||||
|
||||
PngRead png_read(png_buffer);
|
||||
|
||||
// set custom read function
|
||||
png_set_read_fn(png_ptr, (png_voidp)(&png_read), [](png_structp png_ptr, png_bytep out_bytes, png_size_t byte_count_to_read) {
|
||||
png_voidp io_ptr = png_get_io_ptr(png_ptr);
|
||||
assert(io_ptr != nullptr);
|
||||
PngRead* reader = (PngRead*)io_ptr;
|
||||
reader->read(out_bytes, static_cast<size_t>(byte_count_to_read));
|
||||
});
|
||||
|
||||
// tell libpng to skip signature
|
||||
png_set_sig_bytes(png_ptr, PngSignatureLength);
|
||||
|
||||
// read info struct
|
||||
png_read_info(png_ptr, info_ptr);
|
||||
|
||||
// read header
|
||||
png_uint_32 width = 0;
|
||||
png_uint_32 height = 0;
|
||||
int bitDepth = 0;
|
||||
int colorType = -1;
|
||||
png_uint_32 res = png_get_IHDR(png_ptr, info_ptr, &width, &height, &bitDepth, &colorType, nullptr, nullptr, nullptr);
|
||||
if (res != 1) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to read header while loading png from memory";
|
||||
return false;
|
||||
}
|
||||
|
||||
// check dimensions
|
||||
if (width == 0 || height == 0) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
|
||||
BOOST_LOG_TRIVIAL(error) << "Found invalid size while loading png from memory";
|
||||
return false;
|
||||
}
|
||||
|
||||
// temporary constrain, see http://www.libpng.org/pub/png/libpng-1.2.5-manual.html to remove
|
||||
if (colorType != PNG_COLOR_TYPE_RGB && colorType != PNG_COLOR_TYPE_RGB_ALPHA) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
|
||||
BOOST_LOG_TRIVIAL(error) << "Found invalid color type while loading png from memory";
|
||||
return false;
|
||||
}
|
||||
|
||||
// temporary constrain, see http://www.libpng.org/pub/png/libpng-1.2.5-manual.html to remove
|
||||
if (bitDepth != 8) {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
|
||||
BOOST_LOG_TRIVIAL(error) << "Found bit depth while loading png from memory";
|
||||
return false;
|
||||
}
|
||||
|
||||
// read image data
|
||||
std::vector<unsigned char> rgb_data(width * height * 3, 0);
|
||||
std::vector<unsigned char> alpha_data(width * height, 0);
|
||||
const png_uint_32 bytes_per_row = png_get_rowbytes(png_ptr, info_ptr);
|
||||
std::vector<unsigned char> row_data(bytes_per_row, 0);
|
||||
|
||||
for (png_uint_32 row = 0; row < height; ++row) {
|
||||
png_read_row(png_ptr, (png_bytep)row_data.data(), nullptr);
|
||||
for (png_uint_32 col = 0; col < width; ++col) {
|
||||
const png_uint_32 alpha_id = row * width + col;
|
||||
const png_uint_32 rgb_id = 3 * alpha_id;
|
||||
switch (colorType)
|
||||
{
|
||||
case PNG_COLOR_TYPE_RGB:
|
||||
{
|
||||
const png_uint_32 col_id = 3 * col;
|
||||
rgb_data[rgb_id + 0] = row_data[col_id + 0];
|
||||
rgb_data[rgb_id + 1] = row_data[col_id + 1];
|
||||
rgb_data[rgb_id + 2] = row_data[col_id + 2];
|
||||
alpha_data[alpha_id] = 255;
|
||||
break;
|
||||
}
|
||||
case PNG_COLOR_TYPE_RGB_ALPHA:
|
||||
{
|
||||
const png_uint_32 col_id = 4 * col;
|
||||
rgb_data[rgb_id + 0] = row_data[col_id + 0];
|
||||
rgb_data[rgb_id + 1] = row_data[col_id + 1];
|
||||
rgb_data[rgb_id + 2] = row_data[col_id + 2];
|
||||
alpha_data[alpha_id] = row_data[col_id + 3];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
|
||||
|
||||
m_width = static_cast<int>(width);
|
||||
m_height = static_cast<int>(height);
|
||||
|
||||
const int n_pixels = m_width * m_height;
|
||||
if (n_pixels <= 0) {
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
wxImage image(m_width, m_height);
|
||||
image.InitAlpha();
|
||||
image.SetData(rgb_data.data(), true);
|
||||
image.SetAlpha(alpha_data.data(), true);
|
||||
|
||||
bool requires_rescale = false;
|
||||
|
||||
if (compression_enabled && compression_type == ECompressionType::MultiThreaded)
|
||||
requires_rescale = adjust_size_for_compression();
|
||||
|
||||
if (requires_rescale)
|
||||
image = image.ResampleBicubic(m_width, m_height);
|
||||
|
||||
#if DEBUG_OUTPUT
|
||||
image.SaveFile(out_file, wxBITMAP_TYPE_PNG);
|
||||
#endif // DEBUG_OUTPUT
|
||||
|
||||
std::vector<unsigned char> data;
|
||||
|
||||
// Get RGB & alpha raw data from wxImage, pack them into an array.
|
||||
auto copy_data = [this](wxImage& image, std::vector<unsigned char>& data, int n_pixels) {
|
||||
unsigned char* img_rgb = image.GetData();
|
||||
unsigned char* img_alpha = image.GetAlpha();
|
||||
data.resize(n_pixels * 4);
|
||||
for (int i = 0; i < n_pixels; ++i) {
|
||||
const int data_id = i * 4;
|
||||
const int img_id = i * 3;
|
||||
data[data_id + 0] = img_rgb[img_id + 0];
|
||||
data[data_id + 1] = img_rgb[img_id + 1];
|
||||
data[data_id + 2] = img_rgb[img_id + 2];
|
||||
data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255;
|
||||
}
|
||||
};
|
||||
|
||||
copy_data(image, data, n_pixels);
|
||||
|
||||
send_to_gpu(data, use_mipmaps, compression_type, apply_anisotropy, [&image, copy_data](int lod_w, int lod_h, std::vector<unsigned char>& data) {
|
||||
wxImage im = image.ResampleBicubic(lod_w, lod_h);
|
||||
copy_data(im, data, lod_w * lod_h);
|
||||
});
|
||||
|
||||
if (compression_enabled && compression_type == ECompressionType::MultiThreaded)
|
||||
// start asynchronous compression
|
||||
m_compressor.start_compressing();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLTexture::reset()
|
||||
{
|
||||
if (m_id != 0)
|
||||
@ -338,7 +582,7 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right,
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
}
|
||||
|
||||
bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy)
|
||||
bool GLTexture::load_from_png_internal(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy)
|
||||
{
|
||||
reset();
|
||||
|
||||
@ -399,7 +643,7 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECo
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px)
|
||||
bool GLTexture::load_from_svg_internal(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px)
|
||||
{
|
||||
reset();
|
||||
|
||||
|
@ -89,8 +89,9 @@ namespace GUI {
|
||||
GLTexture() : m_compressor(*this) {}
|
||||
virtual ~GLTexture() { reset(); }
|
||||
|
||||
bool load_from_file(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy);
|
||||
bool load_from_png_file(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy);
|
||||
bool load_from_svg_file(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px);
|
||||
bool load_from_ideamaker_texture_file(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy);
|
||||
// meanings of states: (std::pair<int, bool>)
|
||||
// first field (int):
|
||||
// 0 -> no changes
|
||||
@ -100,6 +101,8 @@ namespace GUI {
|
||||
// false -> no changes
|
||||
// true -> add background color
|
||||
bool load_from_svg_files_as_sprites_array(const std::vector<std::string>& filenames, const std::vector<std::pair<int, bool>>& states, unsigned int sprite_size_px, bool compress);
|
||||
bool load_from_png_buffer(const std::vector<unsigned char>& png_buffer, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy);
|
||||
|
||||
void reset();
|
||||
|
||||
unsigned int get_id() const { return m_id; }
|
||||
@ -116,8 +119,8 @@ namespace GUI {
|
||||
static void render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const Quad_UVs& uvs);
|
||||
|
||||
private:
|
||||
bool load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy);
|
||||
bool load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px);
|
||||
bool load_from_png_internal(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy);
|
||||
bool load_from_svg_internal(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px);
|
||||
|
||||
bool adjust_size_for_compression();
|
||||
void send_to_gpu(std::vector<unsigned char>& data, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy,
|
||||
|
@ -176,8 +176,10 @@ bool GLToolbar::init(const BackgroundTexture::Metadata& background_texture)
|
||||
std::string path = resources_dir() + "/icons/";
|
||||
bool res = false;
|
||||
|
||||
if (!background_texture.filename.empty())
|
||||
res = m_background_texture.texture.load_from_file(path + background_texture.filename, false, GLTexture::ECompressionType::SingleThreaded, false);
|
||||
if (!background_texture.filename.empty()) {
|
||||
assert(boost::algorithm::iends_with(background_texture.filename, ".png"));
|
||||
res = m_background_texture.texture.load_from_png_file(path + background_texture.filename, false, GLTexture::ECompressionType::SingleThreaded, false);
|
||||
}
|
||||
|
||||
if (res)
|
||||
m_background_texture.metadata = background_texture;
|
||||
|
@ -87,7 +87,8 @@ bool GLGizmosManager::init()
|
||||
m_background_texture.metadata.bottom = 16;
|
||||
|
||||
if (!m_background_texture.metadata.filename.empty()) {
|
||||
if (!m_background_texture.texture.load_from_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false, GLTexture::ECompressionType::SingleThreaded, false))
|
||||
assert(boost::algorithm::iends_with(m_background_texture.metadata.filename, ".png"));
|
||||
if (!m_background_texture.texture.load_from_png_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false, GLTexture::ECompressionType::SingleThreaded, false))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user