|
|
|
@ -87,117 +87,7 @@ CAIRO_GAL::~CAIRO_GAL() |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::onPaint( wxPaintEvent& aEvent ) |
|
|
|
{ |
|
|
|
PostPaint(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::ResizeScreen( int aWidth, int aHeight ) |
|
|
|
{ |
|
|
|
screenSize = VECTOR2D( aWidth, aHeight ); |
|
|
|
|
|
|
|
// Recreate the bitmaps
|
|
|
|
deleteBitmaps(); |
|
|
|
allocateBitmaps(); |
|
|
|
|
|
|
|
SetSize( wxSize( aWidth, aHeight ) ); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::skipMouseEvent( wxMouseEvent& aEvent ) |
|
|
|
{ |
|
|
|
// Post the mouse event to the event listener registered in constructor, if any
|
|
|
|
if( mouseListener ) |
|
|
|
wxPostEvent( mouseListener, aEvent ); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::initSurface() |
|
|
|
{ |
|
|
|
wxASSERT( !isInitialized ); |
|
|
|
|
|
|
|
// Create the Cairo surface
|
|
|
|
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( context ); |
|
|
|
wxASSERT_MSG( status == CAIRO_STATUS_SUCCESS, "Cairo context creation error" ); |
|
|
|
#endif /* __WXDEBUG__ */
|
|
|
|
currentContext = context; |
|
|
|
|
|
|
|
cairo_set_antialias( context, CAIRO_ANTIALIAS_SUBPIXEL ); |
|
|
|
|
|
|
|
// Clear the screen
|
|
|
|
ClearScreen(); |
|
|
|
|
|
|
|
// Compute the world <-> screen transformations
|
|
|
|
ComputeWorldScreenMatrix(); |
|
|
|
|
|
|
|
cairo_matrix_init( &cairoWorldScreenMatrix, worldScreenMatrix.m_data[0][0], |
|
|
|
worldScreenMatrix.m_data[1][0], worldScreenMatrix.m_data[0][1], |
|
|
|
worldScreenMatrix.m_data[1][1], worldScreenMatrix.m_data[0][2], |
|
|
|
worldScreenMatrix.m_data[1][2] ); |
|
|
|
|
|
|
|
cairo_set_matrix( context, &cairoWorldScreenMatrix ); |
|
|
|
|
|
|
|
isSetAttributes = false; |
|
|
|
|
|
|
|
// Start drawing with a new path
|
|
|
|
cairo_new_path( context ); |
|
|
|
isElementAdded = true; |
|
|
|
|
|
|
|
cairo_set_line_join( context, CAIRO_LINE_JOIN_ROUND ); |
|
|
|
cairo_set_line_cap( context, CAIRO_LINE_CAP_ROUND ); |
|
|
|
|
|
|
|
lineWidth = 0; |
|
|
|
|
|
|
|
isDeleteSavedPixels = true; |
|
|
|
isInitialized = true; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::deinitSurface() |
|
|
|
{ |
|
|
|
if( !isInitialized ) |
|
|
|
return; |
|
|
|
|
|
|
|
// Destroy Cairo objects
|
|
|
|
cairo_destroy( context ); |
|
|
|
cairo_surface_destroy( surface ); |
|
|
|
|
|
|
|
isInitialized = false; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::setCompositor() |
|
|
|
{ |
|
|
|
// Recreate the compositor with the new Cairo context
|
|
|
|
compositor.reset( new CAIRO_COMPOSITOR( ¤tContext ) ); |
|
|
|
compositor->Resize( screenSize.x, screenSize.y ); |
|
|
|
|
|
|
|
// Prepare buffers
|
|
|
|
mainBuffer = compositor->GetBuffer(); |
|
|
|
overlayBuffer = compositor->GetBuffer(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
unsigned int CAIRO_GAL::getNewGroupNumber() |
|
|
|
{ |
|
|
|
wxASSERT_MSG( groups.size() < std::numeric_limits<unsigned int>::max(), |
|
|
|
wxT( "There are no free slots to store a group" ) ); |
|
|
|
|
|
|
|
while( groups.find( groupCounter ) != groups.end() ) |
|
|
|
{ |
|
|
|
groupCounter++; |
|
|
|
} |
|
|
|
|
|
|
|
return groupCounter++; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::BeginDrawing() throw( int ) |
|
|
|
void CAIRO_GAL::BeginDrawing() |
|
|
|
{ |
|
|
|
initSurface(); |
|
|
|
setCompositor(); |
|
|
|
@ -245,65 +135,6 @@ void CAIRO_GAL::EndDrawing() |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::SaveScreen() |
|
|
|
{ |
|
|
|
// Copy the current bitmap to the backup buffer
|
|
|
|
int offset = 0; |
|
|
|
|
|
|
|
for( int j = 0; j < screenSize.y; j++ ) |
|
|
|
{ |
|
|
|
for( int i = 0; i < stride; i++ ) |
|
|
|
{ |
|
|
|
bitmapBufferBackup[offset + i] = bitmapBuffer[offset + i]; |
|
|
|
offset += stride; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::RestoreScreen() |
|
|
|
{ |
|
|
|
int offset = 0; |
|
|
|
|
|
|
|
for( int j = 0; j < screenSize.y; j++ ) |
|
|
|
{ |
|
|
|
for( int i = 0; i < stride; i++ ) |
|
|
|
{ |
|
|
|
bitmapBuffer[offset + i] = bitmapBufferBackup[offset + i]; |
|
|
|
offset += stride; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::SetTarget( RenderTarget aTarget ) |
|
|
|
{ |
|
|
|
// 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( currentContext, aStartPoint.x, aStartPoint.y ); |
|
|
|
@ -335,7 +166,7 @@ void CAIRO_GAL::DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPo |
|
|
|
cairo_translate( currentContext, aStartPoint.x, aStartPoint.y ); |
|
|
|
cairo_rotate( currentContext, lineAngle ); |
|
|
|
|
|
|
|
cairo_arc( currentContext, 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( currentContext, lineLength, 0.0, aWidth / 2.0, -M_PI / 2.0, M_PI / 2.0 ); |
|
|
|
|
|
|
|
cairo_move_to( currentContext, 0.0, aWidth / 2.0 ); |
|
|
|
@ -373,12 +204,30 @@ void CAIRO_GAL::DrawArc( const VECTOR2D& aCenterPoint, double aRadius, double aS |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) |
|
|
|
{ |
|
|
|
// Calculate the diagonal points
|
|
|
|
VECTOR2D diagonalPointA( aEndPoint.x, aStartPoint.y ); |
|
|
|
VECTOR2D diagonalPointB( aStartPoint.x, aEndPoint.y ); |
|
|
|
|
|
|
|
// The path is composed from 4 segments
|
|
|
|
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; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::DrawPolyline( std::deque<VECTOR2D>& aPointList ) |
|
|
|
{ |
|
|
|
// Iterate over the point list and draw the segments
|
|
|
|
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( currentContext, it->x, it->y ); |
|
|
|
@ -394,6 +243,7 @@ void CAIRO_GAL::DrawPolygon( const std::deque<VECTOR2D>& aPointList ) |
|
|
|
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( currentContext, it->x, it->y ); |
|
|
|
@ -403,38 +253,53 @@ void CAIRO_GAL::DrawPolygon( const std::deque<VECTOR2D>& aPointList ) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) |
|
|
|
void CAIRO_GAL::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA, |
|
|
|
const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint ) |
|
|
|
{ |
|
|
|
// Calculate the diagonal points
|
|
|
|
VECTOR2D diagonalPointA( aEndPoint.x, aStartPoint.y ); |
|
|
|
VECTOR2D diagonalPointB( aStartPoint.x, aEndPoint.y ); |
|
|
|
|
|
|
|
// The path is composed from 4 segments
|
|
|
|
cairo_move_to( currentContext, aStartPoint.x, aStartPoint.y ); |
|
|
|
cairo_line_to( currentContext, diagonalPointA.x, diagonalPointA.y ); |
|
|
|
cairo_curve_to( currentContext, aControlPointA.x, aControlPointA.y, aControlPointB.x, |
|
|
|
aControlPointB.y, aEndPoint.x, aEndPoint.y ); |
|
|
|
cairo_line_to( currentContext, aEndPoint.x, aEndPoint.y ); |
|
|
|
cairo_line_to( currentContext, diagonalPointB.x, diagonalPointB.y ); |
|
|
|
cairo_close_path( currentContext ); |
|
|
|
|
|
|
|
isElementAdded = true; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::DrawCurve( const VECTOR2D& aStartPoint, const VECTOR2D& aControlPointA, |
|
|
|
const VECTOR2D& aControlPointB, const VECTOR2D& aEndPoint ) |
|
|
|
void CAIRO_GAL::ResizeScreen( int aWidth, int aHeight ) |
|
|
|
{ |
|
|
|
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( currentContext, aEndPoint.x, aEndPoint.y ); |
|
|
|
screenSize = VECTOR2D( aWidth, aHeight ); |
|
|
|
|
|
|
|
isElementAdded = true; |
|
|
|
// Recreate the bitmaps
|
|
|
|
deleteBitmaps(); |
|
|
|
allocateBitmaps(); |
|
|
|
|
|
|
|
SetSize( wxSize( aWidth, aHeight ) ); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool CAIRO_GAL::Show( bool aShow ) |
|
|
|
{ |
|
|
|
bool s = wxWindow::Show( aShow ); |
|
|
|
|
|
|
|
if( aShow ) |
|
|
|
wxWindow::Raise(); |
|
|
|
|
|
|
|
return s; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::Flush() |
|
|
|
{ |
|
|
|
storePath(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::SetBackgroundColor( const COLOR4D& aColor ) |
|
|
|
void CAIRO_GAL::ClearScreen() |
|
|
|
{ |
|
|
|
backgroundColor = aColor; |
|
|
|
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 ); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -528,15 +393,6 @@ void CAIRO_GAL::SetLineWidth( double aLineWidth ) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::ClearScreen() |
|
|
|
{ |
|
|
|
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 ); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::SetLayerDepth( double aLayerDepth ) |
|
|
|
{ |
|
|
|
super::SetLayerDepth( aLayerDepth ); |
|
|
|
@ -686,39 +542,10 @@ void CAIRO_GAL::EndGroup() |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::ClearCache() |
|
|
|
void CAIRO_GAL::DrawGroup( int aGroupNumber ) |
|
|
|
{ |
|
|
|
for( int i = groups.size() - 1; i >= 0; --i ) |
|
|
|
{ |
|
|
|
DeleteGroup( i ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::DeleteGroup( int aGroupNumber ) |
|
|
|
{ |
|
|
|
storePath(); |
|
|
|
|
|
|
|
// Delete the Cairo paths
|
|
|
|
std::deque<GroupElement>::iterator it, end; |
|
|
|
|
|
|
|
for( it = groups[aGroupNumber].begin(), end = groups[aGroupNumber].end(); it != end; ++it ) |
|
|
|
{ |
|
|
|
if( it->command == CMD_FILL_PATH || it->command == CMD_STROKE_PATH ) |
|
|
|
{ |
|
|
|
cairo_path_destroy( it->cairoPath ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Delete the group
|
|
|
|
groups.erase( aGroupNumber ); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::DrawGroup( int aGroupNumber ) |
|
|
|
{ |
|
|
|
// This method implements a small Virtual Machine - all stored commands
|
|
|
|
// are executed; nested calling is also possible
|
|
|
|
// This method implements a small Virtual Machine - all stored commands
|
|
|
|
// are executed; nested calling is also possible
|
|
|
|
|
|
|
|
storePath(); |
|
|
|
|
|
|
|
@ -828,31 +655,147 @@ void CAIRO_GAL::ChangeGroupDepth( int aGroupNumber, int aDepth ) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::Flush() |
|
|
|
void CAIRO_GAL::DeleteGroup( int aGroupNumber ) |
|
|
|
{ |
|
|
|
storePath(); |
|
|
|
|
|
|
|
// Delete the Cairo paths
|
|
|
|
std::deque<GroupElement>::iterator it, end; |
|
|
|
|
|
|
|
for( it = groups[aGroupNumber].begin(), end = groups[aGroupNumber].end(); it != end; ++it ) |
|
|
|
{ |
|
|
|
if( it->command == CMD_FILL_PATH || it->command == CMD_STROKE_PATH ) |
|
|
|
{ |
|
|
|
cairo_path_destroy( it->cairoPath ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Delete the group
|
|
|
|
groups.erase( aGroupNumber ); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::ClearCache() |
|
|
|
{ |
|
|
|
for( int i = groups.size() - 1; i >= 0; --i ) |
|
|
|
{ |
|
|
|
DeleteGroup( i ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::SaveScreen() |
|
|
|
{ |
|
|
|
// Copy the current bitmap to the backup buffer
|
|
|
|
int offset = 0; |
|
|
|
|
|
|
|
for( int j = 0; j < screenSize.y; j++ ) |
|
|
|
{ |
|
|
|
for( int i = 0; i < stride; i++ ) |
|
|
|
{ |
|
|
|
bitmapBufferBackup[offset + i] = bitmapBuffer[offset + i]; |
|
|
|
offset += stride; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::RestoreScreen() |
|
|
|
{ |
|
|
|
int offset = 0; |
|
|
|
|
|
|
|
for( int j = 0; j < screenSize.y; j++ ) |
|
|
|
{ |
|
|
|
for( int i = 0; i < stride; i++ ) |
|
|
|
{ |
|
|
|
bitmapBuffer[offset + i] = bitmapBufferBackup[offset + i]; |
|
|
|
offset += stride; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::SetTarget( RenderTarget aTarget ) |
|
|
|
{ |
|
|
|
// 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 ); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
VECTOR2D CAIRO_GAL::ComputeCursorToWorld( const VECTOR2D& aCursorPosition ) |
|
|
|
{ |
|
|
|
MATRIX3x3D inverseMatrix = worldScreenMatrix.Inverse(); |
|
|
|
VECTOR2D cursorPositionWorld = inverseMatrix * aCursorPosition; |
|
|
|
|
|
|
|
return cursorPositionWorld; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::ComputeWorldScreenMatrix() |
|
|
|
void CAIRO_GAL::DrawCursor( VECTOR2D aCursorPosition ) |
|
|
|
{ |
|
|
|
ComputeWorldScale(); |
|
|
|
if( !IsShownOnScreen() ) |
|
|
|
return; |
|
|
|
|
|
|
|
wxClientDC clientDC( this ); |
|
|
|
wxMemoryDC cursorSave( *cursorPixelsSaved ); |
|
|
|
wxMemoryDC cursorShape( *cursorPixels ); |
|
|
|
|
|
|
|
// Snap to grid
|
|
|
|
VECTOR2D cursorPositionWorld = ComputeCursorToWorld( aCursorPosition ); |
|
|
|
|
|
|
|
worldScreenMatrix.SetIdentity(); |
|
|
|
cursorPositionWorld.x = round( cursorPositionWorld.x / gridSize.x ) * gridSize.x; |
|
|
|
cursorPositionWorld.y = round( cursorPositionWorld.y / gridSize.y ) * gridSize.y; |
|
|
|
aCursorPosition = worldScreenMatrix * cursorPositionWorld; |
|
|
|
aCursorPosition = aCursorPosition - VECTOR2D( cursorSize / 2, cursorSize / 2 ); |
|
|
|
|
|
|
|
if( !isDeleteSavedPixels ) |
|
|
|
{ |
|
|
|
clientDC.Blit( savedCursorPosition.x, savedCursorPosition.y, cursorSize, cursorSize, |
|
|
|
&cursorSave, 0, 0 ); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
isDeleteSavedPixels = false; |
|
|
|
} |
|
|
|
|
|
|
|
cursorSave.Blit( 0, 0, cursorSize, cursorSize, &clientDC, aCursorPosition.x, |
|
|
|
aCursorPosition.y ); |
|
|
|
|
|
|
|
MATRIX3x3D translation; |
|
|
|
translation.SetIdentity(); |
|
|
|
translation.SetTranslation( 0.5 * screenSize ); |
|
|
|
clientDC.Blit( aCursorPosition.x, aCursorPosition.y, cursorSize, cursorSize, &cursorShape, 0, |
|
|
|
0, wxOR ); |
|
|
|
|
|
|
|
MATRIX3x3D scale; |
|
|
|
scale.SetIdentity(); |
|
|
|
scale.SetScale( VECTOR2D( worldScale, worldScale ) ); |
|
|
|
savedCursorPosition.x = (wxCoord) aCursorPosition.x; |
|
|
|
savedCursorPosition.y = (wxCoord) aCursorPosition.y; |
|
|
|
} |
|
|
|
|
|
|
|
MATRIX3x3D lookat; |
|
|
|
lookat.SetIdentity(); |
|
|
|
lookat.SetTranslation( -lookAtPoint ); |
|
|
|
|
|
|
|
worldScreenMatrix = translation * scale * lookat * worldScreenMatrix; |
|
|
|
void CAIRO_GAL::drawGridLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) |
|
|
|
{ |
|
|
|
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 ); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -872,7 +815,8 @@ void CAIRO_GAL::storePath() |
|
|
|
|
|
|
|
if( isStrokeEnabled ) |
|
|
|
{ |
|
|
|
cairo_set_source_rgb( currentContext, strokeColor.r, strokeColor.g, strokeColor.b ); |
|
|
|
cairo_set_source_rgb( currentContext, strokeColor.r, strokeColor.g, |
|
|
|
strokeColor.b ); |
|
|
|
cairo_stroke_preserve( currentContext ); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -903,9 +847,18 @@ void CAIRO_GAL::storePath() |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ---------------
|
|
|
|
// Cursor handling
|
|
|
|
// ---------------
|
|
|
|
void CAIRO_GAL::onPaint( wxPaintEvent& aEvent ) |
|
|
|
{ |
|
|
|
PostPaint(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::skipMouseEvent( wxMouseEvent& aEvent ) |
|
|
|
{ |
|
|
|
// Post the mouse event to the event listener registered in constructor, if any
|
|
|
|
if( mouseListener ) |
|
|
|
wxPostEvent( mouseListener, aEvent ); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::initCursor( int aCursorSize ) |
|
|
|
@ -919,7 +872,7 @@ void CAIRO_GAL::initCursor( int aCursorSize ) |
|
|
|
cursorShape.SetBackground( *wxTRANSPARENT_BRUSH ); |
|
|
|
wxColour color( cursorColor.r * cursorColor.a * 255, cursorColor.g * cursorColor.a * 255, |
|
|
|
cursorColor.b * cursorColor.a * 255, 255 ); |
|
|
|
wxPen pen = wxPen( color ); |
|
|
|
wxPen pen = wxPen( color ); |
|
|
|
cursorShape.SetPen( pen ); |
|
|
|
cursorShape.Clear(); |
|
|
|
|
|
|
|
@ -928,88 +881,103 @@ void CAIRO_GAL::initCursor( int aCursorSize ) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
VECTOR2D CAIRO_GAL::ComputeCursorToWorld( const VECTOR2D& aCursorPosition ) |
|
|
|
void CAIRO_GAL::allocateBitmaps() |
|
|
|
{ |
|
|
|
MATRIX3x3D inverseMatrix = worldScreenMatrix.Inverse(); |
|
|
|
VECTOR2D cursorPositionWorld = inverseMatrix * aCursorPosition; |
|
|
|
// Create buffer, use the system independent Cairo context backend
|
|
|
|
stride = cairo_format_stride_for_width( GAL_FORMAT, screenSize.x ); |
|
|
|
bufferSize = stride * screenSize.y; |
|
|
|
|
|
|
|
return cursorPositionWorld; |
|
|
|
bitmapBuffer = new unsigned int[bufferSize]; |
|
|
|
bitmapBufferBackup = new unsigned int[bufferSize]; |
|
|
|
wxOutput = new unsigned char[bufferSize * 3]; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::DrawCursor( VECTOR2D aCursorPosition ) |
|
|
|
void CAIRO_GAL::deleteBitmaps() |
|
|
|
{ |
|
|
|
if( !IsShownOnScreen() ) |
|
|
|
return; |
|
|
|
delete[] bitmapBuffer; |
|
|
|
delete[] bitmapBufferBackup; |
|
|
|
delete[] wxOutput; |
|
|
|
} |
|
|
|
|
|
|
|
wxClientDC clientDC( this ); |
|
|
|
wxMemoryDC cursorSave( *cursorPixelsSaved ); |
|
|
|
wxMemoryDC cursorShape( *cursorPixels ); |
|
|
|
|
|
|
|
// Snap to grid
|
|
|
|
VECTOR2D cursorPositionWorld = ComputeCursorToWorld( aCursorPosition ); |
|
|
|
void CAIRO_GAL::initSurface() |
|
|
|
{ |
|
|
|
wxASSERT( !isInitialized ); |
|
|
|
|
|
|
|
cursorPositionWorld.x = round( cursorPositionWorld.x / gridSize.x ) * gridSize.x; |
|
|
|
cursorPositionWorld.y = round( cursorPositionWorld.y / gridSize.y ) * gridSize.y; |
|
|
|
aCursorPosition = worldScreenMatrix * cursorPositionWorld; |
|
|
|
aCursorPosition = aCursorPosition - VECTOR2D( cursorSize / 2, cursorSize / 2 ); |
|
|
|
// Create the Cairo surface
|
|
|
|
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( context ); |
|
|
|
wxASSERT_MSG( status == CAIRO_STATUS_SUCCESS, "Cairo context creation error" ); |
|
|
|
#endif /* __WXDEBUG__ */
|
|
|
|
currentContext = context; |
|
|
|
|
|
|
|
if( !isDeleteSavedPixels ) |
|
|
|
{ |
|
|
|
clientDC.Blit( savedCursorPosition.x, savedCursorPosition.y, cursorSize, cursorSize, |
|
|
|
&cursorSave, 0, 0 ); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
isDeleteSavedPixels = false; |
|
|
|
} |
|
|
|
cairo_set_antialias( context, CAIRO_ANTIALIAS_SUBPIXEL ); |
|
|
|
|
|
|
|
cursorSave.Blit( 0, 0, cursorSize, cursorSize, &clientDC, aCursorPosition.x, |
|
|
|
aCursorPosition.y ); |
|
|
|
// Clear the screen
|
|
|
|
ClearScreen(); |
|
|
|
|
|
|
|
clientDC.Blit( aCursorPosition.x, aCursorPosition.y, cursorSize, cursorSize, &cursorShape, 0, |
|
|
|
0, wxOR ); |
|
|
|
// Compute the world <-> screen transformations
|
|
|
|
ComputeWorldScreenMatrix(); |
|
|
|
|
|
|
|
savedCursorPosition.x = (wxCoord) aCursorPosition.x; |
|
|
|
savedCursorPosition.y = (wxCoord) aCursorPosition.y; |
|
|
|
} |
|
|
|
cairo_matrix_init( &cairoWorldScreenMatrix, worldScreenMatrix.m_data[0][0], |
|
|
|
worldScreenMatrix.m_data[1][0], worldScreenMatrix.m_data[0][1], |
|
|
|
worldScreenMatrix.m_data[1][1], worldScreenMatrix.m_data[0][2], |
|
|
|
worldScreenMatrix.m_data[1][2] ); |
|
|
|
|
|
|
|
cairo_set_matrix( context, &cairoWorldScreenMatrix ); |
|
|
|
|
|
|
|
void CAIRO_GAL::DrawGridLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) |
|
|
|
{ |
|
|
|
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 ); |
|
|
|
// Start drawing with a new path
|
|
|
|
cairo_new_path( context ); |
|
|
|
isElementAdded = true; |
|
|
|
|
|
|
|
cairo_set_line_join( context, CAIRO_LINE_JOIN_ROUND ); |
|
|
|
cairo_set_line_cap( context, CAIRO_LINE_CAP_ROUND ); |
|
|
|
|
|
|
|
lineWidth = 0; |
|
|
|
|
|
|
|
isDeleteSavedPixels = true; |
|
|
|
isInitialized = true; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::allocateBitmaps() |
|
|
|
void CAIRO_GAL::deinitSurface() |
|
|
|
{ |
|
|
|
// Create buffer, use the system independent Cairo context backend
|
|
|
|
stride = cairo_format_stride_for_width( GAL_FORMAT, screenSize.x ); |
|
|
|
bufferSize = stride * screenSize.y; |
|
|
|
if( !isInitialized ) |
|
|
|
return; |
|
|
|
|
|
|
|
bitmapBuffer = new unsigned int[bufferSize]; |
|
|
|
bitmapBufferBackup = new unsigned int[bufferSize]; |
|
|
|
wxOutput = new unsigned char[bufferSize * 3]; |
|
|
|
// Destroy Cairo objects
|
|
|
|
cairo_destroy( context ); |
|
|
|
cairo_surface_destroy( surface ); |
|
|
|
|
|
|
|
isInitialized = false; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void CAIRO_GAL::deleteBitmaps() |
|
|
|
void CAIRO_GAL::setCompositor() |
|
|
|
{ |
|
|
|
delete[] bitmapBuffer; |
|
|
|
delete[] bitmapBufferBackup; |
|
|
|
delete[] wxOutput; |
|
|
|
// 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(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool CAIRO_GAL::Show( bool aShow ) |
|
|
|
unsigned int CAIRO_GAL::getNewGroupNumber() |
|
|
|
{ |
|
|
|
bool s = wxWindow::Show( aShow ); |
|
|
|
wxASSERT_MSG( groups.size() < std::numeric_limits<unsigned int>::max(), |
|
|
|
wxT( "There are no free slots to store a group" ) ); |
|
|
|
|
|
|
|
if( aShow ) |
|
|
|
wxWindow::Raise(); |
|
|
|
while( groups.find( groupCounter ) != groups.end() ) |
|
|
|
{ |
|
|
|
groupCounter++; |
|
|
|
} |
|
|
|
|
|
|
|
return s; |
|
|
|
return groupCounter++; |
|
|
|
} |