Gerbview: fix cairo negative items and implement real differential mode

Layers with negative objects need to be drawn in a subsurface before
copying so they don't _CLEAR the draw items below them when a negative
object is drawn.

Differential layers are basically the same thing only they use a
different copying operation onto the layers below.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/1863
Fixes https://gitlab.com/kicad/code/kicad/-/issues/4495
This commit is contained in:
Mike Williams 2021-08-24 12:00:46 -04:00 committed by Wayne Stambaugh
parent 21365fff3e
commit 30987cebfe
15 changed files with 252 additions and 7 deletions

View File

@ -142,6 +142,29 @@ void CAIRO_COMPOSITOR::ClearBuffer( const COLOR4D& aColor )
} }
void CAIRO_COMPOSITOR::DrawBuffer( unsigned int aSourceHandle, unsigned int aDestHandle,
cairo_operator_t op )
{
wxASSERT_MSG( aSourceHandle <= usedBuffers() && aDestHandle <= usedBuffers(),
wxT( "Tried to use a not existing buffer" ) );
// Reset the transformation matrix, so it is possible to composite images using
// screen coordinates instead of world coordinates
cairo_get_matrix( m_mainContext, &m_matrix );
cairo_identity_matrix( m_mainContext );
// Draw the selected buffer contents
cairo_t* ct = cairo_create( m_buffers[aDestHandle - 1].surface );
cairo_set_operator( ct, op );
cairo_set_source_surface( ct, m_buffers[aSourceHandle - 1].surface, 0.0, 0.0 );
cairo_paint( ct );
cairo_destroy( ct );
// Restore the transformation matrix
cairo_set_matrix( m_mainContext, &m_matrix );
}
void CAIRO_COMPOSITOR::DrawBuffer( unsigned int aBufferHandle ) void CAIRO_COMPOSITOR::DrawBuffer( unsigned int aBufferHandle )
{ {
wxASSERT_MSG( aBufferHandle <= usedBuffers(), wxT( "Tried to use a not existing buffer" ) ); wxASSERT_MSG( aBufferHandle <= usedBuffers(), wxT( "Tried to use a not existing buffer" ) );

View File

@ -925,6 +925,32 @@ void CAIRO_GAL_BASE::SetNegativeDrawMode( bool aSetting )
} }
void CAIRO_GAL::StartDiffLayer()
{
SetTarget( TARGET_TEMP );
ClearTarget( TARGET_TEMP );
}
void CAIRO_GAL::EndDiffLayer()
{
m_compositor->DrawBuffer( m_tempBuffer, m_mainBuffer, CAIRO_OPERATOR_ADD );
}
void CAIRO_GAL::StartNegativesLayer()
{
SetTarget( TARGET_TEMP );
ClearTarget( TARGET_TEMP );
}
void CAIRO_GAL::EndNegativesLayer()
{
m_compositor->DrawBuffer( m_tempBuffer, m_mainBuffer, CAIRO_OPERATOR_OVER );
}
void CAIRO_GAL_BASE::DrawCursor( const VECTOR2D& aCursorPosition ) void CAIRO_GAL_BASE::DrawCursor( const VECTOR2D& aCursorPosition )
{ {
m_cursorPosition = aCursorPosition; m_cursorPosition = aCursorPosition;
@ -1222,6 +1248,7 @@ CAIRO_GAL::CAIRO_GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions, wxWindow* aParent,
// Initialise compositing state // Initialise compositing state
m_mainBuffer = 0; m_mainBuffer = 0;
m_overlayBuffer = 0; m_overlayBuffer = 0;
m_tempBuffer = 0;
m_validCompositor = false; m_validCompositor = false;
SetTarget( TARGET_NONCACHED ); SetTarget( TARGET_NONCACHED );
@ -1391,6 +1418,7 @@ void CAIRO_GAL::SetTarget( RENDER_TARGET aTarget )
case TARGET_CACHED: case TARGET_CACHED:
case TARGET_NONCACHED: m_compositor->SetBuffer( m_mainBuffer ); break; case TARGET_NONCACHED: m_compositor->SetBuffer( m_mainBuffer ); break;
case TARGET_OVERLAY: m_compositor->SetBuffer( m_overlayBuffer ); break; case TARGET_OVERLAY: m_compositor->SetBuffer( m_overlayBuffer ); break;
case TARGET_TEMP: m_compositor->SetBuffer( m_tempBuffer ); break;
} }
m_currentTarget = aTarget; m_currentTarget = aTarget;
@ -1415,6 +1443,7 @@ void CAIRO_GAL::ClearTarget( RENDER_TARGET aTarget )
case TARGET_CACHED: case TARGET_CACHED:
case TARGET_NONCACHED: m_compositor->SetBuffer( m_mainBuffer ); break; case TARGET_NONCACHED: m_compositor->SetBuffer( m_mainBuffer ); break;
case TARGET_OVERLAY: m_compositor->SetBuffer( m_overlayBuffer ); break; case TARGET_OVERLAY: m_compositor->SetBuffer( m_overlayBuffer ); break;
case TARGET_TEMP: m_compositor->SetBuffer( m_tempBuffer ); break;
} }
m_compositor->ClearBuffer( COLOR4D::BLACK ); m_compositor->ClearBuffer( COLOR4D::BLACK );
@ -1498,6 +1527,7 @@ void CAIRO_GAL::setCompositor()
// Prepare buffers // Prepare buffers
m_mainBuffer = m_compositor->CreateBuffer(); m_mainBuffer = m_compositor->CreateBuffer();
m_overlayBuffer = m_compositor->CreateBuffer(); m_overlayBuffer = m_compositor->CreateBuffer();
m_tempBuffer = m_compositor->CreateBuffer();
m_validCompositor = true; m_validCompositor = true;
} }

View File

@ -1739,6 +1739,20 @@ bool OPENGL_GAL::HasTarget( RENDER_TARGET aTarget )
} }
void OPENGL_GAL::StartDiffLayer()
{
m_currentManager->EndDrawing();
}
void OPENGL_GAL::EndDiffLayer()
{
glBlendFunc( GL_SRC_ALPHA, GL_ONE );
m_currentManager->EndDrawing();
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
}
bool OPENGL_GAL::SetNativeCursorStyle( KICURSOR aCursor ) bool OPENGL_GAL::SetNativeCursorStyle( KICURSOR aCursor )
{ {
// Store the current cursor type and get the wxCursor for it // Store the current cursor type and get the wxCursor for it

View File

@ -302,6 +302,8 @@ VIEW::VIEW( bool aIsDynamic ) :
m_layers[ii].renderingOrder = ii; m_layers[ii].renderingOrder = ii;
m_layers[ii].visible = true; m_layers[ii].visible = true;
m_layers[ii].displayOnly = false; m_layers[ii].displayOnly = false;
m_layers[ii].diffLayer = false;
m_layers[ii].hasNegatives = false;
m_layers[ii].target = TARGET_CACHED; m_layers[ii].target = TARGET_CACHED;
} }
@ -995,10 +997,24 @@ void VIEW::redrawRect( const BOX2I& aRect )
m_gal->SetTarget( l->target ); m_gal->SetTarget( l->target );
m_gal->SetLayerDepth( l->renderingOrder ); m_gal->SetLayerDepth( l->renderingOrder );
// Differential layer also work for the negatives, since both special layer types
// will composite on separate layers (at least in Cairo)
if( l->diffLayer )
m_gal->StartDiffLayer();
else if( l->hasNegatives )
m_gal->StartNegativesLayer();
l->items->Query( aRect, drawFunc ); l->items->Query( aRect, drawFunc );
if( m_useDrawPriority ) if( m_useDrawPriority )
drawFunc.deferredDraw(); drawFunc.deferredDraw();
if( l->diffLayer )
m_gal->EndDiffLayer();
else if( l->hasNegatives )
m_gal->EndNegativesLayer();
} }
} }
} }
@ -1142,10 +1158,9 @@ void VIEW::Redraw()
recti.SetMaximum(); recti.SetMaximum();
redrawRect( recti ); redrawRect( recti );
// All targets were redrawn, so nothing is dirty // All targets were redrawn, so nothing is dirty
markTargetClean( TARGET_CACHED ); MarkClean();
markTargetClean( TARGET_NONCACHED );
markTargetClean( TARGET_OVERLAY );
#ifdef KICAD_GAL_PROFILE #ifdef KICAD_GAL_PROFILE
totalRealTime.Stop(); totalRealTime.Stop();

