Add textFitWidth and textFitHeight Properties to Sprites (#2780)

Co-authored-by: Xiaolian Qin <xiaoq@microsoft.com>
This commit is contained in:
qqz003
2024-09-13 21:02:02 +08:00
committed by GitHub
parent 814e1015a8
commit cb38119451
58 changed files with 2116 additions and 103 deletions

View File

@@ -14,6 +14,12 @@ namespace style {
using ImageStretch = std::pair<float, float>; using ImageStretch = std::pair<float, float>;
using ImageStretches = std::vector<ImageStretch>; using ImageStretches = std::vector<ImageStretch>;
enum class TextFit {
stretchOrShrink,
stretchOnly,
proportional
};
class ImageContent { class ImageContent {
public: public:
float left; float left;
@@ -34,15 +40,26 @@ public:
bool sdf, bool sdf,
ImageStretches stretchX = {}, ImageStretches stretchX = {},
ImageStretches stretchY = {}, ImageStretches stretchY = {},
const std::optional<ImageContent>& content = std::nullopt); const std::optional<ImageContent>& content = std::nullopt,
const std::optional<TextFit>& textFitWidth = std::nullopt,
const std::optional<TextFit>& textFitHeight = std::nullopt);
Image(std::string id, Image(std::string id,
PremultipliedImage&& image, PremultipliedImage&& image,
float pixelRatio, float pixelRatio,
ImageStretches stretchX = {}, ImageStretches stretchX = {},
ImageStretches stretchY = {}, ImageStretches stretchY = {},
const std::optional<ImageContent>& content = std::nullopt) const std::optional<ImageContent>& content = std::nullopt,
: Image(std::move(id), std::move(image), pixelRatio, false, std::move(stretchX), std::move(stretchY), content) { const std::optional<TextFit>& textFitWidth = std::nullopt,
} const std::optional<TextFit>& textFitHeight = std::nullopt)
: Image(std::move(id),
std::move(image),
pixelRatio,
false,
std::move(stretchX),
std::move(stretchY),
content,
textFitWidth,
textFitHeight) {}
Image(const Image&); Image(const Image&);
std::string getID() const; std::string getID() const;
@@ -63,6 +80,12 @@ public:
/// The space where text can be fit into this image. /// The space where text can be fit into this image.
const std::optional<ImageContent>& getContent() const; const std::optional<ImageContent>& getContent() const;
/// The constraints on the horizontal scaling of the image when `icon-text-fit` is used
const std::optional<TextFit>& getTextFitWidth() const;
/// The constraints on the vertical scaling of the image when `icon-text-fit` is used
const std::optional<TextFit>& getTextFitHeight() const;
class Impl; class Impl;
Immutable<Impl> baseImpl; Immutable<Impl> baseImpl;
explicit Image(Immutable<Impl> baseImpl_) explicit Image(Immutable<Impl> baseImpl_)

Binary file not shown.

View File

@@ -85,6 +85,7 @@
"render-tests/fill-pattern/update-feature-state": "https://github.com/mapbox/mapbox-gl-native/issues/15895", "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/geojson/inline-linestring-fill": "current behavior is arbitrary",
"render-tests/icon-text-fit/text-variable-anchor-overlap": "https://github.com/mapbox/mapbox-gl-native/issues/15809", "render-tests/icon-text-fit/text-variable-anchor-overlap": "https://github.com/mapbox/mapbox-gl-native/issues/15809",
"render-tests/icon-text-fit/textFit-grid-long": "Needs to be investigated and fixed.",
"render-tests/mixed-zoom/z10-z11": "https://github.com/mapbox/mapbox-gl-native/issues/10397", "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", "render-tests/raster-masking/overlapping-zoom": "https://github.com/mapbox/mapbox-gl-native/issues/10195",
"render-tests/real-world/bangkok": "https://github.com/mapbox/mapbox-gl-native/issues/10412", "render-tests/real-world/bangkok": "https://github.com/mapbox/mapbox-gl-native/issues/10412",

View File

@@ -0,0 +1,85 @@
{
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"cell": "top-left"
},
"geometry": {
"type": "Point",
"coordinates": [ -70, 60 ]
}
}, {
"type": "Feature",
"properties": {
"cell": "top-middle"
},
"geometry": {
"type": "Point",
"coordinates": [ 0, 60 ]
}
}, {
"type": "Feature",
"properties": {
"cell": "top-right"
},
"geometry": {
"type": "Point",
"coordinates": [ 70, 60 ]
}
}, {
"type": "Feature",
"properties": {
"cell": "middle-left"
},
"geometry": {
"type": "Point",
"coordinates": [ -70, 0 ]
}
}, {
"type": "Feature",
"properties": {
"cell": "middle-middle"
},
"geometry": {
"type": "Point",
"coordinates": [ 0, 0 ]
}
}, {
"type": "Feature",
"properties": {
"cell": "middle-right"
},
"geometry": {
"type": "Point",
"coordinates": [ 70, 0 ]
}
}, {
"type": "Feature",
"properties": {
"cell": "bottom-left"
},
"geometry": {
"type": "Point",
"coordinates": [ -70, -60 ]
}
}, {
"type": "Feature",
"properties": {
"cell": "bottom-middle"
},
"geometry": {
"type": "Point",
"coordinates": [ 0, -60 ]
}
}, {
"type": "Feature",
"properties": {
"cell": "bottom-right"
},
"geometry": {
"type": "Point",
"coordinates": [ 70, -60 ]
}
}]
}

View File

