Skip to content

Add ImDrawTextFlags_NoPixelSnap for subpixel accurate text rendering - #9417

Closed
petrihakkinen wants to merge 1 commit into
ocornut:masterfrom
petrihakkinen:petrih-no-snap-to-pixel-flag
Closed

Add ImDrawTextFlags_NoPixelSnap for subpixel accurate text rendering#9417
petrihakkinen wants to merge 1 commit into
ocornut:masterfrom
petrihakkinen:petrih-no-snap-to-pixel-flag

Conversation

@petrihakkinen

Copy link
Copy Markdown

Currently ImDrawList::AddText() internally snaps the given text position to integer pixels. This is problematic when using a font size with non-integer advance: Rendering text in multiple parts (e.g. "foo" followed by "bar") does not yield the same result as rendering the text in one go ("foobar"). With multiple parts every text fragment is floored individually, but when rendered as a single fragment, only the starting location is floored.

In order to fix this, this PR adds a new flag for disabling flooring in RenderText().

The PR is minimal to keep changes to the API as small as possible; the new flag is only supported by one overload of ImDrawList::AddText().

Related issues:

#2291
#3437
#791

@ocornut

ocornut commented May 29, 2026

Copy link
Copy Markdown
Owner

Thanks Petri. As discussed I'll see if I can come up with a short-term workaround.
In your copy I'd recommend keeping ImDrawTextFlags_CpuFineClip value unchanged to maximize compat with old/extension code passing a bool to RenderText().

ocornut added a commit that referenced this pull request May 29, 2026
…f AddText() coordinates for a given scope. (#3437, #9417, #2291)

This may evolve into a per-call ImDrawFlags option as well.
+ clip_rect.Max.y early out may be applied before truncation.
ocornut added a commit that referenced this pull request May 29, 2026
@ocornut

ocornut commented May 29, 2026

Copy link
Copy Markdown
Owner

After much consideration I decided for now to expose this as a ImDrawList state.

draw_list->Flags |= ImDrawListFlags_TextNoPixelSnap;
// (do your rendering)
draw_list->Flags &= ~ImDrawListFlags_TextNoPixelSnap;

Some reasoning:

  • In the case of animated/over 3d, this is likely desirable for a whole scope. This is analogous to existing _Fringe setting in ImDrawList (for scalable canvas) and e.g. ImDrawListFlags_AntiAliasedLines for a scope.
  • I realize Petri this is not your case but it won't make it harder to use (actually it should make it easier). Though in theory for your specific case, it would make logical sense that the info is pulled directly from the font. I think ultimately both are desirable: a per-font flag, and a DrawList scope flag.
  • It naturally means less API contact area (~2 lines for set/clear), bit easier to evolve/redesign if needed.
  • Usable from ImGui:: calls (vs using a per-cell flag).
  • Can always add per-call ImDrawFlags_NoPixelSnap later and/or specific ImFontFlags, but for now as the expectation is to revamp text functions I'd prefer not sticking flags to an existing long signature bounds to be changed.

Closing this as hopefully solved but don't hesitate to reach out if not.

@petrihakkinen

Copy link
Copy Markdown
Author

Thank you! This should resolve our issue.

We will probably create a wrapper for AddText with the flag as argument though. Our main use case is Lua and we do live coding extensively. Exposing this as argument means that we don't have to worry about a script forgetting to reset the state. Also, on error we would need to reset any global state so that it won't leak to other places when we resume after error.

@petrihakkinen

Copy link
Copy Markdown
Author

Minor note: ImDrawListFlags_TextNoPixelSnap is currently tagged [Internal]. Shouldn't this be a public flag?

Our Lua binding generator does not expose flags marked with [Internal], but we can add a special case...

@petrihakkinen

Copy link
Copy Markdown
Author

I've merged this change to our code base and can confirm it works.

@ocornut

ocornut commented Jun 1, 2026

Copy link
Copy Markdown
Owner

Minor note: ImDrawListFlags_TextNoPixelSnap is currently tagged [Internal]. Shouldn't this be a public flag?

Removed the comment, indeed. While toying with the feature I first assumed it would be temporarily marked as such, but since it is advertised in the changelog it shouldn't.

We will probably create a wrapper for AddText with the flag as argument though. Our main use case is Lua and we do live coding extensively. Exposing this as argument means that we don't have to worry about a script forgetting to reset the state. Also, on error we would need to reset any global state so that it won't leak to other places when we resume after error.

Understood. In theory should should be exposed as stacks but the flags are extremely rarely used for now, and I'd rather not add another alloc to each draw-list.

Note some error recovery feature exist for stack related issues: https://github.com/ocornut/imgui/wiki/Error-Handling, which indeed will not recover this specific bit. If you implement a recovery handler for Lua script you might decide to e.g. backup/restore specific state. Realistically speaking I would assume you are not wildly overwriting this flag, is your text editor in Lua or C++?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants