diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-07-11 15:29:01 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-07-11 15:29:01 +0300 |
commit | 1ffac41ed4c8674b82eabf55582b7cb72f923ffd (patch) | |
tree | ef41f72360fe131089729416a3585bef548ac7bf | |
parent | 6748fe343a388dd24dfe45cfbda75cf85de8ebcf (diff) |
GmDocument: Refactoring line typesetter
The goal is to switch to `WrapText` so the entire paragraph can be processed in one go without having to re-initialize the HarfBuzz buffers after every wrap.
-rw-r--r-- | src/gmdocument.c | 112 |
1 files changed, 73 insertions, 39 deletions
diff --git a/src/gmdocument.c b/src/gmdocument.c index 3d91fb34..3ea35ba9 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -367,6 +367,66 @@ static void updateOpenURLs_GmDocument_(iGmDocument *d) { | |||
367 | d->openURLs = listOpenURLs_App(); | 367 | d->openURLs = listOpenURLs_App(); |
368 | } | 368 | } |
369 | 369 | ||
370 | iDeclareType(RunTypesetter) | ||
371 | |||
372 | struct Impl_RunTypesetter { | ||
373 | iGmDocument *doc; | ||
374 | iGmRun run; | ||
375 | iInt2 pos; | ||
376 | float lineHeightReduction; | ||
377 | int indent; | ||
378 | int layoutWidth; | ||
379 | int rightMargin; | ||
380 | iBool isWordWrapped; | ||
381 | iBool isPreformat; | ||
382 | int bigCount; | ||
383 | const int *fonts; | ||
384 | }; | ||
385 | |||
386 | static const int colors[max_GmLineType] = { | ||
387 | tmParagraph_ColorId, | ||
388 | tmParagraph_ColorId, | ||
389 | tmPreformatted_ColorId, | ||
390 | tmQuote_ColorId, | ||
391 | tmHeading1_ColorId, | ||
392 | tmHeading2_ColorId, | ||
393 | tmHeading3_ColorId, | ||
394 | tmLinkText_ColorId, | ||
395 | }; | ||
396 | |||
397 | static iRangecc typesetOneLine_RunTypesetter_(iRunTypesetter *d, iRangecc runLine) { | ||
398 | if (~d->run.flags & startOfLine_GmRunFlag && d->lineHeightReduction > 0.0f) { | ||
399 | d->pos.y -= d->lineHeightReduction * lineHeight_Text(d->run.font); | ||
400 | } | ||
401 | d->run.bounds.pos = addX_I2(d->pos, d->indent); | ||
402 | const int wrapAvail = d->layoutWidth - d->run.bounds.pos.x - d->rightMargin; | ||
403 | const int avail = d->isWordWrapped ? wrapAvail : 0; | ||
404 | const char *contPos; | ||
405 | const iInt2 dims = tryAdvance_Text(d->run.font, runLine, avail, &contPos); | ||
406 | iChangeFlags(d->run.flags, wide_GmRunFlag, (d->isPreformat && dims.x > d->layoutWidth)); | ||
407 | d->run.bounds.size.x = iMax(wrapAvail, dims.x); /* Extends to the right edge for selection. */ | ||
408 | d->run.bounds.size.y = dims.y; | ||
409 | d->run.visBounds = d->run.bounds; | ||
410 | d->run.visBounds.size.x = dims.x; | ||
411 | if (contPos > runLine.start) { | ||
412 | d->run.text = (iRangecc){ runLine.start, contPos }; | ||
413 | } | ||
414 | else { | ||
415 | d->run.text = runLine; | ||
416 | contPos = runLine.end; | ||
417 | } | ||
418 | pushBack_Array(&d->doc->layout, &d->run); | ||
419 | d->run.flags &= ~startOfLine_GmRunFlag; | ||
420 | runLine.start = contPos; | ||
421 | trimStart_Rangecc(&runLine); | ||
422 | d->pos.y += lineHeight_Text(d->run.font) * prefs_App()->lineSpacing; | ||
423 | if (--d->bigCount == 0) { | ||
424 | d->run.font = d->fonts[text_GmLineType]; | ||
425 | d->run.color = colors[text_GmLineType]; | ||
426 | } | ||
427 | return runLine; | ||
428 | } | ||
429 | |||
370 | static void doLayout_GmDocument_(iGmDocument *d) { | 430 | static void doLayout_GmDocument_(iGmDocument *d) { |
371 | const iPrefs *prefs = prefs_App(); | 431 | const iPrefs *prefs = prefs_App(); |
372 | const iBool isMono = isForcedMonospace_GmDocument_(d); | 432 | const iBool isMono = isForcedMonospace_GmDocument_(d); |
@@ -390,16 +450,6 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
390 | ? bold_FontId | 450 | ? bold_FontId |
391 | : paragraph_FontId, | 451 | : paragraph_FontId, |
392 | }; | 452 | }; |
393 | static const int colors[max_GmLineType] = { | ||
394 | tmParagraph_ColorId, | ||
395 | tmParagraph_ColorId, | ||
396 | tmPreformatted_ColorId, | ||
397 | tmQuote_ColorId, | ||
398 | tmHeading1_ColorId, | ||
399 | tmHeading2_ColorId, | ||
400 | tmHeading3_ColorId, | ||
401 | tmLinkText_ColorId, | ||
402 | }; | ||
403 | float indents[max_GmLineType] = { | 453 | float indents[max_GmLineType] = { |
404 | 5, 10, 5, isNarrow ? 5 : 10, 0, 0, 0, 5 | 454 | 5, 10, 5, isNarrow ? 5 : 10, 0, 0, 0, 5 |
405 | }; | 455 | }; |
@@ -750,37 +800,21 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
750 | } | 800 | } |
751 | } | 801 | } |
752 | iAssert(!isEmpty_Range(&runLine)); /* must have something at this point */ | 802 | iAssert(!isEmpty_Range(&runLine)); /* must have something at this point */ |
803 | iRunTypesetter rts = { .doc = d, | ||
804 | .run = run, | ||
805 | .pos = pos, | ||
806 | .lineHeightReduction = lineHeightReduction, | ||
807 | .indent = indent * gap_Text, | ||
808 | .layoutWidth = d->size.x, | ||
809 | .rightMargin = rightMargin * gap_Text, | ||
810 | .isWordWrapped = isWordWrapped, | ||
811 | .isPreformat = isPreformat, | ||
812 | .bigCount = bigCount, | ||
813 | .fonts = fonts }; | ||
753 | while (!isEmpty_Range(&runLine)) { | 814 | while (!isEmpty_Range(&runLine)) { |
754 | if (~run.flags & startOfLine_GmRunFlag && lineHeightReduction > 0.0f) { | 815 | runLine = typesetOneLine_RunTypesetter_(&rts, runLine); |
755 | pos.y -= lineHeightReduction * lineHeight_Text(run.font); | ||
756 | } | ||
757 | run.bounds.pos = addX_I2(pos, indent * gap_Text); | ||
758 | const int wrapAvail = d->size.x - run.bounds.pos.x - rightMargin * gap_Text; | ||
759 | const int avail = isWordWrapped ? wrapAvail : 0; | ||
760 | const char *contPos; | ||
761 | const iInt2 dims = tryAdvance_Text(run.font, runLine, avail, &contPos); | ||
762 | iChangeFlags(run.flags, wide_GmRunFlag, (isPreformat && dims.x > d->size.x)); | ||
763 | run.bounds.size.x = iMax(wrapAvail, dims.x); /* Extends to the right edge for selection. */ | ||
764 | run.bounds.size.y = dims.y; | ||
765 | run.visBounds = run.bounds; | ||
766 | run.visBounds.size.x = dims.x; | ||
767 | if (contPos > runLine.start) { | ||
768 | run.text = (iRangecc){ runLine.start, contPos }; | ||
769 | } | ||
770 | else { | ||
771 | run.text = runLine; | ||
772 | contPos = runLine.end; | ||
773 | } | ||
774 | pushBack_Array(&d->layout, &run); | ||
775 | run.flags &= ~startOfLine_GmRunFlag; | ||
776 | runLine.start = contPos; | ||
777 | trimStart_Rangecc(&runLine); | ||
778 | pos.y += lineHeight_Text(run.font) * prefs->lineSpacing; | ||
779 | if (--bigCount == 0) { | ||
780 | run.font = fonts[text_GmLineType]; | ||
781 | run.color = colors[text_GmLineType]; | ||
782 | } | ||
783 | } | 816 | } |
817 | pos = rts.pos; | ||
784 | /* Flag the end of line, too. */ | 818 | /* Flag the end of line, too. */ |
785 | ((iGmRun *) back_Array(&d->layout))->flags |= endOfLine_GmRunFlag; | 819 | ((iGmRun *) back_Array(&d->layout))->flags |= endOfLine_GmRunFlag; |
786 | /* Image or audio content. */ | 820 | /* Image or audio content. */ |