Main.pas 14 KB

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