From 60471ecc1ea39126998aef7752c9c7553b8f5198 Mon Sep 17 00:00:00 2001 From: Maciej Suminski Date: Thu, 25 Jul 2013 14:40:04 +0200 Subject: [PATCH] Added a layer compositor for the Cairo backend. --- common/CMakeLists.txt | 1 + common/gal/cairo/cairo_compositor.cpp | 161 ++++++++++++++ common/gal/cairo/cairo_gal.cpp | 303 ++++++++++++++------------ include/gal/cairo/cairo_compositor.h | 108 +++++++++ include/gal/cairo/cairo_gal.h | 39 ++-- 5 files changed, 460 insertions(+), 152 deletions(-) create mode 100644 common/gal/cairo/cairo_compositor.cpp create mode 100644 include/gal/cairo/cairo_compositor.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 9e28a9fb3b..9b7c285168 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -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}) diff --git a/common/gal/cairo/cairo_compositor.cpp b/common/gal/cairo/cairo_compositor.cpp new file mode 100644 index 0000000000..53c8cc431e --- /dev/null +++ b/common/gal/cairo/cairo_compositor.cpp @@ -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 + * + * 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 +#include + +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(); +} diff --git a/common/gal/cairo/cairo_gal.cpp b/common/gal/cairo/cairo_gal.cpp index e6a2a59d82..c17b0c7641 100644 --- a/common/gal/cairo/cairo_gal.cpp +++ b/common/gal/cairo/cairo_gal.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -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::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& aPointList ) { - bool isFirstPoint = true; - // Iterate over the point list and draw the segments - std::deque::const_iterator it; - for( it = aPointList.begin(); it != aPointList.end(); ++it ) + std::deque::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& aPointList ) void CAIRO_GAL::DrawPolygon( const std::deque& aPointList ) { - bool isFirstPoint = true; - // Iterate over the point list and draw the polygon - std::deque::const_iterator it; - for( it = aPointList.begin(); it != aPointList.end(); ++it ) + std::deque::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]; diff --git a/include/gal/cairo/cairo_compositor.h b/include/gal/cairo/cairo_compositor.h new file mode 100644 index 0000000000..24b730b28c --- /dev/null +++ b/include/gal/cairo/cairo_compositor.h @@ -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 + * + * 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 +#include +#include +#include + +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 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_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_ */ diff --git a/include/gal/cairo/cairo_gal.h b/include/gal/cairo/cairo_gal.h index 35e3188d07..bccf92d724 100644 --- a/include/gal/cairo/cairo_gal.h +++ b/include/gal/cairo/cairo_gal.h @@ -33,7 +33,7 @@ #include #include - +#include #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 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 Group; ///< A graphic group type definition - std::map groups; ///< List of graphic groups - unsigned int groupCounter; ///< Counter used for generating keys for groups - Group* currentGroup; ///< Currently used group + std::map 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