From 31778480a93d19f2053c1162303d8b5123bb470d Mon Sep 17 00:00:00 2001 From: Julian Smart Date: Mon, 21 Apr 2008 15:26:27 +0000 Subject: [PATCH] Speeded up wrapping (again), this time using partial text extents. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@53285 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/richtext/richtextbuffer.h | 14 +- src/richtext/richtextbuffer.cpp | 183 +++++++++++++++++++++------ 2 files changed, 149 insertions(+), 48 deletions(-) diff --git a/include/wx/richtext/richtextbuffer.h b/include/wx/richtext/richtextbuffer.h index ecf8444c38..d8b577b8d1 100644 --- a/include/wx/richtext/richtextbuffer.h +++ b/include/wx/richtext/richtextbuffer.h @@ -338,7 +338,7 @@ public: /// Get the object size for the given range. Returns false if the range /// is invalid for this object. - virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0)) const = 0; + virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0), wxArrayInt* partialExtents = NULL) const = 0; /// Do a split, returning an object containing the second part, and setting /// the first part in 'this'. @@ -565,7 +565,7 @@ public: /// Get/set the object size for the given range. Returns false if the range /// is invalid for this object. - virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0)) const; + virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0), wxArrayInt* partialExtents = NULL) const; // Accessors @@ -604,7 +604,7 @@ public: /// Get/set the object size for the given range. Returns false if the range /// is invalid for this object. - virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0)) const; + virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0), wxArrayInt* partialExtents = NULL) const; /// Delete range virtual bool DeleteRange(const wxRichTextRange& range); @@ -920,7 +920,7 @@ public: /// Get/set the object size for the given range. Returns false if the range /// is invalid for this object. - virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0)) const; + virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0), wxArrayInt* partialExtents = NULL) const; /// Finds the absolute position and row height for the given character position virtual bool FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart); @@ -972,7 +972,7 @@ public: /// Find a suitable wrap position. wrapPosition is the last position in the line to the left /// of the split. - bool FindWrapPosition(const wxRichTextRange& range, wxDC& dc, int availableSpace, long& wrapPosition); + bool FindWrapPosition(const wxRichTextRange& range, wxDC& dc, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents); /// Find the object at the given position wxRichTextObject* FindObjectAtPosition(long position); @@ -1037,7 +1037,7 @@ public: /// Get/set the object size for the given range. Returns false if the range /// is invalid for this object. - virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position/* = wxPoint(0,0)*/) const; + virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0), wxArrayInt* partialExtents = NULL) const; /// Get any text in this object for the given range virtual wxString GetTextForRange(const wxRichTextRange& range) const; @@ -1199,7 +1199,7 @@ public: /// Get the object size for the given range. Returns false if the range /// is invalid for this object. - virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0)) const; + virtual bool GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position = wxPoint(0,0), wxArrayInt* partialExtents = NULL) const; /// Returns true if the object is empty virtual bool IsEmpty() const { return !m_image.Ok(); } diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp index 06ba477452..be0a26852c 100644 --- a/src/richtext/richtextbuffer.cpp +++ b/src/richtext/richtextbuffer.cpp @@ -48,6 +48,9 @@ WX_DEFINE_LIST(wxRichTextLineList) // Switch off if the platform doesn't like it for some reason #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1 +// Use GetPartialTextExtents for platforms that support it natively +#define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1 + const wxChar wxRichTextLineBreakChar = (wxChar) 29; // Helpers for efficiency @@ -510,13 +513,13 @@ bool wxRichTextBox::Layout(wxDC& dc, const wxRect& rect, int style) } /// Get/set the size for the given range. Assume only has one child. -bool wxRichTextBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position) const +bool wxRichTextBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* partialExtents) const { wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst(); if (node) { wxRichTextObject* child = node->GetData(); - return child->GetRangeSize(range, size, descent, dc, flags, position); + return child->GetRangeSize(range, size, descent, dc, flags, position, partialExtents); } else return false; @@ -741,7 +744,7 @@ void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj) } /// Get/set the size for the given range. -bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position) const +bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* WXUNUSED(partialExtents)) const { wxSize sz; @@ -3284,6 +3287,16 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, int style) node = node->GetNext(); } +#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS + wxArrayInt partialExtents; + + wxSize paraSize; + int paraDescent; + + // This calculates the partial text extents + GetRangeSize(GetRange(), paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED, wxPoint(0,0), & partialExtents); +#endif + // Split up lines // We may need to go back to a previous child, in which case create the new line, @@ -3341,7 +3354,7 @@ bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, int style) // Find a place to wrap. This may walk back to previous children, // for example if a word spans several objects. - if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, availableSpaceForText, wrapPosition)) + if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, availableSpaceForText, wrapPosition, & partialExtents)) { // If the function failed, just cut it off at the end of this child. wrapPosition = child->GetRange().GetEnd(); @@ -3568,7 +3581,7 @@ void wxRichTextParagraph::ClearLines() /// Get/set the object size for the given range. Returns false if the range /// is invalid for this object. -bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position) const +bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* partialExtents) const { if (!range.IsWithin(GetRange())) return false; @@ -3580,9 +3593,17 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz wxSize sz; + wxArrayInt childExtents; + wxArrayInt* p; + if (partialExtents) + p = & childExtents; + else + p = NULL; + wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst(); while (node) { + wxRichTextObject* child = node->GetData(); if (!child->GetRange().IsOutside(range)) { @@ -3592,12 +3613,30 @@ bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& siz rangeToUse.LimitTo(child->GetRange()); int childDescent = 0; - if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags, wxPoint(position.x + sz.x, position.y))) + if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags, wxPoint(position.x + sz.x, position.y), p)) { sz.y = wxMax(sz.y, childSize.y); sz.x += childSize.x; descent = wxMax(descent, childDescent); + + if (partialExtents) + { + int lastSize; + if (partialExtents->GetCount() > 0) + lastSize = (*partialExtents)[partialExtents->GetCount()-1]; + else + lastSize = 0; + + size_t i; + for (i = 0; i < childExtents.GetCount(); i++) + { + partialExtents->Add(childExtents[i] + lastSize); + } + } } + + if (p) + p->Clear(); } node = node->GetNext(); @@ -3981,55 +4020,81 @@ bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTex } /// Find a suitable wrap position. -bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, int availableSpace, long& wrapPosition) +bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents) { // Find the first position where the line exceeds the available space. wxSize sz; long breakPosition = range.GetEnd(); - // Binary chop for speed - long minPos = range.GetStart(); - long maxPos = range.GetEnd(); - while (true) +#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS + if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline { - if (minPos == maxPos) - { - int descent = 0; - GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED); + int widthBefore; - if (sz.x > availableSpace) - breakPosition = minPos - 1; - break; - } - else if ((maxPos - minPos) == 1) + if (range.GetStart() > GetRange().GetStart()) + widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1]; + else + widthBefore = 0; + + size_t i; + for (i = (size_t) range.GetStart(); i < (size_t) range.GetEnd(); i++) { - int descent = 0; - GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED); + int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore; - if (sz.x > availableSpace) - breakPosition = minPos - 1; - else + if (widthFromStartOfThisRange >= availableSpace) { - GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED); - if (sz.x > availableSpace) - breakPosition = maxPos-1; + breakPosition = i-1; + break; } - break; } - else + } + else +#endif + { + // Binary chop for speed + long minPos = range.GetStart(); + long maxPos = range.GetEnd(); + while (true) { - long nextPos = minPos + ((maxPos - minPos) / 2); - - int descent = 0; - GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED); + if (minPos == maxPos) + { + int descent = 0; + GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED); - if (sz.x > availableSpace) + if (sz.x > availableSpace) + breakPosition = minPos - 1; + break; + } + else if ((maxPos - minPos) == 1) { - maxPos = nextPos; + int descent = 0; + GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED); + + if (sz.x > availableSpace) + breakPosition = minPos - 1; + else + { + GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED); + if (sz.x > availableSpace) + breakPosition = maxPos-1; + } + break; } else { - minPos = nextPos; + long nextPos = minPos + ((maxPos - minPos) / 2); + + int descent = 0; + GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED); + + if (sz.x > availableSpace) + { + maxPos = nextPos; + } + else + { + minPos = nextPos; + } } } } @@ -4595,7 +4660,7 @@ void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj) /// Get/set the object size for the given range. Returns false if the range /// is invalid for this object. -bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int WXUNUSED(flags), wxPoint position) const +bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int WXUNUSED(flags), wxPoint position, wxArrayInt* partialExtents) const { if (!range.IsWithin(GetRange())) return false; @@ -4668,10 +4733,21 @@ bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& siz // break up the string at the Tab wxString stringFragment = stringChunk.BeforeFirst(wxT('\t')); stringChunk = stringChunk.AfterFirst(wxT('\t')); + int oldWidth = width; dc.GetTextExtent(stringFragment, & w, & h); width += w; int absoluteWidth = width + position.x; + if (partialExtents) + { + // Add these partial extents + wxArrayInt p; + dc.GetPartialTextExtents(stringFragment, p); + size_t j; + for (j = 0; j < p.GetCount(); j++) + partialExtents->Add(oldWidth + p[j]); + } + bool notFound = true; for (int i = 0; i < tabCount && notFound; ++i) { @@ -4690,13 +4766,30 @@ bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& siz notFound = false; width = nextTabPos - position.x; + + if (partialExtents) + partialExtents->Add(width); } } } } - dc.GetTextExtent(stringChunk, & w, & h, & descent); - width += w; + if (!stringChunk.IsEmpty()) + { + dc.GetTextExtent(stringChunk, & w, & h, & descent); + int oldWidth = width; + width += w; + + if (partialExtents) + { + // Add these partial extents + wxArrayInt p; + dc.GetPartialTextExtents(stringChunk, p); + size_t j; + for (j = 0; j < p.GetCount(); j++) + partialExtents->Add(oldWidth + p[j]); + } + } if ( bScript ) dc.SetFont(font); @@ -6694,11 +6787,19 @@ bool wxRichTextImage::Layout(wxDC& WXUNUSED(dc), const wxRect& rect, int WXUNUSE /// Get/set the object size for the given range. Returns false if the range /// is invalid for this object. -bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& WXUNUSED(dc), int WXUNUSED(flags), wxPoint WXUNUSED(position)) const +bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& WXUNUSED(dc), int WXUNUSED(flags), wxPoint WXUNUSED(position), wxArrayInt* partialExtents) const { if (!range.IsWithin(GetRange())) return false; + if (partialExtents) + { + if (m_image.Ok()) + partialExtents->Add(m_image.GetWidth()); + else + partialExtents->Add(0); + } + if (!m_image.Ok()) return false; -- 2.45.2