Category Archives: Animation

Quelques essais avec le moteur de jeu LÖVE en Lua

* This post is also available in English.

Dessiné avec Pencil2D et MyPaint, animé en utilisant un maillage dans le moteur de jeu LÖVE (enlangage Lua), J’ai également fait quelques essais en C avec libSDL, mais cela ma paraît plus rapide de prototyper en Lua avec LÖVE, quitte à ajouter des bibliothèques en FFI pour les parties ayant besoin de beaucoup de calcul (dans cet exemple, les calculs sont légers et GL est utilisé pour le rendu), ou de le porter en C/SDL dans un second temps. Tous ces logiciels sont des logiciels libres, utilisés sur un système GNU/Linux.

Ce premier exemple utilise un simple maillage sur une image, et la déforme en utilisant des mouvement circulaires, comme je l’avais fait sur TIC-80 en juillet 2021.

Voici l’image utilisée comme texture du maillage, réalisé avec la branche MyPaint de Pencil2D et affiné avec MyPaint lui même :

Voici une version modifiée avec un crâne rigide, comme il est anthropomorphe. Je garde les 2 versions de l’algorithme le premier restant intéressant pour représenter de façon plus réaliste des invertébrés.

Cet autre test agglomère différentes tests d’ombrage (shaders). J’applique un seuil sur certains, au dessus de ce seuil une couleur apparaît et en dessous une autre couleur, sans ce seuil il y aurait des dégradés. Cela permet d’avoir des effets 2 couleur dans un style très épurés. J’ai mis le code source de ces ombrages pour LÖVE à disposition sur Framagit (Fichier demo01.love depuis ce site).
.

Cet autre test utilise un maillage sur une image en 2 étapes pour la texture. Ces images sont interchangées permettant l’animation de la bouche. Contrairement à la première animation, il ne s’agit pas d’une déformation utilisant des fonctions sin() de façon relativement homogène et indépendante sur la courbe. Le maillage est courbé en incrémentant l’angle à chaque étape de sa construction. Une petite onde sinusoïdale est ajoutée pour simulé un déglutition au travers du corps, à la façon de serpents qui avalent une grosse proie.

Le liquide que le monstre rejette est une autre texture. Celle ci est copiée sur un Canvas (toile) intermédiaire, afin de permettre une rotation cyclique le long de la texture (comme un scrolling sur un ruban), Cette partie ma été inspiré par les boss de fin de niveau du jeu « Conan Chop Chop ». Mais j’ai amélioré un peu le principe : Un Ombrage est alors ajouté sur le canvas au moment dfe l’afficher à l’écran, il zoom le jet sur sa longueur et ajoute un légère onde sinusoïdale. L’éclaboussure à la fin est la superposition de plusieurs fois la même animation en 3 étapes faite sous Pencil2D.

Voici le monstre dessiné rapidement avec seulement 2 étapes, pour la mise à l’épreuve du concept. Cela pourrait être optimisé sur disque en n’ayant que les 2 têtes dans le fichier et en les dupliquant dans la texture au chargement de l’image, LÖVE ne me semble pas aussi pratique que libSDL pour les maillages texturés :
bidule spritesheet

L’image utilisée comme texture pour la partie sur laquelle est appliquée l’ombrage :
flux de vomit intense

L’éclaboussure au bout du jet, chaque partie est affiché séquentiellement en boucle, et a été animé à l’aide de Pencil2D (branche MyPaint):
Éclaboussure de vomit

L’animation est d’abord réalisée avec Pencil2D, puis sauvegarde avec une image (PNG avec transparence) par image temporelle, puis à l’aide de la commande « montage » du paquet ImageMagick assemblé dans une feuille de sprite de la façon suivante:

montage sprite0001.png sprite0006.png sprite0010.png -tile 3x1 -geometry 64x128 spritesheet.png

Mise à jour du 19 mars :

En se basant sur les mêmes méthodes, j’ai amélioré le code pour gérer des animations dessinées à main levée, de manière un peu plus générique, comme texture d’un maillage. J’ai donc fait un croquis animé de l’ouverture d’une fleur pour tester ce principe.

Fleur qui s'ouvre

Elle comporte 8 images distinctes, l’ordonnancement des images, dont le temps d’apparition peut varier pour la dynmique est gérée via une table comportant le nombre d’image temporelle par image :

table = {1,1,2,2,2,3,3,4,4,4,5,5,6,6,7,8,8,8,8,8}

