#include #include #include #include #include #include #include #include #include using namespace mbgl; void Painter::renderFill(FillBucket& bucket, const FillProperties& properties, const Tile::ID& id, const mat4 &vtxMatrix) { Color fill_color = properties.fill_color; fill_color[0] *= properties.opacity; fill_color[1] *= properties.opacity; fill_color[2] *= properties.opacity; fill_color[3] *= properties.opacity; Color stroke_color = properties.stroke_color; if (stroke_color[3] < 0) { stroke_color = fill_color; } else { stroke_color[0] *= properties.opacity; stroke_color[1] *= properties.opacity; stroke_color[2] *= properties.opacity; stroke_color[3] *= properties.opacity; } const bool pattern = properties.image.size(); bool outline = properties.antialias && !pattern && properties.stroke_color != properties.fill_color; bool fringeline = properties.antialias && !pattern && properties.stroke_color == properties.fill_color; // Because we're drawing top-to-bottom, and we update the stencil mask // below, we have to draw the outline first (!) if (outline && pass == RenderPass::Translucent) { useProgram(outlineShader->program); outlineShader->setMatrix(vtxMatrix); lineWidth(2.0f); // This is always fixed and does not depend on the pixelRatio! outlineShader->setColor(stroke_color); // Draw the entire line outlineShader->setWorld({{ static_cast(map.getState().getFramebufferWidth()), static_cast(map.getState().getFramebufferHeight()) }}); depthRange(strata, 1.0f); bucket.drawVertices(*outlineShader); } if (pattern) { // Image fill. Sprite &sprite = *map.getSprite(); if (pass == RenderPass::Translucent && sprite) { SpriteAtlas &spriteAtlas = *map.getSpriteAtlas(); const Rect pos = spriteAtlas.getImage(properties.image, sprite); // `repeating` indicates that the image will be used in a repeating pattern // repeating pattern images are assumed to have a 1px padding that mirrors the opposite edge // positions for repeating images are adjusted to exclude the edge const int repeating = 1; const std::array size {{ float(pos.w) / spriteAtlas.getPixelRatio(), float(pos.h) / spriteAtlas.getPixelRatio(), }}; const std::array tl {{ (float(pos.x + repeating) / spriteAtlas.getWidth()), (float(pos.y + repeating) / spriteAtlas.getHeight()), }}; const std::array br {{ (float(pos.x + pos.w - 2 * repeating) / spriteAtlas.getWidth()), (float(pos.y + pos.h - 2 * repeating) / spriteAtlas.getHeight()), }}; const float mix = std::fmod(float(map.getState().getZoom()), 1.0f); const float factor = 8.0 / std::pow(2, map.getState().getIntegerZoom() - id.z); mat3 patternMatrix; matrix::identity(patternMatrix); matrix::scale(patternMatrix, patternMatrix, 1.0f / (size[0] * factor), 1.0f / (size[1] * factor)); useProgram(patternShader->program); patternShader->setMatrix(vtxMatrix); patternShader->setPatternTopLeft(tl); patternShader->setPatternBottomRight(br); patternShader->setOpacity(properties.opacity); patternShader->setImage(0); patternShader->setMix(mix); patternShader->setPatternMatrix(patternMatrix); glActiveTexture(GL_TEXTURE0); spriteAtlas.bind(true); // Draw the actual triangles into the color & stencil buffer. depthRange(strata, 1.0f); bucket.drawElements(*patternShader); } } else { // No image fill. if ((fill_color[3] >= 1.0f) == (pass == RenderPass::Opaque)) { // Only draw the fill when it's either opaque and we're drawing opaque // fragments or when it's translucent and we're drawing translucent // fragments // Draw filling rectangle. useProgram(plainShader->program); plainShader->setMatrix(vtxMatrix); plainShader->setColor(fill_color); // Draw the actual triangles into the color & stencil buffer. depthRange(strata + strata_epsilon, 1.0f); bucket.drawElements(*plainShader); } } // Because we're drawing top-to-bottom, and we update the stencil mask // below, we have to draw the outline first (!) if (fringeline && pass == RenderPass::Translucent) { useProgram(outlineShader->program); outlineShader->setMatrix(vtxMatrix); lineWidth(2.0f); // This is always fixed and does not depend on the pixelRatio! outlineShader->setColor(fill_color); // Draw the entire line outlineShader->setWorld({{ static_cast(map.getState().getFramebufferWidth()), static_cast(map.getState().getFramebufferHeight()) }}); depthRange(strata + strata_epsilon, 1.0f); bucket.drawVertices(*outlineShader); } } void Painter::renderFill(FillBucket& bucket, std::shared_ptr layer_desc, const Tile::ID& id, const mat4 &matrix) { // Abort early. if (!bucket.hasData()) return; const FillProperties &properties = layer_desc->getProperties(); const mat4 &vtxMatrix = translatedMatrix(matrix, properties.translate, id, properties.translateAnchor); renderFill(bucket, properties, id, vtxMatrix); }