diff options
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/ui/listwidget.c | 131 | ||||
-rw-r--r-- | src/ui/visbuf.c | 116 | ||||
-rw-r--r-- | src/ui/visbuf.h | 30 |
4 files changed, 179 insertions, 100 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 46d418e8..f0968831 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -104,6 +104,8 @@ set (SOURCES | |||
104 | src/ui/text.h | 104 | src/ui/text.h |
105 | src/ui/util.c | 105 | src/ui/util.c |
106 | src/ui/util.h | 106 | src/ui/util.h |
107 | src/ui/visbuf.c | ||
108 | src/ui/visbuf.h | ||
107 | src/ui/window.c | 109 | src/ui/window.c |
108 | src/ui/window.h | 110 | src/ui/window.h |
109 | # Widgets: | 111 | # Widgets: |
diff --git a/src/ui/listwidget.c b/src/ui/listwidget.c index 5cb2754f..e0047179 100644 --- a/src/ui/listwidget.c +++ b/src/ui/listwidget.c | |||
@@ -25,6 +25,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
25 | #include "paint.h" | 25 | #include "paint.h" |
26 | #include "util.h" | 26 | #include "util.h" |
27 | #include "command.h" | 27 | #include "command.h" |
28 | #include "visbuf.h" | ||
28 | 29 | ||
29 | #include <the_Foundation/intset.h> | 30 | #include <the_Foundation/intset.h> |
30 | 31 | ||
@@ -44,22 +45,6 @@ iDefineClass(ListItem) | |||
44 | 45 | ||
45 | iDefineObjectConstruction(ListWidget) | 46 | iDefineObjectConstruction(ListWidget) |
46 | 47 | ||
47 | enum iBufferValidity { | ||
48 | none_BufferValidity, | ||
49 | partial_BufferValidity, | ||
50 | full_BufferValidity, | ||
51 | }; | ||
52 | |||
53 | #define numVisBuffers_ListWidget_ 3 | ||
54 | |||
55 | iDeclareType(ListVisBuffer) | ||
56 | |||
57 | struct Impl_ListVisBuffer { | ||
58 | SDL_Texture *texture; | ||
59 | int origin; | ||
60 | iRangei validRange; | ||
61 | }; | ||
62 | |||
63 | struct Impl_ListWidget { | 48 | struct Impl_ListWidget { |
64 | iWidget widget; | 49 | iWidget widget; |
65 | iScrollWidget *scroll; | 50 | iScrollWidget *scroll; |
@@ -69,8 +54,7 @@ struct Impl_ListWidget { | |||
69 | size_t hoverItem; | 54 | size_t hoverItem; |
70 | iClick click; | 55 | iClick click; |
71 | iIntSet invalidItems; | 56 | iIntSet invalidItems; |
72 | iInt2 visBufSize; | 57 | iVisBuf *visBuf; |
73 | iListVisBuffer visBuffers[numVisBuffers_ListWidget_]; | ||
74 | }; | 58 | }; |
75 | 59 | ||
76 | void init_ListWidget(iListWidget *d) { | 60 | void init_ListWidget(iListWidget *d) { |
@@ -86,22 +70,17 @@ void init_ListWidget(iListWidget *d) { | |||
86 | d->hoverItem = iInvalidPos; | 70 | d->hoverItem = iInvalidPos; |
87 | init_Click(&d->click, d, SDL_BUTTON_LEFT); | 71 | init_Click(&d->click, d, SDL_BUTTON_LEFT); |
88 | init_IntSet(&d->invalidItems); | 72 | init_IntSet(&d->invalidItems); |
89 | d->visBufSize = zero_I2(); | 73 | d->visBuf = new_VisBuf(); |
90 | iZap(d->visBuffers); | ||
91 | } | 74 | } |
92 | 75 | ||
93 | void deinit_ListWidget(iListWidget *d) { | 76 | void deinit_ListWidget(iListWidget *d) { |
94 | clear_ListWidget(d); | 77 | clear_ListWidget(d); |
95 | deinit_PtrArray(&d->items); | 78 | deinit_PtrArray(&d->items); |
96 | iForIndices(i, d->visBuffers) { | 79 | delete_VisBuf(d->visBuf); |
97 | SDL_DestroyTexture(d->visBuffers[i].texture); | ||
98 | } | ||
99 | } | 80 | } |
100 | 81 | ||
101 | void invalidate_ListWidget(iListWidget *d) { | 82 | void invalidate_ListWidget(iListWidget *d) { |
102 | iForIndices(i, d->visBuffers) { | 83 | invalidate_VisBuf(d->visBuf); |
103 | iZap(d->visBuffers[i].validRange); | ||
104 | } | ||
105 | clear_IntSet(&d->invalidItems); /* all will be drawn */ | 84 | clear_IntSet(&d->invalidItems); /* all will be drawn */ |
106 | refresh_Widget(as_Widget(d)); | 85 | refresh_Widget(as_Widget(d)); |
107 | } | 86 | } |
@@ -323,9 +302,10 @@ static iBool processEvent_ListWidget_(iListWidget *d, const SDL_Event *ev) { | |||
323 | return processEvent_Widget(w, ev); | 302 | return processEvent_Widget(w, ev); |
324 | } | 303 | } |
325 | 304 | ||
305 | #if 0 | ||
326 | static void allocVisBuffer_ListWidget_(iListWidget *d) { | 306 | static void allocVisBuffer_ListWidget_(iListWidget *d) { |
327 | /* Make sure two buffers cover the entire visible area. */ | 307 | /* Make sure two buffers cover the entire visible area. */ |
328 | const iRect inner = innerBounds_Widget(as_Widget(d)); | 308 | // const iRect inner = innerBounds_Widget(as_Widget(d)); |
329 | const iInt2 size = init_I2(inner.size.x, (inner.size.y / 2 / d->itemHeight + 1) * d->itemHeight); | 309 | const iInt2 size = init_I2(inner.size.x, (inner.size.y / 2 / d->itemHeight + 1) * d->itemHeight); |
330 | if (!d->visBuffers[0].texture || !isEqual_I2(size, d->visBufSize)) { | 310 | if (!d->visBuffers[0].texture || !isEqual_I2(size, d->visBufSize)) { |
331 | d->visBufSize = size; | 311 | d->visBufSize = size; |
@@ -345,6 +325,7 @@ static void allocVisBuffer_ListWidget_(iListWidget *d) { | |||
345 | } | 325 | } |
346 | } | 326 | } |
347 | } | 327 | } |
328 | #endif | ||
348 | 329 | ||
349 | static void drawItem_ListWidget_(const iListWidget *d, iPaint *p, size_t index, iInt2 pos) { | 330 | static void drawItem_ListWidget_(const iListWidget *d, iPaint *p, size_t index, iInt2 pos) { |
350 | const iWidget * w = constAs_Widget(d); | 331 | const iWidget * w = constAs_Widget(d); |
@@ -361,89 +342,48 @@ static const iListItem *item_ListWidget_(const iListWidget *d, size_t pos) { | |||
361 | static void draw_ListWidget_(const iListWidget *d) { | 342 | static void draw_ListWidget_(const iListWidget *d) { |
362 | const iWidget *w = constAs_Widget(d); | 343 | const iWidget *w = constAs_Widget(d); |
363 | const iRect bounds = innerBounds_Widget(w); | 344 | const iRect bounds = innerBounds_Widget(w); |
364 | if (!bounds.size.y || !bounds.size.x) return; | 345 | if (!bounds.size.y || !bounds.size.x || !d->itemHeight) { |
346 | return; | ||
347 | } | ||
365 | iPaint p; | 348 | iPaint p; |
366 | init_Paint(&p); | 349 | init_Paint(&p); |
367 | SDL_Renderer *render = renderer_Window(get_Window()); | ||
368 | drawBackground_Widget(w); | 350 | drawBackground_Widget(w); |
369 | iListWidget *m = iConstCast(iListWidget *, d); | 351 | // iListWidget *m = iConstCast(iListWidget *, d); |
370 | allocVisBuffer_ListWidget_(m); | 352 | alloc_VisBuf(d->visBuf, bounds.size, d->itemHeight); |
371 | /* Update invalid regions/items. */ | 353 | /* Update invalid regions/items. */ { |
372 | /* TODO: This seems to draw two items per each shift of the visible region, even though | 354 | /* TODO: This seems to draw two items per each shift of the visible region, even though |
373 | one should be enough. Probably an off-by-one error in the calculation of the | 355 | one should be enough. Probably an off-by-one error in the calculation of the |
374 | invalid range. */ | 356 | invalid range. */ |
375 | if (d->visBufSize.y > 0) { | 357 | iAssert(d->visBuf->buffers[0].texture); |
376 | iAssert(d->visBuffers[0].texture); | 358 | iAssert(d->visBuf->buffers[1].texture); |
377 | iAssert(d->visBuffers[1].texture); | 359 | iAssert(d->visBuf->buffers[2].texture); |
378 | iAssert(d->visBuffers[2].texture); | ||
379 | const int bg[3] = { w->bgColor, w->bgColor, w->bgColor }; | 360 | const int bg[3] = { w->bgColor, w->bgColor, w->bgColor }; |
380 | // const int bg[3] = { red_ColorId, magenta_ColorId, blue_ColorId }; | 361 | // const int bg[3] = { red_ColorId, magenta_ColorId, blue_ColorId }; |
381 | const int bottom = numItems_ListWidget(d) * d->itemHeight; | 362 | const int bottom = numItems_ListWidget(d) * d->itemHeight; |
382 | const iRangei vis = { d->scrollY / d->itemHeight * d->itemHeight, | 363 | const iRangei vis = { d->scrollY / d->itemHeight * d->itemHeight, |
383 | ((d->scrollY + bounds.size.y) / d->itemHeight + 1) * d->itemHeight }; | 364 | ((d->scrollY + bounds.size.y) / d->itemHeight + 1) * d->itemHeight }; |
384 | iRangei good = { 0, 0 }; | ||
385 | // printf("visBufSize.y = %d\n", d->visBufSize.y); | 365 | // printf("visBufSize.y = %d\n", d->visBufSize.y); |
386 | size_t avail[3], numAvail = 0; | 366 | reposition_VisBuf(d->visBuf, vis); |
387 | /* Check which buffers are available for reuse. */ { | ||
388 | iForIndices(i, d->visBuffers) { | ||
389 | iListVisBuffer *buf = m->visBuffers + i; | ||
390 | const iRangei region = { buf->origin, buf->origin + d->visBufSize.y }; | ||
391 | if (region.start >= vis.end || region.end <= vis.start) { | ||
392 | avail[numAvail++] = i; | ||
393 | iZap(buf->validRange); | ||
394 | } | ||
395 | else { | ||
396 | good = union_Rangei(good, region); | ||
397 | } | ||
398 | } | ||
399 | } | ||
400 | if (numAvail == numVisBuffers_ListWidget_) { | ||
401 | /* All buffers are outside the visible range, do a reset. */ | ||
402 | m->visBuffers[0].origin = vis.start; | ||
403 | m->visBuffers[1].origin = vis.start + d->visBufSize.y; | ||
404 | } | ||
405 | else { | ||
406 | /* Extend to cover the visible range. */ | ||
407 | while (vis.start < good.start) { | ||
408 | iAssert(numAvail > 0); | ||
409 | m->visBuffers[avail[--numAvail]].origin = good.start - d->visBufSize.y; | ||
410 | good.start -= d->visBufSize.y; | ||
411 | } | ||
412 | while (vis.end > good.end) { | ||
413 | iAssert(numAvail > 0); | ||
414 | m->visBuffers[avail[--numAvail]].origin = good.end; | ||
415 | good.end += d->visBufSize.y; | ||
416 | } | ||
417 | } | ||
418 | /* Check which parts are invalid. */ | 367 | /* Check which parts are invalid. */ |
419 | iRangei invalidRange[3]; | 368 | iRangei invalidRange[3]; |
420 | iForIndices(i, d->visBuffers) { | 369 | invalidRanges_VisBuf(d->visBuf, (iRangei){ 0, bottom }, invalidRange); |
421 | const iListVisBuffer *buf = d->visBuffers + i; | 370 | iForIndices(i, d->visBuf->buffers) { |
422 | const iRangei region = intersect_Rangei(vis, (iRangei){ buf->origin, buf->origin + d->visBufSize.y }); | 371 | iVisBufTexture *buf = &d->visBuf->buffers[i]; |
423 | const iRangei before = { 0, buf->validRange.start }; | ||
424 | const iRangei after = { buf->validRange.end, bottom }; | ||
425 | invalidRange[i] = intersect_Rangei(before, region); | ||
426 | if (isEmpty_Rangei(invalidRange[i])) { | ||
427 | invalidRange[i] = intersect_Rangei(after, region); | ||
428 | } | ||
429 | } | ||
430 | iForIndices(i, d->visBuffers) { | ||
431 | iListVisBuffer *buf = m->visBuffers + i; | ||
432 | // printf("%zu: orig %d, invalid %d ... %d\n", i, buf->origin, invalidRange[i].start, invalidRange[i].end); | 372 | // printf("%zu: orig %d, invalid %d ... %d\n", i, buf->origin, invalidRange[i].start, invalidRange[i].end); |
433 | iRanges drawItems = { iMax(0, buf->origin) / d->itemHeight, | 373 | iRanges drawItems = { iMax(0, buf->origin) / d->itemHeight, |
434 | iMax(0, buf->origin + d->visBufSize.y) / d->itemHeight }; | 374 | iMax(0, buf->origin + d->visBuf->texSize.y) / d->itemHeight }; |
435 | iBool isTargetSet = iFalse; | 375 | iBool isTargetSet = iFalse; |
436 | if (isEmpty_Rangei(buf->validRange)) { | 376 | if (isEmpty_Rangei(buf->validRange)) { |
437 | isTargetSet = iTrue; | 377 | isTargetSet = iTrue; |
438 | beginTarget_Paint(&p, buf->texture); | 378 | beginTarget_Paint(&p, buf->texture); |
439 | fillRect_Paint(&p, (iRect){ zero_I2(), d->visBufSize }, bg[i]); | 379 | fillRect_Paint(&p, (iRect){ zero_I2(), d->visBuf->texSize }, bg[i]); |
440 | } | 380 | } |
441 | iConstForEach(IntSet, v, &d->invalidItems) { | 381 | iConstForEach(IntSet, v, &d->invalidItems) { |
442 | const size_t index = *v.value; | 382 | const size_t index = *v.value; |
443 | if (contains_Range(&drawItems, index)) { | 383 | if (contains_Range(&drawItems, index)) { |
444 | const iListItem *item = constAt_PtrArray(&d->items, index); | 384 | const iListItem *item = constAt_PtrArray(&d->items, index); |
445 | const iRect itemRect = { init_I2(0, index * d->itemHeight - buf->origin), | 385 | const iRect itemRect = { init_I2(0, index * d->itemHeight - buf->origin), |
446 | init_I2(d->visBufSize.x, d->itemHeight) }; | 386 | init_I2(d->visBuf->texSize.x, d->itemHeight) }; |
447 | if (!isTargetSet) { | 387 | if (!isTargetSet) { |
448 | beginTarget_Paint(&p, buf->texture); | 388 | beginTarget_Paint(&p, buf->texture); |
449 | isTargetSet = iTrue; | 389 | isTargetSet = iTrue; |
@@ -464,7 +404,7 @@ static void draw_ListWidget_(const iListWidget *d) { | |||
464 | for (size_t j = drawItems.start; j < drawItems.end && j < size_PtrArray(&d->items); j++) { | 404 | for (size_t j = drawItems.start; j < drawItems.end && j < size_PtrArray(&d->items); j++) { |
465 | const iListItem *item = constAt_PtrArray(&d->items, j); | 405 | const iListItem *item = constAt_PtrArray(&d->items, j); |
466 | const iRect itemRect = { init_I2(0, j * d->itemHeight - buf->origin), | 406 | const iRect itemRect = { init_I2(0, j * d->itemHeight - buf->origin), |
467 | init_I2(d->visBufSize.x, d->itemHeight) }; | 407 | init_I2(d->visBuf->texSize.x, d->itemHeight) }; |
468 | fillRect_Paint(&p, itemRect, bg[i]); | 408 | fillRect_Paint(&p, itemRect, bg[i]); |
469 | class_ListItem(item)->draw(item, &p, itemRect, d); | 409 | class_ListItem(item)->draw(item, &p, itemRect, d); |
470 | // printf("- drawing item %zu\n", j); | 410 | // printf("- drawing item %zu\n", j); |
@@ -474,22 +414,13 @@ static void draw_ListWidget_(const iListWidget *d) { | |||
474 | endTarget_Paint(&p); | 414 | endTarget_Paint(&p); |
475 | } | 415 | } |
476 | buf->validRange = | 416 | buf->validRange = |
477 | intersect_Rangei(vis, (iRangei){ buf->origin, buf->origin + d->visBufSize.y }); | 417 | intersect_Rangei(vis, (iRangei){ buf->origin, buf->origin + d->visBuf->texSize.y }); |
478 | // fflush(stdout); | 418 | // fflush(stdout); |
479 | } | 419 | } |
480 | clear_IntSet(&m->invalidItems); | 420 | clear_IntSet(&iConstCast(iListWidget *, d)->invalidItems); |
481 | } | 421 | } |
482 | setClip_Paint(&p, bounds_Widget(w)); | 422 | setClip_Paint(&p, bounds_Widget(w)); |
483 | iForIndices(i, d->visBuffers) { | 423 | draw_VisBuf(d->visBuf, addY_I2(topLeft_Rect(bounds), -d->scrollY)); |
484 | const iListVisBuffer *buf = d->visBuffers + i; | ||
485 | SDL_RenderCopy(render, | ||
486 | buf->texture, | ||
487 | NULL, | ||
488 | &(SDL_Rect){ left_Rect(bounds), | ||
489 | top_Rect(bounds) - d->scrollY + buf->origin, | ||
490 | d->visBufSize.x, | ||
491 | d->visBufSize.y }); | ||
492 | } | ||
493 | unsetClip_Paint(&p); | 424 | unsetClip_Paint(&p); |
494 | drawChildren_Widget(w); | 425 | drawChildren_Widget(w); |
495 | } | 426 | } |
diff --git a/src/ui/visbuf.c b/src/ui/visbuf.c new file mode 100644 index 00000000..82346ff0 --- /dev/null +++ b/src/ui/visbuf.c | |||
@@ -0,0 +1,116 @@ | |||
1 | #include "visbuf.h" | ||
2 | #include "window.h" | ||
3 | #include "util.h" | ||
4 | |||
5 | iDefineTypeConstruction(VisBuf) | ||
6 | |||
7 | void init_VisBuf(iVisBuf *d) { | ||
8 | d->texSize = zero_I2(); | ||
9 | iZap(d->buffers); | ||
10 | } | ||
11 | |||
12 | void deinit_VisBuf(iVisBuf *d) { | ||
13 | dealloc_VisBuf(d); | ||
14 | } | ||
15 | |||
16 | void invalidate_VisBuf(iVisBuf *d) { | ||
17 | iForIndices(i, d->buffers) { | ||
18 | iZap(d->buffers[i].validRange); | ||
19 | } | ||
20 | } | ||
21 | |||
22 | void alloc_VisBuf(iVisBuf *d, const iInt2 size, int granularity) { | ||
23 | const iInt2 texSize = init_I2(size.x, (size.y / 2 / granularity + 1) * granularity); | ||
24 | if (!d->buffers[0].texture || !isEqual_I2(texSize, d->texSize)) { | ||
25 | d->texSize = texSize; | ||
26 | iForIndices(i, d->buffers) { | ||
27 | iVisBufTexture *tex = &d->buffers[i]; | ||
28 | if (tex->texture) { | ||
29 | SDL_DestroyTexture(tex->texture); | ||
30 | } | ||
31 | tex->texture = | ||
32 | SDL_CreateTexture(renderer_Window(get_Window()), | ||
33 | SDL_PIXELFORMAT_RGBA8888, | ||
34 | SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, | ||
35 | texSize.x, | ||
36 | texSize.y); | ||
37 | SDL_SetTextureBlendMode(tex->texture, SDL_BLENDMODE_NONE); | ||
38 | tex->origin = i * texSize.y; | ||
39 | iZap(tex->validRange); | ||
40 | } | ||
41 | } | ||
42 | } | ||
43 | |||
44 | void dealloc_VisBuf(iVisBuf *d) { | ||
45 | d->texSize = zero_I2(); | ||
46 | iForIndices(i, d->buffers) { | ||
47 | SDL_DestroyTexture(d->buffers[i].texture); | ||
48 | d->buffers[i].texture = NULL; | ||
49 | } | ||
50 | } | ||
51 | |||
52 | void reposition_VisBuf(iVisBuf *d, const iRangei vis) { | ||
53 | d->vis = vis; | ||
54 | iRangei good = { 0, 0 }; | ||
55 | size_t avail[3], numAvail = 0; | ||
56 | /* Check which buffers are available for reuse. */ { | ||
57 | iForIndices(i, d->buffers) { | ||
58 | iVisBufTexture *buf = d->buffers + i; | ||
59 | const iRangei region = { buf->origin, buf->origin + d->texSize.y }; | ||
60 | if (region.start >= vis.end || region.end <= vis.start) { | ||
61 | avail[numAvail++] = i; | ||
62 | iZap(buf->validRange); | ||
63 | } | ||
64 | else { | ||
65 | good = union_Rangei(good, region); | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | if (numAvail == iElemCount(d->buffers)) { | ||
70 | /* All buffers are outside the visible range, do a reset. */ | ||
71 | d->buffers[0].origin = vis.start; | ||
72 | d->buffers[1].origin = vis.start + d->texSize.y; | ||
73 | d->buffers[2].origin = vis.start + 2 * d->texSize.y; | ||
74 | } | ||
75 | else { | ||
76 | /* Extend to cover the visible range. */ | ||
77 | while (vis.start < good.start) { | ||
78 | iAssert(numAvail > 0); | ||
79 | d->buffers[avail[--numAvail]].origin = good.start - d->texSize.y; | ||
80 | good.start -= d->texSize.y; | ||
81 | } | ||
82 | while (vis.end > good.end) { | ||
83 | iAssert(numAvail > 0); | ||
84 | d->buffers[avail[--numAvail]].origin = good.end; | ||
85 | good.end += d->texSize.y; | ||
86 | } | ||
87 | } | ||
88 | } | ||
89 | |||
90 | void invalidRanges_VisBuf(const iVisBuf *d, const iRangei full, iRangei *out_invalidRanges) { | ||
91 | iForIndices(i, d->buffers) { | ||
92 | const iVisBufTexture *buf = d->buffers + i; | ||
93 | const iRangei before = { full.start, buf->validRange.start }; | ||
94 | const iRangei after = { buf->validRange.end, full.end }; | ||
95 | const iRangei region = intersect_Rangei(d->vis, (iRangei){ buf->origin, | ||
96 | buf->origin + d->texSize.y }); | ||
97 | out_invalidRanges[i] = intersect_Rangei(before, region); | ||
98 | if (isEmpty_Rangei(out_invalidRanges[i])) { | ||
99 | out_invalidRanges[i] = intersect_Rangei(after, region); | ||
100 | } | ||
101 | } | ||
102 | } | ||
103 | |||
104 | void draw_VisBuf(const iVisBuf *d, iInt2 topLeft) { | ||
105 | SDL_Renderer *render = renderer_Window(get_Window()); | ||
106 | iForIndices(i, d->buffers) { | ||
107 | const iVisBufTexture *buf = d->buffers + i; | ||
108 | SDL_RenderCopy(render, | ||
109 | buf->texture, | ||
110 | NULL, | ||
111 | &(SDL_Rect){ topLeft.x, | ||
112 | topLeft.y + buf->origin, | ||
113 | d->texSize.x, | ||
114 | d->texSize.y }); | ||
115 | } | ||
116 | } | ||
diff --git a/src/ui/visbuf.h b/src/ui/visbuf.h new file mode 100644 index 00000000..80370b29 --- /dev/null +++ b/src/ui/visbuf.h | |||
@@ -0,0 +1,30 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <the_Foundation/range.h> | ||
4 | #include <the_Foundation/vec2.h> | ||
5 | #include <SDL_render.h> | ||
6 | |||
7 | iDeclareType(VisBuf) | ||
8 | iDeclareType(VisBufTexture) | ||
9 | |||
10 | struct Impl_VisBufTexture { | ||
11 | SDL_Texture *texture; | ||
12 | int origin; | ||
13 | iRangei validRange; | ||
14 | }; | ||
15 | |||
16 | struct Impl_VisBuf { | ||
17 | iInt2 texSize; | ||
18 | iRangei vis; | ||
19 | iVisBufTexture buffers[3]; | ||
20 | }; | ||
21 | |||
22 | iDeclareTypeConstruction(VisBuf) | ||
23 | |||
24 | void invalidate_VisBuf (iVisBuf *); | ||
25 | void alloc_VisBuf (iVisBuf *, const iInt2 size, int granularity); | ||
26 | void dealloc_VisBuf (iVisBuf *); | ||
27 | void reposition_VisBuf (iVisBuf *, const iRangei vis); | ||
28 | |||
29 | void invalidRanges_VisBuf (const iVisBuf *, const iRangei full, iRangei *out_invalidRanges); | ||
30 | void draw_VisBuf (const iVisBuf *, iInt2 topLeft); | ||