View File

@ -86,6 +86,9 @@ bool PANEL_GERBVIEW_DISPLAY_OPTIONS::TransferDataFromWindow()
m_galOptsPanel->TransferDataFromWindow(); m_galOptsPanel->TransferDataFromWindow();
if( displayOptions.m_DiffMode )
m_Parent->UpdateDiffLayers();
// Apply changes to the GAL // Apply changes to the GAL
auto view = m_Parent->GetCanvas()->GetView(); auto view = m_Parent->GetCanvas()->GetView();
auto painter = static_cast<KIGFX::GERBVIEW_PAINTER*>( view->GetPainter() ); auto painter = static_cast<KIGFX::GERBVIEW_PAINTER*>( view->GetPainter() );

View File

@ -33,6 +33,7 @@
#include <gerber_file_image_list.h> #include <gerber_file_image_list.h>
#include <excellon_image.h> #include <excellon_image.h>
#include <wildcards_and_files_ext.h> #include <wildcards_and_files_ext.h>
#include <view/view.h>
#include <widgets/wx_progress_reporters.h> #include <widgets/wx_progress_reporters.h>
#include "widgets/gerbview_layer_widget.h" #include "widgets/gerbview_layer_widget.h"
@ -293,6 +294,9 @@ bool GERBVIEW_FRAME::LoadListOfGerberAndDrillFiles( const wxString& aPath,
{ {
UpdateFileHistory( m_lastFileName ); UpdateFileHistory( m_lastFileName );
GetCanvas()->GetView()->SetLayerHasNegatives(
GERBER_DRAW_LAYER( layer ), GetGbrImage( layer )->HasNegativeItems() );
layer = getNextAvailableLayer( layer ); layer = getNextAvailableLayer( layer );
if( layer == NO_AVAILABLE_LAYERS && ii < aFilenameList.GetCount() - 1 ) if( layer == NO_AVAILABLE_LAYERS && ii < aFilenameList.GetCount() - 1 )
@ -584,6 +588,10 @@ bool GERBVIEW_FRAME::unarchiveFiles( const wxString& aFullFileName, REPORTER* aR
{ {
// Read gerber files: each file is loaded on a new GerbView layer // Read gerber files: each file is loaded on a new GerbView layer
read_ok = Read_GERBER_File( unzipped_tempfile ); read_ok = Read_GERBER_File( unzipped_tempfile );
if( read_ok )
GetCanvas()->GetView()->SetLayerHasNegatives(
GERBER_DRAW_LAYER( layer ), GetGbrImage( layer )->HasNegativeItems() );
} }
else // if( curr_ext == "drl" ) else // if( curr_ext == "drl" )
{ {

View File

@ -534,6 +534,38 @@ void GERBVIEW_FRAME::SortLayersByX2Attributes()
GetCanvas()->Refresh(); GetCanvas()->Refresh();
} }
void GERBVIEW_FRAME::UpdateDiffLayers()
{
auto target = GetCanvas()->GetBackend() == GERBVIEW_DRAW_PANEL_GAL::GAL_TYPE::GAL_TYPE_OPENGL
? KIGFX::TARGET_CACHED
: KIGFX::TARGET_NONCACHED;
auto view = GetCanvas()->GetView();
int lastVisibleLayer = -1;
for( int i = 0; i < GERBER_DRAWLAYERS_COUNT; i++ )
{
view->SetLayerDiff( GERBER_DRAW_LAYER( i ), m_DisplayOptions.m_DiffMode );
// Caching doesn't work with layered rendering of diff'd layers
view->SetLayerTarget( GERBER_DRAW_LAYER( i ),
m_DisplayOptions.m_DiffMode ? KIGFX::TARGET_NONCACHED : target );
//We want the last visible layer, but deprioritize the active layer unless it's the only layer
if( ( lastVisibleLayer == -1 )
|| ( view->IsLayerVisible( GERBER_DRAW_LAYER( i ) ) && i != GetActiveLayer() ) )
lastVisibleLayer = i;
}
//We don't want to diff the last visible layer onto the background, etc.
if( lastVisibleLayer != -1 )
{
view->SetLayerTarget( GERBER_DRAW_LAYER( lastVisibleLayer ), target );
view->SetLayerDiff( GERBER_DRAW_LAYER( lastVisibleLayer ), false );
}
view->RecacheAllItems();
view->MarkDirty();
view->UpdateAllItems( KIGFX::ALL );
}
void GERBVIEW_FRAME::UpdateDisplayOptions( const GBR_DISPLAY_OPTIONS& aOptions ) void GERBVIEW_FRAME::UpdateDisplayOptions( const GBR_DISPLAY_OPTIONS& aOptions )
{ {
@ -543,12 +575,16 @@ void GERBVIEW_FRAME::UpdateDisplayOptions( const GBR_DISPLAY_OPTIONS& aOptions )
aOptions.m_DisplayLinesFill ); aOptions.m_DisplayLinesFill );
bool update_polygons = ( m_DisplayOptions.m_DisplayPolygonsFill != bool update_polygons = ( m_DisplayOptions.m_DisplayPolygonsFill !=
aOptions.m_DisplayPolygonsFill ); aOptions.m_DisplayPolygonsFill );
bool update_diff_mode = ( m_DisplayOptions.m_DiffMode != aOptions.m_DiffMode );
auto view = GetCanvas()->GetView();
m_DisplayOptions = aOptions; m_DisplayOptions = aOptions;
applyDisplaySettingsToGAL(); applyDisplaySettingsToGAL();
auto view = GetCanvas()->GetView(); if( update_diff_mode )
UpdateDiffLayers();
if( update_flashed ) if( update_flashed )
{ {
@ -819,6 +855,9 @@ void GERBVIEW_FRAME::SetActiveLayer( int aLayer, bool doLayerWidgetUpdate )
{ {
m_activeLayer = aLayer; m_activeLayer = aLayer;
if( m_DisplayOptions.m_DiffMode )
UpdateDiffLayers();
if( doLayerWidgetUpdate ) if( doLayerWidgetUpdate )
m_LayersManager->SelectLayer( aLayer ); m_LayersManager->SelectLayer( aLayer );

View File

@ -345,6 +345,12 @@ public:
void SortLayersByX2Attributes(); void SortLayersByX2Attributes();
/**
* Update each layers' differential option. Needed when diff mode changes or the active layer
* changes (due to changing rendering order) which matters for diff mode but not otherwise.
*/
void UpdateDiffLayers();
/** /**
* Update the display options and refreshes the view as needed. * Update the display options and refreshes the view as needed.
* *

View File

@ -66,9 +66,6 @@ void GERBVIEW_RENDER_SETTINGS::LoadColors( const COLOR_SETTINGS* aSettings )
if( baseColor == COLOR4D::UNSPECIFIED ) if( baseColor == COLOR4D::UNSPECIFIED )
baseColor = aSettings->m_Palette[ ( palette_idx++ ) % palette_size ]; baseColor = aSettings->m_Palette[ ( palette_idx++ ) % palette_size ];
if( m_diffMode )
baseColor.a = 0.75;
m_layerColors[i] = baseColor; m_layerColors[i] = baseColor;
m_layerColorsHi[i] = baseColor.Brightened( 0.5 ); m_layerColorsHi[i] = baseColor.Brightened( 0.5 );
m_layerColorsSel[i] = baseColor.Brightened( 0.8 ); m_layerColorsSel[i] = baseColor.Brightened( 0.8 );

View File

@ -71,6 +71,15 @@ public:
/// @copydoc COMPOSITOR::ClearBuffer() /// @copydoc COMPOSITOR::ClearBuffer()
virtual void ClearBuffer( const COLOR4D& aColor ) override; virtual void ClearBuffer( const COLOR4D& aColor ) override;
/**
* Paints source to destination using the cairo operator. Useful for differential mode.
*
* @param aSourceHandle Source buffer to paint
* @param aDestHandle Destination buffer to paint on to
* @param op Painting operation
*/
void DrawBuffer( unsigned int aSourceHandle, unsigned int aDestHandle, cairo_operator_t op );
/// @copydoc COMPOSITOR::DrawBuffer() /// @copydoc COMPOSITOR::DrawBuffer()
virtual void DrawBuffer( unsigned int aBufferHandle ) override; virtual void DrawBuffer( unsigned int aBufferHandle ) override;

View File

@ -389,6 +389,18 @@ public:
void ClearTarget( RENDER_TARGET aTarget ) override; void ClearTarget( RENDER_TARGET aTarget ) override;
/// @copydoc GAL::StartDiffLayer()
void StartDiffLayer() override;
/// @copydoc GAL::EndDiffLayer()
void EndDiffLayer() override;
/// @copydoc GAL::StartNegativesLayer()
void StartNegativesLayer() override;
/// @copydoc GAL::EndNegativesLayer()
void EndNegativesLayer() override;
/** /**
* Post an event to m_paint_listener. * Post an event to m_paint_listener.
* *
@ -461,6 +473,8 @@ protected:
std::shared_ptr<CAIRO_COMPOSITOR> m_compositor; ///< Object for layers compositing std::shared_ptr<CAIRO_COMPOSITOR> m_compositor; ///< Object for layers compositing
unsigned int m_mainBuffer; ///< Handle to the main buffer unsigned int m_mainBuffer; ///< Handle to the main buffer
unsigned int m_overlayBuffer; ///< Handle to the overlay buffer unsigned int m_overlayBuffer; ///< Handle to the overlay buffer
unsigned int m_tempBuffer; ///< Handle to the temp buffer
unsigned int m_savedBuffer; ///< Handle to buffer to restore after rendering to temp buffer
RENDER_TARGET m_currentTarget; ///< Current rendering target RENDER_TARGET m_currentTarget; ///< Current rendering target
bool m_validCompositor; ///< Compositor initialization flag bool m_validCompositor; ///< Compositor initialization flag

View File

@ -48,6 +48,7 @@ enum RENDER_TARGET
TARGET_CACHED = 0, ///< Main rendering target (cached) TARGET_CACHED = 0, ///< Main rendering target (cached)
TARGET_NONCACHED, ///< Auxiliary rendering target (noncached) TARGET_NONCACHED, ///< Auxiliary rendering target (noncached)
TARGET_OVERLAY, ///< Items that may change while the view stays the same (noncached) TARGET_OVERLAY, ///< Items that may change while the view stays the same (noncached)
TARGET_TEMP, ///< Temporary target for drawing in separate layer
TARGETS_NUMBER ///< Number of available rendering targets TARGETS_NUMBER ///< Number of available rendering targets
}; };
} // namespace KIGFX } // namespace KIGFX

View File

@ -818,6 +818,38 @@ public:
*/ */
virtual void SetNegativeDrawMode( bool aSetting ) {}; virtual void SetNegativeDrawMode( bool aSetting ) {};
/**
* Begins rendering of a differential layer. Used by gerbview's differential mode.
*
* Differential layers have their drawn objects blended onto the lower layers
* differently so we need to end drawing of current objects and start a new
* set to be completed with a different blend mode.
*/
virtual void StartDiffLayer() {};
/**
* Ends rendering of a differential layer. Objects drawn after the StartDiffLayer()
* will be drawn and composited with a differential blend mode, then drawing is
* returned to normal.
*/
virtual void EndDiffLayer() {};
/**
* Begins rendering in a new layer that will be copied to the main
* layer in EndNegativesLayer().
*
* For Cairo, layers with negative items need a new layer so when
* negative layers _CLEAR sections it doesn't delete drawings on layers
* below them. No-op in OpenGL
*/
virtual void StartNegativesLayer(){};
/**
* Ends rendering of a negatives layer and draws it to the main layer.
* No-op in OpenGL.
*/
virtual void EndNegativesLayer(){};
// ------------- // -------------
// Grid methods // Grid methods
// ------------- // -------------

View File

@ -241,6 +241,12 @@ public:
/// @copydoc GAL::SetNegativeDrawMode() /// @copydoc GAL::SetNegativeDrawMode()
void SetNegativeDrawMode( bool aSetting ) override {} void SetNegativeDrawMode( bool aSetting ) override {}
/// @copydoc GAL::StartDiffLayer()
void StartDiffLayer() override;
//
/// @copydoc GAL::EndDiffLayer()
void EndDiffLayer() override;
void ComputeWorldScreenMatrix() override; void ComputeWorldScreenMatrix() override;
// ------- // -------

View File

@ -36,6 +36,7 @@
#include <gal/definitions.h> #include <gal/definitions.h>
#include <view/view_overlay.h> #include <view/view_overlay.h>
#include <view/view.h>
class EDA_ITEM; class EDA_ITEM;
@ -409,6 +410,42 @@ public:
return m_layers.at( aLayer ).visible; return m_layers.at( aLayer ).visible;
} }
/**
* Set the whether the layer should drawn differentially.
*
* @param aLayer is the layer to set to be draw differentially
* @param aDiff is the layer diff'ing state.
*/
inline void SetLayerDiff( int aLayer, bool aDiff = true )
{
wxCHECK( aLayer < (int) m_layers.size(), /*void*/ );
if( m_layers[aLayer].diffLayer != aDiff )
{
// Target has to be redrawn after changing its layers' diff status
MarkTargetDirty( m_layers[aLayer].target );
m_layers[aLayer].diffLayer = aDiff;
}
}
/**
* Set the status of negatives presense in a particular layer.
*
* @param aLayer is the layer to set as containing negatives (or not).
* @param aNegatives is the layer negatives state.
*/
inline void SetLayerHasNegatives( int aLayer, bool aNegatives = true )
{
wxCHECK( aLayer < (int) m_layers.size(), /*void*/ );
if( m_layers[aLayer].hasNegatives != aNegatives )
{
// Target has to be redrawn after changing a layers' negatives
MarkTargetDirty( m_layers[aLayer].target );
m_layers[aLayer].hasNegatives = aNegatives;
}
}
inline void SetLayerDisplayOnly( int aLayer, bool aDisplayOnly = true ) inline void SetLayerDisplayOnly( int aLayer, bool aDisplayOnly = true )
{ {
wxCHECK( aLayer < (int) m_layers.size(), /*void*/ ); wxCHECK( aLayer < (int) m_layers.size(), /*void*/ );
@ -599,6 +636,15 @@ public:
m_dirtyTargets[i] = true; m_dirtyTargets[i] = true;
} }
/**
* Force redraw of view on the next rendering.
*/
void MarkClean()
{
for( int i = 0; i < TARGETS_NUMBER; ++i )
m_dirtyTargets[i] = false;
}
/** /**
* Add an item to a list of items that are going to be refreshed upon the next frame rendering. * Add an item to a list of items that are going to be refreshed upon the next frame rendering.
* *
@ -684,6 +730,8 @@ protected:
{ {
bool visible; ///< Is the layer to be rendered? bool visible; ///< Is the layer to be rendered?
bool displayOnly; ///< Is the layer display only? bool displayOnly; ///< Is the layer display only?
bool diffLayer; ///< Layer should be drawn differentially over lower layers
bool hasNegatives; ///< Layer should be drawn separately to not delete lower layers
std::shared_ptr<VIEW_RTREE> items; ///< R-tree indexing all items on this layer. std::shared_ptr<VIEW_RTREE> items; ///< R-tree indexing all items on this layer.
int renderingOrder; ///< Rendering order of this layer. int renderingOrder; ///< Rendering order of this layer.
int id; ///< Layer ID. int id; ///< Layer ID.