4380
+ − 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.