00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "gfx_layout.h"
00014 #include "string_func.h"
00015 #include "strings_func.h"
00016 #include "debug.h"
00017
00018 #include "table/control_codes.h"
00019
00020 #ifdef WITH_ICU
00021 #include <unicode/ustring.h>
00022 #endif
00023
00024
00026 Layouter::LineCache *Layouter::linecache;
00027
00029 Layouter::FontColourMap Layouter::fonts[FS_END];
00030
00031
00037 Font::Font(FontSize size, TextColour colour) :
00038 fc(FontCache::Get(size)), colour(colour)
00039 {
00040 assert(size < FS_END);
00041 }
00042
00043 #ifdef WITH_ICU
00044
00045
00046 le_int32 Font::getUnitsPerEM() const
00047 {
00048 return this->fc->GetUnitsPerEM();
00049 }
00050
00051 le_int32 Font::getAscent() const
00052 {
00053 return this->fc->GetAscender();
00054 }
00055
00056 le_int32 Font::getDescent() const
00057 {
00058 return -this->fc->GetDescender();
00059 }
00060
00061 le_int32 Font::getLeading() const
00062 {
00063 return this->fc->GetHeight();
00064 }
00065
00066 float Font::getXPixelsPerEm() const
00067 {
00068 return (float)this->fc->GetHeight();
00069 }
00070
00071 float Font::getYPixelsPerEm() const
00072 {
00073 return (float)this->fc->GetHeight();
00074 }
00075
00076 float Font::getScaleFactorX() const
00077 {
00078 return 1.0f;
00079 }
00080
00081 float Font::getScaleFactorY() const
00082 {
00083 return 1.0f;
00084 }
00085
00086 const void *Font::getFontTable(LETag tableTag) const
00087 {
00088 size_t length;
00089 return this->getFontTable(tableTag, length);
00090 }
00091
00092 const void *Font::getFontTable(LETag tableTag, size_t &length) const
00093 {
00094 return this->fc->GetFontTable(tableTag, length);
00095 }
00096
00097 LEGlyphID Font::mapCharToGlyph(LEUnicode32 ch) const
00098 {
00099 if (IsTextDirectionChar(ch)) return 0;
00100 return this->fc->MapCharToGlyph(ch);
00101 }
00102
00103 void Font::getGlyphAdvance(LEGlyphID glyph, LEPoint &advance) const
00104 {
00105 advance.fX = glyph == 0xFFFF ? 0 : this->fc->GetGlyphWidth(glyph);
00106 advance.fY = 0;
00107 }
00108
00109 le_bool Font::getGlyphPoint(LEGlyphID glyph, le_int32 pointNumber, LEPoint &point) const
00110 {
00111 return FALSE;
00112 }
00113
00114 static size_t AppendToBuffer(UChar *buff, const UChar *buffer_last, WChar c)
00115 {
00116
00117 int32 length = 0;
00118 UErrorCode err = U_ZERO_ERROR;
00119 u_strFromUTF32(buff, buffer_last - buff, &length, (UChar32*)&c, 1, &err);
00120 return length;
00121 }
00122
00126 class ICUParagraphLayout : public AutoDeleteSmallVector<ParagraphLayouter::Line *, 4>, public ParagraphLayouter {
00127 ParagraphLayout *p;
00128 public:
00130 typedef UChar CharType;
00132 static const bool SUPPORTS_RTL = true;
00133
00135 class ICUVisualRun : public ParagraphLayouter::VisualRun {
00136 const ParagraphLayout::VisualRun *vr;
00137
00138 public:
00139 ICUVisualRun(const ParagraphLayout::VisualRun *vr) : vr(vr) { }
00140
00141 const Font *GetFont() const { return (const Font*)vr->getFont(); }
00142 int GetGlyphCount() const { return vr->getGlyphCount(); }
00143 const GlyphID *GetGlyphs() const { return vr->getGlyphs(); }
00144 const float *GetPositions() const { return vr->getPositions(); }
00145 int GetLeading() const { return vr->getLeading(); }
00146 const int *GetGlyphToCharMap() const { return vr->getGlyphToCharMap(); }
00147 };
00148
00150 class ICULine : public AutoDeleteSmallVector<ICUVisualRun *, 4>, public ParagraphLayouter::Line {
00151 ParagraphLayout::Line *l;
00152
00153 public:
00154 ICULine(ParagraphLayout::Line *l) : l(l)
00155 {
00156 for (int i = 0; i < l->countRuns(); i++) {
00157 *this->Append() = new ICUVisualRun(l->getVisualRun(i));
00158 }
00159 }
00160 ~ICULine() { delete l; }
00161
00162 int GetLeading() const { return l->getLeading(); }
00163 int GetWidth() const { return l->getWidth(); }
00164 int CountRuns() const { return l->countRuns(); }
00165 const ParagraphLayouter::VisualRun *GetVisualRun(int run) const { return *this->Get(run); }
00166
00167 int GetInternalCharLength(WChar c) const
00168 {
00169
00170 return Utf8CharLen(c) < 4 ? 1 : 2;
00171 }
00172 };
00173
00174 ICUParagraphLayout(ParagraphLayout *p) : p(p) { }
00175 ~ICUParagraphLayout() { delete p; }
00176 void Reflow() { p->reflow(); }
00177
00178 ParagraphLayouter::Line *NextLine(int max_width)
00179 {
00180 ParagraphLayout::Line *l = p->nextLine(max_width);
00181 return l == NULL ? NULL : new ICULine(l);
00182 }
00183 };
00184
00185 static ParagraphLayouter *GetParagraphLayout(UChar *buff, UChar *buff_end, FontMap &fontMapping)
00186 {
00187 int32 length = buff_end - buff;
00188
00189 if (length == 0) {
00190
00191 buff[0] = ' ';
00192 length = 1;
00193 fontMapping.End()[-1].first++;
00194 }
00195
00196
00197 FontRuns runs(fontMapping.Length());
00198 for (FontMap::iterator iter = fontMapping.Begin(); iter != fontMapping.End(); iter++) {
00199 runs.add(iter->second, iter->first);
00200 }
00201
00202 LEErrorCode status = LE_NO_ERROR;
00203
00204
00205 ParagraphLayout *p = new ParagraphLayout(buff, length, &runs, NULL, NULL, NULL, _current_text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, false, status);
00206 if (status != LE_NO_ERROR) {
00207 delete p;
00208 return NULL;
00209 }
00210
00211 return new ICUParagraphLayout(p);
00212 }
00213
00214 #endif
00215
00216
00235 class FallbackParagraphLayout : public ParagraphLayouter {
00236 public:
00238 typedef WChar CharType;
00240 static const bool SUPPORTS_RTL = false;
00241
00243 class FallbackVisualRun : public ParagraphLayouter::VisualRun {
00244 Font *font;
00245 GlyphID *glyphs;
00246 float *positions;
00247 int *glyph_to_char;
00248 int glyph_count;
00249
00250 public:
00251 FallbackVisualRun(Font *font, const WChar *chars, int glyph_count, int x);
00252 ~FallbackVisualRun();
00253 const Font *GetFont() const;
00254 int GetGlyphCount() const;
00255 const GlyphID *GetGlyphs() const;
00256 const float *GetPositions() const;
00257 int GetLeading() const;
00258 const int *GetGlyphToCharMap() const;
00259 };
00260
00262 class FallbackLine : public AutoDeleteSmallVector<FallbackVisualRun *, 4>, public ParagraphLayouter::Line {
00263 public:
00264 int GetLeading() const;
00265 int GetWidth() const;
00266 int CountRuns() const;
00267 const ParagraphLayouter::VisualRun *GetVisualRun(int run) const;
00268
00269 int GetInternalCharLength(WChar c) const { return 1; }
00270 };
00271
00272 const WChar *buffer_begin;
00273 const WChar *buffer;
00274 FontMap &runs;
00275
00276 FallbackParagraphLayout(WChar *buffer, int length, FontMap &runs);
00277 void Reflow();
00278 const ParagraphLayouter::Line *NextLine(int max_width);
00279 };
00280
00288 FallbackParagraphLayout::FallbackVisualRun::FallbackVisualRun(Font *font, const WChar *chars, int char_count, int x) :
00289 font(font), glyph_count(char_count)
00290 {
00291 this->glyphs = MallocT<GlyphID>(this->glyph_count);
00292 this->glyph_to_char = MallocT<int>(this->glyph_count);
00293
00294
00295 this->positions = MallocT<float>(this->glyph_count * 2 + 2);
00296 this->positions[0] = x;
00297 this->positions[1] = 0;
00298
00299 for (int i = 0; i < this->glyph_count; i++) {
00300 this->glyphs[i] = font->fc->MapCharToGlyph(chars[i]);
00301 this->positions[2 * i + 2] = this->positions[2 * i] + font->fc->GetGlyphWidth(this->glyphs[i]);
00302 this->positions[2 * i + 3] = 0;
00303 this->glyph_to_char[i] = i;
00304 }
00305 }
00306
00308 FallbackParagraphLayout::FallbackVisualRun::~FallbackVisualRun()
00309 {
00310 free(this->positions);
00311 free(this->glyph_to_char);
00312 free(this->glyphs);
00313 }
00314
00319 const Font *FallbackParagraphLayout::FallbackVisualRun::GetFont() const
00320 {
00321 return this->font;
00322 }
00323
00328 int FallbackParagraphLayout::FallbackVisualRun::GetGlyphCount() const
00329 {
00330 return this->glyph_count;
00331 }
00332
00337 const GlyphID *FallbackParagraphLayout::FallbackVisualRun::GetGlyphs() const
00338 {
00339 return this->glyphs;
00340 }
00341
00346 const float *FallbackParagraphLayout::FallbackVisualRun::GetPositions() const
00347 {
00348 return this->positions;
00349 }
00350
00355 const int *FallbackParagraphLayout::FallbackVisualRun::GetGlyphToCharMap() const
00356 {
00357 return this->glyph_to_char;
00358 }
00359
00364 int FallbackParagraphLayout::FallbackVisualRun::GetLeading() const
00365 {
00366 return this->GetFont()->fc->GetHeight();
00367 }
00368
00373 int FallbackParagraphLayout::FallbackLine::GetLeading() const
00374 {
00375 int leading = 0;
00376 for (const FallbackVisualRun * const *run = this->Begin(); run != this->End(); run++) {
00377 leading = max(leading, (*run)->GetLeading());
00378 }
00379
00380 return leading;
00381 }
00382
00387 int FallbackParagraphLayout::FallbackLine::GetWidth() const
00388 {
00389 if (this->Length() == 0) return 0;
00390
00391
00392
00393
00394
00395
00396 const ParagraphLayouter::VisualRun *run = this->GetVisualRun(this->CountRuns() - 1);
00397 return (int)run->GetPositions()[run->GetGlyphCount() * 2];
00398 }
00399
00404 int FallbackParagraphLayout::FallbackLine::CountRuns() const
00405 {
00406 return this->Length();
00407 }
00408
00413 const ParagraphLayouter::VisualRun *FallbackParagraphLayout::FallbackLine::GetVisualRun(int run) const
00414 {
00415 return *this->Get(run);
00416 }
00417
00424 FallbackParagraphLayout::FallbackParagraphLayout(WChar *buffer, int length, FontMap &runs) : buffer_begin(buffer), buffer(buffer), runs(runs)
00425 {
00426 assert(runs.End()[-1].first == length);
00427 }
00428
00432 void FallbackParagraphLayout::Reflow()
00433 {
00434 this->buffer = this->buffer_begin;
00435 }
00436
00442 const ParagraphLayouter::Line *FallbackParagraphLayout::NextLine(int max_width)
00443 {
00444
00445
00446
00447
00448 if (this->buffer == NULL) return NULL;
00449
00450 FallbackLine *l = new FallbackLine();
00451
00452 if (*this->buffer == '\0') {
00453
00454 this->buffer = NULL;
00455 *l->Append() = new FallbackVisualRun(this->runs.Begin()->second, this->buffer, 0, 0);
00456 return l;
00457 }
00458
00459 const WChar *begin = this->buffer;
00460 const WChar *last_space = NULL;
00461 const WChar *last_char = begin;
00462 int width = 0;
00463
00464 int offset = this->buffer - this->buffer_begin;
00465 FontMap::iterator iter = this->runs.Begin();
00466 while (iter->first <= offset) {
00467 iter++;
00468 assert(iter != this->runs.End());
00469 }
00470
00471 const FontCache *fc = iter->second->fc;
00472 const WChar *next_run = this->buffer_begin + iter->first;
00473
00474 for (;;) {
00475 WChar c = *this->buffer;
00476 last_char = this->buffer;
00477
00478 if (c == '\0') {
00479 this->buffer = NULL;
00480 break;
00481 }
00482
00483 if (this->buffer == next_run) {
00484 int w = l->GetWidth();
00485 *l->Append() = new FallbackVisualRun(iter->second, begin, this->buffer - begin, w);
00486 iter++;
00487 assert(iter != this->runs.End());
00488
00489 next_run = this->buffer_begin + iter->first;
00490 begin = this->buffer;
00491
00492 last_space = NULL;
00493 }
00494
00495 if (IsWhitespace(c)) last_space = this->buffer;
00496
00497 if (IsPrintable(c) && !IsTextDirectionChar(c)) {
00498 int char_width = GetCharacterWidth(fc->GetSize(), c);
00499 width += char_width;
00500 if (width > max_width) {
00501
00502
00503 if (width == char_width) {
00504
00505
00506 this->buffer = NULL;
00507 return l;
00508 }
00509
00510 if (last_space == NULL) {
00511
00512
00513
00514
00515
00516
00517 last_char = this->buffer;
00518 } else {
00519
00520 this->buffer = last_space + 1;
00521 last_char = last_space;
00522 }
00523 break;
00524 }
00525 }
00526
00527 this->buffer++;
00528 }
00529
00530 if (l->Length() == 0 || last_char - begin != 0) {
00531 int w = l->GetWidth();
00532 *l->Append() = new FallbackVisualRun(iter->second, begin, last_char - begin, w);
00533 }
00534 return l;
00535 }
00536
00544 static size_t AppendToBuffer(WChar *buff, const WChar *buffer_last, WChar c)
00545 {
00546 *buff = c;
00547 return 1;
00548 }
00549
00557 static FallbackParagraphLayout *GetParagraphLayout(WChar *buff, WChar *buff_end, FontMap &fontMapping)
00558 {
00559 return new FallbackParagraphLayout(buff, buff_end - buff, fontMapping);
00560 }
00561
00571 template <typename T>
00572 static inline void GetLayouter(Layouter::LineCacheItem &line, const char *&str, FontState &state)
00573 {
00574 if (line.buffer != NULL) free(line.buffer);
00575
00576 typename T::CharType *buff_begin = MallocT<typename T::CharType>(DRAW_STRING_BUFFER);
00577 const typename T::CharType *buffer_last = buff_begin + DRAW_STRING_BUFFER;
00578 typename T::CharType *buff = buff_begin;
00579 FontMap &fontMapping = line.runs;
00580 Font *f = Layouter::GetFont(state.fontsize, state.cur_colour);
00581
00582 line.buffer = buff_begin;
00583
00584
00585
00586
00587
00588
00589 for (; buff < buffer_last;) {
00590 WChar c = Utf8Consume(const_cast<const char **>(&str));
00591 if (c == '\0' || c == '\n') {
00592 break;
00593 } else if (c >= SCC_BLUE && c <= SCC_BLACK) {
00594 state.SetColour((TextColour)(c - SCC_BLUE));
00595 } else if (c == SCC_PREVIOUS_COLOUR) {
00596 state.SetPreviousColour();
00597 } else if (c == SCC_TINYFONT) {
00598 state.SetFontSize(FS_SMALL);
00599 } else if (c == SCC_BIGFONT) {
00600 state.SetFontSize(FS_LARGE);
00601 } else {
00602
00603
00604
00605 if (!T::SUPPORTS_RTL && IsTextDirectionChar(c)) continue;
00606 buff += AppendToBuffer(buff, buffer_last, c);
00607 continue;
00608 }
00609
00610 if (!fontMapping.Contains(buff - buff_begin)) {
00611 fontMapping.Insert(buff - buff_begin, f);
00612 }
00613 f = Layouter::GetFont(state.fontsize, state.cur_colour);
00614 }
00615
00616
00617 *buff = '\0';
00618
00619 if (!fontMapping.Contains(buff - buff_begin)) {
00620 fontMapping.Insert(buff - buff_begin, f);
00621 }
00622 line.layout = GetParagraphLayout(buff_begin, buff, fontMapping);
00623 line.state_after = state;
00624 }
00625
00633 Layouter::Layouter(const char *str, int maxw, TextColour colour, FontSize fontsize) : string(str)
00634 {
00635 FontState state(colour, fontsize);
00636 WChar c = 0;
00637
00638 do {
00639
00640 const char *lineend = str;
00641 for (;;) {
00642 size_t len = Utf8Decode(&c, lineend);
00643 if (c == '\0' || c == '\n') break;
00644 lineend += len;
00645 }
00646
00647 LineCacheItem& line = GetCachedParagraphLayout(str, lineend - str, state);
00648 if (line.layout != NULL) {
00649
00650 str = lineend + 1;
00651 state = line.state_after;
00652 line.layout->Reflow();
00653 } else {
00654
00655 #ifdef WITH_ICU
00656 FontState old_state = state;
00657 const char *old_str = str;
00658
00659 GetLayouter<ICUParagraphLayout>(line, str, state);
00660 if (line.layout == NULL) {
00661 static bool warned = false;
00662 if (!warned) {
00663 DEBUG(misc, 0, "ICU layouter bailed on the font. Falling back to the fallback layouter");
00664 warned = true;
00665 }
00666
00667 state = old_state;
00668 str = old_str;
00669 GetLayouter<FallbackParagraphLayout>(line, str, state);
00670 }
00671 #else
00672 GetLayouter<FallbackParagraphLayout>(line, str, state);
00673 #endif
00674 }
00675
00676
00677 const ParagraphLayouter::Line *l;
00678 while ((l = line.layout->NextLine(maxw)) != NULL) {
00679 *this->Append() = l;
00680 }
00681
00682 } while (c != '\0');
00683 }
00684
00689 Dimension Layouter::GetBounds()
00690 {
00691 Dimension d = { 0, 0 };
00692 for (const ParagraphLayouter::Line **l = this->Begin(); l != this->End(); l++) {
00693 d.width = max<uint>(d.width, (*l)->GetWidth());
00694 d.height += (*l)->GetLeading();
00695 }
00696 return d;
00697 }
00698
00705 Point Layouter::GetCharPosition(const char *ch) const
00706 {
00707
00708
00709 size_t index = 0;
00710 const char *str = this->string;
00711 while (str < ch) {
00712 WChar c;
00713 size_t len = Utf8Decode(&c, str);
00714 if (c == '\0' || c == '\n') break;
00715 str += len;
00716 index += (*this->Begin())->GetInternalCharLength(c);
00717 }
00718
00719 if (str == ch) {
00720
00721 const ParagraphLayouter::Line *line = *this->Begin();
00722
00723
00724 if (*ch == '\0' || *ch == '\n') {
00725 Point p = { line->GetWidth(), 0 };
00726 return p;
00727 }
00728
00729
00730 for (int run_index = 0; run_index < line->CountRuns(); run_index++) {
00731 const ParagraphLayouter::VisualRun *run = line->GetVisualRun(run_index);
00732
00733 for (int i = 0; i < run->GetGlyphCount(); i++) {
00734
00735 if ((size_t)run->GetGlyphToCharMap()[i] == index) {
00736 Point p = { (int)run->GetPositions()[i * 2], (int)run->GetPositions()[i * 2 + 1] };
00737 return p;
00738 }
00739 }
00740 }
00741 }
00742
00743 Point p = { 0, 0 };
00744 return p;
00745 }
00746
00752 const char *Layouter::GetCharAtPosition(int x) const
00753 {
00754 const ParagraphLayouter::Line *line = *this->Begin();;
00755
00756 for (int run_index = 0; run_index < line->CountRuns(); run_index++) {
00757 const ParagraphLayouter::VisualRun *run = line->GetVisualRun(run_index);
00758
00759 for (int i = 0; i < run->GetGlyphCount(); i++) {
00760
00761 if (run->GetGlyphs()[i] == 0xFFFF) continue;
00762
00763 int begin_x = (int)run->GetPositions()[i * 2];
00764 int end_x = (int)run->GetPositions()[i * 2 + 2];
00765
00766 if (IsInsideMM(x, begin_x, end_x)) {
00767
00768 size_t index = run->GetGlyphToCharMap()[i];
00769
00770 size_t cur_idx = 0;
00771 for (const char *str = this->string; *str != '\0'; ) {
00772 if (cur_idx == index) return str;
00773
00774 WChar c = Utf8Consume(&str);
00775 cur_idx += line->GetInternalCharLength(c);
00776 }
00777 }
00778 }
00779 }
00780
00781 return NULL;
00782 }
00783
00787 Font *Layouter::GetFont(FontSize size, TextColour colour)
00788 {
00789 FontColourMap::iterator it = fonts[size].Find(colour);
00790 if (it != fonts[size].End()) return it->second;
00791
00792 Font *f = new Font(size, colour);
00793 *fonts[size].Append() = FontColourMap::Pair(colour, f);
00794 return f;
00795 }
00796
00801 void Layouter::ResetFontCache(FontSize size)
00802 {
00803 for (FontColourMap::iterator it = fonts[size].Begin(); it != fonts[size].End(); ++it) {
00804 delete it->second;
00805 }
00806 fonts[size].Clear();
00807
00808
00809 ResetLineCache();
00810 }
00811
00820 Layouter::LineCacheItem &Layouter::GetCachedParagraphLayout(const char *str, size_t len, const FontState &state)
00821 {
00822 if (linecache == NULL) {
00823
00824 linecache = new LineCache();
00825 }
00826
00827 LineCacheKey key;
00828 key.state_before = state;
00829 key.str.assign(str, len);
00830 return (*linecache)[key];
00831 }
00832
00836 void Layouter::ResetLineCache()
00837 {
00838 if (linecache != NULL) linecache->clear();
00839 }
00840
00844 void Layouter::ReduceLineCache()
00845 {
00846 if (linecache != NULL) {
00847
00848 if (linecache->size() > 4096) ResetLineCache();
00849 }
00850 }