Downsample used ResizeImagePolicy.exact which forced a 2048×2048 square and stretched non-square images. Switch to ResizeImagePolicy.fit to preserve aspect ratio while still capping both dimensions.
4.8 KiB
Analiz: Aspect Ratio Bozulması + RAM Büyümesi
Tarih: 2026-08-14 Sürüm: v1.3.0 sonrası regresyon
1. Aspect Ratio Bug'ı (kök neden bulundu)
Belirti
- Next/prev ile resim değişince görüntü en/boy oranı bozulmuş (gerilmiş/sıkışmış) geliyor.
- Fazla zoom yapınca aspect düzeliyor.
Kök Neden — ResizeImagePolicy.exact
lib/widgets/image_canvas.dart:429:
ResizeImage.resizeIfNeeded(effectiveWidth, effectiveWidth, _cachedFileImage!)
Bu çağrı Flutter SDK içinde şuna dönüşür (image_provider.dart:1302-1310):
static ImageProvider<Object> resizeIfNeeded(int? cacheWidth, int? cacheHeight, provider) {
if (cacheWidth != null || cacheHeight != null) {
return ResizeImage(provider, width: cacheWidth, height: cacheHeight);
}
return provider;
}
Ve ResizeImage constructor'ı (image_provider.dart:1262-1268):
const ResizeImage(this.imageProvider, { this.width, this.height,
this.policy = ResizeImagePolicy.exact, // ← VARSAYILAN exact
this.allowUpscaling = false,
});
ResizeImagePolicy.exact: genişlik ve yüksekliği bağımsız ölçekler, aspect ratio'yu
koruMAZ. Yani width: 2048, height: 2048 demek → her resmi 2048×2048 kareye
zorla. 4000×2000 bir resim 2048×2048'e gerilir (2:1 → 1:1), görüntü bozulur.
Zoom yapınca _checkFullResSwap() → _usingFullRes = true → _cachedProvider = _cachedFileImage (orijinal boyutlu FileImage, aspect doğru) → görüntü düzelir.
Bu, belirtiyle birebir örtüşüyor.
Düzeltme
resizeIfNeeded yerine doğrudan ResizeImage + policy: ResizeImagePolicy.fit:
_cachedProvider = effectiveWidth != null
? ResizeImage(
_cachedFileImage!,
width: effectiveWidth,
height: effectiveWidth,
policy: ResizeImagePolicy.fit,
)
: _cachedFileImage;
fit policy'si (SDK image_provider.dart:1378-1409) aspect ratio'yu koruyarak
2048×2048 kutusuna sığdırır:
- 4000×2000 → 2048×1024 (2:1 korunur)
- 800×20000 dikey → 82×2048 (HATA-2'nin istediği dikey sınırlama da korunur)
- 20000×800 panorama → 2048×82
Not:
resizeIfNeededhelper'ıpolicyparametresi almaz, bu yüzdenfitiçinResizeImage'ı doğrudan kurmak zorunlu.
2. RAM Büyümesi Analizi
Gözlem
RAM 200MB'a kadar büyüdü. "Daha büyür mü?" — Evet, aşağıdaki modele göre büyüyebilir.
RAM modeli (mevcut)
| Kaynak | Boyut |
|---|---|
| Downsample (fit 2048×2048, RGBA) | en fazla 2048×2048×4 = 16.7MB |
| Full-res swap (zoom ≥ 1.5x) | resme bağlı: 12000px → 576MB, 4000px → 64MB |
ImageCache.maximumSize |
16 entry |
ImageCache.maximumSizeBytes |
256MB |
Neden 200MB'a büyüdü?
- Kullanıcı zoom yaptı →
_checkFullResSwapthreshold 1.5 aşıldı → tam çözünürlük decode edildi → RAM'e ~100MB+ girdi. - Full-res resim canlı olduğu için
maximumSizeBytes = 256MBsınırı onu atamaz (Flutter aktif kullanımdaki image'ı evict etmez).
Daha büyür mü? — Evet, iki senaryo
- Daha büyük resim: 12000px panorama full-res'e geçince tek resim ~576MB.
maximumSizeBytes = 256MBcanlı resmi korur, sınır aşılır. - Çok sayıda full-res swap: her resme zoom yapıp geçince,
didUpdateWidgeteski provider'ıevicteder ama evict asenkron/gecikmeli olabilir; ardışık hızlı geçişte 2-3 full-res aynı anda RAM'de kalabilir.
Yapılacak iyileştirmeler
- Full-res swap'ı kademeli yap (şu an binary: 2048 → full). Orta seviye (ör. 4096px) ekleyerek RAM'i sınırla. Ama bu kullanıcı isteği değilse yapma.
_checkFullResSwapstretch eşiğini yükselt (1.5 → 2.0) → daha az full-res swap, daha az RAM. Trade-off: zoom'da biraz daha geç keskinleşme.maximumSizeBytes'ı düşür (256MB → 128MB) → LRU daha agresif. Ama canlı resmi yine korur, etkisi sınırlı.- Full-res swap'ı dispose etme zamanlamasını netleştir — şu an
didUpdateWidgetevict yapıyor;dispose()içine_cachedProvider?.evict()de eklenebilir.
Karar önerisi
Aspect bug'ı kesin düzeltilmeli (görsel regresyon, her resimde). RAM büyümesi
ise full-res swap'ın beklenen davranışı — 200MB makul, ama 576MB'lık dev
panoramalarda tek resimde patlayabilir. fit policy'ye geçmek downsample'ı da
düzelteceği için ortalama RAM'i de bir miktar düşürür (exact kare yerine doğru
orantılı küçük resim).
3. Öncelik Sırası
- Aspect bug fix —
ResizeImagePolicy.fit(tek satır, kritik görsel düzeltme) - RAM — şimdilik izle; 2. eşik/kademeli swap istersen ayrı yaparız.
4. Doğrulama
flutter analyze: 0 error- Release build: ✓
- Manuel test: kullanıcı (aspect düzelmesi + RAM gözlemi)