#include #include #include #include #include #include #include #include #include namespace mbgl { using namespace style; SymbolQuad getIconQuad(const PositionedIcon& shapedIcon, const SymbolLayoutProperties::Evaluated& layout, const float layoutTextSize, const Shaping& shapedText) { const ImagePosition& image = shapedIcon.image(); // If you have a 10px icon that isn't perfectly aligned to the pixel grid it will cover 11 actual // pixels. The quad needs to be padded to account for this, otherwise they'll look slightly clipped // on one edge in some cases. const float border = 1.0; float top = shapedIcon.top() - border / image.pixelRatio; float left = shapedIcon.left() - border / image.pixelRatio; float bottom = shapedIcon.bottom() + border / image.pixelRatio; float right = shapedIcon.right() + border / image.pixelRatio; Point tl; Point tr; Point br; Point bl; if (layout.get() != IconTextFitType::None && shapedText) { auto iconWidth = right - left; auto iconHeight = bottom - top; auto size = layoutTextSize / 24.0f; auto textLeft = shapedText.left * size; auto textRight = shapedText.right * size; auto textTop = shapedText.top * size; auto textBottom = shapedText.bottom * size; auto textWidth = textRight - textLeft; auto textHeight = textBottom - textTop; auto padT = layout.get()[0]; auto padR = layout.get()[1]; auto padB = layout.get()[2]; auto padL = layout.get()[3]; auto offsetY = layout.get() == IconTextFitType::Width ? (textHeight - iconHeight) * 0.5 : 0; auto offsetX = layout.get() == IconTextFitType::Height ? (textWidth - iconWidth) * 0.5 : 0; auto width = layout.get() == IconTextFitType::Width || layout.get() == IconTextFitType::Both ? textWidth : iconWidth; auto height = layout.get() == IconTextFitType::Height || layout.get() == IconTextFitType::Both ? textHeight : iconHeight; left = textLeft + offsetX - padL; top = textTop + offsetY - padT; right = textLeft + offsetX + padR + width; bottom = textTop + offsetY + padB + height; tl = {left, top}; tr = {right, top}; br = {right, bottom}; bl = {left, bottom}; } else { tl = {left, top}; tr = {right, top}; br = {right, bottom}; bl = {left, bottom}; } const float angle = shapedIcon.angle(); if (angle) { // Compute the transformation matrix. float angle_sin = std::sin(angle); float angle_cos = std::cos(angle); std::array matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}}; tl = util::matrixMultiply(matrix, tl); tr = util::matrixMultiply(matrix, tr); bl = util::matrixMultiply(matrix, bl); br = util::matrixMultiply(matrix, br); } // Icon quad is padded, so texture coordinates also need to be padded. Rect textureRect { static_cast(image.textureRect.x - border), static_cast(image.textureRect.y - border), static_cast(image.textureRect.w + border * 2), static_cast(image.textureRect.h + border * 2) }; return SymbolQuad { tl, tr, bl, br, textureRect, shapedText.writingMode, { 0.0f, 0.0f } }; } SymbolQuads getGlyphQuads(const Shaping& shapedText, const SymbolLayoutProperties::Evaluated& layout, const style::SymbolPlacementType placement, const GlyphPositionMap& positions) { const float textRotate = layout.get() * util::DEG2RAD; const float oneEm = 24.0; std::array textOffset = layout.get(); textOffset[0] *= oneEm; textOffset[1] *= oneEm; SymbolQuads quads; for (const PositionedGlyph &positionedGlyph: shapedText.positionedGlyphs) { auto positionsIt = positions.find(positionedGlyph.glyph); if (positionsIt == positions.end()) continue; const GlyphPosition& glyph = positionsIt->second; const Rect& rect = glyph.rect; // The rects have an addditional buffer that is not included in their size; const float glyphPadding = 1.0f; const float rectBuffer = 3.0f + glyphPadding; const float halfAdvance = glyph.metrics.advance / 2.0; const bool alongLine = layout.get() == AlignmentType::Map && placement == SymbolPlacementType::Line; const Point glyphOffset = alongLine ? Point{ positionedGlyph.x + halfAdvance, positionedGlyph.y } : Point{ 0.0f, 0.0f }; const Point builtInOffset = alongLine ? Point{ 0.0f, 0.0f } : Point{ positionedGlyph.x + halfAdvance + textOffset[0], positionedGlyph.y + textOffset[1] }; const float x1 = glyph.metrics.left - rectBuffer - halfAdvance + builtInOffset.x; const float y1 = -glyph.metrics.top - rectBuffer + builtInOffset.y; const float x2 = x1 + rect.w; const float y2 = y1 + rect.h; const Point center{builtInOffset.x - halfAdvance, static_cast(static_cast(glyph.metrics.advance) / 2.0)}; Point tl{x1, y1}; Point tr{x2, y1}; Point bl{x1, y2}; Point br{x2, y2}; if (positionedGlyph.angle != 0) { tl = util::rotate(tl - center, positionedGlyph.angle) + center; tr = util::rotate(tr - center, positionedGlyph.angle) + center; bl = util::rotate(bl - center, positionedGlyph.angle) + center; br = util::rotate(br - center, positionedGlyph.angle) + center; } if (textRotate) { // Compute the transformation matrix. float angle_sin = std::sin(textRotate); float angle_cos = std::cos(textRotate); std::array matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}}; tl = util::matrixMultiply(matrix, tl); tr = util::matrixMultiply(matrix, tr); bl = util::matrixMultiply(matrix, bl); br = util::matrixMultiply(matrix, br); } quads.emplace_back(tl, tr, bl, br, rect, shapedText.writingMode, glyphOffset); } return quads; } } // namespace mbgl