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

4.8 KiB
Raw Permalink Blame History

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: 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 fixResizeImagePolicy.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)