fpreadxpm.pp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2003 by the Free Pascal development team
  4. XPM reader class.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$mode objfpc}{$h+}
  12. unit FPReadXPM;
  13. interface
  14. uses FPImage, classes, sysutils;
  15. type
  16. TFPReaderXPM = class (TFPCustomImageReader)
  17. private
  18. width, height, ncols, cpp, xhot, yhot : integer;
  19. xpmext : boolean;
  20. palette : TStringList;
  21. function HexToColor(s : string) : TFPColor;
  22. function NameToColor(s : string) : TFPColor;
  23. function DiminishWhiteSpace (s : string) : string;
  24. protected
  25. procedure InternalRead (Str:TStream; Img:TFPCustomImage); override;
  26. function InternalCheck (Str:TStream) : boolean; override;
  27. public
  28. constructor Create; override;
  29. destructor Destroy; override;
  30. end;
  31. implementation
  32. const
  33. WhiteSpace = ' '#8#10#13;
  34. constructor TFPReaderXPM.create;
  35. begin
  36. inherited create;
  37. palette := TStringList.Create;
  38. end;
  39. destructor TFPReaderXPM.Destroy;
  40. begin
  41. Palette.Free;
  42. inherited destroy;
  43. end;
  44. function TFPReaderXPM.HexToColor(s : string) : TFPColor;
  45. var l : integer;
  46. function CharConv (c : char) : longword;
  47. begin
  48. if (c >= 'A') and (c <= 'F') then
  49. result := ord (c) - ord('A') + 10
  50. else if (c >= '0') and (c <= '9') then
  51. result := ord (c) - ord('0')
  52. else
  53. raise exception.CreateFmt ('Wrong character (%s) in hexadecimal number', [c]);
  54. end;
  55. function convert (n : string) : word;
  56. var t,r: integer;
  57. begin
  58. result := 0;
  59. t := length(n);
  60. if t > 4 then
  61. raise exception.CreateFmt ('Too many bytes for color (%s)',[s]);
  62. for r := 1 to length(n) do
  63. result := (result shl 4) or CharConv(n[r]);
  64. // fill missing bits
  65. case t of
  66. 1: result:=result or (result shl 4) or (result shl 8) or (result shl 12);
  67. 2: result:=result or (result shl 8);
  68. 3: result:=result or (result shl 12);
  69. end;
  70. end;
  71. begin
  72. s := uppercase (s);
  73. l := length(s) div 3;
  74. result.red := (Convert(copy(s,1,l)));
  75. result.green := (Convert(copy(s,l+1,l)));
  76. result.blue := Convert(copy(s,l+l+1,l));
  77. result.alpha := AlphaOpaque;
  78. end;
  79. function TFPReaderXPM.NameToColor(s : string) : TFPColor;
  80. begin
  81. s := lowercase (s);
  82. if s = 'transparent' then
  83. result := colTransparent
  84. else if s = 'none' then
  85. result := colTransparent
  86. else if s = 'black' then
  87. result := colBlack
  88. else if s = 'blue' then
  89. result := colBlue
  90. else if s = 'green' then
  91. result := colGreen
  92. else if s = 'cyan' then
  93. result := colCyan
  94. else if s = 'red' then
  95. result := colRed
  96. else if s = 'magenta' then
  97. result := colMagenta
  98. else if s = 'yellow' then
  99. result := colYellow
  100. else if s = 'white' then
  101. result := colWhite
  102. else if s = 'gray' then
  103. result := colGray
  104. else if s = 'ltgray' then
  105. result := colLtGray
  106. else if s = 'dkblue' then
  107. result := colDkBlue
  108. else if s = 'dkgreen' then
  109. result := colDkGreen
  110. else if s = 'dkcyan' then
  111. result := colDkCyan
  112. else if s = 'dkred' then
  113. result := colDkRed
  114. else if s = 'dkmagenta' then
  115. result := colDkMagenta
  116. else if s = 'dkyellow' then
  117. result := colDkYellow
  118. else if s = 'maroon' then
  119. result := colMaroon
  120. else if s = 'ltgreen' then
  121. result := colLtGreen
  122. else if s = 'olive' then
  123. result := colOlive
  124. else if s = 'navy' then
  125. result := colNavy
  126. else if s = 'purple' then
  127. result := colPurple
  128. else if s = 'teal' then
  129. result := colTeal
  130. else if s = 'silver' then
  131. result := colSilver
  132. else if s = 'lime' then
  133. result := colLime
  134. else if s = 'fuchsia' then
  135. result := colFuchsia
  136. else if s = 'aqua' then
  137. result := colAqua
  138. else
  139. result := colTransparent;
  140. end;
  141. function TFPReaderXPM.DiminishWhiteSpace (s : string) : string;
  142. var r : integer;
  143. Doit : boolean;
  144. begin
  145. Doit := true;
  146. result := '';
  147. for r := 1 to length(s) do
  148. if pos(s[r],WhiteSpace)>0 then
  149. begin
  150. if DoIt then
  151. result := result + ' ';
  152. DoIt := false;
  153. end
  154. else
  155. begin
  156. DoIt := True;
  157. result := result + s[r];
  158. end;
  159. end;
  160. procedure TFPReaderXPM.InternalRead (Str:TStream; Img:TFPCustomImage);
  161. var l : TStringList;
  162. procedure TakeInteger (var s : string; var i : integer);
  163. var r : integer;
  164. begin
  165. r := pos (' ', s);
  166. if r = 0 then
  167. begin
  168. i := StrToInt(s);
  169. s := '';
  170. end
  171. else
  172. begin
  173. i := StrToInt(copy(s,1,r-1));
  174. delete (s, 1, r);
  175. end;
  176. end;
  177. procedure ParseFirstLine;
  178. var s : string;
  179. begin
  180. s := l[0];
  181. // diminish all whitespace to 1 blank
  182. s := DiminishWhiteSpace (trim(s));
  183. Takeinteger (s, width);
  184. Takeinteger (s, height);
  185. Takeinteger (s, ncols);
  186. Takeinteger (s, cpp);
  187. if s <> '' then
  188. begin
  189. Takeinteger (s, xhot);
  190. Takeinteger (s, yhot);
  191. xpmext := (comparetext(s, 'XPMEXT') = 0);
  192. if (s <> '') and not xpmext then
  193. Raise Exception.Create ('Wrong word for XPMEXT tag');
  194. end;
  195. end;
  196. procedure AddPalette (const code:string;const Acolor:TFPColor);
  197. var r : integer;
  198. begin
  199. r := Palette.Add(code);
  200. img.palette.Color[r] := Acolor;
  201. end;
  202. procedure AddToPalette(s : string);
  203. var code : string;
  204. c : TFPColor;
  205. p : integer;
  206. begin
  207. code := copy(s,1,cpp);
  208. s := trim(diminishWhiteSpace (copy(s,cpp+1,maxint)));
  209. // Search for c-key in the color values
  210. if s[1] = 'c' then
  211. delete (s, 1, 2)
  212. else
  213. begin
  214. p := pos (' c ',s);
  215. if p = 0 then
  216. s := ''
  217. else
  218. delete (s, 1, p+2);
  219. end;
  220. // c color value is first word, remove the rest of the line
  221. p := pos(' ', s);
  222. if p > 0 then
  223. delete (s, p, maxint);
  224. // check if exists
  225. if s = '' then
  226. raise exception.Create ('Only c-key is used for colors');
  227. // convert #hexadecimal value to integer and place in palette
  228. if s[1] = '#' then
  229. c := HexToColor(copy(s,2,maxint))
  230. else
  231. c := NameToColor(s);
  232. AddPalette(code,c);
  233. end;
  234. procedure ReadPalette;
  235. var r : integer;
  236. begin
  237. Palette.Clear;
  238. Img.Palette.Count := ncols;
  239. for r := 1 to ncols do
  240. AddToPalette (l[r]);
  241. end;
  242. procedure ReadLine (const s : string; imgindex : integer);
  243. var color, r, p : integer;
  244. code : string;
  245. begin
  246. p := 1;
  247. for r := 1 to width do
  248. begin
  249. code := copy(s, p, cpp);
  250. inc(p,cpp);
  251. for color := 0 to Palette.Count-1 do
  252. { Can't use indexof, as compare must be case sensitive }
  253. if code = Palette[color] then begin
  254. img.pixels[r-1,imgindex] := color;
  255. Break;
  256. end;
  257. end;
  258. end;
  259. procedure ReadData;
  260. var r : integer;
  261. begin
  262. for r := 1 to height do
  263. ReadLine (l[ncols+r], r-1);
  264. end;
  265. var p, r : integer;
  266. begin
  267. l := TStringList.Create;
  268. try
  269. l.LoadFromStream (Str);
  270. for r := l.count-1 downto 0 do
  271. begin
  272. p := pos ('"', l[r]);
  273. if p > 0 then
  274. l[r] := copy(l[r], p+1, lastdelimiter('"',l[r])-p-1)
  275. else
  276. l.delete(r);
  277. end;
  278. ParseFirstLine;
  279. Img.SetSize (width, height);
  280. ReadPalette;
  281. ReadData;
  282. finally
  283. l.Free;
  284. end;
  285. end;
  286. function TFPReaderXPM.InternalCheck (Str:TStream) : boolean;
  287. var s : string[9];
  288. l : integer;
  289. begin
  290. try
  291. l := str.Read (s[1],9);
  292. s[0] := char(l);
  293. if l <> 9 then
  294. result := False
  295. else
  296. result := (s = '/* XPM */');
  297. except
  298. result := false;
  299. end;
  300. end;
  301. initialization
  302. ImageHandlers.RegisterImageReader ('XPM Format', 'xpm', TFPReaderXPM);
  303. end.