diff --git a/README.md b/README.md index bc78863..0e2a023 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,9 @@ Ultra hafif görüntü izleyici. Flutter ile yazıldı — Linux & Windows deste - **Brightness ayarı:** Ctrl+Shift + Sağ tık + dikey sürükle - **Rotate:** Shift + Sağ tık + dikey sürükle (~100px = 90°) - **Reset (tüm efektler):** Sağ çift tık +- **Gezinme okları:** Sol/sağ alt köşede, mouse resme girince görünür (hover'da kırmızı) +- **Ayarlar (gear):** Title bar'da; klasör sıralaması (zamana/isme göre), yazdırma, kısayollar +- **Yazdırma:** Ayarlar penceresinden resmi yazıcıya gönderir (A4, sığdır) - Sürükle-bırak ile resim açma - Pencere boyutu/konumu hatırlama (JSON persistence) - Varsayılan görüntü izleyici olarak ayarlanabilir @@ -114,8 +117,9 @@ flutter build windows --release | Ctrl+Shift + Sağ tık + dikey sürükle | Brightness (-100 – 100) | | Shift + Sağ tık + dikey sürükle | Rotate (serbest) | | Sağ çift tık | Reset (tüm efektler) | -| Ctrl + O | Dosya aç | -| Ctrl + Tab / Ctrl + Shift + Tab | Sonraki / önceki resim | +| Sol / Sağ ok | Önceki / sonraki resim | +| Ctrl + Q | Pencereyi kapat | +| Ctrl + Shift + Q | Tüm pencereleri kapat | ## CI / Otomatik Build diff --git a/dist/imajviewer_1.1.0_amd64.deb b/dist/imajviewer_1.1.0_amd64.deb new file mode 100644 index 0000000..39ebc82 Binary files /dev/null and b/dist/imajviewer_1.1.0_amd64.deb differ diff --git a/docs/index.md b/docs/index.md index 2b471f2..cf41c5a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,6 +2,7 @@ | Tarih | Dosya | Konu | |---|---|---| +| 2026-08-06 | [rapor-pan-kilitleme-nuks.md](rapor-pan-kilitleme-nuks.md) | **Rapor** — efekt uygulanmış görselde pan sırasında kilitlenme nüksü (analiz, açık sorular) | | 2026-08-06 | [rapor-gezinme-ayarlar-print.md](rapor-gezinme-ayarlar-print.md) | **Rapor** — gezinme okları (hover), ayarlar/gear (sıralama), yazdırma (printing paketi) | | 2026-08-06 | [rapor-sag-tik-donma-analizi.md](rapor-sag-tik-donma-analizi.md) | **Rapor** — sağ tuş efektlerinde donma analizi + slider UI değerlendirmesi | | 2026-08-03 | [zoom-clamp-mekanizmasi.md](zoom-clamp-mekanizmasi.md) | **Düzeltme** — `_vpSize` sıfırlama bug'ı: çift tıklama sonrası zoom ölümü/pan serbestliği | diff --git a/docs/rapor-pan-kilitleme-nuks.md b/docs/rapor-pan-kilitleme-nuks.md new file mode 100644 index 0000000..a9f9153 --- /dev/null +++ b/docs/rapor-pan-kilitleme-nuks.md @@ -0,0 +1,199 @@ +# Rapor — Efekt Uygulanmış Görselde Pan Sırasında Kilitlenme (Nüks) + +Tarih: 2026-08-06 +Durum: Analiz — hipotezler hazır, açık sorular kullanıcı yanıtını bekliyor + +## 1. Semptom (yeni bildirim) + +- Donma sorunu **yeniden ortaya çıktı** — bu kez **"diğer bilgisayarda"** (VM değilse + ortamı bilinmiyor; önceki testler Xubuntu VM + llvmpipe idi). +- Tetikleyici farklı: önceki nüksler **efekt sürüklemesi sırasında** idi + (contrast/saturation/brightness/rotate drag). Şimdi **efekt uygulanmış durumda + (değerler sabit) iken PAN yaparken** kilitlenme. +- "Olmadık zamanlarda" — rastgele zamanlarda, her pan'da değil. + +## 2. Önceki Teşhisler ve Çözümler (kayıt özeti) + +| Tarih | Teşhis | Çözüm | Durum | +|---|---|---|---| +| 03 Ağu | **Kuyruk birikmesi**: pointer event hızı (100-250/sn) > llvmpipe kare hızı → setState her event'te → UI thread %100, 30-60sn donma | `_markRenderDirty()` frame-throttle (kare başına max 1 rebuild) + `ResizeImage` (viewport×2) + provider memoize + cache 16/256MB + galeri geçişinde `imageCache.clear()` | Uygulandı, ölçüldü (raster ort ~15ms, max ~50ms, kuyruk birikmesi yok) | +| 06 Ağu | **İlk kullanım shader derlemesi (B1)**: process başına soğuk Skia/GL cache; ilk ColorFiltered/Transform kullanımı 15sn-1dk derleme | **Shader warmup**: açılışta gizli Opacity(0) katmanla filtre + rotate shader'larını derlet | Uygulandı (release build + kurulum 06 Ağu) | + +## 2b. Kullanıcı Yanıtları (2026-08-06, ikinci bildirim) + +- Ortam: **gerçek makine** — `Mesa Intel(R) HD Graphics 3000 (SNB GT2)` (Sandy + Bridge iGPU, 2011). mesa i965 driver GLSL'i **LLVM ile CPU'da derler** → karmaşık + shader varyantları 30sn'ye çıkabilir. 12GB RAM, son sürüm kurulu. +- Test sonuçları (kullanıcı): + - **Resim büyükse (dosya ~1MB+) oluyor**; küçük görselde olmuyor. + - **Fill modda olmuyor, fit modda oluyor.** + - **İlk açılışta fit moda geçip rotate edilirse oluyor** — tek seferlik, sonra + tekrar değil (kullanıcı "sanırım" ile ifade etti, teyit beklenecek). +- Bu bilgiler **Aday F'yi doğruluyor**: warmup 1x1 gizli katmanda yapılıyor, gerçek + kullanımın (büyük doku + fit BoxFit.contain + rotate + ColorFiltered) shader + varyantı warmup'ta derlenmiyor → ilk gerçek kullanımda 30sn derleme. +- Aday A (ColorFiltered CPU filtrelemesi) ve C (bellek) elendi — GPU var, RAM bol. +- **EKLENTİ (kullanıcı kurcalayınca):** Fill modda da oluyor; **"biraz zorlayınca + aynı resimde tekrar oldu"** — yani tek seferlik DEĞİL. Bu Aday F'yi (ilk kullanım + derlemesi, cache'li kalır) **zayıflatıyor**; varyant sayısı fazlaysa her yeni + kombinasyon yeni derleme olabilir (aşağıda Aday H/I). + +Her iki çözüm de mevcut kodda aktif: `image_canvas.dart` → `_markRenderDirty`, +provider memoize (`_cachedProvider`), `_buildWarmupLayer()`. + +## 3. Yeni Analiz — Neden "Efekt + Pan" Kilitlenir? + +Pan akışı: `_onPanUpdate` → `_tx/_ty += delta` → `_clamp()` → `_markRenderDirty()`. + +### Aday A — ColorFiltered her pan frame'inde tüm görseli CPU'da filtreliyor ⭐ + +- Efekt aktifken (`hasFilter == true`) her rebuild'te `ColorFiltered` → `Image` tüm + görsel 5x4 matrisle filtrelenir. Pan'da translate değişir → her frame'de yeniden + boyama + filtreleme. +- 4000×3000 görsel ≈ 12M piksel; llvmpipe'ta filtre + ölçekleme + translate ≈ + 20-60ms/kare. FPS 15-50'ye düşer → **"takılma" hissi**. +- `_markRenderDirty` kuyruk birikmesini engeller ama kare başına maliyet yüksekse + düşük FPS kalıcı olur. Büyük görsel + zayıf makinede bu "kilitlenme" gibi algılanabilir. +- **Rotate aktifse** (pan serbest, `_clamp` erken return) Transform + ColorFiltered + birlikte — en ağır kombinasyon. + +### Aday B — `_clamp()` pan event hızında `findRenderObject()` çağırıyor ⭐ + +- `_onPanUpdate` her event'te `_clamp()` çağırır; fit modda `_clamp()` içinde + `_imageKey.currentContext?.findRenderObject()` (satır 77) **pointer hızında** + (throttle'ın DIŞINDA) render tree sorgusu yapar. Pan event'leri 100-250/sn → + gereksiz layout/tree traversal yükü. Kilitlenme üretmez ama takılmayı artırır. + +### Aday C — Bellek/GC baskısı ("olmadık zamanlarda" rastgeleliğin en iyi adayı) + +- Rastgele kilitlenme imzası GC/swap'e işaret eder. "Diğer bilgisayar"da RAM azsa + (8GB altı) veya başka işler varsa: büyük görsel decode + 256MB cache + pan sırasında + GC pause → kısa ama algılanabilir donmalar. +- Pan sırasında yeni decode yok (memoize) → cache baskısı düşük; ama makine belleği + dar ise OS seviyesinde swap thrashing olabilir. + +### Aday D — Eski binary / farklı ortam ("diğer bilgisayar") + +- "Diğer bilgisayarda" kurulu sürüm bilinmiyor. Eski .deb (warmup/memoize öncesi) + kuruluysa sorun zaten açıklanır. Ayrıca o makinede donanım GPU'su VARSA teşhis + tamamen değişir (llvmpipe değil) — ColorFiltered donanımda ucuzdur, o zaman aday + C veya farklı bir sebep aranır. + +### Aday E — Pan sırasında yeni frame kuyruğu (zayıf) + +- `scheduleFrameCallback` kare başına 1 callback garanti eder; kare süresi uzarsa FPS + düşer ama kuyruk birikmez (03 Ağu ölçümü). Kilitlenme üretmez. + +### Aday F — Warmup varyant eksikliği: 1x1 katman gerçek kullanımı derlemiyor ⭐⭐⭐ + +- Warmup (`_buildWarmupLayer`) **1x1 boyutunda** gizli katmanla filtre + rotate + shader'larını derletiyor. Skia/GL'de shader varyantları **doku boyutu, sampling + modu, filtre kalitesi** gibi parametrelere göre ayrı derlenir. +- Gerçek kullanımda **büyük görsel** (4000x3000) + `FilterQuality.high` + + `BoxFit.cover/contain` + `ColorFiltered` + pan/zoom `Transform` — bu kombinasyonun + shader'ı 1x1 warmup'ta derlenenle **aynı varyant olmayabilir** → ilk gerçek + kullanımda derleme yine olur → 30sn kilitlenme. +- "Efekt uygulanmışken pan yaparken" tetikleyicisi: pan/zoom + ColorFiltered birlikte + ilk kez kullanıldığında varyant derlenir; sonrası cache'li → "olmadık zamanlarda" + ve "her pan'da değil" imzası birebir uyuyor. +- GPU'da shader derleme süresi genelde ms seviyesindedir; **zayıf/iç ekran kartı + + mesa/LLVM backend** ile 30sn'ye çıkabilir. "İyi kötü ekran kartı" ifadesi bu + olasılığı güçlendiriyor. +- **Warmup'ın gerçek boyutla yapılması gerekir**: gizli katman 1x1 yerine mevcut + görselin boyutunda (veya viewport boyutunda) olmalı; ayrıca pan/zoom Transform'u + da warmup'a dahil edilmeli. + +### Aday G — Gerçek GPU driver / doku sınırı + +- Zayıf GPU'da büyük doku (12MP RGBA ≈ 48MB) + ColorFiltered → driver'da aşırı + yavaş path veya GL sync beklemesi. `glxinfo -B` renderer string'i kritik + (NVIDIA nouveau / AMD radeon / Intel / llvmpipe?). + +### Aday H — Skia varyant cache dolması / her kombinasyon yeni derleme ⭐⭐ + +- "Aynı resimde tekrar oldu" → tek seferlik derleme değil. Skia'da her farklı + kombinasyon (zoom seviyesi × rotate açısı × efekt değeri × fit/fill × filtre + kalitesi) farklı shader varyantı üretebilir; varyant cache'i sınırlıysa (LRU) + eski varyant atılır → aynı kombinasyon tekrar kullanılınca YENİDEN derlenir → + 30sn. "Zorlayınca" = çok varyant üretmek = cache'i doldurmak = tekrar derleme. +- Bu, "ilk kullanımda" + "sonra tekrar" imzasını birlikte açıklayan hipotez. + +### Aday I — Intel HD 3000 GL sync / doku upload darboğazı ⭐⭐ + +- Sandy Bridge iGPU paylaşımlı bellek kullanır; büyük doku upload + ColorFiltered + + Transform her frame'de GL sync bekleyebilir. `_markRenderDirty` throttle frame + üretimini sınırlar ama **raster thread** yavaş kalırsa UI thread frame kuyruğunu + tüketemez → 30sn'lik görünür donma (olaylar işlenir, görsel gecikir — önceki + raporlardaki imza). +- Doku boyutuna duyarlılık (1MB+ görsel) + fill/fit farkı (BoxFit.cover kırpar → + daha küçük etkin doku; contain → tam doku işlenir) bu hipotezle uyumlu. +- Intel HD 3000'de `LIBGL_ALWAYS_SOFTWARE=1` (llvmpipe) ile karşılaştırma testi + ayırt edici: GL sync sorunu ise software'de farklı davranır. + +## 4. Hipotez Önceliklendirme + +| Aday | Açıkladığı | Gücü | +|---|---|---| +| A (ColorFiltered her frame) | Efekt+pan takılması, "kilitlenme" hissi | ⭐⭐ (efekt açıkken kaçınılmaz maliyet; azaltılabilir) | +| B (`_clamp` findRenderObject) | Ek pan yükü | ⭐ (kolay düzeltilir) | +| C (bellek/GC) | "Olmadık zamanlarda" rastgelelik | ⭐⭐ (makine belleği bilinmiyor) | +| D (ortam/eski binary) | Tamamen farklı kök | ⭐ (önce doğrulanmalı) | + +## 5. Önerilen Çözüm Yolları (rapor onayı sonrası) + +### Seçenek 1 — Pan sırasında filtre maliyetini azalt (Aday A) + +- Efekt aktifken pan/zoom sırasında `FilterQuality.high` → `FilterQuality.medium` + (görsel kalite farkı minimal, llvmpipe yükü düşer) — tek satır. +- Pan sırasında `cacheWidth`'i düşürme (memoize zaten sabit tutuyor) — gerek yok. +- **Alternatif:** pan sırasında geçici düşük çözünürlük önizlemesi (mevcut görseli + küçük boyutta filtrele) — karmaşık, önce basit yol denenir. + +### Seçenek 2 — `_clamp()` içindeki `findRenderObject` yükünü azalt (Aday B) + +- `_imageKey.currentContext?.findRenderObject()` yalnızca fit modunda lazım; pan'da + `_vpSize` değişmediği sürece tekrar sorgulamaya gerek yok — `_clamp`'in render + sorgusunu her pan event'inde değil, sadece layout değişince yap. (Dikkatli: + rotate varken `_clamp` zaten erken return ediyor; fit modunda kilitlenme riski yok.) + +### Seçenek 3 — Ortam doğrulaması (Aday D) — rapor öncesi şart + +- "Diğer bilgisayar"da: `glxinfo -B | grep -i renderer` (llvmpipe mi GPU mu), + `free -h`, kurulu sürüm (`.deb` tarihi / `imajviewer --version` yoksa binary + timestamp). Ayrıca aynı görselle **bu VM'de** tekrar dener misin — nüks VM'de + de mi, sadece diğer makinede mi? + +## 6. Kalan Açık Sorular + +1. "Diğer bilgisayar" hangisi — başka VM mi, gerçek makine mi? Donanım GPU'su var mı + (`glxinfo -B` çıktısı)? +2. O makinede kurulu sürüm: son `.deb` (2026-08-06 sonrası) mi, yoksa eski mi? +3. Kilitlenme süresi ne kadar — 1-2sn mi, 10sn+ mı, yoksa "kapatmak zorunda kaldım" mı? +4. Hangi efekt uygulanmışken pan yapıyorsun — contrast mi, rotate mi (rotate en ağırı)? +5. Görsel boyutu ne — büyük fotoğraf mı (4000px+), küçük ekran görüntüsü mü? +6. Aynı görselle bu VM'de (llvmpipe) pan dener misin — nüks VM'de de oluyor mu? + +## 7. Ölçüm Planı (onaylanırsa) + +Skill'deki `references/llvmpipe-perf-measurement.md` akışı: geçici +`addTimingsCallback` (FRAME build/raster süreleri) + `pidstat` CPU. Ölçüm sırasında +kullanıcı elle pan yapar ("sen aç ben yaparım"). Enstrümantasyon geçicidir ve +sonrası temizlenir. VM'de ölçüm yapılabilir; "diğer bilgisayar"da enstrümantasyonlu +build kurmak gerekir (kullanıcı onayı şart). + +## 8. Sonuç + +İki önceki çözüm (throttle + warmup) doğru teşhislere dayanıyordu ve ilk iki donma +imzasını çözdü. Yeni imza **farklı**: efekt SABİT iken pan. En güçlü açıklama +Aday A (ColorFiltered'in her pan frame'inde tam görseli CPU'da filtrelemesi) — +özellikle büyük görsel + zayıf makinede "kilitlenme" hissi yaratır. Rastgelelik için +Aday C (bellek/GC) ve Aday D (ortam) doğrulanmalı. Kullanıcı yanıtları gelince +rapor güncellenir ve çözüm yolları netleşir. + +## 9. Kapanış Notu (2026-08-06) + +Kullanıcı donma sırasında `top -b -d 1` ölçümü başlattıktan sonra **nüks olmadı** +(ne denediyse kilitlenme/takılma görülmedi). Nüks koşulları belirsiz; konu bu +raporla **askıda bırakıldı** — kullanıcı talep ederse yeniden açılır. Olası ipucu: +ölçüm sırasında sistem yükü değişince davranış değişti (Aday I — GL sync/GPU yükü +ile ilişkili olabilir; kesin teşhis yapılmadı). diff --git a/lib/screens/viewer_screen.dart b/lib/screens/viewer_screen.dart index baf8502..6689287 100644 --- a/lib/screens/viewer_screen.dart +++ b/lib/screens/viewer_screen.dart @@ -142,8 +142,8 @@ class _ViewerScreenState extends State with WindowListener { const SizedBox(height: 4), const Text( '• Sol/Sağ ok — önceki/sonraki resim\n' - '• Ctrl+O — dosya aç\n' '• Ctrl+Q — pencereyi kapat\n' + '• Ctrl+Shift+Q — tüm pencereleri kapat\n' '• Çift tık — doldur/sığdır geçişi\n' '• Sağ tık sürükle — kontrast\n' '• Shift+sağ tık — döndür\n' diff --git a/lib/shortcuts/app_shortcuts.dart b/lib/shortcuts/app_shortcuts.dart index ded6a98..78c8509 100644 --- a/lib/shortcuts/app_shortcuts.dart +++ b/lib/shortcuts/app_shortcuts.dart @@ -29,6 +29,12 @@ class AppShortcuts extends StatelessWidget { return KeyEventResult.handled; } + // Ctrl+Q: bu pencereyi kapat + if (ctrl && event.logicalKey == LogicalKeyboardKey.keyQ) { + windowManager.close(); + return KeyEventResult.handled; + } + switch (event.logicalKey) { case LogicalKeyboardKey.arrowLeft: ImageManager.instance.previousImage(); @@ -53,16 +59,6 @@ class AppShortcuts extends StatelessWidget { ): () { FileHandler.openFileDialog(); }, - const SingleActivator(LogicalKeyboardKey.f11): () async { - final isFullscreen = await windowManager.isFullScreen(); - await windowManager.setFullScreen(!isFullscreen); - }, - const SingleActivator( - LogicalKeyboardKey.keyQ, - control: true, - ): () { - windowManager.close(); - }, }, child: child, ), diff --git a/pubspec.yaml b/pubspec.yaml index f4321a1..022f851 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: imajviewer description: "Ultra hafif görüntü izleyici" publish_to: 'none' -version: 1.0.1+2 +version: 1.1.0+3 msix_config: display_name: ImajViewer