Skip to content

fix(compositor): centrer les blocs de texte, ajuster leur plaque, et les sortir du zoom - #214

Merged
EtienneLescot merged 3 commits into
release/v1.8.0from
claude/text-block-padding-centering-865284
Jul 30, 2026
Merged

fix(compositor): centrer les blocs de texte, ajuster leur plaque, et les sortir du zoom#214
EtienneLescot merged 3 commits into
release/v1.8.0from
claude/text-block-padding-centering-865284

Conversation

@EtienneLescot

@EtienneLescot EtienneLescot commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Trois défauts de rendu sur les blocs de texte — sous-titres et annotations, qui partagent le même chemin (captionCuesToTextRegions projette les captions en annotations texte). Les deux premiers étaient macOS seulement, le troisième touche les deux backends.

1. Le bloc se collait en haut de sa boîte (macOS)

Rapport : « il y a une énorme marge en bas ». Mesuré sur une vraie bande de sous-titres (1536×238 en 1080p), avant correctif : 11 px d'encre au-dessus du texte, 180 px en dessous.

CTFrameDraw remplit son cadre du haut vers le bas, et le cadre couvrait toute la boîte. Direct2D n'avait pas le problème — il pose DWRITE_PARAGRAPH_ALIGNMENT_CENTER — macOS était le seul à ne pas centrer. Ça se voit surtout sur les sous-titres parce qu'une bande fait 22 % de la hauteur d'image (CAPTION_BAND_HEIGHT_PCT, volontairement généreuse pour absorber deux lignes) : la boîte est toujours bien plus haute que son texte.

macOS mesure maintenant le bloc (CTFramesetterSuggestFrameSizeWithConstraints) et block_layout en déduit un cadre centré. Après : 84 px / 84 px.

2. La plaque de fond couvrait la boîte entière (macOS)

Un CGContextFillRect sur toute la boîte, là où Direct2D la dessine sur DWRITE_TEXT_METRICS, donc ajustée au texte. Une bande de sous-titres était un pavé opaque de 22 % de la hauteur d'image.

La plaque épouse désormais le bloc, avec la marge et les coins arrondis du modèle de boîte de référence de l'app — le <span> que l'overlay DOM posait derrière le texte et son jumeau canvas (lib/exporter/annotationRenderer.ts) : padding: 0.1em 0.2em, border-radius: 4px. Windows reçoit la même plaque, au lieu des métriques nues.

Les trois nombres vivent dans un text_plate.rs partagé plutôt que recopiés de chaque côté : rien dans un rendu Windows ne signalerait qu'une marge macOS a bougé.

3. Les annotations suivaient le zoom (les deux backends)

Rapport : « les sous-titres étaient zoomés avec le screen recording ». Le contrat de SceneAnnotation est explicite :

deliberately NOT affected by the zoom crop — the overlay is a sibling of the element carrying the zoom transform, so annotations hold still while the content zooms underneath them.

Tant que le zoom se jouait dans la coupe source, s_dst tenait ce rôle gratuitement. L'issue #179 a rendu le zoom à la boîte — pour qu'il atteigne les bords du cadre au lieu de buter sur le padding — donc s_dst grandit et se déplace avec lui, et les deux compositeurs continuaient d'y ancrer les annotations.

plan_frame expose s_ann, la boîte écran avant le remap_box du zoom, c'est-à-dire le rect que l'app a résolu (layout.screenRect) et que l'overlay web reçoit comme conteneur. Effet de bord utile : la poignée de sélection DOM était déjà posée sur ce rect non zoomé, donc les pixels natifs et le cadre de sélection se recollent — ils divergeaient dès qu'un zoom était actif.

Vérification

macOScargo test -p openscreen-compositor --lib --tests vert : 106 unitaires + 5 d'intégration, goldens de géométrie compris. Le golden plan_frame reste inchangé au bit près (ajouter un champ ne déplace aucune des 21 valeurs épinglées). Tests ajoutés :

  • cinq qui rastérisent pour de vrai sur le device Metal — centrage, plaque ajustée, marge autour des glyphes, débordement, non-miroir ;
  • the_annotation_anchor_ignores_the_zoom, qui vérifie d'abord que le zoom agit bel et bien sur s_dst : sans cette assertion il passerait aussi si remap_box cessait de faire son travail, et ne prouverait rien ;
  • le modèle de boîte partagé, épinglé sur ses valeurs de référence.

Le test qui affirmait « le texte se dessine EN HAUT de sa boîte » est réécrit : il comparait les deux moitiés de la texture, ce qui ne veut plus rien dire une fois le bloc centré. Il compare maintenant les deux moitiés de la boîte englobante de l'encre, ce qui détecte le miroir sans dépendre du centrage.

L'app a été lancée avec un compositor_view.node rebuildé sur cette branche, pour valider le rendu en conditions réelles.

Windows — pas de machine sous la main. text_windows.rs type-checke pour x86_64-pc-windows-msvc sans warning, et les signatures FillRoundedRectangle / D2D1_ROUNDED_RECT ont été relues dans les bindings windows-0.58 vendorisés. Le rendu Windows lui-même n'est pas vérifié à l'œil — c'est le point à regarder en review.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ae6e6843-1c20-474d-b853-73d9ab716ffb

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

sepion02 added 2 commits July 30, 2026 18:04
…eur plaque épouse le texte

