uimagepreview.pas 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit UImagePreview;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, StdCtrls, Graphics, Controls, BGRAVirtualScreen,
  7. LazPaintType, UGraph, UResourceStrings, UFileSystem, Forms, UVolatileScrollBar,
  8. BGRABitmap, BGRAAnimatedGif, BGRAIconCursor, BGRABitmapTypes, BGRAThumbnail,
  9. UTiff, fgl;
  10. const
  11. IconSize = 32;
  12. SubImageSize = 128;
  13. type
  14. TBGRABitmapList = specialize TFPGObjectList<TBGRABitmap>;
  15. { TImagePreview }
  16. TImagePreview = class
  17. private
  18. function GetScaledIconSize: integer;
  19. protected
  20. FSurface: TBGRAVirtualScreen;
  21. FScaling: single;
  22. FSurfaceScaledHeight: Integer;
  23. FScrollbar: TVolatileScrollBar;
  24. FScrolling: boolean;
  25. FStatus: TLabel;
  26. FFilename: string;
  27. FLoadError: string;
  28. FInUpdatePreview: boolean;
  29. FImageFormat: TBGRAImageFormat;
  30. FImageNbLayers: integer;
  31. FSingleImage: TBGRABitmap;
  32. FAnimatedGif: TBGRAAnimatedGif; //has frames
  33. FTiff: TTiff; //has entries
  34. FIconCursor: TBGRAIconCursor; //has entries
  35. FThumbnails: TBGRABitmapList;
  36. FDuplicateEntrySourceIndex: integer;
  37. FSelectedMenuIndex: integer;
  38. FImageMenu: array of record
  39. Area, IconArea: TRect;
  40. DeleteArea: TRect;
  41. FrameIndex: integer;
  42. IsNew,IsDuplicate,IsLoopCount: boolean;
  43. end;
  44. FOnValidate: TNotifyEvent;
  45. FOnEscape: TNotifyEvent;
  46. FAnimate: boolean;
  47. function GetPreviewDataLoss: boolean;
  48. procedure SetFilename(AValue: string);
  49. procedure SurfaceRedraw(Sender: TObject; Bitmap: TBGRABitmap);
  50. procedure SurfaceMouseDown(Sender: TObject; {%H-}Button: TMouseButton; {%H-}Shift: TShiftState; X, Y: Integer);
  51. procedure SurfaceMouseMove(Sender: TObject; {%H-}Shift: TShiftState; X, Y: Integer);
  52. procedure SurfaceMouseUp(Sender: TObject; {%H-}Button: TMouseButton; {%H-}Shift: TShiftState; X, Y: Integer);
  53. procedure SurfaceMouseWheel(Sender: TObject; {%H-}Shift: TShiftState;
  54. WheelDelta: Integer; {%H-}MousePos: TPoint; var Handled: Boolean);
  55. procedure SurfaceDblClick(Sender: TObject);
  56. procedure SurfaceKeyDown(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState);
  57. procedure DrawMenu(Bitmap: TBGRABitmap);
  58. function TryMenuLayout(AWidth: integer; AColCount, ABottom: integer): integer;
  59. procedure ScrollToSelectedMenu;
  60. function CanAddNewEntry: boolean;
  61. function CanDuplicateEntry: boolean;
  62. function CanDeleteEntry(index: integer): boolean;
  63. procedure DeleteEntry(i: integer);
  64. function GetEntryCount: integer;
  65. function GetEntryWidth(index: integer): integer;
  66. function GetEntryHeight(index: integer): integer;
  67. function GetEntryBitDepth(index: integer): integer;
  68. function GetEntryBitmap(index: integer): TImageEntry;
  69. procedure SetEntryBitmap(var AEntry: TImageEntry);
  70. function GetEntryThumbnail(index: integer; stretchWidth, stretchHeight: integer): TBGRABitmap;
  71. procedure DrawCurrentFrame(Bitmap: TBGRABitmap);
  72. function GetCurrentFrameBitmap: TBGRABitmap;
  73. procedure ClearMenu;
  74. procedure ClearThumbnails;
  75. procedure DoValidate;
  76. procedure SetLoopCount;
  77. procedure FinishUpdatePreview;
  78. public
  79. LazPaintInstance: TLazPaintCustomInstance;
  80. constructor Create(ASurface: TBGRAVirtualScreen; AStatus: TLabel; AAnimate: boolean);
  81. destructor Destroy; override;
  82. procedure UpdatePreview;
  83. procedure HandleTimer;
  84. property Filename: string read FFilename write SetFilename;
  85. property PreviewDataLoss: boolean read GetPreviewDataLoss;
  86. property OnValidate: TNotifyEvent read FOnValidate write FOnValidate;
  87. property OnEscape: TNotifyEvent read FOnEscape write FOnEscape;
  88. property EntryCount: integer read GetEntryCount;
  89. function GetPreviewBitmap: TImageEntry;
  90. property DuplicateEntrySourceIndex: integer read FDuplicateEntrySourceIndex write FDuplicateEntrySourceIndex;
  91. property ScaledIconSize: integer read GetScaledIconSize;
  92. end;
  93. implementation
  94. uses FPimage, BGRAReadJpeg, BGRAOpenRaster, BGRAPaintNet, BGRAReadLzp, Dialogs, UNewimage,
  95. LCLType, BGRAPhoxo, BGRASVG, math, URaw, UImage, LCScaleDPI, BGRAUnits;
  96. { TImagePreview }
  97. function TImagePreview.GetScaledIconSize: integer;
  98. begin
  99. result := round(IconSize * FScaling);
  100. end;
  101. function TImagePreview.GetPreviewDataLoss: boolean;
  102. begin
  103. FinishUpdatePreview;
  104. result := (FImageFormat in[ifJpeg, {compression loss}
  105. ifLazPaint, {layer loss}
  106. ifOpenRaster,
  107. ifSvg, {vector loss}
  108. ifPhoxo,
  109. ifPaintDotNet])
  110. or (FAnimate and Assigned(FAnimatedGif) and (FAnimatedGif.Count > 1)); {frame loss}
  111. end;
  112. procedure TImagePreview.SetFilename(AValue: string);
  113. begin
  114. if FFilename=AValue then Exit;
  115. FFilename:=AValue;
  116. UpdatePreview;
  117. end;
  118. procedure TImagePreview.SurfaceRedraw(Sender: TObject; Bitmap: TBGRABitmap);
  119. begin
  120. FScaling := FSurface.GetCanvasScaleFactor * Trunc(Screen.PixelsPerInch/OriginalDPI+0.25);
  121. TVolatileScrollBar.InitDPI(FScaling);
  122. FSurfaceScaledHeight := Bitmap.Height;
  123. if (Bitmap.Width = 0) or (Bitmap.Height = 0) then
  124. begin
  125. ClearMenu;
  126. exit;
  127. end;
  128. if (CanAddNewEntry or (GetEntryCount > 1)) and not (Assigned(FAnimatedGif) and FAnimate) then
  129. DrawMenu(Bitmap)
  130. else
  131. DrawCurrentFrame(Bitmap);
  132. end;
  133. procedure TImagePreview.SurfaceMouseDown(Sender: TObject; Button: TMouseButton;
  134. Shift: TShiftState; X, Y: Integer);
  135. var
  136. i: Integer;
  137. scrollPos: integer;
  138. begin
  139. X := round(X*FScaling);
  140. Y := round(Y*FScaling);
  141. if (Button = mbLeft) and Assigned(FScrollbar) and FScrollbar.MouseDown(X,Y) then
  142. begin
  143. FScrolling:= true;
  144. FSurface.DiscardBitmap;
  145. end else
  146. begin
  147. if Assigned(FScrollbar) then
  148. scrollPos := FScrollbar.Position
  149. else scrollPos := 0;
  150. for i := 0 to high(FImageMenu) do
  151. if PtInRect(Point(x,y+scrollPos), FImageMenu[i].DeleteArea) then
  152. begin
  153. DeleteEntry(FImageMenu[i].FrameIndex);
  154. exit;
  155. end;
  156. for i := 0 to high(FImageMenu) do
  157. if PtInRect(Point(x,y+scrollPos), FImageMenu[i].Area) then
  158. begin
  159. FSelectedMenuIndex:= i;
  160. ScrollToSelectedMenu;
  161. break;
  162. end;
  163. end;
  164. end;
  165. procedure TImagePreview.SurfaceMouseMove(Sender: TObject; Shift: TShiftState;
  166. X, Y: Integer);
  167. var
  168. i, scrollPos: Integer;
  169. begin
  170. X := round(X*FScaling);
  171. Y := round(Y*FScaling);
  172. if FScrolling and Assigned(FScrollbar) and FScrollbar.MouseMove(X,Y) then
  173. FSurface.DiscardBitmap else
  174. begin
  175. if Assigned(FScrollbar) then
  176. scrollPos := FScrollbar.Position
  177. else scrollPos := 0;
  178. for i := 0 to high(FImageMenu) do
  179. if PtInRect(Point(x,y+scrollPos), FImageMenu[i].DeleteArea) then
  180. begin
  181. FSurface.Cursor := crHandPoint;
  182. exit;
  183. end;
  184. FSurface.Cursor := crDefault;
  185. end;
  186. end;
  187. procedure TImagePreview.SurfaceMouseUp(Sender: TObject; Button: TMouseButton;
  188. Shift: TShiftState; X, Y: Integer);
  189. begin
  190. X := round(X*FScaling);
  191. Y := round(Y*FScaling);
  192. if (Button = mbLeft) and FScrolling and Assigned(FScrollbar) and FScrollbar.MouseUp(X,Y) then
  193. begin
  194. FSurface.DiscardBitmap;
  195. FScrolling:= false;
  196. end;
  197. end;
  198. procedure TImagePreview.SurfaceMouseWheel(Sender: TObject; Shift: TShiftState;
  199. WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
  200. begin
  201. if Assigned(FScrollbar) then
  202. begin
  203. FScrollbar.Position := FScrollbar.Position - WheelDelta*32 div 120;
  204. FSurface.DiscardBitmap;
  205. Handled := true;
  206. end;
  207. end;
  208. procedure TImagePreview.SurfaceDblClick(Sender: TObject);
  209. begin
  210. DoValidate;
  211. end;
  212. procedure TImagePreview.SurfaceKeyDown(Sender: TObject; var Key: Word;
  213. Shift: TShiftState);
  214. begin
  215. If Key = VK_UP then
  216. begin
  217. Key := 0;
  218. if FSelectedMenuIndex > 0 then
  219. begin
  220. dec(FSelectedMenuIndex);
  221. ScrollToSelectedMenu;
  222. end;
  223. end else
  224. if Key = VK_DOWN then
  225. begin
  226. Key := 0;
  227. if FSelectedMenuIndex < High(FImageMenu) then
  228. begin
  229. inc(FSelectedMenuIndex);
  230. ScrollToSelectedMenu;
  231. end;
  232. end else
  233. if Key = VK_HOME then
  234. begin
  235. Key := 0;
  236. if (FSelectedMenuIndex <> 0) and (length(FImageMenu) > 0) then
  237. begin
  238. FSelectedMenuIndex:= 0;
  239. ScrollToSelectedMenu;
  240. end;
  241. end else
  242. if Key = VK_END then
  243. begin
  244. Key := 0;
  245. if (FSelectedMenuIndex <> high(FImageMenu)) and (length(FImageMenu) > 0) then
  246. begin
  247. FSelectedMenuIndex:= high(FImageMenu);
  248. ScrollToSelectedMenu;
  249. end;
  250. end else
  251. if Key = VK_RETURN then
  252. begin
  253. Key := 0;
  254. DoValidate;
  255. end else
  256. if Key = VK_ESCAPE then
  257. begin
  258. Key := 0;
  259. if Assigned(FOnEscape) then FOnEscape(self);
  260. end else
  261. if Key = VK_DELETE then
  262. begin
  263. Key := 0;
  264. if (FSelectedMenuIndex >= 0) and (FSelectedMenuIndex <= high(FImageMenu)) and
  265. not FImageMenu[FSelectedMenuIndex].IsNew and
  266. not FImageMenu[FSelectedMenuIndex].IsDuplicate then
  267. begin
  268. DeleteEntry(FImageMenu[FSelectedMenuIndex].FrameIndex);
  269. end;
  270. end;
  271. end;
  272. procedure TImagePreview.DrawCurrentFrame(Bitmap: TBGRABitmap);
  273. var x,y,w,h,ofs: integer;
  274. frame: TBGRABitmap;
  275. checkerScale: single;
  276. begin
  277. ClearMenu;
  278. frame := GetCurrentFrameBitmap;
  279. if Assigned(frame) then
  280. begin
  281. w := frame.Width;
  282. h := frame.Height;
  283. end
  284. else
  285. exit;
  286. // SVG is already scaled
  287. if FImageFormat <> ifSVG then
  288. begin
  289. w := round(w*FScaling);
  290. h := round(h*FScaling);
  291. end;
  292. if w > bitmap.Width then
  293. begin
  294. h := round(h/w*bitmap.Width);
  295. w := bitmap.Width;
  296. end;
  297. if h > bitmap.Height then
  298. begin
  299. w := round(w/h*bitmap.Height);
  300. h := bitmap.Height;
  301. end;
  302. x := (bitmap.Width-w) div 2;
  303. y := (bitmap.Height-h) div 2;
  304. checkerScale := DoScaleX(round(60*FScaling), OriginalDPI)/60;
  305. ofs := round(4*checkerScale);
  306. if w < ofs then ofs := w;
  307. if h < ofs then ofs := h;
  308. bitmap.FillRect(rect(x+w,y+ofs,x+ofs+w,y+ofs+h), BGRA(0,0,0,128),dmDrawWithTransparency);
  309. bitmap.FillRect(rect(x+ofs,y+h,x+w,y+ofs+h), BGRA(0,0,0,128),dmDrawWithTransparency);
  310. DrawThumbnailCheckers(Bitmap, rect(x,y,x+w,y+h), false, checkerScale);
  311. bitmap.StretchPutImage(rect(x,y,x+w,y+h), frame, dmDrawWithTransparency)
  312. end;
  313. procedure TImagePreview.DrawMenu(Bitmap: TBGRABitmap);
  314. procedure DrawSheet(x,y,sw,sh: single);
  315. var
  316. ptsF,ptsF2: ArrayOfTPointF;
  317. j: integer;
  318. begin
  319. ptsF := PointsF([PointF(x+sw*0.20,y+sh*0.1),PointF(x+sw*0.55,y+sh*0.1),PointF(x+sw*0.75,y+sh*0.3),
  320. PointF(x+sw*0.75,y+sh*0.9),PointF(x+sw*0.20,y+sh*0.9)]);
  321. ptsF2 := nil;
  322. setlength(ptsF2,length(ptsF));
  323. for j := 0 to high(ptsF) do
  324. ptsF2[j] := ptsF[j] + PointF(3,3);
  325. bitmap.FillPolyAntialias(ptsF2, BGRA(0,0,0,96));
  326. bitmap.FillPolyAntialias(ptsF, BGRAWhite);
  327. bitmap.DrawPolygonAntialias(ptsF, BGRABlack, 1.5);
  328. bitmap.DrawPolyLineAntialias([PointF(x+sw*0.55,y+sh*0.1),PointF(x+sw*0.55,y+sh*0.3),PointF(x+sw*0.75,y+sh*0.3)], BGRABlack,1.5);
  329. end;
  330. var scrollPos, totalHeight, maxScroll, availableWidth: integer;
  331. i: integer;
  332. x,y,sw,sh: integer;
  333. textRight, bpp: integer;
  334. iconCaption: string;
  335. scrolledArea, inter: TRect;
  336. begin
  337. if (Bitmap.Width < 8) or (Bitmap.Height < 8) or (GetEntryCount = 0) then exit;
  338. if Assigned(FScrollbar) then
  339. begin
  340. scrollPos := FScrollbar.Position;
  341. end else
  342. scrollPos := 0;
  343. if not FScrolling then
  344. begin
  345. FreeAndNil(FScrollbar);
  346. availableWidth := Bitmap.Width;
  347. totalHeight := TryMenuLayout(availableWidth, 1, 0);
  348. if (totalHeight > Bitmap.Height) and (GetEntryCount > 1) and (Bitmap.Width >= 500) then
  349. begin
  350. totalHeight := TryMenuLayout(availableWidth, 2, totalHeight div 2);
  351. end;
  352. maxScroll := totalHeight-Bitmap.Height;
  353. if maxScroll < 0 then maxScroll := 0;
  354. if scrollPos > maxScroll then scrollPos := maxScroll;
  355. if (totalHeight > Bitmap.Height) and (Bitmap.Width > 8+VolatileScrollBarSize) and
  356. (Bitmap.Height > VolatileThumbSize) then
  357. begin
  358. availableWidth -= VolatileScrollBarSize;
  359. FScrollbar := TVolatileScrollBar.Create(availableWidth,0,VolatileScrollBarSize,Bitmap.Height,sbVertical,
  360. scrollPos,0,maxScroll);
  361. totalHeight := TryMenuLayout(availableWidth, 1, 0);
  362. if (totalHeight > Bitmap.Height) and (GetEntryCount > 1) and (Bitmap.Width >= 500) then
  363. begin
  364. totalHeight := TryMenuLayout(availableWidth, 2, totalHeight div 2);
  365. end;
  366. end else
  367. begin
  368. scrollPos := 0;
  369. end;
  370. end else
  371. begin
  372. availableWidth := Bitmap.Width;
  373. if Assigned(FScrollbar) then
  374. availableWidth -= VolatileScrollBarSize;
  375. end;
  376. if FSelectedMenuIndex >= length(FImageMenu) then
  377. FSelectedMenuIndex:= -1;
  378. if (FSelectedMenuIndex = -1) and (length(FImageMenu) > 0) then
  379. begin
  380. FSelectedMenuIndex:= 0;
  381. while (FSelectedMenuIndex < length(FImageMenu)) and
  382. (FImageMenu[FSelectedMenuIndex].IsNew or FImageMenu[FSelectedMenuIndex].IsDuplicate
  383. or FImageMenu[FSelectedMenuIndex].IsLoopCount) do
  384. inc(FSelectedMenuIndex);
  385. //do not select special entries by default
  386. end;
  387. for i := 0 to high(FImageMenu) do
  388. with FImageMenu[i] do
  389. begin
  390. DeleteArea := EmptyRect;
  391. textRight := availableWidth;
  392. scrolledArea := rect(Area.Left, Area.Top-scrollPos, Area.Right, Area.Bottom-scrollPos);
  393. inter := RectInter(scrolledArea, bitmap.ClipRect);
  394. if (inter.Width = 0) or (inter.Height = 0) then continue;
  395. if i = FSelectedMenuIndex then
  396. begin
  397. bitmap.FillRect(scrolledArea, ColorToRGB(clHighlight));
  398. if not IsNew and not IsLoopCount and not IsDuplicate and (Area.Right - IconArea.Right > ScaledIconSize) and CanDeleteEntry(FrameIndex) then
  399. begin
  400. sh := (Area.Right - IconArea.Right - 8) div 4;
  401. if sh < ScaledIconSize div 2 then sh := ScaledIconSize div 2;
  402. if sh > ScaledIconSize then sh := ScaledIconSize;
  403. if sh > Area.Bottom-Area.Top-4 then sh := Area.Bottom-Area.Top-4;
  404. sw := sh;
  405. DeleteArea := RectWithSize(Area.Right-8-sw,(Area.Top+Area.Bottom-sh) div 2, sw,sh);
  406. bitmap.DrawLineAntialias(DeleteArea.Left+3,DeleteArea.Top+3-scrollPos,DeleteArea.Right-4,DeleteArea.Bottom-4-scrollPos,BGRABlack,6);
  407. bitmap.DrawLineAntialias(DeleteArea.Left+3,DeleteArea.Bottom-4-scrollPos,DeleteArea.Right-4,DeleteArea.Top+3-scrollPos,BGRABlack,6);
  408. bitmap.DrawLineAntialias(DeleteArea.Left+3,DeleteArea.Bottom-4-scrollPos,DeleteArea.Right-4,DeleteArea.Top+3-scrollPos,CSSRed,4);
  409. bitmap.DrawLineAntialias(DeleteArea.Left+3-1.5,DeleteArea.Bottom-4-1.5-scrollPos,DeleteArea.Right-4-1.5,DeleteArea.Top+3-1.5-scrollPos,BGRA(255,255,255,128),1);
  410. bitmap.FillEllipseAntialias(DeleteArea.Left+3,DeleteArea.Top+3-scrollPos,2,2, CSSRed);
  411. bitmap.FillEllipseAntialias(DeleteArea.Left+3,DeleteArea.Top+3-scrollPos,2,2, BGRA(255,255,255,128));
  412. bitmap.FillEllipseAntialias(DeleteArea.Right-4,DeleteArea.Bottom-4-scrollPos,2,2, CSSRed);
  413. bitmap.FillEllipseAntialias(DeleteArea.Right-4,DeleteArea.Bottom-4-scrollPos,2,2, BGRA(0,0,0,128));
  414. bitmap.DrawLineAntialias(DeleteArea.Left+4,DeleteArea.Top+4-scrollPos,DeleteArea.Right-5,DeleteArea.Bottom-5-scrollPos,CSSRed,4);
  415. textRight := DeleteArea.Left;
  416. end;
  417. end;
  418. x := IconArea.left;
  419. y := IconArea.Top;
  420. sw := IconArea.Right-x;
  421. sh := IconArea.Bottom-y;
  422. y -= scrollPos;
  423. if IsLoopCount then
  424. begin
  425. bitmap.EllipseAntialias(x+sw*0.5,y+sw*0.5, sw*0.3,sh*0.3, BGRABlack, sw*0.2);
  426. bitmap.EllipseAntialias(x+sw*0.5,y+sw*0.5, sw*0.3,sh*0.3, CSSGreen, sw*0.1);
  427. end else
  428. if IsNew then
  429. begin
  430. DrawSheet(x,y,sw,sh);
  431. end else
  432. if IsDuplicate then
  433. begin
  434. DrawSheet(x-sw*0.15,y-sh*0.1,sw,sh*0.9);
  435. DrawSheet(x+sw*0.1,y+sh*0.1,sw,sh*0.9);
  436. bitmap.FontFullHeight:= round(sh*0.7);
  437. end else
  438. begin
  439. bitmap.FillRect(rect(x+2,y+2, x+sw+2,y+sh+2), BGRA(0,0,0,96), dmDrawWithTransparency);
  440. bitmap.StretchPutImage(rect(x,y,x+sw,y+sh), GetEntryThumbnail(FrameIndex, sw,sh), dmDrawWithTransparency);
  441. end;
  442. if IsNew then iconCaption := rsNewImage else
  443. if IsDuplicate then iconCaption := rsDuplicateImage else
  444. if IsLoopCount then
  445. begin
  446. iconCaption:= rsLoopCount+': ';
  447. if FAnimatedGif.LoopCount = 0 then
  448. iconCaption += rsInfinity
  449. else
  450. iconCaption += inttostr(FAnimatedGif.LoopCount);
  451. end else
  452. begin
  453. if Assigned(FAnimatedGif) then
  454. begin
  455. iconCaption := '#' + inttostr(FrameIndex+1) + ', ' + inttostr(FAnimatedGif.FrameDelayMs[FrameIndex])+' ms';
  456. end else
  457. begin
  458. iconCaption := inttostr(GetEntryWidth(FrameIndex))+'x'+inttostr(GetEntryHeight(FrameIndex));
  459. if Assigned(FTiff) then iconCaption += ' #' + inttostr(FrameIndex+1)
  460. else if Assigned(FIconCursor) then
  461. begin
  462. bpp := GetEntryBitDepth(FrameIndex);
  463. if bpp <> 0 then iconCaption += ' '+inttostr(bpp)+'bit';
  464. end;
  465. end;
  466. end;
  467. if (y+16 < bitmap.height) and (y+sh-16 > 0) then
  468. NiceText(bitmap, x+sw+4,y+sh div 2, textRight,bitmap.height,
  469. iconCaption, taLeftJustify, tlCenter);
  470. end;
  471. if Assigned(FScrollbar) then FScrollbar.Draw(Bitmap);
  472. end;
  473. function TImagePreview.TryMenuLayout(AWidth: integer; AColCount, ABottom: integer): integer;
  474. var x,y,i,frameIndex,h,w,sw,sh: integer;
  475. newItem, LoopCountItem, DuplicateItem,
  476. colLeft,colRight, maxWidth, maxHeight: integer;
  477. currentCol: integer;
  478. procedure ComputeColumn;
  479. var
  480. scaledSubImageSize: integer;
  481. begin
  482. colLeft := (AWidth*currentCol) div AColCount;
  483. colRight := (AWidth*(currentCol+1)) div AColCount;
  484. x := colLeft+2;
  485. y := 2;
  486. maxWidth := colRight-colLeft-8;
  487. scaledSubImageSize := round(SubImageSize*FScaling);
  488. if maxWidth > scaledSubImageSize then maxWidth := scaledSubImageSize;
  489. maxHeight := scaledSubImageSize;
  490. end;
  491. begin
  492. ClearMenu;
  493. currentCol := 0;
  494. ComputeColumn;
  495. result := y+2;
  496. if Assigned(FAnimatedGif) then LoopCountItem := 1 else LoopCountItem:= 0;
  497. if CanAddNewEntry then NewItem := 1 else NewItem := 0;
  498. if CanDuplicateEntry then DuplicateItem := 1 else DuplicateItem := 0;
  499. setlength(FImageMenu, GetEntryCount + LoopCountItem + NewItem + DuplicateItem);
  500. for i := 0 to high(FImageMenu) do
  501. begin
  502. if (LoopCountItem = 1) and (i = 0) then
  503. begin
  504. frameIndex := -1;
  505. FImageMenu[i].IsLoopCount := true;
  506. w := ScaledIconSize;
  507. h := w;
  508. end else
  509. if (NewItem = 1) and (i = LoopCountItem) then
  510. begin
  511. frameIndex := GetEntryCount;
  512. FImageMenu[i].IsNew := true;
  513. w := ScaledIconSize;
  514. h := w;
  515. end
  516. else
  517. if (DuplicateItem = 1) and (i = LoopCountItem + NewItem) then
  518. begin
  519. frameIndex := GetEntryCount;
  520. FImageMenu[i].IsDuplicate := true;
  521. w := ScaledIconSize;
  522. h := w;
  523. end
  524. else
  525. begin
  526. frameIndex := i-NewItem-LoopCountItem-DuplicateItem;
  527. w := round(GetEntryWidth(frameIndex)*FScaling);
  528. h := round(GetEntryHeight(frameIndex)*FScaling);
  529. end;
  530. if w > maxWidth then
  531. begin
  532. sw := maxWidth;
  533. sh := round(h/w*maxWidth);
  534. if (sh = 0) and (h <> 0) then sh := 1;
  535. end else
  536. begin
  537. sw := w;
  538. sh := h;
  539. end;
  540. if sh > maxHeight then
  541. begin
  542. sw := round(sw/sh*maxHeight);
  543. sh := maxHeight;
  544. if (sw = 0) and (w <> 0) then sw := 1;
  545. end;
  546. FImageMenu[i].FrameIndex:= frameIndex;
  547. FImageMenu[i].Area := rect(x,y,colRight-2,y+sh+4);
  548. FImageMenu[i].IconArea := rect(x+2,y+2,x+2+sw,y+2+sh);
  549. y += sh+4;
  550. if y+2 > result then result := y+2;
  551. if (y+2 > ABottom) and (currentCol+1 < AColCount) then
  552. begin
  553. currentCol += 1;
  554. ComputeColumn;
  555. end;
  556. end;
  557. end;
  558. function TImagePreview.CanAddNewEntry: boolean;
  559. begin
  560. result := Assigned(FIconCursor) or Assigned(FTiff) or Assigned(FAnimatedGif);
  561. end;
  562. function TImagePreview.CanDuplicateEntry: boolean;
  563. begin
  564. result := (Assigned(FTiff) or Assigned(FAnimatedGif)) and
  565. (FDuplicateEntrySourceIndex >= 0) and (FDuplicateEntrySourceIndex < EntryCount);
  566. end;
  567. function TImagePreview.GetEntryCount: integer;
  568. begin
  569. if Assigned(FIconCursor) then
  570. result := FIconCursor.Count
  571. else if Assigned(FTiff) then
  572. result := FTiff.Count
  573. else if Assigned(FAnimatedGif) and not FAnimate then
  574. result := FAnimatedGif.Count
  575. else
  576. result := 1;
  577. end;
  578. function TImagePreview.GetEntryWidth(index: integer): integer;
  579. begin
  580. if Assigned(FIconCursor) then
  581. result := FIconCursor.Width[index]
  582. else if Assigned(FTiff) then
  583. result := FTiff.Entry[index].Width
  584. else if Assigned(FAnimatedGif) then
  585. result := FAnimatedGif.Width
  586. else if Assigned(FSingleImage) then
  587. result := FSingleImage.Width;
  588. end;
  589. function TImagePreview.GetEntryHeight(index: integer): integer;
  590. begin
  591. if Assigned(FIconCursor) then
  592. result := FIconCursor.Height[index]
  593. else if Assigned(FTiff) then
  594. result := FTiff.Entry[index].Height
  595. else if Assigned(FAnimatedGif) then
  596. result := FAnimatedGif.Height
  597. else if Assigned(FSingleImage) then
  598. result := FSingleImage.Height;
  599. end;
  600. function TImagePreview.GetEntryBitDepth(index: integer): integer;
  601. begin
  602. if Assigned(FIconCursor) then
  603. result := FIconCursor.BitDepth[index]
  604. else if Assigned(FTiff) then
  605. result := FTiff.Entry[index].BitDepth
  606. else
  607. result := 0;
  608. end;
  609. function TImagePreview.GetEntryBitmap(index: integer): TImageEntry;
  610. var
  611. mem: TMemoryStream;
  612. begin
  613. if (index < 0) or (index >= GetEntryCount) then
  614. raise exception.Create('Index out of bounds');
  615. result := TImageEntry.Empty;
  616. result.frameCount:= EntryCount;
  617. try
  618. if Assigned(FIconCursor) then
  619. begin
  620. result.bmp := FIconCursor.GetBitmap(index) as TBGRABitmap;
  621. result.bpp := FIconCursor.BitDepth[index];
  622. result.frameIndex := index;
  623. end
  624. else
  625. if Assigned(FTiff) then
  626. begin
  627. mem := TMemoryStream.Create;
  628. try
  629. FTiff.SaveToStream(mem, [index]);
  630. mem.Position:= 0;
  631. result.bmp := TBGRABitmap.Create(mem);
  632. result.bpp := FTiff.Entry[index].BitDepth;
  633. result.frameIndex := index;
  634. finally
  635. mem.Free;
  636. end;
  637. end else
  638. if Assigned(FAnimatedGif) and not FAnimate then
  639. begin
  640. FAnimatedGif.CurrentImage := index;
  641. result.bmp := FAnimatedGif.MemBitmap.Duplicate as TBGRABitmap;
  642. result.frameIndex := index;
  643. end;
  644. except on ex:exception do
  645. begin
  646. if result.bmp = nil then
  647. begin
  648. result.bmp := TBGRABitmap.Create(GetEntryWidth(index), GetEntryHeight(index), BGRAWhite);
  649. result.bpp := GetEntryBitDepth(index);
  650. result.frameIndex:= index;
  651. end;
  652. end;
  653. end;
  654. end;
  655. procedure TImagePreview.SetEntryBitmap(var AEntry: TImageEntry);
  656. var
  657. sAddedTiff: TMemoryStream;
  658. addedTiff: TTiff;
  659. sOut: TStream;
  660. begin
  661. if (AEntry.frameIndex < 0) or (AEntry.frameIndex > GetEntryCount) then
  662. raise exception.Create('Index out of bounds');
  663. if Filename = '' then raise exception.create('Filename undefined');
  664. AEntry.frameCount:= GetEntryCount;
  665. if Assigned(FTiff) then
  666. begin
  667. addedTiff := TTiff.Create;
  668. sAddedTiff := TMemoryStream.Create;
  669. try
  670. AEntry.bmp.SaveToStreamAs(sAddedTiff, ifTiff);
  671. sAddedTiff.Position:= 0;
  672. if addedTiff.LoadFromStream(sAddedTiff) <> teNone then
  673. raise Exception.Create(rsInternalError);
  674. if AEntry.frameIndex > FTiff.Count then
  675. AEntry.frameIndex := FTiff.Count;
  676. FTiff.Move(addedTiff,0, AEntry.frameIndex);
  677. sOut := FileManager.CreateFileStream(Filename,fmCreate);
  678. try
  679. FTiff.SaveToStream(sOut);
  680. finally
  681. sOut.Free;
  682. end;
  683. finally
  684. sAddedTiff.Free;
  685. addedTiff.Free;
  686. end;
  687. end else
  688. if Assigned(FAnimatedGif) then
  689. begin
  690. if AEntry.frameIndex >= FAnimatedGif.Count then
  691. AEntry.frameIndex := FAnimatedGif.AddFullFrame(AEntry.bmp, FAnimatedGif.AverageDelayMs)
  692. else
  693. FAnimatedGif.ReplaceFullFrame(AEntry.frameIndex, AEntry.bmp, FAnimatedGif.FrameDelayMs[AEntry.frameIndex]);
  694. sOut := FileManager.CreateFileStream(Filename,fmCreate);
  695. try
  696. FAnimatedGif.SaveToStream(sOut);
  697. finally
  698. sOut.Free;
  699. end;
  700. end;
  701. end;
  702. function TImagePreview.GetEntryThumbnail(index: integer; stretchWidth, stretchHeight: integer): TBGRABitmap;
  703. var
  704. entry: TImageEntry;
  705. begin
  706. if (index < FThumbnails.Count) and Assigned(FThumbnails[index]) then
  707. begin
  708. result := FThumbnails[index];
  709. exit;
  710. end;
  711. entry := GetEntryBitmap(index);
  712. if Assigned(entry.bmp) then
  713. begin
  714. try
  715. if Assigned(FIconCursor) then
  716. result := GetBitmapThumbnail(entry.bmp, FIconCursor.FileType,stretchWidth,stretchHeight, BGRAPixelTransparent,true)
  717. else
  718. result := GetBitmapThumbnail(entry.bmp, stretchWidth,stretchHeight, BGRAPixelTransparent,true);
  719. while FThumbnails.Count < index+1 do FThumbnails.Add(nil);
  720. FThumbnails[index] := result;
  721. finally
  722. entry.bmp.free;
  723. end;
  724. end else
  725. result := nil;
  726. end;
  727. function TImagePreview.CanDeleteEntry(index: integer): boolean;
  728. begin
  729. result := (Assigned(FIconCursor) or Assigned(FTiff) or Assigned(FAnimatedGif)) and
  730. (index >= 0) and (index < GetEntryCount) and
  731. (GetEntryCount > 1);
  732. end;
  733. function TImagePreview.GetCurrentFrameBitmap: TBGRABitmap;
  734. begin
  735. if Assigned(FSingleImage) then
  736. result := FSingleImage
  737. else if Assigned(FAnimatedGif) then
  738. result := FAnimatedGif.MemBitmap
  739. else if Assigned(FTiff) then
  740. begin
  741. FSingleImage := GetEntryBitmap(0).bmp;
  742. result := FSingleImage;
  743. end
  744. else
  745. result := nil;
  746. end;
  747. procedure TImagePreview.ClearMenu;
  748. begin
  749. FImageMenu := nil;
  750. end;
  751. procedure TImagePreview.ClearThumbnails;
  752. begin
  753. FThumbnails.Clear;
  754. end;
  755. procedure TImagePreview.DoValidate;
  756. begin
  757. if (FSelectedMenuIndex >= 0) and (FSelectedMenuIndex < length(FImageMenu)) and
  758. FImageMenu[FSelectedMenuIndex].IsLoopCount then
  759. begin
  760. SetLoopCount;
  761. end else
  762. if Assigned(FOnValidate) then FOnValidate(self);
  763. end;
  764. procedure TImagePreview.SetLoopCount;
  765. var newLoopCount: Word;
  766. errPos: integer;
  767. outputStream: TStream;
  768. begin
  769. if Assigned(FAnimatedGif) then
  770. begin
  771. val(InputBox(rsAnimatedGIF, rsLoopCount+' (0='+ rsInfinity+'):', inttostr(FAnimatedGif.LoopCount)), newLoopCount, errPos);
  772. if errPos = 0 then
  773. begin
  774. if newLoopCount > 65534 then newLoopCount:= 0;
  775. if newLoopCount <> FAnimatedGif.LoopCount then
  776. begin
  777. FAnimatedGif.LoopCount := newLoopCount;
  778. outputStream := nil;
  779. try
  780. outputStream := FileManager.CreateFileStream(Filename, fmCreate);
  781. FAnimatedGif.SaveToStream(outputStream);
  782. FreeAndNil(outputStream);
  783. except
  784. on ex:exception do
  785. begin
  786. if Assigned(outputStream) then
  787. FileManager.CancelStreamAndFree(outputStream);
  788. ShowMessage(ex.Message);
  789. end;
  790. end;
  791. outputStream.Free;
  792. FSurface.DiscardBitmap;
  793. end;
  794. end;
  795. end;
  796. end;
  797. procedure TImagePreview.FinishUpdatePreview;
  798. var reader: TFPCustomImageReader;
  799. jpegReader: TBGRAReaderJpeg;
  800. source: TStream;
  801. svg: TBGRASVG;
  802. tr: TTiffError;
  803. screenDpi: Integer;
  804. singleSize: string;
  805. begin
  806. if FInUpdatePreview then
  807. begin
  808. source := nil;
  809. singleSize := '';
  810. try
  811. source := FileManager.CreateFileStream(FFilename, fmOpenRead or fmShareDenyWrite);
  812. FImageFormat := DetectFileFormat(source,ExtractFileExt(FFilename));
  813. if IsRawFilename(FFilename) then
  814. begin
  815. try
  816. FSingleImage := GetRawStreamImage(source);
  817. FImageNbLayers := 1;
  818. except
  819. on ex: Exception do
  820. begin
  821. FLoadError:= ex.Message;
  822. FreeAndNil(FSingleImage);
  823. end;
  824. end;
  825. end else
  826. case FImageFormat of
  827. ifGif:
  828. begin
  829. try
  830. FAnimatedGif := TBGRAAnimatedGif.Create;
  831. FAnimatedGif.LoadFromStream(source);
  832. FImageNbLayers := 1;
  833. except
  834. on ex: Exception do
  835. begin
  836. FLoadError := ex.Message;
  837. FreeAndNil(FAnimatedGif);
  838. end;
  839. end;
  840. end;
  841. ifTiff:
  842. begin
  843. try
  844. FTiff := TTiff.Create;
  845. tr := FTiff.LoadFromStream(source);
  846. if tr <> teNone then
  847. raise exception.Create(rsCannotOpenFile+' (TIFF '+inttostr(ord(tr))+')');
  848. FImageNbLayers := 1;
  849. if FTiff.Count = 0 then
  850. begin
  851. FreeAndNil(FTiff);
  852. FLoadError := rsFileCannotBeEmpty;
  853. end;
  854. except
  855. on ex: Exception do
  856. begin
  857. FLoadError := ex.Message;
  858. FreeAndNil(FTiff);
  859. end;
  860. end;
  861. end;
  862. ifIco,ifCur:
  863. begin
  864. FIconCursor := TBGRAIconCursor.Create;
  865. try
  866. FIconCursor.LoadFromStream(source);
  867. FImageNbLayers := 1;
  868. except
  869. on ex: Exception do
  870. begin
  871. FLoadError:= ex.Message;
  872. FreeAndNil(FIconCursor);
  873. end;
  874. end;
  875. end;
  876. ifJpeg:
  877. begin
  878. jpegReader := TBGRAReaderJpeg.Create;
  879. jpegReader.Performance := jpBestSpeed;
  880. jpegReader.MinWidth := Screen.Width;
  881. jpegReader.MinHeight := Screen.Height;
  882. try
  883. FSingleImage := TBGRABitmap.Create;
  884. FSingleImage.LoadFromStream(source,jpegReader);
  885. FImageNbLayers := 1;
  886. except
  887. on ex: Exception do
  888. begin
  889. FLoadError:= ex.Message;
  890. FreeAndNil(FSingleImage);
  891. end;
  892. end;
  893. jpegReader.Free;
  894. end;
  895. ifSvg:
  896. begin
  897. svg := TBGRASVG.Create(source);
  898. singleSize := svg.Units.formatValue(svg.Width) + ' x ' + svg.Units.formatValue(svg.Height);
  899. FImageNbLayers:= max(1, svg.LayerCount);
  900. screenDpi:= Screen.PixelsPerInch * CanvasScale;
  901. svg.Units.ContainerWidth := FloatWithCSSUnit(Screen.Width * CanvasScale / screenDpi * svg.DefaultDpi, cuPixel);
  902. svg.Units.ContainerHeight := FloatWithCSSUnit(Screen.Height * CanvasScale / screenDpi * svg.DefaultDpi, cuPixel);
  903. svg.CropToViewBox(screenDpi / svg.DefaultDpi);
  904. with ComputeAcceptableImageSize(floor(svg.WidthAsPixel + 0.95), floor(svg.HeightAsPixel + 0.95)) do
  905. FSingleImage := TBGRABitmap.Create(cx,cy);
  906. svg.StretchDraw(FSingleImage.Canvas2d,0,0,FSingleImage.Width,FSingleImage.Height);
  907. svg.Free;
  908. end
  909. else
  910. begin
  911. reader := CreateBGRAImageReader(FImageFormat);
  912. try
  913. FSingleImage := TBGRABitmap.Create;
  914. FSingleImage.LoadFromStream(source,reader,[loBmpAutoOpaque]);
  915. if reader is TFPReaderOpenRaster then FImageNbLayers := TFPReaderOpenRaster(reader).NbLayers else
  916. if reader is TFPReaderPaintDotNet then FImageNbLayers := TFPReaderPaintDotNet(reader).NbLayers else
  917. if reader is TBGRAReaderLazPaint then FImageNbLayers := TBGRAReaderLazPaint(reader).NbLayers else
  918. if reader is TBGRAReaderOXO then FImageNbLayers := TBGRAReaderOXO(reader).NbLayers else
  919. FImageNbLayers := 1;
  920. except
  921. on ex: Exception do
  922. begin
  923. FLoadError:= ex.Message;
  924. FreeAndNil(FSingleImage);
  925. end;
  926. end;
  927. reader.Free;
  928. end;
  929. end;
  930. except
  931. on ex: Exception do
  932. FLoadError:= ex.Message;
  933. end;
  934. source.Free;
  935. if Assigned(FIconCursor) then
  936. begin
  937. if FIconCursor.Count > 0 then
  938. FStatus.Caption := rsCanvasSize + ': ' + IntToStr(FIconCursor.Width[0])+'x'+IntToStr(FIconCursor.Height[0])+ ', ' +
  939. rsEntries + ': ' + IntToStr(FIconCursor.Count)
  940. else
  941. FStatus.Caption := rsEntries + ': ' + IntToStr(FIconCursor.Count);
  942. end else
  943. if Assigned(FAnimatedGif) then
  944. begin
  945. FStatus.Caption := rsCanvasSize + ': ' + IntToStr(FAnimatedGif.Width)+'x'+IntToStr(FAnimatedGif.Height)+', '+
  946. rsFrames+': '+IntToStr(FAnimatedGif.Count);
  947. end else
  948. if Assigned(FTiff) then
  949. begin
  950. with FTiff.GetBiggestImage do
  951. FStatus.Caption := rsCanvasSize + ': ' + IntToStr(Width)+'x'+IntToStr(Height)+', '+
  952. rsEntries+': '+IntToStr(FTiff.Count);
  953. end else
  954. if Assigned(FSingleImage) then
  955. begin
  956. if singleSize = '' then singleSize := IntToStr(FSingleImage.Width)+'x'+IntToStr(FSingleImage.Height);
  957. FStatus.Caption := rsCanvasSize + ': ' + singleSize +', '+
  958. rsLayers+': '+IntToStr(FImageNbLayers);
  959. end else
  960. if FLoadError <> '' then
  961. begin
  962. FStatus.Caption := FLoadError;
  963. end else
  964. FStatus.Caption := '';
  965. FInUpdatePreview := false;
  966. FSurface.RedrawBitmap;
  967. end;
  968. end;
  969. procedure TImagePreview.DeleteEntry(i: integer);
  970. var outputStream: TStream;
  971. begin
  972. if (assigned(FIconCursor) or assigned(FTiff) or assigned(FAnimatedGif)) and (i < GetEntryCount) and (i >= 0) then
  973. begin
  974. if GetEntryCount = 1 then
  975. begin
  976. ShowMessage(rsFileCannotBeEmpty);
  977. end else
  978. if QuestionDlg (rsDeleteFile,rsDeleteImageEntry,mtConfirmation,[mrOk,rsOkay,mrCancel,rsCancel],'') = mrOk then
  979. begin
  980. try
  981. if assigned(FIconCursor) then
  982. FIconCursor.Delete(i)
  983. else if assigned(FTiff) then
  984. FTiff.Delete(i)
  985. else if assigned(FAnimatedGif) then
  986. FAnimatedGif.DeleteFrame(i, true);
  987. if FThumbnails.Count >= i+1 then
  988. FThumbnails.Delete(i);
  989. outputStream := FileManager.CreateFileStream(Filename, fmCreate);
  990. try
  991. if assigned(FIconCursor) then
  992. FIconCursor.SaveToStream(outputStream)
  993. else if assigned(FTiff) then
  994. FTiff.SaveToStream(outputStream)
  995. else if assigned(FAnimatedGif) then
  996. FAnimatedGif.SaveToStream(outputStream);
  997. outputStream.Free;
  998. if (LazPaintInstance.Image.currentFilenameUTF8 = Filename) and
  999. (LazPaintInstance.Image.FrameIndex >= i) then
  1000. begin
  1001. if LazPaintInstance.Image.FrameIndex > i then
  1002. dec(LazPaintInstance.Image.FrameIndex)
  1003. else
  1004. LazPaintInstance.Image.FrameIndex := TImageEntry.NewFrameIndex;
  1005. LazPaintInstance.Image.OnImageChanged.NotifyObservers;
  1006. end;
  1007. dec(LazPaintInstance.Image.FrameCount);
  1008. except on ex: Exception do
  1009. begin
  1010. FileManager.CancelStreamAndFree(outputStream);
  1011. ShowMessage(ex.Message);
  1012. end;
  1013. end;
  1014. if FSelectedMenuIndex = high(FImageMenu) then
  1015. dec(FSelectedMenuIndex);
  1016. FSurface.RedrawBitmap;
  1017. except
  1018. on ex:exception do
  1019. ShowMessage(ex.Message);
  1020. end;
  1021. end;
  1022. end;
  1023. end;
  1024. procedure TImagePreview.ScrollToSelectedMenu;
  1025. var
  1026. scrollPos: Integer;
  1027. begin
  1028. if Assigned(FScrollbar) then
  1029. scrollPos := FScrollbar.Position
  1030. else scrollPos := 0;
  1031. if (FSelectedMenuIndex >= 0) and (FSelectedMenuIndex <= high(FImageMenu)) then
  1032. begin
  1033. if scrollPos < FImageMenu[FSelectedMenuIndex].Area.Bottom-FSurfaceScaledHeight then
  1034. scrollPos := FImageMenu[FSelectedMenuIndex].Area.Bottom-FSurfaceScaledHeight;
  1035. if scrollPos > FImageMenu[FSelectedMenuIndex].Area.Top then
  1036. scrollPos := FImageMenu[FSelectedMenuIndex].Area.Top;
  1037. if Assigned(FScrollbar) then FScrollbar.Position := scrollPos;
  1038. FSurface.DiscardBitmap;
  1039. end;
  1040. end;
  1041. constructor TImagePreview.Create(ASurface: TBGRAVirtualScreen; AStatus: TLabel; AAnimate: boolean);
  1042. begin
  1043. FSurface := ASurface;
  1044. FSurface.BitmapAutoScale:= false;
  1045. FStatus := AStatus;
  1046. FAnimate:= AAnimate;
  1047. FSelectedMenuIndex := -1;
  1048. FDuplicateEntrySourceIndex := -1;
  1049. {$IFDEF WINDOWS}
  1050. ASurface.Color := clAppWorkspace;
  1051. {$ENDIF}
  1052. FSurface.OnRedraw:= @SurfaceRedraw;
  1053. FSurface.OnMouseDown:= @SurfaceMouseDown;
  1054. FSurface.OnMouseMove:= @SurfaceMouseMove;
  1055. FSurface.OnMouseUp:= @SurfaceMouseUp;
  1056. FSurface.OnMouseWheel:= @SurfaceMouseWheel;
  1057. FSurface.OnDblClick:= @SurfaceDblClick;
  1058. FSurface.OnKeyDown:= @SurfaceKeyDown;
  1059. FSingleImage := nil;
  1060. FAnimatedGif := nil;
  1061. FIconCursor := nil;
  1062. FTiff := nil;
  1063. FThumbnails := TBGRABitmapList.Create;
  1064. FOnValidate := nil;
  1065. FScrollbar := nil;
  1066. FScrolling:= false;
  1067. end;
  1068. destructor TImagePreview.Destroy;
  1069. begin
  1070. ClearMenu;
  1071. ClearThumbnails;
  1072. if FSurface.OnRedraw = @SurfaceRedraw then FSurface.OnRedraw:= nil;
  1073. if FSurface.OnMouseDown = @SurfaceMouseDown then FSurface.OnMouseDown:= nil;
  1074. if FSurface.OnMouseMove = @SurfaceMouseMove then FSurface.OnMouseMove:= nil;
  1075. if FSurface.OnMouseUp = @SurfaceMouseUp then FSurface.OnMouseUp:= nil;
  1076. if FSurface.OnMouseWheel = @SurfaceMouseWheel then FSurface.OnMouseWheel:= nil;
  1077. if FSurface.OnDblClick = @SurfaceDblClick then FSurface.OnDblClick := nil;
  1078. if FSurface.OnKeyDown = @SurfaceKeyDown then FSurface.OnKeyDown := nil;
  1079. FreeAndNil(FSingleImage);
  1080. FreeAndNil(FTiff);
  1081. FreeAndNil(FAnimatedGif);
  1082. FreeAndNil(FIconCursor);
  1083. FreeAndNil(FThumbnails);
  1084. FreeAndNil(FScrollbar);
  1085. inherited Destroy;
  1086. end;
  1087. procedure TImagePreview.UpdatePreview;
  1088. begin
  1089. ClearThumbnails;
  1090. FreeAndNil(FSingleImage);
  1091. FreeAndNil(FTiff);
  1092. FreeAndNil(FAnimatedGif);
  1093. FreeAndNil(FIconCursor);
  1094. FImageNbLayers := 0;
  1095. FImageFormat:= ifUnknown;
  1096. FLoadError := '';
  1097. ClearMenu;
  1098. FreeAndNil(FScrollbar);
  1099. FSelectedMenuIndex := -1;
  1100. FSurface.RedrawBitmap;
  1101. FStatus.Caption := rsLoading+'...';
  1102. FStatus.Update;
  1103. FInUpdatePreview := true;
  1104. {$IFDEF LINUX}
  1105. Application.ProcessMessages;
  1106. {$ENDIF}
  1107. FinishUpdatePreview;
  1108. end;
  1109. procedure TImagePreview.HandleTimer;
  1110. begin
  1111. if FAnimate and assigned(FAnimatedGif) and (FAnimatedGif.TimeUntilNextImageMs <= 0) then
  1112. FSurface.RedrawBitmap;
  1113. end;
  1114. function TImagePreview.GetPreviewBitmap: TImageEntry;
  1115. var tx,ty,bpp: integer; back: TBGRAPixel;
  1116. begin
  1117. FinishUpdatePreview;
  1118. result := TImageEntry.Empty;
  1119. result.frameCount := GetEntryCount;
  1120. if Assigned(FIconCursor) then
  1121. begin
  1122. if Assigned(FImageMenu) then
  1123. begin
  1124. if (FSelectedMenuIndex >= 0) and (FSelectedMenuIndex < length(FImageMenu)) then
  1125. begin
  1126. if FImageMenu[FSelectedMenuIndex].IsNew then
  1127. begin
  1128. if Assigned(LazPaintInstance) and ShowNewImageDlg(LazPaintInstance, true, tx,ty,bpp, back) then
  1129. begin
  1130. if FIconCursor.IndexOf(tx,ty,bpp)<>-1 then
  1131. LazPaintInstance.ShowMessage(rsNewImage, rsIconImageAlreadyExists)
  1132. else
  1133. begin
  1134. result.bmp := TBGRABitmap.Create(tx,ty,back);
  1135. result.bpp := bpp;
  1136. result.frameIndex:= TImageEntry.NewFrameIndex;
  1137. end;
  1138. end;
  1139. end else
  1140. result := GetEntryBitmap(FImageMenu[FSelectedMenuIndex].FrameIndex);
  1141. end;
  1142. end else
  1143. if GetEntryCount > 0 then
  1144. result := GetEntryBitmap(0);
  1145. end else
  1146. if Assigned(FTiff) then
  1147. begin
  1148. if FImageMenu <> nil then
  1149. begin
  1150. if (FSelectedMenuIndex >= 0) and (FSelectedMenuIndex < length(FImageMenu)) then
  1151. begin
  1152. if FImageMenu[FSelectedMenuIndex].IsNew then
  1153. begin
  1154. if Assigned(LazPaintInstance) and ShowNewImageDlg(LazPaintInstance, false, tx,ty,bpp, back) then
  1155. begin
  1156. result.bmp := TBGRABitmap.Create(tx,ty,back);
  1157. result.frameIndex:= TImageEntry.NewFrameIndex;
  1158. end;
  1159. end else
  1160. if FImageMenu[FSelectedMenuIndex].IsDuplicate then
  1161. begin
  1162. result := GetEntryBitmap(DuplicateEntrySourceIndex);
  1163. result.frameIndex:= GetEntryCount;
  1164. result.isDuplicate:= true;
  1165. SetEntryBitmap(result);
  1166. end
  1167. else
  1168. result := GetEntryBitmap(FImageMenu[FSelectedMenuIndex].FrameIndex);
  1169. end;
  1170. end else
  1171. if GetEntryCount > 0 then
  1172. result := GetEntryBitmap(0);
  1173. end else
  1174. if Assigned(FSingleImage) then
  1175. begin
  1176. result.bmp := FSingleImage;
  1177. result.frameIndex:= 0;
  1178. FSingleImage := nil;
  1179. end else
  1180. if Assigned(FAnimatedGif) then
  1181. begin
  1182. if FAnimate and (FAnimatedGif.Count = 1) then
  1183. result.bmp := FAnimatedGif.MemBitmap.Duplicate as TBGRABitmap
  1184. else
  1185. if not FAnimate then
  1186. begin
  1187. if FImageMenu <> nil then
  1188. begin
  1189. if (FSelectedMenuIndex >= 0) and (FSelectedMenuIndex < length(FImageMenu)) then
  1190. begin
  1191. if FImageMenu[FSelectedMenuIndex].IsNew then
  1192. begin
  1193. result.bmp := TBGRABitmap.Create(FAnimatedGif.Width,FAnimatedGif.Height,BGRAPixelTransparent);
  1194. result.frameIndex:= TImageEntry.NewFrameIndex;
  1195. end else
  1196. if FImageMenu[FSelectedMenuIndex].IsDuplicate then
  1197. begin
  1198. result := GetEntryBitmap(DuplicateEntrySourceIndex);
  1199. result.frameIndex:= GetEntryCount;
  1200. result.isDuplicate:= true;
  1201. SetEntryBitmap(result);
  1202. end
  1203. else
  1204. result := GetEntryBitmap(FImageMenu[FSelectedMenuIndex].FrameIndex);
  1205. end;
  1206. end else
  1207. if GetEntryCount > 0 then
  1208. result := GetEntryBitmap(0);
  1209. end;
  1210. end;
  1211. end;
  1212. end.