Skip to content

Commit b197f98

Browse files
committed
Prevent gaps when rendering fractionally sized elements, resolves #438
Previously, when placing elements border-to-border one could often see 1px gaps between the elements. This was often the case if their position or size was fractionally sized. Which is a relatively common situation, particularly when using dp-dependent layout and scaling the dp-ratio by a fractional size. To address this, we adjust the rounded/rendered sizes of elements based on their absolute position, in such a way that the bottom-right of one element exactly matches the top-left of the next element. One implication of this is that the size of the element depends on its absolute position. Every time the element's size changes, its geometry needs to be re-generated. However, this isn't as bad as it sounds. In fact, as long as we move or scroll elements at integer pixel intervals, then its effective size won't change. Both element scrolling and the handle element already rounded to integer pixels, so we almost never need to re-generate geometry when interacting with those. With that said, changing absolute coordinates manually (top/right/bottom/left) without rounding will typically cause a lot of geometry re-generation. When rendering, we effectively have to match the position and size of the background in a way that replicates how the layout engine decides to place the next element. Due to floating-point precision, there are still some rare cases where we will see gaps. This is because the result of floating-point operations depend on the order of the operations (being non-associative). We try to match the layout engine in the sense that we add small relative values before we add the large absolute value, and that seemed to help a lot, but this will never match exactly. A more robust solution would use fixed-point arithmetic for layout and rendering. However, this commit alone should cover at least 99% of gaps. Adds RenderBox class which represents the data needed to generate a pixel-accurate mesh from an element's box. The element can construct a render box which can be submitted to the MeshUtilities to generate a background and border mesh. The background and decorators have been updated to use the RenderBox. Some tests are added to ensure that geometry is not re-generated excessively due to this change. Benchmarks show no measurable performance regression. This commit fixes several 1px-issues: - Gap of 1px between border or backgrounds of neighboring elements. - Overlap of 1px between border or backgrounds of neighboring elements. - Table cell backgrounds 1px over the table border. - Clipping area off by 1px compared to border. Breaking changes: - Changed the signature of `MeshUtilities::GenerateBackground` and `MeshUtilities::GenerateBackgroundBorder`. They now take a `RenderBox` class as input. One can use the new `Element::GetRenderBox` function to construct it. - Changed `ComputedValues::border_radius` to return an array instead of Vector4f.
1 parent 3c7d0bd commit b197f98

25 files changed

+512
-183
lines changed