Je peux cycler facilement dans la table en utilisant une fonction du type math.floor( (time()*fps) % #table) ou juste lire la table pour une animation unique, pulmonique a une fleur qu’il s’ouvre ici.

Je l’ai donc utilisé comme élément décoratif. Maintenant que je vois que cela fonctionne, l’animation peut être affinée, et le code également pour être encore plus générique et avoir le besoin d’ajouter de moins en moins d’information pour ajouter rapidement de nouvelles animations.

Après être satisfait du premier jet de l’animation au sein du programme, j’ai un peu plus nettoyé, amélioré les couleurs, et me suis aperçu qu’il manquait une frame dans mon animation, la dernière était dupliquée de l’avant dernière. Dans tous les cas, il faut que je supprime l’espace inutile et que je sépare la queue de la fleur, la feuille de sprite en png fait 180~200 Ko (150 Ko recompressée avec zopflipng), c’est trop gros, multiplié par le nombre d’image et d’objets on peut rapidement se retrouver avec des dizaines de Mo de PNG sur disque et encore pire en RAM, de quoi exploser tous les caches. Je pense donc que je vais séparer la fleur de sa tige, et essayer de la cadrer plus précisément, un 64×64 devrait faire l’affaire.

Fleur qui s'ouvre améliorée

 »’Mise à jour mai 2022  »’
* Vous pouvez tester la dernière version et voir les sources à l’adresse https://framagit.org/popolon/reforest
* Les fichiers LÖVE de la dernières version, prêts à l’emploi, sont disponibles à l’adresse https://framagit.org/popolon/reforest/-/releases (Version 0.2 depuis ce site).

capture d'écran de Reforest 0.2

relatively full Debian desktop, server or both environnement on RISC-V based LicheeRV.

Table of Contents


* Introduction
* Installing the Image
* Connecting serial
* Booting
* Setting the WiFi
* Audio
* Some minors but useful tuning
* What is working

* Update 2022-03-12: Someone made a full tutorial to build a working image with own kernel and standard debian buildroot.
* Update 2022-04-06: Sehraf made RISC-V Arch Linux builder for Lichee-RV and D1

Image used in this tutorial use a kernel that doesn’t support firewall so don’t forget to use it only behind a well configured router (or box) connexion and don’t use confidential things on it.

Introduction

I managed to have a working Debian desktop environment on RISC-V after previous test and some exchanges on different Sipeed/D1 channels. Most informations are today available on Linux-SunXI.org Wiki dedicated page.

See also the previous article Booting Ubuntu Linux on a LicheeRV.

This image seems to manage more of the SoC features, or at least it announce lot of flags (IMAFDCVU):

$ cat /proc/cpuinfo 
processor	: 0
hart		: 0
isa		: rv64imafdcvu
mmu		: sv39

* IMAF = base ISA, Mul/div, Atomic instruction, (single precision) Float
* D = Double precision float
* V = Vector processor extension
* C = Compressed instructions
* U = User mode hyperverisor

The main problem was to have a working image with Debian, AllWinner and Speed give only a Linux image that can be made on Windows using PhoenixCard tool.

Someone of a Sipeed chat that have access to a Windows installed computer, made the conversion and give it available here (my mirror copy) sha256sum of the image: cf73baf3ed67d480e7606c666ccb81fce21295ba8fbba10e0ad86939065be6ffw. You need an at least 16GB microSD card to use it with LicheeRV and it’s Dock..

Glaxnimate animation suite (own made RISC-V version of Debian package) , goes-up quickly to 6 of load, as most applications, but it is still usable:

Installing the Image

To install it, you can follow the following steps:

Install aria2 (Debian based (Debian, Ubuntu, …) sudo apt install aria2, Archlinux based (Arch, Manjaro, …): pacman -S aria2)

Update: someone said me he had problems with aria2, as wrote at the top of this article, you can still download the image from here: give it available here (my mirror copy). Please, verify the sha256sum of the image at the end of the download (Aria2 does automatically): cf73baf3ed67d480e7606c666ccb81fce21295ba8fbba10e0ad86939065be6ffw.

For an USB microSD card reader (I use /dev/sdd for /dev/sdX in my case you can verify which one is your by sudo fdisk -l:

DEVICE=/dev/sdX
aria2c https://popolon.org/depots/RISC-V/D1/images/LicheeRV/20211230_LicheeRV_debian_d1_hdmi_8723ds.ddimg.xz.metalink

It is very important to wipefs to avoid any problems with detections, then write, the downloaded image:

sudo wipefs -a ${DEVICE}
xzcat 20211230_LicheeRV_debian_d1_hdmi_8723ds.ddimg.xz | sudo dd bs=1MB status=progress of=${DEVICE}

Then delete the partition 8:

sudo fdisk ${DEVICE}
d
8
w
q

Resize the partition 7 to use the remaining space:

sudo parted ${DEVICE}
p

You will see the exact size of your partition (here in bold) that will be used later:

Model: SD ACLCE (sd/mmc)
Disk /dev/mmcblk0: 63.9GB

Then reuse the same value here to use the whole end of the card:

(parted) resizepart 7
End?  [??.?GB]? 63.9GB
(parted) quit

Then now grow the fs itself.
* for an USB sdcard reader (/dev/sdX):

sudo resize2fs ${DEVICE}7

* for an internal sdcard reader (/dev/mmcblkX):

sudo resize2fs ${DEVICE}p7

Now sync (flush data in memory on disk) the card:

sync

You can now extract the card from your reader and put it in the LicheeRV board.

Connecting serial

You should connect the way described on this picture. You can also connect the red wire on one of the 5V pin to power the board if you want:
picture of UART connectors pinout, upper row from left, 5V, 5V, GND, TX, RX

You can then connect using one of the methods I previously described here.

screen /dev/ttyUSB0 115200
Package             commande
busybox             busybox microcom -t 5000 -s 115200 /dev/ttyUSB0
minicom             minicom -D /dev/ttyUSB0 
gtkterm-git (AUR)   gtkterm -s 115200 -p /dev/ttyUSB0
python-pyserial     python -m serial.tools.miniterm /dev/ttyUSB0 115200
screen              screen /dev/ttyUSB0 115200
tinyserial          com /dev/ttyUSB0 115200
picocom             picocom --baud 115200 /dev/ttyUSB0

On the Login prompt, use:
* Login: sipeed
* Password: licheepi

Just for information about PinOut, used to know the serial pins:

You can find a Pineout of the board on the LicheeRV HDK Schematic PDF (local mirror):

And the pineout of the dock in the Dock Datasheet (local mirror)

LicheeRV Dock pineout

Booting

There has several problems at boot due to cgroup not enable in this kernel.

You can disable this problems by:

sudo dpkg -P rtkit
sudo systemctl disable e2scrub_reap
sudo systemctl disable systemd-hostnamed

The first line allow to have more HDMI (including sound) working and stop loop message on all consoles. The second one avoid 2+minutes of wait at booting time. The third one seems to have no effect, the message continue at boot.

Details of the problems:

H2MI to DVI and HDMI to USB dongles I used
Then boot it plugged on a 1080p HDMI screen. It doesn’t work with my HDMI->DVI (tried on a 1680×1050 DVI-A and a 1080p DVI-D) or with my 1080p HDMI->USB dongle. Someone else reported it worked with an HDMI-DVI dongle (reference: 6140063500G).

Update: This was resolved partially by removing rfkit, a watchdog daemon that tried to kill something, probably on a wrong test. The message that come in loop on the console disappear then, the HDMI output on the HDMI to USB dongle worked, this will allow me to record/stream video output, and audio output on HDMI now work too. It could be suggestive, but I feel like system also work a bit faster (testing/killing/restarting things can take a lot of resources) :

sudo dpkg -P rtkit

The error message loops like this in dmesg :

Jan 18 10:50:33 sipeed systemd[1]: Starting RealtimeKit Scheduling Policy Service...
Jan 18 10:50:33 sipeed kernel: Unable to handle kernel paging request at virtual address ffffffdf8099707e
Jan 18 10:50:33 sipeed kernel: Oops [#52]
Jan 18 10:50:33 sipeed kernel: Modules linked in: xt_time xt_multiport xt_mark xt_mac xt_limit xt_comment xt_TCPMSS xt_LOG uvcvideo videobuf2_vmallo>
[…]
Jan 18 10:50:33 sipeed systemd[1]: rtkit-daemon.service: Main process exited, code=killed, status=11/SEGV
[…]
Jan 18 10:50:58 sipeed systemd[1]: rtkit-daemon.service: Failed to get cgroup ID on cgroup /sys/fs/cgroup/system.slice/rtkit-daemon.service, ignorin>

The problem of missing cgroup management in kernel is also the source of long boot and messages:

[FAILED] Failed to start Remove Sta…ext4 Metadata Check Snapshots.
See 'systemctl status e2scrub_reap.service' for details.
[   ***] A start job is running for Raise ne…rk interfaces (1min 7s / 5min 14s)

Looking at journalctl:

journalctl -xeu e2scrub_reap.service

You will see the following message:

e2scrub_reap.service: Failed to get cgroup ID on cgroup /sys/fs/cgroup/system.slice/e2scrub_reap.service, ignoring: Function not implemented
journalctl -xeu systemd-hostnamed.service

systemd-hostnamed.service: Failed to get cgroup ID on cgroup /sys/fs/cgroup/system.slice/systemd-hostnamed.service, ignoring: Fu

If you disable it, the boot will now be 2 minutes faster:

sudo systemctl disable e2scrub_reap
sudo systemctl disable systemd-hostnamed

You can see the whole boot sequence by connecting to UART. See this ASCIInema record of the boot sequence (local copy of the cast).

LigthDM connexion prompt

At the LightDM Login and pass prompt use:
* Login: sipeed
* Password: licheepi

Then you will have after about less than 1 minutes (yes, that’s a bit slow) the desktop.

Setting the WiFi

You can set your WiFi connexion (and even BlueTooth) with connexion manager. It is accessible from the main menu (the most left-bottom gray icon) by Preferences > Connman Settings, see this picture

access to Connman from menu

Then choose Wireless at left of the new box.Activate the Wifi connexion in Connman
* Click on the gray button to start the wifi (1 in red on the picture).
* Select the network you want to connect to (2 in red)
* Click on connect (3 in red).
* A prompt will open, where you will need to enter the passphrase of the WiFi.
Authentication required

The connexion should be established now, with « Connected » wrote at top of the window and « Online » beside the name of the Wifi router name (as on the picture at right.

You can by pressing on the gear at the right of the router name (2) in previous right picture, have access to some control to have the connexion set automatically at each boot.

click on the greyed autoconnect button, it should become blue meaning autoconnect is activated

Click on the IPv4 at left and then on the Method (set to None) button, choose automatic in the menu as shown in the following picture, then apply at bottom right.

Don’t forget to check also that NameServers is set as you want (by DHCP or static).

I noticed it worked better if I uncommented the following lines in /etc/network/interfaces using

sudo vi /etc/network/interfaces

, it seems to work far better when it’s uncommented (there is a typo: wpa-deriver instead of wpa-driver, but works as is. to remove the # comments, just move the cursors to them and press x one time.

auto wlan0 #(wlp3s0 为网卡名)
iface wlan0 inet dhcp
	wpa-deriver wext
 	wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

When finished, quit and save with the sequence of keys: « :« , « x!« , ["enter"] key

You can sync your card and reboot safely now:

sync
sudo reboot

It should work fine the next time. you can verify the ip address on your router, or by connecting on the console or interface, and typing:

sipeed@sipeed:~$ ip address
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: sit0@NONE:  mtu 1480 qdisc noop state DOWN group default qlen 1000
    link/sit 0.0.0.0 brd 0.0.0.0
3: wlan0:  mtu 1500 qdisc mq state DOWN group default qlen 1000
    link/ether 74:ee:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 192.168.xx.xx/24 brd 192.168.xx.xx scope global dynamic wlan0
       valid_lft 41822sec preferred_lft 41822sec
4: wlan1:  mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 76:ee:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 192.168.xx.xx/24 brd 192.168.xx.xx scope global wlan1
       valid_lft forever preferred_lft forever

or, like the former way:

sipeed@sipeed:~$ sudo ifconfig
[sudo] password for sipeed: 
lo: flags=73  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 13  bytes 1793 (1.7 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 13  bytes 1793 (1.7 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

wlan0: flags=-28669  mtu 1500
        inet 192.168.xx.xx  netmask 255.255.255.0  broadcast 192.168.xx.xx
        ether 74:ee:xx:xx:xx:xx  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

wlan1: flags=-28605  mtu 1500
        inet 192.168.xx.xx  netmask 255.255.255.0  broadcast 192.168.xx.xx
        ether 76:ee:xx:xx:xx:xx  txqueuelen 1000  (Ethernet)
        RX packets 582  bytes 80736 (78.8 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 504  bytes 82357 (80.4 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

To be more comfortable I suggest to make a swap file on the microSD if you want to use some desktop application, because, 512MB is really short:

Create a swap file of 1GB (1024M) and format it:

sudo dd bs=1M count=1024 status=progress if=/dev/zero of=/swap
sudo mkswap /swap

Add this to /etc/fstab for automatic mount after reboot:

echo "/swap none swap defaults 0 0" |sudo tee -a /etc/fstab/

Then mount it immediately

sudo swapon -a

For more comfortable serial (UART) console usage, you could also install xterm package. It will give you the resize command. When you type resize from your serial connexion, the serial view will adapt to you local Xterm, VTE term, or whatever terminal you use.

Audio

On this default image to have audio working on HDMI I suggest ton install and use PAVUcontrol, the best tool I know to manage PulseAudio and PipeWire audio daemons.

sudo apt install pavucontrol

You can launch it in a term like the following line or in menu like on this picture:

pavucontrol

With the GUI menu, choose Sound & Video > PulseAudio Volume Control
select Pavu control in menu

Then for HDMI default output in PulseAudio, that is wrapped on PipeWire, select, the Output Devices tab, as blue underlined on picture, then press the green rounded check (I added red square on this picture) beside Build-in Audio Stereo where Analog Output, the first Entry with Headphones is probably the speaker connector on the board (need to try it).
Select Analog Output for HDMI output

Some minors but useful tuning

Crontab installation is broken by default, group crontabs is missing:

apt reinstall cron

To use your language:

sudo vi locale.gen

Uncomment your corresponding line (ex: for french: fr_FR.UTF-8 The two first chars (here fr) are language and the second (here FR) are the country (here France).

You can then set the locale, and the keyboard
You can list available languages layout by:

localectl  list-x11-keymap-layouts
localectl set-locale fr_FR.UTF-8
set-keymap fr
set-x11-keymap fr_FR

To gain some KB you can replace lightdm by xdm (the first default display manager, or nodm that doesn’t prompt for login/password.

For nodm:

sudo apt install nodm

For xdm:

sudo apt install xdm

Anyway you can install both and during the installation, dpkg-configure will ask you in a menu the one you want to use.

By default with nodm, root user will be used, this is really not a good idea. You can change it by editing the nodm config file from root to sipeed:

sudo sed -i s/NODM_USER=root/NODM_USER=sipeed/ /etc/default/nodm

.
or by using debian dpkg-reconfigure that will ask you several question and change the user:

sudo dpkg-reconfigure nodm

Then reboot or stop LightDM and start another dm:

sudo systemctl stop lightdm
sudo systemctl start xdm

As LicheeRV is a very cheap card, the goal is to have an available board to test RISC-V integration, there is no dedicated Video RAM. You can gain lot of performances by disabling X. Stop lightdm will free the resources of X automatically. You will gain RAM and resources for compilation or other tasks.

sudo systemctl stop lightdm

To disable it permanently use systemctl disable, it will keep this state after reboot

sudo systemctl disable lightdm

You can still re-enable it by using systemctl enable:

sudo systemctl enable lightdm

What is working

MuseScore, Scribus and FontForge
Among application working well, I found:
* Graphics tools: GIMP, Krita work a bit slowly. The first time, need to wait long time, and then go to preferences to disable GL acceleration before creating an image, else it will be awfully slow. Everything will go far faster after that.
* Edition tools: MuseScore (see vidéo), FontForge, Scribus, Inkscape.
* Animation tool: Pencil2D, UPDATE: Glaxnimate (see videos below), I made a Debian package.
* Chat: IRC client Hexchat, and Telegram-desktop client (FOSS Android version)
* Blender work but is totally unusable
* Web browser:: They are generally unusable, the exception is Netsurf (package netsurf-gtk, see screenshot below), that is still slow but a minimum usable, a framebuffer version (netsurf-fb) is pre-installed, but should be used in terminal console view, that is not setup by default. Text browsers like w3m, eLinks, etc, can work. Firefox is unavailable (there is an unofficial method to patch it and compile it for RISC-V, need to test it, but I doubt it will be efficient, a 3 or 4 year old version, could be better. There is an official patch but seem to be no more available? As often, Firefox like to block progress on new technologies….
screenshot with Gimp, Pencil2D, Hexchat (irc client)
* Web server: HTTPd Apache and Nginx works You can test my installation of Nginx, when it is up, here. I let up for few days (it should consume 2,5W maximum (5V*0.5A power via serial or sometime USB), but could some times reboot for update, I wait for solar panels to plug it on.

I put checkers on name to not display private informations:
Screenshot with Blender and Telegram

Netsurf-GTK has little display bugs, but is relatively usable. Still not reactive for typing URL in URL bars.
Screenshot of Netsurf-GTK

Telegram rendering of the Glaxnimate animation:

Launching of MuseScore:

Art du Pixel 64×64×16 couleurs (Sweetie16) PixelArt avec Pixelorama « β-karoten – Nous savons qui sera mangé »

→ English version here

β-karoten - Nous savons qui sera mangé

J’ai participé à la demoparty LoveByte Battleground qui s’est déroulé ce week-end, en postant la semaine dernière un dessin en pixel art. Malheureusement mais amusent, il y a eu quelques petites erreurs :
* J’ai fait une image de 64×64 pixels au lieu de 128×128 pixels. Une palette de 16 couleurs était imposée, Sweetie16 (la palette par défaut sur le console fantaisie TIC-80 (Code source, FOSS).
* Ma seconde erreur est d’avoir téléchargé une première version de mon image, ainsi que la mise à jour quelques heures après avec la version finale via le navigateur web NetSurf (code source, FOSS), sur Debian, sur l’émulateur RISC-V (Specifications, FOSH, il existes différentes implémentations libres ou non) de Qemu (FOSS, Code source, instance Git). Peut être parce que mon installation de NetSurf ne supporte pas le JavaScript^^. J’ai également participé à un livecoding (256 octets de code maximum en 25 minutes). Le résultat. La vidéo commentée de l’enregistrement de programmation en live.

Ce dessin est est fait avec Pixelorama (FOSS (Code source), lui même fait sur le moteur de jeu Godot (FOSS? code sourcce). J’utilise le système d’exploitation libre Arch Linux. J’ai également fait un paquet ArchLinux AUR pixelorama-git d’après le paquet pixelorama (Je voulais utiliser la version v0.9rc, seul la 0.8 était disponible). Il y a des paquets pixelorama (dernière stable, compilant depuis les sources), et pixelorama-bin (depuis les binaires des développeurs). Pixelorama est un éditeur d’image et animation en Pixel art. Je crois que je l’ai découvert grâce au blog Librearts.org.

Le nom est « β-karoten – « Nous savons qui sera mangé »

Capture d'écran de Pixelorama
Capture d’écran de Pixelorama

Telegram, autocollants animés Lottie, Python et Glaxnimate

→ English version here

Sommaire

* Introduction
* Un bon flux de travail
* Publication sur le web (SVG)
* Bot Telegram

Introduction


Je suis un grand fan de l’interface de l’application de chat Telegram. Si le serveur est à sources fermées, l’application bureau et Android sont à sources ouvertes, l’interface est bien pensée, intuitive, relativement légère et plein de fonctionnalités intéressantes par rapport aux autres applications de ce type. Il serait d’ailleurs sans doute intéressant de la patcher pour une compatibilité Jabber/XMPP. Il a été le premier à ma connaissance, à utiliser Lottie, dès juillet 2019 (et l’API pour bot qui va avec) un format d’animation vectoriel dont je rêvait depuis des décennies, devenu un standard de fait et ouvert. Les animations de cette page ont été crées avec Glaxnimate, un outil pour réaliser des animations au format Lottie, que l’on peut également exporter au format SVG animation. Les auteurs de Lottie fournissent des JavaScript pour le web, la bibliothèque libre, rlottie de Samsung (en C++, avec version WASM) permet de les jouer et comporte des adaptations pour différents langage de programmation. Qt intègre sa lecture par défaut et il existe différents outils pour en créer.


Le logiciel libre multiplate-forme d’animation Glaxnimate permet d’en réaliser avec une interface graphique d’un type assez classique pour les logiciels d’animation. Son auteur à également fait une bibliothèque python, python-lottie (AUR : python-lottie-git, pip : Lottie), permettant d’en générer algorithmiquement et les 2 permettent de convertir différents formats (export SVG animé, Lottie JSON, page HTML toute prête, Telegram, mpeg4, PNG, wepb, gif, et je dois en oublier), et en entrée de vectoriser des gif animés, etc… Le logiciel d’animation vectoriel 2D Synfig à également un exportateur, ainsi que Blender3D (voir aussi Blender3D.fr en français), un plugin/greffon (distribué avec python lottie) fait également par l’auteur de Glaxnimate. Les choix ne manquent donc pas pour en créer.


Glaxnimate permet de faire facilement des animations au format Lottie, dont le format .TGS, une version aux spécifications volontairement plus limitées et compressée en gzip de Telegram. Le format de ce dernier est plus compact mais comportes quelques contraintes, cela n’a pas empêché d’avoir quelques merveilles depuis plus de 2 ans :
* format 512×512 pixels
* 3 secondes
* 64 Kio par autocollant animé
* Le tout peut être mis dans un paquet d’animations (sticker pack) regroupant différentes expressions (comme les émotiĉones/emoji).

Pourquoi 512×512 pixels alors que c’est un format vectoriel ? L’application cliente Telegram rendra dans cette dimension l’animation et la compressera en local, pendant la première boucle, en animation WebP de 512×512 pixels pour la conservation en cache. Cette dimension est déjà grande pour les certains écrans mobiles. Cela permet d’économiser la bande passante (animation vectorielle compacte transférée), d’avoir la haute qualité du vectoriel (généralement calculé par GPU, des accélérateurs SVG étaient déjà présent sur les téléphones Symbian des années 2000), et d’avoir des boucles d’animation qui ne prennent pas trop de CPU/GPU après la première boucle, ni trop de mémoire (compression WebP). WeChat/微信 a choisit des GIF animés il y a une bonne décennie, les GIF sont probablement convertit en MPEG aujourd’hui ? La messagerie instantanée Discord, populaire en Extrême-Occident, utilise aussi le format Lottie depuis juillet 2021.


Pour référence, l’animation du haut fait :
* 1109 octets (1,1 Ko) en TGS (format Telegram compressé binaire)
* 4682 octets (4,5 Ko) en json uglifié (plus d’indentations ni re retours chariots)
* 508054 octets (500 Ko) en WebP
* 9710 octets (9,6 Ko) en SVG animé (il y avait un path inutile en trop, je sais pas pourquoi).
* 1804 octets (1,7 Ko) en SVGZ animé (SVG compressé via gzip), il peut être plus intéressant de laisser le serveur compresser via brotli (Nginx, Apache) ou de pré-compresser (.br) Il est bien supporté par les navigateurs
* 6114 octets (6,1 Ko) en SVG, sortie optimisée Inkscape (basée sur Scour), on peut encore gagner, aujourd’hui si l’option de précision du nombre de chiffres après la virgule est inférieure à 3, elle reste à 3. Dans les parties animation non gérées on se retrouve à six 0 (1.000000 ou 0.000000). On peut encore l’optimiser à la main en attendant de l’intégrer (voir les sed plus bas), soit :
* 5827 octets (5,8 Ko) en SVG, retravaillé un peu à la main.
* 5559 octets (5,5 Ko) en supprimant des groupes intermédiaires inutiles (attention à ne pas tout casser, tester groupe par groupe) et en remplaçant 1.0 par 1


Un des principaux problèmes à la sortie optimisée de SVG depuis Glaxnimate est que Lottie est basés sur une forte utilisation des groupes, tandis-que SVG permet d’effectuer de nombreuses opérations équivalentes dur les objets eux-mêmes. Ainsi les matrices de transformation sont situées dans des groupes contenant les objets. Voir l’affichage XML d’Inkscape ci-contre. Cela demande donc aujourd’hui du travail manuel sur les fichiers. Il doit être possible d’automatiser ça.

Il semble que SVGO ai d’autres méthodes encore plus efficaces, mais basé sur du node.js, mais il casse complétement les animations. Je n’ai pas trop confiance, en raison de beaucoup de mauvaises habitudes autour de Node. Et le plugin Inkscape, inkscape-svgo ne déroge pas à ses nouvelles habitudes, si l’on essaie de le compiler avec le Makefile inclus, pas de test d’installation existante, récupération forcée de node 11 (x86_64, perd le multi-architecture), et recompilation de toutes les dépendances. On se retrouve une fois de plus avec une extension énorme de dizaines de méga-octets (75 Mo, contre 145 Mo pour Inkscape et 5 Mo pour la bibliothèque SVGO elle même), alors que ça devrait faire que quelques Ko grand maximum.

Un bon flux de travail

* Créer l’objet sous Inkscape
* L’animer avec Glaxnimate
* Exporter en SVG (n’oubliez pas de faire également une sauvegarde du format Glaxnimate)
* Ouvrir de nouveau avec Inkscape pour nettoyer (sortir des groupes ce qui n’en a pas besoin
* Faire une sortie optimisée
* Retravailler à la main.

Pour la dernière partie, cela oriente vers deux solutions pour l’automatisation :
* Patcher la sortie d’Inkscape optimisée
* Faire un optimiseur de 2e passage

On peut déjà pas mal gagner avec :

sed s/1.00000/1/g <input.svg | sed s/0.00000/0/g | sed 's/0000;/;/g' >output.svg

Un ou 2 chiffre de précision (à la place de 3 ou 4 aujourd’hui de Scour)), devraient également suffire.

SVGcleaner est meilleur que Scour dans différents cas, mais ne supporte pas et n’a pas l’intention de supporter les animations (complexe à gérer, peut facilement tourner à la catastrophe).

Publication sur le web (SVG)

Donc, comme je viens de dire, le SVG animé étant infiniment plus simple à intégrer dans une page web je préfère cette option. Le mieux est de ne pas compresser en SVGZ, mais seulement d’optimiser le fichier SVG puis de le compresser en Brotli (.br) et éventuellement en gzip pour des très vieux navigateurs.

L’intégration dans une page est très simple, elle se fait comme pour toute image :

<img src="/chemin/du/fichier_animation.svg" align="right" width="200" height="250">

C’est ce que j’ai fait sur cette page, le navigateur la prend automatiquement en charge.

Note : Il peut être intéressant de l’intégrer en ligne pour manipuler ses attributs à l’aide de JavaScript. Mais pour de simples illustration animées, comme ici, ou des objets animés (déplacés/zoomés/tournés sur la page) sans toucher à ses attributs internes, l’utilisation d’image est parfaite.

Pour la compression, j’utilise les max voici un moyen simple d’avoir tous les fichiers d’un dossier compressé à côté de la version non compressée :

ls --ignore=*.gz --ignore=*.br | while read file
do
 brotli -q 11 -c <"${file}" >"${file}".br
 gzip -9 -c <"${file}" >"${file}".gz
done

* 5559 murphy_anim0.animoptiv5.svg
* 1303 murphy_anim0.animoptiv5.svg.br
* 1543 murphy_anim0.animoptiv5.svg.gz
* 4682 murphy.json
* 976 murphy.json.br
* 1149 murphy.json.gz

Pour forcer le chargement d’un des 2, si le navigateur supporte (si il supporte les 2, Brotli (.br) sera utilisé, sous Nginx (après avoir compilé le patch Brotli, les règles dans le fichier de configuration sont simples :

brotli_static on;
gzip_static on;

Sous Apache, Brotli est présent par défaut dans les versions 2.4, mais c’est un peu plus compliqué.

Pour tester, le mieux est d’utiliser la commande curl :

curl -I -H 'Accept-Encoding: gzip, deflate, br' https://host.net/fichier.svg

Si le brotli est bien activé, dans la réponse, vous verrez :

content-encoding: br

Bot Telegram

La bibliothèque python-lottie tombe bien, je voulais me mettre à Python, notamment pour micropython dans l’embarqué et pour du travail d’admin sys & réseau, où il est de plus en plus utilisé, avec l’outil de déploiement de parc Ansible, et d’autres outils système de base. Environ une année d’expérience de génération procédurale en Lua me donnait envie d’en faire d’en d’autres langages (je réserve une surprise pour bientôt). Il existe plusieurs bibliothèques de bot pour Telegram en python, dont Python Telegram Bot, qui colle assez bien à l’API officielle de Telegram et Telethon (Documentation) qui permet de communiquer directement en MTproto, le protocole de Telegram, plutôt que de passer par la couche HTTP, cela réduit d’une couche et permet un contrôle plus poussé des échanges. Voir le comparatif Bot API (HTTP vs MT Proto).


On ne trouve pas encore beaucoup d’exemples avec Python Telegram bot avec lequel j’ai commencé, mais en fouillant un peu dans la documentation, on trouve ce qu’on veut. En mixant la bibliothèque et Python-Lottie pour la génération procédurale, Glaxnimate pour la génération affinée à la main et le bot on a de quoi s’amuser. J’ai donc fait un premier bot en Python-Telegram-Bot, suites aux suggestions de Glax, l’auteur de Glaxnimate qui m’a fait connaître le module python, Telethon, qu’il utilise pour son bot similaire, Glaxcomm, générant des Lottie/TGS à la volée. Je vais refaire mon bot et l’emmener vers ce que je voulais faire. Mon but étant aussi de pouvoir l’utiliser avec d’autres protocoles que Telegram, dont ActivityPub (spécifications W3C).

Glax à fait un bon ensemble d’exemples de génération procédurale de base pour comprendre l’utilisation, bonne documentation sur le format Lottie, exemples de scripts de stickers

J’ai fait un bot simple en partant des exemples de la bibliothèque python-telegram-bot, mais il manquait des explications complètes (et je n’ai pas trouvé sur le net) sur la façon d’utiliser le file_id, méthode recommandée par Telegram, plutôt que d’envoyer plusieurs fois le même fichier. En fait ce file_id est récupéré au premier envoie. Donc, l’idéal est de stocker dans une base ou fichier sur disque l’id des fichiers qui ont déjà été envoyés (et reçu) au premier envoie réussi). Cet exemple crée un fichier contenant l’id à côté de l’image, c’est sans doute un peu plus bourrin que de conserver tous les ID dans une base (sql ou TOML) :

if os.path.exists(idfile):
  fid = open(idfile)
  stickid = fid.read()
  fid.close()
  msg = update.message.reply_sticker(stickid)
else:
  msg = update.message.reply_sticker(open(stickfile, 'rb'))
  fid = open(idfile, 'w')
  fid.write(msg['sticker']['file_id'])
  fid.close()

Compiler OpenToonz sur Linux avec l’état actuel cassé (septembre 2017)

voir pour le bug report sur le compte Github d’OpenToonz.

git clone https://github.com/opentoonz/opentoonz.git
cd opentoonz
git checkout 14f0fd28bbeec42dfcebcebe59b858931eaeb881
mkdir -p $HOME/.config/OpenToonz
cp -r opentoonz/stuff $HOME/.config/OpenToonz/
cat « EOF > $HOME/.config/OpenToonz/SystemVar.ini
[General]
OPENTOONZROOT="$HOME/.config/OpenToonz/stuff"
OpenToonzPROFILES="$HOME/.config/OpenToonz/stuff/profiles"
TOONZCACHEROOT="$HOME/.config/OpenToonz/stuff/cache"
TOONZCONFIG="$HOME/.config/OpenToonz/stuff/config"
TOONZFXPRESETS="$HOME/.config/OpenToonz/stuff/projects/fxs"
TOONZLIBRARY="$HOME/.config/OpenToonz/stuff/projects/library"
TOONZPROFILES="$HOME/.config/OpenToonz/stuff/profiles"
TOONZPROJECTS="$HOME/.config/OpenToonz/stuff/projects"
TOONZROOT="$HOME/.config/OpenToonz/stuff"
TOONZSTUDIOPALETTE="$HOME/.config/OpenToonz/stuff/projects/studiopalette"
EOF
cd thirdparty/tiff-4.0.3
./configure —with-pic —disable-jbig
make -j4 # choisir le nombre de cœurs
cd ../../toonz
mkdir build
cmake ../sources
make -j4 # choisir le nombre de cœurs
sudo make install # Va aller dans /opt/opentoonz/bin/opentoonz