# HG changeset patch
# User unC0Rr
# Date 1290086395 -10800
# Node ID b78638b36b892f5dcbf839e2115685d31e545a91
# Parent  6cd6b77df8b8bf2efbb768716bdf85618ee2c927
Introduce uRenderUtils

diff -r 6cd6b77df8b8 -r b78638b36b89 hedgewars/CMakeLists.txt
--- a/hedgewars/CMakeLists.txt	Thu Nov 18 15:55:02 2010 +0300
+++ b/hedgewars/CMakeLists.txt	Thu Nov 18 16:19:55 2010 +0300
@@ -63,6 +63,7 @@
     uMobile.pas
     uRandom.pas
     uRender.pas
+    uRenderUtils.pas
     uScript.pas
     uSound.pas
     uStats.pas
diff -r 6cd6b77df8b8 -r b78638b36b89 hedgewars/SDLh.pas
--- a/hedgewars/SDLh.pas	Thu Nov 18 15:55:02 2010 +0300
+++ b/hedgewars/SDLh.pas	Thu Nov 18 16:19:55 2010 +0300
@@ -774,7 +774,7 @@
 function  TTF_Init: LongInt; cdecl; external SDL_TTFLibName;
 procedure TTF_Quit; cdecl; external SDL_TTFLibName;
 
-function  TTF_SizeUTF8(font: PTTF_Font; const text: PChar; var w, h: LongInt): LongInt; cdecl; external SDL_TTFLibName;
+function  TTF_SizeUTF8(font: PTTF_Font; const text: PChar; out w, h: LongInt): LongInt; cdecl; external SDL_TTFLibName;
 
 function  TTF_RenderUTF8_Solid(font: PTTF_Font; const text: PChar; fg: TSDL_Color): PSDL_Surface; cdecl; external SDL_TTFLibName;
 function  TTF_RenderUTF8_Blended(font: PTTF_Font; const text: PChar; fg: TSDL_Color): PSDL_Surface; cdecl; external SDL_TTFLibName;
diff -r 6cd6b77df8b8 -r b78638b36b89 hedgewars/uGears.pas
--- a/hedgewars/uGears.pas	Thu Nov 18 15:55:02 2010 +0300
+++ b/hedgewars/uGears.pas	Thu Nov 18 16:19:55 2010 +0300
@@ -47,7 +47,7 @@
 implementation
 uses uWorld, uStore, uSound, uTeams, uRandom, uCollisions, uIO, uLandGraphics,
      uAIMisc, uLocale, uAI, uAmmos, uStats, uVisualGears, uScript, GLunit, uMobile, uVariables,
-     uCommands, uUtils, uTextures, uRender;
+     uCommands, uUtils, uTextures, uRender, uRenderUtils;
 
 const MAXROPEPOINTS = 384;
 var RopePoints: record
