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