Skip to content

Enable processing pixel lengths as float #415

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 25 commits into
base: master
Choose a base branch
from

Conversation

DRKV333
Copy link

@DRKV333 DRKV333 commented Jul 1, 2025

Hey, this is still very much WIP, but I would like to get some feedback on it.

I've started work on converting the whole library to use float rather than int when calculating the length of things in pixels, to avoid compounding rounding errors. This is something users have requested a few times by now. The general plan was:

  • Introduce a new pixel_t typedef in types.h to represent pixel sizes, just pointing to int initially
  • Look at every usage if int everywhere, and if it represents a size in pixels, replace it with pixel_t
  • Add a preprocessor define, switchable through cmake, to have pixel_t point to float instead
  • Look at compiler warnings and fix any conversion issues

At this point I have looked through the code and replaced everything. I've also tested it with float and it does compile, but there are a few things to fix still.

I do have a few questions though on how to proceed:

Are you sure you would want to support both int and float pixel types?
It seems like this would be quite annoying. Both variants would have to be tested, or if one isn't tested it might break. I'm not sure what the benefit of int would be (other than backward compatibility maybe). A page would have to be several million pixels large before precision becomes a problem. The performance seems about the same from what I've measured.

How should the API be handled?
I think it would make the most sense to keep the pixels as float all the way until the document container, and then that can decide what to do with them. But then this would be a breaking change. From what I can tell the API surface is basically most things in the include folder, and a lot of that has changed. In theory it might be possible to keep having the API use int by duplicating a bunch of structures, but that would be very painful.
Right now, with pixel_t being an alias for int, there is no breakage, so keeping that the default and adding a switch could solve this problem.
Also there are some structures whose name doesn't make sense now, like typed_int and int_int_cache. These also always store pixel sizes so they now use pixel_t. But they are part of the API surface so renaming them would be breaking.

I've also replaced font size and media query related pixel values with pixel_t for consistency, though I'm not sure how necessary that is really.

As a demonstration consider an example similar to #14:

HTML
<!DOCTYPE html>
<html>
    <head>
        <title>Hello</title>
        <style>
            .expected {
                position: absolute;
                height: 100px;
                width: 151px;
                top: 50px;
                background-color: red;
            }

            .test1 {
                position: relative;
                height: 100px;
                width: 50px;
                top: 10px;
                left: 33.3px;
                background-color: blue;
            }

            .test2 {
                position: relative;
                height: 90px;
                width: 56.9px;
                left: 60.8px;
                background-color: green;
            }
        </style>
    </head>
    <body>
        <div class="test1">
            <div class="test2">
            </div>
        </div>

        <div class="expected"></div>
    </body>
</html>

int:
image

float:
image

The right edge of the red box should line up with the right edge of the green box, but with int there is a noticeable gap. Using float fixes this.

Closes #47
Closes #14

@tordex
Copy link
Member

tordex commented Jul 1, 2025

This is very impressive work!

Are you sure you would want to support both int and float pixel types?

I think the float support is enough.

How should the API be handled?
I think it would make the most sense to keep the pixels as float all the way until the document container, and then that can decide what to do with them.

You are right, this is the only painless way. As for API break - the library is in beta status. API is changed frequently. The latest master is not compatible with the latest numerical version because of new features and removing legacy. It is OK.

Also there are some structures whose name doesn't make sense now, like typed_int and int_int_cache. These also always store pixel sizes so they now use pixel_t. But they are part of the API surface so renaming them would be breaking.