diff -r 6cd6b77df8b8 -r b78638b36b89 hedgewars/uRenderUtils.pas
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uRenderUtils.pas	Thu Nov 18 16:19:55 2010 +0300
@@ -0,0 +1,356 @@
+{$INCLUDE "options.inc"}
+unit uRenderUtils;
+
+interface
+uses SDLh, uTypes;
+
+procedure flipSurface(Surface: PSDL_Surface; Vertical: Boolean);
+procedure copyRotatedSurface(src, dest: PSDL_Surface); // this is necessary since width/height are read only in SDL
+procedure copyToXY(src, dest: PSDL_Surface; destX, destY: LongInt);
+function  RenderStringTex(s: ansistring; Color: Longword; font: THWFont): PTexture;
+function  RenderSpeechBubbleTex(s: ansistring; SpeechType: Longword; font: THWFont): PTexture;
+procedure DrawRoundRect(rect: PSDL_Rect; BorderColor, FillColor: Longword; Surface: PSDL_Surface; Clear: boolean);
+
+implementation
+uses uIO, uUtils, uVariables, uConsts, uTextures, sysutils;
+
+procedure DrawRoundRect(rect: PSDL_Rect; BorderColor, FillColor: Longword; Surface: PSDL_Surface; Clear: boolean);
+var r: TSDL_Rect;
+begin
+    r:= rect^;
+    if Clear then SDL_FillRect(Surface, @r, 0);
+
+    BorderColor:= SDL_MapRGB(Surface^.format, BorderColor shr 16, BorderColor shr 8, BorderColor and $FF);
+    FillColor:= SDL_MapRGB(Surface^.format, FillColor shr 16, FillColor shr 8, FillColor and $FF);
+
+    r.y:= rect^.y + 1;
+    r.h:= rect^.h - 2;
+    SDL_FillRect(Surface, @r, BorderColor);
+    r.x:= rect^.x + 1;
+    r.w:= rect^.w - 2;
+    r.y:= rect^.y;
+    r.h:= rect^.h;
+    SDL_FillRect(Surface, @r, BorderColor);
+    r.x:= rect^.x + 2;
+    r.y:= rect^.y + 1;
+    r.w:= rect^.w - 4;
+    r.h:= rect^.h - 2;
+    SDL_FillRect(Surface, @r, FillColor);
+    r.x:= rect^.x + 1;
+    r.y:= rect^.y + 2;
+    r.w:= rect^.w - 2;
+    r.h:= rect^.h - 4;
+    SDL_FillRect(Surface, @r, FillColor)
+end;
+
+function WriteInRoundRect(Surface: PSDL_Surface; X, Y: LongInt; Color: LongWord; Font: THWFont; s: ansistring): TSDL_Rect;
+var w, h: LongInt;
+    tmpsurf: PSDL_Surface;
+    clr: TSDL_Color;
+    finalRect: TSDL_Rect;
+begin
+    TTF_SizeUTF8(Fontz[Font].Handle, Str2PChar(s), w, h);
+    finalRect.x:= X;
+    finalRect.y:= Y;
+    finalRect.w:= w + FontBorder * 2 + 4;
+    finalRect.h:= h + FontBorder * 2;
+    DrawRoundRect(@finalRect, cWhiteColor, endian(cNearBlackColorChannels.value), Surface, true);
+    clr.r:= (Color shr 16) and $FF;
+    clr.g:= (Color shr 8) and $FF;
+    clr.b:= Color and $FF;
+    tmpsurf:= TTF_RenderUTF8_Blended(Fontz[Font].Handle, Str2PChar(s), clr);
+    finalRect.x:= X + FontBorder + 2;
+    finalRect.y:= Y + FontBorder;
+    SDLTry(tmpsurf <> nil, true);
+    SDL_UpperBlit(tmpsurf, nil, Surface, @finalRect);
+    SDL_FreeSurface(tmpsurf);
+    finalRect.x:= X;
+    finalRect.y:= Y;
+    finalRect.w:= w + FontBorder * 2 + 4;
+    finalRect.h:= h + FontBorder * 2;
+    WriteInRoundRect:= finalRect;
+end;
+
+procedure flipSurface(Surface: PSDL_Surface; Vertical: Boolean);
+var y, x, i, j: LongInt;
+    tmpPixel: Longword;
+    pixels: PLongWordArray;
+begin
+    TryDo(Surface^.format^.BytesPerPixel = 4, 'flipSurface failed, expecting 32 bit surface', true);
+    pixels:= Surface^.pixels;
+    if Vertical then
+    for y := 0 to (Surface^.h div 2) - 1 do
+        for x := 0 to Surface^.w - 1 do
+            begin
+            i:= y * Surface^.w + x;
+            j:= (Surface^.h - y - 1) * Surface^.w + x;
+            tmpPixel:= pixels^[i];
+            pixels^[i]:= pixels^[j];
+            pixels^[j]:= tmpPixel;
+            end
+    else
+    for x := 0 to (Surface^.w div 2) - 1 do
+        for y := 0 to Surface^.h -1 do
+            begin
+            i:= y*Surface^.w + x;
+            j:= y*Surface^.w + (Surface^.w - x - 1);
+            tmpPixel:= pixels^[i];
+            pixels^[i]:= pixels^[j];
+            pixels^[j]:= tmpPixel;
+            end;
+end;
+
+procedure copyToXY(src, dest: PSDL_Surface; destX, destY: LongInt);
+var srcX, srcY, i, j, maxDest: LongInt;
+    srcPixels, destPixels: PLongWordArray;
+    r0, g0, b0, a0, r1, g1, b1, a1: Byte;
+begin
+    maxDest:= (dest^.pitch div 4) * dest^.h;
+    srcPixels:= src^.pixels;
+    destPixels:= dest^.pixels;
+
+    for srcX:= 0 to src^.w - 1 do
+    for srcY:= 0 to src^.h - 1 do
+        begin
+        i:= (destY + srcY) * (dest^.pitch div 4) + destX + srcX;
+        j:= srcY * (src^.pitch div 4) + srcX;
+        if (i < maxDest) and (srcPixels^[j] and AMask <> 0) then
+            begin
+            SDL_GetRGBA(destPixels^[i], dest^.format, @r0, @g0, @b0, @a0);
+            SDL_GetRGBA(srcPixels^[j], src^.format, @r1, @g1, @b1, @a1);
+            r0:= (r0 * (255 - LongInt(a1)) + r1 * LongInt(a1)) div 255;
+            g0:= (g0 * (255 - LongInt(a1)) + g1 * LongInt(a1)) div 255;
+            b0:= (b0 * (255 - LongInt(a1)) + b1 * LongInt(a1)) div 255;
+            a0:= (a0 * (255 - LongInt(a1)) + a1 * LongInt(a1)) div 255;
+            destPixels^[i]:= SDL_MapRGBA(dest^.format, r0, g0, b0, a0);
+            end;
+        end;
+end;
+
+procedure copyRotatedSurface(src, dest: PSDL_Surface); // this is necessary since width/height are read only in SDL, apparently
+var y, x, i, j: LongInt;
+    srcPixels, destPixels: PLongWordArray;
+begin
+    TryDo(src^.format^.BytesPerPixel = 4, 'rotateSurface failed, expecting 32 bit surface', true);
+    TryDo(dest^.format^.BytesPerPixel = 4, 'rotateSurface failed, expecting 32 bit surface', true);
+
+    srcPixels:= src^.pixels;
+    destPixels:= dest^.pixels;
+
+    j:= 0;
+    for x := 0 to src^.w - 1 do
+        for y := 0 to src^.h - 1 do
+            begin
+            i:= (src^.h - 1 - y) * (src^.pitch div 4) + x;
+            destPixels^[j]:= srcPixels^[i];
+            inc(j)
+            end;
+end;
+
+function  RenderStringTex(s: ansistring; Color: Longword; font: THWFont): PTexture;
+var w, h: LongInt;
+    finalSurface: PSDL_Surface;
+begin
+    if length(s) = 0 then s:= ' ';
+    font:= CheckCJKFont(s, font);
+    w:= 0; h:= 0; // avoid compiler hints
+    TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(s), w, h);
+
+    finalSurface:= SDL_CreateRGBSurface(SDL_SWSURFACE, w + FontBorder * 2 + 4, h + FontBorder * 2,
+            32, RMask, GMask, BMask, AMask);
+
+    TryDo(finalSurface <> nil, 'RenderString: fail to create surface', true);
+
+    WriteInRoundRect(finalSurface, 0, 0, Color, font, s);
+
+    TryDo(SDL_SetColorKey(finalSurface, SDL_SRCCOLORKEY, 0) = 0, errmsgTransparentSet, true);
+
+    RenderStringTex:= Surface2Tex(finalSurface, false);
+
+    SDL_FreeSurface(finalSurface);
+end;
+
+
+function RenderSpeechBubbleTex(s: ansistring; SpeechType: Longword; font: THWFont): PTexture;
+var textWidth, textHeight, x, y, w, h, i, j, pos, prevpos, line, numLines, edgeWidth, edgeHeight, cornerWidth, cornerHeight: LongInt;
+    finalSurface, tmpsurf, rotatedEdge: PSDL_Surface;
+    rect: TSDL_Rect;
+    chars: set of char = [#9,' ','.',';',':','?','!',','];
+    substr: shortstring;
+    edge, corner, tail: TSPrite;
+begin
+    case SpeechType of
+        1: begin;
+        edge:= sprSpeechEdge;
+        corner:= sprSpeechCorner;
+        tail:= sprSpeechTail;
+        end;
+        2: begin;
+        edge:= sprThoughtEdge;
+        corner:= sprThoughtCorner;
+        tail:= sprThoughtTail;
+        end;
+        3: begin;
+        edge:= sprShoutEdge;
+        corner:= sprShoutCorner;
+        tail:= sprShoutTail;
+        end;
+        end;
+    edgeHeight:= SpritesData[edge].Height;
+    edgeWidth:= SpritesData[edge].Width;
+    cornerWidth:= SpritesData[corner].Width;
+    cornerHeight:= SpritesData[corner].Height;
+    // This one screws up WrapText
+    //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...';
+    // This one does not
+    //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... ';
+
+    numLines:= 0;
+
+    if length(s) = 0 then s:= '...';
+    font:= CheckCJKFont(s, font);
+    w:= 0; h:= 0; // avoid compiler hints
+    TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(s), w, h);
+    if w<8 then w:= 8;
+    j:= 0;
+    if (length(s) > 20) then
+        begin
+        w:= 0;
+        i:= round(Sqrt(length(s)) * 2);
+        s:= WrapText(s, #1, chars, i);
+        pos:= 1; prevpos:= 0; line:= 0;
+    // Find the longest line for the purposes of centring the text.  Font dependant.
+        while pos <= length(s) do
+            begin
+            if (s[pos] = #1) or (pos = length(s)) then
+                begin
+                inc(numlines);
+                if s[pos] <> #1 then inc(pos);
+                while s[prevpos+1] = ' ' do inc(prevpos);
+                substr:= copy(s, prevpos+1, pos-prevpos-1);
+                i:= 0; j:= 0;
+                TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(substr), i, j);
+                if i > w then w:= i;
+                prevpos:= pos;
+                end;
+            inc(pos);
+            end;
+        end
+    else numLines := 1;
+
+    textWidth:=((w-(cornerWidth-edgeWidth)*2) div edgeWidth)*edgeWidth+edgeWidth;
+    textHeight:=(((numlines * h + 2)-((cornerHeight-edgeWidth)*2)) div edgeWidth)*edgeWidth;
+
+    textHeight:=max(textHeight,edgeWidth);
+    //textWidth:=max(textWidth,SpritesData[tail].Width);
+    rect.x:= 0;
+    rect.y:= 0;
+    rect.w:= textWidth + (cornerWidth * 2);
+    rect.h:= textHeight + cornerHeight*2 - edgeHeight + SpritesData[tail].Height;
+    //s:= inttostr(w) + ' ' + inttostr(numlines) + ' ' + inttostr(rect.x) + ' '+inttostr(rect.y) + ' ' + inttostr(rect.w) + ' ' + inttostr(rect.h);
+
+    finalSurface:= SDL_CreateRGBSurface(SDL_SWSURFACE, rect.w, rect.h, 32, RMask, GMask, BMask, AMask);
+
+    TryDo(finalSurface <> nil, 'RenderString: fail to create surface', true);
+
+    //////////////////////////////// CORNERS ///////////////////////////////
+    copyToXY(SpritesData[corner].Surface, finalSurface, 0, 0); /////////////////// NW
+
+    flipSurface(SpritesData[corner].Surface, true); // store all 4 versions in memory to avoid repeated flips?
+    x:= 0;
+    y:= textHeight + cornerHeight -1;
+    copyToXY(SpritesData[corner].Surface, finalSurface, x, y); /////////////////// SW
+
+    flipSurface(SpritesData[corner].Surface, false);
+    x:= rect.w-cornerWidth-1;
+    y:= textHeight + cornerHeight -1;
+    copyToXY(SpritesData[corner].Surface, finalSurface, x, y); /////////////////// SE
+
+    flipSurface(SpritesData[corner].Surface, true);
+    x:= rect.w-cornerWidth-1;
+    y:= 0;
+    copyToXY(SpritesData[corner].Surface, finalSurface, x, y); /////////////////// NE
+    flipSurface(SpritesData[corner].Surface, false); // restore original position
+    //////////////////////////////// END CORNERS ///////////////////////////////
+
+    //////////////////////////////// EDGES //////////////////////////////////////
+    x:= cornerWidth;
+    y:= 0;
+    while x < rect.w-cornerWidth-1 do
+        begin
+        copyToXY(SpritesData[edge].Surface, finalSurface, x, y); ///////////////// top edge
+        inc(x,edgeWidth);
+        end;
+    flipSurface(SpritesData[edge].Surface, true);
+    x:= cornerWidth;
+    y:= textHeight + cornerHeight*2 - edgeHeight-1;
+    while x < rect.w-cornerWidth-1 do
+        begin
+        copyToXY(SpritesData[edge].Surface, finalSurface, x, y); ///////////////// bottom edge
+        inc(x,edgeWidth);
+        end;
+    flipSurface(SpritesData[edge].Surface, true); // restore original position
+
+    rotatedEdge:= SDL_CreateRGBSurface(SDL_SWSURFACE, edgeHeight, edgeWidth, 32, RMask, GMask, BMask, AMask);
+    x:= rect.w - edgeHeight - 1;
+    y:= cornerHeight;
+    //// initially was going to rotate in place, but the SDL spec claims width/height are read only
+    copyRotatedSurface(SpritesData[edge].Surface,rotatedEdge);
+    while y < textHeight + cornerHeight do
+        begin
+        copyToXY(rotatedEdge, finalSurface, x, y);
+        inc(y,edgeWidth);
+        end;
+    flipSurface(rotatedEdge, false); // restore original position
+    x:= 0;
+    y:= cornerHeight;
+    while y < textHeight + cornerHeight do
+        begin
+        copyToXY(rotatedEdge, finalSurface, x, y);
+        inc(y,edgeWidth);
+        end;
+    //////////////////////////////// END EDGES //////////////////////////////////////
+
+    x:= cornerWidth;
+    y:= textHeight + cornerHeight * 2 - edgeHeight - 1;
+    copyToXY(SpritesData[tail].Surface, finalSurface, x, y);
+
+    rect.x:= edgeHeight;
+    rect.y:= edgeHeight;
+    rect.w:= rect.w - edgeHeight * 2;
+    rect.h:= textHeight + cornerHeight * 2 - edgeHeight * 2;
+    i:= rect.w;
+    j:= rect.h;
+    SDL_FillRect(finalSurface, @rect, cWhiteColor);
+
+    pos:= 1; prevpos:= 0; line:= 0;
+    while pos <= length(s) do
+        begin
+        if (s[pos] = #1) or (pos = length(s)) then
+            begin
+            if s[pos] <> #1 then inc(pos);
+            while s[prevpos+1] = ' 'do inc(prevpos);
+            substr:= copy(s, prevpos+1, pos-prevpos-1);
+            if Length(substr) <> 0 then
+            begin
+            tmpsurf:= TTF_RenderUTF8_Blended(Fontz[Font].Handle, Str2PChar(substr), cNearBlackColorChannels);
+            rect.x:= edgeHeight + 1 + ((i - w) div 2);
+            // trying to more evenly position the text, vertically
+            rect.y:= edgeHeight + ((j-(numLines*h)) div 2) + line * h;
+            SDLTry(tmpsurf <> nil, true);
+            SDL_UpperBlit(tmpsurf, nil, finalSurface, @rect);
+            SDL_FreeSurface(tmpsurf);
+            inc(line);
+            prevpos:= pos;
+            end;
+            end;
+        inc(pos);
+        end;
+
+    RenderSpeechBubbleTex:= Surface2Tex(finalSurface, true);
+
+    SDL_FreeSurface(rotatedEdge);
+    SDL_FreeSurface(finalSurface);
+end;
+
+end.
\ No newline at end of file
diff -r 6cd6b77df8b8 -r b78638b36b89 hedgewars/uStore.pas
--- a/hedgewars/uStore.pas	Thu Nov 18 15:55:02 2010 +0300
+++ b/hedgewars/uStore.pas	Thu Nov 18 16:19:55 2010 +0300
@@ -27,15 +27,7 @@
 
 procedure StoreLoad;
 procedure StoreRelease;
-procedure DrawRoundRect(rect: PSDL_Rect; BorderColor, FillColor: Longword; Surface: PSDL_Surface; Clear: boolean);
 procedure DrawHedgehog(X, Y: LongInt; Dir: LongInt; Pos, Step: LongWord; Angle: real);
-function  CheckCJKFont(s: ansistring; font: THWFont): THWFont;
-function  RenderStringTex(s: ansistring; Color: Longword; font: THWFont): PTexture;
-function  RenderSpeechBubbleTex(s: ansistring; SpeechType: Longword; font: THWFont): PTexture;
-procedure flipSurface(Surface: PSDL_Surface; Vertical: Boolean);
-//procedure rotateSurface(Surface: PSDL_Surface);
-procedure copyRotatedSurface(src, dest: PSDL_Surface); // this is necessary since width/height are read only in SDL
-procedure copyToXY(src, dest: PSDL_Surface; destX, destY: LongInt);
 procedure RenderHealth(var Hedgehog: THedgehog);
 procedure AddProgress;
 procedure FinishProgress;
@@ -48,7 +40,7 @@
 procedure FreeWeaponTooltip;
 
 implementation
-uses uMisc, uConsole, uLocale, uMobile, uVariables, uUtils, uTextures, uIO, uRender;
+uses uMisc, uConsole, uLocale, uMobile, uVariables, uUtils, uTextures, uIO, uRender, uRenderUtils;
 
 type TGPUVendor = (gvUnknown, gvNVIDIA, gvATI, gvIntel, gvApple);
 
@@ -56,64 +48,6 @@
     MaxTextureSize: LongInt;
     cGPUVendor: TGPUVendor;
 
-procedure DrawRoundRect(rect: PSDL_Rect; BorderColor, FillColor: Longword; Surface: PSDL_Surface; Clear: boolean);
-var r: TSDL_Rect;
-begin
-r:= rect^;
-if Clear then SDL_FillRect(Surface, @r, 0);
-
-BorderColor:= SDL_MapRGB(Surface^.format, BorderColor shr 16, BorderColor shr 8, BorderColor and $FF);
-FillColor:= SDL_MapRGB(Surface^.format, FillColor shr 16, FillColor shr 8, FillColor and $FF);
-
-r.y:= rect^.y + 1;
-r.h:= rect^.h - 2;
-SDL_FillRect(Surface, @r, BorderColor);
-r.x:= rect^.x + 1;
-r.w:= rect^.w - 2;
-r.y:= rect^.y;
-r.h:= rect^.h;
-SDL_FillRect(Surface, @r, BorderColor);
-r.x:= rect^.x + 2;
-r.y:= rect^.y + 1;
-r.w:= rect^.w - 4;
-r.h:= rect^.h - 2;
-SDL_FillRect(Surface, @r, FillColor);
-r.x:= rect^.x + 1;
-r.y:= rect^.y + 2;
-r.w:= rect^.w - 2;
-r.h:= rect^.h - 4;
-SDL_FillRect(Surface, @r, FillColor)
-end;
-
-function WriteInRoundRect(Surface: PSDL_Surface; X, Y: LongInt; Color: LongWord; Font: THWFont; s: ansistring): TSDL_Rect;
-var w, h: LongInt;
-    tmpsurf: PSDL_Surface;
-    clr: TSDL_Color;
-    finalRect: TSDL_Rect;
-begin
-w:= 0; h:= 0; // avoid compiler hints
-TTF_SizeUTF8(Fontz[Font].Handle, Str2PChar(s), w, h);
-finalRect.x:= X;
-finalRect.y:= Y;
-finalRect.w:= w + FontBorder * 2 + 4;
-finalRect.h:= h + FontBorder * 2;
-DrawRoundRect(@finalRect, cWhiteColor, endian(cNearBlackColorChannels.value), Surface, true);
-clr.r:= (Color shr 16) and $FF;
-clr.g:= (Color shr 8) and $FF;
-clr.b:= Color and $FF;
-tmpsurf:= TTF_RenderUTF8_Blended(Fontz[Font].Handle, Str2PChar(s), clr);
-finalRect.x:= X + FontBorder + 2;
-finalRect.y:= Y + FontBorder;
-SDLTry(tmpsurf <> nil, true);
-SDL_UpperBlit(tmpsurf, nil, Surface, @finalRect);
-SDL_FreeSurface(tmpsurf);
-finalRect.x:= X;
-finalRect.y:= Y;
-finalRect.w:= w + FontBorder * 2 + 4;
-finalRect.h:= h + FontBorder * 2;
-WriteInRoundRect:= finalRect;
-end;
-
 function WriteInRect(Surface: PSDL_Surface; X, Y: LongInt; Color: LongWord; Font: THWFont; s: ansistring): TSDL_Rect;
 var w, h: LongInt;
     tmpsurf: PSDL_Surface;
@@ -497,247 +431,6 @@
 end;
 
 
-function CheckCJKFont(s: ansistring; font: THWFont): THWFont;
-var l, i : LongInt;
-    u: WideChar;
-    tmpstr: array[0..256] of WideChar;
-begin
-
-{$IFNDEF IPHONEOS}
-// remove chinese fonts for now
-if (font >= CJKfnt16) or (length(s) = 0) then
-{$ENDIF}
-    exit(font);
-
-l:= Utf8ToUnicode(@tmpstr, Str2PChar(s), length(s))-1;
-i:= 0;
-while i < l do
-    begin
-    u:= tmpstr[i];
-    if (#$2E80  <= u) and  (
-                           (u <= #$2FDF )  or // CJK Radicals Supplement / Kangxi Radicals
-       ((#$2FF0  <= u) and (u <= #$303F))  or // Ideographic Description Characters / CJK Radicals Supplement
-       ((#$31C0  <= u) and (u <= #$31EF))  or // CJK Strokes
-       ((#$3200  <= u) and (u <= #$4DBF))  or // Enclosed CJK Letters and Months / CJK Compatibility / CJK Unified Ideographs Extension A
-       ((#$4E00  <= u) and (u <= #$9FFF))  or // CJK Unified Ideographs
-       ((#$F900  <= u) and (u <= #$FAFF))  or // CJK Compatibility Ideographs
-       ((#$FE30  <= u) and (u <= #$FE4F)))    // CJK Compatibility Forms
-       then exit(THWFont( ord(font) + ((ord(High(THWFont))+1) div 2) ));
-    inc(i)
-    end;
-exit(font);
-(* two more to check. pascal WideChar is only 16 bit though
-       ((#$20000 <= u) and (u >= #$2A6DF)) or // CJK Unified Ideographs Extension B
-       ((#$2F800 <= u) and (u >= #$2FA1F)))   // CJK Compatibility Ideographs Supplement *)
-end;
-
-function  RenderStringTex(s: ansistring; Color: Longword; font: THWFont): PTexture;
-var w, h: LongInt;
-    finalSurface: PSDL_Surface;
-begin
-if length(s) = 0 then s:= ' ';
-font:= CheckCJKFont(s, font);
-w:= 0; h:= 0; // avoid compiler hints
-TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(s), w, h);
-
-finalSurface:= SDL_CreateRGBSurface(SDL_SWSURFACE, w + FontBorder * 2 + 4, h + FontBorder * 2,
-         32, RMask, GMask, BMask, AMask);
-
-TryDo(finalSurface <> nil, 'RenderString: fail to create surface', true);
-
-WriteInRoundRect(finalSurface, 0, 0, Color, font, s);
-
-TryDo(SDL_SetColorKey(finalSurface, SDL_SRCCOLORKEY, 0) = 0, errmsgTransparentSet, true);
-
-RenderStringTex:= Surface2Tex(finalSurface, false);
-
-SDL_FreeSurface(finalSurface);
-end;
-
-function RenderSpeechBubbleTex(s: ansistring; SpeechType: Longword; font: THWFont): PTexture;
-var textWidth, textHeight, x, y, w, h, i, j, pos, prevpos, line, numLines, edgeWidth, edgeHeight, cornerWidth, cornerHeight: LongInt;
-    finalSurface, tmpsurf, rotatedEdge: PSDL_Surface;
-    rect: TSDL_Rect;
-    chars: TSysCharSet = [#9,' ','.',';',':','?','!',','];
-    substr: shortstring;
-    edge, corner, tail: TSPrite;
-begin
-
-case SpeechType of
-    1: begin;
-       edge:= sprSpeechEdge;
-       corner:= sprSpeechCorner;
-       tail:= sprSpeechTail;
-       end;
-    2: begin;
-       edge:= sprThoughtEdge;
-       corner:= sprThoughtCorner;
-       tail:= sprThoughtTail;
-       end;
-    3: begin;
-       edge:= sprShoutEdge;
-       corner:= sprShoutCorner;
-       tail:= sprShoutTail;
-       end;
-    end;
-edgeHeight:= SpritesData[edge].Height;
-edgeWidth:= SpritesData[edge].Width;
-cornerWidth:= SpritesData[corner].Width;
-cornerHeight:= SpritesData[corner].Height;
-// This one screws up WrapText
-//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...';
-// This one does not
-//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... ';
-
-numLines:= 0;
-
-if length(s) = 0 then s:= '...';
-font:= CheckCJKFont(s, font);
-w:= 0; h:= 0; // avoid compiler hints
-TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(s), w, h);
-if w<8 then w:= 8;
-j:= 0;
-if (length(s) > 20) then
-    begin
-    w:= 0;
-    i:= round(Sqrt(length(s)) * 2);
-    s:= WrapText(s, #1, chars, i);
-    pos:= 1; prevpos:= 0; line:= 0;
-// Find the longest line for the purposes of centring the text.  Font dependant.
-    while pos <= length(s) do
-        begin
-        if (s[pos] = #1) or (pos = length(s)) then
-            begin
-            inc(numlines);
-            if s[pos] <> #1 then inc(pos);
-            while s[prevpos+1] = ' ' do inc(prevpos);
-            substr:= copy(s, prevpos+1, pos-prevpos-1);
-            i:= 0; j:= 0;
-            TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(substr), i, j);
-            if i > w then w:= i;
-            prevpos:= pos;
-            end;
-        inc(pos);
-        end;
-    end
-else numLines := 1;
-
-textWidth:=((w-(cornerWidth-edgeWidth)*2) div edgeWidth)*edgeWidth+edgeWidth;
-textHeight:=(((numlines * h + 2)-((cornerHeight-edgeWidth)*2)) div edgeWidth)*edgeWidth;
-
-textHeight:=max(textHeight,edgeWidth);
-//textWidth:=max(textWidth,SpritesData[tail].Width);
-rect.x:= 0;
-rect.y:= 0;
-rect.w:= textWidth + (cornerWidth * 2);
-rect.h:= textHeight + cornerHeight*2 - edgeHeight + SpritesData[tail].Height;
-//s:= inttostr(w) + ' ' + inttostr(numlines) + ' ' + inttostr(rect.x) + ' '+inttostr(rect.y) + ' ' + inttostr(rect.w) + ' ' + inttostr(rect.h);
-
-finalSurface:= SDL_CreateRGBSurface(SDL_SWSURFACE, rect.w, rect.h, 32, RMask, GMask, BMask, AMask);
-
-TryDo(finalSurface <> nil, 'RenderString: fail to create surface', true);
-
-//////////////////////////////// CORNERS ///////////////////////////////
-copyToXY(SpritesData[corner].Surface, finalSurface, 0, 0); /////////////////// NW
-
-flipSurface(SpritesData[corner].Surface, true); // store all 4 versions in memory to avoid repeated flips?
-x:= 0;
-y:= textHeight + cornerHeight -1;
-copyToXY(SpritesData[corner].Surface, finalSurface, x, y); /////////////////// SW
-
-flipSurface(SpritesData[corner].Surface, false);
-x:= rect.w-cornerWidth-1;
-y:= textHeight + cornerHeight -1;
-copyToXY(SpritesData[corner].Surface, finalSurface, x, y); /////////////////// SE
-
-flipSurface(SpritesData[corner].Surface, true);
-x:= rect.w-cornerWidth-1;
-y:= 0;
-copyToXY(SpritesData[corner].Surface, finalSurface, x, y); /////////////////// NE
-flipSurface(SpritesData[corner].Surface, false); // restore original position
-//////////////////////////////// END CORNERS ///////////////////////////////
-
-//////////////////////////////// EDGES //////////////////////////////////////
-x:= cornerWidth;
-y:= 0;
-while x < rect.w-cornerWidth-1 do
-    begin
-    copyToXY(SpritesData[edge].Surface, finalSurface, x, y); ///////////////// top edge
-    inc(x,edgeWidth);
-    end;
-flipSurface(SpritesData[edge].Surface, true);
-x:= cornerWidth;
-y:= textHeight + cornerHeight*2 - edgeHeight-1;
-while x < rect.w-cornerWidth-1 do
-    begin
-    copyToXY(SpritesData[edge].Surface, finalSurface, x, y); ///////////////// bottom edge
-    inc(x,edgeWidth);
-    end;
-flipSurface(SpritesData[edge].Surface, true); // restore original position
-
-rotatedEdge:= SDL_CreateRGBSurface(SDL_SWSURFACE, edgeHeight, edgeWidth, 32, RMask, GMask, BMask, AMask);
-x:= rect.w - edgeHeight - 1;
-y:= cornerHeight;
-//// initially was going to rotate in place, but the SDL spec claims width/height are read only
-copyRotatedSurface(SpritesData[edge].Surface,rotatedEdge);
-while y < textHeight + cornerHeight do
-    begin
-    copyToXY(rotatedEdge, finalSurface, x, y);
-    inc(y,edgeWidth);
-    end;
-flipSurface(rotatedEdge, false); // restore original position
-x:= 0;
-y:= cornerHeight;
-while y < textHeight + cornerHeight do
-    begin
-    copyToXY(rotatedEdge, finalSurface, x, y);
-    inc(y,edgeWidth);
-    end;
-//////////////////////////////// END EDGES //////////////////////////////////////
-
-x:= cornerWidth;
-y:= textHeight + cornerHeight * 2 - edgeHeight - 1;
-copyToXY(SpritesData[tail].Surface, finalSurface, x, y);
-
-rect.x:= edgeHeight;
-rect.y:= edgeHeight;
-rect.w:= rect.w - edgeHeight * 2;
-rect.h:= textHeight + cornerHeight * 2 - edgeHeight * 2;
-i:= rect.w;
-j:= rect.h;
-SDL_FillRect(finalSurface, @rect, cWhiteColor);
-
-pos:= 1; prevpos:= 0; line:= 0;
-while pos <= length(s) do
-    begin
-    if (s[pos] = #1) or (pos = length(s)) then
-        begin
-        if s[pos] <> #1 then inc(pos);
-        while s[prevpos+1] = ' 'do inc(prevpos);
-        substr:= copy(s, prevpos+1, pos-prevpos-1);
-        if Length(substr) <> 0 then
-           begin
-           tmpsurf:= TTF_RenderUTF8_Blended(Fontz[Font].Handle, Str2PChar(substr), cNearBlackColorChannels);
-           rect.x:= edgeHeight + 1 + ((i - w) div 2);
-           // trying to more evenly position the text, vertically
-           rect.y:= edgeHeight + ((j-(numLines*h)) div 2) + line * h;
-           SDLTry(tmpsurf <> nil, true);
-           SDL_UpperBlit(tmpsurf, nil, finalSurface, @rect);
-           SDL_FreeSurface(tmpsurf);
-           inc(line);
-           prevpos:= pos;
-           end;
-        end;
-    inc(pos);
-    end;
-
-//TryDo(SDL_SetColorKey(finalSurface, SDL_SRCCOLORKEY, 0) = 0, errmsgTransparentSet, true);
-RenderSpeechBubbleTex:= Surface2Tex(finalSurface, true);
-
-SDL_FreeSurface(rotatedEdge);
-SDL_FreeSurface(finalSurface);
-end;
-
 procedure RenderHealth(var Hedgehog: THedgehog);
 var s: shortstring;
 begin
@@ -963,82 +656,6 @@
     perfExt_FinishProgress();
 end;
 
-procedure flipSurface(Surface: PSDL_Surface; Vertical: Boolean);
-var y, x, i, j: LongInt;
-    tmpPixel: Longword;
-    pixels: PLongWordArray;
-begin
-TryDo(Surface^.format^.BytesPerPixel = 4, 'flipSurface failed, expecting 32 bit surface', true);
-pixels:= Surface^.pixels;
-if Vertical then
-   for y := 0 to (Surface^.h div 2) - 1 do
-       for x := 0 to Surface^.w - 1 do
-           begin
-           i:= y * Surface^.w + x;
-           j:= (Surface^.h - y - 1) * Surface^.w + x;
-           tmpPixel:= pixels^[i];
-           pixels^[i]:= pixels^[j];
-           pixels^[j]:= tmpPixel;
-           end
-else
-   for x := 0 to (Surface^.w div 2) - 1 do
-       for y := 0 to Surface^.h -1 do
-           begin
-           i:= y*Surface^.w + x;
-           j:= y*Surface^.w + (Surface^.w - x - 1);
-           tmpPixel:= pixels^[i];
-           pixels^[i]:= pixels^[j];
-           pixels^[j]:= tmpPixel;
-           end;
-end;
-
-procedure copyToXY(src, dest: PSDL_Surface; destX, destY: LongInt);
-var srcX, srcY, i, j, maxDest: LongInt;
-    srcPixels, destPixels: PLongWordArray;
-    r0, g0, b0, a0, r1, g1, b1, a1: Byte;
-begin
-maxDest:= (dest^.pitch div 4) * dest^.h;
-srcPixels:= src^.pixels;
-destPixels:= dest^.pixels;
-
-for srcX:= 0 to src^.w - 1 do
-   for srcY:= 0 to src^.h - 1 do
-      begin
-      i:= (destY + srcY) * (dest^.pitch div 4) + destX + srcX;
-      j:= srcY * (src^.pitch div 4) + srcX;
-      if (i < maxDest) and (srcPixels^[j] and AMask <> 0) then
-         begin
-         SDL_GetRGBA(destPixels^[i], dest^.format, @r0, @g0, @b0, @a0);
-         SDL_GetRGBA(srcPixels^[j], src^.format, @r1, @g1, @b1, @a1);
-         r0:= (r0 * (255 - LongInt(a1)) + r1 * LongInt(a1)) div 255;
-         g0:= (g0 * (255 - LongInt(a1)) + g1 * LongInt(a1)) div 255;
-         b0:= (b0 * (255 - LongInt(a1)) + b1 * LongInt(a1)) div 255;
-         a0:= (a0 * (255 - LongInt(a1)) + a1 * LongInt(a1)) div 255;
-         destPixels^[i]:= SDL_MapRGBA(dest^.format, r0, g0, b0, a0);
-         end;
-      end;
-end;
-
-procedure copyRotatedSurface(src, dest: PSDL_Surface); // this is necessary since width/height are read only in SDL, apparently
-var y, x, i, j: LongInt;
-    srcPixels, destPixels: PLongWordArray;
-begin
-TryDo(src^.format^.BytesPerPixel = 4, 'rotateSurface failed, expecting 32 bit surface', true);
-TryDo(dest^.format^.BytesPerPixel = 4, 'rotateSurface failed, expecting 32 bit surface', true);
-
-srcPixels:= src^.pixels;
-destPixels:= dest^.pixels;
-
-j:= 0;
-for x := 0 to src^.w - 1 do
-    for y := 0 to src^.h - 1 do
-        begin
-        i:= (src^.h - 1 - y) * (src^.pitch div 4) + x;
-        destPixels^[j]:= srcPixels^[i];
-        inc(j)
-        end;
-end;
-
 function RenderHelpWindow(caption, subcaption, description, extra: ansistring; extracolor: LongInt; iconsurf: PSDL_Surface; iconrect: PSDL_Rect): PTexture;
 var tmpsurf: PSDL_SURFACE;
     w, h, i, j: LongInt;
diff -r 6cd6b77df8b8 -r b78638b36b89 hedgewars/uUtils.pas
--- a/hedgewars/uUtils.pas	Thu Nov 18 15:55:02 2010 +0300
+++ b/hedgewars/uUtils.pas	Thu Nov 18 16:19:55 2010 +0300
@@ -32,6 +32,8 @@
 
 function  endian(independent: LongWord): LongWord; inline;
 
+function  CheckCJKFont(s: ansistring; font: THWFont): THWFont;
+
 {$IFDEF DEBUGFILE}
 procedure AddFileLog(s: shortstring);
 {$ENDIF}
@@ -228,6 +230,40 @@
 {$ENDIF}
 
 
+function CheckCJKFont(s: ansistring; font: THWFont): THWFont;
+var l, i : LongInt;
+    u: WideChar;
+    tmpstr: array[0..256] of WideChar;
+begin
+
+{$IFNDEF IPHONEOS}
+// remove chinese fonts for now
+if (font >= CJKfnt16) or (length(s) = 0) then
+{$ENDIF}
+    exit(font);
+
+l:= Utf8ToUnicode(@tmpstr, Str2PChar(s), length(s))-1;
+i:= 0;
+while i < l do
+    begin
+    u:= tmpstr[i];
+    if (#$2E80  <= u) and  (
+                           (u <= #$2FDF )  or // CJK Radicals Supplement / Kangxi Radicals
+       ((#$2FF0  <= u) and (u <= #$303F))  or // Ideographic Description Characters / CJK Radicals Supplement
+       ((#$31C0  <= u) and (u <= #$31EF))  or // CJK Strokes
+       ((#$3200  <= u) and (u <= #$4DBF))  or // Enclosed CJK Letters and Months / CJK Compatibility / CJK Unified Ideographs Extension A
+       ((#$4E00  <= u) and (u <= #$9FFF))  or // CJK Unified Ideographs
+       ((#$F900  <= u) and (u <= #$FAFF))  or // CJK Compatibility Ideographs
+       ((#$FE30  <= u) and (u <= #$FE4F)))    // CJK Compatibility Forms
+       then exit(THWFont( ord(font) + ((ord(High(THWFont))+1) div 2) ));
+    inc(i)
+    end;
+exit(font);
+(* two more to check. pascal WideChar is only 16 bit though
+       ((#$20000 <= u) and (u >= #$2A6DF)) or // CJK Unified Ideographs Extension B
+       ((#$2F800 <= u) and (u >= #$2FA1F)))   // CJK Compatibility Ideographs Supplement *)
+end;
+
 procedure initModule;
 {$IFDEF DEBUGFILE}{$IFNDEF IPHONEOS}var i: LongInt;{$ENDIF}{$ENDIF}
 begin
diff -r 6cd6b77df8b8 -r b78638b36b89 hedgewars/uVisualGears.pas
--- a/hedgewars/uVisualGears.pas	Thu Nov 18 15:55:02 2010 +0300
+++ b/hedgewars/uVisualGears.pas	Thu Nov 18 16:19:55 2010 +0300
@@ -34,7 +34,7 @@
 procedure AddDamageTag(X, Y, Damage, Color: LongWord);
 
 implementation
-uses uMisc, uStore, uSound, uMobile, uVariables, uTextures, uRender, uUtils, Math;
+uses uMisc, uStore, uSound, uMobile, uVariables, uTextures, uRender, uUtils, Math, uRenderUtils;
 
 const cExplFrameTicks = 110;
 
diff -r 6cd6b77df8b8 -r b78638b36b89 hedgewars/uWorld.pas
--- a/hedgewars/uWorld.pas	Thu Nov 18 15:55:02 2010 +0300
+++ b/hedgewars/uWorld.pas	Thu Nov 18 16:19:55 2010 +0300
@@ -48,7 +48,8 @@
     uVariables,
     uUtils,
     uTextures,
-    uRender
+    uRender,
+    uRenderUtils
     ;
 
 type TCaptionStr = record