diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-04-14 16:19:42 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-04-15 10:23:06 +0300 |
commit | fefbd1c259c912fe126a5f34245a8b4c494cb753 (patch) | |
tree | 4f1add012361fc9c8430ee43987562252d123bb9 /src/ui/documentwidget.c | |
parent | d3b4292242dd938503a31ba68066d53b29f364a4 (diff) |
Progressive document rendering
VisBuf now guarantees that all the buffers are sequentially ordered. This ensure complete utilization of all the buffers. Previously some buffers were not used at all, or allocated to the same origin as another one (!).
DocumentWidget is able to progressively fill up a buffer while scrolling. After any change to the visible region has been detected (and there are no ongoing changes), prerender the remaining runs one at a time. This solves the issue where one text run would be always unnecessarily rendered while scrolling, even if the scroll distance was just one pixel.
Diffstat (limited to 'src/ui/documentwidget.c')
-rw-r--r-- | src/ui/documentwidget.c | 276 |
1 files changed, 213 insertions, 63 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 70499ed5..ddca557b 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -20,7 +20,7 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | 20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ |
22 | 22 | ||
23 | /* TODO: This file is a little too large. DocumentWidget could be split into | 23 | /* TODO: This file is a little (!) too large. DocumentWidget could be split into |
24 | a couple of smaller objects. One for rendering the document, for instance. */ | 24 | a couple of smaller objects. One for rendering the document, for instance. */ |
25 | 25 | ||
26 | #include "documentwidget.h" | 26 | #include "documentwidget.h" |
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
41 | #include "labelwidget.h" | 41 | #include "labelwidget.h" |
42 | #include "media.h" | 42 | #include "media.h" |
43 | #include "paint.h" | 43 | #include "paint.h" |
44 | #include "periodic.h" | ||
44 | #include "mediaui.h" | 45 | #include "mediaui.h" |
45 | #include "scrollwidget.h" | 46 | #include "scrollwidget.h" |
46 | #include "touch.h" | 47 | #include "touch.h" |
@@ -156,12 +157,14 @@ struct Impl_DrawBufs { | |||
156 | int flags; | 157 | int flags; |
157 | SDL_Texture * sideIconBuf; | 158 | SDL_Texture * sideIconBuf; |
158 | iTextBuf * timestampBuf; | 159 | iTextBuf * timestampBuf; |
160 | iRangei lastVis; | ||
159 | }; | 161 | }; |
160 | 162 | ||
161 | static void init_DrawBufs(iDrawBufs *d) { | 163 | static void init_DrawBufs(iDrawBufs *d) { |
162 | d->flags = 0; | 164 | d->flags = 0; |
163 | d->sideIconBuf = NULL; | 165 | d->sideIconBuf = NULL; |
164 | d->timestampBuf = NULL; | 166 | d->timestampBuf = NULL; |
167 | iZap(d->lastVis); | ||
165 | } | 168 | } |
166 | 169 | ||
167 | static void deinit_DrawBufs(iDrawBufs *d) { | 170 | static void deinit_DrawBufs(iDrawBufs *d) { |
@@ -175,9 +178,23 @@ iDefineTypeConstruction(DrawBufs) | |||
175 | 178 | ||
176 | /*----------------------------------------------------------------------------------------------*/ | 179 | /*----------------------------------------------------------------------------------------------*/ |
177 | 180 | ||
181 | iDeclareType(VisBufMeta) | ||
182 | |||
183 | struct Impl_VisBufMeta { | ||
184 | iGmRunRange runsDrawn; | ||
185 | }; | ||
186 | |||
187 | static void visBufInvalidated_(iVisBuf *d, size_t index) { | ||
188 | iVisBufMeta *meta = d->buffers[index].user; | ||
189 | iZap(meta->runsDrawn); | ||
190 | } | ||
191 | |||
192 | /*----------------------------------------------------------------------------------------------*/ | ||
193 | |||
178 | static void animate_DocumentWidget_ (void *ticker); | 194 | static void animate_DocumentWidget_ (void *ticker); |
179 | static void animateMedia_DocumentWidget_ (iDocumentWidget *d); | 195 | static void animateMedia_DocumentWidget_ (iDocumentWidget *d); |
180 | static void updateSideIconBuf_DocumentWidget_ (const iDocumentWidget *d); | 196 | static void updateSideIconBuf_DocumentWidget_ (const iDocumentWidget *d); |
197 | static void prerender_DocumentWidget_ (iAny *); | ||
181 | 198 | ||
182 | static const int smoothDuration_DocumentWidget_ = 600; /* milliseconds */ | 199 | static const int smoothDuration_DocumentWidget_ = 600; /* milliseconds */ |
183 | static const int outlineMinWidth_DocumentWdiget_ = 45; /* times gap_UI */ | 200 | static const int outlineMinWidth_DocumentWdiget_ = 45; /* times gap_UI */ |
@@ -251,8 +268,8 @@ struct Impl_DocumentWidget { | |||
251 | const iGmRun * hoverAltPre; /* for drawing alt text */ | 268 | const iGmRun * hoverAltPre; /* for drawing alt text */ |
252 | const iGmRun * hoverLink; | 269 | const iGmRun * hoverLink; |
253 | const iGmRun * contextLink; | 270 | const iGmRun * contextLink; |
254 | const iGmRun * firstVisibleRun; | 271 | iGmRunRange visibleRuns; |
255 | const iGmRun * lastVisibleRun; | 272 | iGmRunRange renderRuns; |
256 | iClick click; | 273 | iClick click; |
257 | iInt2 contextPos; /* coordinates of latest right click */ | 274 | iInt2 contextPos; /* coordinates of latest right click */ |
258 | iString pendingGotoHeading; | 275 | iString pendingGotoHeading; |
@@ -265,6 +282,7 @@ struct Impl_DocumentWidget { | |||
265 | iWidget * playerMenu; | 282 | iWidget * playerMenu; |
266 | iWidget * copyMenu; | 283 | iWidget * copyMenu; |
267 | iVisBuf * visBuf; | 284 | iVisBuf * visBuf; |
285 | iGmRunRange * visBufMeta; | ||
268 | iPtrSet * invalidRuns; | 286 | iPtrSet * invalidRuns; |
269 | iDrawBufs * drawBufs; /* dynamic state for drawing */ | 287 | iDrawBufs * drawBufs; /* dynamic state for drawing */ |
270 | iTranslation * translation; | 288 | iTranslation * translation; |
@@ -306,10 +324,17 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
306 | d->hoverAltPre = NULL; | 324 | d->hoverAltPre = NULL; |
307 | d->hoverLink = NULL; | 325 | d->hoverLink = NULL; |
308 | d->contextLink = NULL; | 326 | d->contextLink = NULL; |
309 | d->firstVisibleRun = NULL; | 327 | iZap(d->renderRuns); |
310 | d->lastVisibleRun = NULL; | 328 | iZap(d->visibleRuns); |
311 | d->visBuf = new_VisBuf(); | 329 | d->visBuf = new_VisBuf(); { |
312 | d->invalidRuns = new_PtrSet(); | 330 | d->visBufMeta = malloc(sizeof(iVisBufMeta) * numBuffers_VisBuf); |
331 | /* Additional metadata for each buffer. */ | ||
332 | d->visBuf->bufferInvalidated = visBufInvalidated_; | ||
333 | for (size_t i = 0; i < numBuffers_VisBuf; i++) { | ||
334 | d->visBuf->buffers[i].user = d->visBufMeta + i; | ||
335 | } | ||
336 | } | ||
337 | d->invalidRuns = new_PtrSet(); | ||
313 | init_Anim(&d->sideOpacity, 0); | 338 | init_Anim(&d->sideOpacity, 0); |
314 | init_Anim(&d->altTextOpacity, 0); | 339 | init_Anim(&d->altTextOpacity, 0); |
315 | d->sourceStatus = none_GmStatusCode; | 340 | d->sourceStatus = none_GmStatusCode; |
@@ -349,9 +374,12 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
349 | 374 | ||
350 | void deinit_DocumentWidget(iDocumentWidget *d) { | 375 | void deinit_DocumentWidget(iDocumentWidget *d) { |
351 | removeTicker_App(animate_DocumentWidget_, d); | 376 | removeTicker_App(animate_DocumentWidget_, d); |
377 | removeTicker_App(prerender_DocumentWidget_, d); | ||
378 | remove_Periodic(periodic_App(), d); | ||
352 | delete_Translation(d->translation); | 379 | delete_Translation(d->translation); |
353 | delete_DrawBufs(d->drawBufs); | 380 | delete_DrawBufs(d->drawBufs); |
354 | delete_VisBuf(d->visBuf); | 381 | delete_VisBuf(d->visBuf); |
382 | free(d->visBufMeta); | ||
355 | delete_PtrSet(d->invalidRuns); | 383 | delete_PtrSet(d->invalidRuns); |
356 | iRelease(d->media); | 384 | iRelease(d->media); |
357 | iRelease(d->request); | 385 | iRelease(d->request); |
@@ -486,10 +514,10 @@ static iRangei visibleRange_DocumentWidget_(const iDocumentWidget *d) { | |||
486 | static void addVisible_DocumentWidget_(void *context, const iGmRun *run) { | 514 | static void addVisible_DocumentWidget_(void *context, const iGmRun *run) { |
487 | iDocumentWidget *d = context; | 515 | iDocumentWidget *d = context; |
488 | if (~run->flags & decoration_GmRunFlag && !run->mediaId) { | 516 | if (~run->flags & decoration_GmRunFlag && !run->mediaId) { |
489 | if (!d->firstVisibleRun) { | 517 | if (!d->visibleRuns.start) { |
490 | d->firstVisibleRun = run; | 518 | d->visibleRuns.start = run; |
491 | } | 519 | } |
492 | d->lastVisibleRun = run; | 520 | d->visibleRuns.end = run; |
493 | } | 521 | } |
494 | if (run->preId) { | 522 | if (run->preId) { |
495 | pushBack_PtrArray(&d->visiblePre, run); | 523 | pushBack_PtrArray(&d->visiblePre, run); |
@@ -743,14 +771,14 @@ static void animateMedia_DocumentWidget_(iDocumentWidget *d) { | |||
743 | 771 | ||
744 | static iRangecc currentHeading_DocumentWidget_(const iDocumentWidget *d) { | 772 | static iRangecc currentHeading_DocumentWidget_(const iDocumentWidget *d) { |
745 | iRangecc heading = iNullRange; | 773 | iRangecc heading = iNullRange; |
746 | if (d->firstVisibleRun) { | 774 | if (d->visibleRuns.start) { |
747 | iConstForEach(Array, i, headings_GmDocument(d->doc)) { | 775 | iConstForEach(Array, i, headings_GmDocument(d->doc)) { |
748 | const iGmHeading *head = i.value; | 776 | const iGmHeading *head = i.value; |
749 | if (head->level == 0) { | 777 | if (head->level == 0) { |
750 | if (head->text.start <= d->firstVisibleRun->text.start) { | 778 | if (head->text.start <= d->visibleRuns.start->text.start) { |
751 | heading = head->text; | 779 | heading = head->text; |
752 | } | 780 | } |
753 | if (d->lastVisibleRun && head->text.start > d->lastVisibleRun->text.start) { | 781 | if (d->visibleRuns.end && head->text.start > d->visibleRuns.end->text.start) { |
754 | break; | 782 | break; |
755 | } | 783 | } |
756 | } | 784 | } |
@@ -766,6 +794,14 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) { | |||
766 | !isSuccess_GmStatusCode(d->sourceStatus)); | 794 | !isSuccess_GmStatusCode(d->sourceStatus)); |
767 | const iRangei visRange = visibleRange_DocumentWidget_(d); | 795 | const iRangei visRange = visibleRange_DocumentWidget_(d); |
768 | const iRect bounds = bounds_Widget(as_Widget(d)); | 796 | const iRect bounds = bounds_Widget(as_Widget(d)); |
797 | if (!equal_Rangei(visRange, d->drawBufs->lastVis)) { | ||
798 | /* After scrolling/resizing stops, begin prendering the visbuf contents. | ||
799 | `Periodic` is used for detecting when the visible range has been modified. */ | ||
800 | removeTicker_App(prerender_DocumentWidget_, d); | ||
801 | remove_Periodic(periodic_App(), d); | ||
802 | add_Periodic(periodic_App(), d, | ||
803 | format_CStr("document.render from:%d to:%d", visRange.start, visRange.end)); | ||
804 | } | ||
769 | setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax_DocumentWidget_(d) }); | 805 | setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax_DocumentWidget_(d) }); |
770 | const int docSize = size_GmDocument(d->doc).y; | 806 | const int docSize = size_GmDocument(d->doc).y; |
771 | setThumb_ScrollWidget(d->scroll, | 807 | setThumb_ScrollWidget(d->scroll, |
@@ -777,7 +813,7 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) { | |||
777 | clear_PtrArray(&d->visibleMedia); | 813 | clear_PtrArray(&d->visibleMedia); |
778 | const iRangecc oldHeading = currentHeading_DocumentWidget_(d); | 814 | const iRangecc oldHeading = currentHeading_DocumentWidget_(d); |
779 | /* Scan for visible runs. */ { | 815 | /* Scan for visible runs. */ { |
780 | d->firstVisibleRun = NULL; | 816 | iZap(d->visibleRuns); |
781 | render_GmDocument(d->doc, visRange, addVisible_DocumentWidget_, d); | 817 | render_GmDocument(d->doc, visRange, addVisible_DocumentWidget_, d); |
782 | } | 818 | } |
783 | const iRangecc newHeading = currentHeading_DocumentWidget_(d); | 819 | const iRangecc newHeading = currentHeading_DocumentWidget_(d); |
@@ -898,8 +934,8 @@ static void documentRunsInvalidated_DocumentWidget_(iDocumentWidget *d) { | |||
898 | d->hoverAltPre = NULL; | 934 | d->hoverAltPre = NULL; |
899 | d->hoverLink = NULL; | 935 | d->hoverLink = NULL; |
900 | d->contextLink = NULL; | 936 | d->contextLink = NULL; |
901 | d->firstVisibleRun = NULL; | 937 | iZap(d->visibleRuns); |
902 | d->lastVisibleRun = NULL; | 938 | iZap(d->renderRuns); |
903 | } | 939 | } |
904 | 940 | ||
905 | void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { | 941 | void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { |
@@ -1675,7 +1711,7 @@ static iBool updateDocumentWidthRetainingScrollPosition_DocumentWidget_(iDocumen | |||
1675 | } | 1711 | } |
1676 | /* Font changes (i.e., zooming) will keep the view centered, otherwise keep the top | 1712 | /* Font changes (i.e., zooming) will keep the view centered, otherwise keep the top |
1677 | of the visible area fixed. */ | 1713 | of the visible area fixed. */ |
1678 | const iGmRun *run = keepCenter ? middleRun_DocumentWidget_(d) : d->firstVisibleRun; | 1714 | const iGmRun *run = keepCenter ? middleRun_DocumentWidget_(d) : d->visibleRuns.start; |
1679 | const char * runLoc = (run ? run->text.start : NULL); | 1715 | const char * runLoc = (run ? run->text.start : NULL); |
1680 | int voffset = 0; | 1716 | int voffset = 0; |
1681 | if (!keepCenter && run) { | 1717 | if (!keepCenter && run) { |
@@ -1727,7 +1763,17 @@ static iBool handlePinch_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | |||
1727 | 1763 | ||
1728 | static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | 1764 | static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { |
1729 | iWidget *w = as_Widget(d); | 1765 | iWidget *w = as_Widget(d); |
1730 | if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "font.changed")) { | 1766 | if (equal_Command(cmd, "document.render")) { |
1767 | const iRangei vis = { argLabel_Command(cmd, "from"), argLabel_Command(cmd, "to") }; | ||
1768 | const iRangei current = visibleRange_DocumentWidget_(d); | ||
1769 | if (equal_Rangei(vis, current)) { | ||
1770 | remove_Periodic(periodic_App(), d); | ||
1771 | /* Scrolling has stopped, begin filling up the buffer. */ | ||
1772 | addTicker_App(prerender_DocumentWidget_, d); | ||
1773 | } | ||
1774 | return iTrue; | ||
1775 | } | ||
1776 | else if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "font.changed")) { | ||
1731 | /* Alt/Option key may be involved in window size changes. */ | 1777 | /* Alt/Option key may be involved in window size changes. */ |
1732 | setLinkNumberMode_DocumentWidget_(d, iFalse); | 1778 | setLinkNumberMode_DocumentWidget_(d, iFalse); |
1733 | d->phoneToolbar = findWidget_App("toolbar"); | 1779 | d->phoneToolbar = findWidget_App("toolbar"); |
@@ -3103,6 +3149,8 @@ iDeclareType(DrawContext) | |||
3103 | struct Impl_DrawContext { | 3149 | struct Impl_DrawContext { |
3104 | const iDocumentWidget *widget; | 3150 | const iDocumentWidget *widget; |
3105 | iRect widgetBounds; | 3151 | iRect widgetBounds; |
3152 | iRect docBounds; | ||
3153 | iRangei vis; | ||
3106 | iInt2 viewPos; /* document area origin */ | 3154 | iInt2 viewPos; /* document area origin */ |
3107 | iPaint paint; | 3155 | iPaint paint; |
3108 | iBool inSelectMark; | 3156 | iBool inSelectMark; |
@@ -3110,6 +3158,7 @@ struct Impl_DrawContext { | |||
3110 | iBool showLinkNumbers; | 3158 | iBool showLinkNumbers; |
3111 | iRect firstMarkRect; | 3159 | iRect firstMarkRect; |
3112 | iRect lastMarkRect; | 3160 | iRect lastMarkRect; |
3161 | iGmRunRange runsDrawn; | ||
3113 | }; | 3162 | }; |
3114 | 3163 | ||
3115 | static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iColorId color, | 3164 | static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iColorId color, |
@@ -3269,6 +3318,14 @@ static void drawBannerRun_DrawContext_(iDrawContext *d, const iGmRun *run, iInt2 | |||
3269 | static void drawRun_DrawContext_(void *context, const iGmRun *run) { | 3318 | static void drawRun_DrawContext_(void *context, const iGmRun *run) { |
3270 | iDrawContext *d = context; | 3319 | iDrawContext *d = context; |
3271 | const iInt2 origin = d->viewPos; | 3320 | const iInt2 origin = d->viewPos; |
3321 | /* Keep track of the drawn visible runs. */ { | ||
3322 | if (!d->runsDrawn.start || run < d->runsDrawn.start) { | ||
3323 | d->runsDrawn.start = run; | ||
3324 | } | ||
3325 | if (!d->runsDrawn.end || run > d->runsDrawn.end) { | ||
3326 | d->runsDrawn.end = run; | ||
3327 | } | ||
3328 | } | ||
3272 | if (run->mediaType == image_GmRunMediaType) { | 3329 | if (run->mediaType == image_GmRunMediaType) { |
3273 | SDL_Texture *tex = imageTexture_Media(media_GmDocument(d->widget->doc), run->mediaId); | 3330 | SDL_Texture *tex = imageTexture_Media(media_GmDocument(d->widget->doc), run->mediaId); |
3274 | const iRect dst = moved_Rect(run->visBounds, origin); | 3331 | const iRect dst = moved_Rect(run->visBounds, origin); |
@@ -3291,6 +3348,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
3291 | /* Media UIs are drawn afterwards as a dynamic overlay. */ | 3348 | /* Media UIs are drawn afterwards as a dynamic overlay. */ |
3292 | return; | 3349 | return; |
3293 | } | 3350 | } |
3351 | // printf(" drawRun: {%s}\n", cstr_Rangecc(run->text)); | ||
3294 | enum iColorId fg = run->color; | 3352 | enum iColorId fg = run->color; |
3295 | const iGmDocument *doc = d->widget->doc; | 3353 | const iGmDocument *doc = d->widget->doc; |
3296 | iBool isHover = | 3354 | iBool isHover = |
@@ -3644,48 +3702,100 @@ static void drawPin_(iPaint *p, iRect rangeRect, int dir) { | |||
3644 | init1_I2(gap_UI * 2)), pinColor); | 3702 | init1_I2(gap_UI * 2)), pinColor); |
3645 | } | 3703 | } |
3646 | 3704 | ||
3647 | static void draw_DocumentWidget_(const iDocumentWidget *d) { | 3705 | static iBool render_DocumentWidget_(const iDocumentWidget *d, iDrawContext *ctx, iBool prerenderExtra) { |
3648 | const iWidget *w = constAs_Widget(d); | 3706 | iBool didDraw = iFalse; |
3649 | const iRect bounds = bounds_Widget(w); | 3707 | const iRect bounds = bounds_Widget(constAs_Widget(d)); |
3650 | iVisBuf * visBuf = d->visBuf; /* will be updated now */ | ||
3651 | if (width_Rect(bounds) <= 0) { | ||
3652 | return; | ||
3653 | } | ||
3654 | draw_Widget(w); | ||
3655 | if (d->drawBufs->flags & updateTimestampBuf_DrawBufsFlag) { | ||
3656 | updateTimestampBuf_DocumentWidget_(d); | ||
3657 | } | ||
3658 | if (d->drawBufs->flags & updateSideBuf_DrawBufsFlag) { | ||
3659 | updateSideIconBuf_DocumentWidget_(d); | ||
3660 | } | ||
3661 | allocVisBuffer_DocumentWidget_(d); | ||
3662 | const iRect ctxWidgetBounds = init_Rect( | 3708 | const iRect ctxWidgetBounds = init_Rect( |
3663 | 0, 0, width_Rect(bounds) - constAs_Widget(d->scroll)->rect.size.x, height_Rect(bounds)); | 3709 | 0, 0, width_Rect(bounds) - constAs_Widget(d->scroll)->rect.size.x, height_Rect(bounds)); |
3664 | const iRect docBounds = documentBounds_DocumentWidget_(d); | ||
3665 | iDrawContext ctx = { | ||
3666 | .widget = d, | ||
3667 | .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0, | ||
3668 | }; | ||
3669 | const iBool isTouchSelecting = (flags_Widget(w) & touchDrag_WidgetFlag) != 0; | ||
3670 | /* Currently visible region. */ | ||
3671 | const iRangei vis = visibleRange_DocumentWidget_(d); | ||
3672 | const iRangei full = { 0, size_GmDocument(d->doc).y }; | 3710 | const iRangei full = { 0, size_GmDocument(d->doc).y }; |
3711 | const iRangei vis = ctx->vis; | ||
3712 | iVisBuf *visBuf = d->visBuf; /* will be updated now */ | ||
3713 | /* Swap buffers around to have room available both before and after the visible region. */ | ||
3714 | allocVisBuffer_DocumentWidget_(d); | ||
3673 | reposition_VisBuf(visBuf, vis); | 3715 | reposition_VisBuf(visBuf, vis); |
3674 | iRangei invalidRange[iElemCount(d->visBuf->buffers)]; | ||
3675 | invalidRanges_VisBuf(visBuf, full, invalidRange); | ||
3676 | /* Redraw the invalid ranges. */ { | 3716 | /* Redraw the invalid ranges. */ { |
3677 | iPaint *p = &ctx.paint; | 3717 | iPaint *p = &ctx->paint; |
3678 | init_Paint(p); | 3718 | init_Paint(p); |
3679 | iForIndices(i, visBuf->buffers) { | 3719 | iForIndices(i, visBuf->buffers) { |
3680 | iVisBufTexture *buf = &visBuf->buffers[i]; | 3720 | iVisBufTexture *buf = &visBuf->buffers[i]; |
3681 | ctx.widgetBounds = moved_Rect(ctxWidgetBounds, init_I2(0, -buf->origin)); | 3721 | iVisBufMeta * meta = buf->user; |
3682 | ctx.viewPos = init_I2(left_Rect(docBounds) - left_Rect(bounds), -buf->origin); | 3722 | const iRangei bufRange = bufferRange_VisBuf(visBuf, i); |
3683 | if (!isEmpty_Rangei(invalidRange[i])) { | 3723 | const iRangei bufVisRange = intersect_Rangei(bufRange, vis); |
3684 | beginTarget_Paint(p, buf->texture); | 3724 | ctx->widgetBounds = moved_Rect(ctxWidgetBounds, init_I2(0, -buf->origin)); |
3725 | ctx->viewPos = init_I2(left_Rect(ctx->docBounds) - left_Rect(bounds), -buf->origin); | ||
3726 | //printf(" buffer %zu: invalid range %d...%d\n", i, invalidRange[i].start, invalidRange[i].end); | ||
3727 | if (!prerenderExtra && !isEmpty_Range(&bufVisRange)) { | ||
3728 | didDraw = iTrue; | ||
3685 | if (isEmpty_Rangei(buf->validRange)) { | 3729 | if (isEmpty_Rangei(buf->validRange)) { |
3686 | fillRect_Paint(p, (iRect){ zero_I2(), visBuf->texSize }, tmBackground_ColorId); | 3730 | /* Fill the required currently visible range (vis). */ |
3731 | const iRangei bufVisRange = intersect_Rangei(bufRange, vis); | ||
3732 | if (!isEmpty_Range(&bufVisRange)) { | ||
3733 | beginTarget_Paint(p, buf->texture); | ||
3734 | fillRect_Paint(p, (iRect){ zero_I2(), visBuf->texSize }, tmBackground_ColorId); | ||
3735 | iZap(ctx->runsDrawn); | ||
3736 | render_GmDocument(d->doc, bufVisRange, drawRun_DrawContext_, ctx); | ||
3737 | meta->runsDrawn = ctx->runsDrawn; | ||
3738 | meta->runsDrawn.start--; | ||
3739 | meta->runsDrawn.end++; | ||
3740 | buf->validRange = bufVisRange; | ||
3741 | // printf(" buffer %zu valid %d...%d\n", i, bufRange.start, bufRange.end); | ||
3742 | } | ||
3743 | } | ||
3744 | else { | ||
3745 | /* Progressively fill the required runs. */ | ||
3746 | if (meta->runsDrawn.start) { | ||
3747 | beginTarget_Paint(p, buf->texture); | ||
3748 | meta->runsDrawn.start = renderProgressive_GmDocument(d->doc, meta->runsDrawn.start, | ||
3749 | -1, iInvalidSize, | ||
3750 | bufVisRange, | ||
3751 | drawRun_DrawContext_, | ||
3752 | ctx); | ||
3753 | buf->validRange.start = bufVisRange.start; | ||
3754 | } | ||
3755 | if (meta->runsDrawn.end) { | ||
3756 | beginTarget_Paint(p, buf->texture); | ||
3757 | meta->runsDrawn.end = renderProgressive_GmDocument(d->doc, meta->runsDrawn.end, | ||
3758 | +1, iInvalidSize, | ||
3759 | bufVisRange, | ||
3760 | drawRun_DrawContext_, | ||
3761 | ctx); | ||
3762 | buf->validRange.end = bufVisRange.end; | ||
3763 | } | ||
3764 | } | ||
3765 | } | ||
3766 | /* Progressively draw the rest of the buffer if it isn't fully valid. */ | ||
3767 | if (prerenderExtra && !equal_Rangei(bufRange, buf->validRange)) { | ||
3768 | const iGmRun *next; | ||
3769 | if (meta->runsDrawn.start) { | ||
3770 | const iRangei upper = intersect_Rangei(bufRange, (iRangei){ full.start, buf->validRange.start }); | ||
3771 | if (upper.end > upper.start) { | ||
3772 | beginTarget_Paint(p, buf->texture); | ||
3773 | next = renderProgressive_GmDocument(d->doc, meta->runsDrawn.start, | ||
3774 | -1, 1, upper, | ||
3775 | drawRun_DrawContext_, | ||
3776 | ctx); | ||
3777 | if (meta->runsDrawn.start != next) { | ||
3778 | meta->runsDrawn.start = next; | ||
3779 | didDraw = iTrue; | ||
3780 | } | ||
3781 | buf->validRange.start = bufRange.start; | ||
3782 | } | ||
3783 | } | ||
3784 | if (meta->runsDrawn.end) { | ||
3785 | const iRangei lower = intersect_Rangei(bufRange, (iRangei){ buf->validRange.end, full.end }); | ||
3786 | if (lower.end > lower.start) { | ||
3787 | beginTarget_Paint(p, buf->texture); | ||
3788 | next = renderProgressive_GmDocument(d->doc, meta->runsDrawn.end, | ||
3789 | +1, 1, lower, | ||
3790 | drawRun_DrawContext_, | ||
3791 | ctx); | ||
3792 | if (meta->runsDrawn.end != next) { | ||
3793 | meta->runsDrawn.end = next; | ||
3794 | didDraw = iTrue; | ||
3795 | } | ||
3796 | buf->validRange.end = bufRange.end; | ||
3797 | } | ||
3687 | } | 3798 | } |
3688 | render_GmDocument(d->doc, invalidRange[i], drawRun_DrawContext_, &ctx); | ||
3689 | } | 3799 | } |
3690 | /* Draw any invalidated runs that fall within this buffer. */ { | 3800 | /* Draw any invalidated runs that fall within this buffer. */ { |
3691 | const iRangei bufRange = { buf->origin, buf->origin + visBuf->texSize.y }; | 3801 | const iRangei bufRange = { buf->origin, buf->origin + visBuf->texSize.y }; |
@@ -3694,7 +3804,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
3694 | const iGmRun *run = *r.value; | 3804 | const iGmRun *run = *r.value; |
3695 | if (isOverlapping_Rangei(bufRange, ySpan_Rect(run->visBounds))) { | 3805 | if (isOverlapping_Rangei(bufRange, ySpan_Rect(run->visBounds))) { |
3696 | beginTarget_Paint(p, buf->texture); | 3806 | beginTarget_Paint(p, buf->texture); |
3697 | fillRect_Paint(&ctx.paint, | 3807 | fillRect_Paint(p, |
3698 | init_Rect(0, | 3808 | init_Rect(0, |
3699 | run->visBounds.pos.y - buf->origin, | 3809 | run->visBounds.pos.y - buf->origin, |
3700 | visBuf->texSize.x, | 3810 | visBuf->texSize.x, |
@@ -3707,21 +3817,61 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
3707 | const iGmRun *run = *r.value; | 3817 | const iGmRun *run = *r.value; |
3708 | if (isOverlapping_Rangei(bufRange, ySpan_Rect(run->visBounds))) { | 3818 | if (isOverlapping_Rangei(bufRange, ySpan_Rect(run->visBounds))) { |
3709 | beginTarget_Paint(p, buf->texture); | 3819 | beginTarget_Paint(p, buf->texture); |
3710 | drawRun_DrawContext_(&ctx, run); | 3820 | drawRun_DrawContext_(ctx, run); |
3711 | } | 3821 | } |
3712 | } | 3822 | } |
3713 | } | 3823 | } |
3714 | endTarget_Paint(&ctx.paint); | 3824 | endTarget_Paint(p); |
3715 | } | 3825 | } |
3716 | validate_VisBuf(visBuf); | ||
3717 | clear_PtrSet(d->invalidRuns); | 3826 | clear_PtrSet(d->invalidRuns); |
3718 | } | 3827 | } |
3828 | return didDraw; | ||
3829 | } | ||
3830 | |||
3831 | static void prerender_DocumentWidget_(iAny *context) { | ||
3832 | const iDocumentWidget *d = context; | ||
3833 | iDrawContext ctx = { | ||
3834 | .widget = d, | ||
3835 | .docBounds = documentBounds_DocumentWidget_(d), | ||
3836 | .vis = visibleRange_DocumentWidget_(d), | ||
3837 | .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0 | ||
3838 | }; | ||
3839 | // printf("%u prerendering\n", SDL_GetTicks()); | ||
3840 | if (render_DocumentWidget_(d, &ctx, iTrue /* just fill up progressively */)) { | ||
3841 | /* Something was drawn, should check if there is still more to do. */ | ||
3842 | addTicker_App(prerender_DocumentWidget_, context); | ||
3843 | } | ||
3844 | } | ||
3845 | |||
3846 | static void draw_DocumentWidget_(const iDocumentWidget *d) { | ||
3847 | const iWidget *w = constAs_Widget(d); | ||
3848 | const iRect bounds = bounds_Widget(w); | ||
3849 | if (width_Rect(bounds) <= 0) { | ||
3850 | return; | ||
3851 | } | ||
3852 | draw_Widget(w); | ||
3853 | if (d->drawBufs->flags & updateTimestampBuf_DrawBufsFlag) { | ||
3854 | updateTimestampBuf_DocumentWidget_(d); | ||
3855 | } | ||
3856 | if (d->drawBufs->flags & updateSideBuf_DrawBufsFlag) { | ||
3857 | updateSideIconBuf_DocumentWidget_(d); | ||
3858 | } | ||
3859 | const iRect docBounds = documentBounds_DocumentWidget_(d); | ||
3860 | const iRangei vis = visibleRange_DocumentWidget_(d); | ||
3861 | iDrawContext ctx = { | ||
3862 | .widget = d, | ||
3863 | .docBounds = docBounds, | ||
3864 | .vis = vis, | ||
3865 | .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0, | ||
3866 | }; | ||
3867 | render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */); | ||
3719 | setClip_Paint(&ctx.paint, bounds); | 3868 | setClip_Paint(&ctx.paint, bounds); |
3720 | const int yTop = docBounds.pos.y - value_Anim(&d->scrollY); | 3869 | const int yTop = docBounds.pos.y - value_Anim(&d->scrollY); |
3721 | draw_VisBuf(visBuf, init_I2(bounds.pos.x, yTop)); | 3870 | draw_VisBuf(d->visBuf, init_I2(bounds.pos.x, yTop)); |
3722 | /* Text markers. */ | 3871 | /* Text markers. */ |
3872 | const iBool isTouchSelecting = (flags_Widget(w) & touchDrag_WidgetFlag) != 0; | ||
3723 | if (!isEmpty_Range(&d->foundMark) || !isEmpty_Range(&d->selectMark)) { | 3873 | if (!isEmpty_Range(&d->foundMark) || !isEmpty_Range(&d->selectMark)) { |
3724 | SDL_Renderer *render = renderer_Window(get_Window()); | 3874 | SDL_Renderer *render = renderer_Window(get_Window()); |
3725 | ctx.firstMarkRect = zero_Rect(); | 3875 | ctx.firstMarkRect = zero_Rect(); |
3726 | ctx.lastMarkRect = zero_Rect(); | 3876 | ctx.lastMarkRect = zero_Rect(); |
3727 | SDL_SetRenderDrawBlendMode(render, | 3877 | SDL_SetRenderDrawBlendMode(render, |
@@ -3729,15 +3879,15 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
3729 | : SDL_BLENDMODE_BLEND); | 3879 | : SDL_BLENDMODE_BLEND); |
3730 | ctx.viewPos = topLeft_Rect(docBounds); | 3880 | ctx.viewPos = topLeft_Rect(docBounds); |
3731 | /* Marker starting outside the visible range? */ | 3881 | /* Marker starting outside the visible range? */ |
3732 | if (d->firstVisibleRun) { | 3882 | if (d->visibleRuns.start) { |
3733 | if (!isEmpty_Range(&d->selectMark) && | 3883 | if (!isEmpty_Range(&d->selectMark) && |
3734 | d->selectMark.start < d->firstVisibleRun->text.start && | 3884 | d->selectMark.start < d->visibleRuns.start->text.start && |
3735 | d->selectMark.end > d->firstVisibleRun->text.start) { | 3885 | d->selectMark.end > d->visibleRuns.start->text.start) { |
3736 | ctx.inSelectMark = iTrue; | 3886 | ctx.inSelectMark = iTrue; |
3737 | } | 3887 | } |
3738 | if (isEmpty_Range(&d->foundMark) && | 3888 | if (isEmpty_Range(&d->foundMark) && |
3739 | d->foundMark.start < d->firstVisibleRun->text.start && | 3889 | d->foundMark.start < d->visibleRuns.start->text.start && |
3740 | d->foundMark.end > d->firstVisibleRun->text.start) { | 3890 | d->foundMark.end > d->visibleRuns.start->text.start) { |
3741 | ctx.inFoundMark = iTrue; | 3891 | ctx.inFoundMark = iTrue; |
3742 | } | 3892 | } |
3743 | } | 3893 | } |