feat: downsampling + full-res swap + RAM leak fix
- Büyük resimler ilk yüklemede max 2048px'e downsampling (RAM ~1.6MB) - Zoom 1.5x stretch'i geçince full-res'e swap (orijinal çözünürlük) - Resim geçişinde eski provider evict + ImageCache.evict() (RAM leak) - Tiled render kodu tamamen kaldırıldı
This commit is contained in:
@@ -39,6 +39,11 @@ class _ImageCanvasState extends State<ImageCanvas> {
|
||||
bool _ctrlPressed = false;
|
||||
bool _shiftPressed = false;
|
||||
|
||||
// ── Downsampling + swap ──
|
||||
bool _usingFullRes = false;
|
||||
Size _imageSize = Size.zero;
|
||||
static const int _downsampleTarget = 2048;
|
||||
|
||||
// ── Kare hızına eşitlenmiş render tick ──
|
||||
final ValueNotifier<int> _renderTick = ValueNotifier(0);
|
||||
bool _frameScheduled = false;
|
||||
@@ -179,12 +184,22 @@ class _ImageCanvasState extends State<ImageCanvas> {
|
||||
@override
|
||||
void didUpdateWidget(ImageCanvas old) {
|
||||
super.didUpdateWidget(old);
|
||||
if (old.filePath != widget.filePath) _resetView();
|
||||
if (old.filePath != widget.filePath) {
|
||||
_cachedFileImage?.evict();
|
||||
_cachedProvider?.evict();
|
||||
_cachedFileImage = null;
|
||||
_cachedProvider = null;
|
||||
_cachedPath = null;
|
||||
_cachedCacheWidth = null;
|
||||
PaintingBinding.instance.imageCache.evict(FileImage(File(old.filePath)));
|
||||
_resetView();
|
||||
}
|
||||
}
|
||||
|
||||
void _resetView() {
|
||||
_tx = 0; _ty = 0; _sc = 1.0; _isFilled = true;
|
||||
_angle = 0; _contrast = 1.0; _saturation = 1.0; _brightness = 0;
|
||||
_usingFullRes = false;
|
||||
_markRenderDirty();
|
||||
}
|
||||
|
||||
@@ -216,6 +231,7 @@ class _ImageCanvasState extends State<ImageCanvas> {
|
||||
_ty -= dh / 2;
|
||||
}
|
||||
_sc = newSc;
|
||||
_checkFullResSwap();
|
||||
_clamp();
|
||||
_markRenderDirty();
|
||||
}
|
||||
@@ -372,16 +388,29 @@ class _ImageCanvasState extends State<ImageCanvas> {
|
||||
String? _cachedPath;
|
||||
int? _cachedCacheWidth;
|
||||
|
||||
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) {
|
||||
setState(() {
|
||||
_usingFullRes = true;
|
||||
_cachedProvider = _cachedFileImage;
|
||||
_cachedCacheWidth = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildImage() {
|
||||
final longest = _vpSize.longestSide;
|
||||
final cacheWidth = longest > 0 ? (longest * _sc * 2).round() : null;
|
||||
if (_cachedPath != widget.filePath || _cachedCacheWidth != cacheWidth) {
|
||||
final needFullRes = _usingFullRes;
|
||||
final effectiveWidth = needFullRes ? null : _downsampleTarget;
|
||||
if (_cachedPath != widget.filePath || _cachedCacheWidth != effectiveWidth) {
|
||||
_cachedPath = widget.filePath;
|
||||
_cachedCacheWidth = cacheWidth;
|
||||
_cachedCacheWidth = effectiveWidth;
|
||||
_cachedFileImage = FileImage(File(widget.filePath));
|
||||
_cachedProvider = cacheWidth != null
|
||||
? ResizeImage.resizeIfNeeded(cacheWidth, null, _cachedFileImage!)
|
||||
: null;
|
||||
_cachedProvider = effectiveWidth != null
|
||||
? ResizeImage.resizeIfNeeded(effectiveWidth, null, _cachedFileImage!)
|
||||
: _cachedFileImage;
|
||||
}
|
||||
final ImageProvider<Object> provider = _cachedProvider ?? _cachedFileImage!;
|
||||
return Image(
|
||||
@@ -391,6 +420,18 @@ 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