import 'dart:math'; import 'package:aves/image_providers/full_image_provider.dart'; import 'package:aves/image_providers/region_provider.dart'; import 'package:aves/image_providers/thumbnail_provider.dart'; import 'package:aves/model/entry/cache.dart'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/utils/math_utils.dart'; import 'package:collection/collection.dart'; import 'package:flutter/painting.dart'; import 'package:aves/remote/remote_http.dart'; extension ExtraAvesEntryImages on AvesEntry { // === Helper per remoti === bool get isRemote => origin == 1; bool isThumbnailReady({double extent = 0}) => _isReady(_getThumbnailProviderKey(extent)); ThumbnailProvider getThumbnail({double extent = 0}) { return ThumbnailProvider(_getThumbnailProviderKey(extent)); } ThumbnailProviderKey _getThumbnailProviderKey(double extent) { final requestExtent = extent.roundToDouble(); EntryCache.markThumbnailExtent(requestExtent); return ThumbnailProviderKey( uri: uri, mimeType: mimeType, pageId: pageId, rotationDegrees: rotationDegrees, isFlipped: isFlipped, dateModifiedMillis: dateModifiedMillis ?? -1, extent: requestExtent, ); } RegionProvider getRegion({int sampleSize = 1, double scale = 1, required Rectangle region}) { if (isRemote) { // Non supportiamo il tiling di region su immagini remote (non c’è file locale). throw UnsupportedError("Region tiling not supported for remote images"); } return RegionProvider( RegionProviderKey( uri: uri, mimeType: mimeType, pageId: pageId, sizeBytes: sizeBytes, rotationDegrees: rotationDegrees, isFlipped: isFlipped, sampleSize: sampleSize, regionRect: Rectangle( (region.left * scale).round(), (region.top * scale).round(), (region.width * scale).round(), (region.height * scale).round(), ), imageSize: Size((width * scale).toDouble(), (height * scale).toDouble()), ), ); } Rectangle get fullImageRegion => Rectangle(.0, .0, width.toDouble(), height.toDouble()); /// Provider dell’immagine completa. /// - Remoto → `NetworkImage` con header Bearer “sincroni” (se già in cache via RemoteHttp.headers()). /// - Locale → `FullImage` (pipeline classica Aves). ImageProvider get fullImage { if (isRemote) { final abs = RemoteHttp.absUrl(remotePath ?? path); // Se la cache non è ancora popolata, può essere null: chi usa il ramo viewer remoto fa comunque await degli headers. final hdrs = RemoteHttp.peekHeaders(); return NetworkImage(abs, headers: hdrs); } return FullImage( uri: uri, mimeType: mimeType, pageId: pageId, rotationDegrees: rotationDegrees, isFlipped: isFlipped, isAnimated: isAnimated, sizeBytes: sizeBytes, ); } bool _isReady(Object providerKey) => imageCache.statusForKey(providerKey).keepAlive; List get cachedThumbnails => EntryCache.thumbnailRequestExtents.map(_getThumbnailProviderKey).where(_isReady).map(ThumbnailProvider.new).toList(); ThumbnailProvider get bestCachedThumbnail { final sizedThumbnailKey = EntryCache.thumbnailRequestExtents.map(_getThumbnailProviderKey).firstWhereOrNull(_isReady); return sizedThumbnailKey != null ? ThumbnailProvider(sizedThumbnailKey) : getThumbnail(); } static int sampleSizeForScale({ required double magnifierScale, required double devicePixelRatio, }) { var sample = 0; if (0 < magnifierScale && magnifierScale < 1) { sample = highestPowerOf2(1 / (magnifierScale * devicePixelRatio)); } return max(1, sample); } }