2019-02-20 14:23:23 +00:00
# include "libslic3r/libslic3r.h"
2018-06-13 07:12:16 +00:00
# include "GLTexture.hpp"
2019-03-27 13:42:09 +00:00
# include "3DScene.hpp"
2018-06-13 07:12:16 +00:00
# include <GL/glew.h>
# include <wx/image.h>
2018-06-21 07:54:43 +00:00
# include <boost/filesystem.hpp>
2019-02-20 14:23:23 +00:00
# include <boost/algorithm/string/predicate.hpp>
2018-06-21 07:54:43 +00:00
2018-06-13 07:12:16 +00:00
# include <vector>
2018-06-14 08:37:28 +00:00
# include <algorithm>
2019-05-31 13:25:02 +00:00
# include <thread>
# define STB_DXT_IMPLEMENTATION
# include "stb_dxt/stb_dxt.h"
2018-06-13 07:12:16 +00:00
2019-02-20 14:23:23 +00:00
# include "nanosvg/nanosvg.h"
# include "nanosvg/nanosvgrast.h"
2019-02-27 09:28:36 +00:00
# include "libslic3r/Utils.hpp"
2019-02-27 09:03:58 +00:00
2018-06-13 07:12:16 +00:00
namespace Slic3r {
namespace GUI {
2019-05-31 13:25:02 +00:00
void GLTexture : : Compressor : : reset ( )
{
2019-08-06 09:29:26 +00:00
if ( m_thread . joinable ( ) ) {
2019-08-01 09:01:18 +00:00
m_abort_compressing = true ;
2019-08-06 09:29:26 +00:00
m_thread . join ( ) ;
m_levels . clear ( ) ;
m_num_levels_compressed = 0 ;
m_abort_compressing = false ;
}
assert ( m_levels . empty ( ) ) ;
assert ( m_abort_compressing = = false ) ;
assert ( m_num_levels_compressed = = 0 ) ;
2019-05-31 13:25:02 +00:00
}
void GLTexture : : Compressor : : start_compressing ( )
{
2019-08-06 09:29:26 +00:00
// The worker thread should be stopped already.
assert ( ! m_thread . joinable ( ) ) ;
assert ( ! m_levels . empty ( ) ) ;
assert ( m_abort_compressing = = false ) ;
assert ( m_num_levels_compressed = = 0 ) ;
if ( ! m_levels . empty ( ) ) {
std : : thread thrd ( & GLTexture : : Compressor : : compress , this ) ;
m_thread = std : : move ( thrd ) ;
}
2019-05-31 13:25:02 +00:00
}
bool GLTexture : : Compressor : : unsent_compressed_data_available ( ) const
{
2019-08-06 09:29:26 +00:00
if ( m_levels . empty ( ) )
return false ;
2019-09-04 07:47:00 +00:00
// Querying the atomic m_num_levels_compressed value synchronizes processor caches, so that the data of m_levels modified by the worker thread are accessible to the calling thread.
2019-08-06 09:29:26 +00:00
unsigned int num_compressed = m_num_levels_compressed ;
for ( unsigned int i = 0 ; i < num_compressed ; + + i )
if ( ! m_levels [ i ] . sent_to_gpu & & ! m_levels [ i ] . compressed_data . empty ( ) )
2019-05-31 13:25:02 +00:00
return true ;
return false ;
}
void GLTexture : : Compressor : : send_compressed_data_to_gpu ( )
{
2019-06-03 11:53:30 +00:00
// this method should be called inside the main thread of Slicer or a new OpenGL context (sharing resources) would be needed
2019-08-06 09:29:26 +00:00
if ( m_levels . empty ( ) )
return ;
2019-06-03 11:53:30 +00:00
2019-05-31 13:25:02 +00:00
glsafe ( : : glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 ) ) ;
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , m_texture . m_id ) ) ;
2019-08-06 09:29:26 +00:00
// Querying the atomic m_num_levels_compressed value synchronizes processor caches, so that the dat of m_levels modified by the worker thread are accessible to the calling thread.
int num_compressed = ( int ) m_num_levels_compressed ;
for ( int i = 0 ; i < num_compressed ; + + i )
2019-05-31 13:25:02 +00:00
{
Level & level = m_levels [ i ] ;
2019-08-06 09:29:26 +00:00
if ( ! level . sent_to_gpu & & ! level . compressed_data . empty ( ) )
2019-05-31 13:25:02 +00:00
{
glsafe ( : : glCompressedTexSubImage2D ( GL_TEXTURE_2D , ( GLint ) i , 0 , 0 , ( GLsizei ) level . w , ( GLsizei ) level . h , GL_COMPRESSED_RGBA_S3TC_DXT5_EXT , ( GLsizei ) level . compressed_data . size ( ) , ( const GLvoid * ) level . compressed_data . data ( ) ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , i ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , ( i > 0 ) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR ) ) ;
level . sent_to_gpu = true ;
// we are done with the compressed data, we can discard it
level . compressed_data . clear ( ) ;
}
}
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , 0 ) ) ;
2019-06-05 08:07:59 +00:00
2019-09-04 07:47:00 +00:00
if ( num_compressed = = ( int ) m_levels . size ( ) )
// Finalize the worker thread, close it.
2019-08-06 09:29:26 +00:00
this - > reset ( ) ;
2019-06-05 08:07:59 +00:00
}
2019-05-31 13:25:02 +00:00
void GLTexture : : Compressor : : compress ( )
{
// reference: https://github.com/Cyan4973/RygsDXTc
2019-08-06 09:29:26 +00:00
assert ( m_num_levels_compressed = = 0 ) ;
assert ( m_abort_compressing = = false ) ;
2019-08-01 09:01:18 +00:00
2019-05-31 13:25:02 +00:00
for ( Level & level : m_levels )
{
if ( m_abort_compressing )
break ;
// stb_dxt library, despite claiming that the needed size of the destination buffer is equal to (source buffer size)/4,
2019-10-02 08:57:07 +00:00
// crashes if doing so, requiring a minimum of 16 bytes and up to a third of the source buffer size, so we set the destination buffer initial size to be half the source buffer size
level . compressed_data = std : : vector < unsigned char > ( std : : max ( ( unsigned int ) 16 , level . w * level . h * 2 ) , 0 ) ;
2019-05-31 13:25:02 +00:00
int compressed_size = 0 ;
rygCompress ( level . compressed_data . data ( ) , level . src_data . data ( ) , level . w , level . h , 1 , compressed_size ) ;
level . compressed_data . resize ( compressed_size ) ;
// we are done with the source data, we can discard it
level . src_data . clear ( ) ;
2019-08-06 09:29:26 +00:00
+ + m_num_levels_compressed ;
2019-05-31 13:25:02 +00:00
}
}
2018-07-31 10:25:00 +00:00
GLTexture : : Quad_UVs GLTexture : : FullTextureUVs = { { 0.0f , 1.0f } , { 1.0f , 1.0f } , { 1.0f , 0.0f } , { 0.0f , 0.0f } } ;
2018-06-13 07:12:16 +00:00
GLTexture : : GLTexture ( )
: m_id ( 0 )
, m_width ( 0 )
, m_height ( 0 )
, m_source ( " " )
2019-05-31 13:25:02 +00:00
, m_compressor ( * this )
2018-06-13 07:12:16 +00:00
{
}
GLTexture : : ~ GLTexture ( )
{
reset ( ) ;
}
2019-07-19 09:18:19 +00:00
bool GLTexture : : load_from_file ( const std : : string & filename , bool use_mipmaps , ECompressionType compression_type , bool apply_anisotropy )
2019-02-20 14:23:23 +00:00
{
reset ( ) ;
if ( ! boost : : filesystem : : exists ( filename ) )
return false ;
if ( boost : : algorithm : : iends_with ( filename , " .png " ) )
2019-07-19 09:18:19 +00:00
return load_from_png ( filename , use_mipmaps , compression_type , apply_anisotropy ) ;
2019-07-19 07:18:09 +00:00
else
return false ;
}
2019-05-28 13:21:34 +00:00
bool GLTexture : : load_from_svg_file ( const std : : string & filename , bool use_mipmaps , bool compress , bool apply_anisotropy , unsigned int max_size_px )
2019-02-20 14:23:23 +00:00
{
reset ( ) ;
if ( ! boost : : filesystem : : exists ( filename ) )
return false ;
if ( boost : : algorithm : : iends_with ( filename , " .svg " ) )
2019-05-28 13:21:34 +00:00
return load_from_svg ( filename , use_mipmaps , compress , apply_anisotropy , max_size_px ) ;
2019-02-20 14:23:23 +00:00
else
return false ;
}
2018-06-13 07:12:16 +00:00
2019-05-28 10:53:16 +00:00
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 )
2019-02-26 08:56:23 +00:00
{
reset ( ) ;
2019-02-26 11:56:13 +00:00
if ( filenames . empty ( ) | | states . empty ( ) | | ( sprite_size_px = = 0 ) )
2019-02-26 08:56:23 +00:00
return false ;
2020-01-07 11:40:03 +00:00
# if ENABLE_MODIFIED_TOOLBAR_TEXTURES
// every tile needs to have a 1px border around it to avoid artifacts when linear sampling on its edges
unsigned int sprite_size_px_ex = sprite_size_px + 1 ;
m_width = 1 + ( int ) ( sprite_size_px_ex * states . size ( ) ) ;
m_height = 1 + ( int ) ( sprite_size_px_ex * filenames . size ( ) ) ;
# else
2019-02-26 11:56:13 +00:00
m_width = ( int ) ( sprite_size_px * states . size ( ) ) ;
2019-02-26 09:40:00 +00:00
m_height = ( int ) ( sprite_size_px * filenames . size ( ) ) ;
2020-01-07 11:40:03 +00:00
# endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES
2019-02-26 08:56:23 +00:00
int n_pixels = m_width * m_height ;
2020-01-07 11:40:03 +00:00
# if ENABLE_MODIFIED_TOOLBAR_TEXTURES
int sprite_n_pixels = sprite_size_px_ex * sprite_size_px_ex ;
int sprite_stride = sprite_size_px_ex * 4 ;
# else
2019-02-26 11:56:13 +00:00
int sprite_n_pixels = sprite_size_px * sprite_size_px ;
int sprite_stride = sprite_size_px * 4 ;
2020-01-07 11:40:03 +00:00
# endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES
int sprite_bytes = sprite_n_pixels * 4 ;
2019-02-26 08:56:23 +00:00
if ( n_pixels < = 0 )
{
reset ( ) ;
return false ;
}
std : : vector < unsigned char > data ( n_pixels * 4 , 0 ) ;
2019-02-26 11:56:13 +00:00
std : : vector < unsigned char > sprite_data ( sprite_bytes , 0 ) ;
std : : vector < unsigned char > sprite_white_only_data ( sprite_bytes , 0 ) ;
std : : vector < unsigned char > sprite_gray_only_data ( sprite_bytes , 0 ) ;
std : : vector < unsigned char > output_data ( sprite_bytes , 0 ) ;
2019-02-26 08:56:23 +00:00
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 ;
2019-03-27 14:42:01 +00:00
NSVGimage * image = nsvgParseFromFile ( filename . c_str ( ) , " px " , 96.0f ) ;
2019-02-26 08:56:23 +00:00
if ( image = = nullptr )
continue ;
float scale = ( float ) sprite_size_px / std : : max ( image - > width , image - > height ) ;
2020-01-07 11:40:03 +00:00
# if ENABLE_MODIFIED_TOOLBAR_TEXTURES
// offset by 1 to leave the first pixel empty (both in x and y)
nsvgRasterize ( rast , image , 1 , 1 , scale , sprite_data . data ( ) , sprite_size_px , sprite_size_px , sprite_stride ) ;
# else
2019-02-26 11:56:13 +00:00
nsvgRasterize ( rast , image , 0 , 0 , scale , sprite_data . data ( ) , sprite_size_px , sprite_size_px , sprite_stride ) ;
2020-01-07 11:40:03 +00:00
# endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES
2019-02-26 11:56:13 +00:00
// makes white only copy of the sprite
: : memcpy ( ( void * ) sprite_white_only_data . data ( ) , ( const void * ) sprite_data . data ( ) , sprite_bytes ) ;
for ( int i = 0 ; i < sprite_n_pixels ; + + i )
{
2019-02-27 09:03:58 +00:00
int offset = i * 4 ;
if ( sprite_white_only_data . data ( ) [ offset ] ! = 0 )
: : memset ( ( void * ) & sprite_white_only_data . data ( ) [ offset ] , 255 , 3 ) ;
2019-02-26 11:56:13 +00:00
}
// makes gray only copy of the sprite
: : memcpy ( ( void * ) sprite_gray_only_data . data ( ) , ( const void * ) sprite_data . data ( ) , sprite_bytes ) ;
for ( int i = 0 ; i < sprite_n_pixels ; + + i )
{
2019-02-27 09:03:58 +00:00
int offset = i * 4 ;
if ( sprite_gray_only_data . data ( ) [ offset ] ! = 0 )
: : memset ( ( void * ) & sprite_gray_only_data . data ( ) [ offset ] , 128 , 3 ) ;
2019-02-26 11:56:13 +00:00
}
2019-02-26 08:56:23 +00:00
2020-01-07 11:40:03 +00:00
# if ENABLE_MODIFIED_TOOLBAR_TEXTURES
int sprite_offset_px = sprite_id * ( int ) sprite_size_px_ex * m_width ;
# else
2019-02-26 08:56:23 +00:00
int sprite_offset_px = sprite_id * sprite_size_px * m_width ;
2020-01-07 11:40:03 +00:00
# endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES
2019-02-26 11:56:13 +00:00
int state_id = - 1 ;
for ( const std : : pair < int , bool > & state : states )
2019-02-26 08:56:23 +00:00
{
2019-02-26 11:56:13 +00:00
+ + state_id ;
// select the sprite variant
std : : vector < unsigned char > * src = nullptr ;
switch ( state . first )
{
case 1 : { src = & sprite_white_only_data ; break ; }
case 2 : { src = & sprite_gray_only_data ; break ; }
default : { src = & sprite_data ; break ; }
}
: : memcpy ( ( void * ) output_data . data ( ) , ( const void * ) src - > data ( ) , sprite_bytes ) ;
// applies background, if needed
if ( state . second )
{
2020-01-07 11:40:03 +00:00
# if ENABLE_MODIFIED_TOOLBAR_TEXTURES
float inv_255 = 1.0f / 255.0f ;
// offset by 1 to leave the first pixel empty (both in x and y)
2020-01-17 09:34:32 +00:00
for ( unsigned int r = 1 ; r < = sprite_size_px ; + + r )
2020-01-07 11:40:03 +00:00
{
2020-01-17 09:34:32 +00:00
unsigned int offset_r = r * sprite_size_px_ex ;
for ( unsigned int c = 1 ; c < = sprite_size_px ; + + c )
2020-01-07 11:40:03 +00:00
{
2020-01-17 09:34:32 +00:00
unsigned int offset = ( offset_r + c ) * 4 ;
2020-01-07 11:40:03 +00:00
float alpha = ( float ) output_data . data ( ) [ offset + 3 ] * inv_255 ;
output_data . data ( ) [ offset + 0 ] = ( unsigned char ) ( output_data . data ( ) [ offset + 0 ] * alpha ) ;
output_data . data ( ) [ offset + 1 ] = ( unsigned char ) ( output_data . data ( ) [ offset + 1 ] * alpha ) ;
output_data . data ( ) [ offset + 2 ] = ( unsigned char ) ( output_data . data ( ) [ offset + 2 ] * alpha ) ;
output_data . data ( ) [ offset + 3 ] = ( unsigned char ) ( 128 * ( 1.0f - alpha ) + output_data . data ( ) [ offset + 3 ] * alpha ) ;
}
}
# else
2019-02-26 11:56:13 +00:00
for ( int i = 0 ; i < sprite_n_pixels ; + + i )
{
2019-02-27 09:03:58 +00:00
int offset = i * 4 ;
float alpha = ( float ) output_data . data ( ) [ offset + 3 ] / 255.0f ;
output_data . data ( ) [ offset + 0 ] = ( unsigned char ) ( output_data . data ( ) [ offset + 0 ] * alpha ) ;
output_data . data ( ) [ offset + 1 ] = ( unsigned char ) ( output_data . data ( ) [ offset + 1 ] * alpha ) ;
output_data . data ( ) [ offset + 2 ] = ( unsigned char ) ( output_data . data ( ) [ offset + 2 ] * alpha ) ;
output_data . data ( ) [ offset + 3 ] = ( unsigned char ) ( 128 * ( 1.0f - alpha ) + output_data . data ( ) [ offset + 3 ] * alpha ) ;
2019-02-26 11:56:13 +00:00
}
2020-01-07 11:40:03 +00:00
# endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES
2019-02-26 11:56:13 +00:00
}
2020-01-07 11:40:03 +00:00
# if ENABLE_MODIFIED_TOOLBAR_TEXTURES
int state_offset_px = sprite_offset_px + state_id * sprite_size_px_ex ;
for ( int j = 0 ; j < ( int ) sprite_size_px_ex ; + + j )
{
: : memcpy ( ( void * ) & data . data ( ) [ ( state_offset_px + j * m_width ) * 4 ] , ( const void * ) & output_data . data ( ) [ j * sprite_stride ] , sprite_stride ) ;
}
# else
2019-02-26 11:56:13 +00:00
int state_offset_px = sprite_offset_px + state_id * sprite_size_px ;
2019-03-05 09:54:03 +00:00
for ( int j = 0 ; j < ( int ) sprite_size_px ; + + j )
2019-02-26 08:56:23 +00:00
{
2019-02-27 09:03:58 +00:00
: : memcpy ( ( void * ) & data . data ( ) [ ( state_offset_px + j * m_width ) * 4 ] , ( const void * ) & output_data . data ( ) [ j * sprite_stride ] , sprite_stride ) ;
2019-02-26 08:56:23 +00:00
}
2020-01-07 11:40:03 +00:00
# endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES
2019-02-26 08:56:23 +00:00
}
nsvgDelete ( image ) ;
}
nsvgDeleteRasterizer ( rast ) ;
// sends data to gpu
2019-03-27 13:42:09 +00:00
glsafe ( : : glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 ) ) ;
glsafe ( : : glGenTextures ( 1 , & m_id ) ) ;
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , m_id ) ) ;
2019-05-28 10:53:16 +00:00
if ( compress & & GLEW_EXT_texture_compression_s3tc )
2019-05-21 12:19:03 +00:00
glsafe ( : : glTexImage2D ( GL_TEXTURE_2D , 0 , GL_COMPRESSED_RGBA_S3TC_DXT5_EXT , ( GLsizei ) m_width , ( GLsizei ) m_height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , ( const void * ) data . data ( ) ) ) ;
else
glsafe ( : : glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , ( GLsizei ) m_width , ( GLsizei ) m_height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , ( const void * ) data . data ( ) ) ) ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , 0 ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ) ;
2019-02-26 08:56:23 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , 0 ) ) ;
2019-02-26 08:56:23 +00:00
m_source = filenames . front ( ) ;
2019-02-26 11:56:13 +00:00
#if 0
// debug output
static int pass = 0 ;
+ + pass ;
2019-02-26 08:56:23 +00:00
wxImage output ( m_width , m_height ) ;
output . InitAlpha ( ) ;
for ( int h = 0 ; h < m_height ; + + h )
{
2019-02-27 09:03:58 +00:00
int px_h = h * m_width ;
2019-02-26 08:56:23 +00:00
for ( int w = 0 ; w < m_width ; + + w )
{
2019-02-27 09:03:58 +00:00
int offset = ( px_h + w ) * 4 ;
output . SetRGB ( w , h , data . data ( ) [ offset + 0 ] , data . data ( ) [ offset + 1 ] , data . data ( ) [ offset + 2 ] ) ;
output . SetAlpha ( w , h , data . data ( ) [ offset + 3 ] ) ;
2019-02-26 08:56:23 +00:00
}
}
2019-02-27 09:03:58 +00:00
std : : string out_filename = resources_dir ( ) + " /icons/test_ " + std : : to_string ( pass ) + " .png " ;
output . SaveFile ( out_filename , wxBITMAP_TYPE_PNG ) ;
2019-02-26 11:56:13 +00:00
# endif // 0
2019-02-26 08:56:23 +00:00
return true ;
}
2018-06-13 07:12:16 +00:00
void GLTexture : : reset ( )
{
if ( m_id ! = 0 )
2019-03-27 13:42:09 +00:00
glsafe ( : : glDeleteTextures ( 1 , & m_id ) ) ;
2018-06-13 07:12:16 +00:00
m_id = 0 ;
m_width = 0 ;
m_height = 0 ;
m_source = " " ;
2019-05-31 13:25:02 +00:00
m_compressor . reset ( ) ;
}
2018-06-13 07:12:16 +00:00
void GLTexture : : render_texture ( unsigned int tex_id , float left , float right , float bottom , float top )
2018-07-31 10:25:00 +00:00
{
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 )
2018-06-13 07:12:16 +00:00
{
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnable ( GL_BLEND ) ) ;
glsafe ( : : glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ) ;
2018-06-22 13:11:04 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnable ( GL_TEXTURE_2D ) ) ;
glsafe ( : : glTexEnvi ( GL_TEXTURE_ENV , GL_TEXTURE_ENV_MODE , GL_REPLACE ) ) ;
2018-06-13 07:12:16 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , ( GLuint ) tex_id ) ) ;
2018-06-13 07:12:16 +00:00
: : glBegin ( GL_QUADS ) ;
2018-07-31 10:25:00 +00:00
: : 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 ) ;
2019-03-27 13:42:09 +00:00
glsafe ( : : glEnd ( ) ) ;
2018-06-13 07:12:16 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , 0 ) ) ;
2018-06-13 07:12:16 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glDisable ( GL_TEXTURE_2D ) ) ;
glsafe ( : : glDisable ( GL_BLEND ) ) ;
2018-06-13 07:12:16 +00:00
}
2019-07-19 09:18:19 +00:00
bool GLTexture : : load_from_png ( const std : : string & filename , bool use_mipmaps , ECompressionType compression_type , bool apply_anisotropy )
2019-07-19 07:18:09 +00:00
{
2019-07-19 09:18:19 +00:00
bool compression_enabled = ( compression_type ! = None ) & & GLEW_EXT_texture_compression_s3tc ;
2019-07-19 07:18:09 +00:00
// 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 ( ) ;
bool requires_rescale = false ;
2019-07-19 09:18:19 +00:00
if ( compression_enabled & & ( compression_type = = MultiThreaded ) )
2019-07-19 07:18:09 +00:00
{
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 4
int width_rem = m_width % 4 ;
int height_rem = m_height % 4 ;
if ( width_rem ! = 0 )
{
m_width + = ( 4 - width_rem ) ;
requires_rescale = true ;
}
if ( height_rem ! = 0 )
{
m_height + = ( 4 - height_rem ) ;
requires_rescale = true ;
}
}
if ( requires_rescale )
image = image . ResampleBicubic ( m_width , m_height ) ;
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 < unsigned char > 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
glsafe ( : : glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 ) ) ;
glsafe ( : : glGenTextures ( 1 , & m_id ) ) ;
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , m_id ) ) ;
if ( apply_anisotropy )
{
GLfloat max_anisotropy = GLCanvas3DManager : : get_gl_info ( ) . get_max_anisotropy ( ) ;
if ( max_anisotropy > 1.0f )
glsafe ( : : glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_MAX_ANISOTROPY_EXT , max_anisotropy ) ) ;
}
if ( compression_enabled )
{
2019-07-19 09:18:19 +00:00
if ( compression_type = = SingleThreaded )
glsafe ( : : glTexImage2D ( GL_TEXTURE_2D , 0 , GL_COMPRESSED_RGBA_S3TC_DXT5_EXT , ( GLsizei ) m_width , ( GLsizei ) m_height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , ( const void * ) data . data ( ) ) ) ;
else
{
// initializes the texture on GPU
glsafe ( : : glTexImage2D ( GL_TEXTURE_2D , 0 , GL_COMPRESSED_RGBA_S3TC_DXT5_EXT , ( GLsizei ) m_width , ( GLsizei ) m_height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , 0 ) ) ;
// and send the uncompressed data to the compressor
m_compressor . add_level ( ( unsigned int ) m_width , ( unsigned int ) m_height , data ) ;
}
2019-07-19 07:18:09 +00:00
}
else
glsafe ( : : 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 ;
2019-10-02 08:57:07 +00:00
while ( ( lod_w > 1 ) | | ( lod_h > 1 ) )
2019-07-19 07:18:09 +00:00
{
+ + level ;
lod_w = std : : max ( lod_w / 2 , 1 ) ;
lod_h = std : : max ( lod_h / 2 , 1 ) ;
n_pixels = lod_w * lod_h ;
image = image . ResampleBicubic ( lod_w , lod_h ) ;
data . resize ( n_pixels * 4 ) ;
img_rgb = image . GetData ( ) ;
img_alpha = image . GetAlpha ( ) ;
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 ;
}
if ( compression_enabled )
{
2019-07-19 09:18:19 +00:00
if ( compression_type = = SingleThreaded )
glsafe ( : : glTexImage2D ( GL_TEXTURE_2D , level , GL_COMPRESSED_RGBA_S3TC_DXT5_EXT , ( GLsizei ) m_width , ( GLsizei ) m_height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , ( const void * ) data . data ( ) ) ) ;
else
{
// initializes the texture on GPU
glsafe ( : : glTexImage2D ( GL_TEXTURE_2D , level , GL_COMPRESSED_RGBA_S3TC_DXT5_EXT , ( GLsizei ) lod_w , ( GLsizei ) lod_h , 0 , GL_RGBA , GL_UNSIGNED_BYTE , 0 ) ) ;
// and send the uncompressed data to the compressor
m_compressor . add_level ( ( unsigned int ) lod_w , ( unsigned int ) lod_h , data ) ;
}
2019-07-19 07:18:09 +00:00
}
else
glsafe ( : : glTexImage2D ( GL_TEXTURE_2D , level , GL_RGBA , ( GLsizei ) lod_w , ( GLsizei ) lod_h , 0 , GL_RGBA , GL_UNSIGNED_BYTE , ( const void * ) data . data ( ) ) ) ;
}
if ( ! compression_enabled )
{
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , level ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR_MIPMAP_LINEAR ) ) ;
}
}
else
{
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , 0 ) ) ;
}
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ) ;
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , 0 ) ) ;
m_source = filename ;
2019-07-19 09:18:19 +00:00
if ( compression_enabled & & ( compression_type = = MultiThreaded ) )
2019-07-19 07:18:09 +00:00
// start asynchronous compression
m_compressor . start_compressing ( ) ;
return true ;
}
2019-05-28 13:21:34 +00:00
bool GLTexture : : load_from_svg ( const std : : string & filename , bool use_mipmaps , bool compress , bool apply_anisotropy , unsigned int max_size_px )
2019-02-20 14:23:23 +00:00
{
2019-05-31 13:25:02 +00:00
bool compression_enabled = compress & & GLEW_EXT_texture_compression_s3tc ;
2019-03-27 14:42:01 +00:00
NSVGimage * image = nsvgParseFromFile ( filename . c_str ( ) , " px " , 96.0f ) ;
2019-02-20 14:23:23 +00:00
if ( image = = nullptr )
{
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 ) ;
2019-05-31 13:25:02 +00:00
if ( compression_enabled )
{
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 4
int width_rem = m_width % 4 ;
int height_rem = m_height % 4 ;
if ( width_rem ! = 0 )
m_width + = ( 4 - width_rem ) ;
if ( height_rem ! = 0 )
m_height + = ( 4 - height_rem ) ;
}
2019-02-20 14:23:23 +00:00
int n_pixels = m_width * m_height ;
if ( n_pixels < = 0 )
{
reset ( ) ;
2019-04-04 07:20:11 +00:00
nsvgDelete ( image ) ;
2019-02-20 14:23:23 +00:00
return false ;
}
NSVGrasterizer * rast = nsvgCreateRasterizer ( ) ;
if ( rast = = nullptr )
{
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 < unsigned char > data ( n_pixels * 4 , 0 ) ;
nsvgRasterize ( rast , image , 0 , 0 , scale , data . data ( ) , m_width , m_height , m_width * 4 ) ;
// sends data to gpu
2019-03-27 13:42:09 +00:00
glsafe ( : : glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 ) ) ;
glsafe ( : : glGenTextures ( 1 , & m_id ) ) ;
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , m_id ) ) ;
2019-06-24 07:54:58 +00:00
2019-05-28 13:21:34 +00:00
if ( apply_anisotropy )
{
GLfloat max_anisotropy = GLCanvas3DManager : : get_gl_info ( ) . get_max_anisotropy ( ) ;
if ( max_anisotropy > 1.0f )
glsafe ( : : glTexParameterf ( GL_TEXTURE_2D , GL_TEXTURE_MAX_ANISOTROPY_EXT , max_anisotropy ) ) ;
}
2019-05-31 13:25:02 +00:00
if ( compression_enabled )
{
// initializes the texture on GPU
glsafe ( : : glTexImage2D ( GL_TEXTURE_2D , 0 , GL_COMPRESSED_RGBA_S3TC_DXT5_EXT , ( GLsizei ) m_width , ( GLsizei ) m_height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , 0 ) ) ;
// and send the uncompressed data to the compressor
m_compressor . add_level ( ( unsigned int ) m_width , ( unsigned int ) m_height , data ) ;
}
2019-05-21 12:19:03 +00:00
else
glsafe ( : : glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , ( GLsizei ) m_width , ( GLsizei ) m_height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , ( const void * ) data . data ( ) ) ) ;
2019-06-24 07:54:58 +00:00
2019-02-20 14:23:23 +00:00
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 ;
2019-10-02 08:57:07 +00:00
while ( ( lod_w > 1 ) | | ( lod_h > 1 ) )
2019-02-20 14:23:23 +00:00
{
+ + level ;
lod_w = std : : max ( lod_w / 2 , 1 ) ;
lod_h = std : : max ( lod_h / 2 , 1 ) ;
scale / = 2.0f ;
2019-05-31 13:25:02 +00:00
data . resize ( lod_w * lod_h * 4 ) ;
2019-02-20 14:23:23 +00:00
nsvgRasterize ( rast , image , 0 , 0 , scale , data . data ( ) , lod_w , lod_h , lod_w * 4 ) ;
2019-05-31 13:25:02 +00:00
if ( compression_enabled )
{
// initializes the texture on GPU
glsafe ( : : glTexImage2D ( GL_TEXTURE_2D , level , GL_COMPRESSED_RGBA_S3TC_DXT5_EXT , ( GLsizei ) lod_w , ( GLsizei ) lod_h , 0 , GL_RGBA , GL_UNSIGNED_BYTE , 0 ) ) ;
// and send the uncompressed data to the compressor
m_compressor . add_level ( ( unsigned int ) lod_w , ( unsigned int ) lod_h , data ) ;
}
2019-05-21 12:19:03 +00:00
else
glsafe ( : : glTexImage2D ( GL_TEXTURE_2D , level , GL_RGBA , ( GLsizei ) lod_w , ( GLsizei ) lod_h , 0 , GL_RGBA , GL_UNSIGNED_BYTE , ( const void * ) data . data ( ) ) ) ;
2019-02-20 14:23:23 +00:00
}
2019-05-31 13:25:02 +00:00
if ( ! compression_enabled )
{
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , level ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR_MIPMAP_LINEAR ) ) ;
}
2019-02-20 14:23:23 +00:00
}
else
{
2019-03-27 13:42:09 +00:00
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ) ;
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , 0 ) ) ;
2019-02-20 14:23:23 +00:00
}
2019-07-19 07:18:09 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ) ;
2019-02-20 14:23:23 +00:00
2019-03-27 13:42:09 +00:00
glsafe ( : : glBindTexture ( GL_TEXTURE_2D , 0 ) ) ;
2019-02-20 14:23:23 +00:00
m_source = filename ;
2019-05-31 13:25:02 +00:00
if ( compression_enabled )
// start asynchronous compression
m_compressor . start_compressing ( ) ;
2019-02-20 14:23:23 +00:00
nsvgDeleteRasterizer ( rast ) ;
nsvgDelete ( image ) ;
return true ;
}
2018-06-13 07:12:16 +00:00
} // namespace GUI
} // namespace Slic3r