@@ -0,0 +1,85 @@
{
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"cell": "top-left"
},
"geometry": {
"type": "Point",
"coordinates": [ -70, 35 ]
}
}, {
"type": "Feature",
"properties": {
"cell": "top-middle"
},
"geometry": {
"type": "Point",
"coordinates": [ 0, 35 ]
}
}, {
"type": "Feature",
"properties": {
"cell": "top-right"
},
"geometry": {
"type": "Point",
"coordinates": [ 70, 35 ]
}
}, {
"type": "Feature",
"properties": {
"cell": "middle-left"
},
"geometry": {
"type": "Point",
"coordinates": [ -70, 0 ]
}
}, {
"type": "Feature",
"properties": {
"cell": "middle-middle"
},
"geometry": {
"type": "Point",
"coordinates": [ 0, 0 ]
}
}, {
"type": "Feature",
"properties": {
"cell": "middle-right"
},
"geometry": {
"type": "Point",
"coordinates": [ 70, 0 ]
}
}, {
"type": "Feature",
"properties": {
"cell": "bottom-left"
},
"geometry": {
"type": "Point",
"coordinates": [ -70, -35 ]
}
}, {
"type": "Feature",
"properties": {
"cell": "bottom-middle"
},
"geometry": {
"type": "Point",
"coordinates": [ 0, -35 ]
}
}, {
"type": "Feature",
"properties": {
"cell": "bottom-right"
},
"geometry": {
"type": "Point",
"coordinates": [ 70, -35 ]
}
}]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -0,0 +1,193 @@
{
"version": 8,
"metadata": {
"test": {
"width": 200,
"height": 150,
"description": "This an adaptation of stretch-nine-part-content but with a long enough text content to stretch beyond aspect ratio."
}
},
"sources": {
"geojson": {
"type": "geojson",
"data": "local://geojson/anchors.json"
}
},
"sprite": "local://sprites/stretch",
"glyphs": "local://glyphs/{fontstack}/{range}.pbf",
"layers": [
{
"id": "anchor-center",
"type": "symbol",
"source": "geojson",
"filter": ["==", "anchor", "center"],
"layout": {
"text-field": "123456",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "anchor-left",
"type": "symbol",
"source": "geojson",
"filter": ["==", "anchor", "left"],
"layout": {
"text-field": "123456",
"text-size": 20,
"text-anchor": "left",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "anchor-top-left",
"type": "symbol",
"source": "geojson",
"filter": ["==", "anchor", "top-left"],
"layout": {
"text-field": "123456",
"text-size": 20,
"text-anchor": "top-left",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "anchor-top",
"type": "symbol",
"source": "geojson",
"filter": ["==", "anchor", "top"],
"layout": {
"text-field": "123456",
"text-size": 20,
"text-anchor": "top",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "anchor-top-right",
"type": "symbol",
"source": "geojson",
"filter": ["==", "anchor", "top-right"],
"layout": {
"text-field": "123456",
"text-size": 20,
"text-anchor": "top-right",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "anchor-right",
"type": "symbol",
"source": "geojson",
"filter": ["==", "anchor", "right"],
"layout": {
"text-field": "123456",
"text-size": 20,
"text-anchor": "right",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "anchor-bottom-left",
"type": "symbol",
"source": "geojson",
"filter": ["==", "anchor", "bottom-left"],
"layout": {
"text-field": "123456",
"text-size": 20,
"text-anchor": "bottom-left",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "anchor-bottom",
"type": "symbol",
"source": "geojson",
"filter": ["==", "anchor", "bottom"],
"layout": {
"text-field": "123456",
"text-size": 20,
"text-anchor": "bottom",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "anchor-bottom-right",
"type": "symbol",
"source": "geojson",
"filter": ["==", "anchor", "bottom-right"],
"layout": {
"text-field": "123456",
"text-size": 20,
"text-anchor": "bottom-right",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "anchors",
"type": "circle",
"source": "geojson",
"paint": {
"circle-radius": 2,
"circle-color": "green",
"circle-stroke-color": "white",
"circle-stroke-width": 1
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@@ -0,0 +1,193 @@
{
"version": 8,
"metadata": {
"test": {
"width": 200,
"height": 150,
"description": "This an adaptation of stretch-nine-part-content but with '1' as the text and textFitWidth=stretchOnly and textFitHeight=proportional. Because of this, the aspect ratio of the content won't get more narrow than the starting content aspect ratio."
}
},
"sources": {
"geojson": {
"type": "geojson",
"data": "local://geojson/anchors.json"
}
},
"sprite": "local://sprites/stretch",
"glyphs": "local://glyphs/{fontstack}/{range}.pbf",
"layers": [
{
"id": "anchor-center",
"type": "symbol",
"source": "geojson",
"filter": ["==", "anchor", "center"],
"layout": {
"text-field": "1",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "anchor-left",
"type": "symbol",
"source": "geojson",
"filter": ["==", "anchor", "left"],
"layout": {
"text-field": "1",
"text-size": 20,
"text-anchor": "left",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "anchor-top-left",
"type": "symbol",
"source": "geojson",
"filter": ["==", "anchor", "top-left"],
"layout": {
"text-field": "1",
"text-size": 20,
"text-anchor": "top-left",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "anchor-top",
"type": "symbol",
"source": "geojson",
"filter": ["==", "anchor", "top"],
"layout": {
"text-field": "1",
"text-size": 20,
"text-anchor": "top",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "anchor-top-right",
"type": "symbol",
"source": "geojson",
"filter": ["==", "anchor", "top-right"],
"layout": {
"text-field": "1",
"text-size": 20,
"text-anchor": "top-right",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "anchor-right",
"type": "symbol",
"source": "geojson",
"filter": ["==", "anchor", "right"],
"layout": {
"text-field": "1",
"text-size": 20,
"text-anchor": "right",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "anchor-bottom-left",
"type": "symbol",
"source": "geojson",
"filter": ["==", "anchor", "bottom-left"],
"layout": {
"text-field": "1",
"text-size": 20,
"text-anchor": "bottom-left",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "anchor-bottom",
"type": "symbol",
"source": "geojson",
"filter": ["==", "anchor", "bottom"],
"layout": {
"text-field": "1",
"text-size": 20,
"text-anchor": "bottom",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "anchor-bottom-right",
"type": "symbol",
"source": "geojson",
"filter": ["==", "anchor", "bottom-right"],
"layout": {
"text-field": "1",
"text-size": 20,
"text-anchor": "bottom-right",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "anchors",
"type": "circle",
"source": "geojson",
"paint": {
"circle-radius": 2,
"circle-color": "green",
"circle-stroke-color": "white",
"circle-stroke-width": 1
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,99 @@
{
"version": 8,
"metadata": {
"test": {
"width": 64,
"height": 64
}
},
"sources": {
"geojson": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"order": "no-match"
},
"geometry": {
"type": "Point",
"coordinates": [-6.6, 12]
}
}, {
"type": "Feature",
"properties": {
"order": "no-match"
},
"geometry": {
"type": "Point",
"coordinates": [6.6, 12]
}
}, {
"type": "Feature",
"properties": {
"order": "match"
},
"geometry": {
"type": "Point",
"coordinates": [-6.6, -11.5]
}
}, {
"type": "Feature",
"properties": {
"order": "match"
},
"geometry": {
"type": "Point",
"coordinates": [6.6, -11.5]
}
}]
}
}
},
"sprite": "local://sprites/stretch",
"glyphs": "local://glyphs/{fontstack}/{range}.pbf",
"layers": [
{
"id": "symbol",
"type": "symbol",
"source": "geojson",
"filter": ["==", "order", "no-match"],
"layout": {
"text-field": "I",
"text-font": [
"Open Sans Semibold",
"Arial Unicode MS Bold"
],
"icon-image": "nine-part-content",
"icon-text-fit": "both"
}
},
{
"id": "symbol_with_offset",
"type": "symbol",
"source": "geojson",
"filter": ["==", "order", "match"],
"layout": {
"text-field": "I",
"text-font": [
"Open Sans Semibold",
"Arial Unicode MS Bold"
],
"icon-image": "nine-part-textfit-wh-stretchOnly-proportional",
"icon-text-fit": "both"
}
},
{
"id": "anchors",
"type": "circle",
"source": "geojson",
"paint": {
"circle-radius": 2,
"circle-color": "green",
"circle-stroke-color": "white",
"circle-stroke-width": 1
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -0,0 +1,191 @@
{
"version": 8,
"metadata": {
"test": {
"width": 360,
"height": 360,
"description": "This draws the matrix of all possible combinations of textFitWidth (columns) and textFitHeight (rows) where they proceed from the top-left in the order stretchOrShrink, stretchOnly, and proportional. This version shows the effect of a long string with a wider aspect ratio than the source image."
}
},
"sources": {
"geojson": {
"type": "geojson",
"data": "local://geojson/grid-tall.json"
}
},
"sprite": "local://sprites/textFit",
"glyphs": "local://glyphs/{fontstack}/{range}.pbf",
"layers": [
{
"id": "wh-stretchOrShrink-stretchOrShrink",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "top-left"],
"layout": {
"text-field": "一二三四",
"text-size": 20,
"text-anchor": "center",
"text-writing-mode": ["vertical"],
"text-font": [ "NotoCJK" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOrShrink-stretchOrShrink",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOnly-stretchOrShrink",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "top-middle"],
"layout": {
"text-field": "一二三四",
"text-size": 20,
"text-anchor": "center",
"text-writing-mode": ["vertical"],
"text-font": [ "NotoCJK" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOnly-stretchOrShrink",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-proportional-stretchOrShrink",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "top-right"],
"layout": {
"text-field": "一二三四",
"text-size": 20,
"text-anchor": "center",
"text-writing-mode": ["vertical"],
"text-font": [ "NotoCJK" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-proportional-stretchOrShrink",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOrShrink-stretchOnly",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "middle-left"],
"layout": {
"text-field": "一二三四",
"text-size": 20,
"text-anchor": "center",
"text-writing-mode": ["vertical"],
"text-font": [ "NotoCJK" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOrShrink-stretchOnly",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOnly-stretchOnly",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "middle-middle"],
"layout": {
"text-field": "一二三四",
"text-size": 20,
"text-anchor": "center",
"text-writing-mode": ["vertical"],
"text-font": [ "NotoCJK" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOnly-stretchOnly",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-proportional-stretchOnly",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "middle-right"],
"layout": {
"text-field": "一二三四",
"text-size": 20,
"text-anchor": "center",
"text-writing-mode": ["vertical"],
"text-font": [ "NotoCJK" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-proportional-stretchOnly",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOrShrink-proportional",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "bottom-left"],
"layout": {
"text-field": "一二三四",
"text-size": 20,
"text-anchor": "center",
"text-writing-mode": ["vertical"],
"text-font": [ "NotoCJK" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOrShrink-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOnly-proportional",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "bottom-middle"],
"layout": {
"text-field": "一二三四",
"text-size": 20,
"text-anchor": "center",
"text-writing-mode": ["vertical"],
"text-font": [ "NotoCJK" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-proportional-proportional",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "bottom-right"],
"layout": {
"text-field": "一二三四",
"text-size": 20,
"text-anchor": "center",
"text-writing-mode": ["vertical"],
"text-font": [ "NotoCJK" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-proportional-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,182 @@
{
"version": 8,
"metadata": {
"test": {
"width": 360,
"height": 180,
"description": "This draws the matrix of all possible combinations of textFitWidth (columns) and textFitHeight (rows) where they proceed from the top-left in the order stretchOrShrink, stretchOnly, and proportional. This version shows the effect of a long string with a wider aspect ratio than the source image."
}
},
"sources": {
"geojson": {
"type": "geojson",
"data": "local://geojson/grid.json"
}
},
"sprite": "local://sprites/textFit",
"glyphs": "local://glyphs/{fontstack}/{range}.pbf",
"layers": [
{
"id": "wh-stretchOrShrink-stretchOrShrink",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "top-left"],
"layout": {
"text-field": "12345",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOrShrink-stretchOrShrink",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOnly-stretchOrShrink",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "top-middle"],
"layout": {
"text-field": "12345",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOnly-stretchOrShrink",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-proportional-stretchOrShrink",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "top-right"],
"layout": {
"text-field": "12345",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-proportional-stretchOrShrink",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOrShrink-stretchOnly",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "middle-left"],
"layout": {
"text-field": "12345",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOrShrink-stretchOnly",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOnly-stretchOnly",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "middle-middle"],
"layout": {
"text-field": "12345",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOnly-stretchOnly",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-proportional-stretchOnly",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "middle-right"],
"layout": {
"text-field": "12345",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-proportional-stretchOnly",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOrShrink-proportional",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "bottom-left"],
"layout": {
"text-field": "12345",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOrShrink-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOnly-proportional",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "bottom-middle"],
"layout": {
"text-field": "12345",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-proportional-proportional",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "bottom-right"],
"layout": {
"text-field": "12345",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-proportional-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -0,0 +1,191 @@
{
"version": 8,
"metadata": {
"test": {
"width": 360,
"height": 360,
"description": "This draws the matrix of all possible combinations of textFitWidth (columns) and textFitHeight (rows) where they proceed from the top-left in the order stretchOrShrink, stretchOnly, and proportional. This version shows the effect of a long string with a wider aspect ratio than the source image."
}
},
"sources": {
"geojson": {
"type": "geojson",
"data": "local://geojson/grid-tall.json"
}
},
"sprite": "local://sprites/textFit",
"glyphs": "local://glyphs/{fontstack}/{range}.pbf",
"layers": [
{
"id": "wh-stretchOrShrink-stretchOrShrink",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "top-left"],
"layout": {
"text-field": "四",
"text-size": 20,
"text-anchor": "center",
"text-writing-mode": ["vertical"],
"text-font": [ "NotoCJK" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOrShrink-stretchOrShrink",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOnly-stretchOrShrink",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "top-middle"],
"layout": {
"text-field": "四",
"text-size": 20,
"text-anchor": "center",
"text-writing-mode": ["vertical"],
"text-font": [ "NotoCJK" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOnly-stretchOrShrink",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-proportional-stretchOrShrink",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "top-right"],
"layout": {
"text-field": "四",
"text-size": 20,
"text-anchor": "center",
"text-writing-mode": ["vertical"],
"text-font": [ "NotoCJK" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-proportional-stretchOrShrink",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOrShrink-stretchOnly",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "middle-left"],
"layout": {
"text-field": "四",
"text-size": 20,
"text-anchor": "center",
"text-writing-mode": ["vertical"],
"text-font": [ "NotoCJK" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOrShrink-stretchOnly",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOnly-stretchOnly",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "middle-middle"],
"layout": {
"text-field": "四",
"text-size": 20,
"text-anchor": "center",
"text-writing-mode": ["vertical"],
"text-font": [ "NotoCJK" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOnly-stretchOnly",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-proportional-stretchOnly",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "middle-right"],
"layout": {
"text-field": "四",
"text-size": 20,
"text-anchor": "center",
"text-writing-mode": ["vertical"],
"text-font": [ "NotoCJK" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-proportional-stretchOnly",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOrShrink-proportional",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "bottom-left"],
"layout": {
"text-field": "四",
"text-size": 20,
"text-anchor": "center",
"text-writing-mode": ["vertical"],
"text-font": [ "NotoCJK" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOrShrink-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOnly-proportional",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "bottom-middle"],
"layout": {
"text-field": "四",
"text-size": 20,
"text-anchor": "center",
"text-writing-mode": ["vertical"],
"text-font": [ "NotoCJK" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-proportional-proportional",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "bottom-right"],
"layout": {
"text-field": "四",
"text-size": 20,
"text-anchor": "center",
"text-writing-mode": ["vertical"],
"text-font": [ "NotoCJK" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-proportional-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,182 @@
{
"version": 8,
"metadata": {
"test": {
"width": 360,
"height": 180,
"description": "This draws the matrix of all possible combinations of textFitWidth (columns) and textFitHeight (rows) where they proceed from the top-left in the order stretchOrShrink, stretchOnly, and proportional. This version shows the effect of a short string with a narrower aspect ratio than the source image."
}
},
"sources": {
"geojson": {
"type": "geojson",
"data": "local://geojson/grid.json"
}
},
"sprite": "local://sprites/textFit",
"glyphs": "local://glyphs/{fontstack}/{range}.pbf",
"layers": [
{
"id": "wh-stretchOrShrink-stretchOrShrink",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "top-left"],
"layout": {
"text-field": "1",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOrShrink-stretchOrShrink",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOnly-stretchOrShrink",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "top-middle"],
"layout": {
"text-field": "1",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOnly-stretchOrShrink",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-proportional-stretchOrShrink",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "top-right"],
"layout": {
"text-field": "1",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-proportional-stretchOrShrink",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOrShrink-stretchOnly",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "middle-left"],
"layout": {
"text-field": "1",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOrShrink-stretchOnly",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOnly-stretchOnly",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "middle-middle"],
"layout": {
"text-field": "1",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOnly-stretchOnly",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-proportional-stretchOnly",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "middle-right"],
"layout": {
"text-field": "1",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-proportional-stretchOnly",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOrShrink-proportional",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "bottom-left"],
"layout": {
"text-field": "1",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOrShrink-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-stretchOnly-proportional",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "bottom-middle"],
"layout": {
"text-field": "1",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-stretchOnly-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
},
{
"id": "wh-proportional-proportional",
"type": "symbol",
"source": "geojson",
"filter": ["==", "cell", "bottom-right"],
"layout": {
"text-field": "1",
"text-size": 20,
"text-anchor": "center",
"text-font": [ "Open Sans Semibold", "Arial Unicode MS Bold" ],
"text-allow-overlap": true,
"text-ignore-placement": true,
"icon-image": "textFit-wh-proportional-proportional",
"icon-text-fit": "both",
"icon-allow-overlap": true,
"icon-ignore-placement": true
}
}
]
}

View File

@@ -20,6 +20,19 @@
"sdf": false, "sdf": false,
"content": [10, 10, 30, 30] "content": [10, 10, 30, 30]
}, },
"nine-part-textfit-wh-stretchOnly-proportional": {
"x": 0,
"y": 0,
"width": 40,
"height": 40,
"pixelRatio": 2,
"stretchX": [[10, 30]],
"stretchY": [[10, 30]],
"sdf": false,
"content": [10, 10, 30, 30],
"textFitWidth": "stretchOnly",
"textFitHeight": "proportional"
},
"fifteen-part": { "fifteen-part": {
"x": 50, "x": 50,
"y": 0, "y": 0,

View File

@@ -0,0 +1,119 @@
{
"textFit-wh-stretchOrShrink-stretchOrShrink": {
"x": 0,
"y": 0,
"width": 100,
"height": 70,
"pixelRatio": 1,
"stretchX": [[9, 91]],
"stretchY": [[9, 61]],
"sdf": false,
"content": [9, 9, 91, 61],
"textFitWidth": "stretchOrShrink",
"textFitHeight": "stretchOrShrink"
},
"textFit-wh-stretchOnly-stretchOrShrink": {
"x": 0,
"y": 0,
"width": 100,
"height": 70,
"pixelRatio": 1,
"stretchX": [[9, 91]],
"stretchY": [[9, 61]],
"sdf": false,
"content": [9, 9, 91, 61],
"textFitWidth": "stretchOnly",
"textFitHeight": "stretchOrShrink"
},
"textFit-wh-proportional-stretchOrShrink": {
"x": 0,
"y": 0,
"width": 100,
"height": 70,
"pixelRatio": 1,
"stretchX": [[9, 91]],
"stretchY": [[9, 61]],
"sdf": false,
"content": [9, 9, 91, 61],
"textFitWidth": "proportional",
"textFitHeight": "stretchOrShrink"
},
"textFit-wh-stretchOrShrink-stretchOnly": {
"x": 0,
"y": 0,
"width": 100,
"height": 70,
"pixelRatio": 1,
"stretchX": [[9, 91]],
"stretchY": [[9, 61]],
"sdf": false,
"content": [9, 9, 91, 61],
"textFitWidth": "stretchOrShrink",
"textFitHeight": "stretchOnly"
},
"textFit-wh-stretchOnly-stretchOnly": {
"x": 0,
"y": 0,
"width": 100,
"height": 70,
"pixelRatio": 1,
"stretchX": [[9, 91]],
"stretchY": [[9, 61]],
"sdf": false,
"content": [9, 9, 91, 61],
"textFitWidth": "stretchOnly",
"textFitHeight": "stretchOnly"
},
"textFit-wh-proportional-stretchOnly": {
"x": 0,
"y": 0,
"width": 100,
"height": 70,
"pixelRatio": 1,
"stretchX": [[9, 91]],
"stretchY": [[9, 61]],
"sdf": false,
"content": [9, 9, 91, 61],
"textFitWidth": "proportional",
"textFitHeight": "stretchOnly"
},
"textFit-wh-stretchOrShrink-proportional": {
"x": 0,
"y": 0,
"width": 100,
"height": 70,
"pixelRatio": 1,
"stretchX": [[9, 91]],
"stretchY": [[9, 61]],
"sdf": false,
"content": [9, 9, 91, 61],
"textFitWidth": "stretchOrShrink",
"textFitHeight": "proportional"
},
"textFit-wh-stretchOnly-proportional": {
"x": 0,
"y": 0,
"width": 100,
"height": 70,
"pixelRatio": 1,
"stretchX": [[9, 91]],
"stretchY": [[9, 61]],
"sdf": false,
"content": [9, 9, 91, 61],
"textFitWidth": "stretchOnly",
"textFitHeight": "proportional"
},
"textFit-wh-proportional-proportional": {
"x": 0,
"y": 0,
"width": 100,
"height": 70,
"pixelRatio": 1,
"stretchX": [[9, 91]],
"stretchY": [[9, 61]],
"sdf": false,
"content": [9, 9, 91, 61],
"textFitWidth": "proportional",
"textFitHeight": "proportional"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

View File

@@ -14,4 +14,4 @@
"probeGFX", "probeGFX",
"probeNetwork" "probeNetwork"
] ]
} }

View File

@@ -3,7 +3,7 @@
[ [
"probeNetwork - default - end", "probeNetwork - default - end",
3, 3,
3380 3696
], ],
[ [
"probeNetwork - default - start", "probeNetwork - default - start",
@@ -32,4 +32,4 @@
] ]
] ]
] ]
} }

View File

@@ -3,7 +3,7 @@
[ [
"probeNetwork - default - end", "probeNetwork - default - end",
3, 3,
3380 3696
], ],
[ [
"probeNetwork - default - start", "probeNetwork - default - start",
@@ -32,4 +32,4 @@
] ]
] ]
] ]
} }

View File

@@ -3,7 +3,7 @@
[ [
"probeNetwork - default - end", "probeNetwork - default - end",
4, 4,
88322 88638
], ],
[ [
"probeNetwork - default - start", "probeNetwork - default - start",
@@ -32,4 +32,4 @@
] ]
] ]
] ]
} }

View File

@@ -3,7 +3,7 @@
[ [
"probeNetwork - default - end", "probeNetwork - default - end",
4, 4,
88322 88638
], ],
[ [
"probeNetwork - default - start", "probeNetwork - default - start",
@@ -32,4 +32,4 @@
] ]
] ]
] ]
} }

View File

@@ -3,7 +3,7 @@
[ [
"probeNetwork - default - end", "probeNetwork - default - end",
4, 4,
88322 88638
], ],
[ [
"probeNetwork - default - start", "probeNetwork - default - start",
@@ -32,4 +32,4 @@
] ]
] ]
] ]
} }

View File

@@ -3,7 +3,7 @@
[ [
"probeNetwork - default - end", "probeNetwork - default - end",
4, 4,
88322 88638
], ],
[ [
"probeNetwork - default - start", "probeNetwork - default - start",
@@ -32,4 +32,4 @@
] ]
] ]
] ]
} }

View File

@@ -3,7 +3,7 @@
[ [
"probeNetwork - default - end", "probeNetwork - default - end",
4, 4,
88322 88638
], ],
[ [
"probeNetwork - default - start", "probeNetwork - default - start",
@@ -32,4 +32,4 @@
] ]
] ]
] ]
} }

View File

@@ -3,7 +3,7 @@
[ [
"probeNetwork - default - end", "probeNetwork - default - end",
4, 4,
88322 88638
], ],
[ [
"probeNetwork - default - start", "probeNetwork - default - start",
@@ -32,4 +32,4 @@
] ]
] ]
] ]
} }

View File

@@ -3,7 +3,7 @@
[ [
"probeNetwork - default - end", "probeNetwork - default - end",
4, 4,
88322 88638
], ],
[ [
"probeNetwork - default - start", "probeNetwork - default - start",
@@ -32,4 +32,4 @@
] ]
] ]
] ]
} }

View File

@@ -3,7 +3,7 @@
[ [
"probeNetwork - default - end", "probeNetwork - default - end",
4, 4,
88322 88638
], ],
[ [
"probeNetwork - default - start", "probeNetwork - default - start",
@@ -32,4 +32,4 @@
] ]
] ]
] ]
} }

