1+ using System . IO ;
2+ using FairyGUI ;
3+ using GameFrameX . Runtime ;
4+ using UnityEngine ;
5+
6+ namespace Unity . Startup
7+ {
8+ using System . Collections . Generic ;
9+ using UnityEngine ;
10+
11+ public class LRUCache
12+ {
13+ private class CacheItem
14+ {
15+ public string key ;
16+ public NTexture texture ;
17+
18+ public CacheItem ( string key , NTexture texture )
19+ {
20+ this . key = key ;
21+ this . texture = texture ;
22+ }
23+ }
24+
25+ private readonly int maxSize ;
26+ private Dictionary < string , CacheItem > cache ;
27+ private LinkedList < CacheItem > lruList ;
28+
29+ public LRUCache ( int maxSize )
30+ {
31+ this . maxSize = maxSize ;
32+ cache = new Dictionary < string , CacheItem > ( ) ;
33+ lruList = new LinkedList < CacheItem > ( ) ;
34+ }
35+
36+ public NTexture Get ( string key )
37+ {
38+ if ( cache . TryGetValue ( key , out CacheItem item ) )
39+ {
40+ // 移动到最近使用的位置
41+ lruList . Remove ( item ) ;
42+ lruList . AddFirst ( item ) ;
43+ return item . texture ;
44+ }
45+
46+ return null ; // 纹理未找到
47+ }
48+
49+ public void Put ( string key , NTexture texture )
50+ {
51+ if ( key == null )
52+ {
53+ return ;
54+ }
55+
56+ if ( cache . ContainsKey ( key ) )
57+ {
58+ // 更新已有项并移动到最近使用位置
59+ CacheItem existingItem = cache [ key ] ;
60+ lruList . Remove ( existingItem ) ;
61+ existingItem . texture = texture ; // 更新纹理
62+ lruList . AddFirst ( existingItem ) ;
63+ }
64+ else
65+ {
66+ // 如果超过最大数量,则移除最少使用的项
67+ if ( cache . Count >= maxSize )
68+ {
69+ RemoveLeastRecentlyUsed ( ) ;
70+ }
71+
72+ // 添加新项
73+ CacheItem newItem = new CacheItem ( key , texture ) ;
74+ cache [ key ] = newItem ;
75+ lruList . AddFirst ( newItem ) ;
76+ }
77+ }
78+
79+ private void RemoveLeastRecentlyUsed ( )
80+ {
81+ if ( lruList . Count > 0 )
82+ {
83+ CacheItem leastUsedItem = lruList . Last . Value ;
84+ lruList . RemoveLast ( ) ;
85+ cache . Remove ( leastUsedItem . key ) ;
86+ leastUsedItem . texture . Dispose ( ) ;
87+ if ( leastUsedItem . texture . nativeTexture . IsNotNull ( ) )
88+ {
89+ Object . Destroy ( leastUsedItem . texture . nativeTexture ) ; // 释放纹理资源
90+ }
91+
92+ if ( leastUsedItem . texture . alphaTexture . IsNotNull ( ) )
93+ {
94+ Object . Destroy ( leastUsedItem . texture . alphaTexture ) ; // 释放纹理资源
95+ }
96+ }
97+ }
98+
99+ public void Clear ( )
100+ {
101+ foreach ( var item in lruList )
102+ {
103+ item . texture . Dispose ( ) ;
104+ if ( item . texture . nativeTexture . IsNotNull ( ) )
105+ {
106+ Object . Destroy ( item . texture . nativeTexture ) ; // 释放纹理资源
107+ }
108+
109+ if ( item . texture . alphaTexture . IsNotNull ( ) )
110+ {
111+ Object . Destroy ( item . texture . alphaTexture ) ; // 释放纹理资源
112+ }
113+ }
114+
115+ cache . Clear ( ) ;
116+ lruList . Clear ( ) ;
117+ }
118+ }
119+
120+
121+ public sealed class FairyGuiExtensionLoader : GLoader
122+ {
123+ private static LRUCache cache = new LRUCache ( 100 ) ;
124+ private static string _cachePath ;
125+
126+ public FairyGuiExtensionLoader ( )
127+ {
128+ _cachePath = PathHelper . AppHotfixResPath + "/cache/images/" ;
129+ if ( ! Directory . Exists ( _cachePath ) )
130+ {
131+ Directory . CreateDirectory ( _cachePath ) ;
132+ }
133+ }
134+
135+ protected override async void LoadExternal ( )
136+ {
137+ if ( url . IsNullOrWhiteSpace ( ) )
138+ {
139+ onExternalLoadFailed ( ) ;
140+ return ;
141+ }
142+
143+ NTexture tempTexture = null ;
144+ if ( url . StartsWithFast ( "http://" ) || url . StartsWithFast ( "https://" ) )
145+ {
146+ // 网络资源
147+ var nTexture = cache . Get ( url ) ;
148+ if ( nTexture . IsNull ( ) )
149+ {
150+ var hash = Utility . Hash . MD5 . Hash ( url ) ;
151+
152+ var path = $ "{ _cachePath } { hash } .png";
153+ var isExists = FileHelper . IsExists ( path ) ;
154+ var texture2D = Texture2D . whiteTexture ;
155+ if ( isExists )
156+ {
157+ var buffer = FileHelper . ReadAllBytes ( path ) ;
158+ texture2D . LoadImage ( buffer ) ;
159+ }
160+ else
161+ {
162+ var webBufferResult = await GameApp . Web . GetToBytes ( url , null ) ;
163+ FileHelper . WriteAllBytes ( path , webBufferResult . Result ) ;
164+ texture2D . LoadImage ( webBufferResult . Result ) ;
165+ }
166+
167+ tempTexture = new NTexture ( texture2D ) ;
168+ cache . Put ( url , tempTexture ) ;
169+ }
170+ else
171+ {
172+ tempTexture = nTexture ;
173+ }
174+ }
175+ else if ( url . StartsWithFast ( "ui://" ) )
176+ {
177+ // 包内资源
178+ LoadContent ( ) ;
179+ }
180+ else
181+ {
182+ var nTexture = cache . Get ( url ) ;
183+ if ( nTexture . IsNotNull ( ) )
184+ {
185+ tempTexture = nTexture ;
186+ }
187+ else
188+ {
189+ var assetInfo = GameApp . Asset . GetAssetInfo ( url ) ;
190+ if ( assetInfo . IsInvalid == false )
191+ {
192+ var assetHandle = await GameApp . Asset . LoadAssetAsync < Texture2D > ( url ) ;
193+ if ( assetHandle . IsSucceed )
194+ {
195+ tempTexture = new NTexture ( assetHandle . GetAssetObject < Texture2D > ( ) ) ;
196+ cache . Put ( url , tempTexture ) ;
197+ }
198+ }
199+ else
200+ {
201+ if ( FileHelper . IsExists ( url ) )
202+ {
203+ var buffer = FileHelper . ReadAllBytes ( url ) ;
204+
205+ var texture2D = new Texture2D ( Screen . width , Screen . height , TextureFormat . ARGB32 , false , false ) ;
206+ texture2D . LoadImage ( buffer ) ;
207+ tempTexture = new NTexture ( texture2D ) ;
208+ cache . Put ( url , tempTexture ) ;
209+ }
210+ }
211+ }
212+ }
213+
214+ if ( tempTexture . IsNotNull ( ) )
215+ {
216+ onExternalLoadSuccess ( tempTexture ) ;
217+ }
218+ else
219+ {
220+ onExternalLoadFailed ( ) ;
221+ }
222+ }
223+ }
224+ }
0 commit comments