Skip to content

Commit 0d1a2eb

Browse files
maxmmitchellcopybara-github
authored andcommitted
Introduces code to calculate the minimum required version of Ink for a BrushFamily (and each part of a BrushFamily). The code is used during serialization to compute the value to store in the min_version field. During deserialization, the min_version field is used to determine if a BrushFamily is compatible with the current version of Ink; if it isn't, decoding it is aborted.
To ensure the code in the web of `CalculateMinimumRequiredVersion` functions lines up with expectations, protobuf options are used to build consistency check tests. `CalculateMinimumRequiredVersion` serves as the enforcement of the expectations set in the protobuf itself. The tests are: * A fuzz test to generate valid brush families and serialize them. Then, it checks the value in `min_version` against all the options set for the fields, messages, and enum values, to ensure it is equal to the highest value set for the relevant options. This ensures `CalculateMinimumRequiredVersion` is consistent with the protobuf options. * A test to iterate over all the messages, fields, and enum values in `brush_family.proto`, starting at the top-level `BrushFamily` message, and verify that they all have a relevant `field_min_version`, `message_min_version`, or `enum_value_min_version` set, and that this value set is less than or equal to the maximum compatible version (`version::kMax`). * A test to verify that decoding a `proto::Version` higher than `version::kMax` will fail. * A test to verify that decoding a `proto::Version` with an unrecognized `proto::Version::Cycle` enum value will fail. * A test to verify that decoding a `proto::BrushFamily` with no value set for its `min_version` field will ***not*** fail, to ensure older brushes are still able to be loaded. A more direct implementation, using reflection to examine the descriptors of the protobuf directly during serialization, was considered, but ultimately rejected. It would have required both static and runtime reflection, and increased `libink.so` size by ~2%. PiperOrigin-RevId: 872916698
1 parent 87fe423 commit 0d1a2eb

23 files changed

Lines changed: 2766 additions & 221 deletions

