imageFor method
Returns an exact cached image or schedules bounded work and returns null. Oversized/busy requests fall back to vectors without allocating a queue.
Implementation
ui.Image? imageFor({
required Object key,
required Object revision,
required int width,
required int height,
required void Function(ui.Canvas) paint,
}) {
if (_disposed) throw StateError('Raster cache is disposed');
if (width <= 0 || height <= 0) {
throw ArgumentError('Raster dimensions must be positive');
}
final old = _entries[key];
if (old != null &&
old.revision == revision &&
old.width == width &&
old.height == height) {
// Touch in place: removing/reinserting a compact hash-map entry on every
// hit creates tombstones and repeated backing-store compaction.
old.lastUsed = ++_accessClock;
if (_diagnostics && old.image != null) _hits++;
return old.image;
}
if (_diagnostics) _misses++;
if (old != null) {
_entries.remove(key);
_release(old);
}
final bytes = width * height * 4;
if (width > maximumSide ||
height > maximumSide ||
bytes > maximumBytes ||
_pendingCount >= maximumConcurrent) {
return null;
}
while (_residentBytes + _pendingBytes + bytes > maximumBytes ||
_entries.length >= maximumEntries) {
Object? victim;
var oldest = _accessClock + 1;
for (final entry in _entries.entries) {
if (!entry.value.pending && entry.value.lastUsed < oldest) {
victim = entry.key;
oldest = entry.value.lastUsed;
}
}
if (victim == null) return null;
_release(_entries.remove(victim)!);
if (_diagnostics) _evictions++;
}
final recorder = ui.PictureRecorder();
late final ui.Picture picture;
try {
paint(ui.Canvas(recorder));
picture = recorder.endRecording();
} catch (_) {
if (recorder.isRecording) recorder.endRecording().dispose();
rethrow;
}
final entry = _RasterEntry(revision, width, height)
..lastUsed = ++_accessClock;
if (_diagnostics) _admissions++;
_entries[key] = entry;
_pendingCount++;
_pendingBytes += bytes;
_finish(key, entry, picture);
return null;
}