1 #include <mbgl/layout/symbol_projection.hpp> 2 #include <mbgl/map/transform_state.hpp> 3 #include <mbgl/renderer/render_tile.hpp> 4 #include <mbgl/renderer/buckets/symbol_bucket.hpp> 5 #include <mbgl/renderer/layers/render_symbol_layer.hpp> 6 #include <mbgl/util/optional.hpp> 7 #include <mbgl/util/math.hpp> 8 9 namespace mbgl { 10 11 /* 12 * # Overview of coordinate spaces 13 * 14 * ## Tile coordinate spaces 15 * Each label has an anchor. Some labels have corresponding line geometries. 16 * The points for both anchors and lines are stored in tile units. Each tile has it's own 17 * coordinate space going from (0, 0) at the top left to (EXTENT, EXTENT) at the bottom right. 18 * 19 * ## GL coordinate space 20 * At the end of everything, the vertex shader needs to produce a position in GL coordinate space, 21 * which is (-1, 1) at the top left and (1, -1) in the bottom right. 22 * 23 * ## Map pixel coordinate spaces 24 * Each tile has a pixel coordinate space. It's just the tile units scaled so that one unit is 25 * whatever counts as 1 pixel at the current zoom. 26 * This space is used for pitch-alignment=map, rotation-alignment=map 27 * 28 * ## Rotated map pixel coordinate spaces 29 * Like the above, but rotated so axis of the space are aligned with the viewport instead of the tile. 30 * This space is used for pitch-alignment=map, rotation-alignment=viewport 31 * 32 * ## Viewport pixel coordinate space 33 * (0, 0) is at the top left of the canvas and (pixelWidth, pixelHeight) is at the bottom right corner 34 * of the canvas. This space is used for pitch-alignment=viewport 35 * 36 * 37 * # Vertex projection 38 * It goes roughly like this: 39 * 1. project the anchor and line from tile units into the correct label coordinate space 40 * - map pixel space pitch-alignment=map rotation-alignment=map 41 * - rotated map pixel space pitch-alignment=map rotation-alignment=viewport 42 * - viewport pixel space pitch-alignment=viewport rotation-alignment=* 43 * 2. if the label follows a line, find the point along the line that is the correct distance from the anchor. 44 * 3. add the glyph's corner offset to the point from step 3 45 * 4. convert from the label coordinate space to gl coordinates 46 * 47 * For horizontal labels we want to do step 1 in the shader for performance reasons (no cpu work). 48 * This is what `u_label_plane_matrix` is used for. 49 * For labels aligned with lines we have to steps 1 and 2 on the cpu since we need access to the line geometry. 50 * This is what `updateLineLabels(...)` does. 51 * Since the conversion is handled on the cpu we just set `u_label_plane_matrix` to an identity matrix. 52 * 53 * Steps 3 and 4 are done in the shaders for all labels. 54 */ 55 56 /* 57 * Returns a matrix for converting from tile units to the correct label coordinate space. 58 */ getLabelPlaneMatrix(const mat4 & posMatrix,const bool pitchWithMap,const bool rotateWithMap,const TransformState & state,const float pixelsToTileUnits)59 mat4 getLabelPlaneMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits) { 60 mat4 m; 61 matrix::identity(m); 62 if (pitchWithMap) { 63 matrix::scale(m, m, 1 / pixelsToTileUnits, 1 / pixelsToTileUnits, 1); 64 if (!rotateWithMap) { 65 matrix::rotate_z(m, m, state.getAngle()); 66 } 67 } else { 68 matrix::scale(m, m, state.getSize().width / 2.0, -(state.getSize().height / 2.0), 1.0); 69 matrix::translate(m, m, 1, -1, 0); 70 matrix::multiply(m, m, posMatrix); 71 } 72 return m; 73 } 74 75 /* 76 * Returns a matrix for converting from the correct label coordinate space to gl coords. 77 */ getGlCoordMatrix(const mat4 & posMatrix,const bool pitchWithMap,const bool rotateWithMap,const TransformState & state,const float pixelsToTileUnits)78 mat4 getGlCoordMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits) { 79 mat4 m; 80 matrix::identity(m); 81 if (pitchWithMap) { 82 matrix::multiply(m, m, posMatrix); 83 matrix::scale(m, m, pixelsToTileUnits, pixelsToTileUnits, 1); 84 if (!rotateWithMap) { 85 matrix::rotate_z(m, m, -state.getAngle()); 86 } 87 } else { 88 matrix::scale(m, m, 1, -1, 1); 89 matrix::translate(m, m, -1, -1, 0); 90 matrix::scale(m, m, 2.0 / state.getSize().width, 2.0 / state.getSize().height, 1.0); 91 } 92 return m; 93 } 94 project(const Point<float> & point,const mat4 & matrix)95 PointAndCameraDistance project(const Point<float>& point, const mat4& matrix) { 96 vec4 pos = {{ point.x, point.y, 0, 1 }}; 97 matrix::transformMat4(pos, pos, matrix); 98 return {{ static_cast<float>(pos[0] / pos[3]), static_cast<float>(pos[1] / pos[3]) }, pos[3] }; 99 } 100 evaluateSizeForFeature(const ZoomEvaluatedSize & zoomEvaluatedSize,const PlacedSymbol & placedSymbol)101 float evaluateSizeForFeature(const ZoomEvaluatedSize& zoomEvaluatedSize, const PlacedSymbol& placedSymbol) { 102 if (zoomEvaluatedSize.isFeatureConstant) { 103 return zoomEvaluatedSize.size; 104 } else { 105 if (zoomEvaluatedSize.isZoomConstant) { 106 return placedSymbol.lowerSize; 107 } else { 108 return placedSymbol.lowerSize + zoomEvaluatedSize.sizeT * (placedSymbol.upperSize - placedSymbol.lowerSize); 109 } 110 } 111 } 112 isVisible(const vec4 & anchorPos,const std::array<double,2> & clippingBuffer)113 bool isVisible(const vec4& anchorPos, const std::array<double, 2>& clippingBuffer) { 114 const float x = anchorPos[0] / anchorPos[3]; 115 const float y = anchorPos[1] / anchorPos[3]; 116 const bool inPaddedViewport = ( 117 x >= -clippingBuffer[0] && 118 x <= clippingBuffer[0] && 119 y >= -clippingBuffer[1] && 120 y <= clippingBuffer[1]); 121 return inPaddedViewport; 122 } 123 addDynamicAttributes(const Point<float> & anchorPoint,const float angle,gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> & dynamicVertexArray)124 void addDynamicAttributes(const Point<float>& anchorPoint, const float angle, 125 gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray) { 126 auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(anchorPoint, angle); 127 dynamicVertexArray.emplace_back(dynamicVertex); 128 dynamicVertexArray.emplace_back(dynamicVertex); 129 dynamicVertexArray.emplace_back(dynamicVertex); 130 dynamicVertexArray.emplace_back(dynamicVertex); 131 } 132 hideGlyphs(size_t numGlyphs,gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> & dynamicVertexArray)133 void hideGlyphs(size_t numGlyphs, gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray) { 134 const Point<float> offscreenPoint = { -INFINITY, -INFINITY }; 135 for (size_t i = 0; i < numGlyphs; i++) { 136 addDynamicAttributes(offscreenPoint, 0, dynamicVertexArray); 137 } 138 } 139 140 enum PlacementResult { 141 OK, 142 NotEnoughRoom, 143 NeedsFlipping, 144 UseVertical 145 }; 146 projectTruncatedLineSegment(const Point<float> & previousTilePoint,const Point<float> & currentTilePoint,const Point<float> & previousProjectedPoint,const float minimumLength,const mat4 & projectionMatrix)147 Point<float> projectTruncatedLineSegment(const Point<float>& previousTilePoint, const Point<float>& currentTilePoint, const Point<float>& previousProjectedPoint, const float minimumLength, const mat4& projectionMatrix) { 148 // We are assuming "previousTilePoint" won't project to a point within one unit of the camera plane 149 // If it did, that would mean our label extended all the way out from within the viewport to a (very distant) 150 // point near the plane of the camera. We wouldn't be able to render the label anyway once it crossed the 151 // plane of the camera. 152 const Point<float> projectedUnitVertex = project(previousTilePoint + util::unit<float>(previousTilePoint - currentTilePoint), projectionMatrix).first; 153 const Point<float> projectedUnitSegment = previousProjectedPoint - projectedUnitVertex; 154 155 return previousProjectedPoint + (projectedUnitSegment * (minimumLength / util::mag<float>(projectedUnitSegment))); 156 } 157 placeGlyphAlongLine(const float offsetX,const float lineOffsetX,const float lineOffsetY,const bool flip,const Point<float> & projectedAnchorPoint,const Point<float> & tileAnchorPoint,const uint16_t anchorSegment,const GeometryCoordinates & line,const std::vector<float> & tileDistances,const mat4 & labelPlaneMatrix,const bool returnTileDistance)158 optional<PlacedGlyph> placeGlyphAlongLine(const float offsetX, const float lineOffsetX, const float lineOffsetY, const bool flip, 159 const Point<float>& projectedAnchorPoint, const Point<float>& tileAnchorPoint, const uint16_t anchorSegment, const GeometryCoordinates& line, const std::vector<float>& tileDistances, const mat4& labelPlaneMatrix, const bool returnTileDistance) { 160 161 const float combinedOffsetX = flip ? 162 offsetX - lineOffsetX : 163 offsetX + lineOffsetX; 164 165 int16_t dir = combinedOffsetX > 0 ? 1 : -1; 166 167 float angle = 0.0; 168 if (flip) { 169 // The label needs to be flipped to keep text upright. 170 // Iterate in the reverse direction. 171 dir *= -1; 172 angle = M_PI; 173 } 174 175 if (dir < 0) angle += M_PI; 176 177 int32_t currentIndex = dir > 0 ? anchorSegment : anchorSegment + 1; 178 179 const int32_t initialIndex = currentIndex; 180 Point<float> current = projectedAnchorPoint; 181 Point<float> prev = projectedAnchorPoint; 182 float distanceToPrev = 0.0; 183 float currentSegmentDistance = 0.0; 184 const float absOffsetX = std::abs(combinedOffsetX); 185 186 while (distanceToPrev + currentSegmentDistance <= absOffsetX) { 187 currentIndex += dir; 188 189 // offset does not fit on the projected line 190 if (currentIndex < 0 || currentIndex >= static_cast<int32_t>(line.size())) { 191 return {}; 192 } 193 194 prev = current; 195 PointAndCameraDistance projection = project(convertPoint<float>(line.at(currentIndex)), labelPlaneMatrix); 196 if (projection.second > 0) { 197 current = projection.first; 198 } else { 199 // The vertex is behind the plane of the camera, so we can't project it 200 // Instead, we'll create a vertex along the line that's far enough to include the glyph 201 const Point<float> previousTilePoint = distanceToPrev == 0 ? 202 tileAnchorPoint : 203 convertPoint<float>(line.at(currentIndex - dir)); 204 const Point<float> currentTilePoint = convertPoint<float>(line.at(currentIndex)); 205 current = projectTruncatedLineSegment(previousTilePoint, currentTilePoint, prev, absOffsetX - distanceToPrev + 1, labelPlaneMatrix); 206 } 207 208 distanceToPrev += currentSegmentDistance; 209 currentSegmentDistance = util::dist<float>(prev, current); 210 } 211 212 // The point is on the current segment. Interpolate to find it. 213 const float segmentInterpolationT = (absOffsetX - distanceToPrev) / currentSegmentDistance; 214 const Point<float> prevToCurrent = current - prev; 215 Point<float> p = (prevToCurrent * segmentInterpolationT) + prev; 216 217 // offset the point from the line to text-offset and icon-offset 218 p += util::perp(prevToCurrent) * static_cast<float>(lineOffsetY * dir / util::mag(prevToCurrent)); 219 220 const float segmentAngle = angle + std::atan2(current.y - prev.y, current.x - prev.x); 221 222 return {{ 223 p, 224 segmentAngle, 225 returnTileDistance ? 226 TileDistance( 227 (currentIndex - dir) == initialIndex ? 0 : tileDistances[currentIndex - dir], 228 absOffsetX - distanceToPrev 229 ) : 230 optional<TileDistance>() 231 }}; 232 } 233 placeFirstAndLastGlyph(const float fontScale,const float lineOffsetX,const float lineOffsetY,const bool flip,const Point<float> & anchorPoint,const Point<float> & tileAnchorPoint,const PlacedSymbol & symbol,const mat4 & labelPlaneMatrix,const bool returnTileDistance)234 optional<std::pair<PlacedGlyph, PlacedGlyph>> placeFirstAndLastGlyph(const float fontScale, 235 const float lineOffsetX, 236 const float lineOffsetY, 237 const bool flip, 238 const Point<float>& anchorPoint, 239 const Point<float>& tileAnchorPoint, 240 const PlacedSymbol& symbol, 241 const mat4& labelPlaneMatrix, 242 const bool returnTileDistance) { 243 if (symbol.glyphOffsets.empty()) { 244 assert(false); 245 return optional<std::pair<PlacedGlyph, PlacedGlyph>>(); 246 } 247 248 const float firstGlyphOffset = symbol.glyphOffsets.front(); 249 const float lastGlyphOffset = symbol.glyphOffsets.back();; 250 251 optional<PlacedGlyph> firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol.segment, symbol.line, symbol.tileDistances, labelPlaneMatrix, returnTileDistance); 252 if (!firstPlacedGlyph) 253 return optional<std::pair<PlacedGlyph, PlacedGlyph>>(); 254 255 optional<PlacedGlyph> lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol.segment, symbol.line, symbol.tileDistances, labelPlaneMatrix, returnTileDistance); 256 if (!lastPlacedGlyph) 257 return optional<std::pair<PlacedGlyph, PlacedGlyph>>(); 258 259 return std::make_pair(*firstPlacedGlyph, *lastPlacedGlyph); 260 } 261 requiresOrientationChange(const WritingModeType writingModes,const Point<float> & firstPoint,const Point<float> & lastPoint,const float aspectRatio)262 optional<PlacementResult> requiresOrientationChange(const WritingModeType writingModes, const Point<float>& firstPoint, const Point<float>& lastPoint, const float aspectRatio) { 263 if (writingModes == (WritingModeType::Horizontal | WritingModeType::Vertical)) { 264 // On top of choosing whether to flip, choose whether to render this version of the glyphs or the alternate 265 // vertical glyphs. We can't just filter out vertical glyphs in the horizontal range because the horizontal 266 // and vertical versions can have slightly different projections which could lead to angles where both or 267 // neither showed. 268 auto rise = std::abs(lastPoint.y - firstPoint.y); 269 auto run = std::abs(lastPoint.x - firstPoint.x) * aspectRatio; 270 if (rise > run) { 271 return PlacementResult::UseVertical; 272 } 273 } 274 275 if ((writingModes == WritingModeType::Vertical) ? 276 (firstPoint.y < lastPoint.y) : 277 (firstPoint.x > lastPoint.x)) { 278 // Includes "horizontalOnly" case for labels without vertical glyphs 279 return PlacementResult::NeedsFlipping; 280 } 281 return {}; 282 } 283 placeGlyphsAlongLine(const PlacedSymbol & symbol,const float fontSize,const bool flip,const bool keepUpright,const mat4 & posMatrix,const mat4 & labelPlaneMatrix,const mat4 & glCoordMatrix,gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> & dynamicVertexArray,const Point<float> & projectedAnchorPoint,const float aspectRatio)284 PlacementResult placeGlyphsAlongLine(const PlacedSymbol& symbol, 285 const float fontSize, 286 const bool flip, 287 const bool keepUpright, 288 const mat4& posMatrix, 289 const mat4& labelPlaneMatrix, 290 const mat4& glCoordMatrix, 291 gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray, 292 const Point<float>& projectedAnchorPoint, 293 const float aspectRatio) { 294 const float fontScale = fontSize / 24.0; 295 const float lineOffsetX = symbol.lineOffset[0] * fontSize; 296 const float lineOffsetY = symbol.lineOffset[1] * fontSize; 297 298 std::vector<PlacedGlyph> placedGlyphs; 299 if (symbol.glyphOffsets.size() > 1) { 300 301 const optional<std::pair<PlacedGlyph, PlacedGlyph>> firstAndLastGlyph = 302 placeFirstAndLastGlyph(fontScale, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol, labelPlaneMatrix, false); 303 if (!firstAndLastGlyph) { 304 return PlacementResult::NotEnoughRoom; 305 } 306 307 const Point<float> firstPoint = project(firstAndLastGlyph->first.point, glCoordMatrix).first; 308 const Point<float> lastPoint = project(firstAndLastGlyph->second.point, glCoordMatrix).first; 309 310 if (keepUpright && !flip) { 311 auto orientationChange = requiresOrientationChange(symbol.writingModes, firstPoint, lastPoint, aspectRatio); 312 if (orientationChange) { 313 return *orientationChange; 314 } 315 } 316 317 placedGlyphs.push_back(firstAndLastGlyph->first); 318 for (size_t glyphIndex = 1; glyphIndex < symbol.glyphOffsets.size() - 1; glyphIndex++) { 319 const float glyphOffsetX = symbol.glyphOffsets[glyphIndex]; 320 // Since first and last glyph fit on the line, we're sure that the rest of the glyphs can be placed 321 auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, symbol.tileDistances, labelPlaneMatrix, false); 322 placedGlyphs.push_back(*placedGlyph); 323 } 324 placedGlyphs.push_back(firstAndLastGlyph->second); 325 } else if (symbol.glyphOffsets.size() == 1) { 326 // Only a single glyph to place 327 // So, determine whether to flip based on projected angle of the line segment it's on 328 if (keepUpright && !flip) { 329 const Point<float> a = project(symbol.anchorPoint, posMatrix).first; 330 const Point<float> tileSegmentEnd = convertPoint<float>(symbol.line.at(symbol.segment + 1)); 331 const PointAndCameraDistance projectedVertex = project(tileSegmentEnd, posMatrix); 332 // We know the anchor will be in the viewport, but the end of the line segment may be 333 // behind the plane of the camera, in which case we can use a point at any arbitrary (closer) 334 // point on the segment. 335 const Point<float> b = (projectedVertex.second > 0) ? 336 projectedVertex.first : 337 projectTruncatedLineSegment(symbol.anchorPoint,tileSegmentEnd, a, 1, posMatrix); 338 339 auto orientationChange = requiresOrientationChange(symbol.writingModes, a, b, aspectRatio); 340 if (orientationChange) { 341 return *orientationChange; 342 } 343 } 344 const float glyphOffsetX = symbol.glyphOffsets.front(); 345 optional<PlacedGlyph> singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetX, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, 346 symbol.line, symbol.tileDistances, labelPlaneMatrix, false); 347 if (!singleGlyph) 348 return PlacementResult::NotEnoughRoom; 349 350 placedGlyphs.push_back(*singleGlyph); 351 } 352 353 // The number of placedGlyphs must equal the number of glyphOffsets, which must correspond to the number of glyph vertices 354 // There may be 0 glyphs here, if a label consists entirely of glyphs that have 0x0 dimensions 355 for (auto& placedGlyph : placedGlyphs) { 356 addDynamicAttributes(placedGlyph.point, placedGlyph.angle, dynamicVertexArray); 357 } 358 359 return PlacementResult::OK; 360 } 361 362 reprojectLineLabels(gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> & dynamicVertexArray,const std::vector<PlacedSymbol> & placedSymbols,const mat4 & posMatrix,const style::SymbolPropertyValues & values,const RenderTile & tile,const SymbolSizeBinder & sizeBinder,const TransformState & state)363 void reprojectLineLabels(gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray, const std::vector<PlacedSymbol>& placedSymbols, 364 const mat4& posMatrix, const style::SymbolPropertyValues& values, 365 const RenderTile& tile, const SymbolSizeBinder& sizeBinder, const TransformState& state) { 366 367 const ZoomEvaluatedSize partiallyEvaluatedSize = sizeBinder.evaluateForZoom(state.getZoom()); 368 369 const std::array<double, 2> clippingBuffer = {{ 256.0 / state.getSize().width * 2.0 + 1.0, 256.0 / state.getSize().height * 2.0 + 1.0 }}; 370 371 const bool pitchWithMap = values.pitchAlignment == style::AlignmentType::Map; 372 const bool rotateWithMap = values.rotationAlignment == style::AlignmentType::Map; 373 const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1, state.getZoom()); 374 375 const mat4 labelPlaneMatrix = getLabelPlaneMatrix(posMatrix, pitchWithMap, 376 rotateWithMap, state, pixelsToTileUnits); 377 378 const mat4 glCoordMatrix = getGlCoordMatrix(posMatrix, pitchWithMap, rotateWithMap, state, pixelsToTileUnits); 379 380 dynamicVertexArray.clear(); 381 382 bool useVertical = false; 383 384 for (auto& placedSymbol : placedSymbols) { 385 // Don't do calculations for vertical glyphs unless the previous symbol was horizontal 386 // and we determined that vertical glyphs were necessary. 387 // Also don't do calculations for symbols that are collided and fully faded out 388 if (placedSymbol.hidden || (placedSymbol.writingModes == WritingModeType::Vertical && !useVertical)) { 389 hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray); 390 continue; 391 } 392 // Awkward... but we're counting on the paired "vertical" symbol coming immediately after its horizontal counterpart 393 useVertical = false; 394 395 vec4 anchorPos = {{ placedSymbol.anchorPoint.x, placedSymbol.anchorPoint.y, 0, 1 }}; 396 matrix::transformMat4(anchorPos, anchorPos, posMatrix); 397 398 // Don't bother calculating the correct point for invisible labels. 399 if (!isVisible(anchorPos, clippingBuffer)) { 400 hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray); 401 continue; 402 } 403 404 const float cameraToAnchorDistance = anchorPos[3]; 405 const float perspectiveRatio = 0.5 + 0.5 * (cameraToAnchorDistance / state.getCameraToCenterDistance()); 406 407 const float fontSize = evaluateSizeForFeature(partiallyEvaluatedSize, placedSymbol); 408 const float pitchScaledFontSize = values.pitchAlignment == style::AlignmentType::Map ? 409 fontSize * perspectiveRatio : 410 fontSize / perspectiveRatio; 411 412 const Point<float> anchorPoint = project(placedSymbol.anchorPoint, labelPlaneMatrix).first; 413 414 PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint, state.getSize().aspectRatio()); 415 416 useVertical = placeUnflipped == PlacementResult::UseVertical; 417 418 if (placeUnflipped == PlacementResult::NotEnoughRoom || useVertical || 419 (placeUnflipped == PlacementResult::NeedsFlipping && 420 placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint, state.getSize().aspectRatio()) == PlacementResult::NotEnoughRoom)) { 421 hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray); 422 } 423 } 424 } 425 } // end namespace mbgl 426