Alchemy
Loading...
Searching...
No Matches

measure.c

Go to the documentation of this file.
1/// @file
2/// Sizing windows to their text and fitting them on the screen.
3/// Part of window.c, the window object, compiled only through it.
4
6
7#if TBS_PATCH_2
8
9#define FIT_FIXED_LIMIT 1
10#endif
11
12/// Sizes a window for decoded text no and moves (*px, *py) so the window fits
13/// on the screen. flags: bit 0 measures every page (UiText_MeasureAllPages)
14/// rather than the first; bit 1 takes the size in *pw and *ph as given, in
15/// tiles, instead of measuring. Measuring returns the size in tiles in *pw and
16/// *ph and fills spacing with each line's extra character gap.
17void UiWindow_FitOnScreen(s32 entryIndex, s32 *windowX, s32 *windowY,
18 u32 *windowWidth, u32 *windowHeight, u16 *spacing,
19 u32 flags) {
20#if !defined(FIT_FIXED_LIMIT)
21 u8 *base;
22#endif
23 s32 x;
24 s32 y;
25 s32 rightLimit;
26 s32 right;
27 s32 bottom;
28 s32 overflow;
29 s32 fittedPosition;
30
31#if !defined(FIT_FIXED_LIMIT)
32 base = gWindowWork[0];
33#endif
34 x = *windowX;
35 y = *windowY;
36 rightLimit = 30;
37
38 if (!(flags & 2)) {
39 if (flags & 1)
40 UiText_MeasureAllPages(entryIndex, windowWidth, windowHeight, spacing);
41 else
42 UiText_MeasureEntryDimensions(entryIndex, windowWidth, windowHeight,
43 spacing);
44 }
45
46 if (*windowWidth == 0 && *windowHeight == 0)
47 return;
48
49 // Pixels to tiles, with room for the frame and a margin.
50 if (!(flags & 2)) {
51 *windowWidth = (*windowWidth + 19) >> 3;
52 *windowHeight = (*windowHeight + 15) >> 3;
53#if !defined(FIT_FIXED_LIMIT)
54 if (((struct UiRenderWork *)base)->mode != 0) {
55 *windowWidth += 2;
56 rightLimit = 29;
57 }
58#endif
59 }
60
61 // Slide the window back onto the screen (limit tiles by 20).
62 right = x + *windowWidth;
63 if (right > rightLimit) {
64 overflow = right - rightLimit;
65 fittedPosition = x - overflow;
66 if (fittedPosition >= 0)
67 x = fittedPosition;
68 else
69 x = 0;
70 }
71 bottom = y + *windowHeight;
72 if (bottom > 20) {
73 overflow = bottom - 20;
74 fittedPosition = y - overflow;
75 if (fittedPosition >= 0)
76 y = fittedPosition;
77 else
78 y = 0;
79 }
80 if (x < 0)
81 x = 0;
82 if (y < 0)
83 y = 0;
84 if (x > rightLimit - *windowWidth)
85 x = rightLimit - *windowWidth;
86 if (y > 20 - *windowHeight)
87 y = 20 - *windowHeight;
88 *windowX = x;
89 *windowY = y;
90}
91
92/// Decodes message no and returns its text's width and height in pixels.
95 (u32 *)y, 0);
96}
97
100
101/// Measures decoded text from entryIndex to its first page's end: returns the
102/// width of the longest line and the height (15 pixels a line) in pixels. With
103/// spacing set, fills one entry per line with the extra gap between characters,
104/// in 1/256 pixel, that spreads the line to fill the window. spacing, when
105/// non-NULL, has room for four line entries.
106void UiText_MeasureEntryDimensions(s32 entryIndex, u32 *outWidth,
107 u32 *outHeight, u16 *spacing) {
108 struct UiRenderWork *work;
109 u32 lines;
110 u32 width;
111 u32 height;
112 u32 lineWidth;
113 u32 count;
114 u32 code;
115 u32 glyph;
116 u32 lineIndex;
117 u16 lineSpacingCounts[4];
118 u16 lineWidths[4];
119 s32 gap;
120 u32 style;
121#if (LANGUAGE == LANGUAGE_JAPANESE)
122 s32 trailing;
123#endif
124
125 work = UI_WORK;
126#if (LANGUAGE == LANGUAGE_JAPANESE)
127 trailing = 0;
128#endif
129 width = 0;
130 height = 15;
131 lines = 0;
132 lineWidth = 0;
133 count = 0;
134 for (;;) {
135 code = work->entries[entryIndex];
136 entryIndex = (entryIndex + 1) & RENDER_ENTRY_MASK;
137 // A character adds its width. Spaces (in Japanese every character) are
138 // counted as the points the line can be stretched at.
139 if (code > 31) {
140#if (LANGUAGE != LANGUAGE_JAPANESE)
141 if (code == 32) {
142 lineWidth += 5;
143 count++;
144 } else {
145 glyph = gUiText_Glyphs[code - 32].width;
146 style = work->outline;
147 if (style == 1 || style == 5)
148 glyph++;
149 lineWidth += glyph;
150 }
151#else
152 if (code != TEXT_VOICED_MARK && code != TEXT_SEMIVOICED_MARK) {
153 if (code == 32) {
154 lineWidth += 7;
155 } else {
156 // Punctuation at the end of a line takes less room than its cell.
157 if (code == 0xa5)
158 trailing = -1;
159 else if (code == 0x21)
160 trailing = -3;
161 else if (code == 0xa1 || code == 0xa4)
162 trailing = -6;
163 else
164 trailing = 0;
165 glyph = 10;
166 if (code <= 0xff)
167 glyph = gUiText_Glyphs[code - 32].width;
168 style = work->outline;
169 if (style == 1 || style == 5)
170 glyph++;
171 lineWidth += glyph;
172 }
173 }
174 count++;
175#endif
176 continue;
177 }
178 switch (code) {
179 case TEXT_END:
180#if !(LANGUAGE == LANGUAGE_SPANISH) && !(LANGUAGE == LANGUAGE_FRENCH) && \
181 !(LANGUAGE == LANGUAGE_ITALIAN)
182 case TEXT_PAGE:
183#endif
184 goto done;
185 case TEXT_NEWLINE:
186#if (LANGUAGE != LANGUAGE_JAPANESE)
187 count++;
188#else
189 lineWidth += trailing;
190#endif
191 lineSpacingCounts[lines] = count;
192 lineWidths[lines] = lineWidth;
193 if (width < lineWidth)
194 width = lineWidth;
195 if (lines < 3)
196 lines++;
197 count = 0;
198 lineWidth = 0;
199 height += 15;
200 break;
201#if TBS_PATCH_3
202 case TEXT_PAGE:
203 work->outline = 0;
204 goto done;
205#endif
206 case 14:
207 case 28:
208 entryIndex = (entryIndex + 1) & RENDER_ENTRY_MASK;
209 // Skip the second operand too.
210 // Fall through.
211 case TEXT_COLOUR:
214 case 17:
215 entryIndex = (entryIndex + 1) & RENDER_ENTRY_MASK;
216 break;
217 case TEXT_OUTLINE:
218 work->outline = work->entries[entryIndex];
219 entryIndex = (entryIndex + 1) & RENDER_ENTRY_MASK;
220 break;
221 }
222 }
223done:
224#if (LANGUAGE != LANGUAGE_JAPANESE)
225 count++;
226#else
227 lineWidth += trailing;
228#endif
229 lineSpacingCounts[lines] = count;
230 lineWidths[lines] = lineWidth;
231 if (width < lineWidth)
232 width = lineWidth;
233#if (LANGUAGE == LANGUAGE_JAPANESE) || (LANGUAGE == LANGUAGE_ENGLISH)
234 if (work->mode)
235 width = width + 2;
236#endif
237 *outWidth = width;
238 *outHeight = height;
239 // Each line's extra gap between characters, in 1/256 pixel, to spread it
240 // across the window's inner width.
241 width = (width + 19) >> 3;
242 width = (width << 3) - 16;
243 if (spacing != NULL) {
244 lineIndex = 0;
245 while (lineIndex <= lines) {
246 if (lineSpacingCounts[lineIndex] <= 1) {
247 *spacing++ = 0;
248 } else {
249 gap = width - lineWidths[lineIndex];
250 gap -= 4;
251 if (gap < 0)
252 gap = 0;
253 gap = ((gap << 8) / (lineSpacingCounts[lineIndex] - 1));
254#if (LANGUAGE != LANGUAGE_JAPANESE)
255 if (gap < 0 || gap > 0xc00)
256 gap = 0x200;
257#else
258 if (gap < 0 || gap > 0x800)
259 gap = 0;
260 if (gap < 0 || gap > 0x100)
261 gap = 0x100;
262#endif
263 *spacing++ = gap;
264 }
265 lineIndex++;
266 }
267 }
268}
269
270/// Measures decoded text from start over all its pages: the width of the first
271/// page's longest line and the height of its tallest page, in pixels, with each
272/// line's extra character gap in spacing as UiText_MeasureEntryDimensions.
273/// The text must fit 16 page-height slots; this routine does not clamp the page
274/// count.
275void UiText_MeasureAllPages(s32 entryIndex, u32 *outWidth, u32 *outHeight,
276 u16 *spacing) {
277 u32 pageHeights[16];
278 u16 lineSpacingCounts[4];
279 u16 lineWidths[4];
280 struct UiRenderWork *work;
281 u32 maxWidth;
282 u32 line;
283 u32 width;
284 u32 count;
285 u32 page;
286 u32 code;
287 u32 glyph;
288 u32 outline;
289 u32 pageIndex;
290 u32 lineIndex;
291 s32 gap;
292#if (LANGUAGE == LANGUAGE_JAPANESE)
293 s32 trailing;
294
295 trailing = 0;
296#endif
297 work = UI_WORK;
298 pageHeights[0] = 15;
299 pageHeights[1] = 15;
300 pageHeights[2] = 15;
301 pageHeights[3] = 15;
302 pageHeights[4] = 15;
303 pageHeights[5] = 15;
304 pageHeights[6] = 15;
305 pageHeights[7] = 15;
306 pageHeights[8] = 15;
307 pageHeights[9] = 15;
308 pageHeights[10] = 15;
309 pageHeights[11] = 15;
310 pageHeights[12] = 15;
311 maxWidth = 0;
312 pageHeights[13] = 15;
313 pageHeights[14] = 15;
314 pageHeights[15] = 15;
315 line = 0;
316 width = 0;
317 count = 0;
318 page = 0;
319 for (;;) {
320 code = work->entries[entryIndex];
321 entryIndex = (entryIndex + 1) & RENDER_ENTRY_MASK;
322 if (code >= 32) {
323#if (LANGUAGE != LANGUAGE_JAPANESE)
324 if (code == 32) {
325 width += 5;
326 count++;
327 } else {
328 glyph = gUiText_Glyphs[code - 32].width;
329 outline = work->outline;
330 if (outline == 1 || outline == 5)
331 glyph++;
332 width += glyph;
333 }
334#else
335 if (code != TEXT_VOICED_MARK && code != TEXT_SEMIVOICED_MARK) {
336 if (code == 32) {
337 width += 7;
338 } else {
339 if (code == 0xa5)
340 trailing = -1;
341 else if (code == 0x21)
342 trailing = -3;
343 else if (code == 0xa1 || code == 0xa4)
344 trailing = -6;
345 else
346 trailing = 0;
347 glyph = 10;
348 if (code <= 0xff)
349 glyph = gUiText_Glyphs[code - 32].width;
350 outline = work->outline;
351 if (outline == 1 || outline == 5)
352 glyph++;
353 width += glyph;
354 }
355 }
356 count++;
357#endif
358 continue;
359 }
360 switch (code) {
361 case TEXT_NEWLINE:
362#if (LANGUAGE != LANGUAGE_JAPANESE)
363 lineSpacingCounts[line] = ++count;
364#else
365 width += trailing;
366 lineSpacingCounts[line] = count;
367#endif
368 lineWidths[line] = width;
369 if (page == 0 && maxWidth < width)
370 maxWidth = width;
371 if (line < 3)
372 line++;
373 count = 0;
374 width = 0;
375 pageHeights[page] += 15;
376 break;
377 case 0:
378#if (LANGUAGE != LANGUAGE_JAPANESE)
379 lineSpacingCounts[line] = ++count;
380#else
381 width += trailing;
382 lineSpacingCounts[line] = count;
383#endif
384 lineWidths[line] = width;
385 if (page == 0 && maxWidth < width)
386 maxWidth = width;
387 page++;
388 goto done;
389 case TEXT_PAGE:
390#if (LANGUAGE != LANGUAGE_JAPANESE)
391 lineSpacingCounts[line] = ++count;
392#else
393 width += trailing;
394 lineSpacingCounts[line] = count;
395#endif
396 lineWidths[line] = width;
397 if (page == 0 && maxWidth < width)
398 maxWidth = width;
399#if TBS_PATCH_3
400 work->outline = 0;
401#endif
402 page++;
403 break;
404 case 14:
405 case 28:
406 entryIndex = (entryIndex + 1) & RENDER_ENTRY_MASK;
407 // Skip the second operand too.
408 // Fall through.
409 case TEXT_COLOUR:
412 case 17:
413 entryIndex = (entryIndex + 1) & RENDER_ENTRY_MASK;
414 break;
415 case TEXT_OUTLINE:
416 work->outline = work->entries[entryIndex];
417 entryIndex = (entryIndex + 1) & RENDER_ENTRY_MASK;
418 break;
419 }
420 }
421done:
422#if !(LANGUAGE == LANGUAGE_GERMAN) && !(LANGUAGE == LANGUAGE_FRENCH)
423 if (work->mode != 0)
424 maxWidth += 2;
425#endif
426 for (pageIndex = 0; pageIndex < page; pageIndex++) {
427 if (pageIndex == 0)
428 *outHeight = pageHeights[0];
429 else if (*outHeight < pageHeights[pageIndex])
430 *outHeight = pageHeights[pageIndex];
431 }
432 *outWidth = maxWidth;
433 maxWidth = (((maxWidth + 19) >> 3) << 3) - 16;
434 if (spacing != NULL) {
435 for (lineIndex = 0; lineIndex <= line; lineIndex++) {
436 if (lineSpacingCounts[lineIndex] <= 1) {
437 *spacing++ = 0;
438 } else {
439 gap = maxWidth - lineWidths[lineIndex];
440 gap -= 4;
441 if (gap < 0)
442 gap = 0;
443 gap = (gap << 8) / (lineSpacingCounts[lineIndex] - 1);
444#if (LANGUAGE == LANGUAGE_JAPANESE)
445 if (gap < 0 || gap > 0x800)
446 gap = 0;
447 if (gap < 0 || gap > 0x100)
448 gap = 0x100;
449#endif
450 *spacing++ = gap;
451 }
452 }
453 }
454}
s32 UiText_BuildRenderEntries(s32 script, s32 clear)
Decodes message script into the ring of decoded text (UiRenderWork.entries) and returns where it star...
Definition decode.c:276
const struct FontGlyph gUiText_Glyphs[]
void UiText_MeasureAllPages(s32 entryIndex, u32 *outWidth, u32 *outHeight, u16 *spacing)
Measures decoded text from start over all its pages: the width of the first page's longest line and t...
Definition measure.c:275
void UiText_MeasureEntryDimensions(s32 entryIndex, u32 *outWidth, u32 *outHeight, u16 *spacing)
Measures decoded text from entryIndex to its first page's end: returns the width of the longest line ...
Definition measure.c:106
void UiText_MeasureResourceEntries(s32 message, s32 *x, s32 *y)
Decodes message no and returns its text's width and height in pixels.
Definition measure.c:93
void UiWindow_FitOnScreen(s32 entryIndex, s32 *windowX, s32 *windowY, u32 *windowWidth, u32 *windowHeight, u16 *spacing, u32 flags)
Sizes a window for decoded text no and moves (*px, *py) so the window fits on the screen.
Definition measure.c:17
#define NULL
Definition bilibin.h:6
u8 gWindowWork[]
u16 width
pixels
Definition glyph.h:29
The window system's work: the windows' tilemap, the windows, the three text channels,...
Definition window.h:19
u8 mode
Set while Mind Read shows thoughts: round frame corners, colour 8 text with no shadow,...
Definition window.h:35
u16 entries[RENDER_ENTRY_MASK+1]
decoded text, a ring of codes
Definition window.h:46
u16 count
where the next code goes in entries
Definition window.h:48
u16 outline
the current channel's, copied in to draw
Definition window.h:44
#define RENDER_ENTRY_MASK
Definition tbs_edition.h:68
signed int s32
Definition types.h:14
unsigned int u32
Definition types.h:15
unsigned short u16
Definition types.h:13
unsigned char u8
Definition types.h:11
s32 UiText_GetResourceDimensions(s32 no, s32 *x, s32 *y, u32 *width, u32 *height)
#define UI_WORK
Definition window.h:267
s32 UiText_GetResourceDimensionsAlt(s32 no, s32 *x, s32 *y, u32 *width, u32 *height)
#define DEFINE_GET_RESOURCE_DIMENSIONS(name, flags)
Defines name(no, x, y, width, height): decodes message no and fits a window for it on the screen near...
Definition window.h:274
What every part of the window object shares: its includes, edition values, macros and the codes of de...
@ TEXT_WINDOW_STATE
the next entry is the window effect to run
@ TEXT_END
@ TEXT_OUTLINE
the next entry is the outline colour
@ TEXT_VOICED_MARK
dakuten
@ TEXT_PAGE
wait for a button, then clear the window for a new page
@ TEXT_SEMIVOICED_MARK
handakuten
@ TEXT_NEWLINE
@ TEXT_COLOUR
the next entry is the text colour
@ TEXT_LINE_SPACING
the next entry is the line spacing