#include #include #include #include #include #include #include #include #include #include #include namespace mbgl { using namespace style; void Painter::renderFill(PaintParameters& parameters, FillBucket& bucket, const FillLayer& layer, const RenderTile& tile) { const FillPaintProperties& properties = layer.impl->paint; mat4 vertexMatrix = tile.translatedMatrix(properties.fillTranslate, properties.fillTranslateAnchor, state); Color fillColor = properties.fillColor; float opacity = properties.fillOpacity; const bool isOutlineColorDefined = !properties.fillOutlineColor.isUndefined(); Color strokeColor = isOutlineColorDefined? properties.fillOutlineColor : fillColor; const auto viewport = context.viewport.getCurrentValue(); const std::array worldSize{ { static_cast(viewport.width), static_cast(viewport.height) } }; bool pattern = !properties.fillPattern.value.from.empty(); bool outline = properties.fillAntialias && !pattern && isOutlineColorDefined; bool fringeline = properties.fillAntialias && !pattern && !isOutlineColorDefined; context.stencilOp = { gl::StencilTestOperation::Keep, gl::StencilTestOperation::Keep, gl::StencilTestOperation::Replace }; context.stencilTest = true; context.depthFunc = gl::DepthTestFunction::LessEqual; context.depthTest = true; context.depthMask = true; context.lineWidth = 2.0f; // This is always fixed and does not depend on the pixelRatio! auto& outlineShader = parameters.shaders.fillOutline; auto& patternShader = parameters.shaders.fillPattern; auto& outlinePatternShader = parameters.shaders.fillOutlinePattern; auto& plainShader = parameters.shaders.fill; // Because we're drawing top-to-bottom, and we update the stencil mask // befrom, we have to draw the outline first (!) if (outline && pass == RenderPass::Translucent) { context.program = outlineShader.getID(); outlineShader.u_matrix = vertexMatrix; outlineShader.u_outline_color = strokeColor; outlineShader.u_opacity = opacity; // Draw the entire line outlineShader.u_world = worldSize; if (isOutlineColorDefined) { // If we defined a different color for the fill outline, we are // going to ignore the bits in 0x07 and just care about the global // clipping mask. setDepthSublayer(2); // OK } else { // Otherwise, we only want to drawFill the antialiased parts that are // *outside* the current shape. This is important in case the fill // or stroke color is translucent. If we wouldn't clip to outside // the current shape, some pixels from the outline stroke overlapped // the (non-antialiased) fill. setDepthSublayer(0); // OK } bucket.drawVertices(outlineShader, context, paintMode()); } if (pattern) { optional imagePosA = spriteAtlas->getPosition( properties.fillPattern.value.from, SpritePatternMode::Repeating); optional imagePosB = spriteAtlas->getPosition(properties.fillPattern.value.to, SpritePatternMode::Repeating); // Image fill. if (pass == RenderPass::Translucent && imagePosA && imagePosB) { context.program = patternShader.getID(); patternShader.u_matrix = vertexMatrix; patternShader.u_pattern_tl_a = imagePosA->tl; patternShader.u_pattern_br_a = imagePosA->br; patternShader.u_pattern_tl_b = imagePosB->tl; patternShader.u_pattern_br_b = imagePosB->br; patternShader.u_opacity = properties.fillOpacity; patternShader.u_image = 0; patternShader.u_mix = properties.fillPattern.value.t; patternShader.u_pattern_size_a = imagePosA->size; patternShader.u_pattern_size_b = imagePosB->size; patternShader.u_scale_a = properties.fillPattern.value.fromScale; patternShader.u_scale_b = properties.fillPattern.value.toScale; patternShader.u_tile_units_to_pixels = 1.0f / tile.id.pixelsToTileUnits(1.0f, state.getIntegerZoom()); GLint tileSizeAtNearestZoom = util::tileSize * state.zoomScale(state.getIntegerZoom() - tile.id.canonical.z); GLint pixelX = tileSizeAtNearestZoom * (tile.id.canonical.x + tile.id.wrap * state.zoomScale(tile.id.canonical.z)); GLint pixelY = tileSizeAtNearestZoom * tile.id.canonical.y; patternShader.u_pixel_coord_upper = {{ float(pixelX >> 16), float(pixelY >> 16) }}; patternShader.u_pixel_coord_lower = {{ float(pixelX & 0xFFFF), float(pixelY & 0xFFFF) }}; spriteAtlas->bind(true, context, 0); // Draw the actual triangles into the color & stencil buffer. setDepthSublayer(0); bucket.drawElements(patternShader, context, paintMode()); if (properties.fillAntialias && !isOutlineColorDefined) { context.program = outlinePatternShader.getID(); outlinePatternShader.u_matrix = vertexMatrix; outlinePatternShader.u_pattern_tl_a = imagePosA->tl; outlinePatternShader.u_pattern_br_a = imagePosA->br; outlinePatternShader.u_pattern_tl_b = imagePosB->tl; outlinePatternShader.u_pattern_br_b = imagePosB->br; outlinePatternShader.u_opacity = properties.fillOpacity; outlinePatternShader.u_image = 0; outlinePatternShader.u_mix = properties.fillPattern.value.t; outlinePatternShader.u_pattern_size_a = imagePosA->size; outlinePatternShader.u_pattern_size_b = imagePosB->size; outlinePatternShader.u_scale_a = properties.fillPattern.value.fromScale; outlinePatternShader.u_scale_b = properties.fillPattern.value.toScale; outlinePatternShader.u_tile_units_to_pixels = 1.0f / tile.id.pixelsToTileUnits(1.0f, state.getIntegerZoom()); outlinePatternShader.u_pixel_coord_upper = {{ float(pixelX >> 16), float(pixelY >> 16) }}; outlinePatternShader.u_pixel_coord_lower = {{ float(pixelX & 0xFFFF), float(pixelY & 0xFFFF) }}; // Draw the entire line outlinePatternShader.u_world = worldSize; spriteAtlas->bind(true, context, 0); setDepthSublayer(2); bucket.drawVertices(outlinePatternShader, context, paintMode()); } } } else { // No image fill. if ((fillColor.a >= 1.0f && opacity >= 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. context.program = plainShader.getID(); plainShader.u_matrix = vertexMatrix; plainShader.u_color = fillColor; plainShader.u_opacity = opacity; // Draw the actual triangles into the color & stencil buffer. setDepthSublayer(1); bucket.drawElements(plainShader, context, paintMode()); } } // 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) { context.program = outlineShader.getID(); outlineShader.u_matrix = vertexMatrix; outlineShader.u_outline_color = fillColor; outlineShader.u_opacity = opacity; // Draw the entire line outlineShader.u_world = worldSize; setDepthSublayer(2); bucket.drawVertices(outlineShader, context, paintMode()); } } } // namespace mbgl