Primitive::Ptr Renderer::CreateImage( const sf::FloatRect& rect, const sf::Image& image, sf::Color background_color_hint ) { sf::Vector2f offset( LoadImage( image, background_color_hint ) ); Primitive::Ptr primitive( new Primitive ); Primitive::Vertex vertex0; Primitive::Vertex vertex1; Primitive::Vertex vertex2; Primitive::Vertex vertex3; vertex0.position = sf::Vector2f( std::floor( rect.left + .5f ), std::floor( rect.top + .5f ) ); vertex1.position = sf::Vector2f( std::floor( rect.left + .5f ), std::floor( rect.top + .5f ) + std::floor( rect.height + .5f ) ); vertex2.position = sf::Vector2f( std::floor( rect.left + .5f ) + std::floor( rect.width + .5f ), std::floor( rect.top + .5f ) ); vertex3.position = sf::Vector2f( std::floor( rect.left + .5f ) + std::floor( rect.width + .5f ), std::floor( rect.top + .5f ) + std::floor( rect.height + .5f ) ); vertex0.color = sf::Color( 255, 255, 255, 255 ); vertex1.color = sf::Color( 255, 255, 255, 255 ); vertex2.color = sf::Color( 255, 255, 255, 255 ); vertex3.color = sf::Color( 255, 255, 255, 255 ); vertex0.texture_coordinate = offset + sf::Vector2f( 0.f, 0.f ); vertex1.texture_coordinate = offset + sf::Vector2f( 0.f, static_cast<float>( image.getHeight() ) ); vertex2.texture_coordinate = offset + sf::Vector2f( static_cast<float>( image.getWidth() ), 0.f ); vertex3.texture_coordinate = offset + sf::Vector2f( static_cast<float>( image.getWidth() ), static_cast<float>( image.getHeight() ) ); primitive->AddVertex( vertex0 ); primitive->AddVertex( vertex1 ); primitive->AddVertex( vertex2 ); primitive->AddVertex( vertex2 ); primitive->AddVertex( vertex1 ); primitive->AddVertex( vertex3 ); AddPrimitive( primitive ); return primitive; }
sf::Vector2f Renderer::LoadImage( const sf::Image& image, sf::Color background_color_hint, sf::Color foreground_color_hint, bool force_insert ) { const sf::Uint8* pixels_ptr = image.getPixelsPtr(); if( !force_insert ) { std::map<const sf::Uint8*, sf::Vector2f>::iterator iter( m_atlas_offsets.find( pixels_ptr ) ); if( iter != m_atlas_offsets.end() ) { return iter->second; } } sf::Image preblended_image; preblended_image.create( image.getWidth(), image.getHeight(), image.getPixelsPtr() ); // If we get a proper background color hint and preblend is enabled, // precompute blended color with ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ). if( m_preblend ) { if( background_color_hint.a == 255 ) { float foreground_r_factor = 1.f; float foreground_g_factor = 1.f; float foreground_b_factor = 1.f; if( foreground_color_hint.a == 255 ) { foreground_r_factor = static_cast<float>( foreground_color_hint.r ) / 255.f; foreground_g_factor = static_cast<float>( foreground_color_hint.g ) / 255.f; foreground_b_factor = static_cast<float>( foreground_color_hint.b ) / 255.f; } std::size_t pixel_count = preblended_image.getWidth() * preblended_image.getHeight(); sf::Uint8* bytes = new sf::Uint8[pixel_count * 4]; memcpy( bytes, preblended_image.getPixelsPtr(), pixel_count * 4 ); for( std::size_t index = 0; index < pixel_count; ++index ) { // Alpha float alpha = static_cast<float>( bytes[index * 4 + 3] ) / 255.f; // Red bytes[index * 4 + 0] = static_cast<sf::Uint8>( static_cast<float>( bytes[index * 4 + 0] ) * foreground_r_factor * alpha + static_cast<float>( background_color_hint.r ) * ( 1.f - alpha ) ); // Green bytes[index * 4 + 1] = static_cast<sf::Uint8>( static_cast<float>( bytes[index * 4 + 1] ) * foreground_g_factor * alpha + static_cast<float>( background_color_hint.g ) * ( 1.f - alpha ) ); // Blue bytes[index * 4 + 2] = static_cast<sf::Uint8>( static_cast<float>( bytes[index * 4 + 2] ) * foreground_b_factor * alpha + static_cast<float>( background_color_hint.b ) * ( 1.f - alpha ) ); } preblended_image.create( preblended_image.getWidth(), preblended_image.getHeight(), bytes ); delete[] bytes; } else { m_preblend = false; #ifdef SFGUI_DEBUG std::cerr << "Detected alpha value " << static_cast<int>( background_color_hint.a ) << " in background color hint. Disabling preblend.\n"; #endif } } const sf::Uint8* bytes = preblended_image.getPixelsPtr(); std::size_t byte_count = preblended_image.getWidth() * preblended_image.getHeight() * 4; // Disable this check for now. static sf::Uint8 alpha_threshold = 255; for ( ; byte_count; --byte_count ) { // Check if the image makes intentional use of the alpha channel. if( m_depth_clear_strategy && !( byte_count % 4 ) && ( bytes[ byte_count - 1 ] > alpha_threshold ) && ( bytes[ byte_count - 1 ] < 255 ) ) { #ifdef SFGUI_DEBUG std::cerr << "Detected alpha value " << static_cast<int>( bytes[ byte_count - 1 ] ) << " in texture, disabling depth test.\n"; #endif m_depth_clear_strategy = NO_DEPTH; } } // Image needs to be loaded into atlas. sf::Image old_image = m_texture_atlas.copyToImage(); sf::Image new_image; // We insert padding between atlas elements to prevent // texture filtering from screwing up our images. // If 1 pixel isn't enough, increase. const static unsigned int padding = 1; new_image.create( std::max( old_image.getWidth(), preblended_image.getWidth() ), old_image.getHeight() + preblended_image.getHeight() + padding, sf::Color::White ); new_image.copy( old_image, 0, 0 ); new_image.copy( preblended_image, 0, old_image.getHeight() + padding ); m_texture_atlas.loadFromImage( new_image ); sf::Vector2f offset = sf::Vector2f( 0.f, static_cast<float>( old_image.getHeight() + padding ) ); InvalidateVBO(); if( !force_insert ) { m_atlas_offsets[pixels_ptr] = offset; } return offset; }