2
0

Main.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. {
  2. Vampyre Imaging Library Demo
  3. Image Browser (class api, canvas, VCL/LCL interaction)
  4. This simple viewer application shows usage of high level class interface
  5. to Imaging library and also drawing images onto standard VCL/LCL TCanvas.
  6. TImagingCanvas class is also used here.
  7. In the left part of the window is shell tree view component. Here you can
  8. select files located in your computer. If the selected file is in one of the
  9. supported formats it is displayed in the viewer
  10. area and some information about the file is displayed in the info area.
  11. If image file contains subimages you can view them too. Select active subimage
  12. by clicking on buttons with arrows (Previous/Next).
  13. When supported file is selected in shell tree view it is loaded to
  14. TMultiImage and converted to ifA8R8G8B8
  15. data format. Active subimage is then drawn TPainBox component's
  16. client area using DisplayImage procedure (direct bit copy, no need to
  17. convert Imaging's data to TGraphic).
  18. Delphi: ShellCtrls unit with used TShellTreeView component is not installed in the IDE
  19. by default in most Delphi versions. It used to be a part of demos
  20. (in BDS 2006 you can find them in Demos\DelphiWin32\VCLWin32\ShellControls,
  21. in newer in c:\Users\Public\Documents\Embarcadero\Studio\21.0\Samples).
  22. Later the unit was incorporated in VCL sources
  23. (c:\Program Files (x86)\Embarcadero\RAD Studio\10.0\source\vcl\Vcl.Shell.ShellCtrls.pas)
  24. but the demo and package was removed in XE7 (https://stackoverflow.com/questions/578241)
  25. }
  26. unit Main;
  27. {$I ImagingOptions.inc}
  28. interface
  29. uses
  30. {$IFDEF MSWINDOWS}
  31. Windows,
  32. {$ENDIF}
  33. Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  34. Dialogs, ComCtrls, ShellCtrls, ExtCtrls, StdCtrls, Buttons, ExtDlgs,
  35. ImagingTypes,
  36. Imaging,
  37. ImagingClasses,
  38. ImagingComponents,
  39. ImagingColors,
  40. ImagingCanvases,
  41. ImagingFormats,
  42. ImagingUtility;
  43. type
  44. TMainForm = class(TForm)
  45. ImageList: TImageList;
  46. LeftPanel: TPanel;
  47. RightPanel: TPanel;
  48. InfoPanel: TPanel;
  49. LabDataFormat: TLabel;
  50. LabFileFormat: TLabel;
  51. LabDim: TLabel;
  52. LabFileName: TLabel;
  53. Label3: TLabel;
  54. Label2: TLabel;
  55. Label1: TLabel;
  56. Lab1: TLabel;
  57. ViewPanel: TPanel;
  58. PaintBox: TPaintBox;
  59. Tree: TShellTreeView;
  60. Splitter1: TSplitter;
  61. Label4: TLabel;
  62. LabActImage: TLabel;
  63. StatusBar: TStatusBar;
  64. BtnPrev: TSpeedButton;
  65. BtnNext: TSpeedButton;
  66. BtnFirst: TSpeedButton;
  67. BtnLast: TSpeedButton;
  68. BtnSave: TButton;
  69. SaveDialog: TSavePictureDialog;
  70. CheckFilter: TCheckBox;
  71. procedure PaintBoxPaint(Sender: TObject);
  72. procedure FormCreate(Sender: TObject);
  73. procedure FormDestroy(Sender: TObject);
  74. procedure TreeChange(Sender: TObject; Node: TTreeNode);
  75. procedure BtnPrevClick(Sender: TObject);
  76. procedure BtnNextClick(Sender: TObject);
  77. procedure TreeKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  78. procedure BtnFirstClick(Sender: TObject);
  79. procedure BtnLastClick(Sender: TObject);
  80. procedure BtnSaveClick(Sender: TObject);
  81. procedure ViewPanelResize(Sender: TObject);
  82. procedure CheckFilterClick(Sender: TObject);
  83. private
  84. // Class that holds multiple images (loaded from MNG or DDS files for instance)
  85. FImage: ImagingClasses.TMultiImage;
  86. // Canvas for drawing on loaded images
  87. FImageCanvas: ImagingCanvases.TImagingCanvas;
  88. // Image background
  89. FBack: ImagingClasses.TSingleImage;
  90. // Canvas for background image
  91. FBackCanvas: ImagingCanvases.TImagingCanvas;
  92. FFileName: string;
  93. FLastTime: LongInt;
  94. FOriginalFormats: array of TImageFormat;
  95. FOriginalSizes: array of Integer;
  96. FSupported: Boolean;
  97. {$IFDEF FPC}
  98. procedure TreeGetImageIndex(Sender: TObject; Node: TTreeNode);
  99. procedure TreeGetSelectedIndex(Sender: TObject; Node: TTreeNode);
  100. {$ENDIF}
  101. public
  102. procedure SetSupported;
  103. procedure SetUnsupported;
  104. procedure LoadFile;
  105. procedure FillDefault;
  106. end;
  107. const
  108. FillColor = $FFE6F2FF;
  109. CheckersDensity = 32;
  110. SUnsupportedFormat = 'Selected item format not supported';
  111. var
  112. MainForm: TMainForm;
  113. implementation
  114. {$IFDEF FPC}
  115. {$R *.lfm}
  116. uses
  117. LCLType;
  118. {$ELSE}
  119. {$R *.dfm}
  120. {$ENDIF}
  121. procedure TMainForm.FormCreate(Sender: TObject);
  122. begin
  123. Caption := Caption + ' version ' + Imaging.GetVersionStr;
  124. FImage := TMultiImage.Create;
  125. FImageCanvas := TImagingCanvas.Create;
  126. FBack := TSingleImage.CreateFromParams(128, 128, ifA8R8G8B8);
  127. FBackCanvas := FindBestCanvasForImage(FBack).CreateForImage(FBack);
  128. SetUnsupported;
  129. {$IFDEF FPC}
  130. Tree.OnGetImageIndex := TreeGetImageIndex;
  131. Tree.OnGetSelectedIndex := TreeGetSelectedIndex;
  132. {$ENDIF}
  133. end;
  134. procedure TMainForm.FormDestroy(Sender: TObject);
  135. begin
  136. FImage.Free;
  137. FImageCanvas.Free;
  138. FBack.Free;
  139. FBackCanvas.Free;
  140. end;
  141. procedure TMainForm.LoadFile;
  142. var
  143. I: LongInt;
  144. T: Int64;
  145. begin
  146. try
  147. // DetermineFileFormat reads file header and returns image
  148. // file format identifier (like 'jpg', 'tga') if file is valid,
  149. // otherwise empty string is returned
  150. if Imaging.DetermineFileFormat(FFileName) <> '' then
  151. try
  152. // Load all subimages in file
  153. T := ImagingUtility.GetTimeMicroseconds;
  154. FImage.LoadMultiFromFile(FFileName);
  155. if not FImage.AllImagesValid then
  156. begin
  157. SetUnsupported;
  158. Exit;
  159. end;
  160. FLastTime := (ImagingUtility.GetTimeMicroseconds - T) div 1000;
  161. StatusBar.SimpleText := Format('Last image loaded in: %.0n ms', [FLastTime * 1.0]);
  162. // Store original data formats and sizes for later use
  163. SetLength(FOriginalFormats, FImage.ImageCount);
  164. SetLength(FOriginalSizes, FImage.ImageCount);
  165. for I := 0 to FImage.ImageCount - 1 do
  166. begin
  167. FImage.ActiveImage := I;
  168. FOriginalFormats[I] := FImage.Format;
  169. FOriginalSizes[I] := FImage.Size;
  170. // Convert image to 32bit ARGB format if current format is not supported
  171. // by canvas class
  172. if not (FImage.Format in TImagingCanvas.GetSupportedFormats) then
  173. FImage.Format := ifA8R8G8B8;
  174. end;
  175. // Activate first image and update UI
  176. FImage.ActiveImage := 0;
  177. SetSupported;
  178. PaintBox.Repaint;
  179. except
  180. SetUnsupported;
  181. raise;
  182. end
  183. else
  184. SetUnsupported;
  185. except
  186. SetUnsupported;
  187. end;
  188. end;
  189. procedure TMainForm.SetSupported;
  190. var
  191. XRes, YRes: Double;
  192. ImgSize: Integer;
  193. begin
  194. // Update image info and enable previous/next buttons
  195. ImgSize := FOriginalSizes[FImage.ActiveImage];
  196. if ImgSize > 8192 then
  197. ImgSize := ImgSize div 1024;
  198. LabDim.Caption := Format('%dx%d pixels', [FImage.Width, FImage.Height]);
  199. if GlobalMetadata.GetPhysicalPixelSize(ruDpi, XRes, YRes) then
  200. LabDim.Caption := LabDim.Caption + Format(' (DPI %.0nx%.0n)', [XRes, YRes]);
  201. LabFileFormat.Caption := Imaging.FindImageFileFormatByName(FFileName).Name;
  202. LabDataFormat.Caption := Imaging.GetFormatName(FOriginalFormats[FImage.ActiveImage]);
  203. LabDataFormat.Caption := LabDataFormat.Caption +
  204. Format(' (Size in memory: %s %s)', [IntToStrFmt(ImgSize), Iff(ImgSize = FOriginalSizes[FImage.ActiveImage], 'B', 'KiB')]);
  205. LabActImage.Caption := Format('%d/%d', [FImage.ActiveImage + 1, FImage.ImageCount]);
  206. BtnPrev.Enabled := True;
  207. BtnNext.Enabled := True;
  208. BtnFirst.Enabled := True;
  209. BtnLast.Enabled := True;
  210. BtnSave.Enabled := True;
  211. CheckFilter.Enabled := True;
  212. FSupported := True;
  213. end;
  214. procedure TMainForm.SetUnsupported;
  215. var
  216. X, Y, Step: LongInt;
  217. begin
  218. // Set info texts to 'unsupported' and create default image to show
  219. LabDim.Caption := SUnsupportedFormat;
  220. LabFileFormat.Caption := SUnsupportedFormat;
  221. LabDataFormat.Caption := SUnsupportedFormat;
  222. LabActImage.Caption := '0/0';
  223. StatusBar.SimpleText := 'No image loaded';
  224. BtnPrev.Enabled := False;
  225. BtnNext.Enabled := False;
  226. BtnFirst.Enabled := False;
  227. BtnLast.Enabled := False;
  228. BtnSave.Enabled := False;
  229. CheckFilter.Enabled := False;
  230. FSupported := False;
  231. if Assigned(FImage) then
  232. begin
  233. FImage.CreateFromParams(CheckersDensity, CheckersDensity, ifA8R8G8B8, 1);
  234. FImageCanvas.Free;
  235. FImageCanvas := FindBestCanvasForImage(FImage).CreateForImage(FImage);
  236. Step := FImage.Width div CheckersDensity;
  237. for Y := 0 to CheckersDensity - 1 do
  238. for X := 0 to CheckersDensity - 1 do
  239. begin
  240. FImageCanvas.FillColor32 := IffUnsigned((Odd(X) and not Odd(Y)) or (not Odd(X) and Odd(Y)),
  241. pcWhite, pcGray);
  242. FImageCanvas.FillRect(Rect(X * Step, Y * Step, (X + 1) * Step, (Y + 1) * Step));
  243. end;
  244. end;
  245. // Paint current image
  246. PaintBox.Repaint;
  247. end;
  248. procedure TMainForm.BtnPrevClick(Sender: TObject);
  249. begin
  250. FImage.ActiveImage := FImage.ActiveImage - 1;
  251. SetSupported;
  252. PaintBox.Repaint;
  253. end;
  254. procedure TMainForm.BtnSaveClick(Sender: TObject);
  255. var
  256. CopyPath: string;
  257. begin
  258. SaveDialog.Filter := Imaging.GetImageFileFormatsFilter(False);
  259. SaveDialog.FileName := ChangeFileExt(ExtractFileName(FFileName), '');
  260. SaveDialog.FilterIndex := Imaging.GetFileNameFilterIndex(FFileName, False);
  261. if SaveDialog.Execute then
  262. begin
  263. CopyPath := ChangeFileExt(SaveDialog.FileName, '.' +
  264. Imaging.GetFilterIndexExtension(SaveDialog.FilterIndex, False));
  265. FImage.SaveMultiToFile(CopyPath);
  266. end;
  267. end;
  268. procedure TMainForm.CheckFilterClick(Sender: TObject);
  269. begin
  270. PaintBox.Repaint;
  271. end;
  272. procedure TMainForm.BtnFirstClick(Sender: TObject);
  273. begin
  274. FImage.ActiveImage := 0;
  275. SetSupported;
  276. PaintBox.Repaint;
  277. end;
  278. procedure TMainForm.BtnLastClick(Sender: TObject);
  279. begin
  280. FImage.ActiveImage := FImage.ImageCount - 1;
  281. SetSupported;
  282. PaintBox.Repaint;
  283. end;
  284. procedure TMainForm.BtnNextClick(Sender: TObject);
  285. begin
  286. FImage.ActiveImage := FImage.ActiveImage + 1;
  287. SetSupported;
  288. PaintBox.Repaint;
  289. end;
  290. procedure TMainForm.TreeChange(Sender: TObject; Node: TTreeNode);
  291. begin
  292. // Selected item in the shell tree view has been changed
  293. // we check whether the selected item is valid file in one of the
  294. // supported formats
  295. FFileName := Tree.Path;
  296. LabFileName.Caption := ExtractFileName(FFileName);
  297. if FileExists(FFileName) and Assigned(Imaging.FindImageFileFormatByName(FFileName)) then
  298. LoadFile
  299. else
  300. SetUnsupported;
  301. end;
  302. {$IFDEF FPC}
  303. procedure TMainForm.TreeGetImageIndex(Sender: TObject; Node: TTreeNode);
  304. begin
  305. if Node.HasChildren then
  306. Node.ImageIndex := 1
  307. else if IsFileFormatSupported(Node.Text) then
  308. Node.ImageIndex := 0;
  309. end;
  310. procedure TMainForm.TreeGetSelectedIndex(Sender: TObject; Node: TTreeNode);
  311. begin
  312. Node.SelectedIndex := Node.ImageIndex;
  313. end;
  314. {$ENDIF}
  315. procedure TMainForm.TreeKeyDown(Sender: TObject; var Key: Word;
  316. Shift: TShiftState);
  317. begin
  318. if FImage.ImageCount > 1 then
  319. begin
  320. if Key = VK_SPACE then
  321. BtnNextClick(Self);
  322. end;
  323. end;
  324. procedure TMainForm.ViewPanelResize(Sender: TObject);
  325. begin
  326. // Resize background image to fit the paint box
  327. FBack.Resize(PaintBox.ClientWidth, PaintBox.ClientHeight, rfNearest);
  328. // Update back canvas state after resizing of associated image
  329. FBackCanvas.UpdateCanvasState;
  330. end;
  331. procedure TMainForm.PaintBoxPaint(Sender: TObject);
  332. var
  333. R: TRect;
  334. Filter: TResizeFilter;
  335. begin
  336. // Fill background with default color
  337. FillDefault;
  338. // Determine which stretching filter to use
  339. if FSupported and CheckFilter.Checked then
  340. Filter := rfBicubic
  341. else
  342. Filter := rfNearest;
  343. // Scale image to fit the paint box
  344. R := ImagingUtility.ScaleRectToRect(FImage.BoundsRect, PaintBox.ClientRect);
  345. // Create canvas for current image frame
  346. FImageCanvas.Free;
  347. FImageCanvas := FindBestCanvasForImage(FImage).CreateForImage(FImage);
  348. // Stretch image over background canvas
  349. FImageCanvas.StretchDrawAlpha(FImage.BoundsRect, FBackCanvas, R, Filter);
  350. // Draw image to canvas (without conversion) using OS drawing functions.
  351. // Note that DisplayImage only supports images in ifA8R8G8B8 format so
  352. // if you have image in different format you must convert it or
  353. // create standard TBitmap by calling ImagingComponents.ConvertImageToBitmap
  354. ImagingComponents.DisplayImage(PaintBox.Canvas, PaintBox.BoundsRect, FBack);
  355. end;
  356. procedure TMainForm.FillDefault;
  357. begin
  358. // Fill background canvas with default color
  359. FBackCanvas.FillColor32 := FillColor;
  360. FBackCanvas.FillRect(Rect(0, 0, FBack.Width, FBack.Height));
  361. end;
  362. {
  363. File Notes:
  364. -- 0.80 Changes/Bug Fixes ---------------------------------
  365. - Added Lazarus support so dropped "VCL" prefix.
  366. -- 0.77 Changes/Bug Fixes ---------------------------------
  367. - Displays size of image in memory.
  368. -- 0.26.5 Changes/Bug Fixes ---------------------------------
  369. - Displays image physical resolution if present.
  370. -- 0.26.3 Changes/Bug Fixes ---------------------------------
  371. - Creates best canvas class for given image for faster
  372. blending and scaling.
  373. -- 0.25.0 Changes/Bug Fixes ---------------------------------
  374. - Added alpha blended drawing with optional filtered stretching.
  375. -- 0.21 Changes/Bug Fixes -----------------------------------
  376. - Added Save Image Copy button and related stuff.
  377. - Added XP controls manifest (no TXPManifest since its not
  378. in older Delphis).
  379. - Wrong active image index was shown sometimes after several
  380. clicks on Prev/Next buttons.
  381. - Added First/Last subimage buttons.
  382. - Original data format of subimages at index >1 is displayed right now
  383. (was always A8R8G8B8)
  384. - Space key now shows next subimage if multi-images is loaded.
  385. -- 0.19 Changes/Bug Fixes -----------------------------------
  386. - added canvas usage too
  387. - added support for viewing multiimages (like MNG)
  388. - change drawing to use stuff from ImagingComponents unit instead of
  389. converting to TBitmap
  390. - changed demo to use high level interface instead of low level
  391. }
  392. end.