From 5093a01479001c0761c14f26b7d7e5db1ff0dbbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Mon, 14 Oct 2019 14:38:17 +0200 Subject: [PATCH 1/3] [core] map image type to string until we have a dedicated implementation --- platform/android/scripts/generate-style-code.js | 6 ++++++ platform/darwin/scripts/generate-style-code.js | 9 +++++++++ scripts/generate-style-code.js | 3 +++ 3 files changed, 18 insertions(+) diff --git a/platform/android/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js index fa814f89af5..83fb5c116e3 100755 --- a/platform/android/scripts/generate-style-code.js +++ b/platform/android/scripts/generate-style-code.js @@ -72,6 +72,7 @@ global.propertyType = function propertyType(property) { case 'formatted': return 'Formatted'; case 'string': + case 'image': // TODO: replace once we implement image expressions return 'String'; case 'enum': return 'String'; @@ -93,6 +94,7 @@ global.propertyJavaType = function propertyType(property) { case 'formatted': return 'Formatted'; case 'string': + case 'image': // TODO: replace once we implement image expressions return 'String'; case 'enum': return 'String'; @@ -141,6 +143,7 @@ global.propertyNativeType = function (property) { return 'float'; case 'formatted': case 'string': + case 'image': // TODO: replace once we implement image expressions return 'std::string'; case 'enum': if(property['light-property']){ @@ -177,6 +180,7 @@ global.defaultExpressionJava = function(property) { case 'formatted': return 'format'; case 'string': + case 'image': // TODO: replace once we implement image expressions return "string"; case 'enum': return "string"; @@ -203,6 +207,7 @@ global.defaultValueJava = function(property) { case 'formatted': return 'new Formatted(new FormattedSection("default"))' case 'string': + case 'image': // TODO: replace once we implement image expressions return '"' + property['default'] + '"'; case 'enum': return snakeCaseUpper(property.name) + "_" + snakeCaseUpper(Object.keys(property.values)[0]); @@ -335,6 +340,7 @@ global.evaluatedType = function (property) { return 'float'; case 'formatted': case 'string': + case 'image': // TODO: replace once we implement image expressions return 'std::string'; case 'enum': return (isLightProperty(property) ? 'Light' : '') + `${camelize(property.name)}Type`; diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js index 38066c9f43f..75dbdf367cf 100755 --- a/platform/darwin/scripts/generate-style-code.js +++ b/platform/darwin/scripts/generate-style-code.js @@ -159,6 +159,7 @@ global.objCTestValue = function (property, layerType, arraysAsStructs, indent) { `@"'${_.startCase(propertyName)}'"` : `@"${_.startCase(propertyName)}"`; case 'string': + case 'image': // TODO: replace once we implement image expressions return `@"'${_.startCase(propertyName)}'"`; case 'enum': return `@"'${_.last(_.keys(property.values))}'"`; @@ -208,6 +209,7 @@ global.mbglTestValue = function (property, layerType) { return '1.0'; case 'formatted': case 'string': + case 'image': // TODO: replace once we implement image expressions return `"${_.startCase(propertyName)}"`; case 'enum': { let type = camelize(originalPropertyName(property)); @@ -294,6 +296,7 @@ global.testHelperMessage = function (property, layerType, isFunction) { return 'testNumber' + fnSuffix; case 'formatted': case 'string': + case 'image': // TODO: replace once we implement image expressions return 'testString' + fnSuffix; case 'enum': let objCType = global.objCType(layerType, property.name); @@ -474,6 +477,7 @@ global.describeType = function (property) { return 'numeric'; case 'formatted': case 'string': + case 'image': // TODO: replace once we implement image expressions return 'string'; case 'enum': return '`MGL' + camelize(property.name) + '`'; @@ -522,6 +526,7 @@ global.describeValue = function (value, property, layerType) { return 'the float ' + '`' + formatNumber(value) + '`'; case 'formatted': case 'string': + case 'image': // TODO: replace once we implement image expressions if (value === '') { return 'the empty string'; } @@ -608,6 +613,7 @@ global.propertyType = function (property) { return 'NSNumber *'; case 'formatted': case 'string': + case 'image': // TODO: replace once we implement image expressions return 'NSString *'; case 'enum': return 'NSValue *'; @@ -640,6 +646,7 @@ global.isInterpolatable = function (property) { return type !== 'boolean' && type !== 'enum' && type !== 'string' && + type !== 'image' && type !== 'formatted'; }; @@ -653,6 +660,7 @@ global.valueTransformerArguments = function (property) { case 'formatted': return ['mbgl::style::expression::Formatted', objCType]; case 'string': + case 'image': // TODO: replace once we implement image expressions return ['std::string', objCType]; case 'enum': return [mbglType(property), 'NSValue *', mbglType(property), `MGL${camelize(property.name)}`]; @@ -692,6 +700,7 @@ global.mbglType = function(property) { case 'formatted': return 'mbgl::style::expression::Formatted'; case 'string': + case 'image': // TODO: replace once we implement image expressions return 'std::string'; case 'enum': { let type = camelize(originalPropertyName(property)); diff --git a/scripts/generate-style-code.js b/scripts/generate-style-code.js index 1f26e6fd86e..0aedba909b1 100755 --- a/scripts/generate-style-code.js +++ b/scripts/generate-style-code.js @@ -36,6 +36,7 @@ global.expressionType = function (property) { case 'enum': return 'NumberType'; case 'string': + case 'image': // TODO: replace once we implement image expressions return 'StringType'; case 'color': return `ColorType`; @@ -68,6 +69,7 @@ global.evaluatedType = function (property) { case 'formatted': return 'expression::Formatted'; case 'string': + case 'image': // TODO: replace once we implement image expressions return 'std::string'; case 'enum': return (isLightProperty(property) ? 'Light' : '') + `${camelize(property.name)}Type`; @@ -166,6 +168,7 @@ global.defaultValue = function (property) { } case 'formatted': case 'string': + case 'image': // TODO: replace once we implement image expressions return JSON.stringify(property.default || ""); case 'enum': if (property.default === undefined) { From aa438026cf4eca0061ca5da67df3d4a05fd94581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Mon, 14 Oct 2019 15:15:54 +0200 Subject: [PATCH 2/3] [core] map image type to string until we have a dedicated implementation --- expression-test/expression_test_parser.cpp | 3 +- platform/node/test/ignores.json | 4 ++ src/mbgl/style/expression/assertion.cpp | 6 +- src/mbgl/style/expression/parsing_context.cpp | 66 ++++++++++--------- src/mbgl/text/shaping.cpp | 2 +- 5 files changed, 44 insertions(+), 37 deletions(-) diff --git a/expression-test/expression_test_parser.cpp b/expression-test/expression_test_parser.cpp index 546a96b3e0f..3b40eeb3b0e 100644 --- a/expression-test/expression_test_parser.cpp +++ b/expression-test/expression_test_parser.cpp @@ -104,7 +104,8 @@ optional toValue(const JSValue& jsvalue) { style::expression::type::Type stringToType(const std::string& type) { using namespace style::expression; - if (type == "string"s || type == "number-format"s) { + if (type == "string"s || type == "number-format"s || + type == "image"s) { // TODO: replace once we implement image expressions return type::String; } else if (type == "number"s) { return type::Number; diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index 8da2905dbb0..289f38be388 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -16,6 +16,10 @@ "expression-tests/legacy/interval/composite": "https://github.com/mapbox/mapbox-gl-native/issues/12747", "expression-tests/legacy/interval/composite-default": "https://github.com/mapbox/mapbox-gl-native/issues/12747", "expression-tests/legacy/interval/tokens-zoom": "https://github.com/mapbox/mapbox-gl-native/issues/12747", + "expression-tests/image/basic": "https://github.com/mapbox/mapbox-gl-native/issues/15800", + "expression-tests/image/compound": "https://github.com/mapbox/mapbox-gl-native/issues/15800", + "expression-tests/image/coalesce": "https://github.com/mapbox/mapbox-gl-native/issues/15800", + "expression-tests/image/implicit-assert": "https://github.com/mapbox/mapbox-gl-native/issues/15800", "query-tests/geometry/multilinestring": "needs investigation", "query-tests/geometry/multipolygon": "needs investigation", "query-tests/geometry/polygon": "needs investigation", diff --git a/src/mbgl/style/expression/assertion.cpp b/src/mbgl/style/expression/assertion.cpp index 8e5a8b555d9..17f8925511d 100644 --- a/src/mbgl/style/expression/assertion.cpp +++ b/src/mbgl/style/expression/assertion.cpp @@ -16,12 +16,12 @@ Assertion::Assertion(type::Type type_, std::vector> } ParseResult Assertion::parse(const Convertible& value, ParsingContext& ctx) { - static std::unordered_map types { + static std::unordered_map types{ {"string", type::String}, + {"image", type::String}, // TODO: replace once we implement image expressions {"number", type::Number}, {"boolean", type::Boolean}, - {"object", type::Object} - }; + {"object", type::Object}}; std::size_t length = arrayLength(value); diff --git a/src/mbgl/style/expression/parsing_context.cpp b/src/mbgl/style/expression/parsing_context.cpp index 6ce3a9bfaa6..699190608bd 100644 --- a/src/mbgl/style/expression/parsing_context.cpp +++ b/src/mbgl/style/expression/parsing_context.cpp @@ -100,38 +100,40 @@ ParseResult ParsingContext::parse(const Convertible& value, std::size_t index_, } using ParseFunction = ParseResult (*)(const conversion::Convertible&, ParsingContext&); -MAPBOX_ETERNAL_CONSTEXPR const auto expressionRegistry = mapbox::eternal::hash_map({ - {"==", parseComparison}, - {"!=", parseComparison}, - {">", parseComparison}, - {"<", parseComparison}, - {">=", parseComparison}, - {"<=", parseComparison}, - {"all", All::parse}, - {"any", Any::parse}, - {"array", Assertion::parse}, - {"at", At::parse}, - {"boolean", Assertion::parse}, - {"case", Case::parse}, - {"coalesce", Coalesce::parse}, - {"collator", CollatorExpression::parse}, - {"format", FormatExpression::parse}, - {"interpolate", parseInterpolate}, - {"length", Length::parse}, - {"let", Let::parse}, - {"literal", Literal::parse}, - {"match", parseMatch}, - {"number", Assertion::parse}, - {"number-format", NumberFormat::parse}, - {"object", Assertion::parse}, - {"step", Step::parse}, - {"string", Assertion::parse}, - {"to-boolean", Coercion::parse}, - {"to-color", Coercion::parse}, - {"to-number", Coercion::parse}, - {"to-string", Coercion::parse}, - {"var", Var::parse}, -}); +MAPBOX_ETERNAL_CONSTEXPR const auto expressionRegistry = + mapbox::eternal::hash_map({ + {"==", parseComparison}, + {"!=", parseComparison}, + {">", parseComparison}, + {"<", parseComparison}, + {">=", parseComparison}, + {"<=", parseComparison}, + {"all", All::parse}, + {"any", Any::parse}, + {"array", Assertion::parse}, + {"at", At::parse}, + {"boolean", Assertion::parse}, + {"case", Case::parse}, + {"coalesce", Coalesce::parse}, + {"collator", CollatorExpression::parse}, + {"format", FormatExpression::parse}, + {"image", Assertion::parse}, // TODO: replace once we implement image expressions + {"interpolate", parseInterpolate}, + {"length", Length::parse}, + {"let", Let::parse}, + {"literal", Literal::parse}, + {"match", parseMatch}, + {"number", Assertion::parse}, + {"number-format", NumberFormat::parse}, + {"object", Assertion::parse}, + {"step", Step::parse}, + {"string", Assertion::parse}, + {"to-boolean", Coercion::parse}, + {"to-color", Coercion::parse}, + {"to-number", Coercion::parse}, + {"to-string", Coercion::parse}, + {"var", Var::parse}, + }); bool isExpression(const std::string& name) { return expressionRegistry.contains(name.c_str()); diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp index d6d9a3d34e0..4ae9d0cf205 100644 --- a/src/mbgl/text/shaping.cpp +++ b/src/mbgl/text/shaping.cpp @@ -32,7 +32,7 @@ AnchorAlignment AnchorAlignment::getAnchorAlignment(style::SymbolAnchorType anch result.horizontalAlign = 0.0f; break; default: - break; + break; } switch (anchor) { From afedfcc79be6b340e4cc889c090f6bffda83d1dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Mon, 14 Oct 2019 15:16:14 +0200 Subject: [PATCH 3/3] [core] fix icon-text-fit This fixes rendering by account for the 1px texture padding around icons that were stretched with icon-text-fit. We've added the 1px padding before, but didn't scale it accordingly when we are resizing the icon when it is stretched to fit the text. Adjusts the code to match the logic in GL JS. --- mapbox-gl-js | 2 +- platform/node/test/ignores.json | 3 + src/mbgl/layout/symbol_layout.cpp | 19 +- src/mbgl/text/quads.cpp | 28 ++- src/mbgl/text/shaping.cpp | 62 +++--- src/mbgl/text/shaping.hpp | 8 +- test/text/quads.test.cpp | 329 +++++++++++++----------------- 7 files changed, 215 insertions(+), 236 deletions(-) diff --git a/mapbox-gl-js b/mapbox-gl-js index 7ea73ed381a..2dbbf634906 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit 7ea73ed381a81c3ff7e48b523b25d50793baf1f5 +Subproject commit 2dbbf634906dc1b02b48cb740dadb6de16348475 diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index 289f38be388..51decb351c0 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -76,6 +76,9 @@ "render-tests/fill-sort-key/literal": "https://github.com/mapbox/mapbox-gl-native/issues/15008", "render-tests/line-sort-key/literal": "https://github.com/mapbox/mapbox-gl-native/issues/15008", "render-tests/regressions/mapbox-gl-js#8817": "skip - https://github.com/mapbox/mapbox-gl-native/issues/15737", + "render-tests/text-max-width/zero-width-point-placement": "https://github.com/mapbox/mapbox-gl-native/issues/15648", + "render-tests/icon-image/image-expression": "https://github.com/mapbox/mapbox-gl-native/issues/15800", + "render-tests/icon-text-fit/text-variable-anchor-overlap": "https://github.com/mapbox/mapbox-gl-native/issues/15809", "query-tests/fill-extrusion/base-in": "https://github.com/mapbox/mapbox-gl-native/issues/13139", "query-tests/fill-extrusion/box-in": "https://github.com/mapbox/mapbox-gl-native/issues/13139", "query-tests/fill-extrusion/side-in": "https://github.com/mapbox/mapbox-gl-native/issues/13139", diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 81d1d9a5b67..d0227c36c56 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -121,7 +121,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, allowVerticalPlacement = allowVerticalPlacement || placementMode == style::TextWritingModeType::Vertical; return !seen.insert(placementMode).second; }); - modes.erase(end, modes.end()); + modes.erase(end, modes.end()); placementModes = std::move(modes); } @@ -525,21 +525,22 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex, const float textRepeatDistance = symbolSpacing / 2; const auto evaluatedLayoutProperties = layout->evaluate(zoom, feature); IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketLeaderID, symbolInstances.size()); - const bool hasIconTextFit = evaluatedLayoutProperties.get() != IconTextFitType::None; + const auto iconTextFit = evaluatedLayoutProperties.get(); // Adjust shaped icon size when icon-text-fit is used. optional verticallyShapedIcon; - if (shapedIcon && hasIconTextFit) { + if (shapedIcon && iconTextFit != IconTextFitType::None) { // Create vertically shaped icon for vertical writing mode if needed. if (allowVerticalPlacement && shapedTextOrientations.vertical) { verticallyShapedIcon = shapedIcon; - verticallyShapedIcon->fitIconToText(evaluatedLayoutProperties, - shapedTextOrientations.vertical, - layoutTextSize); + verticallyShapedIcon->fitIconToText( + shapedTextOrientations.vertical, iconTextFit, layout->get(), iconOffset, fontScale); + } + const auto shapedText = getDefaultHorizontalShaping(shapedTextOrientations); + if (shapedText) { + shapedIcon->fitIconToText( + shapedText, iconTextFit, layout->get(), iconOffset, fontScale); } - shapedIcon->fitIconToText(evaluatedLayoutProperties, - getDefaultHorizontalShaping(shapedTextOrientations), - layoutTextSize); } auto addSymbolInstance = [&] (Anchor& anchor, std::shared_ptr sharedData) { diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp index 281c5d99de8..a94bfee336c 100644 --- a/src/mbgl/text/quads.cpp +++ b/src/mbgl/text/quads.cpp @@ -20,12 +20,28 @@ SymbolQuad getIconQuad(const PositionedIcon& shapedIcon, // If you have a 10px icon that isn't perfectly aligned to the pixel grid it will cover 11 actual // pixels. The quad needs to be padded to account for this, otherwise they'll look slightly clipped // on one edge in some cases. - const float border = 1.0; - - float top = shapedIcon.top() - border / image.pixelRatio; - float left = shapedIcon.left() - border / image.pixelRatio; - float bottom = shapedIcon.bottom() + border / image.pixelRatio; - float right = shapedIcon.right() + border / image.pixelRatio; + constexpr const float border = 1.0f; + + // Expand the box to respect the 1 pixel border in the atlas image. We're using `image.paddedRect - border` + // instead of image.displaySize because we only pad with one pixel for retina images as well, and the + // displaySize uses the logical dimensions, not the physical pixel dimensions. + // Unlike the JavaScript version, we're _not_ including the padding in the texture rect, so the + // logic "dimension * padded / non-padded - dimension" is swapped. + const float iconWidth = shapedIcon.right() - shapedIcon.left(); + const float expandX = (iconWidth * (static_cast(image.textureRect.w) + 2.0f * border) / + static_cast(image.textureRect.w) - + iconWidth) / + 2.0f; + const float left = shapedIcon.left() - expandX; + const float right = shapedIcon.right() + expandX; + + const float iconHeight = shapedIcon.bottom() - shapedIcon.top(); + const float expandY = (iconHeight * (static_cast(image.textureRect.h) + 2.0f * border) / + static_cast(image.textureRect.h) - + iconHeight) / + 2.0f; + const float top = shapedIcon.top() - expandY; + const float bottom = shapedIcon.bottom() + expandY; Point tl{left, top}; Point tr{right, top}; diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp index 4ae9d0cf205..8eb885af5d5 100644 --- a/src/mbgl/text/shaping.cpp +++ b/src/mbgl/text/shaping.cpp @@ -83,33 +83,41 @@ PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, return PositionedIcon { image, top, bottom, left, right, iconRotation }; } -void PositionedIcon::fitIconToText(const style::SymbolLayoutProperties::Evaluated& layout, - const Shaping& shapedText, - float layoutTextSize) { - using namespace style; - assert(layout.get() != IconTextFitType::None); - if (shapedText) { - auto iconWidth = _right - _left; - auto iconHeight = _bottom - _top; - auto size = layoutTextSize / 24.0f; - auto textLeft = shapedText.left * size; - auto textRight = shapedText.right * size; - auto textTop = shapedText.top * size; - auto textBottom = shapedText.bottom * size; - auto textWidth = textRight - textLeft; - auto textHeight = textBottom - textTop; - auto padT = layout.get()[0]; - auto padR = layout.get()[1]; - auto padB = layout.get()[2]; - auto padL = layout.get()[3]; - auto offsetY = layout.get() == IconTextFitType::Width ? (textHeight - iconHeight) * 0.5 : 0; - auto offsetX = layout.get() == IconTextFitType::Height ? (textWidth - iconWidth) * 0.5 : 0; - auto width = layout.get() == IconTextFitType::Width || layout.get() == IconTextFitType::Both ? textWidth : iconWidth; - auto height = layout.get() == IconTextFitType::Height || layout.get() == IconTextFitType::Both ? textHeight : iconHeight; - _left = textLeft + offsetX - padL; - _top = textTop + offsetY - padT; - _right = textLeft + offsetX + padR + width; - _bottom = textTop + offsetY + padB + height; +void PositionedIcon::fitIconToText(const Shaping& shapedText, + const style::IconTextFitType textFit, + const std::array& padding, + const std::array& iconOffset, + const float fontScale) { + assert(textFit != style::IconTextFitType::None); + assert(shapedText); + + // We don't respect the icon-anchor, because icon-text-fit is set. Instead, + // the icon will be centered on the text, then stretched in the given + // dimensions. + + const float textLeft = shapedText.left * fontScale; + const float textRight = shapedText.right * fontScale; + + if (textFit == style::IconTextFitType::Width || textFit == style::IconTextFitType::Both) { + // Stretched horizontally to the text width + _left = iconOffset[0] + textLeft - padding[3]; + _right = iconOffset[0] + textRight + padding[1]; + } else { + // Centered on the text + _left = iconOffset[0] + (textLeft + textRight - image().displaySize()[0]) / 2.0f; + _right = _left + image().displaySize()[0]; + } + + const float textTop = shapedText.top * fontScale; + const float textBottom = shapedText.bottom * fontScale; + if (textFit == style::IconTextFitType::Height || textFit == style::IconTextFitType::Both) { + // Stretched vertically to the text height + _top = iconOffset[1] + textTop - padding[0]; + _bottom = iconOffset[1] + textBottom + padding[2]; + } else { + // Centered on the text + _top = iconOffset[1] + (textTop + textBottom - image().displaySize()[1]) / 2.0f; + _bottom = _top + image().displaySize()[1]; } } diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp index 28730e9db9b..6ed1b5cb0e6 100644 --- a/src/mbgl/text/shaping.hpp +++ b/src/mbgl/text/shaping.hpp @@ -45,9 +45,11 @@ class PositionedIcon { // Updates shaped icon's bounds based on shaped text's bounds and provided // layout properties. - void fitIconToText(const style::SymbolLayoutProperties::Evaluated& layout, - const Shaping& shapedText, - float layoutTextSize); + void fitIconToText(const Shaping& shapedText, + const style::IconTextFitType textFit, + const std::array& padding, + const std::array& iconOffset, + const float fontScale); const ImagePosition& image() const { return _image; } float top() const { return _top; } diff --git a/test/text/quads.test.cpp b/test/text/quads.test.cpp index 7aaeb4870df..b04617a40b5 100644 --- a/test/text/quads.test.cpp +++ b/test/text/quads.test.cpp @@ -36,10 +36,8 @@ TEST(getIconQuads, normal) { TEST(getIconQuads, style) { Anchor anchor(0.0, 0.0, 0.0, 0.5f, 0); - ImagePosition image = { - mapbox::Bin(-1, 20, 20, 0, 0, 0, 0), - style::Image::Impl("test", PremultipliedImage({1,1}), 1.0) - }; + const ImagePosition image = {mapbox::Bin(-1, 20, 20, 0, 0, 0, 0), + style::Image::Impl("test", PremultipliedImage({1, 1}), 1.0)}; GeometryCoordinates line; Shaping shapedText; @@ -51,235 +49,186 @@ TEST(getIconQuads, style) { // none { - auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); + auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center, 0); + + EXPECT_FLOAT_EQ(-18.5f, shapedIcon.top()); + EXPECT_FLOAT_EQ(-0.5f, shapedIcon.right()); + EXPECT_FLOAT_EQ(-0.5f, shapedIcon.bottom()); + EXPECT_FLOAT_EQ(-18.5f, shapedIcon.left()); + SymbolLayoutProperties::Evaluated layout; - SymbolQuad quad = - getIconQuad(shapedIcon, WritingModeType::Horizontal); - - EXPECT_EQ(quad.tl.x, -19.5); - EXPECT_EQ(quad.tl.y, -19.5); - EXPECT_EQ(quad.tr.x, 0.5); - EXPECT_EQ(quad.tr.y, -19.5); - EXPECT_EQ(quad.bl.x, -19.5); - EXPECT_EQ(quad.bl.y, 0.5); - EXPECT_EQ(quad.br.x, 0.5); - EXPECT_EQ(quad.br.y, 0.5); + SymbolQuad quad = getIconQuad(shapedIcon, WritingModeType::Horizontal); + + EXPECT_FLOAT_EQ(quad.tl.x, -19.5); + EXPECT_FLOAT_EQ(quad.tl.y, -19.5); + EXPECT_FLOAT_EQ(quad.tr.x, 0.5); + EXPECT_FLOAT_EQ(quad.tr.y, -19.5); + EXPECT_FLOAT_EQ(quad.bl.x, -19.5); + EXPECT_FLOAT_EQ(quad.bl.y, 0.5); + EXPECT_FLOAT_EQ(quad.br.x, 0.5); + EXPECT_FLOAT_EQ(quad.br.y, 0.5); } // width { - SymbolLayoutProperties::Evaluated layout; - layout.get() = 24.0f; - layout.get() = IconTextFitType::Width; - auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); - shapedIcon.fitIconToText(layout, shapedText, 24.0f); - SymbolQuad quad = - getIconQuad(shapedIcon, WritingModeType::Horizontal); - - EXPECT_EQ(quad.tl.x, -61); - EXPECT_EQ(quad.tl.y, 0); - EXPECT_EQ(quad.tr.x, 21); - EXPECT_EQ(quad.tr.y, 0); - EXPECT_EQ(quad.bl.x, -61); - EXPECT_EQ(quad.bl.y, 20); - EXPECT_EQ(quad.br.x, 21); - EXPECT_EQ(quad.br.y, 20); + auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(shapedText, IconTextFitType::Width, {{0, 0, 0, 0}}, {{0, 0}}, 24.0f / 24.0f); + SymbolQuad quad = getIconQuad(shapedIcon, WritingModeType::Horizontal); + + EXPECT_FLOAT_EQ(quad.tl.x, -64.4444427); + EXPECT_FLOAT_EQ(quad.tl.y, 0); + EXPECT_FLOAT_EQ(quad.tr.x, 24.4444427); + EXPECT_FLOAT_EQ(quad.tr.y, 0); + EXPECT_FLOAT_EQ(quad.bl.x, -64.4444427); + EXPECT_FLOAT_EQ(quad.bl.y, 20); + EXPECT_FLOAT_EQ(quad.br.x, 24.4444427); + EXPECT_FLOAT_EQ(quad.br.y, 20); } // width x textSize { - SymbolLayoutProperties::Evaluated layout; - layout.get() = 12.0f; - layout.get() = IconTextFitType::Width; - auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); - shapedIcon.fitIconToText(layout, shapedText, 12.0f); - SymbolQuad quad = - getIconQuad(shapedIcon, WritingModeType::Horizontal); - - EXPECT_EQ(quad.tl.x, -31); - EXPECT_EQ(quad.tl.y, -5); - EXPECT_EQ(quad.tr.x, 11); - EXPECT_EQ(quad.tr.y, -5); - EXPECT_EQ(quad.bl.x, -31); - EXPECT_EQ(quad.bl.y, 15); - EXPECT_EQ(quad.br.x, 11); - EXPECT_EQ(quad.br.y, 15); + auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(shapedText, IconTextFitType::Width, {{0, 0, 0, 0}}, {{0, 0}}, 12.0f / 24.0f); + SymbolQuad quad = getIconQuad(shapedIcon, WritingModeType::Horizontal); + + EXPECT_FLOAT_EQ(quad.tl.x, -32.2222214); + EXPECT_FLOAT_EQ(quad.tl.y, -5); + EXPECT_FLOAT_EQ(quad.tr.x, 12.2222214); + EXPECT_FLOAT_EQ(quad.tr.y, -5); + EXPECT_FLOAT_EQ(quad.bl.x, -32.2222214); + EXPECT_FLOAT_EQ(quad.bl.y, 15); + EXPECT_FLOAT_EQ(quad.br.x, 12.2222214); + EXPECT_FLOAT_EQ(quad.br.y, 15); } // width x textSize + padding { - SymbolLayoutProperties::Evaluated layout; - layout.get() = 12.0f; - layout.get() = IconTextFitType::Width; - layout.get()[0] = 5.0f; - layout.get()[1] = 10.0f; - layout.get()[2] = 5.0f; - layout.get()[3] = 10.0f; - auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); - shapedIcon.fitIconToText(layout, shapedText, 12.0f); - SymbolQuad quad = - getIconQuad(shapedIcon, WritingModeType::Horizontal); - - EXPECT_EQ(quad.tl.x, -41); - EXPECT_EQ(quad.tl.y, -10); - EXPECT_EQ(quad.tr.x, 21); - EXPECT_EQ(quad.tr.y, -10); - EXPECT_EQ(quad.bl.x, -41); - EXPECT_EQ(quad.bl.y, 20); - EXPECT_EQ(quad.br.x, 21); - EXPECT_EQ(quad.br.y, 20); + auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(shapedText, IconTextFitType::Width, {{5, 10, 5, 10}}, {{0, 0}}, 12.0f / 24.0f); + SymbolQuad quad = getIconQuad(shapedIcon, WritingModeType::Horizontal); + + EXPECT_FLOAT_EQ(quad.tl.x, -43.3333321); + EXPECT_FLOAT_EQ(quad.tl.y, -5); + EXPECT_FLOAT_EQ(quad.tr.x, 23.3333321); + EXPECT_FLOAT_EQ(quad.tr.y, -5); + EXPECT_FLOAT_EQ(quad.bl.x, -43.3333321); + EXPECT_FLOAT_EQ(quad.bl.y, 15); + EXPECT_FLOAT_EQ(quad.br.x, 23.3333321); + EXPECT_FLOAT_EQ(quad.br.y, 15); } // height { - SymbolLayoutProperties::Evaluated layout; - layout.get() = 24.0f; - layout.get() = IconTextFitType::Height; - auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); - shapedIcon.fitIconToText(layout, shapedText, 24.0f); - SymbolQuad quad = - getIconQuad(shapedIcon, WritingModeType::Horizontal); - - EXPECT_EQ(quad.tl.x, -30); - EXPECT_EQ(quad.tl.y, -11); - EXPECT_EQ(quad.tr.x, -10); - EXPECT_EQ(quad.tr.y, -11); - EXPECT_EQ(quad.bl.x, -30); - EXPECT_EQ(quad.bl.y, 31); - EXPECT_EQ(quad.br.x, -10); - EXPECT_EQ(quad.br.y, 31); + auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(shapedText, IconTextFitType::Height, {{0, 0, 0, 0}}, {{0, 0}}, 24.0f / 24.0f); + SymbolQuad quad = getIconQuad(shapedIcon, WritingModeType::Horizontal); + + EXPECT_FLOAT_EQ(quad.tl.x, -30); + EXPECT_FLOAT_EQ(quad.tl.y, -12.2222214); + EXPECT_FLOAT_EQ(quad.tr.x, -10); + EXPECT_FLOAT_EQ(quad.tr.y, -12.2222214); + EXPECT_FLOAT_EQ(quad.bl.x, -30); + EXPECT_FLOAT_EQ(quad.bl.y, 32.2222214); + EXPECT_FLOAT_EQ(quad.br.x, -10); + EXPECT_FLOAT_EQ(quad.br.y, 32.2222214); } // height x textSize { SymbolLayoutProperties::Evaluated layout; - layout.get() = 12.0f; - layout.get() = IconTextFitType::Height; - auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); - shapedIcon.fitIconToText(layout, shapedText, 12.0f); - SymbolQuad quad = - getIconQuad(shapedIcon, WritingModeType::Horizontal); - - EXPECT_EQ(quad.tl.x, -20); - EXPECT_EQ(quad.tl.y, -6); - EXPECT_EQ(quad.tr.x, 0); - EXPECT_EQ(quad.tr.y, -6); - EXPECT_EQ(quad.bl.x, -20); - EXPECT_EQ(quad.bl.y, 16); - EXPECT_EQ(quad.br.x, 0); - EXPECT_EQ(quad.br.y, 16); + auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(shapedText, IconTextFitType::Height, {{0, 0, 0, 0}}, {{0, 0}}, 12.0f / 24.0f); + SymbolQuad quad = getIconQuad(shapedIcon, WritingModeType::Horizontal); + + EXPECT_FLOAT_EQ(quad.tl.x, -20); + EXPECT_FLOAT_EQ(quad.tl.y, -6.11111069); + EXPECT_FLOAT_EQ(quad.tr.x, 0); + EXPECT_FLOAT_EQ(quad.tr.y, -6.11111069); + EXPECT_FLOAT_EQ(quad.bl.x, -20); + EXPECT_FLOAT_EQ(quad.bl.y, 16.1111107); + EXPECT_FLOAT_EQ(quad.br.x, 0); + EXPECT_FLOAT_EQ(quad.br.y, 16.1111107); } // height x textSize + padding { - SymbolLayoutProperties::Evaluated layout; - layout.get() = 12.0f; - layout.get() = IconTextFitType::Height; - layout.get()[0] = 5.0f; - layout.get()[1] = 10.0f; - layout.get()[2] = 5.0f; - layout.get()[3] = 10.0f; - auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); - shapedIcon.fitIconToText(layout, shapedText, 12.0f); - SymbolQuad quad = - getIconQuad(shapedIcon, WritingModeType::Horizontal); - - EXPECT_EQ(quad.tl.x, -30); - EXPECT_EQ(quad.tl.y, -11); - EXPECT_EQ(quad.tr.x, 10); - EXPECT_EQ(quad.tr.y, -11); - EXPECT_EQ(quad.bl.x, -30); - EXPECT_EQ(quad.bl.y, 21); - EXPECT_EQ(quad.br.x, 10); - EXPECT_EQ(quad.br.y, 21); + auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(shapedText, IconTextFitType::Height, {{5, 10, 5, 20}}, {{0, 0}}, 12.0f / 24.0f); + SymbolQuad quad = getIconQuad(shapedIcon, WritingModeType::Horizontal); + + EXPECT_FLOAT_EQ(quad.tl.x, -20); + EXPECT_FLOAT_EQ(quad.tl.y, -11.666666); + EXPECT_FLOAT_EQ(quad.tr.x, 0); + EXPECT_FLOAT_EQ(quad.tr.y, -11.666666); + EXPECT_FLOAT_EQ(quad.bl.x, -20); + EXPECT_FLOAT_EQ(quad.bl.y, 21.666666); + EXPECT_FLOAT_EQ(quad.br.x, 0); + EXPECT_FLOAT_EQ(quad.br.y, 21.666666); } // both { - SymbolLayoutProperties::Evaluated layout; - layout.get() = 24.0f; - layout.get() = IconTextFitType::Both; - auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); - shapedIcon.fitIconToText(layout, shapedText, 24.0f); - SymbolQuad quad = - getIconQuad(shapedIcon, WritingModeType::Horizontal); - - EXPECT_EQ(quad.tl.x, -61); - EXPECT_EQ(quad.tl.y, -11); - EXPECT_EQ(quad.tr.x, 21); - EXPECT_EQ(quad.tr.y, -11); - EXPECT_EQ(quad.bl.x, -61); - EXPECT_EQ(quad.bl.y, 31); - EXPECT_EQ(quad.br.x, 21); - EXPECT_EQ(quad.br.y, 31); + auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(shapedText, IconTextFitType::Both, {{0, 0, 0, 0}}, {{0, 0}}, 24.0f / 24.0f); + SymbolQuad quad = getIconQuad(shapedIcon, WritingModeType::Horizontal); + + EXPECT_FLOAT_EQ(quad.tl.x, -64.4444427); + EXPECT_FLOAT_EQ(quad.tl.y, -12.2222214); + EXPECT_FLOAT_EQ(quad.tr.x, 24.4444427); + EXPECT_FLOAT_EQ(quad.tr.y, -12.2222214); + EXPECT_FLOAT_EQ(quad.bl.x, -64.4444427); + EXPECT_FLOAT_EQ(quad.bl.y, 32.2222214); + EXPECT_FLOAT_EQ(quad.br.x, 24.4444427); + EXPECT_FLOAT_EQ(quad.br.y, 32.2222214); } // both x textSize { - SymbolLayoutProperties::Evaluated layout; - layout.get() = 12.0f; - layout.get() = IconTextFitType::Both; - auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); - shapedIcon.fitIconToText(layout, shapedText, 12.0f); - SymbolQuad quad = - getIconQuad(shapedIcon, WritingModeType::Horizontal); - - EXPECT_EQ(quad.tl.x, -31); - EXPECT_EQ(quad.tl.y, -6); - EXPECT_EQ(quad.tr.x, 11); - EXPECT_EQ(quad.tr.y, -6); - EXPECT_EQ(quad.bl.x, -31); - EXPECT_EQ(quad.bl.y, 16); - EXPECT_EQ(quad.br.x, 11); - EXPECT_EQ(quad.br.y, 16); + auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(shapedText, IconTextFitType::Both, {{0, 0, 0, 0}}, {{0, 0}}, 12.0f / 24.0f); + SymbolQuad quad = getIconQuad(shapedIcon, WritingModeType::Horizontal); + + EXPECT_FLOAT_EQ(quad.tl.x, -32.2222214); + EXPECT_FLOAT_EQ(quad.tl.y, -6.11111069); + EXPECT_FLOAT_EQ(quad.tr.x, 12.2222214); + EXPECT_FLOAT_EQ(quad.tr.y, -6.11111069); + EXPECT_FLOAT_EQ(quad.bl.x, -32.2222214); + EXPECT_FLOAT_EQ(quad.bl.y, 16.1111107); + EXPECT_FLOAT_EQ(quad.br.x, 12.2222214); + EXPECT_FLOAT_EQ(quad.br.y, 16.1111107); } // both x textSize + padding { - SymbolLayoutProperties::Evaluated layout; - layout.get() = 12.0f; - layout.get() = IconTextFitType::Both; - layout.get()[0] = 5.0f; - layout.get()[1] = 10.0f; - layout.get()[2] = 5.0f; - layout.get()[3] = 10.0f; - auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); - shapedIcon.fitIconToText(layout, shapedText, 12.0f); - SymbolQuad quad = - getIconQuad(shapedIcon, WritingModeType::Horizontal); - - EXPECT_EQ(quad.tl.x, -41); - EXPECT_EQ(quad.tl.y, -11); - EXPECT_EQ(quad.tr.x, 21); - EXPECT_EQ(quad.tr.y, -11); - EXPECT_EQ(quad.bl.x, -41); - EXPECT_EQ(quad.bl.y, 21); - EXPECT_EQ(quad.br.x, 21); - EXPECT_EQ(quad.br.y, 21); + auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(shapedText, IconTextFitType::Both, {{5, 10, 5, 10}}, {{0, 0}}, 12.0f / 24.0f); + SymbolQuad quad = getIconQuad(shapedIcon, WritingModeType::Horizontal); + + EXPECT_FLOAT_EQ(quad.tl.x, -43.3333321); + EXPECT_FLOAT_EQ(quad.tl.y, -11.666666); + EXPECT_FLOAT_EQ(quad.tr.x, 23.3333321); + EXPECT_FLOAT_EQ(quad.tr.y, -11.666666); + EXPECT_FLOAT_EQ(quad.bl.x, -43.3333321); + EXPECT_FLOAT_EQ(quad.bl.y, 21.666666); + EXPECT_FLOAT_EQ(quad.br.x, 23.3333321); + EXPECT_FLOAT_EQ(quad.br.y, 21.666666); } // both x textSize + padding t/r/b/l { SymbolLayoutProperties::Evaluated layout; layout.get() = 12.0f; - layout.get() = IconTextFitType::Both; - layout.get()[0] = 0.0f; - layout.get()[1] = 5.0f; - layout.get()[2] = 10.0f; - layout.get()[3] = 15.0f; - auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0); - shapedIcon.fitIconToText(layout, shapedText, 12.0f); - SymbolQuad quad = - getIconQuad(shapedIcon, WritingModeType::Horizontal); - - EXPECT_EQ(quad.tl.x, -46); - EXPECT_EQ(quad.tl.y, -6); - EXPECT_EQ(quad.tr.x, 16); - EXPECT_EQ(quad.tr.y, -6); - EXPECT_EQ(quad.bl.x, -46); - EXPECT_EQ(quad.bl.y, 26); - EXPECT_EQ(quad.br.x, 16); - EXPECT_EQ(quad.br.y, 26); + auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center, 0); + shapedIcon.fitIconToText(shapedText, IconTextFitType::Both, {{0, 5, 10, 15}}, {{0, 0}}, 12.0f / 24.0f); + SymbolQuad quad = getIconQuad(shapedIcon, WritingModeType::Horizontal); + + EXPECT_FLOAT_EQ(quad.tl.x, -48.3333321); + EXPECT_FLOAT_EQ(quad.tl.y, -6.66666603); + EXPECT_FLOAT_EQ(quad.tr.x, 18.3333321); + EXPECT_FLOAT_EQ(quad.tr.y, -6.66666603); + EXPECT_FLOAT_EQ(quad.bl.x, -48.3333321); + EXPECT_FLOAT_EQ(quad.bl.y, 26.666666); + EXPECT_FLOAT_EQ(quad.br.x, 18.3333321); + EXPECT_FLOAT_EQ(quad.br.y, 26.666666); } } -