From 8df3bfccc95fa605e063199e5c8f52165a279c78 Mon Sep 17 00:00:00 2001 From: cxl Date: Sun, 23 Aug 2009 17:27:01 +0000 Subject: [PATCH] RichText: Optimized by caching paragraph data and layout git-svn-id: svn://ultimatepp.org/upp/trunk@1524 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- uppsrc/RichText/Para.h | 25 ++++++++++++-- uppsrc/RichText/ParaData.cpp | 61 ++++++++++++++++++++++++++++++-- uppsrc/RichText/ParaPaint.cpp | 2 +- uppsrc/RichText/ParaType.cpp | 65 +++++++++++++++++++++++++++++++---- uppsrc/RichText/RichText.h | 2 -- uppsrc/RichText/TextData.cpp | 2 +- uppsrc/RichText/TextPaint.cpp | 2 +- uppsrc/RichText/TextStyle.cpp | 6 ++-- uppsrc/RichText/Txt.h | 20 ++++++----- uppsrc/RichText/TxtData.cpp | 47 +++++++++++++++++++------ uppsrc/RichText/TxtPaint.cpp | 30 +++++++++++----- 11 files changed, 216 insertions(+), 46 deletions(-) diff --git a/uppsrc/RichText/Para.h b/uppsrc/RichText/Para.h index 8bb587a9e..a11d415ad 100644 --- a/uppsrc/RichText/Para.h +++ b/uppsrc/RichText/Para.h @@ -199,14 +199,27 @@ struct RichPara { Line& operator[](int i) { return line[i]; } const Line& operator[](int i) const { return line[i]; } void Justify(const Format& format); - int BodyHeight(); + int BodyHeight() const; + + Lines(); + ~Lines(); + + private: + static Array& Cache(); + bool incache; + bool justified; + int64 cacheid; + + friend struct RichPara; }; static const VectorMap& fieldtype(); static void Register(Id id, FieldType& ft) init_; - Format format; + int64 cacheid; + bool incache; Array part; + Format format; static void Charformat(Stream& out, const CharFormat& o, const CharFormat& n, const CharFormat& s); @@ -258,6 +271,11 @@ struct RichPara { #endif void ApplyZoom(Zoom z); + + void CacheId(int64 id); + + RichPara(); + ~RichPara(); private: Tab GetNextTab(int pos) const; @@ -273,6 +291,9 @@ private: int *wd, int pos, int len, int x0, int x, int y0, int y, int linecy, int lineascent, Zoom z, bool highlight); int PosInLine(int x, const Rect& page, const Lines& pl, int lni) const; + + static Array& Cache(); + struct StorePart; }; diff --git a/uppsrc/RichText/ParaData.cpp b/uppsrc/RichText/ParaData.cpp index b64076f61..8cc553c2b 100644 --- a/uppsrc/RichText/ParaData.cpp +++ b/uppsrc/RichText/ParaData.cpp @@ -2,6 +2,46 @@ NAMESPACE_UPP +static StaticMutex sCacheMutex; + +Array& RichPara::Cache() +{ + static Array x; + return x; +} + +void RichPara::CacheId(int64 id) +{ + ASSERT(cacheid == 0); + ASSERT(part.GetCount() == 0); + cacheid = id; +} + +RichPara::RichPara() +{ + cacheid = 0; + incache = false; +} + +RichPara::~RichPara() +{ + if(cacheid && !part.IsPicked() && !incache) { + Mutex::Lock __(sCacheMutex); + Array& cache = Cache(); + incache = true; + cache.InsertPick(0, *this); + int total = 0; + for(int i = 1; i < cache.GetCount(); i++) { + total += cache[i].GetLength(); + if(total > 10000 || i > 64) { + cache.SetCount(i); + break; + } + i++; + } + } +} + PaintInfo::PaintInfo() { sell = selh = 0; @@ -211,6 +251,7 @@ void RichPara::Charformat(Stream& out, const RichPara::CharFormat& o, void RichPara::Cat(const WString& s, const RichPara::CharFormat& f) { + cacheid = 0; part.Add(); part.Top().text = s; part.Top().format = f; @@ -223,6 +264,7 @@ void RichPara::Cat(const char *s, const CharFormat& f) void RichPara::Cat(const RichObject& o, const RichPara::CharFormat& f) { + cacheid = 0; part.Add(); part.Top().object = o; part.Top().format = f; @@ -230,6 +272,7 @@ void RichPara::Cat(const RichObject& o, const RichPara::CharFormat& f) void RichPara::Cat(Id field, const String& param, const RichPara::CharFormat& f) { + cacheid = 0; Part& p = part.Add(); p.field = field; p.fieldparam = param; @@ -515,10 +558,21 @@ void RichPara::Unpack(const String& data, const Array& obj, const RichPara::Format& style) { part.Clear(); - StringStream in(data); - format = style; + + if(cacheid) { + Mutex::Lock __(sCacheMutex); + Array& cache = Cache(); + for(int i = 0; i < cache.GetCount(); i++) + if(cache[i].cacheid == cacheid) { + *this = cache[i]; + incache = false; + cache.Remove(i); + return; + } + } + StringStream in(data); dword pattr = in.Get32(); if(pattr & 1) format.align = in.Get16(); @@ -646,6 +700,7 @@ void RichPara::Trim(int pos) } else part.SetCount(i); + cacheid = 0; } void RichPara::Mid(int pos) @@ -656,6 +711,7 @@ void RichPara::Mid(int pos) ASSERT(part[0].IsText()); part[0].text = part[0].text.Mid(pos); } + cacheid = 0; } void ApplyCharStyle(RichPara::CharFormat& format, const RichPara::CharFormat& f0, @@ -695,6 +751,7 @@ void RichPara::ApplyStyle(const Format& newstyle) ApplyCharStyle(h, f0, newstyle); format = newstyle; (CharFormat&)format = h; + cacheid = 0; } #ifdef _DEBUG diff --git a/uppsrc/RichText/ParaPaint.cpp b/uppsrc/RichText/ParaPaint.cpp index ccef3dde7..ac315e35a 100644 --- a/uppsrc/RichText/ParaPaint.cpp +++ b/uppsrc/RichText/ParaPaint.cpp @@ -178,7 +178,7 @@ void RichPara::Paint(PageDraw& pw, const Rect& page, PageY py, const PaintInfo& int y0; int lineascent = 0; for(int lni = 0; lni < pl.GetCount(); lni++) { - Line& li = pl[lni]; + const Line& li = pl[lni]; int linecy = li.Sum(); lineascent = li.ascent; if(BreaksPage(py, pl, lni, page)) { diff --git a/uppsrc/RichText/ParaType.cpp b/uppsrc/RichText/ParaType.cpp index 37a1dfa5d..3676319a8 100644 --- a/uppsrc/RichText/ParaType.cpp +++ b/uppsrc/RichText/ParaType.cpp @@ -78,6 +78,8 @@ struct RichPara::StorePart { void RichPara::StorePart::Store(Lines& lines, const Part& part, int pinc) { + CharFormat& pfmt = lines.hformat.Add(); + pfmt = part.format; if(part.field && pinc) { for(int i = 0; i < part.fieldpart.GetCount(); i++) Store(lines, part.fieldpart[i], 0); @@ -85,7 +87,7 @@ void RichPara::StorePart::Store(Lines& lines, const Part& part, int pinc) } else if(part.object) { - *f++ = &part.format; + *f++ = &pfmt; Size sz = part.object.GetSize(); *w++ = sz.cx; h->ydelta = part.object.GetYDelta(); @@ -123,7 +125,7 @@ void RichPara::StorePart::Store(Lines& lines, const Part& part, int pinc) while(s < lim) { wchar c = *s++; if(c == 9) { - *f++ = &part.format; + *f++ = &pfmt; h->ascent = pfi.GetAscent(); h->descent = pfi.GetDescent(); h->external = pfi.GetExternal(); @@ -139,7 +141,7 @@ void RichPara::StorePart::Store(Lines& lines, const Part& part, int pinc) *w++ = c >= 32 ? cwfi[c] : 0; } else { - *f++ = &part.format; + *f++ = &pfmt; h->ascent = fi.GetAscent(); h->descent = fi.GetDescent(); h->external = fi.GetExternal(); @@ -155,7 +157,7 @@ void RichPara::StorePart::Store(Lines& lines, const Part& part, int pinc) else { while(s < lim) { wchar c = *s++; - *f++ = &part.format; + *f++ = &pfmt; if(c == 9) { h->ascent = pfi.GetAscent(); h->descent = pfi.GetDescent(); @@ -190,10 +192,58 @@ static int CountChars(const Array& part) return n; } +RichPara::Lines::Lines() +{ + justified = false; + incache = false; + cacheid = 0; +} + +Array& RichPara::Lines::Cache() +{ + static Array x; + return x; +} + +static StaticMutex sLineCacheMutex; + +RichPara::Lines::~Lines() +{ + if(cacheid && !line.IsPicked() && !incache) { + Mutex::Lock __(sLineCacheMutex); + Array& cache = Cache(); + incache = true; + cache.Insert(0) = *this; + cache.SetCount(1); + int total = 0; + for(int i = 1; i < cache.GetCount(); i++) { + total += cache[i].clen; + if(total > 10000 || i > 64) { + cache.SetCount(i); + break; + } + i++; + } + } +} + RichPara::Lines RichPara::FormatLines(int acx) const { - int i; Lines lines; + if(cacheid) { + Mutex::Lock __(sLineCacheMutex); + Array& cache = Lines::Cache(); + for(int i = 0; i < cache.GetCount(); i++) + if(cache[i].cacheid == cacheid && cache[i].cx == acx) { + lines = cache[i]; + lines.incache = false; + cache.Remove(i); + return lines; + } + } + + int i; + lines.cacheid = cacheid; lines.cx = acx; lines.len = GetLength(); lines.clen = CountChars(part); @@ -324,6 +374,9 @@ RichPara::Lines RichPara::FormatLines(int acx) const void RichPara::Lines::Justify(const RichPara::Format& format) { + if(justified) + return; + justified = true; if(format.align != ALIGN_JUSTIFY) return; for(int i = 0; i < line.GetCount() - 1; i++) { const Line& li = line[i]; @@ -365,7 +418,7 @@ void RichPara::Lines::Justify(const RichPara::Format& format) } } -int RichPara::Lines::BodyHeight() +int RichPara::Lines::BodyHeight() const { int sum = 0; for(int i = 0; i < line.GetCount(); i++) diff --git a/uppsrc/RichText/RichText.h b/uppsrc/RichText/RichText.h index cafc972f5..177fa572e 100644 --- a/uppsrc/RichText/RichText.h +++ b/uppsrc/RichText/RichText.h @@ -232,8 +232,6 @@ RichObject CreatePNGObject(const Image& img, Size dot_size, Size size); RichObject CreatePNGObject(const Image& img, int cx, int cy); RichObject CreateRawImageObject(const String& s, int cx = 0, int cy = 0); -struct RichPara; - enum { RICHHOT_LM = -1, RICHHOT_RM = -2, diff --git a/uppsrc/RichText/TextData.cpp b/uppsrc/RichText/TextData.cpp index 1d08f03f5..2ca0a2e36 100644 --- a/uppsrc/RichText/TextData.cpp +++ b/uppsrc/RichText/TextData.cpp @@ -9,7 +9,7 @@ void RichText::CatPick(pick_ RichText& p) part.AppendPick(p.part); for(int i = c; i < part.GetCount(); i++) if(IsPara(i)) - part[i].Get().cx = -1; + part[i].Get().Invalidate(); RefreshAll(); } diff --git a/uppsrc/RichText/TextPaint.cpp b/uppsrc/RichText/TextPaint.cpp index 2a8e7c66d..640cdd25c 100644 --- a/uppsrc/RichText/TextPaint.cpp +++ b/uppsrc/RichText/TextPaint.cpp @@ -128,7 +128,7 @@ bool RichText::GetInvalid(PageY& top, PageY& bottom, const Rect& page, else { Sync(r_parti, rc); const Para& pp = part[r_parti].Get(); - if(r_paraocx == pp.cx && + if(r_paraocx == pp.ccx && r_paraocy == Sum(pp.linecy, 0) + pp.ruler + pp.before + pp.after && r_keep == pp.keep && r_keepnext == pp.keepnext && diff --git a/uppsrc/RichText/TextStyle.cpp b/uppsrc/RichText/TextStyle.cpp index 7da33e6d1..75d2054d0 100644 --- a/uppsrc/RichText/TextStyle.cpp +++ b/uppsrc/RichText/TextStyle.cpp @@ -36,7 +36,7 @@ struct RichText::StyleChangeOp : RichTxt::ParaOp { virtual bool operator()(RichTxt::Para& p) { if(p.styleid == id) { p.styleid = nid; - p.cx = -1; + p.Invalidate(); return true; } return false; @@ -60,7 +60,7 @@ struct RichText::SetStylesOp : RichTxt::ParaOp { RichStyles *style; virtual bool operator()(RichTxt::Para& p) { - p.cx = -1; + p.Invalidate(); if(style->Find(p.styleid) < 0) p.styleid = RichStyle::GetDefaultId(); return true; @@ -121,7 +121,7 @@ struct RichText::OverrideStylesOp : RichTxt::ParaOp { virtual bool operator()(RichTxt::Para& p) { p.styleid = cs.Get(p.styleid, RichStyle::GetDefaultId()); - p.cx = -1; + p.Invalidate(); return true; } }; diff --git a/uppsrc/RichText/Txt.h b/uppsrc/RichText/Txt.h index e1abd3c37..3be2ce5e3 100644 --- a/uppsrc/RichText/Txt.h +++ b/uppsrc/RichText/Txt.h @@ -67,7 +67,9 @@ protected: int length; String content; Array object; - mutable int cx; + LazyUpdate dirty; + int64 updateserial; + mutable int ccx; mutable int cy; mutable int ruler; mutable int before; @@ -83,8 +85,10 @@ protected: mutable bool haspos; One number; + void Invalidate(); + Para(const Para& src, int); - Para() { length = 0; cx = -1; numbering = -1; checked = false; haspos = false; } + Para() { length = 0; Invalidate(); numbering = -1; checked = false; haspos = false; } }; struct Part : MoveableAndDeepCopyOption< Part, Any> { @@ -126,13 +130,7 @@ protected: void Put(int i, const RichPara& p, const RichStyles& s); void Sync0(const Para& pp, int parti, const RichContext& rc) const; - inline void Sync(int parti, const RichContext& rc) const { - int cx = rc.page.Width(); - ASSERT(part[parti].Is()); - const Para& pp = part[parti].Get(); - if(pp.cx == cx) return; - Sync0(pp, parti, rc); - } + void Sync(int parti, const RichContext& rc) const; bool BreaksPage(PageY py, const Para& pp, int i, const Rect& page) const; PageY GetNextPageY(int parti, const RichContext& rc) const; PageY GetPartPageY(int parti, RichContext rc) const; @@ -149,6 +147,10 @@ protected: RichTable& GetUpdateTable(int table); RichTxt& GetTableUpdateText(int table, const RichStyles& style, int& pi); + RichPara Get(int parti, const RichStyles& s, bool usecache) const; + RichPara Get(int parai, const Uuid& styleid, const RichStyles& s, bool usecache) const; + RichPara Get(int parai, const RichStyle& style, bool usecache) const; + void CombineFormat(FormatInfo& f, int pi, int pi2, bool& first, const RichStyles& style) const; static void ApplyStyle(const FormatInfo& fi, RichPara& pa, const RichStyles& style); static void Apply(const FormatInfo& fi, RichPara& pa, const RichStyles& style); diff --git a/uppsrc/RichText/TxtData.cpp b/uppsrc/RichText/TxtData.cpp index 353d439a9..aaf279c83 100644 --- a/uppsrc/RichText/TxtData.cpp +++ b/uppsrc/RichText/TxtData.cpp @@ -2,6 +2,16 @@ NAMESPACE_UPP +void RichTxt::Para::Invalidate() +{ + INTERLOCKED { + static int64 ss; + dirty.Invalidate(); + updateserial = ++ss; + ccx = -1; + } +} + RichTxt::Para::Para(const Para& src, int) : object(src.object, 1) { @@ -11,7 +21,7 @@ RichTxt::Para::Para(const Para& src, int) haspos = src.haspos; if(src.number) number = new RichPara::NumberFormat(*src.number); - cx = -1; + Invalidate(); checked = false; } @@ -71,8 +81,8 @@ void RichTxt::SetRefresh(int parti) r_type = PARA; if(IsPara(parti)) { Para& pp = part[parti].Get(); - if(pp.cx >= 0) { - r_paraocx = pp.cx; + if(pp.ccx >= 0) { + r_paraocx = pp.ccx; r_paraocy = Sum(pp.linecy, 0) + pp.before + pp.after; r_keep = pp.keep; r_keepnext = pp.keepnext; @@ -113,7 +123,7 @@ void RichTxt::Put(int i, const RichPara& p, const RichStyle& s) SetRefresh(i); pp.number.Clear(); pp.content = p.Pack(s.format, pp.object); - pp.cx = -1; + pp.Invalidate(); pp.checked = false; pp.styleid = p.format.styleid; pp.length = p.GetLength(); @@ -157,25 +167,42 @@ void RichTxt::SetPick(int i, pick_ RichTable& p) SetRefresh(i); } -RichPara RichTxt::Get(int parai, const RichStyle& style) const +RichPara RichTxt::Get(int parai, const RichStyle& style, bool usecache) const { ASSERT(part[parai].Is()); const Para& pp = part[parai].Get(); RichPara p; + if(usecache) + p.CacheId(pp.updateserial); p.Unpack(pp.content, pp.object, style.format); return p; } -RichPara RichTxt::Get(int parai, const Uuid& styleid, const RichStyles& s) const +RichPara RichTxt::Get(int parai, const Uuid& styleid, const RichStyles& s, bool usecache) const { - RichPara p = Get(parai, GetStyle(s, styleid)); + RichPara p = Get(parai, GetStyle(s, styleid), usecache); p.format.styleid = styleid; return p; } -RichPara RichTxt::Get(int parti, const RichStyles& s) const +RichPara RichTxt::Get(int parti, const RichStyles& s, bool usecache) const { - return Get(parti, part[parti].Get().styleid, s); + return Get(parti, part[parti].Get().styleid, s, usecache); +} + +RichPara RichTxt::Get(int parai, const RichStyle& style) const +{ + return Get(parai, style, false); +} + +RichPara RichTxt::Get(int parai, const Uuid& styleid, const RichStyles& s) const +{ + return Get(parai, styleid, s, false); +} + +RichPara RichTxt::Get(int i, const RichStyles& s) const +{ + return Get(i, s, false); } bool RichTxt::IsEmpty() const @@ -257,7 +284,7 @@ void RichTxt::SetParaStyle(int i, const Uuid& id) ASSERT(IsPara(i)); Para& p = part[i].Get(); p.styleid = id; - p.cx = -1; + p.Invalidate(); SetRefreshFrom(i); } diff --git a/uppsrc/RichText/TxtPaint.cpp b/uppsrc/RichText/TxtPaint.cpp index d26821d8c..aa86c9bcf 100644 --- a/uppsrc/RichText/TxtPaint.cpp +++ b/uppsrc/RichText/TxtPaint.cpp @@ -7,7 +7,7 @@ int RichTxt::GetWidth(const RichStyles& st) const int cx = 0; for(int i = 0; i < part.GetCount(); i++) { if(IsPara(i)) { - RichPara p = Get(i, st); + RichPara p = Get(i, st, true); RichPara::Lines pl = p.FormatLines(INT_MAX); int ccx = 0; Sum(ccx, ~pl.width, ~pl.width + pl.clen); @@ -23,8 +23,8 @@ int RichTxt::GetWidth(const RichStyles& st) const void RichTxt::Sync0(const Para& pp, int parti, const RichContext& rc) const { int cx = rc.page.Width(); - pp.cx = cx; - RichPara p = Get(parti, rc.styles); + pp.ccx = cx; + RichPara p = Get(parti, rc.styles, true); RichPara::Lines pl = p.FormatLines(cx); pp.ruler = p.format.ruler; pp.before = p.format.before; @@ -40,6 +40,16 @@ void RichTxt::Sync0(const Para& pp, int parti, const RichContext& rc) const pp.orphan = p.format.orphan; } +inline void RichTxt::Sync(int parti, const RichContext& rc) const { + int cx = rc.page.Width(); + ASSERT(part[parti].Is()); + const Para& pp = part[parti].Get(); + if(pp.dirty.BeginUpdate()) { + Sync0(pp, parti, rc); + pp.dirty.EndUpdate(); + } +} + inline bool RichTxt::BreaksPage(PageY py, const Para& pp, int i, const Rect& page) const { @@ -152,7 +162,7 @@ void RichTxt::Paint(PageDraw& pw, RichContext rc, const PaintInfo& _pi) const nbefore = pp.before; nline = pp.linecy[0]; } - RichPara p = Get(parti, rc.styles); + RichPara p = Get(parti, rc.styles, true); if(pi.spellingchecker) { if(!pp.checked) { pp.spellerrors = (*pi.spellingchecker)(p); @@ -197,7 +207,8 @@ RichCaret RichTxt::GetCaret(int pos, RichContext rc) const nbefore = pp.before + pp.ruler; nline = pp.linecy[0]; } - RichCaret tp = Get(parti, rc.styles).GetCaret(pos, rc.page, rc.py, nbefore, nline); + RichCaret tp = Get(parti, rc.styles, true) + .GetCaret(pos, rc.page, rc.py, nbefore, nline); tp.textpage = rc.page; return tp; } @@ -233,7 +244,8 @@ int RichTxt::GetPos(int x, PageY y, RichContext rc) const nbefore = pp.before + pp.ruler; nline = pp.linecy[0]; } - return Get(parti, rc.styles).GetPos(x, y, rc.page, rc.py, nbefore, nline) + pos; + return Get(parti, rc.styles, true) + .GetPos(x, y, rc.page, rc.py, nbefore, nline) + pos; } } pos += GetPartLength(parti) + 1; @@ -297,7 +309,7 @@ int RichTxt::GetVertMove(int pos, int gx, RichContext rc, int dir) const } while(pi < GetPartCount()) { int q = IsTable(pi) ? GetTable(pi).GetVertMove(p, gx, rc, dir) - : Get(pi, rc.styles).GetVertMove(p, gx, rc.page, dir); + : Get(pi, rc.styles, true).GetVertMove(p, gx, rc.page, dir); if(q >= 0) return q + pos; if(dir > 0) @@ -330,9 +342,9 @@ void RichTxt::GatherValPos(Vector& f, RichContext rc, int pos, int t } if(p.haspos) if(type == LABELS) - Get(parti, rc.styles).GatherLabels(f, rc.page, rc.py, pos, nbefore, nline); + Get(parti, rc.styles, true).GatherLabels(f, rc.page, rc.py, pos, nbefore, nline); else - Get(parti, rc.styles).GatherIndexes(f, rc.page, rc.py, pos, nbefore, nline); + Get(parti, rc.styles, true).GatherIndexes(f, rc.page, rc.py, pos, nbefore, nline); } pos += GetPartLength(parti) + 1; rc.py = GetNextPageY(parti++, rc);