fpreadpnm.pp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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 normally 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. const
  42. WhiteSpaces=[#9,#10,#13,#32];
  43. {Whitespace (TABs, CRs, LFs, blanks) are separators in the PNM Headers}
  44. { The magic number at the beginning of a pnm file is 'P1', 'P2', ..., 'P7'
  45. followed by a WhiteSpace character }
  46. function TFPReaderPNM.InternalCheck(Stream:TStream):boolean;
  47. var
  48. hdr: array[0..2] of char;
  49. oldPos: Int64;
  50. n: Integer;
  51. begin
  52. Result:=False;
  53. if Stream = nil then
  54. exit;
  55. oldPos := Stream.Position;
  56. try
  57. n := SizeOf(hdr);
  58. Result:=(Stream.Read(hdr[0], n) = n)
  59. and (hdr[0] = 'P')
  60. and (hdr[1] in ['1'..'7'])
  61. and (hdr[2] in WhiteSpaces);
  62. finally
  63. Stream.Position := oldPos;
  64. end;
  65. end;
  66. function DropWhiteSpaces(Stream : TStream) :Char;
  67. begin
  68. with Stream do
  69. begin
  70. repeat
  71. ReadBuffer(DropWhiteSpaces,1);
  72. {If we encounter comment then eate line}
  73. if DropWhiteSpaces='#' then
  74. repeat
  75. ReadBuffer(DropWhiteSpaces,1);
  76. until DropWhiteSpaces=#10;
  77. until not(DropWhiteSpaces in WhiteSpaces);
  78. end;
  79. end;
  80. function ReadInteger(Stream : TStream) :Integer;
  81. var
  82. s:String[7];
  83. begin
  84. s:='';
  85. s[1]:=DropWhiteSpaces(Stream);
  86. with Stream do
  87. repeat
  88. Inc(s[0]);
  89. ReadBuffer(s[Length(s)+1],1)
  90. until (s[0]=#7) or (s[Length(s)+1] in WhiteSpaces);
  91. Result:=StrToInt(s);
  92. end;
  93. procedure TFPReaderPNM.ReadHeader(Stream : TStream);
  94. Var
  95. C : Char;
  96. begin
  97. Stream.ReadBuffer(C,1);
  98. If (C<>'P') then
  99. Raise Exception.Create('Not a valid PNM image.');
  100. Stream.ReadBuffer(C,1);
  101. FBitmapType:=Ord(C)-Ord('0');
  102. If Not (FBitmapType in [1..6]) then
  103. Raise Exception.CreateFmt('Unknown PNM subtype : %s',[C]);
  104. FWidth:=ReadInteger(Stream);
  105. FHeight:=ReadInteger(Stream);
  106. if FBitMapType in [1,4]
  107. then
  108. FMaxVal:=1
  109. else
  110. FMaxVal:=ReadInteger(Stream);
  111. If (FWidth<=0) or (FHeight<=0) or (FMaxVal<=0) then
  112. Raise Exception.Create('Invalid PNM header data');
  113. case FBitMapType of
  114. 1: FBitPP := 1; // 1bit PP (text)
  115. 2: FBitPP := 8 * SizeOf(Word); // Grayscale (text)
  116. 3: FBitPP := 8 * SizeOf(Word)*3; // RGB (text)
  117. 4: FBitPP := 1; // 1bit PP (raw)
  118. 5: If (FMaxval>255) then // Grayscale (raw);
  119. FBitPP:= 8 * 2
  120. else
  121. FBitPP:= 8;
  122. 6: if (FMaxVal>255) then // RGB (raw)
  123. FBitPP:= 8 * 6
  124. else
  125. FBitPP:= 8 * 3
  126. end;
  127. // Writeln(FWidth,'x',Fheight,' Maxval: ',FMaxVal,' BitPP: ',FBitPP);
  128. end;
  129. procedure TFPReaderPNM.InternalRead(Stream:TStream;Img:TFPCustomImage);
  130. var
  131. Row:Integer;
  132. begin
  133. ReadHeader(Stream);
  134. Img.SetSize(FWidth,FHeight);
  135. Case FBitmapType of
  136. 5,6 : FScanLineSize:=(FBitPP div 8) * FWidth;
  137. else
  138. FScanLineSize:=FBitPP*((FWidth+7)shr 3);
  139. end;
  140. GetMem(FScanLine,FScanLineSize);
  141. try
  142. for Row:=0 to img.Height-1 do
  143. begin
  144. ReadScanLine(Row,Stream);
  145. WriteScanLine(Row,Img);
  146. end;
  147. finally
  148. FreeMem(FScanLine);
  149. end;
  150. end;
  151. procedure TFPReaderPNM.ReadScanLine(Row : Integer; Stream:TStream);
  152. Var
  153. P : PWord;
  154. I,j,bitsLeft : Integer;
  155. PB: PByte;
  156. begin
  157. Case FBitmapType of
  158. 1 : begin
  159. PB:=FScanLine;
  160. For I:=0 to ((FWidth+7)shr 3)-1 do
  161. begin
  162. PB^:=0;
  163. bitsLeft := FWidth-(I shl 3)-1;
  164. if bitsLeft > 7 then bitsLeft := 7;
  165. for j:=0 to bitsLeft do
  166. PB^:=PB^ or (ReadInteger(Stream) shl (7-j));
  167. Inc(PB);
  168. end;
  169. end;
  170. 2 : begin
  171. P:=PWord(FScanLine);
  172. For I:=0 to FWidth-1 do
  173. begin
  174. P^:=ReadInteger(Stream);
  175. Inc(P);
  176. end;
  177. end;
  178. 3 : begin
  179. P:=PWord(FScanLine);
  180. For I:=0 to FWidth-1 do
  181. begin
  182. P^:=ReadInteger(Stream); // Red
  183. Inc(P);
  184. P^:=ReadInteger(Stream); // Green
  185. Inc(P);
  186. P^:=ReadInteger(Stream); // Blue;
  187. Inc(P)
  188. end;
  189. end;
  190. 4,5,6 : Stream.ReadBuffer(FScanLine^,FScanLineSize);
  191. end;
  192. end;
  193. procedure TFPReaderPNM.WriteScanLine(Row : Integer; Img : TFPCustomImage);
  194. Var
  195. C : TFPColor;
  196. L : Cardinal;
  197. Scale: Cardinal;
  198. function ScaleByte(B: Byte):Word;
  199. begin
  200. if FMaxVal = 255 then
  201. Result := (B shl 8) or B { As used for reading .BMP files }
  202. else { Mimic the above with multiplications }
  203. Result := (B*(FMaxVal+1) + B) * 65535 div Scale;
  204. end;
  205. function ScaleWord(W: Word):Word;
  206. begin
  207. if FMaxVal = 65535 then
  208. Result := W
  209. else { Mimic the above with multiplications }
  210. Result := Int64(W*(FMaxVal+1) + W) * 65535 div Scale;
  211. end;
  212. Procedure ByteBnWScanLine;
  213. Var
  214. P : PByte;
  215. I,j,x,bitsLeft : Integer;
  216. begin
  217. P:=PByte(FScanLine);
  218. For I:=0 to ((FWidth+7)shr 3)-1 do
  219. begin
  220. L:=P^;
  221. x := I shl 3;
  222. bitsLeft := FWidth-x-1;
  223. if bitsLeft > 7 then bitsLeft := 7;
  224. for j:=0 to bitsLeft do
  225. begin
  226. if L and $80 <> 0 then
  227. Img.Colors[x,Row]:=colBlack
  228. else
  229. Img.Colors[x,Row]:=colWhite;
  230. L:=L shl 1;
  231. inc(x);
  232. end;
  233. Inc(P);
  234. end;
  235. end;
  236. Procedure WordGrayScanLine;
  237. Var
  238. P : PWord;
  239. I : Integer;
  240. begin
  241. P:=PWord(FScanLine);
  242. For I:=0 to FWidth-1 do
  243. begin
  244. L:=ScaleWord(P^);
  245. C.Red:=L;
  246. C.Green:=L;
  247. C.Blue:=L;
  248. Img.Colors[I,Row]:=C;
  249. Inc(P);
  250. end;
  251. end;
  252. Procedure WordRGBScanLine;
  253. Var
  254. P : PWord;
  255. I : Integer;
  256. begin
  257. P:=PWord(FScanLine);
  258. For I:=0 to FWidth-1 do
  259. begin
  260. C.Red:=ScaleWord(P^);
  261. Inc(P);
  262. C.Green:=ScaleWord(P^);
  263. Inc(P);
  264. C.Blue:=ScaleWord(P^);
  265. Img.Colors[I,Row]:=C;
  266. Inc(P);
  267. end;
  268. end;
  269. Procedure ByteGrayScanLine;
  270. Var
  271. P : PByte;
  272. I : Integer;
  273. begin
  274. P:=PByte(FScanLine);
  275. For I:=0 to FWidth-1 do
  276. begin
  277. L:=ScaleByte(P^);
  278. C.Red:=L;
  279. C.Green:=L;
  280. C.Blue:=L;
  281. Img.Colors[I,Row]:=C;
  282. Inc(P);
  283. end;
  284. end;
  285. Procedure ByteRGBScanLine;
  286. Var
  287. P : PByte;
  288. I : Integer;
  289. begin
  290. P:=PByte(FScanLine);
  291. For I:=0 to FWidth-1 do
  292. begin
  293. C.Red:=ScaleByte(P^);
  294. Inc(P);
  295. C.Green:=ScaleByte(P^);
  296. Inc(P);
  297. C.Blue:=ScaleByte(P^);
  298. Img.Colors[I,Row]:=C;
  299. Inc(P);
  300. end;
  301. end;
  302. begin
  303. C.Alpha:=AlphaOpaque;
  304. Scale := FMaxVal*(FMaxVal+1) + FMaxVal;
  305. Case FBitmapType of
  306. 1 : ByteBnWScanLine;
  307. 2 : WordGrayScanline;
  308. 3 : WordRGBScanline;
  309. 4 : ByteBnWScanLine;
  310. 5 : If FBitPP=8 then
  311. ByteGrayScanLine
  312. else
  313. WordGrayScanLine;
  314. 6 : If FBitPP=24 then
  315. ByteRGBScanLine
  316. else
  317. WordRGBScanLine;
  318. end;
  319. end;
  320. initialization
  321. ImageHandlers.RegisterImageReader ('Netpbm format', 'PNM;PGM;PBM;PPM', TFPReaderPNM);
  322. end.