summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/app.c4
-rw-r--r--src/app.h1
-rw-r--r--src/ui/documentwidget.c13
-rw-r--r--src/ui/inputwidget.c10
-rw-r--r--src/ui/text.c157
-rw-r--r--src/ui/text.h2
6 files changed, 167 insertions, 20 deletions
diff --git a/src/app.c b/src/app.c
index f1ba2432..6261c74b 100644
--- a/src/app.c
+++ b/src/app.c
@@ -1050,6 +1050,10 @@ iBool isRefreshPending_App(void) {
1050 return value_Atomic(&app_.pendingRefresh); 1050 return value_Atomic(&app_.pendingRefresh);
1051} 1051}
1052 1052
1053iBool isFinishedLaunching_App(void) {
1054 return app_.isFinishedLaunching;
1055}
1056
1053uint32_t elapsedSinceLastTicker_App(void) { 1057uint32_t elapsedSinceLastTicker_App(void) {
1054 return app_.elapsedSinceLastTicker; 1058 return app_.elapsedSinceLastTicker;
1055} 1059}
diff --git a/src/app.h b/src/app.h
index 015f5a3e..bdb0e22f 100644
--- a/src/app.h
+++ b/src/app.h
@@ -68,6 +68,7 @@ void processEvents_App (enum iAppEventMode mode);
68iBool handleCommand_App (const char *cmd); 68iBool handleCommand_App (const char *cmd);
69void refresh_App (void); 69void refresh_App (void);
70iBool isRefreshPending_App (void); 70iBool isRefreshPending_App (void);
71iBool isFinishedLaunching_App (void);
71uint32_t elapsedSinceLastTicker_App (void); /* milliseconds */ 72uint32_t elapsedSinceLastTicker_App (void); /* milliseconds */
72 73
73iBool isLandscape_App (void); 74iBool isLandscape_App (void);
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index a7666865..9111b546 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -1004,6 +1004,17 @@ static void parseUser_DocumentWidget_(iDocumentWidget *d) {
1004 setRange_String(d->titleUser, urlUser_String(d->mod.url)); 1004 setRange_String(d->titleUser, urlUser_String(d->mod.url));
1005} 1005}
1006 1006
1007static void cacheRunGlyphs_(void *data, const iGmRun *run) {
1008 iUnused(data);
1009 if (!isEmpty_Range(&run->text)) {
1010 cache_Text(run->font, run->text);
1011 }
1012}
1013
1014static void cacheDocumentGlyphs_DocumentWidget_(const iDocumentWidget *d) {
1015 render_GmDocument(d->doc, (iRangei){ 0, size_GmDocument(d->doc).y }, cacheRunGlyphs_, NULL);
1016}
1017
1007static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { 1018static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) {
1008 const iRecentUrl *recent = findUrl_History(d->mod.history, d->mod.url); 1019 const iRecentUrl *recent = findUrl_History(d->mod.history, d->mod.url);
1009 if (recent && recent->cachedResponse) { 1020 if (recent && recent->cachedResponse) {
@@ -1026,6 +1037,7 @@ static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) {
1026 updateSideOpacity_DocumentWidget_(d, iFalse); 1037 updateSideOpacity_DocumentWidget_(d, iFalse);
1027 updateSideIconBuf_DocumentWidget_(d); 1038 updateSideIconBuf_DocumentWidget_(d);
1028 updateVisible_DocumentWidget_(d); 1039 updateVisible_DocumentWidget_(d);
1040 cacheDocumentGlyphs_DocumentWidget_(d);
1029 postCommandf_App("document.changed doc:%p url:%s", d, cstr_String(d->mod.url)); 1041 postCommandf_App("document.changed doc:%p url:%s", d, cstr_String(d->mod.url));
1030 return iTrue; 1042 return iTrue;
1031 } 1043 }
@@ -1762,6 +1774,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1762 scrollToHeading_DocumentWidget_(d, cstr_String(&d->pendingGotoHeading)); 1774 scrollToHeading_DocumentWidget_(d, cstr_String(&d->pendingGotoHeading));
1763 clear_String(&d->pendingGotoHeading); 1775 clear_String(&d->pendingGotoHeading);
1764 } 1776 }
1777 cacheDocumentGlyphs_DocumentWidget_(d);
1765 return iFalse; 1778 return iFalse;
1766 } 1779 }
1767 else if (equal_Command(cmd, "media.updated") || equal_Command(cmd, "media.finished")) { 1780 else if (equal_Command(cmd, "media.updated") || equal_Command(cmd, "media.finished")) {
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index 27f0217c..06e6373e 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -256,10 +256,12 @@ static iString *visText_InputWidget_(const iInputWidget *d) {
256} 256}
257 257
258static void updateBuffered_InputWidget_(iInputWidget *d) { 258static void updateBuffered_InputWidget_(iInputWidget *d) {
259 invalidateBuffered_InputWidget_(d); 259 if (isFinishedLaunching_App()) {
260 iString *visText = visText_InputWidget_(d); 260 invalidateBuffered_InputWidget_(d);
261 d->buffered = new_TextBuf(d->font, cstr_String(visText)); 261 iString *visText = visText_InputWidget_(d);
262 delete_String(visText); 262 d->buffered = new_TextBuf(d->font, cstr_String(visText));
263 delete_String(visText);
264 }
263} 265}
264 266
265void setText_InputWidget(iInputWidget *d, const iString *text) { 267void setText_InputWidget(iInputWidget *d, const iString *text) {
diff --git a/src/ui/text.c b/src/ui/text.c
index 36e98e11..4a39bf72 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -53,8 +53,6 @@ int gap_Text; /* cf. gap_UI in metrics.h */
53int enableHalfPixelGlyphs_Text = iTrue; /* debug setting */ 53int enableHalfPixelGlyphs_Text = iTrue; /* debug setting */
54int enableKerning_Text = iTrue; /* looking up kern pairs is slow */ 54int enableKerning_Text = iTrue; /* looking up kern pairs is slow */
55 55
56static iBool enableRaster_Text_ = iTrue;
57
58enum iGlyphFlag { 56enum iGlyphFlag {
59 rasterized0_GlyphFlag = iBit(1), /* zero offset */ 57 rasterized0_GlyphFlag = iBit(1), /* zero offset */
60 rasterized1_GlyphFlag = iBit(2), /* half-pixel offset */ 58 rasterized1_GlyphFlag = iBit(2), /* half-pixel offset */
@@ -521,12 +519,7 @@ static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, uint32_t glyphIndex, fl
521 SDL_Surface *surface8 = 519 SDL_Surface *surface8 =
522 SDL_CreateRGBSurfaceWithFormatFrom(bmp, w, h, 8, w, SDL_PIXELFORMAT_INDEX8); 520 SDL_CreateRGBSurfaceWithFormatFrom(bmp, w, h, 8, w, SDL_PIXELFORMAT_INDEX8);
523 SDL_SetSurfacePalette(surface8, text_.grayscale); 521 SDL_SetSurfacePalette(surface8, text_.grayscale);
524 SDL_PixelFormat *fmt = SDL_AllocFormat(SDL_PIXELFORMAT_RGBA8888); 522 return surface8;
525 SDL_Surface *surface = SDL_ConvertSurface(surface8, fmt, 0);
526 SDL_FreeFormat(fmt);
527 SDL_FreeSurface(surface8);
528 stbtt_FreeBitmap(bmp, NULL);
529 return surface;
530} 523}
531 524
532iLocalDef SDL_Rect sdlRect_(const iRect rect) { 525iLocalDef SDL_Rect sdlRect_(const iRect rect) {
@@ -578,6 +571,7 @@ static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) {
578 } 571 }
579} 572}
580 573
574#if 0
581static iBool cache_Font_(const iFont *d, iGlyph *glyph, int hoff) { 575static iBool cache_Font_(const iFont *d, iGlyph *glyph, int hoff) {
582 iText * txt = &text_; 576 iText * txt = &text_;
583 SDL_Renderer *render = txt->render; 577 SDL_Renderer *render = txt->render;
@@ -601,6 +595,7 @@ static iBool cache_Font_(const iFont *d, iGlyph *glyph, int hoff) {
601 } 595 }
602 return isRasterized_Glyph_(glyph, hoff); 596 return isRasterized_Glyph_(glyph, hoff);
603} 597}
598#endif
604 599
605iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { 600iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
606 if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) { 601 if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) {
@@ -644,6 +639,7 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
644 return font; 639 return font;
645} 640}
646 641
642#if 0
647static void doRaster_Font_(const iFont *font, iGlyph *glyph) { 643static void doRaster_Font_(const iFont *font, iGlyph *glyph) {
648 SDL_Texture *oldTarget = SDL_GetRenderTarget(text_.render); 644 SDL_Texture *oldTarget = SDL_GetRenderTarget(text_.render);
649 SDL_SetRenderTarget(text_.render, text_.cache); 645 SDL_SetRenderTarget(text_.render, text_.cache);
@@ -655,8 +651,9 @@ static void doRaster_Font_(const iFont *font, iGlyph *glyph) {
655 } 651 }
656 SDL_SetRenderTarget(text_.render, oldTarget); 652 SDL_SetRenderTarget(text_.render, oldTarget);
657} 653}
654#endif
658 655
659static const iGlyph *glyph_Font_(iFont *d, iChar ch) { 656static iGlyph *glyph_Font_(iFont *d, iChar ch) {
660 iGlyph * glyph; 657 iGlyph * glyph;
661 uint32_t glyphIndex = 0; 658 uint32_t glyphIndex = 0;
662 /* The glyph may actually come from a different font; look up the right font. */ 659 /* The glyph may actually come from a different font; look up the right font. */
@@ -682,9 +679,6 @@ static const iGlyph *glyph_Font_(iFont *d, iChar ch) {
682 allocate_Font_(font, glyph, 1); 679 allocate_Font_(font, glyph, 1);
683 insert_Hash(&font->glyphs, &glyph->node); 680 insert_Hash(&font->glyphs, &glyph->node);
684 } 681 }
685 if (enableRaster_Text_ && !isFullyRasterized_Glyph_(glyph)) {
686 doRaster_Font_(font, glyph);
687 }
688 return glyph; 682 return glyph;
689} 683}
690 684
@@ -702,6 +696,129 @@ static iChar nextChar_(const char **chPos, const char *end) {
702 return ch; 696 return ch;
703} 697}
704 698
699iDeclareType(RasterGlyph)
700
701struct Impl_RasterGlyph {
702 iGlyph *glyph;
703 int hoff;
704 iRect rect;
705};
706
707void cacheTextGlyphs_Font_(iFont *d, const iRangecc text) {
708 const char * chPos = text.start;
709 SDL_Surface *buf = NULL;
710 const iInt2 bufSize = init_I2(iMin(512, d->height * iMin(2 * size_Range(&text), 20)),
711 d->height * 4 / 3);
712 int bufX = 0;
713 iArray * rasters = NULL;
714 SDL_Texture *oldTarget = NULL;
715 iBool isTargetChanged = iFalse;
716 /* We'll flush the buffered rasters periodically until everything is cached. */
717 while (chPos < text.end) {
718 while (chPos < text.end) {
719 const char *lastPos = chPos;
720 const iChar ch = nextChar_(&chPos, text.end);
721 if (ch == 0 || isSpace_Char(ch) || isDefaultIgnorable_Char(ch) ||
722 isFitzpatrickType_Char(ch)) {
723 continue;
724 }
725 const int lastCacheBottom = text_.cacheBottom;
726 iGlyph *glyph = glyph_Font_(d, ch);
727 if (text_.cacheBottom < lastCacheBottom) {
728 /* The cache was reset due to running out of space. We need to restart from
729 the beginning! */
730 chPos = text.start;
731 bufX = 0;
732 if (rasters) {
733 clear_Array(rasters);
734 }
735 }
736 if (!isFullyRasterized_Glyph_(glyph)) {
737 /* Need to cache this. */
738 if (buf == NULL) {
739 rasters = new_Array(sizeof(iRasterGlyph));
740 buf = SDL_CreateRGBSurfaceWithFormat(
741 0, bufSize.x, bufSize.y, 8, SDL_PIXELFORMAT_INDEX8);
742 SDL_SetSurfacePalette(buf, text_.grayscale);
743 }
744 SDL_Surface *surfaces[2] = {
745 !isRasterized_Glyph_(glyph, 0) ?
746 rasterizeGlyph_Font_(glyph->font, glyph->glyphIndex, 0) : NULL,
747 !isRasterized_Glyph_(glyph, 1) ?
748 rasterizeGlyph_Font_(glyph->font, glyph->glyphIndex, 0.5f) : NULL
749 };
750 iBool outOfSpace = iFalse;
751 iForIndices(i, surfaces) {
752 if (surfaces[i]) {
753 const int w = surfaces[i]->w;
754 const int h = surfaces[i]->h;
755 if (bufX + w <= bufSize.x) {
756 SDL_BlitSurface(surfaces[i],
757 NULL,
758 buf,
759 &(SDL_Rect){ bufX, 0, w, h });
760 pushBack_Array(rasters,
761 &(iRasterGlyph){ glyph, i, init_Rect(bufX, 0, w, h) });
762 bufX += w;
763 }
764 else {
765 outOfSpace = iTrue;
766 break;
767 }
768 }
769 }
770 iForIndices(i, surfaces) {
771 if (surfaces[i]) {
772 free(surfaces[i]->pixels);
773 SDL_FreeSurface(surfaces[i]);
774 }
775 }
776 if (outOfSpace) {
777 chPos = lastPos;
778 break;
779 }
780 }
781 }
782 /* Finished or the buffer is full, copy the glyphs to the cache texture. */
783 if (!isEmpty_Array(rasters)) {
784 SDL_Texture *bufTex = SDL_CreateTextureFromSurface(text_.render, buf);
785 SDL_SetTextureBlendMode(bufTex, SDL_BLENDMODE_NONE);
786 if (!isTargetChanged) {
787 isTargetChanged = iTrue;
788 oldTarget = SDL_GetRenderTarget(text_.render);
789 SDL_SetRenderTarget(text_.render, text_.cache);
790 }
791 //printf("copying %d rasters\n", size_Array(rasters)); fflush(stdout);
792 iConstForEach(Array, i, rasters) {
793 const iRasterGlyph *rg = i.value;
794// iAssert(isEqual_I2(rg->rect.size, rg->glyph->rect[rg->hoff].size));
795 const iRect *glRect = &rg->glyph->rect[rg->hoff];
796 SDL_RenderCopy(text_.render,
797 bufTex,
798 (const SDL_Rect *) &rg->rect,
799 (const SDL_Rect *) glRect);
800 setRasterized_Glyph_(rg->glyph, rg->hoff);
801 }
802 SDL_DestroyTexture(bufTex);
803 /* Resume with an empty buffer. */
804 clear_Array(rasters);
805 bufX = 0;
806 }
807 else {
808 iAssert(chPos >= text.end);
809 }
810 }
811 if (rasters) {
812 delete_Array(rasters);
813 }
814 if (buf) {
815 SDL_FreeSurface(buf);
816 }
817 if (isTargetChanged) {
818 SDL_SetRenderTarget(text_.render, oldTarget);
819 }
820}
821
705enum iRunMode { 822enum iRunMode {
706 measure_RunMode = 0, 823 measure_RunMode = 0,
707 draw_RunMode = 1, 824 draw_RunMode = 1,
@@ -777,8 +894,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
777 if (isMonospaced) { 894 if (isMonospaced) {
778 monoAdvance = glyph_Font_(d, 'M')->advance; 895 monoAdvance = glyph_Font_(d, 'M')->advance;
779 } 896 }
780 /* Global flag that allows glyph rasterization. */ 897 /* Text rendering is not very straightforward! Let's dive in... */
781 enableRaster_Text_ = !isMeasuring_(mode);
782 for (const char *chPos = args->text.start; chPos != args->text.end; ) { 898 for (const char *chPos = args->text.start; chPos != args->text.end; ) {
783 iAssert(chPos < args->text.end); 899 iAssert(chPos < args->text.end);
784 const char *currentPos = chPos; 900 const char *currentPos = chPos;
@@ -880,6 +996,10 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
880 const iGlyph *glyph = glyph_Font_(d, ch); 996 const iGlyph *glyph = glyph_Font_(d, ch);
881 int x1 = iMax(xpos, xposExtend); 997 int x1 = iMax(xpos, xposExtend);
882 const int hoff = enableHalfPixelGlyphs_Text ? (xpos - x1 > 0.5f ? 1 : 0) : 0; 998 const int hoff = enableHalfPixelGlyphs_Text ? (xpos - x1 > 0.5f ? 1 : 0) : 0;
999 if (!isRasterized_Glyph_(glyph, hoff)) {
1000 /* Need to pause here and make sure all glyphs have been cached in the text. */
1001 cacheTextGlyphs_Font_(d, args->text);
1002 }
883 int x2 = x1 + glyph->rect[hoff].size.x; 1003 int x2 = x1 + glyph->rect[hoff].size.x;
884 /* Out of the allotted space? */ 1004 /* Out of the allotted space? */
885 if (args->xposLimit > 0 && x2 > args->xposLimit) { 1005 if (args->xposLimit > 0 && x2 > args->xposLimit) {
@@ -999,6 +1119,10 @@ iInt2 measure_Text(int fontId, const char *text) {
999 return measureRange_Text(fontId, range_CStr(text)); 1119 return measureRange_Text(fontId, range_CStr(text));
1000} 1120}
1001 1121
1122void cache_Text(int fontId, iRangecc text) {
1123 cacheTextGlyphs_Font_(font_Text_(fontId), text);
1124}
1125
1002static int runFlagsFromId_(enum iFontId fontId) { 1126static int runFlagsFromId_(enum iFontId fontId) {
1003 int runFlags = 0; 1127 int runFlags = 0;
1004 if (fontId & alwaysVariableFlag_FontId) { 1128 if (fontId & alwaysVariableFlag_FontId) {
@@ -1060,10 +1184,11 @@ iInt2 advanceN_Text(int fontId, const char *text, size_t n) {
1060} 1184}
1061 1185
1062static void drawBounded_Text_(int fontId, iInt2 pos, int xposBound, int color, iRangecc text) { 1186static void drawBounded_Text_(int fontId, iInt2 pos, int xposBound, int color, iRangecc text) {
1063 iText *d = &text_; 1187 iText *d = &text_;
1188 iFont *font = font_Text_(fontId);
1064 const iColor clr = get_Color(color & mask_ColorId); 1189 const iColor clr = get_Color(color & mask_ColorId);
1065 SDL_SetTextureColorMod(d->cache, clr.r, clr.g, clr.b); 1190 SDL_SetTextureColorMod(d->cache, clr.r, clr.g, clr.b);
1066 run_Font_(font_Text_(fontId), 1191 run_Font_(font,
1067 &(iRunArgs){ .mode = draw_RunMode | 1192 &(iRunArgs){ .mode = draw_RunMode |
1068 (color & permanent_ColorId ? permanentColorFlag_RunMode : 0) | 1193 (color & permanent_ColorId ? permanentColorFlag_RunMode : 0) |
1069 runFlagsFromId_(fontId), 1194 runFlagsFromId_(fontId),
diff --git a/src/ui/text.h b/src/ui/text.h
index 897dfed0..606096b6 100644
--- a/src/ui/text.h
+++ b/src/ui/text.h
@@ -192,6 +192,8 @@ enum iAlignment {
192 192
193void setOpacity_Text (float opacity); 193void setOpacity_Text (float opacity);
194 194
195void cache_Text (int fontId, iRangecc text); /* pre-render glyphs */
196
195void draw_Text (int fontId, iInt2 pos, int color, const char *text, ...); 197void draw_Text (int fontId, iInt2 pos, int color, const char *text, ...);
196void drawAlign_Text (int fontId, iInt2 pos, int color, enum iAlignment align, const char *text, ...); 198void drawAlign_Text (int fontId, iInt2 pos, int color, enum iAlignment align, const char *text, ...);
197void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...); 199void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...);