ink/brush/BUILD.bazel

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ cc_library(
8282
srcs = ["brush_coat.cc"],
8383
hdrs = ["brush_coat.h"],
8484
deps = [
85-
":brush_behavior",
8685
":brush_paint",
8786
":brush_tip",
87+
":version",
8888
"//ink/geometry:mesh_format",
8989
"@com_google_absl//absl/container:flat_hash_set",
9090
"@com_google_absl//absl/container:inlined_vector",
@@ -120,6 +120,7 @@ cc_library(
120120
":brush_coat",
121121
":brush_paint",
122122
":brush_tip",
123+
":version",
123124
"//ink/types:duration",
124125
"@com_google_absl//absl/status",
125126
"@com_google_absl//absl/status:statusor",
@@ -160,6 +161,7 @@ cc_library(
160161
hdrs = ["brush_tip.h"],
161162
deps = [
162163
":brush_behavior",
164+
":version",
163165
"//ink/geometry:angle",
164166
"//ink/geometry:mesh_format",
165167
"//ink/geometry:vec",
@@ -190,6 +192,7 @@ cc_library(
190192
srcs = ["color_function.cc"],
191193
hdrs = ["color_function.h"],
192194
deps = [
195+
":version",
193196
"//ink/color",
194197
"@com_google_absl//absl/status",
195198
"@com_google_absl//absl/strings",
@@ -219,6 +222,7 @@ cc_library(
219222
srcs = ["easing_function.cc"],
220223
hdrs = ["easing_function.h"],
221224
deps = [
225+
":version",
222226
"//ink/geometry:point",
223227
"@com_google_absl//absl/status",
224228
"@com_google_absl//absl/strings",
@@ -245,6 +249,7 @@ cc_library(
245249
hdrs = ["brush_behavior.h"],
246250
deps = [
247251
":easing_function",
252+
":version",
248253
"@com_google_absl//absl/status",
249254
"@com_google_absl//absl/strings",
250255
"@com_google_absl//absl/strings:str_format",
@@ -273,6 +278,7 @@ cc_library(
273278
hdrs = ["brush_paint.h"],
274279
deps = [
275280
":color_function",
281+
":version",
276282
"//ink/geometry:angle",
277283
"//ink/geometry:mesh_format",
278284
"//ink/geometry:vec",
@@ -365,6 +371,12 @@ cc_library(
365371
],
366372
)
367373

374+
cc_library(
375+
name = "version",
376+
hdrs = ["version.h"],
377+
deps = ["@com_google_absl//absl/strings"],
378+
)
379+
368380
cc_test(
369381
name = "stock_brushes_test",
370382
srcs = ["stock_brushes_test.cc"],

ink/brush/brush_behavior.cc

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "absl/strings/str_format.h"
2626
#include "absl/strings/str_join.h"
2727
#include "ink/brush/easing_function.h"
28+
#include "ink/brush/version.h"
2829

2930
namespace ink {
3031

@@ -473,6 +474,224 @@ absl::Status ValidateBrushBehavior(const BrushBehavior& behavior) {
473474
return absl::OkStatus();
474475
}
475476

477+
namespace {
478+
479+
Version CalculateMinimumRequiredVersion(BrushBehavior::Source source) {
480+
switch (source) {
481+
case BrushBehavior::Source::kNormalizedPressure:
482+
case BrushBehavior::Source::kTiltInRadians:
483+
case BrushBehavior::Source::kTiltXInRadians:
484+
case BrushBehavior::Source::kTiltYInRadians:
485+
case BrushBehavior::Source::kOrientationInRadians:
486+
case BrushBehavior::Source::kOrientationAboutZeroInRadians:
487+
case BrushBehavior::Source::kSpeedInMultiplesOfBrushSizePerSecond:
488+
case BrushBehavior::Source::kVelocityXInMultiplesOfBrushSizePerSecond:
489+
case BrushBehavior::Source::kVelocityYInMultiplesOfBrushSizePerSecond:
490+
case BrushBehavior::Source::kDirectionInRadians:
491+
case BrushBehavior::Source::kDirectionAboutZeroInRadians:
492+
case BrushBehavior::Source::kNormalizedDirectionX:
493+
case BrushBehavior::Source::kNormalizedDirectionY:
494+
case BrushBehavior::Source::kDistanceTraveledInMultiplesOfBrushSize:
495+
case BrushBehavior::Source::kTimeOfInputInSeconds:
496+
case BrushBehavior::Source::
497+
kPredictedDistanceTraveledInMultiplesOfBrushSize:
498+
case BrushBehavior::Source::kPredictedTimeElapsedInSeconds:
499+
case BrushBehavior::Source::kDistanceRemainingInMultiplesOfBrushSize:
500+
case BrushBehavior::Source::kTimeSinceInputInSeconds:
501+
case BrushBehavior::Source::
502+
kAccelerationInMultiplesOfBrushSizePerSecondSquared:
503+
case BrushBehavior::Source::
504+
kAccelerationXInMultiplesOfBrushSizePerSecondSquared:
505+
case BrushBehavior::Source::
506+
kAccelerationYInMultiplesOfBrushSizePerSecondSquared:
507+
case BrushBehavior::Source::
508+
kAccelerationForwardInMultiplesOfBrushSizePerSecondSquared:
509+
case BrushBehavior::Source::
510+
kAccelerationLateralInMultiplesOfBrushSizePerSecondSquared:
511+
case BrushBehavior::Source::kInputSpeedInCentimetersPerSecond:
512+
case BrushBehavior::Source::kInputVelocityXInCentimetersPerSecond:
513+
case BrushBehavior::Source::kInputVelocityYInCentimetersPerSecond:
514+
case BrushBehavior::Source::kInputDistanceTraveledInCentimeters:
515+
case BrushBehavior::Source::kPredictedInputDistanceTraveledInCentimeters:
516+
case BrushBehavior::Source::kInputAccelerationInCentimetersPerSecondSquared:
517+
case BrushBehavior::Source::
518+
kInputAccelerationXInCentimetersPerSecondSquared:
519+
case BrushBehavior::Source::
520+
kInputAccelerationYInCentimetersPerSecondSquared:
521+
case BrushBehavior::Source::
522+
kInputAccelerationForwardInCentimetersPerSecondSquared:
523+
case BrushBehavior::Source::
524+
kInputAccelerationLateralInCentimetersPerSecondSquared:
525+
case BrushBehavior::Source::kDistanceRemainingAsFractionOfStrokeLength:
526+
return version::k1_0_0;
527+
}
528+
}
529+
530+
Version CalculateMinimumRequiredVersion(BrushBehavior::Target target) {
531+
switch (target) {
532+
case BrushBehavior::Target::kWidthMultiplier:
533+
case BrushBehavior::Target::kHeightMultiplier:
534+
case BrushBehavior::Target::kSizeMultiplier:
535+
case BrushBehavior::Target::kSlantOffsetInRadians:
536+
case BrushBehavior::Target::kPinchOffset:
537+
case BrushBehavior::Target::kRotationOffsetInRadians:
538+
case BrushBehavior::Target::kCornerRoundingOffset:
539+
case BrushBehavior::Target::kPositionOffsetXInMultiplesOfBrushSize:
540+
case BrushBehavior::Target::kPositionOffsetYInMultiplesOfBrushSize:
541+
case BrushBehavior::Target::kPositionOffsetForwardInMultiplesOfBrushSize:
542+
case BrushBehavior::Target::kPositionOffsetLateralInMultiplesOfBrushSize:
543+
case BrushBehavior::Target::kTextureAnimationProgressOffset:
544+
case BrushBehavior::Target::kHueOffsetInRadians:
545+
case BrushBehavior::Target::kSaturationMultiplier:
546+
case BrushBehavior::Target::kLuminosity:
547+
case BrushBehavior::Target::kOpacityMultiplier:
548+
return version::k1_0_0;
549+
}
550+
}
551+
552+
Version CalculateMinimumRequiredVersion(BrushBehavior::PolarTarget target) {
553+
switch (target) {
554+
case BrushBehavior::PolarTarget::
555+
kPositionOffsetAbsoluteInRadiansAndMultiplesOfBrushSize:
556+
case BrushBehavior::PolarTarget::
557+
kPositionOffsetRelativeInRadiansAndMultiplesOfBrushSize:
558+
return version::k1_0_0;
559+
}
560+
}
561+
562+
Version CalculateMinimumRequiredVersion(
563+
BrushBehavior::OutOfRange out_of_range) {
564+
switch (out_of_range) {
565+
case BrushBehavior::OutOfRange::kClamp:
566+
case BrushBehavior::OutOfRange::kMirror:
567+
case BrushBehavior::OutOfRange::kRepeat:
568+
return version::k1_0_0;
569+
}
570+
}
571+
572+
Version CalculateMinimumRequiredVersion(
573+
BrushBehavior::EnabledToolTypes enabled) {
574+
return version::k1_0_0;
575+
}
576+
577+
Version CalculateMinimumRequiredVersion(
578+
BrushBehavior::OptionalInputProperty input) {
579+
switch (input) {
580+
case BrushBehavior::OptionalInputProperty::kPressure:
581+
case BrushBehavior::OptionalInputProperty::kTilt:
582+
case BrushBehavior::OptionalInputProperty::kOrientation:
583+
case BrushBehavior::OptionalInputProperty::kTiltXAndY:
584+
return version::k1_0_0;
585+
}
586+
}
587+
588+
Version CalculateMinimumRequiredVersion(BrushBehavior::BinaryOp operation) {
589+
switch (operation) {
590+
case BrushBehavior::BinaryOp::kProduct:
591+
case BrushBehavior::BinaryOp::kSum:
592+
return version::k1_0_0;
593+
case BrushBehavior::BinaryOp::kMin:
594+
case BrushBehavior::BinaryOp::kMax:
595+
return version::k1_1_0_alpha_01;
596+
}
597+
}
598+
599+
Version CalculateMinimumRequiredVersion(
600+
BrushBehavior::ProgressDomain progress_domain) {
601+
switch (progress_domain) {
602+
case BrushBehavior::ProgressDomain::kDistanceInCentimeters:
603+
case BrushBehavior::ProgressDomain::kDistanceInMultiplesOfBrushSize:
604+
case BrushBehavior::ProgressDomain::kTimeInSeconds:
605+
return version::k1_0_0;
606+
}
607+
}
608+
609+
Version CalculateMinimumRequiredVersion(
610+
BrushBehavior::Interpolation interpolation) {
611+
switch (interpolation) {
612+
case BrushBehavior::Interpolation::kLerp:
613+
case BrushBehavior::Interpolation::kInverseLerp:
614+
return version::k1_0_0;
615+
}
616+
}
617+
618+
Version CalculateMinimumRequiredVersion(BrushBehavior::SourceNode node) {
619+
Version max_version =
620+
CalculateMinimumRequiredVersion(node.source_out_of_range_behavior);
621+
max_version =
622+
MaxVersion(max_version, CalculateMinimumRequiredVersion(node.source));
623+
return max_version;
624+
}
625+
626+
Version CalculateMinimumRequiredVersion(BrushBehavior::ConstantNode node) {
627+
return version::k1_0_0;
628+
}
629+
630+
Version CalculateMinimumRequiredVersion(BrushBehavior::NoiseNode node) {
631+
return CalculateMinimumRequiredVersion(node.vary_over);
632+
}
633+
634+
Version CalculateMinimumRequiredVersion(
635+
BrushBehavior::FallbackFilterNode node) {
636+
return CalculateMinimumRequiredVersion(node.is_fallback_for);
637+
}
638+
639+
Version CalculateMinimumRequiredVersion(
640+
BrushBehavior::ToolTypeFilterNode node) {
641+
return CalculateMinimumRequiredVersion(node.enabled_tool_types);
642+
}
643+
644+
Version CalculateMinimumRequiredVersion(BrushBehavior::DampingNode node) {
645+
return CalculateMinimumRequiredVersion(node.damping_source);
646+
}
647+
648+
Version CalculateMinimumRequiredVersion(BrushBehavior::ResponseNode node) {
649+
return brush_internal::CalculateMinimumRequiredVersion(node.response_curve);
650+
}
651+
652+
Version CalculateMinimumRequiredVersion(BrushBehavior::IntegralNode node) {
653+
Version max_version = version::k1_1_0_alpha_01;
654+
max_version = MaxVersion(
655+
max_version, CalculateMinimumRequiredVersion(node.integrate_over));
656+
max_version = MaxVersion(
657+
max_version,
658+
CalculateMinimumRequiredVersion(node.integral_out_of_range_behavior));
659+
return max_version;
660+
}
661+
662+
Version CalculateMinimumRequiredVersion(BrushBehavior::BinaryOpNode node) {
663+
return CalculateMinimumRequiredVersion(node.operation);
664+
}
665+
666+
Version CalculateMinimumRequiredVersion(BrushBehavior::InterpolationNode node) {
667+
return CalculateMinimumRequiredVersion(node.interpolation);
668+
}
669+
670+
Version CalculateMinimumRequiredVersion(BrushBehavior::TargetNode node) {
671+
return CalculateMinimumRequiredVersion(node.target);
672+
}
673+
674+
Version CalculateMinimumRequiredVersion(BrushBehavior::PolarTargetNode node) {
675+
return CalculateMinimumRequiredVersion(node.target);
676+
}
677+
678+
Version CalculateMinimumRequiredVersion(const BrushBehavior::Node& node) {
679+
return std::visit(
680+
[](const auto& node) { return CalculateMinimumRequiredVersion(node); },
681+
node);
682+
}
683+
684+
} // namespace
685+
686+
Version CalculateMinimumRequiredVersion(const BrushBehavior& behavior) {
687+
Version max_version = version::k1_0_0;
688+
for (const BrushBehavior::Node& node : behavior.nodes) {
689+
max_version =
690+
MaxVersion(max_version, CalculateMinimumRequiredVersion(node));
691+
}
692+
return max_version;
693+
}
694+
476695
std::string ToFormattedString(BrushBehavior::Source source) {
477696
switch (source) {
478697
case BrushBehavior::Source::kNormalizedPressure:

ink/brush/brush_behavior.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#include "absl/status/status.h"
2525
#include "ink/brush/easing_function.h"
26+
#include "ink/brush/version.h"
2627

2728
namespace ink {
2829

@@ -690,6 +691,10 @@ absl::Status ValidateBrushBehaviorTopLevel(const BrushBehavior& behavior);
690691

691692
absl::Status ValidateBrushBehaviorNode(const BrushBehavior::Node& node);
692693

694+
// Calculates the minimum version of the Ink library that is required to use
695+
// this brush behavior.
696+
Version CalculateMinimumRequiredVersion(const BrushBehavior& behavior);
697+
693698
std::string ToFormattedString(BrushBehavior::Source source);
694699
std::string ToFormattedString(BrushBehavior::Target target);
695700
std::string ToFormattedString(BrushBehavior::PolarTarget target);

ink/brush/brush_coat.cc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
#include "absl/strings/str_cat.h"
2222
#include "absl/strings/str_format.h"
2323
#include "absl/strings/str_join.h"
24-
#include "ink/brush/brush_behavior.h"
2524
#include "ink/brush/brush_paint.h"
2625
#include "ink/brush/brush_tip.h"
26+
#include "ink/brush/version.h"
2727
#include "ink/geometry/mesh_format.h"
2828

2929
namespace ink {
@@ -45,6 +45,15 @@ absl::Status ValidateBrushCoat(const BrushCoat& coat) {
4545
return absl::OkStatus();
4646
}
4747

48+
Version CalculateMinimumRequiredVersion(const BrushCoat& coat) {
49+
Version max_version = CalculateMinimumRequiredVersion(coat.tip);
50+
for (const BrushPaint& paint : coat.paint_preferences) {
51+
max_version =
52+
MaxVersion(max_version, CalculateMinimumRequiredVersion(paint));
53+
}
54+
return max_version;
55+
}
56+
4857
void AddAttributeIdsRequiredByCoat(
4958
const BrushCoat& coat,
5059
absl::flat_hash_set<MeshFormat::AttributeId>& attribute_ids) {

ink/brush/brush_coat.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "absl/status/status.h"
2323
#include "ink/brush/brush_paint.h"
2424
#include "ink/brush/brush_tip.h"
25+
#include "ink/brush/version.h"
2526
#include "ink/geometry/mesh_format.h"
2627

2728
namespace ink {
@@ -46,6 +47,10 @@ namespace brush_internal {
4647
// BrushFamily, and returns an error if not.
4748
absl::Status ValidateBrushCoat(const BrushCoat& coat);
4849

50+
// Calculates the minimum version of the Ink library that is required to use
51+
// this brush coat.
52+
Version CalculateMinimumRequiredVersion(const BrushCoat& coat);
53+
4954
// Adds the mesh attribute IDs that are required to properly render a mesh
5055
// made with this brush coat to the given `attribute_ids` set. This will always
5156
// include `kPosition` and certain other attribute IDs (`kSideDerivative`,

ink/brush/brush_family.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "ink/brush/brush_coat.h"
2929
#include "ink/brush/brush_paint.h"
3030
#include "ink/brush/brush_tip.h"
31+
#include "ink/brush/version.h"
3132
#include "ink/types/duration.h"
3233

3334
namespace ink {
@@ -94,6 +95,15 @@ std::string BrushFamily::ToFormattedString() const {
9495
return formatted;
9596
}
9697

98+
Version BrushFamily::CalculateMinimumRequiredVersion() const {
99+
Version max_version = version::k1_0_0;
100+
for (const BrushCoat& coat : coats_) {
101+
max_version = MaxVersion(
102+
max_version, brush_internal::CalculateMinimumRequiredVersion(coat));
103+
}
104+
return max_version;
105+
}
106+
97107
namespace brush_internal {
98108
namespace {
99109

0 commit comments

Comments
 (0)