containing_block_context::typed_int is internal type. It can be renamed to typed_pixel for example.
int_int_cache - the same. Can be renamed to ```pixel_cache`` for example.
These names don't make sense now, agree.

It is interesting, how many tests will fail after switching to the floats?

@DRKV333
Copy link
Author

DRKV333 commented Jul 2, 2025

I think the float support is enough.

Cool, should I still keep the pixel_t alias then, or just use float everywhere? I guess it's maybe worth keeping. It's neater, and somebody might want to change it to double in the future, or something.

As for API break - the library is in beta status. API is changed frequently.

Oh, alright, I didn't realize that.

It is interesting, how many tests will fail after switching to the floats?

Yeah, so there are 242 failures right now.
In some of them it looks like there is some extra anti-aliasing. Rather than a clean edge there is a row of pixels that is between the two colors. Which would make sense with floats, but I haven't updated the document containers yet to pixel_t everywhere, so I'm not sure.
There are also tests where text appears to be at a noticeably different place vertically. That's a bit strange, I'll have to investigate it further.

@tordex
Copy link
Member

tordex commented Jul 2, 2025

I guess it's maybe worth keeping. It's neater, and somebody might want to change it to double in the future, or something.

You are right, using pixel_t is the best solution.

Yeah, so there are 242 failures right now.

It is good result 😀 I've expected more failed tests. Anyway the container_cairo and litehtml code contains some workarounds for int rounding issues.

@tordex
Copy link
Member

tordex commented Jul 30, 2025

@DRKV333, please let me know when you finish with this PR. Don't worry about failed tests, I'll update them by myself.

@DRKV333
Copy link
Author

DRKV333 commented Jul 31, 2025

Cool, thanks. I would still like to at least look through all the test failures, to make sure I didn't break anything. I can probably finish that this weekend.

There is one problem I've found, where border drawing seems to be a bit off, for instance in border-top-width-036.htm:
border-top-width-036 htm-FAILED

@DRKV333
Copy link
Author

DRKV333 commented Aug 3, 2025

So, in a lot of test failures, the difference from the expected image is basically imperceptible. Some of these happen because of lines of text being shifted down slightly, due to less rounding error in line height calculations.

Any test that has list markers is also falling. Makers appear a bit smaller now. This is because their size is a third of the line height, which is usually a fraction. The exact size of the marker doesn't really matter anyway, so this should be fine.

Here's a summary of test failures worth looking at:

background-repeat-applies-to-008.htm
border-bottom-width-014.htm
border-bottom-width-036.htm
border-bottom-width-047.htm
border-top-width-014.htm
border-top-width-036.htm
border-top-width-047.htm
c43-center-000.htm
margin-bottom-067.htm
margin-bottom-068.htm
margin-bottom-113.htm
margin-left-067.htm
margin-left-068.htm
padding-left-036.htm
padding-left-047.htm
padding-right-047.htm

These are actually failing, in that the output contains "red pixels". This happens because of weirdness in fractional pixel rendering. e.g. in border-bottom-width-014.htm there is a 1pt red background that is supposed to be fully covered by a 1pt solid black border. That 1pt ends up being 1.333333px, which renders as 1 line of solid pixels, and then a line of 1/3 translucent pixels. The translucent pixels then alpha-blend instead of covering each other, so there is a bit of dark red on the page.

After going trough the spec it looks like this behavior is not correct, and at least borders have to be rounded in a particular way. (https://www.w3.org/TR/css-values-4/#lengths) So I'll still need to fix this. This doesn't apply to heights though, so that test will be even more wrong. In this case implementations get to decide what to do, so I guess just not rounding and letting the container deal with the fractions is still correct. Though in practice browsers do seem to round things to whole pixels, but the exact behavior varies.

c414-flt-wrap-000.htm

See #416, this is failing, thought not directly because of this PR.

border-spacing-047.htm
c544-valgn-002.htm
c544-valgn-003.htm
c544-valgn-004.htm
text-indent-091.htm
text-indent-092.htm
white-space-collapsing-003.htm

These tests use the "Ahem" font. They now have noticeable spaces between characters that weren't there before. I think this is actually correct, it looks similar in other browsers.

font-matching-rule-012.htm

This test says that the size for scalable fonts should be rounded to the nearest whole pixel. I did not find this in the standard though. All that says is that fonts are typically rounded to the nearest whole pixel. (https://www.w3.org/TR/css-fonts-4/#font-style-matching, point 4.4) That is true, that's how other browsers do it, but not rounding should be fine as well.

@DRKV333
Copy link
Author

DRKV333 commented Aug 5, 2025

Ok, I think I'm ready. I've ended up adding some rounding, right before draw operations. This is the way others do it, and it does fix a lot of issues with things being blurry. It also resolves the "red pixel visible" test failures, and reduces the number of failed tests to 375, from the 472 it was before.

@DRKV333 DRKV333 marked this pull request as ready for review August 5, 2025 18:19
@tordex
Copy link
Member

tordex commented Aug 6, 2025

Thank you very much! I'll merge it shortly.

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.

Feature request: Make coordinate types configurable as float percentages are rounded down causing pixel offsets
2 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy