Skip to content

Commit 2c8d2d1

Browse files
sfreilichcopybara-github
authored andcommitted
Allow brush tips to have multiple outlines (e.g. for particle effects)
Rolling forward after rollback in f8502c0 (which incorrectly repeats a truncated version the original change description from ce4c535). In `StrokeShapeBuilder`, this involves losing the assumption that the number of outlines is exactly equal to the number of brush tips, since particle brushes have more than one outline per tip. Without that assumption, it can also discard empty outlines a bit earlier. In `BrushTipExtruder` this involves: * Keeping a vector of outlines instead of a single outline (since the usual case is just one, this uses `absl::InlinedVector<StrokeOutline, 1>`). * Keep track of the number of extrusion breaks in `Geometry`. * Add accessors to `Geometry` to get the number of break-points and the index counts at the lastest break-point. Use those in `Restore` to restore the saved state and in `AppendNewIndices` to get the start points in the latest outline. * Pull calls to `AppendNewIndices` and `Geometry::UpdateMeshDerivatives` into `ExtrudeBreakPoint` because the former computes the latest outline and the latter may update indices, so it needs to be called first. * Similarly, move forward the "first mutated" indices in `Geometry::AddExtrusionBreak`, since those track the first mutated vertices that haven't been recorded in the outline. These are renamed to `first_mutated_{left,right}_index_offset_in_current_partition_`, since these no longer track the same thing as `first_mutated_left_index_` and `first_mutated_right_index_` (which still considers a whole stroke update). In `InProgressStroke`, `GetIndexOutlines` is renamed to `GetCoatOutlines` for clarity, since that makes it clear what the index is referring to. PiperOrigin-RevId: 712619312
1 parent 26b4724 commit 2c8d2d1

18 files changed

+333
-195
lines changed

