fpwritepnm.pp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  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. {Support for writing PNM (Portable aNyMap) formats added :
  14. * PBM (P1,P4) : Portable BitMap format : 1 bit per pixel
  15. * PGM (P2,P5) : Portable GrayMap format : 8 bits per pixel
  16. * PPM (P5,P6) : Portable PixelMap foramt : 24 bits per pixel}
  17. {$mode objfpc}{$h+}
  18. unit FPWritePNM;
  19. interface
  20. uses FPImage, classes, sysutils;
  21. type
  22. TPNMColorDepth = (pcdAuto,pcdBlackWhite, pcdGrayscale, pcdRGB);
  23. { TFPWriterPNM }
  24. TFPWriterPNM = class(TFPCustomImageWriter)
  25. protected
  26. procedure InternalWrite(Stream:TStream;Img:TFPCustomImage);override;
  27. public
  28. ColorDepth: TPNMColorDepth;
  29. BinaryFormat: boolean;
  30. function GuessColorDepthOfImage(Img: TFPCustomImage): TPNMColorDepth;
  31. function GetColorDepthOfExtension(AExtension: string): TPNMColorDepth;
  32. function GetFileExtension(AColorDepth: TPNMColorDepth): string;
  33. constructor Create; override;
  34. end;
  35. { TFPWriterPBM }
  36. TFPWriterPBM = class(TFPWriterPNM)
  37. constructor Create; override;
  38. end;
  39. { TFPWriterPGM }
  40. TFPWriterPGM = class(TFPWriterPNM)
  41. constructor Create; override;
  42. end;
  43. { TFPWriterPPM }
  44. TFPWriterPPM = class(TFPWriterPNM)
  45. constructor Create; override;
  46. end;
  47. procedure SaveImageToPNMFile(Img: TFPCustomImage; filename: string; UseBinaryFormat: boolean = true);
  48. implementation
  49. procedure SaveImageToPNMFile(Img: TFPCustomImage; filename: string; UseBinaryFormat: boolean = true);
  50. var writer: TFPWriterPNM;
  51. curExt: string;
  52. begin
  53. writer := TFPWriterPNM.Create;
  54. writer.BinaryFormat := UseBinaryFormat;
  55. curExt := Lowercase(ExtractFileExt(filename));
  56. if (curExt='.pnm') or (curExt='') then
  57. begin
  58. writer.ColorDepth := writer.GuessColorDepthOfImage(Img);
  59. filename := ChangeFileExt(filename,'.'+writer.GetFileExtension(writer.ColorDepth));
  60. end else
  61. writer.ColorDepth := writer.GetColorDepthOfExtension(curExt);
  62. Img.SaveToFile(filename,writer);
  63. writer.Free;
  64. end;
  65. { TFPWriterPPM }
  66. constructor TFPWriterPPM.Create;
  67. begin
  68. inherited Create;
  69. ColorDepth := pcdRGB;
  70. end;
  71. { TFPWriterPGM }
  72. constructor TFPWriterPGM.Create;
  73. begin
  74. inherited Create;
  75. ColorDepth := pcdGrayscale;
  76. end;
  77. { TFPWriterPBM }
  78. constructor TFPWriterPBM.Create;
  79. begin
  80. inherited Create;
  81. ColorDepth:= pcdBlackWhite;
  82. end;
  83. { TFPWriterPNM }
  84. constructor TFPWriterPNM.Create;
  85. begin
  86. inherited Create;
  87. ColorDepth := pcdAuto;
  88. BinaryFormat := True;
  89. end;
  90. procedure TFPWriterPNM.InternalWrite(Stream:TStream;Img:TFPCustomImage);
  91. var useBitMapType: integer;
  92. function SaveHeader(stream:TStream):boolean;
  93. const
  94. MagicWords:Array[1..6]OF String[2]=('P1','P2','P3','P4','P5','P6');
  95. var
  96. PNMInfo:String;
  97. strWidth,StrHeight:String[15];
  98. begin
  99. SaveHeader:=false;
  100. with Img do
  101. begin
  102. Str(Img.Width,StrWidth);
  103. Str(Img.Height,StrHeight);
  104. end;
  105. PNMInfo:=Concat(MagicWords[useBitMapType],#10,StrWidth,#32,StrHeight,#10);
  106. if useBitMapType in [2,3,5,6]
  107. then
  108. PNMInfo:=Concat(PNMInfo,'255'#10);
  109. stream.seek(0,soFromBeginning);
  110. stream.Write(PNMInfo[1],Length(PNMInfo));
  111. SaveHeader := true;
  112. end;
  113. var
  114. Row,Coulumn,nBpLine,i:Integer;
  115. aColor:TFPColor;
  116. aLine:PByte;
  117. strCol:String[3];
  118. LinuxEndOfLine: char;
  119. UseColorDepth: TPNMColorDepth;
  120. begin
  121. LinuxEndOfLine := #10;
  122. //determine color depth
  123. if ColorDepth = pcdAuto then
  124. UseColorDepth := GuessColorDepthOfImage(Img) else
  125. UseColorDepth := ColorDepth;
  126. //determine file format number (1-6)
  127. case UseColorDepth of
  128. pcdBlackWhite: useBitMapType := 1;
  129. pcdGrayscale: useBitMapType := 2;
  130. pcdRGB: useBitMapType := 3;
  131. end;
  132. if BinaryFormat then inc(useBitMapType,3);
  133. SaveHeader(Stream);
  134. case useBitMapType of
  135. 1:nBpLine:=Img.Width*2;{p p p}
  136. 2:nBpLine:=Img.Width*4;{lll lll lll}
  137. 3:nBpLine:=Img.Width*3*4;{rrr ggg bbb rrr ggg bbb}
  138. 4:nBpLine:=(Img.Width+7) SHR 3;
  139. 5:nBpLine:=Img.Width;
  140. 6:nBpLine:=Img.Width*3;
  141. end;
  142. GetMem(aLine,nBpLine);//3 extra byte for BMP 4Bytes alignement.
  143. for Row:=0 to img.Height-1 do
  144. begin
  145. FillChar(aLine^,nBpLine,0);
  146. for Coulumn:=0 to img.Width-1 do
  147. begin
  148. aColor:=img.Colors[Coulumn,Row];
  149. with aColor do
  150. case useBitMapType of
  151. 1:begin
  152. if(Red<=$2F00)or(Green<=$2F00)or(Blue<=$2F00)
  153. then
  154. aLine[2*Coulumn]:=Ord('1')
  155. else
  156. aLine[2*Coulumn]:=Ord('0');
  157. aLine[2*Coulumn+1]:=32;
  158. end;
  159. 2:begin
  160. Str(Hi(Word(Round(Red*0.299+Green*0.587+Blue*0.114))),strCol);
  161. for i:=0 to Length(StrCol)-1 do
  162. aLine[4*Coulumn+i]:=Ord(StrCol[i+1]);
  163. for i:=Length(StrCol) to 4 do
  164. aLine[4*Coulumn+i]:=32;
  165. end;
  166. 3:begin
  167. Str(Hi(Red),strCol);
  168. for i:=0 to Length(StrCol)-1 do
  169. aLine[4*(3*Coulumn)+i]:=Ord(StrCol[i+1]);
  170. for i:=Length(StrCol) to 4 do
  171. aLine[4*(3*Coulumn)+i]:=32;
  172. Str(Hi(Green),strCol);
  173. for i:=0 to Length(StrCol)-1 do
  174. aLine[4*(3*Coulumn+1)+i]:=Ord(StrCol[i+1]);
  175. for i:=Length(StrCol) to 4 do
  176. aLine[4*(3*Coulumn+1)+i]:=32;
  177. Str(Hi(Blue),strCol);
  178. for i:=0 to Length(StrCol)-1 do
  179. aLine[4*(3*Coulumn+2)+i]:=Ord(StrCol[i+1]);
  180. for i:=Length(StrCol) to 4 do
  181. aLine[4*(3*Coulumn+2)+i]:=32;
  182. end;
  183. 4:if(Red<=$2F00)or(Green<=$2F00)or(Blue<=$2F00)
  184. then
  185. aLine[Coulumn shr 3]:=aLine[Coulumn shr 3] or ($80 shr (Coulumn and $07));
  186. 5:aLine[Coulumn]:=Hi(Word(Round(Red*0.299+Green*0.587+Blue*0.114)));
  187. 6:begin
  188. aLine[3*Coulumn]:=Hi(Red);
  189. aLine[3*Coulumn+1]:=Hi(Green);
  190. aLine[3*Coulumn+2]:=Hi(Blue);
  191. end;
  192. end;
  193. end;
  194. Stream.Write(aLine^,nBpLine);
  195. if useBitMapType in[1..3] then Stream.Write(LinuxEndOfLine,1);
  196. end;
  197. FreeMem(aLine,nBpLine);
  198. end;
  199. function TFPWriterPNM.GetColorDepthOfExtension(AExtension: string
  200. ): TPNMColorDepth;
  201. begin
  202. if (length(AExtension) > 0) and (AExtension[1]='.') then
  203. delete(AExtension,1,1);
  204. AExtension := LowerCase(AExtension);
  205. if AExtension='pbm' then result := pcdBlackWhite else
  206. if AExtension='pgm' then result := pcdGrayscale else
  207. if AExtension='ppm' then result := pcdRGB else
  208. result := pcdAuto;
  209. end;
  210. function TFPWriterPNM.GuessColorDepthOfImage(Img: TFPCustomImage): TPNMColorDepth;
  211. var Row, Col: integer;
  212. aColor: TFPColor;
  213. begin
  214. result := pcdBlackWhite;
  215. for Row:=0 to img.Height-1 do
  216. for Col:=0 to img.Width-1 do
  217. begin
  218. aColor:=img.Colors[Col,Row];
  219. if (AColor.red >= 256) and (AColor.green >= 256) and (AColor.blue >= 256) and
  220. (AColor.red < $FF00) and (AColor.green < $FF00) and (AColor.blue < $FF00) then
  221. begin
  222. if (AColor.red shr 8 <> AColor.Green shr 8) or
  223. (AColor.blue shr 8 <> AColor.Green shr 8) or
  224. (AColor.red shr 8 <> AColor.blue shr 8) then
  225. begin
  226. result := pcdRGB;
  227. exit;
  228. end else
  229. result := pcdGrayscale;
  230. end;
  231. end;
  232. end;
  233. function TFPWriterPNM.GetFileExtension(AColorDepth: TPNMColorDepth): string;
  234. begin
  235. case AColorDepth of
  236. pcdBlackWhite: result := 'pbm';
  237. pcdGrayscale: result := 'pgm';
  238. pcdRGB: result := 'ppm';
  239. else
  240. result := 'pnm';
  241. end;
  242. end;
  243. initialization
  244. ImageHandlers.RegisterImageWriter ('Netpbm Portable aNyMap', 'pnm', TFPWriterPNM);
  245. ImageHandlers.RegisterImageWriter ('Netpbm Portable BitMap', 'pbm', TFPWriterPBM);
  246. ImageHandlers.RegisterImageWriter ('Netpbm Portable GrayMap', 'pgm', TFPWriterPGM);
  247. ImageHandlers.RegisterImageWriter ('Netpbm Portable PixelMap', 'ppm', TFPWriterPPM);
  248. end.