Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
09f6267
Merge pull request #2 from Mairramer/master
Mairramer Dec 10, 2025
2c4e7eb
Fixed RenderFlex overflow in RouteObserver Example (#170980)
prash4931 Dec 10, 2025
aaafeaf
added onUserInteractionIfError for form (#175515)
akashefrath Dec 10, 2025
8fe7618
Fix the issue with pinned headers in nested SliverMainAxisGroup. (#17…
yiiim Dec 10, 2025
e25d71b
Ensure that the engine converts std::filesystem::path objects to UTF-…
jason-simmons Dec 10, 2025
2a950ef
Reland: Add framework-side hitTestBehavior support to Semantics (#178…
flutter-zl Dec 10, 2025
ab089c0
Roll Skia from e61cc6d073fd to 59c6cad539f7 (22 revisions) (#179714)
engine-flutter-autoroll Dec 11, 2025
811a4d2
Use kPreventOverdraw for arcs with overlapping stroke caps (#179312)
b-luk Dec 11, 2025
0c6af9d
Roll Dart SDK from 077062c5e515 to dbcb567e2432 (2 revisions) (#179715)
engine-flutter-autoroll Dec 11, 2025
d94c0e9
Roll Skia from 59c6cad539f7 to deb0153719dd (2 revisions) (#179721)
engine-flutter-autoroll Dec 11, 2025
7f0e5b5
Roll Fuchsia Linux SDK from u5vxWTRT0HlxOP5_r... to QssSL8DkxIbMvf89C…
engine-flutter-autoroll Dec 11, 2025
67b6303
Fixed RenderFlex overflow in RouteObserver Example (#170980)
prash4931 Dec 10, 2025
2f50318
added onUserInteractionIfError for form (#175515)
akashefrath Dec 10, 2025
1dfd694
Fix the issue with pinned headers in nested SliverMainAxisGroup. (#17…
yiiim Dec 10, 2025
c1a514e
Ensure that the engine converts std::filesystem::path objects to UTF-…
jason-simmons Dec 10, 2025
ae4cb25
Reland: Add framework-side hitTestBehavior support to Semantics (#178…
flutter-zl Dec 10, 2025
5fa0ba5
Roll Skia from e61cc6d073fd to 59c6cad539f7 (22 revisions) (#179714)
engine-flutter-autoroll Dec 11, 2025
4714854
Use kPreventOverdraw for arcs with overlapping stroke caps (#179312)
b-luk Dec 11, 2025
adf3ac7
Roll Dart SDK from 077062c5e515 to dbcb567e2432 (2 revisions) (#179715)
engine-flutter-autoroll Dec 11, 2025
74a0f8a
Roll Skia from 59c6cad539f7 to deb0153719dd (2 revisions) (#179721)
engine-flutter-autoroll Dec 11, 2025
6a5ea63
Roll Fuchsia Linux SDK from u5vxWTRT0HlxOP5_r... to QssSL8DkxIbMvf89C…
engine-flutter-autoroll Dec 11, 2025
f605613
Merge branch 'master' into feature/add-currentIndex-and-onItemChanged…
Mairramer Dec 11, 2025
7429b1f
small fixes
Mairramer Dec 17, 2025
7e2de38
Clarify documentation for leading item resolution in CarouselView
Mairramer Dec 17, 2025
34241f1
small improves
Mairramer Dec 18, 2025
c373a0d
refactor tests
Mairramer Dec 18, 2025
428db29
code removal in tests
Mairramer Dec 18, 2025
4755505
corrects the test name
Mairramer Dec 18, 2025
2663f4d
Handle issue when consumeMaxWeight is true
QuncCccccc Dec 19, 2025
9649575
fix tests
Mairramer Dec 19, 2025
66f9ac9
Add comment to clarify carousel animation behavior in tests
Mairramer Dec 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ vars = {
'flutter_git': 'https://flutter.googlesource.com',
'skia_git': 'https://skia.googlesource.com',
'llvm_git': 'https://llvm.googlesource.com',
'skia_revision': 'e61cc6d073fd3dd217b52e7b91d21151aad61478',
'skia_revision': 'deb0153719ddf8895c9c1448a9a6959610785947',

# WARNING: DO NOT EDIT canvaskit_cipd_instance MANUALLY
# See `lib/web_ui/README.md` for how to roll CanvasKit to a new version.
Expand Down Expand Up @@ -59,7 +59,7 @@ vars = {
# updated revision list of existing dependencies. You will need to
# gclient sync before and after update deps to ensure all deps are updated.
# updated revision list of existing dependencies.
'dart_revision': '077062c5e5154cee4ffb1d72532428eaa79bc0fa',
'dart_revision': 'dbcb567e2432c7cf8401ca4d9365a3f040aa5deb',

# WARNING: DO NOT EDIT MANUALLY
# The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py
Expand All @@ -77,7 +77,7 @@ vars = {
'dart_protobuf_rev': '9e30258e0aa6a6430ee36c84b75308a9702fde42',
'dart_pub_rev': 'b297f1f5e42f2569f15a98548f44b4b9a48487c1',
'dart_sync_http_rev': '6666fff944221891182e1f80bf56569338164d72',
'dart_tools_rev': 'd03c394b24829f662098e590a2c812a076b01199',
'dart_tools_rev': '97014f33a2aad59385df73f25baf9c304ca5c866',
'dart_vector_math_rev': '70a9a2cb610d040b247f3ca2cd70a94c1c6f6f23',
'dart_web_rev': '35fc98dd8f9da175ed0a2dcf246299e922e1e1e2',
'dart_webdev_rev': '234e44c2ba0aa6cee5a36026538ca89457bf0d55',
Expand Down Expand Up @@ -810,7 +810,7 @@ deps = {
'packages': [
{
'package': 'fuchsia/sdk/core/linux-amd64',
'version': 'u5vxWTRT0HlxOP5_rIPOkUJXTun41aPDWjrw0MQdakkC'
'version': 'QssSL8DkxIbMvf89CsxO9AsjjFWhqAZd7XoYRjQxeP8C'
}
],
'condition': 'download_fuchsia_deps and not download_fuchsia_sdk',
Expand Down
5 changes: 5 additions & 0 deletions engine/src/flutter/fml/string_conversion.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,9 @@ std::u16string Utf8ToUtf16(const std::string_view string) {
return converter.from_bytes(string.data());
}

std::string PathToUtf8(const std::filesystem::path& path) {
const std::u8string path_u8 = path.u8string();
return std::string(path_u8.begin(), path_u8.end());
}

} // namespace fml
4 changes: 4 additions & 0 deletions engine/src/flutter/fml/string_conversion.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#ifndef FLUTTER_FML_STRING_CONVERSION_H_
#define FLUTTER_FML_STRING_CONVERSION_H_

#include <filesystem>
#include <string>
#include <vector>

Expand All @@ -19,6 +20,9 @@ std::string Utf16ToUtf8(const std::u16string_view string);
// Returns a UTF-16 encoded equivalent of a UTF-8 encoded input string.
std::u16string Utf8ToUtf16(const std::string_view string);

// Returns the pathname encoded in UTF-8.
std::string PathToUtf8(const std::filesystem::path& path);

} // namespace fml

#endif // FLUTTER_FML_STRING_CONVERSION_H_
5 changes: 5 additions & 0 deletions engine/src/flutter/fml/string_conversion_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,10 @@ TEST(StringConversion, Utf16ToUtf8Unicode) {
EXPECT_EQ(Utf16ToUtf8(u"\x2603"), "\xe2\x98\x83");
}

TEST(StringConversion, PathToUtf8) {
EXPECT_EQ(PathToUtf8(std::filesystem::path("abc")), "abc");
EXPECT_EQ(PathToUtf8(std::filesystem::path(u"\x2603")), "\xe2\x98\x83");
}

} // namespace testing
} // namespace fml
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,25 @@ void RenderArcFarm(DisplayListBuilder& builder,
}
builder.Restore();
}

void RenderArcFarmForOverlappingCapsTest(DisplayListBuilder& builder,
const DlPaint& paint) {
builder.Save();
builder.Translate(40, 30);
const Rect arc_bounds = Rect::MakeLTRB(0, 0, 40, 40);
for (int stroke_width = 10; stroke_width <= 40; stroke_width += 3) {
DlPaint modified_paint = DlPaint(paint);
modified_paint.setStrokeWidth(stroke_width);
builder.Save();
for (int sweep = 160; sweep <= 360; sweep += 20) {
builder.DrawArc(arc_bounds, 0, sweep, false, modified_paint);
builder.Translate(84, 0);
}
builder.Restore();
builder.Translate(0, 44 + stroke_width);
}
builder.Restore();
}
} // namespace

TEST_P(AiksTest, FilledArcsRenderCorrectly) {
Expand Down Expand Up @@ -981,6 +1000,21 @@ TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithSquareEnds) {
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithTranslucencyAndSquareEnds) {
DisplayListBuilder builder;
builder.Scale(GetContentScale().x, GetContentScale().y);
builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);

DlPaint paint;
paint.setDrawStyle(DlDrawStyle::kStroke);
paint.setStrokeCap(DlStrokeCap::kSquare);
paint.setColor(DlColor::kBlue().modulateOpacity(0.5));

RenderArcFarmForOverlappingCapsTest(builder, paint);

ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithRoundEnds) {
DisplayListBuilder builder;
builder.Scale(GetContentScale().x, GetContentScale().y);
Expand All @@ -1001,6 +1035,21 @@ TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithRoundEnds) {
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithTranslucencyAndRoundEnds) {
DisplayListBuilder builder;
builder.Scale(GetContentScale().x, GetContentScale().y);
builder.DrawColor(DlColor::kWhite(), DlBlendMode::kSrc);

DlPaint paint;
paint.setDrawStyle(DlDrawStyle::kStroke);
paint.setStrokeCap(DlStrokeCap::kRound);
paint.setColor(DlColor::kBlue().modulateOpacity(0.5));

RenderArcFarmForOverlappingCapsTest(builder, paint);

ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(AiksTest, StrokedArcsRenderCorrectlyWithBevelJoinsAndCenter) {
DisplayListBuilder builder;
builder.Scale(GetContentScale().x, GetContentScale().y);
Expand Down
111 changes: 111 additions & 0 deletions engine/src/flutter/impeller/entity/entity_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2313,6 +2313,117 @@ TEST_P(EntityTest, FillPathGeometryGetPositionBufferReturnsExpectedMode) {
}
}

TEST_P(EntityTest, StrokeArcGeometryGetPositionBufferReturnsExpectedMode) {
RenderTarget target;
testing::MockRenderPass mock_pass(GetContext(), target);
Rect oval_bounds = Rect::MakeLTRB(100, 100, 200, 200);

// Butt caps never overlap
{
StrokeParameters stroke = {.width = 50.0f, .cap = Cap::kButt};
for (auto start = 0; start < 360; start += 60) {
for (auto sweep = 0; sweep < 360; sweep += 12) {
auto geometry = Geometry::MakeStrokedArc(oval_bounds, Degrees(start),
Degrees(sweep), stroke);

GeometryResult result =
geometry->GetPositionBuffer(*GetContentContext(), {}, mock_pass);

EXPECT_EQ(result.mode, GeometryResult::Mode::kNormal)
<< "start: " << start << " sweep: " << sweep;
}
}
}

// Round caps with 10 stroke width overlap starting at 348.6 degrees
{
StrokeParameters stroke = {.width = 10.0f, .cap = Cap::kRound};
for (auto start = 0; start < 360; start += 60) {
for (auto sweep = 0; sweep < 360; sweep += 12) {
auto geometry = Geometry::MakeStrokedArc(oval_bounds, Degrees(start),
Degrees(sweep), stroke);

GeometryResult result =
geometry->GetPositionBuffer(*GetContentContext(), {}, mock_pass);

if (sweep < 348.6) {
EXPECT_EQ(result.mode, GeometryResult::Mode::kNormal)
<< "start: " << start << " sweep: " << sweep;
} else {
EXPECT_EQ(result.mode, GeometryResult::Mode::kPreventOverdraw)
<< "start: " << start << " sweep: " << sweep;
}
Comment on lines +2349 to +2355

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This test case for round caps with a 10px stroke width doesn't seem to be testing the kPreventOverdraw scenario correctly. The for loop for sweep (for (auto sweep = 0; sweep < 360; sweep += 12)) generates values up to 348. Since 348 < 348.6 is true, the else block which checks for GeometryResult::Mode::kPreventOverdraw is never reached. This means the test doesn't verify the overlap detection for this case.

To fix this, you could adjust the loop to include values greater than or equal to 348.6, for example by changing the loop condition to sweep <= 360.

}
}
}

// Round caps with 50 stroke width overlap starting at 300.1 degrees
{
StrokeParameters stroke = {.width = 50.0f, .cap = Cap::kRound};
for (auto start = 0; start < 360; start += 60) {
for (auto sweep = 0; sweep < 360; sweep += 12) {
auto geometry = Geometry::MakeStrokedArc(oval_bounds, Degrees(start),
Degrees(sweep), stroke);

GeometryResult result =
geometry->GetPositionBuffer(*GetContentContext(), {}, mock_pass);

if (sweep < 300.0) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There appears to be a slight logic error in this condition. The comment on line 2360 states that overlap starts at 300.1 degrees. However, for a sweep value of 300, this condition sweep < 300.0 evaluates to false, causing the test to expect kPreventOverdraw. This contradicts the comment, as a sweep of 300.0 degrees should still be considered normal.

To align with the comment, you should adjust the condition.

Suggested change
if (sweep < 300.0) {
if (sweep < 300.1) {

EXPECT_EQ(result.mode, GeometryResult::Mode::kNormal)
<< "start: " << start << " sweep: " << sweep;
} else {
EXPECT_EQ(result.mode, GeometryResult::Mode::kPreventOverdraw)
<< "start: " << start << " sweep: " << sweep;
}
}
}
}

// Square caps with 10 stroke width overlap starting at 347.4 degrees
{
StrokeParameters stroke = {.width = 10.0f, .cap = Cap::kSquare};
for (auto start = 0; start < 360; start += 60) {
for (auto sweep = 0; sweep < 360; sweep += 12) {
auto geometry = Geometry::MakeStrokedArc(oval_bounds, Degrees(start),
Degrees(sweep), stroke);

GeometryResult result =
geometry->GetPositionBuffer(*GetContentContext(), {}, mock_pass);

if (sweep < 347.4) {
EXPECT_EQ(result.mode, GeometryResult::Mode::kNormal)
<< "start: " << start << " sweep: " << sweep;
} else {
EXPECT_EQ(result.mode, GeometryResult::Mode::kPreventOverdraw)
<< "start: " << start << " sweep: " << sweep;
}
}
}
}

// Square caps with 50 stroke width overlap starting at 270.1 degrees
{
StrokeParameters stroke = {.width = 50.0f, .cap = Cap::kSquare};
for (auto start = 0; start < 360; start += 60) {
for (auto sweep = 0; sweep < 360; sweep += 12) {
auto geometry = Geometry::MakeStrokedArc(oval_bounds, Degrees(start),
Degrees(sweep), stroke);

GeometryResult result =
geometry->GetPositionBuffer(*GetContentContext(), {}, mock_pass);

if (sweep < 270.1) {
EXPECT_EQ(result.mode, GeometryResult::Mode::kNormal)
<< "start: " << start << " sweep: " << sweep;
} else {
EXPECT_EQ(result.mode, GeometryResult::Mode::kPreventOverdraw)
<< "start: " << start << " sweep: " << sweep;
}
}
}
}
}

TEST_P(EntityTest, FailOnValidationError) {
if (GetParam() != PlaygroundBackend::kVulkan) {
GTEST_SKIP() << "Validation is only fatal on Vulkan backend.";
Expand Down
56 changes: 55 additions & 1 deletion engine/src/flutter/impeller/entity/geometry/arc_geometry.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "flutter/impeller/entity/geometry/arc_geometry.h"

#include "flutter/impeller/entity/geometry/line_geometry.h"
#include "fml/logging.h"

namespace impeller {

Expand Down Expand Up @@ -47,7 +48,11 @@ GeometryResult ArcGeometry::GetPositionBuffer(const ContentContext& renderer,
auto generator =
renderer.GetTessellator().StrokedArc(transform, arc_, cap_, half_width);

return ComputePositionGeometry(renderer, generator, entity, pass);
auto result = ComputePositionGeometry(renderer, generator, entity, pass);
if (CapsOverlap()) {
result.mode = GeometryResult::Mode::kPreventOverdraw;
}
return result;
}
}

Expand Down Expand Up @@ -80,4 +85,53 @@ bool ArcGeometry::IsAxisAlignedRect() const {
return false;
}

bool ArcGeometry::CapsOverlap() const {
FML_DCHECK(arc_.GetSweep().degrees >= 0.0f);
FML_DCHECK(arc_.GetSweep().degrees <= 360.0f);

if (stroke_width_ < 0 || cap_ == Cap::kButt ||
arc_.GetSweep().degrees <= 180) {
return false;
}

switch (cap_) {
case Cap::kSquare: {
// Square caps overlap if the inner corner of the ending cap extends
// inside the inner edge of the start cap. For a visualization of when
// this occurs, see
// https://github.com/flutter/flutter/issues/178746#issuecomment-3554526727
// Note that testing for overlap is completely independent of the arc's
// start angle. To simplify the overlap test, we treat the arc as if its
// start angle is 0. This allows the test to only require checking the x
// coordinate of the ending cap, rather than needing to calculate overlap
// based on both x and y positions of both caps.
auto radius = arc_.GetOvalSize().width * 0.5f;
auto half_width = stroke_width_ * 0.5f;
auto inner_radius = radius - half_width;
auto inner_arc_end_x =
cos(Radians(arc_.GetSweep()).radians) * inner_radius;
auto inner_square_cap_end_x =
inner_arc_end_x +
cos(Radians(arc_.GetSweep() + Degrees(90)).radians) * half_width;
return inner_square_cap_end_x > inner_radius;
}
case Cap::kRound: {
// Round caps overlap if the distance between the arc's start and end
// points is less than the stroke width.
// https://github.com/flutter/flutter/issues/178746#issuecomment-3554526727
// Note that testing for overlap is completely independent of the arc's
// start angle. To simplify the overlap test, we treat the arc as if its
// start angle is 0.
auto radius = arc_.GetOvalSize().width / 2.0f;
auto start_point = Point(radius, 0);
auto sweep_radians = Radians(arc_.GetSweep()).radians;
auto end_point = Point(cos(sweep_radians), sin(sweep_radians)) * radius;
return start_point.GetDistanceSquared(end_point) <
stroke_width_ * stroke_width_;
}
case Cap::kButt:
FML_UNREACHABLE()
}
}

} // namespace impeller
3 changes: 3 additions & 0 deletions engine/src/flutter/impeller/entity/geometry/arc_geometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ class ArcGeometry final : public Geometry {
// |Geometry|
std::optional<Rect> GetCoverage(const Matrix& transform) const override;

// Whether the arc has overlapping stroke caps
bool CapsOverlap() const;

Arc arc_;
Scalar stroke_width_;
Cap cap_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <filesystem>

#include "flutter/fml/logging.h"
#include "flutter/fml/string_conversion.h"
#include "flutter/shell/platform/common/engine_switches.h" // nogncheck
#include "flutter/shell/platform/common/path_utils.h"

Expand Down Expand Up @@ -73,7 +74,7 @@ UniqueAotDataPtr FlutterProjectBundle::LoadAotData(
<< "; no such file.";
return UniqueAotDataPtr(nullptr, nullptr);
}
std::string path_string = aot_library_path_.string();
std::string path_string = fml::PathToUtf8(aot_library_path_);
FlutterEngineAOTDataSource source = {};
source.type = kFlutterEngineAOTDataSourceTypeElfPath;
source.elf_path = path_string.c_str();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,8 @@ bool FlutterWindowsEngine::Run(std::string_view entrypoint) {
FML_LOG(ERROR) << "Missing or unresolvable paths to assets.";
return false;
}
std::string assets_path_string = project_->assets_path().string();
std::string icu_path_string = project_->icu_path().string();
std::string assets_path_string = fml::PathToUtf8(project_->assets_path());
std::string icu_path_string = fml::PathToUtf8(project_->icu_path());
if (embedder_api_.RunsAOTCompiledDartCode()) {
aot_data_ = project_->LoadAotData(embedder_api_);
if (!aot_data_) {
Expand Down
7 changes: 4 additions & 3 deletions engine/src/flutter/tools/templater/templater_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "flutter/fml/file.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/mapping.h"
#include "flutter/fml/string_conversion.h"
#include "inja/inja.hpp"

namespace flutter {
Expand Down Expand Up @@ -50,9 +51,9 @@ bool TemplaterMain(const fml::CommandLine& command_line) {
reinterpret_cast<const uint8_t*>(rendered_template.data()),
rendered_template.size()};

auto current_dir =
fml::OpenDirectory(std::filesystem::current_path().string().c_str(),
false, fml::FilePermission::kReadWrite);
auto current_dir = fml::OpenDirectory(
fml::PathToUtf8(std::filesystem::current_path()).c_str(), false,
fml::FilePermission::kReadWrite);
if (!current_dir.is_valid()) {
FML_LOG(ERROR) << "Could not open current directory.";
return false;
Expand Down
Loading