#include "PropertyParserTransform.h" #include "../../Include/RmlUi/Core/NumericValue.h" #include "../../Include/RmlUi/Core/Transform.h" #include "../../Include/RmlUi/Core/TransformPrimitive.h" #include namespace Rml { PropertyParserTransform::PropertyParserTransform() : number(Unit::NUMBER), length(Unit::LENGTH, Unit::PX), length_pct(Unit::LENGTH_PERCENT, Unit::PX), angle(Unit::ANGLE, Unit::RAD) {} PropertyParserTransform::~PropertyParserTransform() {} bool PropertyParserTransform::ParseValue(Property& property, const String& value, const ParameterMap& /*parameters*/) const { if (value == "none") { property.value = Variant(TransformPtr()); property.unit = Unit::TRANSFORM; return true; } TransformPtr transform = MakeShared(); char const* next = value.c_str(); NumericValue args[16]; const PropertyParser* number16[] = {&number, &number, &number, &number, &number, &number, &number, &number, &number, &number, &number, &number, &number, &number, &number, &number}; const PropertyParser* lengthpct2_length1[] = {&length_pct, &length_pct, &length}; const PropertyParser* number3angle1[] = {&number, &number, &number, &angle}; const PropertyParser* angle2[] = {&angle, &angle}; const PropertyParser* length1[] = {&length}; // For semantic purposes, define subsets of the above parsers when scanning primitives below. auto lengthpct1 = lengthpct2_length1; auto lengthpct2 = lengthpct2_length1; auto angle1 = angle2; auto number1 = number16; auto number2 = number16; auto number3 = number16; auto number6 = number16; while (*next) { using namespace Transforms; int bytes_read = 0; if (Scan(bytes_read, next, "perspective", length1, args, 1)) { transform->AddPrimitive({Perspective(args)}); } else if (Scan(bytes_read, next, "matrix", number6, args, 6)) { transform->AddPrimitive({Matrix2D(args)}); } else if (Scan(bytes_read, next, "matrix3d", number16, args, 16)) { transform->AddPrimitive({Matrix3D(args)}); } else if (Scan(bytes_read, next, "translateX", lengthpct1, args, 1)) { transform->AddPrimitive({TranslateX(args)}); } else if (Scan(bytes_read, next, "translateY", lengthpct1, args, 1)) { transform->AddPrimitive({TranslateY(args)}); } else if (Scan(bytes_read, next, "translateZ", length1, args, 1)) { transform->AddPrimitive({TranslateZ(args)}); } else if (Scan(bytes_read, next, "translate", lengthpct2, args, 2)) { transform->AddPrimitive({Translate2D(args)}); } else if (Scan(bytes_read, next, "translate3d", lengthpct2_length1, args, 3)) { transform->AddPrimitive({Translate3D(args)}); } else if (Scan(bytes_read, next, "scaleX", number1, args, 1)) { transform->AddPrimitive({ScaleX(args)}); } else if (Scan(bytes_read, next, "scaleY", number1, args, 1)) { transform->AddPrimitive({ScaleY(args)}); } else if (Scan(bytes_read, next, "scaleZ", number1, args, 1)) { transform->AddPrimitive({ScaleZ(args)}); } else if (Scan(bytes_read, next, "scale", number2, args, 2)) { transform->AddPrimitive({Scale2D(args)}); } else if (Scan(bytes_read, next, "scale", number1, args, 1)) { args[1] = args[0]; transform->AddPrimitive({Scale2D(args)}); } else if (Scan(bytes_read, next, "scale3d", number3, args, 3)) { transform->AddPrimitive({Scale3D(args)}); } else if (Scan(bytes_read, next, "rotateX", angle1, args, 1)) { transform->AddPrimitive({RotateX(args)}); } else if (Scan(bytes_read, next, "rotateY", angle1, args, 1)) { transform->AddPrimitive({RotateY(args)}); } else if (Scan(bytes_read, next, "rotateZ", angle1, args, 1)) { transform->AddPrimitive({RotateZ(args)}); } else if (Scan(bytes_read, next, "rotate", angle1, args, 1)) { transform->AddPrimitive({Rotate2D(args)}); } else if (Scan(bytes_read, next, "rotate3d", number3angle1, args, 4)) { transform->AddPrimitive({Rotate3D(args)}); } else if (Scan(bytes_read, next, "skewX", angle1, args, 1)) { transform->AddPrimitive({SkewX(args)}); } else if (Scan(bytes_read, next, "skewY", angle1, args, 1)) { transform->AddPrimitive({SkewY(args)}); } else if (Scan(bytes_read, next, "skew", angle2, args, 2)) { transform->AddPrimitive({Skew2D(args)}); } if (bytes_read > 0) { next += bytes_read; } else { return false; } } property.value = Variant(std::move(transform)); property.unit = Unit::TRANSFORM; return true; } bool PropertyParserTransform::Scan(int& out_bytes_read, const char* str, const char* keyword, const PropertyParser** parsers, NumericValue* args, int nargs) const { out_bytes_read = 0; int total_bytes_read = 0, bytes_read = 0; /* skip leading white space */ bytes_read = 0; sscanf(str, " %n", &bytes_read); str += bytes_read; total_bytes_read += bytes_read; /* find the keyword */ if (!memcmp(str, keyword, strlen(keyword))) { bytes_read = (int)strlen(keyword); str += bytes_read; total_bytes_read += bytes_read; } else { return false; } /* skip any white space */ bytes_read = 0; sscanf(str, " %n", &bytes_read); str += bytes_read; total_bytes_read += bytes_read; /* find the opening brace */ bytes_read = 0; if (sscanf(str, " ( %n", &bytes_read), bytes_read) { str += bytes_read; total_bytes_read += bytes_read; } else { return false; } /* use the quicker stack-based argument buffer, if possible */ char* arg = nullptr; char arg_stack[1024]; String arg_heap; if (strlen(str) < sizeof(arg_stack)) { arg = arg_stack; } else { arg_heap = str; arg = &arg_heap[0]; } /* parse the arguments */ for (int i = 0; i < nargs; ++i) { Property prop; bytes_read = 0; if (sscanf(str, " %[^,)] %n", arg, &bytes_read), bytes_read && parsers[i]->ParseValue(prop, String(arg), ParameterMap())) { args[i].number = prop.value.Get(); args[i].unit = prop.unit; str += bytes_read; total_bytes_read += bytes_read; } else { return false; } /* find the comma */ if (i < nargs - 1) { bytes_read = 0; if (sscanf(str, " , %n", &bytes_read), bytes_read) { str += bytes_read; total_bytes_read += bytes_read; } else { return false; } } } /* find the closing brace */ bytes_read = 0; if (sscanf(str, " ) %n", &bytes_read), bytes_read) { str += bytes_read; total_bytes_read += bytes_read; } else { return false; } out_bytes_read = total_bytes_read; return total_bytes_read > 0; } } // namespace Rml