fix: image_canvas correctness + RAM fixes (v1.3.0)
- _imageSize reset on image switch (wrong swap threshold on nav) - downsample caps both dims (tall images no longer full-res decode) - single _readImageSize instead of leaking loadingBuilder listeners - evict old downsample provider on full-res swap - remove imageCache.clear() that dropped live image (gallery flash)
This commit is contained in:
@@ -69,8 +69,6 @@ class ImageManager extends ChangeNotifier {
|
||||
}
|
||||
if (_currentIndex < 0) _currentIndex = 0;
|
||||
|
||||
// Eski klasörün decode edilmiş görsellerini bırak; canlı görsel korunur
|
||||
PaintingBinding.instance.imageCache.clear();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
||||
@@ -156,6 +156,7 @@ class _ImageCanvasState extends State<ImageCanvas> {
|
||||
void dispose() {
|
||||
HardwareKeyboard.instance.removeHandler(_onHardwareKey);
|
||||
_renderTick.dispose();
|
||||
_imageSizeStream?.removeListener(_imageSizeListener!);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -200,6 +201,7 @@ class _ImageCanvasState extends State<ImageCanvas> {
|
||||
_tx = 0; _ty = 0; _sc = 1.0; _isFilled = true;
|
||||
_angle = 0; _contrast = 1.0; _saturation = 1.0; _brightness = 0;
|
||||
_usingFullRes = false;
|
||||
_imageSize = Size.zero;
|
||||
_markRenderDirty();
|
||||
}
|
||||
|
||||
@@ -387,12 +389,27 @@ class _ImageCanvasState extends State<ImageCanvas> {
|
||||
ImageProvider<Object>? _cachedProvider;
|
||||
String? _cachedPath;
|
||||
int? _cachedCacheWidth;
|
||||
ImageStream? _imageSizeStream;
|
||||
ImageStreamListener? _imageSizeListener;
|
||||
|
||||
/// Provider değiştiğinde boyutu tek sefer oku (listener sızıntısı olmadan).
|
||||
void _readImageSize(ImageProvider<Object> provider) {
|
||||
_imageSizeStream?.removeListener(_imageSizeListener!);
|
||||
_imageSizeStream = provider.resolve(const ImageConfiguration());
|
||||
_imageSizeListener = ImageStreamListener((info, _) {
|
||||
if (mounted) {
|
||||
_imageSize = Size(info.image.width.toDouble(), info.image.height.toDouble());
|
||||
}
|
||||
});
|
||||
_imageSizeStream!.addListener(_imageSizeListener!);
|
||||
}
|
||||
|
||||
void _checkFullResSwap() {
|
||||
if (_usingFullRes || _imageSize.isEmpty) return;
|
||||
final double stretchX = _vpSize.width * _sc / _imageSize.width;
|
||||
final double stretchY = _vpSize.height * _sc / _imageSize.height;
|
||||
if (math.max(stretchX, stretchY) >= 1.5) {
|
||||
_cachedProvider?.evict(); // eski downsample provider'ı cache'ten at
|
||||
setState(() {
|
||||
_usingFullRes = true;
|
||||
_cachedProvider = _cachedFileImage;
|
||||
@@ -409,8 +426,9 @@ class _ImageCanvasState extends State<ImageCanvas> {
|
||||
_cachedCacheWidth = effectiveWidth;
|
||||
_cachedFileImage = FileImage(File(widget.filePath));
|
||||
_cachedProvider = effectiveWidth != null
|
||||
? ResizeImage.resizeIfNeeded(effectiveWidth, null, _cachedFileImage!)
|
||||
? ResizeImage.resizeIfNeeded(effectiveWidth, effectiveWidth, _cachedFileImage!)
|
||||
: _cachedFileImage;
|
||||
_readImageSize(_cachedProvider!);
|
||||
}
|
||||
final ImageProvider<Object> provider = _cachedProvider ?? _cachedFileImage!;
|
||||
return Image(
|
||||
@@ -420,18 +438,6 @@ class _ImageCanvasState extends State<ImageCanvas> {
|
||||
filterQuality: FilterQuality.high,
|
||||
gaplessPlayback: true,
|
||||
errorBuilder: (_, _, _) => _errorWidget(),
|
||||
loadingBuilder: (context, child, loadingProgress) {
|
||||
if (loadingProgress == null && _imageSize.isEmpty) {
|
||||
provider.resolve(const ImageConfiguration()).addListener(
|
||||
ImageStreamListener((info, _) {
|
||||
if (_imageSize.isEmpty) {
|
||||
_imageSize = Size(info.image.width.toDouble(), info.image.height.toDouble());
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
return child;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user