diff options
author | Mikhail Pozdnyakov <mikhail.pozdnyakov@mapbox.com> | 2018-08-08 15:09:27 +0300 |
---|---|---|
committer | Mikhail Pozdnyakov <mikhail.pozdnyakov@mapbox.com> | 2018-08-23 19:01:40 +0300 |
commit | fb5afd34b9b25e993a4b109385215eba0b83923f (patch) | |
tree | 61474b72e7d099808eb85ea60275e58da11bdd39 /src/mbgl/renderer | |
parent | 52275f835a38897ebf1aeb2158ccfca2272e4770 (diff) | |
download | qtlocation-mapboxgl-fb5afd34b9b25e993a4b109385215eba0b83923f.tar.gz |
[core] Add `line-gradient` property
Porting of https://github.com/mapbox/mapbox-gl-js/pull/6303
See the link above for the description of the feature and
its limitations).
Based on patch from @lbud (Lauren Budorick).
Diffstat (limited to 'src/mbgl/renderer')
-rw-r--r-- | src/mbgl/renderer/buckets/line_bucket.cpp | 97 | ||||
-rw-r--r-- | src/mbgl/renderer/buckets/line_bucket.hpp | 9 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_line_layer.cpp | 37 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_line_layer.hpp | 5 | ||||
-rw-r--r-- | src/mbgl/renderer/renderer_impl.cpp | 4 |
5 files changed, 122 insertions, 30 deletions
diff --git a/src/mbgl/renderer/buckets/line_bucket.cpp b/src/mbgl/renderer/buckets/line_bucket.cpp index a10551a7d2..80149edafe 100644 --- a/src/mbgl/renderer/buckets/line_bucket.cpp +++ b/src/mbgl/renderer/buckets/line_bucket.cpp @@ -64,6 +64,27 @@ const float LINE_DISTANCE_SCALE = 1.0 / 2.0; // The maximum line distance, in tile units, that fits in the buffer. const float MAX_LINE_DISTANCE = std::pow(2, LINE_DISTANCE_BUFFER_BITS) / LINE_DISTANCE_SCALE; +class LineBucket::Distances { +public: + Distances(double clipStart_, double clipEnd_, double total_) + : clipStart(clipStart_), clipEnd(clipEnd_), total(total_) {} + + // Scale line distance from tile units to [0, 2^15). + double scaleToMaxLineDistance(double tileDistance) const { + double relativeTileDistance = tileDistance / total; + if (std::isinf(relativeTileDistance) || std::isnan(relativeTileDistance)) { + assert(false); + relativeTileDistance = 0.0; + } + return (relativeTileDistance * (clipEnd - clipStart) + clipStart) * (MAX_LINE_DISTANCE - 1); + } + +private: + double clipStart; + double clipEnd; + double total; +}; + void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const GeometryTileFeature& feature) { const FeatureType type = feature.getType(); const std::size_t len = [&coordinates] { @@ -89,6 +110,22 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome return; } + optional<Distances> lineDistances; + + const auto &props = feature.getProperties(); + auto clip_start_it = props.find("mapbox_clip_start"); + auto clip_end_it = props.find("mapbox_clip_end"); + if (clip_start_it != props.end() && clip_end_it != props.end()) { + double total_length = 0.0; + for (std::size_t i = first; i < len - 1; ++i) { + total_length += util::dist<double>(coordinates[i], coordinates[i + 1]); + } + + lineDistances = Distances{*numericValue<double>(clip_start_it->second), + *numericValue<double>(clip_end_it->second), + total_length}; + } + const LineJoinType joinType = layout.evaluate<LineJoin>(zoom, feature); const float miterLimit = joinType == LineJoinType::Bevel ? 1.05f : float(layout.get<LineMiterLimit>()); @@ -99,7 +136,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome const LineCapType beginCap = layout.get<LineCap>(); const LineCapType endCap = type == FeatureType::Polygon ? LineCapType::Butt : LineCapType(layout.get<LineCap>()); - double distance = 0; + double distance = 0.0; bool startOfLine = true; optional<GeometryCoordinate> currentCoordinate; optional<GeometryCoordinate> prevCoordinate; @@ -191,7 +228,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome if (prevSegmentLength > 2.0 * sharpCornerOffset) { GeometryCoordinate newPrevVertex = *currentCoordinate - convertPoint<int16_t>(util::round(convertPoint<double>(*currentCoordinate - *prevCoordinate) * (sharpCornerOffset / prevSegmentLength))); distance += util::dist<double>(newPrevVertex, *prevCoordinate); - addCurrentVertex(newPrevVertex, distance, *prevNormal, 0, 0, false, startVertex, triangleStore); + addCurrentVertex(newPrevVertex, distance, *prevNormal, 0, 0, false, startVertex, triangleStore, lineDistances); prevCoordinate = newPrevVertex; } } @@ -236,7 +273,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome if (middleVertex && currentJoin == LineJoinType::Miter) { joinNormal = joinNormal * miterLength; addCurrentVertex(*currentCoordinate, distance, joinNormal, 0, 0, false, startVertex, - triangleStore); + triangleStore, lineDistances); } else if (middleVertex && currentJoin == LineJoinType::FlipBevel) { // miter is too big, flip the direction to make a beveled join @@ -252,10 +289,10 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome } addCurrentVertex(*currentCoordinate, distance, joinNormal, 0, 0, false, startVertex, - triangleStore); + triangleStore, lineDistances); addCurrentVertex(*currentCoordinate, distance, joinNormal * -1.0, 0, 0, false, startVertex, - triangleStore); + triangleStore, lineDistances); } else if (middleVertex && (currentJoin == LineJoinType::Bevel || currentJoin == LineJoinType::FakeRound)) { const bool lineTurnsLeft = (prevNormal->x * nextNormal->y - prevNormal->y * nextNormal->x) > 0; const float offset = -std::sqrt(miterLength * miterLength - 1); @@ -273,7 +310,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome // Close previous segement with bevel if (!startOfLine) { addCurrentVertex(*currentCoordinate, distance, *prevNormal, offsetA, offsetB, false, - startVertex, triangleStore); + startVertex, triangleStore, lineDistances); } if (currentJoin == LineJoinType::FakeRound) { @@ -288,41 +325,41 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome for (int m = 0; m < n; m++) { auto approxFractionalJoinNormal = util::unit(*nextNormal * ((m + 1.0) / (n + 1.0)) + *prevNormal); - addPieSliceVertex(*currentCoordinate, distance, approxFractionalJoinNormal, lineTurnsLeft, startVertex, triangleStore); + addPieSliceVertex(*currentCoordinate, distance, approxFractionalJoinNormal, lineTurnsLeft, startVertex, triangleStore, lineDistances); } - addPieSliceVertex(*currentCoordinate, distance, joinNormal, lineTurnsLeft, startVertex, triangleStore); + addPieSliceVertex(*currentCoordinate, distance, joinNormal, lineTurnsLeft, startVertex, triangleStore, lineDistances); for (int k = n - 1; k >= 0; k--) { auto approxFractionalJoinNormal = util::unit(*prevNormal * ((k + 1.0) / (n + 1.0)) + *nextNormal); - addPieSliceVertex(*currentCoordinate, distance, approxFractionalJoinNormal, lineTurnsLeft, startVertex, triangleStore); + addPieSliceVertex(*currentCoordinate, distance, approxFractionalJoinNormal, lineTurnsLeft, startVertex, triangleStore, lineDistances); } } // Start next segment if (nextCoordinate) { addCurrentVertex(*currentCoordinate, distance, *nextNormal, -offsetA, -offsetB, - false, startVertex, triangleStore); + false, startVertex, triangleStore, lineDistances); } } else if (!middleVertex && currentCap == LineCapType::Butt) { if (!startOfLine) { // Close previous segment with a butt addCurrentVertex(*currentCoordinate, distance, *prevNormal, 0, 0, false, - startVertex, triangleStore); + startVertex, triangleStore, lineDistances); } // Start next segment with a butt if (nextCoordinate) { addCurrentVertex(*currentCoordinate, distance, *nextNormal, 0, 0, false, - startVertex, triangleStore); + startVertex, triangleStore, lineDistances); } } else if (!middleVertex && currentCap == LineCapType::Square) { if (!startOfLine) { // Close previous segment with a square cap addCurrentVertex(*currentCoordinate, distance, *prevNormal, 1, 1, false, - startVertex, triangleStore); + startVertex, triangleStore, lineDistances); // The segment is done. Unset vertices to disconnect segments. e1 = e2 = -1; @@ -331,18 +368,18 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome // Start next segment if (nextCoordinate) { addCurrentVertex(*currentCoordinate, distance, *nextNormal, -1, -1, false, - startVertex, triangleStore); + startVertex, triangleStore, lineDistances); } } else if (middleVertex ? currentJoin == LineJoinType::Round : currentCap == LineCapType::Round) { if (!startOfLine) { // Close previous segment with a butt addCurrentVertex(*currentCoordinate, distance, *prevNormal, 0, 0, false, - startVertex, triangleStore); + startVertex, triangleStore, lineDistances); // Add round cap or linejoin at end of segment addCurrentVertex(*currentCoordinate, distance, *prevNormal, 1, 1, true, startVertex, - triangleStore); + triangleStore, lineDistances); // The segment is done. Unset vertices to disconnect segments. e1 = e2 = -1; @@ -352,10 +389,10 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome if (nextCoordinate) { // Add round cap before first segment addCurrentVertex(*currentCoordinate, distance, *nextNormal, -1, -1, true, - startVertex, triangleStore); + startVertex, triangleStore, lineDistances); addCurrentVertex(*currentCoordinate, distance, *nextNormal, 0, 0, false, - startVertex, triangleStore); + startVertex, triangleStore, lineDistances); } } @@ -364,7 +401,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome if (nextSegmentLength > 2 * sharpCornerOffset) { GeometryCoordinate newCurrentVertex = *currentCoordinate + convertPoint<int16_t>(util::round(convertPoint<double>(*nextCoordinate - *currentCoordinate) * (sharpCornerOffset / nextSegmentLength))); distance += util::dist<double>(newCurrentVertex, *currentCoordinate); - addCurrentVertex(newCurrentVertex, distance, *nextNormal, 0, 0, false, startVertex, triangleStore); + addCurrentVertex(newCurrentVertex, distance, *nextNormal, 0, 0, false, startVertex, triangleStore, lineDistances); currentCoordinate = newCurrentVertex; } } @@ -398,11 +435,14 @@ void LineBucket::addCurrentVertex(const GeometryCoordinate& currentCoordinate, double endRight, bool round, std::size_t startVertex, - std::vector<TriangleElement>& triangleStore) { + std::vector<TriangleElement>& triangleStore, + optional<Distances> lineDistances) { Point<double> extrude = normal; + double scaledDistance = lineDistances ? lineDistances->scaleToMaxLineDistance(distance) : distance; + if (endLeft) extrude = extrude - (util::perp(normal) * endLeft); - vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, round, false, endLeft, distance * LINE_DISTANCE_SCALE)); + vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, round, false, endLeft, scaledDistance * LINE_DISTANCE_SCALE)); e3 = vertices.vertexSize() - 1 - startVertex; if (e1 >= 0 && e2 >= 0) { triangleStore.emplace_back(e1, e2, e3); @@ -413,7 +453,7 @@ void LineBucket::addCurrentVertex(const GeometryCoordinate& currentCoordinate, extrude = normal * -1.0; if (endRight) extrude = extrude - (util::perp(normal) * endRight); - vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, round, true, -endRight, distance * LINE_DISTANCE_SCALE)); + vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, round, true, -endRight, scaledDistance * LINE_DISTANCE_SCALE)); e3 = vertices.vertexSize() - 1 - startVertex; if (e1 >= 0 && e2 >= 0) { triangleStore.emplace_back(e1, e2, e3); @@ -425,9 +465,9 @@ void LineBucket::addCurrentVertex(const GeometryCoordinate& currentCoordinate, // When we get close to the distance, reset it to zero and add the vertex again with // a distance of zero. The max distance is determined by the number of bits we allocate // to `linesofar`. - if (distance > MAX_LINE_DISTANCE / 2.0f) { - distance = 0; - addCurrentVertex(currentCoordinate, distance, normal, endLeft, endRight, round, startVertex, triangleStore); + if (distance > MAX_LINE_DISTANCE / 2.0f && !lineDistances) { + distance = 0.0; + addCurrentVertex(currentCoordinate, distance, normal, endLeft, endRight, round, startVertex, triangleStore, lineDistances); } } @@ -436,8 +476,13 @@ void LineBucket::addPieSliceVertex(const GeometryCoordinate& currentVertex, const Point<double>& extrude, bool lineTurnsLeft, std::size_t startVertex, - std::vector<TriangleElement>& triangleStore) { + std::vector<TriangleElement>& triangleStore, + optional<Distances> lineDistances) { Point<double> flippedExtrude = extrude * (lineTurnsLeft ? -1.0 : 1.0); + if (lineDistances) { + distance = lineDistances->scaleToMaxLineDistance(distance); + } + vertices.emplace_back(LineProgram::layoutVertex(currentVertex, flippedExtrude, false, lineTurnsLeft, 0, distance * LINE_DISTANCE_SCALE)); e3 = vertices.vertexSize() - 1 - startVertex; if (e1 >= 0 && e2 >= 0) { diff --git a/src/mbgl/renderer/buckets/line_bucket.hpp b/src/mbgl/renderer/buckets/line_bucket.hpp index 8fe7184941..23d30ca416 100644 --- a/src/mbgl/renderer/buckets/line_bucket.hpp +++ b/src/mbgl/renderer/buckets/line_bucket.hpp @@ -47,12 +47,17 @@ private: TriangleElement(uint16_t a_, uint16_t b_, uint16_t c_) : a(a_), b(b_), c(c_) {} uint16_t a, b, c; }; + + class Distances; void addCurrentVertex(const GeometryCoordinate& currentVertex, double& distance, const Point<double>& normal, double endLeft, double endRight, bool round, - std::size_t startVertex, std::vector<LineBucket::TriangleElement>& triangleStore); + std::size_t startVertex, std::vector<LineBucket::TriangleElement>& triangleStore, + optional<Distances> distances); + void addPieSliceVertex(const GeometryCoordinate& currentVertex, double distance, const Point<double>& extrude, bool lineTurnsLeft, std::size_t startVertex, - std::vector<TriangleElement>& triangleStore); + std::vector<TriangleElement>& triangleStore, + optional<Distances> distances); std::ptrdiff_t e1; std::ptrdiff_t e2; diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp index 4b6ea35e67..f204c909e1 100644 --- a/src/mbgl/renderer/layers/render_line_layer.cpp +++ b/src/mbgl/renderer/layers/render_line_layer.cpp @@ -18,7 +18,8 @@ using namespace style; RenderLineLayer::RenderLineLayer(Immutable<style::LineLayer::Impl> _impl) : RenderLayer(style::LayerType::Line, _impl), - unevaluated(impl().paint.untransitioned()) { + unevaluated(impl().paint.untransitioned()), + colorRamp({256, 1}) { } const style::LineLayer::Impl& RenderLineLayer::impl() const { @@ -134,7 +135,18 @@ void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) { parameters.imageManager.getPixelSize(), *posA, *posB)); - + } else if (!unevaluated.get<LineGradient>().getValue().isUndefined()) { + if (!colorRampTexture) { + colorRampTexture = parameters.context.createTexture(colorRamp); + } + parameters.context.bindTexture(*colorRampTexture, 0, gl::TextureFilter::Linear); + + draw(parameters.programs.lineGradient, + LineGradientProgram::uniformValues( + evaluated, + tile, + parameters.state, + parameters.pixelsToGLUnits)); } else { draw(parameters.programs.line, LineProgram::uniformValues( @@ -207,6 +219,27 @@ bool RenderLineLayer::queryIntersectsFeature( halfWidth); } +void RenderLineLayer::updateColorRamp() { + auto colorValue = unevaluated.get<LineGradient>().getValue(); + if (colorValue.isUndefined()) { + return; + } + + const auto length = colorRamp.bytes(); + + for (uint32_t i = 0; i < length; i += 4) { + const auto color = colorValue.evaluate(static_cast<double>(i) / length); + colorRamp.data[i] = std::floor(color.r * 255); + colorRamp.data[i + 1] = std::floor(color.g * 255); + colorRamp.data[i + 2] = std::floor(color.b * 255); + colorRamp.data[i + 3] = std::floor(color.a * 255); + } + + if (colorRampTexture) { + colorRampTexture = nullopt; + } +} + float RenderLineLayer::getLineWidth(const GeometryTileFeature& feature, const float zoom) const { float lineWidth = evaluated.get<style::LineWidth>() .evaluate(feature, zoom, style::LineWidth::defaultValue()); diff --git a/src/mbgl/renderer/layers/render_line_layer.hpp b/src/mbgl/renderer/layers/render_line_layer.hpp index 5d5d79c044..facab60645 100644 --- a/src/mbgl/renderer/layers/render_line_layer.hpp +++ b/src/mbgl/renderer/layers/render_line_layer.hpp @@ -4,6 +4,7 @@ #include <mbgl/style/layers/line_layer_impl.hpp> #include <mbgl/style/layers/line_layer_properties.hpp> #include <mbgl/programs/uniforms.hpp> +#include <mbgl/util/image.hpp> namespace mbgl { @@ -33,6 +34,8 @@ public: const float, const mat4&) const override; + void updateColorRamp(); + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; // Paint properties @@ -43,6 +46,8 @@ public: private: float getLineWidth(const GeometryTileFeature&, const float) const; + PremultipliedImage colorRamp; + optional<gl::Texture> colorRampTexture; }; template <> diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index d3f72b89b9..bc39c40796 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -189,6 +189,10 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { if (layer.is<RenderHeatmapLayer>()) { layer.as<RenderHeatmapLayer>()->updateColorRamp(); } + + if (layer.is<RenderLineLayer>()) { + layer.as<RenderLineLayer>()->updateColorRamp(); + } } if (layerAdded || layerChanged || zoomChanged || layer.hasTransition()) { |