|
1 {$INCLUDE "options.inc"} |
|
2 unit uRenderUtils; |
|
3 |
|
4 interface |
|
5 uses SDLh, uTypes; |
|
6 |
|
7 procedure flipSurface(Surface: PSDL_Surface; Vertical: Boolean); |
|
8 procedure copyRotatedSurface(src, dest: PSDL_Surface); // this is necessary since width/height are read only in SDL |
|
9 procedure copyToXY(src, dest: PSDL_Surface; destX, destY: LongInt); |
|
10 function RenderStringTex(s: ansistring; Color: Longword; font: THWFont): PTexture; |
|
11 function RenderSpeechBubbleTex(s: ansistring; SpeechType: Longword; font: THWFont): PTexture; |
|
12 procedure DrawRoundRect(rect: PSDL_Rect; BorderColor, FillColor: Longword; Surface: PSDL_Surface; Clear: boolean); |
|
13 |
|
14 implementation |
|
15 uses uIO, uUtils, uVariables, uConsts, uTextures, sysutils; |
|
16 |
|
17 procedure DrawRoundRect(rect: PSDL_Rect; BorderColor, FillColor: Longword; Surface: PSDL_Surface; Clear: boolean); |
|
18 var r: TSDL_Rect; |
|
19 begin |
|
20 r:= rect^; |
|
21 if Clear then SDL_FillRect(Surface, @r, 0); |
|
22 |
|
23 BorderColor:= SDL_MapRGB(Surface^.format, BorderColor shr 16, BorderColor shr 8, BorderColor and $FF); |
|
24 FillColor:= SDL_MapRGB(Surface^.format, FillColor shr 16, FillColor shr 8, FillColor and $FF); |
|
25 |
|
26 r.y:= rect^.y + 1; |
|
27 r.h:= rect^.h - 2; |
|
28 SDL_FillRect(Surface, @r, BorderColor); |
|
29 r.x:= rect^.x + 1; |
|
30 r.w:= rect^.w - 2; |
|
31 r.y:= rect^.y; |
|
32 r.h:= rect^.h; |
|
33 SDL_FillRect(Surface, @r, BorderColor); |
|
34 r.x:= rect^.x + 2; |
|
35 r.y:= rect^.y + 1; |
|
36 r.w:= rect^.w - 4; |
|
37 r.h:= rect^.h - 2; |
|
38 SDL_FillRect(Surface, @r, FillColor); |
|
39 r.x:= rect^.x + 1; |
|
40 r.y:= rect^.y + 2; |
|
41 r.w:= rect^.w - 2; |
|
42 r.h:= rect^.h - 4; |
|
43 SDL_FillRect(Surface, @r, FillColor) |
|
44 end; |
|
45 |
|
46 function WriteInRoundRect(Surface: PSDL_Surface; X, Y: LongInt; Color: LongWord; Font: THWFont; s: ansistring): TSDL_Rect; |
|
47 var w, h: LongInt; |
|
48 tmpsurf: PSDL_Surface; |
|
49 clr: TSDL_Color; |
|
50 finalRect: TSDL_Rect; |
|
51 begin |
|
52 TTF_SizeUTF8(Fontz[Font].Handle, Str2PChar(s), w, h); |
|
53 finalRect.x:= X; |
|
54 finalRect.y:= Y; |
|
55 finalRect.w:= w + FontBorder * 2 + 4; |
|
56 finalRect.h:= h + FontBorder * 2; |
|
57 DrawRoundRect(@finalRect, cWhiteColor, endian(cNearBlackColorChannels.value), Surface, true); |
|
58 clr.r:= (Color shr 16) and $FF; |
|
59 clr.g:= (Color shr 8) and $FF; |
|
60 clr.b:= Color and $FF; |
|
61 tmpsurf:= TTF_RenderUTF8_Blended(Fontz[Font].Handle, Str2PChar(s), clr); |
|
62 finalRect.x:= X + FontBorder + 2; |
|
63 finalRect.y:= Y + FontBorder; |
|
64 SDLTry(tmpsurf <> nil, true); |
|
65 SDL_UpperBlit(tmpsurf, nil, Surface, @finalRect); |
|
66 SDL_FreeSurface(tmpsurf); |
|
67 finalRect.x:= X; |
|
68 finalRect.y:= Y; |
|
69 finalRect.w:= w + FontBorder * 2 + 4; |
|
70 finalRect.h:= h + FontBorder * 2; |
|
71 WriteInRoundRect:= finalRect; |
|
72 end; |
|
73 |
|
74 procedure flipSurface(Surface: PSDL_Surface; Vertical: Boolean); |
|
75 var y, x, i, j: LongInt; |
|
76 tmpPixel: Longword; |
|
77 pixels: PLongWordArray; |
|
78 begin |
|
79 TryDo(Surface^.format^.BytesPerPixel = 4, 'flipSurface failed, expecting 32 bit surface', true); |
|
80 pixels:= Surface^.pixels; |
|
81 if Vertical then |
|
82 for y := 0 to (Surface^.h div 2) - 1 do |
|
83 for x := 0 to Surface^.w - 1 do |
|
84 begin |
|
85 i:= y * Surface^.w + x; |
|
86 j:= (Surface^.h - y - 1) * Surface^.w + x; |
|
87 tmpPixel:= pixels^[i]; |
|
88 pixels^[i]:= pixels^[j]; |
|
89 pixels^[j]:= tmpPixel; |
|
90 end |
|
91 else |
|
92 for x := 0 to (Surface^.w div 2) - 1 do |
|
93 for y := 0 to Surface^.h -1 do |
|
94 begin |
|
95 i:= y*Surface^.w + x; |
|
96 j:= y*Surface^.w + (Surface^.w - x - 1); |
|
97 tmpPixel:= pixels^[i]; |
|
98 pixels^[i]:= pixels^[j]; |
|
99 pixels^[j]:= tmpPixel; |
|
100 end; |
|
101 end; |
|
102 |
|
103 procedure copyToXY(src, dest: PSDL_Surface; destX, destY: LongInt); |
|
104 var srcX, srcY, i, j, maxDest: LongInt; |
|
105 srcPixels, destPixels: PLongWordArray; |
|
106 r0, g0, b0, a0, r1, g1, b1, a1: Byte; |
|
107 begin |
|
108 maxDest:= (dest^.pitch div 4) * dest^.h; |
|
109 srcPixels:= src^.pixels; |
|
110 destPixels:= dest^.pixels; |
|
111 |
|
112 for srcX:= 0 to src^.w - 1 do |
|
113 for srcY:= 0 to src^.h - 1 do |
|
114 begin |
|
115 i:= (destY + srcY) * (dest^.pitch div 4) + destX + srcX; |
|
116 j:= srcY * (src^.pitch div 4) + srcX; |
|
117 if (i < maxDest) and (srcPixels^[j] and AMask <> 0) then |
|
118 begin |
|
119 SDL_GetRGBA(destPixels^[i], dest^.format, @r0, @g0, @b0, @a0); |
|
120 SDL_GetRGBA(srcPixels^[j], src^.format, @r1, @g1, @b1, @a1); |
|
121 r0:= (r0 * (255 - LongInt(a1)) + r1 * LongInt(a1)) div 255; |
|
122 g0:= (g0 * (255 - LongInt(a1)) + g1 * LongInt(a1)) div 255; |
|
123 b0:= (b0 * (255 - LongInt(a1)) + b1 * LongInt(a1)) div 255; |
|
124 a0:= (a0 * (255 - LongInt(a1)) + a1 * LongInt(a1)) div 255; |
|
125 destPixels^[i]:= SDL_MapRGBA(dest^.format, r0, g0, b0, a0); |
|
126 end; |
|
127 end; |
|
128 end; |
|
129 |
|
130 procedure copyRotatedSurface(src, dest: PSDL_Surface); // this is necessary since width/height are read only in SDL, apparently |
|
131 var y, x, i, j: LongInt; |
|
132 srcPixels, destPixels: PLongWordArray; |
|
133 begin |
|
134 TryDo(src^.format^.BytesPerPixel = 4, 'rotateSurface failed, expecting 32 bit surface', true); |
|
135 TryDo(dest^.format^.BytesPerPixel = 4, 'rotateSurface failed, expecting 32 bit surface', true); |
|
136 |
|
137 srcPixels:= src^.pixels; |
|
138 destPixels:= dest^.pixels; |
|
139 |
|
140 j:= 0; |
|
141 for x := 0 to src^.w - 1 do |
|
142 for y := 0 to src^.h - 1 do |
|
143 begin |
|
144 i:= (src^.h - 1 - y) * (src^.pitch div 4) + x; |
|
145 destPixels^[j]:= srcPixels^[i]; |
|
146 inc(j) |
|
147 end; |
|
148 end; |
|
149 |
|
150 function RenderStringTex(s: ansistring; Color: Longword; font: THWFont): PTexture; |
|
151 var w, h: LongInt; |
|
152 finalSurface: PSDL_Surface; |
|
153 begin |
|
154 if length(s) = 0 then s:= ' '; |
|
155 font:= CheckCJKFont(s, font); |
|
156 w:= 0; h:= 0; // avoid compiler hints |
|
157 TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(s), w, h); |
|
158 |
|
159 finalSurface:= SDL_CreateRGBSurface(SDL_SWSURFACE, w + FontBorder * 2 + 4, h + FontBorder * 2, |
|
160 32, RMask, GMask, BMask, AMask); |
|
161 |
|
162 TryDo(finalSurface <> nil, 'RenderString: fail to create surface', true); |
|
163 |
|
164 WriteInRoundRect(finalSurface, 0, 0, Color, font, s); |
|
165 |
|
166 TryDo(SDL_SetColorKey(finalSurface, SDL_SRCCOLORKEY, 0) = 0, errmsgTransparentSet, true); |
|
167 |
|
168 RenderStringTex:= Surface2Tex(finalSurface, false); |
|
169 |
|
170 SDL_FreeSurface(finalSurface); |
|
171 end; |
|
172 |
|
173 |
|
174 function RenderSpeechBubbleTex(s: ansistring; SpeechType: Longword; font: THWFont): PTexture; |
|
175 var textWidth, textHeight, x, y, w, h, i, j, pos, prevpos, line, numLines, edgeWidth, edgeHeight, cornerWidth, cornerHeight: LongInt; |
|
176 finalSurface, tmpsurf, rotatedEdge: PSDL_Surface; |
|
177 rect: TSDL_Rect; |
|
178 chars: set of char = [#9,' ','.',';',':','?','!',',']; |
|
179 substr: shortstring; |
|
180 edge, corner, tail: TSPrite; |
|
181 begin |
|
182 case SpeechType of |
|
183 1: begin; |
|
184 edge:= sprSpeechEdge; |
|
185 corner:= sprSpeechCorner; |
|
186 tail:= sprSpeechTail; |
|
187 end; |
|
188 2: begin; |
|
189 edge:= sprThoughtEdge; |
|
190 corner:= sprThoughtCorner; |
|
191 tail:= sprThoughtTail; |
|
192 end; |
|
193 3: begin; |
|
194 edge:= sprShoutEdge; |
|
195 corner:= sprShoutCorner; |
|
196 tail:= sprShoutTail; |
|
197 end; |
|
198 end; |
|
199 edgeHeight:= SpritesData[edge].Height; |
|
200 edgeWidth:= SpritesData[edge].Width; |
|
201 cornerWidth:= SpritesData[corner].Width; |
|
202 cornerHeight:= SpritesData[corner].Height; |
|
203 // This one screws up WrapText |
|
204 //s:= 'This is the song that never ends. ''cause it goes on and on my friends. Some people, started singing it not knowing what it was. And they''ll just go on singing it forever just because... This is the song that never ends...'; |
|
205 // This one does not |
|
206 //s:= 'This is the song that never ends. cause it goes on and on my friends. Some people, started singing it not knowing what it was. And they will go on singing it forever just because... This is the song that never ends... '; |
|
207 |
|
208 numLines:= 0; |
|
209 |
|
210 if length(s) = 0 then s:= '...'; |
|
211 font:= CheckCJKFont(s, font); |
|
212 w:= 0; h:= 0; // avoid compiler hints |
|
213 TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(s), w, h); |
|
214 if w<8 then w:= 8; |
|
215 j:= 0; |
|
216 if (length(s) > 20) then |
|
217 begin |
|
218 w:= 0; |
|
219 i:= round(Sqrt(length(s)) * 2); |
|
220 s:= WrapText(s, #1, chars, i); |
|
221 pos:= 1; prevpos:= 0; line:= 0; |
|
222 // Find the longest line for the purposes of centring the text. Font dependant. |
|
223 while pos <= length(s) do |
|
224 begin |
|
225 if (s[pos] = #1) or (pos = length(s)) then |
|
226 begin |
|
227 inc(numlines); |
|
228 if s[pos] <> #1 then inc(pos); |
|
229 while s[prevpos+1] = ' ' do inc(prevpos); |
|
230 substr:= copy(s, prevpos+1, pos-prevpos-1); |
|
231 i:= 0; j:= 0; |
|
232 TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(substr), i, j); |
|
233 if i > w then w:= i; |
|
234 prevpos:= pos; |
|
235 end; |
|
236 inc(pos); |
|
237 end; |
|
238 end |
|
239 else numLines := 1; |
|
240 |
|
241 textWidth:=((w-(cornerWidth-edgeWidth)*2) div edgeWidth)*edgeWidth+edgeWidth; |
|
242 textHeight:=(((numlines * h + 2)-((cornerHeight-edgeWidth)*2)) div edgeWidth)*edgeWidth; |
|
243 |
|
244 textHeight:=max(textHeight,edgeWidth); |
|
245 //textWidth:=max(textWidth,SpritesData[tail].Width); |
|
246 rect.x:= 0; |
|
247 rect.y:= 0; |
|
248 rect.w:= textWidth + (cornerWidth * 2); |
|
249 rect.h:= textHeight + cornerHeight*2 - edgeHeight + SpritesData[tail].Height; |
|
250 //s:= inttostr(w) + ' ' + inttostr(numlines) + ' ' + inttostr(rect.x) + ' '+inttostr(rect.y) + ' ' + inttostr(rect.w) + ' ' + inttostr(rect.h); |
|
251 |
|
252 finalSurface:= SDL_CreateRGBSurface(SDL_SWSURFACE, rect.w, rect.h, 32, RMask, GMask, BMask, AMask); |
|
253 |
|
254 TryDo(finalSurface <> nil, 'RenderString: fail to create surface', true); |
|
255 |
|
256 //////////////////////////////// CORNERS /////////////////////////////// |
|
257 copyToXY(SpritesData[corner].Surface, finalSurface, 0, 0); /////////////////// NW |
|
258 |
|
259 flipSurface(SpritesData[corner].Surface, true); // store all 4 versions in memory to avoid repeated flips? |
|
260 x:= 0; |
|
261 y:= textHeight + cornerHeight -1; |
|
262 copyToXY(SpritesData[corner].Surface, finalSurface, x, y); /////////////////// SW |
|
263 |
|
264 flipSurface(SpritesData[corner].Surface, false); |
|
265 x:= rect.w-cornerWidth-1; |
|
266 y:= textHeight + cornerHeight -1; |
|
267 copyToXY(SpritesData[corner].Surface, finalSurface, x, y); /////////////////// SE |
|
268 |
|
269 flipSurface(SpritesData[corner].Surface, true); |
|
270 x:= rect.w-cornerWidth-1; |
|
271 y:= 0; |
|
272 copyToXY(SpritesData[corner].Surface, finalSurface, x, y); /////////////////// NE |
|
273 flipSurface(SpritesData[corner].Surface, false); // restore original position |
|
274 //////////////////////////////// END CORNERS /////////////////////////////// |
|
275 |
|
276 //////////////////////////////// EDGES ////////////////////////////////////// |
|
277 x:= cornerWidth; |
|
278 y:= 0; |
|
279 while x < rect.w-cornerWidth-1 do |
|
280 begin |
|
281 copyToXY(SpritesData[edge].Surface, finalSurface, x, y); ///////////////// top edge |
|
282 inc(x,edgeWidth); |
|
283 end; |
|
284 flipSurface(SpritesData[edge].Surface, true); |
|
285 x:= cornerWidth; |
|
286 y:= textHeight + cornerHeight*2 - edgeHeight-1; |
|
287 while x < rect.w-cornerWidth-1 do |
|
288 begin |
|
289 copyToXY(SpritesData[edge].Surface, finalSurface, x, y); ///////////////// bottom edge |
|
290 inc(x,edgeWidth); |
|
291 end; |
|
292 flipSurface(SpritesData[edge].Surface, true); // restore original position |
|
293 |
|
294 rotatedEdge:= SDL_CreateRGBSurface(SDL_SWSURFACE, edgeHeight, edgeWidth, 32, RMask, GMask, BMask, AMask); |
|
295 x:= rect.w - edgeHeight - 1; |
|
296 y:= cornerHeight; |
|
297 //// initially was going to rotate in place, but the SDL spec claims width/height are read only |
|
298 copyRotatedSurface(SpritesData[edge].Surface,rotatedEdge); |
|
299 while y < textHeight + cornerHeight do |
|
300 begin |
|
301 copyToXY(rotatedEdge, finalSurface, x, y); |
|
302 inc(y,edgeWidth); |
|
303 end; |
|
304 flipSurface(rotatedEdge, false); // restore original position |
|
305 x:= 0; |
|
306 y:= cornerHeight; |
|
307 while y < textHeight + cornerHeight do |
|
308 begin |
|
309 copyToXY(rotatedEdge, finalSurface, x, y); |
|
310 inc(y,edgeWidth); |
|
311 end; |
|
312 //////////////////////////////// END EDGES ////////////////////////////////////// |
|
313 |
|
314 x:= cornerWidth; |
|
315 y:= textHeight + cornerHeight * 2 - edgeHeight - 1; |
|
316 copyToXY(SpritesData[tail].Surface, finalSurface, x, y); |
|
317 |
|
318 rect.x:= edgeHeight; |
|
319 rect.y:= edgeHeight; |
|
320 rect.w:= rect.w - edgeHeight * 2; |
|
321 rect.h:= textHeight + cornerHeight * 2 - edgeHeight * 2; |
|
322 i:= rect.w; |
|
323 j:= rect.h; |
|
324 SDL_FillRect(finalSurface, @rect, cWhiteColor); |
|
325 |
|
326 pos:= 1; prevpos:= 0; line:= 0; |
|
327 while pos <= length(s) do |
|
328 begin |
|
329 if (s[pos] = #1) or (pos = length(s)) then |
|
330 begin |
|
331 if s[pos] <> #1 then inc(pos); |
|
332 while s[prevpos+1] = ' 'do inc(prevpos); |
|
333 substr:= copy(s, prevpos+1, pos-prevpos-1); |
|
334 if Length(substr) <> 0 then |
|
335 begin |
|
336 tmpsurf:= TTF_RenderUTF8_Blended(Fontz[Font].Handle, Str2PChar(substr), cNearBlackColorChannels); |
|
337 rect.x:= edgeHeight + 1 + ((i - w) div 2); |
|
338 // trying to more evenly position the text, vertically |
|
339 rect.y:= edgeHeight + ((j-(numLines*h)) div 2) + line * h; |
|
340 SDLTry(tmpsurf <> nil, true); |
|
341 SDL_UpperBlit(tmpsurf, nil, finalSurface, @rect); |
|
342 SDL_FreeSurface(tmpsurf); |
|
343 inc(line); |
|
344 prevpos:= pos; |
|
345 end; |
|
346 end; |
|
347 inc(pos); |
|
348 end; |
|
349 |
|
350 RenderSpeechBubbleTex:= Surface2Tex(finalSurface, true); |
|
351 |
|
352 SDL_FreeSurface(rotatedEdge); |
|
353 SDL_FreeSurface(finalSurface); |
|
354 end; |
|
355 |
|
356 end. |