Rapport utilisateur : « il y a une énorme marge en bas » sur les blocs de sous-titres, en
rendu Metal. Mesuré sur une vraie bande (1536x238 en 1080p), avant correctif : 11 px d'encre
au-dessus du texte, **180 px en dessous**, et la plaque de fond couvrant la boîte ENTIÈRE.

Deux causes, toutes deux dans `text_macos.rs` :

* `CTFrameDraw` remplit son cadre du haut vers le bas, et le cadre couvrait toute la boîte —
  les lignes se collaient donc en haut. Direct2D n'avait pas le problème, il pose
  `DWRITE_PARAGRAPH_ALIGNMENT_CENTER`. macOS était le seul à ne pas centrer.
* le fond était un `CGContextFillRect` sur la boîte entière, là où Direct2D le dessine sur
  `DWRITE_TEXT_METRICS`, donc ajusté au texte.

Les deux se cumulent sur les sous-titres parce qu'une bande fait 22 % de la hauteur d'image
(`CAPTION_BAND_HEIGHT_PCT`, volontairement généreuse pour absorber deux lignes) : la boîte est
toujours bien plus haute que son texte. Les annotations texte passent par le même rastériseur,
d'où le même symptôme.

macOS mesure désormais le bloc (`CTFramesetterSuggestFrameSizeWithConstraints`) puis
`block_layout` en déduit le cadre — centré verticalement — et la plaque qui l'habille. Un texte
qui déborde de sa boîte part toujours du haut et se coupe en bas, plutôt que d'être rogné des
deux côtés.

Windows reçoit la même plaque : marge et coins arrondis, au lieu des métriques nues. Les trois
nombres qui décident de l'allure du bloc (0.2em / 0.1em / 4px à 48px de police, le modèle de
boîte du `<span>` de l'overlay DOM et de `lib/exporter/annotationRenderer.ts`) vivent dans un
`text_plate.rs` partagé : recopiés, ils dériveraient en silence, puisque rien dans un rendu
Windows ne signale qu'une marge macOS a bougé.

Après, sur la même bande : 84 px / 84 px verticalement, 502 px / 502 px horizontalement.

Vérification. macOS : `cargo test -p openscreen-compositor --lib --tests` vert (106 unitaires +
5 d'intégration, goldens de géométrie compris), dont cinq tests qui rastérisent pour de vrai sur
le device Metal — non-miroir, centrage, plaque ajustée, marge autour des glyphes, débordement.
Le test qui affirmait « le texte se dessine EN HAUT de sa boîte » est réécrit : il comparait les
deux moitiés de la TEXTURE, ce qui ne veut plus rien dire une fois le bloc centré ; il compare
maintenant les deux moitiés de la boîte englobante de l'encre, ce qui détecte le miroir sans
dépendre du centrage. Windows : pas de machine sous la main, mais `text_windows.rs` type-checke
pour `x86_64-pc-windows-msvc` sans warning, et les signatures `FillRoundedRectangle` /
`D2D1_ROUNDED_RECT` ont été relues dans les bindings `windows-0.58`. Le rendu Windows lui-même
n'est pas vérifié à l'œil.
Rapport utilisateur : « les sous-titres étaient zoomés avec le screen recording ». Ce n'est pas
un bug de sous-titres — les captions sont projetées en annotations texte par
`captionCuesToTextRegions` — mais une régression qui touche TOUTES les annotations, sur les
deux backends.

Le contrat de `SceneAnnotation` est explicite : « deliberately NOT affected by the zoom crop —
the overlay is a sibling of the element carrying the zoom transform, so annotations hold still
while the content zooms underneath them ». Tant que le zoom se jouait dans la coupe source,
`s_dst` tenait ce rôle gratuitement : la boîte écran restait le rect paddé. L'issue #179 a rendu
le zoom à la BOÎTE (pour qu'il atteigne les bords du cadre au lieu de buter sur le padding), donc
`s_dst` grandit et se déplace avec lui — et les deux compositeurs continuaient d'y ancrer les
annotations.

`plan_frame` expose donc `s_ann` : la boîte écran AVANT le `remap_box` du zoom, c'est-à-dire le
rect que l'app a résolu (`layout.screenRect`) et que l'overlay web reçoit comme conteneur.
`draw_annotations` s'y ancre des deux côtés. Effet de bord utile : la poignée de sélection DOM
était déjà posée sur ce `screenRect` non zoomé, donc les pixels natifs et le cadre de sélection
se recollent — ils divergeaient dès qu'un zoom était actif.

`the_annotation_anchor_ignores_the_zoom` épingle l'invariant sur la scène golden, avec et sans
région de zoom. Il vérifie d'abord que le zoom agit bel et bien sur `s_dst` — sans cette
assertion, le test passerait aussi si `remap_box` cessait de faire son travail, et ne prouverait
rien. Il tourne dans le job macOS ET dans le job Windows, comme le golden `plan_frame`, qui reste
inchangé au bit près : ajouter un champ ne déplace aucune des 21 valeurs épinglées.

`cargo test -p openscreen-compositor --lib --tests` vert (106 + 5).
@EtienneLescot
EtienneLescot force-pushed the claude/text-block-padding-centering-865284 branch from 8fface0 to a336449 Compare July 30, 2026 16:05
@EtienneLescot
EtienneLescot changed the base branch from main to release/v1.8.0 July 30, 2026 16:05
@EtienneLescot
EtienneLescot merged commit 71f92a9 into release/v1.8.0 Jul 30, 2026
11 checks passed
@EtienneLescot
EtienneLescot deleted the claude/text-block-padding-centering-865284 branch July 30, 2026 16:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants