Grafik & Rendering
AURA verwendet SDL3 für Fenster und Eingabe und Skia für 2D-Grafik und Text. Der eigene C++-Szenengraph verbindet Layout, Interaktion, Animation und Medien.
Grafikbasis
- C++20 und CMake bilden die native Integration.
- Skia zeichnet abgerundete Rechtecke, Text mit HarfBuzz/ICU, SVGs und Bilder.
- Native GPU-Ausgabe und CPU-basiertes Offscreen-Rendering verwenden denselben Szenengraph.
- Bibliotheken, Versionen und Lizenztexte stehen unter Dependencies & Lizenzen.
Aufbau
| Datei | Inhalt |
|---|---|
application.h |
Fenster, GL-Kontext, Skia-GPU-Surface, Event- und Render-Schleife, Offscreen-PNG |
node.h |
Scenegraph: Node, RectNode, ImageNode, SvgNode, TextNode, ScrollNode |
layout.h |
Anker wie in QML (place(node).left(44).below(title, 30)), gridCell für Raster |
animation.h |
Tweens mit Easing (Linear, InOutCubic, InOutQuint, OutQuad, OutBounce); an den Ziel-Node gebunden, stoppen sie mit dessen Löschung |
resources.h |
Cache für Bilder, SVGs und die von der Anwendung bereitgestellten Schriften |
video.h |
VideoPlayer (WebM/VP9, ohne Ton, Playlist mit Endlosschleife) und VideoNode |
- Layouts: Neben expliziten Positionen und Ankern unterstützt AURA heute
Column,RowundGrid. Details stehen in der Sprachreferenz. - Logische Auflösung: Die logische Größe wird über
WindowOptionsfestgelegt; die Szene wird für die Ausgabe in das Fenster skaliert. - Eingabe:
- Antippen über
onTap - Ziehen ab 10 px auf dem nächsten
ScrollNode - Wischen mit Abbremsen und Zurückfedern ähnlich wie bei
Flickable(2500 px/s max., 1500 px/s² Verzögerung)
- Antippen über
- Clipping:
clipschneidet rechteckig. Bilder können mitradiusundroundedCornersselbst gerundete Ecken erhalten. - Rendering bei Bedarf: Ohne Animation schläft die Schleife in
SDL_WaitEventTimeout.
Layout (aura/ui/layout.h)
Die Anker von QML ohne Bindings: Ein Node wird einmal relativ zu seinem Parent oder zu einem Geschwister-Node (gleicher Parent) gesetzt.
qml
Text title {
text: "AURA"
pixelSize: 28
place: left 44, top 44
}
Text subtitle {
text: "Application UI Runtime Architecture"
pixelSize: 18
place: left 44, below title 30
}
| Aufruf | Bedeutung |
|---|---|
left/right/top/bottom(m) |
Abstand zum Rand des Parents (right/bottom brauchen die eigene Größe) |
centerX/centerY(offset), center() |
im Parent zentriert, auf ganze Pixel gerundet wie die alten Layouts |
below/above/rightOf/leftOf(sibling, spacing) |
neben einem Geschwister-Node |
alignLeft/alignTop/alignBottom(sibling), centerXOn/centerYOn(sibling) |
an einem Geschwister-Node ausgerichtet |
size(w, h) |
Größe setzen (vor allem vor right, bottom und center) |
- Die Reihenfolge zählt: Erst die Größe setzen, dann zentrieren oder rechts/unten anlegen. Der Parent braucht zu diesem Zeitpunkt seine Größe.
- Gruppen bekommen einen eigenen Container-Node beziehungsweise ein
Itemin AURA. gridCell(index, columns, w, h, spacingX, spacingY)liefert die Zelle eines zeilenweise gefüllten Rasters (QMLGrid).Texterhält seine natürliche Größe aus dem Inhalt;wrap,fitundContext::fontSize()steuern Umbruch und Schriftgrößen.Node::pressPositionist die Druckstelle (für den Ripple).Animation::onFinishedverkettet Animationen.- Buttons können als
Buttonoder eigene Komponenten beschrieben werden;pressSinkergänzt eine Reaktion auf Drücken. - Eingabe:
aura::ui::InputDriver(input.h) ist der Finger auf einer Szene: gedrückt-Zustand, Tippen beim Loslassen über demselben Node,onDrag, Ziehen des nächstenScrollNode. DieApplicationtreibt einen mit der Maus,aura-vieweinen eigenen. - Entfernen aus einem Handler:
node->removeLater()nimmt den Node sofort aus dem Baum und löscht ihn vor dem nächsten Frame (Node::deleteRemoved(), das dieApplicationaufruft); so darf ein Button seinen eigenen Dialog schließen.
Hintergrundarbeit
aura::TaskRunner(aura/core/tasks.h) führt Jobs auf einem kleinen Pool aus (Prozessorzahl minus eins, 2 bis 4 Threads), abbrechbar überstd::stop_token;stop(id)hält auch einen Job auf, der noch wartet.latest(slot, arbeit, ergebnis)ist für Aufgaben, bei denen nur die neueste zählt (beispielsweise Suche oder Vorschaubilder): ein neuer Job auf demselbenaura::Lateststoppt den alten, dessen Ergebnis verfällt, ebenso nachcancel()oder wenn der Slot nicht mehr existiert. Die Vorschaubilder des Editors werden je Foto als eigener Job dekodiert, also parallel.- Ergebnisse kommen mit
post()in den UI-Thread zurück: in die eine Warteschlange des UI-Threads (aura::post()), die die Application-Schleife vor jedem Frame abarbeitet (aura::runPosted()). Rückrufe eines zerstörtenTaskRunnerverfallen. post()weckt die Event-Loop überApplication::wakeUp(). Ohne das Wecken käme das Ergebnis erst mit dem 500-ms-Idle-Takt.- Der Destruktor stoppt und joint alle Jobs. Callbacks überleben ihren Besitzer also nie.
Text
- Satz mit SkParagraph und HarfBuzz/ICU.
TextNodeunterstützt:- Ausrichtung
HorizontalFit: die Schrift wird bisminimumPixelSizeverkleinert- Umbruch
- einfacher Rich Text (
<b>,<br>, Entities) - gestylte Runs (
setRuns: Gewicht, kursiv, Farbe, Größe) lineHeightwie in QML (Faktor auf die Zeilenhöhe der Schrift). Den Zusatzabstand setzt Qt unter jede Zeile. Skia verteilt ihn, deshalb gibt eskDisableFirstAscentund die Korrektur inimplicitHeight().
- Fehlen einer Schrift Zeichen, sucht Skia unter Linux über fontconfig in den Systemschriften nach Ersatz, wie Qt.
- Surfaces nutzen Graustufen-Antialiasing ohne Gamma- und Kontrastanhebung (
SkSurfaceProps(0, kUnknown, 0, 1)). Mit den Skia-Standardwerten wirkt weiße Schrift auf Rot deutlich fetter als in Qt.
Video
VideoPlayerdemuxt mit libwebm (mkvparser) und dekodiert mit libvpx in einem eigenen Thread.- Die Queue fasst 4 Bilder, danach wartet der Thread.
- libvpx dekodiert mit bis zu 4 Threads.
- Der UI-Thread führt eine Uhr pro Datei und zeigt das jeweils fällige Bild.
- Der Wechsel zur nächsten Datei ist ein harter Schnitt.
- Endlosschleife über die Playlist.
VideoNodelädt die I420-Ebenen als drei A8-Texturen hoch (SkImages::TextureFromYUVAPixmaps). Die Wandlung nach RGB (BT.601 limited) macht die GPU. Ohne GPU, also bei Screenshots, wandelt eine einfache CPU-Routine.- Die Render-Schleife ist auf 60 fps begrenzt, falls der Treiber kein VSync hat.
- Mit
--debugloggt sie alle 5 s die fps. - Der Player loggt die angezeigten Videobilder pro Sekunde.
- Mit
- Die erreichbare Bildrate hängt von Zielgerät, Grafiktreiber, Szene und Video ab.
Offscreen-Screenshots
Application::renderToPng(root, width, height, file) rendert eine Szene ohne Fenster als PNG. Das Hello-Beispiel und die Galerie stellen passende Kommandozeilen bereit:
bash
build/dev/examples/hello/aura-hello --render hello.png
build/dev/examples/demo/aura-demo --render-all /tmp/aura-gallery
build/dev/tools/aura-view/aura-view examples/hello --render /tmp/hello.png
Siehe aura-view für Preview-Galerien und Tests für visuelle Vergleiche.
Dokumentationsquelle:
docs/graphics.md · Markdown herunterladen