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:
parent
21365fff3e
commit
30987cebfe
|
@ -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 )
|
||||
{
|
||||
wxASSERT_MSG( aBufferHandle <= usedBuffers(), wxT( "Tried to use a not existing buffer" ) );
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
m_cursorPosition = aCursorPosition;
|
||||
|
@ -1222,6 +1248,7 @@ CAIRO_GAL::CAIRO_GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions, wxWindow* aParent,
|
|||
// Initialise compositing state
|
||||
m_mainBuffer = 0;
|
||||
m_overlayBuffer = 0;
|
||||
m_tempBuffer = 0;
|
||||
m_validCompositor = false;
|
||||
SetTarget( TARGET_NONCACHED );
|
||||
|
||||
|
@ -1391,6 +1418,7 @@ void CAIRO_GAL::SetTarget( RENDER_TARGET aTarget )
|
|||
case TARGET_CACHED:
|
||||
case TARGET_NONCACHED: m_compositor->SetBuffer( m_mainBuffer ); break;
|
||||
case TARGET_OVERLAY: m_compositor->SetBuffer( m_overlayBuffer ); break;
|
||||
case TARGET_TEMP: m_compositor->SetBuffer( m_tempBuffer ); break;
|
||||
}
|
||||
|
||||
m_currentTarget = aTarget;
|
||||
|
@ -1415,6 +1443,7 @@ void CAIRO_GAL::ClearTarget( RENDER_TARGET aTarget )
|
|||
case TARGET_CACHED:
|
||||
case TARGET_NONCACHED: m_compositor->SetBuffer( m_mainBuffer ); break;
|
||||
case TARGET_OVERLAY: m_compositor->SetBuffer( m_overlayBuffer ); break;
|
||||
case TARGET_TEMP: m_compositor->SetBuffer( m_tempBuffer ); break;
|
||||
}
|
||||
|
||||
m_compositor->ClearBuffer( COLOR4D::BLACK );
|
||||
|
@ -1498,6 +1527,7 @@ void CAIRO_GAL::setCompositor()
|
|||
// Prepare buffers
|
||||
m_mainBuffer = m_compositor->CreateBuffer();
|
||||
m_overlayBuffer = m_compositor->CreateBuffer();
|
||||
m_tempBuffer = m_compositor->CreateBuffer();
|
||||
|
||||
m_validCompositor = true;
|
||||
}
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
// Store the current cursor type and get the wxCursor for it
|
||||
|
|
|
@ -302,6 +302,8 @@ VIEW::VIEW( bool aIsDynamic ) :
|
|||
m_layers[ii].renderingOrder = ii;
|
||||
m_layers[ii].visible = true;
|
||||
m_layers[ii].displayOnly = false;
|
||||
m_layers[ii].diffLayer = false;
|
||||
m_layers[ii].hasNegatives = false;
|
||||
m_layers[ii].target = TARGET_CACHED;
|
||||
}
|
||||
|
||||
|
@ -995,10 +997,24 @@ void VIEW::redrawRect( const BOX2I& aRect )
|
|||
|
||||
m_gal->SetTarget( l->target );
|
||||
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 );
|
||||
|
||||
if( m_useDrawPriority )
|
||||
drawFunc.deferredDraw();
|
||||
|
||||
if( l->diffLayer )
|
||||
m_gal->EndDiffLayer();
|
||||
else if( l->hasNegatives )
|
||||
m_gal->EndNegativesLayer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1142,10 +1158,9 @@ void VIEW::Redraw()
|
|||
recti.SetMaximum();
|
||||
|
||||
redrawRect( recti );
|
||||
|
||||
// All targets were redrawn, so nothing is dirty
|
||||
markTargetClean( TARGET_CACHED );
|
||||
markTargetClean( TARGET_NONCACHED );
|
||||
markTargetClean( TARGET_OVERLAY );
|
||||
MarkClean();
|
||||
|
||||
#ifdef KICAD_GAL_PROFILE
|
||||
totalRealTime.Stop();
|
||||
|
|
|
@ -86,6 +86,9 @@ bool PANEL_GERBVIEW_DISPLAY_OPTIONS::TransferDataFromWindow()
|
|||
|
||||
m_galOptsPanel->TransferDataFromWindow();
|
||||
|
||||
if( displayOptions.m_DiffMode )
|
||||
m_Parent->UpdateDiffLayers();
|
||||
|
||||
// Apply changes to the GAL
|
||||
auto view = m_Parent->GetCanvas()->GetView();
|
||||
auto painter = static_cast<KIGFX::GERBVIEW_PAINTER*>( view->GetPainter() );
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <gerber_file_image_list.h>
|
||||
#include <excellon_image.h>
|
||||
#include <wildcards_and_files_ext.h>
|
||||
#include <view/view.h>
|
||||
#include <widgets/wx_progress_reporters.h>
|
||||
#include "widgets/gerbview_layer_widget.h"
|
||||
|
||||
|
@ -293,6 +294,9 @@ bool GERBVIEW_FRAME::LoadListOfGerberAndDrillFiles( const wxString& aPath,
|
|||
{
|
||||
UpdateFileHistory( m_lastFileName );
|
||||
|
||||
GetCanvas()->GetView()->SetLayerHasNegatives(
|
||||
GERBER_DRAW_LAYER( layer ), GetGbrImage( layer )->HasNegativeItems() );
|
||||
|
||||
layer = getNextAvailableLayer( layer );
|
||||
|
||||
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_ok = Read_GERBER_File( unzipped_tempfile );
|
||||
|
||||
if( read_ok )
|
||||
GetCanvas()->GetView()->SetLayerHasNegatives(
|
||||
GERBER_DRAW_LAYER( layer ), GetGbrImage( layer )->HasNegativeItems() );
|
||||
}
|
||||
else // if( curr_ext == "drl" )
|
||||
{
|
||||
|
|
|
@ -534,6 +534,38 @@ void GERBVIEW_FRAME::SortLayersByX2Attributes()
|
|||
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 )
|
||||
{
|
||||
|
@ -543,12 +575,16 @@ void GERBVIEW_FRAME::UpdateDisplayOptions( const GBR_DISPLAY_OPTIONS& aOptions )
|
|||
aOptions.m_DisplayLinesFill );
|
||||
bool update_polygons = ( m_DisplayOptions.m_DisplayPolygonsFill !=
|
||||
aOptions.m_DisplayPolygonsFill );
|
||||
bool update_diff_mode = ( m_DisplayOptions.m_DiffMode != aOptions.m_DiffMode );
|
||||
|
||||
auto view = GetCanvas()->GetView();
|
||||
|
||||
m_DisplayOptions = aOptions;
|
||||
|
||||
applyDisplaySettingsToGAL();
|
||||
|
||||
auto view = GetCanvas()->GetView();
|
||||
if( update_diff_mode )
|
||||
UpdateDiffLayers();
|
||||
|
||||
if( update_flashed )
|
||||
{
|
||||
|
@ -819,6 +855,9 @@ void GERBVIEW_FRAME::SetActiveLayer( int aLayer, bool doLayerWidgetUpdate )
|
|||
{
|
||||
m_activeLayer = aLayer;
|
||||
|
||||
if( m_DisplayOptions.m_DiffMode )
|
||||
UpdateDiffLayers();
|
||||
|
||||
if( doLayerWidgetUpdate )
|
||||
m_LayersManager->SelectLayer( aLayer );
|
||||
|
||||
|
|
|
@ -345,6 +345,12 @@ public:
|
|||
|
||||
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.
|
||||
*
|
||||
|
|
|
@ -66,9 +66,6 @@ void GERBVIEW_RENDER_SETTINGS::LoadColors( const COLOR_SETTINGS* aSettings )
|
|||
if( baseColor == COLOR4D::UNSPECIFIED )
|
||||
baseColor = aSettings->m_Palette[ ( palette_idx++ ) % palette_size ];
|
||||
|
||||
if( m_diffMode )
|
||||
baseColor.a = 0.75;
|
||||
|
||||
m_layerColors[i] = baseColor;
|
||||
m_layerColorsHi[i] = baseColor.Brightened( 0.5 );
|
||||
m_layerColorsSel[i] = baseColor.Brightened( 0.8 );
|
||||
|
|
|
@ -71,6 +71,15 @@ public:
|
|||
/// @copydoc COMPOSITOR::ClearBuffer()
|
||||
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()
|
||||
virtual void DrawBuffer( unsigned int aBufferHandle ) override;
|
||||
|
||||
|
|
|
@ -389,6 +389,18 @@ public:
|
|||
|
||||
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.
|
||||
*
|
||||
|
@ -461,6 +473,8 @@ protected:
|
|||
std::shared_ptr<CAIRO_COMPOSITOR> m_compositor; ///< Object for layers compositing
|
||||
unsigned int m_mainBuffer; ///< Handle to the main 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
|
||||
bool m_validCompositor; ///< Compositor initialization flag
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ enum RENDER_TARGET
|
|||
TARGET_CACHED = 0, ///< Main rendering target (cached)
|
||||
TARGET_NONCACHED, ///< Auxiliary rendering target (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
|
||||
};
|
||||
} // namespace KIGFX
|
||||
|
|
|
@ -818,6 +818,38 @@ public:
|
|||
*/
|
||||
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
|
||||
// -------------
|
||||
|
|
|
@ -241,6 +241,12 @@ public:
|
|||
/// @copydoc GAL::SetNegativeDrawMode()
|
||||
void SetNegativeDrawMode( bool aSetting ) override {}
|
||||
|
||||
/// @copydoc GAL::StartDiffLayer()
|
||||
void StartDiffLayer() override;
|
||||
//
|
||||
/// @copydoc GAL::EndDiffLayer()
|
||||
void EndDiffLayer() override;
|
||||
|
||||
void ComputeWorldScreenMatrix() override;
|
||||
|
||||
// -------
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <gal/definitions.h>
|
||||
|
||||
#include <view/view_overlay.h>
|
||||
#include <view/view.h>
|
||||
|
||||
class EDA_ITEM;
|
||||
|
||||
|
@ -409,6 +410,42 @@ public:
|
|||
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 )
|
||||
{
|
||||
wxCHECK( aLayer < (int) m_layers.size(), /*void*/ );
|
||||
|
@ -599,6 +636,15 @@ public:
|
|||
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.
|
||||
*
|
||||
|
@ -684,6 +730,8 @@ protected:
|
|||
{
|
||||
bool visible; ///< Is the layer to be rendered?
|
||||
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.
|
||||
int renderingOrder; ///< Rendering order of this layer.
|
||||
int id; ///< Layer ID.
|
||||
|
|
Loading…
Reference in New Issue