TEST(Buckets, LineBucket) { HeadlessBackend backend({ 512, 256 }); BackendScope scope { backend }; gl::Context context; LineBucket bucket { { {0, 0, 0}, MapMode::Static, 1.0 }, {}, {} }; ASSERT_FALSE(bucket.hasData()); ASSERT_FALSE(bucket.needsUpload()); // Ignore invalid feature type. GeometryCollection point { { { 0, 0 } } }; bucket.addFeature(StubGeometryTileFeature { {}, FeatureType::Point, point, properties }, point); ASSERT_FALSE(bucket.hasData()); GeometryCollection line { { { 0, 0 }, { 1, 1 } } }; bucket.addFeature(StubGeometryTileFeature { {}, FeatureType::LineString, line, properties }, line); ASSERT_TRUE(bucket.hasData()); ASSERT_TRUE(bucket.needsUpload()); bucket.upload(context); ASSERT_FALSE(bucket.needsUpload()); }
void Painter::renderLine(LineBucket& bucket, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) { // Abort early. if (pass == RenderPass::Opaque) return; if (!bucket.hasData()) return; const LineProperties &properties = layer_desc->getProperties<LineProperties>(); float antialiasing = 1 / state.getPixelRatio(); float width = properties.width; float offset = properties.gap_width == 0 ? 0 : (properties.gap_width + width) / 2; float blur = properties.blur + antialiasing; float inset = std::fmin((std::fmax(-1, offset - width / 2 - antialiasing / 2) + 1), 16.0f); float outset = std::fmin(offset + width / 2 + antialiasing / 2, 16.0f); Color color = properties.color; color[0] *= properties.opacity; color[1] *= properties.opacity; color[2] *= properties.opacity; color[3] *= properties.opacity; float dash_length = properties.dash_array[0]; float dash_gap = properties.dash_array[1]; float ratio = state.getPixelRatio(); mat4 vtxMatrix = translatedMatrix(matrix, properties.translate, id, properties.translateAnchor); depthRange(strata, 1.0f); // We're only drawing end caps + round line joins if the line is > 2px. Otherwise, they aren't visible anyway. if (bucket.hasPoints() && outset > 1.0f) { useProgram(linejoinShader->program); linejoinShader->u_matrix = vtxMatrix; linejoinShader->u_color = color; linejoinShader->u_world = {{ state.getFramebufferWidth() * 0.5f, state.getFramebufferHeight() * 0.5f }}; linejoinShader->u_linewidth = {{ ((outset - 0.25f) * state.getPixelRatio()), ((inset - 0.25f) * state.getPixelRatio()) }}; float pointSize = std::ceil(state.getPixelRatio() * outset * 2.0); #if defined(GL_ES_VERSION_2_0) linejoinShader->u_size = pointSize; #else MBGL_CHECK_ERROR(glPointSize(pointSize)); #endif bucket.drawPoints(*linejoinShader); } if (properties.image.size()) { SpriteAtlasPosition imagePos = spriteAtlas.getPosition(properties.image); float factor = 8.0 / std::pow(2, state.getIntegerZoom() - id.z); float fade = std::fmod(state.getZoom(), 1.0); useProgram(linepatternShader->program); linepatternShader->u_matrix = vtxMatrix; linepatternShader->u_exmatrix = extrudeMatrix; linepatternShader->u_linewidth = {{ outset, inset }}; linepatternShader->u_ratio = ratio; linepatternShader->u_blur = blur; linepatternShader->u_pattern_size = {{imagePos.size[0] * factor, imagePos.size[1]}}; linepatternShader->u_pattern_tl = imagePos.tl; linepatternShader->u_pattern_br = imagePos.br; linepatternShader->u_fade = fade; spriteAtlas.bind(true); MBGL_CHECK_ERROR(glDepthRange(strata + strata_epsilon, 1.0f)); // may or may not matter bucket.drawLinePatterns(*linepatternShader); } else { useProgram(lineShader->program); lineShader->u_matrix = vtxMatrix; lineShader->u_exmatrix = extrudeMatrix; lineShader->u_linewidth = {{ outset, inset }}; lineShader->u_ratio = ratio; lineShader->u_blur = blur; lineShader->u_color = color; lineShader->u_dasharray = {{ dash_length, dash_gap }}; bucket.drawLines(*lineShader); } }
void Painter::renderLine(LineBucket& bucket, const LineLayer& layer, const TileID& id, const mat4& matrix) { // Abort early. if (pass == RenderPass::Opaque) return; config.stencilOp.reset(); config.stencilTest = GL_TRUE; config.depthFunc.reset(); config.depthTest = GL_TRUE; config.depthMask = GL_FALSE; const auto& properties = layer.paint; const auto& layout = bucket.layout; // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. float antialiasing = 1.0 / data.pixelRatio; float blur = properties.blur + antialiasing; float edgeWidth = properties.width / 2.0; float inset = -1; float offset = 0; float shift = 0; if (properties.gapWidth != 0) { inset = properties.gapWidth / 2.0 + antialiasing * 0.5; edgeWidth = properties.width; // shift outer lines half a pixel towards the middle to eliminate the crack offset = inset - antialiasing / 2.0; } float outset = offset + edgeWidth + antialiasing / 2.0 + shift; Color color = properties.color; color[0] *= properties.opacity; color[1] *= properties.opacity; color[2] *= properties.opacity; color[3] *= properties.opacity; float ratio = state.getScale() / std::pow(2, id.z) / 8.0 * id.overscaling; mat2 antialiasingMatrix; matrix::identity(antialiasingMatrix); matrix::scale(antialiasingMatrix, antialiasingMatrix, 1.0, std::cos(state.getPitch())); matrix::rotate(antialiasingMatrix, antialiasingMatrix, state.getAngle()); // calculate how much longer the real world distance is at the top of the screen // than at the middle of the screen. float topedgelength = std::sqrt(std::pow(state.getHeight(), 2) / 4 * (1 + std::pow(state.getAltitude(), 2))); float x = state.getHeight() / 2.0f * std::tan(state.getPitch()); float extra = (topedgelength + x) / topedgelength - 1; mat4 vtxMatrix = translatedMatrix(matrix, properties.translate, id, properties.translateAnchor); setDepthSublayer(0); if (!properties.dasharray.value.from.empty()) { config.program = linesdfShader->program; linesdfShader->u_matrix = vtxMatrix; linesdfShader->u_exmatrix = extrudeMatrix; linesdfShader->u_linewidth = {{ outset, inset }}; linesdfShader->u_ratio = ratio; linesdfShader->u_blur = blur; linesdfShader->u_color = color; LinePatternPos posA = lineAtlas->getDashPosition(properties.dasharray.value.from, layout.cap == CapType::Round); LinePatternPos posB = lineAtlas->getDashPosition(properties.dasharray.value.to, layout.cap == CapType::Round); lineAtlas->bind(); float patternratio = std::pow(2.0, std::floor(::log2(state.getScale())) - id.z) / 8.0 * id.overscaling; float scaleXA = patternratio / posA.width / properties.dashLineWidth / properties.dasharray.value.fromScale; float scaleYA = -posA.height / 2.0; float scaleXB = patternratio / posB.width / properties.dashLineWidth / properties.dasharray.value.toScale; float scaleYB = -posB.height / 2.0; linesdfShader->u_patternscale_a = {{ scaleXA, scaleYA }}; linesdfShader->u_tex_y_a = posA.y; linesdfShader->u_patternscale_b = {{ scaleXB, scaleYB }}; linesdfShader->u_tex_y_b = posB.y; linesdfShader->u_image = 0; linesdfShader->u_sdfgamma = lineAtlas->width / (properties.dashLineWidth * std::min(posA.width, posB.width) * 256.0 * data.pixelRatio) / 2; linesdfShader->u_mix = properties.dasharray.value.t; linesdfShader->u_extra = extra; linesdfShader->u_antialiasingmatrix = antialiasingMatrix; bucket.drawLineSDF(*linesdfShader); } else if (!properties.pattern.value.from.empty()) { mapbox::util::optional<SpriteAtlasPosition> imagePosA = spriteAtlas->getPosition(properties.pattern.value.from, true); mapbox::util::optional<SpriteAtlasPosition> imagePosB = spriteAtlas->getPosition(properties.pattern.value.to, true); if (!imagePosA || !imagePosB) return; float factor = 8.0 / std::pow(2, state.getIntegerZoom() - id.z) * id.overscaling; config.program = linepatternShader->program; linepatternShader->u_matrix = vtxMatrix; linepatternShader->u_exmatrix = extrudeMatrix; linepatternShader->u_linewidth = {{ outset, inset }}; linepatternShader->u_ratio = ratio; linepatternShader->u_blur = blur; linepatternShader->u_pattern_size_a = {{(*imagePosA).size[0] * factor * properties.pattern.value.fromScale, (*imagePosA).size[1]}}; linepatternShader->u_pattern_tl_a = (*imagePosA).tl; linepatternShader->u_pattern_br_a = (*imagePosA).br; linepatternShader->u_pattern_size_b = {{(*imagePosB).size[0] * factor * properties.pattern.value.toScale, (*imagePosB).size[1]}}; linepatternShader->u_pattern_tl_b = (*imagePosB).tl; linepatternShader->u_pattern_br_b = (*imagePosB).br; linepatternShader->u_fade = properties.pattern.value.t; linepatternShader->u_opacity = properties.opacity; linepatternShader->u_extra = extra; linepatternShader->u_antialiasingmatrix = antialiasingMatrix; MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0)); spriteAtlas->bind(true); bucket.drawLinePatterns(*linepatternShader); } else { config.program = lineShader->program; lineShader->u_matrix = vtxMatrix; lineShader->u_exmatrix = extrudeMatrix; lineShader->u_linewidth = {{ outset, inset }}; lineShader->u_ratio = ratio; lineShader->u_blur = blur; lineShader->u_extra = extra; lineShader->u_antialiasingmatrix = antialiasingMatrix; lineShader->u_color = color; bucket.drawLines(*lineShader); } }
void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const TileID& id, const mat4 &matrix) { // Abort early. if (pass == RenderPass::Opaque) return; if (!bucket.hasData()) return; depthMask(false); const auto &properties = layer_desc.getProperties<LineProperties>(); const auto &layout = bucket.layout; // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. float antialiasing = 1 / state.getPixelRatio(); float blur = properties.blur + antialiasing; float edgeWidth = properties.width / 2.0; float inset = -1; float offset = 0; float shift = 0; if (properties.gap_width != 0) { inset = properties.gap_width / 2.0 + antialiasing * 0.5; edgeWidth = properties.width; // shift outer lines half a pixel towards the middle to eliminate the crack offset = inset - antialiasing / 2.0; } float outset = offset + edgeWidth + antialiasing / 2.0 + shift; Color color = properties.color; color[0] *= properties.opacity; color[1] *= properties.opacity; color[2] *= properties.opacity; color[3] *= properties.opacity; float ratio = state.getPixelRatio(); mat4 vtxMatrix = translatedMatrix(matrix, properties.translate, id, properties.translateAnchor); depthRange(strata, 1.0f); if (properties.dash_array.from.size()) { useProgram(linesdfShader->program); linesdfShader->u_matrix = vtxMatrix; linesdfShader->u_exmatrix = extrudeMatrix; linesdfShader->u_linewidth = {{ outset, inset }}; linesdfShader->u_ratio = ratio; linesdfShader->u_blur = blur; linesdfShader->u_color = color; LinePatternPos posA = lineAtlas.getDashPosition(properties.dash_array.from, layout.cap == CapType::Round); LinePatternPos posB = lineAtlas.getDashPosition(properties.dash_array.to, layout.cap == CapType::Round); lineAtlas.bind(); float patternratio = std::pow(2.0, std::floor(std::log2(state.getScale())) - id.z) / 8.0; float scaleXA = patternratio / posA.width / properties.dash_line_width / properties.dash_array.fromScale; float scaleYA = -posA.height / 2.0; float scaleXB = patternratio / posB.width / properties.dash_line_width / properties.dash_array.toScale; float scaleYB = -posB.height / 2.0; linesdfShader->u_patternscale_a = {{ scaleXA, scaleYA }}; linesdfShader->u_tex_y_a = posA.y; linesdfShader->u_patternscale_b = {{ scaleXB, scaleYB }}; linesdfShader->u_tex_y_b = posB.y; linesdfShader->u_image = 0; linesdfShader->u_sdfgamma = lineAtlas.width / (properties.dash_line_width * std::min(posA.width, posB.width) * 256.0 * state.getPixelRatio()) / 2; linesdfShader->u_mix = properties.dash_array.t; bucket.drawLineSDF(*linesdfShader); } else if (properties.image.from.size()) { SpriteAtlasPosition imagePosA = spriteAtlas.getPosition(properties.image.from, true); SpriteAtlasPosition imagePosB = spriteAtlas.getPosition(properties.image.to, true); float factor = 8.0 / std::pow(2, state.getIntegerZoom() - id.z); useProgram(linepatternShader->program); linepatternShader->u_matrix = vtxMatrix; linepatternShader->u_exmatrix = extrudeMatrix; linepatternShader->u_linewidth = {{ outset, inset }}; linepatternShader->u_ratio = ratio; linepatternShader->u_blur = blur; linepatternShader->u_pattern_size_a = {{imagePosA.size[0] * factor * properties.image.fromScale, imagePosA.size[1]}}; linepatternShader->u_pattern_tl_a = imagePosA.tl; linepatternShader->u_pattern_br_a = imagePosA.br; linepatternShader->u_pattern_size_b = {{imagePosB.size[0] * factor * properties.image.toScale, imagePosB.size[1]}}; linepatternShader->u_pattern_tl_b = imagePosB.tl; linepatternShader->u_pattern_br_b = imagePosB.br; linepatternShader->u_fade = properties.image.t; linepatternShader->u_opacity = properties.opacity; MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0)); spriteAtlas.bind(true); MBGL_CHECK_ERROR(glDepthRange(strata + strata_epsilon, 1.0f)); // may or may not matter bucket.drawLinePatterns(*linepatternShader); } else { useProgram(lineShader->program); lineShader->u_matrix = vtxMatrix; lineShader->u_exmatrix = extrudeMatrix; lineShader->u_linewidth = {{ outset, inset }}; lineShader->u_ratio = ratio; lineShader->u_blur = blur; lineShader->u_color = color; bucket.drawLines(*lineShader); } }
void Painter::renderLine(LineBucket& bucket, const LineLayer& layer, const UnwrappedTileID& tileID, const mat4& matrix) { // Abort early. if (pass == RenderPass::Opaque) return; config.stencilOp.reset(); config.stencilTest = GL_TRUE; config.depthFunc.reset(); config.depthTest = GL_TRUE; config.depthMask = GL_FALSE; const auto& properties = layer.impl->paint; const auto& layout = bucket.layout; // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. float antialiasing = 1.0 / frame.pixelRatio; bool wireframe = frame.debugOptions & MapDebugOptions::Wireframe; float blur = properties.lineBlur + antialiasing; Color color = Color::white(); float opacity = 1.0f; if (!wireframe) { color = properties.lineColor; opacity = properties.lineOpacity; } const float ratio = 1.0 / tileID.pixelsToTileUnits(1.0, state.getZoom()); mat2 antialiasingMatrix; matrix::identity(antialiasingMatrix); matrix::scale(antialiasingMatrix, antialiasingMatrix, 1.0, std::cos(state.getPitch())); matrix::rotate(antialiasingMatrix, antialiasingMatrix, state.getAngle()); // calculate how much longer the real world distance is at the top of the screen // than at the middle of the screen. float topedgelength = std::sqrt(std::pow(state.getHeight(), 2.0f) / 4.0f * (1.0f + std::pow(state.getAltitude(), 2.0f))); float x = state.getHeight() / 2.0f * std::tan(state.getPitch()); float extra = (topedgelength + x) / topedgelength - 1.0f; mat4 vtxMatrix = translatedMatrix(matrix, properties.lineTranslate, tileID, properties.lineTranslateAnchor); setDepthSublayer(0); if (!properties.lineDasharray.value.from.empty()) { config.program = linesdfShader->getID(); linesdfShader->u_matrix = vtxMatrix; linesdfShader->u_linewidth = properties.lineWidth / 2; linesdfShader->u_gapwidth = properties.lineGapWidth / 2; linesdfShader->u_antialiasing = antialiasing / 2; linesdfShader->u_ratio = ratio; linesdfShader->u_blur = blur; linesdfShader->u_color = color; linesdfShader->u_opacity = opacity; LinePatternPos posA = lineAtlas->getDashPosition(properties.lineDasharray.value.from, layout.lineCap == LineCapType::Round, store); LinePatternPos posB = lineAtlas->getDashPosition(properties.lineDasharray.value.to, layout.lineCap == LineCapType::Round, store); const float widthA = posA.width * properties.lineDasharray.value.fromScale * layer.impl->dashLineWidth; const float widthB = posB.width * properties.lineDasharray.value.toScale * layer.impl->dashLineWidth; float scaleXA = 1.0 / tileID.pixelsToTileUnits(widthA, state.getIntegerZoom()); float scaleYA = -posA.height / 2.0; float scaleXB = 1.0 / tileID.pixelsToTileUnits(widthB, state.getIntegerZoom()); float scaleYB = -posB.height / 2.0; linesdfShader->u_patternscale_a = {{ scaleXA, scaleYA }}; linesdfShader->u_tex_y_a = posA.y; linesdfShader->u_patternscale_b = {{ scaleXB, scaleYB }}; linesdfShader->u_tex_y_b = posB.y; linesdfShader->u_sdfgamma = lineAtlas->width / (std::min(widthA, widthB) * 256.0 * frame.pixelRatio) / 2; linesdfShader->u_mix = properties.lineDasharray.value.t; linesdfShader->u_extra = extra; linesdfShader->u_offset = -properties.lineOffset; linesdfShader->u_antialiasingmatrix = antialiasingMatrix; linesdfShader->u_image = 0; config.activeTexture = GL_TEXTURE0; lineAtlas->bind(store); bucket.drawLineSDF(*linesdfShader, store); } else if (!properties.linePattern.value.from.empty()) { optional<SpriteAtlasPosition> imagePosA = spriteAtlas->getPosition(properties.linePattern.value.from, true); optional<SpriteAtlasPosition> imagePosB = spriteAtlas->getPosition(properties.linePattern.value.to, true); if (!imagePosA || !imagePosB) return; config.program = linepatternShader->getID(); linepatternShader->u_matrix = vtxMatrix; linepatternShader->u_linewidth = properties.lineWidth / 2; linepatternShader->u_gapwidth = properties.lineGapWidth / 2; linepatternShader->u_antialiasing = antialiasing / 2; linepatternShader->u_ratio = ratio; linepatternShader->u_blur = blur; linepatternShader->u_pattern_size_a = {{ tileID.pixelsToTileUnits((*imagePosA).size[0] * properties.linePattern.value.fromScale, state.getIntegerZoom()), (*imagePosA).size[1] }}; linepatternShader->u_pattern_tl_a = (*imagePosA).tl; linepatternShader->u_pattern_br_a = (*imagePosA).br; linepatternShader->u_pattern_size_b = {{ tileID.pixelsToTileUnits((*imagePosB).size[0] * properties.linePattern.value.toScale, state.getIntegerZoom()), (*imagePosB).size[1] }}; linepatternShader->u_pattern_tl_b = (*imagePosB).tl; linepatternShader->u_pattern_br_b = (*imagePosB).br; linepatternShader->u_fade = properties.linePattern.value.t; linepatternShader->u_opacity = properties.lineOpacity; linepatternShader->u_extra = extra; linepatternShader->u_offset = -properties.lineOffset; linepatternShader->u_antialiasingmatrix = antialiasingMatrix; linepatternShader->u_image = 0; config.activeTexture = GL_TEXTURE0; spriteAtlas->bind(true, store); bucket.drawLinePatterns(*linepatternShader, store); } else { config.program = lineShader->getID(); lineShader->u_matrix = vtxMatrix; lineShader->u_linewidth = properties.lineWidth / 2; lineShader->u_gapwidth = properties.lineGapWidth / 2; lineShader->u_antialiasing = antialiasing / 2; lineShader->u_ratio = ratio; lineShader->u_blur = blur; lineShader->u_extra = extra; lineShader->u_offset = -properties.lineOffset; lineShader->u_antialiasingmatrix = antialiasingMatrix; lineShader->u_color = color; lineShader->u_opacity = opacity; bucket.drawLines(*lineShader, store); } }