Skip to content

Commit 0a5e876

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 0a5e876

25 files changed

+2892
-221
lines changed

ink/brush/BUILD.bazel

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ cc_library(
3333
":brush_tip",
3434
":color_function",
3535
":easing_function",
36+
":version",
3637
"//:gtest_for_library_testonly",
3738
"//ink/geometry:type_matchers",
3839
"//ink/types:type_matchers",
@@ -82,9 +83,9 @@ cc_library(
8283
srcs = ["brush_coat.cc"],
8384
hdrs = ["brush_coat.h"],
8485
deps = [
85-
":brush_behavior",
8686
":brush_paint",
8787
":brush_tip",
88+
":version",
8889
"//ink/geometry:mesh_format",
8990
"@com_google_absl//absl/container:flat_hash_set",
9091
"@com_google_absl//absl/container:inlined_vector",
@@ -120,6 +121,7 @@ cc_library(
120121
":brush_coat",
121122
":brush_paint",
122123
":brush_tip",
124+
":version",
123125
"//ink/types:duration",
124126
"@com_google_absl//absl/status",
125127
"@com_google_absl//absl/status:statusor",
@@ -160,6 +162,7 @@ cc_library(
160162
hdrs = ["brush_tip.h"],
161163
deps = [
162164
":brush_behavior",
165+
":version",
163166
"//ink/geometry:angle",
164167
"//ink/geometry:mesh_format",
165168
"//ink/geometry:vec",
@@ -190,6 +193,7 @@ cc_library(
190193
srcs = ["color_function.cc"],
191194
hdrs = ["color_function.h"],
192195
deps = [
196+
":version",
193197
"//ink/color",
194198
"@com_google_absl//absl/status",
195199
"@com_google_absl//absl/strings",
@@ -219,6 +223,7 @@ cc_library(
219223
srcs = ["easing_function.cc"],
220224
hdrs = ["easing_function.h"],
221225
deps = [
226+
":version",
222227
"//ink/geometry:point",
223228
"@com_google_absl//absl/status",
224229
"@com_google_absl//absl/strings",
@@ -245,6 +250,7 @@ cc_library(
245250
hdrs = ["brush_behavior.h"],
246251
deps = [
247252
":easing_function",
253+
":version",
248254
"@com_google_absl//absl/status",
249255
"@com_google_absl//absl/strings",
250256
"@com_google_absl//absl/strings:str_format",
@@ -273,6 +279,7 @@ cc_library(
273279
hdrs = ["brush_paint.h"],
274280
deps = [
275281
":color_function",
282+
":version",
276283
"//ink/geometry:angle",
277284
"//ink/geometry:mesh_format",
278285
"//ink/geometry:vec",
@@ -365,6 +372,12 @@ cc_library(
365372
],
366373
)
367374

375+
cc_library(
376+
name = "version",
377+
hdrs = ["version.h"],
378+
deps = ["@com_google_absl//absl/strings"],
379+
)
380+
368381
cc_test(
369382
name = "stock_brushes_test",
370383
srcs = ["stock_brushes_test.cc"],

ink/brush/brush_behavior.cc

Lines changed: 272 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,277 @@ absl::Status ValidateBrushBehavior(const BrushBehavior& behavior) {
473474
return absl::OkStatus();
474475
}
475476

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

0 commit comments

Comments
 (0)