Support hidpi in Cairo GAL canvas; performance improvements.

We now draw onto wxBitmap directly, reducing the amount of copying.
Also moves the bitmap blit into paint event handler.
Modifies ClearScreen to work universally between Cairo/OpenGL backends.
This commit is contained in:
Alex Shvartzkop 2024-06-08 12:45:46 +03:00
parent 31a0652c58
commit 8e90063258
4 changed files with 82 additions and 60 deletions

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2017 CERN
* Copyright (C) 2013-2023, 2024 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2013-2024, 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* @author Maciej Suminski <maciej.suminski@cern.ch>
@ -281,20 +281,10 @@ bool EDA_DRAW_PANEL_GAL::DoRePaint()
m_gal->SetGridColor( settings->GetGridColor() );
m_gal->SetCursorColor( settings->GetCursorColor() );
// TODO: find why ClearScreen() must be called here in opengl mode
// and only if m_view->IsDirty() in Cairo mode to avoid display artifacts
// when moving the mouse cursor
if( m_backend == GAL_TYPE_OPENGL )
m_gal->ClearScreen();
m_gal->ClearScreen();
if( m_view->IsDirty() )
{
if( m_backend != GAL_TYPE_OPENGL // Already called in opengl
&& m_view->IsTargetDirty( KIGFX::TARGET_NONCACHED ) )
{
m_gal->ClearScreen();
}
m_view->ClearTargets();
// Grid has to be redrawn only when the NONCACHED target is redrawn

View File

@ -2,7 +2,7 @@
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
* Copyright (C) 2012-2023 Kicad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2012-2024 Kicad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2017-2018 CERN
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
@ -29,6 +29,7 @@
#include <wx/image.h>
#include <wx/log.h>
#include <wx/rawbmp.h>
#include <gal/cairo/cairo_gal.h>
#include <gal/cairo/cairo_compositor.h>
@ -583,6 +584,9 @@ void CAIRO_GAL_BASE::DrawBitmap( const BITMAP_BASE& aBitmap, double alphaBlend )
void CAIRO_GAL_BASE::ResizeScreen( int aWidth, int aHeight )
{
m_screenSize = VECTOR2I( aWidth, aHeight );
m_bitmapSize = VECTOR2I( std::ceil( m_screenSize.x * getScalingFactor() ),
std::ceil( m_screenSize.y * getScalingFactor() ) );
}
@ -594,9 +598,9 @@ void CAIRO_GAL_BASE::Flush()
void CAIRO_GAL_BASE::ClearScreen()
{
cairo_set_source_rgb( m_currentContext, m_clearColor.r, m_clearColor.g, m_clearColor.b );
cairo_rectangle( m_currentContext, 0.0, 0.0, m_screenSize.x, m_screenSize.y );
cairo_fill( m_currentContext );
cairo_set_source_rgb( m_context, m_clearColor.r, m_clearColor.g, m_clearColor.b );
cairo_rectangle( m_context, 0.0, 0.0, m_bitmapSize.x, m_bitmapSize.y );
cairo_fill( m_context );
}
@ -1021,15 +1025,15 @@ void CAIRO_GAL_BASE::resetContext()
m_imageSurfaces.clear();
ClearScreen();
// Compute the world <-> screen transformations
ComputeWorldScreenMatrix();
cairo_matrix_init( &m_cairoWorldScreenMatrix, m_worldScreenMatrix.m_data[0][0],
m_worldScreenMatrix.m_data[1][0], m_worldScreenMatrix.m_data[0][1],
m_worldScreenMatrix.m_data[1][1], m_worldScreenMatrix.m_data[0][2],
m_worldScreenMatrix.m_data[1][2] );
double sf = getScalingFactor();
cairo_matrix_init( &m_cairoWorldScreenMatrix,
m_worldScreenMatrix.m_data[0][0] * sf, m_worldScreenMatrix.m_data[1][0] * sf,
m_worldScreenMatrix.m_data[0][1] * sf, m_worldScreenMatrix.m_data[1][1] * sf,
m_worldScreenMatrix.m_data[0][2] * sf, m_worldScreenMatrix.m_data[1][2] * sf );
// we work in screen-space coordinates and do the transforms outside.
cairo_identity_matrix( m_context );
@ -1337,8 +1341,10 @@ CAIRO_GAL::CAIRO_GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions, wxWindow* aParent,
m_currentTarget = TARGET_NONCACHED;
SetTarget( TARGET_NONCACHED );
SetBackgroundStyle( wxBG_STYLE_PAINT );
m_bitmapBuffer = nullptr;
m_wxOutput = nullptr;
m_wxBitmap = nullptr;
m_parentWindow = aParent;
m_mouseListener = aMouseListener;
@ -1374,10 +1380,6 @@ CAIRO_GAL::CAIRO_GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions, wxWindow* aParent,
#endif
SetSize( aParent->GetClientSize() );
m_screenSize = ToVECTOR2I( aParent->GetClientSize() );
// Allocate memory for pixel storage
allocateBitmaps();
m_isInitialized = false;
}
@ -1413,45 +1415,44 @@ void CAIRO_GAL::EndDrawing()
// Now translate the raw context data from the format stored
// by cairo into a format understood by wxImage.
int height = m_screenSize.y;
int height = m_bitmapSize.y;
int stride = m_stride;
unsigned char* srcRow = m_bitmapBuffer;
unsigned char* dst = m_wxOutput;
wxNativePixelData dstData( *m_wxBitmap );
wxNativePixelData::Iterator di( dstData );
for( int y = 0; y < height; y++ )
{
for( int x = 0; x < stride; x += 4 )
wxNativePixelData::Iterator rowStart = di;
for( int x = 0; x < stride; x += 4, ++di )
{
const unsigned char* src = srcRow + x;
#if defined( __BYTE_ORDER__ ) && ( __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ )
// XRGB
dst[0] = src[1];
dst[1] = src[2];
dst[2] = src[3];
di.Red() = src[1];
di.Green() = src[2];
di.Blue() = src[3];
#else
// BGRX
dst[0] = src[2];
dst[1] = src[1];
dst[2] = src[0];
di.Red() = src[2];
di.Green() = src[1];
di.Blue() = src[0];
#endif
dst += 3;
}
srcRow += stride;
di = rowStart;
di.OffsetY( dstData, 1 );
}
wxImage img( m_wxBufferWidth, m_screenSize.y, m_wxOutput, true );
wxBitmap bmp( img );
wxMemoryDC mdc( bmp );
wxClientDC clientDC( this );
// Now it is the time to blit the mouse cursor
blitCursor( mdc );
clientDC.Blit( 0, 0, m_screenSize.x, m_screenSize.y, &mdc, 0, 0, wxCOPY );
deinitSurface();
Refresh(); // Trigger repaint
}
@ -1472,7 +1473,7 @@ void CAIRO_GAL::ResizeScreen( int aWidth, int aHeight )
allocateBitmaps();
if( m_validCompositor )
m_compositor->Resize( aWidth, aHeight );
m_compositor->Resize( m_bitmapSize.x, m_bitmapSize.y );
m_validCompositor = false;
@ -1563,7 +1564,7 @@ void CAIRO_GAL::initSurface()
return;
m_surface = cairo_image_surface_create_for_data( m_bitmapBuffer, GAL_FORMAT, m_wxBufferWidth,
m_screenSize.y, m_stride );
m_bitmapSize.y, m_stride );
m_context = cairo_create( m_surface );
@ -1594,17 +1595,18 @@ void CAIRO_GAL::deinitSurface()
void CAIRO_GAL::allocateBitmaps()
{
m_wxBufferWidth = m_screenSize.x;
m_wxBufferWidth = m_bitmapSize.x;
// Create buffer, use the system independent Cairo context backend
m_stride = cairo_format_stride_for_width( GAL_FORMAT, m_wxBufferWidth );
m_bufferSize = m_stride * m_screenSize.y;
m_bufferSize = m_stride * m_bitmapSize.y;
wxASSERT( m_bitmapBuffer == nullptr );
m_bitmapBuffer = new unsigned char[m_bufferSize];
wxASSERT( m_wxOutput == nullptr );
m_wxOutput = new unsigned char[m_wxBufferWidth * 3 * m_screenSize.y];
wxASSERT( m_wxBitmap == nullptr );
m_wxBitmap = new wxBitmap( m_wxBufferWidth, m_bitmapSize.y, 24 );
m_wxBitmap->SetScaleFactor( getScalingFactor() );
}
@ -1613,8 +1615,8 @@ void CAIRO_GAL::deleteBitmaps()
delete[] m_bitmapBuffer;
m_bitmapBuffer = nullptr;
delete[] m_wxOutput;
m_wxOutput = nullptr;
delete m_wxBitmap;
m_wxBitmap = nullptr;
}
@ -1622,7 +1624,7 @@ void CAIRO_GAL::setCompositor()
{
// Recreate the compositor with the new Cairo context
m_compositor.reset( new CAIRO_COMPOSITOR( &m_currentContext ) );
m_compositor->Resize( m_screenSize.x, m_screenSize.y );
m_compositor->Resize( m_bitmapSize.x, m_bitmapSize.y );
m_compositor->SetAntialiasingMode( m_options.cairo_antialiasing_mode );
// Prepare buffers
@ -1636,7 +1638,22 @@ void CAIRO_GAL::setCompositor()
void CAIRO_GAL::onPaint( wxPaintEvent& aEvent )
{
PostPaint( aEvent );
// We should have the rendered image in m_wxBitmap after EDA_DRAW_PANEL_GAL::onPaint
if( !m_wxBitmap )
{
wxLogDebug( "CAIRO_GAL::onPaint null output bitmap buffer" );
return;
}
{
// Now it is the time to blit the mouse cursor
wxMemoryDC mdc( *m_wxBitmap );
blitCursor( mdc );
}
wxPaintDC paintDC( this );
paintDC.DrawBitmap( *m_wxBitmap, 0, 0 );
}
@ -1672,6 +1689,12 @@ bool CAIRO_GAL::updatedGalDisplayOptions( const GAL_DISPLAY_OPTIONS& aOptions )
}
double CAIRO_GAL::getScalingFactor()
{
return GetContentScaleFactor();
};
bool CAIRO_GAL::SetNativeCursorStyle( KICURSOR aCursor, bool aHiDPI )
{
// Store the current cursor type and get the wxCursor for it

View File

@ -2,7 +2,7 @@
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
* Copyright (C) 2012-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2012-2024 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2017-2018 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
@ -252,6 +252,9 @@ protected:
const VECTOR2D xform( double x, double y ); // rotation, scale and offset
const VECTOR2D xform( const VECTOR2D& aP ); // rotation, scale and offset
// Return the scaling factor for current window.
virtual double getScalingFactor() { return 1.0; }
/**
* Transform according to the rotation from m_currentWorld2Screen transform matrix.
*
@ -452,6 +455,8 @@ public:
/// @copydoc GAL::EndDrawing()
void EndDrawing() override;
protected:
/// Prepare Cairo surfaces for drawing
void initSurface();
@ -492,6 +497,9 @@ public:
///< Cairo-specific update handlers
bool updatedGalDisplayOptions( const GAL_DISPLAY_OPTIONS& aOptions ) override;
///< For HiDPI support
double getScalingFactor() override;
protected:
// Compositor related variables
std::shared_ptr<CAIRO_COMPOSITOR> m_compositor; ///< Object for layers compositing
@ -507,7 +515,7 @@ protected:
wxEvtHandler* m_mouseListener; ///< Mouse listener
wxEvtHandler* m_paintListener; ///< Paint listener
unsigned int m_bufferSize; ///< Size of buffers cairoOutput, bitmapBuffers
unsigned char* m_wxOutput; ///< wxImage compatible buffer
wxBitmap* m_wxBitmap; ///< Output buffer bitmap
// Variables related to Cairo <-> wxWidgets
unsigned char* m_bitmapBuffer; ///< Storage of the Cairo image

View File

@ -2,7 +2,7 @@
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
* Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2016-2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* Graphics Abstraction Layer (GAL) - base class
*
@ -1038,7 +1038,8 @@ protected:
UTIL::LINK m_observerLink;
std::stack<double> m_depthStack; ///< Stored depth values
VECTOR2I m_screenSize; ///< Screen size in screen coordinates
VECTOR2I m_screenSize; ///< Screen size in screen (wx logical) coordinates
VECTOR2I m_bitmapSize; ///< Bitmap size, in physical pixels
double m_worldUnitLength; ///< The unit length of the world coordinates [inch]
double m_screenDPI; ///< The dots per inch of the screen