[PR #791] fix(buffer): preserve wide characters under translucent overlays #1566

Open
opened 2026-03-14 09:43:34 +03:00 by kerem · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/anomalyco/opentui/pull/791
Author: @aarcamp
Created: 3/8/2026
Status: 🔄 Open

Base: mainHead: fix-wide-char-alpha-blending


📝 Commits (1)

  • c84ec57 fix(buffer): preserve wide characters under translucent overlays

📊 Changes

4 files changed (+1064 additions, -36 deletions)

View changed files

packages/core/docs/wide-grapheme-alpha-blending-tests.svg (+309 -0)
📝 packages/core/src/zig/buffer.zig (+156 -34)
📝 packages/core/src/zig/lib.zig (+1 -1)
📝 packages/core/src/zig/tests/buffer_test.zig (+598 -1)

📄 Description

blendCells() refused to preserve characters with display width > 1, so any semi-transparent overlay (e.g. a tinted box) replaced grapheme start cells with spaces, making CJK/emoji invisible underneath. Now we preserve underlying wide graphemes when translucent space overlays land on them instead of replacing continuation cells with spaces or tinting only the touched cell.

Make setCellWithAlphaBlending() the source of truth for preserved wide-grapheme alpha blending: resolve the full grapheme span from any touched cell, blend once from the canonical start cell, and apply the resulting style uniformly across the span. Return the rightmost written x-coordinate so sequential writers can coordinate around preserved spans.

Introduce a per-pass BlendCursor for left-to-right alpha-writing loops so repeated space writes inside the same preserved span are skipped without leaking state across separate draw calls. Use it in fillRect(), drawText(), drawFrameBuffer(), drawTextBufferInternal(), and the image/grayscale compositors.

Respect scissor clipping during span style propagation and add native regression coverage for preservation, partial overlap, mixed space/character overwrites, multi-space passes, multiple graphemes, repeated overlays, and clipped-then-unclipped writes.

Discovered this while adding a feature that detects uid == 0 use and applies a tinted red overlay to the terminal.

Sample text file with wide chars:

cjk-overlay-none

With overlay applied, before the fix (wide chars disappear):

cjk-overlay-pre-fix

With overlay applied, after the fix (wide chars remain visible as expected):

cjk-overlay-post-fix

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/anomalyco/opentui/pull/791 **Author:** [@aarcamp](https://github.com/aarcamp) **Created:** 3/8/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `fix-wide-char-alpha-blending` --- ### 📝 Commits (1) - [`c84ec57`](https://github.com/anomalyco/opentui/commit/c84ec573e9d9bcf0d2ab3fda0010472d22170a0b) fix(buffer): preserve wide characters under translucent overlays ### 📊 Changes **4 files changed** (+1064 additions, -36 deletions) <details> <summary>View changed files</summary> ➕ `packages/core/docs/wide-grapheme-alpha-blending-tests.svg` (+309 -0) 📝 `packages/core/src/zig/buffer.zig` (+156 -34) 📝 `packages/core/src/zig/lib.zig` (+1 -1) 📝 `packages/core/src/zig/tests/buffer_test.zig` (+598 -1) </details> ### 📄 Description blendCells() refused to preserve characters with display width > 1, so any semi-transparent overlay (e.g. a tinted box) replaced grapheme start cells with spaces, making CJK/emoji invisible underneath. Now we preserve underlying wide graphemes when translucent space overlays land on them instead of replacing continuation cells with spaces or tinting only the touched cell. Make setCellWithAlphaBlending() the source of truth for preserved wide-grapheme alpha blending: resolve the full grapheme span from any touched cell, blend once from the canonical start cell, and apply the resulting style uniformly across the span. Return the rightmost written x-coordinate so sequential writers can coordinate around preserved spans. Introduce a per-pass BlendCursor for left-to-right alpha-writing loops so repeated space writes inside the same preserved span are skipped without leaking state across separate draw calls. Use it in fillRect(), drawText(), drawFrameBuffer(), drawTextBufferInternal(), and the image/grayscale compositors. Respect scissor clipping during span style propagation and add native regression coverage for preservation, partial overlap, mixed space/character overwrites, multi-space passes, multiple graphemes, repeated overlays, and clipped-then-unclipped writes. Discovered this while adding a feature that detects uid == 0 use and applies a tinted red overlay to the terminal. Sample text file with wide chars: <img width="453" height="302" alt="cjk-overlay-none" src="https://github.com/user-attachments/assets/f09a28da-5c65-4c66-9b01-14dee5f8faec" /> With overlay applied, before the fix (wide chars disappear): <img width="453" height="322" alt="cjk-overlay-pre-fix" src="https://github.com/user-attachments/assets/a90b7930-20e4-405e-ae33-5201b301f52e" /> With overlay applied, after the fix (wide chars remain visible as expected): <img width="450" height="321" alt="cjk-overlay-post-fix" src="https://github.com/user-attachments/assets/60cfaf1f-eab4-44b1-8c29-386a77c7aaa9" /> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/opentui#1566
No description provided.