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
+ − 71
TTF_SizeUTF8(Fontz[Font].Handle, Str2PChar(s), w, h);
+ − 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
+ − 176
TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(s), w, h);
+ − 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
+ − 232
TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(s), w, h);
+ − 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;
+ − 251
TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(substr), i, j);
+ − 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.