fpreadjpeg.pas 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. { Copyright (C) 2003 Mattias Gaertner
  2. This library is free software; you can redistribute it and/or modify it
  3. under the terms of the GNU Library General Public License as published by
  4. the Free Software Foundation; either version 2 of the License, or (at your
  5. option) any later version.
  6. This program is distributed in the hope that it will be useful, but WITHOUT
  7. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  8. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  9. for more details.
  10. You should have received a copy of the GNU Library General Public License
  11. along with this library; if not, write to the Free Software Foundation,
  12. Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  13. ToDo:
  14. - grayscale
  15. - palette
  16. }
  17. unit FPReadJPEG;
  18. {$mode objfpc}{$H+}
  19. interface
  20. uses
  21. Classes, SysUtils, FPImage, JPEGLib, JdAPImin, JDataSrc, JdAPIstd, JmoreCfg;
  22. type
  23. { TFPReaderJPEG }
  24. { This is a FPImage reader for jpeg images. }
  25. TFPReaderJPEG = class;
  26. PFPJPEGProgressManager = ^TFPJPEGProgressManager;
  27. TFPJPEGProgressManager = record
  28. pub : jpeg_progress_mgr;
  29. instance: TObject;
  30. last_pass: Integer;
  31. last_pct: Integer;
  32. last_time: Integer;
  33. last_scanline: Integer;
  34. end;
  35. TJPEGScale = (jsFullSize, jsHalf, jsQuarter, jsEighth);
  36. TJPEGReadPerformance = (jpBestQuality, jpBestSpeed);
  37. TFPReaderJPEG = class(TFPCustomImageReader)
  38. private
  39. FSmoothing: boolean;
  40. FWidth: Integer;
  41. FHeight: Integer;
  42. FGrayscale: boolean;
  43. FProgressiveEncoding: boolean;
  44. FError: jpeg_error_mgr;
  45. FProgressMgr: TFPJPEGProgressManager;
  46. FInfo: jpeg_decompress_struct;
  47. FScale: TJPEGScale;
  48. FPerformance: TJPEGReadPerformance;
  49. procedure SetPerformance(const AValue: TJPEGReadPerformance);
  50. procedure SetSmoothing(const AValue: boolean);
  51. protected
  52. procedure InternalRead(Str: TStream; Img: TFPCustomImage); override;
  53. function InternalCheck(Str: TStream): boolean; override;
  54. public
  55. constructor Create; override;
  56. destructor Destroy; override;
  57. property GrayScale: boolean read FGrayscale;
  58. property ProgressiveEncoding: boolean read FProgressiveEncoding;
  59. property Smoothing: boolean read FSmoothing write SetSmoothing;
  60. property Performance: TJPEGReadPerformance read FPerformance write SetPerformance;
  61. end;
  62. implementation
  63. procedure ReadCompleteStreamToStream(SrcStream, DestStream: TStream;
  64. StartSize: integer);
  65. var
  66. NewLength: Integer;
  67. ReadLen: Integer;
  68. Buffer: string;
  69. begin
  70. if (SrcStream is TMemoryStream) or (SrcStream is TFileStream)
  71. or (SrcStream is TStringStream)
  72. then begin
  73. // read as one block
  74. DestStream.CopyFrom(SrcStream,SrcStream.Size-SrcStream.Position);
  75. end else begin
  76. // read exponential
  77. if StartSize<=0 then StartSize:=1024;
  78. SetLength(Buffer,StartSize);
  79. NewLength:=0;
  80. repeat
  81. ReadLen:=SrcStream.Read(Buffer[NewLength+1],length(Buffer)-NewLength);
  82. inc(NewLength,ReadLen);
  83. if NewLength<length(Buffer) then break;
  84. SetLength(Buffer,length(Buffer)*2);
  85. until false;
  86. if NewLength>0 then
  87. DestStream.Write(Buffer[1],NewLength);
  88. end;
  89. end;
  90. procedure JPEGError(CurInfo: j_common_ptr);
  91. begin
  92. if CurInfo=nil then exit;
  93. writeln('JPEGError ',CurInfo^.err^.msg_code,' ');
  94. raise Exception.CreateFmt('JPEG error',[CurInfo^.err^.msg_code]);
  95. end;
  96. procedure EmitMessage(CurInfo: j_common_ptr; msg_level: Integer);
  97. begin
  98. if CurInfo=nil then exit;
  99. if msg_level=0 then ;
  100. end;
  101. procedure OutputMessage(CurInfo: j_common_ptr);
  102. begin
  103. if CurInfo=nil then exit;
  104. end;
  105. procedure FormatMessage(CurInfo: j_common_ptr; var buffer: string);
  106. begin
  107. if CurInfo=nil then exit;
  108. writeln('FormatMessage ',buffer);
  109. end;
  110. procedure ResetErrorMgr(CurInfo: j_common_ptr);
  111. begin
  112. if CurInfo=nil then exit;
  113. CurInfo^.err^.num_warnings := 0;
  114. CurInfo^.err^.msg_code := 0;
  115. end;
  116. var
  117. jpeg_std_error: jpeg_error_mgr;
  118. procedure ProgressCallback(CurInfo: j_common_ptr);
  119. begin
  120. if CurInfo=nil then exit;
  121. // ToDo
  122. end;
  123. { TFPReaderJPEG }
  124. procedure TFPReaderJPEG.SetSmoothing(const AValue: boolean);
  125. begin
  126. if FSmoothing=AValue then exit;
  127. FSmoothing:=AValue;
  128. end;
  129. procedure TFPReaderJPEG.SetPerformance(const AValue: TJPEGReadPerformance);
  130. begin
  131. if FPerformance=AValue then exit;
  132. FPerformance:=AValue;
  133. end;
  134. procedure TFPReaderJPEG.InternalRead(Str: TStream; Img: TFPCustomImage);
  135. var
  136. MemStream: TMemoryStream;
  137. procedure SetSource;
  138. begin
  139. MemStream.Position:=0;
  140. jpeg_stdio_src(@FInfo, @MemStream);
  141. end;
  142. procedure ReadHeader;
  143. begin
  144. jpeg_read_header(@FInfo, TRUE);
  145. FWidth := FInfo.image_width;
  146. FHeight := FInfo.image_height;
  147. FGrayscale := FInfo.jpeg_color_space = JCS_GRAYSCALE;
  148. FProgressiveEncoding := jpeg_has_multiple_scans(@FInfo);
  149. end;
  150. procedure InitReadingPixels;
  151. begin
  152. FInfo.scale_num := 1;
  153. FInfo.scale_denom := 1;// shl Byte(FScale);
  154. FInfo.do_block_smoothing := FSmoothing;
  155. if FGrayscale then FInfo.out_color_space := JCS_GRAYSCALE;
  156. if (FInfo.out_color_space = JCS_GRAYSCALE) then begin
  157. FInfo.quantize_colors := True;
  158. FInfo.desired_number_of_colors := 236;
  159. end;
  160. if FPerformance = jpBestSpeed then begin
  161. FInfo.dct_method := JDCT_IFAST;
  162. FInfo.two_pass_quantize := False;
  163. FInfo.dither_mode := JDITHER_ORDERED;
  164. // FInfo.do_fancy_upsampling := False; can create an AV inside jpeglib
  165. end;
  166. if FProgressiveEncoding then begin
  167. FInfo.enable_2pass_quant := FInfo.two_pass_quantize;
  168. FInfo.buffered_image := True;
  169. end;
  170. end;
  171. procedure ReadPixels;
  172. var
  173. Continue: Boolean;
  174. SampArray: JSAMPARRAY;
  175. SampRow: JSAMPROW;
  176. Color: TFPColor;
  177. LinesRead: Cardinal;
  178. x: Integer;
  179. y: Integer;
  180. begin
  181. InitReadingPixels;
  182. Continue:=true;
  183. Progress(psStarting, 0, False, Rect(0,0,0,0), '', Continue);
  184. if not Continue then exit;
  185. jpeg_start_decompress(@FInfo);
  186. Img.SetSize(FInfo.output_width,FInfo.output_height);
  187. // read one line per call
  188. GetMem(SampArray,SizeOf(JSAMPROW));
  189. GetMem(SampRow,FInfo.output_width*FInfo.output_components);
  190. SampArray^[0]:=SampRow;
  191. try
  192. Color.Alpha:=alphaOpaque;
  193. y:=0;
  194. while (FInfo.output_scanline < FInfo.output_height) do begin
  195. LinesRead := jpeg_read_scanlines(@FInfo, SampArray, 1);
  196. if LinesRead<1 then break;
  197. for x:=0 to FInfo.output_width-1 do begin
  198. Color.Red:=SampRow^[x*3+0] shl 8;
  199. Color.Green:=SampRow^[x*3+1] shl 8;
  200. Color.Blue:=SampRow^[x*3+2] shl 8;
  201. Img.Colors[x,y]:=Color;
  202. end;
  203. inc(y);
  204. end;
  205. finally
  206. FreeMem(SampRow);
  207. FreeMem(SampArray);
  208. end;
  209. if FInfo.buffered_image then jpeg_finish_output(@FInfo);
  210. jpeg_finish_decompress(@FInfo);
  211. Progress(psEnding, 100, false, Rect(0,0,0,0), '', Continue);
  212. end;
  213. begin
  214. FWidth:=0;
  215. FHeight:=0;
  216. MemStream:=nil;
  217. FillChar(FInfo,SizeOf(FInfo),0);
  218. try
  219. if Str is TMemoryStream then
  220. MemStream:=TMemoryStream(Str)
  221. else begin
  222. MemStream:=TMemoryStream.Create;
  223. ReadCompleteStreamToStream(Str,MemStream,1024);
  224. MemStream.Position:=0;
  225. end;
  226. if MemStream.Size > 0 then begin
  227. FError:=jpeg_std_error;
  228. FInfo.err := @FError;
  229. jpeg_CreateDecompress(@FInfo, JPEG_LIB_VERSION, SizeOf(FInfo));
  230. try
  231. FProgressMgr.pub.progress_monitor := @ProgressCallback;
  232. FProgressMgr.instance := Self;
  233. FInfo.progress := @FProgressMgr.pub;
  234. SetSource;
  235. ReadHeader;
  236. ReadPixels;
  237. finally
  238. jpeg_Destroy_Decompress(@FInfo);
  239. end;
  240. end;
  241. finally
  242. if (MemStream<>nil) and (MemStream<>Str) then
  243. MemStream.Free;
  244. end;
  245. end;
  246. function TFPReaderJPEG.InternalCheck(Str: TStream): boolean;
  247. begin
  248. // ToDo: read header and check
  249. Result:=false;
  250. if Str=nil then exit;
  251. Result:=true;
  252. end;
  253. constructor TFPReaderJPEG.Create;
  254. begin
  255. FScale:=jsFullSize;
  256. FPerformance:=jpBestSpeed;
  257. inherited Create;
  258. end;
  259. destructor TFPReaderJPEG.Destroy;
  260. begin
  261. inherited Destroy;
  262. end;
  263. initialization
  264. with jpeg_std_error do begin
  265. error_exit:=@JPEGError;
  266. emit_message:=@EmitMessage;
  267. output_message:=@OutputMessage;
  268. format_message:=@FormatMessage;
  269. reset_error_mgr:=@ResetErrorMgr;
  270. end;
  271. ImageHandlers.RegisterImageReader ('JPEG Graphics', 'jpg;jpeg', TFPReaderJPEG);
  272. end.