[core] Add DDS support for {text,icon}-size (#8593)

* Update gl-js and generate style code

* Factor out packUint8Pair() helper function

* Draft implementation of DDS for {text,icon}-size

Ports https://github.com/mapbox/mapbox-gl-js/pull/4455

* Fix text-size/composite-function-line-placement test

* Refactor to PaintPropertyBinders-like strategy

* Dedupe gl::Program construction

* Use exponential function base for interpolation

* Dedupe coveringZoomStops method

* Fixup tests

* Fix CI errors (hidden within #if block)
This commit is contained in:
Anand Thakker
2017-04-06 15:29:59 -04:00
committed by GitHub
parent f9cc044357
commit 693c9f3641
37 changed files with 1009 additions and 219 deletions

View File

@@ -37,7 +37,7 @@ auto makeLayoutPropertySetters() {
result["icon-ignore-placement"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconIgnorePlacement>;
result["icon-optional"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconOptional>;
result["icon-rotation-alignment"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconRotationAlignment>;
result["icon-size"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconSize>;
result["icon-size"] = &setLayoutProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconSize>;
result["icon-text-fit"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<IconTextFitType>, &SymbolLayer::setIconTextFit>;
result["icon-text-fit-padding"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<std::array<float, 4>>, &SymbolLayer::setIconTextFitPadding>;
result["icon-image"] = &setLayoutProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setIconImage>;
@@ -49,7 +49,7 @@ auto makeLayoutPropertySetters() {
result["text-rotation-alignment"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextRotationAlignment>;
result["text-field"] = &setLayoutProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setTextField>;
result["text-font"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<std::vector<std::string>>, &SymbolLayer::setTextFont>;
result["text-size"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextSize>;
result["text-size"] = &setLayoutProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextSize>;
result["text-max-width"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxWidth>;
result["text-line-height"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLineHeight>;
result["text-letter-spacing"] = &setLayoutProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLetterSpacing>;

View File

@@ -45,6 +45,15 @@ public:
bool isDataDriven() const {
return value.template is<SourceFunction<T>>() || value.template is<CompositeFunction<T>>();
}
bool isZoomConstant() const {
return !value.template is<CameraFunction<T>>() && !value.template is<CompositeFunction<T>>();
}
template <class... Ts>
auto match(Ts&&... ts) const {
return value.match(std::forward<Ts>(ts)...);
}
template <typename Evaluator>
auto evaluate(const Evaluator& evaluator) const {

View File

@@ -28,7 +28,7 @@ public:
return s.evaluate(Value(double(zoom))).value_or(T());
});
}
friend bool operator==(const CameraFunction& lhs,
const CameraFunction& rhs) {
return lhs.stops == rhs.stops;

View File

@@ -59,9 +59,9 @@ public:
PropertyValue<AlignmentType> getIconRotationAlignment() const;
void setIconRotationAlignment(PropertyValue<AlignmentType>);
static PropertyValue<float> getDefaultIconSize();
PropertyValue<float> getIconSize() const;
void setIconSize(PropertyValue<float>);
static DataDrivenPropertyValue<float> getDefaultIconSize();
DataDrivenPropertyValue<float> getIconSize() const;
void setIconSize(DataDrivenPropertyValue<float>);
static PropertyValue<IconTextFitType> getDefaultIconTextFit();
PropertyValue<IconTextFitType> getIconTextFit() const;
@@ -107,9 +107,9 @@ public:
PropertyValue<std::vector<std::string>> getTextFont() const;
void setTextFont(PropertyValue<std::vector<std::string>>);
static PropertyValue<float> getDefaultTextSize();
PropertyValue<float> getTextSize() const;
void setTextSize(PropertyValue<float>);
static DataDrivenPropertyValue<float> getDefaultTextSize();
DataDrivenPropertyValue<float> getTextSize() const;
void setTextSize(DataDrivenPropertyValue<float>);
static PropertyValue<float> getDefaultTextMaxWidth();
PropertyValue<float> getTextMaxWidth() const;

View File

@@ -1526,11 +1526,11 @@ public class PropertyFactory {
/**
* Scale factor for icon. 1 is original size, 3 triples the 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>> iconSize(CameraFunction<Z, Float> function) {
public static <T> PropertyValue<Function<T, Float>> iconSize(Function<T, Float> function) {
return new LayoutPropertyValue<>("icon-size", function);
}
@@ -1802,11 +1802,11 @@ public class PropertyFactory {
/**
* 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>> textSize(CameraFunction<Z, Float> function) {
public static <T> PropertyValue<Function<T, Float>> textSize(Function<T, Float> function) {
return new LayoutPropertyValue<>("text-size", function);
}

View File

@@ -382,6 +382,114 @@ public class SymbolLayerTest extends BaseStyleTest {
}
@Test
public void testIconSizeAsIdentitySourceFunction() {
checkViewIsDisplayed(R.id.mapView);
Timber.i("icon-size");
assertNotNull(layer);
// Set
layer.setProperties(
iconSize(property("FeaturePropertyA", Stops.<Float>identity()))
);
// Verify
assertNotNull(layer.getIconSize());
assertNotNull(layer.getIconSize().getFunction());
assertEquals(SourceFunction.class, layer.getIconSize().getFunction().getClass());
assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconSize().getFunction()).getProperty());
assertEquals(IdentityStops.class, layer.getIconSize().getFunction().getStops().getClass());
}
@Test
public void testIconSizeAsExponentialSourceFunction() {
checkViewIsDisplayed(R.id.mapView);
Timber.i("icon-size");
assertNotNull(layer);
// Set
layer.setProperties(
iconSize(
property(
"FeaturePropertyA",
exponential(
stop(0.3f, iconSize(0.3f))
).withBase(0.5f)
)
)
);
// Verify
assertNotNull(layer.getIconSize());
assertNotNull(layer.getIconSize().getFunction());
assertEquals(SourceFunction.class, layer.getIconSize().getFunction().getClass());
assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconSize().getFunction()).getProperty());
assertEquals(ExponentialStops.class, layer.getIconSize().getFunction().getStops().getClass());
}
@Test
public void testIconSizeAsCategoricalSourceFunction() {
checkViewIsDisplayed(R.id.mapView);
Timber.i("icon-size");
assertNotNull(layer);
// Set
layer.setProperties(
iconSize(
property(
"FeaturePropertyA",
categorical(
stop(1.0f, iconSize(0.3f))
)
).withDefaultValue(iconSize(0.3f))
)
);
// Verify
assertNotNull(layer.getIconSize());
assertNotNull(layer.getIconSize().getFunction());
assertEquals(SourceFunction.class, layer.getIconSize().getFunction().getClass());
assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconSize().getFunction()).getProperty());
assertEquals(CategoricalStops.class, layer.getIconSize().getFunction().getStops().getClass());
assertNotNull(((SourceFunction) layer.getIconSize().getFunction()).getDefaultValue());
assertNotNull(((SourceFunction) layer.getIconSize().getFunction()).getDefaultValue().getValue());
assertEquals(0.3f, ((SourceFunction) layer.getIconSize().getFunction()).getDefaultValue().getValue());
}
@Test
public void testIconSizeAsCompositeFunction() {
checkViewIsDisplayed(R.id.mapView);
Timber.i("icon-size");
assertNotNull(layer);
// Set
layer.setProperties(
iconSize(
composite(
"FeaturePropertyA",
exponential(
stop(0, 0.3f, iconSize(0.9f))
).withBase(0.5f)
).withDefaultValue(iconSize(0.3f))
)
);
// Verify
assertNotNull(layer.getIconSize());
assertNotNull(layer.getIconSize().getFunction());
assertEquals(CompositeFunction.class, layer.getIconSize().getFunction().getClass());
assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getIconSize().getFunction()).getProperty());
assertEquals(ExponentialStops.class, layer.getIconSize().getFunction().getStops().getClass());
assertEquals(1, ((ExponentialStops) layer.getIconSize().getFunction().getStops()).size());
ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
(ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getIconSize().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 testIconTextFitAsConstant() {
checkViewIsDisplayed(R.id.mapView);
@@ -1074,6 +1182,114 @@ public class SymbolLayerTest extends BaseStyleTest {
}
@Test
public void testTextSizeAsIdentitySourceFunction() {
checkViewIsDisplayed(R.id.mapView);
Timber.i("text-size");
assertNotNull(layer);
// Set
layer.setProperties(
textSize(property("FeaturePropertyA", Stops.<Float>identity()))
);
// Verify
assertNotNull(layer.getTextSize());
assertNotNull(layer.getTextSize().getFunction());
assertEquals(SourceFunction.class, layer.getTextSize().getFunction().getClass());
assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextSize().getFunction()).getProperty());
assertEquals(IdentityStops.class, layer.getTextSize().getFunction().getStops().getClass());
}
@Test
public void testTextSizeAsExponentialSourceFunction() {
checkViewIsDisplayed(R.id.mapView);
Timber.i("text-size");
assertNotNull(layer);
// Set
layer.setProperties(
textSize(
property(
"FeaturePropertyA",
exponential(
stop(0.3f, textSize(0.3f))
).withBase(0.5f)
)
)
);
// Verify
assertNotNull(layer.getTextSize());
assertNotNull(layer.getTextSize().getFunction());
assertEquals(SourceFunction.class, layer.getTextSize().getFunction().getClass());
assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextSize().getFunction()).getProperty());
assertEquals(ExponentialStops.class, layer.getTextSize().getFunction().getStops().getClass());
}
@Test
public void testTextSizeAsCategoricalSourceFunction() {
checkViewIsDisplayed(R.id.mapView);
Timber.i("text-size");
assertNotNull(layer);
// Set
layer.setProperties(
textSize(
property(
"FeaturePropertyA",
categorical(
stop(1.0f, textSize(0.3f))
)
).withDefaultValue(textSize(0.3f))
)
);
// Verify
assertNotNull(layer.getTextSize());
assertNotNull(layer.getTextSize().getFunction());
assertEquals(SourceFunction.class, layer.getTextSize().getFunction().getClass());
assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextSize().getFunction()).getProperty());
assertEquals(CategoricalStops.class, layer.getTextSize().getFunction().getStops().getClass());
assertNotNull(((SourceFunction) layer.getTextSize().getFunction()).getDefaultValue());
assertNotNull(((SourceFunction) layer.getTextSize().getFunction()).getDefaultValue().getValue());
assertEquals(0.3f, ((SourceFunction) layer.getTextSize().getFunction()).getDefaultValue().getValue());
}
@Test
public void testTextSizeAsCompositeFunction() {
checkViewIsDisplayed(R.id.mapView);
Timber.i("text-size");
assertNotNull(layer);
// Set
layer.setProperties(
textSize(
composite(
"FeaturePropertyA",
exponential(
stop(0, 0.3f, textSize(0.9f))
).withBase(0.5f)
).withDefaultValue(textSize(0.3f))
)
);
// Verify
assertNotNull(layer.getTextSize());
assertNotNull(layer.getTextSize().getFunction());
assertEquals(CompositeFunction.class, layer.getTextSize().getFunction().getClass());
assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextSize().getFunction()).getProperty());
assertEquals(ExponentialStops.class, layer.getTextSize().getFunction().getStops().getClass());
assertEquals(1, ((ExponentialStops) layer.getTextSize().getFunction().getStops()).size());
ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
(ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getTextSize().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 testTextMaxWidthAsConstant() {
checkViewIsDisplayed(R.id.mapView);

View File

@@ -537,6 +537,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 *> *iconScale;
@@ -919,6 +928,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 *> *textFontSize;

View File

@@ -356,7 +356,7 @@ namespace mbgl {
- (void)setIconScale:(MGLStyleValue<NSNumber *> *)iconScale {
MGLAssertStyleLayerIsValid();
auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(iconScale);
auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(iconScale);
self.rawLayer->setIconSize(mbglValue);
}
@@ -365,9 +365,9 @@ namespace mbgl {
auto propertyValue = self.rawLayer->getIconSize();
if (propertyValue.isUndefined()) {
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultIconSize());
return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconSize());
}
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
}
- (void)setIconSize:(MGLStyleValue<NSNumber *> *)iconSize {
@@ -657,7 +657,7 @@ namespace mbgl {
- (void)setTextFontSize:(MGLStyleValue<NSNumber *> *)textFontSize {
MGLAssertStyleLayerIsValid();
auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(textFontSize);
auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(textFontSize);
self.rawLayer->setTextSize(mbglValue);
}
@@ -666,9 +666,9 @@ namespace mbgl {
auto propertyValue = self.rawLayer->getTextSize();
if (propertyValue.isUndefined()) {
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextSize());
return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextSize());
}
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
}
- (void)setTextSize:(MGLStyleValue<NSNumber *> *)textSize {

View File

@@ -405,7 +405,7 @@
MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
layer.iconScale = constantStyleValue;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getIconSize(), propertyValue,
@"Setting iconScale to a constant value should update icon-size.");
XCTAssertEqualObjects(layer.iconScale, constantStyleValue,
@@ -422,6 +422,29 @@
XCTAssertEqualObjects(layer.iconScale, functionStyleValue,
@"iconScale should round-trip camera functions.");
functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
layer.iconScale = functionStyleValue;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getIconSize(), propertyValue,
@"Setting iconScale to a source function should update icon-size.");
XCTAssertEqualObjects(layer.iconScale, functionStyleValue,
@"iconScale should round-trip source functions.");
functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
layer.iconScale = 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->getIconSize(), propertyValue,
@"Setting iconScale to a composite function should update icon-size.");
XCTAssertEqualObjects(layer.iconScale, functionStyleValue,
@"iconScale should round-trip composite functions.");
layer.iconScale = nil;
@@ -429,11 +452,6 @@
@"Unsetting iconScale should return icon-size to the default value.");
XCTAssertEqualObjects(layer.iconScale, defaultStyleValue,
@"iconScale should return the default value after being unset.");
functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
XCTAssertThrowsSpecificNamed(layer.iconScale = 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.iconScale = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
}
// icon-text-fit
@@ -952,7 +970,7 @@
MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
layer.textFontSize = constantStyleValue;
mbgl::style::PropertyValue<float> propertyValue = { 0xff };
mbgl::style::DataDrivenPropertyValue<float> propertyValue = { 0xff };
XCTAssertEqual(rawLayer->getTextSize(), propertyValue,
@"Setting textFontSize to a constant value should update text-size.");
XCTAssertEqualObjects(layer.textFontSize, constantStyleValue,
@@ -969,6 +987,29 @@
XCTAssertEqualObjects(layer.textFontSize, functionStyleValue,
@"textFontSize should round-trip camera functions.");
functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
layer.textFontSize = functionStyleValue;
mbgl::style::ExponentialStops<float> exponentialStops = { {{18, 0xff}}, 1.0 };
propertyValue = mbgl::style::SourceFunction<float> { "keyName", exponentialStops };
XCTAssertEqual(rawLayer->getTextSize(), propertyValue,
@"Setting textFontSize to a source function should update text-size.");
XCTAssertEqualObjects(layer.textFontSize, functionStyleValue,
@"textFontSize should round-trip source functions.");
functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
layer.textFontSize = 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->getTextSize(), propertyValue,
@"Setting textFontSize to a composite function should update text-size.");
XCTAssertEqualObjects(layer.textFontSize, functionStyleValue,
@"textFontSize should round-trip composite functions.");
layer.textFontSize = nil;
@@ -976,11 +1017,6 @@
@"Unsetting textFontSize should return text-size to the default value.");
XCTAssertEqualObjects(layer.textFontSize, defaultStyleValue,
@"textFontSize should return the default value after being unset.");
functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
XCTAssertThrowsSpecificNamed(layer.textFontSize = 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.textFontSize = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
}
// text-ignore-placement

View File

@@ -8,6 +8,12 @@
#include <mbgl/gl/attribute.hpp>
#include <mbgl/gl/uniform.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/programs/binary_program.hpp>
#include <mbgl/programs/program_parameters.hpp>
#include <mbgl/shaders/shaders.hpp>
#include <string>
namespace mbgl {
@@ -37,6 +43,61 @@ public:
attributeLocations(Attributes::loadNamedLocations(binaryProgram)),
uniformsState(Uniforms::loadNamedLocations(binaryProgram)) {
}
static Program createProgram(gl::Context& context,
const ProgramParameters& programParameters,
const char* name,
const char* vertexSource_,
const char* fragmentSource_) {
#if MBGL_HAS_BINARY_PROGRAMS
if (!programParameters.cacheDir.empty() && context.supportsProgramBinaries()) {
const std::string vertexSource =
shaders::vertexSource(programParameters, vertexSource_);
const std::string fragmentSource =
shaders::fragmentSource(programParameters, fragmentSource_);
const std::string cachePath =
shaders::programCachePath(programParameters, name);
const std::string identifier =
shaders::programIdentifier(vertexSource, fragmentSource_);
try {
if (auto cachedBinaryProgram = util::readFile(cachePath)) {
const BinaryProgram binaryProgram(std::move(*cachedBinaryProgram));
if (binaryProgram.identifier() == identifier) {
return Program { context, binaryProgram };
} else {
Log::Warning(Event::OpenGL,
"Cached program %s changed. Recompilation required.",
name);
}
}
} catch (std::runtime_error& error) {
Log::Warning(Event::OpenGL, "Could not load cached program: %s",
error.what());
}
// Compile the shader
Program result{ context, vertexSource, fragmentSource };
try {
if (const auto binaryProgram =
result.template get<BinaryProgram>(context, identifier)) {
util::write_file(cachePath, binaryProgram->serialize());
Log::Warning(Event::OpenGL, "Caching program in: %s", cachePath.c_str());
}
} catch (std::runtime_error& error) {
Log::Warning(Event::OpenGL, "Failed to cache program: %s", error.what());
}
return std::move(result);
}
#endif
(void)name;
return Program {
context, shaders::vertexSource(programParameters, vertexSource_),
shaders::fragmentSource(programParameters, fragmentSource_)
};
}
template <class BinaryProgram>
optional<BinaryProgram> get(Context& context, const std::string& identifier) const {

View File

@@ -10,6 +10,7 @@ SymbolInstance::SymbolInstance(Anchor& anchor,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
const PositionedIcon& shapedIcon,
const SymbolLayoutProperties::Evaluated& layout,
const float layoutTextSize,
const bool addToBuffers,
const uint32_t index_,
const float textBoxScale,
@@ -26,15 +27,18 @@ SymbolInstance::SymbolInstance(Anchor& anchor,
hasText(shapedTextOrientations.first || shapedTextOrientations.second),
hasIcon(shapedIcon),
// 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),
featureIndex(featureIndex_) {
// Create the quads used for rendering the icon and glyphs.
if (addToBuffers) {
if (shapedIcon) {
iconQuad = getIconQuad(anchor, shapedIcon, line, layout, iconPlacement, shapedTextOrientations.first);
iconQuad = getIconQuad(anchor, shapedIcon, line, layout, layoutTextSize, iconPlacement, shapedTextOrientations.first);
}
if (shapedTextOrientations.first) {
auto quads = getGlyphQuads(anchor, shapedTextOrientations.first, textBoxScale, line, layout, textPlacement, face);
@@ -55,6 +59,8 @@ SymbolInstance::SymbolInstance(Anchor& anchor,
} else {
writingModes = WritingModeType::None;
}
}
} // namespace mbgl

View File

@@ -17,6 +17,7 @@ public:
const std::pair<Shaping, Shaping>& shapedTextOrientations,
const PositionedIcon& shapedIcon,
const style::SymbolLayoutProperties::Evaluated&,
const float layoutTextSize,
const bool inside,
const uint32_t index,
const float textBoxScale,

View File

@@ -50,7 +50,10 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
mode(parameters.mode),
spriteAtlasMapIndex(_spriteAtlasMapIndex),
tileSize(util::tileSize * overscaling),
tilePixelRatio(float(util::EXTENT) / tileSize) {
tilePixelRatio(float(util::EXTENT) / tileSize),
textSize(layers.at(0)->as<SymbolLayer>()->impl->layout.unevaluated.get<TextSize>()),
iconSize(layers.at(0)->as<SymbolLayer>()->impl->layout.unevaluated.get<IconSize>())
{
const SymbolLayer::Impl& leader = *layers.at(0)->as<SymbolLayer>()->impl;
@@ -77,11 +80,6 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
layout.get<TextPitchAlignment>() = layout.get<TextRotationAlignment>();
}
textMaxSize = leader.layout.evaluate<TextSize>(PropertyEvaluationParameters(18));
layout.get<IconSize>() = leader.layout.evaluate<IconSize>(PropertyEvaluationParameters(zoom + 1));
layout.get<TextSize>() = leader.layout.evaluate<TextSize>(PropertyEvaluationParameters(zoom + 1));
const bool hasText = has<TextField>(layout) && !layout.get<TextFont>().empty();
const bool hasIcon = has<IconImage>(layout);
@@ -298,11 +296,20 @@ void SymbolLayout::addFeature(const std::size_t index,
const GlyphPositions& glyphs) {
const float minScale = 0.5f;
const float glyphSize = 24.0f;
const float fontScale = layout.get<TextSize>() / glyphSize;
const float layoutTextSize = layout.evaluate<TextSize>(zoom + 1, feature);
const float layoutIconSize = layout.evaluate<IconSize>(zoom + 1, feature);
// To reduce the number of labels that jump around when zooming we need
// to use a text-size value that is the same for all zoom levels.
// This calculates text-size at a high zoom level so that all tiles can
// use the same value when calculating anchor positions.
const float textMaxSize = layout.evaluate<TextSize>(18, feature);
const float fontScale = layoutTextSize / glyphSize;
const float textBoxScale = tilePixelRatio * fontScale;
const float textMaxBoxScale = tilePixelRatio * textMaxSize / glyphSize;
const float iconBoxScale = tilePixelRatio * layout.get<IconSize>();
const float iconBoxScale = tilePixelRatio * layoutIconSize;
const float symbolSpacing = tilePixelRatio * layout.get<SymbolSpacing>();
const bool avoidEdges = layout.get<SymbolAvoidEdges>() && layout.get<SymbolPlacement>() != SymbolPlacementType::Line;
const float textPadding = layout.get<TextPadding>() * tilePixelRatio;
@@ -316,7 +323,7 @@ void SymbolLayout::addFeature(const std::size_t index,
: layout.get<SymbolPlacement>();
const float textRepeatDistance = symbolSpacing / 2;
IndexedSubfeature indexedFeature = {feature.index, sourceLayerName, bucketName, symbolInstances.size()};
auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) {
// https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers
// +-------------------+ Symbols with anchors located on tile edges
@@ -338,7 +345,9 @@ void SymbolLayout::addFeature(const std::size_t index,
const bool addToBuffers = mode == MapMode::Still || withinPlus0;
symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon, layout.evaluate(zoom, feature), addToBuffers, symbolInstances.size(),
symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon,
layout.evaluate(zoom, feature), layoutTextSize,
addToBuffers, symbolInstances.size(),
textBoxScale, textPadding, textPlacement,
iconBoxScale, iconPadding, iconPlacement,
glyphs, indexedFeature, index);
@@ -413,7 +422,7 @@ bool SymbolLayout::anchorIsTooClose(const std::u16string& text, const float repe
}
std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) {
auto bucket = std::make_unique<SymbolBucket>(layout, layerPaintProperties, zoom, sdfIcons, iconsNeedLinear);
auto bucket = std::make_unique<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear);
// Calculate which labels can be shown and when they can be shown and
// create the bufers used for rendering.
@@ -477,6 +486,7 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile)
iconScale = util::max(iconScale, glyphScale);
}
const auto& feature = features.at(symbolInstance.featureIndex);
// Insert final placement into collision tree and add glyphs/icons to buffers
@@ -486,7 +496,7 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile)
if (glyphScale < collisionTile.maxScale) {
for (const auto& symbol : symbolInstance.glyphQuads) {
addSymbol(
bucket->text, symbol, placementZoom,
bucket->text, *bucket->textSizeBinder, symbol, feature, placementZoom,
keepUpright, textPlacement, collisionTile.config.angle, symbolInstance.writingModes);
}
}
@@ -497,12 +507,11 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile)
collisionTile.insertFeature(symbolInstance.iconCollisionFeature, iconScale, layout.get<IconIgnorePlacement>());
if (iconScale < collisionTile.maxScale && symbolInstance.iconQuad) {
addSymbol(
bucket->icon, *symbolInstance.iconQuad, placementZoom,
bucket->icon, *bucket->iconSizeBinder, *symbolInstance.iconQuad, feature, placementZoom,
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());
@@ -518,7 +527,9 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile)
template <typename Buffer>
void SymbolLayout::addSymbol(Buffer& buffer,
SymbolSizeBinder& sizeBinder,
const SymbolQuad& symbol,
const SymbolFeature& feature,
const float placementZoom,
const bool keepUpright,
const style::SymbolPlacementType placement,
@@ -580,6 +591,8 @@ void SymbolLayout::addSymbol(Buffer& buffer,
minZoom, maxZoom, placementZoom, glyphAngle));
buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, br, tex.x + tex.w, tex.y + tex.h,
minZoom, maxZoom, placementZoom, glyphAngle));
sizeBinder.populateVertexVector(feature);
// add the two triangles, referencing the four coordinates we just inserted.
buffer.triangles.emplace_back(index + 0, index + 1, index + 2);

View File

@@ -6,6 +6,7 @@
#include <mbgl/layout/symbol_instance.hpp>
#include <mbgl/text/bidi.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
#include <mbgl/programs/symbol_program.hpp>
#include <memory>
#include <map>
@@ -64,9 +65,15 @@ private:
// Adds placed items to the buffer.
template <typename Buffer>
void addSymbol(Buffer&, const SymbolQuad&, float scale,
const bool keepUpright, const style::SymbolPlacementType, const float placementAngle,
WritingModeType writingModes);
void addSymbol(Buffer&,
SymbolSizeBinder& sizeBinder,
const SymbolQuad&,
const SymbolFeature& feature,
float scale,
const bool keepUpright,
const style::SymbolPlacementType,
const float placementAngle,
WritingModeType writingModes);
const std::string sourceLayerName;
const std::string bucketName;
@@ -75,7 +82,6 @@ private:
const MapMode mode;
style::SymbolLayoutProperties::PossiblyEvaluated layout;
float textMaxSize;
uintptr_t spriteAtlasMapIndex; // Actually a pointer to the SpriteAtlas for this symbol's layer, but don't use it from worker threads!
@@ -84,6 +90,9 @@ private:
bool sdfIcons = false;
bool iconsNeedLinear = false;
style::TextSize::UnevaluatedType textSize;
style::IconSize::UnevaluatedType iconSize;
std::vector<SymbolInstance> symbolInstances;
std::vector<SymbolFeature> features;

View File

@@ -8,6 +8,16 @@
namespace mbgl {
namespace attributes {
/*
* Pack a pair of values, interpreted as uint8's, into a single float.
* Used to conserve vertex attributes. Values are unpacked in the vertex
* shader using the `unpack_float()` function, defined in _prelude.vertex.glsl.
*/
template <typename T>
inline uint16_t packUint8Pair(T a, T b) {
return static_cast<uint16_t>(a) * 256 + static_cast<uint16_t>(b);
}
// Layout attributes
MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_pos);
@@ -15,10 +25,15 @@ MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_extrude);
MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_pos_offset);
MBGL_DEFINE_ATTRIBUTE(uint16_t, 2, a_texture_pos);
template <std::size_t N>
template <typename T, std::size_t N>
struct a_data {
static auto name() { return "a_data"; }
using Type = gl::Attribute<uint8_t, N>;
using Type = gl::Attribute<T, N>;
};
struct a_size {
static auto name() { return "a_size"; }
using Type = gl::Attribute<uint16_t, 3>;
};
template <std::size_t N>

View File

@@ -18,7 +18,7 @@ MBGL_DEFINE_UNIFORM_SCALAR(float, u_maxzoom);
using CollisionBoxAttributes = gl::Attributes<
attributes::a_pos,
attributes::a_extrude,
attributes::a_data<2>>;
attributes::a_data<uint8_t, 2>>;
class CollisionBoxProgram : public Program<
shaders::collision_box,

View File

@@ -32,7 +32,7 @@ MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_gl_units_to_pixels);
struct LineLayoutAttributes : gl::Attributes<
attributes::a_pos,
attributes::a_data<4>>
attributes::a_data<uint8_t, 4>>
{};
class LineProgram : public Program<

View File

@@ -34,55 +34,12 @@ public:
ProgramType program;
Program(gl::Context& context, const ProgramParameters& programParameters)
: program([&] {
#if MBGL_HAS_BINARY_PROGRAMS
if (!programParameters.cacheDir.empty() && context.supportsProgramBinaries()) {
const std::string vertexSource =
shaders::vertexSource(programParameters, Shaders::vertexSource);
const std::string fragmentSource =
shaders::fragmentSource(programParameters, Shaders::fragmentSource);
const std::string cachePath =
shaders::programCachePath(programParameters, Shaders::name);
const std::string identifier =
shaders::programIdentifier(vertexSource, fragmentSource);
try {
if (auto cachedBinaryProgram = util::readFile(cachePath)) {
const BinaryProgram binaryProgram(std::move(*cachedBinaryProgram));
if (binaryProgram.identifier() == identifier) {
return ProgramType{ context, binaryProgram };
} else {
Log::Warning(Event::OpenGL,
"Cached program %s changed. Recompilation required.",
Shaders::name);
}
}
} catch (std::runtime_error& error) {
Log::Warning(Event::OpenGL, "Could not load cached program: %s",
error.what());
}
// Compile the shader
ProgramType result{ context, vertexSource, fragmentSource };
try {
if (const auto binaryProgram =
result.template get<BinaryProgram>(context, identifier)) {
util::write_file(cachePath, binaryProgram->serialize());
Log::Warning(Event::OpenGL, "Caching program in: %s", cachePath.c_str());
}
} catch (std::runtime_error& error) {
Log::Warning(Event::OpenGL, "Failed to cache program: %s", error.what());
}
return std::move(result);
}
#endif
return ProgramType{
context, shaders::vertexSource(programParameters, Shaders::vertexSource),
shaders::fragmentSource(programParameters, Shaders::fragmentSource)
};
}()) {
: program(ProgramType::createProgram(
context,
programParameters,
Shaders::name,
Shaders::vertexSource,
Shaders::fragmentSource)) {
}
template <class DrawMode>

View File

@@ -3,6 +3,7 @@
#include <mbgl/map/transform_state.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
#include <mbgl/util/enum.hpp>
#include <mbgl/math/clamp.hpp>
namespace mbgl {
@@ -10,51 +11,66 @@ using namespace style;
static_assert(sizeof(SymbolLayoutVertex) == 16, "expected SymbolLayoutVertex size");
std::unique_ptr<SymbolSizeBinder> SymbolSizeBinder::create(const float tileZoom,
const style::DataDrivenPropertyValue<float>& sizeProperty,
const float defaultValue) {
return sizeProperty.match(
[&] (const style::CompositeFunction<float>& function) -> std::unique_ptr<SymbolSizeBinder> {
return std::make_unique<CompositeFunctionSymbolSizeBinder>(tileZoom, function, defaultValue);
},
[&] (const style::SourceFunction<float>& function) {
return std::make_unique<SourceFunctionSymbolSizeBinder>(tileZoom, function, defaultValue);
},
[&] (const auto& value) -> std::unique_ptr<SymbolSizeBinder> {
return std::make_unique<ConstantSymbolSizeBinder>(tileZoom, value, defaultValue);
}
);
}
template <class Values, class...Args>
Values makeValues(const style::SymbolPropertyValues& values,
Values makeValues(const bool isText,
const style::SymbolPropertyValues& values,
const Size& texsize,
const std::array<float, 2>& pixelsToGLUnits,
const RenderTile& tile,
const TransformState& state,
Args&&... args) {
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);
extrudeScale.fill(tile.id.pixelsToTileUnits(1, state.getZoom()));
} else {
extrudeScale = {{
pixelsToGLUnits[0] * scale * state.getCameraToCenterDistance(),
pixelsToGLUnits[1] * scale * state.getCameraToCenterDistance()
pixelsToGLUnits[0] * state.getCameraToCenterDistance(),
pixelsToGLUnits[1] * state.getCameraToCenterDistance()
}};
}
// adjust min/max zooms for variable font sies
float zoomAdjust = std::log(values.paintSize / values.layoutSize) / std::log(2);
return Values {
uniforms::u_matrix::Value{ tile.translatedMatrix(values.translate,
values.translateAnchor,
state) },
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) },
uniforms::u_zoom::Value{ float(state.getZoom()) },
uniforms::u_rotate_with_map::Value{ values.rotationAlignment == AlignmentType::Map },
uniforms::u_texture::Value{ 0 },
uniforms::u_fadetexture::Value{ 1 },
uniforms::u_is_text::Value{ isText },
std::forward<Args>(args)...
};
}
SymbolIconProgram::UniformValues
SymbolIconProgram::uniformValues(const style::SymbolPropertyValues& values,
SymbolIconProgram::uniformValues(const bool isText,
const style::SymbolPropertyValues& values,
const Size& texsize,
const std::array<float, 2>& pixelsToGLUnits,
const RenderTile& tile,
const TransformState& state)
{
return makeValues<SymbolIconProgram::UniformValues>(
isText,
values,
texsize,
pixelsToGLUnits,
@@ -64,26 +80,26 @@ SymbolIconProgram::uniformValues(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,
const SymbolSDFPart part)
typename SymbolSDFProgram<PaintProperties>::UniformValues SymbolSDFProgram<PaintProperties>::uniformValues(
const bool isText,
const style::SymbolPropertyValues& values,
const Size& texsize,
const std::array<float, 2>& pixelsToGLUnits,
const RenderTile& tile,
const TransformState& state,
const SymbolSDFPart part)
{
const float scale = values.paintSize / values.sdfScale;
const float gammaScale = scale * (values.pitchAlignment == AlignmentType::Map
const float gammaScale = (values.pitchAlignment == AlignmentType::Map
? std::cos(state.getPitch())
: 1.0) * state.getCameraToCenterDistance();
return makeValues<SymbolSDFProgram<PaintProperties>::UniformValues>(
isText,
values,
texsize,
pixelsToGLUnits,
tile,
state,
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() },

View File

@@ -1,6 +1,10 @@
#pragma once
#include <mbgl/programs/program.hpp>
#include <mbgl/gl/context.hpp>
#include <mbgl/gl/program.hpp>
#include <mbgl/math/clamp.hpp>
#include <mbgl/util/interpolate.hpp>
#include <mbgl/programs/attributes.hpp>
#include <mbgl/programs/uniforms.hpp>
#include <mbgl/shaders/symbol_icon.hpp>
@@ -10,6 +14,7 @@
#include <mbgl/style/layers/symbol_layer_properties.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
#include <cmath>
#include <array>
@@ -30,14 +35,19 @@ MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_texture);
MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_fadetexture);
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);
MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_text);
MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_size_zoom_constant);
MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_size_feature_constant);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_size_t);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_size);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_layout_size);
} // namespace uniforms
struct SymbolLayoutAttributes : gl::Attributes<
attributes::a_pos_offset,
attributes::a_texture_pos,
attributes::a_data<4>>
attributes::a_data<uint16_t, 4>>
{
static Vertex vertex(Point<float> a,
Point<float> o,
@@ -57,19 +67,324 @@ struct SymbolLayoutAttributes : gl::Attributes<
}},
{{
static_cast<uint16_t>(tx / 4),
static_cast<uint16_t>(ty / 4)
}},
{{
static_cast<uint8_t>(labelminzoom * 10), // 1/10 zoom levels: z16 == 160
static_cast<uint8_t>(labelangle),
static_cast<uint8_t>(minzoom * 10),
static_cast<uint8_t>(::fmin(maxzoom, 25) * 10)
static_cast<uint16_t>(ty / 4),
mbgl::attributes::packUint8Pair(
static_cast<uint8_t>(labelminzoom * 10), // 1/10 zoom levels: z16 == 160
static_cast<uint8_t>(labelangle)
),
mbgl::attributes::packUint8Pair(
static_cast<uint8_t>(minzoom * 10),
static_cast<uint8_t>(::fmin(maxzoom, 25) * 10)
)
}}
};
}
};
class SymbolSizeAttributes : public gl::Attributes<attributes::a_size> {
public:
using Attribute = attributes::a_size::Type;
};
class SymbolIconProgram : public Program<
// Mimic the PaintPropertyBinder technique specifically for the {text,icon}-size layout properties
// in order to provide a 'custom' scheme for encoding the necessary attribute data. As with
// PaintPropertyBinder, SymbolSizeBinder is an abstract class whose implementations handle the
// particular attribute & uniform logic needed by each possible type of the {Text,Icon}Size properties.
class SymbolSizeBinder {
public:
using Uniforms = gl::Uniforms<
uniforms::u_is_size_zoom_constant,
uniforms::u_is_size_feature_constant,
uniforms::u_size_t,
uniforms::u_size,
uniforms::u_layout_size>;
using UniformValues = Uniforms::Values;
static std::unique_ptr<SymbolSizeBinder> create(const float tileZoom,
const style::DataDrivenPropertyValue<float>& sizeProperty,
const float defaultValue);
virtual SymbolSizeAttributes::Bindings attributeBindings(const style::PossiblyEvaluatedPropertyValue<float> currentValue) const = 0;
virtual void populateVertexVector(const GeometryTileFeature& feature) = 0;
virtual UniformValues uniformValues(float currentZoom) const = 0;
virtual void upload(gl::Context&) = 0;
};
// Return the smallest range of stops that covers the interval [lowerZoom, upperZoom]
template <class Stops>
Range<float> getCoveringStops(Stops s, float lowerZoom, float upperZoom) {
assert(!s.stops.empty());
auto minIt = s.stops.lower_bound(lowerZoom);
auto maxIt = s.stops.lower_bound(upperZoom);
// lower_bound yields first element >= lowerZoom, but we want the *last*
// element <= lowerZoom, so if we found a stop > lowerZoom, back up by one.
if (minIt != s.stops.begin() && minIt->first > lowerZoom) {
minIt--;
}
return Range<float> {
minIt == s.stops.end() ? s.stops.rbegin()->first : minIt->first,
maxIt == s.stops.end() ? s.stops.rbegin()->first : maxIt->first
};
}
class ConstantSymbolSizeBinder : public SymbolSizeBinder {
public:
using PropertyValue = variant<float, style::CameraFunction<float>>;
ConstantSymbolSizeBinder(const float /*tileZoom*/, const float& size, const float /*defaultValue*/)
: layoutSize(size) {}
ConstantSymbolSizeBinder(const float /*tileZoom*/, const style::Undefined&, const float defaultValue)
: layoutSize(defaultValue) {}
ConstantSymbolSizeBinder(const float tileZoom, const style::CameraFunction<float>& function_, const float /*defaultValue*/)
: layoutSize(function_.evaluate(tileZoom + 1)) {
function_.stops.match(
[&] (const style::ExponentialStops<float>& stops) {
coveringRanges = std::make_tuple(
getCoveringStops(stops, tileZoom, tileZoom + 1),
Range<float> { function_.evaluate(tileZoom), function_.evaluate(tileZoom + 1) }
);
functionInterpolationBase = stops.base;
},
[&] (const style::IntervalStops<float>&) {
function = function_;
}
);
}
SymbolSizeAttributes::Bindings attributeBindings(const style::PossiblyEvaluatedPropertyValue<float>) const override {
return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::ConstantBinding {{{0, 0, 0}}} };
}
void upload(gl::Context&) override {}
void populateVertexVector(const GeometryTileFeature&) override {};
UniformValues uniformValues(float currentZoom) const override {
float size = layoutSize;
bool isZoomConstant = !(coveringRanges || function);
if (coveringRanges) {
// Even though we could get the exact value of the camera function
// at z = currentZoom, we intentionally do not: instead, we interpolate
// between the camera function values at a pair of zoom stops covering
// [tileZoom, tileZoom + 1] in order to be consistent with this
// restriction on composite functions.
const Range<float>& zoomLevels = std::get<0>(*coveringRanges);
const Range<float>& sizeLevels = std::get<1>(*coveringRanges);
float t = util::clamp(
util::interpolationFactor(*functionInterpolationBase, zoomLevels, currentZoom),
0.0f, 1.0f
);
size = sizeLevels.min + t * (sizeLevels.max - sizeLevels.min);
} else if (function) {
size = function->evaluate(currentZoom);
}
return UniformValues {
uniforms::u_is_size_zoom_constant::Value{ isZoomConstant },
uniforms::u_is_size_feature_constant::Value{ true },
uniforms::u_size_t::Value{ 0.0f }, // unused
uniforms::u_size::Value{ size },
uniforms::u_layout_size::Value{ layoutSize }
};
}
float layoutSize;
// used for exponential functions
optional<std::tuple<Range<float>, Range<float>>> coveringRanges;
optional<float> functionInterpolationBase;
// used for interval functions
optional<style::CameraFunction<float>> function;
};
class SourceFunctionSymbolSizeBinder : public SymbolSizeBinder {
public:
using Vertex = gl::detail::Vertex<gl::Attribute<uint16_t, 1>>;
using VertexVector = gl::VertexVector<Vertex>;
using VertexBuffer = gl::VertexBuffer<Vertex>;
SourceFunctionSymbolSizeBinder(const float /*tileZoom*/, const style::SourceFunction<float>& function_, const float defaultValue_)
: function(function_),
defaultValue(defaultValue_) {
}
SymbolSizeAttributes::Bindings attributeBindings(const style::PossiblyEvaluatedPropertyValue<float> currentValue) const override {
if (currentValue.isConstant()) {
return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::ConstantBinding {{{0, 0, 0}}} };
}
return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::variableBinding(*buffer, 0, 1) };
}
void populateVertexVector(const GeometryTileFeature& feature) override {
const auto sizeVertex = Vertex {
{{
static_cast<uint16_t>(function.evaluate(feature, defaultValue) * 10)
}}
};
vertices.emplace_back(sizeVertex);
vertices.emplace_back(sizeVertex);
vertices.emplace_back(sizeVertex);
vertices.emplace_back(sizeVertex);
};
UniformValues uniformValues(float) const override {
return UniformValues {
uniforms::u_is_size_zoom_constant::Value{ true },
uniforms::u_is_size_feature_constant::Value{ false },
uniforms::u_size_t::Value{ 0.0f }, // unused
uniforms::u_size::Value{ 0.0f }, // unused
uniforms::u_layout_size::Value{ 0.0f } // unused
};
}
void upload(gl::Context& context) override {
buffer = VertexBuffer { context.createVertexBuffer(std::move(vertices)) };
}
const style::SourceFunction<float>& function;
const float defaultValue;
VertexVector vertices;
optional<VertexBuffer> buffer;
};
class CompositeFunctionSymbolSizeBinder: public SymbolSizeBinder {
public:
using Vertex = SymbolSizeAttributes::Vertex;
using VertexVector = gl::VertexVector<Vertex>;
using VertexBuffer = gl::VertexBuffer<Vertex>;
CompositeFunctionSymbolSizeBinder(const float tileZoom, const style::CompositeFunction<float>& function_, const float defaultValue_)
: function(function_),
defaultValue(defaultValue_),
layoutZoom(tileZoom + 1),
coveringZoomStops(function.stops.match(
[&] (const auto& stops) {
return getCoveringStops(stops, tileZoom, tileZoom + 1); }))
{}
SymbolSizeAttributes::Bindings attributeBindings(const style::PossiblyEvaluatedPropertyValue<float> currentValue) const override {
if (currentValue.isConstant()) {
return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::ConstantBinding {{{0, 0, 0}}} };
}
return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::variableBinding(*buffer, 0) };
}
void populateVertexVector(const GeometryTileFeature& feature) override {
const auto sizeVertex = Vertex {
{{
static_cast<uint16_t>(function.evaluate(coveringZoomStops.min, feature, defaultValue) * 10),
static_cast<uint16_t>(function.evaluate(coveringZoomStops.max, feature, defaultValue) * 10),
static_cast<uint16_t>(function.evaluate(layoutZoom, feature, defaultValue) * 10)
}}
};
vertices.emplace_back(sizeVertex);
vertices.emplace_back(sizeVertex);
vertices.emplace_back(sizeVertex);
vertices.emplace_back(sizeVertex);
};
UniformValues uniformValues(float currentZoom) const override {
float sizeInterpolationT = util::clamp(
util::interpolationFactor(1.0f, coveringZoomStops, currentZoom),
0.0f, 1.0f
);
return UniformValues {
uniforms::u_is_size_zoom_constant::Value{ false },
uniforms::u_is_size_feature_constant::Value{ false },
uniforms::u_size_t::Value{ sizeInterpolationT },
uniforms::u_size::Value{ 0.0f }, // unused
uniforms::u_layout_size::Value{ 0.0f } // unused
};
}
void upload(gl::Context& context) override {
buffer = VertexBuffer { context.createVertexBuffer(std::move(vertices)) };
}
const style::CompositeFunction<float>& function;
const float defaultValue;
float layoutZoom;
Range<float> coveringZoomStops;
VertexVector vertices;
optional<VertexBuffer> buffer;
};
template <class Shaders,
class Primitive,
class LayoutAttrs,
class Uniforms,
class PaintProperties>
class SymbolProgram {
public:
using LayoutAttributes = LayoutAttrs;
using LayoutVertex = typename LayoutAttributes::Vertex;
using LayoutAndSizeAttributes = gl::ConcatenateAttributes<LayoutAttributes, SymbolSizeAttributes>;
using PaintPropertyBinders = typename PaintProperties::Binders;
using PaintAttributes = typename PaintPropertyBinders::Attributes;
using Attributes = gl::ConcatenateAttributes<LayoutAndSizeAttributes, PaintAttributes>;
using UniformValues = typename Uniforms::Values;
using SizeUniforms = typename SymbolSizeBinder::Uniforms;
using PaintUniforms = typename PaintPropertyBinders::Uniforms;
using AllUniforms = gl::ConcatenateUniforms<Uniforms, gl::ConcatenateUniforms<SizeUniforms, PaintUniforms>>;
using ProgramType = gl::Program<Primitive, Attributes, AllUniforms>;
ProgramType program;
SymbolProgram(gl::Context& context, const ProgramParameters& programParameters)
: program(ProgramType::createProgram(
context,
programParameters,
Shaders::name,
Shaders::vertexSource,
Shaders::fragmentSource)) {
}
template <class DrawMode>
void draw(gl::Context& context,
DrawMode drawMode,
gl::DepthMode depthMode,
gl::StencilMode stencilMode,
gl::ColorMode colorMode,
UniformValues&& uniformValues,
const gl::VertexBuffer<LayoutVertex>& layoutVertexBuffer,
const SymbolSizeBinder& symbolSizeBinder,
const style::PossiblyEvaluatedPropertyValue<float>& currentSizeValue,
const gl::IndexBuffer<DrawMode>& indexBuffer,
const gl::SegmentVector<Attributes>& segments,
const PaintPropertyBinders& paintPropertyBinders,
const typename PaintProperties::Evaluated& currentProperties,
float currentZoom) {
program.draw(
context,
std::move(drawMode),
std::move(depthMode),
std::move(stencilMode),
std::move(colorMode),
uniformValues
.concat(symbolSizeBinder.uniformValues(currentZoom))
.concat(paintPropertyBinders.uniformValues(currentZoom)),
LayoutAttributes::allVariableBindings(layoutVertexBuffer)
.concat(symbolSizeBinder.attributeBindings(currentSizeValue))
.concat(paintPropertyBinders.attributeBindings(currentProperties)),
indexBuffer,
segments
);
}
};
class SymbolIconProgram : public SymbolProgram<
shaders::symbol_icon,
gl::Triangle,
SymbolLayoutAttributes,
@@ -80,13 +395,15 @@ class SymbolIconProgram : public Program<
uniforms::u_zoom,
uniforms::u_rotate_with_map,
uniforms::u_texture,
uniforms::u_fadetexture>,
uniforms::u_fadetexture,
uniforms::u_is_text>,
style::IconPaintProperties>
{
public:
using Program::Program;
using SymbolProgram::SymbolProgram;
static UniformValues uniformValues(const style::SymbolPropertyValues&,
static UniformValues uniformValues(const bool isText,
const style::SymbolPropertyValues&,
const Size& texsize,
const std::array<float, 2>& pixelsToGLUnits,
const RenderTile&,
@@ -99,7 +416,7 @@ enum class SymbolSDFPart {
};
template <class PaintProperties>
class SymbolSDFProgram : public Program<
class SymbolSDFProgram : public SymbolProgram<
shaders::symbol_sdf,
gl::Triangle,
SymbolLayoutAttributes,
@@ -111,7 +428,7 @@ class SymbolSDFProgram : public Program<
uniforms::u_rotate_with_map,
uniforms::u_texture,
uniforms::u_fadetexture,
uniforms::u_font_scale,
uniforms::u_is_text,
uniforms::u_gamma_scale,
uniforms::u_pitch,
uniforms::u_bearing,
@@ -121,7 +438,7 @@ class SymbolSDFProgram : public Program<
PaintProperties>
{
public:
using BaseProgram = Program<shaders::symbol_sdf,
using BaseProgram = SymbolProgram<shaders::symbol_sdf,
gl::Triangle,
SymbolLayoutAttributes,
gl::Uniforms<
@@ -132,7 +449,7 @@ public:
uniforms::u_rotate_with_map,
uniforms::u_texture,
uniforms::u_fadetexture,
uniforms::u_font_scale,
uniforms::u_is_text,
uniforms::u_gamma_scale,
uniforms::u_pitch,
uniforms::u_bearing,
@@ -147,7 +464,8 @@ public:
using BaseProgram::BaseProgram;
static UniformValues uniformValues(const style::SymbolPropertyValues&,
static UniformValues uniformValues(const bool isText,
const style::SymbolPropertyValues&,
const Size& texsize,
const std::array<float, 2>& pixelsToGLUnits,
const RenderTile&,

View File

@@ -33,6 +33,7 @@ void Painter::renderSymbol(PaintParameters& parameters,
auto draw = [&] (auto& program,
auto&& uniformValues,
const auto& buffers,
const auto& symbolSizeBinder,
const SymbolPropertyValues& values_,
const auto& binders,
const auto& paintProperties)
@@ -52,6 +53,8 @@ void Painter::renderSymbol(PaintParameters& parameters,
colorModeForRenderPass(),
std::move(uniformValues),
*buffers.vertexBuffer,
*symbolSizeBinder,
values_.layoutSize,
*buffers.indexBuffer,
buffers.segments,
binders,
@@ -65,7 +68,9 @@ void Painter::renderSymbol(PaintParameters& parameters,
auto paintPropertyValues = layer.impl->iconPaintProperties();
SpriteAtlas& atlas = *layer.impl->spriteAtlas;
const bool iconScaled = values.paintSize != 1.0f || frame.pixelRatio != atlas.getPixelRatio() || bucket.iconsNeedLinear;
const bool iconScaled = layout.get<IconSize>().constantOr(1.0) != 1.0 ||
frame.pixelRatio != atlas.getPixelRatio() ||
bucket.iconsNeedLinear;
const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || state.getPitch() != 0;
atlas.bind(bucket.sdfIcons || state.isChanging() || iconScaled || iconTransformed, context, 0);
@@ -74,8 +79,9 @@ void Painter::renderSymbol(PaintParameters& parameters,
if (bucket.sdfIcons) {
if (values.hasHalo) {
draw(parameters.programs.symbolIconSDF,
SymbolSDFIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo),
SymbolSDFIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo),
bucket.icon,
bucket.iconSizeBinder,
values,
bucket.paintPropertyBinders.at(layer.getID()).first,
paintPropertyValues);
@@ -83,16 +89,18 @@ void Painter::renderSymbol(PaintParameters& parameters,
if (values.hasFill) {
draw(parameters.programs.symbolIconSDF,
SymbolSDFIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill),
SymbolSDFIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill),
bucket.icon,
bucket.iconSizeBinder,
values,
bucket.paintPropertyBinders.at(layer.getID()).first,
paintPropertyValues);
}
} else {
draw(parameters.programs.symbolIcon,
SymbolIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state),
SymbolIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state),
bucket.icon,
bucket.iconSizeBinder,
values,
bucket.paintPropertyBinders.at(layer.getID()).first,
paintPropertyValues);
@@ -109,8 +117,9 @@ void Painter::renderSymbol(PaintParameters& parameters,
if (values.hasHalo) {
draw(parameters.programs.symbolGlyph,
SymbolSDFTextProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo),
SymbolSDFTextProgram::uniformValues(true, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo),
bucket.text,
bucket.textSizeBinder,
values,
bucket.paintPropertyBinders.at(layer.getID()).second,
paintPropertyValues);
@@ -118,8 +127,9 @@ void Painter::renderSymbol(PaintParameters& parameters,
if (values.hasFill) {
draw(parameters.programs.symbolGlyph,
SymbolSDFTextProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill),
SymbolSDFTextProgram::uniformValues(true, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill),
bucket.text,
bucket.textSizeBinder,
values,
bucket.paintPropertyBinders.at(layer.getID()).second,
paintPropertyValues);

View File

@@ -11,12 +11,17 @@ using namespace style;
SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layout_,
const std::map<std::string, std::pair<
style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>& layerPaintProperties,
const style::DataDrivenPropertyValue<float>& textSize,
const style::DataDrivenPropertyValue<float>& iconSize,
float zoom,
bool sdfIcons_,
bool iconsNeedLinear_)
: layout(std::move(layout_)),
sdfIcons(sdfIcons_),
iconsNeedLinear(iconsNeedLinear_) {
iconsNeedLinear(iconsNeedLinear_ || iconSize.isDataDriven() || !iconSize.isZoomConstant()),
textSizeBinder(SymbolSizeBinder::create(zoom, textSize, TextSize::defaultValue())),
iconSizeBinder(SymbolSizeBinder::create(zoom, iconSize, IconSize::defaultValue())) {
for (const auto& pair : layerPaintProperties) {
paintPropertyBinders.emplace(
std::piecewise_construct,
@@ -32,11 +37,13 @@ void SymbolBucket::upload(gl::Context& context) {
if (hasTextData()) {
text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices));
text.indexBuffer = context.createIndexBuffer(std::move(text.triangles));
textSizeBinder->upload(context);
}
if (hasIconData()) {
icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices));
icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles));
iconSizeBinder->upload(context);
}
if (!collisionBox.vertices.empty()) {

View File

@@ -9,6 +9,7 @@
#include <mbgl/programs/collision_box_program.hpp>
#include <mbgl/text/glyph_range.hpp>
#include <mbgl/style/layers/symbol_layer_properties.hpp>
#include <mbgl/layout/symbol_feature.hpp>
#include <vector>
@@ -18,6 +19,8 @@ class SymbolBucket : public Bucket {
public:
SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated,
const std::map<std::string, std::pair<style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>&,
const style::DataDrivenPropertyValue<float>& textSize,
const style::DataDrivenPropertyValue<float>& iconSize,
float zoom,
bool sdfIcons,
bool iconsNeedLinear);
@@ -36,6 +39,8 @@ public:
std::map<std::string, std::pair<
SymbolIconProgram::PaintPropertyBinders,
SymbolSDFTextProgram::PaintPropertyBinders>> paintPropertyBinders;
std::unique_ptr<SymbolSizeBinder> textSizeBinder;
struct TextBuffer {
gl::VertexVector<SymbolLayoutVertex> vertices;
@@ -45,7 +50,9 @@ public:
optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer;
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
} text;
std::unique_ptr<SymbolSizeBinder> iconSizeBinder;
struct IconBuffer {
gl::VertexVector<SymbolLayoutVertex> vertices;
gl::IndexVector<gl::Triangles> triangles;

View File

@@ -43,17 +43,25 @@ vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 v
}
}
// Unpack a pair of values that have been packed into a single float.
// The packed values are assumed to be 8-bit unsigned integers, and are
// packed like so:
// packedValue = floor(input[0]) * 256 + input[1],
vec2 unpack_float(const float packedValue) {
float v0 = floor(packedValue / 256.0);
return vec2(v0, packedValue - v0 * 256.0);
}
// To minimize the number of attributes needed in the mapbox-gl-native shaders,
// we encode a 4-component color into a pair of floats (i.e. a vec2) as follows:
// [ floor(color.r * 255) * 256 + color.g * 255,
// floor(color.b * 255) * 256 + color.g * 255 ]
vec4 decode_color(const vec2 encodedColor) {
float r = floor(encodedColor[0]/256.0)/255.0;
float g = (encodedColor[0] - r*256.0*255.0)/255.0;
float b = floor(encodedColor[1]/256.0)/255.0;
float a = (encodedColor[1] - b*256.0*255.0)/255.0;
return vec4(r, g, b, a);
return vec4(
unpack_float(encodedColor[0]) / 255.0,
unpack_float(encodedColor[1]) / 255.0
);
}
// Unpack a pair of paint values and interpolate between them.

View File

@@ -9,9 +9,16 @@ const char* symbol_icon::name = "symbol_icon";
const char* symbol_icon::vertexSource = R"MBGL_SHADER(
attribute vec4 a_pos_offset;
attribute vec2 a_texture_pos;
attribute vec4 a_data;
// icon-size data (see symbol_sdf.vertex.glsl for more)
attribute vec3 a_size;
uniform bool u_is_size_zoom_constant;
uniform bool u_is_size_feature_constant;
uniform mediump float u_size_t; // used to interpolate between zoom stops when size is a composite function
uniform mediump float u_size; // used when size is both zoom and feature constant
uniform mediump float u_layout_size; // used when size is feature constant
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
varying lowp float opacity;
@@ -19,6 +26,7 @@ varying lowp float opacity;
// matrix is for the vertex position.
uniform mat4 u_matrix;
uniform bool u_is_text;
uniform mediump float u_zoom;
uniform bool u_rotate_with_map;
uniform vec2 u_extrude_scale;
@@ -34,16 +42,45 @@ void main() {
vec2 a_pos = a_pos_offset.xy;
vec2 a_offset = a_pos_offset.zw;
vec2 a_tex = a_texture_pos.xy;
mediump float a_labelminzoom = a_data[0];
mediump vec2 a_zoom = a_data.pq;
vec2 a_tex = a_data.xy;
mediump vec2 label_data = unpack_float(a_data[2]);
mediump float a_labelminzoom = label_data[0];
mediump vec2 a_zoom = unpack_float(a_data[3]);
mediump float a_minzoom = a_zoom[0];
mediump float a_maxzoom = a_zoom[1];
// u_zoom is the current zoom level adjusted for the change in font size
mediump float z = 2.0 - step(a_minzoom, u_zoom) - (1.0 - step(a_maxzoom, u_zoom));
float size;
// In order to accommodate placing labels around corners in
// symbol-placement: line, each glyph in a label could have multiple
// "quad"s only one of which should be shown at a given zoom level.
// The min/max zoom assigned to each quad is based on the font size at
// the vector tile's zoom level, which might be different than at the
// currently rendered zoom level if text-size is zoom-dependent.
// Thus, we compensate for this difference by calculating an adjustment
// based on the scale of rendered text size relative to layout text size.
mediump float layoutSize;
if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {
size = mix(a_size[0], a_size[1], u_size_t) / 10.0;
layoutSize = a_size[2] / 10.0;
} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {
size = a_size[0] / 10.0;
layoutSize = size;
} else if (!u_is_size_zoom_constant && u_is_size_feature_constant) {
size = u_size;
layoutSize = u_layout_size;
} else {
size = u_size;
layoutSize = u_size;
}
vec2 extrude = u_extrude_scale * (a_offset / 64.0);
float fontScale = u_is_text ? size / 24.0 : size;
mediump float zoomAdjust = log2(size / layoutSize);
mediump float adjustedZoom = (u_zoom - zoomAdjust) * 10.0;
// result: z = 0 if a_minzoom <= adjustedZoom < a_maxzoom, and 1 otherwise
mediump float z = 2.0 - step(a_minzoom, adjustedZoom) - (1.0 - step(a_maxzoom, adjustedZoom));
vec2 extrude = fontScale * u_extrude_scale * (a_offset / 64.0);
if (u_rotate_with_map) {
gl_Position = u_matrix * vec4(a_pos + extrude, 0, 1);
gl_Position.z += z * gl_Position.w;

View File

@@ -10,9 +10,23 @@ const char* symbol_sdf::vertexSource = R"MBGL_SHADER(
const float PI = 3.141592653589793;
attribute vec4 a_pos_offset;
attribute vec2 a_texture_pos;
attribute vec4 a_data;
// contents of a_size vary based on the type of property value
// used for {text,icon}-size.
// For constants, a_size is disabled.
// For source functions, we bind only one value per vertex: the value of {text,icon}-size evaluated for the current feature.
// For composite functions:
// [ text-size(lowerZoomStop, feature),
// text-size(upperZoomStop, feature),
// layoutSize == text-size(layoutZoomLevel, feature) ]
attribute vec3 a_size;
uniform bool u_is_size_zoom_constant;
uniform bool u_is_size_feature_constant;
uniform mediump float u_size_t; // used to interpolate between zoom stops when size is a composite function
uniform mediump float u_size; // used when size is both zoom and feature constant
uniform mediump float u_layout_size; // used when size is feature constant
uniform lowp float a_fill_color_t;
attribute highp vec4 a_fill_color;
varying highp vec4 fill_color;
@@ -32,6 +46,7 @@ varying lowp float halo_blur;
// matrix is for the vertex position.
uniform mat4 u_matrix;
uniform bool u_is_text;
uniform mediump float u_zoom;
uniform bool u_rotate_with_map;
uniform bool u_pitch_with_map;
@@ -45,6 +60,7 @@ uniform vec2 u_texsize;
varying vec2 v_tex;
varying vec2 v_fade_tex;
varying float v_gamma_scale;
varying float v_size;
void main() {
fill_color = unpack_mix_vec4(a_fill_color, a_fill_color_t);
@@ -56,24 +72,57 @@ void main() {
vec2 a_pos = a_pos_offset.xy;
vec2 a_offset = a_pos_offset.zw;
vec2 a_tex = a_texture_pos.xy;
mediump float a_labelminzoom = a_data[0];
mediump vec2 a_zoom = a_data.pq;
vec2 a_tex = a_data.xy;
mediump vec2 label_data = unpack_float(a_data[2]);
mediump float a_labelminzoom = label_data[0];
mediump float a_labelangle = label_data[1];
mediump vec2 a_zoom = unpack_float(a_data[3]);
mediump float a_minzoom = a_zoom[0];
mediump float a_maxzoom = a_zoom[1];
// u_zoom is the current zoom level adjusted for the change in font size
mediump float z = 2.0 - step(a_minzoom, u_zoom) - (1.0 - step(a_maxzoom, u_zoom));
// In order to accommodate placing labels around corners in
// symbol-placement: line, each glyph in a label could have multiple
// "quad"s only one of which should be shown at a given zoom level.
// The min/max zoom assigned to each quad is based on the font size at
// the vector tile's zoom level, which might be different than at the
// currently rendered zoom level if text-size is zoom-dependent.
// Thus, we compensate for this difference by calculating an adjustment
// based on the scale of rendered text size relative to layout text size.
mediump float layoutSize;
if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {
v_size = mix(a_size[0], a_size[1], u_size_t) / 10.0;
layoutSize = a_size[2] / 10.0;
} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {
v_size = a_size[0] / 10.0;
layoutSize = v_size;
} else if (!u_is_size_zoom_constant && u_is_size_feature_constant) {
v_size = u_size;
layoutSize = u_layout_size;
} else {
v_size = u_size;
layoutSize = u_size;
}
float fontScale = u_is_text ? v_size / 24.0 : v_size;
mediump float zoomAdjust = log2(v_size / layoutSize);
mediump float adjustedZoom = (u_zoom - zoomAdjust) * 10.0;
// result: z = 0 if a_minzoom <= adjustedZoom < a_maxzoom, and 1 otherwise
// Used below to move the vertex out of the clip space for when the current
// zoom is out of the glyph's zoom range.
mediump float z = 2.0 - step(a_minzoom, adjustedZoom) - (1.0 - step(a_maxzoom, adjustedZoom));
// pitch-alignment: map
// rotation-alignment: map | viewport
if (u_pitch_with_map) {
lowp float angle = u_rotate_with_map ? (a_data[1] / 256.0 * 2.0 * PI) : u_bearing;
lowp float angle = u_rotate_with_map ? (a_labelangle / 256.0 * 2.0 * PI) : u_bearing;
lowp float asin = sin(angle);
lowp float acos = cos(angle);
mat2 RotationMatrix = mat2(acos, asin, -1.0 * asin, acos);
vec2 offset = RotationMatrix * a_offset;
vec2 extrude = u_extrude_scale * (offset / 64.0);
vec2 extrude = fontScale * u_extrude_scale * (offset / 64.0);
gl_Position = u_matrix * vec4(a_pos + extrude, 0, 1);
gl_Position.z += z * gl_Position.w;
// pitch-alignment: viewport
@@ -84,7 +133,7 @@ void main() {
// it goes from 0% foreshortening to up to around 70% foreshortening
lowp float pitchfactor = 1.0 - cos(u_pitch * sin(u_pitch * 0.75));
lowp float lineangle = a_data[1] / 256.0 * 2.0 * PI;
lowp float lineangle = a_labelangle / 256.0 * 2.0 * PI;
// use the lineangle to position points a,b along the line
// project the points and calculate the label angle in projected space
@@ -97,13 +146,13 @@ void main() {
mat2 RotationMatrix = mat2(acos, -1.0 * asin, asin, acos);
vec2 offset = RotationMatrix * (vec2((1.0-pitchfactor)+(pitchfactor*cos(angle*2.0)), 1.0) * a_offset);
vec2 extrude = u_extrude_scale * (offset / 64.0);
vec2 extrude = fontScale * u_extrude_scale * (offset / 64.0);
gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0);
gl_Position.z += z * gl_Position.w;
// pitch-alignment: viewport
// rotation-alignment: viewport
} else {
vec2 extrude = u_extrude_scale * (a_offset / 64.0);
vec2 extrude = fontScale * u_extrude_scale * (a_offset / 64.0);
gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0);
}
@@ -127,12 +176,13 @@ varying lowp float halo_blur;
uniform sampler2D u_texture;
uniform sampler2D u_fadetexture;
uniform lowp float u_font_scale;
uniform highp float u_gamma_scale;
uniform bool u_is_text;
varying vec2 v_tex;
varying vec2 v_fade_tex;
varying float v_gamma_scale;
varying float v_size;
void main() {
@@ -141,13 +191,15 @@ void main() {
float fontScale = u_is_text ? v_size / 24.0 : v_size;
lowp vec4 color = fill_color;
highp float gamma = EDGE_GAMMA / u_gamma_scale;
highp float gamma = EDGE_GAMMA / (fontScale * 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;
gamma = (halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / (fontScale * u_gamma_scale);
buff = (6.0 - halo_width / fontScale) / SDF_PX;
}
lowp float dist = texture2D(u_texture, v_tex).a;

View File

@@ -161,15 +161,15 @@ void SymbolLayer::setIconRotationAlignment(PropertyValue<AlignmentType> value) {
impl->layout.unevaluated.get<IconRotationAlignment>() = value;
impl->observer->onLayerLayoutPropertyChanged(*this, "icon-rotation-alignment");
}
PropertyValue<float> SymbolLayer::getDefaultIconSize() {
DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconSize() {
return IconSize::defaultValue();
}
PropertyValue<float> SymbolLayer::getIconSize() const {
DataDrivenPropertyValue<float> SymbolLayer::getIconSize() const {
return impl->layout.unevaluated.get<IconSize>();
}
void SymbolLayer::setIconSize(PropertyValue<float> value) {
void SymbolLayer::setIconSize(DataDrivenPropertyValue<float> value) {
if (value == getIconSize())
return;
impl->layout.unevaluated.get<IconSize>() = value;
@@ -329,15 +329,15 @@ void SymbolLayer::setTextFont(PropertyValue<std::vector<std::string>> value) {
impl->layout.unevaluated.get<TextFont>() = value;
impl->observer->onLayerLayoutPropertyChanged(*this, "text-font");
}
PropertyValue<float> SymbolLayer::getDefaultTextSize() {
DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextSize() {
return TextSize::defaultValue();
}
PropertyValue<float> SymbolLayer::getTextSize() const {
DataDrivenPropertyValue<float> SymbolLayer::getTextSize() const {
return impl->layout.unevaluated.get<TextSize>();
}
void SymbolLayer::setTextSize(PropertyValue<float> value) {
void SymbolLayer::setTextSize(DataDrivenPropertyValue<float> value) {
if (value == getTextSize())
return;
impl->layout.unevaluated.get<TextSize>() = value;

View File

@@ -13,10 +13,6 @@ void SymbolLayer::Impl::cascade(const CascadeParameters& parameters) {
bool SymbolLayer::Impl::evaluate(const PropertyEvaluationParameters& parameters) {
paint.evaluate(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);
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 ||

View File

@@ -44,7 +44,7 @@ public:
// Layout
AlignmentType pitchAlignment;
AlignmentType rotationAlignment;
float layoutSize;
PossiblyEvaluatedPropertyValue<float> layoutSize;
// Paint
std::array<float, 2> translate;

View File

@@ -45,7 +45,7 @@ struct IconRotationAlignment : LayoutProperty<AlignmentType> {
static AlignmentType defaultValue() { return AlignmentType::Auto; }
};
struct IconSize : LayoutProperty<float> {
struct IconSize : DataDrivenLayoutProperty<float> {
static constexpr const char * key = "icon-size";
static float defaultValue() { return 1; }
};
@@ -105,7 +105,7 @@ struct TextFont : LayoutProperty<std::vector<std::string>> {
static std::vector<std::string> defaultValue() { return { "Open Sans Regular", "Arial Unicode MS Regular" }; }
};
struct TextSize : LayoutProperty<float> {
struct TextSize : DataDrivenLayoutProperty<float> {
static constexpr const char * key = "text-size";
static float defaultValue() { return 16; }
};

View File

@@ -26,16 +26,12 @@ inline std::array<float, 1> attributeValue(float v) {
uses 8-bit precision for each color component, for each float we use the upper 8
bits for one component (e.g. (color.r * 255) * 256), and the lower 8 for another.
Also note:
- Colors come in as floats 0..1, so we scale by 255.
- Casting the scaled values to ints is important: without doing this, e.g., the
fractional part of the `r` component would corrupt the lower-8 bits of the encoded
value, which must be reserved for the `g` component.
Also note that colors come in as floats 0..1, so we scale by 255.
*/
inline std::array<float, 2> attributeValue(const Color& color) {
return {{
static_cast<float>(static_cast<uint16_t>(color.r * 255) * 256 + static_cast<uint16_t>(color.g * 255)),
static_cast<float>(static_cast<uint16_t>(color.b * 255) * 256 + static_cast<uint16_t>(color.a * 255))
static_cast<float>(mbgl::attributes::packUint8Pair(255 * color.r, 255 * color.g)),
static_cast<float>(mbgl::attributes::packUint8Pair(255 * color.b, 255 * color.a))
}};
}

View File

@@ -19,6 +19,7 @@ SymbolQuad getIconQuad(const Anchor& anchor,
const PositionedIcon& shapedIcon,
const GeometryCoordinates& line,
const SymbolLayoutProperties::Evaluated& layout,
const float layoutTextSize,
const style::SymbolPlacementType placement,
const Shaping& shapedText) {
auto image = *(shapedIcon.image);
@@ -36,7 +37,7 @@ SymbolQuad getIconQuad(const Anchor& anchor,
if (layout.get<IconTextFit>() != IconTextFitType::None && shapedText) {
auto iconWidth = right - left;
auto iconHeight = bottom - top;
auto size = layout.get<TextSize>() / 24.0f;
auto size = layoutTextSize / 24.0f;
auto textLeft = shapedText.left * size;
auto textRight = shapedText.right * size;
auto textTop = shapedText.top * size;

View File

@@ -55,6 +55,7 @@ SymbolQuad getIconQuad(const Anchor& anchor,
const PositionedIcon& shapedIcon,
const GeometryCoordinates& line,
const style::SymbolLayoutProperties::Evaluated&,
const float layoutTextSize,
style::SymbolPlacementType placement,
const Shaping& shapedText);

View File

@@ -31,7 +31,7 @@ TEST(Buckets, SymbolBucket) {
bool sdfIcons = false;
bool iconsNeedLinear = false;
SymbolBucket bucket { layout, {}, 0, sdfIcons, iconsNeedLinear };
SymbolBucket bucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear };
ASSERT_FALSE(bucket.hasIconData());
ASSERT_FALSE(bucket.hasTextData());
ASSERT_FALSE(bucket.hasCollisionBoxData());

View File

@@ -23,7 +23,7 @@ TEST(getIconQuads, normal) {
Shaping shapedText;
SymbolQuad quad =
getIconQuad(anchor, shapedIcon, line, layout, SymbolPlacementType::Point, shapedText);
getIconQuad(anchor, shapedIcon, line, layout, 16.0f, SymbolPlacementType::Point, shapedText);
ASSERT_EQ(quad.anchorPoint.x, 2);
ASSERT_EQ(quad.anchorPoint.y, 3);
@@ -61,7 +61,7 @@ TEST(getIconQuads, style) {
{
SymbolLayoutProperties::Evaluated layout;
SymbolQuad quad =
getIconQuad(anchor, shapedIcon, line, layout, SymbolPlacementType::Point, shapedText);
getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText);
ASSERT_EQ(quad.anchorPoint.x, 0);
ASSERT_EQ(quad.anchorPoint.y, 0);
@@ -84,7 +84,7 @@ TEST(getIconQuads, style) {
layout.get<TextSize>() = 24.0f;
layout.get<IconTextFit>() = IconTextFitType::Width;
SymbolQuad quad =
getIconQuad(anchor, shapedIcon, line, layout, SymbolPlacementType::Point, shapedText);
getIconQuad(anchor, shapedIcon, line, layout, 24.0f, SymbolPlacementType::Point, shapedText);
ASSERT_EQ(quad.tl.x, -60);
ASSERT_EQ(quad.tl.y, 0);
@@ -102,7 +102,7 @@ TEST(getIconQuads, style) {
layout.get<TextSize>() = 12.0f;
layout.get<IconTextFit>() = IconTextFitType::Width;
SymbolQuad quad =
getIconQuad(anchor, shapedIcon, line, layout, SymbolPlacementType::Point, shapedText);
getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText);
ASSERT_EQ(quad.tl.x, -30);
ASSERT_EQ(quad.tl.y, -5);
@@ -124,7 +124,7 @@ TEST(getIconQuads, style) {
layout.get<IconTextFitPadding>()[2] = 5.0f;
layout.get<IconTextFitPadding>()[3] = 10.0f;
SymbolQuad quad =
getIconQuad(anchor, shapedIcon, line, layout, SymbolPlacementType::Point, shapedText);
getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText);
ASSERT_EQ(quad.tl.x, -40);
ASSERT_EQ(quad.tl.y, -10);
@@ -142,7 +142,7 @@ TEST(getIconQuads, style) {
layout.get<TextSize>() = 24.0f;
layout.get<IconTextFit>() = IconTextFitType::Height;
SymbolQuad quad =
getIconQuad(anchor, shapedIcon, line, layout, SymbolPlacementType::Point, shapedText);
getIconQuad(anchor, shapedIcon, line, layout, 24.0f, SymbolPlacementType::Point, shapedText);
ASSERT_EQ(quad.tl.x, -30);
ASSERT_EQ(quad.tl.y, -10);
@@ -160,7 +160,7 @@ TEST(getIconQuads, style) {
layout.get<TextSize>() = 12.0f;
layout.get<IconTextFit>() = IconTextFitType::Height;
SymbolQuad quad =
getIconQuad(anchor, shapedIcon, line, layout, SymbolPlacementType::Point, shapedText);
getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText);
ASSERT_EQ(quad.tl.x, -20);
ASSERT_EQ(quad.tl.y, -5);
@@ -182,7 +182,7 @@ TEST(getIconQuads, style) {
layout.get<IconTextFitPadding>()[2] = 5.0f;
layout.get<IconTextFitPadding>()[3] = 10.0f;
SymbolQuad quad =
getIconQuad(anchor, shapedIcon, line, layout, SymbolPlacementType::Point, shapedText);
getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText);
ASSERT_EQ(quad.tl.x, -30);
ASSERT_EQ(quad.tl.y, -10);
@@ -200,7 +200,7 @@ TEST(getIconQuads, style) {
layout.get<TextSize>() = 24.0f;
layout.get<IconTextFit>() = IconTextFitType::Both;
SymbolQuad quad =
getIconQuad(anchor, shapedIcon, line, layout, SymbolPlacementType::Point, shapedText);
getIconQuad(anchor, shapedIcon, line, layout, 24.0f, SymbolPlacementType::Point, shapedText);
ASSERT_EQ(quad.tl.x, -60);
ASSERT_EQ(quad.tl.y, -10);
@@ -218,7 +218,7 @@ TEST(getIconQuads, style) {
layout.get<TextSize>() = 12.0f;
layout.get<IconTextFit>() = IconTextFitType::Both;
SymbolQuad quad =
getIconQuad(anchor, shapedIcon, line, layout, SymbolPlacementType::Point, shapedText);
getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText);
ASSERT_EQ(quad.tl.x, -30);
ASSERT_EQ(quad.tl.y, -5);
@@ -240,7 +240,7 @@ TEST(getIconQuads, style) {
layout.get<IconTextFitPadding>()[2] = 5.0f;
layout.get<IconTextFitPadding>()[3] = 10.0f;
SymbolQuad quad =
getIconQuad(anchor, shapedIcon, line, layout, SymbolPlacementType::Point, shapedText);
getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText);
ASSERT_EQ(quad.tl.x, -40);
ASSERT_EQ(quad.tl.y, -10);
@@ -262,7 +262,7 @@ TEST(getIconQuads, style) {
layout.get<IconTextFitPadding>()[2] = 10.0f;
layout.get<IconTextFitPadding>()[3] = 15.0f;
SymbolQuad quad =
getIconQuad(anchor, shapedIcon, line, layout, SymbolPlacementType::Point, shapedText);
getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText);
ASSERT_EQ(quad.tl.x, -45);
ASSERT_EQ(quad.tl.y, -5);

View File

@@ -64,7 +64,7 @@ TEST(VectorTile, Issue7615) {
std::map<
std::string,
std::pair<style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>(),
0.0f, false, false);
16.0f, 1.0f, 0.0f, false, false);
// Simulate placement of a symbol layer.
tile.onPlacement(GeometryTile::PlacementResult {