ink/geometry/partitioned_mesh.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,10 @@ class PartitionedMesh {
188188
absl::Span<const Mesh> Meshes() const;
189189

190190
// Returns the number of outlines (which may be zero) in render group
191-
// `group_index`.
191+
// `group_index`. Groups with discontinuous geometry will always have multiple
192+
// outlines, but even a group with continuous geometry may be drawn with
193+
// multiple overlapping outlines when this improves rendering quality or
194+
// performance.
192195
//
193196
// This method CHECK-fails if `group_index` >= `RenderGroupCount()`.
194197
uint32_t OutlineCount(uint32_t group_index) const;

ink/rendering/skia/native/skia_renderer.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ absl::StatusOr<SkiaRenderer::Drawable> SkiaRenderer::CreateDrawable(
134134

135135
if (UsePathRendering(context, brush->GetCoats()[coat_index].paint)) {
136136
drawables.push_back(PathDrawable(
137-
stroke.GetMesh(coat_index), stroke.GetIndexOutlines(coat_index),
137+
stroke.GetMesh(coat_index), stroke.GetCoatOutlines(coat_index),
138138
brush->GetColor(), OpacityMultiplierForPath(*brush, coat_index)));
139139
continue;
140140
}

ink/strokes/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ cc_library(
110110
"//ink/strokes/input:stroke_input",
111111
"//ink/strokes/input:stroke_input_batch",
112112
"//ink/strokes/input/internal:stroke_input_validation_helpers",
113+
"//ink/strokes/internal:stroke_outline",
113114
"//ink/strokes/internal:stroke_shape_builder",
114115
"//ink/strokes/internal:stroke_shape_update",
115116
"//ink/strokes/internal:stroke_vertex",

ink/strokes/in_progress_stroke.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ Stroke InProgressStroke::CopyToStroke(
272272

273273
mesh_groups.push_back({
274274
.mesh = &GetMesh(coat_index),
275-
.outlines = GetIndexOutlines(coat_index),
275+
.outlines = GetCoatOutlines(coat_index),
276276
.omit_attributes = omit_attributes[coat_index],
277277
.packing_params = custom_packing_arrays[coat_index].Values(),
278278
});

ink/strokes/in_progress_stroke.h

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "ink/geometry/envelope.h"
2929
#include "ink/geometry/mutable_mesh.h"
3030
#include "ink/strokes/input/stroke_input_batch.h"
31+
#include "ink/strokes/internal/stroke_outline.h"
3132
#include "ink/strokes/internal/stroke_shape_builder.h"
3233
#include "ink/strokes/stroke.h"
3334
#include "ink/types/duration.h"
@@ -204,19 +205,22 @@ class InProgressStroke {
204205
// specified coat of paint.
205206
const Envelope& GetMeshBounds(uint32_t coat_index) const;
206207

207-
// Returns one or more spans of vertex indices, one for each of the stroke
208-
// outlines for the specified coat of paint.
208+
// Returns zero or more non-empty spans of vertex indices, one for each of the
209+
// stroke outlines for the specified coat of paint. There will be at least one
210+
// outline for each brush tip if the stroke is non-empty. Stroke coats with
211+
// discontinuous geometry will always have multiple outlines, but even a coat
212+
// with continuous geometry may be drawn with multiple overlapping outlines
213+
// when this improves rendering quality or performance.
209214
//
210215
// Every returned index value can be used to get an outline position from the
211216
// `MutableMesh` returned by `GetMesh()`.
212217
//
213-
// For each non-empty span of indices, the first and last elements reference
214-
// vertices at the end of the stroke. The indices traverse the mesh such that
215-
// the outline has a negative winding number when viewed from the positive
216-
// z-axis. I.e. the outline positions would be appear in clockwise order if
217-
// the y-axis points up, and in counter-clockwise order if the y-axis points
218-
// down.
219-
absl::Span<const absl::Span<const uint32_t>> GetIndexOutlines(
218+
// The first and last elements in each span reference the vertices at the end
219+
// of the stroke outline. The indices traverse the mesh such that the outline
220+
// has a negative winding number when viewed from the positive z-axis. That
221+
// is, the outline positions are in clockwise order if the y-axis points up,
222+
// counter-clockwise order if the y-axis points down.
223+
absl::Span<const absl::Span<const uint32_t>> GetCoatOutlines(
220224
uint32_t coat_index) const;
221225

222226
// Returns the bounding rectangle of mesh positions added, modified, or
@@ -305,9 +309,9 @@ inline const Envelope& InProgressStroke::GetMeshBounds(
305309
}
306310

307311
inline absl::Span<const absl::Span<const uint32_t>>
308-
InProgressStroke::GetIndexOutlines(uint32_t coat_index) const {
312+
InProgressStroke::GetCoatOutlines(uint32_t coat_index) const {
309313
ABSL_CHECK_LT(coat_index, BrushCoatCount());
310-
return shape_builders_[coat_index].GetIndexOutlines();
314+
return shape_builders_[coat_index].GetOutlines();
311315
}
312316

313317
inline const Envelope& InProgressStroke::GetUpdatedRegion() const {

ink/strokes/in_progress_stroke_test.cc

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ MATCHER_P3(UpdateShapeFailsAndDoesNotModifyStroke, current_elapsed_time,
154154
meshes_before_update.push_back(arg->GetMesh(i).Clone());
155155
bounds_before_update.push_back(arg->GetMeshBounds(i));
156156
std::vector<std::vector<uint32_t>> outlines;
157-
for (absl::Span<const uint32_t> outline : arg->GetIndexOutlines(i)) {
157+
for (absl::Span<const uint32_t> outline : arg->GetCoatOutlines(i)) {
158158
outlines.push_back(std::vector<uint32_t>(outline.begin(), outline.end()));
159159
}
160160
index_outlines_before_update.push_back(std::move(outlines));
@@ -186,7 +186,7 @@ MATCHER_P3(UpdateShapeFailsAndDoesNotModifyStroke, current_elapsed_time,
186186
!ExplainMatchResult(EnvelopeEq(bounds_before_update[i]),
187187
arg->GetMeshBounds(i), result_listener) ||
188188
!ExplainMatchResult(ElementsAreArray(index_outlines_before_update[i]),
189-
arg->GetIndexOutlines(i), result_listener)) {
189+
arg->GetCoatOutlines(i), result_listener)) {
190190
return false;
191191
}
192192
}
@@ -263,7 +263,7 @@ TEST(InProgressStrokeTest, StartAfterConstruction) {
263263
EXPECT_EQ(stroke.GetMesh(0).VertexCount(), 0u);
264264
EXPECT_EQ(stroke.GetMesh(0).TriangleCount(), 0u);
265265
EXPECT_TRUE(stroke.GetMeshBounds(0).IsEmpty());
266-
EXPECT_THAT(stroke.GetIndexOutlines(0), ElementsAre(IsEmpty()));
266+
EXPECT_THAT(stroke.GetCoatOutlines(0), IsEmpty());
267267
EXPECT_TRUE(stroke.GetUpdatedRegion().IsEmpty());
268268
EXPECT_FALSE(stroke.InputsAreFinished());
269269
EXPECT_FALSE(stroke.NeedsUpdate());
@@ -338,7 +338,7 @@ TEST(InProgressStrokeTest, EmptyEnqueueInputsAndUpdateAfterStart) {
338338
EXPECT_EQ(stroke.GetMesh(0).VertexCount(), 0u);
339339
EXPECT_EQ(stroke.GetMesh(0).TriangleCount(), 0u);
340340
EXPECT_TRUE(stroke.GetMeshBounds(0).IsEmpty());
341-
EXPECT_THAT(stroke.GetIndexOutlines(0), ElementsAre(IsEmpty()));
341+
EXPECT_THAT(stroke.GetCoatOutlines(0), IsEmpty());
342342
EXPECT_TRUE(stroke.GetUpdatedRegion().IsEmpty());
343343
EXPECT_FALSE(stroke.NeedsUpdate());
344344
}
@@ -392,7 +392,7 @@ TEST(InProgressStrokeTest, NonEmptyInputs) {
392392
Optional(RectNear(*CalculateEnvelope(stroke.GetMesh(0)).AsRect(),
393393
0.0001)));
394394

395-
EXPECT_THAT(stroke.GetIndexOutlines(0), ElementsAre(Not(IsEmpty())));
395+
EXPECT_THAT(stroke.GetCoatOutlines(0), ElementsAre(Not(IsEmpty())));
396396
EXPECT_THAT(stroke.GetUpdatedRegion().AsRect(),
397397
Optional(RectNear(
398398
Rect::FromTwoPoints({-0.88, 0.13}, {4.90, 5.87}), 0.01)));
@@ -419,7 +419,7 @@ TEST(InProgressStrokeTest, NonEmptyInputs) {
419419
Optional(RectNear(*CalculateEnvelope(stroke.GetMesh(0)).AsRect(),
420420
0.0001)));
421421

422-
EXPECT_THAT(stroke.GetIndexOutlines(0), ElementsAre(Not(IsEmpty())));
422+
EXPECT_THAT(stroke.GetCoatOutlines(0), ElementsAre(Not(IsEmpty())));
423423
EXPECT_THAT(stroke.GetUpdatedRegion().AsRect(),
424424
Optional(RectNear(
425425
Rect::FromTwoPoints({-0.88, -2.88}, {5.88, 5.87}), 0.01)));
@@ -447,7 +447,7 @@ TEST(InProgressStrokeTest, ExtendWithEmptyPredictedButNonEmptyReal) {
447447
EnvelopeNear(CalculateEnvelope(stroke.GetMesh(0)).AsRect().value(),
448448
0.0001));
449449

450-
EXPECT_THAT(stroke.GetIndexOutlines(0), ElementsAre(Not(IsEmpty())));
450+
EXPECT_THAT(stroke.GetCoatOutlines(0), ElementsAre(Not(IsEmpty())));
451451
EXPECT_THAT(stroke.GetUpdatedRegion().AsRect(),
452452
Optional(RectNear(
453453
Rect::FromTwoPoints({-0.88, 0.13}, {4.87, 3.87}), 0.01)));
@@ -470,7 +470,7 @@ TEST(InProgressStrokeTest, ExtendWithEmptyPredictedButNonEmptyReal) {
470470
EnvelopeNear(CalculateEnvelope(stroke.GetMesh(0)).AsRect().value(),
471471
0.0001));
472472

473-
EXPECT_THAT(stroke.GetIndexOutlines(0), ElementsAre(Not(IsEmpty())));
473+
EXPECT_THAT(stroke.GetCoatOutlines(0), ElementsAre(Not(IsEmpty())));
474474
EXPECT_THAT(stroke.GetUpdatedRegion().AsRect(),
475475
Optional(RectNear(
476476
Rect::FromTwoPoints({-0.88, -1.87}, {4.90, 3.88}), 0.01)));
@@ -726,7 +726,7 @@ TEST(InProgressStrokeTest, StartAfterExtendingStroke) {
726726
ASSERT_NE(stroke.GetMesh(0).VertexCount(), 0u);
727727
ASSERT_NE(stroke.GetMesh(0).TriangleCount(), 0u);
728728
ASSERT_FALSE(stroke.GetMeshBounds(0).IsEmpty());
729-
ASSERT_THAT(stroke.GetIndexOutlines(0), ElementsAre(Not(IsEmpty())));
729+
ASSERT_THAT(stroke.GetCoatOutlines(0), ElementsAre(Not(IsEmpty())));
730730
ASSERT_FALSE(stroke.GetUpdatedRegion().IsEmpty());
731731

732732
stroke.Start(replacement_brush);
@@ -737,7 +737,7 @@ TEST(InProgressStrokeTest, StartAfterExtendingStroke) {
737737
EXPECT_EQ(stroke.GetMesh(0).VertexCount(), 0u);
738738
EXPECT_EQ(stroke.GetMesh(0).TriangleCount(), 0u);
739739
EXPECT_TRUE(stroke.GetMeshBounds(0).IsEmpty());
740-
EXPECT_THAT(stroke.GetIndexOutlines(0), ElementsAre(IsEmpty()));
740+
EXPECT_THAT(stroke.GetCoatOutlines(0), IsEmpty());
741741
EXPECT_TRUE(stroke.GetUpdatedRegion().IsEmpty());
742742
}
743743

ink/strokes/internal/BUILD.bazel

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ cc_library(
363363
"//ink/strokes/internal/brush_tip_extruder:mutable_mesh_view",
364364
"//ink/strokes/internal/brush_tip_extruder:side",
365365
"@com_google_absl//absl/algorithm:container",
366+
"@com_google_absl//absl/container:inlined_vector",
366367
"@com_google_absl//absl/log:absl_check",
367368
"@com_google_absl//absl/types:span",
368369
],
@@ -454,18 +455,17 @@ cc_library(
454455
":brush_tip_extruder",
455456
":brush_tip_modeler",
456457
":stroke_input_modeler",
458+
":stroke_outline",
457459
":stroke_shape_update",
458460
":stroke_vertex",
459461
"//ink/brush:brush_coat",
460462
"//ink/brush:brush_family",
461463
"//ink/brush:brush_paint",
462-
"//ink/brush:brush_tip",
463464
"//ink/geometry:envelope",
464465
"//ink/geometry:mutable_mesh",
465466
"//ink/strokes/input:stroke_input_batch",
466467
"//ink/types:duration",
467468
"@com_google_absl//absl/container:inlined_vector",
468-
"@com_google_absl//absl/log",
469469
"@com_google_absl//absl/log:absl_check",
470470
"@com_google_absl//absl/types:span",
471471
],

ink/strokes/internal/brush_tip_extruder.cc

Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -68,23 +68,12 @@ void BrushTipExtruder::StartStroke(float brush_epsilon,
6868
deleted_save_point_extrusions_.clear();
6969
geometry_.Reset(MutableMeshView(mesh));
7070
bounds_ = {};
71-
outline_.TruncateIndices({0, 0});
71+
outlines_.resize(1);
72+
outlines_[0].TruncateIndices({0, 0});
7273
}
7374

7475
namespace {
7576

76-
// Appends left and right indices to `outline` that have been added to
77-
// `geometry` since the last call to `BrushTipExtruder::Restore()`.
78-
void AppendNewIndicesToOutline(const Geometry& geometry,
79-
StrokeOutline& outline) {
80-
StrokeOutline::IndexCounts counts = outline.GetIndexCounts();
81-
auto new_left_indices =
82-
absl::MakeSpan(geometry.LeftSide().indices).subspan(counts.left);
83-
auto new_right_indices =
84-
absl::MakeSpan(geometry.RightSide().indices).subspan(counts.right);
85-
outline.AppendNewIndices(new_left_indices, new_right_indices);
86-
}
87-
8877
StrokeShapeUpdate ConstructUpdate(const Geometry& geometry,
8978
uint32_t triangle_count_before_update,
9079
uint32_t vertex_count_before_update) {
@@ -136,9 +125,6 @@ StrokeShapeUpdate BrushTipExtruder::ExtendStroke(
136125
}
137126

138127
ExtrudeBreakPoint();
139-
geometry_.UpdateMeshDerivatives();
140-
141-
AppendNewIndicesToOutline(geometry_, outline_);
142128
UpdateCurrentBounds();
143129
return ConstructUpdate(geometry_, triangle_count_before_update,
144130
vertex_count_before_update);
@@ -225,13 +211,35 @@ void BrushTipExtruder::Save() {
225211
geometry_.SetSavePoint();
226212
}
227213

214+
void BrushTipExtruder::TruncateOutlines() {
215+
ABSL_DCHECK_LE(geometry_.ExtrusionBreakCount(), outlines_.size());
216+
// If we're already working on the outline after the last remaining break
217+
// point, drop outlines after that (if there are any) and truncate the last
218+
// one.
219+
uint32_t max_num_outlines = geometry_.ExtrusionBreakCount() + 1;
220+
if (outlines_.size() >= max_num_outlines) {
221+
outlines_.resize(max_num_outlines);
222+
const Geometry::IndexCounts& last_extrusion_break_offset =
223+
geometry_.IndexCountsAtLastExtrusionBreak();
224+
ABSL_DCHECK_GE(geometry_.FirstMutatedLeftIndexOffsetInCurrentPartition(),
225+
last_extrusion_break_offset.left);
226+
ABSL_DCHECK_GE(geometry_.FirstMutatedRightIndexOffsetInCurrentPartition(),
227+
last_extrusion_break_offset.right);
228+
outlines_.back().TruncateIndices({
229+
.left = geometry_.FirstMutatedLeftIndexOffsetInCurrentPartition() -
230+
last_extrusion_break_offset.left,
231+
.right = geometry_.FirstMutatedRightIndexOffsetInCurrentPartition() -
232+
last_extrusion_break_offset.right,
233+
});
234+
}
235+
}
236+
228237
void BrushTipExtruder::Restore() {
229238
extrusions_.resize(saved_extrusion_data_count_);
230239
absl::c_copy(deleted_save_point_extrusions_,
231240
extrusions_.end() - deleted_save_point_extrusions_.size());
232241
geometry_.RevertToSavePoint();
233-
outline_.TruncateIndices({.left = geometry_.FirstMutatedLeftIndexOffset(),
234-
.right = geometry_.FirstMutatedRightIndexOffset()});
242+
TruncateOutlines();
235243
}
236244

237245
void BrushTipExtruder::ClearSinceLastExtrusionBreak(
@@ -268,8 +276,7 @@ void BrushTipExtruder::ClearSinceLastExtrusionBreak(
268276

269277
extrusions_.erase(first_extrusion_to_erase, extrusions_.end());
270278
geometry_.ClearSinceLastExtrusionBreak();
271-
outline_.TruncateIndices({.left = geometry_.FirstMutatedLeftIndexOffset(),
272-
.right = geometry_.FirstMutatedRightIndexOffset()});
279+
TruncateOutlines();
273280
ClearCachedPartialBounds();
274281
}
275282

@@ -507,8 +514,45 @@ void BrushTipExtruder::ExtrudeBreakPoint() {
507514
ExtrudeGeometry(current_extrusion_points_, extrusions_.back().GetState(),
508515
simplification_threshold_, is_winding_texture_particle_brush_,
509516
geometry_);
517+
518+
// If no new geometry was added after the last breakpoint, we don't need to
519+
// do anything.
520+
Geometry::IndexCounts counts_at_last_break =
521+
geometry_.IndexCountsAtLastExtrusionBreak();
522+
523+
if (geometry_.LeftSide().indices.size() <= counts_at_last_break.left &&
524+
geometry_.RightSide().indices.size() <= counts_at_last_break.right) {
525+
return;
526+
}
527+
528+
// Close the outline and update the mesh derivatives. This needs to be done
529+
// after recording the counts (because we want the counts back to the previous
530+
// break point) but before adding the new outline (because
531+
// UpdateMeshDerivatives can modify the outline indices). Possibly we're
532+
// adding a new empty outline here, possibly we're just filling in the end
533+
// of a last outline we already allocated.
534+
if (outlines_.size() == geometry_.ExtrusionBreakCount()) {
535+
outlines_.emplace_back();
536+
}
510537
geometry_.AddExtrusionBreak();
511538
extrusions_.emplace_back(BrushTipExtrusion::BreakPoint{});
539+
geometry_.UpdateMeshDerivatives();
540+
541+
// Complete the outline before the last breakpoint.
542+
StrokeOutline& outline = outlines_.back();
543+
ABSL_DCHECK_EQ(geometry_.ExtrusionBreakCount(), outlines_.size());
544+
StrokeOutline::IndexCounts outline_counts = outline.GetIndexCounts();
545+
ABSL_DCHECK_GE(geometry_.LeftSide().indices.size(),
546+
counts_at_last_break.left + outline_counts.left);
547+
absl::Span<const uint32_t> new_left_indices =
548+
absl::MakeSpan(geometry_.LeftSide().indices)
549+
.subspan(counts_at_last_break.left + outline_counts.left);
550+
ABSL_DCHECK_GE(geometry_.RightSide().indices.size(),
551+
counts_at_last_break.right + outline_counts.right);
552+
absl::Span<const uint32_t> new_right_indices =
553+
absl::MakeSpan(geometry_.RightSide().indices)
554+
.subspan(counts_at_last_break.right + outline_counts.right);
555+
outline.AppendNewIndices(new_left_indices, new_right_indices);
512556
}
513557

514558
} // namespace ink::strokes_internal

ink/strokes/internal/brush_tip_extruder.h

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <cstdint>
2020
#include <vector>
2121

22+
#include "absl/container/inlined_vector.h"
2223
#include "absl/types/span.h"
2324
#include "ink/geometry/envelope.h"
2425
#include "ink/geometry/mutable_mesh.h"
@@ -96,15 +97,10 @@ class BrushTipExtruder {
9697
// Returns the bounding region of positions extruded into the current mesh.
9798
const Envelope& GetBounds() const;
9899

99-
// Returns the current outline indices.
100-
//
101-
// If the returned span is not empty, its first and last elements reference
102-
// vertices at the end of the stroke. The indices traverse the mesh such that
103-
// the outline has a negative winding number when viewed from the positive
104-
// z-axis. I.e. the outline will be appear in clockwise orientation if the
105-
// y-axis points up, and in counter-clockwise orientation if the y-axis points
106-
// down.
107-
absl::Span<const uint32_t> GetOutlineIndices() const;
100+
// Returns the outlines for the current brush tip. This can include empty
101+
// outlines. (In particular, we greedily allocate the first outline, so
102+
// that is empty if the stroke is empty.)
103+
absl::Span<const StrokeOutline> GetOutlines() const;
108104

109105
private:
110106
// Data used to incrementally update the bounds of geometry extruded into the
@@ -155,6 +151,9 @@ class BrushTipExtruder {
155151
// Restores the saved state of the extruder.
156152
void Restore();
157153

154+
// Truncate outlines to match the current geometry.
155+
void TruncateOutlines();
156+
158157
// Clears the geometry, extrusions, and outline indices since the last break
159158
// in extrusion. This is either since the last explicitly added break-point,
160159
// or since the implicit break-point at start of the stroke.
@@ -220,7 +219,7 @@ class BrushTipExtruder {
220219
ExtrusionPoints current_extrusion_points_;
221220
brush_tip_extruder_internal::Geometry geometry_;
222221
Bounds bounds_;
223-
StrokeOutline outline_;
222+
absl::InlinedVector<StrokeOutline, 1> outlines_;
224223
};
225224

226225
// ---------------------------------------------------------------------------
@@ -230,8 +229,8 @@ inline const Envelope& BrushTipExtruder::GetBounds() const {
230229
return bounds_.current;
231230
}
232231

233-
inline absl::Span<const uint32_t> BrushTipExtruder::GetOutlineIndices() const {
234-
return outline_.GetIndices();
232+
inline absl::Span<const StrokeOutline> BrushTipExtruder::GetOutlines() const {
233+
return outlines_;
235234
}
236235

237236
} // namespace ink::strokes_internal

0 commit comments

Comments
 (0)