1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 *
22 * Based on the original sources
23 * Faery Tale II -- The Halls of the Dead
24 * (c) 1993-1996 The Wyrmkeep Entertainment Co.
25 */
26
27 #include "saga2/saga2.h"
28 #include "saga2/gdraw.h"
29
30 namespace Saga2 {
31
32 #define textStyleBar (textStyleUnderBar|textStyleHiLiteBar)
33
34 #define TempAlloc malloc
35 #define TempFree free
36
37 /* ============================================================================ *
38 Text Blitting Routines
39 * ============================================================================ */
40
41 /* Notes:
42
43 These functions attempt to render planar fonts onto a chunky bitmap.
44
45 All non-pointer variables are 16 bits or less. (I don't know how
46 big pointers need to be).
47 */
48
49 /* DrawChar: This function renders a single bitmapped character
50 into an offscreen buffer.
51
52 'drawchar' is the ascii code of the character to be drawn.
53
54 'xpos' is the position in the buffer to draw the outline.
55
56 'baseline' is the address of the first scanline in the buffer.
57 (it can actually be any scanline in the buffer if we want to
58 draw the character at a different y position).
59 Note that this has nothing to do with the Amiga "BASELINE" field,
60 this routine always renders relative to the top of the font.
61
62 'color' is the pixel value to render into the buffer.
63
64 Note that this code does unaligned int16 writes to byte addresses,
65 and will not run on an 68000 or 68010 processor.
66 */
67
DrawChar(gFont * font,int drawchar,int xpos,uint8 * baseline,uint8 color,uint16 destwidth)68 void DrawChar(gFont *font, int drawchar, int xpos, uint8 *baseline, uint8 color,
69 uint16 destwidth) {
70 short w,
71 font_mod; // width of font in bytes
72
73 uint8 *src, // start of char data
74 *dst; // start of dest row
75
76 /*
77 This code assumes that the font characters are all byte-aligned,
78 and that there is no extra junk after the character in the bits
79 that pad to the next byte. Also, the code currently makes no
80 provision for "LoChar" or "HiChar" (i.e. there are blank table
81 entries for characters 0-1f). This can be changed if desired.
82 */
83
84 font_mod = font->rowMod;
85 uint16 offset = font->charXOffset[drawchar];
86 src = &font->fontdata[offset];
87 dst = baseline + xpos;
88
89 for (w = font->charWidth[drawchar]; w > 0; w -= 8) {
90 uint8 *src_row,
91 *dst_row;
92 short h;
93
94 src_row = src;
95 dst_row = dst;
96
97 for (h = font->height; h > 0; h--) {
98 uint8 *b;
99 uint8 s;
100
101 b = dst_row;
102
103 for (s = *src_row; s != 0; s <<= 1) {
104 if ((s & 0x80) != 0)
105 *b = color;
106 ++b;
107 }
108 src_row += font_mod;
109 dst_row += destwidth;
110 }
111
112 ++src;
113 dst += 8;
114 }
115 }
116 /* DrawChar3x3Outline:
117
118 This function renders a single bitmapped character into an offscreen
119 buffer. The character will be "ballooned", i.e. expanded, by 1 pixel
120 in each direction. It does not render the center part of the outlined
121 character in a different color -- that must be done as a seperate
122 step.
123
124 'drawchar' is the ascii code of the character to be drawn.
125
126 'xpos' is the position in the buffer to draw the outline. Note that
127 the first pixel of the outline may be drawn at xpos-1, since the
128 outline expands in both directions.
129
130 'baseline' is the address of the first scanline in the buffer.
131 (it can actually be any scanline in the buffer if we want to
132 draw the character at a different y position).
133 Note that this has nothing to do with the Amiga "BASELINE" field,
134 this routine always renders relative to the top of the font.
135
136 'color' is the pixel value to render into the buffer.
137
138 This code assumes that the font characters are all byte-aligned,
139 and that there is no extra junk after the character in the bits
140 that pad to the next byte. Also, the code currently makes no
141 provision for "LoChar" or "HiChar" (i.e. there are blank table
142 entries for characters 0-1f). This can be changed if desired.
143
144 Note that this code does unaligned int16 writes to byte addresses,
145 and will not run on an 68000 or 68010 processor. (Even with the
146 speed penalty, this is still pretty fast).
147 */
148
149 #define SQUARE_OUTLINES 1
150
DrawChar3x3Outline(gFont * font,int drawchar,int xpos,uint8 * baseline,uint8 color,uint16 destwidth)151 void DrawChar3x3Outline(gFont *font, int drawchar, int xpos, uint8 *baseline,
152 uint8 color, uint16 destwidth) {
153 uint8 *d;
154 short h, // font height counter
155 rowmod;
156
157 short charwidth, w; // width of character in pixels
158
159 uint8 *chardata; // pointer to start of char data
160
161 uint8 *src, *dst;
162 unsigned short s;
163 unsigned short txt1, txt2, txt3;
164
165 // point to the first byte of the first scanline of the source char
166 uint16 offset = font->charXOffset[drawchar];
167 chardata = &font->fontdata[offset];
168
169 // get the width of the character in pixels
170 charwidth = font->charWidth[drawchar];
171
172 // point to the first byte of where we want to place the character
173 baseline += xpos - 1;
174
175 // this loop goes once for each 8 pixels wide that the character is
176 rowmod = font->rowMod;
177
178 for (w = (charwidth + 7) >> 3; w > 0; w--) {
179 src = chardata;
180 dst = baseline;
181
182 txt1 = txt2 = 0;
183
184 for (h = font->height + 2; h; h--) {
185 d = dst;
186
187 txt3 = txt2;
188 txt2 = txt1;
189 txt1 = h > 2 ? *src : 0;
190
191 s = txt1 | txt2 | txt3;
192
193 s = s | (s << 1) | (s << 2);
194
195 while (s) {
196 if (s & 0x200)
197 *d = color;
198 ++d;
199 s <<= 1;
200 }
201
202 src += rowmod;
203 dst += destwidth;
204 }
205
206 chardata++;
207 baseline += 8;
208 }
209
210 }
211
DrawChar5x5Outline(gFont * font,int drawchar,int xpos,uint8 * baseline,uint8 color,uint16 destwidth)212 void DrawChar5x5Outline(gFont *font, int drawchar, int xpos, uint8 *baseline,
213 uint8 color, uint16 destwidth) {
214 uint8 *d;
215 short h, /* font height counter */
216 rowmod;
217
218 short charwidth, w; /* width of character in pixels */
219
220 uint8 *chardata; /* pointer to start of char data */
221
222 uint8 *src, *dst;
223 unsigned short s0, s1;
224 unsigned short txt[5];
225
226 // point to the first byte of the first scanline of the source char
227 uint16 offset = font->charXOffset[drawchar];
228 chardata = &font->fontdata[offset];
229
230 // get the width of the character in pixels
231 charwidth = font->charWidth[drawchar];
232
233 // point to the first byte of where we want to place the character
234 baseline += xpos - 2;
235
236 // this loop goes once for each 8 pixels wide that the character is
237 rowmod = font->rowMod;
238
239 for (w = (charwidth + 7) >> 3; w > 0; w--) {
240 src = chardata;
241 dst = baseline;
242
243 txt[0] = txt[1] = txt[2] = txt[3 ] = txt[ 4 ] = 0;
244
245 for (h = font->height + 4; h; h--) {
246 d = dst;
247
248 txt[4] = txt[3];
249 txt[3] = txt[2];
250 txt[2] = txt[1];
251 txt[1] = txt[0];
252 txt[0] = h > 4 ? *src : 0;
253
254 s0 = txt[1] | txt[2] | txt[3];
255 s1 = s0 | txt[0] | txt[4];
256
257 s0 = s0 | (s0 << 1) | (s0 << 2) | (s0 << 3) | (s0 << 4);
258 s0 |= (s1 << 1) | (s1 << 2) | (s1 << 3);
259
260 while (s0) {
261 if (s0 & 0x800)
262 *d = color;
263 ++d;
264 s0 <<= 1;
265 }
266
267 src += rowmod;
268 dst += destwidth;
269 }
270
271 chardata++;
272 baseline += 8;
273 }
274
275 }
276
277 /* A private routine to render a string of characters into a temp
278 buffer.
279 */
280
drawStringChars(const char * str,int16 len,gPixelMap & dest,int xpos,int ypos)281 void gPort::drawStringChars(
282 const char *str, // string to draw
283 int16 len, // length of string
284 gPixelMap &dest,
285 int xpos, // x position to draw it
286 int ypos) { // y position to draw it
287 const char *s; // pointer to string
288 uint8 drawchar; // char to draw
289 int16 x; // current x position
290 uint8 *buffer, // buffer to render to
291 *uBuffer; // underline buffer
292 uint16 drowMod = dest.size.x; // row modulus of dest
293 int16 i; // loop index
294 uint8 underbar = (textStyles & textStyleBar) != 0;
295 bool underscore;
296 int16 underPos;
297
298 // the address to start rendering pixels to.
299
300 underPos = font->baseLine + 2;
301 if (underPos > font->height) underPos = font->height;
302 buffer = dest.data + (ypos * drowMod);
303 uBuffer = buffer + (underPos * drowMod);
304
305 // draw drop-shadow, if any
306
307 if (textStyles & textStyleShadow) {
308 x = xpos - 1;
309 s = str;
310
311 if (textStyles & textStyleOutline) { // if outlining
312 for (i = 0; i < len; i++) {
313 drawchar = *s++; // draw thick drop shadow
314 x += font->charKern[drawchar];
315 DrawChar3x3Outline(font, drawchar, x, buffer, shPen, drowMod);
316 x += font->charSpace[drawchar] + textSpacing;
317 }
318 } else if (textStyles & textStyleThickOutline) { // if outlining
319 for (i = 0; i < len; i++) {
320 drawchar = *s++; // draw thick drop shadow
321 x += font->charKern[drawchar];
322 DrawChar5x5Outline(font, drawchar, x, buffer, shPen, drowMod);
323 x += font->charSpace[drawchar] + textSpacing;
324 }
325 } else {
326 for (i = 0; i < len; i++) {
327 drawchar = *s++; // draw thick drop shadow
328 x += font->charKern[drawchar];
329 DrawChar(font, drawchar, x, buffer + drowMod,
330 shPen, drowMod);
331 x += font->charSpace[drawchar] + textSpacing;
332 }
333 }
334 }
335
336 // draw outline, if any
337
338 if (textStyles & textStyleOutline) { // if outlining
339 x = xpos;
340 s = str;
341
342 for (i = 0; i < len; i++) {
343 drawchar = *s++; // draw thick text
344 x += font->charKern[drawchar];
345 DrawChar3x3Outline(font, drawchar, x, buffer - drowMod,
346 olPen, drowMod);
347 x += font->charSpace[drawchar] + textSpacing;
348 }
349 } else if (textStyles & textStyleThickOutline) { // if thick outlining
350 x = xpos;
351 s = str;
352
353 for (i = 0; i < len; i++) {
354 drawchar = *s++; // draw extra thick text
355 x += font->charKern[drawchar];
356 DrawChar5x5Outline(font, drawchar, x, buffer - drowMod * 2,
357 olPen, drowMod);
358 x += font->charSpace[drawchar] + textSpacing;
359 }
360 }
361
362 // draw inner part
363
364 x = xpos;
365 s = str;
366 underscore = textStyles & textStyleUnderScore ? true : false;
367
368 for (i = 0; i < len; i++) {
369 int16 last_x = x;
370 uint8 color = fgPen;
371
372 drawchar = *s++; // draw thick drop shadow
373 if (drawchar == '_' && underbar) {
374 len--;
375 drawchar = *s++;
376 if (textStyles & textStyleUnderBar)
377 underscore = true;
378 if (textStyles & textStyleHiLiteBar)
379 color = bgPen;
380 }
381 x += font->charKern[drawchar];
382 DrawChar(font, drawchar, x, buffer, color, drowMod);
383 x += font->charSpace[drawchar] + textSpacing;
384
385 if (underscore) { // draw underscore
386 uint8 *put = uBuffer + last_x;
387 int16 width = x - last_x;
388
389 while (width-- > 0) {
390 *put++ = color;
391 }
392
393 if (!(textStyles & textStyleUnderScore))
394 underscore = false;
395 }
396 }
397 }
398
399 // Draws a string clipped to the current clipping rectangle.
400 // Note that if several clipping rectangles were to be used,
401 // we would probably do this differently...
402
drawClippedString(const char * s,int16 len,int xpos,int ypos)403 int16 gPort::drawClippedString(
404 const char *s, // string to draw
405 int16 len, // length of string
406 int xpos, // x position to draw it
407 int ypos) { // y position to draw it
408 int16 clipWidth = 0, // width of clipped string
409 clipLen; // chars to draw
410 gPixelMap tempMap; // temp buffer for text
411 uint8 underbar = (textStyles & textStyleBar) != 0;
412 int16 xoff = 0, // offset for outlines
413 yoff = 0; // offset for outlines
414 int16 penMove = 0; // keep track of pen movement
415
416 // First, we want to avoid rendering any characters to the
417 // left of the clipping rectangle. Scan the string until
418 // we find a character that is not completely to the left
419 // of the clip.
420
421 while (len > 0) {
422 int16 drawchar = *s,
423 charwidth;
424
425 if (drawchar == '_' && underbar) {
426 drawchar = s[1];
427 charwidth = font->charKern[drawchar]
428 + font->charSpace[drawchar] + textSpacing;
429
430 if (xpos + charwidth >= clip.x)
431 break;
432 s++;
433 } else {
434 charwidth = font->charKern[drawchar]
435 + font->charSpace[drawchar] + textSpacing;
436 if (xpos + charwidth >= clip.x)
437 break;
438 }
439
440 s++;
441 len--;
442 xpos += charwidth;
443 penMove += charwidth;
444 }
445
446 // Now, we also want to only draw that portion of the string
447 // that actually appears in the clip, so scan the rest of the
448 // string until we find a character who's left edge is past
449 // the right edge of the clip.
450
451 for (clipLen = 0; clipLen < len; clipLen++) {
452 int16 drawchar = s[clipLen];
453
454 if (drawchar == '_' && underbar)
455 continue;
456
457 clipWidth += font->charKern[drawchar]
458 + font->charSpace[drawchar] + textSpacing;
459
460 if (xpos > clip.x + clip.width)
461 break;
462 }
463
464 // Handle special case of negative kern value of 1st character
465
466 if (font->charKern[(byte)s[0]] < 0) {
467 int16 kern = - font->charKern[(byte)s[0]];
468
469 clipWidth += kern; // increase size of map to render
470 xoff += kern; // offset text into map right
471 xpos -= kern; // offset map into port left
472 }
473
474 // Set up a temporary bitmap to hold the string.
475
476 tempMap.size.x = clipWidth;
477 tempMap.size.y = font->height;
478
479 // Adjust the size and positioning of the temp map due
480 // to text style effects.
481
482 if (textStyles & textStyleOutline) {
483 xoff = yoff = 1;
484 xpos--;
485 ypos--;
486 tempMap.size.x += 2;
487 tempMap.size.y += 2;
488 } else if (textStyles & textStyleThickOutline) {
489 xoff = yoff = 2;
490 xpos -= 2;
491 ypos -= 2;
492 tempMap.size.x += 4;
493 tempMap.size.y += 4;
494 }
495
496 if (textStyles & (textStyleShadow | textStyleUnderScore | textStyleUnderBar)) {
497 tempMap.size.x += 1;
498 tempMap.size.y += 1;
499 }
500
501 if (textStyles & textStyleItalics) {
502 int n = (font->height - font->baseLine - 1) / 2;
503
504 if (n > 0) xpos += n;
505 tempMap.size.x += tempMap.size.y / 2;
506 }
507
508 // Allocate a temporary bitmap
509
510 if (tempMap.bytes() == 0)
511 return 0;
512 tempMap.data = (uint8 *)TempAlloc(tempMap.bytes());
513 if (tempMap.data != NULL) {
514 // Fill the buffer with background pen if we're
515 // not doing a transparent blit.
516
517 memset(tempMap.data,
518 (drawMode == drawModeReplace) ? bgPen : 0,
519 tempMap.bytes());
520
521 // Draw the characters into the buffer
522
523 drawStringChars(s, clipLen, tempMap, xoff, yoff);
524
525 // apply slant if italics
526
527 if (textStyles & textStyleItalics) {
528 int n = (font->height - font->baseLine - 1) / 2;
529 int shift = (n > 0 ? n : 0);
530 int flag = (font->height - font->baseLine - 1) & 1;
531
532 shift = -shift;
533
534 for (int k = font->height - 1; k >= 0; k--) {
535 if (shift < 0) {
536 uint8 *dest = tempMap.data + k * tempMap.size.x,
537 *src = dest - shift;
538 int j;
539
540 for (j = 0; j < tempMap.size.x + shift; j++) {
541 *dest++ = *src++;
542 }
543 for (; j < tempMap.size.x; j++) {
544 *dest++ = 0;
545 }
546 } else if (shift > 0) {
547 uint8 *dest = tempMap.data + (k + 1) * tempMap.size.x,
548 *src = dest - shift;
549 int j;
550
551 for (j = 0; j < tempMap.size.x - shift; j++) {
552 *--dest = *--src;
553 }
554 for (; j < tempMap.size.x; j++) {
555 *--dest = 0;
556 }
557 }
558
559 flag ^= 1;
560 if (flag)
561 shift++;
562 }
563 }
564
565 // blit the temp buffer onto the screen.
566
567 bltPixels(tempMap, 0, 0,
568 xpos, ypos,
569 tempMap.size.x, tempMap.size.y);
570
571 TempFree(tempMap.data);
572 }
573
574 // Now, we still need to scan the rest of the string
575 // so that we can move the pen position by the right amount.
576
577 // penMove += clipWidth;
578 for (clipLen = 0; clipLen < len; clipLen++) {
579 int16 drawchar = s[clipLen];
580
581 if (drawchar == '_' && underbar)
582 continue;
583
584 penMove += font->charKern[drawchar]
585 + font->charSpace[drawchar] + textSpacing;
586 }
587
588 return penMove;
589 }
590
591 /********* gtext.cpp/gPort::drawText *********************************
592 *
593 * NAME
594 * gPort::drawText -- draw a text string into a gPort
595 *
596 * SYNOPSIS
597 * port.drawText( str, length );
598 *
599 * void gPort::drawText( char *, int16 = -1 );
600 *
601 * FUNCTION
602 * This function draws a string of text at the current pen
603 * position in the current pen color. The pen position is
604 * updated to the end of the text.
605 *
606 * The text will be positioned such that the top-left corner
607 * of the first character will be at the pen position.
608 *
609 * INPUTS
610 * str A string of characters.
611 *
612 * length [Optional parameter] If supplied, it indicates
613 * the length of the string; Otherwise, strlen()
614 * is used.
615 *
616 * RESULT
617 * none
618 *
619 * SEE ALSO
620 * gPort class
621 *
622 **********************************************************************
623 */
drawText(const char * str,int16 length)624 void gPort::drawText(
625 const char *str, /* string to draw */
626 int16 length) {
627 if (length < 0)
628 length = strlen(str);
629
630 if (length > 0)
631 penPos.x += drawClippedString(str, length, penPos.x, penPos.y);
632 }
633
634 /********* gtext.cpp/gPort::drawTextInBox *********************************
635 *
636 * NAME
637 * gPort::drawTextInBox -- draw text within a box
638 *
639 * SYNOPSIS
640 * port.drawTextInBox( str, len, rect, pos, borderSpace );
641 *
642 * void gPort::drawTextInBox( char *, int16, const Rect16 &,
643 * /c/ int16, Point16 );
644 *
645 * FUNCTION
646 * This function can draw a text string centered or justified
647 * within a rectangular area, and clipped to that area as well.
648 * The text string is drawn in the current pen color, and with
649 * the current draw mode.
650 *
651 * INPUTS
652 * str The text to draw.
653 *
654 * len The length of the string or -1 to indicate a
655 * null-terminated string.
656 *
657 * rect The rectangle to draw the text within.
658 *
659 * pos How to position the text string within the
660 * rectangle. The default (0) is to center the
661 * string both horizontally and vertically; However,
662 * the following flags will modify this:
663 *
664 * /i/ textPosLeft -- draw text left-justified.
665 *
666 * /i/ textPosRight -- draw text right-justified.
667 *
668 * /i/ textPosHigh -- draw text flush with top edge.
669 *
670 * /i/ textPosLow -- draw text flush with bottom edge.
671 *
672 * borderSpace A Point16 object, which indicates how much space
673 * (in both x and y) to place between the text and
674 * the border when the text is justified to a
675 * particular edge. Does not apply when text is centered.
676 *
677 * RESULT
678 * none
679 *
680 * SEE ALSO
681 * gPort class
682 *
683 **********************************************************************
684 */
drawTextInBox(const char * str,int16 length,const Rect16 & r,int16 pos,Point16 borders)685 void gPort::drawTextInBox(
686 const char *str,
687 int16 length,
688 const Rect16 &r,
689 int16 pos,
690 Point16 borders) {
691 int16 height, width;
692 int16 x, y;
693 Rect16 newClip,
694 saveClip = clip;
695
696 if (!font)
697 return;
698
699 height = font->height;
700 width = TextWidth(font, str, length, textStyles);
701
702 if (textStyles & (textStyleUnderScore | textStyleUnderBar)) {
703 if (font->baseLine + 2 >= font->height)
704 height++;
705 }
706
707 // Calculate x position of text string
708
709 if (pos & textPosLeft)
710 x = r.x + borders.x;
711 else if (pos & textPosRight)
712 x = r.x + r.width - width - borders.x;
713 else
714 x = r.x + (r.width - width) / 2;
715
716 // Calculate y position of text string
717
718 if (pos & textPosHigh)
719 y = r.y + borders.y;
720 else if (pos & textPosLow)
721 y = r.y + r.height - height - borders.y;
722 else
723 y = r.y + (r.height - height) / 2;
724
725 // Calculate clipping region
726
727 clip = intersect(clip, r);
728
729 // Draw the text
730
731 moveTo(x, y);
732 drawText(str, length);
733
734 // Restore the clipping region
735
736 clip = saveClip;
737 }
738
739 // Attach to gFont?
740
741 /********* gtext.cpp/TextWidth ***************************************
742 *
743 * NAME
744 * TextWidth -- returns length of text string in pixels
745 *
746 * SYNOPSIS
747 * width = TextWidth( font, str, len, styles );
748 *
749 * int16 TextWidth( gFont *, char *, int16, int16 );
750 *
751 * FUNCTION
752 * This function computes the length of a text string in pixels
753 * for a given font and text style.
754 *
755 * INPUTS
756 * font The text font of the text.
757 *
758 * str The text string to measure.
759 *
760 * len The length of the string, or -1 to use strlen()
761 *
762 * styles The text rendering style (see gPort::setStyle).
763 * 0 = normal.
764 *
765 * RESULT
766 * The width of the text.
767 *
768 **********************************************************************
769 */
TextWidth(gFont * font,const char * s,int16 length,int16 styles)770 int16 TextWidth(gFont *font, const char *s, int16 length, int16 styles) {
771 int16 count = 0;
772
773 if (length < 0)
774 length = strlen(s);
775
776 while (length--) {
777 uint8 chr = *s++;
778
779 if (chr == '_' && (styles & textStyleBar))
780 continue;
781
782 count += font->charKern[chr] + font->charSpace[chr];
783 }
784
785 if (styles & textStyleItalics) {
786 count += (font->baseLine + 1) / 2 +
787 (font->height - font->baseLine - 1) / 2;
788 }
789 if (styles & textStyleOutline)
790 count += 2;
791 else if (styles & textStyleThickOutline)
792 count += 4;
793 if (styles & textStyleShadow)
794 count += 1;
795
796 return count;
797 }
798
799 // Searches for the insertion point between chars under the cursor
800
801 /********* gtext.cpp/WhichIChar **************************************
802 *
803 * NAME
804 * WhichIChar -- search for insertion point between characters
805 *
806 * SYNOPSIS
807 * charNum = WhichIChar( font, str, pick, maxLen );
808 *
809 * int16 WhichIChar( gFont *, uint8 *, int16, int16 );
810 *
811 * FUNCTION
812 * This function is used by the gTextBox class to select
813 * the position of the insertion point when the text box
814 * is clicked on. It computes the width of each character
815 * in the string (as it would be rendered
816 * on the screen) and determines which two characters the
817 * pick position would fall between, in other words it finds
818 * the nearest insertion point between characters.
819 *
820 * INPUTS
821 * font The font to use in the character-width calculation.
822 *
823 * str The string to search.
824 *
825 * pick The pick position, relative to the start of the string.
826 *
827 * maxLen The length of the string.
828 *
829 * RESULT
830 * The index of the selected character.
831 *
832 **********************************************************************
833 */
WhichIChar(gFont * font,uint8 * s,int16 length,int16 maxLen)834 int16 WhichIChar(gFont *font, uint8 *s, int16 length, int16 maxLen) {
835 int16 count = 0;
836
837 if (maxLen == -1)
838 maxLen = strlen((char *)s);
839
840 for (count = 0; count < maxLen; count++) {
841 uint8 chr = *s++;
842 int16 width;
843
844 width = font->charKern[chr] + font->charSpace[chr];
845
846 if (length < width / 2)
847 break;
848 length -= width;
849 }
850 return count;
851 }
852
853 /****** gtext.cpp/GTextWrap *****************************************
854 *
855 * NAME
856 * GTextWrap -- given text buffer and pixel width, find wrap point
857 *
858 * SYNOPSIS
859 * offset = GTextWrap( font, buffer, count, width, styles );
860 *
861 * int32 GTextWrap( gFont *, char *, uint16 &, uint16, int16 );
862 *
863 * FUNCTION
864 * This function finds the point in a text buffer that text
865 * wrapping would have to occur given the indicated pixel width.
866 * Linefeeds are considered implied wrap points. Tabs are not
867 * expanded.
868 *
869 * INPUTS
870 * font a gFont that the text will be rendered in.
871 * buffer a NULL-terminated text buffer.
872 * count reference to variable to store count of characters
873 * to render before wrap point.
874 * width width in pixels to wrap point.
875 * styles possible text styles.
876 *
877 * RESULT
878 * offset amount to add to buffer for next call to GTextWrap,
879 * or -1 if the NULL-terminator was reached.
880 *
881 **********************************************************************
882 */
GTextWrap(gFont * font,char * mark,uint16 & count,uint16 width,int16 styles)883 int32 GTextWrap(gFont *font, char *mark, uint16 &count, uint16 width, int16 styles) {
884 char *text = mark;
885 char *atext;
886 uint16 pixlen;
887 int aTextIndex = 0;
888
889 if (!strchr(text, '\n')) {
890 count = strlen(text);
891 pixlen = TextWidth(font, text, count, styles);
892 if (pixlen <= width)
893 return -1;
894 }
895
896 atext = text;
897 while (1) {
898 Common::String s = atext;
899
900 int nTextIndex = aTextIndex + s.findFirstOf(' ');
901 int lTextIndex = aTextIndex + s.findFirstOf('\n');
902
903 if (!s.contains(' ') || (s.contains('\n') && lTextIndex < nTextIndex)) {
904 pixlen = TextWidth(font, text, lTextIndex, styles);
905 if (pixlen <= width) {
906 count = lTextIndex;
907 return count + 1;
908 }
909
910 if (!s.contains(' ')) {
911 if (atext == text)
912 break;
913
914 count = aTextIndex - 1;
915 return count + 1;
916 }
917 }
918
919 pixlen = TextWidth(font, text, nTextIndex, styles);
920 if (pixlen > width) {
921 if (atext == text)
922 break;
923
924 count = aTextIndex - 1;
925 return count + 1;
926 }
927
928 atext = text + nTextIndex + 1;
929 aTextIndex = nTextIndex + 1;
930 }
931
932 if (atext == text) {
933 // current text has no spaces AND doesn't fit
934
935 count = strlen(text);
936 while (--count) {
937 pixlen = TextWidth(font, text, count, styles);
938 if (pixlen <= width)
939 return count;
940 }
941 } else {
942 count = aTextIndex - 1;
943 return count + 1;
944 }
945
946 return -1;
947 }
948
949 } // end of namespace Saga2
950