feat(contacts): animated avatars (GIF/WebP) with a Settings toggle

Animate contact avatars end to end:

- texture_loader gains LoadAnimatedRGBA: decodes an image into a downscaled
  RGBA frame sequence + per-frame durations — animated GIF via stb
  (stbi_load_gif_from_memory) and animated WebP via libwebp's WebPAnimDecoder;
  stills (and APNG, which stb reads as one image) return a single frame. Frames
  are box-downscaled (smaller cap for animations) to bound VRAM, capped at 300.
- The contacts avatar cache now holds a frame sequence; currentAvatarFrame()
  advances animated avatars by the ImGui clock and is used everywhere avatars
  draw (list, cards, table, grid, preview). When a live animated frame is drawn
  it flags the render loop (ConsumeContactsAvatarAnimation, clear-on-read) so
  main.cpp keeps producing frames while animation plays and idles when it stops
  or the contacts view is hidden.
- New animate_avatars setting (default on) + a Settings appearance toggle
  ("Animate avatars"); off shows the first frame only. currentAvatarFrame
  honors it. +i18n (8 langs, CJK subset rebuilt for 帧/播/첫).

Verified: a 3-frame GIF and a 3-frame animated WebP both decode to 3 frames
with correct 120ms delays through the exact libwebp/stb calls used here.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-15 11:11:06 -05:00
parent 923d086092
commit 4b8d80b2fc
18 changed files with 259 additions and 44 deletions

View File

@@ -54,6 +54,7 @@
"amount_details": "BETRAGSDETAILS",
"amount_exceeds_balance": "Betrag übersteigt Guthaben",
"amount_label": "Betrag:",
"animate_avatars": "Avatare animieren",
"appearance": "ERSCHEINUNGSBILD",
"auto_shield": "Mining automatisch abschirmen",
"av_intro": "Mining-Software wird oft als potenziell unerwünscht eingestuft. Führen Sie diese Schritte aus, um das Pool-Mining zu aktivieren:",
@@ -1329,6 +1330,7 @@
"try_again": "Erneut versuchen",
"tt_addr_url": "Basis-URL zum Anzeigen von Adressen in einem Block-Explorer",
"tt_address_book": "Gespeicherte Adressen für schnelles Senden verwalten",
"tt_animate_avatars": "Animierte Kontakt-Avatare (GIF / WebP) abspielen; aus zeigt nur das erste Bild",
"tt_auto_lock": "Wallet nach dieser Inaktivitätszeit sperren",
"tt_auto_shield": "Transparentes Guthaben automatisch an geschirmte Adressen für Datenschutz verschieben",
"tt_backup": "Eine Sicherungskopie Ihrer wallet.dat erstellen",

View File

@@ -54,6 +54,7 @@
"amount_details": "DETALLES DE CANTIDAD",
"amount_exceeds_balance": "La cantidad excede el saldo",
"amount_label": "Cantidad:",
"animate_avatars": "Animar avatares",
"appearance": "APARIENCIA",
"auto_shield": "Auto-proteger minería",
"av_intro": "El software de minería suele marcarse como potencialmente no deseado. Sigue estos pasos para habilitar la minería en pool:",
@@ -1329,6 +1330,7 @@
"try_again": "Reintentar",
"tt_addr_url": "URL base para ver direcciones en un explorador de bloques",
"tt_address_book": "Administrar direcciones guardadas para envío rápido",
"tt_animate_avatars": "Reproduce avatares de contacto animados (GIF / WebP); desactivado muestra solo el primer fotograma",
"tt_auto_lock": "Bloquear billetera después de este tiempo de inactividad",
"tt_auto_shield": "Mover automáticamente el saldo transparente a direcciones blindadas para privacidad",
"tt_backup": "Crear una copia de seguridad de su wallet.dat",

View File

@@ -54,6 +54,7 @@
"amount_details": "DÉTAILS DU MONTANT",
"amount_exceeds_balance": "Le montant dépasse le solde",
"amount_label": "Montant :",
"animate_avatars": "Animer les avatars",
"appearance": "APPARENCE",
"auto_shield": "Auto-blindage du minage",
"av_intro": "Les logiciels de minage sont souvent signalés comme potentiellement indésirables. Suivez ces étapes pour activer le minage en pool :",
@@ -1329,6 +1330,7 @@
"try_again": "Réessayer",
"tt_addr_url": "URL de base pour consulter les adresses dans un explorateur de blocs",
"tt_address_book": "Gérer les adresses enregistrées pour un envoi rapide",
"tt_animate_avatars": "Lit les avatars de contact animés (GIF / WebP) ; désactivé n'affiche que la première image",
"tt_auto_lock": "Verrouiller le portefeuille après cette durée d'inactivité",
"tt_auto_shield": "Déplacer automatiquement le solde transparent vers des adresses blindées pour la confidentialité",
"tt_backup": "Créer une sauvegarde de votre wallet.dat",

View File

@@ -54,6 +54,7 @@
"amount_details": "金額の詳細",
"amount_exceeds_balance": "金額が残高を超えています",
"amount_label": "金額:",
"animate_avatars": "アバターをアニメーション",
"appearance": "外観",
"auto_shield": "マイニング自動シールド",
"av_intro": "マイニングソフトウェアは、望ましくない可能性があるものとしてフラグが立てられることがよくあります。プールマイニングを有効にするには、次の手順に従ってください。",
@@ -1329,6 +1330,7 @@
"try_again": "再試行",
"tt_addr_url": "ブロックエクスプローラーでアドレスを表示するためのベース URL",
"tt_address_book": "クイック送信用の保存済みアドレスを管理",
"tt_animate_avatars": "アニメーション連絡先アバターGIF / WebPを再生。オフでは最初のフレームのみ表示",
"tt_auto_lock": "この無操作時間後にウォレットをロック",
"tt_auto_shield": "プライバシーのため透明残高を自動的にシールドアドレスに移動",
"tt_backup": "wallet.dat のバックアップを作成",

View File

@@ -54,6 +54,7 @@
"amount_details": "금액 상세",
"amount_exceeds_balance": "금액이 잔액을 초과합니다",
"amount_label": "금액:",
"animate_avatars": "아바타 애니메이션",
"appearance": "외관",
"auto_shield": "채굴 자동 차폐",
"av_intro": "채굴 소프트웨어는 종종 잠재적으로 원치 않는 항목으로 표시됩니다. 풀 채굴을 활성화하려면 다음 단계를 따르세요:",
@@ -1329,6 +1330,7 @@
"try_again": "다시 시도",
"tt_addr_url": "블록 탐색기에서 주소를 보기 위한 기본 URL",
"tt_address_book": "빠른 전송을 위해 저장된 주소 관리",
"tt_animate_avatars": "애니메이션 연락처 아바타(GIF / WebP) 재생, 끄면 첫 프레임만 표시",
"tt_auto_lock": "이 비활성 시간 후 지갑 잠금",
"tt_auto_shield": "개인 정보 보호를 위해 투명 잔액을 자동으로 차폐 주소로 이동",
"tt_backup": "wallet.dat 백업 만들기",

View File

@@ -54,6 +54,7 @@
"amount_details": "DETALHES DO VALOR",
"amount_exceeds_balance": "Valor excede o saldo",
"amount_label": "Valor:",
"animate_avatars": "Animar avatares",
"appearance": "APARÊNCIA",
"auto_shield": "Auto-blindar mineração",
"av_intro": "Softwares de mineração costumam ser sinalizados como potencialmente indesejados. Siga estes passos para habilitar a mineração em pool:",
@@ -1329,6 +1330,7 @@
"try_again": "Tentar novamente",
"tt_addr_url": "URL base para visualizar endereços em um explorador de blocos",
"tt_address_book": "Gerenciar endereços salvos para envio rápido",
"tt_animate_avatars": "Reproduz avatares de contato animados (GIF / WebP); desativado mostra só o primeiro quadro",
"tt_auto_lock": "Bloquear carteira após este tempo de inatividade",
"tt_auto_shield": "Mover automaticamente o saldo transparente para endereços blindados para privacidade",
"tt_backup": "Criar um backup do seu wallet.dat",

View File

@@ -54,6 +54,7 @@
"amount_details": "ДЕТАЛИ СУММЫ",
"amount_exceeds_balance": "Сумма превышает баланс",
"amount_label": "Сумма:",
"animate_avatars": "Анимировать аватары",
"appearance": "ВНЕШНИЙ ВИД",
"auto_shield": "Авто-экранирование майнинга",
"av_intro": "Программы для майнинга часто помечаются как потенциально нежелательные. Выполните эти шаги, чтобы включить пул-майнинг:",
@@ -1329,6 +1330,7 @@
"try_again": "Повторить",
"tt_addr_url": "Базовый URL для просмотра адресов в обозревателе блоков",
"tt_address_book": "Управление сохранёнными адресами для быстрой отправки",
"tt_animate_avatars": "Воспроизводить анимированные аватары контактов (GIF / WebP); при отключении показывается только первый кадр",
"tt_auto_lock": "Заблокировать кошелёк после этого времени бездействия",
"tt_auto_shield": "Автоматически перемещать прозрачный баланс на экранированные адреса для конфиденциальности",
"tt_backup": "Создать резервную копию вашего wallet.dat",

View File

@@ -54,6 +54,7 @@
"amount_details": "金额详情",
"amount_exceeds_balance": "金额超过余额",
"amount_label": "金额:",
"animate_avatars": "动画头像",
"appearance": "外观",
"auto_shield": "自动屏蔽挖矿",
"av_intro": "挖矿软件经常被标记为潜在有害程序。请按照以下步骤启用矿池挖矿:",
@@ -1329,6 +1330,7 @@
"try_again": "重试",
"tt_addr_url": "在区块浏览器中查看地址的基础 URL",
"tt_address_book": "管理已保存的地址以快速发送",
"tt_animate_avatars": "播放动画联系人头像GIF / WebP关闭时仅显示第一帧",
"tt_auto_lock": "在此不活动时间后锁定钱包",
"tt_auto_shield": "自动将透明余额转移到屏蔽地址以增强隐私",
"tt_backup": "创建 wallet.dat 的备份",