ImagingXpm.pas 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. {
  2. $Id$
  3. Vampyre Imaging Library
  4. by Marek Mauder
  5. http://imaginglib.sourceforge.net
  6. The contents of this file are used with permission, subject to the Mozilla
  7. Public License Version 1.1 (the "License"); you may not use this file except
  8. in compliance with the License. You may obtain a copy of the License at
  9. http://www.mozilla.org/MPL/MPL-1.1.html
  10. Software distributed under the License is distributed on an "AS IS" basis,
  11. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  12. the specific language governing rights and limitations under the License.
  13. Alternatively, the contents of this file may be used under the terms of the
  14. GNU Lesser General Public License (the "LGPL License"), in which case the
  15. provisions of the LGPL License are applicable instead of those above.
  16. If you wish to allow use of your version of this file only under the terms
  17. of the LGPL License and not to allow others to use your version of this file
  18. under the MPL, indicate your decision by deleting the provisions above and
  19. replace them with the notice and other provisions required by the LGPL
  20. License. If you do not delete the provisions above, a recipient may use
  21. your version of this file under either the MPL or the LGPL License.
  22. For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
  23. }
  24. { This unit contains image format loader for X Window Pixmap images.}
  25. unit ImagingXpm;
  26. {$I ImagingOptions.inc}
  27. interface
  28. uses
  29. SysUtils, Classes, ImagingTypes, Imaging, ImagingUtility, ImagingFormats,
  30. ImagingIO, ImagingCanvases;
  31. type
  32. { Class for loading X Window Pixmap images known as XPM.
  33. It is ASCII-text-based format, basicaly a fragment of C code
  34. declaring static array. Loaded image is in ifA8R8G8B8 data format.
  35. Only loading is supported now.}
  36. TXPMFileFormat = class(TImageFileFormat)
  37. protected
  38. function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray;
  39. OnlyFirstLevel: Boolean): Boolean; override;
  40. public
  41. constructor Create; override;
  42. function TestFormat(Handle: TImagingHandle): Boolean; override;
  43. end;
  44. implementation
  45. const
  46. SXPMFormatName = 'X Window Pixmap';
  47. SXPMMasks = '*.xpm';
  48. const
  49. SXPMId = '/* XPM */';
  50. WhiteSpaces = [#9, #10, #13, #32];
  51. type
  52. TColorHolder = class
  53. public
  54. Color: TColor32;
  55. end;
  56. {
  57. TXPMFileFormat implementation
  58. }
  59. constructor TXPMFileFormat.Create;
  60. begin
  61. inherited Create;
  62. FName := SXPMFormatName;
  63. FCanLoad := True;
  64. FCanSave := False;
  65. FIsMultiImageFormat := False;
  66. AddMasks(SXPMMasks);
  67. end;
  68. function TXPMFileFormat.LoadData(Handle: TImagingHandle;
  69. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  70. var
  71. Contents, PalLookup: TStringList;
  72. S: AnsiString;
  73. I, J, NumColors, Cpp, Line: Integer;
  74. procedure SkipWhiteSpace(var Line: string);
  75. begin
  76. while (Length(Line) > 0) and (Line[1] in WhiteSpaces) do
  77. Delete(Line, 1, 1);
  78. end;
  79. function ReadString(var Line: string): string;
  80. begin
  81. Result := '';
  82. SkipWhiteSpace(Line);
  83. while (Length(Line) > 0) and not (Line[1] in WhiteSpaces) do
  84. begin
  85. SetLength(Result, Length(Result) + 1);
  86. Result[Length(Result)] := Line[1];
  87. Delete(Line, 1, 1);
  88. end;
  89. end;
  90. function ReadInt(var Line: string): Integer;
  91. begin
  92. Result := StrToInt(ReadString(Line));
  93. end;
  94. function ParseHeader: Boolean;
  95. var
  96. S: string;
  97. begin
  98. S := Contents[0];
  99. try
  100. Images[0].Width := ReadInt(S);
  101. Images[0].Height := ReadInt(S);
  102. NumColors := ReadInt(S);
  103. Cpp := ReadInt(S);
  104. Line := 1;
  105. Result := True;
  106. except
  107. Result := False;
  108. end;
  109. end;
  110. function NamedToColor(const ColStr: string): TColor32;
  111. var
  112. S: string;
  113. begin
  114. S := LowerCase(ColStr);
  115. if (S = 'transparent') or (S = 'none') then
  116. Result := pcClear
  117. else if S = 'black' then
  118. Result := pcBlack
  119. else if S = 'blue' then
  120. Result := pcBlue
  121. else if S = 'green' then
  122. Result := pcGreen
  123. else if S = 'cyan' then
  124. Result := pcAqua
  125. else if S = 'red' then
  126. Result := pcRed
  127. else if S = 'magenta' then
  128. Result := pcFuchsia
  129. else if S = 'yellow' then
  130. Result := pcYellow
  131. else if S = 'white' then
  132. Result := pcWhite
  133. else if S = 'gray' then
  134. Result := pcLtGray
  135. else if S = 'dkblue' then
  136. Result := pcNavy
  137. else if S = 'dkgreen' then
  138. Result := pcGreen
  139. else if S = 'dkcyan' then
  140. Result := pcTeal
  141. else if S = 'dkred' then
  142. Result := pcMaroon
  143. else if S = 'dkmagenta' then
  144. Result := pcPurple
  145. else if S = 'dkyellow' then
  146. Result := pcOlive
  147. else if S = 'maroon' then
  148. Result := pcMaroon
  149. else if S = 'olive' then
  150. Result := pcOlive
  151. else if S = 'navy' then
  152. Result := pcNavy
  153. else if S = 'purple' then
  154. Result := pcPurple
  155. else if S = 'teal' then
  156. Result := pcTeal
  157. else if S = 'silver' then
  158. Result := pcSilver
  159. else if S = 'lime' then
  160. Result := pcLime
  161. else if S = 'fuchsia' then
  162. Result := pcFuchsia
  163. else if S = 'aqua' then
  164. Result := pcAqua
  165. else
  166. Result := pcClear;
  167. end;
  168. procedure ParsePalette;
  169. var
  170. I: Integer;
  171. S, ColType, ColStr, Code: string;
  172. Color: TColor32;
  173. Holder: TColorHolder;
  174. begin
  175. for I := 0 to NumColors - 1 do
  176. begin
  177. Holder := TColorHolder.Create;
  178. // Parse pixel code and color
  179. S := Contents[Line + I];
  180. Code := Copy(S, 1, Cpp);
  181. Delete(S, 1, Cpp);
  182. ColType := ReadString(S);
  183. ColStr := ReadString(S);
  184. // Convert color from hex number or named constant
  185. if ColStr[1] = '#' then
  186. begin
  187. Delete(ColStr, 1, 1);
  188. Color := LongWord(StrToInt('$' + ColStr)) or $FF000000;
  189. end
  190. else
  191. Color := NamedToColor(ColStr);
  192. // Store code and color in table for later lookup
  193. Holder.Color := Color;
  194. PalLookup.AddObject(Code, Holder);
  195. end;
  196. Inc(Line, NumColors);
  197. end;
  198. procedure ParsePixels;
  199. var
  200. X, Y, Idx: Integer;
  201. S, Code: string;
  202. Pix: PColor32;
  203. begin
  204. Pix := Images[0].Bits;
  205. for Y := 0 to Images[0].Height - 1 do
  206. begin
  207. S := Contents[Line + Y];
  208. for X := 0 to Images[0].Width - 1 do
  209. begin
  210. // Read code and look up color in the palette
  211. Code := Copy(S, X * Cpp + 1, Cpp);
  212. if PalLookup.Find(Code, Idx) then
  213. Pix^ := TColorHolder(PalLookup.Objects[Idx]).Color
  214. else
  215. Pix^ := pcClear;
  216. Inc(Pix);
  217. end;
  218. end;
  219. end;
  220. begin
  221. Result := False;
  222. SetLength(Images, 1);
  223. with GetIO, Images[0] do
  224. begin
  225. // Look up table for XPM palette entries
  226. PalLookup := TStringList.Create;
  227. PalLookup.Sorted := True;
  228. PalLookup.CaseSensitive := True;
  229. // Read whole file and assign it to string list
  230. Contents := TStringList.Create;
  231. SetLength(S, GetInputSize(GetIO, Handle));
  232. Read(Handle, @S[1], Length(S));
  233. Contents.Text := S;
  234. // Remove quotes and other stuff
  235. for I := Contents.Count - 1 downto 0 do
  236. begin
  237. J := Pos('"', Contents[I]);
  238. if J > 0 then
  239. Contents[I] := Copy(Contents[I], J + 1, LastDelimiter('"', Contents[I]) - J - 1)
  240. else
  241. Contents.Delete(I);
  242. end;
  243. // Parse header and create new image
  244. if not ParseHeader then
  245. Exit;
  246. NewImage(Width, Height, ifA8R8G8B8, Images[0]);
  247. // Read palette entries and assign colors to pixels
  248. ParsePalette;
  249. ParsePixels;
  250. Contents.Free;
  251. for I := 0 to PalLookup.Count - 1 do
  252. PalLookup.Objects[I].Free;
  253. PalLookup.Free;
  254. Result := True;
  255. end;
  256. end;
  257. function TXPMFileFormat.TestFormat(Handle: TImagingHandle): Boolean;
  258. var
  259. Id: array[0..8] of AnsiChar;
  260. ReadCount: Integer;
  261. begin
  262. Result := False;
  263. if Handle <> nil then
  264. begin
  265. ReadCount := GetIO.Read(Handle, @Id, SizeOf(Id));
  266. GetIO.Seek(Handle, -ReadCount, smFromCurrent);
  267. Result := (Id = SXPMId) and (ReadCount = SizeOf(Id));
  268. end;
  269. end;
  270. initialization
  271. RegisterImageFileFormat(TXPMFileFormat);
  272. {
  273. File Notes:
  274. -- TODOS ----------------------------------------------------
  275. - nothing now
  276. -- 0.25.0 Changes/Bug Fixes -----------------------------------
  277. - Added XPM loading.
  278. - Unit created.
  279. }
  280. end.