Added a layer compositor for the Cairo backend.

This commit is contained in:
Maciej Suminski 2013-07-25 14:40:04 +02:00
parent 27113348b9
commit 4076f99332
5 changed files with 460 additions and 152 deletions

View File

@ -50,6 +50,7 @@ set(GAL_SRCS
# Cairo GAL # Cairo GAL
gal/cairo/cairo_gal.cpp gal/cairo/cairo_gal.cpp
gal/cairo/cairo_compositor.cpp
) )
add_library(gal STATIC ${GAL_SRCS}) add_library(gal STATIC ${GAL_SRCS})

View File

@ -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();
}

View File

@ -29,6 +29,7 @@
#include <wx/log.h> #include <wx/log.h>
#include <gal/cairo/cairo_gal.h> #include <gal/cairo/cairo_gal.h>
#include <gal/cairo/cairo_compositor.h>
#include <gal/definitions.h> #include <gal/definitions.h>
#include <limits> #include <limits>
@ -89,13 +90,12 @@ CAIRO_GAL::CAIRO_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener,
CAIRO_GAL::~CAIRO_GAL() CAIRO_GAL::~CAIRO_GAL()
{ {
deinitSurface(); deinitSurface();
deleteBitmaps();
delete cursorPixels; delete cursorPixels;
delete cursorPixelsSaved; delete cursorPixelsSaved;
ClearCache(); ClearCache();
deleteBitmaps();
} }
@ -107,11 +107,10 @@ void CAIRO_GAL::onPaint( wxPaintEvent& aEvent )
void CAIRO_GAL::ResizeScreen( int aWidth, int aHeight ) void CAIRO_GAL::ResizeScreen( int aWidth, int aHeight )
{ {
deleteBitmaps();
screenSize = VECTOR2D( aWidth, aHeight ); screenSize = VECTOR2D( aWidth, aHeight );
// Recreate the bitmaps // Recreate the bitmaps
deleteBitmaps();
allocateBitmaps(); allocateBitmaps();
SetSize( wxSize( aWidth, aHeight ) ); SetSize( wxSize( aWidth, aHeight ) );
@ -128,28 +127,19 @@ void CAIRO_GAL::skipMouseEvent( wxMouseEvent& aEvent )
void CAIRO_GAL::initSurface() void CAIRO_GAL::initSurface()
{ {
if( isInitialized ) wxASSERT( !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;
// Create the Cairo surface // Create the Cairo surface
cairoSurface = cairo_image_surface_create_for_data( (unsigned char*) bitmapBuffer, surface = cairo_image_surface_create_for_data( (unsigned char*) bitmapBuffer, GAL_FORMAT,
CAIRO_FORMAT_RGB24, clientRectangle.width, screenSize.x, screenSize.y, stride );
clientRectangle.height, stride ); context = cairo_create( surface );
cairoImage = cairo_create( cairoSurface );
#ifdef __WXDEBUG__ #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" ); wxASSERT_MSG( status == CAIRO_STATUS_SUCCESS, "Cairo context creation error" );
#endif /* __WXDEBUG__ */ #endif /* __WXDEBUG__ */
currentContext = context;
// ----------------------------------------------------------------- cairo_set_antialias( context, CAIRO_ANTIALIAS_SUBPIXEL );
cairo_set_antialias( cairoImage, CAIRO_ANTIALIAS_SUBPIXEL );
// Clear the screen // Clear the screen
ClearScreen(); ClearScreen();
@ -162,21 +152,20 @@ void CAIRO_GAL::initSurface()
worldScreenMatrix.m_data[1][1], worldScreenMatrix.m_data[0][2], worldScreenMatrix.m_data[1][1], worldScreenMatrix.m_data[0][2],
worldScreenMatrix.m_data[1][2] ); worldScreenMatrix.m_data[1][2] );
cairo_set_matrix( cairoImage, &cairoWorldScreenMatrix ); cairo_set_matrix( context, &cairoWorldScreenMatrix );
isSetAttributes = false; isSetAttributes = false;
// Start drawing with a new path // Start drawing with a new path
cairo_new_path( cairoImage ); cairo_new_path( context );
isElementAdded = true; isElementAdded = true;
cairo_set_line_join( cairoImage, CAIRO_LINE_JOIN_ROUND ); cairo_set_line_join( context, CAIRO_LINE_JOIN_ROUND );
cairo_set_line_cap( cairoImage, CAIRO_LINE_CAP_ROUND ); cairo_set_line_cap( context, CAIRO_LINE_CAP_ROUND );
lineWidth = 0; lineWidth = 0;
isDeleteSavedPixels = true; isDeleteSavedPixels = true;
isInitialized = true; isInitialized = true;
} }
@ -187,13 +176,25 @@ void CAIRO_GAL::deinitSurface()
return; return;
// Destroy Cairo objects // Destroy Cairo objects
cairo_destroy( cairoImage ); cairo_destroy( context );
cairo_surface_destroy( cairoSurface ); cairo_surface_destroy( surface );
isInitialized = false; isInitialized = false;
} }
void CAIRO_GAL::setCompositor()
{
// Recreate the compositor with the new Cairo context
compositor.reset( new CAIRO_COMPOSITOR( &currentContext ) );
compositor->Resize( screenSize.x, screenSize.y );
// Prepare buffers
mainBuffer = compositor->GetBuffer();
overlayBuffer = compositor->GetBuffer();
}
unsigned int CAIRO_GAL::getNewGroupNumber() unsigned int CAIRO_GAL::getNewGroupNumber()
{ {
wxASSERT_MSG( groups.size() < std::numeric_limits<unsigned int>::max(), 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 ) void CAIRO_GAL::BeginDrawing() throw( int )
{ {
initSurface(); 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 // Force remaining objects to be drawn
Flush(); Flush();
cairo_pop_group_to_source( cairoImage ); // Cairo grouping prevents display of overlapping items on the same layer in the lighter color
cairo_paint_with_alpha( cairoImage, fillColor.a ); 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 // This code was taken from the wxCairo example - it's not the most efficient one
// Here is a good place for optimizations // 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. // by cairo into a format understood by wxImage.
unsigned char* wxOutputPtr = wxOutput; unsigned char* wxOutputPtr = wxOutput;
@ -282,14 +290,36 @@ void CAIRO_GAL::RestoreScreen()
void CAIRO_GAL::SetTarget( RenderTarget aTarget ) 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 ) void CAIRO_GAL::DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
{ {
cairo_move_to( cairoImage, aStartPoint.x, aStartPoint.y ); cairo_move_to( currentContext, aStartPoint.x, aStartPoint.y );
cairo_line_to( cairoImage, aEndPoint.x, aEndPoint.y ); cairo_line_to( currentContext, aEndPoint.x, aEndPoint.y );
isElementAdded = true; isElementAdded = true;
} }
@ -299,32 +329,34 @@ void CAIRO_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPo
{ {
if( isFillEnabled ) if( isFillEnabled )
{ {
// Filled tracks mode
SetLineWidth( aWidth ); SetLineWidth( aWidth );
cairo_move_to( cairoImage, (double) aStartPoint.x, (double) aStartPoint.y ); cairo_move_to( currentContext, (double) aStartPoint.x, (double) aStartPoint.y );
cairo_line_to( cairoImage, (double) aEndPoint.x, (double) aEndPoint.y ); cairo_line_to( currentContext, (double) aEndPoint.x, (double) aEndPoint.y );
} }
else else
{ {
// Outline mode for tracks
VECTOR2D startEndVector = aEndPoint - aStartPoint; VECTOR2D startEndVector = aEndPoint - aStartPoint;
double lineAngle = atan2( startEndVector.y, startEndVector.x ); double lineAngle = atan2( startEndVector.y, startEndVector.x );
double lineLength = startEndVector.EuclideanNorm(); double lineLength = startEndVector.EuclideanNorm();
cairo_save( cairoImage ); cairo_save( currentContext );
cairo_translate( cairoImage, aStartPoint.x, aStartPoint.y ); cairo_translate( currentContext, aStartPoint.x, aStartPoint.y );
cairo_rotate( cairoImage, lineAngle ); 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( currentContext, 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, lineLength, 0.0, aWidth / 2.0, -M_PI / 2.0, M_PI / 2.0 );
cairo_move_to( cairoImage, 0.0, aWidth / 2.0 ); cairo_move_to( currentContext, 0.0, aWidth / 2.0 );
cairo_line_to( cairoImage, lineLength, aWidth / 2.0 ); cairo_line_to( currentContext, lineLength, aWidth / 2.0 );
cairo_move_to( cairoImage, 0.0, -aWidth / 2.0 ); cairo_move_to( currentContext, 0.0, -aWidth / 2.0 );
cairo_line_to( cairoImage, lineLength, -aWidth / 2.0 ); cairo_line_to( currentContext, lineLength, -aWidth / 2.0 );
cairo_restore( cairoImage ); cairo_restore( currentContext );
} }
isElementAdded = true; 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 ) void CAIRO_GAL::DrawCircle( const VECTOR2D& aCenterPoint, double aRadius )
{ {
// A circle is drawn using an arc // A circle is drawn using an arc
cairo_new_sub_path( cairoImage ); cairo_new_sub_path( currentContext );
cairo_arc( cairoImage, aCenterPoint.x, aCenterPoint.y, aRadius, 0.0, 2 * M_PI ); cairo_arc( currentContext, aCenterPoint.x, aCenterPoint.y, aRadius, 0.0, 2 * M_PI );
isElementAdded = true; isElementAdded = true;
} }
@ -346,8 +378,8 @@ void CAIRO_GAL::DrawArc( const VECTOR2D& aCenterPoint, double aRadius, double aS
{ {
SWAP( aStartAngle, >, aEndAngle ); SWAP( aStartAngle, >, aEndAngle );
cairo_new_sub_path( cairoImage ); cairo_new_sub_path( currentContext );
cairo_arc( cairoImage, aCenterPoint.x, aCenterPoint.y, aRadius, aStartAngle, aEndAngle ); cairo_arc( currentContext, aCenterPoint.x, aCenterPoint.y, aRadius, aStartAngle, aEndAngle );
isElementAdded = true; 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 ) void CAIRO_GAL::DrawPolyline( std::deque<VECTOR2D>& aPointList )
{ {
bool isFirstPoint = true;
// Iterate over the point list and draw the segments // Iterate over the point list and draw the segments
std::deque<VECTOR2D>::const_iterator it; std::deque<VECTOR2D>::const_iterator it = aPointList.begin();
for( it = aPointList.begin(); it != aPointList.end(); ++it )
cairo_move_to( currentContext, it->x, it->y );
for( ++it; it != aPointList.end(); ++it )
{ {
if( isFirstPoint ) cairo_line_to( currentContext, it->x, it->y );
{
cairo_move_to( cairoImage, it->x, it->y );
isFirstPoint = false;
}
else
{
cairo_line_to( cairoImage, it->x, it->y );
}
} }
isElementAdded = true; isElementAdded = true;
@ -378,21 +402,13 @@ void CAIRO_GAL::DrawPolyline( std::deque<VECTOR2D>& aPointList )
void CAIRO_GAL::DrawPolygon( const 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 // Iterate over the point list and draw the polygon
std::deque<VECTOR2D>::const_iterator it; std::deque<VECTOR2D>::const_iterator it = aPointList.begin();
for( it = aPointList.begin(); it != aPointList.end(); ++it )
cairo_move_to( currentContext, it->x, it->y );
for( ++it; it != aPointList.end(); ++it )
{ {
if( isFirstPoint ) cairo_line_to( currentContext, it->x, it->y );
{
cairo_move_to( cairoImage, it->x, it->y );
isFirstPoint = false;
}
else
{
cairo_line_to( cairoImage, it->x, it->y );
}
} }
isElementAdded = true; isElementAdded = true;
@ -406,11 +422,11 @@ void CAIRO_GAL::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEnd
VECTOR2D diagonalPointB( aStartPoint.x, aEndPoint.y ); VECTOR2D diagonalPointB( aStartPoint.x, aEndPoint.y );
// The path is composed from 4 segments // The path is composed from 4 segments
cairo_move_to( cairoImage, aStartPoint.x, aStartPoint.y ); cairo_move_to( currentContext, aStartPoint.x, aStartPoint.y );
cairo_line_to( cairoImage, diagonalPointA.x, diagonalPointA.y ); cairo_line_to( currentContext, diagonalPointA.x, diagonalPointA.y );
cairo_line_to( cairoImage, aEndPoint.x, aEndPoint.y ); cairo_line_to( currentContext, aEndPoint.x, aEndPoint.y );
cairo_line_to( cairoImage, diagonalPointB.x, diagonalPointB.y ); cairo_line_to( currentContext, diagonalPointB.x, diagonalPointB.y );
cairo_close_path( cairoImage ); cairo_close_path( currentContext );
isElementAdded = true; 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, void CAIRO_GAL::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA,
const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint ) const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint )
{ {
cairo_move_to( cairoImage, aStartPoint.x, aStartPoint.y ); cairo_move_to( currentContext, aStartPoint.x, aStartPoint.y );
cairo_curve_to( cairoImage, aControlPointA.x, aControlPointA.y, aControlPointB.x, cairo_curve_to( currentContext, aControlPointA.x, aControlPointA.y, aControlPointB.x,
aControlPointB.y, aEndPoint.x, aEndPoint.y ); aControlPointB.y, aEndPoint.x, aEndPoint.y );
cairo_line_to( cairoImage, aEndPoint.x, aEndPoint.y ); cairo_line_to( currentContext, aEndPoint.x, aEndPoint.y );
isElementAdded = true; isElementAdded = true;
} }
@ -466,7 +483,6 @@ void CAIRO_GAL::SetIsStroke( bool aIsStrokeEnabled )
void CAIRO_GAL::SetStrokeColor( const COLOR4D& aColor ) void CAIRO_GAL::SetStrokeColor( const COLOR4D& aColor )
{ {
storePath(); storePath();
strokeColor = aColor; strokeColor = aColor;
if( isGrouping ) if( isGrouping )
@ -506,12 +522,6 @@ void CAIRO_GAL::SetLineWidth( double aLineWidth )
lineWidth = 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 ) if( isGrouping )
{ {
GroupElement groupElement; GroupElement groupElement;
@ -519,16 +529,23 @@ void CAIRO_GAL::SetLineWidth( double aLineWidth )
groupElement.arguments[0] = aLineWidth; groupElement.arguments[0] = aLineWidth;
currentGroup->push_back( groupElement ); 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() void CAIRO_GAL::ClearScreen()
{ {
// Clear screen cairo_set_source_rgb( currentContext,
cairo_set_source_rgba( cairoImage, backgroundColor.r, backgroundColor.g, backgroundColor.b );
backgroundColor.r, backgroundColor.g, backgroundColor.b, 1.0 ); cairo_rectangle( currentContext, 0.0, 0.0, screenSize.x, screenSize.y );
cairo_rectangle( cairoImage, 0.0, 0.0, screenSize.x, screenSize.y ); cairo_fill( currentContext );
cairo_fill( cairoImage );
} }
@ -540,10 +557,10 @@ void CAIRO_GAL::SetLayerDepth( double aLayerDepth )
{ {
storePath(); storePath();
cairo_pop_group_to_source( cairoImage ); // cairo_pop_group_to_source( currentContext );
cairo_paint_with_alpha( cairoImage, fillColor.a ); // cairo_paint_with_alpha( currentContext, fillColor.a );
//
cairo_push_group( cairoImage ); // cairo_push_group( currentContext );
} }
} }
@ -560,7 +577,7 @@ void CAIRO_GAL::Transform( MATRIX3x3D aTransformation )
aTransformation.m_data[0][2], aTransformation.m_data[0][2],
aTransformation.m_data[1][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(); storePath();
cairo_rotate( cairoImage, aAngle );
if( isGrouping ) if( isGrouping )
{ {
GroupElement groupElement; GroupElement groupElement;
@ -577,6 +592,10 @@ void CAIRO_GAL::Rotate( double aAngle )
groupElement.arguments[0] = aAngle; groupElement.arguments[0] = aAngle;
currentGroup->push_back( groupElement ); currentGroup->push_back( groupElement );
} }
else
{
cairo_rotate( currentContext, aAngle );
}
} }
@ -584,8 +603,6 @@ void CAIRO_GAL::Translate( const VECTOR2D& aTranslation )
{ {
storePath(); storePath();
cairo_translate( cairoImage, aTranslation.x, aTranslation.y );
if( isGrouping ) if( isGrouping )
{ {
GroupElement groupElement; GroupElement groupElement;
@ -594,6 +611,10 @@ void CAIRO_GAL::Translate( const VECTOR2D& aTranslation )
groupElement.arguments[1] = aTranslation.y; groupElement.arguments[1] = aTranslation.y;
currentGroup->push_back( groupElement ); currentGroup->push_back( groupElement );
} }
else
{
cairo_translate( currentContext, aTranslation.x, aTranslation.y );
}
} }
@ -601,8 +622,6 @@ void CAIRO_GAL::Scale( const VECTOR2D& aScale )
{ {
storePath(); storePath();
cairo_scale( cairoImage, aScale.x, aScale.y );
if( isGrouping ) if( isGrouping )
{ {
GroupElement groupElement; GroupElement groupElement;
@ -611,6 +630,10 @@ void CAIRO_GAL::Scale( const VECTOR2D& aScale )
groupElement.arguments[1] = aScale.y; groupElement.arguments[1] = aScale.y;
currentGroup->push_back( groupElement ); currentGroup->push_back( groupElement );
} }
else
{
cairo_scale( currentContext, aScale.x, aScale.y );
}
} }
@ -618,14 +641,16 @@ void CAIRO_GAL::Save()
{ {
storePath(); storePath();
cairo_save( cairoImage );
if( isGrouping ) if( isGrouping )
{ {
GroupElement groupElement; GroupElement groupElement;
groupElement.command = CMD_SAVE; groupElement.command = CMD_SAVE;
currentGroup->push_back( groupElement ); currentGroup->push_back( groupElement );
} }
else
{
cairo_save( currentContext );
}
} }
@ -633,14 +658,16 @@ void CAIRO_GAL::Restore()
{ {
storePath(); storePath();
cairo_restore( cairoImage );
if( isGrouping ) if( isGrouping )
{ {
GroupElement groupElement; GroupElement groupElement;
groupElement.command = CMD_RESTORE; groupElement.command = CMD_RESTORE;
currentGroup->push_back( groupElement ); 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 // Make lines appear at least 1 pixel wide, no matter of zoom
double x = 1.0, y = 1.0; 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 ) ); 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; break;
case CMD_STROKE_PATH: case CMD_STROKE_PATH:
cairo_set_source_rgb( cairoImage, strokeColor.r, strokeColor.g, strokeColor.b ); cairo_set_source_rgb( currentContext, strokeColor.r, strokeColor.g, strokeColor.b );
cairo_append_path( cairoImage, it->cairoPath ); cairo_append_path( currentContext, it->cairoPath );
cairo_stroke( cairoImage ); cairo_stroke( currentContext );
break; break;
case CMD_FILL_PATH: case CMD_FILL_PATH:
cairo_set_source_rgb( cairoImage, fillColor.r, fillColor.g, fillColor.b ); cairo_set_source_rgb( currentContext, fillColor.r, fillColor.g, fillColor.b );
cairo_append_path( cairoImage, it->cairoPath ); cairo_append_path( currentContext, it->cairoPath );
cairo_fill( cairoImage ); cairo_fill( currentContext );
break; break;
case CMD_TRANSFORM: case CMD_TRANSFORM:
cairo_matrix_t matrix; cairo_matrix_t matrix;
cairo_matrix_init( &matrix, it->arguments[0], it->arguments[1], it->arguments[2], cairo_matrix_init( &matrix, it->arguments[0], it->arguments[1], it->arguments[2],
it->arguments[3], it->arguments[4], it->arguments[5] ); it->arguments[3], it->arguments[4], it->arguments[5] );
cairo_transform( cairoImage, &matrix ); cairo_transform( currentContext, &matrix );
break; break;
case CMD_ROTATE: case CMD_ROTATE:
cairo_rotate( cairoImage, it->arguments[0] ); cairo_rotate( currentContext, it->arguments[0] );
break; break;
case CMD_TRANSLATE: case CMD_TRANSLATE:
cairo_translate( cairoImage, it->arguments[0], it->arguments[1] ); cairo_translate( currentContext, it->arguments[0], it->arguments[1] );
break; break;
case CMD_SCALE: case CMD_SCALE:
cairo_scale( cairoImage, it->arguments[0], it->arguments[1] ); cairo_scale( currentContext, it->arguments[0], it->arguments[1] );
break; break;
case CMD_SAVE: case CMD_SAVE:
cairo_save( cairoImage ); cairo_save( currentContext );
break; break;
case CMD_RESTORE: case CMD_RESTORE:
cairo_restore( cairoImage ); cairo_restore( currentContext );
break; break;
case CMD_CALL_GROUP: case CMD_CALL_GROUP:
@ -851,14 +878,14 @@ void CAIRO_GAL::storePath()
{ {
if( isFillEnabled ) if( isFillEnabled )
{ {
cairo_set_source_rgb( cairoImage, fillColor.r, fillColor.g, fillColor.b ); cairo_set_source_rgb( currentContext, fillColor.r, fillColor.g, fillColor.b );
cairo_fill_preserve( cairoImage ); cairo_fill_preserve( currentContext );
} }
if( isStrokeEnabled ) if( isStrokeEnabled )
{ {
cairo_set_source_rgb( cairoImage, strokeColor.r, strokeColor.g, strokeColor.b ); cairo_set_source_rgb( currentContext, strokeColor.r, strokeColor.g, strokeColor.b );
cairo_stroke_preserve( cairoImage ); cairo_stroke_preserve( currentContext );
} }
} }
else else
@ -869,7 +896,7 @@ void CAIRO_GAL::storePath()
if( isStrokeEnabled ) if( isStrokeEnabled )
{ {
GroupElement groupElement; GroupElement groupElement;
groupElement.cairoPath = cairo_copy_path( cairoImage ); groupElement.cairoPath = cairo_copy_path( currentContext );
groupElement.command = CMD_STROKE_PATH; groupElement.command = CMD_STROKE_PATH;
currentGroup->push_back( groupElement ); currentGroup->push_back( groupElement );
} }
@ -877,13 +904,13 @@ void CAIRO_GAL::storePath()
if( isFillEnabled ) if( isFillEnabled )
{ {
GroupElement groupElement; GroupElement groupElement;
groupElement.cairoPath = cairo_copy_path( cairoImage ); groupElement.cairoPath = cairo_copy_path( currentContext );
groupElement.command = CMD_FILL_PATH; groupElement.command = CMD_FILL_PATH;
currentGroup->push_back( groupElement ); 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 ) void CAIRO_GAL::DrawGridLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint )
{ {
cairo_move_to( cairoImage, aStartPoint.x, aStartPoint.y ); cairo_move_to( currentContext, aStartPoint.x, aStartPoint.y );
cairo_line_to( cairoImage, aEndPoint.x, aEndPoint.y ); cairo_line_to( currentContext, aEndPoint.x, aEndPoint.y );
cairo_set_source_rgba( cairoImage, gridColor.r, gridColor.g, gridColor.b, gridColor.a ); cairo_set_source_rgb( currentContext, gridColor.r, gridColor.g, gridColor.b );
cairo_stroke( cairoImage ); cairo_stroke( currentContext );
} }
void CAIRO_GAL::allocateBitmaps() void CAIRO_GAL::allocateBitmaps()
{ {
// Create buffer, use the system independent Cairo image backend // Create buffer, use the system independent Cairo context backend
stride = cairo_format_stride_for_width( CAIRO_FORMAT_RGB24, screenSize.x ); stride = cairo_format_stride_for_width( GAL_FORMAT, screenSize.x );
bufferSize = stride * screenSize.y; bufferSize = stride * screenSize.y;
bitmapBuffer = new unsigned int[bufferSize]; bitmapBuffer = new unsigned int[bufferSize];

View File

@ -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_ */

View File

@ -33,7 +33,7 @@
#include <cairo.h> #include <cairo.h>
#include <gal/graphics_abstraction_layer.h> #include <gal/graphics_abstraction_layer.h>
#include <boost/smart_ptr/shared_ptr.hpp>
#if defined(__WXMSW__) #if defined(__WXMSW__)
#define SCREEN_DEPTH 24 #define SCREEN_DEPTH 24
@ -62,6 +62,8 @@
*/ */
namespace KiGfx namespace KiGfx
{ {
class CAIRO_COMPOSITOR;
class CAIRO_GAL : public GAL, public wxWindow class CAIRO_GAL : public GAL, public wxWindow
{ {
public: public:
@ -81,7 +83,7 @@ public:
* @param aName is the name of this window for use by wxWindow::FindWindowByName() * @param aName is the name of this window for use by wxWindow::FindWindowByName()
*/ */
CAIRO_GAL( wxWindow* aParent, wxEvtHandler* aMouseListener = NULL, 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(); virtual ~CAIRO_GAL();
@ -299,11 +301,15 @@ private:
/// Super class definition /// Super class definition
typedef GAL super; 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 // Variables related to wxWidgets
wxWindow* parentWindow; ///< Parent window wxWindow* parentWindow; ///< Parent window
wxEvtHandler* mouseListener; ///< Mouse listener wxEvtHandler* mouseListener; ///< Mouse listener
wxEvtHandler* paintListener; ///< Paint listener wxEvtHandler* paintListener; ///< Paint listener
wxRect clientRectangle; ///< Area definition of the surface
unsigned int bufferSize; ///< Size of buffers cairoOutput, bitmapBuffers unsigned int bufferSize; ///< Size of buffers cairoOutput, bitmapBuffers
unsigned char* wxOutput; ///< wxImage comaptible buffer unsigned char* wxOutput; ///< wxImage comaptible buffer
@ -315,11 +321,6 @@ private:
wxBitmap* cursorPixelsSaved; ///< Saved cursor pixels wxBitmap* cursorPixelsSaved; ///< Saved cursor pixels
int cursorSize; ///< Cursor size 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 /// Maximum number of arguments for one command
static const int MAX_CAIRO_ARGUMENTS = 6; static const int MAX_CAIRO_ARGUMENTS = 6;
@ -352,15 +353,19 @@ private:
cairo_path_t* cairoPath; ///< Pointer to a Cairo path cairo_path_t* cairoPath; ///< Pointer to a Cairo path
} GroupElement; } 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 typedef std::deque<GroupElement> Group; ///< A graphic group type definition
std::map<int, Group> groups; ///< List of graphic groups std::map<int, Group> groups; ///< List of graphic groups
unsigned int groupCounter; ///< Counter used for generating keys for groups unsigned int groupCounter; ///< Counter used for generating keys for groups
Group* currentGroup; ///< Currently used group Group* currentGroup; ///< Currently used group
// Variables related to Cairo <-> wxWidgets // Variables related to Cairo <-> wxWidgets
cairo_matrix_t cairoWorldScreenMatrix; ///< Cairo world to screen transformation matrix cairo_matrix_t cairoWorldScreenMatrix; ///< Cairo world to screen transformation matrix
cairo_t* cairoImage; ///< Cairo image cairo_t* currentContext; ///< Currently used Cairo context for drawing
cairo_surface_t* cairoSurface; ///< Cairo surface cairo_t* context; ///< Cairo image
cairo_surface_t* surface; ///< Cairo surface
unsigned int* bitmapBuffer; ///< Storage of the cairo image unsigned int* bitmapBuffer; ///< Storage of the cairo image
unsigned int* bitmapBufferBackup; ///< Backup storage of the cairo image unsigned int* bitmapBufferBackup; ///< Backup storage of the cairo image
int stride; ///< Stride value for Cairo int stride; ///< Stride value for Cairo
@ -400,15 +405,21 @@ private:
/// Prepare Cairo surfaces for drawing /// Prepare Cairo surfaces for drawing
void initSurface(); void initSurface();
// Destroy Cairo surfaces when are not needed anymore /// Destroy Cairo surfaces when are not needed anymore
void deinitSurface(); void deinitSurface();
/// Prepare the compositor
void setCompositor();
/** /**
* @brief Returns a valid key that can be used as a new group number. * @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. * @return An unique group number that is not used by any other group.
*/ */
unsigned int getNewGroupNumber(); unsigned int getNewGroupNumber();
/// Format used to store pixels
static const cairo_format_t GAL_FORMAT = CAIRO_FORMAT_RGB24;
}; };
} // namespace KiGfx } // namespace KiGfx