View File

@@ -3,7 +3,7 @@
[ [
"probeNetwork - default - end", "probeNetwork - default - end",
4, 4,
88322 88638
], ],
[ [
"probeNetwork - default - start", "probeNetwork - default - start",
@@ -32,4 +32,4 @@
] ]
] ]
] ]
} }

View File

@@ -3,7 +3,7 @@
[ [
"probeNetwork - default - end", "probeNetwork - default - end",
4, 4,
88322 88638
], ],
[ [
"probeNetwork - default - start", "probeNetwork - default - start",
@@ -32,4 +32,4 @@
] ]
] ]
] ]
} }

View File

@@ -0,0 +1,14 @@
{
"network": [
[
"probeNetwork - default - end",
4,
88638
],
[
"probeNetwork - default - start",
0,
0
]
]
}

View File

@@ -0,0 +1,14 @@
{
"network": [
[
"probeNetwork - default - end",
4,
88638
],
[
"probeNetwork - default - start",
0,
0
]
]
}

View File

@@ -0,0 +1,14 @@
{
"network": [
[
"probeNetwork - default - end",
3,
86757
],
[
"probeNetwork - default - start",
0,
0
]
]
}

View File

@@ -0,0 +1,14 @@
{
"network": [
[
"probeNetwork - default - end",
5,
390425
],
[
"probeNetwork - default - start",
0,
0
]
]
}

