Repository navigation
Expand file tree
/
Copy pathbitmap-cache.mjs
More file actions
88 lines (75 loc) · 2.16 KB
/
Copy pathbitmap-cache.mjs
File metadata and controls
88 lines (75 loc) · 2.16 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
const DEFAULT_MAX_BYTES = 128 * 1024 * 1024;
const DEFAULT_MAX_ENTRIES = 128;
function bitmapBytes(canvas) {
return canvas.width * canvas.height * 4;
}
function zeroCanvas(canvas) {
canvas.width = 0;
canvas.height = 0;
}
export class BitmapCache {
#entries = new Map();
#bytes = 0;
#maxBytes;
#maxEntries;
constructor({ maxBytes = DEFAULT_MAX_BYTES, maxEntries = DEFAULT_MAX_ENTRIES } = {}) {
this.#maxBytes = maxBytes;
this.#maxEntries = maxEntries;
}
get(key) {
const entry = this.#entries.get(key);
if (!entry) return undefined;
this.#entries.delete(key);
this.#entries.set(key, entry);
return entry.canvas;
}
set(key, canvas) {
const bytes = bitmapBytes(canvas);
if (bytes > this.#maxBytes || this.#maxEntries <= 0) return false;
const existing = this.#entries.get(key);
if (existing) {
this.#entries.delete(key);
this.#bytes -= existing.bytes;
if (existing.canvas !== canvas) this.#release(existing.canvas);
}
while (this.#entries.size >= this.#maxEntries || this.#bytes + bytes > this.#maxBytes) {
const [oldestKey, oldest] = this.#entries.entries().next().value;
this.#entries.delete(oldestKey);
this.#bytes -= oldest.bytes;
this.#release(oldest.canvas, canvas);
}
this.#entries.set(key, { canvas, bytes });
this.#bytes += bytes;
return true;
}
delete(key) {
const entry = this.#entries.get(key);
if (!entry) return false;
this.#entries.delete(key);
this.#bytes -= entry.bytes;
this.#release(entry.canvas);
return true;
}
clear() {
const canvases = new Set();
for (const entry of this.#entries.values()) canvases.add(entry.canvas);
this.#entries.clear();
this.#bytes = 0;
for (const canvas of canvases) zeroCanvas(canvas);
}
stats() {
return {
bytes: this.#bytes,
entries: this.#entries.size,
maxBytes: this.#maxBytes,
maxEntries: this.#maxEntries,
};
}
#release(canvas, protectedCanvas) {
if (canvas === protectedCanvas) return;
for (const entry of this.#entries.values()) {
if (entry.canvas === canvas) return;
}
zeroCanvas(canvas);
}
}