imageFor method

Image? imageFor({
  1. required Object key,
  2. required Object revision,
  3. required int width,
  4. required int height,
  5. required void paint(
    1. Canvas
    ),
})

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;
}