summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2019-12-10 17:38:21 +0100
committerAlexander Shalamov <alexander.shalamov@mapbox.com>2020-01-15 15:02:11 +0200
commit5a1e4c6b816ef495e53e54f563ecfcef3c5839aa (patch)
treef5654a0a5f9c517170b231dfb23ec4d430479761
parent027e803b722301a03e9ef53b584c2795ff47e7bd (diff)
downloadqtlocation-mapboxgl-5a1e4c6b816ef495e53e54f563ecfcef3c5839aa.tar.gz
[core] implement stretchable icons for icon-text-fit
-rw-r--r--include/mbgl/style/image.hpp2
-rw-r--r--metrics/ignores/platform-all.json10
-rw-r--r--src/mbgl/layout/symbol_instance.cpp5
-rw-r--r--src/mbgl/layout/symbol_instance.hpp1
-rw-r--r--src/mbgl/layout/symbol_layout.cpp4
-rw-r--r--src/mbgl/renderer/image_atlas.hpp2
-rw-r--r--src/mbgl/style/image.cpp2
-rw-r--r--src/mbgl/style/image_impl.cpp6
-rw-r--r--src/mbgl/style/image_impl.hpp2
-rw-r--r--src/mbgl/text/collision_feature.cpp19
-rw-r--r--src/mbgl/text/collision_feature.hpp23
-rw-r--r--src/mbgl/text/quads.cpp224
-rw-r--r--src/mbgl/text/quads.hpp5
-rw-r--r--src/mbgl/text/shaping.cpp12
-rw-r--r--src/mbgl/text/shaping.hpp26
-rw-r--r--test/text/cross_tile_symbol_index.test.cpp1
-rw-r--r--test/text/quads.test.cpp40
17 files changed, 279 insertions, 105 deletions
diff --git a/include/mbgl/style/image.hpp b/include/mbgl/style/image.hpp
index bbea081c71..0d9c863e85 100644
--- a/include/mbgl/style/image.hpp
+++ b/include/mbgl/style/image.hpp
@@ -65,7 +65,7 @@ public:
const ImageStretches& getStretchY() const;
// The space where text can be fit into this image.
- const ImageContent& getContent() const;
+ const optional<ImageContent>& getContent() const;
class Impl;
Immutable<Impl> baseImpl;
diff --git a/metrics/ignores/platform-all.json b/metrics/ignores/platform-all.json
index e1b5c57fc0..b92310a756 100644
--- a/metrics/ignores/platform-all.json
+++ b/metrics/ignores/platform-all.json
@@ -82,16 +82,6 @@
"render-tests/fill-extrusion-pattern/tile-buffer": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
"render-tests/fill-pattern/update-feature-state": "https://github.com/mapbox/mapbox-gl-native/issues/15895",
"render-tests/geojson/inline-linestring-fill": "current behavior is arbitrary",
- "render-tests/icon-text-fit/stretch-fifteen-part": "https://github.com/mapbox/mapbox-gl-native/issues/16017",
- "render-tests/icon-text-fit/stretch-nine-part": "https://github.com/mapbox/mapbox-gl-native/issues/16017",
- "render-tests/icon-text-fit/stretch-nine-part-@2x": "https://github.com/mapbox/mapbox-gl-native/issues/16017",
- "render-tests/icon-text-fit/stretch-nine-part-content": "https://github.com/mapbox/mapbox-gl-native/issues/16017",
- "render-tests/icon-text-fit/stretch-nine-part-content-collision": "https://github.com/mapbox/mapbox-gl-native/issues/16017",
- "render-tests/icon-text-fit/stretch-nine-part-just-height": "https://github.com/mapbox/mapbox-gl-native/issues/16017",
- "render-tests/icon-text-fit/stretch-nine-part-just-width": "https://github.com/mapbox/mapbox-gl-native/issues/16017",
- "render-tests/icon-text-fit/stretch-three-part": "https://github.com/mapbox/mapbox-gl-native/issues/16017",
- "render-tests/icon-text-fit/stretch-two-part": "https://github.com/mapbox/mapbox-gl-native/issues/16017",
- "render-tests/icon-text-fit/stretch-underscale": "https://github.com/mapbox/mapbox-gl-native/issues/16017",
"render-tests/icon-text-fit/text-variable-anchor-overlap": "https://github.com/mapbox/mapbox-gl-native/issues/15809",
"render-tests/mixed-zoom/z10-z11": "https://github.com/mapbox/mapbox-gl-native/issues/10397",
"render-tests/raster-masking/overlapping-zoom": "https://github.com/mapbox/mapbox-gl-native/issues/10195",
diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp
index 01ba3e26a6..9eda80d4ea 100644
--- a/src/mbgl/layout/symbol_instance.cpp
+++ b/src/mbgl/layout/symbol_instance.cpp
@@ -28,13 +28,14 @@ SymbolInstanceSharedData::SymbolInstanceSharedData(GeometryCoordinates line_,
const ImageMap& imageMap,
float iconRotation,
SymbolContent iconType,
+ bool hasIconTextFit,
bool allowVerticalPlacement)
: line(std::move(line_)) {
// Create the quads used for rendering the icon and glyphs.
if (shapedIcon) {
- iconQuads = getIconQuads(*shapedIcon, iconRotation, iconType);
+ iconQuads = getIconQuads(*shapedIcon, iconRotation, iconType, hasIconTextFit);
if (verticallyShapedIcon) {
- verticalIconQuads = getIconQuads(*verticallyShapedIcon, iconRotation, iconType);
+ verticalIconQuads = getIconQuads(*verticallyShapedIcon, iconRotation, iconType, hasIconTextFit);
}
}
diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp
index 56dd82240e..2a25c655aa 100644
--- a/src/mbgl/layout/symbol_instance.hpp
+++ b/src/mbgl/layout/symbol_instance.hpp
@@ -34,6 +34,7 @@ struct SymbolInstanceSharedData {
const ImageMap& imageMap,
float iconRotation,
SymbolContent iconType,
+ bool hasIconTextFit,
bool allowVerticalPlacement);
bool empty() const;
GeometryCoordinates line;
diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp
index 1772d1ef91..1f447ddc9f 100644
--- a/src/mbgl/layout/symbol_layout.cpp
+++ b/src/mbgl/layout/symbol_layout.cpp
@@ -555,9 +555,10 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketLeaderID, symbolInstances.size());
const auto iconTextFit = evaluatedLayoutProperties.get<style::IconTextFit>();
+ const bool hasIconTextFit = iconTextFit != IconTextFitType::None;
// Adjust shaped icon size when icon-text-fit is used.
optional<PositionedIcon> verticallyShapedIcon;
- if (shapedIcon && iconTextFit != IconTextFitType::None) {
+ if (shapedIcon && hasIconTextFit) {
// Create vertically shaped icon for vertical writing mode if needed.
if (allowVerticalPlacement && shapedTextOrientations.vertical) {
verticallyShapedIcon = shapedIcon;
@@ -602,6 +603,7 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
imageMap,
iconRotation,
iconType,
+ hasIconTextFit,
allowVerticalPlacement);
};
diff --git a/src/mbgl/renderer/image_atlas.hpp b/src/mbgl/renderer/image_atlas.hpp
index 8be975e98c..690d6e34b8 100644
--- a/src/mbgl/renderer/image_atlas.hpp
+++ b/src/mbgl/renderer/image_atlas.hpp
@@ -26,7 +26,7 @@ public:
uint32_t version;
style::ImageStretches stretchX;
style::ImageStretches stretchY;
- style::ImageContent content;
+ optional<style::ImageContent> content;
std::array<uint16_t, 2> tl() const {
return {{static_cast<uint16_t>(paddedRect.x + padding), static_cast<uint16_t>(paddedRect.y + padding)}};
diff --git a/src/mbgl/style/image.cpp b/src/mbgl/style/image.cpp
index 269f5f9d1a..be7e52abfa 100644
--- a/src/mbgl/style/image.cpp
+++ b/src/mbgl/style/image.cpp
@@ -41,7 +41,7 @@ const ImageStretches& Image::getStretchY() const {
return baseImpl->stretchY;
}
-const ImageContent& Image::getContent() const {
+const optional<ImageContent>& Image::getContent() const {
return baseImpl->content;
}
diff --git a/src/mbgl/style/image_impl.cpp b/src/mbgl/style/image_impl.cpp
index c43bb552ee..939b1c130c 100644
--- a/src/mbgl/style/image_impl.cpp
+++ b/src/mbgl/style/image_impl.cpp
@@ -45,9 +45,7 @@ Image::Impl::Impl(std::string id_,
sdf(sdf_),
stretchX(std::move(stretchX_)),
stretchY(std::move(stretchY_)),
- content(content_
- ? std::move(*content_)
- : ImageContent{0, 0, static_cast<float>(image.size.width), static_cast<float>(image.size.height)}) {
+ content(std::move(content_)) {
if (!image.valid()) {
throw util::StyleImageException("dimensions may not be zero");
} else if (pixelRatio <= 0) {
@@ -56,7 +54,7 @@ Image::Impl::Impl(std::string id_,
throw util::StyleImageException("stretchX is out of bounds or overlapping");
} else if (!validateStretch(stretchY, image.size.height)) {
throw util::StyleImageException("stretchY is out of bounds or overlapping");
- } else if (!validateContent(content, image.size)) {
+ } else if (content && !validateContent(*content, image.size)) {
throw util::StyleImageException("content area is invalid");
}
}
diff --git a/src/mbgl/style/image_impl.hpp b/src/mbgl/style/image_impl.hpp
index 683cd3347b..76a68017e2 100644
--- a/src/mbgl/style/image_impl.hpp
+++ b/src/mbgl/style/image_impl.hpp
@@ -34,7 +34,7 @@ public:
const ImageStretches stretchY;
// The space where text can be fit into this image.
- const ImageContent content;
+ const optional<ImageContent> content;
};
} // namespace style
diff --git a/src/mbgl/text/collision_feature.cpp b/src/mbgl/text/collision_feature.cpp
index 744ba916c6..d2fdd2da59 100644
--- a/src/mbgl/text/collision_feature.cpp
+++ b/src/mbgl/text/collision_feature.cpp
@@ -10,20 +10,27 @@ CollisionFeature::CollisionFeature(const GeometryCoordinates& line,
const float bottom,
const float left,
const float right,
+ const optional<Padding>& collisionPadding,
const float boxScale,
const float padding,
const style::SymbolPlacementType placement,
IndexedSubfeature indexedFeature_,
const float overscaling,
const float rotate)
- : indexedFeature(std::move(indexedFeature_))
- , alongLine(placement != style::SymbolPlacementType::Point) {
+ : indexedFeature(std::move(indexedFeature_)), alongLine(placement != style::SymbolPlacementType::Point) {
if (top == 0 && bottom == 0 && left == 0 && right == 0) return;
- const float y1 = top * boxScale - padding;
- const float y2 = bottom * boxScale + padding;
- const float x1 = left * boxScale - padding;
- const float x2 = right * boxScale + padding;
+ float y1 = top * boxScale - padding;
+ float y2 = bottom * boxScale + padding;
+ float x1 = left * boxScale - padding;
+ float x2 = right * boxScale + padding;
+
+ if (collisionPadding) {
+ x1 -= collisionPadding->left * boxScale;
+ y1 -= collisionPadding->top * boxScale;
+ x2 += collisionPadding->right * boxScale;
+ y2 += collisionPadding->bottom * boxScale;
+ }
if (alongLine) {
float height = y2 - y1;
diff --git a/src/mbgl/text/collision_feature.hpp b/src/mbgl/text/collision_feature.hpp
index ebda4a5673..af9931a031 100644
--- a/src/mbgl/text/collision_feature.hpp
+++ b/src/mbgl/text/collision_feature.hpp
@@ -79,7 +79,19 @@ public:
const IndexedSubfeature& indexedFeature_,
const float overscaling,
const float rotate)
- : CollisionFeature(line, anchor, shapedText.top, shapedText.bottom, shapedText.left, shapedText.right, boxScale, padding, placement, indexedFeature_, overscaling, rotate) {}
+ : CollisionFeature(line,
+ anchor,
+ shapedText.top,
+ shapedText.bottom,
+ shapedText.left,
+ shapedText.right,
+ nullopt,
+ boxScale,
+ padding,
+ placement,
+ indexedFeature_,
+ overscaling,
+ rotate) {}
// for icons
// Icons collision features are always SymbolPlacementType::Point, which means the collision feature
@@ -94,15 +106,19 @@ public:
const float padding,
const IndexedSubfeature& indexedFeature_,
const float rotate)
- : CollisionFeature(line, anchor,
+ : CollisionFeature(line,
+ anchor,
(shapedIcon ? shapedIcon->top() : 0),
(shapedIcon ? shapedIcon->bottom() : 0),
(shapedIcon ? shapedIcon->left() : 0),
(shapedIcon ? shapedIcon->right() : 0),
+ (shapedIcon ? shapedIcon->collisionPadding() : optional<Padding>{nullopt}),
boxScale,
padding,
style::SymbolPlacementType::Point,
- indexedFeature_, 1, rotate) {}
+ indexedFeature_,
+ 1,
+ rotate) {}
CollisionFeature(const GeometryCoordinates& line,
const Anchor&,
@@ -110,6 +126,7 @@ public:
const float bottom,
const float left,
const float right,
+ const optional<Padding>& collisionPadding,
const float boxScale,
const float padding,
const style::SymbolPlacementType,
diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp
index b6427d0d04..26b26c5566 100644
--- a/src/mbgl/text/quads.cpp
+++ b/src/mbgl/text/quads.cpp
@@ -1,5 +1,6 @@
#include <mbgl/geometry/anchor.hpp>
#include <mbgl/layout/symbol_instance.hpp>
+#include <mbgl/math/minmax.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
#include <mbgl/text/quads.hpp>
#include <mbgl/text/shaping.hpp>
@@ -14,63 +15,184 @@ namespace mbgl {
using namespace style;
-SymbolQuads getIconQuads(const PositionedIcon& shapedIcon, const float iconRotate, const SymbolContent iconType) {
- const ImagePosition& image = shapedIcon.image();
+constexpr const auto border = ImagePosition::padding;
- // 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.
- constexpr auto border = ImagePosition::padding;
+float computeStretchSum(const ImageStretches& stretches) {
+ float sum = 0;
+ for (auto& stretch : stretches) {
+ sum += stretch.second - stretch.first;
+ }
+ return sum;
+}
- // 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 * image.paddedRect.w / (image.paddedRect.w - 2 * border) - iconWidth) / 2.0f;
- const float left = shapedIcon.left() - expandX;
- const float right = shapedIcon.right() + expandX;
+float sumWithinRange(const ImageStretches& stretches, const float min, const float max) {
+ float sum = 0;
+ for (auto& stretch : stretches) {
+ sum += util::max(min, util::min(max, stretch.second)) - util::max(min, util::min(max, stretch.first));
+ }
+ return sum;
+}
+
+inline float getEmOffset(float stretchOffset, float stretchSize, float iconSize, float iconOffset) {
+ return iconOffset + iconSize * stretchOffset / stretchSize;
+}
+
+inline float getPxOffset(float fixedOffset, float fixedSize, float stretchOffset, float stretchSize) {
+ return fixedOffset - fixedSize * stretchOffset / stretchSize;
+}
+
+struct Cut {
+ float fixed;
+ float stretch;
+};
+
+using Cuts = std::vector<Cut>;
+
+Cuts stretchZonesToCuts(const ImageStretches& stretchZones, const float fixedSize, const float stretchSize) {
+ Cuts cuts{{-border, 0}};
+
+ for (auto& zone : stretchZones) {
+ const auto c1 = zone.first;
+ const auto c2 = zone.second;
+ const auto lastStretch = cuts.back().stretch;
+ cuts.emplace_back(Cut{c1 - lastStretch, lastStretch});
+ cuts.emplace_back(Cut{c1 - lastStretch, lastStretch + (c2 - c1)});
+ }
+ cuts.emplace_back(Cut{fixedSize + border, stretchSize});
+ return cuts;
+}
+SymbolQuads getIconQuads(const PositionedIcon& shapedIcon,
+ const float iconRotate,
+ const SymbolContent iconType,
+ const bool hasIconTextFit) {
+ SymbolQuads quads;
+
+ const ImagePosition& image = shapedIcon.image();
+ const float pixelRatio = image.pixelRatio;
+ const uint16_t imageWidth = image.paddedRect.w - 2 * border;
+ const uint16_t imageHeight = image.paddedRect.h - 2 * border;
+
+ const float iconWidth = shapedIcon.right() - shapedIcon.left();
const float iconHeight = shapedIcon.bottom() - shapedIcon.top();
- const float expandY = (iconHeight * image.paddedRect.h / (image.paddedRect.h - 2 * border) - iconHeight) / 2.0f;
- const float top = shapedIcon.top() - expandY;
- const float bottom = shapedIcon.bottom() + expandY;
-
- Point<float> tl{left, top};
- Point<float> tr{right, top};
- Point<float> br{right, bottom};
- Point<float> bl{left, bottom};
-
- const float angle = iconRotate * util::DEG2RAD;
-
- if (angle) {
- // Compute the transformation matrix.
- float angle_sin = std::sin(angle);
- float angle_cos = std::cos(angle);
- std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}};
-
- tl = util::matrixMultiply(matrix, tl);
- tr = util::matrixMultiply(matrix, tr);
- bl = util::matrixMultiply(matrix, bl);
- br = util::matrixMultiply(matrix, br);
+
+ const ImageStretches stretchXFull{{0, imageWidth}};
+ const ImageStretches stretchYFull{{0, imageHeight}};
+ const ImageStretches& stretchX = !image.stretchX.empty() ? image.stretchX : stretchXFull;
+ const ImageStretches& stretchY = !image.stretchY.empty() ? image.stretchY : stretchYFull;
+
+ const float stretchWidth = computeStretchSum(stretchX);
+ const float stretchHeight = computeStretchSum(stretchY);
+ const float fixedWidth = imageWidth - stretchWidth;
+ const float fixedHeight = imageHeight - stretchHeight;
+
+ float stretchOffsetX = 0;
+ float stretchContentWidth = stretchWidth;
+ float stretchOffsetY = 0;
+ float stretchContentHeight = stretchHeight;
+ float fixedOffsetX = 0;
+ float fixedContentWidth = fixedWidth;
+ float fixedOffsetY = 0;
+ float fixedContentHeight = fixedHeight;
+
+ if (hasIconTextFit && image.content) {
+ auto& content = *image.content;
+ stretchOffsetX = sumWithinRange(stretchX, 0, content.left);
+ stretchOffsetY = sumWithinRange(stretchY, 0, content.top);
+ stretchContentWidth = sumWithinRange(stretchX, content.left, content.right);
+ stretchContentHeight = sumWithinRange(stretchY, content.top, content.bottom);
+ fixedOffsetX = content.left - stretchOffsetX;
+ fixedOffsetY = content.top - stretchOffsetY;
+ fixedContentWidth = content.right - content.left - stretchContentWidth;
+ fixedContentHeight = content.bottom - content.top - stretchContentHeight;
+ }
+
+ optional<std::array<float, 4>> matrix{nullopt};
+ if (iconRotate) {
+ const float angle = iconRotate * util::DEG2RAD;
+ const float angle_sin = std::sin(angle);
+ const float angle_cos = std::cos(angle);
+ matrix = std::array<float, 4>{{angle_cos, -angle_sin, angle_sin, angle_cos}};
}
- Point<float> pixelOffsetTL;
- Point<float> pixelOffsetBR;
- Point<float> minFontScale;
-
- return {SymbolQuad{tl,
- tr,
- bl,
- br,
- image.paddedRect,
- WritingModeType::None,
- {0.0f, 0.0f},
- iconType == SymbolContent::IconSDF,
- pixelOffsetTL,
- pixelOffsetBR,
- minFontScale}};
+ auto makeBox = [&](Cut left, Cut top, Cut right, Cut bottom) {
+ const float leftEm =
+ getEmOffset(left.stretch - stretchOffsetX, stretchContentWidth, iconWidth, shapedIcon.left());
+ const float leftPx = getPxOffset(left.fixed - fixedOffsetX, fixedContentWidth, left.stretch, stretchWidth);
+
+ const float topEm =
+ getEmOffset(top.stretch - stretchOffsetY, stretchContentHeight, iconHeight, shapedIcon.top());
+ const float topPx = getPxOffset(top.fixed - fixedOffsetY, fixedContentHeight, top.stretch, stretchHeight);
+
+ const float rightEm =
+ getEmOffset(right.stretch - stretchOffsetX, stretchContentWidth, iconWidth, shapedIcon.left());
+ const float rightPx = getPxOffset(right.fixed - fixedOffsetX, fixedContentWidth, right.stretch, stretchWidth);
+
+ const float bottomEm =
+ getEmOffset(bottom.stretch - stretchOffsetY, stretchContentHeight, iconHeight, shapedIcon.top());
+ const float bottomPx =
+ getPxOffset(bottom.fixed - fixedOffsetY, fixedContentHeight, bottom.stretch, stretchHeight);
+
+ Point<float> tl(leftEm, topEm);
+ Point<float> tr(rightEm, topEm);
+ Point<float> br(rightEm, bottomEm);
+ Point<float> bl(leftEm, bottomEm);
+ const Point<float> pixelOffsetTL(leftPx / pixelRatio, topPx / pixelRatio);
+ const Point<float> pixelOffsetBR(rightPx / pixelRatio, bottomPx / pixelRatio);
+
+ if (matrix) {
+ tl = util::matrixMultiply(*matrix, tl);
+ tr = util::matrixMultiply(*matrix, tr);
+ bl = util::matrixMultiply(*matrix, bl);
+ br = util::matrixMultiply(*matrix, br);
+ }
+
+ const float x1 = left.stretch + left.fixed;
+ const float x2 = right.stretch + right.fixed;
+ const float y1 = top.stretch + top.fixed;
+ const float y2 = bottom.stretch + bottom.fixed;
+
+ // TODO: consider making texture coordinates float instead of uint16_t
+ const Rect<uint16_t> subRect{static_cast<uint16_t>(image.paddedRect.x + border + x1),
+ static_cast<uint16_t>(image.paddedRect.y + border + y1),
+ static_cast<uint16_t>(x2 - x1),
+ static_cast<uint16_t>(y2 - y1)};
+
+ const float minFontScaleX = fixedContentWidth / pixelRatio / iconWidth;
+ const float minFontScaleY = fixedContentHeight / pixelRatio / iconHeight;
+
+ // Icon quad is padded, so texture coordinates also need to be padded.
+ quads.emplace_back(tl,
+ tr,
+ bl,
+ br,
+ subRect,
+ WritingModeType::None,
+ Point<float>{0.0f, 0.0f},
+ iconType == SymbolContent::IconSDF,
+ pixelOffsetTL,
+ pixelOffsetBR,
+ Point<float>{minFontScaleX, minFontScaleY});
+ };
+
+ if (!hasIconTextFit || (image.stretchX.empty() && image.stretchY.empty())) {
+ makeBox({0, -1}, {0, -1}, {0, static_cast<float>(imageWidth + 1)}, {0, static_cast<float>(imageHeight + 1)});
+ } else {
+ const auto xCuts = stretchZonesToCuts(stretchX, fixedWidth, stretchWidth);
+ const auto yCuts = stretchZonesToCuts(stretchY, fixedHeight, stretchHeight);
+
+ for (size_t xi = 0; xi < xCuts.size() - 1; xi++) {
+ const auto& x1 = xCuts[xi];
+ const auto& x2 = xCuts[xi + 1];
+ for (size_t yi = 0; yi < yCuts.size() - 1; yi++) {
+ const auto& y1 = yCuts[yi];
+ const auto& y2 = yCuts[yi + 1];
+ makeBox(x1, y1, x2, y2);
+ }
+ }
+ }
+
+ return quads;
}
SymbolQuads getGlyphQuads(const Shaping& shapedText,
diff --git a/src/mbgl/text/quads.hpp b/src/mbgl/text/quads.hpp
index ef058989a5..18e6ce53c5 100644
--- a/src/mbgl/text/quads.hpp
+++ b/src/mbgl/text/quads.hpp
@@ -57,7 +57,10 @@ public:
using SymbolQuads = std::vector<SymbolQuad>;
-SymbolQuads getIconQuads(const PositionedIcon& shapedIcon, float iconRotate, SymbolContent iconType);
+SymbolQuads getIconQuads(const PositionedIcon& shapedIcon,
+ float iconRotate,
+ SymbolContent iconType,
+ bool hasIconTextFit);
SymbolQuads getGlyphQuads(const Shaping& shapedText,
const std::array<float, 2> textOffset,
diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp
index 033cbdf4e5..725a93555b 100644
--- a/src/mbgl/text/shaping.cpp
+++ b/src/mbgl/text/shaping.cpp
@@ -79,7 +79,17 @@ PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image,
float top = dy - image.displaySize()[1] * anchorAlign.verticalAlign;
float bottom = top + image.displaySize()[1];
- return PositionedIcon{image, top, bottom, left, right};
+ Padding collisionPadding;
+ if (image.content) {
+ auto& content = *image.content;
+ const auto pixelRatio = image.pixelRatio;
+ collisionPadding.left = content.left / pixelRatio;
+ collisionPadding.top = content.top / pixelRatio;
+ collisionPadding.right = image.displaySize()[0] - content.right / pixelRatio;
+ collisionPadding.bottom = image.displaySize()[1] - content.bottom / pixelRatio;
+ }
+
+ return PositionedIcon{image, top, bottom, left, right, collisionPadding};
}
void PositionedIcon::fitIconToText(const Shaping& shapedText,
diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp
index 0c238d36b0..5343d4c3b2 100644
--- a/src/mbgl/text/shaping.hpp
+++ b/src/mbgl/text/shaping.hpp
@@ -26,16 +26,37 @@ style::TextJustifyType getAnchorJustification(style::SymbolAnchorType anchor);
class SymbolFeature;
class BiDi;
+class Padding {
+public:
+ float left = 0;
+ float top = 0;
+ float right = 0;
+ float bottom = 0;
+
+ explicit operator bool() const { return left != 0 || top != 0 || right != 0 || bottom != 0; }
+
+ bool operator==(const Padding& rhs) const {
+ return left == rhs.left && top == rhs.top && right == rhs.right && bottom == rhs.bottom;
+ }
+};
+
class PositionedIcon {
private:
- PositionedIcon(ImagePosition image_, float top_, float bottom_, float left_, float right_)
- : _image(image_), _top(top_), _bottom(bottom_), _left(left_), _right(right_) {}
+ PositionedIcon(
+ ImagePosition image_, float top_, float bottom_, float left_, float right_, const Padding& collisionPadding_)
+ : _image(image_),
+ _top(top_),
+ _bottom(bottom_),
+ _left(left_),
+ _right(right_),
+ _collisionPadding(collisionPadding_) {}
ImagePosition _image;
float _top;
float _bottom;
float _left;
float _right;
+ Padding _collisionPadding;
public:
static PositionedIcon shapeIcon(const ImagePosition&,
@@ -55,6 +76,7 @@ public:
float bottom() const { return _bottom; }
float left() const { return _left; }
float right() const { return _right; }
+ const Padding& collisionPadding() const { return _collisionPadding; }
};
const Shaping getShaping(const TaggedString& string,
diff --git a/test/text/cross_tile_symbol_index.test.cpp b/test/text/cross_tile_symbol_index.test.cpp
index 3b02a90422..fe1375c66d 100644
--- a/test/text/cross_tile_symbol_index.test.cpp
+++ b/test/text/cross_tile_symbol_index.test.cpp
@@ -26,6 +26,7 @@ SymbolInstance makeSymbolInstance(float x, float y, std::u16string key) {
imageMap,
0,
SymbolContent::IconSDF,
+ false,
false);
return SymbolInstance(anchor, std::move(sharedData), shaping, nullopt, nullopt, 0, 0, placementType, textOffset, 0, 0, iconOffset, subfeature, 0, 0, key, 0.0f, 0.0f, 0.0f, variableTextOffset, false);
}
diff --git a/test/text/quads.test.cpp b/test/text/quads.test.cpp
index 4068fe643a..a95379a5d7 100644
--- a/test/text/quads.test.cpp
+++ b/test/text/quads.test.cpp
@@ -22,18 +22,18 @@ TEST(getIconQuads, normal) {
GeometryCoordinates line;
- SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA);
+ SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA, false);
ASSERT_EQ(quads.size(), 1);
const auto& quad = quads[0];
- EXPECT_EQ(quad.tl.x, -14);
- EXPECT_EQ(quad.tl.y, -10);
- EXPECT_EQ(quad.tr.x, 1);
- EXPECT_EQ(quad.tr.y, -10);
- EXPECT_EQ(quad.bl.x, -14);
- EXPECT_EQ(quad.bl.y, 1);
- EXPECT_EQ(quad.br.x, 1);
- EXPECT_EQ(quad.br.y, 1);
+ EXPECT_FLOAT_EQ(quad.tl.x, -14);
+ EXPECT_FLOAT_EQ(quad.tl.y, -10);
+ EXPECT_FLOAT_EQ(quad.tr.x, 1);
+ EXPECT_FLOAT_EQ(quad.tr.y, -10);
+ EXPECT_FLOAT_EQ(quad.bl.x, -14);
+ EXPECT_FLOAT_EQ(quad.bl.y, 1);
+ EXPECT_FLOAT_EQ(quad.br.x, 1);
+ EXPECT_FLOAT_EQ(quad.br.y, 1);
}
TEST(getIconQuads, style) {
@@ -62,7 +62,7 @@ TEST(getIconQuads, style) {
EXPECT_FLOAT_EQ(-18.5f, shapedIcon.left());
SymbolLayoutProperties::Evaluated layout;
- SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA);
+ SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA, false);
ASSERT_EQ(quads.size(), 1);
const auto& quad = quads[0];
@@ -81,7 +81,7 @@ TEST(getIconQuads, style) {
{
auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center);
shapedIcon.fitIconToText(shapedText, IconTextFitType::Width, {{0, 0, 0, 0}}, {{0, 0}}, 24.0f / 24.0f);
- SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA);
+ SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA, false);
ASSERT_EQ(quads.size(), 1);
const auto& quad = quads[0];
@@ -100,7 +100,7 @@ TEST(getIconQuads, style) {
{
auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center);
shapedIcon.fitIconToText(shapedText, IconTextFitType::Width, {{0, 0, 0, 0}}, {{0, 0}}, 12.0f / 24.0f);
- SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA);
+ SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA, false);
ASSERT_EQ(quads.size(), 1);
const auto& quad = quads[0];
@@ -119,7 +119,7 @@ TEST(getIconQuads, style) {
{
auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center);
shapedIcon.fitIconToText(shapedText, IconTextFitType::Width, {{5, 10, 5, 10}}, {{0, 0}}, 12.0f / 24.0f);
- SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA);
+ SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA, false);
ASSERT_EQ(quads.size(), 1);
const auto& quad = quads[0];
@@ -138,7 +138,7 @@ TEST(getIconQuads, style) {
{
auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center);
shapedIcon.fitIconToText(shapedText, IconTextFitType::Height, {{0, 0, 0, 0}}, {{0, 0}}, 24.0f / 24.0f);
- SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA);
+ SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA, false);
ASSERT_EQ(quads.size(), 1);
const auto& quad = quads[0];
@@ -158,7 +158,7 @@ TEST(getIconQuads, style) {
SymbolLayoutProperties::Evaluated layout;
auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center);
shapedIcon.fitIconToText(shapedText, IconTextFitType::Height, {{0, 0, 0, 0}}, {{0, 0}}, 12.0f / 24.0f);
- SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA);
+ SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA, false);
ASSERT_EQ(quads.size(), 1);
const auto& quad = quads[0];
@@ -177,7 +177,7 @@ TEST(getIconQuads, style) {
{
auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center);
shapedIcon.fitIconToText(shapedText, IconTextFitType::Height, {{5, 10, 5, 20}}, {{0, 0}}, 12.0f / 24.0f);
- SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA);
+ SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA, false);
ASSERT_EQ(quads.size(), 1);
const auto& quad = quads[0];
@@ -196,7 +196,7 @@ TEST(getIconQuads, style) {
{
auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center);
shapedIcon.fitIconToText(shapedText, IconTextFitType::Both, {{0, 0, 0, 0}}, {{0, 0}}, 24.0f / 24.0f);
- SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA);
+ SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA, false);
ASSERT_EQ(quads.size(), 1);
const auto& quad = quads[0];
@@ -215,7 +215,7 @@ TEST(getIconQuads, style) {
{
auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center);
shapedIcon.fitIconToText(shapedText, IconTextFitType::Both, {{0, 0, 0, 0}}, {{0, 0}}, 12.0f / 24.0f);
- SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA);
+ SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA, false);
ASSERT_EQ(quads.size(), 1);
const auto& quad = quads[0];
@@ -234,7 +234,7 @@ TEST(getIconQuads, style) {
{
auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center);
shapedIcon.fitIconToText(shapedText, IconTextFitType::Both, {{5, 10, 5, 10}}, {{0, 0}}, 12.0f / 24.0f);
- SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA);
+ SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA, false);
ASSERT_EQ(quads.size(), 1);
const auto& quad = quads[0];
@@ -255,7 +255,7 @@ TEST(getIconQuads, style) {
layout.get<TextSize>() = 12.0f;
auto shapedIcon = PositionedIcon::shapeIcon(image, {{-9.5f, -9.5f}}, SymbolAnchorType::Center);
shapedIcon.fitIconToText(shapedText, IconTextFitType::Both, {{0, 5, 10, 15}}, {{0, 0}}, 12.0f / 24.0f);
- SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA);
+ SymbolQuads quads = getIconQuads(shapedIcon, 0, SymbolContent::IconRGBA, false);
ASSERT_EQ(quads.size(), 1);
const auto& quad = quads[0];