diff options
author | Anand Thakker <anandthakker@users.noreply.github.com> | 2017-02-28 19:54:24 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-28 19:54:24 -0800 |
commit | f901e776b3e63aaaa6bc0cc4476624bf84127fe6 (patch) | |
tree | 3e311971d57109c64e5ace45c111fb5909e7fb7b | |
parent | c3ed1f51ca677c8c2045320fe13ec881cbd94772 (diff) | |
download | qtlocation-mapboxgl-f901e776b3e63aaaa6bc0cc4476624bf84127fe6.tar.gz |
[core] Implement data-driven styling for {text,icon}-{color,opacity,halo-color,halo-blur,halo-width} (#7939)
* Add symbol dds attributes and adapt style code generation
* Update to mapbox-gl-js/master
* Refactor SymbolFeature as a subclass of GeometryTileFeature
Prepares for enabling DDS on symbol paint properties by allowing the
SymbolFeatures, which we keep around after constructing SymbolLayout,
to be used in evaluating data-driven paint properties later in the
layout process.
* Draft approach for splitting icon/text paint properties
The `Program` types are set up to bind GL attributes to each of the
data-driven paint properties specified in the `PaintProperties` type
provided. Since `SymbolPaintProperties` specifies both `Text*` and
`Icon*` properties, the symbolIcon, symbolIconSDF, and symbolGlyph
programs each attempt to bind roughly double the number of attributes
that they actually need.
This change addresses this by:
- Adding the more specific `IconPaintProperties` and `TextPaintProperties` types, which are subsets of the full `SymbolPaintProperties`.
- The symbol layer continues to use its `SymbolPaintProperties paint` member to track layer property state, but it provides helpers that construct objects of each the specific `{Icon,Text}PaintProperties::Evaluated` type, for use by the painter.
- The three symbol programs instantiate `Program<>` using the appropriate `{Icon,Text}PaintProperties` type.
* check in generated style code
* Populate paint buffers for symbol DDS properties
* Address first round of review comments
* Refactor VectorTile{Layer,Feature} to explicitly share data
* Update submodule
31 files changed, 1985 insertions, 478 deletions
diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp index 8f655d6f77..aeccabb97e 100644 --- a/include/mbgl/style/layers/symbol_layer.hpp +++ b/include/mbgl/style/layers/symbol_layer.hpp @@ -169,29 +169,29 @@ public: // Paint properties - static PropertyValue<float> getDefaultIconOpacity(); - PropertyValue<float> getIconOpacity(const optional<std::string>& klass = {}) const; - void setIconOpacity(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultIconOpacity(); + DataDrivenPropertyValue<float> getIconOpacity(const optional<std::string>& klass = {}) const; + void setIconOpacity(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {}); void setIconOpacityTransition(const TransitionOptions&, const optional<std::string>& klass = {}); - static PropertyValue<Color> getDefaultIconColor(); - PropertyValue<Color> getIconColor(const optional<std::string>& klass = {}) const; - void setIconColor(PropertyValue<Color>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<Color> getDefaultIconColor(); + DataDrivenPropertyValue<Color> getIconColor(const optional<std::string>& klass = {}) const; + void setIconColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {}); void setIconColorTransition(const TransitionOptions&, const optional<std::string>& klass = {}); - static PropertyValue<Color> getDefaultIconHaloColor(); - PropertyValue<Color> getIconHaloColor(const optional<std::string>& klass = {}) const; - void setIconHaloColor(PropertyValue<Color>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<Color> getDefaultIconHaloColor(); + DataDrivenPropertyValue<Color> getIconHaloColor(const optional<std::string>& klass = {}) const; + void setIconHaloColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {}); void setIconHaloColorTransition(const TransitionOptions&, const optional<std::string>& klass = {}); - static PropertyValue<float> getDefaultIconHaloWidth(); - PropertyValue<float> getIconHaloWidth(const optional<std::string>& klass = {}) const; - void setIconHaloWidth(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultIconHaloWidth(); + DataDrivenPropertyValue<float> getIconHaloWidth(const optional<std::string>& klass = {}) const; + void setIconHaloWidth(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {}); void setIconHaloWidthTransition(const TransitionOptions&, const optional<std::string>& klass = {}); - static PropertyValue<float> getDefaultIconHaloBlur(); - PropertyValue<float> getIconHaloBlur(const optional<std::string>& klass = {}) const; - void setIconHaloBlur(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultIconHaloBlur(); + DataDrivenPropertyValue<float> getIconHaloBlur(const optional<std::string>& klass = {}) const; + void setIconHaloBlur(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {}); void setIconHaloBlurTransition(const TransitionOptions&, const optional<std::string>& klass = {}); static PropertyValue<std::array<float, 2>> getDefaultIconTranslate(); @@ -204,29 +204,29 @@ public: void setIconTranslateAnchor(PropertyValue<TranslateAnchorType>, const optional<std::string>& klass = {}); void setIconTranslateAnchorTransition(const TransitionOptions&, const optional<std::string>& klass = {}); - static PropertyValue<float> getDefaultTextOpacity(); - PropertyValue<float> getTextOpacity(const optional<std::string>& klass = {}) const; - void setTextOpacity(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultTextOpacity(); + DataDrivenPropertyValue<float> getTextOpacity(const optional<std::string>& klass = {}) const; + void setTextOpacity(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {}); void setTextOpacityTransition(const TransitionOptions&, const optional<std::string>& klass = {}); - static PropertyValue<Color> getDefaultTextColor(); - PropertyValue<Color> getTextColor(const optional<std::string>& klass = {}) const; - void setTextColor(PropertyValue<Color>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<Color> getDefaultTextColor(); + DataDrivenPropertyValue<Color> getTextColor(const optional<std::string>& klass = {}) const; + void setTextColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {}); void setTextColorTransition(const TransitionOptions&, const optional<std::string>& klass = {}); - static PropertyValue<Color> getDefaultTextHaloColor(); - PropertyValue<Color> getTextHaloColor(const optional<std::string>& klass = {}) const; - void setTextHaloColor(PropertyValue<Color>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<Color> getDefaultTextHaloColor(); + DataDrivenPropertyValue<Color> getTextHaloColor(const optional<std::string>& klass = {}) const; + void setTextHaloColor(DataDrivenPropertyValue<Color>, const optional<std::string>& klass = {}); void setTextHaloColorTransition(const TransitionOptions&, const optional<std::string>& klass = {}); - static PropertyValue<float> getDefaultTextHaloWidth(); - PropertyValue<float> getTextHaloWidth(const optional<std::string>& klass = {}) const; - void setTextHaloWidth(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultTextHaloWidth(); + DataDrivenPropertyValue<float> getTextHaloWidth(const optional<std::string>& klass = {}) const; + void setTextHaloWidth(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {}); void setTextHaloWidthTransition(const TransitionOptions&, const optional<std::string>& klass = {}); - static PropertyValue<float> getDefaultTextHaloBlur(); - PropertyValue<float> getTextHaloBlur(const optional<std::string>& klass = {}) const; - void setTextHaloBlur(PropertyValue<float>, const optional<std::string>& klass = {}); + static DataDrivenPropertyValue<float> getDefaultTextHaloBlur(); + DataDrivenPropertyValue<float> getTextHaloBlur(const optional<std::string>& klass = {}) const; + void setTextHaloBlur(DataDrivenPropertyValue<float>, const optional<std::string>& klass = {}); void setTextHaloBlurTransition(const TransitionOptions&, const optional<std::string>& klass = {}); static PropertyValue<std::array<float, 2>> getDefaultTextTranslate(); diff --git a/mapbox-gl-js b/mapbox-gl-js -Subproject fd73395ef71cf77993d717b2e23c5dab5e372b6 +Subproject 7804faf89741cf7db90582d9e0881253cb9d4cd diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java index 03980e6a8b..5cd0d99270 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java @@ -454,11 +454,11 @@ public class PropertyFactory { /** * The opacity at which the icon will be drawn. * - * @param <Z> the zoom parameter type - * @param function a wrapper {@link CameraFunction} for Float + * @param <T> the function input type + * @param function a wrapper function for Float * @return property wrapper around a Float function */ - public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> iconOpacity(CameraFunction<Z, Float> function) { + public static <T> PropertyValue<Function<T, Float>> iconOpacity(Function<T, Float> function) { return new PaintPropertyValue<>("icon-opacity", function); } @@ -486,11 +486,11 @@ public class PropertyFactory { /** * The color of the icon. This can only be used with sdf icons. * - * @param <Z> the zoom parameter type - * @param function a wrapper {@link CameraFunction} for String + * @param <T> the function input type + * @param function a wrapper function for String * @return property wrapper around a String function */ - public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> iconColor(CameraFunction<Z, String> function) { + public static <T> PropertyValue<Function<T, String>> iconColor(Function<T, String> function) { return new PaintPropertyValue<>("icon-color", function); } @@ -518,11 +518,11 @@ public class PropertyFactory { /** * The color of the icon's halo. Icon halos can only be used with SDF icons. * - * @param <Z> the zoom parameter type - * @param function a wrapper {@link CameraFunction} for String + * @param <T> the function input type + * @param function a wrapper function for String * @return property wrapper around a String function */ - public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> iconHaloColor(CameraFunction<Z, String> function) { + public static <T> PropertyValue<Function<T, String>> iconHaloColor(Function<T, String> function) { return new PaintPropertyValue<>("icon-halo-color", function); } @@ -540,11 +540,11 @@ public class PropertyFactory { /** * Distance of halo to the icon outline. * - * @param <Z> the zoom parameter type - * @param function a wrapper {@link CameraFunction} for Float + * @param <T> the function input type + * @param function a wrapper function for Float * @return property wrapper around a Float function */ - public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> iconHaloWidth(CameraFunction<Z, Float> function) { + public static <T> PropertyValue<Function<T, Float>> iconHaloWidth(Function<T, Float> function) { return new PaintPropertyValue<>("icon-halo-width", function); } @@ -562,11 +562,11 @@ public class PropertyFactory { /** * Fade out the halo towards the outside. * - * @param <Z> the zoom parameter type - * @param function a wrapper {@link CameraFunction} for Float + * @param <T> the function input type + * @param function a wrapper function for Float * @return property wrapper around a Float function */ - public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> iconHaloBlur(CameraFunction<Z, Float> function) { + public static <T> PropertyValue<Function<T, Float>> iconHaloBlur(Function<T, Float> function) { return new PaintPropertyValue<>("icon-halo-blur", function); } @@ -628,11 +628,11 @@ public class PropertyFactory { /** * The opacity at which the text will be drawn. * - * @param <Z> the zoom parameter type - * @param function a wrapper {@link CameraFunction} for Float + * @param <T> the function input type + * @param function a wrapper function for Float * @return property wrapper around a Float function */ - public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> textOpacity(CameraFunction<Z, Float> function) { + public static <T> PropertyValue<Function<T, Float>> textOpacity(Function<T, Float> function) { return new PaintPropertyValue<>("text-opacity", function); } @@ -660,11 +660,11 @@ public class PropertyFactory { /** * The color with which the text will be drawn. * - * @param <Z> the zoom parameter type - * @param function a wrapper {@link CameraFunction} for String + * @param <T> the function input type + * @param function a wrapper function for String * @return property wrapper around a String function */ - public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> textColor(CameraFunction<Z, String> function) { + public static <T> PropertyValue<Function<T, String>> textColor(Function<T, String> function) { return new PaintPropertyValue<>("text-color", function); } @@ -692,11 +692,11 @@ public class PropertyFactory { /** * The color of the text's halo, which helps it stand out from backgrounds. * - * @param <Z> the zoom parameter type - * @param function a wrapper {@link CameraFunction} for String + * @param <T> the function input type + * @param function a wrapper function for String * @return property wrapper around a String function */ - public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> textHaloColor(CameraFunction<Z, String> function) { + public static <T> PropertyValue<Function<T, String>> textHaloColor(Function<T, String> function) { return new PaintPropertyValue<>("text-halo-color", function); } @@ -714,11 +714,11 @@ public class PropertyFactory { /** * Distance of halo to the font outline. Max text halo width is 1/4 of the font-size. * - * @param <Z> the zoom parameter type - * @param function a wrapper {@link CameraFunction} for Float + * @param <T> the function input type + * @param function a wrapper function for Float * @return property wrapper around a Float function */ - public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> textHaloWidth(CameraFunction<Z, Float> function) { + public static <T> PropertyValue<Function<T, Float>> textHaloWidth(Function<T, Float> function) { return new PaintPropertyValue<>("text-halo-width", function); } @@ -736,11 +736,11 @@ public class PropertyFactory { /** * The halo's fadeout distance towards the outside. * - * @param <Z> the zoom parameter type - * @param function a wrapper {@link CameraFunction} for Float + * @param <T> the function input type + * @param function a wrapper function for Float * @return property wrapper around a Float function */ - public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> textHaloBlur(CameraFunction<Z, Float> function) { + public static <T> PropertyValue<Function<T, Float>> textHaloBlur(Function<T, Float> function) { return new PaintPropertyValue<>("text-halo-blur", function); } @@ -1742,7 +1742,7 @@ public class PropertyFactory { } /** - * Value to use for a text label. Feature properties are specified using tokens like {field_name}. (Token replacement is only supported for literal `textField` values--not for property functions.) + * Value to use for a text label. Feature properties are specified using tokens like {field_name}. (Token replacement is only supported for literal {@link PropertyFactory#textField} values--not for property functions.) * * @param value a String value * @return property wrapper around String @@ -1754,7 +1754,7 @@ public class PropertyFactory { /** - * Value to use for a text label. Feature properties are specified using tokens like {field_name}. (Token replacement is only supported for literal `textField` values--not for property functions.) + * Value to use for a text label. Feature properties are specified using tokens like {field_name}. (Token replacement is only supported for literal {@link PropertyFactory#textField} values--not for property functions.) * * @param <T> the function input type * @param function a wrapper function for String diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java index 74b9555183..3b3bc9c8b5 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java @@ -1598,6 +1598,112 @@ public class SymbolLayerTest extends BaseStyleTest { } @Test + public void testIconOpacityAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-opacity"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconOpacity(property("FeaturePropertyA", Stops.<Float>identity())) + ); + + // Verify + assertNotNull(layer.getIconOpacity()); + assertNotNull(layer.getIconOpacity().getFunction()); + assertEquals(SourceFunction.class, layer.getIconOpacity().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconOpacity().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getIconOpacity().getFunction().getStops().getClass()); + } + + @Test + public void testIconOpacityAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-opacity"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconOpacity( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, iconOpacity(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getIconOpacity()); + assertNotNull(layer.getIconOpacity().getFunction()); + assertEquals(SourceFunction.class, layer.getIconOpacity().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconOpacity().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getIconOpacity().getFunction().getStops().getClass()); + } + + @Test + public void testIconOpacityAsCategoricalSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-opacity"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconOpacity( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, iconOpacity(0.3f)) + ) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getIconOpacity()); + assertNotNull(layer.getIconOpacity().getFunction()); + assertEquals(SourceFunction.class, layer.getIconOpacity().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconOpacity().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getIconOpacity().getFunction().getStops().getClass()); + assertEquals(0.3f, ((SourceFunction) layer.getIconOpacity().getFunction()).getDefaultValue()); + } + + @Test + public void testIconOpacityAsCompositeFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-opacity"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconOpacity( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, iconOpacity(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getIconOpacity()); + assertNotNull(layer.getIconOpacity().getFunction()); + assertEquals(CompositeFunction.class, layer.getIconOpacity().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getIconOpacity().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getIconOpacity().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getIconOpacity().getFunction().getStops()).size()); + + ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops = + (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getIconOpacity().getFunction().getStops(); + Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next(); + assertEquals(0f, stop.in.zoom, 0.001); + assertEquals(0.3f, stop.in.value, 0.001f); + assertEquals(0.9f, stop.out, 0.001f); + } + + @Test public void testIconColorAsConstant() { checkViewIsDisplayed(R.id.mapView); Timber.i("icon-color"); @@ -1635,6 +1741,77 @@ public class SymbolLayerTest extends BaseStyleTest { } @Test + public void testIconColorAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconColor(property("FeaturePropertyA", Stops.<String>identity())) + ); + + // Verify + assertNotNull(layer.getIconColor()); + assertNotNull(layer.getIconColor().getFunction()); + assertEquals(SourceFunction.class, layer.getIconColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconColor().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getIconColor().getFunction().getStops().getClass()); + } + + @Test + public void testIconColorAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconColor( + property( + "FeaturePropertyA", + exponential( + stop(Color.RED, iconColor(Color.RED)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getIconColor()); + assertNotNull(layer.getIconColor().getFunction()); + assertEquals(SourceFunction.class, layer.getIconColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconColor().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getIconColor().getFunction().getStops().getClass()); + } + + @Test + public void testIconColorAsCategoricalSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconColor( + property( + "FeaturePropertyA", + categorical( + stop("valueA", iconColor(Color.RED)) + ) + ) + ) + ); + + // Verify + assertNotNull(layer.getIconColor()); + assertNotNull(layer.getIconColor().getFunction()); + assertEquals(SourceFunction.class, layer.getIconColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconColor().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getIconColor().getFunction().getStops().getClass()); + } + + @Test public void testIconColorAsIntConstant() { checkViewIsDisplayed(R.id.mapView); Timber.i("icon-color"); @@ -1683,6 +1860,77 @@ public class SymbolLayerTest extends BaseStyleTest { } @Test + public void testIconHaloColorAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloColor(property("FeaturePropertyA", Stops.<String>identity())) + ); + + // Verify + assertNotNull(layer.getIconHaloColor()); + assertNotNull(layer.getIconHaloColor().getFunction()); + assertEquals(SourceFunction.class, layer.getIconHaloColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloColor().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getIconHaloColor().getFunction().getStops().getClass()); + } + + @Test + public void testIconHaloColorAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloColor( + property( + "FeaturePropertyA", + exponential( + stop(Color.RED, iconHaloColor(Color.RED)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getIconHaloColor()); + assertNotNull(layer.getIconHaloColor().getFunction()); + assertEquals(SourceFunction.class, layer.getIconHaloColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloColor().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getIconHaloColor().getFunction().getStops().getClass()); + } + + @Test + public void testIconHaloColorAsCategoricalSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloColor( + property( + "FeaturePropertyA", + categorical( + stop("valueA", iconHaloColor(Color.RED)) + ) + ) + ) + ); + + // Verify + assertNotNull(layer.getIconHaloColor()); + assertNotNull(layer.getIconHaloColor().getFunction()); + assertEquals(SourceFunction.class, layer.getIconHaloColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloColor().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getIconHaloColor().getFunction().getStops().getClass()); + } + + @Test public void testIconHaloColorAsIntConstant() { checkViewIsDisplayed(R.id.mapView); Timber.i("icon-halo-color"); @@ -1731,6 +1979,112 @@ public class SymbolLayerTest extends BaseStyleTest { } @Test + public void testIconHaloWidthAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-width"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloWidth(property("FeaturePropertyA", Stops.<Float>identity())) + ); + + // Verify + assertNotNull(layer.getIconHaloWidth()); + assertNotNull(layer.getIconHaloWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getIconHaloWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloWidth().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getIconHaloWidth().getFunction().getStops().getClass()); + } + + @Test + public void testIconHaloWidthAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-width"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloWidth( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, iconHaloWidth(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getIconHaloWidth()); + assertNotNull(layer.getIconHaloWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getIconHaloWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloWidth().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getIconHaloWidth().getFunction().getStops().getClass()); + } + + @Test + public void testIconHaloWidthAsCategoricalSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-width"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloWidth( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, iconHaloWidth(0.3f)) + ) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getIconHaloWidth()); + assertNotNull(layer.getIconHaloWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getIconHaloWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloWidth().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getIconHaloWidth().getFunction().getStops().getClass()); + assertEquals(0.3f, ((SourceFunction) layer.getIconHaloWidth().getFunction()).getDefaultValue()); + } + + @Test + public void testIconHaloWidthAsCompositeFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-width"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloWidth( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, iconHaloWidth(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getIconHaloWidth()); + assertNotNull(layer.getIconHaloWidth().getFunction()); + assertEquals(CompositeFunction.class, layer.getIconHaloWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getIconHaloWidth().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getIconHaloWidth().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getIconHaloWidth().getFunction().getStops()).size()); + + ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops = + (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getIconHaloWidth().getFunction().getStops(); + Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next(); + assertEquals(0f, stop.in.zoom, 0.001); + assertEquals(0.3f, stop.in.value, 0.001f); + assertEquals(0.9f, stop.out, 0.001f); + } + + @Test public void testIconHaloBlurAsConstant() { checkViewIsDisplayed(R.id.mapView); Timber.i("icon-halo-blur"); @@ -1768,6 +2122,112 @@ public class SymbolLayerTest extends BaseStyleTest { } @Test + public void testIconHaloBlurAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-blur"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloBlur(property("FeaturePropertyA", Stops.<Float>identity())) + ); + + // Verify + assertNotNull(layer.getIconHaloBlur()); + assertNotNull(layer.getIconHaloBlur().getFunction()); + assertEquals(SourceFunction.class, layer.getIconHaloBlur().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloBlur().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getIconHaloBlur().getFunction().getStops().getClass()); + } + + @Test + public void testIconHaloBlurAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-blur"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloBlur( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, iconHaloBlur(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getIconHaloBlur()); + assertNotNull(layer.getIconHaloBlur().getFunction()); + assertEquals(SourceFunction.class, layer.getIconHaloBlur().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloBlur().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getIconHaloBlur().getFunction().getStops().getClass()); + } + + @Test + public void testIconHaloBlurAsCategoricalSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-blur"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloBlur( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, iconHaloBlur(0.3f)) + ) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getIconHaloBlur()); + assertNotNull(layer.getIconHaloBlur().getFunction()); + assertEquals(SourceFunction.class, layer.getIconHaloBlur().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloBlur().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getIconHaloBlur().getFunction().getStops().getClass()); + assertEquals(0.3f, ((SourceFunction) layer.getIconHaloBlur().getFunction()).getDefaultValue()); + } + + @Test + public void testIconHaloBlurAsCompositeFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-blur"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloBlur( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, iconHaloBlur(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getIconHaloBlur()); + assertNotNull(layer.getIconHaloBlur().getFunction()); + assertEquals(CompositeFunction.class, layer.getIconHaloBlur().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getIconHaloBlur().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getIconHaloBlur().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getIconHaloBlur().getFunction().getStops()).size()); + + ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops = + (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getIconHaloBlur().getFunction().getStops(); + Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next(); + assertEquals(0f, stop.in.zoom, 0.001); + assertEquals(0.3f, stop.in.value, 0.001f); + assertEquals(0.9f, stop.out, 0.001f); + } + + @Test public void testIconTranslateAsConstant() { checkViewIsDisplayed(R.id.mapView); Timber.i("icon-translate"); @@ -1878,6 +2338,112 @@ public class SymbolLayerTest extends BaseStyleTest { } @Test + public void testTextOpacityAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-opacity"); + assertNotNull(layer); + + // Set + layer.setProperties( + textOpacity(property("FeaturePropertyA", Stops.<Float>identity())) + ); + + // Verify + assertNotNull(layer.getTextOpacity()); + assertNotNull(layer.getTextOpacity().getFunction()); + assertEquals(SourceFunction.class, layer.getTextOpacity().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextOpacity().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getTextOpacity().getFunction().getStops().getClass()); + } + + @Test + public void testTextOpacityAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-opacity"); + assertNotNull(layer); + + // Set + layer.setProperties( + textOpacity( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, textOpacity(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getTextOpacity()); + assertNotNull(layer.getTextOpacity().getFunction()); + assertEquals(SourceFunction.class, layer.getTextOpacity().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextOpacity().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextOpacity().getFunction().getStops().getClass()); + } + + @Test + public void testTextOpacityAsCategoricalSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-opacity"); + assertNotNull(layer); + + // Set + layer.setProperties( + textOpacity( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, textOpacity(0.3f)) + ) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getTextOpacity()); + assertNotNull(layer.getTextOpacity().getFunction()); + assertEquals(SourceFunction.class, layer.getTextOpacity().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextOpacity().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getTextOpacity().getFunction().getStops().getClass()); + assertEquals(0.3f, ((SourceFunction) layer.getTextOpacity().getFunction()).getDefaultValue()); + } + + @Test + public void testTextOpacityAsCompositeFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-opacity"); + assertNotNull(layer); + + // Set + layer.setProperties( + textOpacity( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, textOpacity(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getTextOpacity()); + assertNotNull(layer.getTextOpacity().getFunction()); + assertEquals(CompositeFunction.class, layer.getTextOpacity().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextOpacity().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextOpacity().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getTextOpacity().getFunction().getStops()).size()); + + ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops = + (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getTextOpacity().getFunction().getStops(); + Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next(); + assertEquals(0f, stop.in.zoom, 0.001); + assertEquals(0.3f, stop.in.value, 0.001f); + assertEquals(0.9f, stop.out, 0.001f); + } + + @Test public void testTextColorAsConstant() { checkViewIsDisplayed(R.id.mapView); Timber.i("text-color"); @@ -1915,6 +2481,77 @@ public class SymbolLayerTest extends BaseStyleTest { } @Test + public void testTextColorAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + textColor(property("FeaturePropertyA", Stops.<String>identity())) + ); + + // Verify + assertNotNull(layer.getTextColor()); + assertNotNull(layer.getTextColor().getFunction()); + assertEquals(SourceFunction.class, layer.getTextColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextColor().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getTextColor().getFunction().getStops().getClass()); + } + + @Test + public void testTextColorAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + textColor( + property( + "FeaturePropertyA", + exponential( + stop(Color.RED, textColor(Color.RED)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getTextColor()); + assertNotNull(layer.getTextColor().getFunction()); + assertEquals(SourceFunction.class, layer.getTextColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextColor().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextColor().getFunction().getStops().getClass()); + } + + @Test + public void testTextColorAsCategoricalSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + textColor( + property( + "FeaturePropertyA", + categorical( + stop("valueA", textColor(Color.RED)) + ) + ) + ) + ); + + // Verify + assertNotNull(layer.getTextColor()); + assertNotNull(layer.getTextColor().getFunction()); + assertEquals(SourceFunction.class, layer.getTextColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextColor().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getTextColor().getFunction().getStops().getClass()); + } + + @Test public void testTextColorAsIntConstant() { checkViewIsDisplayed(R.id.mapView); Timber.i("text-color"); @@ -1963,6 +2600,77 @@ public class SymbolLayerTest extends BaseStyleTest { } @Test + public void testTextHaloColorAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloColor(property("FeaturePropertyA", Stops.<String>identity())) + ); + + // Verify + assertNotNull(layer.getTextHaloColor()); + assertNotNull(layer.getTextHaloColor().getFunction()); + assertEquals(SourceFunction.class, layer.getTextHaloColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloColor().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getTextHaloColor().getFunction().getStops().getClass()); + } + + @Test + public void testTextHaloColorAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloColor( + property( + "FeaturePropertyA", + exponential( + stop(Color.RED, textHaloColor(Color.RED)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getTextHaloColor()); + assertNotNull(layer.getTextHaloColor().getFunction()); + assertEquals(SourceFunction.class, layer.getTextHaloColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloColor().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextHaloColor().getFunction().getStops().getClass()); + } + + @Test + public void testTextHaloColorAsCategoricalSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloColor( + property( + "FeaturePropertyA", + categorical( + stop("valueA", textHaloColor(Color.RED)) + ) + ) + ) + ); + + // Verify + assertNotNull(layer.getTextHaloColor()); + assertNotNull(layer.getTextHaloColor().getFunction()); + assertEquals(SourceFunction.class, layer.getTextHaloColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloColor().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getTextHaloColor().getFunction().getStops().getClass()); + } + + @Test public void testTextHaloColorAsIntConstant() { checkViewIsDisplayed(R.id.mapView); Timber.i("text-halo-color"); @@ -2011,6 +2719,112 @@ public class SymbolLayerTest extends BaseStyleTest { } @Test + public void testTextHaloWidthAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-width"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloWidth(property("FeaturePropertyA", Stops.<Float>identity())) + ); + + // Verify + assertNotNull(layer.getTextHaloWidth()); + assertNotNull(layer.getTextHaloWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getTextHaloWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloWidth().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getTextHaloWidth().getFunction().getStops().getClass()); + } + + @Test + public void testTextHaloWidthAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-width"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloWidth( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, textHaloWidth(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getTextHaloWidth()); + assertNotNull(layer.getTextHaloWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getTextHaloWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloWidth().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextHaloWidth().getFunction().getStops().getClass()); + } + + @Test + public void testTextHaloWidthAsCategoricalSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-width"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloWidth( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, textHaloWidth(0.3f)) + ) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getTextHaloWidth()); + assertNotNull(layer.getTextHaloWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getTextHaloWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloWidth().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getTextHaloWidth().getFunction().getStops().getClass()); + assertEquals(0.3f, ((SourceFunction) layer.getTextHaloWidth().getFunction()).getDefaultValue()); + } + + @Test + public void testTextHaloWidthAsCompositeFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-width"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloWidth( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, textHaloWidth(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getTextHaloWidth()); + assertNotNull(layer.getTextHaloWidth().getFunction()); + assertEquals(CompositeFunction.class, layer.getTextHaloWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextHaloWidth().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextHaloWidth().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getTextHaloWidth().getFunction().getStops()).size()); + + ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops = + (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getTextHaloWidth().getFunction().getStops(); + Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next(); + assertEquals(0f, stop.in.zoom, 0.001); + assertEquals(0.3f, stop.in.value, 0.001f); + assertEquals(0.9f, stop.out, 0.001f); + } + + @Test public void testTextHaloBlurAsConstant() { checkViewIsDisplayed(R.id.mapView); Timber.i("text-halo-blur"); @@ -2048,6 +2862,112 @@ public class SymbolLayerTest extends BaseStyleTest { } @Test + public void testTextHaloBlurAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-blur"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloBlur(property("FeaturePropertyA", Stops.<Float>identity())) + ); + + // Verify + assertNotNull(layer.getTextHaloBlur()); + assertNotNull(layer.getTextHaloBlur().getFunction()); + assertEquals(SourceFunction.class, layer.getTextHaloBlur().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloBlur().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getTextHaloBlur().getFunction().getStops().getClass()); + } + + @Test + public void testTextHaloBlurAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-blur"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloBlur( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, textHaloBlur(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getTextHaloBlur()); + assertNotNull(layer.getTextHaloBlur().getFunction()); + assertEquals(SourceFunction.class, layer.getTextHaloBlur().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloBlur().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextHaloBlur().getFunction().getStops().getClass()); + } + + @Test + public void testTextHaloBlurAsCategoricalSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-blur"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloBlur( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, textHaloBlur(0.3f)) + ) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getTextHaloBlur()); + assertNotNull(layer.getTextHaloBlur().getFunction()); + assertEquals(SourceFunction.class, layer.getTextHaloBlur().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloBlur().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getTextHaloBlur().getFunction().getStops().getClass()); + assertEquals(0.3f, ((SourceFunction) layer.getTextHaloBlur().getFunction()).getDefaultValue()); + } + + @Test + public void testTextHaloBlurAsCompositeFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-blur"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloBlur( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, textHaloBlur(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getTextHaloBlur()); + assertNotNull(layer.getTextHaloBlur().getFunction()); + assertEquals(CompositeFunction.class, layer.getTextHaloBlur().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextHaloBlur().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextHaloBlur().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getTextHaloBlur().getFunction().getStops()).size()); + + ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops = + (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getTextHaloBlur().getFunction().getStops(); + Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next(); + assertEquals(0f, stop.in.zoom, 0.001); + assertEquals(0.3f, stop.in.value, 0.001f); + assertEquals(0.9f, stop.out, 0.001f); + } + + @Test public void testTextTranslateAsConstant() { checkViewIsDisplayed(R.id.mapView); Timber.i("text-translate"); diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h index d2892f9627..a4850a4e18 100644 --- a/platform/darwin/src/MGLSymbolStyleLayer.h +++ b/platform/darwin/src/MGLSymbolStyleLayer.h @@ -1204,6 +1204,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *iconColor; #else @@ -1224,6 +1233,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *iconColor; #endif @@ -1246,6 +1264,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconHaloBlur; @@ -1267,6 +1294,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *iconHaloColor; #else @@ -1287,6 +1323,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *iconHaloColor; #endif @@ -1309,6 +1354,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconHaloWidth; @@ -1328,6 +1382,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *iconOpacity; @@ -1426,6 +1489,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *textColor; #else @@ -1445,6 +1517,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *textColor; #endif @@ -1467,6 +1548,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textHaloBlur; @@ -1487,6 +1577,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue<UIColor *> *textHaloColor; #else @@ -1506,6 +1605,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue<NSColor *> *textHaloColor; #endif @@ -1529,6 +1637,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textHaloWidth; @@ -1548,6 +1665,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *textOpacity; diff --git a/platform/darwin/src/MGLSymbolStyleLayer.mm b/platform/darwin/src/MGLSymbolStyleLayer.mm index 0b065cd7b9..52648e7a05 100644 --- a/platform/darwin/src/MGLSymbolStyleLayer.mm +++ b/platform/darwin/src/MGLSymbolStyleLayer.mm @@ -890,7 +890,7 @@ namespace mbgl { - (void)setIconColor:(MGLStyleValue<MGLColor *> *)iconColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toInterpolatablePropertyValue(iconColor); + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(iconColor); self.rawLayer->setIconColor(mbglValue); } @@ -899,15 +899,15 @@ namespace mbgl { auto propertyValue = self.rawLayer->getIconColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(self.rawLayer->getDefaultIconColor()); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconColor()); } - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue); } - (void)setIconHaloBlur:(MGLStyleValue<NSNumber *> *)iconHaloBlur { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(iconHaloBlur); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(iconHaloBlur); self.rawLayer->setIconHaloBlur(mbglValue); } @@ -916,15 +916,15 @@ namespace mbgl { auto propertyValue = self.rawLayer->getIconHaloBlur(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultIconHaloBlur()); + return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconHaloBlur()); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); } - (void)setIconHaloColor:(MGLStyleValue<MGLColor *> *)iconHaloColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toInterpolatablePropertyValue(iconHaloColor); + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(iconHaloColor); self.rawLayer->setIconHaloColor(mbglValue); } @@ -933,15 +933,15 @@ namespace mbgl { auto propertyValue = self.rawLayer->getIconHaloColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(self.rawLayer->getDefaultIconHaloColor()); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconHaloColor()); } - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue); } - (void)setIconHaloWidth:(MGLStyleValue<NSNumber *> *)iconHaloWidth { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(iconHaloWidth); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(iconHaloWidth); self.rawLayer->setIconHaloWidth(mbglValue); } @@ -950,15 +950,15 @@ namespace mbgl { auto propertyValue = self.rawLayer->getIconHaloWidth(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultIconHaloWidth()); + return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconHaloWidth()); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); } - (void)setIconOpacity:(MGLStyleValue<NSNumber *> *)iconOpacity { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(iconOpacity); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(iconOpacity); self.rawLayer->setIconOpacity(mbglValue); } @@ -967,9 +967,9 @@ namespace mbgl { auto propertyValue = self.rawLayer->getIconOpacity(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultIconOpacity()); + return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconOpacity()); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); } - (void)setIconTranslation:(MGLStyleValue<NSValue *> *)iconTranslation { @@ -1023,7 +1023,7 @@ namespace mbgl { - (void)setTextColor:(MGLStyleValue<MGLColor *> *)textColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toInterpolatablePropertyValue(textColor); + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(textColor); self.rawLayer->setTextColor(mbglValue); } @@ -1032,15 +1032,15 @@ namespace mbgl { auto propertyValue = self.rawLayer->getTextColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(self.rawLayer->getDefaultTextColor()); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextColor()); } - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue); } - (void)setTextHaloBlur:(MGLStyleValue<NSNumber *> *)textHaloBlur { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(textHaloBlur); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(textHaloBlur); self.rawLayer->setTextHaloBlur(mbglValue); } @@ -1049,15 +1049,15 @@ namespace mbgl { auto propertyValue = self.rawLayer->getTextHaloBlur(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextHaloBlur()); + return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextHaloBlur()); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); } - (void)setTextHaloColor:(MGLStyleValue<MGLColor *> *)textHaloColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toInterpolatablePropertyValue(textHaloColor); + auto mbglValue = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenPropertyValue(textHaloColor); self.rawLayer->setTextHaloColor(mbglValue); } @@ -1066,15 +1066,15 @@ namespace mbgl { auto propertyValue = self.rawLayer->getTextHaloColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(self.rawLayer->getDefaultTextHaloColor()); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextHaloColor()); } - return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toDataDrivenStyleValue(propertyValue); } - (void)setTextHaloWidth:(MGLStyleValue<NSNumber *> *)textHaloWidth { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(textHaloWidth); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(textHaloWidth); self.rawLayer->setTextHaloWidth(mbglValue); } @@ -1083,15 +1083,15 @@ namespace mbgl { auto propertyValue = self.rawLayer->getTextHaloWidth(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextHaloWidth()); + return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextHaloWidth()); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); } - (void)setTextOpacity:(MGLStyleValue<NSNumber *> *)textOpacity { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(textOpacity); + auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(textOpacity); self.rawLayer->setTextOpacity(mbglValue); } @@ -1100,9 +1100,9 @@ namespace mbgl { auto propertyValue = self.rawLayer->getTextOpacity(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextOpacity()); + return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextOpacity()); } - return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue); + return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue); } - (void)setTextTranslation:(MGLStyleValue<NSValue *> *)textTranslation { diff --git a/platform/darwin/test/MGLSymbolStyleLayerTests.mm b/platform/darwin/test/MGLSymbolStyleLayerTests.mm index ce4aa11cee..1d599f5aca 100644 --- a/platform/darwin/test/MGLSymbolStyleLayerTests.mm +++ b/platform/darwin/test/MGLSymbolStyleLayerTests.mm @@ -1421,7 +1421,7 @@ MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; layer.iconColor = constantStyleValue; - mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; + mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getIconColor(), propertyValue, @"Setting iconColor to a constant value should update icon-color."); XCTAssertEqualObjects(layer.iconColor, constantStyleValue, @@ -1438,6 +1438,29 @@ XCTAssertEqualObjects(layer.iconColor, functionStyleValue, @"iconColor should round-trip camera functions."); + functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.iconColor = functionStyleValue; + + mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getIconColor(), propertyValue, + @"Setting iconColor to a source function should update icon-color."); + XCTAssertEqualObjects(layer.iconColor, functionStyleValue, + @"iconColor should round-trip source functions."); + + functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.iconColor = functionStyleValue; + + std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} }; + mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getIconColor(), propertyValue, + @"Setting iconColor to a composite function should update icon-color."); + XCTAssertEqualObjects(layer.iconColor, functionStyleValue, + @"iconColor should round-trip composite functions."); layer.iconColor = nil; @@ -1445,11 +1468,6 @@ @"Unsetting iconColor should return icon-color to the default value."); XCTAssertEqualObjects(layer.iconColor, defaultStyleValue, @"iconColor should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // icon-halo-blur @@ -1460,7 +1478,7 @@ MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; layer.iconHaloBlur = constantStyleValue; - mbgl::style::PropertyValue<float> propertyValue = { 0xff }; + mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue, @"Setting iconHaloBlur to a constant value should update icon-halo-blur."); XCTAssertEqualObjects(layer.iconHaloBlur, constantStyleValue, @@ -1477,6 +1495,29 @@ XCTAssertEqualObjects(layer.iconHaloBlur, functionStyleValue, @"iconHaloBlur should round-trip camera functions."); + functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.iconHaloBlur = functionStyleValue; + + mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue, + @"Setting iconHaloBlur to a source function should update icon-halo-blur."); + XCTAssertEqualObjects(layer.iconHaloBlur, functionStyleValue, + @"iconHaloBlur should round-trip source functions."); + + functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.iconHaloBlur = functionStyleValue; + + std::map<float, float> innerStops { {18, 0xff} }; + mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue, + @"Setting iconHaloBlur to a composite function should update icon-halo-blur."); + XCTAssertEqualObjects(layer.iconHaloBlur, functionStyleValue, + @"iconHaloBlur should round-trip composite functions."); layer.iconHaloBlur = nil; @@ -1484,11 +1525,6 @@ @"Unsetting iconHaloBlur should return icon-halo-blur to the default value."); XCTAssertEqualObjects(layer.iconHaloBlur, defaultStyleValue, @"iconHaloBlur should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconHaloBlur = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconHaloBlur = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // icon-halo-color @@ -1499,7 +1535,7 @@ MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; layer.iconHaloColor = constantStyleValue; - mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; + mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue, @"Setting iconHaloColor to a constant value should update icon-halo-color."); XCTAssertEqualObjects(layer.iconHaloColor, constantStyleValue, @@ -1516,6 +1552,29 @@ XCTAssertEqualObjects(layer.iconHaloColor, functionStyleValue, @"iconHaloColor should round-trip camera functions."); + functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.iconHaloColor = functionStyleValue; + + mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue, + @"Setting iconHaloColor to a source function should update icon-halo-color."); + XCTAssertEqualObjects(layer.iconHaloColor, functionStyleValue, + @"iconHaloColor should round-trip source functions."); + + functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.iconHaloColor = functionStyleValue; + + std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} }; + mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue, + @"Setting iconHaloColor to a composite function should update icon-halo-color."); + XCTAssertEqualObjects(layer.iconHaloColor, functionStyleValue, + @"iconHaloColor should round-trip composite functions."); layer.iconHaloColor = nil; @@ -1523,11 +1582,6 @@ @"Unsetting iconHaloColor should return icon-halo-color to the default value."); XCTAssertEqualObjects(layer.iconHaloColor, defaultStyleValue, @"iconHaloColor should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconHaloColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconHaloColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // icon-halo-width @@ -1538,7 +1592,7 @@ MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; layer.iconHaloWidth = constantStyleValue; - mbgl::style::PropertyValue<float> propertyValue = { 0xff }; + mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue, @"Setting iconHaloWidth to a constant value should update icon-halo-width."); XCTAssertEqualObjects(layer.iconHaloWidth, constantStyleValue, @@ -1555,6 +1609,29 @@ XCTAssertEqualObjects(layer.iconHaloWidth, functionStyleValue, @"iconHaloWidth should round-trip camera functions."); + functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.iconHaloWidth = functionStyleValue; + + mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue, + @"Setting iconHaloWidth to a source function should update icon-halo-width."); + XCTAssertEqualObjects(layer.iconHaloWidth, functionStyleValue, + @"iconHaloWidth should round-trip source functions."); + + functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.iconHaloWidth = functionStyleValue; + + std::map<float, float> innerStops { {18, 0xff} }; + mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue, + @"Setting iconHaloWidth to a composite function should update icon-halo-width."); + XCTAssertEqualObjects(layer.iconHaloWidth, functionStyleValue, + @"iconHaloWidth should round-trip composite functions."); layer.iconHaloWidth = nil; @@ -1562,11 +1639,6 @@ @"Unsetting iconHaloWidth should return icon-halo-width to the default value."); XCTAssertEqualObjects(layer.iconHaloWidth, defaultStyleValue, @"iconHaloWidth should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconHaloWidth = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconHaloWidth = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // icon-opacity @@ -1577,7 +1649,7 @@ MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; layer.iconOpacity = constantStyleValue; - mbgl::style::PropertyValue<float> propertyValue = { 0xff }; + mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue, @"Setting iconOpacity to a constant value should update icon-opacity."); XCTAssertEqualObjects(layer.iconOpacity, constantStyleValue, @@ -1594,6 +1666,29 @@ XCTAssertEqualObjects(layer.iconOpacity, functionStyleValue, @"iconOpacity should round-trip camera functions."); + functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.iconOpacity = functionStyleValue; + + mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue, + @"Setting iconOpacity to a source function should update icon-opacity."); + XCTAssertEqualObjects(layer.iconOpacity, functionStyleValue, + @"iconOpacity should round-trip source functions."); + + functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.iconOpacity = functionStyleValue; + + std::map<float, float> innerStops { {18, 0xff} }; + mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue, + @"Setting iconOpacity to a composite function should update icon-opacity."); + XCTAssertEqualObjects(layer.iconOpacity, functionStyleValue, + @"iconOpacity should round-trip composite functions."); layer.iconOpacity = nil; @@ -1601,11 +1696,6 @@ @"Unsetting iconOpacity should return icon-opacity to the default value."); XCTAssertEqualObjects(layer.iconOpacity, defaultStyleValue, @"iconOpacity should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // icon-translate @@ -1700,7 +1790,7 @@ MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; layer.textColor = constantStyleValue; - mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; + mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getTextColor(), propertyValue, @"Setting textColor to a constant value should update text-color."); XCTAssertEqualObjects(layer.textColor, constantStyleValue, @@ -1717,6 +1807,29 @@ XCTAssertEqualObjects(layer.textColor, functionStyleValue, @"textColor should round-trip camera functions."); + functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.textColor = functionStyleValue; + + mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getTextColor(), propertyValue, + @"Setting textColor to a source function should update text-color."); + XCTAssertEqualObjects(layer.textColor, functionStyleValue, + @"textColor should round-trip source functions."); + + functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.textColor = functionStyleValue; + + std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} }; + mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getTextColor(), propertyValue, + @"Setting textColor to a composite function should update text-color."); + XCTAssertEqualObjects(layer.textColor, functionStyleValue, + @"textColor should round-trip composite functions."); layer.textColor = nil; @@ -1724,11 +1837,6 @@ @"Unsetting textColor should return text-color to the default value."); XCTAssertEqualObjects(layer.textColor, defaultStyleValue, @"textColor should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // text-halo-blur @@ -1739,7 +1847,7 @@ MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; layer.textHaloBlur = constantStyleValue; - mbgl::style::PropertyValue<float> propertyValue = { 0xff }; + mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue, @"Setting textHaloBlur to a constant value should update text-halo-blur."); XCTAssertEqualObjects(layer.textHaloBlur, constantStyleValue, @@ -1756,6 +1864,29 @@ XCTAssertEqualObjects(layer.textHaloBlur, functionStyleValue, @"textHaloBlur should round-trip camera functions."); + functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.textHaloBlur = functionStyleValue; + + mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue, + @"Setting textHaloBlur to a source function should update text-halo-blur."); + XCTAssertEqualObjects(layer.textHaloBlur, functionStyleValue, + @"textHaloBlur should round-trip source functions."); + + functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.textHaloBlur = functionStyleValue; + + std::map<float, float> innerStops { {18, 0xff} }; + mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue, + @"Setting textHaloBlur to a composite function should update text-halo-blur."); + XCTAssertEqualObjects(layer.textHaloBlur, functionStyleValue, + @"textHaloBlur should round-trip composite functions."); layer.textHaloBlur = nil; @@ -1763,11 +1894,6 @@ @"Unsetting textHaloBlur should return text-halo-blur to the default value."); XCTAssertEqualObjects(layer.textHaloBlur, defaultStyleValue, @"textHaloBlur should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textHaloBlur = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textHaloBlur = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // text-halo-color @@ -1778,7 +1904,7 @@ MGLStyleValue<MGLColor *> *constantStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor redColor]]; layer.textHaloColor = constantStyleValue; - mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; + mbgl::style::DataDrivenPropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue, @"Setting textHaloColor to a constant value should update text-halo-color."); XCTAssertEqualObjects(layer.textHaloColor, constantStyleValue, @@ -1795,6 +1921,29 @@ XCTAssertEqualObjects(layer.textHaloColor, functionStyleValue, @"textHaloColor should round-trip camera functions."); + functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.textHaloColor = functionStyleValue; + + mbgl::style::ExponentialStops<mbgl::Color> exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction<mbgl::Color> { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue, + @"Setting textHaloColor to a source function should update text-halo-color."); + XCTAssertEqualObjects(layer.textHaloColor, functionStyleValue, + @"textHaloColor should round-trip source functions."); + + functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.textHaloColor = functionStyleValue; + + std::map<float, mbgl::Color> innerStops { {18, { 1, 0, 0, 1 }} }; + mbgl::style::CompositeExponentialStops<mbgl::Color> compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction<mbgl::Color> { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue, + @"Setting textHaloColor to a composite function should update text-halo-color."); + XCTAssertEqualObjects(layer.textHaloColor, functionStyleValue, + @"textHaloColor should round-trip composite functions."); layer.textHaloColor = nil; @@ -1802,11 +1951,6 @@ @"Unsetting textHaloColor should return text-halo-color to the default value."); XCTAssertEqualObjects(layer.textHaloColor, defaultStyleValue, @"textHaloColor should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textHaloColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<MGLColor *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textHaloColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // text-halo-width @@ -1817,7 +1961,7 @@ MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; layer.textHaloWidth = constantStyleValue; - mbgl::style::PropertyValue<float> propertyValue = { 0xff }; + mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue, @"Setting textHaloWidth to a constant value should update text-halo-width."); XCTAssertEqualObjects(layer.textHaloWidth, constantStyleValue, @@ -1834,6 +1978,29 @@ XCTAssertEqualObjects(layer.textHaloWidth, functionStyleValue, @"textHaloWidth should round-trip camera functions."); + functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.textHaloWidth = functionStyleValue; + + mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue, + @"Setting textHaloWidth to a source function should update text-halo-width."); + XCTAssertEqualObjects(layer.textHaloWidth, functionStyleValue, + @"textHaloWidth should round-trip source functions."); + + functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.textHaloWidth = functionStyleValue; + + std::map<float, float> innerStops { {18, 0xff} }; + mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue, + @"Setting textHaloWidth to a composite function should update text-halo-width."); + XCTAssertEqualObjects(layer.textHaloWidth, functionStyleValue, + @"textHaloWidth should round-trip composite functions."); layer.textHaloWidth = nil; @@ -1841,11 +2008,6 @@ @"Unsetting textHaloWidth should return text-halo-width to the default value."); XCTAssertEqualObjects(layer.textHaloWidth, defaultStyleValue, @"textHaloWidth should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textHaloWidth = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textHaloWidth = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // text-opacity @@ -1856,7 +2018,7 @@ MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff]; layer.textOpacity = constantStyleValue; - mbgl::style::PropertyValue<float> propertyValue = { 0xff }; + mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue, @"Setting textOpacity to a constant value should update text-opacity."); XCTAssertEqualObjects(layer.textOpacity, constantStyleValue, @@ -1873,6 +2035,29 @@ XCTAssertEqualObjects(layer.textOpacity, functionStyleValue, @"textOpacity should round-trip camera functions."); + functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.textOpacity = functionStyleValue; + + mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue, + @"Setting textOpacity to a source function should update text-opacity."); + XCTAssertEqualObjects(layer.textOpacity, functionStyleValue, + @"textOpacity should round-trip source functions."); + + functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.textOpacity = functionStyleValue; + + std::map<float, float> innerStops { {18, 0xff} }; + mbgl::style::CompositeExponentialStops<float> compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction<float> { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue, + @"Setting textOpacity to a composite function should update text-opacity."); + XCTAssertEqualObjects(layer.textOpacity, functionStyleValue, + @"textOpacity should round-trip composite functions."); layer.textOpacity = nil; @@ -1880,11 +2065,6 @@ @"Unsetting textOpacity should return text-opacity to the default value."); XCTAssertEqualObjects(layer.textOpacity, defaultStyleValue, @"textOpacity should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); - functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // text-translate diff --git a/scripts/generate-style-code.js b/scripts/generate-style-code.js index bf9bef837a..9629c8fe45 100644 --- a/scripts/generate-style-code.js +++ b/scripts/generate-style-code.js @@ -47,7 +47,20 @@ global.evaluatedType = function (property) { }; function attributeType(property, type) { - const name = property.name.replace(type + '-', '').replace('-', '_'); + const attributeNameExceptions = { + 'text-opacity': 'opacity', + 'icon-opacity': 'opacity', + 'text-color': 'fill_color', + 'icon-color': 'fill_color', + 'text-halo-color': 'halo_color', + 'icon-halo-color': 'halo_color', + 'text-halo-blur': 'halo_blur', + 'icon-halo-blur': 'halo_blur', + 'text-halo-width': 'halo_width', + 'icon-halo-width': 'halo_width' + } + const name = attributeNameExceptions[property.name] || + property.name.replace(type + '-', '').replace(/-/g, '_'); return `attributes::a_${name}${name === 'offset' ? '<1>' : ''}`; } diff --git a/src/mbgl/layout/symbol_feature.hpp b/src/mbgl/layout/symbol_feature.hpp index e55995f952..f4dc1680bc 100644 --- a/src/mbgl/layout/symbol_feature.hpp +++ b/src/mbgl/layout/symbol_feature.hpp @@ -8,14 +8,23 @@ namespace mbgl { -class SymbolFeature { +class SymbolFeature : public GeometryTileFeature { public: - FeatureType type; + SymbolFeature(std::unique_ptr<GeometryTileFeature> feature_) : + feature(std::move(feature_)), + geometry(feature->getGeometries()) // we need a mutable copy of the geometry for mergeLines() + {} + + FeatureType getType() const override { return feature->getType(); } + optional<Value> getValue(const std::string& key) const override { return feature->getValue(key); }; + std::unordered_map<std::string,Value> getProperties() const override { return feature->getProperties(); }; + optional<FeatureIdentifier> getID() const override { return feature->getID(); }; + GeometryCollection getGeometries() const override { return geometry; }; + + std::unique_ptr<GeometryTileFeature> feature; GeometryCollection geometry; optional<std::u16string> text; optional<std::string> icon; - std::array<float, 2> iconOffset; - float iconRotation; std::size_t index; }; diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp index 4f425641e7..d81783b2f6 100644 --- a/src/mbgl/layout/symbol_instance.cpp +++ b/src/mbgl/layout/symbol_instance.cpp @@ -10,7 +10,7 @@ SymbolInstance::SymbolInstance(Anchor& anchor, const GeometryCoordinates& line, const SymbolLayoutProperties::Evaluated& layout, const bool addToBuffers, const uint32_t index_, const float textBoxScale, const float textPadding, const SymbolPlacementType textPlacement, const float iconBoxScale, const float iconPadding, const SymbolPlacementType iconPlacement, - const GlyphPositions& face, const IndexedSubfeature& indexedFeature) : + const GlyphPositions& face, const IndexedSubfeature& indexedFeature, const std::size_t featureIndex_) : point(anchor.point), index(index_), hasText(shapedTextOrientations.first || shapedTextOrientations.second), @@ -18,7 +18,8 @@ SymbolInstance::SymbolInstance(Anchor& anchor, const GeometryCoordinates& line, // Create the collision features that will be used to check whether this symbol instance can be placed textCollisionFeature(line, anchor, shapedTextOrientations.second ?: shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature), - iconCollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature) { + iconCollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature), + featureIndex(featureIndex_) { // Create the quads used for rendering the icon and glyphs. if (addToBuffers) { diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp index 2dbb3bac23..532a4d30d8 100644 --- a/src/mbgl/layout/symbol_instance.hpp +++ b/src/mbgl/layout/symbol_instance.hpp @@ -17,7 +17,7 @@ public: const style::SymbolLayoutProperties::Evaluated&, const bool inside, const uint32_t index, const float textBoxScale, const float textPadding, style::SymbolPlacementType textPlacement, const float iconBoxScale, const float iconPadding, style::SymbolPlacementType iconPlacement, - const GlyphPositions& face, const IndexedSubfeature& indexedfeature); + const GlyphPositions& face, const IndexedSubfeature& indexedfeature, const std::size_t featureIndex); Point<float> point; uint32_t index; @@ -28,6 +28,7 @@ public: CollisionFeature textCollisionFeature; CollisionFeature iconCollisionFeature; WritingModeType writingModes; + std::size_t featureIndex; }; } // namespace mbgl diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index cf9e784c26..3a2c082ad8 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -87,7 +87,10 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, } for (const auto& layer : layers) { - layerPaintProperties.emplace(layer->getID(), layer->as<SymbolLayer>()->impl->paint.evaluated); + layerPaintProperties.emplace(layer->getID(), std::make_pair( + layer->as<SymbolLayer>()->impl->iconPaintProperties(), + layer->as<SymbolLayer>()->impl->textPaintProperties() + )); } // Determine and load glyph ranges @@ -96,12 +99,13 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, auto feature = sourceLayer.getFeature(i); if (!leader.filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); })) continue; + + SymbolFeature ft(std::move(feature)); - SymbolFeature ft; ft.index = i; - auto getValue = [&feature](const std::string& key) -> std::string { - auto value = feature->getValue(key); + auto getValue = [&ft](const std::string& key) -> std::string { + auto value = ft.getValue(key); if (!value) return std::string(); if (value->is<std::string>()) @@ -118,12 +122,12 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, }; if (hasText) { - std::string u8string = layout.evaluate<TextField>(zoom, *feature); + std::string u8string = layout.evaluate<TextField>(zoom, ft); if (layout.get<TextField>().isConstant()) { u8string = util::replaceTokens(u8string, getValue); } - auto textTransform = layout.evaluate<TextTransform>(zoom, *feature); + auto textTransform = layout.evaluate<TextTransform>(zoom, ft); if (textTransform == TextTransformType::Uppercase) { u8string = platform::uppercase(u8string); @@ -144,13 +148,9 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, if (hasIcon) { ft.icon = util::replaceTokens(layout.get<IconImage>(), getValue); - ft.iconOffset = layout.evaluate<IconOffset>(zoom, *feature); - ft.iconRotation = layout.evaluate<IconRotate>(zoom, *feature) * util::DEG2RAD; } if (ft.text || ft.icon) { - ft.type = feature->getType(); - ft.geometry = feature->getGeometries(); features.push_back(std::move(ft)); } } @@ -229,7 +229,8 @@ void SymbolLayout::prepare(uintptr_t tileUID, const bool textAlongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map && layout.get<SymbolPlacement>() == SymbolPlacementType::Line; - for (const auto& feature : features) { + for (auto it = features.begin(); it != features.end(); ++it) { + auto& feature = *it; if (feature.geometry.empty()) continue; std::pair<Shaping, Shaping> shapedTextOrientations; @@ -273,7 +274,9 @@ void SymbolLayout::prepare(uintptr_t tileUID, if (feature.icon) { auto image = spriteAtlas.getIcon(*feature.icon); if (image) { - shapedIcon = shapeIcon(*image, feature); + shapedIcon = shapeIcon(*image, + layout.evaluate<IconOffset>(zoom, feature), + layout.evaluate<IconRotate>(zoom, feature) * util::DEG2RAD); assert((*image).spriteImage); if ((*image).spriteImage->sdf) { sdfIcons = true; @@ -288,15 +291,17 @@ void SymbolLayout::prepare(uintptr_t tileUID, // if either shapedText or icon position is present, add the feature if (shapedTextOrientations.first || shapedIcon) { - addFeature(feature, shapedTextOrientations, shapedIcon, face); + addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, face); } + + feature.geometry.clear(); } - features.clear(); compareText.clear(); } -void SymbolLayout::addFeature(const SymbolFeature& feature, +void SymbolLayout::addFeature(const std::size_t index, + const SymbolFeature& feature, const std::pair<Shaping, Shaping>& shapedTextOrientations, const PositionedIcon& shapedIcon, const GlyphPositions& face) { @@ -345,8 +350,10 @@ void SymbolLayout::addFeature(const SymbolFeature& feature, symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon, layout, addToBuffers, symbolInstances.size(), textBoxScale, textPadding, textPlacement, iconBoxScale, iconPadding, iconPlacement, - face, indexedFeature); + face, indexedFeature, index); }; + + const auto& type = feature.getType(); if (layout.get<SymbolPlacement>() == SymbolPlacementType::Line) { auto clippedLines = util::clipLines(feature.geometry, 0, 0, util::EXTENT, util::EXTENT); @@ -368,7 +375,7 @@ void SymbolLayout::addFeature(const SymbolFeature& feature, } } } - } else if (feature.type == FeatureType::Polygon) { + } else if (type == FeatureType::Polygon) { for (const auto& polygon : classifyRings(feature.geometry)) { Polygon<double> poly; for (const auto& ring : polygon) { @@ -384,12 +391,12 @@ void SymbolLayout::addFeature(const SymbolFeature& feature, Anchor anchor(poi.x, poi.y, 0, minScale); addSymbolInstance(polygon[0], anchor); } - } else if (feature.type == FeatureType::LineString) { + } else if (type == FeatureType::LineString) { for (const auto& line : feature.geometry) { Anchor anchor(line[0].x, line[0].y, 0, minScale); addSymbolInstance(line, anchor); } - } else if (feature.type == FeatureType::Point) { + } else if (type == FeatureType::Point) { for (const auto& points : feature.geometry) { for (const auto& point : points) { Anchor anchor(point.x, point.y, 0, minScale); @@ -503,6 +510,12 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) keepUpright, iconPlacement, collisionTile.config.angle, symbolInstance.writingModes); } } + + const auto& feature = features.at(symbolInstance.featureIndex); + for (auto& pair : bucket->paintPropertyBinders) { + pair.second.first.populateVertexVectors(feature, bucket->icon.vertices.vertexSize()); + pair.second.second.populateVertexVectors(feature, bucket->text.vertices.vertexSize()); + } } if (collisionTile.config.debug) { diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp index dbfdad22d9..491d0078da 100644 --- a/src/mbgl/layout/symbol_layout.hpp +++ b/src/mbgl/layout/symbol_layout.hpp @@ -5,6 +5,7 @@ #include <mbgl/layout/symbol_feature.hpp> #include <mbgl/layout/symbol_instance.hpp> #include <mbgl/text/bidi.hpp> +#include <mbgl/style/layers/symbol_layer_impl.hpp> #include <memory> #include <map> @@ -51,10 +52,12 @@ public: State state = Pending; - std::unordered_map<std::string, style::SymbolPaintProperties::Evaluated> layerPaintProperties; + std::unordered_map<std::string, + std::pair<style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>> layerPaintProperties; private: - void addFeature(const SymbolFeature&, + void addFeature(const size_t, + const SymbolFeature&, const std::pair<Shaping, Shaping>& shapedTextOrientations, const PositionedIcon& shapedIcon, const GlyphPositions& face); diff --git a/src/mbgl/programs/attributes.hpp b/src/mbgl/programs/attributes.hpp index c4cc5dea8b..bb90f2c13c 100644 --- a/src/mbgl/programs/attributes.hpp +++ b/src/mbgl/programs/attributes.hpp @@ -64,6 +64,34 @@ struct a_color : gl::Attribute<gl::Normalized<uint8_t>, 4> { } }; +// used in the symbol sdf shader +struct a_fill_color : gl::Attribute<gl::Normalized<uint8_t>, 4> { + static auto name() { return "a_fill_color"; } + + static Value value(const Color& color) { + return {{ + gl::Normalized<uint8_t>(color.r), + gl::Normalized<uint8_t>(color.g), + gl::Normalized<uint8_t>(color.b), + gl::Normalized<uint8_t>(color.a) + }}; + } +}; + +// used in the symbol sdf shader +struct a_halo_color : gl::Attribute<gl::Normalized<uint8_t>, 4> { + static auto name() { return "a_halo_color"; } + + static Value value(const Color& color) { + return {{ + gl::Normalized<uint8_t>(color.r), + gl::Normalized<uint8_t>(color.g), + gl::Normalized<uint8_t>(color.b), + gl::Normalized<uint8_t>(color.a) + }}; + } +}; + struct a_stroke_color : gl::Attribute<gl::Normalized<uint8_t>, 4> { static auto name() { return "a_stroke_color"; } @@ -171,5 +199,23 @@ struct a_offset<1> : gl::Attribute<float, 1> { } }; +struct a_halo_width : gl::Attribute<float, 1> { + static auto name() { return "a_halo_width"; } + + static Value value(float width) { + return {{ width }}; + } +}; + +struct a_halo_blur : gl::Attribute<float, 1> { + static auto name() { return "a_halo_blur"; } + + static Value value(float blur) { + return {{ blur }}; + } +}; + + + } // namespace attributes } // namespace mbgl diff --git a/src/mbgl/programs/programs.hpp b/src/mbgl/programs/programs.hpp index dd71c2ce97..742c5a221b 100644 --- a/src/mbgl/programs/programs.hpp +++ b/src/mbgl/programs/programs.hpp @@ -40,8 +40,8 @@ public: LinePatternProgram linePattern; RasterProgram raster; SymbolIconProgram symbolIcon; - SymbolSDFProgram symbolIconSDF; - SymbolSDFProgram symbolGlyph; + SymbolSDFIconProgram symbolIconSDF; + SymbolSDFTextProgram symbolGlyph; DebugProgram debug; CollisionBoxProgram collisionBox; diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp index 3f59000b94..19fe2bc2f6 100644 --- a/src/mbgl/programs/symbol_program.cpp +++ b/src/mbgl/programs/symbol_program.cpp @@ -2,6 +2,7 @@ #include <mbgl/renderer/render_tile.hpp> #include <mbgl/map/transform_state.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> +#include <mbgl/util/enum.hpp> namespace mbgl { @@ -19,6 +20,7 @@ Values makeValues(const style::SymbolPropertyValues& values, std::array<float, 2> extrudeScale; const float scale = values.paintSize / values.sdfScale; + if (values.pitchAlignment == AlignmentType::Map) { extrudeScale.fill(tile.id.pixelsToTileUnits(1, state.getZoom()) * scale); } else { @@ -27,7 +29,7 @@ Values makeValues(const style::SymbolPropertyValues& values, pixelsToGLUnits[1] * scale * state.getCameraToCenterDistance() }}; } - + // adjust min/max zooms for variable font sies float zoomAdjust = std::log(values.paintSize / values.layoutSize) / std::log(2); @@ -35,7 +37,6 @@ Values makeValues(const style::SymbolPropertyValues& values, uniforms::u_matrix::Value{ tile.translatedMatrix(values.translate, values.translateAnchor, state) }, - uniforms::u_opacity::Value{ values.opacity }, uniforms::u_extrude_scale::Value{ extrudeScale }, uniforms::u_texsize::Value{ std::array<float, 2> {{ float(texsize.width) / 4, float(texsize.height) / 4 }} }, uniforms::u_zoom::Value{ float((state.getZoom() - zoomAdjust) * 10) }, @@ -62,84 +63,37 @@ SymbolIconProgram::uniformValues(const style::SymbolPropertyValues& values, ); } -static SymbolSDFProgram::UniformValues makeSDFValues(const style::SymbolPropertyValues& values, - const Size& texsize, - const std::array<float, 2>& pixelsToGLUnits, - const RenderTile& tile, - const TransformState& state, - float pixelRatio, - Color color, - float buffer, - float gammaAdjust) -{ - // The default gamma value has to be adjust for the current pixelratio so that we're not - // drawing blurry font on retina screens. - const float gammaBase = 0.105 * values.sdfScale / values.paintSize / pixelRatio; - const float gammaScale = (values.pitchAlignment == AlignmentType::Map - ? 1.0 / std::cos(state.getPitch()) - : 1.0) / state.getCameraToCenterDistance(); - - return makeValues<SymbolSDFProgram::UniformValues>( - values, - texsize, - pixelsToGLUnits, - tile, - state, - uniforms::u_color::Value{ color }, - uniforms::u_buffer::Value{ buffer }, - uniforms::u_gamma::Value{ (gammaBase + gammaAdjust) * gammaScale }, - uniforms::u_pitch::Value{ state.getPitch() }, - uniforms::u_bearing::Value{ -1.0f * state.getAngle() }, - uniforms::u_aspect_ratio::Value{ (state.getSize().width * 1.0f) / (state.getSize().height * 1.0f) }, - uniforms::u_pitch_with_map::Value{ values.pitchAlignment == AlignmentType::Map } - ); -} - -SymbolSDFProgram::UniformValues -SymbolSDFProgram::haloUniformValues(const style::SymbolPropertyValues& values, +template <class PaintProperties> +typename SymbolSDFProgram<PaintProperties>::UniformValues SymbolSDFProgram<PaintProperties>::uniformValues(const style::SymbolPropertyValues& values, const Size& texsize, const std::array<float, 2>& pixelsToGLUnits, const RenderTile& tile, const TransformState& state, - float pixelRatio) + const SymbolSDFPart part) { const float scale = values.paintSize / values.sdfScale; - const float sdfPx = 8.0f; - const float blurOffset = 1.19f; - const float haloOffset = 6.0f; - - return makeSDFValues( + + const float gammaScale = scale * (values.pitchAlignment == AlignmentType::Map + ? std::cos(state.getPitch()) + : 1.0) * state.getCameraToCenterDistance(); + + return makeValues<SymbolSDFProgram<PaintProperties>::UniformValues>( values, texsize, pixelsToGLUnits, tile, state, - pixelRatio, - values.haloColor, - (haloOffset - values.haloWidth / scale) / sdfPx, - values.haloBlur * blurOffset / scale / sdfPx + uniforms::u_font_scale::Value{ scale }, + uniforms::u_gamma_scale::Value{ gammaScale }, + uniforms::u_pitch::Value{ state.getPitch() }, + uniforms::u_bearing::Value{ -1.0f * state.getAngle() }, + uniforms::u_aspect_ratio::Value{ (state.getSize().width * 1.0f) / (state.getSize().height * 1.0f) }, + uniforms::u_pitch_with_map::Value{ values.pitchAlignment == AlignmentType::Map }, + uniforms::u_is_halo::Value{ part == SymbolSDFPart::Halo } ); } -SymbolSDFProgram::UniformValues -SymbolSDFProgram::foregroundUniformValues(const style::SymbolPropertyValues& values, - const Size& texsize, - const std::array<float, 2>& pixelsToGLUnits, - const RenderTile& tile, - const TransformState& state, - float pixelRatio) -{ - return makeSDFValues( - values, - texsize, - pixelsToGLUnits, - tile, - state, - pixelRatio, - values.color, - (256.0f - 64.0f) / 256.0f, - 0 - ); -} +template class SymbolSDFProgram<style::IconPaintProperties>; +template class SymbolSDFProgram<style::TextPaintProperties>; } // namespace mbgl diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index e0e90f0fa4..0537c25a2c 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -8,6 +8,7 @@ #include <mbgl/util/geometry.hpp> #include <mbgl/util/size.hpp> #include <mbgl/style/layers/symbol_layer_properties.hpp> +#include <mbgl/style/layers/symbol_layer_impl.hpp> #include <cmath> #include <array> @@ -27,9 +28,10 @@ MBGL_DEFINE_UNIFORM_SCALAR(bool, u_rotate_with_map); MBGL_DEFINE_UNIFORM_SCALAR(bool, u_pitch_with_map); MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_texture); MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_fadetexture); -MBGL_DEFINE_UNIFORM_SCALAR(float, u_buffer); -MBGL_DEFINE_UNIFORM_SCALAR(float, u_gamma); MBGL_DEFINE_UNIFORM_SCALAR(float, u_aspect_ratio); +MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_halo); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_font_scale); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_gamma_scale); } // namespace uniforms struct SymbolLayoutAttributes : gl::Attributes< @@ -75,14 +77,13 @@ class SymbolIconProgram : public Program< SymbolLayoutAttributes, gl::Uniforms< uniforms::u_matrix, - uniforms::u_opacity, uniforms::u_extrude_scale, uniforms::u_texsize, uniforms::u_zoom, uniforms::u_rotate_with_map, uniforms::u_texture, uniforms::u_fadetexture>, - style::SymbolPaintProperties> + style::IconPaintProperties> { public: using Program::Program; @@ -94,47 +95,73 @@ public: const TransformState&); }; +enum class SymbolSDFPart { + Fill = 1, + Halo = 0 +}; + +template <class PaintProperties> class SymbolSDFProgram : public Program< shaders::symbol_sdf, gl::Triangle, SymbolLayoutAttributes, gl::Uniforms< uniforms::u_matrix, - uniforms::u_opacity, uniforms::u_extrude_scale, uniforms::u_texsize, uniforms::u_zoom, uniforms::u_rotate_with_map, uniforms::u_texture, uniforms::u_fadetexture, - uniforms::u_color, - uniforms::u_buffer, - uniforms::u_gamma, + uniforms::u_font_scale, + uniforms::u_gamma_scale, uniforms::u_pitch, uniforms::u_bearing, uniforms::u_aspect_ratio, - uniforms::u_pitch_with_map>, - style::SymbolPaintProperties> + uniforms::u_pitch_with_map, + uniforms::u_is_halo>, + PaintProperties> { public: - using Program::Program; + using BaseProgram = Program<shaders::symbol_sdf, + gl::Triangle, + SymbolLayoutAttributes, + gl::Uniforms< + uniforms::u_matrix, + uniforms::u_extrude_scale, + uniforms::u_texsize, + uniforms::u_zoom, + uniforms::u_rotate_with_map, + uniforms::u_texture, + uniforms::u_fadetexture, + uniforms::u_font_scale, + uniforms::u_gamma_scale, + uniforms::u_pitch, + uniforms::u_bearing, + uniforms::u_aspect_ratio, + uniforms::u_pitch_with_map, + uniforms::u_is_halo>, + PaintProperties>; + + using UniformValues = typename BaseProgram::UniformValues; + - static UniformValues haloUniformValues(const style::SymbolPropertyValues&, - const Size& texsize, - const std::array<float, 2>& pixelsToGLUnits, - const RenderTile&, - const TransformState&, - float pixelRatio); - - static UniformValues foregroundUniformValues(const style::SymbolPropertyValues&, - const Size& texsize, - const std::array<float, 2>& pixelsToGLUnits, - const RenderTile&, - const TransformState&, - float pixelRatio); + + using BaseProgram::BaseProgram; + + static UniformValues uniformValues(const style::SymbolPropertyValues&, + const Size& texsize, + const std::array<float, 2>& pixelsToGLUnits, + const RenderTile&, + const TransformState&, + const SymbolSDFPart); }; +using SymbolSDFIconProgram = SymbolSDFProgram<style::IconPaintProperties>; +using SymbolSDFTextProgram = SymbolSDFProgram<style::TextPaintProperties>; + using SymbolLayoutVertex = SymbolLayoutAttributes::Vertex; -using SymbolAttributes = SymbolIconProgram::Attributes; +using SymbolIconAttributes = SymbolIconProgram::Attributes; +using SymbolTextAttributes = SymbolSDFTextProgram::Attributes; } // namespace mbgl diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp index 0113c15a08..48c2e7ff66 100644 --- a/src/mbgl/renderer/painter_symbol.cpp +++ b/src/mbgl/renderer/painter_symbol.cpp @@ -33,7 +33,9 @@ void Painter::renderSymbol(PaintParameters& parameters, auto draw = [&] (auto& program, auto&& uniformValues, const auto& buffers, - const SymbolPropertyValues& values_) + const SymbolPropertyValues& values_, + const auto& binders, + const auto& paintProperties) { // We clip symbols to their tile extent in still mode. const bool needsClipping = frame.mapMode == MapMode::Still; @@ -52,14 +54,15 @@ void Painter::renderSymbol(PaintParameters& parameters, *buffers.vertexBuffer, *buffers.indexBuffer, buffers.segments, - bucket.paintPropertyBinders.at(layer.getID()), - layer.impl->paint.evaluated, + binders, + paintProperties, state.getZoom() ); }; if (bucket.hasIconData()) { auto values = layer.impl->iconPropertyValues(layout); + auto paintPropertyValues = layer.impl->iconPaintProperties(); SpriteAtlas& atlas = *layer.impl->spriteAtlas; const bool iconScaled = values.paintSize != 1.0f || frame.pixelRatio != atlas.getPixelRatio() || bucket.iconsNeedLinear; @@ -69,24 +72,30 @@ void Painter::renderSymbol(PaintParameters& parameters, const Size texsize = atlas.getSize(); if (bucket.sdfIcons) { - if (values.hasHalo()) { + if (values.hasHalo) { draw(parameters.programs.symbolIconSDF, - SymbolSDFProgram::haloUniformValues(values, texsize, pixelsToGLUnits, tile, state, frame.pixelRatio), + SymbolSDFIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), bucket.icon, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).first, + paintPropertyValues); } - if (values.hasForeground()) { + if (values.hasFill) { draw(parameters.programs.symbolIconSDF, - SymbolSDFProgram::foregroundUniformValues(values, texsize, pixelsToGLUnits, tile, state, frame.pixelRatio), + SymbolSDFIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), bucket.icon, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).first, + paintPropertyValues); } } else { draw(parameters.programs.symbolIcon, SymbolIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state), bucket.icon, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).first, + paintPropertyValues); } } @@ -94,21 +103,26 @@ void Painter::renderSymbol(PaintParameters& parameters, glyphAtlas->bind(context, 0); auto values = layer.impl->textPropertyValues(layout); + auto paintPropertyValues = layer.impl->textPaintProperties(); const Size texsize = glyphAtlas->getSize(); - if (values.hasHalo()) { + if (values.hasHalo) { draw(parameters.programs.symbolGlyph, - SymbolSDFProgram::haloUniformValues(values, texsize, pixelsToGLUnits, tile, state, frame.pixelRatio), + SymbolSDFTextProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), bucket.text, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).second, + paintPropertyValues); } - if (values.hasForeground()) { + if (values.hasFill) { draw(parameters.programs.symbolGlyph, - SymbolSDFProgram::foregroundUniformValues(values, texsize, pixelsToGLUnits, tile, state, frame.pixelRatio), + SymbolSDFTextProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), bucket.text, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).second, + paintPropertyValues); } } diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp index 9d4bde9d07..fa4178dda1 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/symbol_bucket.cpp @@ -9,7 +9,8 @@ namespace mbgl { using namespace style; SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::Evaluated layout_, - const std::unordered_map<std::string, style::SymbolPaintProperties::Evaluated>& layerPaintProperties, + const std::unordered_map<std::string, std::pair< + style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>& layerPaintProperties, float zoom, bool sdfIcons_, bool iconsNeedLinear_) @@ -17,8 +18,10 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::Evaluated layout_, sdfIcons(sdfIcons_), iconsNeedLinear(iconsNeedLinear_) { for (const auto& pair : layerPaintProperties) { - paintPropertyBinders.emplace(pair.first, - SymbolIconProgram::PaintPropertyBinders(pair.second, zoom)); + paintPropertyBinders.emplace(pair.first, std::make_pair( + SymbolIconProgram::PaintPropertyBinders(pair.second.first, zoom), + SymbolSDFTextProgram::PaintPropertyBinders(pair.second.second, zoom) + )); } } @@ -39,7 +42,8 @@ void SymbolBucket::upload(gl::Context& context) { } for (auto& pair : paintPropertyBinders) { - pair.second.upload(context); + pair.second.first.upload(context); + pair.second.second.upload(context); } uploaded = true; diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/symbol_bucket.hpp index 0b40bb34ae..dcf3f5f495 100644 --- a/src/mbgl/renderer/symbol_bucket.hpp +++ b/src/mbgl/renderer/symbol_bucket.hpp @@ -17,7 +17,7 @@ namespace mbgl { class SymbolBucket : public Bucket { public: SymbolBucket(style::SymbolLayoutProperties::Evaluated, - const std::unordered_map<std::string, style::SymbolPaintProperties::Evaluated>&, + const std::unordered_map<std::string, std::pair<style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>&, float zoom, bool sdfIcons, bool iconsNeedLinear); @@ -33,12 +33,14 @@ public: const bool sdfIcons; const bool iconsNeedLinear; - std::unordered_map<std::string, SymbolIconProgram::PaintPropertyBinders> paintPropertyBinders; + std::unordered_map<std::string, std::pair< + SymbolIconProgram::PaintPropertyBinders, + SymbolSDFTextProgram::PaintPropertyBinders>> paintPropertyBinders; struct TextBuffer { gl::VertexVector<SymbolLayoutVertex> vertices; gl::IndexVector<gl::Triangles> triangles; - gl::SegmentVector<SymbolAttributes> segments; + gl::SegmentVector<SymbolTextAttributes> segments; optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer; optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; @@ -47,7 +49,7 @@ public: struct IconBuffer { gl::VertexVector<SymbolLayoutVertex> vertices; gl::IndexVector<gl::Triangles> triangles; - gl::SegmentVector<SymbolAttributes> segments; + gl::SegmentVector<SymbolIconAttributes> segments; optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer; optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; diff --git a/src/mbgl/shaders/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp index eca9342d54..e6728e15de 100644 --- a/src/mbgl/shaders/symbol_icon.cpp +++ b/src/mbgl/shaders/symbol_icon.cpp @@ -66,6 +66,10 @@ attribute vec2 a_offset; attribute vec2 a_texture_pos; attribute vec4 a_data; +uniform lowp float a_opacity_t; +attribute lowp float a_opacity_min; +attribute lowp float a_opacity_max; +varying lowp float opacity; // matrix is for the vertex position. uniform mat4 u_matrix; @@ -80,6 +84,8 @@ varying vec2 v_tex; varying vec2 v_fade_tex; void main() { + opacity = mix(a_opacity_min, a_opacity_max, a_opacity_t); + vec2 a_tex = a_texture_pos.xy; mediump float a_labelminzoom = a_data[0]; mediump vec2 a_zoom = a_data.pq; @@ -122,13 +128,16 @@ precision mediump float; #endif uniform sampler2D u_texture; uniform sampler2D u_fadetexture; -uniform lowp float u_opacity; + +varying lowp float opacity; varying vec2 v_tex; varying vec2 v_fade_tex; void main() { - lowp float alpha = texture2D(u_fadetexture, v_fade_tex).a * u_opacity; + + + lowp float alpha = texture2D(u_fadetexture, v_fade_tex).a * opacity; gl_FragColor = texture2D(u_texture, v_tex) * alpha; #ifdef OVERDRAW_INSPECTOR diff --git a/src/mbgl/shaders/symbol_sdf.cpp b/src/mbgl/shaders/symbol_sdf.cpp index 7554597893..e087242bf8 100644 --- a/src/mbgl/shaders/symbol_sdf.cpp +++ b/src/mbgl/shaders/symbol_sdf.cpp @@ -68,6 +68,26 @@ attribute vec2 a_offset; attribute vec2 a_texture_pos; attribute vec4 a_data; +uniform lowp float a_fill_color_t; +attribute lowp vec4 a_fill_color_min; +attribute lowp vec4 a_fill_color_max; +varying lowp vec4 fill_color; +uniform lowp float a_halo_color_t; +attribute lowp vec4 a_halo_color_min; +attribute lowp vec4 a_halo_color_max; +varying lowp vec4 halo_color; +uniform lowp float a_opacity_t; +attribute lowp float a_opacity_min; +attribute lowp float a_opacity_max; +varying lowp float opacity; +uniform lowp float a_halo_width_t; +attribute lowp float a_halo_width_min; +attribute lowp float a_halo_width_max; +varying lowp float halo_width; +uniform lowp float a_halo_blur_t; +attribute lowp float a_halo_blur_min; +attribute lowp float a_halo_blur_max; +varying lowp float halo_blur; // matrix is for the vertex position. uniform mat4 u_matrix; @@ -87,6 +107,12 @@ varying vec2 v_fade_tex; varying float v_gamma_scale; void main() { + fill_color = mix(a_fill_color_min, a_fill_color_max, a_fill_color_t); + halo_color = mix(a_halo_color_min, a_halo_color_max, a_halo_color_t); + opacity = mix(a_opacity_min, a_opacity_max, a_opacity_t); + halo_width = mix(a_halo_width_min, a_halo_width_max, a_halo_width_t); + halo_blur = mix(a_halo_blur_min, a_halo_blur_max, a_halo_blur_t); + vec2 a_tex = a_texture_pos.xy; mediump float a_labelminzoom = a_data[0]; mediump vec2 a_zoom = a_data.pq; @@ -163,24 +189,47 @@ precision mediump float; #endif #endif +#define SDF_PX 8.0 +#define EDGE_GAMMA 0.105/DEVICE_PIXEL_RATIO + +uniform bool u_is_halo; +varying lowp vec4 fill_color; +varying lowp vec4 halo_color; +varying lowp float opacity; +varying lowp float halo_width; +varying lowp float halo_blur; + uniform sampler2D u_texture; uniform sampler2D u_fadetexture; -uniform lowp vec4 u_color; -uniform lowp float u_opacity; -uniform lowp float u_buffer; -uniform highp float u_gamma; +uniform lowp float u_font_scale; +uniform highp float u_gamma_scale; varying vec2 v_tex; varying vec2 v_fade_tex; varying float v_gamma_scale; void main() { + + + + + + + lowp vec4 color = fill_color; + lowp float gamma = EDGE_GAMMA / u_gamma_scale; + lowp float buff = (256.0 - 64.0) / 256.0; + if (u_is_halo) { + color = halo_color; + gamma = (halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / u_gamma_scale; + buff = (6.0 - halo_width / u_font_scale) / SDF_PX; + } + lowp float dist = texture2D(u_texture, v_tex).a; lowp float fade_alpha = texture2D(u_fadetexture, v_fade_tex).a; - highp float gamma = u_gamma * v_gamma_scale; - highp float alpha = smoothstep(u_buffer - gamma, u_buffer + gamma, dist) * fade_alpha; + highp float gamma_scaled = gamma * v_gamma_scale; + highp float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist) * fade_alpha; - gl_FragColor = u_color * (alpha * u_opacity); + gl_FragColor = color * (alpha * opacity); #ifdef OVERDRAW_INSPECTOR gl_FragColor = vec4(1.0); diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp index 6364091207..d85b8c00e6 100644 --- a/src/mbgl/style/layers/symbol_layer.cpp +++ b/src/mbgl/style/layers/symbol_layer.cpp @@ -542,95 +542,115 @@ void SymbolLayer::setTextOptional(PropertyValue<bool> value) { // Paint properties -PropertyValue<float> SymbolLayer::getDefaultIconOpacity() { +DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconOpacity() { return { 1 }; } -PropertyValue<float> SymbolLayer::getIconOpacity(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> SymbolLayer::getIconOpacity(const optional<std::string>& klass) const { return impl->paint.get<IconOpacity>(klass); } -void SymbolLayer::setIconOpacity(PropertyValue<float> value, const optional<std::string>& klass) { +void SymbolLayer::setIconOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getIconOpacity(klass)) return; impl->paint.set<IconOpacity>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setIconOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { impl->paint.setTransition<IconOpacity>(value, klass); } -PropertyValue<Color> SymbolLayer::getDefaultIconColor() { +DataDrivenPropertyValue<Color> SymbolLayer::getDefaultIconColor() { return { Color::black() }; } -PropertyValue<Color> SymbolLayer::getIconColor(const optional<std::string>& klass) const { +DataDrivenPropertyValue<Color> SymbolLayer::getIconColor(const optional<std::string>& klass) const { return impl->paint.get<IconColor>(klass); } -void SymbolLayer::setIconColor(PropertyValue<Color> value, const optional<std::string>& klass) { +void SymbolLayer::setIconColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { if (value == getIconColor(klass)) return; impl->paint.set<IconColor>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setIconColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { impl->paint.setTransition<IconColor>(value, klass); } -PropertyValue<Color> SymbolLayer::getDefaultIconHaloColor() { +DataDrivenPropertyValue<Color> SymbolLayer::getDefaultIconHaloColor() { return { {} }; } -PropertyValue<Color> SymbolLayer::getIconHaloColor(const optional<std::string>& klass) const { +DataDrivenPropertyValue<Color> SymbolLayer::getIconHaloColor(const optional<std::string>& klass) const { return impl->paint.get<IconHaloColor>(klass); } -void SymbolLayer::setIconHaloColor(PropertyValue<Color> value, const optional<std::string>& klass) { +void SymbolLayer::setIconHaloColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { if (value == getIconHaloColor(klass)) return; impl->paint.set<IconHaloColor>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setIconHaloColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { impl->paint.setTransition<IconHaloColor>(value, klass); } -PropertyValue<float> SymbolLayer::getDefaultIconHaloWidth() { +DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconHaloWidth() { return { 0 }; } -PropertyValue<float> SymbolLayer::getIconHaloWidth(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> SymbolLayer::getIconHaloWidth(const optional<std::string>& klass) const { return impl->paint.get<IconHaloWidth>(klass); } -void SymbolLayer::setIconHaloWidth(PropertyValue<float> value, const optional<std::string>& klass) { +void SymbolLayer::setIconHaloWidth(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getIconHaloWidth(klass)) return; impl->paint.set<IconHaloWidth>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setIconHaloWidthTransition(const TransitionOptions& value, const optional<std::string>& klass) { impl->paint.setTransition<IconHaloWidth>(value, klass); } -PropertyValue<float> SymbolLayer::getDefaultIconHaloBlur() { +DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconHaloBlur() { return { 0 }; } -PropertyValue<float> SymbolLayer::getIconHaloBlur(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> SymbolLayer::getIconHaloBlur(const optional<std::string>& klass) const { return impl->paint.get<IconHaloBlur>(klass); } -void SymbolLayer::setIconHaloBlur(PropertyValue<float> value, const optional<std::string>& klass) { +void SymbolLayer::setIconHaloBlur(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getIconHaloBlur(klass)) return; impl->paint.set<IconHaloBlur>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setIconHaloBlurTransition(const TransitionOptions& value, const optional<std::string>& klass) { @@ -671,99 +691,119 @@ void SymbolLayer::setIconTranslateAnchor(PropertyValue<TranslateAnchorType> valu impl->observer->onLayerPaintPropertyChanged(*this); } -PropertyValue<float> SymbolLayer::getDefaultTextOpacity() { - return { 1 }; -} - void SymbolLayer::setIconTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) { impl->paint.setTransition<IconTranslateAnchor>(value, klass); } -PropertyValue<float> SymbolLayer::getTextOpacity(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextOpacity() { + return { 1 }; +} + +DataDrivenPropertyValue<float> SymbolLayer::getTextOpacity(const optional<std::string>& klass) const { return impl->paint.get<TextOpacity>(klass); } -void SymbolLayer::setTextOpacity(PropertyValue<float> value, const optional<std::string>& klass) { +void SymbolLayer::setTextOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getTextOpacity(klass)) return; impl->paint.set<TextOpacity>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); -} - -PropertyValue<Color> SymbolLayer::getDefaultTextColor() { - return { Color::black() }; + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setTextOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { impl->paint.setTransition<TextOpacity>(value, klass); } -PropertyValue<Color> SymbolLayer::getTextColor(const optional<std::string>& klass) const { +DataDrivenPropertyValue<Color> SymbolLayer::getDefaultTextColor() { + return { Color::black() }; +} + +DataDrivenPropertyValue<Color> SymbolLayer::getTextColor(const optional<std::string>& klass) const { return impl->paint.get<TextColor>(klass); } -void SymbolLayer::setTextColor(PropertyValue<Color> value, const optional<std::string>& klass) { +void SymbolLayer::setTextColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { if (value == getTextColor(klass)) return; impl->paint.set<TextColor>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setTextColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { impl->paint.setTransition<TextColor>(value, klass); } -PropertyValue<Color> SymbolLayer::getDefaultTextHaloColor() { +DataDrivenPropertyValue<Color> SymbolLayer::getDefaultTextHaloColor() { return { {} }; } -PropertyValue<Color> SymbolLayer::getTextHaloColor(const optional<std::string>& klass) const { +DataDrivenPropertyValue<Color> SymbolLayer::getTextHaloColor(const optional<std::string>& klass) const { return impl->paint.get<TextHaloColor>(klass); } -void SymbolLayer::setTextHaloColor(PropertyValue<Color> value, const optional<std::string>& klass) { +void SymbolLayer::setTextHaloColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { if (value == getTextHaloColor(klass)) return; impl->paint.set<TextHaloColor>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setTextHaloColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { impl->paint.setTransition<TextHaloColor>(value, klass); } -PropertyValue<float> SymbolLayer::getDefaultTextHaloWidth() { +DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextHaloWidth() { return { 0 }; } -PropertyValue<float> SymbolLayer::getTextHaloWidth(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> SymbolLayer::getTextHaloWidth(const optional<std::string>& klass) const { return impl->paint.get<TextHaloWidth>(klass); } -void SymbolLayer::setTextHaloWidth(PropertyValue<float> value, const optional<std::string>& klass) { +void SymbolLayer::setTextHaloWidth(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getTextHaloWidth(klass)) return; impl->paint.set<TextHaloWidth>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setTextHaloWidthTransition(const TransitionOptions& value, const optional<std::string>& klass) { impl->paint.setTransition<TextHaloWidth>(value, klass); } -PropertyValue<float> SymbolLayer::getDefaultTextHaloBlur() { +DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextHaloBlur() { return { 0 }; } -PropertyValue<float> SymbolLayer::getTextHaloBlur(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> SymbolLayer::getTextHaloBlur(const optional<std::string>& klass) const { return impl->paint.get<TextHaloBlur>(klass); } -void SymbolLayer::setTextHaloBlur(PropertyValue<float> value, const optional<std::string>& klass) { +void SymbolLayer::setTextHaloBlur(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getTextHaloBlur(klass)) return; impl->paint.set<TextHaloBlur>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setTextHaloBlurTransition(const TransitionOptions& value, const optional<std::string>& klass) { diff --git a/src/mbgl/style/layers/symbol_layer_impl.cpp b/src/mbgl/style/layers/symbol_layer_impl.cpp index 32547e465a..ff59b14d65 100644 --- a/src/mbgl/style/layers/symbol_layer_impl.cpp +++ b/src/mbgl/style/layers/symbol_layer_impl.cpp @@ -16,9 +16,14 @@ bool SymbolLayer::Impl::evaluate(const PropertyEvaluationParameters& parameters) // text-size and icon-size are layout properties but they also need to be evaluated as paint properties: iconSize = layout.evaluate<IconSize>(parameters); textSize = layout.evaluate<TextSize>(parameters); - - passes = ((paint.evaluated.get<IconOpacity>() > 0 && (paint.evaluated.get<IconColor>().a > 0 || paint.evaluated.get<IconHaloColor>().a > 0) && iconSize > 0) - || (paint.evaluated.get<TextOpacity>() > 0 && (paint.evaluated.get<TextColor>().a > 0 || paint.evaluated.get<TextHaloColor>().a > 0) && textSize > 0)) + + auto hasIconOpacity = paint.evaluated.get<IconColor>().constantOr(Color::black()).a > 0 || + paint.evaluated.get<IconHaloColor>().constantOr(Color::black()).a > 0; + auto hasTextOpacity = paint.evaluated.get<TextColor>().constantOr(Color::black()).a > 0 || + paint.evaluated.get<TextHaloColor>().constantOr(Color::black()).a > 0; + + passes = ((paint.evaluated.get<IconOpacity>().constantOr(1) > 0 && hasIconOpacity && iconSize > 0) + || (paint.evaluated.get<TextOpacity>().constantOr(1) > 0 && hasTextOpacity && textSize > 0)) ? RenderPass::Translucent : RenderPass::None; return paint.hasTransition(); @@ -38,20 +43,43 @@ std::unique_ptr<SymbolLayout> SymbolLayer::Impl::createLayout(const BucketParame *spriteAtlas); } -SymbolPropertyValues SymbolLayer::Impl::iconPropertyValues(const SymbolLayoutProperties::Evaluated& layout_) const { - return SymbolPropertyValues { - layout_.get<IconRotationAlignment>(), // icon-pitch-alignment is not yet implemented; inherit the rotation alignment - layout_.get<IconRotationAlignment>(), - layout_.get<IconSize>(), +IconPaintProperties::Evaluated SymbolLayer::Impl::iconPaintProperties() const { + return IconPaintProperties::Evaluated { paint.evaluated.get<IconOpacity>(), paint.evaluated.get<IconColor>(), paint.evaluated.get<IconHaloColor>(), paint.evaluated.get<IconHaloWidth>(), paint.evaluated.get<IconHaloBlur>(), paint.evaluated.get<IconTranslate>(), + paint.evaluated.get<IconTranslateAnchor>() + }; +} + +TextPaintProperties::Evaluated SymbolLayer::Impl::textPaintProperties() const { + return TextPaintProperties::Evaluated { + paint.evaluated.get<TextOpacity>(), + paint.evaluated.get<TextColor>(), + paint.evaluated.get<TextHaloColor>(), + paint.evaluated.get<TextHaloWidth>(), + paint.evaluated.get<TextHaloBlur>(), + paint.evaluated.get<TextTranslate>(), + paint.evaluated.get<TextTranslateAnchor>() + }; +} + + +SymbolPropertyValues SymbolLayer::Impl::iconPropertyValues(const SymbolLayoutProperties::Evaluated& layout_) const { + return SymbolPropertyValues { + layout_.get<IconRotationAlignment>(), // icon-pitch-alignment is not yet implemented; inherit the rotation alignment + layout_.get<IconRotationAlignment>(), + layout_.get<IconSize>(), + paint.evaluated.get<IconTranslate>(), paint.evaluated.get<IconTranslateAnchor>(), iconSize, - 1.0f + 1.0f, + paint.evaluated.get<IconHaloColor>().constantOr(Color::black()).a > 0 && + paint.evaluated.get<IconHaloWidth>().constantOr(1), + paint.evaluated.get<IconColor>().constantOr(Color::black()).a > 0 }; } @@ -60,15 +88,13 @@ SymbolPropertyValues SymbolLayer::Impl::textPropertyValues(const SymbolLayoutPro layout_.get<TextPitchAlignment>(), layout_.get<TextRotationAlignment>(), layout_.get<TextSize>(), - paint.evaluated.get<TextOpacity>(), - paint.evaluated.get<TextColor>(), - paint.evaluated.get<TextHaloColor>(), - paint.evaluated.get<TextHaloWidth>(), - paint.evaluated.get<TextHaloBlur>(), paint.evaluated.get<TextTranslate>(), paint.evaluated.get<TextTranslateAnchor>(), textSize, - 24.0f + 24.0f, + paint.evaluated.get<TextHaloColor>().constantOr(Color::black()).a > 0 && + paint.evaluated.get<TextHaloWidth>().constantOr(1), + paint.evaluated.get<TextColor>().constantOr(Color::black()).a > 0 }; } diff --git a/src/mbgl/style/layers/symbol_layer_impl.hpp b/src/mbgl/style/layers/symbol_layer_impl.hpp index c00c2b0bba..1e9f05e4c7 100644 --- a/src/mbgl/style/layers/symbol_layer_impl.hpp +++ b/src/mbgl/style/layers/symbol_layer_impl.hpp @@ -1,5 +1,6 @@ #pragma once +#include <mbgl/util/variant.hpp> #include <mbgl/style/layer_impl.hpp> #include <mbgl/style/layers/symbol_layer.hpp> #include <mbgl/style/layers/symbol_layer_properties.hpp> @@ -10,6 +11,30 @@ class SpriteAtlas; class SymbolLayout; namespace style { + + +// {icon,text}-specific paint-property packs for use in the symbol Programs. +// Since each program deals either with icons or text, using a smaller property set +// lets us avoid unnecessarily binding attributes for properties the program wouldn't use. +class IconPaintProperties : public PaintProperties< + IconOpacity, + IconColor, + IconHaloColor, + IconHaloWidth, + IconHaloBlur, + IconTranslate, + IconTranslateAnchor +> {}; + +class TextPaintProperties : public PaintProperties< + TextOpacity, + TextColor, + TextHaloColor, + TextHaloWidth, + TextHaloBlur, + TextTranslate, + TextTranslateAnchor +> {}; // Repackaging evaluated values from SymbolLayoutProperties + SymbolPaintProperties // for genericity over icons vs. text. @@ -21,24 +46,14 @@ public: float layoutSize; // Paint - float opacity; - Color color; - Color haloColor; - float haloWidth; - float haloBlur; std::array<float, 2> translate; TranslateAnchorType translateAnchor; float paintSize; float sdfScale; // Constant (1.0 or 24.0) - - bool hasHalo() const { - return haloColor.a > 0.0f && haloWidth > 0.0f; - } - - bool hasForeground() const { - return color.a > 0.0f; - } + + bool hasHalo; + bool hasFill; }; class SymbolLayer::Impl : public Layer::Impl { @@ -54,6 +69,9 @@ public: std::unique_ptr<SymbolLayout> createLayout(const BucketParameters&, const std::vector<const Layer*>&, const GeometryTileLayer&) const; + IconPaintProperties::Evaluated iconPaintProperties() const; + TextPaintProperties::Evaluated textPaintProperties() const; + SymbolPropertyValues iconPropertyValues(const SymbolLayoutProperties::Evaluated&) const; SymbolPropertyValues textPropertyValues(const SymbolLayoutProperties::Evaluated&) const; diff --git a/src/mbgl/style/layers/symbol_layer_properties.hpp b/src/mbgl/style/layers/symbol_layer_properties.hpp index 3bdae377ea..f2b7bfa00f 100644 --- a/src/mbgl/style/layers/symbol_layer_properties.hpp +++ b/src/mbgl/style/layers/symbol_layer_properties.hpp @@ -180,23 +180,23 @@ struct TextOptional : LayoutProperty<bool> { static bool defaultValue() { return false; } }; -struct IconOpacity : PaintProperty<float> { +struct IconOpacity : DataDrivenPaintProperty<float, attributes::a_opacity> { static float defaultValue() { return 1; } }; -struct IconColor : PaintProperty<Color> { +struct IconColor : DataDrivenPaintProperty<Color, attributes::a_fill_color> { static Color defaultValue() { return Color::black(); } }; -struct IconHaloColor : PaintProperty<Color> { +struct IconHaloColor : DataDrivenPaintProperty<Color, attributes::a_halo_color> { static Color defaultValue() { return {}; } }; -struct IconHaloWidth : PaintProperty<float> { +struct IconHaloWidth : DataDrivenPaintProperty<float, attributes::a_halo_width> { static float defaultValue() { return 0; } }; -struct IconHaloBlur : PaintProperty<float> { +struct IconHaloBlur : DataDrivenPaintProperty<float, attributes::a_halo_blur> { static float defaultValue() { return 0; } }; @@ -208,23 +208,23 @@ struct IconTranslateAnchor : PaintProperty<TranslateAnchorType> { static TranslateAnchorType defaultValue() { return TranslateAnchorType::Map; } }; -struct TextOpacity : PaintProperty<float> { +struct TextOpacity : DataDrivenPaintProperty<float, attributes::a_opacity> { static float defaultValue() { return 1; } }; -struct TextColor : PaintProperty<Color> { +struct TextColor : DataDrivenPaintProperty<Color, attributes::a_fill_color> { static Color defaultValue() { return Color::black(); } }; -struct TextHaloColor : PaintProperty<Color> { +struct TextHaloColor : DataDrivenPaintProperty<Color, attributes::a_halo_color> { static Color defaultValue() { return {}; } }; -struct TextHaloWidth : PaintProperty<float> { +struct TextHaloWidth : DataDrivenPaintProperty<float, attributes::a_halo_width> { static float defaultValue() { return 0; } }; -struct TextHaloBlur : PaintProperty<float> { +struct TextHaloBlur : DataDrivenPaintProperty<float, attributes::a_halo_blur> { static float defaultValue() { return 0; } }; diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp index b43ba0220c..e68566d419 100644 --- a/src/mbgl/text/shaping.cpp +++ b/src/mbgl/text/shaping.cpp @@ -3,16 +3,15 @@ namespace mbgl { -PositionedIcon shapeIcon(const SpriteAtlasElement& image, - const SymbolFeature& feature) { - float dx = feature.iconOffset[0]; - float dy = feature.iconOffset[1]; +PositionedIcon shapeIcon(const SpriteAtlasElement& image, const std::array<float, 2>& iconOffset, const float iconRotation) { + float dx = iconOffset[0]; + float dy = iconOffset[1]; float x1 = dx - image.spriteImage->getWidth() / 2.0f; float x2 = x1 + image.spriteImage->getWidth(); float y1 = dy - image.spriteImage->getHeight() / 2.0f; float y2 = y1 + image.spriteImage->getHeight(); - return PositionedIcon(image, y1, y2, x1, x2, feature.iconRotation); + return PositionedIcon(image, y1, y2, x1, x2, iconRotation); } } // namespace mbgl diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp index b0e6ae3b1d..1b7b8b2733 100644 --- a/src/mbgl/text/shaping.hpp +++ b/src/mbgl/text/shaping.hpp @@ -36,7 +36,6 @@ public: explicit operator bool() const { return image && (*image).pos.hasArea(); } }; -PositionedIcon shapeIcon(const SpriteAtlasElement&, - const SymbolFeature&); +PositionedIcon shapeIcon(const SpriteAtlasElement&, const std::array<float, 2>& iconOffset, const float iconRotation); } // namespace mbgl diff --git a/src/mbgl/tile/vector_tile.cpp b/src/mbgl/tile/vector_tile.cpp index a195885415..68f48e81fd 100644 --- a/src/mbgl/tile/vector_tile.cpp +++ b/src/mbgl/tile/vector_tile.cpp @@ -15,9 +15,23 @@ class VectorTileLayer; using packed_iter_type = protozero::iterator_range<protozero::pbf_reader::const_uint32_iterator>; +struct VectorTileLayerData { + VectorTileLayerData(std::shared_ptr<const std::string>); + + // Hold a reference to the underlying pbf data that backs the lazily-built + // components of the owning VectorTileLayer and VectorTileFeature objects + std::shared_ptr<const std::string> data; + + uint32_t version = 1; + uint32_t extent = 4096; + std::unordered_map<std::string, uint32_t> keysMap; + std::vector<std::reference_wrapper<const std::string>> keys; + std::vector<Value> values; +}; + class VectorTileFeature : public GeometryTileFeature { public: - VectorTileFeature(protozero::pbf_reader, const VectorTileLayer&); + VectorTileFeature(protozero::pbf_reader, std::shared_ptr<VectorTileLayerData> layerData); FeatureType getType() const override { return type; } optional<Value> getValue(const std::string&) const override; @@ -26,16 +40,16 @@ public: GeometryCollection getGeometries() const override; private: - const VectorTileLayer& layer; + std::shared_ptr<VectorTileLayerData> layerData; optional<FeatureIdentifier> id; FeatureType type = FeatureType::Unknown; packed_iter_type tags_iter; packed_iter_type geometry_iter; }; - + class VectorTileLayer : public GeometryTileLayer { public: - VectorTileLayer(protozero::pbf_reader); + VectorTileLayer(protozero::pbf_reader, std::shared_ptr<const std::string>); std::size_t featureCount() const override { return features.size(); } std::unique_ptr<GeometryTileFeature> getFeature(std::size_t) const override; @@ -46,12 +60,8 @@ private: friend class VectorTileFeature; std::string name; - uint32_t version = 1; - uint32_t extent = 4096; - std::unordered_map<std::string, uint32_t> keysMap; - std::vector<std::reference_wrapper<const std::string>> keys; - std::vector<Value> values; std::vector<protozero::pbf_reader> features; + std::shared_ptr<VectorTileLayerData> data; }; class VectorTileData : public GeometryTileData { @@ -117,8 +127,8 @@ Value parseValue(protozero::pbf_reader data) { return false; } -VectorTileFeature::VectorTileFeature(protozero::pbf_reader feature_pbf, const VectorTileLayer& layer_) - : layer(layer_) { +VectorTileFeature::VectorTileFeature(protozero::pbf_reader feature_pbf, std::shared_ptr<VectorTileLayerData> layerData_) + : layerData(std::move(layerData_)) { while (feature_pbf.next()) { switch (feature_pbf.tag()) { case 1: // id @@ -141,8 +151,8 @@ VectorTileFeature::VectorTileFeature(protozero::pbf_reader feature_pbf, const Ve } optional<Value> VectorTileFeature::getValue(const std::string& key) const { - auto keyIter = layer.keysMap.find(key); - if (keyIter == layer.keysMap.end()) { + auto keyIter = layerData->keysMap.find(key); + if (keyIter == layerData->keysMap.end()) { return optional<Value>(); } @@ -151,7 +161,7 @@ optional<Value> VectorTileFeature::getValue(const std::string& key) const { while (start_itr != end_itr) { uint32_t tag_key = static_cast<uint32_t>(*start_itr++); - if (layer.keysMap.size() <= tag_key) { + if (layerData->keysMap.size() <= tag_key) { throw std::runtime_error("feature referenced out of range key"); } @@ -160,12 +170,12 @@ optional<Value> VectorTileFeature::getValue(const std::string& key) const { } uint32_t tag_val = static_cast<uint32_t>(*start_itr++);; - if (layer.values.size() <= tag_val) { + if (layerData->values.size() <= tag_val) { throw std::runtime_error("feature referenced out of range value"); } if (tag_key == keyIter->second) { - return layer.values[tag_val]; + return layerData->values[tag_val]; } } @@ -182,7 +192,7 @@ std::unordered_map<std::string,Value> VectorTileFeature::getProperties() const { throw std::runtime_error("uneven number of feature tag ids"); } uint32_t tag_val = static_cast<uint32_t>(*start_itr++); - properties[layer.keys.at(tag_key)] = layer.values.at(tag_val); + properties[layerData->keys.at(tag_key)] = layerData->values.at(tag_val); } return properties; } @@ -196,7 +206,7 @@ GeometryCollection VectorTileFeature::getGeometries() const { uint32_t length = 0; int32_t x = 0; int32_t y = 0; - const float scale = float(util::EXTENT) / layer.extent; + const float scale = float(util::EXTENT) / layerData->extent; GeometryCollection lines; @@ -234,7 +244,7 @@ GeometryCollection VectorTileFeature::getGeometries() const { } } - if (layer.version >= 2 || type != FeatureType::Polygon) { + if (layerData->version >= 2 || type != FeatureType::Polygon) { return lines; } @@ -250,7 +260,7 @@ const GeometryTileLayer* VectorTileData::getLayer(const std::string& name) const parsed = true; protozero::pbf_reader tile_pbf(*data); while (tile_pbf.next(3)) { - VectorTileLayer layer(tile_pbf.get_message()); + VectorTileLayer layer(tile_pbf.get_message(), data); layers.emplace(layer.name, std::move(layer)); } } @@ -262,7 +272,13 @@ const GeometryTileLayer* VectorTileData::getLayer(const std::string& name) const return nullptr; } -VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf) { +VectorTileLayerData::VectorTileLayerData(std::shared_ptr<const std::string> pbfData) : + data(std::move(pbfData)) +{} + +VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf, std::shared_ptr<const std::string> pbfData) + : data(std::make_shared<VectorTileLayerData>(std::move(pbfData))) +{ while (layer_pbf.next()) { switch (layer_pbf.tag()) { case 1: // name @@ -273,18 +289,18 @@ VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf) { break; case 3: // keys { - auto iter = keysMap.emplace(layer_pbf.get_string(), keysMap.size()); - keys.emplace_back(std::reference_wrapper<const std::string>(iter.first->first)); + auto iter = data->keysMap.emplace(layer_pbf.get_string(), data->keysMap.size()); + data->keys.emplace_back(std::reference_wrapper<const std::string>(iter.first->first)); } break; case 4: // values - values.emplace_back(parseValue(layer_pbf.get_message())); + data->values.emplace_back(parseValue(layer_pbf.get_message())); break; case 5: // extent - extent = layer_pbf.get_uint32(); + data->extent = layer_pbf.get_uint32(); break; case 15: // version - version = layer_pbf.get_uint32(); + data->version = layer_pbf.get_uint32(); break; default: layer_pbf.skip(); @@ -294,7 +310,7 @@ VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf) { } std::unique_ptr<GeometryTileFeature> VectorTileLayer::getFeature(std::size_t i) const { - return std::make_unique<VectorTileFeature>(features.at(i), *this); + return std::make_unique<VectorTileFeature>(features.at(i), data); } std::string VectorTileLayer::getName() const { diff --git a/test/tile/vector_tile.test.cpp b/test/tile/vector_tile.test.cpp index 2fe2586ec2..49fdcbd9f8 100644 --- a/test/tile/vector_tile.test.cpp +++ b/test/tile/vector_tile.test.cpp @@ -61,9 +61,11 @@ TEST(VectorTile, Issue7615) { style::SymbolLayer symbolLayer("symbol", "source"); auto symbolBucket = std::make_shared<SymbolBucket>( style::SymbolLayoutProperties::Evaluated(), - std::unordered_map<std::string, style::SymbolPaintProperties::Evaluated>(), + std::unordered_map< + std::string, + std::pair<style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>(), 0.0f, false, false); - + // Simulate placement of a symbol layer. tile.onPlacement(GeometryTile::PlacementResult { {{ diff --git a/test/util/merge_lines.test.cpp b/test/util/merge_lines.test.cpp index 0a39419b70..8a3a400887 100644 --- a/test/util/merge_lines.test.cpp +++ b/test/util/merge_lines.test.cpp @@ -8,95 +8,131 @@ const std::u16string bbb = u"b"; using namespace mbgl; -TEST(MergeLines, SameText) { - // merges lines with the same text - std::vector<mbgl::SymbolFeature> input1 = { - { FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, bbb, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{8, 0}, {9, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{6, 0}, {7, 0}, {8, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{5, 0}, {6, 0}}}, aaa, {}, {{0, 0}}, 0, 0 } +class GeometryTileFeatureStub : public GeometryTileFeature { +public: + GeometryTileFeatureStub(optional<FeatureIdentifier> id_, FeatureType type_, GeometryCollection geometry_, + std::unordered_map<std::string, Value> properties_) : + id(id_), + type(type_), + geometry(geometry_), + properties(properties_) + {} + + FeatureType getType() const override { return type; } + optional<Value> getValue(const std::string& key) const override { + auto it = properties.find(key); + if (it != properties.end()) { + return it->second; + } + return {}; }; + std::unordered_map<std::string,Value> getProperties() const override { return properties; }; + optional<FeatureIdentifier> getID() const override { return id; }; + GeometryCollection getGeometries() const override { return geometry; }; + + optional<FeatureIdentifier> id; + FeatureType type; + GeometryCollection geometry; + std::unordered_map<std::string,Value> properties; +}; + +class SymbolFeatureStub : public SymbolFeature { +public: + SymbolFeatureStub(optional<FeatureIdentifier> id_, FeatureType type_, GeometryCollection geometry_, + std::unordered_map<std::string, Value> properties_, optional<std::u16string> text_, + optional<std::string> icon_, std::size_t index_) : + SymbolFeature(std::make_unique<GeometryTileFeatureStub>(id_, type_, geometry_, properties_)) + { + text = text_; + icon = icon_; + index = index_; + } +}; - const std::vector<mbgl::SymbolFeature> expected1 = { - { FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, bbb, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{}}, aaa, {}, {{0, 0}}, 0, 0 } +TEST(MergeLines, SameText) { + // merges lines with the same text + std::vector<mbgl::SymbolFeature> input1; + input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, {}, aaa, {}, 0)); + input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, {}, bbb, {}, 0)); + input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{8, 0}, {9, 0}}}, {}, aaa, {}, 0)); + input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, {}, aaa, {}, 0)); + input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{6, 0}, {7, 0}, {8, 0}}}, {}, aaa, {}, 0)); + input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{5, 0}, {6, 0}}}, {}, aaa, {}, 0)); + + const std::vector<GeometryTileFeatureStub> expected1 = { + { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}}}, {} }, + { {}, FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, {} }, + { {}, FeatureType::LineString, {{{5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0}}}, {} }, + { {}, FeatureType::LineString, {{}}, {} }, + { {}, FeatureType::LineString, {{}}, {} }, + { {}, FeatureType::LineString, {{}}, {} } }; - + mbgl::util::mergeLines(input1); for (int i = 0; i < 6; i++) { - EXPECT_TRUE(input1[i].geometry == expected1[i].geometry); + EXPECT_TRUE(input1[i].geometry == expected1[i].getGeometries()); } } TEST(MergeLines, BothEnds) { // mergeLines handles merge from both ends - std::vector<mbgl::SymbolFeature> input2 = { - { FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, aaa, {}, {{0, 0}}, 0, 0 } - }; - - const std::vector<mbgl::SymbolFeature> expected2 = { - { FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{}}, aaa, {}, {{0, 0}}, 0, 0 } + std::vector<mbgl::SymbolFeature> input2; + input2.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, {}, aaa, {}, 0 }); + input2.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, {}, aaa, {}, 0 }); + input2.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, {}, aaa, {}, 0 }); + + const std::vector<GeometryTileFeatureStub> expected2 = { + { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}}}, {} }, + { {}, FeatureType::LineString, {{}}, {} }, + { {}, FeatureType::LineString, {{}}, {} } }; mbgl::util::mergeLines(input2); for (int i = 0; i < 3; i++) { - EXPECT_TRUE(input2[i].geometry == expected2[i].geometry); + EXPECT_TRUE(input2[i].geometry == expected2[i].getGeometries()); } } TEST(MergeLines, CircularLines) { // mergeLines handles circular lines - std::vector<mbgl::SymbolFeature> input3 = { - { FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{4, 0}, {0, 0}}}, aaa, {}, {{0, 0}}, 0, 0 } - }; - - const std::vector<mbgl::SymbolFeature> expected3 = { - { FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{}}, aaa, {}, {{0, 0}}, 0, 0 } + std::vector<mbgl::SymbolFeature> input3; + input3.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, {}, aaa, {}, 0 }); + input3.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, {}, aaa, {}, 0 }); + input3.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{4, 0}, {0, 0}}}, {}, aaa, {}, 0 }); + + const std::vector<GeometryTileFeatureStub> expected3 = { + { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 0}}}, {} }, + { {}, FeatureType::LineString, {{}}, {} }, + { {}, FeatureType::LineString, {{}}, {} } }; mbgl::util::mergeLines(input3); for (int i = 0; i < 3; i++) { - EXPECT_TRUE(input3[i].geometry == expected3[i].geometry); + EXPECT_TRUE(input3[i].geometry == expected3[i].getGeometries()); } } TEST(MergeLines, EmptyOuterGeometry) { - std::vector<mbgl::SymbolFeature> input = { - { FeatureType::LineString, {}, aaa, {}, {{0, 0}}, 0, 0 }, - }; + std::vector<mbgl::SymbolFeature> input; + input.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {}, {}, aaa, {}, 0 }); - const std::vector<mbgl::SymbolFeature> expected = input; + const std::vector<GeometryTileFeatureStub> expected = { { {}, FeatureType::LineString, {}, {} } }; mbgl::util::mergeLines(input); - EXPECT_EQ(expected[0].geometry, input[0].geometry); + EXPECT_EQ(input[0].geometry, expected[0].getGeometries()); } TEST(MergeLines, EmptyInnerGeometry) { - std::vector<mbgl::SymbolFeature> input = { - { FeatureType::LineString, {{}}, aaa, {}, {{0, 0}}, 0, 0 }, - }; + std::vector<mbgl::SymbolFeature> input; + input.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{}}, {}, aaa, {}, 0 }); - const std::vector<mbgl::SymbolFeature> expected = input; + const std::vector<GeometryTileFeatureStub> expected = { { {}, FeatureType::LineString, {{}}, {} } }; mbgl::util::mergeLines(input); - EXPECT_EQ(expected[0].geometry, input[0].geometry); + EXPECT_EQ(input[0].geometry, expected[0].getGeometries()); } |