Added a layer compositor for the Cairo backend.
This commit is contained in:
parent
27113348b9
commit
4076f99332
|
@ -50,6 +50,7 @@ set(GAL_SRCS
|
|||
|
||||
# Cairo GAL
|
||||
gal/cairo/cairo_gal.cpp
|
||||
gal/cairo/cairo_compositor.cpp
|
||||
)
|
||||
|
||||
add_library(gal STATIC ${GAL_SRCS})
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
/*i
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2013 CERN
|
||||
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http:O//www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file cairo_compositor.cpp
|
||||
* @brief Class that handles multitarget rendering (ie. to different textures/surfaces) and
|
||||
* later compositing into a single image (Cairo flavour).
|
||||
*/
|
||||
|
||||
#include <gal/cairo/cairo_compositor.h>
|
||||
#include <wx/log.h>
|
||||
|
||||
using namespace KiGfx;
|
||||
|
||||
CAIRO_COMPOSITOR::CAIRO_COMPOSITOR( cairo_t** aMainContext ) :
|
||||
m_current( 0 ), m_currentContext( aMainContext ), m_mainContext( *aMainContext )
|
||||
{
|
||||
// Obtain the transformation matrix used in the main context
|
||||
cairo_get_matrix( m_mainContext, &m_matrix );
|
||||
}
|
||||
|
||||
|
||||
CAIRO_COMPOSITOR::~CAIRO_COMPOSITOR()
|
||||
{
|
||||
clean();
|
||||
}
|
||||
|
||||
|
||||
void CAIRO_COMPOSITOR::Initialize()
|
||||
{
|
||||
// Nothing has to be done
|
||||
}
|
||||
|
||||
|
||||
void CAIRO_COMPOSITOR::Resize( unsigned int aWidth, unsigned int aHeight )
|
||||
{
|
||||
clean();
|
||||
|
||||
m_width = aWidth;
|
||||
m_height = aHeight;
|
||||
|
||||
m_stride = cairo_format_stride_for_width( CAIRO_FORMAT_ARGB32, m_width );
|
||||
m_bufferSize = m_stride * m_height;
|
||||
}
|
||||
|
||||
|
||||
unsigned int CAIRO_COMPOSITOR::GetBuffer()
|
||||
{
|
||||
// Pixel storage
|
||||
BitmapPtr bitmap( new unsigned int[m_bufferSize] );
|
||||
|
||||
// Create the Cairo surface
|
||||
cairo_surface_t* surface = cairo_image_surface_create_for_data(
|
||||
(unsigned char*) bitmap.get(),
|
||||
CAIRO_FORMAT_ARGB32, m_width,
|
||||
m_height, m_stride );
|
||||
cairo_t* context = cairo_create( surface );
|
||||
#ifdef __WXDEBUG__
|
||||
cairo_status_t status = cairo_status( context );
|
||||
wxASSERT_MSG( status == CAIRO_STATUS_SUCCESS, "Cairo context creation error" );
|
||||
#endif /* __WXDEBUG__ */
|
||||
|
||||
// Set default settings for the buffer
|
||||
cairo_set_antialias( context, CAIRO_ANTIALIAS_SUBPIXEL );
|
||||
cairo_set_line_join( context, CAIRO_LINE_JOIN_ROUND );
|
||||
cairo_set_line_cap( context, CAIRO_LINE_CAP_ROUND );
|
||||
|
||||
// Use the same transformation matrix as the main context
|
||||
cairo_set_matrix( context, &m_matrix );
|
||||
|
||||
// Store the new buffer
|
||||
CAIRO_BUFFER buffer = { context, surface, bitmap };
|
||||
m_buffers.push_back( buffer );
|
||||
|
||||
return usedBuffers();
|
||||
}
|
||||
|
||||
|
||||
void CAIRO_COMPOSITOR::SetBuffer( unsigned int aBufferHandle )
|
||||
{
|
||||
if( aBufferHandle <= usedBuffers() )
|
||||
{
|
||||
m_current = aBufferHandle - 1;
|
||||
*m_currentContext = m_buffers[m_current].context;
|
||||
}
|
||||
#ifdef __WXDEBUG__
|
||||
else
|
||||
wxLogDebug( wxT( "Tried to use a not existing buffer" ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void CAIRO_COMPOSITOR::ClearBuffer()
|
||||
{
|
||||
// Reset the transformation matrix, so it is possible to composite images using
|
||||
// screen coordinates instead of world coordinates
|
||||
cairo_identity_matrix( m_buffers[m_current].context );
|
||||
|
||||
cairo_set_source_rgba( m_buffers[m_current].context, 0.0, 0.0, 0.0, 0.0 );
|
||||
cairo_rectangle( m_buffers[m_current].context, 0.0, 0.0, m_width, m_height );
|
||||
cairo_fill( m_buffers[m_current].context );
|
||||
|
||||
// Restore the transformation matrix
|
||||
cairo_set_matrix( m_buffers[m_current].context, &m_matrix );
|
||||
}
|
||||
|
||||
|
||||
void CAIRO_COMPOSITOR::DrawBuffer( unsigned int aBufferHandle )
|
||||
{
|
||||
if( aBufferHandle <= usedBuffers() )
|
||||
{
|
||||
// Reset the transformation matrix, so it is possible to composite images using
|
||||
// screen coordinates instead of world coordinates
|
||||
cairo_identity_matrix( m_mainContext );
|
||||
|
||||
cairo_set_source_surface( m_mainContext, m_buffers[aBufferHandle - 1].surface, 0.0, 0.0 );
|
||||
cairo_paint( m_mainContext );
|
||||
|
||||
// Restore the transformation matrix
|
||||
cairo_set_matrix( m_mainContext, &m_matrix );
|
||||
}
|
||||
#ifdef __WXDEBUG__
|
||||
else
|
||||
wxLogDebug( wxT( "Tried to use a not existing buffer" ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void CAIRO_COMPOSITOR::clean()
|
||||
{
|
||||
CAIRO_BUFFERS::const_iterator it;
|
||||
|
||||
for( it = m_buffers.begin(); it != m_buffers.end(); ++it )
|
||||
{
|
||||
cairo_destroy( it->context );
|
||||
cairo_surface_destroy( it->surface );
|
||||
}
|
||||
|
||||
m_buffers.clear();
|
||||
}
|
|
@ -29,6 +29,7 @@
|
|||
#include <wx/log.h>
|
||||
|
||||
#include <gal/cairo/cairo_gal.h>
|
||||
#include <gal/cairo/cairo_compositor.h>
|
||||
#include <gal/definitions.h>
|
||||
|
||||
#include <limits>
|
||||
|
@ -89,13 +90,12 @@ CAIRO_GAL::CAIRO_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener,
|
|||
CAIRO_GAL::~CAIRO_GAL()
|
||||
{
|
||||
deinitSurface();
|
||||
deleteBitmaps();
|
||||
|
||||
delete cursorPixels;
|
||||
delete cursorPixelsSaved;
|
||||
|
||||
ClearCache();
|
||||
|
||||
deleteBitmaps();
|
||||
}
|
||||
|
||||
|
||||
|
@ -107,11 +107,10 @@ void CAIRO_GAL::onPaint( wxPaintEvent& aEvent )
|
|||
|
||||
void CAIRO_GAL::ResizeScreen( int aWidth, int aHeight )
|
||||
{
|
||||
deleteBitmaps();
|
||||
|
||||
screenSize = VECTOR2D( aWidth, aHeight );
|
||||
|
||||
// Recreate the bitmaps
|
||||
deleteBitmaps();
|
||||
allocateBitmaps();
|
||||
|
||||
SetSize( wxSize( aWidth, aHeight ) );
|
||||
|
@ -128,28 +127,19 @@ void CAIRO_GAL::skipMouseEvent( wxMouseEvent& aEvent )
|
|||
|
||||
void CAIRO_GAL::initSurface()
|
||||
{
|
||||
if( isInitialized )
|
||||
return;
|
||||
|
||||
// The size of the client area needs to be greater than zero
|
||||
clientRectangle = parentWindow->GetClientRect();
|
||||
|
||||
if( clientRectangle.width == 0 || clientRectangle.height == 0 )
|
||||
throw EXCEPTION_ZERO_CLIENT_RECTANGLE;
|
||||
wxASSERT( !isInitialized );
|
||||
|
||||
// Create the Cairo surface
|
||||
cairoSurface = cairo_image_surface_create_for_data( (unsigned char*) bitmapBuffer,
|
||||
CAIRO_FORMAT_RGB24, clientRectangle.width,
|
||||
clientRectangle.height, stride );
|
||||
cairoImage = cairo_create( cairoSurface );
|
||||
surface = cairo_image_surface_create_for_data( (unsigned char*) bitmapBuffer, GAL_FORMAT,
|
||||
screenSize.x, screenSize.y, stride );
|
||||
context = cairo_create( surface );
|
||||
#ifdef __WXDEBUG__
|
||||
cairo_status_t status = cairo_status( cairoImage );
|
||||
cairo_status_t status = cairo_status( context );
|
||||
wxASSERT_MSG( status == CAIRO_STATUS_SUCCESS, "Cairo context creation error" );
|
||||
#endif /* __WXDEBUG__ */
|
||||
currentContext = context;
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
cairo_set_antialias( cairoImage, CAIRO_ANTIALIAS_SUBPIXEL );
|
||||
cairo_set_antialias( context, CAIRO_ANTIALIAS_SUBPIXEL );
|
||||
|
||||
// Clear the screen
|
||||
ClearScreen();
|
||||
|
@ -162,21 +152,20 @@ void CAIRO_GAL::initSurface()
|
|||
worldScreenMatrix.m_data[1][1], worldScreenMatrix.m_data[0][2],
|
||||
worldScreenMatrix.m_data[1][2] );
|
||||
|
||||
cairo_set_matrix( cairoImage, &cairoWorldScreenMatrix );
|
||||
cairo_set_matrix( context, &cairoWorldScreenMatrix );
|
||||
|
||||
isSetAttributes = false;
|
||||
|
||||
// Start drawing with a new path
|
||||
cairo_new_path( cairoImage );
|
||||
cairo_new_path( context );
|
||||
isElementAdded = true;
|
||||
|
||||
cairo_set_line_join( cairoImage, CAIRO_LINE_JOIN_ROUND );
|
||||
cairo_set_line_cap( cairoImage, CAIRO_LINE_CAP_ROUND );
|
||||
cairo_set_line_join( context, CAIRO_LINE_JOIN_ROUND );
|
||||
cairo_set_line_cap( context, CAIRO_LINE_CAP_ROUND );
|
||||
|
||||
lineWidth = 0;
|
||||
|
||||
isDeleteSavedPixels = true;
|
||||
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
|
@ -187,13 +176,25 @@ void CAIRO_GAL::deinitSurface()
|
|||
return;
|
||||
|
||||
// Destroy Cairo objects
|
||||
cairo_destroy( cairoImage );
|
||||
cairo_surface_destroy( cairoSurface );
|
||||
cairo_destroy( context );
|
||||
cairo_surface_destroy( surface );
|
||||
|
||||
isInitialized = false;
|
||||
}
|
||||
|
||||
|
||||
void CAIRO_GAL::setCompositor()
|
||||
{
|
||||
// Recreate the compositor with the new Cairo context
|
||||
compositor.reset( new CAIRO_COMPOSITOR( ¤tContext ) );
|
||||
compositor->Resize( screenSize.x, screenSize.y );
|
||||
|
||||
// Prepare buffers
|
||||
mainBuffer = compositor->GetBuffer();
|
||||
overlayBuffer = compositor->GetBuffer();
|
||||
}
|
||||
|
||||
|
||||
unsigned int CAIRO_GAL::getNewGroupNumber()
|
||||
{
|
||||
wxASSERT_MSG( groups.size() < std::numeric_limits<unsigned int>::max(),
|
||||
|
@ -211,8 +212,10 @@ unsigned int CAIRO_GAL::getNewGroupNumber()
|
|||
void CAIRO_GAL::BeginDrawing() throw( int )
|
||||
{
|
||||
initSurface();
|
||||
setCompositor();
|
||||
|
||||
cairo_push_group( cairoImage );
|
||||
// Cairo grouping prevents display of overlapping items on the same layer in the lighter color
|
||||
cairo_push_group( currentContext );
|
||||
}
|
||||
|
||||
|
||||
|
@ -221,13 +224,18 @@ void CAIRO_GAL::EndDrawing()
|
|||
// Force remaining objects to be drawn
|
||||
Flush();
|
||||
|
||||
cairo_pop_group_to_source( cairoImage );
|
||||
cairo_paint_with_alpha( cairoImage, fillColor.a );
|
||||
// Cairo grouping prevents display of overlapping items on the same layer in the lighter color
|
||||
cairo_pop_group_to_source( currentContext );
|
||||
cairo_paint_with_alpha( currentContext, fillColor.a );
|
||||
|
||||
// Merge buffers on the screen
|
||||
compositor->DrawBuffer( mainBuffer );
|
||||
compositor->DrawBuffer( overlayBuffer );
|
||||
|
||||
// This code was taken from the wxCairo example - it's not the most efficient one
|
||||
// Here is a good place for optimizations
|
||||
|
||||
// Now translate the raw image data from the format stored
|
||||
// Now translate the raw context data from the format stored
|
||||
// by cairo into a format understood by wxImage.
|
||||
unsigned char* wxOutputPtr = wxOutput;
|
||||
|
||||
|
@ -282,14 +290,36 @@ void CAIRO_GAL::RestoreScreen()
|
|||
|
||||
void CAIRO_GAL::SetTarget( RenderTarget aTarget )
|
||||
{
|
||||
wxASSERT_MSG( false, wxT( "Not implemented yet" ) );
|
||||
// If the compositor is not set, that means that there is a recaching process going on
|
||||
// and we do not need the compositor now
|
||||
if( !compositor )
|
||||
return;
|
||||
|
||||
// Cairo grouping prevents display of overlapping items on the same layer in the lighter color
|
||||
cairo_pop_group_to_source( currentContext );
|
||||
cairo_paint_with_alpha( currentContext, fillColor.a );
|
||||
|
||||
switch( aTarget )
|
||||
{
|
||||
default:
|
||||
case TARGET_CACHED:
|
||||
case TARGET_NONCACHED:
|
||||
compositor->SetBuffer( mainBuffer );
|
||||
break;
|
||||
|
||||
case TARGET_OVERLAY:
|
||||
compositor->SetBuffer( overlayBuffer );
|
||||
break;
|
||||
}
|
||||
|
||||
cairo_push_group( currentContext );
|
||||
}
|
||||
|
||||
|
||||
void CAIRO_GAL::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
|
||||
{
|
||||
cairo_move_to( cairoImage, aStartPoint.x, aStartPoint.y );
|
||||
cairo_line_to( cairoImage, aEndPoint.x, aEndPoint.y );
|
||||
cairo_move_to( currentContext, aStartPoint.x, aStartPoint.y );
|
||||
cairo_line_to( currentContext, aEndPoint.x, aEndPoint.y );
|
||||
isElementAdded = true;
|
||||
}
|
||||
|
||||
|
@ -299,32 +329,34 @@ void CAIRO_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPo
|
|||
{
|
||||
if( isFillEnabled )
|
||||
{
|
||||
// Filled tracks mode
|
||||
SetLineWidth( aWidth );
|
||||
|
||||
cairo_move_to( cairoImage, (double) aStartPoint.x, (double) aStartPoint.y );
|
||||
cairo_line_to( cairoImage, (double) aEndPoint.x, (double) aEndPoint.y );
|
||||
cairo_move_to( currentContext, (double) aStartPoint.x, (double) aStartPoint.y );
|
||||
cairo_line_to( currentContext, (double) aEndPoint.x, (double) aEndPoint.y );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Outline mode for tracks
|
||||
VECTOR2D startEndVector = aEndPoint - aStartPoint;
|
||||
double lineAngle = atan2( startEndVector.y, startEndVector.x );
|
||||
double lineLength = startEndVector.EuclideanNorm();
|
||||
|
||||
cairo_save( cairoImage );
|
||||
cairo_save( currentContext );
|
||||
|
||||
cairo_translate( cairoImage, aStartPoint.x, aStartPoint.y );
|
||||
cairo_rotate( cairoImage, lineAngle );
|
||||
cairo_translate( currentContext, aStartPoint.x, aStartPoint.y );
|
||||
cairo_rotate( currentContext, lineAngle );
|
||||
|
||||
cairo_arc( cairoImage, 0.0, 0.0, aWidth / 2.0, M_PI / 2.0, 3.0 * M_PI / 2.0 );
|
||||
cairo_arc( cairoImage, lineLength, 0.0, aWidth / 2.0, -M_PI / 2.0, M_PI / 2.0 );
|
||||
cairo_arc( currentContext, 0.0, 0.0, aWidth / 2.0, M_PI / 2.0, 3.0 * M_PI / 2.0 );
|
||||
cairo_arc( currentContext, lineLength, 0.0, aWidth / 2.0, -M_PI / 2.0, M_PI / 2.0 );
|
||||
|
||||
cairo_move_to( cairoImage, 0.0, aWidth / 2.0 );
|
||||
cairo_line_to( cairoImage, lineLength, aWidth / 2.0 );
|
||||
cairo_move_to( currentContext, 0.0, aWidth / 2.0 );
|
||||
cairo_line_to( currentContext, lineLength, aWidth / 2.0 );
|
||||
|
||||
cairo_move_to( cairoImage, 0.0, -aWidth / 2.0 );
|
||||
cairo_line_to( cairoImage, lineLength, -aWidth / 2.0 );
|
||||
cairo_move_to( currentContext, 0.0, -aWidth / 2.0 );
|
||||
cairo_line_to( currentContext, lineLength, -aWidth / 2.0 );
|
||||
|
||||
cairo_restore( cairoImage );
|
||||
cairo_restore( currentContext );
|
||||
}
|
||||
|
||||
isElementAdded = true;
|
||||
|
@ -334,8 +366,8 @@ void CAIRO_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPo
|
|||
void CAIRO_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius )
|
||||
{
|
||||
// A circle is drawn using an arc
|
||||
cairo_new_sub_path( cairoImage );
|
||||
cairo_arc( cairoImage, aCenterPoint.x, aCenterPoint.y, aRadius, 0.0, 2 * M_PI );
|
||||
cairo_new_sub_path( currentContext );
|
||||
cairo_arc( currentContext, aCenterPoint.x, aCenterPoint.y, aRadius, 0.0, 2 * M_PI );
|
||||
|
||||
isElementAdded = true;
|
||||
}
|
||||
|
@ -346,8 +378,8 @@ void CAIRO_GAL::DrawArc( const VECTOR2D& aCenterPoint, double aRadius, double aS
|
|||
{
|
||||
SWAP( aStartAngle, >, aEndAngle );
|
||||
|
||||
cairo_new_sub_path( cairoImage );
|
||||
cairo_arc( cairoImage, aCenterPoint.x, aCenterPoint.y, aRadius, aStartAngle, aEndAngle );
|
||||
cairo_new_sub_path( currentContext );
|
||||
cairo_arc( currentContext, aCenterPoint.x, aCenterPoint.y, aRadius, aStartAngle, aEndAngle );
|
||||
|
||||
isElementAdded = true;
|
||||
}
|
||||
|
@ -355,21 +387,13 @@ void CAIRO_GAL::DrawArc( const VECTOR2D& aCenterPoint, double aRadius, double aS
|
|||
|
||||
void CAIRO_GAL::DrawPolyline( std::deque<VECTOR2D>& aPointList )
|
||||
{
|
||||
bool isFirstPoint = true;
|
||||
|
||||
// Iterate over the point list and draw the segments
|
||||
std::deque<VECTOR2D>::const_iterator it;
|
||||
for( it = aPointList.begin(); it != aPointList.end(); ++it )
|
||||
std::deque<VECTOR2D>::const_iterator it = aPointList.begin();
|
||||
|
||||
cairo_move_to( currentContext, it->x, it->y );
|
||||
for( ++it; it != aPointList.end(); ++it )
|
||||
{
|
||||
if( isFirstPoint )
|
||||
{
|
||||
cairo_move_to( cairoImage, it->x, it->y );
|
||||
isFirstPoint = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_line_to( cairoImage, it->x, it->y );
|
||||
}
|
||||
cairo_line_to( currentContext, it->x, it->y );
|
||||
}
|
||||
|
||||
isElementAdded = true;
|
||||
|
@ -378,21 +402,13 @@ void CAIRO_GAL::DrawPolyline( std::deque<VECTOR2D>& aPointList )
|
|||
|
||||
void CAIRO_GAL::DrawPolygon( const std::deque<VECTOR2D>& aPointList )
|
||||
{
|
||||
bool isFirstPoint = true;
|
||||
|
||||
// Iterate over the point list and draw the polygon
|
||||
std::deque<VECTOR2D>::const_iterator it;
|
||||
for( it = aPointList.begin(); it != aPointList.end(); ++it )
|
||||
std::deque<VECTOR2D>::const_iterator it = aPointList.begin();
|
||||
|
||||
cairo_move_to( currentContext, it->x, it->y );
|
||||
for( ++it; it != aPointList.end(); ++it )
|
||||
{
|
||||
if( isFirstPoint )
|
||||
{
|
||||
cairo_move_to( cairoImage, it->x, it->y );
|
||||
isFirstPoint = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_line_to( cairoImage, it->x, it->y );
|
||||
}
|
||||
cairo_line_to( currentContext, it->x, it->y );
|
||||
}
|
||||
|
||||
isElementAdded = true;
|
||||
|
@ -406,11 +422,11 @@ void CAIRO_GAL::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEnd
|
|||
VECTOR2D diagonalPointB( aStartPoint.x, aEndPoint.y );
|
||||
|
||||
// The path is composed from 4 segments
|
||||
cairo_move_to( cairoImage, aStartPoint.x, aStartPoint.y );
|
||||
cairo_line_to( cairoImage, diagonalPointA.x, diagonalPointA.y );
|
||||
cairo_line_to( cairoImage, aEndPoint.x, aEndPoint.y );
|
||||
cairo_line_to( cairoImage, diagonalPointB.x, diagonalPointB.y );
|
||||
cairo_close_path( cairoImage );
|
||||
cairo_move_to( currentContext, aStartPoint.x, aStartPoint.y );
|
||||
cairo_line_to( currentContext, diagonalPointA.x, diagonalPointA.y );
|
||||
cairo_line_to( currentContext, aEndPoint.x, aEndPoint.y );
|
||||
cairo_line_to( currentContext, diagonalPointB.x, diagonalPointB.y );
|
||||
cairo_close_path( currentContext );
|
||||
|
||||
isElementAdded = true;
|
||||
}
|
||||
|
@ -419,10 +435,11 @@ void CAIRO_GAL::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEnd
|
|||
void CAIRO_GAL::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA,
|
||||
const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint )
|
||||
{
|
||||
cairo_move_to( cairoImage, aStartPoint.x, aStartPoint.y );
|
||||
cairo_curve_to( cairoImage, aControlPointA.x, aControlPointA.y, aControlPointB.x,
|
||||
cairo_move_to( currentContext, aStartPoint.x, aStartPoint.y );
|
||||
cairo_curve_to( currentContext, aControlPointA.x, aControlPointA.y, aControlPointB.x,
|
||||
aControlPointB.y, aEndPoint.x, aEndPoint.y );
|
||||
cairo_line_to( cairoImage, aEndPoint.x, aEndPoint.y );
|
||||
cairo_line_to( currentContext, aEndPoint.x, aEndPoint.y );
|
||||
|
||||
isElementAdded = true;
|
||||
}
|
||||
|
||||
|
@ -466,7 +483,6 @@ void CAIRO_GAL::SetIsStroke( bool aIsStrokeEnabled )
|
|||
void CAIRO_GAL::SetStrokeColor( const COLOR4D& aColor )
|
||||
{
|
||||
storePath();
|
||||
|
||||
strokeColor = aColor;
|
||||
|
||||
if( isGrouping )
|
||||
|
@ -506,12 +522,6 @@ void CAIRO_GAL::SetLineWidth( double aLineWidth )
|
|||
|
||||
lineWidth = aLineWidth;
|
||||
|
||||
// Make lines appear at least 1 pixel wide, no matter of zoom
|
||||
double x = 1.0, y = 1.0;
|
||||
cairo_device_to_user_distance( cairoImage, &x, &y );
|
||||
double minWidth = std::min( fabs( x ), fabs( y ) );
|
||||
cairo_set_line_width( cairoImage, std::max( aLineWidth, minWidth ) );
|
||||
|
||||
if( isGrouping )
|
||||
{
|
||||
GroupElement groupElement;
|
||||
|
@ -519,16 +529,23 @@ void CAIRO_GAL::SetLineWidth( double aLineWidth )
|
|||
groupElement.arguments[0] = aLineWidth;
|
||||
currentGroup->push_back( groupElement );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make lines appear at least 1 pixel wide, no matter of zoom
|
||||
double x = 1.0, y = 1.0;
|
||||
cairo_device_to_user_distance( currentContext, &x, &y );
|
||||
double minWidth = std::min( fabs( x ), fabs( y ) );
|
||||
cairo_set_line_width( currentContext, std::max( aLineWidth, minWidth ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CAIRO_GAL::ClearScreen()
|
||||
{
|
||||
// Clear screen
|
||||
cairo_set_source_rgba( cairoImage,
|
||||
backgroundColor.r, backgroundColor.g, backgroundColor.b, 1.0 );
|
||||
cairo_rectangle( cairoImage, 0.0, 0.0, screenSize.x, screenSize.y );
|
||||
cairo_fill( cairoImage );
|
||||
cairo_set_source_rgb( currentContext,
|
||||
backgroundColor.r, backgroundColor.g, backgroundColor.b );
|
||||
cairo_rectangle( currentContext, 0.0, 0.0, screenSize.x, screenSize.y );
|
||||
cairo_fill( currentContext );
|
||||
}
|
||||
|
||||
|
||||
|
@ -540,10 +557,10 @@ void CAIRO_GAL::SetLayerDepth( double aLayerDepth )
|
|||
{
|
||||
storePath();
|
||||
|
||||
cairo_pop_group_to_source( cairoImage );
|
||||
cairo_paint_with_alpha( cairoImage, fillColor.a );
|
||||
|
||||
cairo_push_group( cairoImage );
|
||||
// cairo_pop_group_to_source( currentContext );
|
||||
// cairo_paint_with_alpha( currentContext, fillColor.a );
|
||||
//
|
||||
// cairo_push_group( currentContext );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -560,7 +577,7 @@ void CAIRO_GAL::Transform( MATRIX3x3D aTransformation )
|
|||
aTransformation.m_data[0][2],
|
||||
aTransformation.m_data[1][2] );
|
||||
|
||||
cairo_transform( cairoImage, &cairoTransformation );
|
||||
cairo_transform( currentContext, &cairoTransformation );
|
||||
}
|
||||
|
||||
|
||||
|
@ -568,8 +585,6 @@ void CAIRO_GAL::Rotate( double aAngle )
|
|||
{
|
||||
storePath();
|
||||
|
||||
cairo_rotate( cairoImage, aAngle );
|
||||
|
||||
if( isGrouping )
|
||||
{
|
||||
GroupElement groupElement;
|
||||
|
@ -577,6 +592,10 @@ void CAIRO_GAL::Rotate( double aAngle )
|
|||
groupElement.arguments[0] = aAngle;
|
||||
currentGroup->push_back( groupElement );
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_rotate( currentContext, aAngle );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -584,8 +603,6 @@ void CAIRO_GAL::Translate( const VECTOR2D& aTranslation )
|
|||
{
|
||||
storePath();
|
||||
|
||||
cairo_translate( cairoImage, aTranslation.x, aTranslation.y );
|
||||
|
||||
if( isGrouping )
|
||||
{
|
||||
GroupElement groupElement;
|
||||
|
@ -594,6 +611,10 @@ void CAIRO_GAL::Translate( const VECTOR2D& aTranslation )
|
|||
groupElement.arguments[1] = aTranslation.y;
|
||||
currentGroup->push_back( groupElement );
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_translate( currentContext, aTranslation.x, aTranslation.y );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -601,8 +622,6 @@ void CAIRO_GAL::Scale( const VECTOR2D& aScale )
|
|||
{
|
||||
storePath();
|
||||
|
||||
cairo_scale( cairoImage, aScale.x, aScale.y );
|
||||
|
||||
if( isGrouping )
|
||||
{
|
||||
GroupElement groupElement;
|
||||
|
@ -611,6 +630,10 @@ void CAIRO_GAL::Scale( const VECTOR2D& aScale )
|
|||
groupElement.arguments[1] = aScale.y;
|
||||
currentGroup->push_back( groupElement );
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_scale( currentContext, aScale.x, aScale.y );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -618,14 +641,16 @@ void CAIRO_GAL::Save()
|
|||
{
|
||||
storePath();
|
||||
|
||||
cairo_save( cairoImage );
|
||||
|
||||
if( isGrouping )
|
||||
{
|
||||
GroupElement groupElement;
|
||||
groupElement.command = CMD_SAVE;
|
||||
currentGroup->push_back( groupElement );
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_save( currentContext );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -633,14 +658,16 @@ void CAIRO_GAL::Restore()
|
|||
{
|
||||
storePath();
|
||||
|
||||
cairo_restore( cairoImage );
|
||||
|
||||
if( isGrouping )
|
||||
{
|
||||
GroupElement groupElement;
|
||||
groupElement.command = CMD_RESTORE;
|
||||
currentGroup->push_back( groupElement );
|
||||
}
|
||||
else
|
||||
{
|
||||
cairo_restore( currentContext );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -734,50 +761,50 @@ void CAIRO_GAL::DrawGroup( int aGroupNumber )
|
|||
{
|
||||
// Make lines appear at least 1 pixel wide, no matter of zoom
|
||||
double x = 1.0, y = 1.0;
|
||||
cairo_device_to_user_distance( cairoImage, &x, &y );
|
||||
cairo_device_to_user_distance( currentContext, &x, &y );
|
||||
double minWidth = std::min( fabs( x ), fabs( y ) );
|
||||
cairo_set_line_width( cairoImage, std::max( it->arguments[0], minWidth ) );
|
||||
cairo_set_line_width( currentContext, std::max( it->arguments[0], minWidth ) );
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case CMD_STROKE_PATH:
|
||||
cairo_set_source_rgb( cairoImage, strokeColor.r, strokeColor.g, strokeColor.b );
|
||||
cairo_append_path( cairoImage, it->cairoPath );
|
||||
cairo_stroke( cairoImage );
|
||||
cairo_set_source_rgb( currentContext, strokeColor.r, strokeColor.g, strokeColor.b );
|
||||
cairo_append_path( currentContext, it->cairoPath );
|
||||
cairo_stroke( currentContext );
|
||||
break;
|
||||
|
||||
case CMD_FILL_PATH:
|
||||
cairo_set_source_rgb( cairoImage, fillColor.r, fillColor.g, fillColor.b );
|
||||
cairo_append_path( cairoImage, it->cairoPath );
|
||||
cairo_fill( cairoImage );
|
||||
cairo_set_source_rgb( currentContext, fillColor.r, fillColor.g, fillColor.b );
|
||||
cairo_append_path( currentContext, it->cairoPath );
|
||||
cairo_fill( currentContext );
|
||||
break;
|
||||
|
||||
case CMD_TRANSFORM:
|
||||
cairo_matrix_t matrix;
|
||||
cairo_matrix_init( &matrix, it->arguments[0], it->arguments[1], it->arguments[2],
|
||||
it->arguments[3], it->arguments[4], it->arguments[5] );
|
||||
cairo_transform( cairoImage, &matrix );
|
||||
cairo_transform( currentContext, &matrix );
|
||||
break;
|
||||
|
||||
case CMD_ROTATE:
|
||||
cairo_rotate( cairoImage, it->arguments[0] );
|
||||
cairo_rotate( currentContext, it->arguments[0] );
|
||||
break;
|
||||
|
||||
case CMD_TRANSLATE:
|
||||
cairo_translate( cairoImage, it->arguments[0], it->arguments[1] );
|
||||
cairo_translate( currentContext, it->arguments[0], it->arguments[1] );
|
||||
break;
|
||||
|
||||
case CMD_SCALE:
|
||||
cairo_scale( cairoImage, it->arguments[0], it->arguments[1] );
|
||||
cairo_scale( currentContext, it->arguments[0], it->arguments[1] );
|
||||
break;
|
||||
|
||||
case CMD_SAVE:
|
||||
cairo_save( cairoImage );
|
||||
cairo_save( currentContext );
|
||||
break;
|
||||
|
||||
case CMD_RESTORE:
|
||||
cairo_restore( cairoImage );
|
||||
cairo_restore( currentContext );
|
||||
break;
|
||||
|
||||
case CMD_CALL_GROUP:
|
||||
|
@ -851,14 +878,14 @@ void CAIRO_GAL::storePath()
|
|||
{
|
||||
if( isFillEnabled )
|
||||
{
|
||||
cairo_set_source_rgb( cairoImage, fillColor.r, fillColor.g, fillColor.b );
|
||||
cairo_fill_preserve( cairoImage );
|
||||
cairo_set_source_rgb( currentContext, fillColor.r, fillColor.g, fillColor.b );
|
||||
cairo_fill_preserve( currentContext );
|
||||
}
|
||||
|
||||
if( isStrokeEnabled )
|
||||
{
|
||||
cairo_set_source_rgb( cairoImage, strokeColor.r, strokeColor.g, strokeColor.b );
|
||||
cairo_stroke_preserve( cairoImage );
|
||||
cairo_set_source_rgb( currentContext, strokeColor.r, strokeColor.g, strokeColor.b );
|
||||
cairo_stroke_preserve( currentContext );
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -869,7 +896,7 @@ void CAIRO_GAL::storePath()
|
|||
if( isStrokeEnabled )
|
||||
{
|
||||
GroupElement groupElement;
|
||||
groupElement.cairoPath = cairo_copy_path( cairoImage );
|
||||
groupElement.cairoPath = cairo_copy_path( currentContext );
|
||||
groupElement.command = CMD_STROKE_PATH;
|
||||
currentGroup->push_back( groupElement );
|
||||
}
|
||||
|
@ -877,13 +904,13 @@ void CAIRO_GAL::storePath()
|
|||
if( isFillEnabled )
|
||||
{
|
||||
GroupElement groupElement;
|
||||
groupElement.cairoPath = cairo_copy_path( cairoImage );
|
||||
groupElement.cairoPath = cairo_copy_path( currentContext );
|
||||
groupElement.command = CMD_FILL_PATH;
|
||||
currentGroup->push_back( groupElement );
|
||||
}
|
||||
}
|
||||
|
||||
cairo_new_path( cairoImage );
|
||||
cairo_new_path( currentContext );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -962,17 +989,17 @@ void CAIRO_GAL::DrawCursor( VECTOR2D aCursorPosition )
|
|||
|
||||
void CAIRO_GAL::DrawGridLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
|
||||
{
|
||||
cairo_move_to( cairoImage, aStartPoint.x, aStartPoint.y );
|
||||
cairo_line_to( cairoImage, aEndPoint.x, aEndPoint.y );
|
||||
cairo_set_source_rgba( cairoImage, gridColor.r, gridColor.g, gridColor.b, gridColor.a );
|
||||
cairo_stroke( cairoImage );
|
||||
cairo_move_to( currentContext, aStartPoint.x, aStartPoint.y );
|
||||
cairo_line_to( currentContext, aEndPoint.x, aEndPoint.y );
|
||||
cairo_set_source_rgb( currentContext, gridColor.r, gridColor.g, gridColor.b );
|
||||
cairo_stroke( currentContext );
|
||||
}
|
||||
|
||||
|
||||
void CAIRO_GAL::allocateBitmaps()
|
||||
{
|
||||
// Create buffer, use the system independent Cairo image backend
|
||||
stride = cairo_format_stride_for_width( CAIRO_FORMAT_RGB24, screenSize.x );
|
||||
// Create buffer, use the system independent Cairo context backend
|
||||
stride = cairo_format_stride_for_width( GAL_FORMAT, screenSize.x );
|
||||
bufferSize = stride * screenSize.y;
|
||||
|
||||
bitmapBuffer = new unsigned int[bufferSize];
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2013 CERN
|
||||
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file cairo_compositor.h
|
||||
* @brief Class that handles multitarget rendering (ie. to different textures/surfaces) and
|
||||
* later compositing into a single image (Cairo flavour).
|
||||
*/
|
||||
|
||||
#ifndef CAIRO_COMPOSITOR_H_
|
||||
#define CAIRO_COMPOSITOR_H_
|
||||
|
||||
#include <gal/compositor.h>
|
||||
#include <cairo.h>
|
||||
#include <boost/smart_ptr/shared_array.hpp>
|
||||
#include <deque>
|
||||
|
||||
namespace KiGfx
|
||||
{
|
||||
|
||||
class CAIRO_COMPOSITOR : public COMPOSITOR
|
||||
{
|
||||
public:
|
||||
CAIRO_COMPOSITOR( cairo_t** aMainContext );
|
||||
virtual ~CAIRO_COMPOSITOR();
|
||||
|
||||
/// @copydoc COMPOSITOR::Initialize()
|
||||
virtual void Initialize();
|
||||
|
||||
/// @copydoc COMPOSITOR::Resize()
|
||||
virtual void Resize( unsigned int aWidth, unsigned int aHeight );
|
||||
|
||||
/// @copydoc COMPOSITOR::GetBuffer()
|
||||
virtual unsigned int GetBuffer();
|
||||
|
||||
/// @copydoc COMPOSITOR::SetBuffer()
|
||||
virtual void SetBuffer( unsigned int aBufferHandle );
|
||||
|
||||
/// @copydoc COMPOSITOR::ClearBuffer()
|
||||
virtual void ClearBuffer();
|
||||
|
||||
/// @copydoc COMPOSITOR::DrawBuffer()
|
||||
virtual void DrawBuffer( unsigned int aBufferHandle );
|
||||
|
||||
protected:
|
||||
typedef boost::shared_array<unsigned int> BitmapPtr;
|
||||
typedef struct
|
||||
{
|
||||
cairo_t* context; ///< Main texture handle
|
||||
cairo_surface_t* surface; ///< Point to which an image from texture is attached
|
||||
BitmapPtr bitmap; ///< Pixel storage
|
||||
} CAIRO_BUFFER;
|
||||
|
||||
unsigned int m_current; ///< Currently used buffer handle
|
||||
typedef std::deque<CAIRO_BUFFER> CAIRO_BUFFERS;
|
||||
|
||||
/// Pointer to the current context, so it can be changed
|
||||
cairo_t** m_currentContext;
|
||||
|
||||
/// Rendering target used for compositing (the main display)
|
||||
cairo_t* m_mainContext;
|
||||
|
||||
/// Transformation matrix
|
||||
cairo_matrix_t m_matrix;
|
||||
|
||||
/// Stores information about initialized buffers
|
||||
CAIRO_BUFFERS m_buffers;
|
||||
|
||||
unsigned int m_stride; ///< Stride to use given the desired format and width
|
||||
unsigned int m_bufferSize; ///< Amount of memory needed to store a buffer
|
||||
|
||||
/**
|
||||
* Function clean()
|
||||
* performs freeing of resources.
|
||||
*/
|
||||
void clean();
|
||||
|
||||
/// Returns number of currently used buffers
|
||||
unsigned int usedBuffers()
|
||||
{
|
||||
return m_buffers.size();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace KiGfx
|
||||
|
||||
#endif /* COMPOSITOR_H_ */
|
|
@ -33,7 +33,7 @@
|
|||
#include <cairo.h>
|
||||
|
||||
#include <gal/graphics_abstraction_layer.h>
|
||||
|
||||
#include <boost/smart_ptr/shared_ptr.hpp>
|
||||
|
||||
#if defined(__WXMSW__)
|
||||
#define SCREEN_DEPTH 24
|
||||
|
@ -62,6 +62,8 @@
|
|||
*/
|
||||
namespace KiGfx
|
||||
{
|
||||
class CAIRO_COMPOSITOR;
|
||||
|
||||
class CAIRO_GAL : public GAL, public wxWindow
|
||||
{
|
||||
public:
|
||||
|
@ -81,7 +83,7 @@ public:
|
|||
* @param aName is the name of this window for use by wxWindow::FindWindowByName()
|
||||
*/
|
||||
CAIRO_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener = NULL,
|
||||
wxEvtHandler* aPaintListener = NULL, const wxString& aName = wxT("CairoCanvas") );
|
||||
wxEvtHandler* aPaintListener = NULL, const wxString& aName = wxT( "CairoCanvas" ) );
|
||||
|
||||
virtual ~CAIRO_GAL();
|
||||
|
||||
|
@ -299,11 +301,15 @@ private:
|
|||
/// Super class definition
|
||||
typedef GAL super;
|
||||
|
||||
// Compositing variables
|
||||
boost::shared_ptr<CAIRO_COMPOSITOR> compositor; ///< Object for layers compositing
|
||||
unsigned int mainBuffer; ///< Handle to the main buffer
|
||||
unsigned int overlayBuffer; ///< Handle to the overlay buffer
|
||||
|
||||
// Variables related to wxWidgets
|
||||
wxWindow* parentWindow; ///< Parent window
|
||||
wxEvtHandler* mouseListener; ///< Mouse listener
|
||||
wxEvtHandler* paintListener; ///< Paint listener
|
||||
wxRect clientRectangle; ///< Area definition of the surface
|
||||
unsigned int bufferSize; ///< Size of buffers cairoOutput, bitmapBuffers
|
||||
unsigned char* wxOutput; ///< wxImage comaptible buffer
|
||||
|
||||
|
@ -315,11 +321,6 @@ private:
|
|||
wxBitmap* cursorPixelsSaved; ///< Saved cursor pixels
|
||||
int cursorSize; ///< Cursor size
|
||||
|
||||
// Variables for the grouping function
|
||||
int actualGroupIndex; ///< The index of the actual group
|
||||
bool isGrouping; ///< Is grouping enabled ?
|
||||
bool isElementAdded; ///< Was an graphic element added ?
|
||||
|
||||
/// Maximum number of arguments for one command
|
||||
static const int MAX_CAIRO_ARGUMENTS = 6;
|
||||
|
||||
|
@ -352,15 +353,19 @@ private:
|
|||
cairo_path_t* cairoPath; ///< Pointer to a Cairo path
|
||||
} GroupElement;
|
||||
|
||||
// Variables for the grouping function
|
||||
bool isGrouping; ///< Is grouping enabled ?
|
||||
bool isElementAdded; ///< Was an graphic element added ?
|
||||
typedef std::deque<GroupElement> Group; ///< A graphic group type definition
|
||||
std::map<int, Group> groups; ///< List of graphic groups
|
||||
unsigned int groupCounter; ///< Counter used for generating keys for groups
|
||||
Group* currentGroup; ///< Currently used group
|
||||
std::map<int, Group> groups; ///< List of graphic groups
|
||||
unsigned int groupCounter; ///< Counter used for generating keys for groups
|
||||
Group* currentGroup; ///< Currently used group
|
||||
|
||||
// Variables related to Cairo <-> wxWidgets
|
||||
cairo_matrix_t cairoWorldScreenMatrix; ///< Cairo world to screen transformation matrix
|
||||
cairo_t* cairoImage; ///< Cairo image
|
||||
cairo_surface_t* cairoSurface; ///< Cairo surface
|
||||
cairo_t* currentContext; ///< Currently used Cairo context for drawing
|
||||
cairo_t* context; ///< Cairo image
|
||||
cairo_surface_t* surface; ///< Cairo surface
|
||||
unsigned int* bitmapBuffer; ///< Storage of the cairo image
|
||||
unsigned int* bitmapBufferBackup; ///< Backup storage of the cairo image
|
||||
int stride; ///< Stride value for Cairo
|
||||
|
@ -400,15 +405,21 @@ private:
|
|||
/// Prepare Cairo surfaces for drawing
|
||||
void initSurface();
|
||||
|
||||
// Destroy Cairo surfaces when are not needed anymore
|
||||
/// Destroy Cairo surfaces when are not needed anymore
|
||||
void deinitSurface();
|
||||
|
||||
/// Prepare the compositor
|
||||
void setCompositor();
|
||||
|
||||
/**
|
||||
* @brief Returns a valid key that can be used as a new group number.
|
||||
*
|
||||
* @return An unique group number that is not used by any other group.
|
||||
*/
|
||||
unsigned int getNewGroupNumber();
|
||||
|
||||
/// Format used to store pixels
|
||||
static const cairo_format_t GAL_FORMAT = CAIRO_FORMAT_RGB24;
|
||||
};
|
||||
} // namespace KiGfx
|
||||
|
||||
|
|
Loading…
Reference in New Issue