[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:
@@ -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>;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Submodule mapbox-gl-js updated: aa329d31f4...e3b2df2807
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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() },
|
||||
|
||||
@@ -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&,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
// Layout
|
||||
AlignmentType pitchAlignment;
|
||||
AlignmentType rotationAlignment;
|
||||
float layoutSize;
|
||||
PossiblyEvaluatedPropertyValue<float> layoutSize;
|
||||
|
||||
// Paint
|
||||
std::array<float, 2> translate;
|
||||
|
||||
@@ -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; }
|
||||
};
|
||||
|
||||
@@ -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))
|
||||
}};
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user