diff options
Diffstat (limited to 'src/mbgl')
181 files changed, 6528 insertions, 1850 deletions
diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp index f8c1c3adf7..ed1518fb7b 100644 --- a/src/mbgl/annotation/annotation_manager.cpp +++ b/src/mbgl/annotation/annotation_manager.cpp @@ -90,7 +90,7 @@ void AnnotationManager::add(const AnnotationID& id, const StyleSourcedAnnotation Update AnnotationManager::update(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t maxZoom) { Update result = Update::Nothing; - + auto it = symbolAnnotations.find(id); if (it == symbolAnnotations.end()) { assert(false); // Attempt to update a non-existent symbol annotation @@ -222,12 +222,10 @@ void AnnotationManager::removeTile(AnnotationTile& tile) { void AnnotationManager::addIcon(const std::string& name, std::shared_ptr<const SpriteImage> sprite) { spriteAtlas.setSprite(name, sprite); - spriteAtlas.updateDirty(); } void AnnotationManager::removeIcon(const std::string& name) { spriteAtlas.removeSprite(name); - spriteAtlas.updateDirty(); } double AnnotationManager::getTopOffsetPixelsForIcon(const std::string& name) { diff --git a/src/mbgl/gl/attribute.cpp b/src/mbgl/gl/attribute.cpp index 7432fff590..2c16dac3fc 100644 --- a/src/mbgl/gl/attribute.cpp +++ b/src/mbgl/gl/attribute.cpp @@ -1,29 +1,238 @@ #include <mbgl/gl/attribute.hpp> +#include <mbgl/gl/context.hpp> #include <mbgl/gl/gl.hpp> +#include <mbgl/gl/normalization.hpp> namespace mbgl { namespace gl { +static_assert(offsetof(Normalized<uint8_t>, value) == 0, "unexpected normalized offset"); + AttributeLocation bindAttributeLocation(ProgramID id, AttributeLocation location, const char* name) { MBGL_CHECK_ERROR(glBindAttribLocation(id, location, name)); return location; } -void bindAttribute(AttributeLocation location, - std::size_t count, - DataType type, - std::size_t vertexSize, - std::size_t vertexOffset, - std::size_t attributeOffset) { +template <class T> DataType DataTypeOf = static_cast<DataType>(0); +template <> DataType DataTypeOf< int8_t> = DataType::Byte; +template <> DataType DataTypeOf<uint8_t> = DataType::UnsignedByte; +template <> DataType DataTypeOf< int16_t> = DataType::Short; +template <> DataType DataTypeOf<uint16_t> = DataType::UnsignedShort; +template <> DataType DataTypeOf< int32_t> = DataType::Integer; +template <> DataType DataTypeOf<uint32_t> = DataType::UnsignedInteger; +template <> DataType DataTypeOf<float> = DataType::Float; + +template <class T> bool IsNormalized = false; +template <class T> bool IsNormalized<Normalized<T>> = true; +template <class T> DataType DataTypeOf<Normalized<T>> = DataTypeOf<T>; + +template <class T, std::size_t N> +void VariableAttributeBinding<T, N>::bind(Context& context, + AttributeLocation location, + optional<VariableAttributeBinding<T, N>>& oldBinding, + std::size_t vertexOffset) const { + if (oldBinding == *this) { + return; + } + context.vertexBuffer = vertexBuffer; MBGL_CHECK_ERROR(glEnableVertexAttribArray(location)); MBGL_CHECK_ERROR(glVertexAttribPointer( location, - static_cast<GLint>(count), - static_cast<GLenum>(type), - GL_FALSE, + static_cast<GLint>(N), + static_cast<GLenum>(DataTypeOf<T>), + static_cast<GLboolean>(IsNormalized<T>), static_cast<GLsizei>(vertexSize), reinterpret_cast<GLvoid*>(attributeOffset + (vertexSize * vertexOffset)))); } +template class VariableAttributeBinding<uint8_t, 1>; +template class VariableAttributeBinding<uint8_t, 2>; +template class VariableAttributeBinding<uint8_t, 3>; +template class VariableAttributeBinding<uint8_t, 4>; + +template class VariableAttributeBinding<Normalized<uint8_t>, 1>; +template class VariableAttributeBinding<Normalized<uint8_t>, 2>; +template class VariableAttributeBinding<Normalized<uint8_t>, 3>; +template class VariableAttributeBinding<Normalized<uint8_t>, 4>; + +template class VariableAttributeBinding<uint16_t, 1>; +template class VariableAttributeBinding<uint16_t, 2>; +template class VariableAttributeBinding<uint16_t, 3>; +template class VariableAttributeBinding<uint16_t, 4>; + +template class VariableAttributeBinding<int16_t, 1>; +template class VariableAttributeBinding<int16_t, 2>; +template class VariableAttributeBinding<int16_t, 3>; +template class VariableAttributeBinding<int16_t, 4>; + +template class VariableAttributeBinding<float, 1>; +template class VariableAttributeBinding<float, 2>; +template class VariableAttributeBinding<float, 3>; +template class VariableAttributeBinding<float, 4>; + +template <> +void ConstantAttributeBinding<uint8_t, 1>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<uint8_t, 1>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib1f(location, value[0])); +} + +template <> +void ConstantAttributeBinding<uint8_t, 2>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<uint8_t, 2>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib2f(location, value[0], value[1])); +} + +template <> +void ConstantAttributeBinding<uint8_t, 3>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<uint8_t, 3>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib3f(location, value[0], value[1], value[2])); +} + +template <> +void ConstantAttributeBinding<uint8_t, 4>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<uint8_t, 4>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib4f(location, value[0], value[1], value[2], value[3])); +} + + +template <> +void ConstantAttributeBinding<Normalized<uint8_t>, 1>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<Normalized<uint8_t>, 1>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib1f(location, value[0].denormalized())); +} + +template <> +void ConstantAttributeBinding<Normalized<uint8_t>, 2>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<Normalized<uint8_t>, 2>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib2f(location, value[0].denormalized(), value[1].denormalized())); +} + +template <> +void ConstantAttributeBinding<Normalized<uint8_t>, 3>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<Normalized<uint8_t>, 3>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib3f(location, value[0].denormalized(), value[1].denormalized(), value[2].denormalized())); +} + +template <> +void ConstantAttributeBinding<Normalized<uint8_t>, 4>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<Normalized<uint8_t>, 4>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib4f(location, value[0].denormalized(), value[1].denormalized(), value[2].denormalized(), value[3].denormalized())); +} + + +template <> +void ConstantAttributeBinding<uint16_t, 1>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<uint16_t, 1>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib1f(location, value[0])); +} + +template <> +void ConstantAttributeBinding<uint16_t, 2>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<uint16_t, 2>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib2f(location, value[0], value[1])); +} + +template <> +void ConstantAttributeBinding<uint16_t, 3>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<uint16_t, 3>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib3f(location, value[0], value[1], value[2])); +} + +template <> +void ConstantAttributeBinding<uint16_t, 4>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<uint16_t, 4>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib4f(location, value[0], value[1], value[2], value[3])); +} + + +template <> +void ConstantAttributeBinding<int16_t, 1>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<int16_t, 1>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib1f(location, value[0])); +} + +template <> +void ConstantAttributeBinding<int16_t, 2>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<int16_t, 2>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib2f(location, value[0], value[1])); +} + +template <> +void ConstantAttributeBinding<int16_t, 3>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<int16_t, 3>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib3f(location, value[0], value[1], value[2])); +} + +template <> +void ConstantAttributeBinding<int16_t, 4>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<int16_t, 4>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib4f(location, value[0], value[1], value[2], value[3])); +} + + +template <> +void ConstantAttributeBinding<float, 1>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<float, 1>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib1f(location, value[0])); +} + +template <> +void ConstantAttributeBinding<float, 2>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<float, 2>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib2f(location, value[0], value[1])); +} + +template <> +void ConstantAttributeBinding<float, 3>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<float, 3>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib3f(location, value[0], value[1], value[2])); +} + +template <> +void ConstantAttributeBinding<float, 4>::bind(Context&, AttributeLocation location, optional<VariableAttributeBinding<float, 4>>& oldBinding, std::size_t) const { + assert(location != 0); + oldBinding = {}; + MBGL_CHECK_ERROR(glDisableVertexAttribArray(location)); + MBGL_CHECK_ERROR(glVertexAttrib4f(location, value[0], value[1], value[2], value[3])); +} + } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/attribute.hpp b/src/mbgl/gl/attribute.hpp index e45014127b..6300ebb56b 100644 --- a/src/mbgl/gl/attribute.hpp +++ b/src/mbgl/gl/attribute.hpp @@ -1,8 +1,10 @@ #pragma once #include <mbgl/gl/types.hpp> +#include <mbgl/gl/segment.hpp> #include <mbgl/util/ignore.hpp> #include <mbgl/util/indexed_tuple.hpp> +#include <mbgl/util/variant.hpp> #include <cstddef> #include <functional> @@ -10,24 +12,79 @@ namespace mbgl { namespace gl { -template <class Tag, class T, std::size_t N> +template <class T, std::size_t N> +class VariableAttributeBinding { +public: + VariableAttributeBinding(BufferID vertexBuffer_, + std::size_t vertexSize_, + std::size_t attributeOffset_) + : vertexBuffer(vertexBuffer_), + vertexSize(vertexSize_), + attributeOffset(attributeOffset_) + {} + + void bind(Context&, AttributeLocation, optional<VariableAttributeBinding<T, N>>&, std::size_t vertexOffset) const; + + friend bool operator==(const VariableAttributeBinding& lhs, + const VariableAttributeBinding& rhs) { + return lhs.vertexBuffer == rhs.vertexBuffer + && lhs.vertexSize == rhs.vertexSize + && lhs.attributeOffset == rhs.attributeOffset; + } + +private: + BufferID vertexBuffer; + std::size_t vertexSize; + std::size_t attributeOffset; +}; + +template <class T, std::size_t N> +class ConstantAttributeBinding { +public: + ConstantAttributeBinding() { value.fill(T()); } + + explicit ConstantAttributeBinding(std::array<T, N> value_) + : value(std::move(value_)) + {} + + void bind(Context&, AttributeLocation, optional<VariableAttributeBinding<T, N>>&, std::size_t) const; + + friend bool operator==(const ConstantAttributeBinding& lhs, + const ConstantAttributeBinding& rhs) { + return lhs.value == rhs.value; + } + +private: + std::array<T, N> value; +}; + +template <class T, std::size_t N> class Attribute { public: - using Type = T[N]; + using Value = std::array<T, N>; + + using VariableBinding = VariableAttributeBinding<T, N>; + using ConstantBinding = ConstantAttributeBinding<T, N>; - class State { - public: - explicit State(AttributeLocation location_) - : location(location_) {} + using Location = AttributeLocation; - AttributeLocation location; - static constexpr std::size_t count = N; - static constexpr DataType type = DataTypeOf<T>::value; - }; + using Binding = variant< + ConstantBinding, + VariableBinding>; + + static void bind(Context& context, + const Location& location, + optional<VariableBinding>& oldBinding, + const Binding& newBinding, + std::size_t vertexOffset) { + Binding::visit(newBinding, [&] (const auto& binding) { + binding.bind(context, location, oldBinding, vertexOffset); + }); + } }; #define MBGL_DEFINE_ATTRIBUTE(type_, n_, name_) \ - struct name_ : ::mbgl::gl::Attribute<name_, type_, n_> { static constexpr auto name = #name_; } + struct name_ : ::mbgl::gl::Attribute<type_, n_> { static auto name() { return #name_; } } namespace detail { @@ -41,10 +98,16 @@ namespace detail { template <class... As> class Vertex; +template <> +class Vertex<> { +public: + using VertexType = Vertex<>; +}; + template <class A1> class Vertex<A1> { public: - typename A1::Type a1; + typename A1::Value a1; using VertexType = Vertex<A1>; static const std::size_t attributeOffsets[1]; @@ -53,8 +116,8 @@ public: template <class A1, class A2> class Vertex<A1, A2> { public: - typename A1::Type a1; - typename A2::Type a2; + typename A1::Value a1; + typename A2::Value a2; using VertexType = Vertex<A1, A2>; static const std::size_t attributeOffsets[2]; @@ -63,9 +126,9 @@ public: template <class A1, class A2, class A3> class Vertex<A1, A2, A3> { public: - typename A1::Type a1; - typename A2::Type a2; - typename A3::Type a3; + typename A1::Value a1; + typename A2::Value a2; + typename A3::Value a3; using VertexType = Vertex<A1, A2, A3>; static const std::size_t attributeOffsets[3]; @@ -74,10 +137,10 @@ public: template <class A1, class A2, class A3, class A4> class Vertex<A1, A2, A3, A4> { public: - typename A1::Type a1; - typename A2::Type a2; - typename A3::Type a3; - typename A4::Type a4; + typename A1::Value a1; + typename A2::Value a2; + typename A3::Value a3; + typename A4::Value a4; using VertexType = Vertex<A1, A2, A3, A4>; static const std::size_t attributeOffsets[4]; @@ -86,11 +149,11 @@ public: template <class A1, class A2, class A3, class A4, class A5> class Vertex<A1, A2, A3, A4, A5> { public: - typename A1::Type a1; - typename A2::Type a2; - typename A3::Type a3; - typename A4::Type a4; - typename A5::Type a5; + typename A1::Value a1; + typename A2::Value a2; + typename A3::Value a3; + typename A4::Value a4; + typename A5::Value a5; using VertexType = Vertex<A1, A2, A3, A4, A5>; static const std::size_t attributeOffsets[5]; @@ -135,37 +198,71 @@ const std::size_t Vertex<A1, A2, A3, A4, A5>::attributeOffsets[5] = { AttributeLocation bindAttributeLocation(ProgramID, AttributeLocation, const char * name); -void bindAttribute(AttributeLocation location, - std::size_t count, - DataType type, - std::size_t vertexSize, - std::size_t vertexOffset, - std::size_t attributeOffset); - template <class... As> class Attributes { public: - using State = IndexedTuple<TypeList<As...>, TypeList<typename As::State...>>; + using Types = TypeList<As...>; + using Locations = IndexedTuple< + TypeList<As...>, + TypeList<typename As::Location...>>; + using Bindings = IndexedTuple< + TypeList<As...>, + TypeList<typename As::Binding...>>; + using VariableBindings = IndexedTuple< + TypeList<As...>, + TypeList<optional<typename As::VariableBinding>...>>; + using Vertex = detail::Vertex<As...>; template <class A> static constexpr std::size_t Index = TypeIndex<A, As...>::value; - static State state(const ProgramID& id) { - return State { typename As::State(bindAttributeLocation(id, Index<As>, As::name))... }; + static Locations locations(const ProgramID& id) { + return Locations { bindAttributeLocation(id, Index<As>, As::name())... }; } - static std::function<void (std::size_t)> binder(const State& state) { - return [&state] (std::size_t vertexOffset) { - util::ignore({ (bindAttribute(state.template get<As>().location, - state.template get<As>().count, - state.template get<As>().type, - sizeof(Vertex), - vertexOffset, - Vertex::attributeOffsets[Index<As>]), 0)... }); + template <class DrawMode> + static Bindings allVariableBindings(const VertexBuffer<Vertex, DrawMode>& buffer) { + static_assert(std::is_standard_layout<Vertex>::value, "vertex type must use standard layout"); + + return Bindings { + typename As::VariableBinding { + buffer.buffer, + sizeof(Vertex), + Vertex::attributeOffsets[Index<As>] + }... }; } + + static void bind(Context& context, + const Locations& locations, + VariableBindings& oldBindings, + const Bindings& newBindings, + std::size_t vertexOffset) { + util::ignore({ (As::bind(context, + locations.template get<As>(), + oldBindings.template get<As>(), + newBindings.template get<As>(), + vertexOffset), 0)... }); + } }; +namespace detail { + +template <class...> +struct ConcatenateAttributes; + +template <class... As, class... Bs> +struct ConcatenateAttributes<TypeList<As...>, TypeList<Bs...>> { + using Type = Attributes<As..., Bs...>; +}; + +} // namespace detail + +template <class A, class B> +using ConcatenateAttributes = typename detail::ConcatenateAttributes< + typename A::Types, + typename B::Types>::Type; + } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index 5048ffcd66..9c2031ccfd 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -122,6 +122,20 @@ UniqueTexture Context::createTexture() { return UniqueTexture{ std::move(id), { this } }; } +bool Context::supportsVertexArrays() const { + return gl::GenVertexArrays && + gl::BindVertexArray && + gl::DeleteVertexArrays && + !disableVAOExtension; +} + +UniqueVertexArray Context::createVertexArray() { + assert(supportsVertexArrays()); + VertexArrayID id = 0; + MBGL_CHECK_ERROR(gl::GenVertexArrays(1, &id)); + return UniqueVertexArray(std::move(id), { this }); +} + UniqueFramebuffer Context::createFramebuffer() { FramebufferID id = 0; MBGL_CHECK_ERROR(glGenFramebuffers(1, &id)); @@ -407,32 +421,26 @@ void Context::clear(optional<mbgl::Color> color, } #if not MBGL_USE_GLES2 -PrimitiveType Context::operator()(const Points& points) { +void Context::setDrawMode(const Points& points) { pointSize = points.pointSize; - return PrimitiveType::Points; } #else -PrimitiveType Context::operator()(const Points&) { - return PrimitiveType::Points; +void Context::setDrawMode(const Points&) { } #endif // MBGL_USE_GLES2 -PrimitiveType Context::operator()(const Lines& lines) { +void Context::setDrawMode(const Lines& lines) { lineWidth = lines.lineWidth; - return PrimitiveType::Lines; } -PrimitiveType Context::operator()(const LineStrip& lineStrip) { +void Context::setDrawMode(const LineStrip& lineStrip) { lineWidth = lineStrip.lineWidth; - return PrimitiveType::LineStrip; } -PrimitiveType Context::operator()(const Triangles&) { - return PrimitiveType::Triangles; +void Context::setDrawMode(const Triangles&) { } -PrimitiveType Context::operator()(const TriangleStrip&) { - return PrimitiveType::TriangleStrip; +void Context::setDrawMode(const TriangleStrip&) { } void Context::setDepthMode(const DepthMode& depth) { @@ -474,57 +482,14 @@ void Context::setColorMode(const ColorMode& color) { colorMask = color.mask; } -void Context::draw(const Drawable& drawable) { - if (drawable.segments.empty()) { - return; - } - - PrimitiveType primitiveType = apply_visitor([&] (auto m) { return (*this)(m); }, drawable.drawMode); - - setDepthMode(drawable.depthMode); - setStencilMode(drawable.stencilMode); - setColorMode(drawable.colorMode); - - program = drawable.program; - - drawable.bindUniforms(); - - for (const auto& segment : drawable.segments) { - auto needAttributeBindings = [&] () { - if (!gl::GenVertexArrays || !gl::BindVertexArray) { - return true; - } - - if (segment.vao) { - vertexArrayObject = *segment.vao; - return false; - } - - VertexArrayID id = 0; - MBGL_CHECK_ERROR(gl::GenVertexArrays(1, &id)); - vertexArrayObject = id; - segment.vao = UniqueVertexArray(std::move(id), { this }); - - // If we are initializing a new VAO, we need to force the buffers - // to be rebound. VAOs don't inherit the existing buffer bindings. - vertexBuffer.setDirty(); - elementBuffer.setDirty(); - - return true; - }; - - if (needAttributeBindings()) { - vertexBuffer = drawable.vertexBuffer; - elementBuffer = drawable.indexBuffer; - drawable.bindAttributes(segment.vertexOffset); - } - - MBGL_CHECK_ERROR(glDrawElements( - static_cast<GLenum>(primitiveType), - static_cast<GLsizei>(segment.indexLength), - GL_UNSIGNED_SHORT, - reinterpret_cast<GLvoid*>(sizeof(uint16_t) * segment.indexOffset))); - } +void Context::draw(PrimitiveType primitiveType, + std::size_t indexOffset, + std::size_t indexLength) { + MBGL_CHECK_ERROR(glDrawElements( + static_cast<GLenum>(primitiveType), + static_cast<GLsizei>(indexLength), + GL_UNSIGNED_SHORT, + reinterpret_cast<GLvoid*>(sizeof(uint16_t) * indexOffset))); } void Context::performCleanup() { @@ -564,6 +529,7 @@ void Context::performCleanup() { } if (!abandonedVertexArrays.empty()) { + assert(supportsVertexArrays()); for (const auto id : abandonedVertexArrays) { if (vertexArrayObject == id) { vertexArrayObject.setDirty(); diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index 093afa20ed..9d3ecec662 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -13,7 +13,6 @@ #include <mbgl/gl/depth_mode.hpp> #include <mbgl/gl/stencil_mode.hpp> #include <mbgl/gl/color_mode.hpp> -#include <mbgl/gl/segment.hpp> #include <mbgl/util/noncopyable.hpp> @@ -41,6 +40,9 @@ public: void linkProgram(ProgramID); UniqueTexture createTexture(); + bool supportsVertexArrays() const; + UniqueVertexArray createVertexArray(); + template <class Vertex, class DrawMode> VertexBuffer<Vertex, DrawMode> createVertexBuffer(VertexVector<Vertex, DrawMode>&& v) { return VertexBuffer<Vertex, DrawMode> { @@ -119,25 +121,20 @@ public: optional<float> depth, optional<int32_t> stencil); - struct Drawable { - DrawMode drawMode; - DepthMode depthMode; - StencilMode stencilMode; - ColorMode colorMode; - gl::ProgramID program; - gl::BufferID vertexBuffer; - gl::BufferID indexBuffer; - const std::vector<Segment>& segments; - std::function<void ()> bindUniforms; - std::function<void (std::size_t)> bindAttributes; - }; - - void draw(const Drawable&); + void setDrawMode(const Points&); + void setDrawMode(const Lines&); + void setDrawMode(const LineStrip&); + void setDrawMode(const Triangles&); + void setDrawMode(const TriangleStrip&); void setDepthMode(const DepthMode&); void setStencilMode(const StencilMode&); void setColorMode(const ColorMode&); + void draw(PrimitiveType, + std::size_t indexOffset, + std::size_t indexLength); + // Actually remove the objects we marked as abandoned with the above methods. // Only call this while the OpenGL context is exclusive to this thread. void performCleanup(); @@ -164,6 +161,8 @@ public: std::array<State<value::BindTexture>, 2> texture; State<value::BindVertexArray> vertexArrayObject; State<value::Program> program; + State<value::BindVertexBuffer> vertexBuffer; + State<value::BindElementBuffer> elementBuffer; #if not MBGL_USE_GLES2 State<value::PixelZoom> pixelZoom; @@ -196,8 +195,6 @@ private: #if not MBGL_USE_GLES2 State<value::PointSize> pointSize; #endif // MBGL_USE_GLES2 - State<value::BindVertexBuffer> vertexBuffer; - State<value::BindElementBuffer> elementBuffer; UniqueBuffer createVertexBuffer(const void* data, std::size_t size); UniqueBuffer createIndexBuffer(const void* data, std::size_t size); @@ -210,12 +207,6 @@ private: void drawPixels(Size size, const void* data, TextureFormat); #endif // MBGL_USE_GLES2 - PrimitiveType operator()(const Points&); - PrimitiveType operator()(const Lines&); - PrimitiveType operator()(const LineStrip&); - PrimitiveType operator()(const Triangles&); - PrimitiveType operator()(const TriangleStrip&); - friend detail::ProgramDeleter; friend detail::ShaderDeleter; friend detail::BufferDeleter; @@ -233,6 +224,10 @@ private: std::vector<VertexArrayID> abandonedVertexArrays; std::vector<FramebufferID> abandonedFramebuffers; std::vector<RenderbufferID> abandonedRenderbuffers; + +public: + // For testing + bool disableVAOExtension = false; }; } // namespace gl diff --git a/src/mbgl/gl/debugging.cpp b/src/mbgl/gl/debugging.cpp index f99308de7e..5ce3e606ee 100644 --- a/src/mbgl/gl/debugging.cpp +++ b/src/mbgl/gl/debugging.cpp @@ -132,7 +132,7 @@ void enable() { } // This will enable all messages including performance hints - //MBGL_CHECK_ERROR(DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE)); + // MBGL_CHECK_ERROR(DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE)); // This will only enable high and medium severity messages MBGL_CHECK_ERROR(DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, nullptr, GL_TRUE)); diff --git a/src/mbgl/gl/draw_mode.hpp b/src/mbgl/gl/draw_mode.hpp index ab86d5e469..275eb25b89 100644 --- a/src/mbgl/gl/draw_mode.hpp +++ b/src/mbgl/gl/draw_mode.hpp @@ -1,7 +1,7 @@ #pragma once +#include <mbgl/gl/types.hpp> #include <mbgl/gl/primitives.hpp> -#include <mbgl/util/variant.hpp> #include <cassert> @@ -11,7 +11,9 @@ namespace gl { class Points { public: using Primitive = Point; + static constexpr std::size_t bufferGroupSize = 1; + static constexpr PrimitiveType primitiveType = PrimitiveType::Points; explicit Points(float pointSize_) : pointSize(pointSize_) {} @@ -21,7 +23,9 @@ public: class Lines { public: using Primitive = Line; + static constexpr std::size_t bufferGroupSize = 2; + static constexpr PrimitiveType primitiveType = PrimitiveType::Lines; explicit Lines(float lineWidth_) : lineWidth(lineWidth_) { assert(lineWidth > 0); @@ -35,7 +39,9 @@ public: // LineStrip is a form of "Line" rendering, but the element buffer // cannot be grouped into logical elements beyond a single Point. using Primitive = Line; + static constexpr std::size_t bufferGroupSize = 1; + static constexpr PrimitiveType primitiveType = PrimitiveType::LineStrip; explicit LineStrip(float lineWidth_) : lineWidth(lineWidth_) { assert(lineWidth > 0); @@ -47,7 +53,9 @@ public: class Triangles { public: using Primitive = Triangle; + static constexpr std::size_t bufferGroupSize = 3; + static constexpr PrimitiveType primitiveType = PrimitiveType::Triangles; }; class TriangleStrip { @@ -55,7 +63,9 @@ public: // TriangleStrip is a form of "Triangle" rendering, but the element buffer // cannot be grouped into logical elements beyond a single Point. using Primitive = Triangle; + static constexpr std::size_t bufferGroupSize = 1; + static constexpr PrimitiveType primitiveType = PrimitiveType::TriangleStrip; }; // Special draw mode for use with VertexVector<Indexed, Vertex>, in which @@ -65,12 +75,5 @@ public: static constexpr std::size_t bufferGroupSize = 1; }; -using DrawMode = variant< - Points, - Lines, - LineStrip, - Triangles, - TriangleStrip>; - } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/normalization.hpp b/src/mbgl/gl/normalization.hpp new file mode 100644 index 0000000000..83fa67a3ec --- /dev/null +++ b/src/mbgl/gl/normalization.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include <mbgl/math/clamp.hpp> + +#include <cassert> +#include <limits> + +namespace mbgl { +namespace gl { + +template <class T> +class Normalized { +public: + T value; + + Normalized() : value(0) {} + + explicit Normalized(float f) + : value(static_cast<T>(std::numeric_limits<T>::max() * util::clamp(f, 0.0f, 1.0f))) { + assert(f >= 0.0f); + assert(f <= 1.0f); + } + + float denormalized() const { + return float(value) / std::numeric_limits<T>::max(); + } +}; + +template <class T> +bool operator==(const Normalized<T>& lhs, const Normalized<T>& rhs) { + return lhs.value == rhs.value; +} + +} // namespace gl +} // namespace mbgl diff --git a/src/mbgl/gl/program.hpp b/src/mbgl/gl/program.hpp index 33387e9d4e..fa0470796e 100644 --- a/src/mbgl/gl/program.hpp +++ b/src/mbgl/gl/program.hpp @@ -20,16 +20,14 @@ public: using Attributes = As; using Uniforms = Us; - using Vertex = typename Attributes::Vertex; using UniformValues = typename Uniforms::Values; - - static_assert(std::is_standard_layout<Vertex>::value, "vertex type must use standard layout"); + using AttributeBindings = typename Attributes::Bindings; Program(Context& context, const std::string& vertexSource, const std::string& fragmentSource) : vertexShader(context.createShader(ShaderType::Vertex, vertexSource)), fragmentShader(context.createShader(ShaderType::Fragment, fragmentSource)), program(context.createProgram(vertexShader, fragmentShader)), - attributesState(Attributes::state(program)), + attributeLocations(Attributes::locations(program)), uniformsState((context.linkProgram(program), Uniforms::state(program))) {} template <class DrawMode> @@ -39,22 +37,30 @@ public: StencilMode stencilMode, ColorMode colorMode, UniformValues&& uniformValues, - const VertexBuffer<Vertex>& vertexBuffer, + AttributeBindings&& attributeBindings, const IndexBuffer<DrawMode>& indexBuffer, const SegmentVector<Attributes>& segments) { static_assert(std::is_same<Primitive, typename DrawMode::Primitive>::value, "incompatible draw mode"); - context.draw({ - std::move(drawMode), - std::move(depthMode), - std::move(stencilMode), - std::move(colorMode), - program, - vertexBuffer.buffer, - indexBuffer.buffer, - segments, - Uniforms::binder(uniformsState, std::move(uniformValues)), - Attributes::binder(attributesState) - }); + + context.setDrawMode(drawMode); + context.setDepthMode(depthMode); + context.setStencilMode(stencilMode); + context.setColorMode(colorMode); + + context.program = program; + + Uniforms::bind(uniformsState, std::move(uniformValues)); + + for (const auto& segment : segments) { + segment.bind(context, + indexBuffer.buffer, + attributeLocations, + attributeBindings); + + context.draw(drawMode.primitiveType, + segment.indexOffset, + segment.indexLength); + } } private: @@ -62,7 +68,7 @@ private: UniqueShader fragmentShader; UniqueProgram program; - typename Attributes::State attributesState; + typename Attributes::Locations attributeLocations; typename Uniforms::State uniformsState; }; diff --git a/src/mbgl/gl/segment.cpp b/src/mbgl/gl/segment.cpp new file mode 100644 index 0000000000..aabdc83cd4 --- /dev/null +++ b/src/mbgl/gl/segment.cpp @@ -0,0 +1,7 @@ +#include <mbgl/gl/segment.hpp> + +namespace mbgl { +namespace gl { + +} // namespace gl +} // namespace mbgl diff --git a/src/mbgl/gl/segment.hpp b/src/mbgl/gl/segment.hpp index 8f74afd237..bd4926476f 100644 --- a/src/mbgl/gl/segment.hpp +++ b/src/mbgl/gl/segment.hpp @@ -1,12 +1,18 @@ #pragma once +#include <mbgl/gl/context.hpp> +#include <mbgl/gl/vertex_buffer.hpp> #include <mbgl/util/optional.hpp> +#include <mbgl/util/logging.hpp> +#include <mutex> #include <cstddef> +#include <vector> namespace mbgl { namespace gl { +template <class Attributes> class Segment { public: Segment(std::size_t vertexOffset_, @@ -24,13 +30,46 @@ public: std::size_t vertexLength; std::size_t indexLength; + void bind(Context& context, + BufferID indexBuffer_, + const typename Attributes::Locations& attributeLocations, + const typename Attributes::Bindings& attributeBindings_) const { + if (context.supportsVertexArrays()) { + if (!vao) { + vao = context.createVertexArray(); + context.vertexBuffer.setDirty(); + } + context.vertexArrayObject = *vao; + if (indexBuffer != indexBuffer_) { + indexBuffer = indexBuffer_; + context.elementBuffer.setDirty(); + context.elementBuffer = indexBuffer_; + } + } else { + // No VAO support. Force attributes to be rebound. + static std::once_flag reportedOnce; + std::call_once(reportedOnce, [] { + Log::Warning(Event::OpenGL, "Not using Vertex Array Objects"); + }); + context.elementBuffer = indexBuffer_; + variableBindings = {}; + } + + Attributes::bind(context, + attributeLocations, + variableBindings, + attributeBindings_, + vertexOffset); + } + private: - friend class Context; mutable optional<UniqueVertexArray> vao; + mutable optional<BufferID> indexBuffer; + mutable typename Attributes::VariableBindings variableBindings; }; template <class Attributes> -class SegmentVector : public std::vector<Segment> { +class SegmentVector : public std::vector<Segment<Attributes>> { public: SegmentVector() = default; }; diff --git a/src/mbgl/gl/types.hpp b/src/mbgl/gl/types.hpp index 577629d5d3..565ca5754f 100644 --- a/src/mbgl/gl/types.hpp +++ b/src/mbgl/gl/types.hpp @@ -34,16 +34,6 @@ enum class DataType : uint32_t { Float = 0x1406 }; -template <typename T> struct DataTypeOf; - -template <> struct DataTypeOf<int8_t> : std::integral_constant<DataType, DataType::Byte> {}; -template <> struct DataTypeOf<uint8_t> : std::integral_constant<DataType, DataType::UnsignedByte> {}; -template <> struct DataTypeOf<int16_t> : std::integral_constant<DataType, DataType::Short> {}; -template <> struct DataTypeOf<uint16_t> : std::integral_constant<DataType, DataType::UnsignedShort> {}; -template <> struct DataTypeOf<int32_t> : std::integral_constant<DataType, DataType::Integer> {}; -template <> struct DataTypeOf<uint32_t> : std::integral_constant<DataType, DataType::UnsignedInteger> {}; -template <> struct DataTypeOf<float> : std::integral_constant<DataType, DataType::Float> {}; - enum class RenderbufferType : uint32_t { RGBA = 0x8058, DepthStencil = 0x88F0, diff --git a/src/mbgl/gl/uniform.hpp b/src/mbgl/gl/uniform.hpp index 726cd4fe10..92136b61c2 100644 --- a/src/mbgl/gl/uniform.hpp +++ b/src/mbgl/gl/uniform.hpp @@ -50,32 +50,49 @@ template <class Tag, class T, size_t N> using UniformMatrix = Uniform<Tag, std::array<T, N*N>>; #define MBGL_DEFINE_UNIFORM_SCALAR(type_, name_) \ - struct name_ : ::mbgl::gl::UniformScalar<name_, type_> { static constexpr auto name = #name_; } + struct name_ : ::mbgl::gl::UniformScalar<name_, type_> { static auto name() { return #name_; } } #define MBGL_DEFINE_UNIFORM_VECTOR(type_, n_, name_) \ - struct name_ : ::mbgl::gl::UniformVector<name_, type_, n_> { static constexpr auto name = #name_; } + struct name_ : ::mbgl::gl::UniformVector<name_, type_, n_> { static auto name() { return #name_; } } #define MBGL_DEFINE_UNIFORM_MATRIX(type_, n_, name_) \ - struct name_ : ::mbgl::gl::UniformMatrix<name_, type_, n_> { static constexpr auto name = #name_; } + struct name_ : ::mbgl::gl::UniformMatrix<name_, type_, n_> { static auto name() { return #name_; } } UniformLocation uniformLocation(ProgramID, const char * name); template <class... Us> class Uniforms { public: + using Types = TypeList<Us...>; using State = IndexedTuple<TypeList<Us...>, TypeList<typename Us::State...>>; using Values = IndexedTuple<TypeList<Us...>, TypeList<typename Us::Value...>>; static State state(const ProgramID& id) { - return State { { uniformLocation(id, Us::name) }... }; + return State { { uniformLocation(id, Us::name()) }... }; } - static std::function<void ()> binder(State& state, Values&& values_) { - return [&state, values = std::move(values_)] () mutable { - util::ignore({ (state.template get<Us>() = values.template get<Us>(), 0)... }); - }; + static void bind(State& state, Values&& values) { + util::ignore({ (state.template get<Us>() = values.template get<Us>(), 0)... }); } }; + +namespace detail { + +template <class...> +struct ConcatenateUniforms; + +template <class... As, class... Bs> +struct ConcatenateUniforms<TypeList<As...>, TypeList<Bs...>> { + using Type = Uniforms<As..., Bs...>; +}; + +} // namespace detail + +template <class A, class B> +using ConcatenateUniforms = typename detail::ConcatenateUniforms< + typename A::Types, + typename B::Types>::Type; + } // namespace gl } // namespace mbgl diff --git a/src/mbgl/gl/vertex_array.hpp b/src/mbgl/gl/vertex_array.hpp index 6215e56f21..6b6e11324f 100644 --- a/src/mbgl/gl/vertex_array.hpp +++ b/src/mbgl/gl/vertex_array.hpp @@ -11,4 +11,4 @@ extern ExtensionFunction<void(GLsizei n, const GLuint* arrays)> DeleteVertexArra extern ExtensionFunction<void(GLsizei n, GLuint* arrays)> GenVertexArrays; } // namespace gl -} // namespace mbgl
\ No newline at end of file +} // namespace mbgl diff --git a/src/mbgl/layout/symbol_feature.hpp b/src/mbgl/layout/symbol_feature.hpp index 5dd61d9156..f4dc1680bc 100644 --- a/src/mbgl/layout/symbol_feature.hpp +++ b/src/mbgl/layout/symbol_feature.hpp @@ -3,13 +3,25 @@ #include <mbgl/tile/geometry_tile_data.hpp> #include <mbgl/util/optional.hpp> +#include <array> #include <string> namespace mbgl { -class SymbolFeature { +class SymbolFeature : public GeometryTileFeature { public: - FeatureType type; + SymbolFeature(std::unique_ptr<GeometryTileFeature> feature_) : + feature(std::move(feature_)), + geometry(feature->getGeometries()) // we need a mutable copy of the geometry for mergeLines() + {} + + FeatureType getType() const override { return feature->getType(); } + optional<Value> getValue(const std::string& key) const override { return feature->getValue(key); }; + std::unordered_map<std::string,Value> getProperties() const override { return feature->getProperties(); }; + optional<FeatureIdentifier> getID() const override { return feature->getID(); }; + GeometryCollection getGeometries() const override { return geometry; }; + + std::unique_ptr<GeometryTileFeature> feature; GeometryCollection geometry; optional<std::u16string> text; optional<std::string> icon; diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp index fafcc7c15d..d81783b2f6 100644 --- a/src/mbgl/layout/symbol_instance.cpp +++ b/src/mbgl/layout/symbol_instance.cpp @@ -6,29 +6,45 @@ namespace mbgl { using namespace style; SymbolInstance::SymbolInstance(Anchor& anchor, const GeometryCoordinates& line, - const Shaping& shapedText, const PositionedIcon& shapedIcon, + const std::pair<Shaping, Shaping>& shapedTextOrientations, const PositionedIcon& shapedIcon, const SymbolLayoutProperties::Evaluated& layout, const bool addToBuffers, const uint32_t index_, const float textBoxScale, const float textPadding, const SymbolPlacementType textPlacement, const float iconBoxScale, const float iconPadding, const SymbolPlacementType iconPlacement, - const GlyphPositions& face, const IndexedSubfeature& indexedFeature) : + const GlyphPositions& face, const IndexedSubfeature& indexedFeature, const std::size_t featureIndex_) : point(anchor.point), index(index_), - hasText(shapedText), + hasText(shapedTextOrientations.first || shapedTextOrientations.second), hasIcon(shapedIcon), - // Create the quads used for rendering the glyphs. - glyphQuads(addToBuffers && shapedText ? - getGlyphQuads(anchor, shapedText, textBoxScale, line, layout, textPlacement, face) : - SymbolQuads()), + // Create the collision features that will be used to check whether this symbol instance can be placed + textCollisionFeature(line, anchor, shapedTextOrientations.second ?: shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature), + iconCollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature), + featureIndex(featureIndex_) { - // Create the quad used for rendering the icon. - iconQuads(addToBuffers && shapedIcon ? - getIconQuads(anchor, shapedIcon, line, layout, iconPlacement, shapedText) : - SymbolQuads()), + // Create the quads used for rendering the icon and glyphs. + if (addToBuffers) { + if (shapedIcon) { + iconQuad = getIconQuad(anchor, shapedIcon, line, layout, iconPlacement, shapedTextOrientations.first); + } + if (shapedTextOrientations.first) { + auto quads = getGlyphQuads(anchor, shapedTextOrientations.first, textBoxScale, line, layout, textPlacement, face); + glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end()); + } + if (shapedTextOrientations.second) { + auto quads = getGlyphQuads(anchor, shapedTextOrientations.second, textBoxScale, line, layout, textPlacement, face); + glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end()); + } + } - // Create the collision features that will be used to check whether this symbol instance can be placed - textCollisionFeature(line, anchor, shapedText, textBoxScale, textPadding, textPlacement, indexedFeature), - iconCollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature) - {} + if (shapedTextOrientations.first && shapedTextOrientations.second) { + writingModes = WritingModeType::Horizontal | WritingModeType::Vertical; + } else if (shapedTextOrientations.first) { + writingModes = WritingModeType::Horizontal; + } else if (shapedTextOrientations.second) { + writingModes = WritingModeType::Vertical; + } else { + writingModes = WritingModeType::None; + } +} } // namespace mbgl diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp index 508c11a394..532a4d30d8 100644 --- a/src/mbgl/layout/symbol_instance.hpp +++ b/src/mbgl/layout/symbol_instance.hpp @@ -1,6 +1,7 @@ #pragma once #include <mbgl/text/quads.hpp> +#include <mbgl/text/glyph.hpp> #include <mbgl/text/collision_feature.hpp> #include <mbgl/style/layers/symbol_layer_properties.hpp> @@ -12,20 +13,22 @@ class IndexedSubfeature; class SymbolInstance { public: explicit SymbolInstance(Anchor& anchor, const GeometryCoordinates& line, - const Shaping& shapedText, const PositionedIcon& shapedIcon, + const std::pair<Shaping, Shaping>& shapedTextOrientations, const PositionedIcon& shapedIcon, const style::SymbolLayoutProperties::Evaluated&, const bool inside, const uint32_t index, const float textBoxScale, const float textPadding, style::SymbolPlacementType textPlacement, const float iconBoxScale, const float iconPadding, style::SymbolPlacementType iconPlacement, - const GlyphPositions& face, const IndexedSubfeature& indexedfeature); + const GlyphPositions& face, const IndexedSubfeature& indexedfeature, const std::size_t featureIndex); Point<float> point; uint32_t index; bool hasText; bool hasIcon; SymbolQuads glyphQuads; - SymbolQuads iconQuads; + optional<SymbolQuad> iconQuad; CollisionFeature textCollisionFeature; CollisionFeature iconCollisionFeature; + WritingModeType writingModes; + std::size_t featureIndex; }; } // namespace mbgl diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index eaa0332995..3a2c082ad8 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -3,7 +3,9 @@ #include <mbgl/layout/clip_lines.hpp> #include <mbgl/renderer/symbol_bucket.hpp> #include <mbgl/style/filter_evaluator.hpp> -#include <mbgl/style/layer.hpp> +#include <mbgl/style/bucket_parameters.hpp> +#include <mbgl/style/layers/symbol_layer.hpp> +#include <mbgl/style/layers/symbol_layer_impl.hpp> #include <mbgl/sprite/sprite_atlas.hpp> #include <mbgl/text/glyph_atlas.hpp> #include <mbgl/text/get_anchors.hpp> @@ -15,6 +17,7 @@ #include <mbgl/util/std.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/string.hpp> +#include <mbgl/util/i18n.hpp> #include <mbgl/math/clamp.hpp> #include <mbgl/math/minmax.hpp> #include <mbgl/math/log2.hpp> @@ -27,48 +30,82 @@ namespace mbgl { using namespace style; -SymbolLayout::SymbolLayout(std::vector<std::string> layerIDs_, - std::string sourceLayerName_, - uint32_t overscaling_, - float zoom_, - const MapMode mode_, - const GeometryTileLayer& layer, - const style::Filter& filter, - style::SymbolLayoutProperties::Evaluated layout_, - float textMaxSize_, +SymbolLayout::SymbolLayout(const BucketParameters& parameters, + const std::vector<const Layer*>& layers, + const GeometryTileLayer& sourceLayer, SpriteAtlas& spriteAtlas_) - : layerIDs(std::move(layerIDs_)), - sourceLayerName(std::move(sourceLayerName_)), - overscaling(overscaling_), - zoom(zoom_), - mode(mode_), - layout(std::move(layout_)), - textMaxSize(textMaxSize_), + : sourceLayerName(sourceLayer.getName()), + bucketName(layers.at(0)->getID()), + overscaling(parameters.tileID.overscaleFactor()), + zoom(parameters.tileID.overscaledZ), + mode(parameters.mode), spriteAtlas(spriteAtlas_), - tileSize(util::tileSize * overscaling_), + tileSize(util::tileSize * overscaling), tilePixelRatio(float(util::EXTENT) / tileSize) { - const bool hasText = !layout.get<TextField>().empty() && !layout.get<TextFont>().empty(); + const SymbolLayer::Impl& leader = *layers.at(0)->as<SymbolLayer>()->impl; + + layout = leader.layout.evaluate(PropertyEvaluationParameters(zoom)); + + if (layout.get<IconRotationAlignment>() == AlignmentType::Auto) { + if (layout.get<SymbolPlacement>() == SymbolPlacementType::Line) { + layout.get<IconRotationAlignment>() = AlignmentType::Map; + } else { + layout.get<IconRotationAlignment>() = AlignmentType::Viewport; + } + } + + if (layout.get<TextRotationAlignment>() == AlignmentType::Auto) { + if (layout.get<SymbolPlacement>() == SymbolPlacementType::Line) { + layout.get<TextRotationAlignment>() = AlignmentType::Map; + } else { + layout.get<TextRotationAlignment>() = AlignmentType::Viewport; + } + } + + // If unspecified `text-pitch-alignment` inherits `text-rotation-alignment` + if (layout.get<TextPitchAlignment>() == AlignmentType::Auto) { + layout.get<TextPitchAlignment>() = layout.get<TextRotationAlignment>(); + } + + textMaxSize = leader.layout.evaluate<TextSize>(PropertyEvaluationParameters(18)); + + layout.get<IconSize>() = leader.layout.evaluate<IconSize>(PropertyEvaluationParameters(zoom + 1)); + layout.get<TextSize>() = leader.layout.evaluate<TextSize>(PropertyEvaluationParameters(zoom + 1)); + + const bool hasTextField = layout.get<TextField>().match( + [&] (const std::string& s) { return !s.empty(); }, + [&] (const auto&) { return true; } + ); + + const bool hasText = !layout.get<TextFont>().empty() && hasTextField; + const bool hasIcon = !layout.get<IconImage>().empty(); if (!hasText && !hasIcon) { return; } - auto layerName = layer.getName(); + for (const auto& layer : layers) { + layerPaintProperties.emplace(layer->getID(), std::make_pair( + layer->as<SymbolLayer>()->impl->iconPaintProperties(), + layer->as<SymbolLayer>()->impl->textPaintProperties() + )); + } // Determine and load glyph ranges - const size_t featureCount = layer.featureCount(); + const size_t featureCount = sourceLayer.featureCount(); for (size_t i = 0; i < featureCount; ++i) { - auto feature = layer.getFeature(i); - if (!filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); })) + auto feature = sourceLayer.getFeature(i); + if (!leader.filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); })) continue; + + SymbolFeature ft(std::move(feature)); - SymbolFeature ft; ft.index = i; - auto getValue = [&feature](const std::string& key) -> std::string { - auto value = feature->getValue(key); + auto getValue = [&ft](const std::string& key) -> std::string { + auto value = ft.getValue(key); if (!value) return std::string(); if (value->is<std::string>()) @@ -83,13 +120,18 @@ SymbolLayout::SymbolLayout(std::vector<std::string> layerIDs_, return util::toString(value->get<double>()); return "null"; }; - + if (hasText) { - std::string u8string = util::replaceTokens(layout.get<TextField>(), getValue); + std::string u8string = layout.evaluate<TextField>(zoom, ft); + if (layout.get<TextField>().isConstant()) { + u8string = util::replaceTokens(u8string, getValue); + } + + auto textTransform = layout.evaluate<TextTransform>(zoom, ft); - if (layout.get<TextTransform>() == TextTransformType::Uppercase) { + if (textTransform == TextTransformType::Uppercase) { u8string = platform::uppercase(u8string); - } else if (layout.get<TextTransform>() == TextTransformType::Lowercase) { + } else if (textTransform == TextTransformType::Lowercase) { u8string = platform::lowercase(u8string); } @@ -98,6 +140,9 @@ SymbolLayout::SymbolLayout(std::vector<std::string> layerIDs_, // Loop through all characters of this text and collect unique codepoints. for (char16_t chr : *ft.text) { ranges.insert(getGlyphRange(chr)); + if (char16_t verticalChr = util::i18n::verticalizePunctuation(chr)) { + ranges.insert(getGlyphRange(verticalChr)); + } } } @@ -106,8 +151,6 @@ SymbolLayout::SymbolLayout(std::vector<std::string> layerIDs_, } if (ft.text || ft.icon) { - ft.type = feature->getType(); - ft.geometry = feature->getGeometries(); features.push_back(std::move(ft)); } } @@ -122,7 +165,12 @@ bool SymbolLayout::hasSymbolInstances() const { } bool SymbolLayout::canPrepare(GlyphAtlas& glyphAtlas) { - if (!layout.get<TextField>().empty() && !layout.get<TextFont>().empty() && !glyphAtlas.hasGlyphRanges(layout.get<TextFont>(), ranges)) { + const bool hasTextField = layout.get<TextField>().match( + [&] (const std::string& s) { return !s.empty(); }, + [&] (const auto&) { return true; } + ); + + if (hasTextField && !layout.get<TextFont>().empty() && !glyphAtlas.hasGlyphRanges(layout.get<TextFont>(), ranges)) { return false; } @@ -178,61 +226,83 @@ void SymbolLayout::prepare(uintptr_t tileUID, auto glyphSet = glyphAtlas.getGlyphSet(layout.get<TextFont>()); - for (const auto& feature : features) { + const bool textAlongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map && + layout.get<SymbolPlacement>() == SymbolPlacementType::Line; + + for (auto it = features.begin(); it != features.end(); ++it) { + auto& feature = *it; if (feature.geometry.empty()) continue; - Shaping shapedText; + std::pair<Shaping, Shaping> shapedTextOrientations; PositionedIcon shapedIcon; GlyphPositions face; // if feature has text, shape the text if (feature.text) { - shapedText = glyphSet->getShaping( - /* string */ *feature.text, - /* maxWidth: ems */ layout.get<SymbolPlacement>() != SymbolPlacementType::Line ? - layout.get<TextMaxWidth>() * 24 : 0, - /* lineHeight: ems */ layout.get<TextLineHeight>() * 24, - /* horizontalAlign */ horizontalAlign, - /* verticalAlign */ verticalAlign, - /* justify */ justify, - /* spacing: ems */ layout.get<TextLetterSpacing>() * 24, - /* translate */ Point<float>(layout.get<TextOffset>()[0], layout.get<TextOffset>()[1]), - /* bidirectional algorithm object */ bidi); - - // Add the glyphs we need for this label to the glyph atlas. - if (shapedText) { - glyphAtlas.addGlyphs(tileUID, *feature.text, layout.get<TextFont>(), **glyphSet, face); + auto getShaping = [&] (const std::u16string& text, WritingModeType writingMode) { + const float oneEm = 24.0f; + const Shaping result = glyphSet->getShaping( + /* string */ text, + /* maxWidth: ems */ layout.get<SymbolPlacement>() != SymbolPlacementType::Line ? + layout.get<TextMaxWidth>() * oneEm : 0, + /* lineHeight: ems */ layout.get<TextLineHeight>() * oneEm, + /* horizontalAlign */ horizontalAlign, + /* verticalAlign */ verticalAlign, + /* justify */ justify, + /* spacing: ems */ layout.get<TextLetterSpacing>() * oneEm, + /* translate */ Point<float>(layout.get<TextOffset>()[0], layout.get<TextOffset>()[1]), + /* verticalHeight */ oneEm, + /* writingMode */ writingMode, + /* bidirectional algorithm object */ bidi); + + // Add the glyphs we need for this label to the glyph atlas. + if (result) { + glyphAtlas.addGlyphs(tileUID, text, layout.get<TextFont>(), glyphSet, face); + } + + return result; + }; + + shapedTextOrientations.first = getShaping(*feature.text, WritingModeType::Horizontal); + + if (util::i18n::allowsVerticalWritingMode(*feature.text) && textAlongLine) { + shapedTextOrientations.second = getShaping(util::i18n::verticalizePunctuation(*feature.text), WritingModeType::Vertical); } } // if feature has icon, get sprite atlas position if (feature.icon) { - auto image = spriteAtlas.getImage(*feature.icon, SpritePatternMode::Single); + auto image = spriteAtlas.getIcon(*feature.icon); if (image) { - shapedIcon = shapeIcon(*image, layout); + shapedIcon = shapeIcon(*image, + layout.evaluate<IconOffset>(zoom, feature), + layout.evaluate<IconRotate>(zoom, feature) * util::DEG2RAD); assert((*image).spriteImage); if ((*image).spriteImage->sdf) { sdfIcons = true; } if ((*image).relativePixelRatio != 1.0f) { iconsNeedLinear = true; - } else if (layout.get<IconRotate>() != 0) { + } else if (layout.get<IconRotate>().constantOr(1) != 0) { iconsNeedLinear = true; } } } // if either shapedText or icon position is present, add the feature - if (shapedText || shapedIcon) { - addFeature(feature, shapedText, shapedIcon, face); + if (shapedTextOrientations.first || shapedIcon) { + addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, face); } + + feature.geometry.clear(); } - features.clear(); + compareText.clear(); } -void SymbolLayout::addFeature(const SymbolFeature& feature, - const Shaping& shapedText, +void SymbolLayout::addFeature(const std::size_t index, + const SymbolFeature& feature, + const std::pair<Shaping, Shaping>& shapedTextOrientations, const PositionedIcon& shapedIcon, const GlyphPositions& face) { const float minScale = 0.5f; @@ -254,7 +324,7 @@ void SymbolLayout::addFeature(const SymbolFeature& feature, ? SymbolPlacementType::Point : layout.get<SymbolPlacement>(); const float textRepeatDistance = symbolSpacing / 2; - IndexedSubfeature indexedFeature = {feature.index, sourceLayerName, layerIDs.at(0), symbolInstances.size()}; + IndexedSubfeature indexedFeature = {feature.index, sourceLayerName, bucketName, symbolInstances.size()}; auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) { // https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers @@ -277,11 +347,13 @@ void SymbolLayout::addFeature(const SymbolFeature& feature, const bool addToBuffers = mode == MapMode::Still || withinPlus0; - symbolInstances.emplace_back(anchor, line, shapedText, shapedIcon, layout, addToBuffers, symbolInstances.size(), + symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon, layout, addToBuffers, symbolInstances.size(), textBoxScale, textPadding, textPlacement, iconBoxScale, iconPadding, iconPlacement, - face, indexedFeature); + face, indexedFeature, index); }; + + const auto& type = feature.getType(); if (layout.get<SymbolPlacement>() == SymbolPlacementType::Line) { auto clippedLines = util::clipLines(feature.geometry, 0, 0, util::EXTENT, util::EXTENT); @@ -289,8 +361,8 @@ void SymbolLayout::addFeature(const SymbolFeature& feature, Anchors anchors = getAnchors(line, symbolSpacing, textMaxAngle, - shapedText.left, - shapedText.right, + (shapedTextOrientations.second ?: shapedTextOrientations.first).left, + (shapedTextOrientations.second ?: shapedTextOrientations.first).right, shapedIcon.left, shapedIcon.right, glyphSize, @@ -298,12 +370,12 @@ void SymbolLayout::addFeature(const SymbolFeature& feature, overscaling); for (auto& anchor : anchors) { - if (!shapedText || !anchorIsTooClose(shapedText.text, textRepeatDistance, anchor)) { + if (!feature.text || !anchorIsTooClose(*feature.text, textRepeatDistance, anchor)) { addSymbolInstance(line, anchor); } } } - } else if (feature.type == FeatureType::Polygon) { + } else if (type == FeatureType::Polygon) { for (const auto& polygon : classifyRings(feature.geometry)) { Polygon<double> poly; for (const auto& ring : polygon) { @@ -319,12 +391,12 @@ void SymbolLayout::addFeature(const SymbolFeature& feature, Anchor anchor(poi.x, poi.y, 0, minScale); addSymbolInstance(polygon[0], anchor); } - } else if (feature.type == FeatureType::LineString) { + } else if (type == FeatureType::LineString) { for (const auto& line : feature.geometry) { Anchor anchor(line[0].x, line[0].y, 0, minScale); addSymbolInstance(line, anchor); } - } else if (feature.type == FeatureType::Point) { + } else if (type == FeatureType::Point) { for (const auto& points : feature.geometry) { for (const auto& point : points) { Anchor anchor(point.x, point.y, 0, minScale); @@ -350,7 +422,7 @@ bool SymbolLayout::anchorIsTooClose(const std::u16string& text, const float repe } std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) { - auto bucket = std::make_unique<SymbolBucket>(mode, layout, sdfIcons, iconsNeedLinear); + auto bucket = std::make_unique<SymbolBucket>(layout, layerPaintProperties, zoom, sdfIcons, iconsNeedLinear); // Calculate which labels can be shown and when they can be shown and // create the bufers used for rendering. @@ -365,6 +437,8 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) const bool mayOverlap = layout.get<TextAllowOverlap>() || layout.get<IconAllowOverlap>() || layout.get<TextIgnorePlacement>() || layout.get<IconIgnorePlacement>(); + const bool keepUpright = layout.get<TextKeepUpright>(); + // Sort symbols by their y position on the canvas so that they lower symbols // are drawn on top of higher symbols. // Don't sort symbols that won't overlap because it isn't necessary and @@ -416,22 +490,32 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) // Insert final placement into collision tree and add glyphs/icons to buffers if (hasText) { + const float placementZoom = util::max(util::log2(glyphScale) + zoom, 0.0f); collisionTile.insertFeature(symbolInstance.textCollisionFeature, glyphScale, layout.get<TextIgnorePlacement>()); if (glyphScale < collisionTile.maxScale) { - addSymbols( - bucket->text, symbolInstance.glyphQuads, glyphScale, - layout.get<TextKeepUpright>(), textPlacement, collisionTile.config.angle); + for (const auto& symbol : symbolInstance.glyphQuads) { + addSymbol( + bucket->text, symbol, placementZoom, + keepUpright, textPlacement, collisionTile.config.angle, symbolInstance.writingModes); + } } } if (hasIcon) { + const float placementZoom = util::max(util::log2(iconScale) + zoom, 0.0f); collisionTile.insertFeature(symbolInstance.iconCollisionFeature, iconScale, layout.get<IconIgnorePlacement>()); - if (iconScale < collisionTile.maxScale) { - addSymbols( - bucket->icon, symbolInstance.iconQuads, iconScale, - layout.get<IconKeepUpright>(), iconPlacement, collisionTile.config.angle); + if (iconScale < collisionTile.maxScale && symbolInstance.iconQuad) { + addSymbol( + bucket->icon, *symbolInstance.iconQuad, placementZoom, + keepUpright, iconPlacement, collisionTile.config.angle, symbolInstance.writingModes); } } + + const auto& feature = features.at(symbolInstance.featureIndex); + for (auto& pair : bucket->paintPropertyBinders) { + pair.second.first.populateVertexVectors(feature, bucket->icon.vertices.vertexSize()); + pair.second.second.populateVertexVectors(feature, bucket->text.vertices.vertexSize()); + } } if (collisionTile.config.debug) { @@ -442,67 +526,76 @@ std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) } template <typename Buffer> -void SymbolLayout::addSymbols(Buffer &buffer, const SymbolQuads &symbols, float scale, const bool keepUpright, const style::SymbolPlacementType placement, const float placementAngle) { +void SymbolLayout::addSymbol(Buffer& buffer, + const SymbolQuad& symbol, + const float placementZoom, + const bool keepUpright, + const style::SymbolPlacementType placement, + const float placementAngle, + const WritingModeType writingModes) { constexpr const uint16_t vertexLength = 4; - const float placementZoom = util::max(util::log2(scale) + zoom, 0.0f); - - for (const auto& symbol : symbols) { - const auto &tl = symbol.tl; - const auto &tr = symbol.tr; - const auto &bl = symbol.bl; - const auto &br = symbol.br; - const auto &tex = symbol.tex; - - float minZoom = util::max(zoom + util::log2(symbol.minScale), placementZoom); - float maxZoom = util::min(zoom + util::log2(symbol.maxScale), util::MAX_ZOOM_F); - const auto &anchorPoint = symbol.anchorPoint; - - // drop upside down versions of glyphs - const float a = std::fmod(symbol.anchorAngle + placementAngle + M_PI, M_PI * 2); - if (keepUpright && placement == style::SymbolPlacementType::Line && - (a <= M_PI / 2 || a > M_PI * 3 / 2)) { - continue; - } - if (maxZoom <= minZoom) - continue; + const auto &tl = symbol.tl; + const auto &tr = symbol.tr; + const auto &bl = symbol.bl; + const auto &br = symbol.br; + const auto &tex = symbol.tex; + + float minZoom = util::max(zoom + util::log2(symbol.minScale), placementZoom); + float maxZoom = util::min(zoom + util::log2(symbol.maxScale), util::MAX_ZOOM_F); + const auto &anchorPoint = symbol.anchorPoint; + + // drop incorrectly oriented glyphs + const float a = std::fmod(symbol.anchorAngle + placementAngle + M_PI, M_PI * 2); + if (writingModes & WritingModeType::Vertical) { + if (placement == style::SymbolPlacementType::Line && symbol.writingMode == WritingModeType::Vertical) { + if (keepUpright && placement == style::SymbolPlacementType::Line && (a <= (M_PI * 5 / 4) || a > (M_PI * 7 / 4))) + return; + } else if (keepUpright && placement == style::SymbolPlacementType::Line && (a <= (M_PI * 3 / 4) || a > (M_PI * 5 / 4))) + return; + } else if (keepUpright && placement == style::SymbolPlacementType::Line && + (a <= M_PI / 2 || a > M_PI * 3 / 2)) { + return; + } - // Lower min zoom so that while fading out the label - // it can be shown outside of collision-free zoom levels - if (minZoom == placementZoom) { - minZoom = 0; - } + if (maxZoom <= minZoom) + return; - if (buffer.segments.empty() || buffer.segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) { - buffer.segments.emplace_back(buffer.vertices.vertexSize(), buffer.triangles.indexSize()); - } + // Lower min zoom so that while fading out the label + // it can be shown outside of collision-free zoom levels + if (minZoom == placementZoom) { + minZoom = 0; + } - // We're generating triangle fans, so we always start with the first - // coordinate in this polygon. - auto& segment = buffer.segments.back(); - assert(segment.vertexLength <= std::numeric_limits<uint16_t>::max()); - uint16_t index = segment.vertexLength; - - // Encode angle of glyph - uint8_t glyphAngle = std::round((symbol.glyphAngle / (M_PI * 2)) * 256); - - // coordinates (2 triangles) - buffer.vertices.emplace_back(SymbolAttributes::vertex(anchorPoint, tl, tex.x, tex.y, - minZoom, maxZoom, placementZoom, glyphAngle)); - buffer.vertices.emplace_back(SymbolAttributes::vertex(anchorPoint, tr, tex.x + tex.w, tex.y, - minZoom, maxZoom, placementZoom, glyphAngle)); - buffer.vertices.emplace_back(SymbolAttributes::vertex(anchorPoint, bl, tex.x, tex.y + tex.h, - minZoom, maxZoom, placementZoom, glyphAngle)); - buffer.vertices.emplace_back(SymbolAttributes::vertex(anchorPoint, br, tex.x + tex.w, tex.y + tex.h, - minZoom, maxZoom, placementZoom, glyphAngle)); - - // add the two triangles, referencing the four coordinates we just inserted. - buffer.triangles.emplace_back(index + 0, index + 1, index + 2); - buffer.triangles.emplace_back(index + 1, index + 2, index + 3); - - segment.vertexLength += vertexLength; - segment.indexLength += 6; + if (buffer.segments.empty() || buffer.segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) { + buffer.segments.emplace_back(buffer.vertices.vertexSize(), buffer.triangles.indexSize()); } + + // We're generating triangle fans, so we always start with the first + // coordinate in this polygon. + auto& segment = buffer.segments.back(); + assert(segment.vertexLength <= std::numeric_limits<uint16_t>::max()); + uint16_t index = segment.vertexLength; + + // Encode angle of glyph + uint8_t glyphAngle = std::round((symbol.glyphAngle / (M_PI * 2)) * 256); + + // coordinates (2 triangles) + buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, tl, tex.x, tex.y, + minZoom, maxZoom, placementZoom, glyphAngle)); + buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, tr, tex.x + tex.w, tex.y, + minZoom, maxZoom, placementZoom, glyphAngle)); + buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, bl, tex.x, tex.y + tex.h, + minZoom, maxZoom, placementZoom, glyphAngle)); + buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, br, tex.x + tex.w, tex.y + tex.h, + minZoom, maxZoom, placementZoom, glyphAngle)); + + // add the two triangles, referencing the four coordinates we just inserted. + buffer.triangles.emplace_back(index + 0, index + 1, index + 2); + buffer.triangles.emplace_back(index + 1, index + 2, index + 3); + + segment.vertexLength += vertexLength; + segment.indexLength += 6; } void SymbolLayout::addToDebugBuffers(CollisionTile& collisionTile, SymbolBucket& bucket) { diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp index c89b791ccc..491d0078da 100644 --- a/src/mbgl/layout/symbol_layout.hpp +++ b/src/mbgl/layout/symbol_layout.hpp @@ -5,6 +5,7 @@ #include <mbgl/layout/symbol_feature.hpp> #include <mbgl/layout/symbol_instance.hpp> #include <mbgl/text/bidi.hpp> +#include <mbgl/style/layers/symbol_layer_impl.hpp> #include <memory> #include <map> @@ -20,6 +21,7 @@ class GlyphAtlas; class SymbolBucket; namespace style { +class BucketParameters; class Filter; class Layer; } // namespace style @@ -28,15 +30,9 @@ struct Anchor; class SymbolLayout { public: - SymbolLayout(std::vector<std::string> layerIDs_, - std::string sourceLayerName_, - uint32_t overscaling, - float zoom, - const MapMode, + SymbolLayout(const style::BucketParameters&, + const std::vector<const style::Layer*>&, const GeometryTileLayer&, - const style::Filter&, - style::SymbolLayoutProperties::Evaluated, - float textMaxSize, SpriteAtlas&); bool canPrepare(GlyphAtlas&); @@ -56,12 +52,13 @@ public: State state = Pending; - const std::vector<std::string> layerIDs; - const std::string sourceLayerName; + std::unordered_map<std::string, + std::pair<style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>> layerPaintProperties; private: - void addFeature(const SymbolFeature&, - const Shaping& shapedText, + void addFeature(const size_t, + const SymbolFeature&, + const std::pair<Shaping, Shaping>& shapedTextOrientations, const PositionedIcon& shapedIcon, const GlyphPositions& face); @@ -72,14 +69,18 @@ private: // Adds placed items to the buffer. template <typename Buffer> - void addSymbols(Buffer&, const SymbolQuads&, float scale, - const bool keepUpright, const style::SymbolPlacementType, const float placementAngle); + void addSymbol(Buffer&, const SymbolQuad&, float scale, + const bool keepUpright, const style::SymbolPlacementType, const float placementAngle, + WritingModeType writingModes); + const std::string sourceLayerName; + const std::string bucketName; const float overscaling; const float zoom; const MapMode mode; - const style::SymbolLayoutProperties::Evaluated layout; - const float textMaxSize; + + style::SymbolLayoutProperties::Evaluated layout; + float textMaxSize; SpriteAtlas& spriteAtlas; @@ -92,7 +93,7 @@ private: GlyphRangeSet ranges; std::vector<SymbolInstance> symbolInstances; std::vector<SymbolFeature> features; - + BiDi bidi; // Consider moving this up to geometry tile worker to reduce reinstantiation costs; use of BiDi/ubiditransform object must be constrained to one thread }; diff --git a/src/mbgl/map/backend_scope.cpp b/src/mbgl/map/backend_scope.cpp new file mode 100644 index 0000000000..98775ceadb --- /dev/null +++ b/src/mbgl/map/backend_scope.cpp @@ -0,0 +1,36 @@ +#include <mbgl/map/backend_scope.hpp> +#include <mbgl/map/backend.hpp> +#include <mbgl/util/thread_local.hpp> + +#include <cassert> + +namespace mbgl { + +static util::ThreadLocal<BackendScope> currentScope; + +BackendScope::BackendScope(Backend& backend_) + : priorScope(currentScope.get()), + nextScope(nullptr), + backend(backend_) { + if (priorScope) { + assert(priorScope->nextScope == nullptr); + priorScope->nextScope = this; + } + backend.activate(); + currentScope.set(this); +} + +BackendScope::~BackendScope() { + assert(nextScope == nullptr); + if (priorScope) { + priorScope->backend.activate(); + currentScope.set(priorScope); + assert(priorScope->nextScope == this); + priorScope->nextScope = nullptr; + } else { + backend.deactivate(); + currentScope.set(nullptr); + } +} + +} // namespace mbgl diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index e9d4d9e247..857f088b62 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -2,6 +2,7 @@ #include <mbgl/map/camera.hpp> #include <mbgl/map/view.hpp> #include <mbgl/map/backend.hpp> +#include <mbgl/map/backend_scope.hpp> #include <mbgl/map/transform.hpp> #include <mbgl/map/transform_state.hpp> #include <mbgl/annotation/annotation_manager.hpp> @@ -24,6 +25,7 @@ #include <mbgl/util/tile_coordinate.hpp> #include <mbgl/actor/scheduler.hpp> #include <mbgl/util/logging.hpp> +#include <mbgl/util/string.hpp> #include <mbgl/math/log2.hpp> namespace mbgl { @@ -314,10 +316,15 @@ void Map::Impl::render(View& view) { contextMode, debugOptions }; - painter->render(*style, - frameData, - view, - annotationManager->getSpriteAtlas()); + try { + painter->render(*style, + frameData, + view, + annotationManager->getSpriteAtlas()); + } catch (...) { + Log::Error(Event::General, "Exception in render: %s", util::toString(std::current_exception()).c_str()); + exit(1); + } auto request = std::move(stillImageRequest); request->callback(nullptr); @@ -471,26 +478,26 @@ void Map::flyTo(const CameraOptions& camera, const AnimationOptions& animation) #pragma mark - Position -void Map::moveBy(const ScreenCoordinate& point, const Duration& duration) { +void Map::moveBy(const ScreenCoordinate& point, const AnimationOptions& animation) { impl->cameraMutated = true; - impl->transform.moveBy(point, duration); + impl->transform.moveBy(point, animation); impl->onUpdate(Update::Repaint); } -void Map::setLatLng(const LatLng& latLng, const Duration& duration) { +void Map::setLatLng(const LatLng& latLng, const AnimationOptions& animation) { impl->cameraMutated = true; - setLatLng(latLng, optional<ScreenCoordinate> {}, duration); + setLatLng(latLng, optional<ScreenCoordinate> {}, animation); } -void Map::setLatLng(const LatLng& latLng, optional<EdgeInsets> padding, const Duration& duration) { +void Map::setLatLng(const LatLng& latLng, optional<EdgeInsets> padding, const AnimationOptions& animation) { impl->cameraMutated = true; - impl->transform.setLatLng(latLng, padding, duration); + impl->transform.setLatLng(latLng, padding, animation); impl->onUpdate(Update::Repaint); } -void Map::setLatLng(const LatLng& latLng, optional<ScreenCoordinate> anchor, const Duration& duration) { +void Map::setLatLng(const LatLng& latLng, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { impl->cameraMutated = true; - impl->transform.setLatLng(latLng, anchor, duration); + impl->transform.setLatLng(latLng, anchor, animation); impl->onUpdate(Update::Repaint); } @@ -513,15 +520,15 @@ void Map::resetPosition(optional<EdgeInsets> padding) { #pragma mark - Scale -void Map::scaleBy(double ds, optional<ScreenCoordinate> anchor, const Duration& duration) { +void Map::scaleBy(double ds, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { impl->cameraMutated = true; - impl->transform.scaleBy(ds, anchor, duration); + impl->transform.scaleBy(ds, anchor, animation); impl->onUpdate(Update::RecalculateStyle); } -void Map::setScale(double scale, optional<ScreenCoordinate> anchor, const Duration& duration) { +void Map::setScale(double scale, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { impl->cameraMutated = true; - impl->transform.setScale(scale, anchor, duration); + impl->transform.setScale(scale, anchor, animation); impl->onUpdate(Update::RecalculateStyle); } @@ -529,14 +536,20 @@ double Map::getScale() const { return impl->transform.getScale(); } -void Map::setZoom(double zoom, const Duration& duration) { +void Map::setZoom(double zoom, const AnimationOptions& animation) { impl->cameraMutated = true; - setZoom(zoom, {}, duration); + setZoom(zoom, optional<EdgeInsets> {}, animation); } -void Map::setZoom(double zoom, optional<EdgeInsets> padding, const Duration& duration) { +void Map::setZoom(double zoom, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { impl->cameraMutated = true; - impl->transform.setZoom(zoom, padding, duration); + impl->transform.setZoom(zoom, anchor, animation); + impl->onUpdate(Update::RecalculateStyle); +} + +void Map::setZoom(double zoom, optional<EdgeInsets> padding, const AnimationOptions& animation) { + impl->cameraMutated = true; + impl->transform.setZoom(zoom, padding, animation); impl->onUpdate(Update::RecalculateStyle); } @@ -544,14 +557,14 @@ double Map::getZoom() const { return impl->transform.getZoom(); } -void Map::setLatLngZoom(const LatLng& latLng, double zoom, const Duration& duration) { +void Map::setLatLngZoom(const LatLng& latLng, double zoom, const AnimationOptions& animation) { impl->cameraMutated = true; - setLatLngZoom(latLng, zoom, {}, duration); + setLatLngZoom(latLng, zoom, {}, animation); } -void Map::setLatLngZoom(const LatLng& latLng, double zoom, optional<EdgeInsets> padding, const Duration& duration) { +void Map::setLatLngZoom(const LatLng& latLng, double zoom, optional<EdgeInsets> padding, const AnimationOptions& animation) { impl->cameraMutated = true; - impl->transform.setLatLngZoom(latLng, zoom, padding, duration); + impl->transform.setLatLngZoom(latLng, zoom, padding, animation); impl->onUpdate(Update::RecalculateStyle); } @@ -661,26 +674,26 @@ Size Map::getSize() const { #pragma mark - Rotation -void Map::rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const Duration& duration) { +void Map::rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const AnimationOptions& animation) { impl->cameraMutated = true; - impl->transform.rotateBy(first, second, duration); + impl->transform.rotateBy(first, second, animation); impl->onUpdate(Update::Repaint); } -void Map::setBearing(double degrees, const Duration& duration) { +void Map::setBearing(double degrees, const AnimationOptions& animation) { impl->cameraMutated = true; - setBearing(degrees, EdgeInsets(), duration); + setBearing(degrees, EdgeInsets(), animation); } -void Map::setBearing(double degrees, optional<ScreenCoordinate> anchor, const Duration& duration) { +void Map::setBearing(double degrees, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { impl->cameraMutated = true; - impl->transform.setAngle(-degrees * util::DEG2RAD, anchor, duration); + impl->transform.setAngle(-degrees * util::DEG2RAD, anchor, animation); impl->onUpdate(Update::Repaint); } -void Map::setBearing(double degrees, optional<EdgeInsets> padding, const Duration& duration) { +void Map::setBearing(double degrees, optional<EdgeInsets> padding, const AnimationOptions& animation) { impl->cameraMutated = true; - impl->transform.setAngle(-degrees * util::DEG2RAD, padding, duration); + impl->transform.setAngle(-degrees * util::DEG2RAD, padding, animation); impl->onUpdate(Update::Repaint); } @@ -688,22 +701,22 @@ double Map::getBearing() const { return -impl->transform.getAngle() * util::RAD2DEG; } -void Map::resetNorth(const Duration& duration) { +void Map::resetNorth(const AnimationOptions& animation) { impl->cameraMutated = true; - impl->transform.setAngle(0, duration); + impl->transform.setAngle(0, animation); impl->onUpdate(Update::Repaint); } #pragma mark - Pitch -void Map::setPitch(double pitch, const Duration& duration) { +void Map::setPitch(double pitch, const AnimationOptions& animation) { impl->cameraMutated = true; - setPitch(pitch, {}, duration); + setPitch(pitch, {}, animation); } -void Map::setPitch(double pitch, optional<ScreenCoordinate> anchor, const Duration& duration) { +void Map::setPitch(double pitch, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { impl->cameraMutated = true; - impl->transform.setPitch(pitch * util::DEG2RAD, anchor, duration); + impl->transform.setPitch(pitch * util::DEG2RAD, anchor, animation); impl->onUpdate(Update::Repaint); } @@ -912,8 +925,6 @@ void Map::addImage(const std::string& name, std::unique_ptr<const SpriteImage> i impl->styleMutated = true; impl->style->spriteAtlas->setSprite(name, std::move(image)); - impl->style->spriteAtlas->updateDirty(); - impl->onUpdate(Update::Repaint); } @@ -924,8 +935,6 @@ void Map::removeImage(const std::string& name) { impl->styleMutated = true; impl->style->spriteAtlas->removeSprite(name); - impl->style->spriteAtlas->updateDirty(); - impl->onUpdate(Update::Repaint); } diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index ba5e205301..eab9e97bc0 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -322,7 +322,7 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima #pragma mark - Position -void Transform::moveBy(const ScreenCoordinate& offset, const Duration& duration) { +void Transform::moveBy(const ScreenCoordinate& offset, const AnimationOptions& animation) { ScreenCoordinate centerOffset = { offset.x, -offset.y, @@ -331,22 +331,22 @@ void Transform::moveBy(const ScreenCoordinate& offset, const Duration& duration) CameraOptions camera; camera.center = state.screenCoordinateToLatLng(centerPoint); - easeTo(camera, duration); + easeTo(camera, animation); } -void Transform::setLatLng(const LatLng& latLng, const Duration& duration) { - setLatLng(latLng, optional<ScreenCoordinate> {}, duration); +void Transform::setLatLng(const LatLng& latLng, const AnimationOptions& animation) { + setLatLng(latLng, optional<ScreenCoordinate> {}, animation); } -void Transform::setLatLng(const LatLng& latLng, optional<EdgeInsets> padding, const Duration& duration) { +void Transform::setLatLng(const LatLng& latLng, optional<EdgeInsets> padding, const AnimationOptions& animation) { if (!latLng) return; CameraOptions camera; camera.center = latLng; camera.padding = padding; - easeTo(camera, duration); + easeTo(camera, animation); } -void Transform::setLatLng(const LatLng& latLng, optional<ScreenCoordinate> anchor, const Duration& duration) { +void Transform::setLatLng(const LatLng& latLng, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { if (!latLng) return; CameraOptions camera; camera.center = latLng; @@ -358,21 +358,21 @@ void Transform::setLatLng(const LatLng& latLng, optional<ScreenCoordinate> ancho padding.right = state.size.width - anchor->x; if (padding) camera.padding = padding; } - easeTo(camera, duration); + easeTo(camera, animation); } -void Transform::setLatLngZoom(const LatLng& latLng, double zoom, const Duration& duration) { - setLatLngZoom(latLng, zoom, EdgeInsets {}, duration); +void Transform::setLatLngZoom(const LatLng& latLng, double zoom, const AnimationOptions& animation) { + setLatLngZoom(latLng, zoom, EdgeInsets {}, animation); } -void Transform::setLatLngZoom(const LatLng& latLng, double zoom, optional<EdgeInsets> padding, const Duration& duration) { +void Transform::setLatLngZoom(const LatLng& latLng, double zoom, optional<EdgeInsets> padding, const AnimationOptions& animation) { if (!latLng || std::isnan(zoom)) return; CameraOptions camera; camera.center = latLng; camera.padding = padding; camera.zoom = zoom; - easeTo(camera, duration); + easeTo(camera, animation); } LatLng Transform::getLatLng(optional<EdgeInsets> padding) const { @@ -394,26 +394,26 @@ ScreenCoordinate Transform::getScreenCoordinate(optional<EdgeInsets> padding) co #pragma mark - Zoom -void Transform::scaleBy(double ds, const Duration& duration) { - scaleBy(ds, optional<ScreenCoordinate> {}, duration); +void Transform::scaleBy(double ds, const AnimationOptions& animation) { + scaleBy(ds, optional<ScreenCoordinate> {}, animation); } -void Transform::scaleBy(double ds, optional<ScreenCoordinate> anchor, const Duration& duration) { +void Transform::scaleBy(double ds, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { if (std::isnan(ds)) return; double scale = util::clamp(state.scale * ds, state.min_scale, state.max_scale); - setScale(scale, anchor, duration); + setScale(scale, anchor, animation); } -void Transform::setZoom(double zoom, const Duration& duration) { - setZoom(zoom, optional<ScreenCoordinate> {}, duration); +void Transform::setZoom(double zoom, const AnimationOptions& animation) { + setZoom(zoom, optional<ScreenCoordinate> {}, animation); } -void Transform::setZoom(double zoom, optional<ScreenCoordinate> anchor, const Duration& duration) { - setScale(state.zoomScale(zoom), anchor, duration); +void Transform::setZoom(double zoom, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { + setScale(state.zoomScale(zoom), anchor, animation); } -void Transform::setZoom(double zoom, optional<EdgeInsets> padding, const Duration& duration) { - setScale(state.zoomScale(zoom), padding, duration); +void Transform::setZoom(double zoom, optional<EdgeInsets> padding, const AnimationOptions& animation) { + setScale(state.zoomScale(zoom), padding, animation); } double Transform::getZoom() const { @@ -424,22 +424,22 @@ double Transform::getScale() const { return state.scale; } -void Transform::setScale(double scale, const Duration& duration) { - setScale(scale, optional<ScreenCoordinate> {}, duration); +void Transform::setScale(double scale, const AnimationOptions& animation) { + setScale(scale, optional<ScreenCoordinate> {}, animation); } -void Transform::setScale(double scale, optional<ScreenCoordinate> anchor, const Duration& duration) { +void Transform::setScale(double scale, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { if (std::isnan(scale)) return; CameraOptions camera; camera.zoom = state.scaleZoom(scale); camera.anchor = anchor; - easeTo(camera, duration); + easeTo(camera, animation); } -void Transform::setScale(double scale, optional<EdgeInsets> padding, const Duration& duration) { +void Transform::setScale(double scale, optional<EdgeInsets> padding, const AnimationOptions& animation) { optional<ScreenCoordinate> anchor; if (padding) anchor = getScreenCoordinate(padding); - setScale(scale, anchor, duration); + setScale(scale, anchor, animation); } void Transform::setMinZoom(const double minZoom) { @@ -454,7 +454,7 @@ void Transform::setMaxZoom(const double maxZoom) { #pragma mark - Angle -void Transform::rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const Duration& duration) { +void Transform::rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const AnimationOptions& animation) { ScreenCoordinate center = getScreenCoordinate(); const ScreenCoordinate offset = first - center; const double distance = std::sqrt(std::pow(2, offset.x) + std::pow(2, offset.y)); @@ -470,25 +470,25 @@ void Transform::rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& CameraOptions camera; camera.angle = state.angle + util::angle_between(first - center, second - center); - easeTo(camera, duration); + easeTo(camera, animation); } -void Transform::setAngle(double angle, const Duration& duration) { - setAngle(angle, optional<ScreenCoordinate> {}, duration); +void Transform::setAngle(double angle, const AnimationOptions& animation) { + setAngle(angle, optional<ScreenCoordinate> {}, animation); } -void Transform::setAngle(double angle, optional<ScreenCoordinate> anchor, const Duration& duration) { +void Transform::setAngle(double angle, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { if (std::isnan(angle)) return; CameraOptions camera; camera.angle = angle; camera.anchor = anchor; - easeTo(camera, duration); + easeTo(camera, animation); } -void Transform::setAngle(double angle, optional<EdgeInsets> padding, const Duration& duration) { +void Transform::setAngle(double angle, optional<EdgeInsets> padding, const AnimationOptions& animation) { optional<ScreenCoordinate> anchor; if (padding && *padding) anchor = getScreenCoordinate(padding); - setAngle(angle, anchor, duration); + setAngle(angle, anchor, animation); } double Transform::getAngle() const { @@ -497,16 +497,16 @@ double Transform::getAngle() const { #pragma mark - Pitch -void Transform::setPitch(double pitch, const Duration& duration) { - setPitch(pitch, optional<ScreenCoordinate> {}, duration); +void Transform::setPitch(double pitch, const AnimationOptions& animation) { + setPitch(pitch, optional<ScreenCoordinate> {}, animation); } -void Transform::setPitch(double pitch, optional<ScreenCoordinate> anchor, const Duration& duration) { +void Transform::setPitch(double pitch, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) { if (std::isnan(pitch)) return; CameraOptions camera; camera.pitch = pitch; camera.anchor = anchor; - easeTo(camera, duration); + easeTo(camera, animation); } double Transform::getPitch() const { diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp index febe71035d..66c9915715 100644 --- a/src/mbgl/map/transform.hpp +++ b/src/mbgl/map/transform.hpp @@ -44,12 +44,12 @@ public: /** Pans the map by the given amount. @param offset The distance to pan the map by, measured in pixels from top to bottom and from left to right. */ - void moveBy(const ScreenCoordinate& offset, const Duration& = Duration::zero()); - void setLatLng(const LatLng&, const Duration& = Duration::zero()); - void setLatLng(const LatLng&, optional<EdgeInsets>, const Duration& = Duration::zero()); - void setLatLng(const LatLng&, optional<ScreenCoordinate>, const Duration& = Duration::zero()); - void setLatLngZoom(const LatLng&, double zoom, const Duration& = Duration::zero()); - void setLatLngZoom(const LatLng&, double zoom, optional<EdgeInsets>, const Duration& = Duration::zero()); + void moveBy(const ScreenCoordinate& offset, const AnimationOptions& = {}); + void setLatLng(const LatLng&, const AnimationOptions& = {}); + void setLatLng(const LatLng&, optional<EdgeInsets>, const AnimationOptions& = {}); + void setLatLng(const LatLng&, optional<ScreenCoordinate>, const AnimationOptions& = {}); + void setLatLngZoom(const LatLng&, double zoom, const AnimationOptions& = {}); + void setLatLngZoom(const LatLng&, double zoom, optional<EdgeInsets>, const AnimationOptions& = {}); LatLng getLatLng(optional<EdgeInsets> = {}) const; ScreenCoordinate getScreenCoordinate(optional<EdgeInsets> = {}) const; @@ -57,36 +57,36 @@ public: /** Scales the map, keeping the given point fixed within the view. @param ds The difference in scale factors to scale the map by. */ - void scaleBy(double ds, const Duration& = Duration::zero()); + void scaleBy(double ds, const AnimationOptions& = {}); /** Scales the map, keeping the given point fixed within the view. @param ds The difference in scale factors to scale the map by. @param anchor A point relative to the top-left corner of the view. If unspecified, the center point is fixed within the view. */ - void scaleBy(double ds, optional<ScreenCoordinate> anchor, const Duration& = Duration::zero()); + void scaleBy(double ds, optional<ScreenCoordinate> anchor, const AnimationOptions& = {}); /** Sets the scale factor, keeping the given point fixed within the view. @param scale The new scale factor. */ - void setScale(double scale, const Duration& = Duration::zero()); + void setScale(double scale, const AnimationOptions& = {}); /** Sets the scale factor, keeping the given point fixed within the view. @param scale The new scale factor. @param anchor A point relative to the top-left corner of the view. If unspecified, the center point is fixed within the view. */ - void setScale(double scale, optional<ScreenCoordinate> anchor, const Duration& = Duration::zero()); + void setScale(double scale, optional<ScreenCoordinate> anchor, const AnimationOptions& = {}); /** Sets the scale factor, keeping the center point fixed within the inset view. @param scale The new scale factor. @param padding The viewport padding that affects the fixed center point. */ - void setScale(double scale, optional<EdgeInsets> padding, const Duration& = Duration::zero()); + void setScale(double scale, optional<EdgeInsets> padding, const AnimationOptions& = {}); /** Sets the zoom level, keeping the given point fixed within the view. @param zoom The new zoom level. */ - void setZoom(double zoom, const Duration& = Duration::zero()); + void setZoom(double zoom, const AnimationOptions& = {}); /** Sets the zoom level, keeping the given point fixed within the view. @param zoom The new zoom level. @param anchor A point relative to the top-left corner of the view. If unspecified, the center point is fixed within the view. */ - void setZoom(double zoom, optional<ScreenCoordinate> anchor, const Duration& = Duration::zero()); + void setZoom(double zoom, optional<ScreenCoordinate> anchor, const AnimationOptions& = {}); /** Sets the zoom level, keeping the center point fixed within the inset view. @param zoom The new zoom level. @param padding The viewport padding that affects the fixed center point. */ - void setZoom(double zoom, optional<EdgeInsets> padding, const Duration& = Duration::zero()); + void setZoom(double zoom, optional<EdgeInsets> padding, const AnimationOptions& = {}); /** Returns the zoom level. */ double getZoom() const; /** Returns the scale factor. */ @@ -97,21 +97,21 @@ public: // Angle - void rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const Duration& = Duration::zero()); + void rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const AnimationOptions& = {}); /** Sets the angle of rotation. @param angle The new angle of rotation, measured in radians counterclockwise from true north. */ - void setAngle(double angle, const Duration& = Duration::zero()); + void setAngle(double angle, const AnimationOptions& = {}); /** Sets the angle of rotation, keeping the given point fixed within the view. @param angle The new angle of rotation, measured in radians counterclockwise from true north. @param anchor A point relative to the top-left corner of the view. */ - void setAngle(double angle, optional<ScreenCoordinate> anchor, const Duration& = Duration::zero()); + void setAngle(double angle, optional<ScreenCoordinate> anchor, const AnimationOptions& = {}); /** Sets the angle of rotation, keeping the center point fixed within the inset view. @param angle The new angle of rotation, measured in radians counterclockwise from true north. @param padding The viewport padding that affects the fixed center point. */ - void setAngle(double angle, optional<EdgeInsets> padding, const Duration& = Duration::zero()); + void setAngle(double angle, optional<EdgeInsets> padding, const AnimationOptions& = {}); /** Returns the angle of rotation. @return The angle of rotation, measured in radians counterclockwise from true north. */ @@ -121,12 +121,12 @@ public: /** Sets the pitch angle. @param angle The new pitch angle, measured in radians toward the horizon. */ - void setPitch(double pitch, const Duration& = Duration::zero()); + void setPitch(double pitch, const AnimationOptions& = {}); /** Sets the pitch angle, keeping the given point fixed within the view. @param angle The new pitch angle, measured in radians toward the horizon. @param anchor A point relative to the top-left corner of the view. */ - void setPitch(double pitch, optional<ScreenCoordinate> anchor, const Duration& = Duration::zero()); + void setPitch(double pitch, optional<ScreenCoordinate> anchor, const AnimationOptions& = {}); double getPitch() const; // North Orientation diff --git a/src/mbgl/programs/attributes.hpp b/src/mbgl/programs/attributes.hpp index 38bbe89377..bb90f2c13c 100644 --- a/src/mbgl/programs/attributes.hpp +++ b/src/mbgl/programs/attributes.hpp @@ -1,23 +1,221 @@ #pragma once #include <mbgl/gl/attribute.hpp> +#include <mbgl/gl/uniform.hpp> +#include <mbgl/gl/normalization.hpp> #include <cstdint> namespace mbgl { namespace attributes { -// Attributes common to several shaders. +// Layout attributes MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_pos); -MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_offset); MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_extrude); MBGL_DEFINE_ATTRIBUTE(uint16_t, 2, a_texture_pos); template <std::size_t N> -struct a_data : gl::Attribute<a_data<N>, uint8_t, N> { - static constexpr auto name = "a_data"; +struct a_data : gl::Attribute<uint8_t, N> { + static auto name() { return "a_data"; } }; +template <std::size_t N> +struct a_offset : gl::Attribute<int16_t, N> { + static auto name() { return "a_offset"; } +}; + +// Paint attributes + +template <class Attr> +struct Min : Attr { + static auto name() { + static const std::string name = Attr::name() + std::string("_min"); + return name.c_str(); + } +}; + +template <class Attr> +struct Max : Attr { + static auto name() { + static const std::string name = Attr::name() + std::string("_max"); + return name.c_str(); + } +}; + +template <class Attr> +struct InterpolationUniform : gl::UniformScalar<InterpolationUniform<Attr>, float> { + static auto name() { + static const std::string name = Attr::name() + std::string("_t"); + return name.c_str(); + } +}; + +struct a_color : gl::Attribute<gl::Normalized<uint8_t>, 4> { + static auto name() { return "a_color"; } + + static Value value(const Color& color) { + return {{ + gl::Normalized<uint8_t>(color.r), + gl::Normalized<uint8_t>(color.g), + gl::Normalized<uint8_t>(color.b), + gl::Normalized<uint8_t>(color.a) + }}; + } +}; + +// used in the symbol sdf shader +struct a_fill_color : gl::Attribute<gl::Normalized<uint8_t>, 4> { + static auto name() { return "a_fill_color"; } + + static Value value(const Color& color) { + return {{ + gl::Normalized<uint8_t>(color.r), + gl::Normalized<uint8_t>(color.g), + gl::Normalized<uint8_t>(color.b), + gl::Normalized<uint8_t>(color.a) + }}; + } +}; + +// used in the symbol sdf shader +struct a_halo_color : gl::Attribute<gl::Normalized<uint8_t>, 4> { + static auto name() { return "a_halo_color"; } + + static Value value(const Color& color) { + return {{ + gl::Normalized<uint8_t>(color.r), + gl::Normalized<uint8_t>(color.g), + gl::Normalized<uint8_t>(color.b), + gl::Normalized<uint8_t>(color.a) + }}; + } +}; + +struct a_stroke_color : gl::Attribute<gl::Normalized<uint8_t>, 4> { + static auto name() { return "a_stroke_color"; } + + static Value value(const Color& color) { + return {{ + gl::Normalized<uint8_t>(color.r), + gl::Normalized<uint8_t>(color.g), + gl::Normalized<uint8_t>(color.b), + gl::Normalized<uint8_t>(color.a) + }}; + } +}; + +struct a_outline_color : gl::Attribute<gl::Normalized<uint8_t>, 4> { + static auto name() { return "a_outline_color"; } + + static Value value(const Color& color) { + return {{ + gl::Normalized<uint8_t>(color.r), + gl::Normalized<uint8_t>(color.g), + gl::Normalized<uint8_t>(color.b), + gl::Normalized<uint8_t>(color.a) + }}; + } +}; + +struct a_opacity : gl::Attribute<gl::Normalized<uint8_t>, 1> { + static auto name() { return "a_opacity"; } + + static Value value(float opacity) { + return {{ gl::Normalized<uint8_t>(opacity) }}; + } +}; + +struct a_stroke_opacity : gl::Attribute<gl::Normalized<uint8_t>, 1> { + static auto name() { return "a_stroke_opacity"; } + + static Value value(float opacity) { + return {{ gl::Normalized<uint8_t>(opacity) }}; + } +}; + +struct a_blur : gl::Attribute<float, 1> { + static auto name() { return "a_blur"; } + + static Value value(float blur) { + return {{ blur }}; + } +}; + +struct a_radius : gl::Attribute<float, 1> { + static auto name() { return "a_radius"; } + + static Value value(float radius) { + return {{ radius }}; + } +}; + +struct a_width : gl::Attribute<float, 1> { + static auto name() { return "a_width"; } + + static Value value(float width) { + return {{ width }}; + } +}; + +struct a_height : gl::Attribute<float, 1> { + static auto name() { return "a_height"; } + + static Value value(float width) { + return {{ width }}; + } +}; + +struct a_base : gl::Attribute<float, 1> { + static auto name() { return "a_base"; } + + static Value value(float width) { + return {{ width }}; + } +}; + +struct a_gap_width : gl::Attribute<float, 1> { + static auto name() { return "a_gapwidth"; } + + static Value value(float width) { + return {{ width }}; + } +}; + +struct a_stroke_width : gl::Attribute<float, 1> { + static auto name() { return "a_stroke_width"; } + + static Value value(float width) { + return {{ width }}; + } +}; + +template <> +struct a_offset<1> : gl::Attribute<float, 1> { + static auto name() { return "a_offset"; } + + static Value value(float offset) { + return {{ offset }}; + } +}; + +struct a_halo_width : gl::Attribute<float, 1> { + static auto name() { return "a_halo_width"; } + + static Value value(float width) { + return {{ width }}; + } +}; + +struct a_halo_blur : gl::Attribute<float, 1> { + static auto name() { return "a_halo_blur"; } + + static Value value(float blur) { + return {{ blur }}; + } +}; + + + } // namespace attributes } // namespace mbgl diff --git a/src/mbgl/programs/circle_program.cpp b/src/mbgl/programs/circle_program.cpp index d6bc439feb..99b47dd5c0 100644 --- a/src/mbgl/programs/circle_program.cpp +++ b/src/mbgl/programs/circle_program.cpp @@ -2,6 +2,6 @@ namespace mbgl { -static_assert(sizeof(CircleProgram::Vertex) == 4, "expected CircleVertex size"); +static_assert(sizeof(CircleLayoutVertex) == 4, "expected CircleLayoutVertex size"); } // namespace mbgl diff --git a/src/mbgl/programs/circle_program.hpp b/src/mbgl/programs/circle_program.hpp index c9aea1d137..8f056048b1 100644 --- a/src/mbgl/programs/circle_program.hpp +++ b/src/mbgl/programs/circle_program.hpp @@ -3,37 +3,26 @@ #include <mbgl/programs/program.hpp> #include <mbgl/programs/attributes.hpp> #include <mbgl/programs/uniforms.hpp> -#include <mbgl/shader/circle.hpp> +#include <mbgl/shaders/circle.hpp> #include <mbgl/util/geometry.hpp> +#include <mbgl/style/layers/circle_layer_properties.hpp> namespace mbgl { namespace uniforms { -MBGL_DEFINE_UNIFORM_SCALAR(float, u_radius); -MBGL_DEFINE_UNIFORM_SCALAR(Color, u_stroke_color); -MBGL_DEFINE_UNIFORM_SCALAR(float, u_stroke_width); -MBGL_DEFINE_UNIFORM_SCALAR(float, u_stroke_opacity); MBGL_DEFINE_UNIFORM_SCALAR(bool, u_scale_with_map); } // namespace uniforms -using CircleAttributes = gl::Attributes< - attributes::a_pos>; - class CircleProgram : public Program< shaders::circle, gl::Triangle, - CircleAttributes, + gl::Attributes< + attributes::a_pos>, gl::Uniforms< uniforms::u_matrix, - uniforms::u_opacity, - uniforms::u_color, - uniforms::u_radius, - uniforms::u_blur, - uniforms::u_stroke_color, - uniforms::u_stroke_width, - uniforms::u_stroke_opacity, uniforms::u_scale_with_map, - uniforms::u_extrude_scale>> + uniforms::u_extrude_scale>, + style::CirclePaintProperties> { public: using Program::Program; @@ -44,16 +33,17 @@ public: * @param {number} ex extrude normal * @param {number} ey extrude normal */ - static Vertex vertex(Point<int16_t> p, float ex, float ey) { - return Vertex { - { + static LayoutVertex vertex(Point<int16_t> p, float ex, float ey) { + return LayoutVertex { + {{ static_cast<int16_t>((p.x * 2) + ((ex + 1) / 2)), static_cast<int16_t>((p.y * 2) + ((ey + 1) / 2)) - } + }} }; } }; -using CircleVertex = CircleProgram::Vertex; +using CircleLayoutVertex = CircleProgram::LayoutVertex; +using CircleAttributes = CircleProgram::Attributes; } // namespace mbgl diff --git a/src/mbgl/programs/collision_box_program.cpp b/src/mbgl/programs/collision_box_program.cpp index d6a36e54a1..a3dc01ebe4 100644 --- a/src/mbgl/programs/collision_box_program.cpp +++ b/src/mbgl/programs/collision_box_program.cpp @@ -2,6 +2,6 @@ namespace mbgl { -static_assert(sizeof(CollisionBoxProgram::Vertex) == 10, "expected CollisionBoxVertex size"); +static_assert(sizeof(CollisionBoxProgram::LayoutVertex) == 10, "expected CollisionBoxVertex size"); } // namespace mbgl diff --git a/src/mbgl/programs/collision_box_program.hpp b/src/mbgl/programs/collision_box_program.hpp index 26e38419a4..78ed6aa0c9 100644 --- a/src/mbgl/programs/collision_box_program.hpp +++ b/src/mbgl/programs/collision_box_program.hpp @@ -3,7 +3,7 @@ #include <mbgl/programs/program.hpp> #include <mbgl/programs/attributes.hpp> #include <mbgl/programs/uniforms.hpp> -#include <mbgl/shader/collision_box.hpp> +#include <mbgl/shaders/collision_box.hpp> #include <mbgl/util/geometry.hpp> #include <cmath> @@ -28,29 +28,30 @@ class CollisionBoxProgram : public Program< uniforms::u_matrix, uniforms::u_scale, uniforms::u_zoom, - uniforms::u_maxzoom>> + uniforms::u_maxzoom>, + style::PaintProperties<>> { public: using Program::Program; - static Vertex vertex(Point<float> a, Point<float> o, float maxzoom, float placementZoom) { - return Vertex { - { + static LayoutVertex vertex(Point<float> a, Point<float> o, float maxzoom, float placementZoom) { + return LayoutVertex { + {{ static_cast<int16_t>(a.x), static_cast<int16_t>(a.y) - }, - { + }}, + {{ static_cast<int16_t>(::round(o.x)), static_cast<int16_t>(::round(o.y)) - }, - { + }}, + {{ static_cast<uint8_t>(maxzoom * 10), static_cast<uint8_t>(placementZoom * 10) - } + }} }; } }; -using CollisionBoxVertex = CollisionBoxProgram::Vertex; +using CollisionBoxVertex = CollisionBoxProgram::LayoutVertex; } // namespace mbgl diff --git a/src/mbgl/programs/debug_program.hpp b/src/mbgl/programs/debug_program.hpp index cd4e08b1bc..de1666b4a8 100644 --- a/src/mbgl/programs/debug_program.hpp +++ b/src/mbgl/programs/debug_program.hpp @@ -3,25 +3,25 @@ #include <mbgl/programs/program.hpp> #include <mbgl/programs/attributes.hpp> #include <mbgl/programs/uniforms.hpp> -#include <mbgl/shader/debug.hpp> +#include <mbgl/shaders/debug.hpp> namespace mbgl { -using DebugAttributes = gl::Attributes< - attributes::a_pos>; - class DebugProgram : public Program< shaders::debug, gl::Line, - DebugAttributes, + gl::Attributes< + attributes::a_pos>, gl::Uniforms< uniforms::u_matrix, - uniforms::u_color>> + uniforms::u_color>, + style::PaintProperties<>> { public: using Program::Program; }; -using DebugVertex = DebugProgram::Vertex; +using DebugLayoutVertex = DebugProgram::LayoutVertex; +using DebugAttributes = DebugProgram::Attributes; } // namespace mbgl diff --git a/src/mbgl/programs/fill_program.cpp b/src/mbgl/programs/fill_program.cpp index a8154d08f9..eebcffd2cb 100644 --- a/src/mbgl/programs/fill_program.cpp +++ b/src/mbgl/programs/fill_program.cpp @@ -8,14 +8,13 @@ namespace mbgl { using namespace style; -static_assert(sizeof(FillAttributes::Vertex) == 4, "expected FillVertex size"); +static_assert(sizeof(FillLayoutVertex) == 4, "expected FillLayoutVertex size"); FillPatternUniforms::Values FillPatternUniforms::values(mat4 matrix, - float opacity, Size framebufferSize, - const SpriteAtlasPosition& a, - const SpriteAtlasPosition& b, + const SpriteAtlasElement& a, + const SpriteAtlasElement& b, const Faded<std::string>& fading, const UnwrappedTileID& tileID, const TransformState& state) @@ -26,7 +25,6 @@ FillPatternUniforms::values(mat4 matrix, return FillPatternUniforms::Values { uniforms::u_matrix::Value{ matrix }, - uniforms::u_opacity::Value{ opacity }, uniforms::u_world::Value{ framebufferSize }, uniforms::u_pattern_tl_a::Value{ a.tl }, uniforms::u_pattern_br_a::Value{ a.br }, diff --git a/src/mbgl/programs/fill_program.hpp b/src/mbgl/programs/fill_program.hpp index d885215c59..84ca2748d6 100644 --- a/src/mbgl/programs/fill_program.hpp +++ b/src/mbgl/programs/fill_program.hpp @@ -3,19 +3,20 @@ #include <mbgl/programs/program.hpp> #include <mbgl/programs/attributes.hpp> #include <mbgl/programs/uniforms.hpp> -#include <mbgl/shader/fill.hpp> -#include <mbgl/shader/fill_pattern.hpp> -#include <mbgl/shader/fill_outline.hpp> -#include <mbgl/shader/fill_outline_pattern.hpp> +#include <mbgl/shaders/fill.hpp> +#include <mbgl/shaders/fill_pattern.hpp> +#include <mbgl/shaders/fill_outline.hpp> +#include <mbgl/shaders/fill_outline_pattern.hpp> #include <mbgl/util/geometry.hpp> #include <mbgl/util/mat4.hpp> #include <mbgl/util/size.hpp> +#include <mbgl/style/layers/fill_layer_properties.hpp> #include <string> namespace mbgl { -class SpriteAtlasPosition; +class SpriteAtlasElement; class UnwrappedTileID; class TransformState; @@ -25,7 +26,6 @@ template <class> class Faded; namespace uniforms { MBGL_DEFINE_UNIFORM_SCALAR(Size, u_world); -MBGL_DEFINE_UNIFORM_SCALAR(Color, u_outline_color); MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale_a); MBGL_DEFINE_UNIFORM_SCALAR(float, u_scale_b); MBGL_DEFINE_UNIFORM_SCALAR(float, u_tile_units_to_pixels); @@ -33,32 +33,17 @@ MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pixel_coord_upper); MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pixel_coord_lower); } // namespace uniforms -struct FillAttributes : gl::Attributes< +struct FillLayoutAttributes : gl::Attributes< attributes::a_pos> -{ - static Vertex vertex(Point<int16_t> p) { - return Vertex { - { - p.x, - p.y - } - }; - } -}; - -using FillVertex = FillAttributes::Vertex; +{}; struct FillUniforms : gl::Uniforms< uniforms::u_matrix, - uniforms::u_opacity, - uniforms::u_color, - uniforms::u_outline_color, uniforms::u_world> {}; struct FillPatternUniforms : gl::Uniforms< uniforms::u_matrix, - uniforms::u_opacity, uniforms::u_world, uniforms::u_pattern_tl_a, uniforms::u_pattern_br_a, @@ -75,10 +60,9 @@ struct FillPatternUniforms : gl::Uniforms< uniforms::u_tile_units_to_pixels> { static Values values(mat4 matrix, - float opacity, Size framebufferSize, - const SpriteAtlasPosition&, - const SpriteAtlasPosition&, + const SpriteAtlasElement&, + const SpriteAtlasElement&, const style::Faded<std::string>&, const UnwrappedTileID&, const TransformState&); @@ -87,37 +71,57 @@ struct FillPatternUniforms : gl::Uniforms< class FillProgram : public Program< shaders::fill, gl::Triangle, - FillAttributes, - FillUniforms> + FillLayoutAttributes, + FillUniforms, + style::FillPaintProperties> { +public: using Program::Program; + + static LayoutVertex layoutVertex(Point<int16_t> p) { + return LayoutVertex { + {{ + p.x, + p.y + }} + }; + } }; class FillPatternProgram : public Program< shaders::fill_pattern, gl::Triangle, - FillAttributes, - FillPatternUniforms> + FillLayoutAttributes, + FillPatternUniforms, + style::FillPaintProperties> { +public: using Program::Program; }; class FillOutlineProgram : public Program< shaders::fill_outline, gl::Line, - FillAttributes, - FillUniforms> + FillLayoutAttributes, + FillUniforms, + style::FillPaintProperties> { +public: using Program::Program; }; class FillOutlinePatternProgram : public Program< shaders::fill_outline_pattern, gl::Line, - FillAttributes, - FillPatternUniforms> + FillLayoutAttributes, + FillPatternUniforms, + style::FillPaintProperties> { +public: using Program::Program; }; +using FillLayoutVertex = FillProgram::LayoutVertex; +using FillAttributes = FillProgram::Attributes; + } // namespace mbgl diff --git a/src/mbgl/programs/line_program.cpp b/src/mbgl/programs/line_program.cpp index f7054d3398..d9778ba7ce 100644 --- a/src/mbgl/programs/line_program.cpp +++ b/src/mbgl/programs/line_program.cpp @@ -10,7 +10,7 @@ namespace mbgl { using namespace style; -static_assert(sizeof(LineAttributes::Vertex) == 8, "expected LineVertex size"); +static_assert(sizeof(LineLayoutVertex) == 8, "expected LineLayoutVertex size"); template <class Values, class...Args> Values makeValues(const LinePaintProperties::Evaluated& properties, @@ -25,11 +25,7 @@ Values makeValues(const LinePaintProperties::Evaluated& properties, properties.get<LineTranslateAnchor>(), state) }, - uniforms::u_opacity::Value{ properties.get<LineOpacity>() }, uniforms::u_width::Value{ properties.get<LineWidth>() }, - uniforms::u_gapwidth::Value{ properties.get<LineGapWidth>() }, - uniforms::u_blur::Value{ properties.get<LineBlur>() }, - uniforms::u_offset::Value{ properties.get<LineOffset>() }, uniforms::u_ratio::Value{ 1.0f / tile.id.pixelsToTileUnits(1.0, state.getZoom()) }, uniforms::u_gl_units_to_pixels::Value{{{ 1.0f / pixelsToGLUnits[0], 1.0f / pixelsToGLUnits[1] }}}, std::forward<Args>(args)... @@ -45,8 +41,7 @@ LineProgram::uniformValues(const LinePaintProperties::Evaluated& properties, properties, tile, state, - pixelsToGLUnits, - uniforms::u_color::Value{ properties.get<LineColor>() } + pixelsToGLUnits ); } @@ -78,7 +73,6 @@ LineSDFProgram::uniformValues(const LinePaintProperties::Evaluated& properties, tile, state, pixelsToGLUnits, - uniforms::u_color::Value{ properties.get<LineColor>() }, uniforms::u_patternscale_a::Value{ scaleA }, uniforms::u_patternscale_b::Value{ scaleB }, uniforms::u_tex_y_a::Value{ posA.y }, @@ -94,8 +88,8 @@ LinePatternProgram::uniformValues(const LinePaintProperties::Evaluated& properti const RenderTile& tile, const TransformState& state, const std::array<float, 2>& pixelsToGLUnits, - const SpriteAtlasPosition& posA, - const SpriteAtlasPosition& posB) { + const SpriteAtlasElement& posA, + const SpriteAtlasElement& posB) { std::array<float, 2> sizeA {{ tile.id.pixelsToTileUnits(posA.size[0] * properties.get<LinePattern>().fromScale, state.getIntegerZoom()), posA.size[1] diff --git a/src/mbgl/programs/line_program.hpp b/src/mbgl/programs/line_program.hpp index 9b97cc47a9..842b4cc602 100644 --- a/src/mbgl/programs/line_program.hpp +++ b/src/mbgl/programs/line_program.hpp @@ -3,11 +3,11 @@ #include <mbgl/programs/program.hpp> #include <mbgl/programs/attributes.hpp> #include <mbgl/programs/uniforms.hpp> -#include <mbgl/shader/line.hpp> -#include <mbgl/shader/line_pattern.hpp> -#include <mbgl/shader/line_sdf.hpp> -#include <mbgl/style/layers/line_layer_properties.hpp> +#include <mbgl/shaders/line.hpp> +#include <mbgl/shaders/line_pattern.hpp> +#include <mbgl/shaders/line_sdf.hpp> #include <mbgl/util/geometry.hpp> +#include <mbgl/style/layers/line_layer_properties.hpp> #include <cmath> @@ -16,13 +16,11 @@ namespace mbgl { class RenderTile; class TransformState; class LinePatternPos; -class SpriteAtlasPosition; +class SpriteAtlasElement; namespace uniforms { MBGL_DEFINE_UNIFORM_SCALAR(float, u_ratio); MBGL_DEFINE_UNIFORM_SCALAR(float, u_width); -MBGL_DEFINE_UNIFORM_SCALAR(float, u_gapwidth); -MBGL_DEFINE_UNIFORM_SCALAR(float, u_offset); MBGL_DEFINE_UNIFORM_SCALAR(float, u_tex_y_a); MBGL_DEFINE_UNIFORM_SCALAR(float, u_tex_y_b); MBGL_DEFINE_UNIFORM_SCALAR(float, u_sdfgamma); @@ -32,24 +30,39 @@ MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_patternscale_b); MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_gl_units_to_pixels); } // namespace uniforms -struct LineAttributes : gl::Attributes< +struct LineLayoutAttributes : gl::Attributes< attributes::a_pos, attributes::a_data<4>> +{}; + +class LineProgram : public Program< + shaders::line, + gl::Triangle, + LineLayoutAttributes, + gl::Uniforms< + uniforms::u_matrix, + uniforms::u_width, + uniforms::u_ratio, + uniforms::u_gl_units_to_pixels>, + style::LinePaintProperties> { +public: + using Program::Program; + /* * @param p vertex position * @param e extrude normal * @param t texture normal * @param dir direction of the line cap (-1/0/1) */ - static Vertex vertex(Point<int16_t> p, Point<double> e, Point<bool> t, int8_t dir, int32_t linesofar = 0) { - return Vertex { - { + static LayoutVertex layoutVertex(Point<int16_t> p, Point<double> e, Point<bool> t, int8_t dir, int32_t linesofar = 0) { + return LayoutVertex { + {{ static_cast<int16_t>((p.x * 2) | t.x), static_cast<int16_t>((p.y * 2) | t.y) - }, - { - // add 128 to store an byte in an unsigned byte + }}, + {{ + // add 128 to store a byte in an unsigned byte static_cast<uint8_t>(::round(extrudeScale * e.x) + 128), static_cast<uint8_t>(::round(extrudeScale * e.y) + 128), @@ -65,7 +78,7 @@ struct LineAttributes : gl::Attributes< // so we need to shift the linesofar. static_cast<uint8_t>(((dir == 0 ? 0 : (dir < 0 ? -1 : 1 )) + 1) | ((linesofar & 0x3F) << 2)), static_cast<uint8_t>(linesofar >> 6) - } + }} }; } @@ -77,27 +90,6 @@ struct LineAttributes : gl::Attributes< * the acute/bevelled line join. */ static const int8_t extrudeScale = 63; -}; - -using LineVertex = LineAttributes::Vertex; - -class LineProgram : public Program< - shaders::line, - gl::Triangle, - LineAttributes, - gl::Uniforms< - uniforms::u_matrix, - uniforms::u_opacity, - uniforms::u_width, - uniforms::u_gapwidth, - uniforms::u_blur, - uniforms::u_offset, - uniforms::u_ratio, - uniforms::u_gl_units_to_pixels, - uniforms::u_color>> -{ -public: - using Program::Program; static UniformValues uniformValues(const style::LinePaintProperties::Evaluated&, const RenderTile&, @@ -108,14 +100,10 @@ public: class LinePatternProgram : public Program< shaders::line_pattern, gl::Triangle, - LineAttributes, + LineLayoutAttributes, gl::Uniforms< uniforms::u_matrix, - uniforms::u_opacity, uniforms::u_width, - uniforms::u_gapwidth, - uniforms::u_blur, - uniforms::u_offset, uniforms::u_ratio, uniforms::u_gl_units_to_pixels, uniforms::u_pattern_tl_a, @@ -125,7 +113,8 @@ class LinePatternProgram : public Program< uniforms::u_pattern_size_a, uniforms::u_pattern_size_b, uniforms::u_fade, - uniforms::u_image>> + uniforms::u_image>, + style::LinePaintProperties> { public: using Program::Program; @@ -134,31 +123,27 @@ public: const RenderTile&, const TransformState&, const std::array<float, 2>& pixelsToGLUnits, - const SpriteAtlasPosition& posA, - const SpriteAtlasPosition& posB); + const SpriteAtlasElement& posA, + const SpriteAtlasElement& posB); }; class LineSDFProgram : public Program< shaders::line_sdf, gl::Triangle, - LineAttributes, + LineLayoutAttributes, gl::Uniforms< uniforms::u_matrix, - uniforms::u_opacity, uniforms::u_width, - uniforms::u_gapwidth, - uniforms::u_blur, - uniforms::u_offset, uniforms::u_ratio, uniforms::u_gl_units_to_pixels, - uniforms::u_color, uniforms::u_patternscale_a, uniforms::u_patternscale_b, uniforms::u_tex_y_a, uniforms::u_tex_y_b, uniforms::u_mix, uniforms::u_sdfgamma, - uniforms::u_image>> + uniforms::u_image>, + style::LinePaintProperties> { public: using Program::Program; @@ -174,4 +159,7 @@ public: float atlasWidth); }; +using LineLayoutVertex = LineProgram::LayoutVertex; +using LineAttributes = LineProgram::Attributes; + } // namespace mbgl diff --git a/src/mbgl/programs/program.hpp b/src/mbgl/programs/program.hpp index e5aae24997..e75dbebf18 100644 --- a/src/mbgl/programs/program.hpp +++ b/src/mbgl/programs/program.hpp @@ -2,21 +2,40 @@ #include <mbgl/gl/program.hpp> #include <mbgl/programs/program_parameters.hpp> +#include <mbgl/programs/attributes.hpp> +#include <mbgl/style/paint_property.hpp> #include <sstream> #include <cassert> namespace mbgl { -template <class Shaders, class Primitive, class Attributes, class Uniforms> -class Program : public gl::Program<Primitive, Attributes, Uniforms> { +template <class Shaders, + class Primitive, + class LayoutAttrs, + class Uniforms, + class PaintProperties> +class Program { public: - using ParentType = gl::Program<Primitive, Attributes, Uniforms>; + using LayoutAttributes = LayoutAttrs; + using LayoutVertex = typename LayoutAttributes::Vertex; + + using PaintPropertyBinders = typename PaintProperties::Binders; + using PaintAttributes = typename PaintPropertyBinders::Attributes; + using Attributes = gl::ConcatenateAttributes<LayoutAttributes, PaintAttributes>; + + using UniformValues = typename Uniforms::Values; + using PaintUniforms = typename PaintPropertyBinders::Uniforms; + using AllUniforms = gl::ConcatenateUniforms<Uniforms, PaintUniforms>; + + using ProgramType = gl::Program<Primitive, Attributes, AllUniforms>; + + ProgramType program; Program(gl::Context& context, const ProgramParameters& programParameters) - : ParentType(context, vertexSource(programParameters), fragmentSource(programParameters)) + : program(context, vertexSource(programParameters), fragmentSource(programParameters)) {} - + static std::string pixelRatioDefine(const ProgramParameters& parameters) { std::ostringstream pixelRatioSS; pixelRatioSS.imbue(std::locale("C")); @@ -38,6 +57,33 @@ public: return pixelRatioDefine(parameters) + Shaders::vertexSource; } + template <class DrawMode> + void draw(gl::Context& context, + DrawMode drawMode, + gl::DepthMode depthMode, + gl::StencilMode stencilMode, + gl::ColorMode colorMode, + UniformValues&& uniformValues, + const gl::VertexBuffer<LayoutVertex>& layoutVertexBuffer, + const gl::IndexBuffer<DrawMode>& indexBuffer, + const gl::SegmentVector<Attributes>& segments, + const PaintPropertyBinders& paintPropertyBinders, + const typename PaintProperties::Evaluated& currentProperties, + float currentZoom) { + program.draw( + context, + std::move(drawMode), + std::move(depthMode), + std::move(stencilMode), + std::move(colorMode), + uniformValues + .concat(paintPropertyBinders.uniformValues(currentZoom)), + LayoutAttributes::allVariableBindings(layoutVertexBuffer) + .concat(paintPropertyBinders.attributeBindings(currentProperties)), + indexBuffer, + segments + ); + } }; } // namespace mbgl diff --git a/src/mbgl/programs/programs.hpp b/src/mbgl/programs/programs.hpp index dd71c2ce97..742c5a221b 100644 --- a/src/mbgl/programs/programs.hpp +++ b/src/mbgl/programs/programs.hpp @@ -40,8 +40,8 @@ public: LinePatternProgram linePattern; RasterProgram raster; SymbolIconProgram symbolIcon; - SymbolSDFProgram symbolIconSDF; - SymbolSDFProgram symbolGlyph; + SymbolSDFIconProgram symbolIconSDF; + SymbolSDFTextProgram symbolGlyph; DebugProgram debug; CollisionBoxProgram collisionBox; diff --git a/src/mbgl/programs/raster_program.cpp b/src/mbgl/programs/raster_program.cpp index ebec4c68cc..6906903e6b 100644 --- a/src/mbgl/programs/raster_program.cpp +++ b/src/mbgl/programs/raster_program.cpp @@ -2,6 +2,6 @@ namespace mbgl { -static_assert(sizeof(RasterProgram::Vertex) == 8, "expected RasterVertex size"); +static_assert(sizeof(RasterLayoutVertex) == 8, "expected RasterLayoutVertex size"); } // namespace mbgl diff --git a/src/mbgl/programs/raster_program.hpp b/src/mbgl/programs/raster_program.hpp index 9aa25cf90c..09cb94ac17 100644 --- a/src/mbgl/programs/raster_program.hpp +++ b/src/mbgl/programs/raster_program.hpp @@ -3,8 +3,9 @@ #include <mbgl/programs/program.hpp> #include <mbgl/programs/attributes.hpp> #include <mbgl/programs/uniforms.hpp> -#include <mbgl/shader/raster.hpp> +#include <mbgl/shaders/raster.hpp> #include <mbgl/util/geometry.hpp> +#include <mbgl/style/layers/raster_layer_properties.hpp> namespace mbgl { @@ -22,14 +23,12 @@ MBGL_DEFINE_UNIFORM_VECTOR(float, 3, u_spin_weights); MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_tl_parent); } // namespace uniforms -using RasterAttributes = gl::Attributes< - attributes::a_pos, - attributes::a_texture_pos>; - class RasterProgram : public Program< shaders::raster, gl::Triangle, - RasterAttributes, + gl::Attributes< + attributes::a_pos, + attributes::a_texture_pos>, gl::Uniforms< uniforms::u_matrix, uniforms::u_image0, @@ -43,25 +42,27 @@ class RasterProgram : public Program< uniforms::u_spin_weights, uniforms::u_buffer_scale, uniforms::u_scale_parent, - uniforms::u_tl_parent>> + uniforms::u_tl_parent>, + style::RasterPaintProperties> { public: using Program::Program; - static Vertex vertex(Point<int16_t> p, Point<uint16_t> t) { - return Vertex { - { + static LayoutVertex layoutVertex(Point<int16_t> p, Point<uint16_t> t) { + return LayoutVertex { + {{ p.x, p.y - }, - { + }}, + {{ t.x, t.y - } + }} }; } }; -using RasterVertex = RasterProgram::Vertex; +using RasterLayoutVertex = RasterProgram::LayoutVertex; +using RasterAttributes = RasterProgram::Attributes; } // namespace mbgl diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp index d609dada8d..19fe2bc2f6 100644 --- a/src/mbgl/programs/symbol_program.cpp +++ b/src/mbgl/programs/symbol_program.cpp @@ -2,12 +2,13 @@ #include <mbgl/renderer/render_tile.hpp> #include <mbgl/map/transform_state.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> +#include <mbgl/util/enum.hpp> namespace mbgl { using namespace style; -static_assert(sizeof(SymbolAttributes::Vertex) == 16, "expected SymbolVertex size"); +static_assert(sizeof(SymbolLayoutVertex) == 16, "expected SymbolLayoutVertex size"); template <class Values, class...Args> Values makeValues(const style::SymbolPropertyValues& values, @@ -19,6 +20,7 @@ Values makeValues(const style::SymbolPropertyValues& values, std::array<float, 2> extrudeScale; const float scale = values.paintSize / values.sdfScale; + if (values.pitchAlignment == AlignmentType::Map) { extrudeScale.fill(tile.id.pixelsToTileUnits(1, state.getZoom()) * scale); } else { @@ -27,7 +29,7 @@ Values makeValues(const style::SymbolPropertyValues& values, pixelsToGLUnits[1] * scale * state.getCameraToCenterDistance() }}; } - + // adjust min/max zooms for variable font sies float zoomAdjust = std::log(values.paintSize / values.layoutSize) / std::log(2); @@ -35,7 +37,6 @@ Values makeValues(const style::SymbolPropertyValues& values, uniforms::u_matrix::Value{ tile.translatedMatrix(values.translate, values.translateAnchor, state) }, - uniforms::u_opacity::Value{ values.opacity }, uniforms::u_extrude_scale::Value{ extrudeScale }, uniforms::u_texsize::Value{ std::array<float, 2> {{ float(texsize.width) / 4, float(texsize.height) / 4 }} }, uniforms::u_zoom::Value{ float((state.getZoom() - zoomAdjust) * 10) }, @@ -62,84 +63,37 @@ SymbolIconProgram::uniformValues(const style::SymbolPropertyValues& values, ); } -static SymbolSDFProgram::UniformValues makeSDFValues(const style::SymbolPropertyValues& values, - const Size& texsize, - const std::array<float, 2>& pixelsToGLUnits, - const RenderTile& tile, - const TransformState& state, - float pixelRatio, - Color color, - float buffer, - float gammaAdjust) -{ - // The default gamma value has to be adjust for the current pixelratio so that we're not - // drawing blurry font on retina screens. - const float gammaBase = 0.105 * values.sdfScale / values.paintSize / pixelRatio; - const float gammaScale = (values.pitchAlignment == AlignmentType::Map - ? 1.0 / std::cos(state.getPitch()) - : 1.0) / state.getCameraToCenterDistance(); - - return makeValues<SymbolSDFProgram::UniformValues>( - values, - texsize, - pixelsToGLUnits, - tile, - state, - uniforms::u_color::Value{ color }, - uniforms::u_buffer::Value{ buffer }, - uniforms::u_gamma::Value{ (gammaBase + gammaAdjust) * gammaScale }, - uniforms::u_pitch::Value{ state.getPitch() }, - uniforms::u_bearing::Value{ -1.0f * state.getAngle() }, - uniforms::u_aspect_ratio::Value{ (state.getSize().width * 1.0f) / (state.getSize().height * 1.0f) }, - uniforms::u_pitch_with_map::Value{ values.pitchAlignment == AlignmentType::Map } - ); -} - -SymbolSDFProgram::UniformValues -SymbolSDFProgram::haloUniformValues(const style::SymbolPropertyValues& values, +template <class PaintProperties> +typename SymbolSDFProgram<PaintProperties>::UniformValues SymbolSDFProgram<PaintProperties>::uniformValues(const style::SymbolPropertyValues& values, const Size& texsize, const std::array<float, 2>& pixelsToGLUnits, const RenderTile& tile, const TransformState& state, - float pixelRatio) + const SymbolSDFPart part) { const float scale = values.paintSize / values.sdfScale; - const float sdfPx = 8.0f; - const float blurOffset = 1.19f; - const float haloOffset = 6.0f; - - return makeSDFValues( + + const float gammaScale = scale * (values.pitchAlignment == AlignmentType::Map + ? std::cos(state.getPitch()) + : 1.0) * state.getCameraToCenterDistance(); + + return makeValues<SymbolSDFProgram<PaintProperties>::UniformValues>( values, texsize, pixelsToGLUnits, tile, state, - pixelRatio, - values.haloColor, - (haloOffset - values.haloWidth / scale) / sdfPx, - values.haloBlur * blurOffset / scale / sdfPx + uniforms::u_font_scale::Value{ scale }, + uniforms::u_gamma_scale::Value{ gammaScale }, + uniforms::u_pitch::Value{ state.getPitch() }, + uniforms::u_bearing::Value{ -1.0f * state.getAngle() }, + uniforms::u_aspect_ratio::Value{ (state.getSize().width * 1.0f) / (state.getSize().height * 1.0f) }, + uniforms::u_pitch_with_map::Value{ values.pitchAlignment == AlignmentType::Map }, + uniforms::u_is_halo::Value{ part == SymbolSDFPart::Halo } ); } -SymbolSDFProgram::UniformValues -SymbolSDFProgram::foregroundUniformValues(const style::SymbolPropertyValues& values, - const Size& texsize, - const std::array<float, 2>& pixelsToGLUnits, - const RenderTile& tile, - const TransformState& state, - float pixelRatio) -{ - return makeSDFValues( - values, - texsize, - pixelsToGLUnits, - tile, - state, - pixelRatio, - values.color, - (256.0f - 64.0f) / 256.0f, - 0 - ); -} +template class SymbolSDFProgram<style::IconPaintProperties>; +template class SymbolSDFProgram<style::TextPaintProperties>; } // namespace mbgl diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index be987551c0..0537c25a2c 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -3,10 +3,12 @@ #include <mbgl/programs/program.hpp> #include <mbgl/programs/attributes.hpp> #include <mbgl/programs/uniforms.hpp> -#include <mbgl/shader/symbol_icon.hpp> -#include <mbgl/shader/symbol_sdf.hpp> +#include <mbgl/shaders/symbol_icon.hpp> +#include <mbgl/shaders/symbol_sdf.hpp> #include <mbgl/util/geometry.hpp> #include <mbgl/util/size.hpp> +#include <mbgl/style/layers/symbol_layer_properties.hpp> +#include <mbgl/style/layers/symbol_layer_impl.hpp> #include <cmath> #include <array> @@ -26,14 +28,15 @@ MBGL_DEFINE_UNIFORM_SCALAR(bool, u_rotate_with_map); MBGL_DEFINE_UNIFORM_SCALAR(bool, u_pitch_with_map); MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_texture); MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_fadetexture); -MBGL_DEFINE_UNIFORM_SCALAR(float, u_buffer); -MBGL_DEFINE_UNIFORM_SCALAR(float, u_gamma); MBGL_DEFINE_UNIFORM_SCALAR(float, u_aspect_ratio); +MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_halo); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_font_scale); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_gamma_scale); } // namespace uniforms -struct SymbolAttributes : gl::Attributes< +struct SymbolLayoutAttributes : gl::Attributes< attributes::a_pos, - attributes::a_offset, + attributes::a_offset<2>, attributes::a_texture_pos, attributes::a_data<4>> { @@ -46,43 +49,41 @@ struct SymbolAttributes : gl::Attributes< float labelminzoom, uint8_t labelangle) { return Vertex { - { + {{ static_cast<int16_t>(a.x), static_cast<int16_t>(a.y) - }, - { + }}, + {{ static_cast<int16_t>(::round(o.x * 64)), // use 1/64 pixels for placement static_cast<int16_t>(::round(o.y * 64)) - }, - { + }}, + {{ static_cast<uint16_t>(tx / 4), static_cast<uint16_t>(ty / 4) - }, - { + }}, + {{ static_cast<uint8_t>(labelminzoom * 10), // 1/10 zoom levels: z16 == 160 static_cast<uint8_t>(labelangle), static_cast<uint8_t>(minzoom * 10), static_cast<uint8_t>(::fmin(maxzoom, 25) * 10) - } + }} }; } }; -using SymbolVertex = SymbolAttributes::Vertex; - class SymbolIconProgram : public Program< shaders::symbol_icon, gl::Triangle, - SymbolAttributes, + SymbolLayoutAttributes, gl::Uniforms< uniforms::u_matrix, - uniforms::u_opacity, uniforms::u_extrude_scale, uniforms::u_texsize, uniforms::u_zoom, uniforms::u_rotate_with_map, uniforms::u_texture, - uniforms::u_fadetexture>> + uniforms::u_fadetexture>, + style::IconPaintProperties> { public: using Program::Program; @@ -94,43 +95,73 @@ public: const TransformState&); }; +enum class SymbolSDFPart { + Fill = 1, + Halo = 0 +}; + +template <class PaintProperties> class SymbolSDFProgram : public Program< shaders::symbol_sdf, gl::Triangle, - SymbolAttributes, + SymbolLayoutAttributes, gl::Uniforms< uniforms::u_matrix, - uniforms::u_opacity, uniforms::u_extrude_scale, uniforms::u_texsize, uniforms::u_zoom, uniforms::u_rotate_with_map, uniforms::u_texture, uniforms::u_fadetexture, - uniforms::u_color, - uniforms::u_buffer, - uniforms::u_gamma, + uniforms::u_font_scale, + uniforms::u_gamma_scale, uniforms::u_pitch, uniforms::u_bearing, uniforms::u_aspect_ratio, - uniforms::u_pitch_with_map>> + uniforms::u_pitch_with_map, + uniforms::u_is_halo>, + PaintProperties> { public: - using Program::Program; + using BaseProgram = Program<shaders::symbol_sdf, + gl::Triangle, + SymbolLayoutAttributes, + gl::Uniforms< + uniforms::u_matrix, + uniforms::u_extrude_scale, + uniforms::u_texsize, + uniforms::u_zoom, + uniforms::u_rotate_with_map, + uniforms::u_texture, + uniforms::u_fadetexture, + uniforms::u_font_scale, + uniforms::u_gamma_scale, + uniforms::u_pitch, + uniforms::u_bearing, + uniforms::u_aspect_ratio, + uniforms::u_pitch_with_map, + uniforms::u_is_halo>, + PaintProperties>; + + using UniformValues = typename BaseProgram::UniformValues; + + + + using BaseProgram::BaseProgram; - static UniformValues haloUniformValues(const style::SymbolPropertyValues&, - const Size& texsize, - const std::array<float, 2>& pixelsToGLUnits, - const RenderTile&, - const TransformState&, - float pixelRatio); - - static UniformValues foregroundUniformValues(const style::SymbolPropertyValues&, - const Size& texsize, - const std::array<float, 2>& pixelsToGLUnits, - const RenderTile&, - const TransformState&, - float pixelRatio); + static UniformValues uniformValues(const style::SymbolPropertyValues&, + const Size& texsize, + const std::array<float, 2>& pixelsToGLUnits, + const RenderTile&, + const TransformState&, + const SymbolSDFPart); }; +using SymbolSDFIconProgram = SymbolSDFProgram<style::IconPaintProperties>; +using SymbolSDFTextProgram = SymbolSDFProgram<style::TextPaintProperties>; + +using SymbolLayoutVertex = SymbolLayoutAttributes::Vertex; +using SymbolIconAttributes = SymbolIconProgram::Attributes; +using SymbolTextAttributes = SymbolSDFTextProgram::Attributes; + } // namespace mbgl diff --git a/src/mbgl/renderer/bucket.hpp b/src/mbgl/renderer/bucket.hpp index 49619c14f7..294e50dd8c 100644 --- a/src/mbgl/renderer/bucket.hpp +++ b/src/mbgl/renderer/bucket.hpp @@ -2,6 +2,7 @@ #include <mbgl/renderer/render_pass.hpp> #include <mbgl/util/noncopyable.hpp> +#include <mbgl/tile/geometry_tile_data.hpp> #include <atomic> @@ -22,6 +23,10 @@ class Layer; class Bucket : private util::noncopyable { public: Bucket() = default; + virtual ~Bucket() = default; + + virtual void addFeature(const GeometryTileFeature&, + const GeometryCollection&) {}; // As long as this bucket has a Prepare render pass, this function is getting called. Typically, // this only happens once when the bucket is being rendered for the first time. @@ -31,8 +36,6 @@ public: // once or twice (for Opaque and Transparent render passes). virtual void render(Painter&, PaintParameters&, const style::Layer&, const RenderTile&) = 0; - virtual ~Bucket() = default; - virtual bool hasData() const = 0; bool needsUpload() const { diff --git a/src/mbgl/renderer/circle_bucket.cpp b/src/mbgl/renderer/circle_bucket.cpp index ba2285c4eb..6722d04497 100644 --- a/src/mbgl/renderer/circle_bucket.cpp +++ b/src/mbgl/renderer/circle_bucket.cpp @@ -1,26 +1,38 @@ #include <mbgl/renderer/circle_bucket.hpp> #include <mbgl/renderer/painter.hpp> -#include <mbgl/gl/context.hpp> - #include <mbgl/programs/circle_program.hpp> +#include <mbgl/style/bucket_parameters.hpp> #include <mbgl/style/layers/circle_layer.hpp> +#include <mbgl/style/layers/circle_layer_impl.hpp> #include <mbgl/util/constants.hpp> namespace mbgl { using namespace style; -CircleBucket::CircleBucket(MapMode mode_) : mode(mode_) { +CircleBucket::CircleBucket(const BucketParameters& parameters, const std::vector<const Layer*>& layers) + : mode(parameters.mode) { + for (const auto& layer : layers) { + paintPropertyBinders.emplace(layer->getID(), + CircleProgram::PaintPropertyBinders( + layer->as<CircleLayer>()->impl->paint.evaluated, + parameters.tileID.overscaledZ)); + } } void CircleBucket::upload(gl::Context& context) { vertexBuffer = context.createVertexBuffer(std::move(vertices)); indexBuffer = context.createIndexBuffer(std::move(triangles)); + + for (auto& pair : paintPropertyBinders) { + pair.second.upload(context); + } + uploaded = true; } void CircleBucket::render(Painter& painter, - PaintParameters& parameters, + PaintParameters& parameters, const Layer& layer, const RenderTile& tile) { painter.renderCircle(parameters, *this, *layer.as<CircleLayer>(), tile); @@ -30,10 +42,11 @@ bool CircleBucket::hasData() const { return !segments.empty(); } -void CircleBucket::addGeometry(const GeometryCollection& geometryCollection) { +void CircleBucket::addFeature(const GeometryTileFeature& feature, + const GeometryCollection& geometry) { constexpr const uint16_t vertexLength = 4; - for (auto& circle : geometryCollection) { + for (auto& circle : geometry) { for(auto& point : circle) { auto x = point.x; auto y = point.y; @@ -76,6 +89,10 @@ void CircleBucket::addGeometry(const GeometryCollection& geometryCollection) { segment.indexLength += 6; } } + + for (auto& pair : paintPropertyBinders) { + pair.second.populateVertexVectors(feature, vertices.vertexSize()); + } } } // namespace mbgl diff --git a/src/mbgl/renderer/circle_bucket.hpp b/src/mbgl/renderer/circle_bucket.hpp index af7041a238..412db53f65 100644 --- a/src/mbgl/renderer/circle_bucket.hpp +++ b/src/mbgl/renderer/circle_bucket.hpp @@ -7,26 +7,34 @@ #include <mbgl/gl/index_buffer.hpp> #include <mbgl/gl/segment.hpp> #include <mbgl/programs/circle_program.hpp> +#include <mbgl/style/layers/circle_layer_properties.hpp> namespace mbgl { +namespace style { +class BucketParameters; +} // namespace style + class CircleBucket : public Bucket { public: - CircleBucket(const MapMode); + CircleBucket(const style::BucketParameters&, const std::vector<const style::Layer*>&); + + void addFeature(const GeometryTileFeature&, + const GeometryCollection&) override; + bool hasData() const override; void upload(gl::Context&) override; void render(Painter&, PaintParameters&, const style::Layer&, const RenderTile&) override; - bool hasData() const override; - void addGeometry(const GeometryCollection&); - - gl::VertexVector<CircleVertex> vertices; + gl::VertexVector<CircleLayoutVertex> vertices; gl::IndexVector<gl::Triangles> triangles; gl::SegmentVector<CircleAttributes> segments; - optional<gl::VertexBuffer<CircleVertex>> vertexBuffer; + optional<gl::VertexBuffer<CircleLayoutVertex>> vertexBuffer; optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; + std::unordered_map<std::string, CircleProgram::PaintPropertyBinders> paintPropertyBinders; + const MapMode mode; }; diff --git a/src/mbgl/renderer/debug_bucket.cpp b/src/mbgl/renderer/debug_bucket.cpp index 167df4376f..2a514989cf 100644 --- a/src/mbgl/renderer/debug_bucket.cpp +++ b/src/mbgl/renderer/debug_bucket.cpp @@ -23,7 +23,7 @@ DebugBucket::DebugBucket(const OverscaledTileID& id, expires(std::move(expires_)), debugMode(debugMode_) { - gl::VertexVector<FillVertex> vertices; + gl::VertexVector<FillLayoutVertex> vertices; gl::IndexVector<gl::Lines> indices; auto addText = [&] (const std::string& text, double left, double baseline, double scale) { @@ -43,7 +43,7 @@ DebugBucket::DebugBucket(const OverscaledTileID& id, int16_t(::round(baseline - glyph.data[j + 1] * scale)) }; - vertices.emplace_back(FillAttributes::vertex(p)); + vertices.emplace_back(FillProgram::layoutVertex(p)); if (prev) { indices.emplace_back(vertices.vertexSize() - 2, diff --git a/src/mbgl/renderer/debug_bucket.hpp b/src/mbgl/renderer/debug_bucket.hpp index 4676381789..756e58a6de 100644 --- a/src/mbgl/renderer/debug_bucket.hpp +++ b/src/mbgl/renderer/debug_bucket.hpp @@ -34,7 +34,7 @@ public: const MapDebugOptions debugMode; gl::SegmentVector<DebugAttributes> segments; - optional<gl::VertexBuffer<DebugVertex>> vertexBuffer; + optional<gl::VertexBuffer<DebugLayoutVertex>> vertexBuffer; optional<gl::IndexBuffer<gl::Lines>> indexBuffer; }; diff --git a/src/mbgl/renderer/fill_bucket.cpp b/src/mbgl/renderer/fill_bucket.cpp index b89e982057..64efafb108 100644 --- a/src/mbgl/renderer/fill_bucket.cpp +++ b/src/mbgl/renderer/fill_bucket.cpp @@ -1,8 +1,9 @@ #include <mbgl/renderer/fill_bucket.hpp> -#include <mbgl/style/layers/fill_layer.hpp> #include <mbgl/renderer/painter.hpp> #include <mbgl/programs/fill_program.hpp> -#include <mbgl/util/logging.hpp> +#include <mbgl/style/bucket_parameters.hpp> +#include <mbgl/style/layers/fill_layer.hpp> +#include <mbgl/style/layers/fill_layer_impl.hpp> #include <mapbox/earcut.hpp> @@ -26,7 +27,17 @@ using namespace style; struct GeometryTooLongException : std::exception {}; -void FillBucket::addGeometry(const GeometryCollection& geometry) { +FillBucket::FillBucket(const BucketParameters& parameters, const std::vector<const Layer*>& layers) { + for (const auto& layer : layers) { + paintPropertyBinders.emplace(layer->getID(), + FillProgram::PaintPropertyBinders( + layer->as<FillLayer>()->impl->paint.evaluated, + parameters.tileID.overscaledZ)); + } +} + +void FillBucket::addFeature(const GeometryTileFeature& feature, + const GeometryCollection& geometry) { for (auto& polygon : classifyRings(geometry)) { // Optimize polygons with many interior rings for earcut tesselation. limitHoles(polygon, 500); @@ -55,11 +66,11 @@ void FillBucket::addGeometry(const GeometryCollection& geometry) { assert(lineSegment.vertexLength <= std::numeric_limits<uint16_t>::max()); uint16_t lineIndex = lineSegment.vertexLength; - vertices.emplace_back(FillAttributes::vertex(ring[0])); + vertices.emplace_back(FillProgram::layoutVertex(ring[0])); lines.emplace_back(lineIndex + nVertices - 1, lineIndex); for (uint32_t i = 1; i < nVertices; i++) { - vertices.emplace_back(FillAttributes::vertex(ring[i])); + vertices.emplace_back(FillProgram::layoutVertex(ring[i])); lines.emplace_back(lineIndex + i - 1, lineIndex + i); } @@ -89,6 +100,10 @@ void FillBucket::addGeometry(const GeometryCollection& geometry) { triangleSegment.vertexLength += totalVertices; triangleSegment.indexLength += nIndicies; } + + for (auto& pair : paintPropertyBinders) { + pair.second.populateVertexVectors(feature, vertices.vertexSize()); + } } void FillBucket::upload(gl::Context& context) { @@ -96,7 +111,10 @@ void FillBucket::upload(gl::Context& context) { lineIndexBuffer = context.createIndexBuffer(std::move(lines)); triangleIndexBuffer = context.createIndexBuffer(std::move(triangles)); - // From now on, we're going to render during the opaque and translucent pass. + for (auto& pair : paintPropertyBinders) { + pair.second.upload(context); + } + uploaded = true; } diff --git a/src/mbgl/renderer/fill_bucket.hpp b/src/mbgl/renderer/fill_bucket.hpp index edb1521c1d..b403e1053b 100644 --- a/src/mbgl/renderer/fill_bucket.hpp +++ b/src/mbgl/renderer/fill_bucket.hpp @@ -6,28 +6,38 @@ #include <mbgl/gl/index_buffer.hpp> #include <mbgl/gl/segment.hpp> #include <mbgl/programs/fill_program.hpp> +#include <mbgl/style/layers/fill_layer_properties.hpp> #include <vector> namespace mbgl { +namespace style { +class BucketParameters; +} // namespace style + class FillBucket : public Bucket { public: - void upload(gl::Context&) override; - void render(Painter&, PaintParameters&, const style::Layer&, const RenderTile&) override; + FillBucket(const style::BucketParameters&, const std::vector<const style::Layer*>&); + + void addFeature(const GeometryTileFeature&, + const GeometryCollection&) override; bool hasData() const override; - void addGeometry(const GeometryCollection&); + void upload(gl::Context&) override; + void render(Painter&, PaintParameters&, const style::Layer&, const RenderTile&) override; - gl::VertexVector<FillVertex> vertices; + gl::VertexVector<FillLayoutVertex> vertices; gl::IndexVector<gl::Lines> lines; gl::IndexVector<gl::Triangles> triangles; gl::SegmentVector<FillAttributes> lineSegments; gl::SegmentVector<FillAttributes> triangleSegments; - optional<gl::VertexBuffer<FillVertex>> vertexBuffer; + optional<gl::VertexBuffer<FillLayoutVertex>> vertexBuffer; optional<gl::IndexBuffer<gl::Lines>> lineIndexBuffer; optional<gl::IndexBuffer<gl::Triangles>> triangleIndexBuffer; + + std::unordered_map<std::string, FillProgram::PaintPropertyBinders> paintPropertyBinders; }; } // namespace mbgl diff --git a/src/mbgl/renderer/frame_history.cpp b/src/mbgl/renderer/frame_history.cpp index 1ee53d87b2..a933a9004a 100644 --- a/src/mbgl/renderer/frame_history.cpp +++ b/src/mbgl/renderer/frame_history.cpp @@ -8,7 +8,7 @@ namespace mbgl { FrameHistory::FrameHistory() { changeOpacities.fill(0); - std::fill(opacities.data.get(), opacities.data.get() + opacities.bytes(), 0); + opacities.fill(0); } void FrameHistory::record(const TimePoint& now, float zoom, const Duration& duration) { diff --git a/src/mbgl/renderer/frame_history.hpp b/src/mbgl/renderer/frame_history.hpp index fffbd113ed..f2b11f5f41 100644 --- a/src/mbgl/renderer/frame_history.hpp +++ b/src/mbgl/renderer/frame_history.hpp @@ -26,7 +26,7 @@ public: private: std::array<TimePoint, 256> changeTimes; std::array<uint8_t, 256> changeOpacities; - const AlphaImage opacities{ { 256, 1 } }; + AlphaImage opacities{ { 256, 1 } }; int16_t previousZoomIndex = 0; TimePoint previousTime; diff --git a/src/mbgl/renderer/line_bucket.cpp b/src/mbgl/renderer/line_bucket.cpp index 007060bd1b..50a70c0fd4 100644 --- a/src/mbgl/renderer/line_bucket.cpp +++ b/src/mbgl/renderer/line_bucket.cpp @@ -1,6 +1,8 @@ #include <mbgl/renderer/line_bucket.hpp> -#include <mbgl/style/layers/line_layer.hpp> #include <mbgl/renderer/painter.hpp> +#include <mbgl/style/layers/line_layer.hpp> +#include <mbgl/style/bucket_parameters.hpp> +#include <mbgl/style/layers/line_layer_impl.hpp> #include <mbgl/util/math.hpp> #include <mbgl/util/constants.hpp> @@ -10,19 +12,29 @@ namespace mbgl { using namespace style; -LineBucket::LineBucket(uint32_t overscaling_) : overscaling(overscaling_) { -} - -LineBucket::~LineBucket() { - // Do not remove. header file only contains forward definitions to unique pointers. +LineBucket::LineBucket(const BucketParameters& parameters, + const std::vector<const Layer*>& layers, + const style::LineLayoutProperties& layout_) + : layout(layout_.evaluate(PropertyEvaluationParameters(parameters.tileID.overscaledZ))), + overscaling(parameters.tileID.overscaleFactor()) { + for (const auto& layer : layers) { + paintPropertyBinders.emplace(layer->getID(), + LineProgram::PaintPropertyBinders( + layer->as<LineLayer>()->impl->paint.evaluated, + parameters.tileID.overscaledZ)); + } } -void LineBucket::addGeometry(const GeometryCollection& geometryCollection) { +void LineBucket::addFeature(const GeometryTileFeature& feature, + const GeometryCollection& geometryCollection) { for (auto& line : geometryCollection) { addGeometry(line); } -} + for (auto& pair : paintPropertyBinders) { + pair.second.populateVertexVectors(feature, vertices.vertexSize()); + } +} /* * Sharp corners cause dashed lines to tilt because the distance along the line @@ -139,7 +151,14 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates) { // Determine the normal of the join extrusion. It is the angle bisector // of the segments between the previous line and the next line. - Point<double> joinNormal = util::unit(*prevNormal + *nextNormal); + // In the case of 180° angles, the prev and next normals cancel each other out: + // prevNormal + nextNormal = (0, 0), its magnitude is 0, so the unit vector would be + // undefined. In that case, we're keeping the joinNormal at (0, 0), so that the cosHalfAngle + // below will also become 0 and miterLength will become Infinity. + Point<double> joinNormal = *prevNormal + *nextNormal; + if (joinNormal.x != 0 || joinNormal.y != 0) { + joinNormal = util::unit(joinNormal); + } /* joinNormal prevNormal * ↖ ↑ @@ -155,7 +174,8 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates) { // Find the cosine of the angle between the next and join normals // using dot product. The inverse of that is the miter length. const double cosHalfAngle = joinNormal.x * nextNormal->x + joinNormal.y * nextNormal->y; - const double miterLength = cosHalfAngle != 0 ? 1 / cosHalfAngle: 1; + const double miterLength = + cosHalfAngle != 0 ? 1 / cosHalfAngle : std::numeric_limits<double>::infinity(); const bool isSharpCorner = cosHalfAngle < COS_HALF_SHARP_CORNER && prevCoordinate && nextCoordinate; @@ -189,7 +209,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates) { if (currentJoin == LineJoinType::Bevel) { // The maximum extrude length is 128 / 63 = 2 times the width of the line - // so if miterLength >= 2 we need to draw a different type of bevel where. + // so if miterLength >= 2 we need to draw a different type of bevel here. if (miterLength > 2) { currentJoin = LineJoinType::FlipBevel; } @@ -216,7 +236,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates) { if (miterLength > 100) { // Almost parallel lines - joinNormal = *nextNormal; + joinNormal = *nextNormal * -1.0; } else { const double direction = prevNormal->x * nextNormal->y - prevNormal->y * nextNormal->x > 0 ? -1 : 1; const double bevelLength = miterLength * util::mag(*prevNormal + *nextNormal) / @@ -375,7 +395,7 @@ void LineBucket::addCurrentVertex(const GeometryCoordinate& currentCoordinate, Point<double> extrude = normal; if (endLeft) extrude = extrude - (util::perp(normal) * endLeft); - vertices.emplace_back(LineAttributes::vertex(currentCoordinate, extrude, { round, false }, endLeft, distance * LINE_DISTANCE_SCALE)); + vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, { round, false }, endLeft, distance * LINE_DISTANCE_SCALE)); e3 = vertices.vertexSize() - 1 - startVertex; if (e1 >= 0 && e2 >= 0) { triangleStore.emplace_back(e1, e2, e3); @@ -386,7 +406,7 @@ void LineBucket::addCurrentVertex(const GeometryCoordinate& currentCoordinate, extrude = normal * -1.0; if (endRight) extrude = extrude - (util::perp(normal) * endRight); - vertices.emplace_back(LineAttributes::vertex(currentCoordinate, extrude, { round, true }, -endRight, distance * LINE_DISTANCE_SCALE)); + vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, { round, true }, -endRight, distance * LINE_DISTANCE_SCALE)); e3 = vertices.vertexSize() - 1 - startVertex; if (e1 >= 0 && e2 >= 0) { triangleStore.emplace_back(e1, e2, e3); @@ -411,7 +431,7 @@ void LineBucket::addPieSliceVertex(const GeometryCoordinate& currentVertex, std::size_t startVertex, std::vector<TriangleElement>& triangleStore) { Point<double> flippedExtrude = extrude * (lineTurnsLeft ? -1.0 : 1.0); - vertices.emplace_back(LineAttributes::vertex(currentVertex, flippedExtrude, { false, lineTurnsLeft }, 0, distance * LINE_DISTANCE_SCALE)); + vertices.emplace_back(LineProgram::layoutVertex(currentVertex, flippedExtrude, { false, lineTurnsLeft }, 0, distance * LINE_DISTANCE_SCALE)); e3 = vertices.vertexSize() - 1 - startVertex; if (e1 >= 0 && e2 >= 0) { triangleStore.emplace_back(e1, e2, e3); @@ -428,7 +448,10 @@ void LineBucket::upload(gl::Context& context) { vertexBuffer = context.createVertexBuffer(std::move(vertices)); indexBuffer = context.createIndexBuffer(std::move(triangles)); - // From now on, we're only going to render during the translucent pass. + for (auto& pair : paintPropertyBinders) { + pair.second.upload(context); + } + uploaded = true; } diff --git a/src/mbgl/renderer/line_bucket.hpp b/src/mbgl/renderer/line_bucket.hpp index d11d78ff69..78293d75f9 100644 --- a/src/mbgl/renderer/line_bucket.hpp +++ b/src/mbgl/renderer/line_bucket.hpp @@ -12,28 +12,37 @@ namespace mbgl { +namespace style { +class BucketParameters; +} // namespace style + class LineBucket : public Bucket { public: - LineBucket(uint32_t overscaling); - ~LineBucket() override; + LineBucket(const style::BucketParameters&, + const std::vector<const style::Layer*>&, + const style::LineLayoutProperties&); - void upload(gl::Context&) override; - void render(Painter&, PaintParameters&, const style::Layer&, const RenderTile&) override; + void addFeature(const GeometryTileFeature&, + const GeometryCollection&) override; bool hasData() const override; - void addGeometry(const GeometryCollection&); - void addGeometry(const GeometryCoordinates& line); + void upload(gl::Context&) override; + void render(Painter&, PaintParameters&, const style::Layer&, const RenderTile&) override; style::LineLayoutProperties::Evaluated layout; - gl::VertexVector<LineVertex> vertices; + gl::VertexVector<LineLayoutVertex> vertices; gl::IndexVector<gl::Triangles> triangles; gl::SegmentVector<LineAttributes> segments; - optional<gl::VertexBuffer<LineVertex>> vertexBuffer; + optional<gl::VertexBuffer<LineLayoutVertex>> vertexBuffer; optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; + std::unordered_map<std::string, LineProgram::PaintPropertyBinders> paintPropertyBinders; + private: + void addGeometry(const GeometryCoordinates& line); + struct TriangleElement { TriangleElement(uint16_t a_, uint16_t b_, uint16_t c_) : a(a_), b(b_), c(c_) {} uint16_t a, b, c; diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp index 505007304d..27d24d14a9 100644 --- a/src/mbgl/renderer/painter.cpp +++ b/src/mbgl/renderer/painter.cpp @@ -42,12 +42,12 @@ namespace mbgl { using namespace style; -static gl::VertexVector<FillVertex> tileVertices() { - gl::VertexVector<FillVertex> result; - result.emplace_back(FillAttributes::vertex({ 0, 0 })); - result.emplace_back(FillAttributes::vertex({ util::EXTENT, 0 })); - result.emplace_back(FillAttributes::vertex({ 0, util::EXTENT })); - result.emplace_back(FillAttributes::vertex({ util::EXTENT, util::EXTENT })); +static gl::VertexVector<FillLayoutVertex> tileVertices() { + gl::VertexVector<FillLayoutVertex> result; + result.emplace_back(FillProgram::layoutVertex({ 0, 0 })); + result.emplace_back(FillProgram::layoutVertex({ util::EXTENT, 0 })); + result.emplace_back(FillProgram::layoutVertex({ 0, util::EXTENT })); + result.emplace_back(FillProgram::layoutVertex({ util::EXTENT, util::EXTENT })); return result; } @@ -68,12 +68,12 @@ static gl::IndexVector<gl::LineStrip> tileLineStripIndices() { return result; } -static gl::VertexVector<RasterVertex> rasterVertices() { - gl::VertexVector<RasterVertex> result; - result.emplace_back(RasterProgram::vertex({ 0, 0 }, { 0, 0 })); - result.emplace_back(RasterProgram::vertex({ util::EXTENT, 0 }, { 32767, 0 })); - result.emplace_back(RasterProgram::vertex({ 0, util::EXTENT }, { 0, 32767 })); - result.emplace_back(RasterProgram::vertex({ util::EXTENT, util::EXTENT }, { 32767, 32767 })); +static gl::VertexVector<RasterLayoutVertex> rasterVertices() { + gl::VertexVector<RasterLayoutVertex> result; + result.emplace_back(RasterProgram::layoutVertex({ 0, 0 }, { 0, 0 })); + result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, 0 }, { 32767, 0 })); + result.emplace_back(RasterProgram::layoutVertex({ 0, util::EXTENT }, { 0, 32767 })); + result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, util::EXTENT }, { 32767, 32767 })); return result; } @@ -94,7 +94,7 @@ Painter::Painter(gl::Context& context_, const TransformState& state_, float pixe ProgramParameters programParameters{ pixelRatio, false }; programs = std::make_unique<Programs>(context, programParameters); #ifndef NDEBUG - + ProgramParameters programParametersOverdraw{ pixelRatio, true }; overdrawPrograms = std::make_unique<Programs>(context, programParametersOverdraw); #endif diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp index dec7fa57fd..91f329a6eb 100644 --- a/src/mbgl/renderer/painter.hpp +++ b/src/mbgl/renderer/painter.hpp @@ -158,8 +158,8 @@ private: std::unique_ptr<Programs> overdrawPrograms; #endif - gl::VertexBuffer<FillVertex> tileVertexBuffer; - gl::VertexBuffer<RasterVertex> rasterVertexBuffer; + gl::VertexBuffer<FillLayoutVertex> tileVertexBuffer; + gl::VertexBuffer<RasterLayoutVertex> rasterVertexBuffer; gl::IndexBuffer<gl::Triangles> tileTriangleIndexBuffer; gl::IndexBuffer<gl::LineStrip> tileBorderIndexBuffer; diff --git a/src/mbgl/renderer/painter_background.cpp b/src/mbgl/renderer/painter_background.cpp index 4a3e41701d..4ac414335b 100644 --- a/src/mbgl/renderer/painter_background.cpp +++ b/src/mbgl/renderer/painter_background.cpp @@ -14,13 +14,18 @@ using namespace style; void Painter::renderBackground(PaintParameters& parameters, const BackgroundLayer& layer) { // Note that for bottommost layers without a pattern, the background color is drawn with // glClear rather than this method. - const BackgroundPaintProperties::Evaluated& properties = layer.impl->paint.evaluated; + const BackgroundPaintProperties::Evaluated& background = layer.impl->paint.evaluated; - if (!properties.get<BackgroundPattern>().to.empty()) { - optional<SpriteAtlasPosition> imagePosA = spriteAtlas->getPosition( - properties.get<BackgroundPattern>().from, SpritePatternMode::Repeating); - optional<SpriteAtlasPosition> imagePosB = spriteAtlas->getPosition( - properties.get<BackgroundPattern>().to, SpritePatternMode::Repeating); + style::FillPaintProperties::Evaluated properties; + properties.get<FillPattern>() = background.get<BackgroundPattern>(); + properties.get<FillOpacity>() = { background.get<BackgroundOpacity>() }; + properties.get<FillColor>() = { background.get<BackgroundColor>() }; + + const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0); + + if (!background.get<BackgroundPattern>().to.empty()) { + optional<SpriteAtlasElement> imagePosA = spriteAtlas->getPattern(background.get<BackgroundPattern>().from); + optional<SpriteAtlasElement> imagePosB = spriteAtlas->getPattern(background.get<BackgroundPattern>().to); if (!imagePosA || !imagePosB) return; @@ -36,17 +41,19 @@ void Painter::renderBackground(PaintParameters& parameters, const BackgroundLaye colorModeForRenderPass(), FillPatternUniforms::values( matrixForTile(tileID), - properties.get<BackgroundOpacity>(), context.viewport.getCurrentValue().size, *imagePosA, *imagePosB, - properties.get<BackgroundPattern>(), + background.get<BackgroundPattern>(), tileID, state ), tileVertexBuffer, tileTriangleIndexBuffer, - tileTriangleSegments + tileTriangleSegments, + paintAttibuteData, + properties, + state.getZoom() ); } } else { @@ -59,14 +66,14 @@ void Painter::renderBackground(PaintParameters& parameters, const BackgroundLaye colorModeForRenderPass(), FillProgram::UniformValues { uniforms::u_matrix::Value{ matrixForTile(tileID) }, - uniforms::u_opacity::Value{ properties.get<BackgroundOpacity>() }, - uniforms::u_color::Value{ properties.get<BackgroundColor>() }, - uniforms::u_outline_color::Value{ properties.get<BackgroundColor>() }, uniforms::u_world::Value{ context.viewport.getCurrentValue().size }, }, tileVertexBuffer, tileTriangleIndexBuffer, - tileTriangleSegments + tileTriangleSegments, + paintAttibuteData, + properties, + state.getZoom() ); } } diff --git a/src/mbgl/renderer/painter_circle.cpp b/src/mbgl/renderer/painter_circle.cpp index 966d58b59b..8d47e75f71 100644 --- a/src/mbgl/renderer/painter_circle.cpp +++ b/src/mbgl/renderer/painter_circle.cpp @@ -37,13 +37,6 @@ void Painter::renderCircle(PaintParameters& parameters, properties.get<CircleTranslateAnchor>(), state) }, - uniforms::u_opacity::Value{ properties.get<CircleOpacity>() }, - uniforms::u_color::Value{ properties.get<CircleColor>() }, - uniforms::u_radius::Value{ properties.get<CircleRadius>() }, - uniforms::u_blur::Value{ properties.get<CircleBlur>() }, - uniforms::u_stroke_color::Value{ properties.get<CircleStrokeColor>() }, - uniforms::u_stroke_width::Value{ properties.get<CircleStrokeWidth>() }, - uniforms::u_stroke_opacity::Value{ properties.get<CircleStrokeOpacity>() }, uniforms::u_scale_with_map::Value{ scaleWithMap }, uniforms::u_extrude_scale::Value{ scaleWithMap ? std::array<float, 2> {{ @@ -54,7 +47,10 @@ void Painter::renderCircle(PaintParameters& parameters, }, *bucket.vertexBuffer, *bucket.indexBuffer, - bucket.segments + bucket.segments, + bucket.paintPropertyBinders.at(layer.getID()), + properties, + state.getZoom() ); } diff --git a/src/mbgl/renderer/painter_clipping.cpp b/src/mbgl/renderer/painter_clipping.cpp index a2529561fe..70df9837e8 100644 --- a/src/mbgl/renderer/painter_clipping.cpp +++ b/src/mbgl/renderer/painter_clipping.cpp @@ -6,6 +6,8 @@ namespace mbgl { void Painter::renderClippingMask(const UnwrappedTileID& tileID, const ClipID& clip) { + static const style::FillPaintProperties::Evaluated properties {}; + static const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0); programs->fill.draw( context, gl::Triangles(), @@ -21,14 +23,14 @@ void Painter::renderClippingMask(const UnwrappedTileID& tileID, const ClipID& cl gl::ColorMode::disabled(), FillProgram::UniformValues { uniforms::u_matrix::Value{ matrixForTile(tileID) }, - uniforms::u_opacity::Value{ 0.0f }, - uniforms::u_color::Value{ Color {} }, - uniforms::u_outline_color::Value{ Color {} }, uniforms::u_world::Value{ context.viewport.getCurrentValue().size }, }, tileVertexBuffer, tileTriangleIndexBuffer, - tileTriangleSegments + tileTriangleSegments, + paintAttibuteData, + properties, + state.getZoom() ); } diff --git a/src/mbgl/renderer/painter_debug.cpp b/src/mbgl/renderer/painter_debug.cpp index 2b838dec0e..5b347884bf 100644 --- a/src/mbgl/renderer/painter_debug.cpp +++ b/src/mbgl/renderer/painter_debug.cpp @@ -12,12 +12,17 @@ namespace mbgl { +using namespace style; + void Painter::renderTileDebug(const RenderTile& renderTile) { if (frame.debugOptions == MapDebugOptions::NoDebug) return; MBGL_DEBUG_GROUP(std::string { "debug " } + util::toString(renderTile.id)); + static const style::PaintProperties<>::Evaluated properties {}; + static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0); + auto draw = [&] (Color color, const auto& vertexBuffer, const auto& indexBuffer, const auto& segments, auto drawMode) { programs->debug.draw( context, @@ -31,7 +36,10 @@ void Painter::renderTileDebug(const RenderTile& renderTile) { }, vertexBuffer, indexBuffer, - segments + segments, + paintAttibuteData, + properties, + state.getZoom() ); }; diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp index 356ccfc0b2..4276bd06ed 100644 --- a/src/mbgl/renderer/painter_fill.cpp +++ b/src/mbgl/renderer/painter_fill.cpp @@ -24,10 +24,8 @@ void Painter::renderFill(PaintParameters& parameters, return; } - optional<SpriteAtlasPosition> imagePosA = spriteAtlas->getPosition( - properties.get<FillPattern>().from, SpritePatternMode::Repeating); - optional<SpriteAtlasPosition> imagePosB = spriteAtlas->getPosition( - properties.get<FillPattern>().to, SpritePatternMode::Repeating); + optional<SpriteAtlasElement> imagePosA = spriteAtlas->getPattern(properties.get<FillPattern>().from); + optional<SpriteAtlasElement> imagePosB = spriteAtlas->getPattern(properties.get<FillPattern>().to); if (!imagePosA || !imagePosB) { return; @@ -38,7 +36,6 @@ void Painter::renderFill(PaintParameters& parameters, auto draw = [&] (uint8_t sublayer, auto& program, const auto& drawMode, - const auto& vertexBuffer, const auto& indexBuffer, const auto& segments) { program.draw( @@ -51,7 +48,6 @@ void Painter::renderFill(PaintParameters& parameters, tile.translatedMatrix(properties.get<FillTranslate>(), properties.get<FillTranslateAnchor>(), state), - properties.get<FillOpacity>(), context.viewport.getCurrentValue().size, *imagePosA, *imagePosB, @@ -59,16 +55,18 @@ void Painter::renderFill(PaintParameters& parameters, tile.id, state ), - vertexBuffer, + *bucket.vertexBuffer, indexBuffer, - segments + segments, + bucket.paintPropertyBinders.at(layer.getID()), + properties, + state.getZoom() ); }; draw(0, parameters.programs.fillPattern, gl::Triangles(), - *bucket.vertexBuffer, *bucket.triangleIndexBuffer, bucket.triangleSegments); @@ -79,15 +77,12 @@ void Painter::renderFill(PaintParameters& parameters, draw(2, parameters.programs.fillOutlinePattern, gl::Lines { 2.0f }, - *bucket.vertexBuffer, *bucket.lineIndexBuffer, bucket.lineSegments); } else { auto draw = [&] (uint8_t sublayer, auto& program, - Color outlineColor, const auto& drawMode, - const auto& vertexBuffer, const auto& indexBuffer, const auto& segments) { program.draw( @@ -97,38 +92,37 @@ void Painter::renderFill(PaintParameters& parameters, stencilModeForClipping(tile.clip), colorModeForRenderPass(), FillProgram::UniformValues { - uniforms::u_matrix::Value{ tile.translatedMatrix(properties.get<FillTranslate>(), - properties.get<FillTranslateAnchor>(), - state) }, - uniforms::u_opacity::Value{ properties.get<FillOpacity>() }, - uniforms::u_color::Value{ properties.get<FillColor>() }, - uniforms::u_outline_color::Value{ outlineColor }, + uniforms::u_matrix::Value{ + tile.translatedMatrix(properties.get<FillTranslate>(), + properties.get<FillTranslateAnchor>(), + state) + }, uniforms::u_world::Value{ context.viewport.getCurrentValue().size }, }, - vertexBuffer, + *bucket.vertexBuffer, indexBuffer, - segments + segments, + bucket.paintPropertyBinders.at(layer.getID()), + properties, + state.getZoom() ); }; if (properties.get<FillAntialias>() && !layer.impl->paint.unevaluated.get<FillOutlineColor>().isUndefined() && pass == RenderPass::Translucent) { draw(2, parameters.programs.fillOutline, - properties.get<FillOutlineColor>(), gl::Lines { 2.0f }, - *bucket.vertexBuffer, *bucket.lineIndexBuffer, bucket.lineSegments); } // Only draw the fill when it's opaque and we're drawing opaque fragments, // or when it's translucent and we're drawing translucent fragments. - if ((properties.get<FillColor>().a >= 1.0f && properties.get<FillOpacity>() >= 1.0f) == (pass == RenderPass::Opaque)) { + if ((properties.get<FillColor>().constantOr(Color()).a >= 1.0f + && properties.get<FillOpacity>().constantOr(0) >= 1.0f) == (pass == RenderPass::Opaque)) { draw(1, parameters.programs.fill, - properties.get<FillOutlineColor>(), gl::Triangles(), - *bucket.vertexBuffer, *bucket.triangleIndexBuffer, bucket.triangleSegments); } @@ -136,9 +130,7 @@ void Painter::renderFill(PaintParameters& parameters, if (properties.get<FillAntialias>() && layer.impl->paint.unevaluated.get<FillOutlineColor>().isUndefined() && pass == RenderPass::Translucent) { draw(2, parameters.programs.fillOutline, - properties.get<FillColor>(), gl::Lines { 2.0f }, - *bucket.vertexBuffer, *bucket.lineIndexBuffer, bucket.lineSegments); } diff --git a/src/mbgl/renderer/painter_line.cpp b/src/mbgl/renderer/painter_line.cpp index 012746d2f2..4e19f841b1 100644 --- a/src/mbgl/renderer/painter_line.cpp +++ b/src/mbgl/renderer/painter_line.cpp @@ -33,7 +33,10 @@ void Painter::renderLine(PaintParameters& parameters, std::move(uniformValues), *bucket.vertexBuffer, *bucket.indexBuffer, - bucket.segments + bucket.segments, + bucket.paintPropertyBinders.at(layer.getID()), + properties, + state.getZoom() ); }; @@ -58,10 +61,8 @@ void Painter::renderLine(PaintParameters& parameters, lineAtlas->getSize().width)); } else if (!properties.get<LinePattern>().from.empty()) { - optional<SpriteAtlasPosition> posA = spriteAtlas->getPosition( - properties.get<LinePattern>().from, SpritePatternMode::Repeating); - optional<SpriteAtlasPosition> posB = spriteAtlas->getPosition( - properties.get<LinePattern>().to, SpritePatternMode::Repeating); + optional<SpriteAtlasElement> posA = spriteAtlas->getPattern(properties.get<LinePattern>().from); + optional<SpriteAtlasElement> posB = spriteAtlas->getPattern(properties.get<LinePattern>().to); if (!posA || !posB) return; diff --git a/src/mbgl/renderer/painter_raster.cpp b/src/mbgl/renderer/painter_raster.cpp index dcf2644140..c216955db8 100644 --- a/src/mbgl/renderer/painter_raster.cpp +++ b/src/mbgl/renderer/painter_raster.cpp @@ -49,6 +49,7 @@ void Painter::renderRaster(PaintParameters& parameters, return; const RasterPaintProperties::Evaluated& properties = layer.impl->paint.evaluated; + const RasterProgram::PaintPropertyBinders paintAttributeData(properties, 0); assert(bucket.texture); context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear); @@ -77,7 +78,10 @@ void Painter::renderRaster(PaintParameters& parameters, }, rasterVertexBuffer, tileTriangleIndexBuffer, - rasterSegments + rasterSegments, + paintAttributeData, + properties, + state.getZoom() ); } diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp index 39075976a0..48c2e7ff66 100644 --- a/src/mbgl/renderer/painter_symbol.cpp +++ b/src/mbgl/renderer/painter_symbol.cpp @@ -33,7 +33,9 @@ void Painter::renderSymbol(PaintParameters& parameters, auto draw = [&] (auto& program, auto&& uniformValues, const auto& buffers, - const SymbolPropertyValues& values_) + const SymbolPropertyValues& values_, + const auto& binders, + const auto& paintProperties) { // We clip symbols to their tile extent in still mode. const bool needsClipping = frame.mapMode == MapMode::Still; @@ -51,12 +53,16 @@ void Painter::renderSymbol(PaintParameters& parameters, std::move(uniformValues), *buffers.vertexBuffer, *buffers.indexBuffer, - buffers.segments + buffers.segments, + binders, + paintProperties, + state.getZoom() ); }; if (bucket.hasIconData()) { auto values = layer.impl->iconPropertyValues(layout); + auto paintPropertyValues = layer.impl->iconPaintProperties(); SpriteAtlas& atlas = *layer.impl->spriteAtlas; const bool iconScaled = values.paintSize != 1.0f || frame.pixelRatio != atlas.getPixelRatio() || bucket.iconsNeedLinear; @@ -66,24 +72,30 @@ void Painter::renderSymbol(PaintParameters& parameters, const Size texsize = atlas.getSize(); if (bucket.sdfIcons) { - if (values.hasHalo()) { + if (values.hasHalo) { draw(parameters.programs.symbolIconSDF, - SymbolSDFProgram::haloUniformValues(values, texsize, pixelsToGLUnits, tile, state, frame.pixelRatio), + SymbolSDFIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), bucket.icon, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).first, + paintPropertyValues); } - if (values.hasForeground()) { + if (values.hasFill) { draw(parameters.programs.symbolIconSDF, - SymbolSDFProgram::foregroundUniformValues(values, texsize, pixelsToGLUnits, tile, state, frame.pixelRatio), + SymbolSDFIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), bucket.icon, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).first, + paintPropertyValues); } } else { draw(parameters.programs.symbolIcon, SymbolIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state), bucket.icon, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).first, + paintPropertyValues); } } @@ -91,25 +103,33 @@ void Painter::renderSymbol(PaintParameters& parameters, glyphAtlas->bind(context, 0); auto values = layer.impl->textPropertyValues(layout); + auto paintPropertyValues = layer.impl->textPaintProperties(); const Size texsize = glyphAtlas->getSize(); - if (values.hasHalo()) { + if (values.hasHalo) { draw(parameters.programs.symbolGlyph, - SymbolSDFProgram::haloUniformValues(values, texsize, pixelsToGLUnits, tile, state, frame.pixelRatio), + SymbolSDFTextProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), bucket.text, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).second, + paintPropertyValues); } - if (values.hasForeground()) { + if (values.hasFill) { draw(parameters.programs.symbolGlyph, - SymbolSDFProgram::foregroundUniformValues(values, texsize, pixelsToGLUnits, tile, state, frame.pixelRatio), + SymbolSDFTextProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), bucket.text, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).second, + paintPropertyValues); } } if (bucket.hasCollisionBoxData()) { + static const style::PaintProperties<>::Evaluated properties {}; + static const CollisionBoxProgram::PaintPropertyBinders paintAttributeData(properties, 0); + programs->collisionBox.draw( context, gl::Lines { 1.0f }, @@ -124,7 +144,10 @@ void Painter::renderSymbol(PaintParameters& parameters, }, *bucket.collisionBox.vertexBuffer, *bucket.collisionBox.indexBuffer, - bucket.collisionBox.segments + bucket.collisionBox.segments, + paintAttributeData, + properties, + state.getZoom() ); } } diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp index 0f2c89339f..fa4178dda1 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/symbol_bucket.cpp @@ -1,19 +1,28 @@ #include <mbgl/renderer/symbol_bucket.hpp> #include <mbgl/renderer/painter.hpp> +#include <mbgl/style/bucket_parameters.hpp> #include <mbgl/style/layers/symbol_layer.hpp> +#include <mbgl/style/layers/symbol_layer_impl.hpp> namespace mbgl { using namespace style; -SymbolBucket::SymbolBucket(const MapMode mode_, - style::SymbolLayoutProperties::Evaluated layout_, +SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::Evaluated layout_, + const std::unordered_map<std::string, std::pair< + style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>& layerPaintProperties, + float zoom, bool sdfIcons_, bool iconsNeedLinear_) - : mode(mode_), - layout(std::move(layout_)), + : layout(std::move(layout_)), sdfIcons(sdfIcons_), iconsNeedLinear(iconsNeedLinear_) { + for (const auto& pair : layerPaintProperties) { + paintPropertyBinders.emplace(pair.first, std::make_pair( + SymbolIconProgram::PaintPropertyBinders(pair.second.first, zoom), + SymbolSDFTextProgram::PaintPropertyBinders(pair.second.second, zoom) + )); + } } void SymbolBucket::upload(gl::Context& context) { @@ -32,6 +41,11 @@ void SymbolBucket::upload(gl::Context& context) { collisionBox.indexBuffer = context.createIndexBuffer(std::move(collisionBox.lines)); } + for (auto& pair : paintPropertyBinders) { + pair.second.first.upload(context); + pair.second.second.upload(context); + } + uploaded = true; } diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/symbol_bucket.hpp index d62a61aab7..dcf3f5f495 100644 --- a/src/mbgl/renderer/symbol_bucket.hpp +++ b/src/mbgl/renderer/symbol_bucket.hpp @@ -16,8 +16,9 @@ namespace mbgl { class SymbolBucket : public Bucket { public: - SymbolBucket(const MapMode, - style::SymbolLayoutProperties::Evaluated, + SymbolBucket(style::SymbolLayoutProperties::Evaluated, + const std::unordered_map<std::string, std::pair<style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>&, + float zoom, bool sdfIcons, bool iconsNeedLinear); @@ -28,26 +29,29 @@ public: bool hasIconData() const; bool hasCollisionBoxData() const; - const MapMode mode; const style::SymbolLayoutProperties::Evaluated layout; const bool sdfIcons; const bool iconsNeedLinear; + std::unordered_map<std::string, std::pair< + SymbolIconProgram::PaintPropertyBinders, + SymbolSDFTextProgram::PaintPropertyBinders>> paintPropertyBinders; + struct TextBuffer { - gl::VertexVector<SymbolVertex> vertices; + gl::VertexVector<SymbolLayoutVertex> vertices; gl::IndexVector<gl::Triangles> triangles; - gl::SegmentVector<SymbolAttributes> segments; + gl::SegmentVector<SymbolTextAttributes> segments; - optional<gl::VertexBuffer<SymbolVertex>> vertexBuffer; + optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer; optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; } text; struct IconBuffer { - gl::VertexVector<SymbolVertex> vertices; + gl::VertexVector<SymbolLayoutVertex> vertices; gl::IndexVector<gl::Triangles> triangles; - gl::SegmentVector<SymbolAttributes> segments; + gl::SegmentVector<SymbolIconAttributes> segments; - optional<gl::VertexBuffer<SymbolVertex>> vertexBuffer; + optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer; optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; } icon; diff --git a/src/mbgl/shaders/circle.cpp b/src/mbgl/shaders/circle.cpp new file mode 100644 index 0000000000..592a883fb3 --- /dev/null +++ b/src/mbgl/shaders/circle.cpp @@ -0,0 +1,191 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/circle.hpp> + +namespace mbgl { +namespace shaders { + +const char* circle::name = "circle"; +const char* circle::vertexSource = R"MBGL_SHADER( +#ifdef GL_ES +precision highp float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif + +float evaluate_zoom_function_1(const vec4 values, const float t) { + if (t < 1.0) { + return mix(values[0], values[1], t); + } else if (t < 2.0) { + return mix(values[1], values[2], t - 1.0); + } else { + return mix(values[2], values[3], t - 2.0); + } +} +vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 value2, const vec4 value3, const float t) { + if (t < 1.0) { + return mix(value0, value1, t); + } else if (t < 2.0) { + return mix(value1, value2, t - 1.0); + } else { + return mix(value2, value3, t - 2.0); + } +} + +// The offset depends on how many pixels are between the world origin and the edge of the tile: +// vec2 offset = mod(pixel_coord, size) +// +// At high zoom levels there are a ton of pixels between the world origin and the edge of the tile. +// The glsl spec only guarantees 16 bits of precision for highp floats. We need more than that. +// +// The pixel_coord is passed in as two 16 bit values: +// pixel_coord_upper = floor(pixel_coord / 2^16) +// pixel_coord_lower = mod(pixel_coord, 2^16) +// +// The offset is calculated in a series of steps that should preserve this precision: +vec2 get_pattern_pos(const vec2 pixel_coord_upper, const vec2 pixel_coord_lower, + const vec2 pattern_size, const float tile_units_to_pixels, const vec2 pos) { + + vec2 offset = mod(mod(mod(pixel_coord_upper, pattern_size) * 256.0, pattern_size) * 256.0 + pixel_coord_lower, pattern_size); + return (tile_units_to_pixels * pos + offset) / pattern_size; +} +uniform mat4 u_matrix; +uniform bool u_scale_with_map; +uniform vec2 u_extrude_scale; + +attribute vec2 a_pos; + +uniform lowp float a_color_t; +attribute lowp vec4 a_color_min; +attribute lowp vec4 a_color_max; +varying lowp vec4 color; +uniform lowp float a_radius_t; +attribute mediump float a_radius_min; +attribute mediump float a_radius_max; +varying mediump float radius; +uniform lowp float a_blur_t; +attribute lowp float a_blur_min; +attribute lowp float a_blur_max; +varying lowp float blur; +uniform lowp float a_opacity_t; +attribute lowp float a_opacity_min; +attribute lowp float a_opacity_max; +varying lowp float opacity; +uniform lowp float a_stroke_color_t; +attribute lowp vec4 a_stroke_color_min; +attribute lowp vec4 a_stroke_color_max; +varying lowp vec4 stroke_color; +uniform lowp float a_stroke_width_t; +attribute mediump float a_stroke_width_min; +attribute mediump float a_stroke_width_max; +varying mediump float stroke_width; +uniform lowp float a_stroke_opacity_t; +attribute lowp float a_stroke_opacity_min; +attribute lowp float a_stroke_opacity_max; +varying lowp float stroke_opacity; + +varying vec2 v_extrude; +varying lowp float v_antialiasblur; + +void main(void) { + color = mix(a_color_min, a_color_max, a_color_t); + radius = mix(a_radius_min, a_radius_max, a_radius_t); + blur = mix(a_blur_min, a_blur_max, a_blur_t); + opacity = mix(a_opacity_min, a_opacity_max, a_opacity_t); + stroke_color = mix(a_stroke_color_min, a_stroke_color_max, a_stroke_color_t); + stroke_width = mix(a_stroke_width_min, a_stroke_width_max, a_stroke_width_t); + stroke_opacity = mix(a_stroke_opacity_min, a_stroke_opacity_max, a_stroke_opacity_t); + + // unencode the extrusion vector that we snuck into the a_pos vector + v_extrude = vec2(mod(a_pos, 2.0) * 2.0 - 1.0); + + vec2 extrude = v_extrude * (radius + stroke_width) * u_extrude_scale; + // multiply a_pos by 0.5, since we had it * 2 in order to sneak + // in extrusion data + gl_Position = u_matrix * vec4(floor(a_pos * 0.5), 0, 1); + + if (u_scale_with_map) { + gl_Position.xy += extrude; + } else { + gl_Position.xy += extrude * gl_Position.w; + } + + // This is a minimum blur distance that serves as a faux-antialiasing for + // the circle. since blur is a ratio of the circle's size and the intent is + // to keep the blur at roughly 1px, the two are inversely related. + v_antialiasblur = 1.0 / DEVICE_PIXEL_RATIO / (radius + stroke_width); +} + +)MBGL_SHADER"; +const char* circle::fragmentSource = R"MBGL_SHADER( +#ifdef GL_ES +precision mediump float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif +varying lowp vec4 color; +varying mediump float radius; +varying lowp float blur; +varying lowp float opacity; +varying lowp vec4 stroke_color; +varying mediump float stroke_width; +varying lowp float stroke_opacity; + +varying vec2 v_extrude; +varying lowp float v_antialiasblur; + +void main() { + + + + + + + + + float extrude_length = length(v_extrude); + float antialiased_blur = -max(blur, v_antialiasblur); + + float opacity_t = smoothstep(0.0, antialiased_blur, extrude_length - 1.0); + + float color_t = stroke_width < 0.01 ? 0.0 : smoothstep( + antialiased_blur, + 0.0, + extrude_length - radius / (radius + stroke_width) + ); + + gl_FragColor = opacity_t * mix(color * opacity, stroke_color * stroke_opacity, color_t); + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(1.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/circle.hpp b/src/mbgl/shaders/circle.hpp new file mode 100644 index 0000000000..d14b26b783 --- /dev/null +++ b/src/mbgl/shaders/circle.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class circle { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/collision_box.cpp b/src/mbgl/shaders/collision_box.cpp new file mode 100644 index 0000000000..6f2b9f3824 --- /dev/null +++ b/src/mbgl/shaders/collision_box.cpp @@ -0,0 +1,128 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/collision_box.hpp> + +namespace mbgl { +namespace shaders { + +const char* collision_box::name = "collision_box"; +const char* collision_box::vertexSource = R"MBGL_SHADER( +#ifdef GL_ES +precision highp float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif + +float evaluate_zoom_function_1(const vec4 values, const float t) { + if (t < 1.0) { + return mix(values[0], values[1], t); + } else if (t < 2.0) { + return mix(values[1], values[2], t - 1.0); + } else { + return mix(values[2], values[3], t - 2.0); + } +} +vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 value2, const vec4 value3, const float t) { + if (t < 1.0) { + return mix(value0, value1, t); + } else if (t < 2.0) { + return mix(value1, value2, t - 1.0); + } else { + return mix(value2, value3, t - 2.0); + } +} + +// The offset depends on how many pixels are between the world origin and the edge of the tile: +// vec2 offset = mod(pixel_coord, size) +// +// At high zoom levels there are a ton of pixels between the world origin and the edge of the tile. +// The glsl spec only guarantees 16 bits of precision for highp floats. We need more than that. +// +// The pixel_coord is passed in as two 16 bit values: +// pixel_coord_upper = floor(pixel_coord / 2^16) +// pixel_coord_lower = mod(pixel_coord, 2^16) +// +// The offset is calculated in a series of steps that should preserve this precision: +vec2 get_pattern_pos(const vec2 pixel_coord_upper, const vec2 pixel_coord_lower, + const vec2 pattern_size, const float tile_units_to_pixels, const vec2 pos) { + + vec2 offset = mod(mod(mod(pixel_coord_upper, pattern_size) * 256.0, pattern_size) * 256.0 + pixel_coord_lower, pattern_size); + return (tile_units_to_pixels * pos + offset) / pattern_size; +} +attribute vec2 a_pos; +attribute vec2 a_extrude; +attribute vec2 a_data; + +uniform mat4 u_matrix; +uniform float u_scale; + +varying float v_max_zoom; +varying float v_placement_zoom; + +void main() { + gl_Position = u_matrix * vec4(a_pos + a_extrude / u_scale, 0.0, 1.0); + + v_max_zoom = a_data.x; + v_placement_zoom = a_data.y; +} + +)MBGL_SHADER"; +const char* collision_box::fragmentSource = R"MBGL_SHADER( +#ifdef GL_ES +precision mediump float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif +uniform float u_zoom; +uniform float u_maxzoom; + +varying float v_max_zoom; +varying float v_placement_zoom; + +void main() { + + float alpha = 0.5; + + gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0) * alpha; + + if (v_placement_zoom > u_zoom) { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * alpha; + } + + if (u_zoom >= v_max_zoom) { + gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0) * alpha * 0.25; + } + + if (v_placement_zoom >= u_maxzoom) { + gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0) * alpha * 0.2; + } +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/collision_box.hpp b/src/mbgl/shaders/collision_box.hpp new file mode 100644 index 0000000000..e0f70c7968 --- /dev/null +++ b/src/mbgl/shaders/collision_box.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class collision_box { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/debug.cpp b/src/mbgl/shaders/debug.cpp new file mode 100644 index 0000000000..2659b2ca79 --- /dev/null +++ b/src/mbgl/shaders/debug.cpp @@ -0,0 +1,100 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/debug.hpp> + +namespace mbgl { +namespace shaders { + +const char* debug::name = "debug"; +const char* debug::vertexSource = R"MBGL_SHADER( +#ifdef GL_ES +precision highp float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif + +float evaluate_zoom_function_1(const vec4 values, const float t) { + if (t < 1.0) { + return mix(values[0], values[1], t); + } else if (t < 2.0) { + return mix(values[1], values[2], t - 1.0); + } else { + return mix(values[2], values[3], t - 2.0); + } +} +vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 value2, const vec4 value3, const float t) { + if (t < 1.0) { + return mix(value0, value1, t); + } else if (t < 2.0) { + return mix(value1, value2, t - 1.0); + } else { + return mix(value2, value3, t - 2.0); + } +} + +// The offset depends on how many pixels are between the world origin and the edge of the tile: +// vec2 offset = mod(pixel_coord, size) +// +// At high zoom levels there are a ton of pixels between the world origin and the edge of the tile. +// The glsl spec only guarantees 16 bits of precision for highp floats. We need more than that. +// +// The pixel_coord is passed in as two 16 bit values: +// pixel_coord_upper = floor(pixel_coord / 2^16) +// pixel_coord_lower = mod(pixel_coord, 2^16) +// +// The offset is calculated in a series of steps that should preserve this precision: +vec2 get_pattern_pos(const vec2 pixel_coord_upper, const vec2 pixel_coord_lower, + const vec2 pattern_size, const float tile_units_to_pixels, const vec2 pos) { + + vec2 offset = mod(mod(mod(pixel_coord_upper, pattern_size) * 256.0, pattern_size) * 256.0 + pixel_coord_lower, pattern_size); + return (tile_units_to_pixels * pos + offset) / pattern_size; +} +attribute vec2 a_pos; + +uniform mat4 u_matrix; + +void main() { + gl_Position = u_matrix * vec4(a_pos, step(32767.0, a_pos.x), 1); +} + +)MBGL_SHADER"; +const char* debug::fragmentSource = R"MBGL_SHADER( +#ifdef GL_ES +precision mediump float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif +uniform lowp vec4 u_color; + +void main() { + gl_FragColor = u_color; +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/debug.hpp b/src/mbgl/shaders/debug.hpp new file mode 100644 index 0000000000..207c7bf075 --- /dev/null +++ b/src/mbgl/shaders/debug.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class debug { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/fill.cpp b/src/mbgl/shaders/fill.cpp new file mode 100644 index 0000000000..066adee447 --- /dev/null +++ b/src/mbgl/shaders/fill.cpp @@ -0,0 +1,120 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/fill.hpp> + +namespace mbgl { +namespace shaders { + +const char* fill::name = "fill"; +const char* fill::vertexSource = R"MBGL_SHADER( +#ifdef GL_ES +precision highp float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif + +float evaluate_zoom_function_1(const vec4 values, const float t) { + if (t < 1.0) { + return mix(values[0], values[1], t); + } else if (t < 2.0) { + return mix(values[1], values[2], t - 1.0); + } else { + return mix(values[2], values[3], t - 2.0); + } +} +vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 value2, const vec4 value3, const float t) { + if (t < 1.0) { + return mix(value0, value1, t); + } else if (t < 2.0) { + return mix(value1, value2, t - 1.0); + } else { + return mix(value2, value3, t - 2.0); + } +} + +// The offset depends on how many pixels are between the world origin and the edge of the tile: +// vec2 offset = mod(pixel_coord, size) +// +// At high zoom levels there are a ton of pixels between the world origin and the edge of the tile. +// The glsl spec only guarantees 16 bits of precision for highp floats. We need more than that. +// +// The pixel_coord is passed in as two 16 bit values: +// pixel_coord_upper = floor(pixel_coord / 2^16) +// pixel_coord_lower = mod(pixel_coord, 2^16) +// +// The offset is calculated in a series of steps that should preserve this precision: +vec2 get_pattern_pos(const vec2 pixel_coord_upper, const vec2 pixel_coord_lower, + const vec2 pattern_size, const float tile_units_to_pixels, const vec2 pos) { + + vec2 offset = mod(mod(mod(pixel_coord_upper, pattern_size) * 256.0, pattern_size) * 256.0 + pixel_coord_lower, pattern_size); + return (tile_units_to_pixels * pos + offset) / pattern_size; +} +attribute vec2 a_pos; + +uniform mat4 u_matrix; + +uniform lowp float a_color_t; +attribute lowp vec4 a_color_min; +attribute lowp vec4 a_color_max; +varying lowp vec4 color; +uniform lowp float a_opacity_t; +attribute lowp float a_opacity_min; +attribute lowp float a_opacity_max; +varying lowp float opacity; + +void main() { + color = mix(a_color_min, a_color_max, a_color_t); + opacity = mix(a_opacity_min, a_opacity_max, a_opacity_t); + + gl_Position = u_matrix * vec4(a_pos, 0, 1); +} + +)MBGL_SHADER"; +const char* fill::fragmentSource = R"MBGL_SHADER( +#ifdef GL_ES +precision mediump float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif +varying lowp vec4 color; +varying lowp float opacity; + +void main() { + + + + gl_FragColor = color * opacity; + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(1.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/fill.hpp b/src/mbgl/shaders/fill.hpp new file mode 100644 index 0000000000..29fede7b55 --- /dev/null +++ b/src/mbgl/shaders/fill.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class fill { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/fill_outline.cpp b/src/mbgl/shaders/fill_outline.cpp new file mode 100644 index 0000000000..0f0f3806a9 --- /dev/null +++ b/src/mbgl/shaders/fill_outline.cpp @@ -0,0 +1,128 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/fill_outline.hpp> + +namespace mbgl { +namespace shaders { + +const char* fill_outline::name = "fill_outline"; +const char* fill_outline::vertexSource = R"MBGL_SHADER( +#ifdef GL_ES +precision highp float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif + +float evaluate_zoom_function_1(const vec4 values, const float t) { + if (t < 1.0) { + return mix(values[0], values[1], t); + } else if (t < 2.0) { + return mix(values[1], values[2], t - 1.0); + } else { + return mix(values[2], values[3], t - 2.0); + } +} +vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 value2, const vec4 value3, const float t) { + if (t < 1.0) { + return mix(value0, value1, t); + } else if (t < 2.0) { + return mix(value1, value2, t - 1.0); + } else { + return mix(value2, value3, t - 2.0); + } +} + +// The offset depends on how many pixels are between the world origin and the edge of the tile: +// vec2 offset = mod(pixel_coord, size) +// +// At high zoom levels there are a ton of pixels between the world origin and the edge of the tile. +// The glsl spec only guarantees 16 bits of precision for highp floats. We need more than that. +// +// The pixel_coord is passed in as two 16 bit values: +// pixel_coord_upper = floor(pixel_coord / 2^16) +// pixel_coord_lower = mod(pixel_coord, 2^16) +// +// The offset is calculated in a series of steps that should preserve this precision: +vec2 get_pattern_pos(const vec2 pixel_coord_upper, const vec2 pixel_coord_lower, + const vec2 pattern_size, const float tile_units_to_pixels, const vec2 pos) { + + vec2 offset = mod(mod(mod(pixel_coord_upper, pattern_size) * 256.0, pattern_size) * 256.0 + pixel_coord_lower, pattern_size); + return (tile_units_to_pixels * pos + offset) / pattern_size; +} +attribute vec2 a_pos; + +uniform mat4 u_matrix; +uniform vec2 u_world; + +varying vec2 v_pos; + +uniform lowp float a_outline_color_t; +attribute lowp vec4 a_outline_color_min; +attribute lowp vec4 a_outline_color_max; +varying lowp vec4 outline_color; +uniform lowp float a_opacity_t; +attribute lowp float a_opacity_min; +attribute lowp float a_opacity_max; +varying lowp float opacity; + +void main() { + outline_color = mix(a_outline_color_min, a_outline_color_max, a_outline_color_t); + opacity = mix(a_opacity_min, a_opacity_max, a_opacity_t); + + gl_Position = u_matrix * vec4(a_pos, 0, 1); + v_pos = (gl_Position.xy / gl_Position.w + 1.0) / 2.0 * u_world; +} + +)MBGL_SHADER"; +const char* fill_outline::fragmentSource = R"MBGL_SHADER( +#ifdef GL_ES +precision mediump float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif +varying lowp vec4 outline_color; +varying lowp float opacity; + +varying vec2 v_pos; + +void main() { + + + + float dist = length(v_pos - gl_FragCoord.xy); + float alpha = smoothstep(1.0, 0.0, dist); + gl_FragColor = outline_color * (alpha * opacity); + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(1.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/fill_outline.hpp b/src/mbgl/shaders/fill_outline.hpp new file mode 100644 index 0000000000..ef685e62fa --- /dev/null +++ b/src/mbgl/shaders/fill_outline.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class fill_outline { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/fill_outline_pattern.cpp b/src/mbgl/shaders/fill_outline_pattern.cpp new file mode 100644 index 0000000000..3921a83e6b --- /dev/null +++ b/src/mbgl/shaders/fill_outline_pattern.cpp @@ -0,0 +1,156 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/fill_outline_pattern.hpp> + +namespace mbgl { +namespace shaders { + +const char* fill_outline_pattern::name = "fill_outline_pattern"; +const char* fill_outline_pattern::vertexSource = R"MBGL_SHADER( +#ifdef GL_ES +precision highp float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif + +float evaluate_zoom_function_1(const vec4 values, const float t) { + if (t < 1.0) { + return mix(values[0], values[1], t); + } else if (t < 2.0) { + return mix(values[1], values[2], t - 1.0); + } else { + return mix(values[2], values[3], t - 2.0); + } +} +vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 value2, const vec4 value3, const float t) { + if (t < 1.0) { + return mix(value0, value1, t); + } else if (t < 2.0) { + return mix(value1, value2, t - 1.0); + } else { + return mix(value2, value3, t - 2.0); + } +} + +// The offset depends on how many pixels are between the world origin and the edge of the tile: +// vec2 offset = mod(pixel_coord, size) +// +// At high zoom levels there are a ton of pixels between the world origin and the edge of the tile. +// The glsl spec only guarantees 16 bits of precision for highp floats. We need more than that. +// +// The pixel_coord is passed in as two 16 bit values: +// pixel_coord_upper = floor(pixel_coord / 2^16) +// pixel_coord_lower = mod(pixel_coord, 2^16) +// +// The offset is calculated in a series of steps that should preserve this precision: +vec2 get_pattern_pos(const vec2 pixel_coord_upper, const vec2 pixel_coord_lower, + const vec2 pattern_size, const float tile_units_to_pixels, const vec2 pos) { + + vec2 offset = mod(mod(mod(pixel_coord_upper, pattern_size) * 256.0, pattern_size) * 256.0 + pixel_coord_lower, pattern_size); + return (tile_units_to_pixels * pos + offset) / pattern_size; +} +uniform mat4 u_matrix; +uniform vec2 u_world; +uniform vec2 u_pattern_size_a; +uniform vec2 u_pattern_size_b; +uniform vec2 u_pixel_coord_upper; +uniform vec2 u_pixel_coord_lower; +uniform float u_scale_a; +uniform float u_scale_b; +uniform float u_tile_units_to_pixels; + +attribute vec2 a_pos; + +varying vec2 v_pos_a; +varying vec2 v_pos_b; +varying vec2 v_pos; + +uniform lowp float a_opacity_t; +attribute lowp float a_opacity_min; +attribute lowp float a_opacity_max; +varying lowp float opacity; + +void main() { + opacity = mix(a_opacity_min, a_opacity_max, a_opacity_t); + + gl_Position = u_matrix * vec4(a_pos, 0, 1); + + v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_a * u_pattern_size_a, u_tile_units_to_pixels, a_pos); + v_pos_b = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_b * u_pattern_size_b, u_tile_units_to_pixels, a_pos); + + v_pos = (gl_Position.xy / gl_Position.w + 1.0) / 2.0 * u_world; +} + +)MBGL_SHADER"; +const char* fill_outline_pattern::fragmentSource = R"MBGL_SHADER( +#ifdef GL_ES +precision mediump float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif +uniform vec2 u_pattern_tl_a; +uniform vec2 u_pattern_br_a; +uniform vec2 u_pattern_tl_b; +uniform vec2 u_pattern_br_b; +uniform float u_mix; + +uniform sampler2D u_image; + +varying vec2 v_pos_a; +varying vec2 v_pos_b; +varying vec2 v_pos; + +varying lowp float opacity; + +void main() { + + + vec2 imagecoord = mod(v_pos_a, 1.0); + vec2 pos = mix(u_pattern_tl_a, u_pattern_br_a, imagecoord); + vec4 color1 = texture2D(u_image, pos); + + vec2 imagecoord_b = mod(v_pos_b, 1.0); + vec2 pos2 = mix(u_pattern_tl_b, u_pattern_br_b, imagecoord_b); + vec4 color2 = texture2D(u_image, pos2); + + // find distance to outline for alpha interpolation + + float dist = length(v_pos - gl_FragCoord.xy); + float alpha = smoothstep(1.0, 0.0, dist); + + + gl_FragColor = mix(color1, color2, u_mix) * alpha * opacity; + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(1.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/fill_outline_pattern.hpp b/src/mbgl/shaders/fill_outline_pattern.hpp new file mode 100644 index 0000000000..e1c7a173f4 --- /dev/null +++ b/src/mbgl/shaders/fill_outline_pattern.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class fill_outline_pattern { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/fill_pattern.cpp b/src/mbgl/shaders/fill_pattern.cpp new file mode 100644 index 0000000000..822a0f7b8f --- /dev/null +++ b/src/mbgl/shaders/fill_pattern.cpp @@ -0,0 +1,145 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/fill_pattern.hpp> + +namespace mbgl { +namespace shaders { + +const char* fill_pattern::name = "fill_pattern"; +const char* fill_pattern::vertexSource = R"MBGL_SHADER( +#ifdef GL_ES +precision highp float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif + +float evaluate_zoom_function_1(const vec4 values, const float t) { + if (t < 1.0) { + return mix(values[0], values[1], t); + } else if (t < 2.0) { + return mix(values[1], values[2], t - 1.0); + } else { + return mix(values[2], values[3], t - 2.0); + } +} +vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 value2, const vec4 value3, const float t) { + if (t < 1.0) { + return mix(value0, value1, t); + } else if (t < 2.0) { + return mix(value1, value2, t - 1.0); + } else { + return mix(value2, value3, t - 2.0); + } +} + +// The offset depends on how many pixels are between the world origin and the edge of the tile: +// vec2 offset = mod(pixel_coord, size) +// +// At high zoom levels there are a ton of pixels between the world origin and the edge of the tile. +// The glsl spec only guarantees 16 bits of precision for highp floats. We need more than that. +// +// The pixel_coord is passed in as two 16 bit values: +// pixel_coord_upper = floor(pixel_coord / 2^16) +// pixel_coord_lower = mod(pixel_coord, 2^16) +// +// The offset is calculated in a series of steps that should preserve this precision: +vec2 get_pattern_pos(const vec2 pixel_coord_upper, const vec2 pixel_coord_lower, + const vec2 pattern_size, const float tile_units_to_pixels, const vec2 pos) { + + vec2 offset = mod(mod(mod(pixel_coord_upper, pattern_size) * 256.0, pattern_size) * 256.0 + pixel_coord_lower, pattern_size); + return (tile_units_to_pixels * pos + offset) / pattern_size; +} +uniform mat4 u_matrix; +uniform vec2 u_pattern_size_a; +uniform vec2 u_pattern_size_b; +uniform vec2 u_pixel_coord_upper; +uniform vec2 u_pixel_coord_lower; +uniform float u_scale_a; +uniform float u_scale_b; +uniform float u_tile_units_to_pixels; + +attribute vec2 a_pos; + +varying vec2 v_pos_a; +varying vec2 v_pos_b; + +uniform lowp float a_opacity_t; +attribute lowp float a_opacity_min; +attribute lowp float a_opacity_max; +varying lowp float opacity; + +void main() { + opacity = mix(a_opacity_min, a_opacity_max, a_opacity_t); + + gl_Position = u_matrix * vec4(a_pos, 0, 1); + + v_pos_a = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_a * u_pattern_size_a, u_tile_units_to_pixels, a_pos); + v_pos_b = get_pattern_pos(u_pixel_coord_upper, u_pixel_coord_lower, u_scale_b * u_pattern_size_b, u_tile_units_to_pixels, a_pos); +} + +)MBGL_SHADER"; +const char* fill_pattern::fragmentSource = R"MBGL_SHADER( +#ifdef GL_ES +precision mediump float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif +uniform vec2 u_pattern_tl_a; +uniform vec2 u_pattern_br_a; +uniform vec2 u_pattern_tl_b; +uniform vec2 u_pattern_br_b; +uniform float u_mix; + +uniform sampler2D u_image; + +varying vec2 v_pos_a; +varying vec2 v_pos_b; + +varying lowp float opacity; + +void main() { + + + vec2 imagecoord = mod(v_pos_a, 1.0); + vec2 pos = mix(u_pattern_tl_a, u_pattern_br_a, imagecoord); + vec4 color1 = texture2D(u_image, pos); + + vec2 imagecoord_b = mod(v_pos_b, 1.0); + vec2 pos2 = mix(u_pattern_tl_b, u_pattern_br_b, imagecoord_b); + vec4 color2 = texture2D(u_image, pos2); + + gl_FragColor = mix(color1, color2, u_mix) * opacity; + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(1.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/fill_pattern.hpp b/src/mbgl/shaders/fill_pattern.hpp new file mode 100644 index 0000000000..4d09519ed8 --- /dev/null +++ b/src/mbgl/shaders/fill_pattern.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class fill_pattern { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/line.cpp b/src/mbgl/shaders/line.cpp new file mode 100644 index 0000000000..59fa9f0cf2 --- /dev/null +++ b/src/mbgl/shaders/line.cpp @@ -0,0 +1,216 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/line.hpp> + +namespace mbgl { +namespace shaders { + +const char* line::name = "line"; +const char* line::vertexSource = R"MBGL_SHADER( +#ifdef GL_ES +precision highp float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif + +float evaluate_zoom_function_1(const vec4 values, const float t) { + if (t < 1.0) { + return mix(values[0], values[1], t); + } else if (t < 2.0) { + return mix(values[1], values[2], t - 1.0); + } else { + return mix(values[2], values[3], t - 2.0); + } +} +vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 value2, const vec4 value3, const float t) { + if (t < 1.0) { + return mix(value0, value1, t); + } else if (t < 2.0) { + return mix(value1, value2, t - 1.0); + } else { + return mix(value2, value3, t - 2.0); + } +} + +// The offset depends on how many pixels are between the world origin and the edge of the tile: +// vec2 offset = mod(pixel_coord, size) +// +// At high zoom levels there are a ton of pixels between the world origin and the edge of the tile. +// The glsl spec only guarantees 16 bits of precision for highp floats. We need more than that. +// +// The pixel_coord is passed in as two 16 bit values: +// pixel_coord_upper = floor(pixel_coord / 2^16) +// pixel_coord_lower = mod(pixel_coord, 2^16) +// +// The offset is calculated in a series of steps that should preserve this precision: +vec2 get_pattern_pos(const vec2 pixel_coord_upper, const vec2 pixel_coord_lower, + const vec2 pattern_size, const float tile_units_to_pixels, const vec2 pos) { + + vec2 offset = mod(mod(mod(pixel_coord_upper, pattern_size) * 256.0, pattern_size) * 256.0 + pixel_coord_lower, pattern_size); + return (tile_units_to_pixels * pos + offset) / pattern_size; +} + + +// the distance over which the line edge fades out. +// Retina devices need a smaller distance to avoid aliasing. +#define ANTIALIASING 1.0 / DEVICE_PIXEL_RATIO / 2.0 + +// floor(127 / 2) == 63.0 +// the maximum allowed miter limit is 2.0 at the moment. the extrude normal is +// stored in a byte (-128..127). we scale regular normals up to length 63, but +// there are also "special" normals that have a bigger length (of up to 126 in +// this case). +// #define scale 63.0 +#define scale 0.015873016 + +attribute vec2 a_pos; +attribute vec4 a_data; + +uniform mat4 u_matrix; +uniform mediump float u_ratio; +uniform mediump float u_width; +uniform vec2 u_gl_units_to_pixels; + +varying vec2 v_normal; +varying vec2 v_width2; +varying float v_gamma_scale; + +uniform lowp float a_color_t; +attribute lowp vec4 a_color_min; +attribute lowp vec4 a_color_max; +varying lowp vec4 color; +uniform lowp float a_blur_t; +attribute lowp float a_blur_min; +attribute lowp float a_blur_max; +varying lowp float blur; +uniform lowp float a_opacity_t; +attribute lowp float a_opacity_min; +attribute lowp float a_opacity_max; +varying lowp float opacity; +uniform lowp float a_gapwidth_t; +attribute mediump float a_gapwidth_min; +attribute mediump float a_gapwidth_max; +varying mediump float gapwidth; +uniform lowp float a_offset_t; +attribute lowp float a_offset_min; +attribute lowp float a_offset_max; +varying lowp float offset; + +void main() { + color = mix(a_color_min, a_color_max, a_color_t); + blur = mix(a_blur_min, a_blur_max, a_blur_t); + opacity = mix(a_opacity_min, a_opacity_max, a_opacity_t); + gapwidth = mix(a_gapwidth_min, a_gapwidth_max, a_gapwidth_t); + offset = mix(a_offset_min, a_offset_max, a_offset_t); + + vec2 a_extrude = a_data.xy - 128.0; + float a_direction = mod(a_data.z, 4.0) - 1.0; + + // We store the texture normals in the most insignificant bit + // transform y so that 0 => -1 and 1 => 1 + // In the texture normal, x is 0 if the normal points straight up/down and 1 if it's a round cap + // y is 1 if the normal points up, and -1 if it points down + mediump vec2 normal = mod(a_pos, 2.0); + normal.y = sign(normal.y - 0.5); + v_normal = normal; + + + // these transformations used to be applied in the JS and native code bases. + // moved them into the shader for clarity and simplicity. + gapwidth = gapwidth / 2.0; + float width = u_width / 2.0; + offset = -1.0 * offset; + + float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0); + float outset = gapwidth + width * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING; + + // Scale the extrusion vector down to a normal and then up by the line width + // of this vertex. + mediump vec2 dist = outset * a_extrude * scale; + + // Calculate the offset when drawing a line that is to the side of the actual line. + // We do this by creating a vector that points towards the extrude, but rotate + // it when we're drawing round end points (a_direction = -1 or 1) since their + // extrude vector points in another direction. + mediump float u = 0.5 * a_direction; + mediump float t = 1.0 - abs(u); + mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t); + + // Remove the texture normal bit to get the position + vec2 pos = floor(a_pos * 0.5); + + vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0); + gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude; + + // calculate how much the perspective view squishes or stretches the extrude + float extrude_length_without_perspective = length(dist); + float extrude_length_with_perspective = length(projected_extrude.xy / gl_Position.w * u_gl_units_to_pixels); + v_gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective; + + v_width2 = vec2(outset, inset); +} + +)MBGL_SHADER"; +const char* line::fragmentSource = R"MBGL_SHADER( +#ifdef GL_ES +precision mediump float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif +varying lowp vec4 color; +varying lowp float blur; +varying lowp float opacity; + +varying vec2 v_width2; +varying vec2 v_normal; +varying float v_gamma_scale; + +void main() { + + + + + // Calculate the distance of the pixel from the line in pixels. + float dist = length(v_normal) * v_width2.s; + + // Calculate the antialiasing fade factor. This is either when fading in + // the line in case of an offset line (v_width2.t) or when fading out + // (v_width2.s) + float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * v_gamma_scale; + float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0); + + gl_FragColor = color * (alpha * opacity); + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(1.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/line.hpp b/src/mbgl/shaders/line.hpp new file mode 100644 index 0000000000..c0d81e6202 --- /dev/null +++ b/src/mbgl/shaders/line.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class line { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/line_pattern.cpp b/src/mbgl/shaders/line_pattern.cpp new file mode 100644 index 0000000000..7f2a31ee44 --- /dev/null +++ b/src/mbgl/shaders/line_pattern.cpp @@ -0,0 +1,233 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/line_pattern.hpp> + +namespace mbgl { +namespace shaders { + +const char* line_pattern::name = "line_pattern"; +const char* line_pattern::vertexSource = R"MBGL_SHADER( +#ifdef GL_ES +precision highp float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif + +float evaluate_zoom_function_1(const vec4 values, const float t) { + if (t < 1.0) { + return mix(values[0], values[1], t); + } else if (t < 2.0) { + return mix(values[1], values[2], t - 1.0); + } else { + return mix(values[2], values[3], t - 2.0); + } +} +vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 value2, const vec4 value3, const float t) { + if (t < 1.0) { + return mix(value0, value1, t); + } else if (t < 2.0) { + return mix(value1, value2, t - 1.0); + } else { + return mix(value2, value3, t - 2.0); + } +} + +// The offset depends on how many pixels are between the world origin and the edge of the tile: +// vec2 offset = mod(pixel_coord, size) +// +// At high zoom levels there are a ton of pixels between the world origin and the edge of the tile. +// The glsl spec only guarantees 16 bits of precision for highp floats. We need more than that. +// +// The pixel_coord is passed in as two 16 bit values: +// pixel_coord_upper = floor(pixel_coord / 2^16) +// pixel_coord_lower = mod(pixel_coord, 2^16) +// +// The offset is calculated in a series of steps that should preserve this precision: +vec2 get_pattern_pos(const vec2 pixel_coord_upper, const vec2 pixel_coord_lower, + const vec2 pattern_size, const float tile_units_to_pixels, const vec2 pos) { + + vec2 offset = mod(mod(mod(pixel_coord_upper, pattern_size) * 256.0, pattern_size) * 256.0 + pixel_coord_lower, pattern_size); + return (tile_units_to_pixels * pos + offset) / pattern_size; +} +// floor(127 / 2) == 63.0 +// the maximum allowed miter limit is 2.0 at the moment. the extrude normal is +// stored in a byte (-128..127). we scale regular normals up to length 63, but +// there are also "special" normals that have a bigger length (of up to 126 in +// this case). +// #define scale 63.0 +#define scale 0.015873016 + +// We scale the distance before adding it to the buffers so that we can store +// long distances for long segments. Use this value to unscale the distance. +#define LINE_DISTANCE_SCALE 2.0 + +// the distance over which the line edge fades out. +// Retina devices need a smaller distance to avoid aliasing. +#define ANTIALIASING 1.0 / DEVICE_PIXEL_RATIO / 2.0 + +attribute vec2 a_pos; +attribute vec4 a_data; + +uniform mat4 u_matrix; +uniform mediump float u_ratio; +uniform mediump float u_width; +uniform vec2 u_gl_units_to_pixels; + +varying vec2 v_normal; +varying vec2 v_width2; +varying float v_linesofar; +varying float v_gamma_scale; + +uniform lowp float a_blur_t; +attribute lowp float a_blur_min; +attribute lowp float a_blur_max; +varying lowp float blur; +uniform lowp float a_opacity_t; +attribute lowp float a_opacity_min; +attribute lowp float a_opacity_max; +varying lowp float opacity; +uniform lowp float a_offset_t; +attribute lowp float a_offset_min; +attribute lowp float a_offset_max; +varying lowp float offset; +uniform lowp float a_gapwidth_t; +attribute mediump float a_gapwidth_min; +attribute mediump float a_gapwidth_max; +varying mediump float gapwidth; + +void main() { + blur = mix(a_blur_min, a_blur_max, a_blur_t); + opacity = mix(a_opacity_min, a_opacity_max, a_opacity_t); + offset = mix(a_offset_min, a_offset_max, a_offset_t); + gapwidth = mix(a_gapwidth_min, a_gapwidth_max, a_gapwidth_t); + + vec2 a_extrude = a_data.xy - 128.0; + float a_direction = mod(a_data.z, 4.0) - 1.0; + float a_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * LINE_DISTANCE_SCALE; + + // We store the texture normals in the most insignificant bit + // transform y so that 0 => -1 and 1 => 1 + // In the texture normal, x is 0 if the normal points straight up/down and 1 if it's a round cap + // y is 1 if the normal points up, and -1 if it points down + mediump vec2 normal = mod(a_pos, 2.0); + normal.y = sign(normal.y - 0.5); + v_normal = normal; + + // these transformations used to be applied in the JS and native code bases. + // moved them into the shader for clarity and simplicity. + gapwidth = gapwidth / 2.0; + float width = u_width / 2.0; + offset = -1.0 * offset; + + float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0); + float outset = gapwidth + width * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING; + + // Scale the extrusion vector down to a normal and then up by the line width + // of this vertex. + mediump vec2 dist = outset * a_extrude * scale; + + // Calculate the offset when drawing a line that is to the side of the actual line. + // We do this by creating a vector that points towards the extrude, but rotate + // it when we're drawing round end points (a_direction = -1 or 1) since their + // extrude vector points in another direction. + mediump float u = 0.5 * a_direction; + mediump float t = 1.0 - abs(u); + mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t); + + // Remove the texture normal bit to get the position + vec2 pos = floor(a_pos * 0.5); + + vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0); + gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude; + + // calculate how much the perspective view squishes or stretches the extrude + float extrude_length_without_perspective = length(dist); + float extrude_length_with_perspective = length(projected_extrude.xy / gl_Position.w * u_gl_units_to_pixels); + v_gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective; + + v_linesofar = a_linesofar; + v_width2 = vec2(outset, inset); +} + +)MBGL_SHADER"; +const char* line_pattern::fragmentSource = R"MBGL_SHADER( +#ifdef GL_ES +precision mediump float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif +uniform vec2 u_pattern_size_a; +uniform vec2 u_pattern_size_b; +uniform vec2 u_pattern_tl_a; +uniform vec2 u_pattern_br_a; +uniform vec2 u_pattern_tl_b; +uniform vec2 u_pattern_br_b; +uniform float u_fade; + +uniform sampler2D u_image; + +varying vec2 v_normal; +varying vec2 v_width2; +varying float v_linesofar; +varying float v_gamma_scale; + +varying lowp float blur; +varying lowp float opacity; + +void main() { + + + + // Calculate the distance of the pixel from the line in pixels. + float dist = length(v_normal) * v_width2.s; + + // Calculate the antialiasing fade factor. This is either when fading in + // the line in case of an offset line (v_width2.t) or when fading out + // (v_width2.s) + float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * v_gamma_scale; + float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0); + + float x_a = mod(v_linesofar / u_pattern_size_a.x, 1.0); + float x_b = mod(v_linesofar / u_pattern_size_b.x, 1.0); + float y_a = 0.5 + (v_normal.y * v_width2.s / u_pattern_size_a.y); + float y_b = 0.5 + (v_normal.y * v_width2.s / u_pattern_size_b.y); + vec2 pos_a = mix(u_pattern_tl_a, u_pattern_br_a, vec2(x_a, y_a)); + vec2 pos_b = mix(u_pattern_tl_b, u_pattern_br_b, vec2(x_b, y_b)); + + vec4 color = mix(texture2D(u_image, pos_a), texture2D(u_image, pos_b), u_fade); + + gl_FragColor = color * alpha * opacity; + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(1.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/line_pattern.hpp b/src/mbgl/shaders/line_pattern.hpp new file mode 100644 index 0000000000..0394e83fe0 --- /dev/null +++ b/src/mbgl/shaders/line_pattern.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class line_pattern { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/line_sdf.cpp b/src/mbgl/shaders/line_sdf.cpp new file mode 100644 index 0000000000..011c1c3738 --- /dev/null +++ b/src/mbgl/shaders/line_sdf.cpp @@ -0,0 +1,239 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/line_sdf.hpp> + +namespace mbgl { +namespace shaders { + +const char* line_sdf::name = "line_sdf"; +const char* line_sdf::vertexSource = R"MBGL_SHADER( +#ifdef GL_ES +precision highp float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif + +float evaluate_zoom_function_1(const vec4 values, const float t) { + if (t < 1.0) { + return mix(values[0], values[1], t); + } else if (t < 2.0) { + return mix(values[1], values[2], t - 1.0); + } else { + return mix(values[2], values[3], t - 2.0); + } +} +vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 value2, const vec4 value3, const float t) { + if (t < 1.0) { + return mix(value0, value1, t); + } else if (t < 2.0) { + return mix(value1, value2, t - 1.0); + } else { + return mix(value2, value3, t - 2.0); + } +} + +// The offset depends on how many pixels are between the world origin and the edge of the tile: +// vec2 offset = mod(pixel_coord, size) +// +// At high zoom levels there are a ton of pixels between the world origin and the edge of the tile. +// The glsl spec only guarantees 16 bits of precision for highp floats. We need more than that. +// +// The pixel_coord is passed in as two 16 bit values: +// pixel_coord_upper = floor(pixel_coord / 2^16) +// pixel_coord_lower = mod(pixel_coord, 2^16) +// +// The offset is calculated in a series of steps that should preserve this precision: +vec2 get_pattern_pos(const vec2 pixel_coord_upper, const vec2 pixel_coord_lower, + const vec2 pattern_size, const float tile_units_to_pixels, const vec2 pos) { + + vec2 offset = mod(mod(mod(pixel_coord_upper, pattern_size) * 256.0, pattern_size) * 256.0 + pixel_coord_lower, pattern_size); + return (tile_units_to_pixels * pos + offset) / pattern_size; +} +// floor(127 / 2) == 63.0 +// the maximum allowed miter limit is 2.0 at the moment. the extrude normal is +// stored in a byte (-128..127). we scale regular normals up to length 63, but +// there are also "special" normals that have a bigger length (of up to 126 in +// this case). +// #define scale 63.0 +#define scale 0.015873016 + +// We scale the distance before adding it to the buffers so that we can store +// long distances for long segments. Use this value to unscale the distance. +#define LINE_DISTANCE_SCALE 2.0 + +// the distance over which the line edge fades out. +// Retina devices need a smaller distance to avoid aliasing. +#define ANTIALIASING 1.0 / DEVICE_PIXEL_RATIO / 2.0 + +attribute vec2 a_pos; +attribute vec4 a_data; + +uniform mat4 u_matrix; +uniform mediump float u_ratio; +uniform vec2 u_patternscale_a; +uniform float u_tex_y_a; +uniform vec2 u_patternscale_b; +uniform float u_tex_y_b; +uniform vec2 u_gl_units_to_pixels; +uniform mediump float u_width; + +varying vec2 v_normal; +varying vec2 v_width2; +varying vec2 v_tex_a; +varying vec2 v_tex_b; +varying float v_gamma_scale; + +uniform lowp float a_color_t; +attribute lowp vec4 a_color_min; +attribute lowp vec4 a_color_max; +varying lowp vec4 color; +uniform lowp float a_blur_t; +attribute lowp float a_blur_min; +attribute lowp float a_blur_max; +varying lowp float blur; +uniform lowp float a_opacity_t; +attribute lowp float a_opacity_min; +attribute lowp float a_opacity_max; +varying lowp float opacity; +uniform lowp float a_gapwidth_t; +attribute mediump float a_gapwidth_min; +attribute mediump float a_gapwidth_max; +varying mediump float gapwidth; +uniform lowp float a_offset_t; +attribute lowp float a_offset_min; +attribute lowp float a_offset_max; +varying lowp float offset; + +void main() { + color = mix(a_color_min, a_color_max, a_color_t); + blur = mix(a_blur_min, a_blur_max, a_blur_t); + opacity = mix(a_opacity_min, a_opacity_max, a_opacity_t); + gapwidth = mix(a_gapwidth_min, a_gapwidth_max, a_gapwidth_t); + offset = mix(a_offset_min, a_offset_max, a_offset_t); + + vec2 a_extrude = a_data.xy - 128.0; + float a_direction = mod(a_data.z, 4.0) - 1.0; + float a_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * LINE_DISTANCE_SCALE; + + // We store the texture normals in the most insignificant bit + // transform y so that 0 => -1 and 1 => 1 + // In the texture normal, x is 0 if the normal points straight up/down and 1 if it's a round cap + // y is 1 if the normal points up, and -1 if it points down + mediump vec2 normal = mod(a_pos, 2.0); + normal.y = sign(normal.y - 0.5); + v_normal = normal; + + // these transformations used to be applied in the JS and native code bases. + // moved them into the shader for clarity and simplicity. + gapwidth = gapwidth / 2.0; + float width = u_width / 2.0; + offset = -1.0 * offset; + + float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0); + float outset = gapwidth + width * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING; + + // Scale the extrusion vector down to a normal and then up by the line width + // of this vertex. + mediump vec2 dist =outset * a_extrude * scale; + + // Calculate the offset when drawing a line that is to the side of the actual line. + // We do this by creating a vector that points towards the extrude, but rotate + // it when we're drawing round end points (a_direction = -1 or 1) since their + // extrude vector points in another direction. + mediump float u = 0.5 * a_direction; + mediump float t = 1.0 - abs(u); + mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t); + + // Remove the texture normal bit to get the position + vec2 pos = floor(a_pos * 0.5); + + vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0); + gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude; + + // calculate how much the perspective view squishes or stretches the extrude + float extrude_length_without_perspective = length(dist); + float extrude_length_with_perspective = length(projected_extrude.xy / gl_Position.w * u_gl_units_to_pixels); + v_gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective; + + v_tex_a = vec2(a_linesofar * u_patternscale_a.x, normal.y * u_patternscale_a.y + u_tex_y_a); + v_tex_b = vec2(a_linesofar * u_patternscale_b.x, normal.y * u_patternscale_b.y + u_tex_y_b); + + v_width2 = vec2(outset, inset); +} + +)MBGL_SHADER"; +const char* line_sdf::fragmentSource = R"MBGL_SHADER( +#ifdef GL_ES +precision mediump float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif + +uniform sampler2D u_image; +uniform float u_sdfgamma; +uniform float u_mix; + +varying vec2 v_normal; +varying vec2 v_width2; +varying vec2 v_tex_a; +varying vec2 v_tex_b; +varying float v_gamma_scale; + +varying lowp vec4 color; +varying lowp float blur; +varying lowp float opacity; + +void main() { + + + + + // Calculate the distance of the pixel from the line in pixels. + float dist = length(v_normal) * v_width2.s; + + // Calculate the antialiasing fade factor. This is either when fading in + // the line in case of an offset line (v_width2.t) or when fading out + // (v_width2.s) + float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * v_gamma_scale; + float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0); + + float sdfdist_a = texture2D(u_image, v_tex_a).a; + float sdfdist_b = texture2D(u_image, v_tex_b).a; + float sdfdist = mix(sdfdist_a, sdfdist_b, u_mix); + alpha *= smoothstep(0.5 - u_sdfgamma, 0.5 + u_sdfgamma, sdfdist); + + gl_FragColor = color * (alpha * opacity); + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(1.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/line_sdf.hpp b/src/mbgl/shaders/line_sdf.hpp new file mode 100644 index 0000000000..1d4b566f25 --- /dev/null +++ b/src/mbgl/shaders/line_sdf.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class line_sdf { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/raster.cpp b/src/mbgl/shaders/raster.cpp new file mode 100644 index 0000000000..ba0aa5cd05 --- /dev/null +++ b/src/mbgl/shaders/raster.cpp @@ -0,0 +1,150 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/raster.hpp> + +namespace mbgl { +namespace shaders { + +const char* raster::name = "raster"; +const char* raster::vertexSource = R"MBGL_SHADER( +#ifdef GL_ES +precision highp float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif + +float evaluate_zoom_function_1(const vec4 values, const float t) { + if (t < 1.0) { + return mix(values[0], values[1], t); + } else if (t < 2.0) { + return mix(values[1], values[2], t - 1.0); + } else { + return mix(values[2], values[3], t - 2.0); + } +} +vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 value2, const vec4 value3, const float t) { + if (t < 1.0) { + return mix(value0, value1, t); + } else if (t < 2.0) { + return mix(value1, value2, t - 1.0); + } else { + return mix(value2, value3, t - 2.0); + } +} + +// The offset depends on how many pixels are between the world origin and the edge of the tile: +// vec2 offset = mod(pixel_coord, size) +// +// At high zoom levels there are a ton of pixels between the world origin and the edge of the tile. +// The glsl spec only guarantees 16 bits of precision for highp floats. We need more than that. +// +// The pixel_coord is passed in as two 16 bit values: +// pixel_coord_upper = floor(pixel_coord / 2^16) +// pixel_coord_lower = mod(pixel_coord, 2^16) +// +// The offset is calculated in a series of steps that should preserve this precision: +vec2 get_pattern_pos(const vec2 pixel_coord_upper, const vec2 pixel_coord_lower, + const vec2 pattern_size, const float tile_units_to_pixels, const vec2 pos) { + + vec2 offset = mod(mod(mod(pixel_coord_upper, pattern_size) * 256.0, pattern_size) * 256.0 + pixel_coord_lower, pattern_size); + return (tile_units_to_pixels * pos + offset) / pattern_size; +} +uniform mat4 u_matrix; +uniform vec2 u_tl_parent; +uniform float u_scale_parent; +uniform float u_buffer_scale; + +attribute vec2 a_pos; +attribute vec2 a_texture_pos; + +varying vec2 v_pos0; +varying vec2 v_pos1; + +void main() { + gl_Position = u_matrix * vec4(a_pos, 0, 1); + v_pos0 = (((a_texture_pos / 32767.0) - 0.5) / u_buffer_scale ) + 0.5; + v_pos1 = (v_pos0 * u_scale_parent) + u_tl_parent; +} + +)MBGL_SHADER"; +const char* raster::fragmentSource = R"MBGL_SHADER( +#ifdef GL_ES +precision mediump float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif +uniform float u_fade_t; +uniform float u_opacity; +uniform sampler2D u_image0; +uniform sampler2D u_image1; +varying vec2 v_pos0; +varying vec2 v_pos1; + +uniform float u_brightness_low; +uniform float u_brightness_high; + +uniform float u_saturation_factor; +uniform float u_contrast_factor; +uniform vec3 u_spin_weights; + +void main() { + + // read and cross-fade colors from the main and parent tiles + vec4 color0 = texture2D(u_image0, v_pos0); + vec4 color1 = texture2D(u_image1, v_pos1); + vec4 color = mix(color0, color1, u_fade_t); + color.a *= u_opacity; + vec3 rgb = color.rgb; + + // spin + rgb = vec3( + dot(rgb, u_spin_weights.xyz), + dot(rgb, u_spin_weights.zxy), + dot(rgb, u_spin_weights.yzx)); + + // saturation + float average = (color.r + color.g + color.b) / 3.0; + rgb += (average - rgb) * u_saturation_factor; + + // contrast + rgb = (rgb - 0.5) * u_contrast_factor + 0.5; + + // brightness + vec3 u_high_vec = vec3(u_brightness_low, u_brightness_low, u_brightness_low); + vec3 u_low_vec = vec3(u_brightness_high, u_brightness_high, u_brightness_high); + + gl_FragColor = vec4(mix(u_high_vec, u_low_vec, rgb) * color.a, color.a); + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(1.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/raster.hpp b/src/mbgl/shaders/raster.hpp new file mode 100644 index 0000000000..791e2c8be0 --- /dev/null +++ b/src/mbgl/shaders/raster.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class raster { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp new file mode 100644 index 0000000000..e6728e15de --- /dev/null +++ b/src/mbgl/shaders/symbol_icon.cpp @@ -0,0 +1,151 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/symbol_icon.hpp> + +namespace mbgl { +namespace shaders { + +const char* symbol_icon::name = "symbol_icon"; +const char* symbol_icon::vertexSource = R"MBGL_SHADER( +#ifdef GL_ES +precision highp float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif + +float evaluate_zoom_function_1(const vec4 values, const float t) { + if (t < 1.0) { + return mix(values[0], values[1], t); + } else if (t < 2.0) { + return mix(values[1], values[2], t - 1.0); + } else { + return mix(values[2], values[3], t - 2.0); + } +} +vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 value2, const vec4 value3, const float t) { + if (t < 1.0) { + return mix(value0, value1, t); + } else if (t < 2.0) { + return mix(value1, value2, t - 1.0); + } else { + return mix(value2, value3, t - 2.0); + } +} + +// The offset depends on how many pixels are between the world origin and the edge of the tile: +// vec2 offset = mod(pixel_coord, size) +// +// At high zoom levels there are a ton of pixels between the world origin and the edge of the tile. +// The glsl spec only guarantees 16 bits of precision for highp floats. We need more than that. +// +// The pixel_coord is passed in as two 16 bit values: +// pixel_coord_upper = floor(pixel_coord / 2^16) +// pixel_coord_lower = mod(pixel_coord, 2^16) +// +// The offset is calculated in a series of steps that should preserve this precision: +vec2 get_pattern_pos(const vec2 pixel_coord_upper, const vec2 pixel_coord_lower, + const vec2 pattern_size, const float tile_units_to_pixels, const vec2 pos) { + + vec2 offset = mod(mod(mod(pixel_coord_upper, pattern_size) * 256.0, pattern_size) * 256.0 + pixel_coord_lower, pattern_size); + return (tile_units_to_pixels * pos + offset) / pattern_size; +} +attribute vec2 a_pos; +attribute vec2 a_offset; +attribute vec2 a_texture_pos; +attribute vec4 a_data; + +uniform lowp float a_opacity_t; +attribute lowp float a_opacity_min; +attribute lowp float a_opacity_max; +varying lowp float opacity; + +// matrix is for the vertex position. +uniform mat4 u_matrix; + +uniform mediump float u_zoom; +uniform bool u_rotate_with_map; +uniform vec2 u_extrude_scale; + +uniform vec2 u_texsize; + +varying vec2 v_tex; +varying vec2 v_fade_tex; + +void main() { + opacity = mix(a_opacity_min, a_opacity_max, a_opacity_t); + + vec2 a_tex = a_texture_pos.xy; + mediump float a_labelminzoom = a_data[0]; + mediump vec2 a_zoom = a_data.pq; + mediump float a_minzoom = a_zoom[0]; + mediump float a_maxzoom = a_zoom[1]; + + // u_zoom is the current zoom level adjusted for the change in font size + mediump float z = 2.0 - step(a_minzoom, u_zoom) - (1.0 - step(a_maxzoom, u_zoom)); + + vec2 extrude = u_extrude_scale * (a_offset / 64.0); + if (u_rotate_with_map) { + gl_Position = u_matrix * vec4(a_pos + extrude, 0, 1); + gl_Position.z += z * gl_Position.w; + } else { + gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0); + } + + v_tex = a_tex / u_texsize; + v_fade_tex = vec2(a_labelminzoom / 255.0, 0.0); +} + +)MBGL_SHADER"; +const char* symbol_icon::fragmentSource = R"MBGL_SHADER( +#ifdef GL_ES +precision mediump float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif +uniform sampler2D u_texture; +uniform sampler2D u_fadetexture; + +varying lowp float opacity; + +varying vec2 v_tex; +varying vec2 v_fade_tex; + +void main() { + + + lowp float alpha = texture2D(u_fadetexture, v_fade_tex).a * opacity; + gl_FragColor = texture2D(u_texture, v_tex) * alpha; + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(1.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/symbol_icon.hpp b/src/mbgl/shaders/symbol_icon.hpp new file mode 100644 index 0000000000..eccf17602d --- /dev/null +++ b/src/mbgl/shaders/symbol_icon.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class symbol_icon { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/symbol_sdf.cpp b/src/mbgl/shaders/symbol_sdf.cpp new file mode 100644 index 0000000000..e087242bf8 --- /dev/null +++ b/src/mbgl/shaders/symbol_sdf.cpp @@ -0,0 +1,242 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/symbol_sdf.hpp> + +namespace mbgl { +namespace shaders { + +const char* symbol_sdf::name = "symbol_sdf"; +const char* symbol_sdf::vertexSource = R"MBGL_SHADER( +#ifdef GL_ES +precision highp float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif + +float evaluate_zoom_function_1(const vec4 values, const float t) { + if (t < 1.0) { + return mix(values[0], values[1], t); + } else if (t < 2.0) { + return mix(values[1], values[2], t - 1.0); + } else { + return mix(values[2], values[3], t - 2.0); + } +} +vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 value2, const vec4 value3, const float t) { + if (t < 1.0) { + return mix(value0, value1, t); + } else if (t < 2.0) { + return mix(value1, value2, t - 1.0); + } else { + return mix(value2, value3, t - 2.0); + } +} + +// The offset depends on how many pixels are between the world origin and the edge of the tile: +// vec2 offset = mod(pixel_coord, size) +// +// At high zoom levels there are a ton of pixels between the world origin and the edge of the tile. +// The glsl spec only guarantees 16 bits of precision for highp floats. We need more than that. +// +// The pixel_coord is passed in as two 16 bit values: +// pixel_coord_upper = floor(pixel_coord / 2^16) +// pixel_coord_lower = mod(pixel_coord, 2^16) +// +// The offset is calculated in a series of steps that should preserve this precision: +vec2 get_pattern_pos(const vec2 pixel_coord_upper, const vec2 pixel_coord_lower, + const vec2 pattern_size, const float tile_units_to_pixels, const vec2 pos) { + + vec2 offset = mod(mod(mod(pixel_coord_upper, pattern_size) * 256.0, pattern_size) * 256.0 + pixel_coord_lower, pattern_size); + return (tile_units_to_pixels * pos + offset) / pattern_size; +} +const float PI = 3.141592653589793; + +attribute vec2 a_pos; +attribute vec2 a_offset; +attribute vec2 a_texture_pos; +attribute vec4 a_data; + +uniform lowp float a_fill_color_t; +attribute lowp vec4 a_fill_color_min; +attribute lowp vec4 a_fill_color_max; +varying lowp vec4 fill_color; +uniform lowp float a_halo_color_t; +attribute lowp vec4 a_halo_color_min; +attribute lowp vec4 a_halo_color_max; +varying lowp vec4 halo_color; +uniform lowp float a_opacity_t; +attribute lowp float a_opacity_min; +attribute lowp float a_opacity_max; +varying lowp float opacity; +uniform lowp float a_halo_width_t; +attribute lowp float a_halo_width_min; +attribute lowp float a_halo_width_max; +varying lowp float halo_width; +uniform lowp float a_halo_blur_t; +attribute lowp float a_halo_blur_min; +attribute lowp float a_halo_blur_max; +varying lowp float halo_blur; + +// matrix is for the vertex position. +uniform mat4 u_matrix; + +uniform mediump float u_zoom; +uniform bool u_rotate_with_map; +uniform bool u_pitch_with_map; +uniform mediump float u_pitch; +uniform mediump float u_bearing; +uniform mediump float u_aspect_ratio; +uniform vec2 u_extrude_scale; + +uniform vec2 u_texsize; + +varying vec2 v_tex; +varying vec2 v_fade_tex; +varying float v_gamma_scale; + +void main() { + fill_color = mix(a_fill_color_min, a_fill_color_max, a_fill_color_t); + halo_color = mix(a_halo_color_min, a_halo_color_max, a_halo_color_t); + opacity = mix(a_opacity_min, a_opacity_max, a_opacity_t); + halo_width = mix(a_halo_width_min, a_halo_width_max, a_halo_width_t); + halo_blur = mix(a_halo_blur_min, a_halo_blur_max, a_halo_blur_t); + + vec2 a_tex = a_texture_pos.xy; + mediump float a_labelminzoom = a_data[0]; + mediump vec2 a_zoom = a_data.pq; + mediump float a_minzoom = a_zoom[0]; + mediump float a_maxzoom = a_zoom[1]; + + // u_zoom is the current zoom level adjusted for the change in font size + mediump float z = 2.0 - step(a_minzoom, u_zoom) - (1.0 - step(a_maxzoom, u_zoom)); + + // pitch-alignment: map + // rotation-alignment: map | viewport + if (u_pitch_with_map) { + lowp float angle = u_rotate_with_map ? (a_data[1] / 256.0 * 2.0 * PI) : u_bearing; + lowp float asin = sin(angle); + lowp float acos = cos(angle); + mat2 RotationMatrix = mat2(acos, asin, -1.0 * asin, acos); + vec2 offset = RotationMatrix * a_offset; + vec2 extrude = u_extrude_scale * (offset / 64.0); + gl_Position = u_matrix * vec4(a_pos + extrude, 0, 1); + gl_Position.z += z * gl_Position.w; + // pitch-alignment: viewport + // rotation-alignment: map + } else if (u_rotate_with_map) { + // foreshortening factor to apply on pitched maps + // as a label goes from horizontal <=> vertical in angle + // it goes from 0% foreshortening to up to around 70% foreshortening + lowp float pitchfactor = 1.0 - cos(u_pitch * sin(u_pitch * 0.75)); + + lowp float lineangle = a_data[1] / 256.0 * 2.0 * PI; + + // use the lineangle to position points a,b along the line + // project the points and calculate the label angle in projected space + // this calculation allows labels to be rendered unskewed on pitched maps + vec4 a = u_matrix * vec4(a_pos, 0, 1); + vec4 b = u_matrix * vec4(a_pos + vec2(cos(lineangle),sin(lineangle)), 0, 1); + lowp float angle = atan((b[1]/b[3] - a[1]/a[3])/u_aspect_ratio, b[0]/b[3] - a[0]/a[3]); + lowp float asin = sin(angle); + lowp float acos = cos(angle); + mat2 RotationMatrix = mat2(acos, -1.0 * asin, asin, acos); + + vec2 offset = RotationMatrix * (vec2((1.0-pitchfactor)+(pitchfactor*cos(angle*2.0)), 1.0) * a_offset); + vec2 extrude = u_extrude_scale * (offset / 64.0); + gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0); + gl_Position.z += z * gl_Position.w; + // pitch-alignment: viewport + // rotation-alignment: viewport + } else { + vec2 extrude = u_extrude_scale * (a_offset / 64.0); + gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0); + } + + v_gamma_scale = gl_Position.w; + + v_tex = a_tex / u_texsize; + v_fade_tex = vec2(a_labelminzoom / 255.0, 0.0); +} + +)MBGL_SHADER"; +const char* symbol_sdf::fragmentSource = R"MBGL_SHADER( +#ifdef GL_ES +precision mediump float; +#else + +#if !defined(lowp) +#define lowp +#endif + +#if !defined(mediump) +#define mediump +#endif + +#if !defined(highp) +#define highp +#endif + +#endif +#define SDF_PX 8.0 +#define EDGE_GAMMA 0.105/DEVICE_PIXEL_RATIO + +uniform bool u_is_halo; +varying lowp vec4 fill_color; +varying lowp vec4 halo_color; +varying lowp float opacity; +varying lowp float halo_width; +varying lowp float halo_blur; + +uniform sampler2D u_texture; +uniform sampler2D u_fadetexture; +uniform lowp float u_font_scale; +uniform highp float u_gamma_scale; + +varying vec2 v_tex; +varying vec2 v_fade_tex; +varying float v_gamma_scale; + +void main() { + + + + + + + lowp vec4 color = fill_color; + lowp float gamma = EDGE_GAMMA / u_gamma_scale; + lowp float buff = (256.0 - 64.0) / 256.0; + if (u_is_halo) { + color = halo_color; + gamma = (halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / u_gamma_scale; + buff = (6.0 - halo_width / u_font_scale) / SDF_PX; + } + + lowp float dist = texture2D(u_texture, v_tex).a; + lowp float fade_alpha = texture2D(u_fadetexture, v_fade_tex).a; + highp float gamma_scaled = gamma * v_gamma_scale; + highp float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist) * fade_alpha; + + gl_FragColor = color * (alpha * opacity); + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(1.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/symbol_sdf.hpp b/src/mbgl/shaders/symbol_sdf.hpp new file mode 100644 index 0000000000..d5761d5fdb --- /dev/null +++ b/src/mbgl/shaders/symbol_sdf.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class symbol_sdf { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/sprite/sprite_atlas.cpp b/src/mbgl/sprite/sprite_atlas.cpp index ea055ce5ec..2bf8a3095a 100644 --- a/src/mbgl/sprite/sprite_atlas.cpp +++ b/src/mbgl/sprite/sprite_atlas.cpp @@ -27,6 +27,23 @@ struct SpriteAtlas::Loader { std::unique_ptr<AsyncRequest> spriteRequest; }; +SpriteAtlasElement::SpriteAtlasElement(Rect<uint16_t> rect_, + std::shared_ptr<const SpriteImage> image_, + Size size_, float pixelRatio) + : pos(std::move(rect_)), + spriteImage(std::move(image_)), + relativePixelRatio(spriteImage->pixelRatio / pixelRatio) { + + const float padding = 1; + + const float w = spriteImage->getWidth() * relativePixelRatio; + const float h = spriteImage->getHeight() * relativePixelRatio; + + size = {{ float(spriteImage->getWidth()), spriteImage->getHeight() }}; + tl = {{ float(pos.x + padding) / size_.width, float(pos.y + padding) / size_.height }}; + br = {{ float(pos.x + padding + w) / size_.width, float(pos.y + padding + h) / size_.height }}; +} + SpriteAtlas::SpriteAtlas(Size size_, float pixelRatio_) : size(std::move(size_)), pixelRatio(pixelRatio_), @@ -115,85 +132,112 @@ void SpriteAtlas::setSprite(const std::string& name, std::shared_ptr<const Sprit void SpriteAtlas::removeSprite(const std::string& name) { std::lock_guard<std::mutex> lock(mutex); - _setSprite(name); + + auto it = entries.find(name); + if (it == entries.end()) { + return; + } + + Entry& entry = it->second; + + if (entry.iconRect) { + bin.release(*entry.iconRect); + } + + if (entry.patternRect) { + bin.release(*entry.patternRect); + } + + entries.erase(it); } void SpriteAtlas::_setSprite(const std::string& name, const std::shared_ptr<const SpriteImage>& sprite) { - if (sprite) { - auto it = sprites.find(name); - if (it != sprites.end()) { - // There is already a sprite with that name in our store. - if (it->second->image.size != sprite->image.size) { - Log::Warning(Event::Sprite, "Can't change sprite dimensions for '%s'", name.c_str()); - return; - } - it->second = sprite; - } else { - sprites.emplace(name, sprite); - } + if (!sprite->image.valid()) { + Log::Warning(Event::Sprite, "invalid sprite image '%s'", name.c_str()); + return; + } - // Always add/replace the value in the dirty list. - auto dirty_it = dirtySprites.find(name); - if (dirty_it != dirtySprites.end()) { - dirty_it->second = sprite; - } else { - dirtySprites.emplace(name, sprite); - } - } else if (sprites.erase(name) > 0) { - dirtySprites.emplace(name, nullptr); + auto it = entries.find(name); + if (it == entries.end()) { + entries.emplace(name, Entry { sprite, {}, {} }); + return; + } + + Entry& entry = it->second; + + // There is already a sprite with that name in our store. + if (entry.spriteImage->image.size != sprite->image.size) { + Log::Warning(Event::Sprite, "Can't change sprite dimensions for '%s'", name.c_str()); + return; + } + + entry.spriteImage = sprite; + + if (entry.iconRect) { + copy(entry, &Entry::iconRect); + } + + if (entry.patternRect) { + copy(entry, &Entry::patternRect); } } std::shared_ptr<const SpriteImage> SpriteAtlas::getSprite(const std::string& name) { std::lock_guard<std::mutex> lock(mutex); - const auto it = sprites.find(name); - if (it != sprites.end()) { - return it->second; + const auto it = entries.find(name); + if (it != entries.end()) { + return it->second.spriteImage; } else { - if (!sprites.empty()) { + if (!entries.empty()) { Log::Info(Event::Sprite, "Can't find sprite named '%s'", name.c_str()); } return nullptr; } } -Rect<uint16_t> SpriteAtlas::allocateImage(const SpriteImage& spriteImage) { - - const uint16_t pixel_width = std::ceil(spriteImage.image.size.width / pixelRatio); - const uint16_t pixel_height = std::ceil(spriteImage.image.size.height / pixelRatio); - - // Increase to next number divisible by 4, but at least 1. - // This is so we can scale down the texture coordinates and pack them - // into 2 bytes rather than 4 bytes. - const uint16_t pack_width = (pixel_width + 1) + (4 - (pixel_width + 1) % 4); - const uint16_t pack_height = (pixel_height + 1) + (4 - (pixel_height + 1) % 4); - - // We have to allocate a new area in the bin, and store an empty image in it. - // Add a 1px border around every image. - Rect<uint16_t> rect = bin.allocate(pack_width, pack_height); - if (rect.w == 0) { - return rect; - } +optional<SpriteAtlasElement> SpriteAtlas::getIcon(const std::string& name) { + return getImage(name, &Entry::iconRect); +} - return rect; +optional<SpriteAtlasElement> SpriteAtlas::getPattern(const std::string& name) { + return getImage(name, &Entry::patternRect); } optional<SpriteAtlasElement> SpriteAtlas::getImage(const std::string& name, - const SpritePatternMode mode) { - std::lock_guard<std::recursive_mutex> lock(mtx); + optional<Rect<uint16_t>> Entry::*entryRect) { + std::lock_guard<std::mutex> lock(mutex); - auto rect_it = images.find({ name, mode }); - if (rect_it != images.end()) { - return SpriteAtlasElement { rect_it->second.pos, rect_it->second.spriteImage, rect_it->second.spriteImage->pixelRatio / pixelRatio }; + auto it = entries.find(name); + if (it == entries.end()) { + if (!entries.empty()) { + Log::Info(Event::Sprite, "Can't find sprite named '%s'", name.c_str()); + } + return {}; } - auto sprite = getSprite(name); - if (!sprite) { - return {}; + Entry& entry = it->second; + + if (entry.*entryRect) { + return SpriteAtlasElement { + *(entry.*entryRect), + entry.spriteImage, + size, + pixelRatio + }; } - Rect<uint16_t> rect = allocateImage(*sprite); + const uint16_t pixelWidth = std::ceil(entry.spriteImage->image.size.width / pixelRatio); + const uint16_t pixelHeight = std::ceil(entry.spriteImage->image.size.height / pixelRatio); + + // Increase to next number divisible by 4, but at least 1. + // This is so we can scale down the texture coordinates and pack them + // into 2 bytes rather than 4 bytes. + const uint16_t packWidth = (pixelWidth + 1) + (4 - (pixelWidth + 1) % 4); + const uint16_t packHeight = (pixelHeight + 1) + (4 - (pixelHeight + 1) % 4); + + // We have to allocate a new area in the bin, and store an empty image in it. + Rect<uint16_t> rect = bin.allocate(packWidth, packHeight); if (rect.w == 0) { if (debug::spriteWarnings) { Log::Warning(Event::Sprite, "sprite atlas bitmap overflow"); @@ -201,112 +245,44 @@ optional<SpriteAtlasElement> SpriteAtlas::getImage(const std::string& name, return {}; } - const Holder& holder = images.emplace(Key{ name, mode }, Holder{ sprite, rect }).first->second; - copy(holder, mode); - - return SpriteAtlasElement { rect, sprite, sprite->pixelRatio / pixelRatio }; -} - -optional<SpriteAtlasPosition> SpriteAtlas::getPosition(const std::string& name, - const SpritePatternMode mode) { - std::lock_guard<std::recursive_mutex> lock(mtx); - - auto img = getImage(name, mode); - if (!img) { - return {}; - } - - auto rect = (*img).pos; - - const float padding = 1; - auto spriteImage = (*img).spriteImage; - - const float w = spriteImage->getWidth() * (*img).relativePixelRatio; - const float h = spriteImage->getHeight() * (*img).relativePixelRatio; + entry.*entryRect = rect; + copy(entry, entryRect); - return SpriteAtlasPosition { - {{ float(spriteImage->getWidth()), spriteImage->getHeight() }}, - {{ float(rect.x + padding) / size.width, float(rect.y + padding) / size.height }}, - {{ float(rect.x + padding + w) / size.width, float(rect.y + padding + h) / size.height }} + return SpriteAtlasElement { + rect, + entry.spriteImage, + size, + pixelRatio }; } -void copyBitmap(const uint32_t *src, const uint32_t srcStride, const uint32_t srcX, const uint32_t srcY, - uint32_t *const dst, const uint32_t dstStride, const uint32_t dstX, const uint32_t dstY, int dstSize, - const int width, const int height, const SpritePatternMode mode) { - - int srcI = srcY * srcStride + srcX; - int dstI = dstY * dstStride + dstX; - int x, y; - - if (mode == SpritePatternMode::Repeating) { - // add 1 pixel wrapped padding on each side of the image - dstI -= dstStride; - for (y = -1; y <= height; y++, srcI = ((y + height) % height + srcY) * srcStride + srcX, dstI += dstStride) { - for (x = -1; x <= width; x++) { - const int dstIndex = (dstI + x + dstSize) % dstSize; - dst[dstIndex] = src[srcI + ((x + width) % width)]; - } - } - - } else { - for (y = 0; y < height; y++, srcI += srcStride, dstI += dstStride) { - for (x = 0; x < width; x++) { - const int dstIndex = (dstI + x + dstSize) % dstSize; - dst[dstIndex] = src[srcI + x]; - } - } - } -} - -void SpriteAtlas::copy(const Holder& holder, const SpritePatternMode mode) { +void SpriteAtlas::copy(const Entry& entry, optional<Rect<uint16_t>> Entry::*entryRect) { if (!image.valid()) { image = PremultipliedImage({ static_cast<uint32_t>(std::ceil(size.width * pixelRatio)), static_cast<uint32_t>(std::ceil(size.height * pixelRatio)) }); - std::fill(image.data.get(), image.data.get() + image.bytes(), 0); + image.fill(0); } - const uint32_t* srcData = - reinterpret_cast<const uint32_t*>(holder.spriteImage->image.data.get()); - if (!srcData) return; - uint32_t* const dstData = reinterpret_cast<uint32_t*>(image.data.get()); - - const int padding = 1; + const PremultipliedImage& src = entry.spriteImage->image; + const Rect<uint16_t>& rect = *(entry.*entryRect); - copyBitmap(srcData, holder.spriteImage->image.size.width, 0, 0, dstData, image.size.width, - (holder.pos.x + padding) * pixelRatio, (holder.pos.y + padding) * pixelRatio, - image.size.width * image.size.height, holder.spriteImage->image.size.width, - holder.spriteImage->image.size.height, mode); + const uint32_t padding = 1; + const uint32_t x = (rect.x + padding) * pixelRatio; + const uint32_t y = (rect.y + padding) * pixelRatio; + const uint32_t w = src.size.width; + const uint32_t h = src.size.height; - dirty = true; -} - -void SpriteAtlas::updateDirty() { - std::lock_guard<std::recursive_mutex> lock(mtx); + PremultipliedImage::copy(src, image, { 0, 0 }, { x, y }, { w, h }); - auto imageIterator = images.begin(); - auto spriteIterator = dirtySprites.begin(); - while (imageIterator != images.end() && spriteIterator != dirtySprites.end()) { - if (imageIterator->first.first < spriteIterator->first) { - ++imageIterator; - } else if (spriteIterator->first < imageIterator->first.first) { - ++spriteIterator; - } else { - // The two names match; - Holder& holder = imageIterator->second; - holder.spriteImage = spriteIterator->second; - if (holder.spriteImage != nullptr) { - copy(holder, imageIterator->first.second); - ++imageIterator; - } else { - images.erase(imageIterator++); - } - // Don't advance the spriteIterator because there might be another sprite with the same - // name, but a different wrap value. - } + if (entryRect == &Entry::patternRect) { + // Add 1 pixel wrapped padding on each side of the image. + PremultipliedImage::copy(src, image, { 0, h - 1 }, { x, y - 1 }, { w, 1 }); // T + PremultipliedImage::copy(src, image, { 0, 0 }, { x, y + h }, { w, 1 }); // B + PremultipliedImage::copy(src, image, { w - 1, 0 }, { x - 1, y }, { 1, h }); // L + PremultipliedImage::copy(src, image, { 0, 0 }, { x + w, y }, { 1, h }); // R } - dirtySprites.clear(); + dirty = true; } void SpriteAtlas::upload(gl::Context& context, gl::TextureUnit unit) { @@ -333,11 +309,4 @@ void SpriteAtlas::bind(bool linear, gl::Context& context, gl::TextureUnit unit) linear ? gl::TextureFilter::Linear : gl::TextureFilter::Nearest); } -SpriteAtlas::Holder::Holder(std::shared_ptr<const SpriteImage> spriteImage_, Rect<uint16_t> pos_) - : spriteImage(std::move(spriteImage_)), pos(std::move(pos_)) { -} - -SpriteAtlas::Holder::Holder(Holder&& h) : spriteImage(std::move(h.spriteImage)), pos(h.pos) { -} - } // namespace mbgl diff --git a/src/mbgl/sprite/sprite_atlas.hpp b/src/mbgl/sprite/sprite_atlas.hpp index c79aec135e..c7b266376b 100644 --- a/src/mbgl/sprite/sprite_atlas.hpp +++ b/src/mbgl/sprite/sprite_atlas.hpp @@ -10,7 +10,7 @@ #include <string> #include <map> #include <mutex> -#include <unordered_set> +#include <unordered_map> #include <array> #include <memory> @@ -23,26 +23,17 @@ namespace gl { class Context; } // namespace gl -class SpriteImage; -class SpritePosition; - -class SpriteAtlasPosition { -public: - std::array<float, 2> size = {{ 0, 0 }}; - std::array<float, 2> tl = {{ 0, 0 }}; - std::array<float, 2> br = {{ 0, 0 }}; -}; - class SpriteAtlasElement { public: + SpriteAtlasElement(Rect<uint16_t>, std::shared_ptr<const SpriteImage>, Size size, float pixelRatio); + Rect<uint16_t> pos; std::shared_ptr<const SpriteImage> spriteImage; - float relativePixelRatio; -}; -enum class SpritePatternMode : bool { - Single = false, - Repeating = true, + float relativePixelRatio; + std::array<float, 2> size; + std::array<float, 2> tl; + std::array<float, 2> br; }; class SpriteAtlas : public util::noncopyable { @@ -62,32 +53,17 @@ public: void setObserver(SpriteAtlasObserver*); - // Adds/replaces a Sprite image. void setSprite(const std::string&, std::shared_ptr<const SpriteImage>); - - // Adds/replaces mutliple Sprite images. - void setSprites(const Sprites& sprites); - - // Removes a Sprite. void removeSprite(const std::string&); - // Obtains a Sprite image. std::shared_ptr<const SpriteImage> getSprite(const std::string&); - // If the sprite is loaded, copies the requsted image from it into the atlas and returns - // the resulting icon measurements. If not, returns an empty optional. - optional<SpriteAtlasElement> getImage(const std::string& name, SpritePatternMode mode); - - // This function is used for getting the position during render time. - optional<SpriteAtlasPosition> getPosition(const std::string& name, - SpritePatternMode mode = SpritePatternMode::Single); + optional<SpriteAtlasElement> getIcon(const std::string& name); + optional<SpriteAtlasElement> getPattern(const std::string& name); // Binds the atlas texture to the GPU, and uploads data if it is out of date. void bind(bool linear, gl::Context&, gl::TextureUnit unit); - // Updates sprites in the atlas texture that may have changed. - void updateDirty(); - // Uploads the texture to the GPU to be available when we need it. This is a lazy operation; // the texture is only bound when the data is out of date (=dirty). void upload(gl::Context&, gl::TextureUnit unit); @@ -96,6 +72,7 @@ public: float getPixelRatio() const { return pixelRatio; } // Only for use in tests. + void setSprites(const Sprites& sprites); const PremultipliedImage& getAtlasImage() const { return image; } @@ -104,6 +81,7 @@ private: void _setSprite(const std::string&, const std::shared_ptr<const SpriteImage>& = nullptr); void emitSpriteLoadedIfComplete(); + const Size size; const float pixelRatio; @@ -114,35 +92,26 @@ private: SpriteAtlasObserver* observer = nullptr; - // Lock for sprites and dirty maps. - std::mutex mutex; - - // Stores all current sprites. - Sprites sprites; - - // Stores all Sprite IDs that changed since the last invocation. - Sprites dirtySprites; - - struct Holder : private util::noncopyable { - Holder(std::shared_ptr<const SpriteImage>, Rect<uint16_t>); - Holder(Holder&&); + struct Entry { std::shared_ptr<const SpriteImage> spriteImage; - const Rect<uint16_t> pos; - }; - using Key = std::pair<std::string, SpritePatternMode>; + // One sprite image might be used as both an icon image and a pattern image. If so, + // it must have two distinct entries in the texture. The one for the icon image has + // a single pixel transparent border, and the one for the pattern image has a single + // pixel border wrapped from the opposite side. + optional<Rect<uint16_t>> iconRect; + optional<Rect<uint16_t>> patternRect; + }; - Rect<uint16_t> allocateImage(const SpriteImage&); - void copy(const Holder& holder, SpritePatternMode mode); + optional<SpriteAtlasElement> getImage(const std::string& name, optional<Rect<uint16_t>> Entry::*rect); + void copy(const Entry&, optional<Rect<uint16_t>> Entry::*rect); - std::recursive_mutex mtx; + std::mutex mutex; + std::unordered_map<std::string, Entry> entries; BinPack<uint16_t> bin; - std::map<Key, Holder> images; - std::unordered_set<std::string> uninitialized; PremultipliedImage image; mbgl::optional<gl::Texture> texture; std::atomic<bool> dirty; - static const int buffer = 1; }; } // namespace mbgl diff --git a/src/mbgl/sprite/sprite_parser.cpp b/src/mbgl/sprite/sprite_parser.cpp index 9de8515e14..66b5ec0606 100644 --- a/src/mbgl/sprite/sprite_parser.cpp +++ b/src/mbgl/sprite/sprite_parser.cpp @@ -34,17 +34,8 @@ SpriteImagePtr createSpriteImage(const PremultipliedImage& image, PremultipliedImage dstImage({ width, height }); - auto srcData = reinterpret_cast<const uint32_t*>(image.data.get()); - auto dstData = reinterpret_cast<uint32_t*>(dstImage.data.get()); - // Copy from the source image into our individual sprite image - for (uint32_t y = 0; y < height; ++y) { - const auto dstRow = y * width; - const auto srcRow = (y + srcY) * image.size.width + srcX; - for (uint32_t x = 0; x < width; ++x) { - dstData[dstRow + x] = srcData[srcRow + x]; - } - } + PremultipliedImage::copy(image, dstImage, { srcX, srcY }, { 0, 0 }, { width, height }); return std::make_unique<const SpriteImage>(std::move(dstImage), ratio, sdf); } diff --git a/src/mbgl/storage/local_file_source.hpp b/src/mbgl/storage/local_file_source.hpp index 5d665c3848..43319bc06e 100644 --- a/src/mbgl/storage/local_file_source.hpp +++ b/src/mbgl/storage/local_file_source.hpp @@ -14,7 +14,7 @@ public: ~LocalFileSource() override; std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override; - + static bool acceptsURL(const std::string& url); private: diff --git a/src/mbgl/style/bucket_parameters.cpp b/src/mbgl/style/bucket_parameters.cpp index e641120c5e..2b02ac4a4a 100644 --- a/src/mbgl/style/bucket_parameters.cpp +++ b/src/mbgl/style/bucket_parameters.cpp @@ -1,21 +1,7 @@ #include <mbgl/style/bucket_parameters.hpp> -#include <mbgl/style/filter_evaluator.hpp> -#include <mbgl/tile/geometry_tile_data.hpp> namespace mbgl { namespace style { -void BucketParameters::eachFilteredFeature(const Filter& filter, - const GeometryTileLayer& layer, - std::function<void (const GeometryTileFeature&, std::size_t index, const std::string& layerName)> function) { - auto name = layer.getName(); - for (std::size_t i = 0; !cancelled() && i < layer.featureCount(); i++) { - auto feature = layer.getFeature(i); - if (!filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); })) - continue; - function(*feature, i, name); - } -} - } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/bucket_parameters.hpp b/src/mbgl/style/bucket_parameters.hpp index d5e05c5dd2..d0edbcac30 100644 --- a/src/mbgl/style/bucket_parameters.hpp +++ b/src/mbgl/style/bucket_parameters.hpp @@ -2,33 +2,14 @@ #include <mbgl/map/mode.hpp> #include <mbgl/tile/tile_id.hpp> -#include <mbgl/style/filter.hpp> - -#include <atomic> -#include <functional> namespace mbgl { - -class GeometryTileLayer; -class GeometryTileFeature; -class FeatureIndex; - namespace style { class BucketParameters { public: - const OverscaledTileID& tileID; - const std::atomic<bool>& obsolete; - FeatureIndex& featureIndex; + const OverscaledTileID tileID; const MapMode mode; - - bool cancelled() const { - return obsolete; - } - - void eachFilteredFeature(const Filter&, - const GeometryTileLayer&, - std::function<void (const GeometryTileFeature&, std::size_t index, const std::string& layerName)>); }; } // namespace style diff --git a/src/mbgl/style/conversion/stringify.hpp b/src/mbgl/style/conversion/stringify.hpp index d06b2f2814..4afbf198e5 100644 --- a/src/mbgl/style/conversion/stringify.hpp +++ b/src/mbgl/style/conversion/stringify.hpp @@ -103,6 +103,29 @@ void stringify(Writer& writer, const Value& v) { } template <class Writer> +void stringify(Writer& writer, FeatureType type) { + switch (type) { + case FeatureType::Unknown: + writer.String("Unknown"); + break; + case FeatureType::Point: + writer.String("Point"); + break; + case FeatureType::LineString: + writer.String("LineString"); + break; + case FeatureType::Polygon: + writer.String("Polygon"); + break; + } +} + +template <class Writer> +void stringify(Writer& writer, const FeatureIdentifier& id) { + FeatureIdentifier::visit(id, [&] (const auto& id_) { stringify(writer, id_); }); +} + +template <class Writer> class StringifyFilter { public: Writer& writer; @@ -156,28 +179,78 @@ public: } void operator()(const HasFilter& f) { - stringifyUnaryFilter(f, "has"); + stringifyUnaryFilter("has", f.key); } void operator()(const NotHasFilter& f) { - stringifyUnaryFilter(f, "!has"); + stringifyUnaryFilter("!has", f.key); + } + + void operator()(const TypeEqualsFilter& f) { + stringifyBinaryFilter(f, "==", "$type"); + } + + void operator()(const TypeNotEqualsFilter& f) { + stringifyBinaryFilter(f, "!=", "$type"); + } + + void operator()(const TypeInFilter& f) { + stringifySetFilter(f, "in", "$type"); + } + + void operator()(const TypeNotInFilter& f) { + stringifySetFilter(f, "!in", "$type"); + } + + void operator()(const IdentifierEqualsFilter& f) { + stringifyBinaryFilter(f, "==", "$id"); + } + + void operator()(const IdentifierNotEqualsFilter& f) { + stringifyBinaryFilter(f, "!=", "$id"); + } + + void operator()(const IdentifierInFilter& f) { + stringifySetFilter(f, "in", "$id"); + } + + void operator()(const IdentifierNotInFilter& f) { + stringifySetFilter(f, "!in", "$id"); + } + + void operator()(const HasIdentifierFilter&) { + stringifyUnaryFilter("has", "$id"); + } + + void operator()(const NotHasIdentifierFilter&) { + stringifyUnaryFilter("!has", "$id"); } private: template <class F> void stringifyBinaryFilter(const F& f, const char * op) { + stringifyBinaryFilter(f, op, f.key); + } + + template <class F> + void stringifyBinaryFilter(const F& f, const char * op, const std::string& key) { writer.StartArray(); writer.String(op); - writer.String(f.key); + writer.String(key); stringify(writer, f.value); writer.EndArray(); } template <class F> void stringifySetFilter(const F& f, const char * op) { + stringifySetFilter(f, op, f.key); + } + + template <class F> + void stringifySetFilter(const F& f, const char * op, const std::string& key) { writer.StartArray(); writer.String(op); - writer.String(f.key); + writer.String(key); for (const auto& value : f.values) { stringify(writer, value); } @@ -194,11 +267,10 @@ private: writer.EndArray(); } - template <class F> - void stringifyUnaryFilter(const F& f, const char * op) { + void stringifyUnaryFilter(const char * op, const std::string& key) { writer.StartArray(); writer.String(op); - writer.String(f.key); + writer.String(key); writer.EndArray(); } }; @@ -214,20 +286,137 @@ void stringify(Writer& writer, const Undefined&) { writer.Null(); } -template <class Writer, class T> -void stringify(Writer& writer, const Function<T>& f) { - writer.StartObject(); - writer.Key("base"); - writer.Double(f.getBase()); - writer.Key("stops"); - writer.StartArray(); - for (const auto& stop : f.getStops()) { +template <class Writer> +void stringify(Writer& writer, const CategoricalValue& v) { + CategoricalValue::visit(v, [&] (const auto& v_) { stringify(writer, v_); }); +} + +template <class Writer> +class StringifyStops { +public: + Writer& writer; + + template <class T> + void operator()(const ExponentialStops<T>& f) { + writer.Key("type"); + writer.String("exponential"); + writer.Key("base"); + writer.Double(f.base); + writer.Key("stops"); + stringifyStops(f.stops); + } + + template <class T> + void operator()(const IntervalStops<T>& f) { + writer.Key("type"); + writer.String("interval"); + writer.Key("stops"); + stringifyStops(f.stops); + } + + template <class T> + void operator()(const CategoricalStops<T>& f) { + writer.Key("type"); + writer.String("categorical"); + writer.Key("stops"); + stringifyStops(f.stops); + } + + template <class T> + void operator()(const IdentityStops<T>&) { + writer.Key("type"); + writer.String("identity"); + } + + template <class T> + void operator()(const CompositeExponentialStops<T>& f) { + writer.Key("type"); + writer.String("exponential"); + writer.Key("base"); + writer.Double(f.base); + writer.Key("stops"); + stringifyCompositeStops(f.stops); + } + + template <class T> + void operator()(const CompositeIntervalStops<T>& f) { + writer.Key("type"); + writer.String("interval"); + writer.Key("stops"); + stringifyCompositeStops(f.stops); + } + + template <class T> + void operator()(const CompositeCategoricalStops<T>& f) { + writer.Key("type"); + writer.String("categorical"); + writer.Key("stops"); + stringifyCompositeStops(f.stops); + } + +private: + template <class K, class V> + void stringifyStops(const std::map<K, V>& stops) { writer.StartArray(); - writer.Double(stop.first); - stringify(writer, stop.second); + for (const auto& stop : stops) { + writer.StartArray(); + stringify(writer, stop.first); + stringify(writer, stop.second); + writer.EndArray(); + } writer.EndArray(); } - writer.EndArray(); + + template <class InnerStops> + void stringifyCompositeStops(const std::map<float, InnerStops>& stops) { + writer.StartArray(); + for (const auto& outer : stops) { + for (const auto& inner : outer.second) { + writer.StartArray(); + writer.StartObject(); + writer.Key("zoom"); + writer.Double(outer.first); + writer.Key("value"); + stringify(writer, inner.first); + writer.EndObject(); + stringify(writer, inner.second); + writer.EndArray(); + } + } + writer.EndArray(); + } +}; + +template <class Writer, class T> +void stringify(Writer& writer, const CameraFunction<T>& f) { + writer.StartObject(); + CameraFunction<T>::Stops::visit(f.stops, StringifyStops<Writer> { writer }); + writer.EndObject(); +} + +template <class Writer, class T> +void stringify(Writer& writer, const SourceFunction<T>& f) { + writer.StartObject(); + writer.Key("property"); + writer.String(f.property); + SourceFunction<T>::Stops::visit(f.stops, StringifyStops<Writer> { writer }); + if (f.defaultValue) { + writer.Key("default"); + stringify(writer, *f.defaultValue); + } + writer.EndObject(); +} + +template <class Writer, class T> +void stringify(Writer& writer, const CompositeFunction<T>& f) { + writer.StartObject(); + writer.Key("property"); + writer.String(f.property); + CompositeFunction<T>::Stops::visit(f.stops, StringifyStops<Writer> { writer }); + if (f.defaultValue) { + writer.Key("default"); + stringify(writer, *f.defaultValue); + } writer.EndObject(); } @@ -238,7 +427,20 @@ void stringify(Writer& writer, const PropertyValue<T>& v) { template <class Property, class Writer, class T> void stringify(Writer& writer, const PropertyValue<T>& value) { - if (value) { + if (!value.isUndefined()) { + writer.Key(Property::key); + stringify(writer, value); + } +} + +template <class Writer, class T> +void stringify(Writer& writer, const DataDrivenPropertyValue<T>& v) { + v.evaluate([&] (const auto& v_) { stringify(writer, v_); }); +} + +template <class Property, class Writer, class T> +void stringify(Writer& writer, const DataDrivenPropertyValue<T>& value) { + if (!value.isUndefined()) { writer.Key(Property::key); stringify(writer, value); } diff --git a/src/mbgl/style/cross_faded_property_evaluator.cpp b/src/mbgl/style/cross_faded_property_evaluator.cpp index 4de939576e..796ca00bbf 100644 --- a/src/mbgl/style/cross_faded_property_evaluator.cpp +++ b/src/mbgl/style/cross_faded_property_evaluator.cpp @@ -17,21 +17,10 @@ Faded<T> CrossFadedPropertyEvaluator<T>::operator()(const T& constant) const { } template <typename T> -T getBiggestStopLessThan(const Function<T>& function, float z) { - const auto& stops = function.getStops(); - for (uint32_t i = 0; i < stops.size(); i++) { - if (stops[i].first > z) { - return stops[i == 0 ? i : i - 1].second; - } - } - return stops.at(stops.size() - 1).second; -} - -template <typename T> -Faded<T> CrossFadedPropertyEvaluator<T>::operator()(const Function<T>& function) const { - return calculate(getBiggestStopLessThan(function, parameters.z - 1.0f), - getBiggestStopLessThan(function, parameters.z), - getBiggestStopLessThan(function, parameters.z + 1.0f)); +Faded<T> CrossFadedPropertyEvaluator<T>::operator()(const CameraFunction<T>& function) const { + return calculate(function.evaluate(parameters.z - 1.0f), + function.evaluate(parameters.z), + function.evaluate(parameters.z + 1.0f)); } template <typename T> diff --git a/src/mbgl/style/cross_faded_property_evaluator.hpp b/src/mbgl/style/cross_faded_property_evaluator.hpp index 70c8c0c978..c5642f5cfb 100644 --- a/src/mbgl/style/cross_faded_property_evaluator.hpp +++ b/src/mbgl/style/cross_faded_property_evaluator.hpp @@ -28,7 +28,7 @@ public: Faded<T> operator()(const Undefined&) const; Faded<T> operator()(const T& constant) const; - Faded<T> operator()(const Function<T>&) const; + Faded<T> operator()(const CameraFunction<T>&) const; private: Faded<T> calculate(const T& min, const T& mid, const T& max) const; diff --git a/src/mbgl/style/data_driven_property_evaluator.hpp b/src/mbgl/style/data_driven_property_evaluator.hpp new file mode 100644 index 0000000000..7a0ff9a094 --- /dev/null +++ b/src/mbgl/style/data_driven_property_evaluator.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include <mbgl/style/property_value.hpp> +#include <mbgl/style/property_evaluation_parameters.hpp> +#include <mbgl/style/possibly_evaluated_property_value.hpp> + +namespace mbgl { +namespace style { + +template <typename T> +class DataDrivenPropertyEvaluator { +public: + using ResultType = PossiblyEvaluatedPropertyValue<T>; + + DataDrivenPropertyEvaluator(const PropertyEvaluationParameters& parameters_, T defaultValue_) + : parameters(parameters_), + defaultValue(std::move(defaultValue_)) {} + + ResultType operator()(const Undefined&) const { + return ResultType(defaultValue); + } + + ResultType operator()(const T& constant) const { + return ResultType(constant); + } + + ResultType operator()(const CameraFunction<T>& function) const { + return ResultType(function.evaluate(parameters.z)); + } + + template <class Function> + ResultType operator()(const Function& function) const { + return ResultType(function); + } + +private: + const PropertyEvaluationParameters& parameters; + T defaultValue; +}; + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/function.cpp b/src/mbgl/style/function.cpp deleted file mode 100644 index 02750c7d2e..0000000000 --- a/src/mbgl/style/function.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include <mbgl/style/function.hpp> -#include <mbgl/style/types.hpp> -#include <mbgl/util/color.hpp> -#include <mbgl/util/interpolate.hpp> - -#include <cmath> - -namespace mbgl { -namespace style { - -template <typename T> -T Function<T>::evaluate(float z) const { - bool smaller = false; - float smaller_z = 0.0f; - T smaller_val = T(); - bool larger = false; - float larger_z = 0.0f; - T larger_val = T(); - - for (uint32_t i = 0; i < stops.size(); i++) { - float stop_z = stops[i].first; - T stop_val = stops[i].second; - if (stop_z <= z && (!smaller || smaller_z < stop_z)) { - smaller = true; - smaller_z = stop_z; - smaller_val = stop_val; - } - if (stop_z >= z && (!larger || larger_z > stop_z)) { - larger = true; - larger_z = stop_z; - larger_val = stop_val; - } - } - - if (smaller && larger) { - if (larger_z == smaller_z || larger_val == smaller_val) { - return smaller_val; - } - const float zoomDiff = larger_z - smaller_z; - const float zoomProgress = z - smaller_z; - if (base == 1.0f) { - const float t = zoomProgress / zoomDiff; - return util::interpolate(smaller_val, larger_val, t); - } else { - const float t = (std::pow(base, zoomProgress) - 1) / (std::pow(base, zoomDiff) - 1); - return util::interpolate(smaller_val, larger_val, t); - } - } else if (larger) { - return larger_val; - } else if (smaller) { - return smaller_val; - } else { - // No stop defined. - assert(false); - return T(); - } -} - -template class Function<bool>; -template class Function<float>; -template class Function<Color>; -template class Function<std::vector<float>>; -template class Function<std::vector<std::string>>; -template class Function<std::array<float, 2>>; -template class Function<std::array<float, 4>>; - -template class Function<std::string>; -template class Function<TranslateAnchorType>; -template class Function<RotateAnchorType>; -template class Function<CirclePitchScaleType>; -template class Function<LineCapType>; -template class Function<LineJoinType>; -template class Function<SymbolPlacementType>; -template class Function<TextAnchorType>; -template class Function<TextJustifyType>; -template class Function<TextTransformType>; -template class Function<AlignmentType>; -template class Function<IconTextFitType>; - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/function/categorical_stops.cpp b/src/mbgl/style/function/categorical_stops.cpp new file mode 100644 index 0000000000..2984c3832f --- /dev/null +++ b/src/mbgl/style/function/categorical_stops.cpp @@ -0,0 +1,38 @@ +#include <mbgl/style/function/categorical_stops.hpp> +#include <mbgl/style/types.hpp> +#include <mbgl/util/color.hpp> + +#include <array> + +namespace mbgl { +namespace style { + +optional<CategoricalValue> categoricalValue(const Value& value) { + return value.match( + [] (bool t) { return optional<CategoricalValue>(t); }, + [] (uint64_t t) { return optional<CategoricalValue>(int64_t(t)); }, + [] (int64_t t) { return optional<CategoricalValue>(t); }, + [] (double t) { return optional<CategoricalValue>(int64_t(t)); }, + [] (const std::string& t) { return optional<CategoricalValue>(t); }, + [] (const auto&) { return optional<CategoricalValue>(); } + ); +} + +template <class T> +optional<T> CategoricalStops<T>::evaluate(const Value& value) const { + auto v = categoricalValue(value); + if (!v) { + return {}; + } + auto it = stops.find(*v); + return it == stops.end() ? optional<T>() : it->second; +} + +template class CategoricalStops<float>; +template class CategoricalStops<Color>; +template class CategoricalStops<std::array<float, 2>>; +template class CategoricalStops<std::string>; +template class CategoricalStops<TextTransformType>; + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/function/identity_stops.cpp b/src/mbgl/style/function/identity_stops.cpp new file mode 100644 index 0000000000..dfb34e9dd4 --- /dev/null +++ b/src/mbgl/style/function/identity_stops.cpp @@ -0,0 +1,61 @@ +#include <mbgl/style/function/identity_stops.hpp> +#include <mbgl/style/types.hpp> +#include <mbgl/util/enum.hpp> +#include <mbgl/util/color.hpp> + +#include <array> + +namespace mbgl { +namespace style { + +template <> +optional<float> IdentityStops<float>::evaluate(const Value& value) const { + return numericValue<float>(value); +} + +template <> +optional<std::string> IdentityStops<std::string>::evaluate(const Value& value) const { + if (!value.is<std::string>()) { + return {}; + } + + return value.get<std::string>(); +} + +template <> +optional<Color> IdentityStops<Color>::evaluate(const Value& value) const { + if (!value.is<std::string>()) { + return {}; + } + + return Color::parse(value.get<std::string>()); +} + +template <> +optional<TextTransformType> IdentityStops<TextTransformType>::evaluate(const Value& value) const { + if (!value.is<std::string>()) { + return {}; + } + + return Enum<TextTransformType>::toEnum(value.get<std::string>()); +} + +template <> +optional<std::array<float, 2>> IdentityStops<std::array<float, 2>>::evaluate(const Value& value) const { + if (!value.is<std::vector<Value>>()) { + return {}; + } + + const std::vector<Value>& vector = value.get<std::vector<Value>>(); + if (vector.size() != 2 || !numericValue<float>(vector[0]) || !numericValue<float>(vector[1])) { + return {}; + } + + return {{{ + *numericValue<float>(vector[0]), + *numericValue<float>(vector[1]) + }}}; +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/layer_impl.hpp b/src/mbgl/style/layer_impl.hpp index 0fea70c10b..9b2bfe4d2c 100644 --- a/src/mbgl/style/layer_impl.hpp +++ b/src/mbgl/style/layer_impl.hpp @@ -61,7 +61,7 @@ public: // Returns true if any paint properties have active transitions. virtual bool evaluate(const PropertyEvaluationParameters&) = 0; - virtual std::unique_ptr<Bucket> createBucket(BucketParameters&, const GeometryTileLayer&) const = 0; + virtual std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const Layer*>&) const = 0; // Checks whether this layer needs to be rendered in the given render pass. bool hasRenderPass(RenderPass) const; diff --git a/src/mbgl/style/layer_observer.hpp b/src/mbgl/style/layer_observer.hpp index a3f9ca7528..2fa1c39660 100644 --- a/src/mbgl/style/layer_observer.hpp +++ b/src/mbgl/style/layer_observer.hpp @@ -12,6 +12,7 @@ public: virtual void onLayerFilterChanged(Layer&) {} virtual void onLayerVisibilityChanged(Layer&) {} virtual void onLayerPaintPropertyChanged(Layer&) {} + virtual void onLayerDataDrivenPaintPropertyChanged(Layer&) {} virtual void onLayerLayoutPropertyChanged(Layer&, const char *) {} }; diff --git a/src/mbgl/style/layers/background_layer.cpp b/src/mbgl/style/layers/background_layer.cpp index a75038bfa0..5a903f1b6b 100644 --- a/src/mbgl/style/layers/background_layer.cpp +++ b/src/mbgl/style/layers/background_layer.cpp @@ -55,6 +55,10 @@ void BackgroundLayer::setBackgroundColor(PropertyValue<Color> value, const optio impl->observer->onLayerPaintPropertyChanged(*this); } +void BackgroundLayer::setBackgroundColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<BackgroundColor>(value, klass); +} + PropertyValue<std::string> BackgroundLayer::getDefaultBackgroundPattern() { return { "" }; } @@ -70,6 +74,10 @@ void BackgroundLayer::setBackgroundPattern(PropertyValue<std::string> value, con impl->observer->onLayerPaintPropertyChanged(*this); } +void BackgroundLayer::setBackgroundPatternTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<BackgroundPattern>(value, klass); +} + PropertyValue<float> BackgroundLayer::getDefaultBackgroundOpacity() { return { 1 }; } @@ -85,5 +93,9 @@ void BackgroundLayer::setBackgroundOpacity(PropertyValue<float> value, const opt impl->observer->onLayerPaintPropertyChanged(*this); } +void BackgroundLayer::setBackgroundOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<BackgroundOpacity>(value, klass); +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/background_layer_impl.cpp b/src/mbgl/style/layers/background_layer_impl.cpp index 4a8fe39c9a..f25ba9cfb4 100644 --- a/src/mbgl/style/layers/background_layer_impl.cpp +++ b/src/mbgl/style/layers/background_layer_impl.cpp @@ -16,7 +16,8 @@ bool BackgroundLayer::Impl::evaluate(const PropertyEvaluationParameters& paramet return paint.hasTransition(); } -std::unique_ptr<Bucket> BackgroundLayer::Impl::createBucket(BucketParameters&, const GeometryTileLayer&) const { +std::unique_ptr<Bucket> BackgroundLayer::Impl::createBucket(const BucketParameters&, const std::vector<const Layer*>&) const { + assert(false); return nullptr; } diff --git a/src/mbgl/style/layers/background_layer_impl.hpp b/src/mbgl/style/layers/background_layer_impl.hpp index 4629217e6d..02a8c423d6 100644 --- a/src/mbgl/style/layers/background_layer_impl.hpp +++ b/src/mbgl/style/layers/background_layer_impl.hpp @@ -16,7 +16,7 @@ public: void cascade(const CascadeParameters&) override; bool evaluate(const PropertyEvaluationParameters&) override; - std::unique_ptr<Bucket> createBucket(BucketParameters&, const GeometryTileLayer&) const override; + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const Layer*>&) const override; BackgroundPaintProperties paint; }; diff --git a/src/mbgl/style/layers/background_layer_properties.hpp b/src/mbgl/style/layers/background_layer_properties.hpp index 792bf3de94..fae6c26a4b 100644 --- a/src/mbgl/style/layers/background_layer_properties.hpp +++ b/src/mbgl/style/layers/background_layer_properties.hpp @@ -5,6 +5,7 @@ #include <mbgl/style/types.hpp> #include <mbgl/style/layout_property.hpp> #include <mbgl/style/paint_property.hpp> +#include <mbgl/programs/attributes.hpp> namespace mbgl { namespace style { diff --git a/src/mbgl/style/layers/circle_layer.cpp b/src/mbgl/style/layers/circle_layer.cpp index 389ab93403..53248e9397 100644 --- a/src/mbgl/style/layers/circle_layer.cpp +++ b/src/mbgl/style/layers/circle_layer.cpp @@ -65,64 +65,96 @@ const Filter& CircleLayer::getFilter() const { // Paint properties -PropertyValue<float> CircleLayer::getDefaultCircleRadius() { +DataDrivenPropertyValue<float> CircleLayer::getDefaultCircleRadius() { return { 5 }; } -PropertyValue<float> CircleLayer::getCircleRadius(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> CircleLayer::getCircleRadius(const optional<std::string>& klass) const { return impl->paint.get<CircleRadius>(klass); } -void CircleLayer::setCircleRadius(PropertyValue<float> value, const optional<std::string>& klass) { +void CircleLayer::setCircleRadius(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getCircleRadius(klass)) return; impl->paint.set<CircleRadius>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void CircleLayer::setCircleRadiusTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<CircleRadius>(value, klass); } -PropertyValue<Color> CircleLayer::getDefaultCircleColor() { +DataDrivenPropertyValue<Color> CircleLayer::getDefaultCircleColor() { return { Color::black() }; } -PropertyValue<Color> CircleLayer::getCircleColor(const optional<std::string>& klass) const { +DataDrivenPropertyValue<Color> CircleLayer::getCircleColor(const optional<std::string>& klass) const { return impl->paint.get<CircleColor>(klass); } -void CircleLayer::setCircleColor(PropertyValue<Color> value, const optional<std::string>& klass) { +void CircleLayer::setCircleColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { if (value == getCircleColor(klass)) return; impl->paint.set<CircleColor>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } -PropertyValue<float> CircleLayer::getDefaultCircleBlur() { +void CircleLayer::setCircleColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<CircleColor>(value, klass); +} + +DataDrivenPropertyValue<float> CircleLayer::getDefaultCircleBlur() { return { 0 }; } -PropertyValue<float> CircleLayer::getCircleBlur(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> CircleLayer::getCircleBlur(const optional<std::string>& klass) const { return impl->paint.get<CircleBlur>(klass); } -void CircleLayer::setCircleBlur(PropertyValue<float> value, const optional<std::string>& klass) { +void CircleLayer::setCircleBlur(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getCircleBlur(klass)) return; impl->paint.set<CircleBlur>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } -PropertyValue<float> CircleLayer::getDefaultCircleOpacity() { +void CircleLayer::setCircleBlurTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<CircleBlur>(value, klass); +} + +DataDrivenPropertyValue<float> CircleLayer::getDefaultCircleOpacity() { return { 1 }; } -PropertyValue<float> CircleLayer::getCircleOpacity(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> CircleLayer::getCircleOpacity(const optional<std::string>& klass) const { return impl->paint.get<CircleOpacity>(klass); } -void CircleLayer::setCircleOpacity(PropertyValue<float> value, const optional<std::string>& klass) { +void CircleLayer::setCircleOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getCircleOpacity(klass)) return; impl->paint.set<CircleOpacity>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void CircleLayer::setCircleOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<CircleOpacity>(value, klass); } PropertyValue<std::array<float, 2>> CircleLayer::getDefaultCircleTranslate() { @@ -140,6 +172,10 @@ void CircleLayer::setCircleTranslate(PropertyValue<std::array<float, 2>> value, impl->observer->onLayerPaintPropertyChanged(*this); } +void CircleLayer::setCircleTranslateTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<CircleTranslate>(value, klass); +} + PropertyValue<TranslateAnchorType> CircleLayer::getDefaultCircleTranslateAnchor() { return { TranslateAnchorType::Map }; } @@ -155,6 +191,10 @@ void CircleLayer::setCircleTranslateAnchor(PropertyValue<TranslateAnchorType> va impl->observer->onLayerPaintPropertyChanged(*this); } +void CircleLayer::setCircleTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<CircleTranslateAnchor>(value, klass); +} + PropertyValue<CirclePitchScaleType> CircleLayer::getDefaultCirclePitchScale() { return { CirclePitchScaleType::Map }; } @@ -170,49 +210,77 @@ void CircleLayer::setCirclePitchScale(PropertyValue<CirclePitchScaleType> value, impl->observer->onLayerPaintPropertyChanged(*this); } -PropertyValue<float> CircleLayer::getDefaultCircleStrokeWidth() { +void CircleLayer::setCirclePitchScaleTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<CirclePitchScale>(value, klass); +} + +DataDrivenPropertyValue<float> CircleLayer::getDefaultCircleStrokeWidth() { return { 0 }; } -PropertyValue<float> CircleLayer::getCircleStrokeWidth(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> CircleLayer::getCircleStrokeWidth(const optional<std::string>& klass) const { return impl->paint.get<CircleStrokeWidth>(klass); } -void CircleLayer::setCircleStrokeWidth(PropertyValue<float> value, const optional<std::string>& klass) { +void CircleLayer::setCircleStrokeWidth(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getCircleStrokeWidth(klass)) return; impl->paint.set<CircleStrokeWidth>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void CircleLayer::setCircleStrokeWidthTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<CircleStrokeWidth>(value, klass); } -PropertyValue<Color> CircleLayer::getDefaultCircleStrokeColor() { +DataDrivenPropertyValue<Color> CircleLayer::getDefaultCircleStrokeColor() { return { Color::black() }; } -PropertyValue<Color> CircleLayer::getCircleStrokeColor(const optional<std::string>& klass) const { +DataDrivenPropertyValue<Color> CircleLayer::getCircleStrokeColor(const optional<std::string>& klass) const { return impl->paint.get<CircleStrokeColor>(klass); } -void CircleLayer::setCircleStrokeColor(PropertyValue<Color> value, const optional<std::string>& klass) { +void CircleLayer::setCircleStrokeColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { if (value == getCircleStrokeColor(klass)) return; impl->paint.set<CircleStrokeColor>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void CircleLayer::setCircleStrokeColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<CircleStrokeColor>(value, klass); } -PropertyValue<float> CircleLayer::getDefaultCircleStrokeOpacity() { +DataDrivenPropertyValue<float> CircleLayer::getDefaultCircleStrokeOpacity() { return { 1 }; } -PropertyValue<float> CircleLayer::getCircleStrokeOpacity(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> CircleLayer::getCircleStrokeOpacity(const optional<std::string>& klass) const { return impl->paint.get<CircleStrokeOpacity>(klass); } -void CircleLayer::setCircleStrokeOpacity(PropertyValue<float> value, const optional<std::string>& klass) { +void CircleLayer::setCircleStrokeOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getCircleStrokeOpacity(klass)) return; impl->paint.set<CircleStrokeOpacity>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void CircleLayer::setCircleStrokeOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<CircleStrokeOpacity>(value, klass); } } // namespace style diff --git a/src/mbgl/style/layers/circle_layer_impl.cpp b/src/mbgl/style/layers/circle_layer_impl.cpp index 136522e41c..ea1d4eeb65 100644 --- a/src/mbgl/style/layers/circle_layer_impl.cpp +++ b/src/mbgl/style/layers/circle_layer_impl.cpp @@ -1,5 +1,4 @@ #include <mbgl/style/layers/circle_layer_impl.hpp> -#include <mbgl/style/bucket_parameters.hpp> #include <mbgl/renderer/circle_bucket.hpp> #include <mbgl/geometry/feature_index.hpp> #include <mbgl/util/math.hpp> @@ -15,27 +14,25 @@ void CircleLayer::Impl::cascade(const CascadeParameters& parameters) { bool CircleLayer::Impl::evaluate(const PropertyEvaluationParameters& parameters) { paint.evaluate(parameters); - passes = (paint.evaluated.get<CircleRadius>() > 0 && paint.evaluated.get<CircleColor>().a > 0 && paint.evaluated.get<CircleOpacity>() > 0) + passes = ((paint.evaluated.get<CircleRadius>().constantOr(1) > 0 || + paint.evaluated.get<CircleStrokeWidth>().constantOr(1) > 0) + && (paint.evaluated.get<CircleColor>().constantOr(Color::black()).a > 0 || + paint.evaluated.get<CircleStrokeColor>().constantOr(Color::black()).a > 0) + && (paint.evaluated.get<CircleOpacity>().constantOr(1) > 0 || + paint.evaluated.get<CircleStrokeOpacity>().constantOr(1) > 0)) ? RenderPass::Translucent : RenderPass::None; return paint.hasTransition(); } -std::unique_ptr<Bucket> CircleLayer::Impl::createBucket(BucketParameters& parameters, const GeometryTileLayer& layer) const { - auto bucket = std::make_unique<CircleBucket>(parameters.mode); - - parameters.eachFilteredFeature(filter, layer, [&] (const auto& feature, std::size_t index, const std::string& layerName) { - auto geometries = feature.getGeometries(); - bucket->addGeometry(geometries); - parameters.featureIndex.insert(geometries, index, layerName, id); - }); - - return std::move(bucket); +std::unique_ptr<Bucket> CircleLayer::Impl::createBucket(const BucketParameters& parameters, const std::vector<const Layer*>& layers) const { + return std::make_unique<CircleBucket>(parameters, layers); } float CircleLayer::Impl::getQueryRadius() const { const std::array<float, 2>& translate = paint.evaluated.get<CircleTranslate>(); - return paint.evaluated.get<CircleRadius>() + util::length(translate[0], translate[1]); + return paint.evaluated.get<CircleRadius>().constantOr(CircleRadius::defaultValue()) + + util::length(translate[0], translate[1]); } bool CircleLayer::Impl::queryIntersectsGeometry( @@ -47,7 +44,7 @@ bool CircleLayer::Impl::queryIntersectsGeometry( auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( queryGeometry, paint.evaluated.get<CircleTranslate>(), paint.evaluated.get<CircleTranslateAnchor>(), bearing, pixelsToTileUnits); - auto circleRadius = paint.evaluated.get<CircleRadius>() * pixelsToTileUnits; + auto circleRadius = paint.evaluated.get<CircleRadius>().constantOr(CircleRadius::defaultValue()) * pixelsToTileUnits; return util::polygonIntersectsBufferedMultiPoint( translatedQueryGeometry.value_or(queryGeometry), geometry, circleRadius); diff --git a/src/mbgl/style/layers/circle_layer_impl.hpp b/src/mbgl/style/layers/circle_layer_impl.hpp index 744a56898c..0f9611d589 100644 --- a/src/mbgl/style/layers/circle_layer_impl.hpp +++ b/src/mbgl/style/layers/circle_layer_impl.hpp @@ -16,7 +16,7 @@ public: void cascade(const CascadeParameters&) override; bool evaluate(const PropertyEvaluationParameters&) override; - std::unique_ptr<Bucket> createBucket(BucketParameters&, const GeometryTileLayer&) const override; + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const Layer*>&) const override; float getQueryRadius() const override; bool queryIntersectsGeometry( diff --git a/src/mbgl/style/layers/circle_layer_properties.hpp b/src/mbgl/style/layers/circle_layer_properties.hpp index ea36b31949..1cb4f5a635 100644 --- a/src/mbgl/style/layers/circle_layer_properties.hpp +++ b/src/mbgl/style/layers/circle_layer_properties.hpp @@ -5,23 +5,24 @@ #include <mbgl/style/types.hpp> #include <mbgl/style/layout_property.hpp> #include <mbgl/style/paint_property.hpp> +#include <mbgl/programs/attributes.hpp> namespace mbgl { namespace style { -struct CircleRadius : PaintProperty<float> { +struct CircleRadius : DataDrivenPaintProperty<float, attributes::a_radius> { static float defaultValue() { return 5; } }; -struct CircleColor : PaintProperty<Color> { +struct CircleColor : DataDrivenPaintProperty<Color, attributes::a_color> { static Color defaultValue() { return Color::black(); } }; -struct CircleBlur : PaintProperty<float> { +struct CircleBlur : DataDrivenPaintProperty<float, attributes::a_blur> { static float defaultValue() { return 0; } }; -struct CircleOpacity : PaintProperty<float> { +struct CircleOpacity : DataDrivenPaintProperty<float, attributes::a_opacity> { static float defaultValue() { return 1; } }; @@ -37,15 +38,15 @@ struct CirclePitchScale : PaintProperty<CirclePitchScaleType> { static CirclePitchScaleType defaultValue() { return CirclePitchScaleType::Map; } }; -struct CircleStrokeWidth : PaintProperty<float> { +struct CircleStrokeWidth : DataDrivenPaintProperty<float, attributes::a_stroke_width> { static float defaultValue() { return 0; } }; -struct CircleStrokeColor : PaintProperty<Color> { +struct CircleStrokeColor : DataDrivenPaintProperty<Color, attributes::a_stroke_color> { static Color defaultValue() { return Color::black(); } }; -struct CircleStrokeOpacity : PaintProperty<float> { +struct CircleStrokeOpacity : DataDrivenPaintProperty<float, attributes::a_stroke_opacity> { static float defaultValue() { return 1; } }; diff --git a/src/mbgl/style/layers/custom_layer_impl.cpp b/src/mbgl/style/layers/custom_layer_impl.cpp index cecd60a296..379de25e8f 100644 --- a/src/mbgl/style/layers/custom_layer_impl.cpp +++ b/src/mbgl/style/layers/custom_layer_impl.cpp @@ -70,7 +70,8 @@ bool CustomLayer::Impl::evaluate(const PropertyEvaluationParameters&) { return false; } -std::unique_ptr<Bucket> CustomLayer::Impl::createBucket(BucketParameters&, const GeometryTileLayer&) const { +std::unique_ptr<Bucket> CustomLayer::Impl::createBucket(const BucketParameters&, const std::vector<const Layer*>&) const { + assert(false); return nullptr; } diff --git a/src/mbgl/style/layers/custom_layer_impl.hpp b/src/mbgl/style/layers/custom_layer_impl.hpp index 71fb46d0d9..33eb86828c 100644 --- a/src/mbgl/style/layers/custom_layer_impl.hpp +++ b/src/mbgl/style/layers/custom_layer_impl.hpp @@ -32,7 +32,7 @@ private: void cascade(const CascadeParameters&) final {} bool evaluate(const PropertyEvaluationParameters&) final; - std::unique_ptr<Bucket> createBucket(BucketParameters&, const GeometryTileLayer&) const final; + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const Layer*>&) const final; CustomLayerInitializeFunction initializeFn = nullptr; CustomLayerRenderFunction renderFn = nullptr; diff --git a/src/mbgl/style/layers/fill_extrusion_layer.cpp b/src/mbgl/style/layers/fill_extrusion_layer.cpp index 34f0267d16..4672ede9b8 100644 --- a/src/mbgl/style/layers/fill_extrusion_layer.cpp +++ b/src/mbgl/style/layers/fill_extrusion_layer.cpp @@ -80,19 +80,31 @@ void FillExtrusionLayer::setFillExtrusionOpacity(PropertyValue<float> value, con impl->observer->onLayerPaintPropertyChanged(*this); } -PropertyValue<Color> FillExtrusionLayer::getDefaultFillExtrusionColor() { +void FillExtrusionLayer::setFillExtrusionOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<FillExtrusionOpacity>(value, klass); +} + +DataDrivenPropertyValue<Color> FillExtrusionLayer::getDefaultFillExtrusionColor() { return { Color::black() }; } -PropertyValue<Color> FillExtrusionLayer::getFillExtrusionColor(const optional<std::string>& klass) const { +DataDrivenPropertyValue<Color> FillExtrusionLayer::getFillExtrusionColor(const optional<std::string>& klass) const { return impl->paint.get<FillExtrusionColor>(klass); } -void FillExtrusionLayer::setFillExtrusionColor(PropertyValue<Color> value, const optional<std::string>& klass) { +void FillExtrusionLayer::setFillExtrusionColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { if (value == getFillExtrusionColor(klass)) return; impl->paint.set<FillExtrusionColor>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void FillExtrusionLayer::setFillExtrusionColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<FillExtrusionColor>(value, klass); } PropertyValue<std::array<float, 2>> FillExtrusionLayer::getDefaultFillExtrusionTranslate() { @@ -110,6 +122,10 @@ void FillExtrusionLayer::setFillExtrusionTranslate(PropertyValue<std::array<floa impl->observer->onLayerPaintPropertyChanged(*this); } +void FillExtrusionLayer::setFillExtrusionTranslateTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<FillExtrusionTranslate>(value, klass); +} + PropertyValue<TranslateAnchorType> FillExtrusionLayer::getDefaultFillExtrusionTranslateAnchor() { return { TranslateAnchorType::Map }; } @@ -125,6 +141,10 @@ void FillExtrusionLayer::setFillExtrusionTranslateAnchor(PropertyValue<Translate impl->observer->onLayerPaintPropertyChanged(*this); } +void FillExtrusionLayer::setFillExtrusionTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<FillExtrusionTranslateAnchor>(value, klass); +} + PropertyValue<std::string> FillExtrusionLayer::getDefaultFillExtrusionPattern() { return { "" }; } @@ -140,34 +160,54 @@ void FillExtrusionLayer::setFillExtrusionPattern(PropertyValue<std::string> valu impl->observer->onLayerPaintPropertyChanged(*this); } -PropertyValue<float> FillExtrusionLayer::getDefaultFillExtrusionHeight() { +void FillExtrusionLayer::setFillExtrusionPatternTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<FillExtrusionPattern>(value, klass); +} + +DataDrivenPropertyValue<float> FillExtrusionLayer::getDefaultFillExtrusionHeight() { return { 0 }; } -PropertyValue<float> FillExtrusionLayer::getFillExtrusionHeight(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> FillExtrusionLayer::getFillExtrusionHeight(const optional<std::string>& klass) const { return impl->paint.get<FillExtrusionHeight>(klass); } -void FillExtrusionLayer::setFillExtrusionHeight(PropertyValue<float> value, const optional<std::string>& klass) { +void FillExtrusionLayer::setFillExtrusionHeight(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getFillExtrusionHeight(klass)) return; impl->paint.set<FillExtrusionHeight>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void FillExtrusionLayer::setFillExtrusionHeightTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<FillExtrusionHeight>(value, klass); } -PropertyValue<float> FillExtrusionLayer::getDefaultFillExtrusionBase() { +DataDrivenPropertyValue<float> FillExtrusionLayer::getDefaultFillExtrusionBase() { return { 0 }; } -PropertyValue<float> FillExtrusionLayer::getFillExtrusionBase(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> FillExtrusionLayer::getFillExtrusionBase(const optional<std::string>& klass) const { return impl->paint.get<FillExtrusionBase>(klass); } -void FillExtrusionLayer::setFillExtrusionBase(PropertyValue<float> value, const optional<std::string>& klass) { +void FillExtrusionLayer::setFillExtrusionBase(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getFillExtrusionBase(klass)) return; impl->paint.set<FillExtrusionBase>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void FillExtrusionLayer::setFillExtrusionBaseTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<FillExtrusionBase>(value, klass); } } // namespace style diff --git a/src/mbgl/style/layers/fill_extrusion_layer_impl.cpp b/src/mbgl/style/layers/fill_extrusion_layer_impl.cpp index ebe9009312..a809820644 100644 --- a/src/mbgl/style/layers/fill_extrusion_layer_impl.cpp +++ b/src/mbgl/style/layers/fill_extrusion_layer_impl.cpp @@ -11,7 +11,7 @@ bool FillExtrusionLayer::Impl::evaluate(const PropertyEvaluationParameters&) { return false; } -std::unique_ptr<Bucket> FillExtrusionLayer::Impl::createBucket(BucketParameters&, const GeometryTileLayer&) const { +std::unique_ptr<Bucket> FillExtrusionLayer::Impl::createBucket(const BucketParameters&, const std::vector<const Layer*>&) const { return nullptr; } diff --git a/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp b/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp index 3dd8bb270a..ed7ef747fb 100644 --- a/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp +++ b/src/mbgl/style/layers/fill_extrusion_layer_impl.hpp @@ -16,7 +16,7 @@ public: void cascade(const CascadeParameters&) override; bool evaluate(const PropertyEvaluationParameters&) override; - std::unique_ptr<Bucket> createBucket(BucketParameters&, const GeometryTileLayer&) const override; + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const Layer*>&) const override; FillExtrusionPaintProperties paint; }; diff --git a/src/mbgl/style/layers/fill_extrusion_layer_properties.hpp b/src/mbgl/style/layers/fill_extrusion_layer_properties.hpp index a2d01199a5..c1dd3b079d 100644 --- a/src/mbgl/style/layers/fill_extrusion_layer_properties.hpp +++ b/src/mbgl/style/layers/fill_extrusion_layer_properties.hpp @@ -5,6 +5,7 @@ #include <mbgl/style/types.hpp> #include <mbgl/style/layout_property.hpp> #include <mbgl/style/paint_property.hpp> +#include <mbgl/programs/attributes.hpp> namespace mbgl { namespace style { @@ -13,7 +14,7 @@ struct FillExtrusionOpacity : PaintProperty<float> { static float defaultValue() { return 1; } }; -struct FillExtrusionColor : PaintProperty<Color> { +struct FillExtrusionColor : DataDrivenPaintProperty<Color, attributes::a_color> { static Color defaultValue() { return Color::black(); } }; @@ -29,11 +30,11 @@ struct FillExtrusionPattern : CrossFadedPaintProperty<std::string> { static std::string defaultValue() { return ""; } }; -struct FillExtrusionHeight : PaintProperty<float> { +struct FillExtrusionHeight : DataDrivenPaintProperty<float, attributes::a_height> { static float defaultValue() { return 0; } }; -struct FillExtrusionBase : PaintProperty<float> { +struct FillExtrusionBase : DataDrivenPaintProperty<float, attributes::a_base> { static float defaultValue() { return 0; } }; diff --git a/src/mbgl/style/layers/fill_layer.cpp b/src/mbgl/style/layers/fill_layer.cpp index b8fa8cad8b..dfa88b5b0f 100644 --- a/src/mbgl/style/layers/fill_layer.cpp +++ b/src/mbgl/style/layers/fill_layer.cpp @@ -80,49 +80,77 @@ void FillLayer::setFillAntialias(PropertyValue<bool> value, const optional<std:: impl->observer->onLayerPaintPropertyChanged(*this); } -PropertyValue<float> FillLayer::getDefaultFillOpacity() { +void FillLayer::setFillAntialiasTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<FillAntialias>(value, klass); +} + +DataDrivenPropertyValue<float> FillLayer::getDefaultFillOpacity() { return { 1 }; } -PropertyValue<float> FillLayer::getFillOpacity(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> FillLayer::getFillOpacity(const optional<std::string>& klass) const { return impl->paint.get<FillOpacity>(klass); } -void FillLayer::setFillOpacity(PropertyValue<float> value, const optional<std::string>& klass) { +void FillLayer::setFillOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getFillOpacity(klass)) return; impl->paint.set<FillOpacity>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void FillLayer::setFillOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<FillOpacity>(value, klass); } -PropertyValue<Color> FillLayer::getDefaultFillColor() { +DataDrivenPropertyValue<Color> FillLayer::getDefaultFillColor() { return { Color::black() }; } -PropertyValue<Color> FillLayer::getFillColor(const optional<std::string>& klass) const { +DataDrivenPropertyValue<Color> FillLayer::getFillColor(const optional<std::string>& klass) const { return impl->paint.get<FillColor>(klass); } -void FillLayer::setFillColor(PropertyValue<Color> value, const optional<std::string>& klass) { +void FillLayer::setFillColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { if (value == getFillColor(klass)) return; impl->paint.set<FillColor>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void FillLayer::setFillColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<FillColor>(value, klass); } -PropertyValue<Color> FillLayer::getDefaultFillOutlineColor() { +DataDrivenPropertyValue<Color> FillLayer::getDefaultFillOutlineColor() { return { {} }; } -PropertyValue<Color> FillLayer::getFillOutlineColor(const optional<std::string>& klass) const { +DataDrivenPropertyValue<Color> FillLayer::getFillOutlineColor(const optional<std::string>& klass) const { return impl->paint.get<FillOutlineColor>(klass); } -void FillLayer::setFillOutlineColor(PropertyValue<Color> value, const optional<std::string>& klass) { +void FillLayer::setFillOutlineColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { if (value == getFillOutlineColor(klass)) return; impl->paint.set<FillOutlineColor>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void FillLayer::setFillOutlineColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<FillOutlineColor>(value, klass); } PropertyValue<std::array<float, 2>> FillLayer::getDefaultFillTranslate() { @@ -140,6 +168,10 @@ void FillLayer::setFillTranslate(PropertyValue<std::array<float, 2>> value, cons impl->observer->onLayerPaintPropertyChanged(*this); } +void FillLayer::setFillTranslateTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<FillTranslate>(value, klass); +} + PropertyValue<TranslateAnchorType> FillLayer::getDefaultFillTranslateAnchor() { return { TranslateAnchorType::Map }; } @@ -155,6 +187,10 @@ void FillLayer::setFillTranslateAnchor(PropertyValue<TranslateAnchorType> value, impl->observer->onLayerPaintPropertyChanged(*this); } +void FillLayer::setFillTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<FillTranslateAnchor>(value, klass); +} + PropertyValue<std::string> FillLayer::getDefaultFillPattern() { return { "" }; } @@ -170,5 +206,9 @@ void FillLayer::setFillPattern(PropertyValue<std::string> value, const optional< impl->observer->onLayerPaintPropertyChanged(*this); } +void FillLayer::setFillPatternTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<FillPattern>(value, klass); +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/fill_layer_impl.cpp b/src/mbgl/style/layers/fill_layer_impl.cpp index 51636820f0..c7c89f8c20 100644 --- a/src/mbgl/style/layers/fill_layer_impl.cpp +++ b/src/mbgl/style/layers/fill_layer_impl.cpp @@ -1,5 +1,4 @@ #include <mbgl/style/layers/fill_layer_impl.hpp> -#include <mbgl/style/bucket_parameters.hpp> #include <mbgl/renderer/fill_bucket.hpp> #include <mbgl/geometry/feature_index.hpp> #include <mbgl/util/math.hpp> @@ -15,13 +14,19 @@ void FillLayer::Impl::cascade(const CascadeParameters& parameters) { bool FillLayer::Impl::evaluate(const PropertyEvaluationParameters& parameters) { paint.evaluate(parameters); + if (paint.unevaluated.get<FillOutlineColor>().isUndefined()) { + paint.evaluated.get<FillOutlineColor>() = paint.evaluated.get<FillColor>(); + } + passes = RenderPass::None; if (paint.evaluated.get<FillAntialias>()) { passes |= RenderPass::Translucent; } - if (!paint.evaluated.get<FillPattern>().from.empty() || (paint.evaluated.get<FillColor>().a * paint.evaluated.get<FillOpacity>()) < 1.0f) { + if (!paint.unevaluated.get<FillPattern>().isUndefined() + || paint.evaluated.get<FillColor>().constantOr(Color()).a < 1.0f + || paint.evaluated.get<FillOpacity>().constantOr(0) < 1.0f) { passes |= RenderPass::Translucent; } else { passes |= RenderPass::Opaque; @@ -30,16 +35,8 @@ bool FillLayer::Impl::evaluate(const PropertyEvaluationParameters& parameters) { return paint.hasTransition(); } -std::unique_ptr<Bucket> FillLayer::Impl::createBucket(BucketParameters& parameters, const GeometryTileLayer& layer) const { - auto bucket = std::make_unique<FillBucket>(); - - parameters.eachFilteredFeature(filter, layer, [&] (const auto& feature, std::size_t index, const std::string& layerName) { - auto geometries = feature.getGeometries(); - bucket->addGeometry(geometries); - parameters.featureIndex.insert(geometries, index, layerName, id); - }); - - return std::move(bucket); +std::unique_ptr<Bucket> FillLayer::Impl::createBucket(const BucketParameters& parameters, const std::vector<const Layer*>& layers) const { + return std::make_unique<FillBucket>(parameters, layers); } float FillLayer::Impl::getQueryRadius() const { diff --git a/src/mbgl/style/layers/fill_layer_impl.hpp b/src/mbgl/style/layers/fill_layer_impl.hpp index 28e2fa7edc..bd25a8bebf 100644 --- a/src/mbgl/style/layers/fill_layer_impl.hpp +++ b/src/mbgl/style/layers/fill_layer_impl.hpp @@ -16,7 +16,7 @@ public: void cascade(const CascadeParameters&) override; bool evaluate(const PropertyEvaluationParameters&) override; - std::unique_ptr<Bucket> createBucket(BucketParameters&, const GeometryTileLayer&) const override; + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const Layer*>&) const override; float getQueryRadius() const override; bool queryIntersectsGeometry( diff --git a/src/mbgl/style/layers/fill_layer_properties.hpp b/src/mbgl/style/layers/fill_layer_properties.hpp index b2d926c31e..f44a18d0e0 100644 --- a/src/mbgl/style/layers/fill_layer_properties.hpp +++ b/src/mbgl/style/layers/fill_layer_properties.hpp @@ -5,6 +5,7 @@ #include <mbgl/style/types.hpp> #include <mbgl/style/layout_property.hpp> #include <mbgl/style/paint_property.hpp> +#include <mbgl/programs/attributes.hpp> namespace mbgl { namespace style { @@ -13,15 +14,15 @@ struct FillAntialias : PaintProperty<bool> { static bool defaultValue() { return true; } }; -struct FillOpacity : PaintProperty<float> { +struct FillOpacity : DataDrivenPaintProperty<float, attributes::a_opacity> { static float defaultValue() { return 1; } }; -struct FillColor : PaintProperty<Color> { +struct FillColor : DataDrivenPaintProperty<Color, attributes::a_color> { static Color defaultValue() { return Color::black(); } }; -struct FillOutlineColor : PaintProperty<Color> { +struct FillOutlineColor : DataDrivenPaintProperty<Color, attributes::a_outline_color> { static Color defaultValue() { return {}; } }; diff --git a/src/mbgl/style/layers/layer.cpp.ejs b/src/mbgl/style/layers/layer.cpp.ejs index e730e3a29b..335573abf3 100644 --- a/src/mbgl/style/layers/layer.cpp.ejs +++ b/src/mbgl/style/layers/layer.cpp.ejs @@ -86,15 +86,15 @@ const Filter& <%- camelize(type) %>Layer::getFilter() const { // Layout properties <% for (const property of layoutProperties) { -%> -PropertyValue<<%- propertyType(property) %>> <%- camelize(type) %>Layer::getDefault<%- camelize(property.name) %>() { +<%- propertyValueType(property) %> <%- camelize(type) %>Layer::getDefault<%- camelize(property.name) %>() { return <%- camelize(property.name) %>::defaultValue(); } -PropertyValue<<%- propertyType(property) %>> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>() const { +<%- propertyValueType(property) %> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>() const { return impl->layout.unevaluated.get<<%- camelize(property.name) %>>(); } -void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(PropertyValue<<%- propertyType(property) %>> value) { +void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(<%- propertyValueType(property) %> value) { if (value == get<%- camelize(property.name) %>()) return; impl->layout.unevaluated.get<<%- camelize(property.name) %>>() = value; @@ -104,19 +104,31 @@ void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(PropertyValue // Paint properties <% for (const property of paintProperties) { %> -PropertyValue<<%- propertyType(property) %>> <%- camelize(type) %>Layer::getDefault<%- camelize(property.name) %>() { +<%- propertyValueType(property) %> <%- camelize(type) %>Layer::getDefault<%- camelize(property.name) %>() { return { <%- defaultValue(property) %> }; } -PropertyValue<<%- propertyType(property) %>> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>(const optional<std::string>& klass) const { +<%- propertyValueType(property) %> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>(const optional<std::string>& klass) const { return impl->paint.get<<%- camelize(property.name) %>>(klass); } -void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(PropertyValue<<%- propertyType(property) %>> value, const optional<std::string>& klass) { +void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(<%- propertyValueType(property) %> value, const optional<std::string>& klass) { if (value == get<%- camelize(property.name) %>(klass)) return; impl->paint.set<<%- camelize(property.name) %>>(value, klass); +<% if (isDataDriven(property)) { -%> + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +<% } else { -%> impl->observer->onLayerPaintPropertyChanged(*this); +<% } -%> +} + +void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>Transition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<<%- camelize(property.name) %>>(value, klass); } <% } -%> diff --git a/src/mbgl/style/layers/layer_properties.hpp.ejs b/src/mbgl/style/layers/layer_properties.hpp.ejs index d18ad44efd..2a736ca388 100644 --- a/src/mbgl/style/layers/layer_properties.hpp.ejs +++ b/src/mbgl/style/layers/layer_properties.hpp.ejs @@ -10,22 +10,21 @@ #include <mbgl/style/types.hpp> #include <mbgl/style/layout_property.hpp> #include <mbgl/style/paint_property.hpp> +#include <mbgl/programs/attributes.hpp> namespace mbgl { namespace style { <% for (const property of layoutProperties) { -%> -struct <%- camelize(property.name) %> : LayoutProperty<<%- propertyType(property) %>> { +struct <%- camelize(property.name) %> : <%- layoutPropertyType(property, type) %> { static constexpr const char * key = "<%- property.name %>"; - static <%- propertyType(property) %> defaultValue() { return <%- defaultValue(property) %>; } + static <%- evaluatedType(property) %> defaultValue() { return <%- defaultValue(property) %>; } }; <% } -%> <% for (const property of paintProperties) { -%> -struct <%- camelize(property.name) %> : <% -if (/-pattern$/.test(property.name) || property.name === 'line-dasharray') { -%>CrossFaded<% } -%>PaintProperty<<%- propertyType(property) %>> { - static <%- propertyType(property) %> defaultValue() { return <%- defaultValue(property) %>; } +struct <%- camelize(property.name) %> : <%- paintPropertyType(property, type) %> { + static <%- evaluatedType(property) %> defaultValue() { return <%- defaultValue(property) %>; } }; <% } -%> diff --git a/src/mbgl/style/layers/line_layer.cpp b/src/mbgl/style/layers/line_layer.cpp index 7f6c148cd1..eaaa0fcd45 100644 --- a/src/mbgl/style/layers/line_layer.cpp +++ b/src/mbgl/style/layers/line_layer.cpp @@ -122,34 +122,50 @@ void LineLayer::setLineRoundLimit(PropertyValue<float> value) { // Paint properties -PropertyValue<float> LineLayer::getDefaultLineOpacity() { +DataDrivenPropertyValue<float> LineLayer::getDefaultLineOpacity() { return { 1 }; } -PropertyValue<float> LineLayer::getLineOpacity(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> LineLayer::getLineOpacity(const optional<std::string>& klass) const { return impl->paint.get<LineOpacity>(klass); } -void LineLayer::setLineOpacity(PropertyValue<float> value, const optional<std::string>& klass) { +void LineLayer::setLineOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getLineOpacity(klass)) return; impl->paint.set<LineOpacity>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void LineLayer::setLineOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<LineOpacity>(value, klass); } -PropertyValue<Color> LineLayer::getDefaultLineColor() { +DataDrivenPropertyValue<Color> LineLayer::getDefaultLineColor() { return { Color::black() }; } -PropertyValue<Color> LineLayer::getLineColor(const optional<std::string>& klass) const { +DataDrivenPropertyValue<Color> LineLayer::getLineColor(const optional<std::string>& klass) const { return impl->paint.get<LineColor>(klass); } -void LineLayer::setLineColor(PropertyValue<Color> value, const optional<std::string>& klass) { +void LineLayer::setLineColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { if (value == getLineColor(klass)) return; impl->paint.set<LineColor>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void LineLayer::setLineColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<LineColor>(value, klass); } PropertyValue<std::array<float, 2>> LineLayer::getDefaultLineTranslate() { @@ -167,6 +183,10 @@ void LineLayer::setLineTranslate(PropertyValue<std::array<float, 2>> value, cons impl->observer->onLayerPaintPropertyChanged(*this); } +void LineLayer::setLineTranslateTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<LineTranslate>(value, klass); +} + PropertyValue<TranslateAnchorType> LineLayer::getDefaultLineTranslateAnchor() { return { TranslateAnchorType::Map }; } @@ -182,6 +202,10 @@ void LineLayer::setLineTranslateAnchor(PropertyValue<TranslateAnchorType> value, impl->observer->onLayerPaintPropertyChanged(*this); } +void LineLayer::setLineTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<LineTranslateAnchor>(value, klass); +} + PropertyValue<float> LineLayer::getDefaultLineWidth() { return { 1 }; } @@ -197,49 +221,77 @@ void LineLayer::setLineWidth(PropertyValue<float> value, const optional<std::str impl->observer->onLayerPaintPropertyChanged(*this); } -PropertyValue<float> LineLayer::getDefaultLineGapWidth() { +void LineLayer::setLineWidthTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<LineWidth>(value, klass); +} + +DataDrivenPropertyValue<float> LineLayer::getDefaultLineGapWidth() { return { 0 }; } -PropertyValue<float> LineLayer::getLineGapWidth(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> LineLayer::getLineGapWidth(const optional<std::string>& klass) const { return impl->paint.get<LineGapWidth>(klass); } -void LineLayer::setLineGapWidth(PropertyValue<float> value, const optional<std::string>& klass) { +void LineLayer::setLineGapWidth(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getLineGapWidth(klass)) return; impl->paint.set<LineGapWidth>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } -PropertyValue<float> LineLayer::getDefaultLineOffset() { +void LineLayer::setLineGapWidthTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<LineGapWidth>(value, klass); +} + +DataDrivenPropertyValue<float> LineLayer::getDefaultLineOffset() { return { 0 }; } -PropertyValue<float> LineLayer::getLineOffset(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> LineLayer::getLineOffset(const optional<std::string>& klass) const { return impl->paint.get<LineOffset>(klass); } -void LineLayer::setLineOffset(PropertyValue<float> value, const optional<std::string>& klass) { +void LineLayer::setLineOffset(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getLineOffset(klass)) return; impl->paint.set<LineOffset>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } -PropertyValue<float> LineLayer::getDefaultLineBlur() { +void LineLayer::setLineOffsetTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<LineOffset>(value, klass); +} + +DataDrivenPropertyValue<float> LineLayer::getDefaultLineBlur() { return { 0 }; } -PropertyValue<float> LineLayer::getLineBlur(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> LineLayer::getLineBlur(const optional<std::string>& klass) const { return impl->paint.get<LineBlur>(klass); } -void LineLayer::setLineBlur(PropertyValue<float> value, const optional<std::string>& klass) { +void LineLayer::setLineBlur(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getLineBlur(klass)) return; impl->paint.set<LineBlur>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void LineLayer::setLineBlurTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<LineBlur>(value, klass); } PropertyValue<std::vector<float>> LineLayer::getDefaultLineDasharray() { @@ -257,6 +309,10 @@ void LineLayer::setLineDasharray(PropertyValue<std::vector<float>> value, const impl->observer->onLayerPaintPropertyChanged(*this); } +void LineLayer::setLineDasharrayTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<LineDasharray>(value, klass); +} + PropertyValue<std::string> LineLayer::getDefaultLinePattern() { return { "" }; } @@ -272,5 +328,9 @@ void LineLayer::setLinePattern(PropertyValue<std::string> value, const optional< impl->observer->onLayerPaintPropertyChanged(*this); } +void LineLayer::setLinePatternTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<LinePattern>(value, klass); +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/line_layer_impl.cpp b/src/mbgl/style/layers/line_layer_impl.cpp index 477579a43c..ef0131e3d5 100644 --- a/src/mbgl/style/layers/line_layer_impl.cpp +++ b/src/mbgl/style/layers/line_layer_impl.cpp @@ -1,5 +1,5 @@ #include <mbgl/style/layers/line_layer_impl.hpp> -#include <mbgl/style/bucket_parameters.hpp> +#include <mbgl/style/property_evaluation_parameters.hpp> #include <mbgl/renderer/line_bucket.hpp> #include <mbgl/geometry/feature_index.hpp> #include <mbgl/util/math.hpp> @@ -20,31 +20,25 @@ bool LineLayer::Impl::evaluate(const PropertyEvaluationParameters& parameters) { paint.evaluate(parameters); - passes = (paint.evaluated.get<LineOpacity>() > 0 && paint.evaluated.get<LineColor>().a > 0 && paint.evaluated.get<LineWidth>() > 0) + passes = (paint.evaluated.get<LineOpacity>().constantOr(1.0) > 0 + && paint.evaluated.get<LineColor>().constantOr(Color::black()).a > 0 + && paint.evaluated.get<LineWidth>() > 0) ? RenderPass::Translucent : RenderPass::None; return paint.hasTransition(); } -std::unique_ptr<Bucket> LineLayer::Impl::createBucket(BucketParameters& parameters, const GeometryTileLayer& layer) const { - auto bucket = std::make_unique<LineBucket>(parameters.tileID.overscaleFactor()); - - bucket->layout = layout.evaluate(PropertyEvaluationParameters(parameters.tileID.overscaledZ)); - - parameters.eachFilteredFeature(filter, layer, [&] (const auto& feature, std::size_t index, const std::string& layerName) { - auto geometries = feature.getGeometries(); - bucket->addGeometry(geometries); - parameters.featureIndex.insert(geometries, index, layerName, id); - }); - - return std::move(bucket); +std::unique_ptr<Bucket> LineLayer::Impl::createBucket(const BucketParameters& parameters, const std::vector<const Layer*>& layers) const { + return std::make_unique<LineBucket>(parameters, layers, layout); } float LineLayer::Impl::getLineWidth() const { - if (paint.evaluated.get<LineGapWidth>() > 0) { - return paint.evaluated.get<LineGapWidth>() + 2 * paint.evaluated.get<LineWidth>(); + float lineWidth = paint.evaluated.get<LineWidth>(); + float gapWidth = paint.evaluated.get<LineGapWidth>().constantOr(0); + if (gapWidth) { + return gapWidth + 2 * lineWidth; } else { - return paint.evaluated.get<LineWidth>(); + return lineWidth; } } @@ -80,7 +74,8 @@ optional<GeometryCollection> offsetLine(const GeometryCollection& rings, const d float LineLayer::Impl::getQueryRadius() const { const std::array<float, 2>& translate = paint.evaluated.get<LineTranslate>(); - return getLineWidth() / 2.0 + std::abs(paint.evaluated.get<LineOffset>()) + util::length(translate[0], translate[1]); + auto offset = paint.evaluated.get<LineOffset>().constantOr(LineOffset::defaultValue()); + return getLineWidth() / 2.0 + std::abs(offset) + util::length(translate[0], translate[1]); } bool LineLayer::Impl::queryIntersectsGeometry( @@ -93,7 +88,9 @@ bool LineLayer::Impl::queryIntersectsGeometry( auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( queryGeometry, paint.evaluated.get<LineTranslate>(), paint.evaluated.get<LineTranslateAnchor>(), bearing, pixelsToTileUnits); - auto offsetGeometry = offsetLine(geometry, paint.evaluated.get<LineOffset>() * pixelsToTileUnits); + + auto offset = paint.evaluated.get<LineOffset>().constantOr(LineOffset::defaultValue()); + auto offsetGeometry = offsetLine(geometry, offset * pixelsToTileUnits); return util::polygonIntersectsBufferedMultiLine( translatedQueryGeometry.value_or(queryGeometry), diff --git a/src/mbgl/style/layers/line_layer_impl.hpp b/src/mbgl/style/layers/line_layer_impl.hpp index 1955c019af..67e793f2ea 100644 --- a/src/mbgl/style/layers/line_layer_impl.hpp +++ b/src/mbgl/style/layers/line_layer_impl.hpp @@ -16,7 +16,7 @@ public: void cascade(const CascadeParameters&) override; bool evaluate(const PropertyEvaluationParameters&) override; - std::unique_ptr<Bucket> createBucket(BucketParameters&, const GeometryTileLayer&) const override; + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const Layer*>&) const override; float getQueryRadius() const override; bool queryIntersectsGeometry( diff --git a/src/mbgl/style/layers/line_layer_properties.hpp b/src/mbgl/style/layers/line_layer_properties.hpp index 2ea7f6b125..724026e3a6 100644 --- a/src/mbgl/style/layers/line_layer_properties.hpp +++ b/src/mbgl/style/layers/line_layer_properties.hpp @@ -5,6 +5,7 @@ #include <mbgl/style/types.hpp> #include <mbgl/style/layout_property.hpp> #include <mbgl/style/paint_property.hpp> +#include <mbgl/programs/attributes.hpp> namespace mbgl { namespace style { @@ -29,11 +30,11 @@ struct LineRoundLimit : LayoutProperty<float> { static float defaultValue() { return 1; } }; -struct LineOpacity : PaintProperty<float> { +struct LineOpacity : DataDrivenPaintProperty<float, attributes::a_opacity> { static float defaultValue() { return 1; } }; -struct LineColor : PaintProperty<Color> { +struct LineColor : DataDrivenPaintProperty<Color, attributes::a_color> { static Color defaultValue() { return Color::black(); } }; @@ -49,15 +50,15 @@ struct LineWidth : PaintProperty<float> { static float defaultValue() { return 1; } }; -struct LineGapWidth : PaintProperty<float> { +struct LineGapWidth : DataDrivenPaintProperty<float, attributes::a_gap_width> { static float defaultValue() { return 0; } }; -struct LineOffset : PaintProperty<float> { +struct LineOffset : DataDrivenPaintProperty<float, attributes::a_offset<1>> { static float defaultValue() { return 0; } }; -struct LineBlur : PaintProperty<float> { +struct LineBlur : DataDrivenPaintProperty<float, attributes::a_blur> { static float defaultValue() { return 0; } }; diff --git a/src/mbgl/style/layers/raster_layer.cpp b/src/mbgl/style/layers/raster_layer.cpp index 0fda27f0dc..2108a5c49f 100644 --- a/src/mbgl/style/layers/raster_layer.cpp +++ b/src/mbgl/style/layers/raster_layer.cpp @@ -62,6 +62,10 @@ void RasterLayer::setRasterOpacity(PropertyValue<float> value, const optional<st impl->observer->onLayerPaintPropertyChanged(*this); } +void RasterLayer::setRasterOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<RasterOpacity>(value, klass); +} + PropertyValue<float> RasterLayer::getDefaultRasterHueRotate() { return { 0 }; } @@ -77,6 +81,10 @@ void RasterLayer::setRasterHueRotate(PropertyValue<float> value, const optional< impl->observer->onLayerPaintPropertyChanged(*this); } +void RasterLayer::setRasterHueRotateTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<RasterHueRotate>(value, klass); +} + PropertyValue<float> RasterLayer::getDefaultRasterBrightnessMin() { return { 0 }; } @@ -92,6 +100,10 @@ void RasterLayer::setRasterBrightnessMin(PropertyValue<float> value, const optio impl->observer->onLayerPaintPropertyChanged(*this); } +void RasterLayer::setRasterBrightnessMinTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<RasterBrightnessMin>(value, klass); +} + PropertyValue<float> RasterLayer::getDefaultRasterBrightnessMax() { return { 1 }; } @@ -107,6 +119,10 @@ void RasterLayer::setRasterBrightnessMax(PropertyValue<float> value, const optio impl->observer->onLayerPaintPropertyChanged(*this); } +void RasterLayer::setRasterBrightnessMaxTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<RasterBrightnessMax>(value, klass); +} + PropertyValue<float> RasterLayer::getDefaultRasterSaturation() { return { 0 }; } @@ -122,6 +138,10 @@ void RasterLayer::setRasterSaturation(PropertyValue<float> value, const optional impl->observer->onLayerPaintPropertyChanged(*this); } +void RasterLayer::setRasterSaturationTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<RasterSaturation>(value, klass); +} + PropertyValue<float> RasterLayer::getDefaultRasterContrast() { return { 0 }; } @@ -137,6 +157,10 @@ void RasterLayer::setRasterContrast(PropertyValue<float> value, const optional<s impl->observer->onLayerPaintPropertyChanged(*this); } +void RasterLayer::setRasterContrastTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<RasterContrast>(value, klass); +} + PropertyValue<float> RasterLayer::getDefaultRasterFadeDuration() { return { 300 }; } @@ -152,5 +176,9 @@ void RasterLayer::setRasterFadeDuration(PropertyValue<float> value, const option impl->observer->onLayerPaintPropertyChanged(*this); } +void RasterLayer::setRasterFadeDurationTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<RasterFadeDuration>(value, klass); +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/raster_layer_impl.cpp b/src/mbgl/style/layers/raster_layer_impl.cpp index a78614aee9..a667ccb5a8 100644 --- a/src/mbgl/style/layers/raster_layer_impl.cpp +++ b/src/mbgl/style/layers/raster_layer_impl.cpp @@ -16,7 +16,8 @@ bool RasterLayer::Impl::evaluate(const PropertyEvaluationParameters& parameters) return paint.hasTransition(); } -std::unique_ptr<Bucket> RasterLayer::Impl::createBucket(BucketParameters&, const GeometryTileLayer&) const { +std::unique_ptr<Bucket> RasterLayer::Impl::createBucket(const BucketParameters&, const std::vector<const Layer*>&) const { + assert(false); return nullptr; } diff --git a/src/mbgl/style/layers/raster_layer_impl.hpp b/src/mbgl/style/layers/raster_layer_impl.hpp index 8e69c21ca8..42985ce0f1 100644 --- a/src/mbgl/style/layers/raster_layer_impl.hpp +++ b/src/mbgl/style/layers/raster_layer_impl.hpp @@ -16,7 +16,7 @@ public: void cascade(const CascadeParameters&) override; bool evaluate(const PropertyEvaluationParameters&) override; - std::unique_ptr<Bucket> createBucket(BucketParameters&, const GeometryTileLayer&) const override; + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const Layer*>&) const override; RasterPaintProperties paint; }; diff --git a/src/mbgl/style/layers/raster_layer_properties.hpp b/src/mbgl/style/layers/raster_layer_properties.hpp index caa6d0c58d..219fe34d8c 100644 --- a/src/mbgl/style/layers/raster_layer_properties.hpp +++ b/src/mbgl/style/layers/raster_layer_properties.hpp @@ -5,6 +5,7 @@ #include <mbgl/style/types.hpp> #include <mbgl/style/layout_property.hpp> #include <mbgl/style/paint_property.hpp> +#include <mbgl/programs/attributes.hpp> namespace mbgl { namespace style { diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp index c9014e7ee7..d85b8c00e6 100644 --- a/src/mbgl/style/layers/symbol_layer.cpp +++ b/src/mbgl/style/layers/symbol_layer.cpp @@ -217,15 +217,15 @@ void SymbolLayer::setIconImage(PropertyValue<std::string> value) { impl->layout.unevaluated.get<IconImage>() = value; impl->observer->onLayerLayoutPropertyChanged(*this, "icon-image"); } -PropertyValue<float> SymbolLayer::getDefaultIconRotate() { +DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconRotate() { return IconRotate::defaultValue(); } -PropertyValue<float> SymbolLayer::getIconRotate() const { +DataDrivenPropertyValue<float> SymbolLayer::getIconRotate() const { return impl->layout.unevaluated.get<IconRotate>(); } -void SymbolLayer::setIconRotate(PropertyValue<float> value) { +void SymbolLayer::setIconRotate(DataDrivenPropertyValue<float> value) { if (value == getIconRotate()) return; impl->layout.unevaluated.get<IconRotate>() = value; @@ -259,15 +259,15 @@ void SymbolLayer::setIconKeepUpright(PropertyValue<bool> value) { impl->layout.unevaluated.get<IconKeepUpright>() = value; impl->observer->onLayerLayoutPropertyChanged(*this, "icon-keep-upright"); } -PropertyValue<std::array<float, 2>> SymbolLayer::getDefaultIconOffset() { +DataDrivenPropertyValue<std::array<float, 2>> SymbolLayer::getDefaultIconOffset() { return IconOffset::defaultValue(); } -PropertyValue<std::array<float, 2>> SymbolLayer::getIconOffset() const { +DataDrivenPropertyValue<std::array<float, 2>> SymbolLayer::getIconOffset() const { return impl->layout.unevaluated.get<IconOffset>(); } -void SymbolLayer::setIconOffset(PropertyValue<std::array<float, 2>> value) { +void SymbolLayer::setIconOffset(DataDrivenPropertyValue<std::array<float, 2>> value) { if (value == getIconOffset()) return; impl->layout.unevaluated.get<IconOffset>() = value; @@ -301,15 +301,15 @@ void SymbolLayer::setTextRotationAlignment(PropertyValue<AlignmentType> value) { impl->layout.unevaluated.get<TextRotationAlignment>() = value; impl->observer->onLayerLayoutPropertyChanged(*this, "text-rotation-alignment"); } -PropertyValue<std::string> SymbolLayer::getDefaultTextField() { +DataDrivenPropertyValue<std::string> SymbolLayer::getDefaultTextField() { return TextField::defaultValue(); } -PropertyValue<std::string> SymbolLayer::getTextField() const { +DataDrivenPropertyValue<std::string> SymbolLayer::getTextField() const { return impl->layout.unevaluated.get<TextField>(); } -void SymbolLayer::setTextField(PropertyValue<std::string> value) { +void SymbolLayer::setTextField(DataDrivenPropertyValue<std::string> value) { if (value == getTextField()) return; impl->layout.unevaluated.get<TextField>() = value; @@ -469,15 +469,15 @@ void SymbolLayer::setTextKeepUpright(PropertyValue<bool> value) { impl->layout.unevaluated.get<TextKeepUpright>() = value; impl->observer->onLayerLayoutPropertyChanged(*this, "text-keep-upright"); } -PropertyValue<TextTransformType> SymbolLayer::getDefaultTextTransform() { +DataDrivenPropertyValue<TextTransformType> SymbolLayer::getDefaultTextTransform() { return TextTransform::defaultValue(); } -PropertyValue<TextTransformType> SymbolLayer::getTextTransform() const { +DataDrivenPropertyValue<TextTransformType> SymbolLayer::getTextTransform() const { return impl->layout.unevaluated.get<TextTransform>(); } -void SymbolLayer::setTextTransform(PropertyValue<TextTransformType> value) { +void SymbolLayer::setTextTransform(DataDrivenPropertyValue<TextTransformType> value) { if (value == getTextTransform()) return; impl->layout.unevaluated.get<TextTransform>() = value; @@ -542,79 +542,119 @@ void SymbolLayer::setTextOptional(PropertyValue<bool> value) { // Paint properties -PropertyValue<float> SymbolLayer::getDefaultIconOpacity() { +DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconOpacity() { return { 1 }; } -PropertyValue<float> SymbolLayer::getIconOpacity(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> SymbolLayer::getIconOpacity(const optional<std::string>& klass) const { return impl->paint.get<IconOpacity>(klass); } -void SymbolLayer::setIconOpacity(PropertyValue<float> value, const optional<std::string>& klass) { +void SymbolLayer::setIconOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getIconOpacity(klass)) return; impl->paint.set<IconOpacity>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void SymbolLayer::setIconOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<IconOpacity>(value, klass); } -PropertyValue<Color> SymbolLayer::getDefaultIconColor() { +DataDrivenPropertyValue<Color> SymbolLayer::getDefaultIconColor() { return { Color::black() }; } -PropertyValue<Color> SymbolLayer::getIconColor(const optional<std::string>& klass) const { +DataDrivenPropertyValue<Color> SymbolLayer::getIconColor(const optional<std::string>& klass) const { return impl->paint.get<IconColor>(klass); } -void SymbolLayer::setIconColor(PropertyValue<Color> value, const optional<std::string>& klass) { +void SymbolLayer::setIconColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { if (value == getIconColor(klass)) return; impl->paint.set<IconColor>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void SymbolLayer::setIconColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<IconColor>(value, klass); } -PropertyValue<Color> SymbolLayer::getDefaultIconHaloColor() { +DataDrivenPropertyValue<Color> SymbolLayer::getDefaultIconHaloColor() { return { {} }; } -PropertyValue<Color> SymbolLayer::getIconHaloColor(const optional<std::string>& klass) const { +DataDrivenPropertyValue<Color> SymbolLayer::getIconHaloColor(const optional<std::string>& klass) const { return impl->paint.get<IconHaloColor>(klass); } -void SymbolLayer::setIconHaloColor(PropertyValue<Color> value, const optional<std::string>& klass) { +void SymbolLayer::setIconHaloColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { if (value == getIconHaloColor(klass)) return; impl->paint.set<IconHaloColor>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } -PropertyValue<float> SymbolLayer::getDefaultIconHaloWidth() { +void SymbolLayer::setIconHaloColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<IconHaloColor>(value, klass); +} + +DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconHaloWidth() { return { 0 }; } -PropertyValue<float> SymbolLayer::getIconHaloWidth(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> SymbolLayer::getIconHaloWidth(const optional<std::string>& klass) const { return impl->paint.get<IconHaloWidth>(klass); } -void SymbolLayer::setIconHaloWidth(PropertyValue<float> value, const optional<std::string>& klass) { +void SymbolLayer::setIconHaloWidth(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getIconHaloWidth(klass)) return; impl->paint.set<IconHaloWidth>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } -PropertyValue<float> SymbolLayer::getDefaultIconHaloBlur() { +void SymbolLayer::setIconHaloWidthTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<IconHaloWidth>(value, klass); +} + +DataDrivenPropertyValue<float> SymbolLayer::getDefaultIconHaloBlur() { return { 0 }; } -PropertyValue<float> SymbolLayer::getIconHaloBlur(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> SymbolLayer::getIconHaloBlur(const optional<std::string>& klass) const { return impl->paint.get<IconHaloBlur>(klass); } -void SymbolLayer::setIconHaloBlur(PropertyValue<float> value, const optional<std::string>& klass) { +void SymbolLayer::setIconHaloBlur(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getIconHaloBlur(klass)) return; impl->paint.set<IconHaloBlur>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void SymbolLayer::setIconHaloBlurTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<IconHaloBlur>(value, klass); } PropertyValue<std::array<float, 2>> SymbolLayer::getDefaultIconTranslate() { @@ -632,6 +672,10 @@ void SymbolLayer::setIconTranslate(PropertyValue<std::array<float, 2>> value, co impl->observer->onLayerPaintPropertyChanged(*this); } +void SymbolLayer::setIconTranslateTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<IconTranslate>(value, klass); +} + PropertyValue<TranslateAnchorType> SymbolLayer::getDefaultIconTranslateAnchor() { return { TranslateAnchorType::Map }; } @@ -647,79 +691,123 @@ void SymbolLayer::setIconTranslateAnchor(PropertyValue<TranslateAnchorType> valu impl->observer->onLayerPaintPropertyChanged(*this); } -PropertyValue<float> SymbolLayer::getDefaultTextOpacity() { +void SymbolLayer::setIconTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<IconTranslateAnchor>(value, klass); +} + +DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextOpacity() { return { 1 }; } -PropertyValue<float> SymbolLayer::getTextOpacity(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> SymbolLayer::getTextOpacity(const optional<std::string>& klass) const { return impl->paint.get<TextOpacity>(klass); } -void SymbolLayer::setTextOpacity(PropertyValue<float> value, const optional<std::string>& klass) { +void SymbolLayer::setTextOpacity(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getTextOpacity(klass)) return; impl->paint.set<TextOpacity>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void SymbolLayer::setTextOpacityTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<TextOpacity>(value, klass); } -PropertyValue<Color> SymbolLayer::getDefaultTextColor() { +DataDrivenPropertyValue<Color> SymbolLayer::getDefaultTextColor() { return { Color::black() }; } -PropertyValue<Color> SymbolLayer::getTextColor(const optional<std::string>& klass) const { +DataDrivenPropertyValue<Color> SymbolLayer::getTextColor(const optional<std::string>& klass) const { return impl->paint.get<TextColor>(klass); } -void SymbolLayer::setTextColor(PropertyValue<Color> value, const optional<std::string>& klass) { +void SymbolLayer::setTextColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { if (value == getTextColor(klass)) return; impl->paint.set<TextColor>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void SymbolLayer::setTextColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<TextColor>(value, klass); } -PropertyValue<Color> SymbolLayer::getDefaultTextHaloColor() { +DataDrivenPropertyValue<Color> SymbolLayer::getDefaultTextHaloColor() { return { {} }; } -PropertyValue<Color> SymbolLayer::getTextHaloColor(const optional<std::string>& klass) const { +DataDrivenPropertyValue<Color> SymbolLayer::getTextHaloColor(const optional<std::string>& klass) const { return impl->paint.get<TextHaloColor>(klass); } -void SymbolLayer::setTextHaloColor(PropertyValue<Color> value, const optional<std::string>& klass) { +void SymbolLayer::setTextHaloColor(DataDrivenPropertyValue<Color> value, const optional<std::string>& klass) { if (value == getTextHaloColor(klass)) return; impl->paint.set<TextHaloColor>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } -PropertyValue<float> SymbolLayer::getDefaultTextHaloWidth() { +void SymbolLayer::setTextHaloColorTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<TextHaloColor>(value, klass); +} + +DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextHaloWidth() { return { 0 }; } -PropertyValue<float> SymbolLayer::getTextHaloWidth(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> SymbolLayer::getTextHaloWidth(const optional<std::string>& klass) const { return impl->paint.get<TextHaloWidth>(klass); } -void SymbolLayer::setTextHaloWidth(PropertyValue<float> value, const optional<std::string>& klass) { +void SymbolLayer::setTextHaloWidth(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getTextHaloWidth(klass)) return; impl->paint.set<TextHaloWidth>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } -PropertyValue<float> SymbolLayer::getDefaultTextHaloBlur() { +void SymbolLayer::setTextHaloWidthTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<TextHaloWidth>(value, klass); +} + +DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextHaloBlur() { return { 0 }; } -PropertyValue<float> SymbolLayer::getTextHaloBlur(const optional<std::string>& klass) const { +DataDrivenPropertyValue<float> SymbolLayer::getTextHaloBlur(const optional<std::string>& klass) const { return impl->paint.get<TextHaloBlur>(klass); } -void SymbolLayer::setTextHaloBlur(PropertyValue<float> value, const optional<std::string>& klass) { +void SymbolLayer::setTextHaloBlur(DataDrivenPropertyValue<float> value, const optional<std::string>& klass) { if (value == getTextHaloBlur(klass)) return; impl->paint.set<TextHaloBlur>(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } +} + +void SymbolLayer::setTextHaloBlurTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<TextHaloBlur>(value, klass); } PropertyValue<std::array<float, 2>> SymbolLayer::getDefaultTextTranslate() { @@ -737,6 +825,10 @@ void SymbolLayer::setTextTranslate(PropertyValue<std::array<float, 2>> value, co impl->observer->onLayerPaintPropertyChanged(*this); } +void SymbolLayer::setTextTranslateTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<TextTranslate>(value, klass); +} + PropertyValue<TranslateAnchorType> SymbolLayer::getDefaultTextTranslateAnchor() { return { TranslateAnchorType::Map }; } @@ -752,5 +844,9 @@ void SymbolLayer::setTextTranslateAnchor(PropertyValue<TranslateAnchorType> valu impl->observer->onLayerPaintPropertyChanged(*this); } +void SymbolLayer::setTextTranslateAnchorTransition(const TransitionOptions& value, const optional<std::string>& klass) { + impl->paint.setTransition<TextTranslateAnchor>(value, klass); +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/symbol_layer_impl.cpp b/src/mbgl/style/layers/symbol_layer_impl.cpp index f058598f5f..ff59b14d65 100644 --- a/src/mbgl/style/layers/symbol_layer_impl.cpp +++ b/src/mbgl/style/layers/symbol_layer_impl.cpp @@ -1,5 +1,5 @@ #include <mbgl/style/layers/symbol_layer_impl.hpp> -#include <mbgl/style/bucket_parameters.hpp> +#include <mbgl/style/property_evaluation_parameters.hpp> #include <mbgl/layout/symbol_layout.hpp> #include <mbgl/renderer/bucket.hpp> @@ -16,77 +16,70 @@ bool SymbolLayer::Impl::evaluate(const PropertyEvaluationParameters& parameters) // text-size and icon-size are layout properties but they also need to be evaluated as paint properties: iconSize = layout.evaluate<IconSize>(parameters); textSize = layout.evaluate<TextSize>(parameters); - - passes = ((paint.evaluated.get<IconOpacity>() > 0 && (paint.evaluated.get<IconColor>().a > 0 || paint.evaluated.get<IconHaloColor>().a > 0) && iconSize > 0) - || (paint.evaluated.get<TextOpacity>() > 0 && (paint.evaluated.get<TextColor>().a > 0 || paint.evaluated.get<TextHaloColor>().a > 0) && textSize > 0)) + + auto hasIconOpacity = paint.evaluated.get<IconColor>().constantOr(Color::black()).a > 0 || + paint.evaluated.get<IconHaloColor>().constantOr(Color::black()).a > 0; + auto hasTextOpacity = paint.evaluated.get<TextColor>().constantOr(Color::black()).a > 0 || + paint.evaluated.get<TextHaloColor>().constantOr(Color::black()).a > 0; + + passes = ((paint.evaluated.get<IconOpacity>().constantOr(1) > 0 && hasIconOpacity && iconSize > 0) + || (paint.evaluated.get<TextOpacity>().constantOr(1) > 0 && hasTextOpacity && textSize > 0)) ? RenderPass::Translucent : RenderPass::None; return paint.hasTransition(); } -std::unique_ptr<Bucket> SymbolLayer::Impl::createBucket(BucketParameters&, const GeometryTileLayer&) const { +std::unique_ptr<Bucket> SymbolLayer::Impl::createBucket(const BucketParameters&, const std::vector<const Layer*>&) const { assert(false); // Should be calling createLayout() instead. return nullptr; } -std::unique_ptr<SymbolLayout> SymbolLayer::Impl::createLayout(BucketParameters& parameters, - const GeometryTileLayer& layer, - std::vector<std::string> group) const { - PropertyEvaluationParameters p(parameters.tileID.overscaledZ); - SymbolLayoutProperties::Evaluated evaluated = layout.evaluate(p); - - if (evaluated.get<IconRotationAlignment>() == AlignmentType::Auto) { - if (evaluated.get<SymbolPlacement>() == SymbolPlacementType::Line) { - evaluated.get<IconRotationAlignment>() = AlignmentType::Map; - } else { - evaluated.get<IconRotationAlignment>() = AlignmentType::Viewport; - } - } - - if (evaluated.get<TextRotationAlignment>() == AlignmentType::Auto) { - if (evaluated.get<SymbolPlacement>() == SymbolPlacementType::Line) { - evaluated.get<TextRotationAlignment>() = AlignmentType::Map; - } else { - evaluated.get<TextRotationAlignment>() = AlignmentType::Viewport; - } - } - - // If unspecified `text-pitch-alignment` inherits `text-rotation-alignment` - if (evaluated.get<TextPitchAlignment>() == AlignmentType::Auto) { - evaluated.get<TextPitchAlignment>() = evaluated.get<TextRotationAlignment>(); - } - - float textMaxSize = layout.evaluate<TextSize>(PropertyEvaluationParameters(18)); - - evaluated.get<IconSize>() = layout.evaluate<IconSize>(PropertyEvaluationParameters(p.z + 1)); - evaluated.get<TextSize>() = layout.evaluate<TextSize>(PropertyEvaluationParameters(p.z + 1)); - - return std::make_unique<SymbolLayout>(std::move(group), - layer.getName(), - parameters.tileID.overscaleFactor(), - parameters.tileID.overscaledZ, - parameters.mode, +std::unique_ptr<SymbolLayout> SymbolLayer::Impl::createLayout(const BucketParameters& parameters, + const std::vector<const Layer*>& group, + const GeometryTileLayer& layer) const { + return std::make_unique<SymbolLayout>(parameters, + group, layer, - filter, - evaluated, - textMaxSize, *spriteAtlas); } -SymbolPropertyValues SymbolLayer::Impl::iconPropertyValues(const SymbolLayoutProperties::Evaluated& layout_) const { - return SymbolPropertyValues { - layout_.get<IconRotationAlignment>(), // icon-pitch-alignment is not yet implemented; inherit the rotation alignment - layout_.get<IconRotationAlignment>(), - layout_.get<IconSize>(), +IconPaintProperties::Evaluated SymbolLayer::Impl::iconPaintProperties() const { + return IconPaintProperties::Evaluated { paint.evaluated.get<IconOpacity>(), paint.evaluated.get<IconColor>(), paint.evaluated.get<IconHaloColor>(), paint.evaluated.get<IconHaloWidth>(), paint.evaluated.get<IconHaloBlur>(), paint.evaluated.get<IconTranslate>(), + paint.evaluated.get<IconTranslateAnchor>() + }; +} + +TextPaintProperties::Evaluated SymbolLayer::Impl::textPaintProperties() const { + return TextPaintProperties::Evaluated { + paint.evaluated.get<TextOpacity>(), + paint.evaluated.get<TextColor>(), + paint.evaluated.get<TextHaloColor>(), + paint.evaluated.get<TextHaloWidth>(), + paint.evaluated.get<TextHaloBlur>(), + paint.evaluated.get<TextTranslate>(), + paint.evaluated.get<TextTranslateAnchor>() + }; +} + + +SymbolPropertyValues SymbolLayer::Impl::iconPropertyValues(const SymbolLayoutProperties::Evaluated& layout_) const { + return SymbolPropertyValues { + layout_.get<IconRotationAlignment>(), // icon-pitch-alignment is not yet implemented; inherit the rotation alignment + layout_.get<IconRotationAlignment>(), + layout_.get<IconSize>(), + paint.evaluated.get<IconTranslate>(), paint.evaluated.get<IconTranslateAnchor>(), iconSize, - 1.0f + 1.0f, + paint.evaluated.get<IconHaloColor>().constantOr(Color::black()).a > 0 && + paint.evaluated.get<IconHaloWidth>().constantOr(1), + paint.evaluated.get<IconColor>().constantOr(Color::black()).a > 0 }; } @@ -95,15 +88,13 @@ SymbolPropertyValues SymbolLayer::Impl::textPropertyValues(const SymbolLayoutPro layout_.get<TextPitchAlignment>(), layout_.get<TextRotationAlignment>(), layout_.get<TextSize>(), - paint.evaluated.get<TextOpacity>(), - paint.evaluated.get<TextColor>(), - paint.evaluated.get<TextHaloColor>(), - paint.evaluated.get<TextHaloWidth>(), - paint.evaluated.get<TextHaloBlur>(), paint.evaluated.get<TextTranslate>(), paint.evaluated.get<TextTranslateAnchor>(), textSize, - 24.0f + 24.0f, + paint.evaluated.get<TextHaloColor>().constantOr(Color::black()).a > 0 && + paint.evaluated.get<TextHaloWidth>().constantOr(1), + paint.evaluated.get<TextColor>().constantOr(Color::black()).a > 0 }; } diff --git a/src/mbgl/style/layers/symbol_layer_impl.hpp b/src/mbgl/style/layers/symbol_layer_impl.hpp index db18da07e2..1e9f05e4c7 100644 --- a/src/mbgl/style/layers/symbol_layer_impl.hpp +++ b/src/mbgl/style/layers/symbol_layer_impl.hpp @@ -1,5 +1,6 @@ #pragma once +#include <mbgl/util/variant.hpp> #include <mbgl/style/layer_impl.hpp> #include <mbgl/style/layers/symbol_layer.hpp> #include <mbgl/style/layers/symbol_layer_properties.hpp> @@ -10,6 +11,30 @@ class SpriteAtlas; class SymbolLayout; namespace style { + + +// {icon,text}-specific paint-property packs for use in the symbol Programs. +// Since each program deals either with icons or text, using a smaller property set +// lets us avoid unnecessarily binding attributes for properties the program wouldn't use. +class IconPaintProperties : public PaintProperties< + IconOpacity, + IconColor, + IconHaloColor, + IconHaloWidth, + IconHaloBlur, + IconTranslate, + IconTranslateAnchor +> {}; + +class TextPaintProperties : public PaintProperties< + TextOpacity, + TextColor, + TextHaloColor, + TextHaloWidth, + TextHaloBlur, + TextTranslate, + TextTranslateAnchor +> {}; // Repackaging evaluated values from SymbolLayoutProperties + SymbolPaintProperties // for genericity over icons vs. text. @@ -21,24 +46,14 @@ public: float layoutSize; // Paint - float opacity; - Color color; - Color haloColor; - float haloWidth; - float haloBlur; std::array<float, 2> translate; TranslateAnchorType translateAnchor; float paintSize; float sdfScale; // Constant (1.0 or 24.0) - - bool hasHalo() const { - return haloColor.a > 0.0f && haloWidth > 0.0f; - } - - bool hasForeground() const { - return color.a > 0.0f; - } + + bool hasHalo; + bool hasFill; }; class SymbolLayer::Impl : public Layer::Impl { @@ -50,10 +65,13 @@ public: void cascade(const CascadeParameters&) override; bool evaluate(const PropertyEvaluationParameters&) override; - std::unique_ptr<Bucket> createBucket(BucketParameters&, const GeometryTileLayer&) const override; - std::unique_ptr<SymbolLayout> createLayout(BucketParameters&, const GeometryTileLayer&, - std::vector<std::string>) const; + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const Layer*>&) const override; + std::unique_ptr<SymbolLayout> createLayout(const BucketParameters&, const std::vector<const Layer*>&, + const GeometryTileLayer&) const; + IconPaintProperties::Evaluated iconPaintProperties() const; + TextPaintProperties::Evaluated textPaintProperties() const; + SymbolPropertyValues iconPropertyValues(const SymbolLayoutProperties::Evaluated&) const; SymbolPropertyValues textPropertyValues(const SymbolLayoutProperties::Evaluated&) const; diff --git a/src/mbgl/style/layers/symbol_layer_properties.hpp b/src/mbgl/style/layers/symbol_layer_properties.hpp index f5fd6ce3df..f2b7bfa00f 100644 --- a/src/mbgl/style/layers/symbol_layer_properties.hpp +++ b/src/mbgl/style/layers/symbol_layer_properties.hpp @@ -5,6 +5,7 @@ #include <mbgl/style/types.hpp> #include <mbgl/style/layout_property.hpp> #include <mbgl/style/paint_property.hpp> +#include <mbgl/programs/attributes.hpp> namespace mbgl { namespace style { @@ -64,7 +65,7 @@ struct IconImage : LayoutProperty<std::string> { static std::string defaultValue() { return ""; } }; -struct IconRotate : LayoutProperty<float> { +struct IconRotate : DataDrivenLayoutProperty<float> { static constexpr const char * key = "icon-rotate"; static float defaultValue() { return 0; } }; @@ -79,7 +80,7 @@ struct IconKeepUpright : LayoutProperty<bool> { static bool defaultValue() { return false; } }; -struct IconOffset : LayoutProperty<std::array<float, 2>> { +struct IconOffset : DataDrivenLayoutProperty<std::array<float, 2>> { static constexpr const char * key = "icon-offset"; static std::array<float, 2> defaultValue() { return {{ 0, 0 }}; } }; @@ -94,7 +95,7 @@ struct TextRotationAlignment : LayoutProperty<AlignmentType> { static AlignmentType defaultValue() { return AlignmentType::Auto; } }; -struct TextField : LayoutProperty<std::string> { +struct TextField : DataDrivenLayoutProperty<std::string> { static constexpr const char * key = "text-field"; static std::string defaultValue() { return ""; } }; @@ -154,7 +155,7 @@ struct TextKeepUpright : LayoutProperty<bool> { static bool defaultValue() { return true; } }; -struct TextTransform : LayoutProperty<TextTransformType> { +struct TextTransform : DataDrivenLayoutProperty<TextTransformType> { static constexpr const char * key = "text-transform"; static TextTransformType defaultValue() { return TextTransformType::None; } }; @@ -179,23 +180,23 @@ struct TextOptional : LayoutProperty<bool> { static bool defaultValue() { return false; } }; -struct IconOpacity : PaintProperty<float> { +struct IconOpacity : DataDrivenPaintProperty<float, attributes::a_opacity> { static float defaultValue() { return 1; } }; -struct IconColor : PaintProperty<Color> { +struct IconColor : DataDrivenPaintProperty<Color, attributes::a_fill_color> { static Color defaultValue() { return Color::black(); } }; -struct IconHaloColor : PaintProperty<Color> { +struct IconHaloColor : DataDrivenPaintProperty<Color, attributes::a_halo_color> { static Color defaultValue() { return {}; } }; -struct IconHaloWidth : PaintProperty<float> { +struct IconHaloWidth : DataDrivenPaintProperty<float, attributes::a_halo_width> { static float defaultValue() { return 0; } }; -struct IconHaloBlur : PaintProperty<float> { +struct IconHaloBlur : DataDrivenPaintProperty<float, attributes::a_halo_blur> { static float defaultValue() { return 0; } }; @@ -207,23 +208,23 @@ struct IconTranslateAnchor : PaintProperty<TranslateAnchorType> { static TranslateAnchorType defaultValue() { return TranslateAnchorType::Map; } }; -struct TextOpacity : PaintProperty<float> { +struct TextOpacity : DataDrivenPaintProperty<float, attributes::a_opacity> { static float defaultValue() { return 1; } }; -struct TextColor : PaintProperty<Color> { +struct TextColor : DataDrivenPaintProperty<Color, attributes::a_fill_color> { static Color defaultValue() { return Color::black(); } }; -struct TextHaloColor : PaintProperty<Color> { +struct TextHaloColor : DataDrivenPaintProperty<Color, attributes::a_halo_color> { static Color defaultValue() { return {}; } }; -struct TextHaloWidth : PaintProperty<float> { +struct TextHaloWidth : DataDrivenPaintProperty<float, attributes::a_halo_width> { static float defaultValue() { return 0; } }; -struct TextHaloBlur : PaintProperty<float> { +struct TextHaloBlur : DataDrivenPaintProperty<float, attributes::a_halo_blur> { static float defaultValue() { return 0; } }; diff --git a/src/mbgl/style/layout_property.hpp b/src/mbgl/style/layout_property.hpp index 6ea06ce556..25cff4b92d 100644 --- a/src/mbgl/style/layout_property.hpp +++ b/src/mbgl/style/layout_property.hpp @@ -1,6 +1,9 @@ #pragma once +#include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/style/property_evaluator.hpp> +#include <mbgl/style/data_driven_property_evaluator.hpp> #include <mbgl/util/indexed_tuple.hpp> namespace mbgl { @@ -11,9 +14,19 @@ class PropertyEvaluationParameters; template <class T> class LayoutProperty { public: - using EvaluatorType = PropertyEvaluator<T>; using UnevaluatedType = PropertyValue<T>; + using EvaluatorType = PropertyEvaluator<T>; using EvaluatedType = T; + using Type = T; +}; + +template <class T> +class DataDrivenLayoutProperty { +public: + using UnevaluatedType = DataDrivenPropertyValue<T>; + using EvaluatorType = DataDrivenPropertyEvaluator<T>; + using EvaluatedType = PossiblyEvaluatedPropertyValue<T>; + using Type = T; }; template <class... Ps> @@ -29,6 +42,15 @@ public: class Evaluated : public Tuple<EvaluatedTypes> { public: using Tuple<EvaluatedTypes>::Tuple; + + template <class P> + typename P::Type evaluate(float z, const GeometryTileFeature& feature) const { + using T = typename P::Type; + return this->template get<P>().match( + [&] (const T& t) { return t; }, + [&] (const SourceFunction<T>& t) { return t.evaluate(feature, P::defaultValue()); }, + [&] (const CompositeFunction<T>& t) { return t.evaluate(z, feature, P::defaultValue()); }); + } }; class Unevaluated : public Tuple<UnevaluatedTypes> { diff --git a/src/mbgl/style/paint_property.hpp b/src/mbgl/style/paint_property.hpp index 7b56f6415d..1fa2390f33 100644 --- a/src/mbgl/style/paint_property.hpp +++ b/src/mbgl/style/paint_property.hpp @@ -1,11 +1,15 @@ #pragma once #include <mbgl/style/class_dictionary.hpp> +#include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/style/property_evaluator.hpp> #include <mbgl/style/cross_faded_property_evaluator.hpp> +#include <mbgl/style/data_driven_property_evaluator.hpp> +#include <mbgl/style/property_evaluation_parameters.hpp> #include <mbgl/style/transition_options.hpp> #include <mbgl/style/cascade_parameters.hpp> -#include <mbgl/style/property_evaluation_parameters.hpp> +#include <mbgl/style/paint_property_binder.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/interpolate.hpp> #include <mbgl/util/indexed_tuple.hpp> @@ -15,17 +19,18 @@ #include <utility> namespace mbgl { + +class GeometryTileFeature; + namespace style { -template <class T, class Evaluator> +template <class Value> class UnevaluatedPaintProperty { public: - using Result = typename Evaluator::ResultType; - UnevaluatedPaintProperty() = default; - UnevaluatedPaintProperty(PropertyValue<T> value_, - UnevaluatedPaintProperty<T, Evaluator> prior_, + UnevaluatedPaintProperty(Value value_, + UnevaluatedPaintProperty<Value> prior_, TransitionOptions transition, TimePoint now) : begin(now + transition.delay.value_or(Duration::zero())), @@ -36,22 +41,23 @@ public: } } - Result evaluate(const PropertyEvaluationParameters& parameters, T defaultValue) { - Result finalValue = value.evaluate(Evaluator(parameters, defaultValue)); + template <class Evaluator> + auto evaluate(const Evaluator& evaluator, TimePoint now) { + auto finalValue = value.evaluate(evaluator); if (!prior) { // No prior value. return finalValue; - } else if (parameters.now >= end) { + } else if (now >= end) { // Transition from prior value is now complete. prior = {}; return finalValue; - } else if (parameters.now < begin) { + } else if (now < begin) { // Transition hasn't started yet. - return prior->get().evaluate(parameters, defaultValue); + return prior->get().evaluate(evaluator, now); } else { // Interpolate between recursively-calculated prior value and final. - float t = std::chrono::duration<float>(parameters.now - begin) / (end - begin); - return util::interpolate(prior->get().evaluate(parameters, defaultValue), finalValue, util::DEFAULT_TRANSITION_EASE.solve(t, 0.001)); + float t = std::chrono::duration<float>(now - begin) / (end - begin); + return util::interpolate(prior->get().evaluate(evaluator, now), finalValue, util::DEFAULT_TRANSITION_EASE.solve(t, 0.001)); } } @@ -63,30 +69,40 @@ public: return value.isUndefined(); } + const Value& getValue() const { + return value; + } + private: - optional<mapbox::util::recursive_wrapper<UnevaluatedPaintProperty<T, Evaluator>>> prior; + optional<mapbox::util::recursive_wrapper<UnevaluatedPaintProperty<Value>>> prior; TimePoint begin; TimePoint end; - PropertyValue<T> value; + Value value; }; -template <class T> +template <class Value> class CascadingPaintProperty { public: bool isUndefined() const { return values.find(ClassID::Default) == values.end(); } - const PropertyValue<T>& get(const optional<std::string>& klass) const { - static const PropertyValue<T> staticValue; + const Value& get(const optional<std::string>& klass) const { + static const Value staticValue{}; const auto it = values.find(klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default); return it == values.end() ? staticValue : it->second; } - void set(const PropertyValue<T>& value_, const optional<std::string>& klass) { + void set(const Value& value_, const optional<std::string>& klass) { values[klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default] = value_; } + const TransitionOptions& getTransition(const optional<std::string>& klass) const { + static const TransitionOptions staticValue{}; + const auto it = transitions.find(klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default); + return it == transitions.end() ? staticValue : it->second; + } + void setTransition(const TransitionOptions& transition, const optional<std::string>& klass) { transitions[klass ? ClassDictionary::Get().lookup(*klass) : ClassID::Default] = transition; } @@ -94,7 +110,7 @@ public: template <class UnevaluatedPaintProperty> UnevaluatedPaintProperty cascade(const CascadeParameters& params, UnevaluatedPaintProperty prior) const { TransitionOptions transition; - PropertyValue<T> value; + Value value; for (const auto classID : params.classes) { if (values.find(classID) != values.end()) { @@ -117,7 +133,7 @@ public: } private: - std::unordered_map<ClassID, PropertyValue<T>> values; + std::unordered_map<ClassID, Value> values; std::unordered_map<ClassID, TransitionOptions> transitions; }; @@ -125,26 +141,48 @@ template <class T> class PaintProperty { public: using ValueType = PropertyValue<T>; - using CascadingType = CascadingPaintProperty<T>; + using CascadingType = CascadingPaintProperty<ValueType>; + using UnevaluatedType = UnevaluatedPaintProperty<ValueType>; using EvaluatorType = PropertyEvaluator<T>; - using UnevaluatedType = UnevaluatedPaintProperty<T, EvaluatorType>; using EvaluatedType = T; + static constexpr bool IsDataDriven = false; +}; + +template <class T, class A> +class DataDrivenPaintProperty { +public: + using ValueType = DataDrivenPropertyValue<T>; + using CascadingType = CascadingPaintProperty<ValueType>; + using UnevaluatedType = UnevaluatedPaintProperty<ValueType>; + using EvaluatorType = DataDrivenPropertyEvaluator<T>; + using EvaluatedType = PossiblyEvaluatedPropertyValue<T>; + static constexpr bool IsDataDriven = true; + + using Type = T; + using Attribute = A; }; template <class T> class CrossFadedPaintProperty { public: using ValueType = PropertyValue<T>; - using CascadingType = CascadingPaintProperty<T>; + using CascadingType = CascadingPaintProperty<ValueType>; + using UnevaluatedType = UnevaluatedPaintProperty<ValueType>; using EvaluatorType = CrossFadedPropertyEvaluator<T>; - using UnevaluatedType = UnevaluatedPaintProperty<T, EvaluatorType>; using EvaluatedType = Faded<T>; + static constexpr bool IsDataDriven = false; }; +template <class P> +struct IsDataDriven : std::integral_constant<bool, P::IsDataDriven> {}; + template <class... Ps> class PaintProperties { public: using Properties = TypeList<Ps...>; + using DataDrivenProperties = FilteredTypeList<Properties, IsDataDriven>; + using Binders = PaintPropertyBinders<DataDrivenProperties>; + using EvaluatedTypes = TypeList<typename Ps::EvaluatedType...>; using UnevaluatedTypes = TypeList<typename Ps::UnevaluatedType...>; using CascadingTypes = TypeList<typename Ps::CascadingType...>; @@ -177,6 +215,11 @@ public: cascading.template get<P>().set(value, klass); } + template <class P> + void setTransition(const TransitionOptions& value, const optional<std::string>& klass) { + cascading.template get<P>().setTransition(value, klass); + } + void cascade(const CascadeParameters& parameters) { unevaluated = Unevaluated { cascading.template get<Ps>().cascade(parameters, @@ -186,7 +229,9 @@ public: template <class P> auto evaluate(const PropertyEvaluationParameters& parameters) { - return unevaluated.template get<P>().evaluate(parameters, P::defaultValue()); + using Evaluator = typename P::EvaluatorType; + return unevaluated.template get<P>() + .evaluate(Evaluator(parameters, P::defaultValue()), parameters.now); } void evaluate(const PropertyEvaluationParameters& parameters) { diff --git a/src/mbgl/style/paint_property_binder.hpp b/src/mbgl/style/paint_property_binder.hpp new file mode 100644 index 0000000000..79c7692b2f --- /dev/null +++ b/src/mbgl/style/paint_property_binder.hpp @@ -0,0 +1,287 @@ +#pragma once + +#include <mbgl/programs/attributes.hpp> +#include <mbgl/gl/attribute.hpp> +#include <mbgl/gl/uniform.hpp> +#include <mbgl/util/type_list.hpp> + +namespace mbgl { +namespace style { + +template <class T, class A> +class ConstantPaintPropertyBinder { +public: + using Attribute = A; + using AttributeValue = typename Attribute::Value; + using AttributeBinding = typename Attribute::Binding; + + ConstantPaintPropertyBinder(T constant_) + : constant(std::move(constant_)) { + } + + void populateVertexVector(const GeometryTileFeature&, std::size_t) {} + void upload(gl::Context&) {} + + AttributeBinding minAttributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const { + return typename Attribute::ConstantBinding { + Attribute::value(currentValue.constantOr(constant)) + }; + } + + AttributeBinding maxAttributeBinding(const PossiblyEvaluatedPropertyValue<T>&) const { + return AttributeBinding(); + } + + float interpolationFactor(float) const { + return 0.0f; + } + +private: + T constant; +}; + +template <class T, class A> +class SourceFunctionPaintPropertyBinder { +public: + using Attribute = A; + using AttributeValue = typename Attribute::Value; + using AttributeBinding = typename Attribute::Binding; + + using Attributes = gl::Attributes<Attribute>; + using Vertex = typename Attributes::Vertex; + + SourceFunctionPaintPropertyBinder(SourceFunction<T> function_, T defaultValue_) + : function(std::move(function_)), + defaultValue(std::move(defaultValue_)) { + } + + void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) { + AttributeValue value = Attribute::value(function.evaluate(feature, defaultValue)); + for (std::size_t i = vertexVector.vertexSize(); i < length; ++i) { + vertexVector.emplace_back(Vertex { value }); + } + } + + void upload(gl::Context& context) { + vertexBuffer = context.createVertexBuffer(std::move(vertexVector)); + } + + AttributeBinding minAttributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const { + if (currentValue.isConstant()) { + return typename Attribute::ConstantBinding { + Attribute::value(*currentValue.constant()) + }; + } else { + return Attributes::allVariableBindings(*vertexBuffer) + .template get<Attribute>(); + } + } + + AttributeBinding maxAttributeBinding(const PossiblyEvaluatedPropertyValue<T>&) const { + return AttributeBinding(); + } + + float interpolationFactor(float) const { + return 0.0f; + } + +private: + SourceFunction<T> function; + T defaultValue; + gl::VertexVector<Vertex> vertexVector; + optional<gl::VertexBuffer<Vertex>> vertexBuffer; +}; + +template <class T, class A> +class CompositeFunctionPaintPropertyBinder { +public: + using Attribute = A; + using AttributeValue = typename Attribute::Value; + using AttributeBinding = typename Attribute::Binding; + + using MinAttribute = attributes::Min<Attribute>; + using MaxAttribute = attributes::Max<Attribute>; + + using Attributes = gl::Attributes<MinAttribute, MaxAttribute>; + using Vertex = typename Attributes::Vertex; + + CompositeFunctionPaintPropertyBinder(CompositeFunction<T> function_, float zoom, T defaultValue_) + : function(std::move(function_)), + defaultValue(std::move(defaultValue_)), + coveringRanges(function.coveringRanges(zoom)) { + } + + void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) { + Range<T> range = function.evaluate(std::get<1>(coveringRanges), feature, defaultValue); + AttributeValue min = Attribute::value(range.min); + AttributeValue max = Attribute::value(range.max); + for (std::size_t i = vertexVector.vertexSize(); i < length; ++i) { + vertexVector.emplace_back(Vertex { min, max }); + } + } + + void upload(gl::Context& context) { + vertexBuffer = context.createVertexBuffer(std::move(vertexVector)); + } + + AttributeBinding minAttributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const { + if (currentValue.isConstant()) { + return typename Attribute::ConstantBinding { + Attribute::value(*currentValue.constant()) + }; + } else { + return Attributes::allVariableBindings(*vertexBuffer) + .template get<MinAttribute>(); + } + } + + AttributeBinding maxAttributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const { + if (currentValue.isConstant()) { + return AttributeBinding(); + } else { + return Attributes::allVariableBindings(*vertexBuffer) + .template get<MaxAttribute>(); + } + } + + float interpolationFactor(float currentZoom) const { + return util::interpolationFactor(1.0f, std::get<0>(coveringRanges), currentZoom); + } + +private: + using InnerStops = typename CompositeFunction<T>::InnerStops; + CompositeFunction<T> function; + T defaultValue; + std::tuple<Range<float>, Range<InnerStops>> coveringRanges; + gl::VertexVector<Vertex> vertexVector; + optional<gl::VertexBuffer<Vertex>> vertexBuffer; +}; + +template <class PaintProperty> +class PaintPropertyBinder { +public: + using Type = typename PaintProperty::Type; + using Attribute = typename PaintProperty::Attribute; + using PropertyValue = typename PaintProperty::EvaluatedType; + + using Binder = variant< + ConstantPaintPropertyBinder<Type, Attribute>, + SourceFunctionPaintPropertyBinder<Type, Attribute>, + CompositeFunctionPaintPropertyBinder<Type, Attribute>>; + + PaintPropertyBinder(const PropertyValue& value, float zoom) + : binder(value.match( + [&] (const Type& constant) -> Binder { + return ConstantPaintPropertyBinder<Type, Attribute>(constant); + }, + [&] (const SourceFunction<Type>& function) { + return SourceFunctionPaintPropertyBinder<Type, Attribute>(function, PaintProperty::defaultValue()); + }, + [&] (const CompositeFunction<Type>& function) { + return CompositeFunctionPaintPropertyBinder<Type, Attribute>(function, zoom, PaintProperty::defaultValue()); + } + )) { + } + + void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) { + binder.match([&] (auto& b) { + b.populateVertexVector(feature, length); + }); + } + + void upload(gl::Context& context) { + binder.match([&] (auto& b) { + b.upload(context); + }); + } + + using MinAttribute = attributes::Min<Attribute>; + using MaxAttribute = attributes::Max<Attribute>; + using AttributeBinding = typename Attribute::Binding; + + AttributeBinding minAttributeBinding(const PropertyValue& currentValue) const { + return binder.match([&] (const auto& b) { + return b.minAttributeBinding(currentValue); + }); + } + + AttributeBinding maxAttributeBinding(const PropertyValue& currentValue) const { + return binder.match([&] (const auto& b) { + return b.maxAttributeBinding(currentValue); + }); + } + + using InterpolationUniform = attributes::InterpolationUniform<Attribute>; + using InterpolationUniformValue = typename InterpolationUniform::Value; + + InterpolationUniformValue interpolationUniformValue(float currentZoom) const { + return InterpolationUniformValue { + binder.match([&] (const auto& b) { + return b.interpolationFactor(currentZoom); + }) + }; + } + +private: + Binder binder; +}; + +template <class Ps> +class PaintPropertyBinders; + +template <class... Ps> +class PaintPropertyBinders<TypeList<Ps...>> { +public: + using Binders = IndexedTuple<TypeList<Ps...>, TypeList<PaintPropertyBinder<Ps>...>>; + + template <class EvaluatedProperties> + PaintPropertyBinders(const EvaluatedProperties& properties, float z) + : binders(PaintPropertyBinder<Ps>(properties.template get<Ps>(), z)...) { + (void)z; // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56958 + } + + void populateVertexVectors(const GeometryTileFeature& feature, std::size_t length) { + util::ignore({ + (binders.template get<Ps>().populateVertexVector(feature, length), 0)... + }); + } + + void upload(gl::Context& context) { + util::ignore({ + (binders.template get<Ps>().upload(context), 0)... + }); + } + + using MinAttributes = gl::Attributes<typename PaintPropertyBinder<Ps>::MinAttribute...>; + using MaxAttributes = gl::Attributes<typename PaintPropertyBinder<Ps>::MaxAttribute...>; + + using Attributes = gl::ConcatenateAttributes<MinAttributes, MaxAttributes>; + using AttributeBindings = typename Attributes::Bindings; + + template <class EvaluatedProperties> + AttributeBindings attributeBindings(const EvaluatedProperties& currentProperties) const { + const typename MinAttributes::Bindings min { + binders.template get<Ps>().minAttributeBinding(currentProperties.template get<Ps>())... + }; + const typename MaxAttributes::Bindings max { + binders.template get<Ps>().maxAttributeBinding(currentProperties.template get<Ps>())... + }; + return min.concat(max); + } + + using Uniforms = gl::Uniforms<typename PaintPropertyBinder<Ps>::InterpolationUniform...>; + using UniformValues = typename Uniforms::Values; + + UniformValues uniformValues(float currentZoom) const { + (void)currentZoom; // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56958 + return UniformValues { + binders.template get<Ps>().interpolationUniformValue(currentZoom)... + }; + } + +private: + Binders binders; +}; + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/parser.cpp b/src/mbgl/style/parser.cpp index c6c6e50dd7..926c243733 100644 --- a/src/mbgl/style/parser.cpp +++ b/src/mbgl/style/parser.cpp @@ -241,10 +241,14 @@ std::vector<FontStack> Parser::fontStacks() const { result.insert({"Open Sans Regular", "Arial Unicode MS Regular"}); } else if (textFont.isConstant()) { result.insert(textFont.asConstant()); - } else if (textFont.isFunction()) { - for (const auto& stop : textFont.asFunction().getStops()) { - result.insert(stop.second); - } + } else if (textFont.isCameraFunction()) { + textFont.asCameraFunction().stops.match( + [&] (const auto& stops) { + for (const auto& stop : stops.stops) { + result.insert(stop.second); + } + } + ); } } } diff --git a/src/mbgl/style/possibly_evaluated_property_value.hpp b/src/mbgl/style/possibly_evaluated_property_value.hpp new file mode 100644 index 0000000000..8c3f1780a6 --- /dev/null +++ b/src/mbgl/style/possibly_evaluated_property_value.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include <mbgl/style/function/source_function.hpp> +#include <mbgl/style/function/composite_function.hpp> +#include <mbgl/util/interpolate.hpp> +#include <mbgl/util/variant.hpp> + +namespace mbgl { + +class GeometryTileFeature; + +namespace style { + +template <class T> +class PossiblyEvaluatedPropertyValue { +private: + using Value = variant< + T, + SourceFunction<T>, + CompositeFunction<T>>; + + Value value; + +public: + PossiblyEvaluatedPropertyValue() = default; + PossiblyEvaluatedPropertyValue(Value v) : value(std::move(v)) {} + + bool isConstant() const { + return value.template is<T>(); + } + + optional<T> constant() const { + return value.match( + [&] (const T& t) { return optional<T>(t); }, + [&] (const auto&) { return optional<T>(); }); + } + + T constantOr(const T& t) const { + return constant().value_or(t); + } + + template <class... Ts> + auto match(Ts&&... ts) const { + return value.match(std::forward<Ts>(ts)...); + } +}; + +} // namespace style + +namespace util { + +template <typename T> +struct Interpolator<style::PossiblyEvaluatedPropertyValue<T>> { + style::PossiblyEvaluatedPropertyValue<T> operator()(const style::PossiblyEvaluatedPropertyValue<T>& a, + const style::PossiblyEvaluatedPropertyValue<T>& b, + const double t) const { + if (a.isConstant() && b.isConstant()) { + return { interpolate(*a.constant(), *b.constant(), t) }; + } else { + return { a }; + } + } +}; + +} // namespace util + +} // namespace mbgl diff --git a/src/mbgl/style/property_evaluator.hpp b/src/mbgl/style/property_evaluator.hpp index ca4962d948..3f629ada4f 100644 --- a/src/mbgl/style/property_evaluator.hpp +++ b/src/mbgl/style/property_evaluator.hpp @@ -17,7 +17,7 @@ public: T operator()(const Undefined&) const { return defaultValue; } T operator()(const T& constant) const { return constant; } - T operator()(const Function<T>& fn) const { return fn.evaluate(parameters.z); } + T operator()(const CameraFunction<T>& fn) const { return fn.evaluate(parameters.z); } private: const PropertyEvaluationParameters& parameters; diff --git a/src/mbgl/style/source_impl.cpp b/src/mbgl/style/source_impl.cpp index 624fee8ee5..003df4f6b1 100644 --- a/src/mbgl/style/source_impl.cpp +++ b/src/mbgl/style/source_impl.cpp @@ -139,10 +139,10 @@ void Source::Impl::updateTiles(const UpdateParameters& parameters) { algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn, renderTileFn, idealTiles, zoomRange, tileZoom); - if (type != SourceType::Annotations && cache.getSize() == 0) { + if (type != SourceType::Annotations) { size_t conservativeCacheSize = - std::max((float)parameters.transformState.getSize().width / util::tileSize, 1.0f) * - std::max((float)parameters.transformState.getSize().height / util::tileSize, 1.0f) * + std::max((float)parameters.transformState.getSize().width / tileSize, 1.0f) * + std::max((float)parameters.transformState.getSize().height / tileSize, 1.0f) * (parameters.transformState.getMaxZoom() - parameters.transformState.getMinZoom() + 1) * 0.5; cache.setSize(conservativeCacheSize); diff --git a/src/mbgl/style/source_observer.hpp b/src/mbgl/style/source_observer.hpp index dcbcaeabaf..9be7c67960 100644 --- a/src/mbgl/style/source_observer.hpp +++ b/src/mbgl/style/source_observer.hpp @@ -20,7 +20,7 @@ public: virtual void onSourceAttributionChanged(Source&, const std::string&) {} virtual void onSourceError(Source&, std::exception_ptr) {} - //Source description needs to be reloaded + // Source description needs to be reloaded virtual void onSourceDescriptionChanged(Source&) {} virtual void onTileChanged(Source&, const OverscaledTileID&) {} diff --git a/src/mbgl/style/sources/geojson_source_impl.cpp b/src/mbgl/style/sources/geojson_source_impl.cpp index 3f3bc879f7..c7c3753076 100644 --- a/src/mbgl/style/sources/geojson_source_impl.cpp +++ b/src/mbgl/style/sources/geojson_source_impl.cpp @@ -39,7 +39,7 @@ GeoJSONSource::Impl::~Impl() = default; void GeoJSONSource::Impl::setURL(std::string url_) { url = std::move(url_); - //Signal that the source description needs a reload + // Signal that the source description needs a reload if (loaded || req) { loaded = false; req.reset(); @@ -57,7 +57,7 @@ void GeoJSONSource::Impl::setGeoJSON(const GeoJSON& geoJSON) { _setGeoJSON(geoJSON); } -//Private implementation +// Private implementation void GeoJSONSource::Impl::_setGeoJSON(const GeoJSON& geoJSON) { double scale = util::EXTENT / util::tileSize; diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp index 0b516f7b9f..b6f14ecf4b 100644 --- a/src/mbgl/style/style.cpp +++ b/src/mbgl/style/style.cpp @@ -134,12 +134,12 @@ void Style::setJSON(const std::string& json) { spriteAtlas->load(parser.spriteURL, fileSource); loaded = true; - + observer->onStyleLoaded(); } void Style::addSource(std::unique_ptr<Source> source) { - //Guard against duplicate source ids + // Guard against duplicate source ids auto it = std::find_if(sources.begin(), sources.end(), [&](const auto& existing) { return existing->getID() == source->getID(); }); @@ -159,7 +159,7 @@ std::unique_ptr<Source> Style::removeSource(const std::string& id) { }); if (it == sources.end()) { - throw std::runtime_error("no such source"); + return nullptr; } auto source = std::move(*it); @@ -231,7 +231,7 @@ std::unique_ptr<Layer> Style::removeLayer(const std::string& id) { }); if (it == layers.end()) - throw std::runtime_error("no such layer"); + return nullptr; auto layer = std::move(*it); @@ -662,12 +662,17 @@ void Style::onLayerPaintPropertyChanged(Layer&) { observer->onUpdate(Update::RecalculateStyle | Update::Classes); } +void Style::onLayerDataDrivenPaintPropertyChanged(Layer& layer) { + layer.accept(QueueSourceReloadVisitor { updateBatch }); + observer->onUpdate(Update::RecalculateStyle | Update::Classes | Update::Layout); +} + void Style::onLayerLayoutPropertyChanged(Layer& layer, const char * property) { layer.accept(QueueSourceReloadVisitor { updateBatch }); auto update = Update::Layout; - //Recalculate the style for certain properties + // Recalculate the style for certain properties bool needsRecalculation = strcmp(property, "icon-size") == 0 || strcmp(property, "text-size") == 0; if (needsRecalculation) { update |= Update::RecalculateStyle; diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp index d46e80e8bf..4c4bcec63a 100644 --- a/src/mbgl/style/style.hpp +++ b/src/mbgl/style/style.hpp @@ -146,6 +146,7 @@ private: void onLayerFilterChanged(Layer&) override; void onLayerVisibilityChanged(Layer&) override; void onLayerPaintPropertyChanged(Layer&) override; + void onLayerDataDrivenPaintPropertyChanged(Layer&) override; void onLayerLayoutPropertyChanged(Layer&, const char *) override; Observer nullObserver; diff --git a/src/mbgl/style/tile_source_impl.hpp b/src/mbgl/style/tile_source_impl.hpp index 366e0b60a2..2b17872d2b 100644 --- a/src/mbgl/style/tile_source_impl.hpp +++ b/src/mbgl/style/tile_source_impl.hpp @@ -33,7 +33,7 @@ public: const variant<std::string, Tileset>& getURLOrTileset() const { return urlOrTileset; } - + optional<std::string> getAttribution() const override; protected: diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp index 2bf1448492..175a36f0fa 100644 --- a/src/mbgl/text/glyph.hpp +++ b/src/mbgl/text/glyph.hpp @@ -2,6 +2,8 @@ #include <mbgl/text/glyph_range.hpp> #include <mbgl/util/rect.hpp> +#include <mbgl/util/traits.hpp> +#include <mbgl/util/image.hpp> #include <cstdint> #include <vector> @@ -52,25 +54,28 @@ typedef std::map<uint32_t, Glyph> GlyphPositions; class PositionedGlyph { public: - explicit PositionedGlyph(uint32_t glyph_, float x_, float y_) - : glyph(glyph_), x(x_), y(y_) {} + explicit PositionedGlyph(uint32_t glyph_, float x_, float y_, float angle_) + : glyph(glyph_), x(x_), y(y_), angle(angle_) {} uint32_t glyph = 0; float x = 0; float y = 0; + float angle = 0; }; +enum class WritingModeType : uint8_t; + class Shaping { public: explicit Shaping() : top(0), bottom(0), left(0), right(0) {} - explicit Shaping(float x, float y, std::u16string text_) - : text(std::move(text_)), top(y), bottom(y), left(x), right(x) {} + explicit Shaping(float x, float y, WritingModeType writingMode_) + : top(y), bottom(y), left(x), right(x), writingMode(writingMode_) {} std::vector<PositionedGlyph> positionedGlyphs; - std::u16string text; int32_t top; int32_t bottom; int32_t left; int32_t right; + WritingModeType writingMode; explicit operator bool() const { return !positionedGlyphs.empty(); } }; @@ -84,10 +89,36 @@ public: uint32_t id = 0; // A signed distance field of the glyph with a border (see above). - std::string bitmap; + AlphaImage bitmap; // Glyph metrics GlyphMetrics metrics; }; +enum class WritingModeType : uint8_t { + None = 0, + Horizontal = 1 << 0, + Vertical = 1 << 1, +}; + +constexpr WritingModeType operator|(WritingModeType a, WritingModeType b) { + return WritingModeType(mbgl::underlying_type(a) | mbgl::underlying_type(b)); +} + +constexpr WritingModeType& operator|=(WritingModeType& a, WritingModeType b) { + return (a = a | b); +} + +constexpr bool operator&(WritingModeType lhs, WritingModeType rhs) { + return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs); +} + +constexpr WritingModeType& operator&=(WritingModeType& lhs, WritingModeType rhs) { + return (lhs = WritingModeType(mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs))); +} + +constexpr WritingModeType operator~(WritingModeType value) { + return WritingModeType(~mbgl::underlying_type(value)); +} + } // end namespace mbgl diff --git a/src/mbgl/text/glyph_atlas.cpp b/src/mbgl/text/glyph_atlas.cpp index dc1fe2b1d9..1b3f7518b5 100644 --- a/src/mbgl/text/glyph_atlas.cpp +++ b/src/mbgl/text/glyph_atlas.cpp @@ -23,16 +23,17 @@ GlyphAtlas::GlyphAtlas(const Size size, FileSource& fileSource_) GlyphAtlas::~GlyphAtlas() = default; void GlyphAtlas::requestGlyphRange(const FontStack& fontStack, const GlyphRange& range) { - std::lock_guard<std::mutex> lock(rangesMutex); - auto& rangeSets = ranges[fontStack]; + std::lock_guard<std::mutex> lock(mutex); + auto& rangeSets = entries[fontStack].ranges; const auto& rangeSetsIt = rangeSets.find(range); if (rangeSetsIt != rangeSets.end()) { return; } - rangeSets.emplace(range, - std::make_unique<GlyphPBF>(this, fontStack, range, observer, fileSource)); + rangeSets.emplace(std::piecewise_construct, + std::forward_as_tuple(range), + std::forward_as_tuple(this, fontStack, range, observer, fileSource)); } bool GlyphAtlas::hasGlyphRanges(const FontStack& fontStack, const GlyphRangeSet& glyphRanges) { @@ -40,8 +41,8 @@ bool GlyphAtlas::hasGlyphRanges(const FontStack& fontStack, const GlyphRangeSet& return true; } - std::lock_guard<std::mutex> lock(rangesMutex); - const auto& rangeSets = ranges[fontStack]; + std::lock_guard<std::mutex> lock(mutex); + const auto& rangeSets = entries[fontStack].ranges; bool hasRanges = true; for (const auto& range : glyphRanges) { @@ -55,7 +56,7 @@ bool GlyphAtlas::hasGlyphRanges(const FontStack& fontStack, const GlyphRangeSet& continue; } - if (!rangeSetsIt->second->isParsed()) { + if (!rangeSetsIt->second.isParsed()) { hasRanges = false; } } @@ -64,16 +65,8 @@ bool GlyphAtlas::hasGlyphRanges(const FontStack& fontStack, const GlyphRangeSet& } util::exclusive<GlyphSet> GlyphAtlas::getGlyphSet(const FontStack& fontStack) { - auto lock = std::make_unique<std::lock_guard<std::mutex>>(glyphSetsMutex); - - auto it = glyphSets.find(fontStack); - if (it == glyphSets.end()) { - it = glyphSets.emplace(fontStack, std::make_unique<GlyphSet>()).first; - } - - // FIXME: We lock all GlyphSets, but what we should - // really do is lock only the one we are returning. - return { it->second.get(), std::move(lock) }; + auto lock = std::make_unique<std::lock_guard<std::mutex>>(mutex); + return { &entries[fontStack].glyphSet, std::move(lock) }; } void GlyphAtlas::setObserver(GlyphAtlasObserver* observer_) { @@ -83,12 +76,10 @@ void GlyphAtlas::setObserver(GlyphAtlasObserver* observer_) { void GlyphAtlas::addGlyphs(uintptr_t tileUID, const std::u16string& text, const FontStack& fontStack, - const GlyphSet& glyphSet, + const util::exclusive<GlyphSet>& glyphSet, GlyphPositions& face) { - std::lock_guard<std::mutex> lock(mtx); - - const std::map<uint32_t, SDFGlyph>& sdfs = glyphSet.getSDFs(); + const std::map<uint32_t, SDFGlyph>& sdfs = glyphSet->getSDFs(); for (char16_t chr : text) { @@ -107,7 +98,7 @@ Rect<uint16_t> GlyphAtlas::addGlyph(uintptr_t tileUID, const FontStack& fontStack, const SDFGlyph& glyph) { - std::map<uint32_t, GlyphValue>& face = index[fontStack]; + std::map<uint32_t, GlyphValue>& face = entries[fontStack].glyphValues; auto it = face.find(glyph.id); // The glyph is already in this texture. @@ -124,48 +115,26 @@ Rect<uint16_t> GlyphAtlas::addGlyph(uintptr_t tileUID, return Rect<uint16_t>{ 0, 0, 0, 0 }; } - uint16_t buffered_width = glyph.metrics.width + SDFGlyph::borderSize * 2; - uint16_t buffered_height = glyph.metrics.height + SDFGlyph::borderSize * 2; - - // Guard against mismatches between the glyph bitmap size and the size mandated by - // its metrics. - if (size_t(buffered_width * buffered_height) != glyph.bitmap.size()) { - return Rect<uint16_t>{ 0, 0, 0, 0 }; - } - // Add a 1px border around every image. - const uint16_t padding = 1; - uint16_t pack_width = buffered_width + 2 * padding; - uint16_t pack_height = buffered_height + 2 * padding; + const uint32_t padding = 1; + uint16_t width = glyph.bitmap.size.width + 2 * padding; + uint16_t height = glyph.bitmap.size.height + 2 * padding; // Increase to next number divisible by 4, but at least 1. // This is so we can scale down the texture coordinates and pack them // into 2 bytes rather than 4 bytes. - pack_width += (4 - pack_width % 4); - pack_height += (4 - pack_height % 4); + width += (4 - width % 4); + height += (4 - height % 4); - Rect<uint16_t> rect = bin.allocate(pack_width, pack_height); + Rect<uint16_t> rect = bin.allocate(width, height); if (rect.w == 0) { Log::Error(Event::OpenGL, "glyph bitmap overflow"); return rect; } - // Verify that binpack didn't produce a rect that goes beyond the size of the image. - // This should never happen. - assert(rect.x + rect.w <= image.size.width); - assert(rect.y + rect.h <= image.size.height); - face.emplace(glyph.id, GlyphValue { rect, tileUID }); - // Copy the bitmap - const uint8_t* source = reinterpret_cast<const uint8_t*>(glyph.bitmap.data()); - for (uint32_t y = 0; y < buffered_height; y++) { - uint32_t y1 = image.size.width * (rect.y + y + padding) + rect.x + padding; - uint32_t y2 = buffered_width * y; - for (uint32_t x = 0; x < buffered_width; x++) { - image.data[y1 + x] = source[y2 + x]; - } - } + AlphaImage::copy(glyph.bitmap, image, { 0, 0 }, { rect.x + padding, rect.y + padding }, glyph.bitmap.size); dirty = true; @@ -173,10 +142,10 @@ Rect<uint16_t> GlyphAtlas::addGlyph(uintptr_t tileUID, } void GlyphAtlas::removeGlyphs(uintptr_t tileUID) { - std::lock_guard<std::mutex> lock(mtx); + std::lock_guard<std::mutex> lock(mutex); - for (auto& faces : index) { - std::map<uint32_t, GlyphValue>& face = faces.second; + for (auto& entry : entries) { + std::map<uint32_t, GlyphValue>& face = entry.second.glyphValues; for (auto it = face.begin(); it != face.end(); /* we advance in the body */) { GlyphValue& value = it->second; value.ids.erase(tileUID); @@ -212,8 +181,6 @@ Size GlyphAtlas::getSize() const { } void GlyphAtlas::upload(gl::Context& context, gl::TextureUnit unit) { - std::lock_guard<std::mutex> lock(mtx); - if (!texture) { texture = context.createTexture(image, unit); } else if (dirty) { diff --git a/src/mbgl/text/glyph_atlas.hpp b/src/mbgl/text/glyph_atlas.hpp index af14aace5b..8267630096 100644 --- a/src/mbgl/text/glyph_atlas.hpp +++ b/src/mbgl/text/glyph_atlas.hpp @@ -17,8 +17,6 @@ #include <unordered_set> #include <unordered_map> #include <mutex> -#include <exception> -#include <vector> namespace mbgl { @@ -57,7 +55,7 @@ public: void addGlyphs(uintptr_t tileUID, const std::u16string& text, const FontStack&, - const GlyphSet&, + const util::exclusive<GlyphSet>&, GlyphPositions&); void removeGlyphs(uintptr_t tileUID); @@ -77,20 +75,9 @@ private: const FontStack&, const SDFGlyph&); - FileSource& fileSource; std::string glyphURL; - std::unordered_map<FontStack, std::map<GlyphRange, std::unique_ptr<GlyphPBF>>, FontStackHash> ranges; - std::mutex rangesMutex; - - std::unordered_map<FontStack, std::unique_ptr<GlyphSet>, FontStackHash> glyphSets; - std::mutex glyphSetsMutex; - - util::WorkQueue workQueue; - - GlyphAtlasObserver* observer = nullptr; - struct GlyphValue { GlyphValue(Rect<uint16_t> rect_, uintptr_t id) : rect(std::move(rect_)), ids({ id }) {} @@ -98,10 +85,20 @@ private: std::unordered_set<uintptr_t> ids; }; - std::mutex mtx; + struct Entry { + std::map<GlyphRange, GlyphPBF> ranges; + GlyphSet glyphSet; + std::map<uint32_t, GlyphValue> glyphValues; + }; + + std::unordered_map<FontStack, Entry, FontStackHash> entries; + std::mutex mutex; + + util::WorkQueue workQueue; + GlyphAtlasObserver* observer = nullptr; + BinPack<uint16_t> bin; - std::unordered_map<FontStack, std::map<uint32_t, GlyphValue>, FontStackHash> index; - const AlphaImage image; + AlphaImage image; std::atomic<bool> dirty; mbgl::optional<gl::Texture> texture; }; diff --git a/src/mbgl/text/glyph_pbf.cpp b/src/mbgl/text/glyph_pbf.cpp index cdeac57984..5c57d278db 100644 --- a/src/mbgl/text/glyph_pbf.cpp +++ b/src/mbgl/text/glyph_pbf.cpp @@ -25,6 +25,7 @@ void parseGlyphPBF(GlyphSet& glyphSet, const GlyphRange& glyphRange, const std:: auto glyph_pbf = fontstack_pbf.get_message(); SDFGlyph glyph; + protozero::data_view glyphData; bool hasID = false, hasWidth = false, hasHeight = false, hasLeft = false, hasTop = false, hasAdvance = false; @@ -36,7 +37,7 @@ void parseGlyphPBF(GlyphSet& glyphSet, const GlyphRange& glyphRange, const std:: hasID = true; break; case 2: // bitmap - glyph.bitmap = glyph_pbf.get_string(); + glyphData = glyph_pbf.get_view(); break; case 3: // width glyph.metrics.width = glyph_pbf.get_uint32(); @@ -64,26 +65,35 @@ void parseGlyphPBF(GlyphSet& glyphSet, const GlyphRange& glyphRange, const std:: } } + // Only treat this glyph as a correct glyph if it has all required fields. It also + // needs to satisfy a few metrics conditions that ensure that the glyph isn't bogus. + // All other glyphs are malformed. We're also discarding all glyphs that are outside + // the expected glyph range. + if (!hasID || !hasWidth || !hasHeight || !hasLeft || !hasTop || !hasAdvance || + glyph.metrics.width >= 256 || glyph.metrics.height >= 256 || + glyph.metrics.left < -128 || glyph.metrics.left >= 128 || + glyph.metrics.top < -128 || glyph.metrics.top >= 128 || + glyph.metrics.advance >= 256 || + glyph.id < glyphRange.first || glyph.id > glyphRange.second) { + continue; + } + // If the area of width/height is non-zero, we need to adjust the expected size // with the implicit border size, otherwise we expect there to be no bitmap at all. - const uint32_t expectedBitmapSize = - glyph.metrics.width && glyph.metrics.height - ? (glyph.metrics.width + 2 * SDFGlyph::borderSize) * - (glyph.metrics.height + 2 * SDFGlyph::borderSize) - : 0; - - // Only treat this glyph as a correct glyph if it has all required fields, and if - // the bitmap has the correct length. It also needs to satisfy a few metrics conditions - // that ensure that the glyph isn't bogus. All other glyphs are malformed. - // We're also discarding all glyphs that are outside the expected glyph range. - if (hasID && hasWidth && hasHeight && hasLeft && hasTop && hasAdvance && - glyph.metrics.width < 256 && glyph.metrics.height < 256 && - glyph.metrics.left >= -128 && glyph.metrics.left < 128 && - glyph.metrics.top >= -128 && glyph.metrics.top < 128 && - glyph.metrics.advance < 256 && glyph.bitmap.size() == expectedBitmapSize && - glyph.id >= glyphRange.first && glyph.id <= glyphRange.second) { - glyphSet.insert(glyph.id, std::move(glyph)); + if (glyph.metrics.width && glyph.metrics.height) { + const Size size { + glyph.metrics.width + 2 * SDFGlyph::borderSize, + glyph.metrics.height + 2 * SDFGlyph::borderSize + }; + + if (size.area() != glyphData.size()) { + continue; + } + + glyph.bitmap = AlphaImage(size, reinterpret_cast<const uint8_t*>(glyphData.data()), glyphData.size()); } + + glyphSet.insert(glyph.id, std::move(glyph)); } } } diff --git a/src/mbgl/text/glyph_set.cpp b/src/mbgl/text/glyph_set.cpp index f9a90f9bb0..19a6e2cddd 100644 --- a/src/mbgl/text/glyph_set.cpp +++ b/src/mbgl/text/glyph_set.cpp @@ -42,18 +42,17 @@ const Shaping GlyphSet::getShaping(const std::u16string& logicalInput, const float justify, const float spacing, const Point<float>& translate, + const float verticalHeight, + const WritingModeType writingMode, BiDi& bidi) const { - - // The string stored in shaping.text is used for finding duplicates, but may end up quite - // different from the glyphs that get shown - Shaping shaping(translate.x * 24, translate.y * 24, logicalInput); + Shaping shaping(translate.x * 24, translate.y * 24, writingMode); std::vector<std::u16string> reorderedLines = bidi.processText(logicalInput, - determineLineBreaks(logicalInput, spacing, maxWidth)); + determineLineBreaks(logicalInput, spacing, maxWidth, writingMode)); shapeLines(shaping, reorderedLines, spacing, lineHeight, horizontalAlign, verticalAlign, - justify, translate); + justify, translate, verticalHeight, writingMode); return shaping; } @@ -114,7 +113,7 @@ float GlyphSet::determineAverageLineWidth(const std::u16string& logicalInput, int32_t targetLineCount = std::fmax(1, std::ceil(totalWidth / maxWidth)); return totalWidth / targetLineCount; } - + float calculateBadness(const float lineWidth, const float targetWidth, const float penalty, const bool isLastBreak) { const float raggedness = std::pow(lineWidth - targetWidth, 2); if (isLastBreak) { @@ -130,7 +129,7 @@ float calculateBadness(const float lineWidth, const float targetWidth, const flo } return raggedness + std::pow(penalty, 2); } - + float calculatePenalty(char16_t codePoint, char16_t nextCodePoint) { float penalty = 0; // Force break on newline @@ -146,28 +145,28 @@ float calculatePenalty(char16_t codePoint, char16_t nextCodePoint) { if (nextCodePoint == 0x29 || nextCodePoint == 0xff09) { penalty += 50; } - + return penalty; } - + struct PotentialBreak { PotentialBreak(const std::size_t p_index, const float p_x, const PotentialBreak* p_priorBreak, const float p_badness) : index(p_index), x(p_x), priorBreak(p_priorBreak), badness(p_badness) {} - + const std::size_t index; const float x; const PotentialBreak* priorBreak; const float badness; }; - + PotentialBreak evaluateBreak(const std::size_t breakIndex, const float breakX, const float targetWidth, const std::list<PotentialBreak>& potentialBreaks, const float penalty, const bool isLastBreak) { // We could skip evaluating breaks where the line length (breakX - priorBreak.x) > maxWidth // ...but in fact we allow lines longer than maxWidth (if there's no break points) // ...and when targetWidth and maxWidth are close, strictly enforcing maxWidth can give // more lopsided results. - + const PotentialBreak* bestPriorBreak = nullptr; float bestBreakBadness = calculateBadness(breakX, targetWidth, penalty, isLastBreak); for (const auto& potentialBreak : potentialBreaks) { @@ -182,7 +181,7 @@ PotentialBreak evaluateBreak(const std::size_t breakIndex, const float breakX, c return PotentialBreak(breakIndex, breakX, bestPriorBreak, bestBreakBadness); } - + std::set<std::size_t> leastBadBreaks(const PotentialBreak& lastLineBreak) { std::set<std::size_t> leastBadBreaks = { lastLineBreak.index }; const PotentialBreak* priorBreak = lastLineBreak.priorBreak; @@ -198,17 +197,18 @@ std::set<std::size_t> leastBadBreaks(const PotentialBreak& lastLineBreak) { // more intuitive, but we can't do that because the visual order may be changed by line breaks! std::set<std::size_t> GlyphSet::determineLineBreaks(const std::u16string& logicalInput, const float spacing, - float maxWidth) const { - if (!maxWidth) { + float maxWidth, + const WritingModeType writingMode) const { + if (!maxWidth || writingMode != WritingModeType::Horizontal) { return {}; } if (logicalInput.empty()) { return {}; } - + const float targetWidth = determineAverageLineWidth(logicalInput, spacing, maxWidth); - + std::list<PotentialBreak> potentialBreaks; float currentX = 0; @@ -218,7 +218,7 @@ std::set<std::size_t> GlyphSet::determineLineBreaks(const std::u16string& logica if (it != sdfs.end() && !boost::algorithm::is_any_of(u" \t\n\v\f\r")(codePoint)) { currentX += it->second.metrics.advance + spacing; } - + // Ideographic characters, spaces, and word-breaking punctuation that often appear without // surrounding spaces. if ((i < logicalInput.size() - 1) && @@ -239,7 +239,9 @@ void GlyphSet::shapeLines(Shaping& shaping, const float horizontalAlign, const float verticalAlign, const float justify, - const Point<float>& translate) const { + const Point<float>& translate, + const float verticalHeight, + const WritingModeType writingMode) const { // the y offset *should* be part of the font metadata const int32_t yOffset = -17; @@ -266,15 +268,21 @@ void GlyphSet::shapeLines(Shaping& shaping, } const SDFGlyph& glyph = it->second; - shaping.positionedGlyphs.emplace_back(chr, x, y); - x += glyph.metrics.advance + spacing; + + if (writingMode == WritingModeType::Horizontal || !util::i18n::hasUprightVerticalOrientation(chr)) { + shaping.positionedGlyphs.emplace_back(chr, x, y, 0); + x += glyph.metrics.advance + spacing; + } else { + shaping.positionedGlyphs.emplace_back(chr, x, 0, -M_PI_2); + x += verticalHeight + spacing; + } } // Only justify if we placed at least one glyph if (shaping.positionedGlyphs.size() != lineStartIndex) { float lineLength = x - spacing; // Don't count trailing spacing maxLineLength = util::max(lineLength, maxLineLength); - + justifyLine(shaping.positionedGlyphs, sdfs, lineStartIndex, shaping.positionedGlyphs.size() - 1, justify); } diff --git a/src/mbgl/text/glyph_set.hpp b/src/mbgl/text/glyph_set.hpp index 3037cefca0..0342c82eb5 100644 --- a/src/mbgl/text/glyph_set.hpp +++ b/src/mbgl/text/glyph_set.hpp @@ -18,6 +18,8 @@ public: float justify, float spacing, const Point<float>& translate, + float verticalHeight, + const WritingModeType, BiDi& bidi) const; private: @@ -26,7 +28,8 @@ private: float maxWidth) const; std::set<std::size_t> determineLineBreaks(const std::u16string& logicalInput, const float spacing, - float maxWidth) const; + float maxWidth, + const WritingModeType) const; void shapeLines(Shaping& shaping, const std::vector<std::u16string>& lines, @@ -35,7 +38,9 @@ private: float horizontalAlign, float verticalAlign, float justify, - const Point<float>& translate) const; + const Point<float>& translate, + float verticalHeight, + const WritingModeType) const; std::map<uint32_t, SDFGlyph> sdfs; }; diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp index 1a05e6f94f..6113d1f5d4 100644 --- a/src/mbgl/text/quads.cpp +++ b/src/mbgl/text/quads.cpp @@ -13,7 +13,7 @@ using namespace style; const float globalMinScale = 0.5f; // underscale by 1 zoom level -SymbolQuads getIconQuads(Anchor& anchor, const PositionedIcon& shapedIcon, +SymbolQuad getIconQuad(Anchor& anchor, const PositionedIcon& shapedIcon, const GeometryCoordinates& line, const SymbolLayoutProperties::Evaluated& layout, const style::SymbolPlacementType placement, const Shaping& shapedText) { @@ -62,7 +62,7 @@ SymbolQuads getIconQuads(Anchor& anchor, const PositionedIcon& shapedIcon, bl = {left, bottom}; } - float angle = layout.get<IconRotate>() * util::DEG2RAD; + float angle = shapedIcon.angle; if (placement == style::SymbolPlacementType::Line) { assert(static_cast<unsigned int>(anchor.segment) < line.size()); const GeometryCoordinate &prev= line[anchor.segment]; @@ -88,9 +88,7 @@ SymbolQuads getIconQuads(Anchor& anchor, const PositionedIcon& shapedIcon, br = util::matrixMultiply(matrix, br); } - SymbolQuads quads; - quads.emplace_back(tl, tr, bl, br, image.pos, 0, 0, anchor.point, globalMinScale, std::numeric_limits<float>::infinity()); - return quads; + return SymbolQuad { tl, tr, bl, br, image.pos, 0, 0, anchor.point, globalMinScale, std::numeric_limits<float>::infinity(), shapedText.writingMode }; } struct GlyphInstance { @@ -207,10 +205,19 @@ SymbolQuads getGlyphQuads(Anchor& anchor, const Shaping& shapedText, const float x2 = x1 + rect.w; const float y2 = y1 + rect.h; - const Point<float> otl{x1, y1}; - const Point<float> otr{x2, y1}; - const Point<float> obl{x1, y2}; - const Point<float> obr{x2, y2}; + const Point<float> center{positionedGlyph.x, static_cast<float>(static_cast<float>(glyph.metrics.advance) / 2.0)}; + + Point<float> otl{x1, y1}; + Point<float> otr{x2, y1}; + Point<float> obl{x1, y2}; + Point<float> obr{x2, y2}; + + if (positionedGlyph.angle != 0) { + otl = util::rotate(otl - center, positionedGlyph.angle) + center; + otr = util::rotate(otr - center, positionedGlyph.angle) + center; + obl = util::rotate(obl - center, positionedGlyph.angle) + center; + obr = util::rotate(obr - center, positionedGlyph.angle) + center; + } for (const GlyphInstance &instance : glyphInstances) { @@ -236,7 +243,7 @@ SymbolQuads getGlyphQuads(Anchor& anchor, const Shaping& shapedText, const float anchorAngle = std::fmod((anchor.angle + instance.offset + 2 * M_PI), (2 * M_PI)); const float glyphAngle = std::fmod((instance.angle + instance.offset + 2 * M_PI), (2 * M_PI)); - quads.emplace_back(tl, tr, bl, br, rect, anchorAngle, glyphAngle, instance.anchorPoint, glyphMinScale, instance.maxScale); + quads.emplace_back(tl, tr, bl, br, rect, anchorAngle, glyphAngle, instance.anchorPoint, glyphMinScale, instance.maxScale, shapedText.writingMode); } diff --git a/src/mbgl/text/quads.hpp b/src/mbgl/text/quads.hpp index 75fb53aade..07a94c763b 100644 --- a/src/mbgl/text/quads.hpp +++ b/src/mbgl/text/quads.hpp @@ -15,7 +15,7 @@ class PositionedIcon; struct SymbolQuad { explicit SymbolQuad(Point<float> tl_, Point<float> tr_, Point<float> bl_, Point<float> br_, Rect<uint16_t> tex_, float anchorAngle_, float glyphAngle_, Point<float> anchorPoint_, - float minScale_, float maxScale_) + float minScale_, float maxScale_, WritingModeType writingMode_) : tl(std::move(tl_)), tr(std::move(tr_)), bl(std::move(bl_)), @@ -25,18 +25,20 @@ struct SymbolQuad { glyphAngle(glyphAngle_), anchorPoint(std::move(anchorPoint_)), minScale(minScale_), - maxScale(maxScale_) {} + maxScale(maxScale_), + writingMode(writingMode_) {} Point<float> tl, tr, bl, br; Rect<uint16_t> tex; float anchorAngle, glyphAngle; Point<float> anchorPoint; float minScale, maxScale; + WritingModeType writingMode; }; typedef std::vector<SymbolQuad> SymbolQuads; -SymbolQuads getIconQuads(Anchor& anchor, const PositionedIcon& shapedIcon, +SymbolQuad getIconQuad(Anchor& anchor, const PositionedIcon& shapedIcon, const GeometryCoordinates& line, const style::SymbolLayoutProperties::Evaluated&, style::SymbolPlacementType placement, const Shaping& shapedText); diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp index 062066aaf4..e68566d419 100644 --- a/src/mbgl/text/shaping.cpp +++ b/src/mbgl/text/shaping.cpp @@ -1,19 +1,17 @@ #include <mbgl/text/shaping.hpp> -#include <mbgl/style/layers/symbol_layer_properties.hpp> +#include <mbgl/layout/symbol_feature.hpp> namespace mbgl { -using namespace style; - -PositionedIcon shapeIcon(const SpriteAtlasElement& image, const SymbolLayoutProperties::Evaluated& layout) { - float dx = layout.get<IconOffset>()[0]; - float dy = layout.get<IconOffset>()[1]; +PositionedIcon shapeIcon(const SpriteAtlasElement& image, const std::array<float, 2>& iconOffset, const float iconRotation) { + float dx = iconOffset[0]; + float dy = iconOffset[1]; float x1 = dx - image.spriteImage->getWidth() / 2.0f; float x2 = x1 + image.spriteImage->getWidth(); float y1 = dy - image.spriteImage->getHeight() / 2.0f; float y2 = y1 + image.spriteImage->getHeight(); - return PositionedIcon(image, y1, y2, x1, x2); + return PositionedIcon(image, y1, y2, x1, x2, iconRotation); } } // namespace mbgl diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp index 30375179b6..1b7b8b2733 100644 --- a/src/mbgl/text/shaping.hpp +++ b/src/mbgl/text/shaping.hpp @@ -3,29 +3,39 @@ #include <mbgl/text/glyph.hpp> #include <mbgl/sprite/sprite_atlas.hpp> #include <mbgl/sprite/sprite_image.hpp> -#include <mbgl/style/layers/symbol_layer_properties.hpp> #include <mbgl/util/optional.hpp> namespace mbgl { class SpriteAtlasElement; +class SymbolFeature; class PositionedIcon { - public: - explicit PositionedIcon() {} - explicit PositionedIcon(const SpriteAtlasElement& _image, - float _top, float _bottom, float _left, float _right) : - image(_image), top(_top), bottom(_bottom), left(_left), right(_right) {} - - optional<SpriteAtlasElement> image; - float top = 0; - float bottom = 0; - float left = 0; - float right = 0; - - explicit operator bool() const { return image && (*image).pos.hasArea(); } +public: + PositionedIcon() = default; + PositionedIcon(const SpriteAtlasElement& image_, + float top_, + float bottom_, + float left_, + float right_, + float angle_) + : image(image_), + top(top_), + bottom(bottom_), + left(left_), + right(right_), + angle(angle_) {} + + optional<SpriteAtlasElement> image; + float top = 0; + float bottom = 0; + float left = 0; + float right = 0; + float angle = 0; + + explicit operator bool() const { return image && (*image).pos.hasArea(); } }; -PositionedIcon shapeIcon(const SpriteAtlasElement& image, const style::SymbolLayoutProperties::Evaluated&); +PositionedIcon shapeIcon(const SpriteAtlasElement&, const std::array<float, 2>& iconOffset, const float iconRotation); } // namespace mbgl diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp index 61437b79b1..85d8b75619 100644 --- a/src/mbgl/tile/geojson_tile.cpp +++ b/src/mbgl/tile/geojson_tile.cpp @@ -25,6 +25,10 @@ public: return feature.properties; } + optional<FeatureIdentifier> getID() const override { + return feature.id; + } + GeometryCollection getGeometries() const override { GeometryCollection geometry = apply_visitor(ToGeometryCollection(), feature.geometry); @@ -80,7 +84,7 @@ GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID, const style::UpdateParameters& parameters) : GeometryTile(overscaledTileID, sourceID_, parameters) { } - + void GeoJSONTile::updateData(const mapbox::geometry::feature_collection<int16_t>& features) { setData(std::make_unique<GeoJSONTileData>(features)); } diff --git a/src/mbgl/tile/geojson_tile.hpp b/src/mbgl/tile/geojson_tile.hpp index 2880408736..6ddc6ea482 100644 --- a/src/mbgl/tile/geojson_tile.hpp +++ b/src/mbgl/tile/geojson_tile.hpp @@ -16,7 +16,7 @@ public: const style::UpdateParameters&); void updateData(const mapbox::geometry::feature_collection<int16_t>&); - + void setNecessity(Necessity) final; }; diff --git a/src/mbgl/tile/geometry_tile_data.cpp b/src/mbgl/tile/geometry_tile_data.cpp index 2e465a6f65..ebf27e7858 100644 --- a/src/mbgl/tile/geometry_tile_data.cpp +++ b/src/mbgl/tile/geometry_tile_data.cpp @@ -127,7 +127,7 @@ void limitHoles(GeometryCollection& polygon, uint32_t maxHoles) { polygon.begin() + 1 + maxHoles, polygon.end(), [] (const auto& a, const auto& b) { - return signedArea(a) > signedArea(b); + return std::fabs(signedArea(a)) > std::fabs(signedArea(b)); }); polygon.resize(1 + maxHoles); } diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index 2a86b7feda..b1fd7a852e 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -6,6 +6,8 @@ #include <mbgl/layout/symbol_layout.hpp> #include <mbgl/style/bucket_parameters.hpp> #include <mbgl/style/group_by_layout.hpp> +#include <mbgl/style/filter.hpp> +#include <mbgl/style/filter_evaluator.hpp> #include <mbgl/style/layers/symbol_layer.hpp> #include <mbgl/style/layers/symbol_layer_impl.hpp> #include <mbgl/renderer/symbol_bucket.hpp> @@ -212,7 +214,7 @@ void GeometryTileWorker::redoLayout() { std::unordered_map<std::string, std::unique_ptr<SymbolLayout>> symbolLayoutMap; std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets; auto featureIndex = std::make_unique<FeatureIndex>(); - BucketParameters parameters { id, obsolete, *featureIndex, mode }; + BucketParameters parameters { id, mode }; std::vector<std::vector<const Layer*>> groups = groupByLayout(*layers); for (auto& group : groups) { @@ -240,12 +242,27 @@ void GeometryTileWorker::redoLayout() { if (leader.is<SymbolLayer>()) { symbolLayoutMap.emplace(leader.getID(), - leader.as<SymbolLayer>()->impl->createLayout(parameters, *geometryLayer, layerIDs)); + leader.as<SymbolLayer>()->impl->createLayout(parameters, group, *geometryLayer)); } else { - std::shared_ptr<Bucket> bucket = leader.baseImpl->createBucket(parameters, *geometryLayer); + const Filter& filter = leader.baseImpl->filter; + const std::string& sourceLayerID = leader.baseImpl->sourceLayer; + std::shared_ptr<Bucket> bucket = leader.baseImpl->createBucket(parameters, group); + + for (std::size_t i = 0; !obsolete && i < geometryLayer->featureCount(); i++) { + std::unique_ptr<GeometryTileFeature> feature = geometryLayer->getFeature(i); + + if (!filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); })) + continue; + + GeometryCollection geometries = feature->getGeometries(); + bucket->addFeature(*feature, geometries); + featureIndex->insert(geometries, i, sourceLayerID, leader.getID()); + } + if (!bucket->hasData()) { continue; } + for (const auto& layer : group) { buckets.emplace(layer->getID(), bucket); } @@ -324,8 +341,8 @@ void GeometryTileWorker::attemptPlacement() { } std::shared_ptr<Bucket> bucket = symbolLayout->place(*collisionTile); - for (const auto& layerID : symbolLayout->layerIDs) { - buckets.emplace(layerID, bucket); + for (const auto& pair : symbolLayout->layerPaintProperties) { + buckets.emplace(pair.first, bucket); } } diff --git a/src/mbgl/tile/raster_tile.cpp b/src/mbgl/tile/raster_tile.cpp index c7a051f841..2347f8c881 100644 --- a/src/mbgl/tile/raster_tile.cpp +++ b/src/mbgl/tile/raster_tile.cpp @@ -41,13 +41,13 @@ void RasterTile::setData(std::shared_ptr<const std::string> data, void RasterTile::onParsed(std::unique_ptr<Bucket> result) { bucket = std::move(result); - availableData = DataAvailability::All; + availableData = bucket ? DataAvailability::All : DataAvailability::None; observer->onTileChanged(*this); } void RasterTile::onError(std::exception_ptr err) { bucket.reset(); - availableData = DataAvailability::All; + availableData = DataAvailability::None; observer->onTileError(*this, err); } diff --git a/src/mbgl/tile/raster_tile_worker.cpp b/src/mbgl/tile/raster_tile_worker.cpp index 219e9a2e41..8c1fc2f673 100644 --- a/src/mbgl/tile/raster_tile_worker.cpp +++ b/src/mbgl/tile/raster_tile_worker.cpp @@ -1,6 +1,6 @@ #include <mbgl/tile/raster_tile_worker.hpp> #include <mbgl/tile/raster_tile.hpp> -#include <mbgl/renderer/raster_bucket.cpp> +#include <mbgl/renderer/raster_bucket.hpp> #include <mbgl/actor/actor.hpp> #include <mbgl/util/premultiply.hpp> diff --git a/src/mbgl/tile/tile_id_hash.hpp b/src/mbgl/tile/tile_id_hash.hpp index 0f52d9816f..f978f20e55 100644 --- a/src/mbgl/tile/tile_id_hash.hpp +++ b/src/mbgl/tile/tile_id_hash.hpp @@ -14,7 +14,7 @@ template <> struct hash<mbgl::CanonicalTileID> { return seed; } }; - + template <> struct hash<mbgl::UnwrappedTileID> { size_t operator()(const mbgl::UnwrappedTileID &id) const { std::size_t seed = 0; @@ -23,7 +23,7 @@ template <> struct hash<mbgl::UnwrappedTileID> { return seed; } }; - + template <> struct hash<mbgl::OverscaledTileID> { size_t operator()(const mbgl::OverscaledTileID &id) const { std::size_t seed = 0; @@ -33,5 +33,5 @@ template <> struct hash<mbgl::OverscaledTileID> { } }; -} // namespace std +} // namespace std diff --git a/src/mbgl/tile/vector_tile.cpp b/src/mbgl/tile/vector_tile.cpp index a195885415..68f48e81fd 100644 --- a/src/mbgl/tile/vector_tile.cpp +++ b/src/mbgl/tile/vector_tile.cpp @@ -15,9 +15,23 @@ class VectorTileLayer; using packed_iter_type = protozero::iterator_range<protozero::pbf_reader::const_uint32_iterator>; +struct VectorTileLayerData { + VectorTileLayerData(std::shared_ptr<const std::string>); + + // Hold a reference to the underlying pbf data that backs the lazily-built + // components of the owning VectorTileLayer and VectorTileFeature objects + std::shared_ptr<const std::string> data; + + uint32_t version = 1; + uint32_t extent = 4096; + std::unordered_map<std::string, uint32_t> keysMap; + std::vector<std::reference_wrapper<const std::string>> keys; + std::vector<Value> values; +}; + class VectorTileFeature : public GeometryTileFeature { public: - VectorTileFeature(protozero::pbf_reader, const VectorTileLayer&); + VectorTileFeature(protozero::pbf_reader, std::shared_ptr<VectorTileLayerData> layerData); FeatureType getType() const override { return type; } optional<Value> getValue(const std::string&) const override; @@ -26,16 +40,16 @@ public: GeometryCollection getGeometries() const override; private: - const VectorTileLayer& layer; + std::shared_ptr<VectorTileLayerData> layerData; optional<FeatureIdentifier> id; FeatureType type = FeatureType::Unknown; packed_iter_type tags_iter; packed_iter_type geometry_iter; }; - + class VectorTileLayer : public GeometryTileLayer { public: - VectorTileLayer(protozero::pbf_reader); + VectorTileLayer(protozero::pbf_reader, std::shared_ptr<const std::string>); std::size_t featureCount() const override { return features.size(); } std::unique_ptr<GeometryTileFeature> getFeature(std::size_t) const override; @@ -46,12 +60,8 @@ private: friend class VectorTileFeature; std::string name; - uint32_t version = 1; - uint32_t extent = 4096; - std::unordered_map<std::string, uint32_t> keysMap; - std::vector<std::reference_wrapper<const std::string>> keys; - std::vector<Value> values; std::vector<protozero::pbf_reader> features; + std::shared_ptr<VectorTileLayerData> data; }; class VectorTileData : public GeometryTileData { @@ -117,8 +127,8 @@ Value parseValue(protozero::pbf_reader data) { return false; } -VectorTileFeature::VectorTileFeature(protozero::pbf_reader feature_pbf, const VectorTileLayer& layer_) - : layer(layer_) { +VectorTileFeature::VectorTileFeature(protozero::pbf_reader feature_pbf, std::shared_ptr<VectorTileLayerData> layerData_) + : layerData(std::move(layerData_)) { while (feature_pbf.next()) { switch (feature_pbf.tag()) { case 1: // id @@ -141,8 +151,8 @@ VectorTileFeature::VectorTileFeature(protozero::pbf_reader feature_pbf, const Ve } optional<Value> VectorTileFeature::getValue(const std::string& key) const { - auto keyIter = layer.keysMap.find(key); - if (keyIter == layer.keysMap.end()) { + auto keyIter = layerData->keysMap.find(key); + if (keyIter == layerData->keysMap.end()) { return optional<Value>(); } @@ -151,7 +161,7 @@ optional<Value> VectorTileFeature::getValue(const std::string& key) const { while (start_itr != end_itr) { uint32_t tag_key = static_cast<uint32_t>(*start_itr++); - if (layer.keysMap.size() <= tag_key) { + if (layerData->keysMap.size() <= tag_key) { throw std::runtime_error("feature referenced out of range key"); } @@ -160,12 +170,12 @@ optional<Value> VectorTileFeature::getValue(const std::string& key) const { } uint32_t tag_val = static_cast<uint32_t>(*start_itr++);; - if (layer.values.size() <= tag_val) { + if (layerData->values.size() <= tag_val) { throw std::runtime_error("feature referenced out of range value"); } if (tag_key == keyIter->second) { - return layer.values[tag_val]; + return layerData->values[tag_val]; } } @@ -182,7 +192,7 @@ std::unordered_map<std::string,Value> VectorTileFeature::getProperties() const { throw std::runtime_error("uneven number of feature tag ids"); } uint32_t tag_val = static_cast<uint32_t>(*start_itr++); - properties[layer.keys.at(tag_key)] = layer.values.at(tag_val); + properties[layerData->keys.at(tag_key)] = layerData->values.at(tag_val); } return properties; } @@ -196,7 +206,7 @@ GeometryCollection VectorTileFeature::getGeometries() const { uint32_t length = 0; int32_t x = 0; int32_t y = 0; - const float scale = float(util::EXTENT) / layer.extent; + const float scale = float(util::EXTENT) / layerData->extent; GeometryCollection lines; @@ -234,7 +244,7 @@ GeometryCollection VectorTileFeature::getGeometries() const { } } - if (layer.version >= 2 || type != FeatureType::Polygon) { + if (layerData->version >= 2 || type != FeatureType::Polygon) { return lines; } @@ -250,7 +260,7 @@ const GeometryTileLayer* VectorTileData::getLayer(const std::string& name) const parsed = true; protozero::pbf_reader tile_pbf(*data); while (tile_pbf.next(3)) { - VectorTileLayer layer(tile_pbf.get_message()); + VectorTileLayer layer(tile_pbf.get_message(), data); layers.emplace(layer.name, std::move(layer)); } } @@ -262,7 +272,13 @@ const GeometryTileLayer* VectorTileData::getLayer(const std::string& name) const return nullptr; } -VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf) { +VectorTileLayerData::VectorTileLayerData(std::shared_ptr<const std::string> pbfData) : + data(std::move(pbfData)) +{} + +VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf, std::shared_ptr<const std::string> pbfData) + : data(std::make_shared<VectorTileLayerData>(std::move(pbfData))) +{ while (layer_pbf.next()) { switch (layer_pbf.tag()) { case 1: // name @@ -273,18 +289,18 @@ VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf) { break; case 3: // keys { - auto iter = keysMap.emplace(layer_pbf.get_string(), keysMap.size()); - keys.emplace_back(std::reference_wrapper<const std::string>(iter.first->first)); + auto iter = data->keysMap.emplace(layer_pbf.get_string(), data->keysMap.size()); + data->keys.emplace_back(std::reference_wrapper<const std::string>(iter.first->first)); } break; case 4: // values - values.emplace_back(parseValue(layer_pbf.get_message())); + data->values.emplace_back(parseValue(layer_pbf.get_message())); break; case 5: // extent - extent = layer_pbf.get_uint32(); + data->extent = layer_pbf.get_uint32(); break; case 15: // version - version = layer_pbf.get_uint32(); + data->version = layer_pbf.get_uint32(); break; default: layer_pbf.skip(); @@ -294,7 +310,7 @@ VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf) { } std::unique_ptr<GeometryTileFeature> VectorTileLayer::getFeature(std::size_t i) const { - return std::make_unique<VectorTileFeature>(features.at(i), *this); + return std::make_unique<VectorTileFeature>(features.at(i), data); } std::string VectorTileLayer::getName() const { diff --git a/src/mbgl/util/chrono.cpp b/src/mbgl/util/chrono.cpp index f338f524b9..5c8fd3c0ff 100644 --- a/src/mbgl/util/chrono.cpp +++ b/src/mbgl/util/chrono.cpp @@ -33,7 +33,7 @@ std::string iso8601(Timestamp timestamp) { Timestamp parseTimestamp(const char* timestamp) { return std::chrono::time_point_cast<Seconds>(std::chrono::system_clock::from_time_t(parse_date(timestamp))); } - + Timestamp parseTimestamp(const int32_t timestamp) { return std::chrono::time_point_cast<Seconds>(std::chrono::system_clock::from_time_t(timestamp)); } diff --git a/src/mbgl/util/http_header.cpp b/src/mbgl/util/http_header.cpp index e337d4c8ab..40711232ff 100644 --- a/src/mbgl/util/http_header.cpp +++ b/src/mbgl/util/http_header.cpp @@ -28,7 +28,7 @@ CacheControl CacheControl::parse(const std::string& value) { optional<Timestamp> CacheControl::toTimePoint() const { return maxAge ? util::now() + Seconds(*maxAge) : optional<Timestamp>{}; } - + optional<Timestamp> parseRetryHeaders(const optional<std::string>& retryAfter, const optional<std::string>& xRateLimitReset) { if (retryAfter) { @@ -45,7 +45,7 @@ optional<Timestamp> parseRetryHeaders(const optional<std::string>& retryAfter, return {}; } } - + return {}; } diff --git a/src/mbgl/util/http_header.hpp b/src/mbgl/util/http_header.hpp index fa76cb724e..23da2c9ea4 100644 --- a/src/mbgl/util/http_header.hpp +++ b/src/mbgl/util/http_header.hpp @@ -17,7 +17,7 @@ public: optional<Timestamp> toTimePoint() const; }; - + optional<Timestamp> parseRetryHeaders(const optional<std::string>& retryAfter, const optional<std::string>& xRateLimitReset); diff --git a/src/mbgl/util/http_timeout.cpp b/src/mbgl/util/http_timeout.cpp index ded0128ac9..ca9a93498f 100644 --- a/src/mbgl/util/http_timeout.cpp +++ b/src/mbgl/util/http_timeout.cpp @@ -17,7 +17,7 @@ Duration errorRetryTimeout(Response::Error::Reason failedRequestReason, uint32_t if (retryAfter) { return *retryAfter - util::now(); } else { - //Default + // Default return Seconds(util::DEFAULT_RATE_LIMIT_TIMEOUT); } } else { @@ -34,7 +34,7 @@ Duration expirationTimeout(optional<Timestamp> expires, uint32_t expiredRequests } else { return Duration::max(); } -} +} } // namespace http } // namespace mbgl diff --git a/src/mbgl/util/i18n.cpp b/src/mbgl/util/i18n.cpp index 33ce5e22de..8e56877a64 100644 --- a/src/mbgl/util/i18n.cpp +++ b/src/mbgl/util/i18n.cpp @@ -1,5 +1,7 @@ #include "i18n.hpp" +#include <map> + namespace { /** Defines a function that returns true if a codepoint is in a named block. @@ -8,7 +10,7 @@ namespace { @param last The last codepoint in the block, inclusive. */ #define DEFINE_IS_IN_UNICODE_BLOCK(name, first, last) \ - inline bool isIn##name(uint16_t codepoint) { \ + inline bool isIn##name(char16_t codepoint) { \ return codepoint >= first && codepoint <= last; \ } @@ -16,7 +18,7 @@ namespace { // Keep it synchronized with <http://www.unicode.org/Public/UCD/latest/ucd/Blocks.txt>. // DEFINE_IS_IN_UNICODE_BLOCK(BasicLatin, 0x0000, 0x007F) -// DEFINE_IS_IN_UNICODE_BLOCK(Latin1Supplement, 0x0080, 0x00FF) +DEFINE_IS_IN_UNICODE_BLOCK(Latin1Supplement, 0x0080, 0x00FF) // DEFINE_IS_IN_UNICODE_BLOCK(LatinExtendedA, 0x0100, 0x017F) // DEFINE_IS_IN_UNICODE_BLOCK(LatinExtendedB, 0x0180, 0x024F) // DEFINE_IS_IN_UNICODE_BLOCK(IPAExtensions, 0x0250, 0x02AF) @@ -50,11 +52,11 @@ namespace { // DEFINE_IS_IN_UNICODE_BLOCK(Tibetan, 0x0F00, 0x0FFF) // DEFINE_IS_IN_UNICODE_BLOCK(Myanmar, 0x1000, 0x109F) // DEFINE_IS_IN_UNICODE_BLOCK(Georgian, 0x10A0, 0x10FF) -// DEFINE_IS_IN_UNICODE_BLOCK(HangulJamo, 0x1100, 0x11FF) +DEFINE_IS_IN_UNICODE_BLOCK(HangulJamo, 0x1100, 0x11FF) // DEFINE_IS_IN_UNICODE_BLOCK(Ethiopic, 0x1200, 0x137F) // DEFINE_IS_IN_UNICODE_BLOCK(EthiopicSupplement, 0x1380, 0x139F) // DEFINE_IS_IN_UNICODE_BLOCK(Cherokee, 0x13A0, 0x13FF) -// DEFINE_IS_IN_UNICODE_BLOCK(UnifiedCanadianAboriginalSyllabics, 0x1400, 0x167F) +DEFINE_IS_IN_UNICODE_BLOCK(UnifiedCanadianAboriginalSyllabics, 0x1400, 0x167F) // DEFINE_IS_IN_UNICODE_BLOCK(Ogham, 0x1680, 0x169F) // DEFINE_IS_IN_UNICODE_BLOCK(Runic, 0x16A0, 0x16FF) // DEFINE_IS_IN_UNICODE_BLOCK(Tagalog, 0x1700, 0x171F) @@ -63,7 +65,7 @@ namespace { // DEFINE_IS_IN_UNICODE_BLOCK(Tagbanwa, 0x1760, 0x177F) // DEFINE_IS_IN_UNICODE_BLOCK(Khmer, 0x1780, 0x17FF) // DEFINE_IS_IN_UNICODE_BLOCK(Mongolian, 0x1800, 0x18AF) -// DEFINE_IS_IN_UNICODE_BLOCK(UnifiedCanadianAboriginalSyllabicsExtended, 0x18B0, 0x18FF) +DEFINE_IS_IN_UNICODE_BLOCK(UnifiedCanadianAboriginalSyllabicsExtended, 0x18B0, 0x18FF) // DEFINE_IS_IN_UNICODE_BLOCK(Limbu, 0x1900, 0x194F) // DEFINE_IS_IN_UNICODE_BLOCK(TaiLe, 0x1950, 0x197F) // DEFINE_IS_IN_UNICODE_BLOCK(NewTaiLue, 0x1980, 0x19DF) @@ -84,22 +86,22 @@ namespace { // DEFINE_IS_IN_UNICODE_BLOCK(CombiningDiacriticalMarksSupplement, 0x1DC0, 0x1DFF) // DEFINE_IS_IN_UNICODE_BLOCK(LatinExtendedAdditional, 0x1E00, 0x1EFF) // DEFINE_IS_IN_UNICODE_BLOCK(GreekExtended, 0x1F00, 0x1FFF) -// DEFINE_IS_IN_UNICODE_BLOCK(GeneralPunctuation, 0x2000, 0x206F) +DEFINE_IS_IN_UNICODE_BLOCK(GeneralPunctuation, 0x2000, 0x206F) // DEFINE_IS_IN_UNICODE_BLOCK(SuperscriptsandSubscripts, 0x2070, 0x209F) // DEFINE_IS_IN_UNICODE_BLOCK(CurrencySymbols, 0x20A0, 0x20CF) // DEFINE_IS_IN_UNICODE_BLOCK(CombiningDiacriticalMarksforSymbols, 0x20D0, 0x20FF) -// DEFINE_IS_IN_UNICODE_BLOCK(LetterlikeSymbols, 0x2100, 0x214F) -// DEFINE_IS_IN_UNICODE_BLOCK(NumberForms, 0x2150, 0x218F) +DEFINE_IS_IN_UNICODE_BLOCK(LetterlikeSymbols, 0x2100, 0x214F) +DEFINE_IS_IN_UNICODE_BLOCK(NumberForms, 0x2150, 0x218F) // DEFINE_IS_IN_UNICODE_BLOCK(Arrows, 0x2190, 0x21FF) // DEFINE_IS_IN_UNICODE_BLOCK(MathematicalOperators, 0x2200, 0x22FF) -// DEFINE_IS_IN_UNICODE_BLOCK(MiscellaneousTechnical, 0x2300, 0x23FF) -// DEFINE_IS_IN_UNICODE_BLOCK(ControlPictures, 0x2400, 0x243F) -// DEFINE_IS_IN_UNICODE_BLOCK(OpticalCharacterRecognition, 0x2440, 0x245F) -// DEFINE_IS_IN_UNICODE_BLOCK(EnclosedAlphanumerics, 0x2460, 0x24FF) +DEFINE_IS_IN_UNICODE_BLOCK(MiscellaneousTechnical, 0x2300, 0x23FF) +DEFINE_IS_IN_UNICODE_BLOCK(ControlPictures, 0x2400, 0x243F) +DEFINE_IS_IN_UNICODE_BLOCK(OpticalCharacterRecognition, 0x2440, 0x245F) +DEFINE_IS_IN_UNICODE_BLOCK(EnclosedAlphanumerics, 0x2460, 0x24FF) // DEFINE_IS_IN_UNICODE_BLOCK(BoxDrawing, 0x2500, 0x257F) // DEFINE_IS_IN_UNICODE_BLOCK(BlockElements, 0x2580, 0x259F) -// DEFINE_IS_IN_UNICODE_BLOCK(GeometricShapes, 0x25A0, 0x25FF) -// DEFINE_IS_IN_UNICODE_BLOCK(MiscellaneousSymbols, 0x2600, 0x26FF) +DEFINE_IS_IN_UNICODE_BLOCK(GeometricShapes, 0x25A0, 0x25FF) +DEFINE_IS_IN_UNICODE_BLOCK(MiscellaneousSymbols, 0x2600, 0x26FF) // DEFINE_IS_IN_UNICODE_BLOCK(Dingbats, 0x2700, 0x27BF) // DEFINE_IS_IN_UNICODE_BLOCK(MiscellaneousMathematicalSymbolsA, 0x27C0, 0x27EF) // DEFINE_IS_IN_UNICODE_BLOCK(SupplementalArrowsA, 0x27F0, 0x27FF) @@ -123,15 +125,15 @@ DEFINE_IS_IN_UNICODE_BLOCK(CJKSymbolsandPunctuation, 0x3000, 0x303F) DEFINE_IS_IN_UNICODE_BLOCK(Hiragana, 0x3040, 0x309F) DEFINE_IS_IN_UNICODE_BLOCK(Katakana, 0x30A0, 0x30FF) DEFINE_IS_IN_UNICODE_BLOCK(Bopomofo, 0x3100, 0x312F) -// DEFINE_IS_IN_UNICODE_BLOCK(HangulCompatibilityJamo, 0x3130, 0x318F) -// DEFINE_IS_IN_UNICODE_BLOCK(Kanbun, 0x3190, 0x319F) +DEFINE_IS_IN_UNICODE_BLOCK(HangulCompatibilityJamo, 0x3130, 0x318F) +DEFINE_IS_IN_UNICODE_BLOCK(Kanbun, 0x3190, 0x319F) DEFINE_IS_IN_UNICODE_BLOCK(BopomofoExtended, 0x31A0, 0x31BF) DEFINE_IS_IN_UNICODE_BLOCK(CJKStrokes, 0x31C0, 0x31EF) DEFINE_IS_IN_UNICODE_BLOCK(KatakanaPhoneticExtensions, 0x31F0, 0x31FF) DEFINE_IS_IN_UNICODE_BLOCK(EnclosedCJKLettersandMonths, 0x3200, 0x32FF) DEFINE_IS_IN_UNICODE_BLOCK(CJKCompatibility, 0x3300, 0x33FF) DEFINE_IS_IN_UNICODE_BLOCK(CJKUnifiedIdeographsExtensionA, 0x3400, 0x4DBF) -// DEFINE_IS_IN_UNICODE_BLOCK(YijingHexagramSymbols, 0x4DC0, 0x4DFF) +DEFINE_IS_IN_UNICODE_BLOCK(YijingHexagramSymbols, 0x4DC0, 0x4DFF) DEFINE_IS_IN_UNICODE_BLOCK(CJKUnifiedIdeographs, 0x4E00, 0x9FFF) DEFINE_IS_IN_UNICODE_BLOCK(YiSyllables, 0xA000, 0xA48F) DEFINE_IS_IN_UNICODE_BLOCK(YiRadicals, 0xA490, 0xA4CF) @@ -148,7 +150,7 @@ DEFINE_IS_IN_UNICODE_BLOCK(YiRadicals, 0xA490, 0xA4CF) // DEFINE_IS_IN_UNICODE_BLOCK(DevanagariExtended, 0xA8E0, 0xA8FF) // DEFINE_IS_IN_UNICODE_BLOCK(KayahLi, 0xA900, 0xA92F) // DEFINE_IS_IN_UNICODE_BLOCK(Rejang, 0xA930, 0xA95F) -// DEFINE_IS_IN_UNICODE_BLOCK(HangulJamoExtendedA, 0xA960, 0xA97F) +DEFINE_IS_IN_UNICODE_BLOCK(HangulJamoExtendedA, 0xA960, 0xA97F) // DEFINE_IS_IN_UNICODE_BLOCK(Javanese, 0xA980, 0xA9DF) // DEFINE_IS_IN_UNICODE_BLOCK(MyanmarExtendedB, 0xA9E0, 0xA9FF) // DEFINE_IS_IN_UNICODE_BLOCK(Cham, 0xAA00, 0xAA5F) @@ -159,12 +161,12 @@ DEFINE_IS_IN_UNICODE_BLOCK(YiRadicals, 0xA490, 0xA4CF) // DEFINE_IS_IN_UNICODE_BLOCK(LatinExtendedE, 0xAB30, 0xAB6F) // DEFINE_IS_IN_UNICODE_BLOCK(CherokeeSupplement, 0xAB70, 0xABBF) // DEFINE_IS_IN_UNICODE_BLOCK(MeeteiMayek, 0xABC0, 0xABFF) -// DEFINE_IS_IN_UNICODE_BLOCK(HangulSyllables, 0xAC00, 0xD7AF) -// DEFINE_IS_IN_UNICODE_BLOCK(HangulJamoExtendedB, 0xD7B0, 0xD7FF) +DEFINE_IS_IN_UNICODE_BLOCK(HangulSyllables, 0xAC00, 0xD7AF) +DEFINE_IS_IN_UNICODE_BLOCK(HangulJamoExtendedB, 0xD7B0, 0xD7FF) // DEFINE_IS_IN_UNICODE_BLOCK(HighSurrogates, 0xD800, 0xDB7F) // DEFINE_IS_IN_UNICODE_BLOCK(HighPrivateUseSurrogates, 0xDB80, 0xDBFF) // DEFINE_IS_IN_UNICODE_BLOCK(LowSurrogates, 0xDC00, 0xDFFF) -// DEFINE_IS_IN_UNICODE_BLOCK(PrivateUseArea, 0xE000, 0xF8FF) +DEFINE_IS_IN_UNICODE_BLOCK(PrivateUseArea, 0xE000, 0xF8FF) DEFINE_IS_IN_UNICODE_BLOCK(CJKCompatibilityIdeographs, 0xF900, 0xFAFF) // DEFINE_IS_IN_UNICODE_BLOCK(AlphabeticPresentationForms, 0xFB00, 0xFB4F) // DEFINE_IS_IN_UNICODE_BLOCK(ArabicPresentationFormsA, 0xFB50, 0xFDFF) @@ -172,7 +174,7 @@ DEFINE_IS_IN_UNICODE_BLOCK(CJKCompatibilityIdeographs, 0xF900, 0xFAFF) DEFINE_IS_IN_UNICODE_BLOCK(VerticalForms, 0xFE10, 0xFE1F) // DEFINE_IS_IN_UNICODE_BLOCK(CombiningHalfMarks, 0xFE20, 0xFE2F) DEFINE_IS_IN_UNICODE_BLOCK(CJKCompatibilityForms, 0xFE30, 0xFE4F) -// DEFINE_IS_IN_UNICODE_BLOCK(SmallFormVariants, 0xFE50, 0xFE6F) +DEFINE_IS_IN_UNICODE_BLOCK(SmallFormVariants, 0xFE50, 0xFE6F) // DEFINE_IS_IN_UNICODE_BLOCK(ArabicPresentationFormsB, 0xFE70, 0xFEFF) DEFINE_IS_IN_UNICODE_BLOCK(HalfwidthandFullwidthForms, 0xFF00, 0xFFEF) // DEFINE_IS_IN_UNICODE_BLOCK(Specials, 0xFFF0, 0xFFFF) @@ -288,13 +290,33 @@ DEFINE_IS_IN_UNICODE_BLOCK(HalfwidthandFullwidthForms, 0xFF00, 0xFFEF) // DEFINE_IS_IN_UNICODE_BLOCK(VariationSelectorsSupplement, 0xE0100, 0xE01EF) // DEFINE_IS_IN_UNICODE_BLOCK(SupplementaryPrivateUseAreaA, 0xF0000, 0xFFFFF) // DEFINE_IS_IN_UNICODE_BLOCK(SupplementaryPrivateUseAreaB, 0x100000, 0x10FFFF) + +const std::map<char16_t, char16_t> verticalPunctuation = { + { u'!', u'︕' }, { u'#', u'#' }, { u'$', u'$' }, { u'%', u'%' }, { u'&', u'&' }, + { u'(', u'︵' }, { u')', u'︶' }, { u'*', u'*' }, { u'+', u'+' }, { u',', u'︐' }, + { u'-', u'︲' }, { u'.', u'・' }, { u'/', u'/' }, { u':', u'︓' }, { u';', u'︔' }, + { u'<', u'︿' }, { u'=', u'=' }, { u'>', u'﹀' }, { u'?', u'︖' }, { u'@', u'@' }, + { u'[', u'﹇' }, { u'\\', u'\' }, { u']', u'﹈' }, { u'^', u'^' }, { u'_', u'︳' }, + { u'`', u'`' }, { u'{', u'︷' }, { u'|', u'―' }, { u'}', u'︸' }, { u'~', u'~' }, + { u'¢', u'¢' }, { u'£', u'£' }, { u'¥', u'¥' }, { u'¦', u'¦' }, { u'¬', u'¬' }, + { u'¯', u' ̄' }, { u'–', u'︲' }, { u'—', u'︱' }, { u'‘', u'﹃' }, { u'’', u'﹄' }, + { u'“', u'﹁' }, { u'”', u'﹂' }, { u'…', u'︙' }, { u'‧', u'・' }, { u'₩', u'₩' }, + { u'、', u'︑' }, { u'。', u'︒' }, { u'〈', u'︿' }, { u'〉', u'﹀' }, { u'《', u'︽' }, + { u'》', u'︾' }, { u'「', u'﹁' }, { u'」', u'﹂' }, { u'『', u'﹃' }, { u'』', u'﹄' }, + { u'【', u'︻' }, { u'】', u'︼' }, { u'〔', u'︹' }, { u'〕', u'︺' }, { u'〖', u'︗' }, + { u'〗', u'︘' }, { u'!', u'︕' }, { u'(', u'︵' }, { u')', u'︶' }, { u',', u'︐' }, + { u'-', u'︲' }, { u'.', u'・' }, { u':', u'︓' }, { u';', u'︔' }, { u'<', u'︿' }, + { u'>', u'﹀' }, { u'?', u'︖' }, { u'[', u'﹇' }, { u']', u'﹈' }, { u'_', u'︳' }, + { u'{', u'︷' }, { u'|', u'―' }, { u'}', u'︸' }, { u'⦅', u'︵' }, { u'⦆', u'︶' }, + { u'。', u'︒' }, { u'「', u'﹁' }, { u'」', u'﹂' }, +}; } namespace mbgl { namespace util { namespace i18n { -bool allowsWordBreaking(uint16_t chr) { +bool allowsWordBreaking(char16_t chr) { return (chr == 0x0a /* newline */ || chr == 0x20 /* space */ || chr == 0x26 /* ampersand */ @@ -311,7 +333,7 @@ bool allowsWordBreaking(uint16_t chr) { } bool allowsIdeographicBreaking(const std::u16string& string) { - for (uint16_t chr : string) { + for (char16_t chr : string) { if (!allowsIdeographicBreaking(chr)) { return false; } @@ -319,7 +341,7 @@ bool allowsIdeographicBreaking(const std::u16string& string) { return true; } -bool allowsIdeographicBreaking(uint16_t chr) { +bool allowsIdeographicBreaking(char16_t chr) { // Allow U+2027 "Interpunct" for hyphenation of Chinese words if (chr == 0x2027) return true; @@ -352,6 +374,188 @@ bool allowsIdeographicBreaking(uint16_t chr) { // || isInCJKCompatibilityIdeographsSupplement(chr)); } +bool allowsVerticalWritingMode(const std::u16string& string) { + for (char32_t chr : string) { + if (hasUprightVerticalOrientation(chr)) { + return true; + } + } + return false; +} + +// The following logic comes from +// <http://www.unicode.org/Public/vertical/revision-16/VerticalOrientation-16.txt>. +// The data file denotes with “U” or “Tu” any codepoint that may be drawn +// upright in vertical text but does not distinguish between upright and +// “neutral” characters. + +bool hasUprightVerticalOrientation(char16_t chr) { + if (chr == u'˪' || chr == u'˫') + return true; + + // Return early for characters outside all ranges whose characters remain + // upright in vertical writing mode. + if (chr < 0x1100) + return false; + + if (isInBopomofo(chr) || isInBopomofoExtended(chr)) + return true; + if (isInCJKCompatibilityForms(chr)) { + if (!(chr >= u'﹉' && chr <= u'﹏')) + return true; + } + if (isInCJKCompatibility(chr) || isInCJKCompatibilityIdeographs(chr) || + isInCJKRadicalsSupplement(chr) || isInCJKStrokes(chr)) + return true; + if (isInCJKSymbolsandPunctuation(chr)) { + if (!(chr >= u'〈' && chr <= u'】') && !(chr >= u'〔' && chr <= u'〟') && chr != u'〰') + return true; + } + if (isInCJKUnifiedIdeographs(chr) || isInCJKUnifiedIdeographsExtensionA(chr) || + isInEnclosedCJKLettersandMonths(chr) || isInHangulCompatibilityJamo(chr) || + isInHangulJamo(chr) || isInHangulJamoExtendedA(chr) || isInHangulJamoExtendedB(chr) || + isInHangulSyllables(chr) || isInHiragana(chr) || + isInIdeographicDescriptionCharacters(chr) || isInKanbun(chr) || isInKangxiRadicals(chr)) + return true; + if (isInKatakana(chr)) { + if (chr != u'ー') + return true; + } + if (isInKatakanaPhoneticExtensions(chr)) + return true; + if (isInHalfwidthandFullwidthForms(chr)) { + if (chr != u'(' && chr != u')' && chr != u'-' && !(chr >= u':' && chr <= u'>') && + chr != u'[' && chr != u']' && chr != u'_' && !(chr >= u'{' && chr <= 0xFFDF) && + chr != u' ̄' && !(chr >= u'│' && chr <= 0xFFEF)) + return true; + } + if (isInSmallFormVariants(chr)) { + if (!(chr >= u'﹘' && chr <= u'﹞') && !(chr >= u'﹣' && chr <= u'﹦')) + return true; + } + if (isInUnifiedCanadianAboriginalSyllabics(chr) || + isInUnifiedCanadianAboriginalSyllabicsExtended(chr) || isInVerticalForms(chr) || + isInYijingHexagramSymbols(chr) || isInYiSyllables(chr) || isInYiRadicals(chr)) + return true; + + // https://github.com/mapbox/mapbox-gl/issues/29 + + // if (isInMeroiticHieroglyphs(chr)) return true; + // if (isInSiddham(chr)) return true; + // if (isInEgyptianHieroglyphs(chr)) return true; + // if (isInAnatolianHieroglyphs(chr)) return true; + // if (isInIdeographicSymbolsandPunctuation(chr)) return true; + // if (isInTangut(chr)) return true; + // if (isInTangutComponents(chr)) return true; + // if (isInKanaSupplement(chr)) return true; + // if (isInByzantineMusicalSymbols(chr)) return true; + // if (isInMusicalSymbols(chr)) return true; + // if (isInTaiXuanJingSymbols(chr)) return true; + // if (isInCountingRodNumerals(chr)) return true; + // if (isInSuttonSignWriting(chr)) return true; + // if (isInMahjongTiles(chr)) return true; + // if (isInDominoTiles(chr)) return true; + // if (isInPlayingCards(chr)) return true; + // if (isInEnclosedAlphanumericSupplement(chr)) return true; + // if (isInEnclosedIdeographicSupplement(chr)) return true; + // if (isInMiscellaneousSymbolsandPictographs(chr)) return true; + // if (isInEmoticons(chr)) return true; + // if (isInOrnamentalDingbats(chr)) return true; + // if (isInTransportandMapSymbols(chr)) return true; + // if (isInAlchemicalSymbols(chr)) return true; + // if (isInGeometricShapesExtended(chr)) return true; + // if (isInSupplementalSymbolsandPictographs(chr)) return true; + // if (isInCJKUnifiedIdeographsExtensionB(chr)) return true; + // if (isInCJKUnifiedIdeographsExtensionC(chr)) return true; + // if (isInCJKUnifiedIdeographsExtensionD(chr)) return true; + // if (isInCJKUnifiedIdeographsExtensionE(chr)) return true; + // if (isInCJKCompatibilityIdeographsSupplement(chr)) return true; + + return false; +} + +bool hasNeutralVerticalOrientation(char16_t chr) { + if (isInLatin1Supplement(chr)) { + if (chr == u'§' || chr == u'©' || chr == u'®' || chr == u'±' || chr == u'¼' || + chr == u'½' || chr == u'¾' || chr == u'×' || chr == u'÷') { + return true; + } + } + if (isInGeneralPunctuation(chr)) { + if (chr == u'‖' || chr == u'†' || chr == u'‡' || chr == u'‰' || chr == u'‱' || + chr == u'※' || chr == u'‼' || chr == u'⁂' || chr == u'⁇' || chr == u'⁈' || + chr == u'⁉' || chr == u'⁑') { + return true; + } + } + if (isInLetterlikeSymbols(chr) || isInNumberForms(chr)) { + return true; + } + if (isInMiscellaneousTechnical(chr)) { + if ((chr >= u'⌀' && chr <= u'⌇') || (chr >= u'⌌' && chr <= u'⌟') || + (chr >= u'⌤' && chr <= u'⌨') || chr == u'⌫' || (chr >= u'⍽' && chr <= u'⎚') || + (chr >= u'⎾' && chr <= u'⏍') || chr == u'⏏' || (chr >= u'⏑' && chr <= u'⏛') || + (chr >= u'⏢' && chr <= 0x23FF)) { + return true; + } + } + if (isInControlPictures(chr) || isInOpticalCharacterRecognition(chr) || + isInEnclosedAlphanumerics(chr) || isInGeometricShapes(chr)) { + return true; + } + if (isInMiscellaneousSymbols(chr)) { + if ((chr >= u'⬒' && chr <= u'⬯') || + (chr >= u'⭐' && chr <= 0x2B59 /* heavy circled saltire */) || + (chr >= 0x2BB8 /* upwards white arrow from bar with horizontal bar */ && + chr <= 0x2BEB)) { + return true; + } + } + if (isInCJKSymbolsandPunctuation(chr) || isInKatakana(chr) || isInPrivateUseArea(chr) || + isInCJKCompatibilityForms(chr) || isInSmallFormVariants(chr) || + isInHalfwidthandFullwidthForms(chr)) { + return true; + } + if (chr == u'∞' || chr == u'∴' || chr == u'∵' || + (chr >= 0x2700 /* black safety scissors */ && chr <= u'❧') || + (chr >= u'❶' && chr <= u'➓') || chr == 0xFFFC /* object replacement character */ || + chr == 0xFFFD /* replacement character */) { + return true; + } + return false; +} + +bool hasRotatedVerticalOrientation(char16_t chr) { + return !(hasUprightVerticalOrientation(chr) || hasNeutralVerticalOrientation(chr)); +} + +std::u16string verticalizePunctuation(const std::u16string& input) { + std::u16string output; + + for (size_t i = 0; i < input.size(); i++) { + char16_t nextCharCode = i < input.size() ? input[i + 1] : 0; + char16_t prevCharCode = i ? input[i - 1] : 0; + + bool canReplacePunctuation = + ((!nextCharCode || !hasRotatedVerticalOrientation(nextCharCode) || + verticalPunctuation.count(input[i + 1])) && + (!prevCharCode || !hasRotatedVerticalOrientation(prevCharCode) || + verticalPunctuation.count(input[i - 1]))); + + if (char16_t repl = canReplacePunctuation ? verticalizePunctuation(input[i]) : 0) { + output += repl; + } else { + output += input[i]; + } + } + + return output; +} + +char16_t verticalizePunctuation(char16_t chr) { + return verticalPunctuation.count(chr) ? verticalPunctuation.at(chr) : 0; +} + } // namespace i18n } // namespace util } // namespace mbgl diff --git a/src/mbgl/util/i18n.hpp b/src/mbgl/util/i18n.hpp index f1d3f53f72..186212f50d 100644 --- a/src/mbgl/util/i18n.hpp +++ b/src/mbgl/util/i18n.hpp @@ -8,7 +8,7 @@ namespace i18n { /** Returns whether a line break can be inserted after the character indicated by the given Unicode codepoint due to word breaking. */ -bool allowsWordBreaking(uint16_t chr); +bool allowsWordBreaking(char16_t chr); /** Returns whether a line break can be inserted after any character in the given string. If false, line breaking should occur on word boundaries @@ -17,7 +17,53 @@ bool allowsIdeographicBreaking(const std::u16string& string); /** Returns whether a line break can be inserted after the character indicated by the given Unicode codepoint due to ideographic breaking. */ -bool allowsIdeographicBreaking(uint16_t chr); +bool allowsIdeographicBreaking(char16_t chr); + +/** Returns whether any substring of the given string can be drawn as vertical + text with upright glyphs. */ +bool allowsVerticalWritingMode(const std::u16string& string); + +/** Returns true if the given Unicode codepoint identifies a character with + upright orientation. + + A character has upright orientation if it is drawn upright (unrotated) + whether the line is oriented horizontally or vertically, even if both + adjacent characters can be rotated. For example, a Chinese character is + always drawn upright. An uprightly oriented character causes an adjacent + “neutral” character to be drawn upright as well. */ +bool hasUprightVerticalOrientation(char16_t chr); + +/** Returns true if the given Unicode codepoint identifies a character with + neutral orientation. + + A character has neutral orientation if it may be drawn rotated or unrotated + when the line is oriented vertically, depending on the orientation of the + adjacent characters. For example, along a verticlly oriented line, the + vulgar fraction ½ is drawn upright among Chinese characters but rotated + among Latin letters. A neutrally oriented character does not influence + whether an adjacent character is drawn upright or rotated. + */ +bool hasNeutralVerticalOrientation(char16_t chr); + +/** Returns true if the given Unicode codepoint identifies a character with + rotated orientation. + + A character has rotated orientation if it is drawn rotated when the line is + oriented vertically, even if both adjacent characters are upright. For + example, a Latin letter is drawn rotated along a vertical line. A rotated + character causes an adjacent “neutral” character to be drawn rotated as + well. + */ +bool hasRotatedVerticalOrientation(char16_t chr); + +/** Returns a copy of the given string with punctuation characters replaced with + their vertical forms wherever applicable. */ +std::u16string verticalizePunctuation(const std::u16string& input); + +/** Returns the form of the given character appropriate for vertical text. + + @return The character’s specialized vertical form; 0 if not applicable. */ +char16_t verticalizePunctuation(char16_t chr); } // namespace i18n } // namespace util diff --git a/src/mbgl/util/ignore.hpp b/src/mbgl/util/ignore.hpp index 955b1de2fa..577bcf4d91 100644 --- a/src/mbgl/util/ignore.hpp +++ b/src/mbgl/util/ignore.hpp @@ -19,5 +19,8 @@ template <class... Ts> void ignore(Ts&&...) {} // template <class T> void ignore(const std::initializer_list<T>&) {} +// Handle the zero-argument case. +inline void ignore(const std::initializer_list<int>&) {} + } // namespace util } // namespace mbgl diff --git a/src/mbgl/util/indexed_tuple.hpp b/src/mbgl/util/indexed_tuple.hpp index 110e7dce12..a414639530 100644 --- a/src/mbgl/util/indexed_tuple.hpp +++ b/src/mbgl/util/indexed_tuple.hpp @@ -1,5 +1,7 @@ #pragma once +#include <mbgl/util/type_list.hpp> + #include <type_traits> #include <tuple> @@ -14,8 +16,6 @@ struct TypeIndex<T, T, Ts...> : std::integral_constant<std::size_t, 0> {}; template <class T, class U, class... Ts> struct TypeIndex<T, U, Ts...> : std::integral_constant<std::size_t, 1 + TypeIndex<T, Ts...>::value> {}; -template <class...> class TypeList {}; - template <class...> class IndexedTuple; // A tuple of Ts, where individual members can be accessed via `t.get<I>()` for I ∈ Is. @@ -42,6 +42,15 @@ public: const auto& get() const { return std::get<Index<I>>(*this); } + + template <class... Js, class... Us> + IndexedTuple<TypeList<Is..., Js...>, TypeList<Ts..., Us...>> + concat(const IndexedTuple<TypeList<Js...>, TypeList<Us...>>& other) const { + return IndexedTuple<TypeList<Is..., Js...>, TypeList<Ts..., Us...>> { + get<Is>()..., + other.template get<Js>()... + }; + } }; } // namespace mbgl diff --git a/src/mbgl/util/interpolate.cpp b/src/mbgl/util/interpolate.cpp new file mode 100644 index 0000000000..306a5c6630 --- /dev/null +++ b/src/mbgl/util/interpolate.cpp @@ -0,0 +1,19 @@ +#include <mbgl/util/interpolate.hpp> + +#include <cmath> + +namespace mbgl { +namespace util { + +float interpolationFactor(float base, Range<float> range, float z) { + const float zoomDiff = range.max - range.min; + const float zoomProgress = z - range.min; + if (base == 1.0f) { + return zoomProgress / zoomDiff; + } else { + return (std::pow(base, zoomProgress) - 1) / (std::pow(base, zoomDiff) - 1); + } +} + +} // namespace util +} // namespace mbgl diff --git a/src/mbgl/util/interpolate.hpp b/src/mbgl/util/interpolate.hpp index ef066377da..d463ffc056 100644 --- a/src/mbgl/util/interpolate.hpp +++ b/src/mbgl/util/interpolate.hpp @@ -1,15 +1,19 @@ #pragma once +#include <mbgl/util/color.hpp> +#include <mbgl/util/range.hpp> + #include <array> #include <vector> #include <string> #include <type_traits> #include <utility> -#include <mbgl/util/color.hpp> namespace mbgl { namespace util { +float interpolationFactor(float base, Range<float> range, float z); + template <class T, class Enabled = void> struct Interpolator; @@ -78,5 +82,8 @@ template <class T> struct Interpolator<std::vector<T>> : Uninterpolated {}; +template <class T> +constexpr bool Interpolatable = !std::is_base_of<Uninterpolated, Interpolator<T>>::value; + } // namespace util } // namespace mbgl diff --git a/src/mbgl/util/thread.hpp b/src/mbgl/util/thread.hpp index b03c7f3e7c..184c6a8a12 100644 --- a/src/mbgl/util/thread.hpp +++ b/src/mbgl/util/thread.hpp @@ -1,5 +1,6 @@ #pragma once +#include <cassert> #include <future> #include <thread> #include <atomic> @@ -9,6 +10,7 @@ #include <mbgl/util/run_loop.hpp> #include <mbgl/util/thread_context.hpp> #include <mbgl/util/platform.hpp> +#include <mbgl/util/util.hpp> namespace mbgl { namespace util { @@ -47,6 +49,8 @@ public: // Invoke object->fn(args...) asynchronously, but wait for the result. template <typename Fn, class... Args> auto invokeSync(Fn fn, Args&&... args) { + assert(!paused); + using R = std::result_of_t<Fn(Object, Args&&...)>; std::packaged_task<R ()> task(std::bind(fn, object, args...)); std::future<R> future = task.get_future(); @@ -54,7 +58,39 @@ public: return future.get(); } + void pause() { + MBGL_VERIFY_THREAD(tid); + + assert(!paused); + + paused = std::make_unique<std::promise<void>>(); + resumed = std::make_unique<std::promise<void>>(); + + auto pausing = paused->get_future(); + + loop->invoke([this] { + auto resuming = resumed->get_future(); + paused->set_value(); + resuming.get(); + }); + + pausing.get(); + } + + void resume() { + MBGL_VERIFY_THREAD(tid); + + assert(paused); + + resumed->set_value(); + + resumed.reset(); + paused.reset(); + } + private: + MBGL_STORE_THREAD(tid); + Thread(const Thread&) = delete; Thread(Thread&&) = delete; Thread& operator=(const Thread&) = delete; @@ -73,6 +109,9 @@ private: std::promise<void> running; std::promise<void> joinable; + std::unique_ptr<std::promise<void>> paused; + std::unique_ptr<std::promise<void>> resumed; + std::thread thread; Object* object = nullptr; @@ -119,6 +158,12 @@ void Thread<Object>::run(P&& params, std::index_sequence<I...>) { template <class Object> Thread<Object>::~Thread() { + MBGL_VERIFY_THREAD(tid); + + if (paused) { + resume(); + } + loop->stop(); joinable.set_value(); thread.join(); diff --git a/src/mbgl/util/type_list.hpp b/src/mbgl/util/type_list.hpp new file mode 100644 index 0000000000..4a5e95c8a4 --- /dev/null +++ b/src/mbgl/util/type_list.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include <type_traits> +#include <tuple> + +namespace mbgl { + +template <class...> +class TypeList {}; + +namespace detail { + +template <class, class> +struct TypeCons; + +template <class T, class... Ts> +struct TypeCons<T, TypeList<Ts...>> { + using Type = TypeList<T, Ts...>; +}; + +template <class, template <class> class> +struct TypeFilter; + +template <template <class> class Predicate> +struct TypeFilter<TypeList<>, Predicate> { + using Type = TypeList<>; +}; + +template <template <class> class Predicate, class T, class... Ts> +struct TypeFilter<TypeList<T, Ts...>, Predicate> { + using Tail = typename TypeFilter<TypeList<Ts...>, Predicate>::Type; + using Type = std::conditional_t<Predicate<T>::value, typename TypeCons<T, Tail>::Type, Tail>; +}; + +} // namespace detail + +template <class TypeList, template <class> class Predicate> +using FilteredTypeList = typename detail::TypeFilter<TypeList, Predicate>::Type; + +} // namespace mbgl diff --git a/src/mbgl/util/url.cpp b/src/mbgl/util/url.cpp index 0a7d096ec0..3f36bc676f 100644 --- a/src/mbgl/util/url.cpp +++ b/src/mbgl/util/url.cpp @@ -89,8 +89,10 @@ URL::URL(const std::string& str) return { queryPos, (hashPos != std::string::npos ? hashPos : str.size()) - queryPos }; }()), scheme([&]() -> Segment { - auto schemeEnd = str.find(':'); - return { 0, schemeEnd == std::string::npos || schemeEnd > query.first ? 0 : schemeEnd }; + if (str.empty() || !isAlphaCharacter(str.front())) return { 0, 0 }; + size_t schemeEnd = 0; + while (schemeEnd < query.first && isSchemeCharacter(str[schemeEnd])) ++schemeEnd; + return { 0, str[schemeEnd] == ':' ? schemeEnd : 0 }; }()), domain([&]() -> Segment { auto domainPos = scheme.first + scheme.second; diff --git a/src/mbgl/util/version.cpp b/src/mbgl/util/version.cpp new file mode 100644 index 0000000000..fcb31f0b71 --- /dev/null +++ b/src/mbgl/util/version.cpp @@ -0,0 +1,9 @@ +#include <mbgl/util/version.hpp> + +namespace mbgl { +namespace version { + +const char* revision = MBGL_VERSION_REV; + +} // namespace version +} // namespace mbgl diff --git a/src/mbgl/util/version.hpp b/src/mbgl/util/version.hpp new file mode 100644 index 0000000000..e652016485 --- /dev/null +++ b/src/mbgl/util/version.hpp @@ -0,0 +1,9 @@ +#pragma once + +namespace mbgl { +namespace version { + +extern const char* revision; + +} // namespace version +} // namespace mbgl diff --git a/src/mbgl/util/version_info.cpp b/src/mbgl/util/version_info.cpp deleted file mode 100644 index f0fb139bca..0000000000 --- a/src/mbgl/util/version_info.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include <mbgl/util/version.hpp> - -namespace mbgl { -namespace version { - -const int major = MBGL_VERSION_MAJOR; -const int minor = MBGL_VERSION_MINOR; -const int patch = MBGL_VERSION_PATCH; -const char *revision = MBGL_VERSION_REV; -const char *string = MBGL_VERSION_STRING; -const unsigned int number = MBGL_VERSION; - -} // namespace version -} // namespace mbgl |