Commit 4076f993 authored by Maciej Suminski's avatar Maciej Suminski

Added a layer compositor for the Cairo backend.

parent 27113348
......@@ -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})
......
/*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( &currentContext ) );
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 )
{
if( isFirstPoint )
{
cairo_move_to( cairoImage, it->x, it->y );
isFirstPoint = false;
}
else
std::deque<VECTOR2D>::const_iterator it = aPointList.begin();
cairo_move_to( currentContext, it->x, it->y );
for( ++it; it != aPointList.end(); ++it )
{
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 )
{
if( isFirstPoint )
{
cairo_move_to( cairoImage, it->x, it->y );
isFirstPoint = false;
}
else
std::deque<VECTOR2D>::const_iterator it = aPointList.begin();
cairo_move_to( currentContext, it->x, it->y );
for( ++it; it != aPointList.end(); ++it )
{
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];
......
/*
* 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,6 +353,9 @@ 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
......@@ -359,8 +363,9 @@ private:
// 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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment