sdlmonofonts.pas 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. unit sdlmonofonts;
  2. {******************************************************************}
  3. { }
  4. { SDL_MonoFonts unit by Róbert Kisnémeth (KiCHY) }
  5. { This unit is part of SDLGui by Róbert Kisnémeth, but works }
  6. { without it. Use and distribute it freely in its unaltered state. }
  7. { }
  8. { If you wish supporting languages other than English & Hungarian }
  9. { send me a letter and I try to implement it (but not Cyrillic or }
  10. { Chinese or something exotic charset, please. Only a few letters.)}
  11. { I know p.e. the French or Spanish (or Finnish) have special }
  12. { characters like us, Hungarians, but I'm very lazy... }
  13. { }
  14. { E-mail: [email protected] }
  15. { }
  16. { Revision History }
  17. { ---------------- }
  18. { September 21 2001 - RK : Initial v1.0 version }
  19. { October 28 2001 - RK : v1.01 Fixed a bug which found by }
  20. { Wojciech ([email protected]) }
  21. { }
  22. {******************************************************************}
  23. {$I jedi-sdl.inc}
  24. interface
  25. uses
  26. Classes,
  27. SysUtils,
  28. sdl,
  29. sdl_image,
  30. sdlutils;
  31. const
  32. CharSet = ' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~áéíóöõúüûÁÉÍÓÖÕÚÜÛ';
  33. type
  34. TAlignment = (taLeftJustify, taRightJustify, taCenter);
  35. PFont = ^TFont;
  36. TFont = object
  37. private
  38. Image: PSDL_Surface;
  39. Rects: array[0..112] of PSDL_Rect;
  40. procedure WriteText2(x, y: integer; Txt: Pchar; TextLength: cardinal);
  41. public
  42. TransparentColor, TextColor: cardinal;
  43. Surface: PSDL_Surface;
  44. function Height: integer;
  45. constructor Initialize(const Filename: string);
  46. destructor Finalize;
  47. procedure LoadFont(const Fontfile: string);
  48. procedure FreeUpAll;
  49. procedure WriteText(x, y: integer; Txt: PChar; Align: TAlignment);
  50. procedure WriteTextWrapped(Rect: PSDL_Rect; Txt: PChar; Align: TAlignment);
  51. function TextWidth: integer;
  52. function WidthOf(Txt: PChar; Len: cardinal): integer; overload;
  53. function WidthOf(Txt: PChar): integer; overload;
  54. end;
  55. implementation
  56. constructor TFont.Initialize(const Filename: string);
  57. begin
  58. LoadFont(Filename);
  59. end;
  60. procedure TFont.LoadFont(const Fontfile: string);
  61. var
  62. i, x , width: integer;
  63. Separator: Cardinal;
  64. begin
  65. FreeUpAll;
  66. if not fileexists(Fontfile) then exit;
  67. Image:=IMG_Load(pchar(Fontfile));
  68. if Image = nil then exit;
  69. Separator:=SDL_MapRGB(Image.format, 255, 0, 255);
  70. x:=0; i:=0;
  71. repeat
  72. // Search first/next separator
  73. while SDL_GetPixel(Image, x, 0)<>Separator do inc(x);
  74. // Determine character's width
  75. if x>=Image.w then break;
  76. Width:=1;
  77. while SDL_GetPixel(Image, x + Width, 0) = Separator do
  78. inc(Width);
  79. Rects[i]:=PSDLRect(x, 1, Width, Image.h-1);
  80. inc(i);
  81. inc(x, Width+1);
  82. until x>=Image.w;
  83. // Determine the transparent color
  84. TransparentColor:=SDL_GetPixel(Image, Rects[0].x+Rects[0].w, 0);
  85. SDL_SetColorKey(Image, SDL_SRCCOLORKEY, TransparentColor);
  86. TextColor:=SDL_MapRGB(Image.format, 0, 0, 0);
  87. end;
  88. procedure TFont.FreeUpAll;
  89. var
  90. i: integer;
  91. begin
  92. for i:=0 to 112 do
  93. if Rects[i]<>nil then Dispose(Rects[i]);
  94. if Image<>nil then SDL_FreeSurface(Image);
  95. end;
  96. destructor TFont.Finalize;
  97. begin
  98. FreeUpAll;
  99. end;
  100. // Read a word from a string until its end or CRLF
  101. procedure ReadWord(Txt: PChar; StartPos: cardinal; var FoundWord: PChar; var ItsLength: cardinal);
  102. var
  103. WasLetter: boolean;
  104. ReadPos, TextLength: integer;
  105. begin
  106. TextLength:=length(Txt);
  107. WasLetter:=false;
  108. ReadPos:=StartPos;
  109. repeat
  110. case Txt[ReadPos] of
  111. ' ': if WasLetter=true then break;
  112. #13: begin
  113. inc(ReadPos,1);
  114. break;
  115. end;
  116. else WasLetter:=true
  117. end;
  118. inc(ReadPos);
  119. until ReadPos>=TextLength;
  120. FoundWord:=pointer(cardinal(Txt)+StartPos);
  121. ItsLength:=ReadPos-StartPos;
  122. end;
  123. function ContainsCR(Txt: PChar; Len: cardinal):boolean;
  124. var
  125. i: integer;
  126. begin
  127. result:=false;
  128. for i:=0 to Len-1 do
  129. if Txt[i]=#13 then begin
  130. Result:=true;
  131. exit;
  132. end;
  133. end;
  134. procedure TFont.WriteTextWrapped(Rect: PSDL_Rect; Txt: PChar; Align: TAlignment);
  135. var
  136. Original_Clip_Rect: TSDL_Rect; // Store the original clipping rectangle
  137. ReadFrom: cardinal; // Reading position
  138. TextLength: cardinal; // The whole text's length
  139. FoundWord: PChar; // The word we found
  140. WordLen: cardinal; // Length of the word we found
  141. Area: TSDL_Rect; // The rectangle we draw in
  142. RowLengthInPixels: cardinal; // Stores a row's length in pixels
  143. RowLengthInChars: cardinal; // Stores a row's length in chars
  144. FoundRow: PChar; // The row we will write out
  145. x, y: integer; // Drawing position
  146. NextWordsLengthInPixels: cardinal;
  147. begin
  148. if (Surface=nil) or (Image=nil) or (Txt=nil) or (Txt='') then exit;
  149. Original_Clip_Rect:=Surface.Clip_Rect;
  150. if Rect=nil then Area:=Surface.Clip_Rect
  151. else Area:=Rect^;
  152. Surface.Clip_Rect:=Area;
  153. ReadFrom:=0;
  154. x:=Area.x;
  155. y:=Area.y;
  156. TextLength:=length(Txt);
  157. repeat
  158. // Collect words until it don't fit in Area's width
  159. // A row always contains minimum one word
  160. ReadWord(Txt, ReadFrom, FoundRow, WordLen); // Read a whole word from text
  161. RowLengthInPixels:=WidthOf(FoundRow, WordLen);
  162. RowLengthInChars:=WordLen;
  163. ReadFrom:=ReadFrom+WordLen+1; // Advance to next word
  164. // Read more words if it fits in Area's width
  165. repeat
  166. if ContainsCR(FoundRow, RowLengthInChars) then break; // We found a CR so break the line
  167. ReadWord(Txt, ReadFrom, FoundWord, WordLen); // Read a whole word from text
  168. NextWordsLengthInPixels:=WidthOf(FoundWord, WordLen);
  169. if RowLengthInPixels+Rects[0].w+1+NextWordsLengthInPixels<Area.w then begin
  170. RowLengthInPixels:=RowLengthInPixels+Rects[0].w+1+NextWordsLengthInPixels;
  171. RowLengthInChars:=RowLengthInChars+1+WordLen;
  172. ReadFrom:=ReadFrom+WordLen+1; // Advance to next word
  173. end else break;
  174. until (RowLengthInPixels>=Area.w) or (ReadFrom>=TextLength);
  175. // calculate alignment
  176. case Align of
  177. taLeftJustify: x:=Area.x;
  178. taCenter: x:=(Area.x+Area.w shr 1)-(WidthOf(FoundRow, RowLengthInChars)-1) shr 1;
  179. taRightJustify: x:=Area.x+Area.w-WidthOf(FoundRow, RowLengthInChars)+1;
  180. end;
  181. WriteText2(x, y, FoundRow, RowLengthInChars);
  182. y:=y+Rects[0].h;
  183. until (y>=Area.y+Area.h) or (ReadFrom>=TextLength);
  184. Surface.Clip_Rect:=Original_Clip_Rect;
  185. end;
  186. // Draw a text in a single line with clipping x & y
  187. procedure TFont.WriteText(x, y: integer; Txt: Pchar; Align: TAlignment);
  188. var
  189. i, len, ch, px, py: integer;
  190. TargetX: integer; // writing position after aligning
  191. begin
  192. if (Surface=nil) or (Image=nil) or (Txt=nil) or (Txt='') then exit;
  193. SDL_LockSurface(Surface);
  194. SDL_LockSurface(Image);
  195. i:=0;
  196. len:=length(txt);
  197. case Align of
  198. taLeftJustify: TargetX:=x;
  199. taCenter: TargetX:=x-(WidthOf(Txt)-1) shr 1;
  200. taRightJustify: TargetX:=x-WidthOf(Txt)+1;
  201. end;
  202. while i<len do begin
  203. if x>=Surface.Clip_Rect.x+Surface.Clip_Rect.w then break; // We reached the right side
  204. ch:=pos(Txt[i], Charset)-1;
  205. if (ch>=0) and (ch<113) then begin
  206. for px:=0 to Rects[ch]^.w-1 do
  207. if (TargetX+px >= Surface.Clip_Rect.x) and // Clip from left
  208. (TargetX+px<Surface.Clip_Rect.x+Surface.Clip_Rect.w) then // Clip from right
  209. for py:=0 to Rects[ch]^.h-1 do
  210. if y+py<Surface.Clip_Rect.y+Surface.Clip_Rect.h then // if we don't reach the bottom border
  211. if SDL_GetPixel(Image, Rects[ch]^.x+px, Rects[ch]^.y+py)<>TransparentColor then
  212. SDL_PutPixel(Surface, TargetX+px, y+py, TextColor);
  213. TargetX:=TargetX+Rects[ch].w+1;
  214. end;
  215. inc(i);
  216. end;
  217. SDL_UnlockSurface(Surface);
  218. SDL_UnlockSurface(Image);
  219. end;
  220. // Draw a partial text in a single line without clipping x
  221. procedure TFont.WriteText2(x, y: integer; Txt: Pchar; TextLength: cardinal);
  222. var
  223. i, ch, px, py: integer;
  224. begin
  225. if (Surface=nil) or (Image=nil) or (Txt=nil) or (Txt='') then exit;
  226. SDL_LockSurface(Surface);
  227. SDL_LockSurface(Image);
  228. i:=0;
  229. while i<TextLength do begin
  230. ch:=pos(Txt[i], CharSet)-1;
  231. if (ch>=0) and (ch<113) then begin
  232. for px:=0 to Rects[ch]^.w-1 do
  233. for py:=0 to Rects[ch]^.h-1 do
  234. if y+py<Surface.Clip_Rect.y+Surface.Clip_Rect.h then
  235. if SDL_GetPixel(Image, Rects[ch]^.x+px, Rects[ch]^.y+py)<>TransparentColor then
  236. SDL_PutPixel(Surface, x+px, y+py, TextColor);
  237. x:=x+Rects[ch].w+1;
  238. end;
  239. inc(i);
  240. end;
  241. SDL_UnlockSurface(Surface);
  242. SDL_UnlockSurface(Image);
  243. end;
  244. function TFont.TextWidth: integer;
  245. begin
  246. Result:=0;
  247. end;
  248. function TFont.WidthOf(Txt: PChar; Len: cardinal): integer;
  249. var
  250. i: cardinal;
  251. p: integer;
  252. begin
  253. Result:=0;
  254. for i:=0 to Len-1 do begin
  255. p:=pos(Txt[i], CharSet)-1;
  256. if p>=0 then Result:=Result+Rects[p].w+1;
  257. end;
  258. end;
  259. function TFont.WidthOf(Txt: PChar): integer;
  260. var
  261. i, len: cardinal;
  262. p: integer;
  263. begin
  264. Result:=0;
  265. Len:=Length(Txt);
  266. for i:=0 to Len-1 do begin
  267. p:=pos(Txt[i], CharSet)-1;
  268. if p>=0 then Result:=Result+Rects[p].w+1;
  269. end;
  270. end;
  271. function TFont.Height: integer;
  272. begin
  273. if Image<>nil then Result:=Image.h else Result:=0;
  274. end;
  275. end.