1 /* Copyright 2020 Jaakko Keränen <jaakko.keranen@iki.fi>
2
3 Redistribution and use in source and binary forms, with or without
4 modification, are permitted provided that the following conditions are met:
5
6 1. Redistributions of source code must retain the above copyright notice, this
7 list of conditions and the following disclaimer.
8 2. Redistributions in binary form must reproduce the above copyright notice,
9 this list of conditions and the following disclaimer in the documentation
10 and/or other materials provided with the distribution.
11
12 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
13 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
16 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19 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
21 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
22
23 #include "visbuf.h"
24 #include "window.h"
25 #include "util.h"
26
iDefineTypeConstruction(VisBuf)27 iDefineTypeConstruction(VisBuf)
28
29 void init_VisBuf(iVisBuf *d) {
30 d->texSize = zero_I2();
31 iZap(d->buffers);
32 iZap(d->vis);
33 d->bufferInvalidated = NULL;
34 }
35
deinit_VisBuf(iVisBuf * d)36 void deinit_VisBuf(iVisBuf *d) {
37 dealloc_VisBuf(d);
38 }
39
invalidate_VisBuf(iVisBuf * d)40 void invalidate_VisBuf(iVisBuf *d) {
41 int origin = iMax(0, d->vis.start - d->texSize.y);
42 iForIndices(i, d->buffers) {
43 d->buffers[i].origin = origin;
44 origin += d->texSize.y;
45 iZap(d->buffers[i].validRange);
46 if (d->bufferInvalidated) {
47 d->bufferInvalidated(d, i);
48 }
49 }
50 }
51
alloc_VisBuf(iVisBuf * d,const iInt2 size,int granularity)52 void alloc_VisBuf(iVisBuf *d, const iInt2 size, int granularity) {
53 const iInt2 texSize = init_I2(size.x, (size.y / 2 / granularity + 1) * granularity);
54 if (!d->buffers[0].texture || !isEqual_I2(texSize, d->texSize)) {
55 d->texSize = texSize;
56 iForIndices(i, d->buffers) {
57 iVisBufTexture *tex = &d->buffers[i];
58 if (tex->texture) {
59 SDL_DestroyTexture(tex->texture);
60 }
61 SDL_Renderer *rend = renderer_Window(get_Window());
62 tex->texture =
63 SDL_CreateTexture(rend,
64 SDL_PIXELFORMAT_RGBA8888,
65 SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET,
66 texSize.x,
67 texSize.y);
68 SDL_SetTextureBlendMode(tex->texture, SDL_BLENDMODE_NONE);
69 }
70 invalidate_VisBuf(d);
71 }
72 }
73
dealloc_VisBuf(iVisBuf * d)74 void dealloc_VisBuf(iVisBuf *d) {
75 d->texSize = zero_I2();
76 iForIndices(i, d->buffers) {
77 SDL_DestroyTexture(d->buffers[i].texture);
78 d->buffers[i].texture = NULL;
79 }
80 }
81
82 #if 0
83 static size_t findMostDistant_VisBuf_(const iVisBuf *d, const size_t *avail, size_t numAvail,
84 const iRangei vis) {
85 size_t chosen = 0;
86 int distChosen = iAbsi(d->buffers[0].origin - vis.start);
87 printf(" avail (got %zu): %zu", numAvail, avail[0]);
88 for (size_t i = 1; i < numAvail; i++) {
89 printf(" %zu", avail[i]);
90 const int dist = iAbsi(d->buffers[i].origin - vis.start);
91 if (dist > distChosen) {
92 chosen = i;
93 distChosen = dist;
94 }
95 }
96 printf("\n chose index %zu (%d)\n", chosen, distChosen);
97 return chosen;
98 }
99
100 static size_t take_(size_t *avail, size_t *numAvail, size_t index) {
101 const size_t value = avail[index];
102 memmove(avail + index, avail + index + 1, sizeof(size_t) * (*numAvail - index - 1));
103 (*numAvail)--;
104 return value;
105 }
106 #endif
107
roll_VisBuf_(iVisBuf * d,int dir)108 static void roll_VisBuf_(iVisBuf *d, int dir) {
109 const size_t lastPos = iElemCount(d->buffers) - 1;
110 if (dir < 0) {
111 /* Last buffer is moved to the beginning. */
112 SDL_Texture *last = d->buffers[lastPos].texture;
113 void * user = d->buffers[lastPos].user;
114 memmove(d->buffers + 1, d->buffers, sizeof(iVisBufTexture) * lastPos);
115 d->buffers[0].texture = last;
116 d->buffers[0].user = user;
117 d->buffers[0].origin = d->buffers[1].origin - d->texSize.y;
118 iZap(d->buffers[0].validRange);
119 if (d->bufferInvalidated) {
120 d->bufferInvalidated(d, 0);
121 }
122 }
123 else {
124 /* First buffer is moved to the end. */
125 SDL_Texture *first = d->buffers[0].texture;
126 void * user = d->buffers[0].user;
127 memmove(d->buffers, d->buffers + 1, sizeof(iVisBufTexture) * lastPos);
128 d->buffers[lastPos].texture = first;
129 d->buffers[lastPos].user = user;
130 d->buffers[lastPos].origin = d->buffers[lastPos - 1].origin + d->texSize.y;
131 iZap(d->buffers[lastPos].validRange);
132 if (d->bufferInvalidated) {
133 d->bufferInvalidated(d, lastPos);
134 }
135 }
136 }
137
reposition_VisBuf(iVisBuf * d,const iRangei vis)138 iBool reposition_VisBuf(iVisBuf *d, const iRangei vis) {
139 if (equal_Rangei(vis, d->vis)) {
140 return iFalse;
141 }
142 const int moveDir = vis.end > d->vis.end ? +1 : -1;
143 d->vis = vis;
144 iBool wasChanged = iFalse;
145 const size_t lastPos = iElemCount(d->buffers) - 1;
146 if (d->buffers[0].origin > vis.end || d->buffers[lastPos].origin + d->texSize.y <= vis.start) {
147 /* All buffers outside the visible region. */
148 invalidate_VisBuf(d);
149 wasChanged = iTrue;
150 }
151 else {
152 /* Check for mandatory rolls. */
153 while (d->buffers[0].origin > vis.start) {
154 roll_VisBuf_(d, -1);
155 wasChanged = iTrue;
156 }
157 if (!wasChanged) {
158 while (d->buffers[lastPos].origin + d->texSize.y < vis.end) {
159 roll_VisBuf_(d, +1);
160 wasChanged = iTrue;
161 }
162 }
163 /* Scroll-direction dependent optional rolls, with a bit of overscroll allowed. */
164 if (moveDir > 0 && d->buffers[0].origin + d->texSize.y + d->texSize.y / 4 < vis.start) {
165 roll_VisBuf_(d, +1);
166 wasChanged = iTrue;
167 }
168 else if (moveDir < 0 && d->buffers[lastPos].origin - d->texSize.y / 4 > vis.end) {
169 roll_VisBuf_(d, -1);
170 wasChanged = iTrue;
171 }
172 }
173 #if 0
174 if (wasChanged) {
175 printf("\nVISIBLE RANGE: %d ... %d\n", vis.start, vis.end);
176 iForIndices(i, d->buffers) {
177 const iVisBufTexture *bt = &d->buffers[i];
178 printf(" %zu: buf %5d ... %5d valid %5d ... %5d\n", i, bt->origin,
179 bt->origin + d->texSize.y,
180 bt->validRange.start,
181 bt->validRange.end);
182 }
183 fflush(stdout);
184 }
185 #endif
186 #if !defined (NDEBUG)
187 /* Buffers must not overlap. */
188 iForIndices(m, d->buffers) {
189 const iRangei M = { d->buffers[m].origin, d->buffers[m].origin + d->texSize.y };
190 iForIndices(n, d->buffers) {
191 if (m == n) continue;
192 const iRangei N = { d->buffers[n].origin, d->buffers[n].origin + d->texSize.y };
193 const iRangei is = intersect_Rangei(M, N);
194 if (size_Range(&is) != 0) {
195 printf("buffers %zu (%i) and %zu (%i) overlap\n",
196 m, M.start, n, N.start);
197 fflush(stdout);
198 }
199 iAssert(size_Range(&is) == 0);
200 }
201 }
202 #endif
203 return iTrue; /* at least the visible range changed */
204 }
205
allocRange_VisBuf(const iVisBuf * d)206 iRangei allocRange_VisBuf(const iVisBuf *d) {
207 return (iRangei){ d->buffers[0].origin,
208 d->buffers[iElemCount(d->buffers) - 1].origin + d->texSize.y };
209 }
210
bufferRange_VisBuf(const iVisBuf * d,size_t index)211 iRangei bufferRange_VisBuf(const iVisBuf *d, size_t index) {
212 return (iRangei){ d->buffers[index].origin, d->buffers[index].origin + d->texSize.y };
213 }
214
invalidRanges_VisBuf(const iVisBuf * d,const iRangei full,iRangei * out_invalidRanges)215 void invalidRanges_VisBuf(const iVisBuf *d, const iRangei full, iRangei *out_invalidRanges) {
216 iForIndices(i, d->buffers) {
217 const iVisBufTexture *buf = d->buffers + i;
218 const iRangei before = { full.start, buf->validRange.start };
219 const iRangei after = { buf->validRange.end, full.end };
220 const iRangei region = intersect_Rangei(d->vis, (iRangei){ buf->origin,
221 buf->origin + d->texSize.y });
222 out_invalidRanges[i] = intersect_Rangei(before, region);
223 if (isEmpty_Rangei(out_invalidRanges[i])) {
224 out_invalidRanges[i] = intersect_Rangei(after, region);
225 }
226 }
227 }
228
validate_VisBuf(iVisBuf * d)229 void validate_VisBuf(iVisBuf *d) {
230 iForIndices(i, d->buffers) {
231 iVisBufTexture *buf = &d->buffers[i];
232 buf->validRange =
233 intersect_Rangei(d->vis, (iRangei){ buf->origin, buf->origin + d->texSize.y });
234 }
235 }
236
237 //#define DEBUG_SCALE 0.5f
238
draw_VisBuf(const iVisBuf * d,const iInt2 topLeft,const iRangei yClipBounds)239 void draw_VisBuf(const iVisBuf *d, const iInt2 topLeft, const iRangei yClipBounds) {
240 SDL_Renderer *render = renderer_Window(get_Window());
241 iForIndices(i, d->buffers) {
242 const iVisBufTexture *buf = d->buffers + i;
243 SDL_Rect dst = { topLeft.x,
244 topLeft.y + buf->origin,
245 d->texSize.x,
246 d->texSize.y };
247 if (dst.y >= yClipBounds.end || dst.y + dst.h < yClipBounds.start) {
248 #if !defined (DEBUG_SCALE)
249 continue; /* Outside the clipping area. */
250 #endif
251 }
252 #if defined (DEBUG_SCALE)
253 dst.w *= DEBUG_SCALE;
254 dst.h *= DEBUG_SCALE;
255 dst.x *= DEBUG_SCALE;
256 dst.y *= DEBUG_SCALE;
257 dst.x += get_Window()->root->rect.size.x / 4;
258 dst.y += get_Window()->root->rect.size.y / 4;
259 #endif
260 SDL_RenderCopy(render, buf->texture, NULL, &dst);
261 #if defined (DEBUG_SCALE)
262 SDL_SetRenderDrawColor(render, 0, 0, 255, 255);
263 SDL_RenderDrawRect(render, &dst);
264 #endif
265 }
266 #if defined (DEBUG_SCALE)
267 SDL_Rect dst = { topLeft.x, yClipBounds.start, d->texSize.x, 2 * d->texSize.y };
268 dst.w *= DEBUG_SCALE;
269 dst.h *= DEBUG_SCALE;
270 dst.x *= DEBUG_SCALE;
271 dst.y *= DEBUG_SCALE;
272 dst.x += get_Window()->root->rect.size.x / 4;
273 dst.y += get_Window()->root->rect.size.y / 4;
274 SDL_SetRenderDrawColor(render, 255, 255, 255, 255);
275 SDL_RenderDrawRect(render, &dst);
276 #endif
277 }
278