diff options
Diffstat (limited to 'src/ui')
-rw-r--r-- | src/ui/documentwidget.c | 13 | ||||
-rw-r--r-- | src/ui/inputwidget.c | 10 | ||||
-rw-r--r-- | src/ui/text.c | 157 | ||||
-rw-r--r-- | src/ui/text.h | 2 |
4 files changed, 162 insertions, 20 deletions
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 | ||
1007 | static 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 | |||
1014 | static void cacheDocumentGlyphs_DocumentWidget_(const iDocumentWidget *d) { | ||
1015 | render_GmDocument(d->doc, (iRangei){ 0, size_GmDocument(d->doc).y }, cacheRunGlyphs_, NULL); | ||
1016 | } | ||
1017 | |||
1007 | static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { | 1018 | static 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 | ||
258 | static void updateBuffered_InputWidget_(iInputWidget *d) { | 258 | static 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 | ||
265 | void setText_InputWidget(iInputWidget *d, const iString *text) { | 267 | void 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 */ | |||
53 | int enableHalfPixelGlyphs_Text = iTrue; /* debug setting */ | 53 | int enableHalfPixelGlyphs_Text = iTrue; /* debug setting */ |
54 | int enableKerning_Text = iTrue; /* looking up kern pairs is slow */ | 54 | int enableKerning_Text = iTrue; /* looking up kern pairs is slow */ |
55 | 55 | ||
56 | static iBool enableRaster_Text_ = iTrue; | ||
57 | |||
58 | enum iGlyphFlag { | 56 | enum 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 | ||
532 | iLocalDef SDL_Rect sdlRect_(const iRect rect) { | 525 | iLocalDef 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 | ||
581 | static iBool cache_Font_(const iFont *d, iGlyph *glyph, int hoff) { | 575 | static 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 | ||
605 | iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | 600 | iLocalDef 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 | ||
647 | static void doRaster_Font_(const iFont *font, iGlyph *glyph) { | 643 | static 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 | ||
659 | static const iGlyph *glyph_Font_(iFont *d, iChar ch) { | 656 | static 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 | ||
699 | iDeclareType(RasterGlyph) | ||
700 | |||
701 | struct Impl_RasterGlyph { | ||
702 | iGlyph *glyph; | ||
703 | int hoff; | ||
704 | iRect rect; | ||
705 | }; | ||
706 | |||
707 | void 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 | |||
705 | enum iRunMode { | 822 | enum 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 | ||
1122 | void cache_Text(int fontId, iRangecc text) { | ||
1123 | cacheTextGlyphs_Font_(font_Text_(fontId), text); | ||
1124 | } | ||
1125 | |||
1002 | static int runFlagsFromId_(enum iFontId fontId) { | 1126 | static 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 | ||
1062 | static void drawBounded_Text_(int fontId, iInt2 pos, int xposBound, int color, iRangecc text) { | 1186 | static 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 | ||
193 | void setOpacity_Text (float opacity); | 193 | void setOpacity_Text (float opacity); |
194 | 194 | ||
195 | void cache_Text (int fontId, iRangecc text); /* pre-render glyphs */ | ||
196 | |||
195 | void draw_Text (int fontId, iInt2 pos, int color, const char *text, ...); | 197 | void draw_Text (int fontId, iInt2 pos, int color, const char *text, ...); |
196 | void drawAlign_Text (int fontId, iInt2 pos, int color, enum iAlignment align, const char *text, ...); | 198 | void drawAlign_Text (int fontId, iInt2 pos, int color, enum iAlignment align, const char *text, ...); |
197 | void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...); | 199 | void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...); |