View File

@@ -0,0 +1,14 @@
{
"network": [
[
"probeNetwork - default - end",
4,
90025
],
[
"probeNetwork - default - start",
0,
0
]
]
}

View File

@@ -0,0 +1,14 @@
{
"network": [
[
"probeNetwork - default - end",
4,
199330
],
[
"probeNetwork - default - start",
0,
0
]
]
}

View File

@@ -0,0 +1,14 @@
{
"network": [
[
"probeNetwork - default - end",
4,
90025
],
[
"probeNetwork - default - start",
0,
0
]
]
}

View File

@@ -13,7 +13,9 @@ ImagePosition::ImagePosition(const mapbox::Bin& bin, const style::Image::Impl& i
version(version_), version(version_),
stretchX(image.stretchX), stretchX(image.stretchX),
stretchY(image.stretchY), stretchY(image.stretchY),
content(image.content) {} content(image.content),
textFitWidth(image.textFitWidth),
textFitHeight(image.textFitHeight) {}
namespace { namespace {

View File

@@ -28,6 +28,8 @@ public:
style::ImageStretches stretchX; style::ImageStretches stretchX;
style::ImageStretches stretchY; style::ImageStretches stretchY;
std::optional<style::ImageContent> content; std::optional<style::ImageContent> content;
std::optional<style::TextFit> textFitWidth;
std::optional<style::TextFit> textFitHeight;
std::array<uint16_t, 2> tl() const { std::array<uint16_t, 2> tl() const {
return {{static_cast<uint16_t>(paddedRect.x + padding), static_cast<uint16_t>(paddedRect.y + padding)}}; return {{static_cast<uint16_t>(paddedRect.x + padding), static_cast<uint16_t>(paddedRect.y + padding)}};

View File

@@ -26,7 +26,9 @@ std::unique_ptr<style::Image> createStyleImage(const std::string& id,
const bool sdf, const bool sdf,
style::ImageStretches&& stretchX, style::ImageStretches&& stretchX,
style::ImageStretches&& stretchY, style::ImageStretches&& stretchY,
const std::optional<style::ImageContent>& content) { const std::optional<style::ImageContent>& content,
const std::optional<style::TextFit>& textFitWidth,
const std::optional<style::TextFit>& textFitHeight) {
// Disallow invalid parameter configurations. // Disallow invalid parameter configurations.
if (width <= 0 || height <= 0 || width > 1024 || height > 1024 || ratio <= 0 || ratio > 10 || srcX < 0 || if (width <= 0 || height <= 0 || width > 1024 || height > 1024 || ratio <= 0 || ratio > 10 || srcX < 0 ||
srcY < 0 || srcX >= static_cast<int32_t>(image.size.width) || srcY >= static_cast<int32_t>(image.size.height) || srcY < 0 || srcX >= static_cast<int32_t>(image.size.width) || srcY >= static_cast<int32_t>(image.size.height) ||
@@ -47,8 +49,15 @@ std::unique_ptr<style::Image> createStyleImage(const std::string& id,
PremultipliedImage::copy(image, dstImage, {static_cast<uint32_t>(srcX), static_cast<uint32_t>(srcY)}, {0, 0}, size); PremultipliedImage::copy(image, dstImage, {static_cast<uint32_t>(srcX), static_cast<uint32_t>(srcY)}, {0, 0}, size);
try { try {
return std::make_unique<style::Image>( return std::make_unique<style::Image>(id,
id, std::move(dstImage), static_cast<float>(ratio), sdf, std::move(stretchX), std::move(stretchY), content); std::move(dstImage),
static_cast<float>(ratio),
sdf,
std::move(stretchX),
std::move(stretchY),
content,
textFitWidth,
textFitHeight);
} catch (const util::StyleImageException& ex) { } catch (const util::StyleImageException& ex) {
Log::Error(Event::Sprite, std::string("Can't create image with invalid metadata: ") + ex.what()); Log::Error(Event::Sprite, std::string("Can't create image with invalid metadata: ") + ex.what());
return nullptr; return nullptr;
@@ -150,6 +159,40 @@ std::optional<style::ImageContent> getContent(const JSValue& value, const char*
return std::nullopt; return std::nullopt;
} }
std::optional<style::TextFit> parseTextFit(const std::string_view& value) {
if (value == "stretchOrShrink") {
return style::TextFit::stretchOrShrink;
} else if (value == "stretchOnly") {
return style::TextFit::stretchOnly;
} else if (value == "proportional") {
return style::TextFit::proportional;
} else {
return std::nullopt;
}
}
std::optional<style::TextFit> getTextFit(const JSValue& value, const char* property, const char* name) {
if (value.HasMember(property)) {
const auto& v = value[property];
if (v.IsString()) {
const auto& valueString = v.GetString();
const auto textFit = parseTextFit(std::string_view(valueString));
if (!textFit.has_value()) {
Log::Warning(Event::Sprite,
std::string("Invalid sprite image '") + name + "': value of '" + property +
"' is an invalid value '" + valueString + "'");
}
return textFit;
} else {
Log::Warning(
Event::Sprite,
std::string("Invalid sprite image '") + name + "': value of '" + property + "' must be a string");
}
}
return std::nullopt;
}
} // namespace } // namespace
std::vector<Immutable<style::Image::Impl>> parseSprite(const std::string& id, std::vector<Immutable<style::Image::Impl>> parseSprite(const std::string& id,
@@ -189,6 +232,8 @@ std::vector<Immutable<style::Image::Impl>> parseSprite(const std::string& id,
style::ImageStretches stretchX = getStretches(value, "stretchX", name.c_str()); style::ImageStretches stretchX = getStretches(value, "stretchX", name.c_str());
style::ImageStretches stretchY = getStretches(value, "stretchY", name.c_str()); style::ImageStretches stretchY = getStretches(value, "stretchY", name.c_str());
std::optional<style::ImageContent> content = getContent(value, "content", name.c_str()); std::optional<style::ImageContent> content = getContent(value, "content", name.c_str());
std::optional<style::TextFit> textFitWidth = getTextFit(value, "textFitWidth", name.c_str());
std::optional<style::TextFit> textFitHeight = getTextFit(value, "textFitHeight", name.c_str());
auto image = createStyleImage(completeName, auto image = createStyleImage(completeName,
raster, raster,
@@ -200,7 +245,9 @@ std::vector<Immutable<style::Image::Impl>> parseSprite(const std::string& id,
sdf, sdf,
std::move(stretchX), std::move(stretchX),
std::move(stretchY), std::move(stretchY),
content); content,
textFitWidth,
textFitHeight);
if (image) { if (image) {
images.push_back(std::move(image->baseImpl)); images.push_back(std::move(image->baseImpl));
} }

View File

@@ -17,7 +17,9 @@ std::unique_ptr<style::Image> createStyleImage(const std::string& id,
bool sdf, bool sdf,
style::ImageStretches&& stretchX = {}, style::ImageStretches&& stretchX = {},
style::ImageStretches&& stretchY = {}, style::ImageStretches&& stretchY = {},
const std::optional<style::ImageContent>& content = std::nullopt); const std::optional<style::ImageContent>& content = std::nullopt,
const std::optional<style::TextFit>& textFitWidth = std::nullopt,
const std::optional<style::TextFit>& textFitHeight = std::nullopt);
// Parses an image and an associated JSON file and returns the sprite objects. // Parses an image and an associated JSON file and returns the sprite objects.
std::vector<Immutable<style::Image::Impl>> parseSprite(const std::string& id, std::vector<Immutable<style::Image::Impl>> parseSprite(const std::string& id,

View File

@@ -11,9 +11,18 @@ Image::Image(std::string id,
bool sdf, bool sdf,
ImageStretches stretchX, ImageStretches stretchX,
ImageStretches stretchY, ImageStretches stretchY,
const std::optional<ImageContent>& content) const std::optional<ImageContent>& content,
: baseImpl(makeMutable<Impl>( const std::optional<TextFit>& textFitWidth,
std::move(id), std::move(image), pixelRatio, sdf, std::move(stretchX), std::move(stretchY), content)) {} const std::optional<TextFit>& textFitHeight)
: baseImpl(makeMutable<Impl>(std::move(id),
std::move(image),
pixelRatio,
sdf,
std::move(stretchX),
std::move(stretchY),
content,
textFitWidth,
textFitHeight)) {}
std::string Image::getID() const { std::string Image::getID() const {
return baseImpl->id; return baseImpl->id;
@@ -45,5 +54,12 @@ const std::optional<ImageContent>& Image::getContent() const {
return baseImpl->content; return baseImpl->content;
} }
const std::optional<TextFit>& Image::getTextFitWidth() const {
return baseImpl->textFitWidth;
}
const std::optional<TextFit>& Image::getTextFitHeight() const {
return baseImpl->textFitHeight;
}
} // namespace style } // namespace style
} // namespace mbgl } // namespace mbgl

View File

@@ -38,14 +38,18 @@ Image::Impl::Impl(std::string id_,
bool sdf_, bool sdf_,
ImageStretches stretchX_, ImageStretches stretchX_,
ImageStretches stretchY_, ImageStretches stretchY_,
std::optional<ImageContent> content_) std::optional<ImageContent> content_,
std::optional<TextFit> textFitWidth_,
std::optional<TextFit> textFitHeight_)
: id(std::move(id_)), : id(std::move(id_)),
image(std::move(image_)), image(std::move(image_)),
pixelRatio(pixelRatio_), pixelRatio(pixelRatio_),
sdf(sdf_), sdf(sdf_),
stretchX(std::move(stretchX_)), stretchX(std::move(stretchX_)),
stretchY(std::move(stretchY_)), stretchY(std::move(stretchY_)),
content(std::move(content_)) { content(std::move(content_)),
textFitWidth(std::move(textFitWidth_)),
textFitHeight(std::move(textFitHeight_)) {
if (!image.valid()) { if (!image.valid()) {
throw util::StyleImageException("dimensions may not be zero"); throw util::StyleImageException("dimensions may not be zero");
} else if (pixelRatio <= 0) { } else if (pixelRatio <= 0) {

View File

@@ -17,7 +17,9 @@ public:
bool sdf = false, bool sdf = false,
ImageStretches stretchX = {}, ImageStretches stretchX = {},
ImageStretches stretchY = {}, ImageStretches stretchY = {},
std::optional<ImageContent> content = std::nullopt); std::optional<ImageContent> content = std::nullopt,
std::optional<TextFit> textFitWidth = std::nullopt,
std::optional<TextFit> textFitHeight = std::nullopt);
const std::string id; const std::string id;
@@ -35,6 +37,13 @@ public:
// The space where text can be fit into this image. // The space where text can be fit into this image.
const std::optional<ImageContent> content; const std::optional<ImageContent> content;
// If `icon-text-fit` is used in a layer with this image, this option defines constraints on the horizontal scaling
// of the image.
const std::optional<TextFit> textFitWidth;
// If `icon-text-fit` is used in a layer with this image, this option defines constraints on the vertical scaling of
// the image.
const std::optional<TextFit> textFitHeight;
}; };
} // namespace style } // namespace style

View File

@@ -7,19 +7,70 @@ namespace mbgl {
CollisionFeature::CollisionFeature(const GeometryCoordinates& line, CollisionFeature::CollisionFeature(const GeometryCoordinates& line,
const Anchor& anchor, const Anchor& anchor,
const float top, const Shaping& shapedText,
const float bottom,
const float left,
const float right,
const std::optional<Padding>& collisionPadding,
const float boxScale, const float boxScale,
const float padding, const float padding,
const style::SymbolPlacementType placement, const style::SymbolPlacementType placement,
IndexedSubfeature indexedFeature_, const RefIndexedSubfeature& indexedFeature_,
const float overscaling, const float overscaling,
const float rotate) const float rotate)
: indexedFeature(std::move(indexedFeature_)), : indexedFeature(std::move(indexedFeature_)),
alongLine(placement != style::SymbolPlacementType::Point) { alongLine(placement != style::SymbolPlacementType::Point) {
initialize(line,
anchor,
shapedText.top,
shapedText.bottom,
shapedText.left,
shapedText.right,
std::nullopt,
boxScale,
padding,
overscaling,
rotate);
}
CollisionFeature::CollisionFeature(const GeometryCoordinates& line,
const Anchor& anchor,
std::optional<PositionedIcon> shapedIcon,
const float boxScale,
const float padding,
const RefIndexedSubfeature& indexedFeature_,
const float rotate)
: indexedFeature(std::move(indexedFeature_)),
alongLine(false) {
if (shapedIcon) {
const auto& image = shapedIcon->image();
auto icon = *shapedIcon;
if (image.content && (image.textFitWidth || image.textFitHeight)) {
icon = shapedIcon->applyTextFit();
}
initialize(line,
anchor,
icon.top(),
icon.bottom(),
icon.left(),
icon.right(),
shapedIcon->collisionPadding(),
boxScale,
padding,
1,
rotate);
} else {
initialize(line, anchor, 0, 0, 0, 0, std::nullopt, boxScale, padding, 1, rotate);
}
}
void CollisionFeature::initialize(const GeometryCoordinates& line,
const Anchor& anchor,
float top,
float bottom,
float left,
float right,
const std::optional<Padding>& collisionPadding,
float boxScale,
float padding,
float overscaling,
float rotate) {
if (top == 0 && bottom == 0 && left == 0 && right == 0) return; if (top == 0 && bottom == 0 && left == 0 && right == 0) return;
float y1 = top * boxScale - padding; float y1 = top * boxScale - padding;

View File

@@ -89,20 +89,7 @@ public:
const style::SymbolPlacementType placement, const style::SymbolPlacementType placement,
const RefIndexedSubfeature& indexedFeature_, const RefIndexedSubfeature& indexedFeature_,
const float overscaling, const float overscaling,
const float rotate) const float rotate);
: CollisionFeature(line,
anchor,
shapedText.top,
shapedText.bottom,
shapedText.left,
shapedText.right,
std::nullopt,
boxScale,
padding,
placement,
indexedFeature_,
overscaling,
rotate) {}
// for icons // for icons
// Icons collision features are always SymbolPlacementType::Point, which // Icons collision features are always SymbolPlacementType::Point, which
@@ -118,40 +105,24 @@ public:
const float boxScale, const float boxScale,
const float padding, const float padding,
const RefIndexedSubfeature& indexedFeature_, const RefIndexedSubfeature& indexedFeature_,
const float rotate) const float rotate);
: CollisionFeature(line,
anchor,
(shapedIcon ? shapedIcon->top() : 0),
(shapedIcon ? shapedIcon->bottom() : 0),
(shapedIcon ? shapedIcon->left() : 0),
(shapedIcon ? shapedIcon->right() : 0),
(shapedIcon ? shapedIcon->collisionPadding() : std::optional<Padding>{std::nullopt}),
boxScale,
padding,
style::SymbolPlacementType::Point,
indexedFeature_,
1,
rotate) {}
CollisionFeature(const GeometryCoordinates& line,
const Anchor&,
float top,
float bottom,
float left,
float right,
const std::optional<Padding>& collisionPadding,
float boxScale,
float padding,
style::SymbolPlacementType,
IndexedSubfeature,
float overscaling,
float rotate);
std::vector<CollisionBox> boxes; std::vector<CollisionBox> boxes;
IndexedSubfeature indexedFeature; IndexedSubfeature indexedFeature;
bool alongLine; bool alongLine;
private: private:
void initialize(const GeometryCoordinates& line,
const Anchor& anchor,
float top,
float bottom,
float left,
float right,
const std::optional<Padding>& collisionPadding,
float boxScale,
float padding,
float overscaling,
float rotate);
void bboxifyLabel(const GeometryCoordinates& line, void bboxifyLabel(const GeometryCoordinates& line,
GeometryCoordinate& anchorPoint, GeometryCoordinate& anchorPoint,
std::size_t segment, std::size_t segment,

View File

@@ -76,9 +76,6 @@ SymbolQuads getIconQuads(const PositionedIcon& shapedIcon,
const uint16_t imageWidth = image.paddedRect.w - 2 * border; const uint16_t imageWidth = image.paddedRect.w - 2 * border;
const uint16_t imageHeight = image.paddedRect.h - 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 ImageStretches stretchXFull{{0.0f, imageWidth}}; const ImageStretches stretchXFull{{0.0f, imageWidth}};
const ImageStretches stretchYFull{{0.0f, imageHeight}}; const ImageStretches stretchYFull{{0.0f, imageHeight}};
const ImageStretches& stretchX = !image.stretchX.empty() ? image.stretchX : stretchXFull; const ImageStretches& stretchX = !image.stretchX.empty() ? image.stretchX : stretchXFull;
@@ -98,16 +95,23 @@ SymbolQuads getIconQuads(const PositionedIcon& shapedIcon,
float fixedOffsetY = 0; float fixedOffsetY = 0;
float fixedContentHeight = fixedHeight; float fixedContentHeight = fixedHeight;
auto icon = shapedIcon;
if (hasIconTextFit && image.content) { if (hasIconTextFit && image.content) {
auto& content = *image.content; const auto& content = *image.content;
const auto contentWidth = content.right - content.left;
const auto contentHeight = content.bottom - content.top;
if (image.textFitWidth || image.textFitHeight) {
icon = icon.applyTextFit();
}
stretchOffsetX = sumWithinRange(stretchX, 0, content.left); stretchOffsetX = sumWithinRange(stretchX, 0, content.left);
stretchOffsetY = sumWithinRange(stretchY, 0, content.top); stretchOffsetY = sumWithinRange(stretchY, 0, content.top);
stretchContentWidth = sumWithinRange(stretchX, content.left, content.right); stretchContentWidth = sumWithinRange(stretchX, content.left, content.right);
stretchContentHeight = sumWithinRange(stretchY, content.top, content.bottom); stretchContentHeight = sumWithinRange(stretchY, content.top, content.bottom);
fixedOffsetX = content.left - stretchOffsetX; fixedOffsetX = content.left - stretchOffsetX;
fixedOffsetY = content.top - stretchOffsetY; fixedOffsetY = content.top - stretchOffsetY;
fixedContentWidth = content.right - content.left - stretchContentWidth; fixedContentWidth = contentWidth - stretchContentWidth;
fixedContentHeight = content.bottom - content.top - stretchContentHeight; fixedContentHeight = contentHeight - stretchContentHeight;
} }
std::optional<std::array<float, 4>> matrix{std::nullopt}; std::optional<std::array<float, 4>> matrix{std::nullopt};
@@ -118,21 +122,22 @@ SymbolQuads getIconQuads(const PositionedIcon& shapedIcon,
matrix = std::array<float, 4>{{angle_cos, -angle_sin, angle_sin, angle_cos}}; matrix = std::array<float, 4>{{angle_cos, -angle_sin, angle_sin, angle_cos}};
} }
const float iconLeft = icon.left();
const float iconTop = icon.top();
const float iconWidth = icon.right() - iconLeft;
const float iconHeight = icon.bottom() - iconTop;
auto makeBox = [&](Cut left, Cut top, Cut right, Cut bottom) { auto makeBox = [&](Cut left, Cut top, Cut right, Cut bottom) {
const float leftEm = getEmOffset( const float leftEm = getEmOffset(left.stretch - stretchOffsetX, stretchContentWidth, iconWidth, iconLeft);
left.stretch - stretchOffsetX, stretchContentWidth, iconWidth, shapedIcon.left());
const float leftPx = getPxOffset(left.fixed - fixedOffsetX, fixedContentWidth, left.stretch, stretchWidth); const float leftPx = getPxOffset(left.fixed - fixedOffsetX, fixedContentWidth, left.stretch, stretchWidth);
const float topEm = getEmOffset( const float topEm = getEmOffset(top.stretch - stretchOffsetY, stretchContentHeight, iconHeight, iconTop);
top.stretch - stretchOffsetY, stretchContentHeight, iconHeight, shapedIcon.top());
const float topPx = getPxOffset(top.fixed - fixedOffsetY, fixedContentHeight, top.stretch, stretchHeight); const float topPx = getPxOffset(top.fixed - fixedOffsetY, fixedContentHeight, top.stretch, stretchHeight);
const float rightEm = getEmOffset( const float rightEm = getEmOffset(right.stretch - stretchOffsetX, stretchContentWidth, iconWidth, iconLeft);
right.stretch - stretchOffsetX, stretchContentWidth, iconWidth, shapedIcon.left());
const float rightPx = getPxOffset(right.fixed - fixedOffsetX, fixedContentWidth, right.stretch, stretchWidth); const float rightPx = getPxOffset(right.fixed - fixedOffsetX, fixedContentWidth, right.stretch, stretchWidth);
const float bottomEm = getEmOffset( const float bottomEm = getEmOffset(bottom.stretch - stretchOffsetY, stretchContentHeight, iconHeight, iconTop);
bottom.stretch - stretchOffsetY, stretchContentHeight, iconHeight, shapedIcon.top());
const float bottomPx = getPxOffset( const float bottomPx = getPxOffset(
bottom.fixed - fixedOffsetY, fixedContentHeight, bottom.stretch, stretchHeight); bottom.fixed - fixedOffsetY, fixedContentHeight, bottom.stretch, stretchHeight);

View File

@@ -92,6 +92,45 @@ PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image,
return PositionedIcon{image, top, bottom, left, right, collisionPadding}; return PositionedIcon{image, top, bottom, left, right, collisionPadding};
} }
PositionedIcon PositionedIcon::applyTextFit() const {
if (!_image.textFitWidth && !_image.textFitHeight) {
return *this;
}
const float width = _right - _left;
const float height = _bottom - _top;
const float contentWidth = _image.content->right - _image.content->left;
const float contentHeight = _image.content->bottom - _image.content->top;
const float contentAspectRatio = contentWidth / contentHeight;
const auto textFitWidth = _image.textFitWidth.value();
const auto textFitHeight = _image.textFitHeight.value();
float newLeft = _left;
float newRight = _right;
float newTop = _top;
float newBottom = _bottom;
if (textFitHeight == style::TextFit::proportional) {
if ((textFitWidth == style::TextFit::stretchOnly && (width / height) < contentAspectRatio) ||
textFitWidth == style::TextFit::proportional) {
float newIconWidth = std::ceil(height * contentAspectRatio);
newLeft *= newIconWidth / width;
newRight = newLeft + newIconWidth;
}
} else if (textFitWidth == style::TextFit::proportional) {
if (textFitHeight == style::TextFit::stretchOnly && contentAspectRatio != 0 &&
(width / height) > contentAspectRatio) {
float newIconHeight = std::ceil(width / contentAspectRatio);
newTop *= newIconHeight / height;
newBottom = newTop + newIconHeight;
}
} else {
// If neither textFitHeight nor textFitWidth are proportional then
// there is no effect since the content rectangle should be precisely
// matched to the content
}
return PositionedIcon{_image, newTop, newBottom, newLeft, newRight, _collisionPadding};
}
void PositionedIcon::fitIconToText(const Shaping& shapedText, void PositionedIcon::fitIconToText(const Shaping& shapedText,
const style::IconTextFitType textFit, const style::IconTextFitType textFit,
const std::array<float, 4>& padding, const std::array<float, 4>& padding,

View File

@@ -73,6 +73,10 @@ public:
const std::array<float, 2>& iconOffset, const std::array<float, 2>& iconOffset,
float fontScale); float fontScale);
// Called after a PositionedIcon has already been run through fitIconToText,
// but needs further adjustment to apply textFitWidth and textFitHeight.
PositionedIcon applyTextFit() const;
const ImagePosition& image() const { return _image; } const ImagePosition& image() const { return _image; }
float top() const { return _top; } float top() const { return _top; }
float bottom() const { return _bottom; } float bottom() const { return _bottom; }

View File

@@ -589,3 +589,87 @@ TEST(Sprite, SpriteParsingNullRatio) {
"sprite", "sprite",
})); }));
} }
TEST(Sprite, SpriteParsingTextFit) {
FixtureLog log;
const auto image_1x = util::read_file("test/fixtures/annotations/emerald.png");
const auto images = parseSprite("default", image_1x, R"JSON({
"image": {
"width": 16,
"height": 16,
"x": 0,
"y": 0,
"pixelRatio": 1,
"textFitWidth": "stretchOrShrink",
"textFitHeight": "proportional"
}
})JSON");
EXPECT_EQ(1u, images.size());
EXPECT_EQ("image", images[0]->id);
EXPECT_EQ(style::TextFit::stretchOrShrink, images[0]->textFitWidth);
EXPECT_EQ(style::TextFit::proportional, images[0]->textFitHeight);
}
TEST(Sprite, SpriteParsingNullTextFit) {
FixtureLog log;
const auto image_1x = util::read_file("test/fixtures/annotations/emerald.png");
const auto images = parseSprite("default", image_1x, R"JSON({
"image": {
"width": 16,
"height": 16,
"x": 0,
"y": 0,
"pixelRatio": 1,
"textFitHeight": "stretchOnly"
}
})JSON");
EXPECT_EQ(1u, images.size());
EXPECT_EQ("image", images[0]->id);
EXPECT_FALSE(images[0]->textFitWidth.has_value());
EXPECT_EQ(style::TextFit::stretchOnly, images[0]->textFitHeight);
}
TEST(Sprite, SpriteParsingInvalidTextFit) {
FixtureLog log;
const auto image_1x = util::read_file("test/fixtures/annotations/emerald.png");
parseSprite("default", image_1x, R"JSON({
"image": {
"width": 16,
"height": 16,
"x": 0,
"y": 0,
"pixelRatio": 1,
"textFitWidth": "invalid",
"textFitHeight": "stretchOnly"
}
})JSON");
EXPECT_EQ(1u,
log.count({EventSeverity::Warning,
Event::Sprite,
int64_t(-1),
"Invalid sprite image 'image': value of 'textFitWidth' "
"is an invalid value 'invalid'"}));
parseSprite("default", image_1x, R"JSON({
"image": {
"width": 16,
"height": 16,
"x": 0,
"y": 0,
"pixelRatio": 1,
"textFitHeight": 0
}
})JSON");
EXPECT_EQ(1u,
log.count({EventSeverity::Warning,
Event::Sprite,
int64_t(-1),
"Invalid sprite image 'image': value of 'textFitHeight' "
"must be a string"}));
}

View File

@@ -103,3 +103,125 @@ TEST(Shaping, ZWSP) {
ASSERT_EQ(shaping.writingMode, WritingModeType::Horizontal); ASSERT_EQ(shaping.writingMode, WritingModeType::Horizontal);
} }
} }
void setupShapedText(Shaping& shapedText, float textSize) {
const auto glyph = PositionedGlyph(32,
0.0f,
0.0f,
false,
0,
1.0,
/*texRect*/ {},
/*metrics*/ {},
/*imageID*/ std::nullopt);
shapedText.right = textSize;
shapedText.bottom = textSize;
shapedText.positionedLines.emplace_back();
shapedText.positionedLines.back().positionedGlyphs.emplace_back(glyph);
}
void testApplyTextFit(const mapbox::Bin& rectangle,
const style::ImageContent& content,
const std::optional<style::TextFit> textFitWidth,
const std::optional<style::TextFit> textFitHeight,
const Shaping& shapedText,
float fontScale,
float expectedRight,
float expectedBottom) {
ImagePosition image = {
rectangle,
style::Image::Impl("test",
PremultipliedImage({static_cast<uint32_t>(rectangle.w), static_cast<uint32_t>(rectangle.h)}),
1.0f,
false,
{},
{},
content,
textFitWidth,
textFitHeight)};
auto shapedIcon = PositionedIcon::shapeIcon(image, {0, 0}, style::SymbolAnchorType::TopLeft);
shapedIcon.fitIconToText(shapedText, style::IconTextFitType::Both, {0, 0, 0, 0}, {0, 0}, fontScale);
const auto& icon = shapedIcon.applyTextFit();
ASSERT_EQ(icon.top(), 0);
ASSERT_EQ(icon.left(), 0);
ASSERT_EQ(icon.right(), expectedRight);
ASSERT_EQ(icon.bottom(), expectedBottom);
}
TEST(Shaping, applyTextFit) {
float textSize = 4;
float fontScale = 4;
float expectedImageSize = textSize * fontScale;
Shaping shapedText;
setupShapedText(shapedText, textSize);
{
// applyTextFitHorizontal
// This set of tests against applyTextFit starts with a 100x20 image with a 5,5,95,15 content box
// that has been fitted to a 4*4 text with scale 4, resulting in a 16*16 image.
const auto horizontalRectangle = mapbox::Bin(-1, 100, 20, -1, -1, 0, 0);
const style::ImageContent horizontalContent = {5, 5, 95, 15};
{
// applyTextFit: not specified
// No change should happen
testApplyTextFit(horizontalRectangle,
horizontalContent,
std::nullopt,
std::nullopt,
shapedText,
fontScale,
expectedImageSize,
expectedImageSize);
}
{
// applyTextFit: both stretchOrShrink
// No change should happen
testApplyTextFit(horizontalRectangle,
horizontalContent,
style::TextFit::stretchOrShrink,
style::TextFit::stretchOrShrink,
shapedText,
fontScale,
expectedImageSize,
expectedImageSize);
}
{
// applyTextFit: stretchOnly, proportional
// Since textFitWidth is stretchOnly, it should be returned to
// the aspect ratio of the content rectangle (9:1) aspect ratio so 144x16.
testApplyTextFit(horizontalRectangle,
horizontalContent,
style::TextFit::stretchOnly,
style::TextFit::proportional,
shapedText,
fontScale,
expectedImageSize * 9,
expectedImageSize);
}
}
{
// applyTextFitVertical
// This set of tests against applyTextFit starts with a 20x100 image with a 5,5,15,95 content box
// that has been fitted to a 4*4 text with scale 4, resulting in a 16*16 image.
const auto verticalRectangle = mapbox::Bin(-1, 20, 100, -1, -1, 0, 0);
const style::ImageContent verticalContent = {5, 5, 15, 95};
{
// applyTextFit: stretchOnly, proportional
// Since textFitWidth is stretchOnly, it should be returned to
// the aspect ratio of the content rectangle (9:1) aspect ratio so 144x16.
testApplyTextFit(verticalRectangle,
verticalContent,
style::TextFit::proportional,
style::TextFit::stretchOnly,
shapedText,
fontScale,
expectedImageSize,
expectedImageSize * 9);
}
}
}