X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/239c1f50b033b9205f3c371ad803a6e9d1d9b3fd..fcbb6b37bfefb6b340fbb49e44a2f4c8259f1211:/contrib/src/canvas/canvas.cpp diff --git a/contrib/src/canvas/canvas.cpp b/contrib/src/canvas/canvas.cpp index e248757bdc..af813f0173 100644 --- a/contrib/src/canvas/canvas.cpp +++ b/contrib/src/canvas/canvas.cpp @@ -25,17 +25,19 @@ #include "wx/gtk/win_gtk.h" #endif -#define USE_FREETYPE 1 +#ifndef wxUSE_FREETYPE + #define wxUSE_FREETYPE 1 +#endif -#if USE_FREETYPE -#include +#if wxUSE_FREETYPE + #include #endif //---------------------------------------------------------------------------- // globals //---------------------------------------------------------------------------- -#if USE_FREETYPE +#if wxUSE_FREETYPE FT_Library g_freetypeLibrary; #endif @@ -43,26 +45,42 @@ FT_Library g_freetypeLibrary; // wxCanvasObject //---------------------------------------------------------------------------- -wxCanvasObject::wxCanvasObject( int x, int y, int width, int height ) +wxCanvasObject::wxCanvasObject() { m_owner = NULL; + m_area.x = -1; + m_area.y = -1; + m_area.width = -1; + m_area.height = -1; + m_isControl = FALSE; + m_isVector = FALSE; + m_isImage = FALSE; +} + +void wxCanvasObject::SetArea( int x, int y, int width, int height ) +{ m_area.x = x; m_area.y = y; m_area.width = width; m_area.height = height; - m_isControl = FALSE; - m_isVector = FALSE; - m_isImage = FALSE; +} + +void wxCanvasObject::SetArea( wxRect rect ) +{ + m_area.x = rect.x; + m_area.y = rect.y; + m_area.width = rect.width; + m_area.height = rect.height; } void wxCanvasObject::Move( int x, int y ) { int old_x = m_area.x; int old_y = m_area.y; - + m_area.x = x; m_area.y = y; - + if (!m_isControl) { // TODO: sometimes faster to merge into 1 Update or @@ -80,32 +98,391 @@ bool wxCanvasObject::IsHit( int x, int y, int margin ) (y <= m_area.y+m_area.height+margin)); } +void wxCanvasObject::CaptureMouse() +{ + m_owner->SetCaptureMouse( this ); +} + +void wxCanvasObject::ReleaseMouse() +{ + m_owner->SetCaptureMouse( NULL ); +} + +bool wxCanvasObject::IsCapturedMouse() +{ + return m_owner->m_captureMouse==this; +} + + +void wxCanvasObject::Render(int xabs, int yabs, int clip_x, int clip_y, int clip_width, int clip_height ) +{ +} + +void wxCanvasObject::Recreate() +{ +} + void wxCanvasObject::WriteSVG( wxTextOutputStream &stream ) { } -void wxCanvasObject::Render( int clip_x, int clip_y, int clip_width, int clip_height ) +//---------------------------------------------------------------------------- +// wxCanvasObjectGroup +//---------------------------------------------------------------------------- + +wxCanvasObjectGroup::wxCanvasObjectGroup() +{ + m_validbounds=false; +} + +void wxCanvasObjectGroup::SetOwner(wxCanvas* canvas) +{ + m_owner=canvas; + wxNode *node = m_objects.First(); + while (node) + { + wxCanvasObject *obj = (wxCanvasObject*) node->Data(); + + obj->SetOwner(canvas); + + node = node->Next(); + } +} + +void wxCanvasObjectGroup::ExtendArea(int x, int y) +{ + if (m_validbounds) + { + if ( x < m_minx ) m_minx = x; + if ( y < m_miny ) m_miny = y; + if ( x > m_maxx ) m_maxx = x; + if ( y > m_maxy ) m_maxy = y; + } + else + { + m_validbounds = true; + + m_minx = x; + m_miny = y; + m_maxx = x; + m_maxy = y; + } + +} + + +void wxCanvasObjectGroup::DeleteContents( bool flag) +{ + m_objects.DeleteContents( flag ); + m_validbounds=false; +} + + +void wxCanvasObjectGroup::Prepend( wxCanvasObject* obj ) +{ + m_objects.Insert( obj ); + m_validbounds=false; +} + +void wxCanvasObjectGroup::Append( wxCanvasObject* obj ) +{ + m_objects.Append( obj ); + m_validbounds=false; +} + +void wxCanvasObjectGroup::Insert( size_t before, wxCanvasObject* obj ) +{ + m_objects.Insert( before, obj ); + m_validbounds=false; +} + +void wxCanvasObjectGroup::Remove( wxCanvasObject* obj ) +{ + m_objects.DeleteObject( obj ); + m_validbounds=false; +} + +void wxCanvasObjectGroup::Recreate() { + m_validbounds=false; + wxNode *node = m_objects.First(); + while (node) + { + wxCanvasObject *obj = (wxCanvasObject*) node->Data(); + + obj->Recreate(); + ExtendArea(obj->GetX(),obj->GetY()); + ExtendArea(obj->GetX()+obj->GetWidth(),obj->GetY()+obj->GetHeight()); + + node = node->Next(); + } +} + +void wxCanvasObjectGroup::Render(int xabs, int yabs, int x, int y, int width, int height ) +{ + wxImage *image = m_owner->GetBuffer(); + // cycle through all objects + wxNode *node = m_objects.First(); + while (node) + { + wxCanvasObject *obj = (wxCanvasObject*) node->Data(); + + if (!obj->IsControl()) + { + // If we have 10.000 objects, we will go through + // this 10.000 times for each update, so we have + // to optimise carefully. + int clip_x = xabs + obj->GetX(); + int clip_width = obj->GetWidth(); + if (clip_x < x) + { + clip_width -= x-clip_x; + clip_x = x; + } + if (clip_width > 0) + { + if (clip_x + clip_width > x + width) + clip_width = x+width-clip_x; + + if (clip_width > 0) + { + int clip_y = yabs + obj->GetY(); + int clip_height = obj->GetHeight(); + if (clip_y < y) + { + clip_height -= y-clip_y; + clip_y = y; + } + if (clip_height > 0) + { + if (clip_y + clip_height > y + height) + clip_height = y+height-clip_y; + + if (clip_height > 0) + obj->Render(xabs,yabs, clip_x, clip_y, clip_width, clip_height ); + } + } + } + } + + node = node->Next(); + } +} + +void wxCanvasObjectGroup::WriteSVG( wxTextOutputStream &stream ) +{ +} + +bool wxCanvasObjectGroup::IsHit( int x, int y, int margin ) +{ + wxNode *node = m_objects.Last(); + while (node) + { + wxCanvasObject *obj = (wxCanvasObject*) node->Data(); + + if (!obj->IsControl()) + { + if (obj->IsHit(x,y,margin)) + { + return true; + } + } + node = node->Previous(); + } + return false; +} + +wxCanvasObject* wxCanvasObjectGroup::IsHitObject( int x, int y, int margin ) +{ + wxCanvasObject *obj=0; + wxNode *node = m_objects.Last(); + while (node) + { + obj=(wxCanvasObject*) node->Data(); + + if (!obj->IsControl()) + { + if (obj->IsHit(x,y,margin)) + { + return obj; + } + } + node = node->Previous(); + } + return 0; } +//---------------------------------------------------------------------------- +// wxCanvasObjectGroupRef +//---------------------------------------------------------------------------- + +wxCanvasObjectGroupRef::wxCanvasObjectGroupRef(double x, double y, wxCanvasObjectGroup* group) + : wxCanvasObject() +{ + m_x = x; + m_y = y; + m_validbounds=false; + m_group=group; +} + +void wxCanvasObjectGroupRef::SetOwner(wxCanvas* canvas) +{ + m_owner=canvas; + m_group->SetOwner(canvas); +} + +void wxCanvasObjectGroupRef::ExtendArea(int x, int y) +{ + if (m_validbounds) + { + if ( x < m_minx ) m_minx = x; + if ( y < m_miny ) m_miny = y; + if ( x > m_maxx ) m_maxx = x; + if ( y > m_maxy ) m_maxy = y; + } + else + { + m_validbounds = true; + + m_minx = x; + m_miny = y; + m_maxx = x; + m_maxy = y; + } + +} + +void wxCanvasObjectGroupRef::Recreate() +{ + m_validbounds=false; + m_group->Recreate(); + ExtendArea(m_group->GetXMin(),m_group->GetYMin()); + ExtendArea(m_group->GetXMax(),m_group->GetYMax()); + + //set the area in pixels relative to the parent + SetArea( m_owner->GetDeviceX( m_x + m_minx ), + m_owner->GetDeviceY( m_y + m_miny ), + m_owner->GetDeviceWidth( m_maxx-m_minx ), + m_owner->GetDeviceHeight( m_maxy-m_miny ) ); +} + +void wxCanvasObjectGroupRef::Render(int xabs, int yabs, int x, int y, int width, int height ) +{ + wxImage *image = m_owner->GetBuffer(); + xabs+=m_owner->GetDeviceX(GetPosX()); + yabs+=m_owner->GetDeviceY(GetPosY()); + + int clip_x = xabs + m_group->GetXMin(); + int clip_width = m_group->GetXMax()-m_group->GetXMin(); + if (clip_x < x) + { + clip_width -= x-clip_x; + clip_x = x; + } + if (clip_width > 0) + { + if (clip_x + clip_width > x + width) + clip_width = x+width-clip_x; + + if (clip_width > 0) + { + int clip_y = yabs + m_group->GetYMin(); + int clip_height = m_group->GetYMax()-m_group->GetYMin(); + if (clip_y < y) + { + clip_height -= y-clip_y; + clip_y = y; + } + if (clip_height > 0) + { + if (clip_y + clip_height > y + height) + clip_height = y+height-clip_y; + + if (clip_height > 0) + m_group->Render(xabs,yabs, clip_x, clip_y, clip_width, clip_height ); + } + } + } +} + +void wxCanvasObjectGroupRef::WriteSVG( wxTextOutputStream &stream ) +{ +} + +bool wxCanvasObjectGroupRef::IsHit( int x, int y, int margin ) +{ + return m_group->IsHit(x-GetPosX(),y-GetPosY(),margin); +} + +wxCanvasObject* wxCanvasObjectGroupRef::IsHitObject( int x, int y, int margin ) +{ + return m_group->IsHitObject(x-GetPosX(),y-GetPosY(),margin); +} + +void wxCanvasObjectGroupRef::Move( int x, int y ) +{ + int old_x = m_x; + int old_y = m_y; + + m_x = x; + m_y = y; + + if (!m_isControl) + { + // TODO: sometimes faster to merge into 1 Update or + // to break up into four + m_owner->Update(m_area.x, m_area.y, m_area.width, m_area.height ); + //calculate the new area in pixels relative to the parent + SetArea( m_owner->GetDeviceX( m_x + m_minx ), + m_owner->GetDeviceY( m_y + m_miny ), + m_owner->GetDeviceWidth( m_maxx-m_minx ), + m_owner->GetDeviceHeight( m_maxy-m_miny ) ); + m_owner->Update( m_area.x, m_area.y, m_area.width, m_area.height ); + } +} + + //---------------------------------------------------------------------------- // wxCanvasRect //---------------------------------------------------------------------------- -wxCanvasRect::wxCanvasRect( int x, int y, int w, int h, unsigned char red, unsigned char green, unsigned char blue ) - : wxCanvasObject( x, y, w, h ) +wxCanvasRect::wxCanvasRect( double x, double y, double w, double h, + unsigned char red, unsigned char green, unsigned char blue ) + : wxCanvasObject() { + m_x = x; + m_y = y; + m_width = w; + m_height = h; + m_red = red; m_green = green; m_blue = blue; } -void wxCanvasRect::Render( int clip_x, int clip_y, int clip_width, int clip_height ) +void wxCanvasRect::Recreate() +{ + SetArea( m_owner->GetDeviceX( m_x ), + m_owner->GetDeviceY( m_y ), + m_owner->GetDeviceWidth( m_width ), + m_owner->GetDeviceHeight( m_height ) ); +} + +void wxCanvasRect::Render(int xabs, int yabs, int clip_x, int clip_y, int clip_width, int clip_height ) { wxImage *image = m_owner->GetBuffer(); + int buffer_x = m_owner->GetBufferX(); + int buffer_y = m_owner->GetBufferY(); + + int start_y = clip_y - buffer_y; + int end_y = clip_y+clip_height - buffer_y; + + int start_x = clip_x - buffer_x; + int end_x = clip_x+clip_width - buffer_x; + // speed up later - for (int y = clip_y; y < clip_y+clip_height; y++) - for (int x = clip_x; x < clip_x+clip_width; x++) + for (int y = start_y; y < end_y; y++) + for (int x = start_x; x < end_x; x++) image->SetRGB( x, y, m_red, m_green, m_blue ); } @@ -117,28 +494,57 @@ void wxCanvasRect::WriteSVG( wxTextOutputStream &stream ) // wxCanvasLine //---------------------------------------------------------------------------- -wxCanvasLine::wxCanvasLine( int x, int y, int w, int h, unsigned char red, unsigned char green, unsigned char blue ) - : wxCanvasObject( x, y, w, h ) +wxCanvasLine::wxCanvasLine( double x1, double y1, double x2, double y2, + unsigned char red, unsigned char green, unsigned char blue ) + : wxCanvasObject() { + m_x1 = x1; + m_y1 = y1; + m_x2 = x2; + m_y2 = y2; + m_red = red; m_green = green; m_blue = blue; } -void wxCanvasLine::Render( int clip_x, int clip_y, int clip_width, int clip_height ) +void wxCanvasLine::Recreate() +{ + int x1 = m_owner->GetDeviceX( m_x1 ); + int y1 = m_owner->GetDeviceY( m_y1 ); + int x2 = m_owner->GetDeviceX( m_x2 ); + int y2 = m_owner->GetDeviceY( m_y2 ); + if (x1 > x2) + { + int tmp = x1; + x1 = x2; + x2 = tmp; + } + if (y1 > y2) + { + int tmp = y1; + y1 = y2; + y2 = tmp; + } + SetArea( x1, y1, x2-x1+1, y2-y1+1 ); +} + +void wxCanvasLine::Render(int xabs, int yabs, int clip_x, int clip_y, int clip_width, int clip_height ) { wxImage *image = m_owner->GetBuffer(); + int buffer_x = m_owner->GetBufferX(); + int buffer_y = m_owner->GetBufferY(); if ((m_area.width == 0) && (m_area.height == 0)) { - image->SetRGB( m_area.x, m_area.y, m_red, m_green, m_blue ); + image->SetRGB( m_area.x-buffer_x, m_area.y-buffer_y, m_red, m_green, m_blue ); } else { - int x1 = m_area.x; - int y1 = m_area.y; - int x2 = m_area.x+m_area.width; - int y2 = m_area.y+m_area.height; + int x1 = xabs + m_owner->GetDeviceX( m_x1 ); + int y1 = yabs + m_owner->GetDeviceY( m_y1 ); + int x2 = xabs + m_owner->GetDeviceX( m_x2 ); + int y2 = yabs + m_owner->GetDeviceY( m_y2 ); wxInt32 d, ii, jj, di, ai, si, dj, aj, sj; di = x1 - x2; @@ -150,23 +556,23 @@ void wxCanvasLine::Render( int clip_x, int clip_y, int clip_width, int clip_heig ii = x2; jj = y2; - + if (ai > aj) { // iterate over i - d = aj - (ai >> 1); - + d = aj - (ai >> 1); + while (ii != x1) { - if ((ii >= clip_x) && (ii <= clip_x+clip_width) && - (jj >= clip_y) && (jj <= clip_y+clip_height)) + if ((ii >= clip_x) && (ii < clip_x+clip_width) && + (jj >= clip_y) && (jj < clip_y+clip_height)) { - image->SetRGB( ii, jj, m_red, m_blue, m_green ); + image->SetRGB( ii-buffer_x, jj-buffer_y, m_red, m_blue, m_green ); } if (d >= 0) { jj += sj; - d -= ai; + d -= ai; } ii += si; d += aj; @@ -179,15 +585,15 @@ void wxCanvasLine::Render( int clip_x, int clip_y, int clip_width, int clip_heig while (jj != y1) { - if ((ii >= clip_x) && (ii <= clip_x+clip_width) && - (jj >= clip_y) && (jj <= clip_y+clip_height)) + if ((ii >= clip_x) && (ii < clip_x+clip_width) && + (jj >= clip_y) && (jj < clip_y+clip_height)) { - image->SetRGB( ii, jj, m_red, m_blue, m_green ); + image->SetRGB( ii-buffer_x, jj-buffer_y, m_red, m_blue, m_green ); } if (d >= 0) { ii += si; - d -= aj; + d -= aj; } jj += sj; d += ai; @@ -198,37 +604,60 @@ void wxCanvasLine::Render( int clip_x, int clip_y, int clip_width, int clip_heig void wxCanvasLine::WriteSVG( wxTextOutputStream &stream ) { + // no idea } //---------------------------------------------------------------------------- // wxCanvasImage //---------------------------------------------------------------------------- -wxCanvasImage::wxCanvasImage( const wxImage &image, int x, int y ) - : wxCanvasObject( x, y, image.GetWidth(), image.GetHeight() ) +wxCanvasImage::wxCanvasImage( const wxImage &image, double x, double y, double w, double h ) + : wxCanvasObject() { + m_x = x; + m_y = y; + m_width = w; + m_height = h; + m_image = image; m_isImage = TRUE; } -void wxCanvasImage::Render( int clip_x, int clip_y, int clip_width, int clip_height ) +void wxCanvasImage::Recreate() { - if ((clip_x == m_area.x) && - (clip_y == m_area.y) && + SetArea( m_owner->GetDeviceX( m_x ), + m_owner->GetDeviceY( m_y ), + m_owner->GetDeviceWidth( m_width ), + m_owner->GetDeviceHeight( m_height ) ); + + if ((m_area.width == m_image.GetWidth()) && + (m_area.width == m_image.GetWidth())) + m_tmp = m_image; + else + m_tmp = m_image.Scale( m_area.width, m_area.height ); +} + +void wxCanvasImage::Render(int xabs, int yabs, int clip_x, int clip_y, int clip_width, int clip_height ) +{ + int buffer_x = m_owner->GetBufferX(); + int buffer_y = m_owner->GetBufferY(); + + if ((clip_x == xabs + m_area.x) && + (clip_y == yabs + m_area.y) && (clip_width == m_area.width) && (clip_height == m_area.height)) { - m_owner->GetBuffer()->Paste( m_image, clip_x, clip_y ); + m_owner->GetBuffer()->Paste( m_tmp, clip_x-buffer_x, clip_y-buffer_y ); } else { // local coordinates - int start_x = clip_x - m_area.x; - int start_y = clip_y - m_area.y; - + int start_x = clip_x - (xabs + m_area.x); + int start_y = clip_y - (yabs + m_area.y); + wxRect rect( start_x, start_y, clip_width, clip_height ); - wxImage sub_image( m_image.GetSubImage( rect ) ); - m_owner->GetBuffer()->Paste( sub_image, clip_x, clip_y ); + wxImage sub_image( m_tmp.GetSubImage( rect ) ); + m_owner->GetBuffer()->Paste( sub_image, clip_x-buffer_x, clip_y-buffer_y ); } } @@ -242,11 +671,10 @@ void wxCanvasImage::WriteSVG( wxTextOutputStream &stream ) //---------------------------------------------------------------------------- wxCanvasControl::wxCanvasControl( wxWindow *control ) - : wxCanvasObject( -1, -1, -1, -1 ) + : wxCanvasObject() { m_isControl = TRUE; m_control = control; - UpdateSize(); } wxCanvasControl::~wxCanvasControl() @@ -254,15 +682,15 @@ wxCanvasControl::~wxCanvasControl() m_control->Destroy(); } -void wxCanvasControl::Move( int x, int y ) +void wxCanvasControl::Recreate() { - m_control->Move( x, y ); + m_control->GetSize( &m_area.width, &m_area.height ); + m_control->GetPosition( &m_area.x, &m_area.y ); } -void wxCanvasControl::UpdateSize() +void wxCanvasControl::Move( int x, int y ) { - m_control->GetSize( &m_area.width, &m_area.height ); - m_control->GetPosition( &m_area.x, &m_area.y ); + m_control->Move( x, y ); } //---------------------------------------------------------------------------- @@ -272,34 +700,33 @@ void wxCanvasControl::UpdateSize() class wxFaceData { public: -#if USE_FREETYPE +#if wxUSE_FREETYPE FT_Face m_face; #else void *m_dummy; -#endif +#endif }; -wxCanvasText::wxCanvasText( const wxString &text, int x, int y, const wxString &fontFile, int size ) - : wxCanvasObject( x, y, -1, -1 ) +wxCanvasText::wxCanvasText( const wxString &text, double x, double y, const wxString &fontFile, int size ) + : wxCanvasObject() { m_text = text; m_fontFileName = fontFile; m_size = size; - + m_red = 0; m_green = 0; m_blue = 0; - - // test - m_area.width = 100; - m_area.height = m_size; - m_alpha = new unsigned char[100*m_size]; - memset( m_alpha, 0, m_area.width*m_area.height ); - -#if USE_FREETYPE + + m_alpha = NULL; + + m_x = x; + m_y = y; + +#if wxUSE_FREETYPE wxFaceData *data = new wxFaceData; m_faceData = data; - + int error = FT_New_Face( g_freetypeLibrary, m_fontFileName, 0, @@ -310,13 +737,12 @@ wxCanvasText::wxCanvasText( const wxString &text, int x, int y, const wxString & m_size*64, 96, // screen dpi 96 ); - CreateBuffer(); #endif } wxCanvasText::~wxCanvasText() { -#if USE_FREETYPE +#if wxUSE_FREETYPE wxFaceData *data = (wxFaceData*) m_faceData; delete data; #endif @@ -336,26 +762,28 @@ void wxCanvasText::SetFlag( int flag ) m_flag = flag; } -void wxCanvasText::Render( int clip_x, int clip_y, int clip_width, int clip_height ) +void wxCanvasText::Render(int xabs, int yabs, int clip_x, int clip_y, int clip_width, int clip_height ) { if (!m_alpha) return; - + wxImage *image = m_owner->GetBuffer(); + int buffer_x = m_owner->GetBufferX(); + int buffer_y = m_owner->GetBufferY(); // local coordinates int start_x = clip_x - m_area.x; int end_x = clip_width + start_x; int start_y = clip_y - m_area.y; int end_y = clip_height + start_y; - + for (int y = start_y; y < end_y; y++) for (int x = start_x; x < end_x; x++) { int alpha = m_alpha[y*m_area.width + x]; if (alpha) { - int image_x = m_area.x+x; - int image_y = m_area.y+y; + int image_x = m_area.x+x - buffer_x; + int image_y = m_area.y+y - buffer_y; if (alpha == 255) { image->SetRGB( image_x, image_y, m_red, m_green, m_blue ); @@ -364,7 +792,7 @@ void wxCanvasText::Render( int clip_x, int clip_y, int clip_width, int clip_heig int red1 = (m_red * alpha) / 255; int green1 = (m_green * alpha) / 255; int blue1 = (m_blue * alpha) / 255; - + alpha = 255-alpha; int red2 = image->GetRed( image_x, image_y ); int green2 = image->GetGreen( image_x, image_y ); @@ -372,7 +800,7 @@ void wxCanvasText::Render( int clip_x, int clip_y, int clip_width, int clip_heig red2 = (red2 * alpha) / 255; green2 = (green2 * alpha) / 255; blue2 = (blue2 * alpha) / 255; - + image->SetRGB( image_x, image_y, red1+red2, green1+green2, blue1+blue2 ); } } @@ -382,24 +810,34 @@ void wxCanvasText::WriteSVG( wxTextOutputStream &stream ) { } -void wxCanvasText::CreateBuffer() +void wxCanvasText::Recreate() { -#if USE_FREETYPE + if (m_alpha) delete [] m_alpha; + + m_area.x = m_owner->GetDeviceX( m_x ); + m_area.y = m_owner->GetDeviceY( m_y ); + + m_area.width = 100; // TODO, calculate length + m_area.height = m_size; + m_alpha = new unsigned char[100*m_size]; + memset( m_alpha, 0, m_area.width*m_area.height ); + +#if wxUSE_FREETYPE FT_Face face = ((wxFaceData*)m_faceData)->m_face; FT_GlyphSlot slot = face->glyph; int pen_x = 0; int pen_y = m_size; - + for (int n = 0; n < (int)m_text.Len(); n++) { FT_UInt index = FT_Get_Char_Index( face, m_text[n] ); - + int error = FT_Load_Glyph( face, index, FT_LOAD_DEFAULT ); if (error) continue; - + error = FT_Render_Glyph( face->glyph, ft_render_mode_normal ); if (error) continue; - + FT_Bitmap *bitmap = &slot->bitmap; unsigned char* buffer = bitmap->buffer; for (int y = 0; y < bitmap->rows; y++) @@ -407,15 +845,15 @@ void wxCanvasText::CreateBuffer() { unsigned char alpha = buffer[ y*bitmap->pitch + x ]; if (alpha == 0) continue; - + int xx = pen_x + slot->bitmap_left + x; int yy = pen_y - slot->bitmap_top + y; m_alpha[ yy * m_area.width + xx ] = alpha; } - + pen_x += slot->advance.x >> 6; pen_y += slot->advance.y >> 6; - } + } #endif } @@ -433,19 +871,28 @@ BEGIN_EVENT_TABLE(wxCanvas,wxScrolledWindow) EVT_MOUSE_EVENTS( wxCanvas::OnMouse ) EVT_SET_FOCUS( wxCanvas::OnSetFocus ) EVT_KILL_FOCUS( wxCanvas::OnKillFocus ) + EVT_ERASE_BACKGROUND( wxCanvas::OnEraseBackground ) END_EVENT_TABLE() wxCanvas::wxCanvas( wxWindow *parent, wxWindowID id, const wxPoint &position, const wxSize& size, long style ) : wxScrolledWindow( parent, id, position, size, style ) { + m_bufferX = 0; + m_bufferY = 0; m_needUpdate = FALSE; - m_objects.DeleteContents( TRUE ); m_red = 0; m_green = 0; m_blue = 0; m_lastMouse = (wxCanvasObject*)NULL; - m_frozen = FALSE; + m_captureMouse = (wxCanvasObject*)NULL; + m_frozen = TRUE; + m_requestNewBuffer = TRUE; + + //root group always at 0,0 + m_root = new wxCanvasObjectGroup(); + m_root->DeleteContents( TRUE ); + m_root->SetOwner(this); } wxCanvas::~wxCanvas() @@ -462,7 +909,6 @@ wxCanvas::~wxCanvas() void wxCanvas::SetArea( int width, int height ) { - m_buffer = wxImage( width, height ); SetScrollbars( 10, 10, width/10, height/10 ); } @@ -471,11 +917,13 @@ void wxCanvas::SetColour( unsigned char red, unsigned char green, unsigned char m_red = red; m_green = green; m_blue = blue; - + + SetBackgroundColour( wxColour( red, green, blue ) ); + if (m_frozen) return; - + unsigned char *data = m_buffer.GetData(); - + for (int y = 0; y < m_buffer.GetHeight(); y++) for (int x = 0; x < m_buffer.GetWidth(); x++) { @@ -488,6 +936,20 @@ void wxCanvas::SetColour( unsigned char red, unsigned char green, unsigned char } } +void wxCanvas::SetCaptureMouse( wxCanvasObject *obj ) +{ + if (obj) + { + wxWindow::CaptureMouse(); + m_captureMouse = obj; + } + else + { + wxWindow::ReleaseMouse(); + m_captureMouse = NULL; + } +} + void wxCanvas::Freeze() { m_frozen = TRUE; @@ -503,101 +965,65 @@ void wxCanvas::Thaw() m_updateRects.DeleteNode( node ); node = m_updateRects.First(); } - + m_frozen = FALSE; - - Update( 0, 0, m_buffer.GetWidth(), m_buffer.GetHeight() ); + + if (m_buffer.Ok()) + Update( m_bufferX, m_bufferY, m_buffer.GetWidth(), m_buffer.GetHeight() ); } -void wxCanvas::Update( int x, int y, int width, int height ) +void wxCanvas::Update( int x, int y, int width, int height, bool blit ) { if (m_frozen) return; - + // clip to buffer - if (x < 0) + if (x < m_bufferX) { - width -= x; - x = 0; + width -= m_bufferX-x; + x = m_bufferX; } if (width < 0) return; - - if (y < 0) + + if (y < m_bufferY) { - height -= y; - y = 0; + height -= m_bufferY-y; + y = m_bufferY; } if (height < 0) return; - - if (x+width > m_buffer.GetWidth()) + + if (x+width > m_bufferX+m_buffer.GetWidth()) { - width = m_buffer.GetWidth() - x; + width = m_bufferX+m_buffer.GetWidth() - x; } if (width < 0) return; - - if (y+height > m_buffer.GetHeight()) + + if (y+height > m_bufferY+m_buffer.GetHeight()) { - height = m_buffer.GetHeight() - y; + height = m_bufferY+m_buffer.GetHeight() - y; } if (height < 0) return; - + // update is within the buffer m_needUpdate = TRUE; - + // has to be blitted to screen later - m_updateRects.Append( - (wxObject*) new wxRect( x,y,width,height ) ); - + if (blit) + { + m_updateRects.Append( + (wxObject*) new wxRect( x,y,width,height ) ); + } + // speed up with direct access, maybe add wxImage::Clear(x,y,w,h,r,g,b) - for (int yy = y; yy < y+height; yy++) - for (int xx = x; xx < x+width; xx++) + int start_y = y - m_bufferY; + int end_y = y+height - m_bufferY; + int start_x = x - m_bufferX; + int end_x = x+width - m_bufferX; + for (int yy = start_y; yy < end_y; yy++) + for (int xx = start_x; xx < end_x; xx++) m_buffer.SetRGB( xx, yy, m_red, m_green, m_blue ); - // cycle through all objects - wxNode *node = m_objects.First(); - while (node) - { - wxCanvasObject *obj = (wxCanvasObject*) node->Data(); - - if (!obj->IsControl()) - { - // If we have 10.000 objects, we will go through - // this 10.000 times for each update, so we have - // to optimise carefully. - int clip_x = obj->GetX(); - int clip_width = obj->GetWidth(); - if (clip_x < x) - { - clip_width -= x-clip_x; - clip_x = x; - } - if (clip_width > 0) - { - if (clip_x + clip_width > x + width) - clip_width = x+width-clip_x; - - if (clip_width > 0) - { - int clip_y = obj->GetY(); - int clip_height = obj->GetHeight(); - if (clip_y < y) - { - clip_height -= y-clip_y; - clip_y = y; - } - if (clip_height > 0) - { - if (clip_y + clip_height > y + height) - clip_height = y+height-clip_y; - - if (clip_height > 0) - obj->Render( clip_x, clip_y, clip_width, clip_height ); - } - } - } - } - - node = node->Next(); - } + m_root->Render(0,0, x, y, width, height ); + } void wxCanvas::BlitBuffer( wxDC &dc ) @@ -606,12 +1032,15 @@ void wxCanvas::BlitBuffer( wxDC &dc ) while (node) { wxRect *rect = (wxRect*) node->Data(); - wxImage sub_image( m_buffer.GetSubImage( *rect ) ); - // DirectDraw here, please + wxRect sub_rect( *rect ); + sub_rect.x -= m_bufferX; + sub_rect.y -= m_bufferY; + + wxImage sub_image( m_buffer.GetSubImage( sub_rect ) ); #ifdef __WXGTK__ - int bpp = wxDisplayDepth(); + int bpp = wxDisplayDepth(); if (bpp > 8) { // the init code is doubled in wxImage @@ -622,14 +1051,10 @@ void wxCanvas::BlitBuffer( wxDC &dc ) gdk_rgb_init(); s_hasInitialized = TRUE; } - - int x = rect->x; - int y = rect->y; - CalcScrolledPosition( x, y, &x, &y ); gdk_draw_rgb_image( GTK_PIZZA(m_wxwindow)->bin_window, m_wxwindow->style->black_gc, - x, y, + sub_rect.x, sub_rect.y, sub_image.GetWidth(), sub_image.GetHeight(), GDK_RGB_DITHER_NONE, sub_image.GetData(), @@ -646,51 +1071,81 @@ void wxCanvas::BlitBuffer( wxDC &dc ) wxBitmap bitmap( sub_image.ConvertToBitmap() ); dc.DrawBitmap( bitmap, rect->x, rect->y ); #endif - + delete rect; m_updateRects.DeleteNode( node ); node = m_updateRects.First(); } - + m_needUpdate = FALSE; } void wxCanvas::UpdateNow() { + if (m_frozen) return; + if (!m_needUpdate) return; - + wxClientDC dc( this ); PrepareDC( dc ); - + BlitBuffer( dc ); } +int wxCanvas::GetDeviceX( double x ) +{ + return (int) x; +} + +int wxCanvas::GetDeviceY( double y ) +{ + return (int) y; +} + +int wxCanvas::GetDeviceWidth( double width ) +{ + return (int) width; +} + +int wxCanvas::GetDeviceHeight( double height ) +{ + return (int) height; +} + +void wxCanvas::Recreate() +{ + m_root->Recreate(); +} + void wxCanvas::Prepend( wxCanvasObject* obj ) { - m_objects.Insert( obj ); - - obj->SetOwner( this ); - + m_root->Prepend( obj ); + obj->SetOwner(this); + + m_root->Recreate(); + if (!obj->IsControl()) Update( obj->GetX(), obj->GetY(), obj->GetWidth(), obj->GetHeight() ); } void wxCanvas::Append( wxCanvasObject* obj ) { - m_objects.Append( obj ); - - obj->SetOwner( this ); - + m_root->Append( obj ); + obj->SetOwner(this); + + m_root->Recreate(); + if (!obj->IsControl()) Update( obj->GetX(), obj->GetY(), obj->GetWidth(), obj->GetHeight() ); } void wxCanvas::Insert( size_t before, wxCanvasObject* obj ) { - m_objects.Insert( before, obj ); - - obj->SetOwner( this ); - + m_root->Insert( before, obj ); + obj->SetOwner(this); + + m_root->Recreate(); + if (!obj->IsControl()) Update( obj->GetX(), obj->GetY(), obj->GetWidth(), obj->GetHeight() ); } @@ -702,9 +1157,9 @@ void wxCanvas::Remove( wxCanvasObject* obj ) int w = obj->GetWidth(); int h = obj->GetHeight(); bool ic = obj->IsControl(); - - m_objects.DeleteObject( obj ); - + + m_root->Remove( obj ); + if (!ic) Update( x, y, w, h ); } @@ -713,7 +1168,11 @@ void wxCanvas::OnPaint(wxPaintEvent &event) { wxPaintDC dc(this); PrepareDC( dc ); - + + if (!m_buffer.Ok()) return; + + if (m_frozen) return; + m_needUpdate = TRUE; wxRegionIterator it( GetUpdateRegion() ); @@ -721,86 +1180,173 @@ void wxCanvas::OnPaint(wxPaintEvent &event) { int x = it.GetX(); int y = it.GetY(); - CalcUnscrolledPosition( x, y, &x, &y ); - + int w = it.GetWidth(); int h = it.GetHeight(); - + if (x+w > m_buffer.GetWidth()) w = m_buffer.GetWidth() - x; if (y+h > m_buffer.GetHeight()) h = m_buffer.GetHeight() - y; - + if ((w > 0) && (h > 0)) + { + CalcUnscrolledPosition( x, y, &x, &y ); m_updateRects.Append( (wxObject*) new wxRect( x, y, w, h ) ); - + } + it++; } - + BlitBuffer( dc ); } -void wxCanvas::OnMouse(wxMouseEvent &event) +void wxCanvas::ScrollWindow( int dx, int dy, const wxRect* rect ) { - // should we implement mouse capture ? + // If any updates are pending, do them now since they will + // expect the previous m_bufferX and m_bufferY values. + UpdateNow(); + // The buffer always starts at the top left corner of the + // client area. Indeed, it is the client area. + CalcUnscrolledPosition( 0, 0, &m_bufferX, &m_bufferY ); + + unsigned char* data = m_buffer.GetData(); + + if (dy != 0) + { + if (dy > 0) + { + unsigned char *source = data; + unsigned char *dest = data + (dy * m_buffer.GetWidth() * 3); + size_t count = (size_t) (m_buffer.GetWidth() * 3 * (m_buffer.GetHeight()-dy)); + memmove( dest, source, count ); + + // We update the new buffer area, but there is no need to + // blit (last param FALSE) since the ensuing paint event will + // do that anyway. + Update( m_bufferX, m_bufferY, m_buffer.GetWidth(), dy, FALSE ); + } + else + { + unsigned char *dest = data; + unsigned char *source = data + (-dy * m_buffer.GetWidth() * 3); + size_t count = (size_t) (m_buffer.GetWidth() * 3 * (m_buffer.GetHeight()+dy)); + memmove( dest, source, count ); + + // We update the new buffer area, but there is no need to + // blit (last param FALSE) since the ensuing paint event will + // do that anyway. + Update( m_bufferX, m_bufferY+m_buffer.GetHeight()+dy, m_buffer.GetWidth(), -dy, FALSE ); + } + } + + if (dx != 0) + { + if (dx > 0) + { + unsigned char *source = data; + for (int y = 0; y < m_buffer.GetHeight(); y++) + { + unsigned char *dest = source + dx*3; + memmove( dest, source, (m_buffer.GetWidth()-dx) * 3 ); + source += m_buffer.GetWidth()*3; + } + + // We update the new buffer area, but there is no need to + // blit (last param FALSE) since the ensuing paint event will + // do that anyway. + Update( m_bufferX, m_bufferY, dx, m_buffer.GetHeight(), FALSE ); + } + else + { + unsigned char *dest = data; + for (int y = 0; y < m_buffer.GetHeight(); y++) + { + unsigned char *source = dest - dx*3; + memmove( dest, source, (m_buffer.GetWidth()+dx) * 3 ); + dest += m_buffer.GetWidth()*3; + } + + // We update the new buffer area, but there is no need to + // blit (last param FALSE) since the ensuing paint event will + // do that anyway. + Update( m_bufferX+m_buffer.GetWidth()+dx, m_bufferY, -dx, m_buffer.GetHeight(), FALSE ); + } + } + + wxWindow::ScrollWindow( dx, dy, rect ); +} + +void wxCanvas::OnMouse(wxMouseEvent &event) +{ int x = event.GetX(); int y = event.GetY(); CalcUnscrolledPosition( x, y, &x, &y ); - + if (event.GetEventType() == wxEVT_MOTION) { - wxNode *node = m_objects.First(); - while (node) + if (m_captureMouse) //no matter what go to this one { - wxCanvasObject *obj = (wxCanvasObject*) node->Data(); - - if (!obj->IsControl()) - { - if (obj->IsHit(x,y)) + wxMouseEvent child_event( wxEVT_MOTION ); + child_event.SetEventObject(m_captureMouse); + child_event.m_x = x - m_captureMouse->GetX(); + child_event.m_y = y - m_captureMouse->GetY(); + child_event.m_leftDown = event.m_leftDown; + child_event.m_rightDown = event.m_rightDown; + child_event.m_middleDown = event.m_middleDown; + child_event.m_controlDown = event.m_controlDown; + child_event.m_shiftDown = event.m_shiftDown; + child_event.m_altDown = event.m_altDown; + child_event.m_metaDown = event.m_metaDown; + m_captureMouse->ProcessEvent( child_event ); + } + else + { + wxCanvasObject *obj = m_root->IsHitObject(x,y,0); + + if (obj && !obj->IsControl()) + { + wxMouseEvent child_event( wxEVT_MOTION ); + child_event.SetEventObject( obj ); + child_event.m_x = x - obj->GetX(); + child_event.m_y = y - obj->GetY(); + child_event.m_leftDown = event.m_leftDown; + child_event.m_rightDown = event.m_rightDown; + child_event.m_middleDown = event.m_middleDown; + child_event.m_controlDown = event.m_controlDown; + child_event.m_shiftDown = event.m_shiftDown; + child_event.m_altDown = event.m_altDown; + child_event.m_metaDown = event.m_metaDown; + + if ((obj != m_lastMouse) && (m_lastMouse != NULL)) { - wxMouseEvent child_event( wxEVT_MOTION ); + child_event.SetEventType( wxEVT_LEAVE_WINDOW ); + child_event.SetEventObject( m_lastMouse ); + child_event.m_x = x - m_lastMouse->GetX(); + child_event.m_y = y - m_lastMouse->GetY(); + m_lastMouse->ProcessEvent( child_event ); + + m_lastMouse = obj; + child_event.SetEventType( wxEVT_ENTER_WINDOW ); + child_event.SetEventObject( m_lastMouse ); + child_event.m_x = x - m_lastMouse->GetX(); + child_event.m_y = y - m_lastMouse->GetY(); + m_lastMouse->ProcessEvent( child_event ); + + child_event.SetEventType( wxEVT_MOTION ); child_event.SetEventObject( obj ); - child_event.m_x = x + obj->GetX(); - child_event.m_y = y + obj->GetY(); - child_event.m_leftDown = event.m_leftDown; - child_event.m_rightDown = event.m_rightDown; - child_event.m_middleDown = event.m_middleDown; - child_event.m_controlDown = event.m_controlDown; - child_event.m_shiftDown = event.m_shiftDown; - child_event.m_altDown = event.m_altDown; - child_event.m_metaDown = event.m_metaDown; - - if ((obj != m_lastMouse) && (m_lastMouse != NULL)) - { - child_event.SetEventType( wxEVT_LEAVE_WINDOW ); - child_event.SetEventObject( m_lastMouse ); - child_event.m_x = x + m_lastMouse->GetX(); - child_event.m_y = y + m_lastMouse->GetY(); - m_lastMouse->ProcessEvent( child_event ); - - m_lastMouse = obj; - child_event.SetEventType( wxEVT_ENTER_WINDOW ); - child_event.SetEventObject( m_lastMouse ); - child_event.m_x = x + m_lastMouse->GetX(); - child_event.m_y = y + m_lastMouse->GetY(); - m_lastMouse->ProcessEvent( child_event ); - - child_event.SetEventType( wxEVT_MOTION ); - child_event.SetEventObject( obj ); - } - obj->ProcessEvent( child_event ); - return; } + obj->ProcessEvent( child_event ); + return; } - node = node->Next(); } if (m_lastMouse) { wxMouseEvent child_event( wxEVT_LEAVE_WINDOW ); child_event.SetEventObject( m_lastMouse ); - child_event.m_x = x + m_lastMouse->GetX(); - child_event.m_y = y + m_lastMouse->GetY(); + child_event.m_x = x - m_lastMouse->GetX(); + child_event.m_y = y - m_lastMouse->GetY(); child_event.m_leftDown = event.m_leftDown; child_event.m_rightDown = event.m_rightDown; child_event.m_middleDown = event.m_middleDown; @@ -809,16 +1355,36 @@ void wxCanvas::OnMouse(wxMouseEvent &event) child_event.m_altDown = event.m_altDown; child_event.m_metaDown = event.m_metaDown; m_lastMouse->ProcessEvent( child_event ); - + m_lastMouse = (wxCanvasObject*) NULL; return; } } + event.Skip(); } void wxCanvas::OnSize(wxSizeEvent &event) { + int w,h; + GetClientSize( &w, &h ); + m_buffer = wxImage( w, h ); + + CalcUnscrolledPosition( 0, 0, &m_bufferX, &m_bufferY ); + + wxNode *node = m_updateRects.First(); + while (node) + { + wxRect *rect = (wxRect*) node->Data(); + delete rect; + m_updateRects.DeleteNode( node ); + node = m_updateRects.First(); + } + + m_frozen = FALSE; + + Update( m_bufferX, m_bufferY, m_buffer.GetWidth(), m_buffer.GetHeight(), FALSE ); + event.Skip(); } @@ -841,6 +1407,10 @@ void wxCanvas::OnChar(wxKeyEvent &event) event.Skip(); } +void wxCanvas::OnEraseBackground(wxEraseEvent &event) +{ +} + //-------------------------------------------------------------------- // wxCanvasModule //-------------------------------------------------------------------- @@ -859,7 +1429,7 @@ IMPLEMENT_DYNAMIC_CLASS(wxCanvasModule, wxModule) bool wxCanvasModule::OnInit() { -#if USE_FREETYPE +#if wxUSE_FREETYPE int error = FT_Init_FreeType( &g_freetypeLibrary ); if (error) return FALSE; #endif @@ -869,7 +1439,7 @@ bool wxCanvasModule::OnInit() void wxCanvasModule::OnExit() { -#if USE_FREETYPE +#if wxUSE_FREETYPE FT_Done_FreeType( g_freetypeLibrary ); #endif }