-
-
Notifications
You must be signed in to change notification settings - Fork 20
Expand file tree
/
Copy pathBitmapDataCache.hx
More file actions
240 lines (202 loc) · 6.51 KB
/
BitmapDataCache.hx
File metadata and controls
240 lines (202 loc) · 6.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
/*
Feathers UI
Copyright 2026 Bowler Hat LLC. All Rights Reserved.
This program is free software. You can redistribute and/or modify it in
accordance with the terms of the accompanying license agreement.
*/
package feathers.utils;
import openfl.display.BitmapData;
import openfl.errors.ArgumentError;
import openfl.errors.IllegalOperationError;
/**
Caches `BitmapData` in memory. Each `BitmapData` object may be saved with
its own key, such as the URL where the original image file is located.
@since 1.0.0
**/
class BitmapDataCache {
/**
Creates a new `BitmapDataCache` object with the given arguments.
@since 1.0.0
**/
public function new(maxUnretained:Int = 0x7FFFFFFF) {
this._maxUnretained = maxUnretained;
}
private var _unretainedKeys:Array<String> = [];
private var _unretainedBitmapData:Map<String, BitmapData> = [];
private var _retainedBitmapData:Map<String, BitmapData> = [];
private var _retainCounts:Map<String, Int> = [];
private var _maxUnretained:Int;
/**
Limits the number of unretained `BitmapData` objeccts that may be
stored in memory. The `BitmapData` objects retained least recently will
be disposed, if there are too many.
@since 1.0.0
**/
public var maxUnretained(get, set):Int;
private function get_maxUnretained():Int {
return this._maxUnretained;
}
private function set_maxUnretained(value:Int):Int {
if (this._maxUnretained == value) {
return this._maxUnretained;
}
this._maxUnretained = value;
if (this._unretainedKeys.length > value) {
this.trimCache();
}
return this._maxUnretained;
}
/**
Disposes the `BitmapData` cache, including all `BitmapData` objects
(even if they are retained, so be careful!).
@since 1.0.0
**/
public function dispose():Void {
for (bitmapData in this._unretainedBitmapData) {
bitmapData.dispose();
}
for (bitmapData in this._retainedBitmapData) {
bitmapData.dispose();
}
this._retainedBitmapData = null;
this._unretainedBitmapData = null;
this._retainCounts = null;
}
/**
Saves a `BitmapData` object, and associates it with a specific key.
@see `BitmapDataCache.removeBitmapData()`
@see `BitmapDataCache.hasBitmapData()`
@since 1.0.0
**/
public function addBitmapData(key:String, bitmapData:BitmapData, retain:Bool = true):Void {
if (this._retainedBitmapData == null) {
throw new IllegalOperationError("Cannot add BitmapData after the cache has been disposed.");
}
if (this._unretainedBitmapData.exists(key) || this._retainedBitmapData.exists(key)) {
throw new ArgumentError('Key "$key" already exists in the cache.');
}
if (retain) {
this._retainedBitmapData.set(key, bitmapData);
this._retainCounts.set(key, 1);
return;
}
this._unretainedBitmapData.set(key, bitmapData);
this._unretainedKeys[this._unretainedKeys.length] = key;
if (this._unretainedKeys.length > this._maxUnretained) {
this.trimCache();
}
}
/**
Removes a specific key from the cache, and optionally disposes the
`BitmapData` object associated with the key.
@see `BitmapDataCache.addBitmapData()`
@since 1.0.0
**/
public function removeBitmapData(key:String, dispose:Bool = false):Void {
if (this._unretainedBitmapData == null) {
return;
}
var bitmapData = this._unretainedBitmapData.get(key);
if (bitmapData != null) {
this.removeUnretainedKey(key);
} else {
bitmapData = this._retainedBitmapData.get(key);
this._retainedBitmapData.remove(key);
this._retainCounts.remove(key);
}
if (dispose && bitmapData != null) {
bitmapData.dispose();
}
}
/**
Indicates if a `BitmapData` object is associated with the specified key.
@since 1.0.0
**/
public function hasTexture(key:String):Bool {
return (this._retainedBitmapData != null && this._retainedBitmapData.exists(key))
|| (this._unretainedBitmapData != null && this._unretainedBitmapData.exists(key));
}
/**
Returns how many times the `BitmapData` object associated with the
specified key has currently been retained.
@since 1.0.0
**/
public function getRetainCount(key:String):Int {
if (this._retainCounts != null && this._retainCounts.exists(key)) {
return this._retainCounts.get(key);
}
return 0;
}
/**
Gets the `BitmapData` object associated with the specified key, and
increments the retain count for the `BitmapData` object. Always remember
to call `releaseBitmapData()` when finished with a retained `BitmapData`
object.
@see `BitmapDataCache.releaseBitmapData()`
@since 1.0.0
**/
public function retainBitmapData(key:String):BitmapData {
if (this._retainedBitmapData == null) {
throw new IllegalOperationError("Cannot retain BitmapData after the cache has been disposed.");
}
if (this._retainedBitmapData.exists(key)) {
var count = this._retainCounts.get(key);
count++;
this._retainCounts.set(key, count);
return this._retainedBitmapData.get(key);
}
if (!this._unretainedBitmapData.exists(key)) {
throw new ArgumentError('BitmapData with key "$key" cannot be retained because it has not been added to the cache.');
}
var bitmapData = this._unretainedBitmapData.get(key);
this.removeUnretainedKey(key);
this._retainedBitmapData.set(key, bitmapData);
this._retainCounts.set(key, 1);
return bitmapData;
}
/**
Releases a retained `BitmapData` object.
@see `BitmapDataCache.retainBitmapData()`
@since 1.0.0
**/
public function releaseBitmapData(key:String):Void {
if (this._retainedBitmapData == null || !this._retainedBitmapData.exists(key)) {
return;
}
var count = this._retainCounts.get(key);
count--;
if (count == 0) {
// get the existing bitmap data
var bitmapData = this._retainedBitmapData.get(key);
// remove from retained
this._retainCounts.remove(key);
this._retainedBitmapData.remove(key);
this._unretainedBitmapData.set(key, bitmapData);
this._unretainedKeys[this._unretainedKeys.length] = key;
if (this._unretainedKeys.length > this._maxUnretained) {
this.trimCache();
}
} else {
this._retainCounts.set(key, count);
}
}
private function removeUnretainedKey(key:String):Void {
var index = this._unretainedKeys.indexOf(key);
if (index == -1) {
return;
}
this._unretainedKeys.splice(index, 1);
this._unretainedBitmapData.remove(key);
}
private function trimCache():Void {
var currentCount = this._unretainedKeys.length;
var maxCount = this._maxUnretained;
while (currentCount > maxCount) {
var key:String = this._unretainedKeys.shift();
var bitmapData = this._unretainedBitmapData.get(key);
bitmapData.dispose();
this._unretainedBitmapData.remove(key);
currentCount--;
}
}
}