Files
imajviewer/docs/rapor-aspect-ratio-ram.md
Alhan 1002156cd4
Some checks failed
Build & Release / build-linux (push) Has been cancelled
Build & Release / build-windows (push) Has been cancelled
Build & Release / create-release (push) Has been cancelled
fix: aspect ratio distortion on image switch (v1.3.1)
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.
2026-08-14 22:17:48 +03:00

136 lines
4.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`:
```dart
ResizeImage.resizeIfNeeded(effectiveWidth, effectiveWidth, _cachedFileImage!)
```
Bu çağrı Flutter SDK içinde şuna dönüşür (`image_provider.dart:1302-1310`):
```dart
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`):
```dart
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`:
```dart
_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: `resizeIfNeeded` helper'ı `policy` parametresi **almaz**, bu yüzden `fit`
> için `ResizeImage`'ı 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ü?
1. Kullanıcı zoom yaptı`_checkFullResSwap` threshold 1.5 aşıldı → tam çözünürlük
decode edildi → RAM'e ~100MB+ girdi.
2. Full-res resim **canlı** olduğu için `maximumSizeBytes = 256MB` sı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 = 256MB` canlı resmi korur, sınır aşılır.
- **Çok sayıda full-res swap:** her resme zoom yapıp geçince, `didUpdateWidget`
eski provider'ı `evict` eder 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
1. **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.
2. **`_checkFullResSwap` stretch 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.
3. **`maximumSizeBytes`'ı düşür** (256MB → 128MB) → LRU daha agresif. Ama canlı
resmi yine korur, etkisi sınırlı.
4. **Full-res swap'ı dispose etme zamanlamasını netleştir** — şu an `didUpdateWidget`
evict 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ı
1. **Aspect bug fix**`ResizeImagePolicy.fit` (tek satır, kritik görsel düzeltme)
2. **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)