jpeg.pas 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944
  1. {*******************************************************}
  2. { }
  3. { Delphi Runtime Library }
  4. { JPEG Image Compression/Decompression Unit }
  5. { }
  6. { Copyright (c) 1997 Borland International }
  7. { Copyright (c) 1998 Jacques Nomssi Nzali }
  8. { }
  9. {*******************************************************}
  10. unit jpeg;
  11. interface
  12. {$I jconfig.inc}
  13. {$ifndef Delphi_Stream}
  14. Define "Delphi_Stream" in jconfig.inc - deliberate syntax error.
  15. {$endif}
  16. uses Windows, SysUtils, Classes, Graphics;
  17. type
  18. TJPEGData = class(TSharedImage)
  19. private
  20. FData: TCustomMemoryStream;
  21. FHeight: Integer;
  22. FWidth: Integer;
  23. FGrayscale: Boolean;
  24. protected
  25. procedure FreeHandle; override;
  26. public
  27. destructor Destroy; override;
  28. end;
  29. TJPEGQualityRange = 1..100; { 100 = best quality, 25 = pretty awful }
  30. TJPEGPerformance = (jpBestQuality, jpBestSpeed);
  31. TJPEGScale = (jsFullSize, jsHalf, jsQuarter, jsEighth);
  32. TJPEGPixelFormat = (jf24Bit, jf8Bit);
  33. TJPEGImage = class(TGraphic)
  34. private
  35. FImage: TJPEGData;
  36. FBitmap: TBitmap;
  37. FScaledWidth: Integer;
  38. FScaledHeight: Integer;
  39. FTempPal: HPalette;
  40. FSmoothing: Boolean;
  41. FGrayScale: Boolean;
  42. FPixelFormat: TJPEGPixelFormat;
  43. FQuality: TJPEGQualityRange;
  44. FProgressiveDisplay: Boolean;
  45. FProgressiveEncoding: Boolean;
  46. FPerformance: TJPEGPerformance;
  47. FScale: TJPEGScale;
  48. FNeedRecalc: Boolean;
  49. procedure CalcOutputDimensions;
  50. function GetBitmap: TBitmap;
  51. function GetGrayscale: Boolean;
  52. procedure SetGrayscale(Value: Boolean);
  53. procedure SetPerformance(Value: TJPEGPerformance);
  54. procedure SetPixelFormat(Value: TJPEGPixelFormat);
  55. procedure SetScale(Value: TJPEGScale);
  56. procedure SetSmoothing(Value: Boolean);
  57. protected
  58. procedure AssignTo(Dest: TPersistent); override;
  59. procedure Changed(Sender: TObject); override;
  60. procedure Draw(ACanvas: TCanvas; const Rect: TRect); override;
  61. function Equals(Graphic: TGraphic): Boolean; override;
  62. procedure FreeBitmap;
  63. function GetEmpty: Boolean; override;
  64. function GetHeight: Integer; override;
  65. function GetPalette: HPALETTE; override;
  66. function GetWidth: Integer; override;
  67. procedure NewBitmap;
  68. procedure NewImage;
  69. procedure ReadData(Stream: TStream); override;
  70. procedure ReadStream(Size: Longint; Stream: TStream);
  71. procedure SetHeight(Value: Integer); override;
  72. procedure SetPalette(Value: HPalette); override;
  73. procedure SetWidth(Value: Integer); override;
  74. procedure WriteData(Stream: TStream); override;
  75. property Bitmap: TBitmap read GetBitmap; { volatile }
  76. public
  77. constructor Create; override;
  78. destructor Destroy; override;
  79. procedure Compress;
  80. procedure DIBNeeded;
  81. procedure JPEGNeeded;
  82. procedure Assign(Source: TPersistent); override;
  83. procedure LoadFromStream(Stream: TStream); override;
  84. procedure SaveToStream(Stream: TStream); override;
  85. procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle;
  86. APalette: HPALETTE); override;
  87. procedure SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
  88. var APalette: HPALETTE); override;
  89. { Options affecting / reflecting compression and decompression behavior }
  90. property Grayscale: Boolean read GetGrayscale write SetGrayscale;
  91. property ProgressiveEncoding: Boolean read FProgressiveEncoding write FProgressiveEncoding;
  92. { Compression options }
  93. property CompressionQuality: TJPEGQualityRange read FQuality write FQuality;
  94. { Decompression options }
  95. property PixelFormat: TJPEGPixelFormat read FPixelFormat write SetPixelFormat;
  96. property ProgressiveDisplay: Boolean read FProgressiveDisplay write FProgressiveDisplay;
  97. property Performance: TJPEGPerformance read FPerformance write SetPerformance;
  98. property Scale: TJPEGScale read FScale write SetScale;
  99. property Smoothing: Boolean read FSmoothing write SetSmoothing;
  100. end;
  101. TJPEGDefaults = record
  102. CompressionQuality: TJPEGQualityRange;
  103. Grayscale: Boolean;
  104. Performance: TJPEGPerformance;
  105. PixelFormat: TJPEGPixelFormat;
  106. ProgressiveDisplay: Boolean;
  107. ProgressiveEncoding: Boolean;
  108. Scale: TJPEGScale;
  109. Smoothing: Boolean;
  110. end;
  111. var { Default settings for all new TJPEGImage instances }
  112. JPEGDefaults: TJPEGDefaults = (
  113. CompressionQuality: 90;
  114. Grayscale: False;
  115. Performance: jpBestQuality;
  116. PixelFormat: jf24Bit; { initialized to match video mode }
  117. ProgressiveDisplay: False;
  118. ProgressiveEncoding: False;
  119. Scale: jsFullSize;
  120. Smoothing: True;
  121. );
  122. implementation
  123. uses jconsts,
  124. jmorecfg, jerror, jpeglib, jcomapi, jdmaster, jdapistd,
  125. jdatadst, jcparam, jcapimin, jcapistd, jdapimin, jdatasrc;
  126. { The following types and external function declarations are used to
  127. call into functions of the Independent JPEG Group's (IJG) implementation
  128. of the JPEG image compression/decompression public standard. The IJG
  129. library's C source code is compiled into OBJ files and linked into
  130. the Delphi application. Only types and functions needed by this unit
  131. are declared; all IJG internal structures are stubbed out with
  132. generic pointers to reduce internal source code congestion.
  133. IJG source code copyright (C) 1991-1996, Thomas G. Lane. }
  134. { Error handler }
  135. { Progress monitor object }
  136. type
  137. new_progress_mgr_ptr = ^new_progress_mgr;
  138. new_progress_mgr = record
  139. pub : jpeg_progress_mgr;
  140. { extra Delphi info }
  141. instance: TJPEGImage; { ptr to current TJPEGImage object }
  142. last_pass: Integer;
  143. last_pct: Integer;
  144. last_time: Integer;
  145. last_scanline: Integer;
  146. end;
  147. TJPEGContext = record
  148. err: jpeg_error_mgr;
  149. progress: new_progress_mgr;
  150. FinalDCT: J_DCT_METHOD;
  151. FinalTwoPassQuant: Boolean;
  152. FinalDitherMode: J_DITHER_MODE;
  153. case byte of
  154. 0: (common: jpeg_common_struct);
  155. 1: (d: jpeg_decompress_struct);
  156. 2: (c: jpeg_compress_struct);
  157. end;
  158. type
  159. EJPEG = class(EInvalidGraphic);
  160. procedure InvalidOperation(const Msg: string); near;
  161. begin
  162. raise EInvalidGraphicOperation.Create(Msg);
  163. end;
  164. procedure JpegError(cinfo: j_common_ptr);
  165. begin
  166. raise EJPEG.CreateFmt(sJPEGError,[cinfo^.err^.msg_code]);
  167. end;
  168. procedure EmitMessage(cinfo: j_common_ptr; msg_level: Integer); far;
  169. begin
  170. { -- !! }
  171. end;
  172. procedure OutputMessage(cinfo: j_common_ptr); far;
  173. begin
  174. { -- !! }
  175. end;
  176. procedure FormatMessage(cinfo: j_common_ptr; var buffer: string); far;
  177. begin
  178. { -- !! }
  179. end;
  180. procedure ResetErrorMgr(cinfo: j_common_ptr);
  181. begin
  182. cinfo^.err^.num_warnings := 0;
  183. cinfo^.err^.msg_code := 0;
  184. end;
  185. const
  186. jpeg_std_error: jpeg_error_mgr = (
  187. error_exit: JpegError;
  188. emit_message: EmitMessage;
  189. output_message: OutputMessage;
  190. format_message: FormatMessage;
  191. reset_error_mgr: ResetErrorMgr);
  192. { TJPEGData }
  193. destructor TJPEGData.Destroy;
  194. begin
  195. FData.Free;
  196. inherited Destroy;
  197. end;
  198. procedure TJPEGData.FreeHandle;
  199. begin
  200. end;
  201. { TJPEGImage }
  202. constructor TJPEGImage.Create;
  203. begin
  204. inherited Create;
  205. NewImage;
  206. FQuality := JPEGDefaults.CompressionQuality;
  207. FGrayscale := JPEGDefaults.Grayscale;
  208. FPerformance := JPEGDefaults.Performance;
  209. FPixelFormat := JPEGDefaults.PixelFormat;
  210. FProgressiveDisplay := JPEGDefaults.ProgressiveDisplay;
  211. FProgressiveEncoding := JPEGDefaults.ProgressiveEncoding;
  212. FScale := JPEGDefaults.Scale;
  213. FSmoothing := JPEGDefaults.Smoothing;
  214. end;
  215. destructor TJPEGImage.Destroy;
  216. begin
  217. if FTempPal <> 0 then DeleteObject(FTempPal);
  218. FBitmap.Free;
  219. FImage.Release;
  220. inherited Destroy;
  221. end;
  222. procedure TJPEGImage.Assign(Source: TPersistent);
  223. begin
  224. if Source is TJPEGImage then
  225. begin
  226. FImage.Release;
  227. FImage := TJPEGImage(Source).FImage;
  228. FImage.Reference;
  229. if TJPEGImage(Source).FBitmap <> nil then
  230. begin
  231. NewBitmap;
  232. FBitmap.Assign(TJPEGImage(Source).FBitmap);
  233. end;
  234. end
  235. else if Source is TBitmap then
  236. begin
  237. NewImage;
  238. NewBitmap;
  239. FBitmap.Assign(Source);
  240. end
  241. else
  242. inherited Assign(Source);
  243. end;
  244. procedure TJPEGImage.AssignTo(Dest: TPersistent);
  245. begin
  246. if Dest is TBitmap then
  247. Dest.Assign(Bitmap)
  248. else
  249. inherited AssignTo(Dest);
  250. end;
  251. procedure ProgressCallback(const cinfo: jpeg_common_struct);
  252. var
  253. Ticks: Integer;
  254. R: TRect;
  255. temp: Integer;
  256. progress : new_progress_mgr_ptr;
  257. begin
  258. progress := new_progress_mgr_ptr(cinfo.progress);
  259. if (progress = nil) or (progress.instance = nil) then Exit;
  260. with progress^,pub do
  261. begin
  262. Ticks := GetTickCount;
  263. if (Ticks - last_time) < 500 then Exit;
  264. temp := last_time;
  265. last_time := Ticks;
  266. if temp = 0 then Exit;
  267. if cinfo.is_decompressor then
  268. with j_decompress_ptr(@cinfo)^ do
  269. begin
  270. R := Rect(0, last_scanline, output_width, output_scanline);
  271. if R.Bottom < last_scanline then
  272. R.Bottom := output_height;
  273. end
  274. else
  275. R := Rect(0,0,0,0);
  276. temp := Trunc(100.0*(completed_passes + (pass_counter/pass_limit))/total_passes);
  277. if temp = last_pct then Exit;
  278. last_pct := temp;
  279. if cinfo.is_decompressor then
  280. last_scanline := j_decompress_ptr(@cinfo)^.output_scanline;
  281. instance.Progress(instance, psRunning, temp, (R.Bottom - R.Top) >= 4, R, '');
  282. end;
  283. end;
  284. procedure ReleaseContext(var jc: TJPEGContext);
  285. begin
  286. if jc.common.err = nil then Exit;
  287. jpeg_destroy(@jc.common);
  288. jc.common.err := nil;
  289. end;
  290. procedure InitDecompressor(Obj: TJPEGImage; var jc: TJPEGContext);
  291. begin
  292. FillChar(jc, sizeof(jc), 0);
  293. jc.err := jpeg_std_error;
  294. jc.common.err := @jc.err;
  295. jpeg_CreateDecompress(@jc.d, JPEG_LIB_VERSION, sizeof(jc.d));
  296. with Obj do
  297. try
  298. jc.progress.pub.progress_monitor := @ProgressCallback;
  299. jc.progress.instance := Obj;
  300. jc.common.progress := @jc.progress;
  301. Obj.FImage.FData.Position := 0;
  302. jpeg_stdio_src(@jc.d, @FImage.FData);
  303. jpeg_read_header(@jc.d, TRUE);
  304. jc.d.scale_num := 1;
  305. jc.d.scale_denom := 1 shl Byte(FScale);
  306. jc.d.do_block_smoothing := FSmoothing;
  307. if FGrayscale then jc.d.out_color_space := JCS_GRAYSCALE;
  308. if (PixelFormat = jf8Bit) or (jc.d.out_color_space = JCS_GRAYSCALE) then
  309. begin
  310. jc.d.quantize_colors := True;
  311. jc.d.desired_number_of_colors := 236;
  312. end;
  313. if FPerformance = jpBestSpeed then
  314. begin
  315. jc.d.dct_method := JDCT_IFAST;
  316. jc.d.two_pass_quantize := False;
  317. { jc.d.do_fancy_upsampling := False; !! AV inside jpeglib }
  318. jc.d.dither_mode := JDITHER_ORDERED;
  319. end;
  320. jc.FinalDCT := jc.d.dct_method;
  321. jc.FinalTwoPassQuant := jc.d.two_pass_quantize;
  322. jc.FinalDitherMode := jc.d.dither_mode;
  323. if FProgressiveDisplay and jpeg_has_multiple_scans(@jc.d) then
  324. begin { save requested settings, reset for fastest on all but last scan }
  325. jc.d.enable_2pass_quant := jc.d.two_pass_quantize;
  326. jc.d.dct_method := JDCT_IFAST;
  327. jc.d.two_pass_quantize := False;
  328. jc.d.dither_mode := JDITHER_ORDERED;
  329. jc.d.buffered_image := True;
  330. end;
  331. except
  332. ReleaseContext(jc);
  333. raise;
  334. end;
  335. end;
  336. procedure TJPEGImage.CalcOutputDimensions;
  337. var
  338. jc: TJPEGContext;
  339. begin
  340. if not FNeedRecalc then Exit;
  341. InitDecompressor(Self, jc);
  342. try
  343. jc.common.progress := nil;
  344. jpeg_calc_output_dimensions(@jc.d);
  345. { read output dimensions }
  346. FScaledWidth := jc.d.output_width;
  347. FScaledHeight := jc.d.output_height;
  348. FProgressiveEncoding := jpeg_has_multiple_scans(@jc.d);
  349. finally
  350. ReleaseContext(jc);
  351. end;
  352. end;
  353. procedure TJPEGImage.Changed(Sender: TObject);
  354. begin
  355. inherited Changed(Sender);
  356. end;
  357. procedure TJPEGImage.Compress;
  358. var
  359. LinesWritten, LinesPerCall: Integer;
  360. SrcScanLine: Pointer;
  361. PtrInc: Integer;
  362. jc: TJPEGContext;
  363. Src: TBitmap;
  364. begin
  365. FillChar(jc, sizeof(jc), 0);
  366. jc.err := jpeg_std_error;
  367. jc.common.err := @jc.err;
  368. jpeg_CreateCompress(@jc.c, JPEG_LIB_VERSION, sizeof(jc.c));
  369. try
  370. try
  371. jc.progress.pub.progress_monitor := @ProgressCallback;
  372. jc.progress.instance := Self;
  373. jc.common.progress := @jc.progress;
  374. if FImage.FData <> nil then NewImage;
  375. FImage.FData := TMemoryStream.Create;
  376. FImage.FData.Position := 0;
  377. jpeg_stdio_dest(@jc.c, @FImage.FData);
  378. if (FBitmap = nil) or (FBitmap.Width = 0) or (FBitmap.Height = 0) then Exit;
  379. jc.c.image_width := FBitmap.Width;
  380. FImage.FWidth := FBitmap.Width;
  381. jc.c.image_height := FBitmap.Height;
  382. FImage.FHeight := FBitmap.Height;
  383. jc.c.input_components := 3; { JPEG requires 24bit RGB input }
  384. jc.c.in_color_space := JCS_RGB;
  385. Src := TBitmap.Create;
  386. try
  387. Src.Assign(FBitmap);
  388. Src.PixelFormat := pf24bit;
  389. jpeg_set_defaults(@jc.c);
  390. jpeg_set_quality(@jc.c, FQuality, True);
  391. if FGrayscale then
  392. begin
  393. FImage.FGrayscale := True;
  394. jpeg_set_colorspace(@jc.c, JCS_GRAYSCALE);
  395. end;
  396. if ProgressiveEncoding then
  397. jpeg_simple_progression(@jc.c);
  398. SrcScanline := Src.ScanLine[0];
  399. PtrInc := Integer(Src.ScanLine[1]) - Integer(SrcScanline);
  400. { if no dword padding required and source bitmap is top-down }
  401. if (PtrInc > 0) and ((PtrInc and 3) = 0) then
  402. LinesPerCall := jc.c.image_height { do whole bitmap in one call }
  403. else
  404. LinesPerCall := 1; { otherwise spoonfeed one row at a time }
  405. Progress(Self, psStarting, 0, False, Rect(0,0,0,0), '');
  406. try
  407. jpeg_start_compress(@jc.c, True);
  408. while (jc.c.next_scanline < jc.c.image_height) do
  409. begin
  410. LinesWritten := jpeg_write_scanlines(@jc.c, @SrcScanline, LinesPerCall);
  411. Inc(Integer(SrcScanline), PtrInc * LinesWritten);
  412. end;
  413. jpeg_finish_compress(@jc.c);
  414. finally
  415. if ExceptObject = nil then
  416. PtrInc := 100
  417. else
  418. PtrInc := 0;
  419. Progress(Self, psEnding, PtrInc, False, Rect(0,0,0,0), '');
  420. end;
  421. finally
  422. Src.Free;
  423. end;
  424. except
  425. on EAbort do { OnProgress can raise EAbort to cancel image save }
  426. NewImage; { Throw away any partial jpg data }
  427. end;
  428. finally
  429. ReleaseContext(jc);
  430. end;
  431. end;
  432. procedure TJPEGImage.DIBNeeded;
  433. begin
  434. GetBitmap;
  435. end;
  436. procedure TJPEGImage.Draw(ACanvas: TCanvas; const Rect: TRect);
  437. begin
  438. ACanvas.StretchDraw(Rect, Bitmap);
  439. end;
  440. function TJPEGImage.Equals(Graphic: TGraphic): Boolean;
  441. begin
  442. Result := (Graphic is TJPEGImage) and
  443. (FImage = TJPEGImage(Graphic).FImage); { ---!! }
  444. end;
  445. procedure TJPEGImage.FreeBitmap;
  446. begin
  447. FBitmap.Free;
  448. FBitmap := nil;
  449. end;
  450. function BuildPalette(const cinfo: jpeg_decompress_struct): HPalette;
  451. var
  452. Pal: TMaxLogPalette;
  453. I: Integer;
  454. C: Byte;
  455. begin
  456. Pal.palVersion := $300;
  457. Pal.palNumEntries := cinfo.actual_number_of_colors;
  458. if cinfo.out_color_space = JCS_GRAYSCALE then
  459. for I := 0 to Pal.palNumEntries-1 do
  460. begin
  461. C := cinfo.colormap^[0]^[I];
  462. Pal.palPalEntry[I].peRed := C;
  463. Pal.palPalEntry[I].peGreen := C;
  464. Pal.palPalEntry[I].peBlue := C;
  465. Pal.palPalEntry[I].peFlags := 0;
  466. end
  467. else
  468. for I := 0 to Pal.palNumEntries-1 do
  469. begin
  470. Pal.palPalEntry[I].peRed := cinfo.colormap^[2]^[I];
  471. Pal.palPalEntry[I].peGreen := cinfo.colormap^[1]^[I];
  472. Pal.palPalEntry[I].peBlue := cinfo.colormap^[0]^[I];
  473. Pal.palPalEntry[I].peFlags := 0;
  474. end;
  475. Result := CreatePalette(PLogPalette(@Pal)^);
  476. end;
  477. procedure BuildColorMap(var cinfo: jpeg_decompress_struct; P: HPalette);
  478. var
  479. Pal: TMaxLogPalette;
  480. Count, I: Integer;
  481. begin
  482. Count := GetPaletteEntries(P, 0, 256, Pal.palPalEntry);
  483. if Count = 0 then Exit; { jpeg_destroy will free colormap }
  484. cinfo.colormap := cinfo.mem.alloc_sarray(j_common_ptr(@cinfo), JPOOL_IMAGE, Count, 3);
  485. cinfo.actual_number_of_colors := Count;
  486. for I := 0 to Count-1 do
  487. begin
  488. Byte(cinfo.colormap^[2]^[I]) := Pal.palPalEntry[I].peRed;
  489. Byte(cinfo.colormap^[1]^[I]) := Pal.palPalEntry[I].peGreen;
  490. Byte(cinfo.colormap^[0]^[I]) := Pal.palPalEntry[I].peBlue;
  491. end;
  492. end;
  493. function TJPEGImage.GetBitmap: TBitmap;
  494. var
  495. LinesPerCall, LinesRead: Integer;
  496. DestScanLine: Pointer;
  497. PtrInc: Integer;
  498. jc: TJPEGContext;
  499. GeneratePalette: Boolean;
  500. begin
  501. Result := FBitmap;
  502. if Result <> nil then Exit;
  503. if (FBitmap = nil) then FBitmap := TBitmap.Create;
  504. Result := FBitmap;
  505. GeneratePalette := True;
  506. InitDecompressor(Self, jc);
  507. try
  508. try
  509. { Set the bitmap pixel format }
  510. FBitmap.Handle := 0;
  511. if (PixelFormat = jf8Bit) or (jc.d.out_color_space = JCS_GRAYSCALE) then
  512. FBitmap.PixelFormat := pf8bit
  513. else
  514. FBitmap.PixelFormat := pf24bit;
  515. Progress(Self, psStarting, 0, False, Rect(0,0,0,0), '');
  516. try
  517. if (FTempPal <> 0) then
  518. begin
  519. if (FPixelFormat = jf8Bit) then
  520. begin { Generate DIB using assigned palette }
  521. BuildColorMap(jc.d, FTempPal);
  522. FBitmap.Palette := CopyPalette(FTempPal); { Keep FTempPal around }
  523. GeneratePalette := False;
  524. end
  525. else
  526. begin
  527. DeleteObject(FTempPal);
  528. FTempPal := 0;
  529. end;
  530. end;
  531. jpeg_start_decompress(@jc.d);
  532. { Set bitmap width and height }
  533. with FBitmap do
  534. begin
  535. Handle := 0;
  536. Width := jc.d.output_width;
  537. Height := jc.d.output_height;
  538. DestScanline := ScanLine[0];
  539. PtrInc := Integer(ScanLine[1]) - Integer(DestScanline);
  540. if (PtrInc > 0) and ((PtrInc and 3) = 0) then
  541. { if no dword padding is required and output bitmap is top-down }
  542. LinesPerCall := jc.d.rec_outbuf_height { read multiple rows per call }
  543. else
  544. LinesPerCall := 1; { otherwise read one row at a time }
  545. end;
  546. if jc.d.buffered_image then
  547. begin { decode progressive scans at low quality, high speed }
  548. while jpeg_consume_input(@jc.d) <> JPEG_REACHED_EOI do
  549. begin
  550. jpeg_start_output(@jc.d, jc.d.input_scan_number);
  551. { extract color palette }
  552. if (jc.common.progress^.completed_passes = 0) and (jc.d.colormap <> nil)
  553. and (FBitmap.PixelFormat = pf8bit) and GeneratePalette then
  554. begin
  555. FBitmap.Palette := BuildPalette(jc.d);
  556. PaletteModified := True;
  557. end;
  558. DestScanLine := FBitmap.ScanLine[0];
  559. while (jc.d.output_scanline < jc.d.output_height) do
  560. begin
  561. LinesRead := jpeg_read_scanlines(@jc.d, @DestScanline, LinesPerCall);
  562. Inc(Integer(DestScanline), PtrInc * LinesRead);
  563. end;
  564. jpeg_finish_output(@jc.d);
  565. end;
  566. { reset options for final pass at requested quality }
  567. jc.d.dct_method := jc.FinalDCT;
  568. jc.d.dither_mode := jc.FinalDitherMode;
  569. if jc.FinalTwoPassQuant then
  570. begin
  571. jc.d.two_pass_quantize := True;
  572. jc.d.colormap := nil;
  573. end;
  574. jpeg_start_output(@jc.d, jc.d.input_scan_number);
  575. DestScanLine := FBitmap.ScanLine[0];
  576. end;
  577. { build final color palette }
  578. if (not jc.d.buffered_image or jc.FinalTwoPassQuant) and
  579. (jc.d.colormap <> nil) and GeneratePalette then
  580. begin
  581. FBitmap.Palette := BuildPalette(jc.d);
  582. PaletteModified := True;
  583. DestScanLine := FBitmap.ScanLine[0];
  584. end;
  585. { final image pass for progressive, first and only pass for baseline }
  586. while (jc.d.output_scanline < jc.d.output_height) do
  587. begin
  588. LinesRead := jpeg_read_scanlines(@jc.d, @DestScanline, LinesPerCall);
  589. Inc(Integer(DestScanline), PtrInc * LinesRead);
  590. end;
  591. if jc.d.buffered_image then jpeg_finish_output(@jc.d);
  592. jpeg_finish_decompress(@jc.d);
  593. finally
  594. if ExceptObject = nil then
  595. PtrInc := 100
  596. else
  597. PtrInc := 0;
  598. Progress(Self, psEnding, PtrInc, PaletteModified, Rect(0,0,0,0), '');
  599. { Make sure new palette gets realized, in case OnProgress event didn't. }
  600. if PaletteModified then
  601. Changed(Self);
  602. end;
  603. except
  604. on EAbort do ; { OnProgress can raise EAbort to cancel image load }
  605. end;
  606. finally
  607. ReleaseContext(jc);
  608. end;
  609. end;
  610. function TJPEGImage.GetEmpty: Boolean;
  611. begin
  612. Result := (FImage.FData = nil) and FBitmap.Empty;
  613. end;
  614. function TJPEGImage.GetGrayscale: Boolean;
  615. begin
  616. Result := FGrayscale or FImage.FGrayscale;
  617. end;
  618. function TJPEGImage.GetPalette: HPalette;
  619. var
  620. DC: HDC;
  621. begin
  622. Result := 0;
  623. if FBitmap <> nil then
  624. Result := FBitmap.Palette
  625. else if FTempPal <> 0 then
  626. Result := FTempPal
  627. else if FPixelFormat = jf24Bit then { check for 8 bit screen }
  628. begin
  629. DC := GetDC(0);
  630. if (GetDeviceCaps(DC, BITSPIXEL) * GetDeviceCaps(DC, PLANES)) <= 8 then
  631. begin
  632. if FTempPal <> 0 then DeleteObject(FTempPal); { Memory leak -- fix }
  633. FTempPal := CreateHalftonePalette(DC);
  634. Result := FTempPal;
  635. end;
  636. ReleaseDC(0, DC);
  637. end;
  638. end;
  639. function TJPEGImage.GetHeight: Integer;
  640. begin
  641. if FBitmap <> nil then
  642. Result := FBitmap.Height
  643. else if FScale = jsFullSize then
  644. Result := FImage.FHeight
  645. else
  646. begin
  647. CalcOutputDimensions;
  648. Result := FScaledHeight;
  649. end;
  650. end;
  651. function TJPEGImage.GetWidth: Integer;
  652. begin
  653. if FBitmap <> nil then
  654. Result := FBitmap.Width
  655. else if FScale = jsFullSize then
  656. Result := FImage.FWidth
  657. else
  658. begin
  659. CalcOutputDimensions;
  660. Result := FScaledWidth;
  661. end;
  662. end;
  663. procedure TJPEGImage.JPEGNeeded;
  664. begin
  665. if FImage.FData = nil then
  666. Compress;
  667. end;
  668. procedure TJPEGImage.LoadFromClipboardFormat(AFormat: Word; AData: THandle;
  669. APalette: HPALETTE);
  670. begin
  671. { --!! check for jpeg clipboard data, mime type image/jpeg }
  672. FBitmap.LoadFromClipboardFormat(AFormat, AData, APalette);
  673. end;
  674. procedure TJPEGImage.LoadFromStream(Stream: TStream);
  675. begin
  676. ReadStream(Stream.Size - Stream.Position, Stream);
  677. end;
  678. procedure TJPEGImage.NewBitmap;
  679. begin
  680. FBitmap.Free;
  681. FBitmap := TBitmap.Create;
  682. end;
  683. procedure TJPEGImage.NewImage;
  684. begin
  685. if FImage <> nil then FImage.Release;
  686. FImage := TJPEGData.Create;
  687. FImage.Reference;
  688. end;
  689. procedure TJPEGImage.ReadData(Stream: TStream);
  690. var
  691. Size: Longint;
  692. begin
  693. Stream.Read(Size, SizeOf(Size));
  694. ReadStream(Size, Stream);
  695. end;
  696. procedure TJPEGImage.ReadStream(Size: Longint; Stream: TStream);
  697. var
  698. jerr: jpeg_error_mgr;
  699. cinfo: jpeg_decompress_struct;
  700. begin
  701. NewImage;
  702. with FImage do
  703. begin
  704. FData := TMemoryStream.Create;
  705. FData.Size := Size;
  706. Stream.ReadBuffer(FData.Memory^, Size);
  707. if Size > 0 then
  708. begin
  709. jerr := jpeg_std_error; { use local var for thread isolation }
  710. cinfo.err := @jerr;
  711. jpeg_CreateDecompress(@cinfo, JPEG_LIB_VERSION, sizeof(cinfo));
  712. try
  713. FData.Position := 0;
  714. jpeg_stdio_src(@cinfo, @FData);
  715. jpeg_read_header(@cinfo, TRUE);
  716. FWidth := cinfo.image_width;
  717. FHeight := cinfo.image_height;
  718. FGrayscale := cinfo.jpeg_color_space = JCS_GRAYSCALE;
  719. FProgressiveEncoding := jpeg_has_multiple_scans(@cinfo);
  720. finally
  721. jpeg_destroy_decompress(@cinfo);
  722. end;
  723. end;
  724. end;
  725. PaletteModified := True;
  726. Changed(Self);
  727. end;
  728. procedure TJPEGImage.SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
  729. var APalette: HPALETTE);
  730. begin
  731. { --!! check for jpeg clipboard format, mime type image/jpeg }
  732. Bitmap.SaveToClipboardFormat(AFormat, AData, APalette);
  733. end;
  734. procedure TJPEGImage.SaveToStream(Stream: TStream);
  735. begin
  736. JPEGNeeded;
  737. with FImage.FData do
  738. Stream.Write(Memory^, Size);
  739. end;
  740. procedure TJPEGImage.SetGrayscale(Value: Boolean);
  741. begin
  742. if FGrayscale <> Value then
  743. begin
  744. FreeBitmap;
  745. FGrayscale := Value;
  746. PaletteModified := True;
  747. Changed(Self);
  748. end;
  749. end;
  750. procedure TJPEGImage.SetHeight(Value: Integer);
  751. begin
  752. InvalidOperation(SChangeJPGSize);
  753. end;
  754. procedure TJPEGImage.SetPalette(Value: HPalette);
  755. var
  756. SignalChange: Boolean;
  757. begin
  758. if Value <> FTempPal then
  759. begin
  760. SignalChange := (FBitmap <> nil) and (Value <> FBitmap.Palette);
  761. if SignalChange then FreeBitmap;
  762. FTempPal := Value;
  763. if SignalChange then
  764. begin
  765. PaletteModified := True;
  766. Changed(Self);
  767. end;
  768. end;
  769. end;
  770. procedure TJPEGImage.SetPerformance(Value: TJPEGPerformance);
  771. begin
  772. if FPerformance <> Value then
  773. begin
  774. FreeBitmap;
  775. FPerformance := Value;
  776. PaletteModified := True;
  777. Changed(Self);
  778. end;
  779. end;
  780. procedure TJPEGImage.SetPixelFormat(Value: TJPEGPixelFormat);
  781. begin
  782. if FPixelFormat <> Value then
  783. begin
  784. FreeBitmap;
  785. FPixelFormat := Value;
  786. PaletteModified := True;
  787. Changed(Self);
  788. end;
  789. end;
  790. procedure TJPEGImage.SetScale(Value: TJPEGScale);
  791. begin
  792. if FScale <> Value then
  793. begin
  794. FreeBitmap;
  795. FScale := Value;
  796. FNeedRecalc := True;
  797. Changed(Self);
  798. end;
  799. end;
  800. procedure TJPEGImage.SetSmoothing(Value: Boolean);
  801. begin
  802. if FSmoothing <> Value then
  803. begin
  804. FreeBitmap;
  805. FSmoothing := Value;
  806. Changed(Self);
  807. end;
  808. end;
  809. procedure TJPEGImage.SetWidth(Value: Integer);
  810. begin
  811. InvalidOperation(SChangeJPGSize);
  812. end;
  813. procedure TJPEGImage.WriteData(Stream: TStream);
  814. var
  815. Size: Longint;
  816. begin
  817. Size := 0;
  818. if Assigned(FImage.FData) then Size := FImage.FData.Size;
  819. Stream.Write(Size, Sizeof(Size));
  820. if Size > 0 then Stream.Write(FImage.FData.Memory^, Size);
  821. end;
  822. procedure InitDefaults;
  823. var
  824. DC: HDC;
  825. begin
  826. DC := GetDC(0);
  827. if (GetDeviceCaps(DC, BITSPIXEL) * GetDeviceCaps(DC, PLANES)) <= 8 then
  828. JPEGDefaults.PixelFormat := jf8Bit
  829. else
  830. JPEGDefaults.PixelFormat := jf24Bit;
  831. ReleaseDC(0, DC);
  832. end;
  833. initialization
  834. InitDefaults;
  835. TPicture.RegisterFileFormat('jpg', 'JPEG Image File', TJPEGImage);
  836. TPicture.RegisterFileFormat('jpeg', 'JPEG Image File', TJPEGImage);
  837. finalization
  838. TPicture.UnregisterGraphicClass(TJPEGImage);
  839. end.