uiconcache.pas 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit UIconCache;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, BGRABitmap;
  7. procedure AddToCache(AFilenames: array of string; ALastModifications: array of TDateTime;
  8. AIconSize: integer);
  9. function GetCachedIcon(AFilename: string; ALastModification: TDateTime; AInvalidIcon: TBGRABitmap): TBGRABitmap;
  10. procedure StopCaching(AWait: boolean = false);
  11. function IsCacheBusy: boolean;
  12. implementation
  13. uses URaw, BGRAThumbnail, UFileSystem, BGRAReadLzp, BGRABitmapTypes, BGRAWriteLzp;
  14. const
  15. MaxIconCacheCount = 512;
  16. type
  17. { TIconCacheThread }
  18. TIconCacheThread = class(TThread)
  19. private
  20. FFilenames: array of string;
  21. FLastModifications: array of TDateTime;
  22. FIconSize: integer;
  23. FSyncStream: TStream;
  24. FSyncExtension: string;
  25. FSyncBitmap: TBGRABitmap;
  26. FSyncResult: boolean;
  27. procedure GetStreamThumbnailSync;
  28. public
  29. constructor Create(AFilenames: array of string;
  30. ALastModifications: array of TDateTime; AIconSize: integer);
  31. procedure Execute; override;
  32. end;
  33. var
  34. IconCache: TStringList;
  35. IconCacheInvalid: TStringList;
  36. CacheThread: TIconCacheThread;
  37. procedure AddToCache(AFilenames: array of string;
  38. ALastModifications: array of TDateTime; AIconSize: integer);
  39. begin
  40. if IsCacheBusy then
  41. raise exception.Create('Cache is busy');
  42. FreeAndNil(CacheThread);
  43. CacheThread := TIconCacheThread.Create(AFilenames, ALastModifications, AIconSize);
  44. end;
  45. function GetCachedIcon(AFilename: string; ALastModification: TDateTime; AInvalidIcon: TBGRABitmap): TBGRABitmap;
  46. var
  47. cacheName, dummyCaption: String;
  48. cacheIndex: Integer;
  49. begin
  50. if IsCacheBusy then exit(nil);
  51. cacheName := AFilename+':'+FloatToStr(ALastModification);
  52. cacheIndex := IconCache.IndexOf(cacheName);
  53. if cacheIndex <> -1 then
  54. begin
  55. TStream(IconCache.Objects[cacheIndex]).Position:= 0;
  56. result := TBGRABitmap.Create;
  57. TBGRAReaderLazPaint.LoadRLEImage(TStream(IconCache.Objects[cacheIndex]), result, dummyCaption);
  58. exit;
  59. end else
  60. if IconCacheInvalid.IndexOf(cacheName) <> -1 then
  61. exit(AInvalidIcon)
  62. else
  63. exit(nil);
  64. end;
  65. procedure StopCaching(AWait: boolean);
  66. begin
  67. if Assigned(CacheThread) then
  68. begin
  69. CacheThread.Terminate;
  70. if AWait then CacheThread.WaitFor;
  71. end;
  72. end;
  73. function IsCacheBusy: boolean;
  74. begin
  75. result := Assigned(CacheThread) and not CacheThread.Finished;
  76. end;
  77. { TIconCacheThread }
  78. procedure TIconCacheThread.GetStreamThumbnailSync;
  79. begin
  80. FSyncResult := GetStreamThumbnail(FSyncStream, FIconSize, FIconSize,
  81. BGRAPixelTransparent, True, FSyncExtension, FSyncBitmap) <> nil;
  82. end;
  83. constructor TIconCacheThread.Create(AFilenames: array of string;
  84. ALastModifications: array of TDateTime; AIconSize: integer);
  85. var
  86. i: Integer;
  87. begin
  88. if length(AFilenames)<>length(ALastModifications) then
  89. raise exception.Create('Array size mismatch');
  90. setlength(FFilenames, length(AFilenames));
  91. setlength(FLastModifications, length(FFilenames));
  92. for i := 0 to high(FFilenames) do
  93. begin
  94. FFilenames[i] := AFilenames[i];
  95. FLastModifications[i] := ALastModifications[i];
  96. end;
  97. FIconSize := AIconSize;
  98. inherited Create(False);
  99. end;
  100. procedure TIconCacheThread.Execute;
  101. var
  102. i, cacheIndex: Integer;
  103. cacheName: String;
  104. bmpIcon: TBGRABitmap;
  105. found: Boolean;
  106. s: TStream;
  107. mem: TMemoryStream;
  108. endTime: TDateTime;
  109. begin
  110. bmpIcon := TBGRABitmap.Create;
  111. endTime := Now + 150/MSecsPerDay;
  112. for i := 0 to high(FFilenames) do
  113. begin
  114. if Terminated or (Now > endTime) then break;
  115. cacheName := FFilenames[i] + ':' + FloatToStr(FLastModifications[i]);
  116. cacheIndex := IconCache.IndexOf(cacheName);
  117. if cacheIndex <> -1 then Continue;
  118. try
  119. s := FileManager.CreateFileStream(FFilenames[i], fmOpenRead or fmShareDenyWrite);
  120. try
  121. if IsRawFilename(FFilenames[i]) then
  122. begin
  123. found := GetRawStreamThumbnail(s, FIconSize, FIconSize, BGRAPixelTransparent,
  124. True, bmpIcon) <> nil;
  125. end else
  126. begin
  127. if DetectFileFormat(s) = ifSvg then
  128. begin
  129. FSyncStream := s;
  130. FSyncExtension := ExtractFileExt(FFilenames[i]);
  131. FSyncBitmap := bmpIcon;
  132. Synchronize(@GetStreamThumbnailSync);
  133. found := FSyncResult;
  134. end else
  135. found := GetStreamThumbnail(s, FIconSize, FIconSize, BGRAPixelTransparent,
  136. True, ExtractFileExt(FFilenames[i]), bmpIcon) <> nil;
  137. end;
  138. finally
  139. s.Free;
  140. end;
  141. except
  142. found := false;
  143. end;
  144. if found then
  145. begin
  146. if IconCache.Count >= MaxIconCacheCount then IconCache.Delete(0);
  147. mem := TMemoryStream.Create;
  148. TBGRAWriterLazPaint.WriteRLEImage(mem, bmpIcon);
  149. IconCache.AddObject(cacheName, mem); //mem owned by IconCache
  150. end else
  151. IconCacheInvalid.Add(cacheName);
  152. end;
  153. bmpIcon.Free;
  154. end;
  155. initialization
  156. IconCache := TStringList.Create;
  157. IconCache.CaseSensitive := true;
  158. IconCache.OwnsObjects := true;
  159. IconCacheInvalid := TStringList.Create;
  160. IconCacheInvalid.CaseSensitive := true;
  161. finalization
  162. StopCaching(true);
  163. CacheThread.Free;
  164. IconCacheInvalid.Free;
  165. IconCache.Free;
  166. end.