1 #include <mbgl/map/transform_state.hpp>
2 #include <mbgl/math/log2.hpp>
3 #include <mbgl/renderer/buckets/raster_bucket.hpp>
4 #include <mbgl/renderer/paint_parameters.hpp>
5 #include <mbgl/renderer/render_tile.hpp>
6 #include <mbgl/renderer/sources/render_image_source.hpp>
7 #include <mbgl/renderer/tile_parameters.hpp>
8 #include <mbgl/renderer/render_static_data.hpp>
9 #include <mbgl/programs/programs.hpp>
10 #include <mbgl/util/tile_coordinate.hpp>
11 #include <mbgl/util/tile_cover.hpp>
12 #include <mbgl/util/logging.hpp>
13 #include <mbgl/util/constants.hpp>
14
15 namespace mbgl {
16
17 using namespace style;
18
RenderImageSource(Immutable<style::ImageSource::Impl> impl_)19 RenderImageSource::RenderImageSource(Immutable<style::ImageSource::Impl> impl_)
20 : RenderSource(impl_) {
21 }
22
23 RenderImageSource::~RenderImageSource() = default;
24
impl() const25 const style::ImageSource::Impl& RenderImageSource::impl() const {
26 return static_cast<const style::ImageSource::Impl&>(*baseImpl);
27 }
28
isLoaded() const29 bool RenderImageSource::isLoaded() const {
30 return !!bucket;
31 }
32
startRender(PaintParameters & parameters)33 void RenderImageSource::startRender(PaintParameters& parameters) {
34 if (!isLoaded()) {
35 return;
36 }
37
38 matrices.clear();
39
40 for (size_t i = 0; i < tileIds.size(); i++) {
41 mat4 matrix;
42 matrix::identity(matrix);
43 parameters.state.matrixFor(matrix, tileIds[i]);
44 matrix::multiply(matrix, parameters.alignedProjMatrix, matrix);
45 matrices.push_back(matrix);
46 }
47
48 if (bucket->needsUpload()) {
49 bucket->upload(parameters.context);
50 }
51 }
52
finishRender(PaintParameters & parameters)53 void RenderImageSource::finishRender(PaintParameters& parameters) {
54 if (!isLoaded() || !(parameters.debugOptions & MapDebugOptions::TileBorders)) {
55 return;
56 }
57
58 static const style::Properties<>::PossiblyEvaluated properties {};
59 static const DebugProgram::PaintPropertyBinders paintAttributeData(properties, 0);
60
61 auto& programInstance = parameters.programs.debug;
62
63 for (auto matrix : matrices) {
64 programInstance.draw(
65 parameters.context,
66 gl::LineStrip { 4.0f * parameters.pixelRatio },
67 gl::DepthMode::disabled(),
68 gl::StencilMode::disabled(),
69 gl::ColorMode::unblended(),
70 parameters.staticData.tileBorderIndexBuffer,
71 parameters.staticData.tileBorderSegments,
72 programInstance.computeAllUniformValues(
73 DebugProgram::UniformValues {
74 uniforms::u_matrix::Value{ matrix },
75 uniforms::u_color::Value{ Color::red() }
76 },
77 paintAttributeData,
78 properties,
79 parameters.state.getZoom()
80 ),
81 programInstance.computeAllAttributeBindings(
82 parameters.staticData.tileVertexBuffer,
83 paintAttributeData,
84 properties
85 ),
86 "image"
87 );
88 }
89 }
90
91 std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString &,const TransformState &,const std::vector<const RenderLayer * > &,const RenderedQueryOptions &,const mat4 &) const92 RenderImageSource::queryRenderedFeatures(const ScreenLineString&,
93 const TransformState&,
94 const std::vector<const RenderLayer*>&,
95 const RenderedQueryOptions&,
96 const mat4&) const {
97 return std::unordered_map<std::string, std::vector<Feature>> {};
98 }
99
querySourceFeatures(const SourceQueryOptions &) const100 std::vector<Feature> RenderImageSource::querySourceFeatures(const SourceQueryOptions&) const {
101 return {};
102 }
103
update(Immutable<style::Source::Impl> baseImpl_,const std::vector<Immutable<Layer::Impl>> &,const bool needsRendering,const bool,const TileParameters & parameters)104 void RenderImageSource::update(Immutable<style::Source::Impl> baseImpl_,
105 const std::vector<Immutable<Layer::Impl>>&,
106 const bool needsRendering,
107 const bool,
108 const TileParameters& parameters) {
109 enabled = needsRendering;
110 if (!needsRendering) {
111 return;
112 }
113
114 auto transformState = parameters.transformState;
115 std::swap(baseImpl, baseImpl_);
116
117 auto coords = impl().getCoordinates();
118 std::shared_ptr<PremultipliedImage> image = impl().getImage();
119
120 if (!image || !image->valid()) {
121 enabled = false;
122 return;
123 }
124
125 // Compute the z0 tile coordinates for the given LatLngs
126 TileCoordinatePoint nePoint = { -INFINITY, -INFINITY };
127 TileCoordinatePoint swPoint = { INFINITY, INFINITY };
128 std::vector<TileCoordinatePoint> tileCoordinates;
129 for (LatLng latLng : coords) {
130 auto point = TileCoordinate::fromLatLng(0, latLng).p;
131 tileCoordinates.push_back(point);
132 swPoint.x = std::min(swPoint.x, point.x);
133 nePoint.x = std::max(nePoint.x, point.x);
134 swPoint.y = std::min(swPoint.y, point.y);
135 nePoint.y = std::max(nePoint.y, point.y);
136 }
137
138 // Calculate the optimum zoom level to determine the tile ids to use for transforms
139 auto dx = nePoint.x - swPoint.x;
140 auto dy = nePoint.y - swPoint.y;
141 auto dMax = std::max(dx, dy);
142 double zoom = std::max(0.0, std::floor(-::log2(dMax)));
143
144 // Only enable if the long side of the image is > 2 pixels. Resulting in a
145 // display of at least 2 x 1 px image
146 // A tile coordinate unit represents the length of one tile (tileSize) at a given zoom.
147 // To convert a tile coordinate to pixels, multiply by tileSize.
148 // Here dMax is in z0 tile units, so we also scale by 2^z to match current zoom.
149 enabled = dMax * std::pow(2.0, transformState.getZoom()) * util::tileSize > 2.0;
150 if (!enabled) {
151 return;
152 }
153
154 auto imageBounds = LatLngBounds::hull(coords[0], coords[1]);
155 imageBounds.extend(coords[2]);
156 imageBounds.extend(coords[3]);
157 auto tileCover = util::tileCover(imageBounds, zoom);
158 tileIds.clear();
159 tileIds.push_back(tileCover[0]);
160
161 bool hasVisibleTile = false;
162 // Add additional wrapped tile ids if neccessary
163 auto idealTiles = util::tileCover(transformState, transformState.getZoom());
164 for (auto tile : idealTiles) {
165 if (tile.wrap != 0 && tileCover[0].canonical.isChildOf(tile.canonical)) {
166 tileIds.push_back({ tile.wrap, tileCover[0].canonical });
167 hasVisibleTile = true;
168 }
169 else if (!hasVisibleTile) {
170 for (auto coveringTile: tileCover) {
171 if(coveringTile.canonical == tile.canonical ||
172 coveringTile.canonical.isChildOf(tile.canonical) ||
173 tile.canonical.isChildOf(coveringTile.canonical)) {
174 hasVisibleTile = true;
175 }
176 }
177 }
178 }
179
180 enabled = hasVisibleTile;
181 if (!enabled) {
182 return;
183 }
184
185 // Calculate Geometry Coordinates based on tile cover at ideal zoom
186 GeometryCoordinates geomCoords;
187 for (auto tileCoords : tileCoordinates) {
188 auto gc = TileCoordinate::toGeometryCoordinate(tileIds[0], tileCoords);
189 geomCoords.push_back(gc);
190 }
191 if (!bucket) {
192 bucket = std::make_unique<RasterBucket>(image);
193 } else {
194 bucket->clear();
195 if (image != bucket->image) {
196 bucket->setImage(image);
197 }
198 }
199
200 // Set Bucket Vertices, Indices, and segments
201 bucket->vertices.emplace_back(
202 RasterProgram::layoutVertex({ geomCoords[0].x, geomCoords[0].y }, { 0, 0 }));
203 bucket->vertices.emplace_back(
204 RasterProgram::layoutVertex({ geomCoords[1].x, geomCoords[1].y }, { util::EXTENT, 0 }));
205 bucket->vertices.emplace_back(
206 RasterProgram::layoutVertex({ geomCoords[3].x, geomCoords[3].y }, { 0, util::EXTENT }));
207 bucket->vertices.emplace_back(
208 RasterProgram::layoutVertex({ geomCoords[2].x, geomCoords[2].y }, { util::EXTENT, util::EXTENT }));
209
210 bucket->indices.emplace_back(0, 1, 2);
211 bucket->indices.emplace_back(1, 2, 3);
212
213 bucket->segments.emplace_back(0, 0, 4, 6);
214 }
215
dumpDebugLogs() const216 void RenderImageSource::dumpDebugLogs() const {
217 Log::Info(Event::General, "RenderImageSource::id: %s", impl().id.c_str());
218 Log::Info(Event::General, "RenderImageSource::loaded: %s", isLoaded() ? "yes" : "no");
219 }
220
221 } // namespace mbgl
222