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  * Text utilities.
22  */
23 
24 #include "tinsel/dw.h"
25 #include "tinsel/graphics.h"	// object plotting
26 #include "tinsel/handle.h"
27 #include "tinsel/sched.h"	// process scheduler defines
28 #include "tinsel/strres.h"	// g_bMultiByte
29 #include "tinsel/text.h"	// text defines
30 
31 namespace Tinsel {
32 
33 /**
34  * Returns the length of one line of a string in pixels.
35  * @param szStr			String
36  * @param pFont			Which font to use for dimensions
37  */
StringLengthPix(char * szStr,const FONT * pFont)38 int StringLengthPix(char *szStr, const FONT *pFont) {
39 	int strLen;	// accumulated length of string
40 	byte	c;
41 	SCNHANDLE	hImg;
42 
43 	// while not end of string or end of line
44 	for (strLen = 0; (c = *szStr) != EOS_CHAR && c != LF_CHAR; szStr++) {
45 		if (g_bMultiByte) {
46 			if (c & 0x80)
47 				c = ((c & ~0x80) << 8) + *++szStr;
48 		}
49 		hImg = FROM_32(pFont->fontDef[c]);
50 
51 		if (hImg) {
52 			// there is a IMAGE for this character
53 			const IMAGE *pChar = (const IMAGE *)LockMem(hImg);
54 
55 			// add width of font bitmap
56 			strLen += FROM_16(pChar->imgWidth);
57 		} else
58 			// use width of space character
59 			strLen += FROM_32(pFont->spaceSize);
60 
61 		// finally add the inter-character spacing
62 		strLen += FROM_32(pFont->xSpacing);
63 	}
64 
65 	// return length of line in pixels - minus inter-char spacing for last character
66 	strLen -= FROM_32(pFont->xSpacing);
67 	return (strLen > 0) ? strLen : 0;
68 }
69 
70 /**
71  * Returns the justified x start position of a line of text.
72  * @param szStr			String to output
73  * @param xPos			X position of string
74  * @param pFont			Which font to use
75  * @param mode			Mode flags for the string
76  */
JustifyText(char * szStr,int xPos,const FONT * pFont,int mode)77 int JustifyText(char *szStr, int xPos, const FONT *pFont, int mode) {
78 	if (mode & TXT_CENTER) {
79 		// center justify the text
80 
81 		// adjust x positioning by half the length of line in pixels
82 		xPos -= StringLengthPix(szStr, pFont) / 2;
83 	} else if (mode & TXT_RIGHT) {
84 		// right justify the text
85 
86 		// adjust x positioning by length of line in pixels
87 		xPos -= StringLengthPix(szStr, pFont);
88 	}
89 
90 	// return text line x start position
91 	return xPos;
92 }
93 
94 /**
95  * Main text outputting routine. If a object list is specified a
96  * multi-object is created for the whole text and a pointer to the head
97  * of the list is returned.
98  * @param pList			Object list to add text to
99  * @param szStr			String to output
100  * @param color		Color for monochrome text
101  * @param xPos			X position of string
102  * @param yPos			Y position of string
103  * @param hFont			Which font to use
104  * @param mode			Mode flags for the string
105  * @param sleepTime		Sleep time between each character (if non-zero)
106  */
ObjectTextOut(OBJECT ** pList,char * szStr,int color,int xPos,int yPos,SCNHANDLE hFont,int mode,int sleepTime)107 OBJECT *ObjectTextOut(OBJECT **pList, char *szStr, int color,
108 					  int xPos, int yPos, SCNHANDLE hFont, int mode, int sleepTime) {
109 	int xJustify;	// x position of text after justification
110 	int yOffset;	// offset to next line of text
111 	OBJECT *pFirst;	// head of multi-object text list
112 	OBJECT *pChar = 0;	// object ptr for the character
113 	byte c;
114 	SCNHANDLE hImg;
115 	const IMAGE *pImg;
116 
117 	// make sure there is a linked list to add text to
118 	assert(pList);
119 
120 	// get font pointer
121 	const FONT *pFont = (const FONT *)LockMem(hFont);
122 
123 	// init head of text list
124 	pFirst = NULL;
125 
126 	// get image for capital W
127 	assert(pFont->fontDef[(int)'W']);
128 	pImg = (const IMAGE *)LockMem(FROM_32(pFont->fontDef[(int)'W']));
129 
130 	// get height of capital W for offset to next line
131 	yOffset = FROM_16(pImg->imgHeight) & ~C16_FLAG_MASK;
132 
133 	while (*szStr) {
134 		// x justify the text according to the mode flags
135 		xJustify = JustifyText(szStr, xPos, pFont, mode);
136 
137 		// repeat until end of string or end of line
138 		while ((c = *szStr) != EOS_CHAR && c != LF_CHAR) {
139 			if (g_bMultiByte) {
140 				if (c & 0x80)
141 					c = ((c & ~0x80) << 8) + *++szStr;
142 			}
143 			hImg = FROM_32(pFont->fontDef[c]);
144 
145 			if (hImg == 0) {
146 				// no image for this character
147 
148 				// add font spacing for a space character
149 				xJustify += FROM_32(pFont->spaceSize);
150 			} else {	// printable character
151 
152 				int aniX, aniY;		// char image animation offsets
153 
154 				OBJ_INIT oi;
155 				oi.hObjImg  = FROM_32(pFont->fontInit.hObjImg);
156 				oi.objFlags = FROM_32(pFont->fontInit.objFlags);
157 				oi.objID    = FROM_32(pFont->fontInit.objID);
158 				oi.objX     = FROM_32(pFont->fontInit.objX);
159 				oi.objY     = FROM_32(pFont->fontInit.objY);
160 				oi.objZ     = FROM_32(pFont->fontInit.objZ);
161 
162 				// allocate and init a character object
163 				if (pFirst == NULL)
164 					// first time - init head of list
165 					pFirst = pChar = InitObject(&oi);	// FIXME: endian issue using fontInit!!!
166 				else
167 					// chain to multi-char list
168 					pChar = pChar->pSlave = InitObject(&oi);	// FIXME: endian issue using fontInit!!!
169 
170 				// convert image handle to pointer
171 				pImg = (const IMAGE *)LockMem(hImg);
172 
173 				// fill in character object
174 				pChar->hImg   = hImg;			// image def
175 				pChar->width  = FROM_16(pImg->imgWidth);		// width of chars bitmap
176 				pChar->height = FROM_16(pImg->imgHeight) & ~C16_FLAG_MASK;	// height of chars bitmap
177 				pChar->hBits  = FROM_32(pImg->hImgBits);		// bitmap
178 
179 				// check for absolute positioning
180 				if (mode & TXT_ABSOLUTE)
181 					pChar->flags |= DMA_ABS;
182 
183 				// set characters color - only effective for mono fonts
184 				pChar->constant = color;
185 
186 				// get Y animation offset
187 				GetAniOffset(hImg, pChar->flags, &aniX, &aniY);
188 
189 				// set x position - ignore animation point
190 				pChar->xPos = intToFrac(xJustify);
191 
192 				// set y position - adjust for animation point
193 				pChar->yPos = intToFrac(yPos - aniY);
194 
195 				if (mode & TXT_SHADOW) {
196 					// we want to shadow the character
197 					OBJECT *pShad;
198 
199 					// allocate a object for the shadow and chain to multi-char list
200 					pShad = pChar->pSlave = AllocObject();
201 
202 					// copy the character for a shadow
203 					CopyObject(pShad, pChar);
204 
205 					// add shadow offsets to characters position
206 					pShad->xPos += intToFrac(FROM_32(pFont->xShadow));
207 					pShad->yPos += intToFrac(FROM_32(pFont->yShadow));
208 
209 					// shadow is behind the character
210 					pShad->zPos--;
211 
212 					// shadow is always mono
213 					pShad->flags = DMA_CNZ | DMA_CHANGED;
214 
215 					// check for absolute positioning
216 					if (mode & TXT_ABSOLUTE)
217 						pShad->flags |= DMA_ABS;
218 
219 					// shadow always uses first palette entry
220 					// should really alloc a palette here also ????
221 					pShad->constant = 1;
222 
223 					// add shadow to object list
224 					InsertObject(pList, pShad);
225 				}
226 
227 				// add character to object list
228 				InsertObject(pList, pChar);
229 
230 				// move to end of list
231 				if (pChar->pSlave)
232 					pChar = pChar->pSlave;
233 
234 				// add character spacing
235 				xJustify += FROM_16(pImg->imgWidth);
236 			}
237 
238 			// finally add the inter-character spacing
239 			xJustify += FROM_32(pFont->xSpacing);
240 
241 			// next character in string
242 			++szStr;
243 		}
244 
245 		// adjust the text y position and add the inter-line spacing
246 		yPos += yOffset + FROM_32(pFont->ySpacing);
247 
248 		// check for newline
249 		if (c == LF_CHAR)
250 			// next character in string
251 			++szStr;
252 	}
253 
254 	// return head of list
255 	return pFirst;
256 }
257 
258 /**
259  * Is there an image for this character in this font?
260  * @param hFont	which font to use
261  * @param c		character to test
262  */
IsCharImage(SCNHANDLE hFont,char c)263 bool IsCharImage(SCNHANDLE hFont, char c) {
264 	byte c2 = (byte)c;
265 
266 	// Inventory save game name editor needs to be more clever for
267 	// multi-byte characters. This bodge will stop it erring.
268 	if (g_bMultiByte && (c2 & 0x80))
269 		return false;
270 
271 	// get font pointer
272 	const FONT *pFont = (const FONT *)LockMem(hFont);
273 
274 	return pFont->fontDef[c2] != 0;
275 }
276 
277 } // End of namespace Tinsel
278