diff --git a/common/gal/cairo/cairo_gal.cpp b/common/gal/cairo/cairo_gal.cpp index 85006603ac..bb14fd79f6 100644 --- a/common/gal/cairo/cairo_gal.cpp +++ b/common/gal/cairo/cairo_gal.cpp @@ -322,6 +322,12 @@ void CAIRO_GAL_BASE::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& a } +void CAIRO_GAL_BASE::DrawHoleWall( const VECTOR2D& aCenterPoint, double aRadius, double aWallWidth ) +{ + DrawCircle( aCenterPoint, aRadius + aWallWidth ); +} + + void CAIRO_GAL_BASE::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius ) { syncLineWidth(); diff --git a/common/gal/graphics_abstraction_layer.cpp b/common/gal/graphics_abstraction_layer.cpp index 44049634c9..cfd8669409 100644 --- a/common/gal/graphics_abstraction_layer.cpp +++ b/common/gal/graphics_abstraction_layer.cpp @@ -67,6 +67,7 @@ GAL::GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions ) : SetLayerDepth( 0.0 ); SetFlip( false, false ); SetLineWidth( 1.0f ); + SetMinLineWidth( 1.0f ); computeWorldScale(); SetAxesEnabled( false ); diff --git a/common/gal/opengl/opengl_gal.cpp b/common/gal/opengl/opengl_gal.cpp index cea17ab97f..bc747dbd53 100644 --- a/common/gal/opengl/opengl_gal.cpp +++ b/common/gal/opengl/opengl_gal.cpp @@ -407,10 +407,13 @@ OPENGL_GAL::OPENGL_GAL( const KIGFX::VC_SETTINGS& aVcSettings, GAL_DISPLAY_OPTIO SetTarget( TARGET_NONCACHED ); // Avoid uninitialized variables: - ufm_worldPixelSize = 1; - ufm_screenPixelSize = 1; - ufm_pixelSizeMultiplier = 1; - ufm_antialiasingOffset = 1; + ufm_worldPixelSize = -1; + ufm_screenPixelSize = -1; + ufm_pixelSizeMultiplier = -1; + ufm_antialiasingOffset = -1; + ufm_minLinePixelWidth = -1; + ufm_fontTexture = -1; + ufm_fontTextureWidth = -1; m_swapInterval = 0; } @@ -666,14 +669,6 @@ void OPENGL_GAL::BeginDrawing() glActiveTexture( GL_TEXTURE0 ); } - // Set shader parameter - GLint ufm_fontTexture = m_shader->AddParameter( "u_fontTexture" ); - GLint ufm_fontTextureWidth = m_shader->AddParameter( "u_fontTextureWidth" ); - ufm_worldPixelSize = m_shader->AddParameter( "u_worldPixelSize" ); - ufm_screenPixelSize = m_shader->AddParameter( "u_screenPixelSize" ); - ufm_pixelSizeMultiplier = m_shader->AddParameter( "u_pixelSizeMultiplier" ); - ufm_antialiasingOffset = m_shader->AddParameter( "u_antialiasingOffset" ); - m_shader->Use(); m_shader->SetParameter( ufm_fontTexture, (int) FONT_TEXTURE_UNIT ); m_shader->SetParameter( ufm_fontTextureWidth, (int) font_image.width ); @@ -694,6 +689,7 @@ void OPENGL_GAL::BeginDrawing() renderingOffset.x *= screenPixelSize.x; renderingOffset.y *= screenPixelSize.y; m_shader->SetParameter( ufm_antialiasingOffset, renderingOffset ); + m_shader->SetParameter( ufm_minLinePixelWidth, GetMinLineWidth() ); m_shader->Deactivate(); // Something between BeginDrawing and EndDrawing seems to depend on @@ -710,6 +706,18 @@ void OPENGL_GAL::BeginDrawing() #endif /* KICAD_GAL_PROFILE */ } +void OPENGL_GAL::SetMinLineWidth( float aLineWidth ) +{ + GAL::SetMinLineWidth( aLineWidth ); + + if( m_shader && ufm_minLinePixelWidth != -1 ) + { + m_shader->Use(); + m_shader->SetParameter( ufm_minLinePixelWidth, aLineWidth ); + m_shader->Deactivate(); + } +} + void OPENGL_GAL::EndDrawing() { @@ -901,6 +909,25 @@ void OPENGL_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius ) } +void OPENGL_GAL::DrawHoleWall( const VECTOR2D& aCenterPoint, double aHoleRadius, + double aWallWidth ) +{ + if( m_isFillEnabled ) + { + m_currentManager->Color( m_fillColor.r, m_fillColor.g, m_fillColor.b, m_fillColor.a ); + + m_currentManager->Shader( SHADER_HOLE_WALL, 1.0, aHoleRadius, aWallWidth ); + m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, m_layerDepth ); + + m_currentManager->Shader( SHADER_HOLE_WALL, 2.0, aHoleRadius, aWallWidth ); + m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, m_layerDepth ); + + m_currentManager->Shader( SHADER_HOLE_WALL, 3.0, aHoleRadius, aWallWidth ); + m_currentManager->Vertex( aCenterPoint.x, aCenterPoint.y, m_layerDepth ); + } +} + + void OPENGL_GAL::drawCircle( const VECTOR2D& aCenterPoint, double aRadius, bool aReserve ) { if( m_isFillEnabled ) @@ -2802,6 +2829,9 @@ void OPENGL_GAL::init() if( !m_shader->IsLinked() && !m_shader->Link() ) throw std::runtime_error( "Cannot link the shaders!" ); + // Set up shader parameters after linking + setupShaderParameters(); + // Check if video card supports textures big enough to fit the font atlas int maxTextureSize; glGetIntegerv( GL_MAX_TEXTURE_SIZE, &maxTextureSize ); @@ -2830,6 +2860,19 @@ void OPENGL_GAL::init() } +void OPENGL_GAL::setupShaderParameters() +{ + // Initialize shader uniform parameter locations + ufm_fontTexture = m_shader->AddParameter( "u_fontTexture" ); + ufm_fontTextureWidth = m_shader->AddParameter( "u_fontTextureWidth" ); + ufm_worldPixelSize = m_shader->AddParameter( "u_worldPixelSize" ); + ufm_screenPixelSize = m_shader->AddParameter( "u_screenPixelSize" ); + ufm_pixelSizeMultiplier = m_shader->AddParameter( "u_pixelSizeMultiplier" ); + ufm_antialiasingOffset = m_shader->AddParameter( "u_antialiasingOffset" ); + ufm_minLinePixelWidth = m_shader->AddParameter( "u_minLinePixelWidth" ); +} + + // Callback functions for the tesselator. Compare Redbook Chapter 11. void CALLBACK VertexCallback( GLvoid* aVertexPtr, void* aData ) { diff --git a/common/gal/shaders/kicad_frag.glsl b/common/gal/shaders/kicad_frag.glsl index ac420d1ee1..7252060e10 100644 --- a/common/gal/shaders/kicad_frag.glsl +++ b/common/gal/shaders/kicad_frag.glsl @@ -35,6 +35,7 @@ const float SHADER_FILLED_CIRCLE = 2.0; const float SHADER_STROKED_CIRCLE = 3.0; const float SHADER_FONT = 4.0; const float SHADER_LINE_A = 5.0; +const float SHADER_HOLE_WALL = 11.0; varying vec4 v_shaderParams; varying vec2 v_circleCoords; @@ -124,6 +125,10 @@ void main() { strokedCircle( v_circleCoords, v_shaderParams[2], v_shaderParams[3] ); } + else if( mode == SHADER_HOLE_WALL ) + { + strokedCircle( v_circleCoords, v_shaderParams[2], v_shaderParams[3] ); + } else if( mode == SHADER_FONT ) { vec2 tex = v_shaderParams.yz; diff --git a/common/gal/shaders/kicad_vert.glsl b/common/gal/shaders/kicad_vert.glsl index da62c6186d..2c806ec4a3 100644 --- a/common/gal/shaders/kicad_vert.glsl +++ b/common/gal/shaders/kicad_vert.glsl @@ -36,6 +36,7 @@ const float SHADER_LINE_C = 7.0; const float SHADER_LINE_D = 8.0; const float SHADER_LINE_E = 9.0; const float SHADER_LINE_F = 10.0; +const float SHADER_HOLE_WALL = 11.0; // Minimum line width const float MIN_WIDTH = 1.0; @@ -72,8 +73,8 @@ void computeLineCoords( bool posture, vec2 vs, vec2 vp, vec2 texcoord, vec2 dir, vec2 s = sign( vec2( gl_ModelViewProjectionMatrix[0][0], gl_ModelViewProjectionMatrix[1][1] ) ); - if( pixelWidth < 1.0 ) - pixelWidth = 1.0; + if( pixelWidth < u_minLinePixelWidth ) + pixelWidth = u_minLinePixelWidth; if ( pixelWidth > 1.0 || u_pixelSizeMultiplier > 1.0 ) { @@ -126,8 +127,59 @@ void computeCircleCoords( float mode, float vertexIndex, float radius, float lin vec4 adjust = vec4(-1, -1, 0, 0); - if( pixelWidth < 1.0 ) - pixelWidth = 1.0; + if( pixelWidth < u_minLinePixelWidth ) + pixelWidth = u_minLinePixelWidth; + + if( vertexIndex == 1.0 ) + { + v_circleCoords = vec2( -sqrt( 3.0 ), -1.0 ); + delta = vec4( -pixelR * sqrt(3.0), -pixelR, 0, 0 ); + } + else if( vertexIndex == 2.0 ) + { + v_circleCoords = vec2( sqrt( 3.0 ), -1.0 ); + delta = vec4( pixelR * sqrt( 3.0 ), -pixelR, 0, 0 ); + } + else if( vertexIndex == 3.0 ) + { + v_circleCoords = vec2( 0.0, 2.0 ); + delta = vec4( 0, 2 * pixelR, 0, 0 ); + } + else if( vertexIndex == 4.0 ) + { + v_circleCoords = vec2( -sqrt( 3.0 ), 0.0 ); + delta = vec4( 0, 0, 0, 0 ); + } + else if( vertexIndex == 5.0 ) + { + v_circleCoords = vec2( sqrt( 3.0 ), 0.0 ); + delta = vec4( 0, 0, 0, 0 ); + } + else if( vertexIndex == 6.0 ) + { + v_circleCoords = vec2( 0.0, 2.0 ); + delta = vec4( 0, 0, 0, 0 ); + } + + v_shaderParams[2] = pixelR; + v_shaderParams[3] = pixelWidth; + + delta.x *= u_screenPixelSize.x; + delta.y *= u_screenPixelSize.y; + + gl_Position = center + delta + adjust; + gl_FrontColor = gl_Color; +} + +void computeHoleWallCoords( float vertexIndex, float radius, float lineWidth ) +{ + vec4 delta; + vec4 center = roundv( gl_ModelViewProjectionMatrix * gl_Vertex + vec4(1, 1, 0, 0), u_screenPixelSize ); + float pixelWidth = roundr( lineWidth / u_worldPixelSize, 1.0 ); + if( pixelWidth < u_minLinePixelWidth ) + pixelWidth = u_minLinePixelWidth; + float pixelR = roundr( radius / u_worldPixelSize, 1.0 ) + pixelWidth; + vec4 adjust = vec4(-1, -1, 0, 0); if( vertexIndex == 1.0 ) { @@ -195,6 +247,8 @@ void main() computeLineCoords( posture, vs, vp, vec2( -1, 1 ), vec2( -1, 0 ), lineWidth, true ); else if( mode == SHADER_LINE_F ) computeLineCoords( posture, -vs, vp, vec2( 1, 1 ), vec2( -1, 0 ), lineWidth, false ); + else if( mode == SHADER_HOLE_WALL ) + computeHoleWallCoords( v_shaderParams.y, v_shaderParams.z, v_shaderParams.w ); else if( mode == SHADER_FILLED_CIRCLE || mode == SHADER_STROKED_CIRCLE) computeCircleCoords( mode, v_shaderParams.y, v_shaderParams.z, v_shaderParams.w ); else diff --git a/include/gal/cairo/cairo_gal.h b/include/gal/cairo/cairo_gal.h index dbc6837372..8b7d022602 100644 --- a/include/gal/cairo/cairo_gal.h +++ b/include/gal/cairo/cairo_gal.h @@ -78,6 +78,10 @@ public: /// @copydoc GAL::DrawCircle() void DrawCircle( const VECTOR2D& aCenterPoint, double aRadius ) override; + /// @copydoc GAL::DrawHoleWall() + void DrawHoleWall( const VECTOR2D& aCenterPoint, double aHoleRadius, + double aWallWidth ) override; + /// @copydoc GAL::DrawArc() void DrawArc( const VECTOR2D& aCenterPoint, double aRadius, const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aAngle ) override; diff --git a/include/gal/graphics_abstraction_layer.h b/include/gal/graphics_abstraction_layer.h index 92e8ee609b..8aa36d5692 100644 --- a/include/gal/graphics_abstraction_layer.h +++ b/include/gal/graphics_abstraction_layer.h @@ -142,6 +142,16 @@ public: */ virtual void DrawCircle( const VECTOR2D& aCenterPoint, double aRadius ) {}; + /** + * Draw a hole wall ring. + * + * @param aCenterPoint is the center point of the hole. + * @param aHoleRadius is the radius of the hole. + * @param aWallWidth is the wall thickness. + */ + virtual void DrawHoleWall( const VECTOR2D& aCenterPoint, double aHoleRadius, + double aWallWidth ) {}; + /** * Draw an arc. * @@ -369,6 +379,16 @@ public: m_lineWidth = aLineWidth; } + /** + * Set the minimum line width in pixels. + * + * @param aLineWidth is the minimum line width. + */ + virtual void SetMinLineWidth( float aLineWidth ) + { + m_minLineWidth = aLineWidth; + } + /** * Get the line width. * @@ -379,6 +399,16 @@ public: return m_lineWidth; } + /** + * Get the minimum line width in pixels. + * + * @return the minimum line width. + */ + inline float GetMinLineWidth() const + { + return m_minLineWidth; + } + /** * Set the depth of the layer (position on the z-axis) * @@ -1084,6 +1114,7 @@ protected: bool m_globalFlipY; ///< Flag for Y axis flipping float m_lineWidth; ///< The line width + float m_minLineWidth; ///< Minimum line width in pixels bool m_isFillEnabled; ///< Is filling of graphic objects enabled ? bool m_isStrokeEnabled; ///< Are the outlines stroked ? diff --git a/include/gal/opengl/opengl_gal.h b/include/gal/opengl/opengl_gal.h index e252085b12..d9d0a41c87 100644 --- a/include/gal/opengl/opengl_gal.h +++ b/include/gal/opengl/opengl_gal.h @@ -113,6 +113,8 @@ public: return IsShownOnScreen() && !GetClientRect().IsEmpty(); } + void SetMinLineWidth( float aLineWidth ) override; + // --------------- // Drawing methods // --------------- @@ -131,6 +133,10 @@ public: /// @copydoc GAL::DrawCircle() void DrawCircle( const VECTOR2D& aCenterPoint, double aRadius ) override; + /// @copydoc GAL::DrawHoleWall() + void DrawHoleWall( const VECTOR2D& aCenterPoint, double aHoleRadius, + double aWallWidth ) override; + /// @copydoc GAL::DrawArc() void DrawArc( const VECTOR2D& aCenterPoint, double aRadius, const EDA_ANGLE& aStartAngle, const EDA_ANGLE& aAngle ) override; @@ -377,6 +383,9 @@ private: GLint ufm_screenPixelSize; GLint ufm_pixelSizeMultiplier; GLint ufm_antialiasingOffset; + GLint ufm_minLinePixelWidth; + GLint ufm_fontTexture; + GLint ufm_fontTextureWidth; /// wx cursor showing the current native cursor. WX_CURSOR_TYPE m_currentwxCursor; @@ -586,6 +595,13 @@ private: VECTOR2D getScreenPixelSize() const; + /** + * Set up the shader parameters for OpenGL rendering. + * This method initializes all the uniform parameter locations + * after the shader has been linked. + */ + void setupShaderParameters(); + /** * Basic OpenGL initialization and feature checks. * diff --git a/include/gal/opengl/vertex_common.h b/include/gal/opengl/vertex_common.h index c2f02cbd7e..b59550a56f 100644 --- a/include/gal/opengl/vertex_common.h +++ b/include/gal/opengl/vertex_common.h @@ -53,7 +53,8 @@ enum SHADER_MODE SHADER_LINE_C = 7, SHADER_LINE_D = 8, SHADER_LINE_E = 9, - SHADER_LINE_F = 10 + SHADER_LINE_F = 10, + SHADER_HOLE_WALL = 11 }; ///< Data structure for vertices {X,Y,Z,R,G,B,A,shader¶m} diff --git a/pcbnew/pad.cpp b/pcbnew/pad.cpp index 8ff2a0a2e7..aff06b7ea4 100644 --- a/pcbnew/pad.cpp +++ b/pcbnew/pad.cpp @@ -1863,17 +1863,6 @@ double PAD::ViewGetLOD( int aLayer, const KIGFX::VIEW* aView ) const return lodScaleForThreshold( aView, minSize, pcbIUScale.mmToIU( 0.5 ) ); } - // Hole walls always need a repaint when zoom level changes after the last - // LAYER_PAD_HOLEWALLS shape rebuild - if( aLayer == LAYER_PAD_HOLEWALLS ) - { - if( aView->GetGAL()->GetZoomFactor() != m_lastGalZoomLevel ) - { - aView->Update( this, KIGFX::REPAINT ); - m_lastGalZoomLevel = aView->GetGAL()->GetZoomFactor(); - } - } - VECTOR2L padSize = GetBoundingBox().GetSize(); int64_t minSide = std::min( padSize.x, padSize.y ); diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index 7a803e037e..f9565d1f83 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -1448,22 +1448,26 @@ void PCB_PAINTER::draw( const PAD* aPad, int aLayer ) m_gal->SetIsStroke( false ); double widthFactor = ADVANCED_CFG::GetCfg().m_HoleWallPaintingMultiplier; double lineWidth = widthFactor * m_holePlatingThickness; - - // Prevent the hole wall from being drawn too thin (at least two pixels) - // or too thick (cap at the size of the pad ) - lineWidth = std::max( lineWidth, 2.0 / m_gal->GetWorldScale() ); lineWidth = std::min( lineWidth, aPad->GetSizeX() / 2.0 ); lineWidth = std::min( lineWidth, aPad->GetSizeY() / 2.0 ); m_gal->SetFillColor( color ); + m_gal->SetMinLineWidth( lineWidth ); std::shared_ptr slot = aPad->GetEffectiveHoleShape(); - int holeSize = slot->GetWidth() + ( 2 * lineWidth ); if( slot->GetSeg().A == slot->GetSeg().B ) // Circular hole - m_gal->DrawCircle( slot->GetSeg().A, KiROUND( holeSize / 2.0 ) ); + { + double holeRadius = slot->GetWidth() / 2.0; + m_gal->DrawHoleWall( slot->GetSeg().A, holeRadius, lineWidth ); + } else + { + int holeSize = slot->GetWidth() + ( 2 * lineWidth ); m_gal->DrawSegment( slot->GetSeg().A, slot->GetSeg().B, holeSize ); + } + + m_gal->SetMinLineWidth( 1.0 ); return; }