2
0

fpreadpnm.pp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. {*****************************************************************************}
  2. {
  3. This file is part of the Free Pascal's "Free Components Library".
  4. Copyright (c) 2003 by Mazen NEIFER of the Free Pascal development team
  5. PNM writer implementation.
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. }
  12. {*****************************************************************************}
  13. {
  14. The PNM (Portable aNyMaps) is a generic name for :
  15. PBM : Portable BitMaps,
  16. PGM : Portable GrayMaps,
  17. PPM : Portable PixMaps.
  18. There is no file format associated with PNM itself.}
  19. {$mode objfpc}{$h+}
  20. unit FPReadPNM;
  21. interface
  22. uses FPImage, classes, sysutils;
  23. type
  24. TFPReaderPNM=class (TFPCustomImageReader)
  25. private
  26. FBitMapType : Integer;
  27. FWidth : Integer;
  28. FHeight : Integer;
  29. protected
  30. FMaxVal : Cardinal;
  31. FBitPP : Byte;
  32. FScanLineSize : Integer;
  33. FScanLine : PByte;
  34. procedure ReadHeader(Stream : TStream);
  35. function InternalCheck (Stream:TStream):boolean;override;
  36. procedure InternalRead(Stream:TStream;Img:TFPCustomImage);override;
  37. procedure ReadScanLine(Row : Integer; Stream:TStream);
  38. procedure WriteScanLine(Row : Integer; Img : TFPCustomImage);
  39. end;
  40. implementation
  41. function TFPReaderPNM.InternalCheck(Stream:TStream):boolean;
  42. begin
  43. InternalCheck:=True;
  44. end;
  45. const
  46. WhiteSpaces=[#9,#10,#13,#32]; {Whitespace (TABs, CRs, LFs, blanks) are separators in the PNM Headers}
  47. function DropWhiteSpaces(Stream : TStream) :Char;
  48. begin
  49. with Stream do
  50. begin
  51. repeat
  52. ReadBuffer(DropWhiteSpaces,1);
  53. {If we encounter comment then eate line}
  54. if DropWhiteSpaces='#' then
  55. repeat
  56. ReadBuffer(DropWhiteSpaces,1);
  57. until DropWhiteSpaces=#10;
  58. until not(DropWhiteSpaces in WhiteSpaces);
  59. end;
  60. end;
  61. function ReadInteger(Stream : TStream) :Integer;
  62. var
  63. s:String[7];
  64. begin
  65. s:='';
  66. s[1]:=DropWhiteSpaces(Stream);
  67. with Stream do
  68. repeat
  69. Inc(s[0]);
  70. ReadBuffer(s[Length(s)+1],1)
  71. until s[Length(s)+1] in WhiteSpaces;
  72. Result:=StrToInt(s);
  73. end;
  74. procedure TFPReaderPNM.ReadHeader(Stream : TStream);
  75. Var
  76. C : Char;
  77. begin
  78. Stream.ReadBuffer(C,1);
  79. If (C<>'P') then
  80. Raise Exception.Create('Not a valid PNM image.');
  81. Stream.ReadBuffer(C,1);
  82. FBitmapType:=Ord(C)-Ord('0');
  83. If Not (FBitmapType in [1..6]) then
  84. Raise Exception.CreateFmt('Unknown PNM subtype : %s',[C]);
  85. FWidth:=ReadInteger(Stream);
  86. FHeight:=ReadInteger(Stream);
  87. if FBitMapType in [1,4]
  88. then
  89. FMaxVal:=1
  90. else
  91. FMaxVal:=ReadInteger(Stream);
  92. If (FWidth<=0) or (FHeight<=0) or (FMaxVal<=0) then
  93. Raise Exception.Create('Invalid PNM header data');
  94. case FBitMapType of
  95. 1: FBitPP := SizeOf(Word);
  96. 2: FBitPP := 8 * SizeOf(Word); // Grayscale (text)
  97. 3: FBitPP := 8 * SizeOf(Word)*3; // RGB (text)
  98. 4: FBitPP := 1; // 1bit PP (row)
  99. 5: If (FMaxval>255) then // Grayscale (raw);
  100. FBitPP:= 8 * 2
  101. else
  102. FBitPP:= 8;
  103. 6: if (FMaxVal>255) then // RGB (raw)
  104. FBitPP:= 8 * 6
  105. else
  106. FBitPP:= 8 * 3
  107. end;
  108. // Writeln(FWidth,'x',Fheight,' Maxval: ',FMaxVal,' BitPP: ',FBitPP);
  109. end;
  110. procedure TFPReaderPNM.InternalRead(Stream:TStream;Img:TFPCustomImage);
  111. var
  112. Row:Integer;
  113. begin
  114. ReadHeader(Stream);
  115. Img.SetSize(FWidth,FHeight);
  116. FScanLineSize:=FBitPP*((FWidth+7)shr 3);
  117. GetMem(FScanLine,FScanLineSize);
  118. try
  119. for Row:=0 to img.Height-1 do
  120. begin
  121. ReadScanLine(Row,Stream);
  122. WriteScanLine(Row,Img);
  123. end;
  124. finally
  125. FreeMem(FScanLine);
  126. end;
  127. end;
  128. procedure TFPReaderPNM.ReadScanLine(Row : Integer; Stream:TStream);
  129. Var
  130. P : PWord;
  131. I,j : Integer;
  132. begin
  133. Case FBitmapType of
  134. 1 : begin
  135. P:=PWord(FScanLine);
  136. For I:=0 to ((FWidth+7)shr 3)-1 do
  137. begin
  138. P^:=0;
  139. for j:=0 to 7 do
  140. P^:=(P^ shr 1)or ReadInteger(Stream);
  141. Inc(P);
  142. end;
  143. end;
  144. 2 : begin
  145. P:=PWord(FScanLine);
  146. For I:=0 to FWidth-1 do
  147. begin
  148. P^:=ReadInteger(Stream);
  149. Inc(P);
  150. end;
  151. end;
  152. 3 : begin
  153. P:=PWord(FScanLine);
  154. For I:=0 to FWidth-1 do
  155. begin
  156. P^:=ReadInteger(Stream); // Red
  157. Inc(P);
  158. P^:=ReadInteger(Stream); // Green
  159. Inc(P);
  160. P^:=ReadInteger(Stream); // Blue;
  161. Inc(P)
  162. end;
  163. end;
  164. 4,5,6 : Stream.ReadBuffer(FScanLine^,FScanLineSize);
  165. end;
  166. end;
  167. procedure TFPReaderPNM.WriteScanLine(Row : Integer; Img : TFPCustomImage);
  168. Var
  169. C : TFPColor;
  170. L : Cardinal;
  171. Scale: Cardinal;
  172. function ScaleByte(B: Byte):Word;
  173. begin
  174. if FMaxVal = 255 then
  175. Result := (B shl 8) or B { As used for reading .BMP files }
  176. else { Mimic the above with multiplications }
  177. Result := (B*(FMaxVal+1) + B) * 65535 div Scale;
  178. end;
  179. function ScaleWord(W: Word):Word;
  180. begin
  181. if FMaxVal = 65535 then
  182. Result := W
  183. else { Mimic the above with multiplications }
  184. Result := Int64(W*(FMaxVal+1) + W) * 65535 div Scale;
  185. end;
  186. Procedure ByteBnWScanLine;
  187. Var
  188. P : PByte;
  189. I,j,x : Integer;
  190. begin
  191. P:=PByte(FScanLine);
  192. x:=7;
  193. For I:=0 to ((FWidth+7)shr 3)-1 do
  194. begin
  195. L:=P^;
  196. for j:=0 to 7 do
  197. begin
  198. if odd(L)
  199. then
  200. Img.Colors[x,Row]:=colBlack
  201. else
  202. Img.Colors[x,Row]:=colWhite;
  203. L:=L shr 1;
  204. dec(x);
  205. end;
  206. Inc(P);
  207. Inc(x,16);
  208. end;
  209. end;
  210. Procedure WordGrayScanLine;
  211. Var
  212. P : PWord;
  213. I : Integer;
  214. begin
  215. P:=PWord(FScanLine);
  216. For I:=0 to FWidth-1 do
  217. begin
  218. L:=ScaleWord(P^);
  219. C.Red:=L;
  220. C.Green:=L;
  221. C.Blue:=L;
  222. Img.Colors[I,Row]:=C;
  223. Inc(P);
  224. end;
  225. end;
  226. Procedure WordRGBScanLine;
  227. Var
  228. P : PWord;
  229. I : Integer;
  230. begin
  231. P:=PWord(FScanLine);
  232. For I:=0 to FWidth-1 do
  233. begin
  234. C.Red:=ScaleWord(P^);
  235. Inc(P);
  236. C.Green:=ScaleWord(P^);
  237. Inc(P);
  238. C.Blue:=ScaleWord(P^);
  239. Img.Colors[I,Row]:=C;
  240. Inc(P);
  241. end;
  242. end;
  243. Procedure ByteGrayScanLine;
  244. Var
  245. P : PByte;
  246. I : Integer;
  247. begin
  248. P:=PByte(FScanLine);
  249. For I:=0 to FWidth-1 do
  250. begin
  251. L:=ScaleByte(P^);
  252. C.Red:=L;
  253. C.Green:=L;
  254. C.Blue:=L;
  255. Img.Colors[I,Row]:=C;
  256. Inc(P);
  257. end;
  258. end;
  259. Procedure ByteRGBScanLine;
  260. Var
  261. P : PByte;
  262. I : Integer;
  263. begin
  264. P:=PByte(FScanLine);
  265. For I:=0 to FWidth-1 do
  266. begin
  267. C.Red:=ScaleByte(P^);
  268. Inc(P);
  269. C.Green:=ScaleByte(P^);
  270. Inc(P);
  271. C.Blue:=ScaleByte(P^);
  272. Img.Colors[I,Row]:=C;
  273. Inc(P);
  274. end;
  275. end;
  276. begin
  277. C.Alpha:=AlphaOpaque;
  278. Scale := FMaxVal*(FMaxVal+1) + FMaxVal;
  279. Case FBitmapType of
  280. 1 : ;
  281. 2 : WordGrayScanline;
  282. 3 : WordRGBScanline;
  283. 4 : ByteBnWScanLine;
  284. 5 : If FBitPP=8 then
  285. ByteGrayScanLine
  286. else
  287. WordGrayScanLine;
  288. 6 : If FBitPP=24 then
  289. ByteRGBScanLine
  290. else
  291. WordRGBScanLine;
  292. end;
  293. end;
  294. initialization
  295. ImageHandlers.RegisterImageReader ('PNM Format', 'PNM;PGM;PBM', TFPReaderPNM);
  296. end.