While IMO, the change makes sense on its own as flattening this function
will just duplicate the code from `draw_glyph()` with no benefits, this
is not what motivated this patch.
When compiling with debug information and ASAN, GCC 13.2 would issue
this warning:
Userland/Libraries/LibGfx/Painter.cpp: In member function ‘void Gfx::Pai
nter::draw_glyph(Gfx::FloatPoint, u32, Gfx::Color)’:
Userland/Libraries/LibGfx/Painter.cpp:1354:14: note: variable tracking s
ize limit exceeded with ‘-fvar-tracking-assignments’, retrying without
1354 | FLATTEN void Painter::draw_glyph(FloatPoint point, u32 code_poin
t, Color color)
| ^~~~~~~
From what I've read online, this is caused by some limit on the number
of symbols in the compiler's internal data structures. People at Google
have fixed this warning by splitting functions:
https://codereview.chromium.org/1164893003
While getting us rid of the warning, it also drastically improves
compilation time. Going from 1min27 to 59s on my machine.
This changes the splitting to use a stack, which ensures the resulting
line segments follow the path in order. This will be important for SVG
`<textPath>`s which place text along a path.
This commit un-deprecates DeprecatedString, and repurposes it as a byte
string.
As the null state has already been removed, there are no other
particularly hairy blockers in repurposing this type as a byte string
(what it _really_ is).
This commit is auto-generated:
$ xs=$(ack -l \bDeprecatedString\b\|deprecated_string AK Userland \
Meta Ports Ladybird Tests Kernel)
$ perl -pie 's/\bDeprecatedString\b/ByteString/g;
s/deprecated_string/byte_string/g' $xs
$ clang-format --style=file -i \
$(git diff --name-only | grep \.cpp\|\.h)
$ gn format $(git ls-files '*.gn' '*.gni')
Previously, we determined the positions of glyphs for each text run at
the time of painting, which constituted a significant portion of the
painting process according to profiles. However, since we already go
through each glyph to figure out the width of each fragment during
layout, we can simultaneously gather data about the position of each
glyph in the layout phase and utilize this information in the painting
phase.
I had to update expectations for a couple of reference tests. These
updates are due to the fact that we now measure glyph positions during
layout using a 1x font, and then linearly scale each glyph's position
to device pixels during painting. This approach should be acceptable,
considering we measure a fragment's width and height with an unscaled
font during layout.
This change separates a part of the `draw_text_run()` function, which
is responsible for calculating the positions for glyphs that need to be
painted, into a separate function called `get_glyph_run()`.
It is a part of the preparation for text run painting using OpenGL,
where we can't immediately blit glyph bitmaps but instead need to
prepare a sequence of quads for them in advance.
This updates fonts so rather than rastering directly to a bitmap, you
can extract paths for glyphs. This is then used to implement a
Gfx::Path::text("some text", font) API, that if given a vector font
appends the path of the text to your Gfx::Path. This then allows
arbitrary manipulation of the text (rotation, skewing, etc), paving the
way for Word Art in Serenity.
Unlike all other primitives elliptical arcs are non-trivial to
manipulate, it's tricky to correctly apply a Gfx::AffineTransform to
them. Prior to this change, Path::copy_transformed() was still
incorrectly applying transforms such as flips and skews to arcs.
This patch very closely approximates arcs with cubic beziers (I can not
visually spot any differences), which can then be easily and correctly
transformed in all cases.
Most of the maths here was taken from:
https://mortoray.com/rendering-an-svg-elliptical-arc-as-bezier-curves/
(which came from https://www.joecridge.me/content/pdf/bezier-arcs.pdf,
now a dead link).
For some reason, we were decoding the source color twice for every pixel
in the inner-most loop of `blit_filtered`. This makes sure we only
decode the source color once, and rearranges the code to improve
readability.
For my synthetic font rendering benchmark, this improves glyph rendering
performance by ~9%.
for_each_line_segment_on_elliptical_arc() flips the start/end points
for negative theta deltas. When doing this we have to make sure the
line segments emitted swap the start/end points back, so that the
(correct) winding order can be calculated from them.
This makes nonzero fills not totally broken for a lot of SVGs.
The glyph bitmap is a grayscale image that is multiplied with the
requested color provided to `Gfx::Painter::draw_glyph()` to get the
final glyph bitmap that can be blitted.
Using `Gfx::Color::multiply()` is unnecessary however: by simply taking
the destination color and copying over the glyph bitmap's alpha value,
we can prevent four multiplications and divisions per pixel.
In an artifical benchmark I wrote, this improved glyph rendering
performance by ~9%.
Previously, calling `.right()` on a `Gfx::Rect` would return the last
column's coordinate still inside the rectangle, or `left + width - 1`.
This is called 'endpoint inclusive' and does not make a lot of sense for
`Gfx::Rect<float>` where a rectangle of width 5 at position (0, 0) would
return 4 as its right side. This same problem exists for `.bottom()`.
This changes `Gfx::Rect` to be endpoint exclusive, which gives us the
nice property that `width = right - left` and `height = bottom - top`.
It enables us to treat `Gfx::Rect<int>` and `Gfx::Rect<float>` exactly
the same.
All users of `Gfx::Rect` have been updated accordingly.
We were performing a check whether source pixels would fall into a
clipped rect too early. Since we already clamp the resulting source
coordinates to the clipped rect, we can just remove this code.
Box sampling is a scaling algorithm that averages all the pixels that
form the source for the target pixel. For example, if you would resize a
9x9 image to 3x3, each target pixel would encompass a 3x3 pixel area in
the source image.
Box sampling is a near perfect scaling algorithm for downscaling. When
upscaling with this algorithm, the result is similar to nearest neighbor
or smooth pixels.
We were performing a check whether source pixels would fall into a
clipped rect too early. Since we already clamp the resulting source
coordinates to the clipped rect, we can just remove this code.
Box sampling is a scaling algorithm that averages all the pixels that
form the source for the target pixel. For example, if you would resize a
9x9 image to 3x3, each target pixel would encompass a 3x3 pixel area in
the source image.
Box sampling is a near perfect scaling algorithm for downscaling. When
upscaling with this algorithm, the result is similar to nearest neighbor
or smooth pixels.
...and instead assume it's BGRx8888 or BGRA8888, for now. Always
treating the target as BGRA8888 leads to the alpha channel being
interpreted incorrectly sometimes (as can be seen with WindowServer
overlays).
Fixes#18749
An oval approximated by quadratic bezier curves ended up with 2048 line
segments when rasterized to a bitmap of around 35 by 35 pixels, which
seems a bit much. :^)
By increasing the tolerance by an order of magnitude, that same oval is
now split up into 512 line segments, which is still more than enough for
a high quality render.
This now applies clipping to the destination bounding box before
painting, which cuts out a load of clipped computation and allows
using the faster set_physical_pixel() method.
Alongside this it also combines the source_transform and
inverse_transform outside the hot loop (which might cut things down
a little).
The `destination_quad.contains()` check is also removed in favour
of just checking if the mapped point is inside the source rect,
which takes less work to compute than checking the bounding box.
This takes this method down from 98% of the time to 10% of the
time when painting Google Street View (with no obvious issues).
This is mostly a simple grayscale bilinear scale, with an extra step
of computing the distance and alpha with a little smoothing. This
can be used to paint more scalable UI elements/icons from rather
small distance fields. A tiny 16x16 SDF seems to do a decent job
for simple icons.
This commit replaces usages of `Color::interpolate()` with
`Color::mixed_with()` in image scaling functions. The latter
uses premultiplied alpha, which results in more visually
pleasing edges when images are scaled against a transparent
background.
These changes affect the SmoothPixels and BilinearBlend scaling modes
of `Painter::draw_scaled_bitmap()` as well as the `Bitmap::scaled()`
function.
Fixes#17153.
This makes all the code for fill_path() member functions of the painter,
and moves them into a new FillPathImplementation.cpp. This allows us
to avoid polluting Painter.h with implementation details, and makes
the edit, compile, retry loop much shorter.
This improves fill_path() performance by adding an API to the painter
that allows painting an entire scanline rather than just a pixel.
With this paths can be clipped a scanline at a time rather than each
pixel, removing a fair amount of checks.
Along with optimized clipping, this can now use a fast_u32_fill() to
paint all but the subpixels of a scanline if a solid color with no
alpha channel is used (which is quite common in SVGs).
This reduces scrolling around on svg.html from 21% in set_pixel() and
19% in fill_path() to just 7.8% in fill_path (with set_pixel()
eliminated). Now fill_path() is far from the slowest code when
scrolling the page.
This class had slightly confusing semantics and the added weirdness
doesn't seem worth it just so we can say "." instead of "->" when
iterating over a vector of NNRPs.
This patch replaces NonnullRefPtrVector<T> with Vector<NNRP<T>>.
Ultimately, we should find a way to route all emoji access through
the font code, but for now, this patch adds a special case for fonts
that are known to have embedded color bitmaps so we can test them.
This patch does three things:
- Font::has_color_bitmaps() (true if CBLC and CBDT are present)
- Glyph now knows when its bitmap comes from a color bitmap font
- Painter draws color bitmap glyphs with the appropriate scaling etc
This API is used by LibWeb's text painter. Bring it up to date with the
glyph width computations performed in draw_text_line() used by other GUI
applications.