#include "libslic3r/libslic3r.h" #include "GLTexture.hpp" #include #include #include #if ENABLE_TEXTURES_FROM_SVG #include #endif // ENABLE_TEXTURES_FROM_SVG #include #include #if ENABLE_TEXTURES_FROM_SVG #define NANOSVG_IMPLEMENTATION #include "nanosvg/nanosvg.h" #define NANOSVGRAST_IMPLEMENTATION #include "nanosvg/nanosvgrast.h" #endif // ENABLE_TEXTURES_FROM_SVG namespace Slic3r { namespace GUI { GLTexture::Quad_UVs GLTexture::FullTextureUVs = { { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 0.0f, 0.0f } }; GLTexture::GLTexture() : m_id(0) , m_width(0) , m_height(0) , m_source("") { } GLTexture::~GLTexture() { reset(); } #if ENABLE_TEXTURES_FROM_SVG bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps) { reset(); if (!boost::filesystem::exists(filename)) return false; if (boost::algorithm::iends_with(filename, ".png")) return load_from_png(filename, use_mipmaps); else return false; } bool GLTexture::load_from_svg_file(const std::string& filename, bool use_mipmaps, unsigned int max_size_px) { reset(); if (!boost::filesystem::exists(filename)) return false; if (boost::algorithm::iends_with(filename, ".svg")) return load_from_svg(filename, use_mipmaps, max_size_px); else return false; } #else bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps) { reset(); if (!boost::filesystem::exists(filename)) return false; // Load a PNG with an alpha channel. wxImage image; if (!image.LoadFile(wxString::FromUTF8(filename.c_str()), wxBITMAP_TYPE_PNG)) { reset(); return false; } m_width = image.GetWidth(); m_height = image.GetHeight(); int n_pixels = m_width * m_height; if (n_pixels <= 0) { reset(); return false; } // Get RGB & alpha raw data from wxImage, pack them into an array. unsigned char* img_rgb = image.GetData(); if (img_rgb == nullptr) { reset(); return false; } unsigned char* img_alpha = image.GetAlpha(); std::vector data(n_pixels * 4, 0); for (int i = 0; i < n_pixels; ++i) { int data_id = i * 4; 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; } // sends data to gpu ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); ::glGenTextures(1, &m_id); ::glBindTexture(GL_TEXTURE_2D, m_id); ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); if (use_mipmaps) { // we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards unsigned int levels_count = generate_mipmaps(image); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels_count); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); } else { ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); } ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ::glBindTexture(GL_TEXTURE_2D, 0); m_source = filename; return true; } #endif // ENABLE_TEXTURES_FROM_SVG #if ENABLE_SVG_ICONS bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector& filenames, unsigned int num_states, unsigned int sprite_size_px) { static int pass = 0; ++pass; reset(); if (filenames.empty() || (num_states == 0) || (sprite_size_px == 0)) return false; m_width = (int)sprite_size_px * std::max((int)(num_states), (int)(filenames.size())); m_height = m_width; int n_pixels = m_width * m_height; if (n_pixels <= 0) { reset(); return false; } std::vector data(n_pixels * 4, 0); std::vector sprite_data(sprite_size_px * sprite_size_px * 4, 0); NSVGrasterizer* rast = nsvgCreateRasterizer(); if (rast == nullptr) { reset(); return false; } int sprite_id = -1; for (const std::string& filename : filenames) { ++sprite_id; if (!boost::filesystem::exists(filename)) continue; if (!boost::algorithm::iends_with(filename, ".svg")) continue; NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f); if (image == nullptr) continue; float scale = (float)sprite_size_px / std::max(image->width, image->height); nsvgRasterize(rast, image, 0, 0, scale, sprite_data.data(), sprite_size_px, sprite_size_px, sprite_size_px * 4); int sprite_offset_px = sprite_id * sprite_size_px * m_width; for (unsigned int i = 0; i < num_states; ++i) { int state_offset_px = sprite_offset_px + i * sprite_size_px; for (int j = 0; j < sprite_size_px; ++j) { int data_offset = (state_offset_px + j * m_width) * 4; ::memcpy((void*)&data.data()[data_offset], (const void*)&sprite_data.data()[j * sprite_size_px * 4], sprite_size_px * 4); } } nsvgDelete(image); } nsvgDeleteRasterizer(rast); // sends data to gpu ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); ::glGenTextures(1, &m_id); ::glBindTexture(GL_TEXTURE_2D, m_id); ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ::glBindTexture(GL_TEXTURE_2D, 0); m_source = filenames.front(); wxImage output(m_width, m_height); output.InitAlpha(); for (int h = 0; h < m_height; ++h) { for (int w = 0; w < m_width; ++w) { int px = h * m_width + w; int px_byte = px * 4; output.SetRGB(w, h, data.data()[px_byte + 0], data.data()[px_byte + 1], data.data()[px_byte + 2]); output.SetAlpha(w, h, data.data()[px_byte + 3]); } } output.SaveFile("C:/prusa/slic3r/svg_icons/temp/test_" + std::to_string(pass) + ".png", wxBITMAP_TYPE_PNG); return true; } #endif // ENABLE_SVG_ICONS void GLTexture::reset() { if (m_id != 0) ::glDeleteTextures(1, &m_id); m_id = 0; m_width = 0; m_height = 0; m_source = ""; } void GLTexture::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) { render_sub_texture(tex_id, left, right, bottom, top, FullTextureUVs); } void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const GLTexture::Quad_UVs& uvs) { ::glEnable(GL_BLEND); ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); ::glEnable(GL_TEXTURE_2D); ::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); ::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id); ::glBegin(GL_QUADS); ::glTexCoord2f(uvs.left_bottom.u, uvs.left_bottom.v); ::glVertex2f(left, bottom); ::glTexCoord2f(uvs.right_bottom.u, uvs.right_bottom.v); ::glVertex2f(right, bottom); ::glTexCoord2f(uvs.right_top.u, uvs.right_top.v); ::glVertex2f(right, top); ::glTexCoord2f(uvs.left_top.u, uvs.left_top.v); ::glVertex2f(left, top); ::glEnd(); ::glBindTexture(GL_TEXTURE_2D, 0); ::glDisable(GL_TEXTURE_2D); ::glDisable(GL_BLEND); } unsigned int GLTexture::generate_mipmaps(wxImage& image) { int w = image.GetWidth(); int h = image.GetHeight(); GLint level = 0; std::vector data(w * h * 4, 0); while ((w > 1) || (h > 1)) { ++level; w = std::max(w / 2, 1); h = std::max(h / 2, 1); int n_pixels = w * h; image = image.ResampleBicubic(w, h); 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) { int data_id = i * 4; 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; } ::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); } return (unsigned int)level; } #if ENABLE_TEXTURES_FROM_SVG bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps) { // Load a PNG with an alpha channel. wxImage image; if (!image.LoadFile(wxString::FromUTF8(filename.c_str()), wxBITMAP_TYPE_PNG)) { reset(); return false; } m_width = image.GetWidth(); m_height = image.GetHeight(); int n_pixels = m_width * m_height; if (n_pixels <= 0) { reset(); return false; } // Get RGB & alpha raw data from wxImage, pack them into an array. unsigned char* img_rgb = image.GetData(); if (img_rgb == nullptr) { reset(); return false; } unsigned char* img_alpha = image.GetAlpha(); std::vector data(n_pixels * 4, 0); for (int i = 0; i < n_pixels; ++i) { int data_id = i * 4; 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; } // sends data to gpu ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); ::glGenTextures(1, &m_id); ::glBindTexture(GL_TEXTURE_2D, m_id); ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); if (use_mipmaps) { // we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards unsigned int levels_count = generate_mipmaps(image); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels_count); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); } else { ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); } ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ::glBindTexture(GL_TEXTURE_2D, 0); m_source = filename; return true; } bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, unsigned int max_size_px) { NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f); if (image == nullptr) { // printf("Could not open SVG image.\n"); reset(); return false; } float scale = (float)max_size_px / std::max(image->width, image->height); m_width = (int)(scale * image->width); m_height = (int)(scale * image->height); int n_pixels = m_width * m_height; if (n_pixels <= 0) { reset(); return false; } NSVGrasterizer* rast = nsvgCreateRasterizer(); if (rast == nullptr) { // printf("Could not init rasterizer.\n"); nsvgDelete(image); reset(); return false; } // creates the temporary buffer only once, with max size, and reuse it for all the levels, if generating mipmaps std::vector data(n_pixels * 4, 0); nsvgRasterize(rast, image, 0, 0, scale, data.data(), m_width, m_height, m_width * 4); // sends data to gpu ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); ::glGenTextures(1, &m_id); ::glBindTexture(GL_TEXTURE_2D, m_id); ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); if (use_mipmaps) { // we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards int lod_w = m_width; int lod_h = m_height; GLint level = 0; while ((lod_w > 1) || (lod_h > 1)) { ++level; lod_w = std::max(lod_w / 2, 1); lod_h = std::max(lod_h / 2, 1); scale /= 2.0f; nsvgRasterize(rast, image, 0, 0, scale, data.data(), lod_w, lod_h, lod_w * 4); ::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); } ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); } else { ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); } ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ::glBindTexture(GL_TEXTURE_2D, 0); m_source = filename; nsvgDeleteRasterizer(rast); nsvgDelete(image); return true; } #endif // ENABLE_TEXTURES_FROM_SVG } // namespace GUI } // namespace Slic3r