Include/RmlUi/Core/ComputedValues.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
#include "Animation.h"
3232
#include "Element.h"
33+
#include "RenderBox.h"
3334
#include "StyleTypes.h"
3435
#include "Types.h"
3536
#include <cfloat>
@@ -213,7 +214,7 @@ namespace Style {
213214
explicit ComputedValues(Element* element) : element(element) {}
214215

215216
// clang-format off
216-
217+
217218
// -- Common --
218219
LengthPercentageAuto width() const { return LengthPercentageAuto(common.width_type, common.width_value); }
219220
LengthPercentageAuto height() const { return LengthPercentageAuto(common.height_type, common.height_value); }
@@ -248,7 +249,7 @@ namespace Style {
248249
Colourb border_bottom_color() const { return common.border_bottom_color; }
249250
Colourb border_left_color() const { return common.border_left_color; }
250251
bool has_decorator() const { return common.has_decorator; }
251-
252+
252253
// -- Inherited --
253254
String font_family() const;
254255
String cursor() const;
@@ -301,7 +302,7 @@ namespace Style {
301302
float border_top_right_radius() const { return (float)rare.border_top_right_radius; }
302303
float border_bottom_right_radius() const { return (float)rare.border_bottom_right_radius; }
303304
float border_bottom_left_radius() const { return (float)rare.border_bottom_left_radius; }
304-
Vector4f border_radius() const { return {(float)rare.border_top_left_radius, (float)rare.border_top_right_radius,
305+
CornerSizes border_radius() const { return {(float)rare.border_top_left_radius, (float)rare.border_top_right_radius,
305306
(float)rare.border_bottom_right_radius, (float)rare.border_bottom_left_radius}; }
306307
Clip clip() const { return rare.clip; }
307308
Drag drag() const { return rare.drag; }
@@ -315,7 +316,7 @@ namespace Style {
315316
bool has_filter() const { return rare.has_filter; }
316317
bool has_backdrop_filter() const { return rare.has_backdrop_filter; }
317318
bool has_box_shadow() const { return rare.has_box_shadow; }
318-
319+
319320
// -- Assignment --
320321
// Common
321322
void width (LengthPercentageAuto value) { common.width_type = value.type; common.width_value = value.value; }

Include/RmlUi/Core/Element.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "Header.h"
3636
#include "ObserverPtr.h"
3737
#include "Property.h"
38+
#include "RenderBox.h"
3839
#include "ScriptInterface.h"
3940
#include "ScrollTypes.h"
4041
#include "StyleTypes.h"
@@ -158,6 +159,11 @@ class RMLUICORE_API Element : public ScriptInterface, public EnableObserverPtr<E
158159
/// @param[out] offset The offset of the box relative to the element's border box.
159160
/// @return The requested box.
160161
const Box& GetBox(int index, Vector2f& offset);
162+
/// Returns one of the render boxes describing how to generate the geometry for the corresponding element's box.
163+
/// @param[in] fill_area The box area that acts as the background, or fill, of the render box.
164+
/// @param[in] index The index of the desired box, with 0 being the main box. If outside of bounds, the main render box will be returned.
165+
/// @return The requested render box.
166+
RenderBox GetRenderBox(BoxArea fill_area = BoxArea::Padding, int index = 0);
161167
/// Returns the number of boxes making up this element's geometry.
162168
/// @return the number of boxes making up this element's geometry.
163169
int GetNumBoxes();
@@ -678,6 +684,7 @@ class RMLUICORE_API Element : public ScriptInterface, public EnableObserverPtr<E
678684

679685
void DirtyAbsoluteOffset();
680686
void DirtyAbsoluteOffsetRecursive();
687+
void UpdateAbsoluteOffsetAndRenderBoxData();
681688
void UpdateOffset();
682689
void SetBaseline(float baseline);
683690

@@ -728,6 +735,7 @@ class RMLUICORE_API Element : public ScriptInterface, public EnableObserverPtr<E
728735

729736
bool offset_fixed;
730737
bool absolute_offset_dirty;
738+
bool rounded_main_padding_size_dirty : 1;
731739

732740
bool dirty_definition : 1; // Implies dirty child definitions as well.
733741
bool dirty_child_definitions : 1;
@@ -770,6 +778,7 @@ class RMLUICORE_API Element : public ScriptInterface, public EnableObserverPtr<E
770778
Vector2f relative_offset_position; // the offset of a relatively positioned element
771779

772780
Vector2f absolute_offset;
781+
Vector2f rounded_main_padding_size;
773782

774783
// The offset this element adds to its logical children due to scrolling content.
775784
Vector2f scroll_offset;

Include/RmlUi/Core/MeshUtilities.h

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@
3030
#define RMLUI_CORE_MESHUTILITIES_H
3131

3232
#include "Header.h"
33+
#include "RenderBox.h"
3334
#include "Types.h"
3435

3536
namespace Rml {
3637

37-
class Box;
3838
struct Mesh;
3939

4040
/**
@@ -43,50 +43,44 @@ struct Mesh;
4343
*/
4444
class RMLUICORE_API MeshUtilities {
4545
public:
46-
/// Generates a quad from a position, size and colour.
46+
/// Generates a quad from a position, size and color.
4747
/// @param[out] mesh A mesh to append the generated vertices and indices into.
4848
/// @param[in] origin The origin of the quad to generate.
4949
/// @param[in] dimensions The dimensions of the quad to generate.
50-
/// @param[in] colour The colour to be assigned to each of the quad's vertices.
51-
static void GenerateQuad(Mesh& mesh, Vector2f origin, Vector2f dimensions, ColourbPremultiplied colour);
52-
/// Generates a quad from a position, size, colour and texture coordinates.
50+
/// @param[in] color The color to be assigned to each of the quad's vertices.
51+
static void GenerateQuad(Mesh& mesh, Vector2f origin, Vector2f dimensions, ColourbPremultiplied color);
52+
/// Generates a quad from a position, size, color and texture coordinates.
5353
/// @param[out] mesh A mesh to append the generated vertices and indices into.
5454
/// @param[in] origin The origin of the quad to generate.
5555
/// @param[in] dimensions The dimensions of the quad to generate.
56-
/// @param[in] colour The colour to be assigned to each of the quad's vertices.
56+
/// @param[in] color The color to be assigned to each of the quad's vertices.
5757
/// @param[in] top_left_texcoord The texture coordinates at the top-left of the quad.
5858
/// @param[in] bottom_right_texcoord The texture coordinates at the bottom-right of the quad.
59-
static void GenerateQuad(Mesh& mesh, Vector2f origin, Vector2f dimensions, ColourbPremultiplied colour, Vector2f top_left_texcoord,
59+
static void GenerateQuad(Mesh& mesh, Vector2f origin, Vector2f dimensions, ColourbPremultiplied color, Vector2f top_left_texcoord,
6060
Vector2f bottom_right_texcoord);
6161

6262
/// Generates the geometry required to render a line.
6363
/// @param[out] mesh A mesh to append the generated vertices and indices into.
6464
/// @param[in] position The top-left position the line.
65-
/// @param[in] position The size of the line.
65+
/// @param[in] size The size of the line.
6666
/// @param[in] color The color to draw the line in.
6767
static void GenerateLine(Mesh& mesh, Vector2f position, Vector2f size, ColourbPremultiplied color);
6868

69-
/// Generates the geometry for an element's background and border, with support for the border-radius property.
69+
/// Generates the geometry for an element's background and border, with support for border-radius.
7070
/// @param[out] mesh A mesh to append the generated vertices and indices into.
71-
/// @param[in] box The box which determines the background and border geometry.
72-
/// @param[in] offset Offset the position of the generated vertices.
73-
/// @param[in] border_radius The border radius in pixel units in the following order: top-left, top-right, bottom-right, bottom-left.
74-
/// @param[in] background_colour The colour applied to the background, set alpha to zero to not generate the background.
75-
/// @param[in] border_colours A four-element array of border colors in top-right-bottom-left order.
71+
/// @param[in] render_box The render box which determines the background and border geometry.
72+
/// @param[in] background_color The color applied to the background, set alpha to zero to not generate the background.
73+
/// @param[in] border_colors A four-element array of border colors in top-right-bottom-left order.
7674
/// @note Vertex positions are relative to the border-box, vertex texture coordinates are default initialized.
77-
static void GenerateBackgroundBorder(Mesh& mesh, const Box& box, Vector2f offset, Vector4f border_radius, ColourbPremultiplied background_colour,
78-
const ColourbPremultiplied border_colours[4]);
75+
static void GenerateBackgroundBorder(Mesh& mesh, const RenderBox& render_box, ColourbPremultiplied background_color,
76+
const ColourbPremultiplied border_colors[4]);
7977

8078
/// Generates the background geometry for an element's area, with support for border-radius.
8179
/// @param[out] mesh A mesh to append the generated vertices and indices into.
82-
/// @param[in] box The box which determines the background geometry.
83-
/// @param[in] offset Offset the position of the generated vertices.
84-
/// @param[in] border_radius The border radius in pixel units in the following order: top-left, top-right, bottom-right, bottom-left.
85-
/// @param[in] colour The colour applied to the background.
86-
/// @param[in] area Either the border, padding or content area to be filled.
80+
/// @param[in] render_box The render box which determines the background geometry.
81+
/// @param[in] color The color applied to the background.
8782
/// @note Vertex positions are relative to the border-box, vertex texture coordinates are default initialized.
88-
static void GenerateBackground(Mesh& mesh, const Box& box, Vector2f offset, Vector4f border_radius, ColourbPremultiplied colour,
89-
BoxArea area = BoxArea::Padding);
83+
static void GenerateBackground(Mesh& mesh, const RenderBox& render_box, ColourbPremultiplied color);
9084

9185
private:
9286
MeshUtilities() = delete;

Include/RmlUi/Core/RenderBox.h

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* This source file is part of RmlUi, the HTML/CSS Interface Middleware
3+
*
4+
* For the latest information, see http://github.com/mikke89/RmlUi
5+
*
6+
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
7+
* Copyright (c) 2019-2024 The RmlUi Team, and contributors
8+
*
9+
* Permission is hereby granted, free of charge, to any person obtaining a copy
10+
* of this software and associated documentation files (the "Software"), to deal
11+
* in the Software without restriction, including without limitation the rights
12+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
* copies of the Software, and to permit persons to whom the Software is
14+
* furnished to do so, subject to the following conditions:
15+
*
16+
* The above copyright notice and this permission notice shall be included in
17+
* all copies or substantial portions of the Software.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
*
27+
*/
28+
29+
#ifndef RMLUI_CORE_RENDERBOX_H
30+
#define RMLUI_CORE_RENDERBOX_H
31+
32+
#include "Types.h"
33+
34+
namespace Rml {
35+
36+
// Ordered by top, right, bottom, left.
37+
using EdgeSizes = Array<float, 4>;
38+
// Ordered by top-left, top-right, bottom-right, bottom-left.
39+
using CornerSizes = Array<float, 4>;
40+
41+
/**
42+
Provides the data needed to generate a mesh for a given element's box.
43+
*/
44+
45+
class RenderBox {
46+
public:
47+
RenderBox(Vector2f fill_size, Vector2f border_offset, EdgeSizes border_widths, CornerSizes border_radius) :
48+
fill_size(fill_size), border_offset(border_offset), border_widths(border_widths), border_radius(border_radius)
49+
{}
50+
51+
/// Returns the size of the fill area of the box.
52+
Vector2f GetFillSize() const { return fill_size; }
53+
/// Sets the size of the fill area of the box.
54+
void SetFillSize(Vector2f value) { fill_size = value; }
55+
/// Returns the offset from the border area to the fill area of the box.
56+
Vector2f GetFillOffset() const { return {border_widths[3], border_widths[0]}; }
57+
58+
/// Returns the offset to the border area of the box.
59+
Vector2f GetBorderOffset() const { return border_offset; }
60+
/// Sets the border offset.
61+
void SetBorderOffset(Vector2f value) { border_offset = value; }
62+
63+
/// Returns the border widths of the box.
64+
EdgeSizes GetBorderWidths() const { return border_widths; }
65+
/// Sets the border widths of the box.
66+
void SetBorderWidths(EdgeSizes value) { border_widths = value; }
67+
68+
/// Returns the border radius of the box.
69+
CornerSizes GetBorderRadius() const { return border_radius; }
70+
/// Sets the border radius of the box.
71+
void SetBorderRadius(CornerSizes value) { border_radius = value; }
72+
73+
private:
74+
Vector2f fill_size;
75+
Vector2f border_offset;
76+
EdgeSizes border_widths;
77+
CornerSizes border_radius;
78+
};
79+
80+
} // namespace Rml
81+
#endif

Source/Core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ target_sources(rmlui_core PRIVATE
301301
"${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertyParser.h"
302302
"${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertySpecification.h"
303303
"${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Rectangle.h"
304+
"${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/RenderBox.h"
304305
"${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/RenderInterface.h"
305306
"${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/RenderInterfaceCompatibility.h"
306307
"${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/RenderManager.h"

Source/Core/DecoratorGradient.cpp

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -189,19 +189,18 @@ bool DecoratorStraightGradient::Initialise(const Direction in_direction, const C
189189

190190
DecoratorDataHandle DecoratorStraightGradient::GenerateElementData(Element* element, BoxArea paint_area) const
191191
{
192-
const Box& box = element->GetBox();
193-
192+
const RenderBox render_box = element->GetRenderBox(paint_area);
194193
const ComputedValues& computed = element->GetComputedValues();
195194
const float opacity = computed.opacity();
196195

197196
Mesh mesh;
198-
MeshUtilities::GenerateBackground(mesh, element->GetBox(), Vector2f(0), computed.border_radius(), ColourbPremultiplied(), paint_area);
197+
MeshUtilities::GenerateBackground(mesh, render_box, ColourbPremultiplied());
199198

200199
ColourbPremultiplied colour_start = start.ToPremultiplied(opacity);
201200
ColourbPremultiplied colour_stop = stop.ToPremultiplied(opacity);
202201

203-
const Vector2f offset = box.GetPosition(paint_area);
204-
const Vector2f size = box.GetSize(paint_area);
202+
const Vector2f offset = render_box.GetFillOffset();
203+
const Vector2f size = render_box.GetFillSize();
205204

206205
Vector<Vertex>& vertices = mesh.vertices;
207206

@@ -296,10 +295,8 @@ DecoratorDataHandle DecoratorLinearGradient::GenerateElementData(Element* elemen
296295

297296
RMLUI_ASSERT(!color_stops.empty());
298297

299-
const Box& box = element->GetBox();
300-
const Vector2f dimensions = box.GetSize(paint_area);
301-
302-
LinearGradientShape gradient_shape = CalculateShape(dimensions);
298+
const RenderBox render_box = element->GetRenderBox(paint_area);
299+
LinearGradientShape gradient_shape = CalculateShape(render_box.GetFillSize());
303300

304301
// One-pixel minimum color stop spacing to avoid aliasing.
305302
const float soft_spacing = 1.f / gradient_shape.length;
@@ -320,9 +317,9 @@ DecoratorDataHandle DecoratorLinearGradient::GenerateElementData(Element* elemen
320317
Mesh mesh;
321318
const ComputedValues& computed = element->GetComputedValues();
322319
const byte alpha = byte(computed.opacity() * 255.f);
323-
MeshUtilities::GenerateBackground(mesh, box, Vector2f(), computed.border_radius(), ColourbPremultiplied(alpha, alpha), paint_area);
320+
MeshUtilities::GenerateBackground(mesh, render_box, ColourbPremultiplied(alpha, alpha));
324321

325-
const Vector2f render_offset = box.GetPosition(paint_area);
322+
const Vector2f render_offset = render_box.GetFillOffset();
326323
for (Vertex& vertex : mesh.vertices)
327324
vertex.tex_coord = vertex.position - render_offset;
328325

@@ -458,16 +455,16 @@ bool DecoratorRadialGradient::Initialise(bool in_repeating, Shape in_shape, Size
458455
return !color_stops.empty();
459456
}
460457

461-
DecoratorDataHandle DecoratorRadialGradient::GenerateElementData(Element* element, BoxArea box_area) const
458+
DecoratorDataHandle DecoratorRadialGradient::GenerateElementData(Element* element, BoxArea paint_area) const
462459
{
463460
RenderManager* render_manager = element->GetRenderManager();
464461
if (!render_manager)
465462
return INVALID_DECORATORDATAHANDLE;
466463

467464
RMLUI_ASSERT(!color_stops.empty() && (shape == Shape::Circle || shape == Shape::Ellipse));
468465

469-
const Box& box = element->GetBox();
470-
const Vector2f dimensions = box.GetSize(box_area);
466+
const RenderBox render_box = element->GetRenderBox(paint_area);
467+
const Vector2f dimensions = render_box.GetFillSize();
471468

472469
RadialGradientShape gradient_shape = CalculateRadialGradientShape(element, dimensions);
473470

@@ -489,9 +486,9 @@ DecoratorDataHandle DecoratorRadialGradient::GenerateElementData(Element* elemen
489486
Mesh mesh;
490487
const ComputedValues& computed = element->GetComputedValues();
491488
const byte alpha = byte(computed.opacity() * 255.f);
492-
MeshUtilities::GenerateBackground(mesh, box, Vector2f(), computed.border_radius(), ColourbPremultiplied(alpha, alpha), box_area);
489+
MeshUtilities::GenerateBackground(mesh, render_box, ColourbPremultiplied(alpha, alpha));
493490

494-
const Vector2f render_offset = box.GetPosition(box_area);
491+
const Vector2f render_offset = render_box.GetFillOffset();
495492
for (Vertex& vertex : mesh.vertices)
496493
vertex.tex_coord = vertex.position - render_offset;
497494

@@ -657,16 +654,16 @@ bool DecoratorConicGradient::Initialise(bool in_repeating, float in_angle, Vecto
657654
return !color_stops.empty();
658655
}
659656

660-
DecoratorDataHandle DecoratorConicGradient::GenerateElementData(Element* element, BoxArea box_area) const
657+
DecoratorDataHandle DecoratorConicGradient::GenerateElementData(Element* element, BoxArea paint_area) const
661658
{
662659
RenderManager* render_manager = element->GetRenderManager();
663660
if (!render_manager)
664661
return INVALID_DECORATORDATAHANDLE;
665662

666663
RMLUI_ASSERT(!color_stops.empty());
667664

668-
const Box& box = element->GetBox();
669-
const Vector2f dimensions = box.GetSize(box_area);
665+
const RenderBox render_box = element->GetRenderBox(paint_area);
666+
const Vector2f dimensions = render_box.GetFillSize();
670667

671668
const Vector2f center =
672669
Vector2f{element->ResolveNumericValue(position.x, dimensions.x), element->ResolveNumericValue(position.y, dimensions.y)}.Round();
@@ -686,9 +683,9 @@ DecoratorDataHandle DecoratorConicGradient::GenerateElementData(Element* element
686683
Mesh mesh;
687684
const ComputedValues& computed = element->GetComputedValues();
688685
const byte alpha = byte(computed.opacity() * 255.f);
689-
MeshUtilities::GenerateBackground(mesh, box, Vector2f(), computed.border_radius(), ColourbPremultiplied(alpha, alpha), box_area);
686+
MeshUtilities::GenerateBackground(mesh, render_box, ColourbPremultiplied(alpha, alpha));
690687

691-
const Vector2f render_offset = box.GetPosition(box_area);
688+
const Vector2f render_offset = render_box.GetFillOffset();
692689
for (Vertex& vertex : mesh.vertices)
693690
vertex.tex_coord = vertex.position - render_offset;
694691

Source/Core/DecoratorNinePatch.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ DecoratorDataHandle DecoratorNinePatch::GenerateElementData(Element* element, Bo
6161
Texture texture = GetTexture();
6262
const Vector2f texture_dimensions(texture.GetDimensions());
6363

64-
const Vector2f surface_offset = element->GetBox().GetPosition(paint_area);
65-
const Vector2f surface_dimensions = element->GetBox().GetSize(paint_area).Round();
64+
const RenderBox render_box = element->GetRenderBox(paint_area);
65+
const Vector2f surface_offset = render_box.GetFillOffset();
66+
const Vector2f surface_dimensions = render_box.GetFillSize();
6667

6768
const ColourbPremultiplied quad_colour = computed.image_color().ToPremultiplied(computed.opacity());
6869

0 commit comments

Comments
 (0)