From 3270440f234570f1426c442898c2400e36608ebf Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Thu, 8 Jun 2017 13:58:46 -0700 Subject: [core] Per-tile glyph/icon atlases --- include/mbgl/util/image.hpp | 17 ++++++ src/mbgl/layout/symbol_instance.cpp | 6 +- src/mbgl/layout/symbol_instance.hpp | 2 +- src/mbgl/layout/symbol_layout.cpp | 81 +++++++++++++-------------- src/mbgl/layout/symbol_layout.hpp | 9 ++- src/mbgl/renderer/buckets/symbol_bucket.cpp | 2 - src/mbgl/renderer/buckets/symbol_bucket.hpp | 3 - src/mbgl/renderer/image_atlas.cpp | 34 +++++------ src/mbgl/renderer/image_manager.cpp | 9 +-- src/mbgl/renderer/painters/painter_symbol.cpp | 13 +++-- src/mbgl/text/glyph_atlas.cpp | 79 +++++++++++++------------- src/mbgl/text/glyph_atlas.hpp | 5 +- src/mbgl/text/quads.cpp | 8 +-- src/mbgl/text/quads.hpp | 2 +- src/mbgl/tile/geometry_tile.cpp | 18 ++++++ src/mbgl/tile/geometry_tile.hpp | 14 +++++ src/mbgl/tile/geometry_tile_worker.cpp | 14 ++++- test/tile/annotation_tile.test.cpp | 18 +++--- test/tile/vector_tile.test.cpp | 2 + test/util/image.test.cpp | 48 ++++++++++------ 20 files changed, 223 insertions(+), 161 deletions(-) diff --git a/include/mbgl/util/image.hpp b/include/mbgl/util/image.hpp index a41b8462bd..91bf06d727 100644 --- a/include/mbgl/util/image.hpp +++ b/include/mbgl/util/image.hpp @@ -78,10 +78,27 @@ public: std::fill(data.get(), data.get() + bytes(), value); } + void resize(Size size_) { + if (size == size_) { + return; + } + Image newImage(size_); + newImage.fill(0); + copy(*this, newImage, {0, 0}, {0, 0}, { + std::min(size.width, size_.width), + std::min(size.height, size_.height) + }); + operator=(std::move(newImage)); + } + // Copy image data within `rect` from `src` to the rectangle of the same size at `pt` // in `dst`. If the specified bounds exceed the bounds of the source or destination, // throw `std::out_of_range`. Must not be used to move data within a single Image. static void copy(const Image& srcImg, Image& dstImg, const Point& srcPt, const Point& dstPt, const Size& size) { + if (size.isEmpty()) { + return; + } + if (!srcImg.valid()) { throw std::invalid_argument("invalid source for image copy"); } diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp index 8816f4c95c..ffb70c7ca2 100644 --- a/src/mbgl/layout/symbol_instance.cpp +++ b/src/mbgl/layout/symbol_instance.cpp @@ -19,7 +19,7 @@ SymbolInstance::SymbolInstance(Anchor& anchor, const float iconBoxScale, const float iconPadding, const SymbolPlacementType iconPlacement, - const GlyphPositions& face, + const GlyphPositionMap& positions, const IndexedSubfeature& indexedFeature, const std::size_t featureIndex_) : point(anchor.point), @@ -38,11 +38,11 @@ SymbolInstance::SymbolInstance(Anchor& anchor, iconQuad = getIconQuad(anchor, *shapedIcon, line, layout, layoutTextSize, iconPlacement, shapedTextOrientations.first); } if (shapedTextOrientations.first) { - auto quads = getGlyphQuads(anchor, shapedTextOrientations.first, textBoxScale, line, layout, textPlacement, face); + auto quads = getGlyphQuads(anchor, shapedTextOrientations.first, textBoxScale, line, layout, textPlacement, positions); glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end()); } if (shapedTextOrientations.second) { - auto quads = getGlyphQuads(anchor, shapedTextOrientations.second, textBoxScale, line, layout, textPlacement, face); + auto quads = getGlyphQuads(anchor, shapedTextOrientations.second, textBoxScale, line, layout, textPlacement, positions); glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end()); } } diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp index 6a5d0c7cf3..f199d929df 100644 --- a/src/mbgl/layout/symbol_instance.hpp +++ b/src/mbgl/layout/symbol_instance.hpp @@ -26,7 +26,7 @@ public: const float iconBoxScale, const float iconPadding, style::SymbolPlacementType iconPlacement, - const GlyphPositions& face, + const GlyphPositionMap&, const IndexedSubfeature&, const std::size_t featureIndex); diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index b64fc4f66e..ed580bcb16 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -175,7 +175,8 @@ bool SymbolLayout::hasSymbolInstances() const { return !symbolInstances.empty(); } -void SymbolLayout::prepare(const GlyphMap& glyphs, const ImageMap& images) { +void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyphPositions, + const ImageMap& imageMap, const ImagePositions& imagePositions) { float horizontalAlign = 0.5; float verticalAlign = 0.5; @@ -220,12 +221,13 @@ void SymbolLayout::prepare(const GlyphMap& glyphs, const ImageMap& images) { const bool textAlongLine = layout.get() == AlignmentType::Map && layout.get() == SymbolPlacementType::Line; - auto glyphPositionsIt = glyphs.find(layout.get()); - if (glyphPositionsIt != glyphs.end()) { - glyphAtlas = makeGlyphAtlas(glyphPositionsIt->second); - } + auto glyphMapIt = glyphMap.find(layout.get()); + const Glyphs& glyphs = glyphMapIt != glyphMap.end() + ? glyphMapIt->second : Glyphs(); - imageAtlas = makeImageAtlas(images); + auto glyphPositionsIt = glyphPositions.find(layout.get()); + const GlyphPositionMap& glyphPositionMap = glyphPositionsIt != glyphPositions.end() + ? glyphPositionsIt->second : GlyphPositionMap(); for (auto it = features.begin(); it != features.end(); ++it) { auto& feature = *it; @@ -236,42 +238,39 @@ void SymbolLayout::prepare(const GlyphMap& glyphs, const ImageMap& images) { // if feature has text, shape the text if (feature.text) { - auto glyphPositions = glyphs.find(layout.get()); - if (glyphPositions != glyphs.end()) { // If there are no glyphs available for this feature, skip shaping - auto applyShaping = [&] (const std::u16string& text, WritingModeType writingMode) { - const float oneEm = 24.0f; - const Shaping result = getShaping( - /* string */ text, - /* maxWidth: ems */ layout.get() != SymbolPlacementType::Line ? - layout.get() * oneEm : 0, - /* lineHeight: ems */ layout.get() * oneEm, - /* horizontalAlign */ horizontalAlign, - /* verticalAlign */ verticalAlign, - /* justify */ justify, - /* spacing: ems */ util::i18n::allowsLetterSpacing(*feature.text) ? layout.get() * oneEm : 0.0f, - /* translate */ Point(layout.evaluate(zoom, feature)[0] * oneEm, layout.evaluate(zoom, feature)[1] * oneEm), - /* verticalHeight */ oneEm, - /* writingMode */ writingMode, - /* bidirectional algorithm object */ bidi, - /* glyphs */ glyphPositions->second); - - return result; - }; - - shapedTextOrientations.first = applyShaping(*feature.text, WritingModeType::Horizontal); - - if (util::i18n::allowsVerticalWritingMode(*feature.text) && textAlongLine) { - shapedTextOrientations.second = applyShaping(util::i18n::verticalizePunctuation(*feature.text), WritingModeType::Vertical); - } + auto applyShaping = [&] (const std::u16string& text, WritingModeType writingMode) { + const float oneEm = 24.0f; + const Shaping result = getShaping( + /* string */ text, + /* maxWidth: ems */ layout.get() != SymbolPlacementType::Line ? + layout.get() * oneEm : 0, + /* lineHeight: ems */ layout.get() * oneEm, + /* horizontalAlign */ horizontalAlign, + /* verticalAlign */ verticalAlign, + /* justify */ justify, + /* spacing: ems */ util::i18n::allowsLetterSpacing(*feature.text) ? layout.get() * oneEm : 0.0f, + /* translate */ Point(layout.evaluate(zoom, feature)[0] * oneEm, layout.evaluate(zoom, feature)[1] * oneEm), + /* verticalHeight */ oneEm, + /* writingMode */ writingMode, + /* bidirectional algorithm object */ bidi, + /* glyphs */ glyphs); + + return result; + }; + + shapedTextOrientations.first = applyShaping(*feature.text, WritingModeType::Horizontal); + + if (util::i18n::allowsVerticalWritingMode(*feature.text) && textAlongLine) { + shapedTextOrientations.second = applyShaping(util::i18n::verticalizePunctuation(*feature.text), WritingModeType::Vertical); } } // if feature has icon, get sprite atlas position if (feature.icon) { - auto image = images.find(*feature.icon); - if (image != images.end()) { + auto image = imageMap.find(*feature.icon); + if (image != imageMap.end()) { shapedIcon = PositionedIcon::shapeIcon( - imageAtlas.positions.at(*feature.icon), + imagePositions.at(*feature.icon), layout.evaluate(zoom, feature), layout.evaluate(zoom, feature) * util::DEG2RAD); if (image->second->sdf) { @@ -287,7 +286,7 @@ void SymbolLayout::prepare(const GlyphMap& glyphs, const ImageMap& images) { // if either shapedText or icon position is present, add the feature if (shapedTextOrientations.first || shapedIcon) { - addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon); + addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap); } feature.geometry.clear(); @@ -299,7 +298,8 @@ void SymbolLayout::prepare(const GlyphMap& glyphs, const ImageMap& images) { void SymbolLayout::addFeature(const std::size_t index, const SymbolFeature& feature, const std::pair& shapedTextOrientations, - optional shapedIcon) { + optional shapedIcon, + const GlyphPositionMap& glyphPositionMap) { const float minScale = 0.5f; const float glyphSize = 24.0f; @@ -356,7 +356,7 @@ void SymbolLayout::addFeature(const std::size_t index, addToBuffers, symbolInstances.size(), textBoxScale, textPadding, textPlacement, iconBoxScale, iconPadding, iconPlacement, - glyphAtlas.positions, indexedFeature, index); + glyphPositionMap, indexedFeature, index); }; const auto& type = feature.getType(); @@ -430,9 +430,6 @@ bool SymbolLayout::anchorIsTooClose(const std::u16string& text, const float repe std::unique_ptr SymbolLayout::place(CollisionTile& collisionTile) { auto bucket = std::make_unique(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear); - bucket->text.atlasImage = glyphAtlas.image.clone(); - bucket->icon.atlasImage = imageAtlas.image.clone(); - // Calculate which labels can be shown and when they can be shown and // create the bufers used for rendering. diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp index 7d6f2319cd..b22c47c567 100644 --- a/src/mbgl/layout/symbol_layout.hpp +++ b/src/mbgl/layout/symbol_layout.hpp @@ -33,7 +33,8 @@ public: ImageDependencies&, GlyphDependencies&); - void prepare(const GlyphMap& glyphs, const ImageMap& icons); + void prepare(const GlyphMap&, const GlyphPositions&, + const ImageMap&, const ImagePositions&); std::unique_ptr place(CollisionTile&); @@ -53,7 +54,8 @@ private: void addFeature(const size_t, const SymbolFeature&, const std::pair& shapedTextOrientations, - optional shapedIcon); + optional shapedIcon, + const GlyphPositionMap&); bool anchorIsTooClose(const std::u16string& text, const float repeatDistance, const Anchor&); std::map> compareText; @@ -93,9 +95,6 @@ private: std::vector symbolInstances; std::vector features; - GlyphAtlas glyphAtlas; - ImageAtlas imageAtlas; - 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/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp index 21d463b1fc..cbddade899 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.cpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp @@ -39,14 +39,12 @@ void SymbolBucket::upload(gl::Context& context) { if (hasTextData()) { text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices)); text.indexBuffer = context.createIndexBuffer(std::move(text.triangles)); - text.atlasTexture = context.createTexture(std::move(text.atlasImage), 0); textSizeBinder->upload(context); } if (hasIconData()) { icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices)); icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles)); - icon.atlasTexture = context.createTexture(std::move(icon.atlasImage), 0); iconSizeBinder->upload(context); } diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp index bc9d564aac..002b6e28b3 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.hpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp @@ -46,11 +46,9 @@ public: gl::VertexVector vertices; gl::IndexVector triangles; gl::SegmentVector segments; - AlphaImage atlasImage; optional> vertexBuffer; optional> indexBuffer; - optional atlasTexture; } text; std::unique_ptr iconSizeBinder; @@ -63,7 +61,6 @@ public: optional> vertexBuffer; optional> indexBuffer; - optional atlasTexture; } icon; struct CollisionBoxBuffer { diff --git a/src/mbgl/renderer/image_atlas.cpp b/src/mbgl/renderer/image_atlas.cpp index b53c2162ea..9649187175 100644 --- a/src/mbgl/renderer/image_atlas.cpp +++ b/src/mbgl/renderer/image_atlas.cpp @@ -23,31 +23,17 @@ ImageAtlas makeImageAtlas(const ImageMap& images) { options.autoResize = true; mapbox::ShelfPack pack(0, 0, options); - std::vector pointers; - pointers.reserve(images.size()); - - std::vector bins; - bins.reserve(images.size()); - for (const auto& entry : images) { const style::Image::Impl& image = *entry.second; - pointers.emplace_back(&image); - bins.emplace_back(pointers.size() - 1, - image.image.size.width + 2 * padding, - image.image.size.height + 2 * padding); - } - mapbox::ShelfPack::PackOptions packOptions; - packOptions.inPlace = true; - pack.pack(bins, packOptions); + const mapbox::Bin& bin = *pack.packOne(-1, + image.image.size.width + 2 * padding, + image.image.size.height + 2 * padding); - result.image = PremultipliedImage({ - static_cast(pack.width()), - static_cast(pack.height()) - }); - - for (const auto& bin : bins) { - const style::Image::Impl& image = *pointers.at(bin.id); + result.image.resize({ + static_cast(pack.width()), + static_cast(pack.height()) + }); PremultipliedImage::copy(image.image, result.image, @@ -62,6 +48,12 @@ ImageAtlas makeImageAtlas(const ImageMap& images) { ImagePosition { bin, image }); } +// pack.shrink(); +// result.image.resize({ +// static_cast(pack.width()), +// static_cast(pack.height()) +// }); + return result; } diff --git a/src/mbgl/renderer/image_manager.cpp b/src/mbgl/renderer/image_manager.cpp index be47004b69..d0a106ede6 100644 --- a/src/mbgl/renderer/image_manager.cpp +++ b/src/mbgl/renderer/image_manager.cpp @@ -119,14 +119,7 @@ optional ImageManager::getPattern(const std::string& id) { return {}; } - if (!atlasImage.valid()) { - atlasImage = PremultipliedImage(getPixelSize()); - atlasImage.fill(0); - } else if (atlasImage.size != getPixelSize()) { - PremultipliedImage newImage(getPixelSize()); - PremultipliedImage::copy(atlasImage, newImage, { 0, 0 }, { 0, 0 }, atlasImage.size); - atlasImage = std::move(newImage); - } + atlasImage.resize(getPixelSize()); const PremultipliedImage& src = image->image; diff --git a/src/mbgl/renderer/painters/painter_symbol.cpp b/src/mbgl/renderer/painters/painter_symbol.cpp index 13baa1a514..58700fc4e8 100644 --- a/src/mbgl/renderer/painters/painter_symbol.cpp +++ b/src/mbgl/renderer/painters/painter_symbol.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include @@ -62,6 +62,9 @@ void Painter::renderSymbol(PaintParameters& parameters, ); }; + assert(dynamic_cast(&tile.tile)); + GeometryTile& geometryTile = static_cast(tile.tile); + if (bucket.hasIconData()) { auto values = layer.iconPropertyValues(layout); auto paintPropertyValues = layer.iconPaintProperties(); @@ -69,11 +72,11 @@ void Painter::renderSymbol(PaintParameters& parameters, const bool iconScaled = layout.get().constantOr(1.0) != 1.0 || bucket.iconsNeedLinear; const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || state.getPitch() != 0; - context.bindTexture(*bucket.icon.atlasTexture, 0, + context.bindTexture(*geometryTile.iconAtlasTexture, 0, bucket.sdfIcons || state.isChanging() || iconScaled || iconTransformed ? gl::TextureFilter::Linear : gl::TextureFilter::Nearest); - const Size texsize = bucket.icon.atlasTexture->size; + const Size texsize = geometryTile.iconAtlasTexture->size; if (bucket.sdfIcons) { if (values.hasHalo) { @@ -107,12 +110,12 @@ void Painter::renderSymbol(PaintParameters& parameters, } if (bucket.hasTextData()) { - context.bindTexture(*bucket.text.atlasTexture, 0, gl::TextureFilter::Linear); + context.bindTexture(*geometryTile.glyphAtlasTexture, 0, gl::TextureFilter::Linear); auto values = layer.textPropertyValues(layout); auto paintPropertyValues = layer.textPaintProperties(); - const Size texsize = bucket.text.atlasTexture->size; + const Size texsize = geometryTile.glyphAtlasTexture->size; if (values.hasHalo) { draw(parameters.programs.symbolGlyph, diff --git a/src/mbgl/text/glyph_atlas.cpp b/src/mbgl/text/glyph_atlas.cpp index a08455ec63..6636c23f34 100644 --- a/src/mbgl/text/glyph_atlas.cpp +++ b/src/mbgl/text/glyph_atlas.cpp @@ -6,58 +6,59 @@ namespace mbgl { static constexpr uint32_t padding = 1; -GlyphAtlas makeGlyphAtlas(const Glyphs& glyphs) { +GlyphAtlas makeGlyphAtlas(const GlyphMap& glyphs) { GlyphAtlas result; mapbox::ShelfPack::ShelfPackOptions options; options.autoResize = true; mapbox::ShelfPack pack(0, 0, options); - std::vector bins; - bins.reserve(glyphs.size()); + for (const auto& glyphMapEntry : glyphs) { + const FontStack& fontStack = glyphMapEntry.first; + GlyphPositionMap& positions = result.positions[fontStack]; - for (const auto& entry : glyphs) { - if (entry.second && (*entry.second)->bitmap.valid()) { - const Glyph& glyph = **entry.second; - bins.emplace_back(glyph.id, - glyph.bitmap.size.width + 2 * padding, - glyph.bitmap.size.height + 2 * padding); - } - } - - mapbox::ShelfPack::PackOptions packOptions; - packOptions.inPlace = true; - pack.pack(bins, packOptions); + for (const auto& entry : glyphMapEntry.second) { + if (entry.second && (*entry.second)->bitmap.valid()) { + const Glyph& glyph = **entry.second; - result.image = AlphaImage({ - static_cast(pack.width()), - static_cast(pack.height()) - }); + const mapbox::Bin& bin = *pack.packOne(-1, + glyph.bitmap.size.width + 2 * padding, + glyph.bitmap.size.height + 2 * padding); - for (const auto& bin : bins) { - const Glyph& glyph = **glyphs.at(bin.id); + result.image.resize({ + static_cast(pack.width()), + static_cast(pack.height()) + }); - AlphaImage::copy(glyph.bitmap, - result.image, - { 0, 0 }, - { - bin.x + padding, - bin.y + padding - }, - glyph.bitmap.size); + AlphaImage::copy(glyph.bitmap, + result.image, + { 0, 0 }, + { + bin.x + padding, + bin.y + padding + }, + glyph.bitmap.size); - result.positions.emplace(glyph.id, - GlyphPosition { - Rect { - static_cast(bin.x), - static_cast(bin.y), - static_cast(bin.w), - static_cast(bin.h) - }, - glyph.metrics - }); + positions.emplace(glyph.id, + GlyphPosition { + Rect { + static_cast(bin.x), + static_cast(bin.y), + static_cast(bin.w), + static_cast(bin.h) + }, + glyph.metrics + }); + } + } } +// pack.shrink(); +// result.image.resize({ +// static_cast(pack.width()), +// static_cast(pack.height()) +// }); + return result; } diff --git a/src/mbgl/text/glyph_atlas.hpp b/src/mbgl/text/glyph_atlas.hpp index 7a90085642..bb9115e4b4 100644 --- a/src/mbgl/text/glyph_atlas.hpp +++ b/src/mbgl/text/glyph_atlas.hpp @@ -11,7 +11,8 @@ struct GlyphPosition { GlyphMetrics metrics; }; -using GlyphPositions = std::map; +using GlyphPositionMap = std::map; +using GlyphPositions = std::map; class GlyphAtlas { public: @@ -19,6 +20,6 @@ public: GlyphPositions positions; }; -GlyphAtlas makeGlyphAtlas(const Glyphs&); +GlyphAtlas makeGlyphAtlas(const GlyphMap&); } // namespace mbgl diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp index 81827ca32e..ab10c5a6b7 100644 --- a/src/mbgl/text/quads.cpp +++ b/src/mbgl/text/quads.cpp @@ -307,18 +307,18 @@ SymbolQuads getGlyphQuads(Anchor& anchor, const GeometryCoordinates& line, const SymbolLayoutProperties::Evaluated& layout, const style::SymbolPlacementType placement, - const GlyphPositions& face) { + const GlyphPositionMap& positions) { const float textRotate = layout.get() * util::DEG2RAD; const bool keepUpright = layout.get(); SymbolQuads quads; for (const PositionedGlyph &positionedGlyph: shapedText.positionedGlyphs) { - auto face_it = face.find(positionedGlyph.glyph); - if (face_it == face.end()) + auto positionsIt = positions.find(positionedGlyph.glyph); + if (positionsIt == positions.end()) continue; - const GlyphPosition& glyph = face_it->second; + const GlyphPosition& glyph = positionsIt->second; const Rect& rect = glyph.rect; const float centerX = (positionedGlyph.x + glyph.metrics.advance / 2.0f) * boxScale; diff --git a/src/mbgl/text/quads.hpp b/src/mbgl/text/quads.hpp index 89df423529..b29f6b0ad3 100644 --- a/src/mbgl/text/quads.hpp +++ b/src/mbgl/text/quads.hpp @@ -65,6 +65,6 @@ SymbolQuads getGlyphQuads(Anchor& anchor, const GeometryCoordinates& line, const style::SymbolLayoutProperties::Evaluated&, style::SymbolPlacementType placement, - const GlyphPositions& face); + const GlyphPositionMap& positions); } // namespace mbgl diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index b7561d677c..4ab11d79fe 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include #include @@ -135,6 +137,12 @@ void GeometryTile::onPlacement(PlacementResult result) { } symbolBuckets = std::move(result.symbolBuckets); collisionTile = std::move(result.collisionTile); + if (result.glyphAtlasImage) { + glyphAtlasImage = std::move(*result.glyphAtlasImage); + } + if (result.iconAtlasImage) { + iconAtlasImage = std::move(*result.iconAtlasImage); + } observer->onTileChanged(*this); } @@ -175,6 +183,16 @@ void GeometryTile::upload(gl::Context& context) { for (auto& entry : symbolBuckets) { upload(*entry.second); } + + if (glyphAtlasImage) { + glyphAtlasTexture = context.createTexture(*glyphAtlasImage, 0); + glyphAtlasImage = {}; + } + + if (iconAtlasImage) { + iconAtlasTexture = context.createTexture(*iconAtlasImage, 0); + iconAtlasImage = {}; + } } Bucket* GeometryTile::getBucket(const Layer::Impl& layer) const { diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp index f2510e317b..77202d20b6 100644 --- a/src/mbgl/tile/geometry_tile.hpp +++ b/src/mbgl/tile/geometry_tile.hpp @@ -23,6 +23,8 @@ class RenderStyle; class RenderLayer; class SourceQueryOptions; class TileParameters; +class GlyphAtlas; +class ImageAtlas; class GeometryTile : public Tile, public GlyphRequestor, ImageRequestor { public: @@ -47,6 +49,9 @@ public: void upload(gl::Context&) override; Bucket* getBucket(const style::Layer::Impl&) const override; + Size bindGlyphAtlas(gl::Context&); + Size bindIconAtlas(gl::Context&); + void queryRenderedFeatures( std::unordered_map>& result, const GeometryCoordinates& queryGeometry, @@ -73,6 +78,8 @@ public: public: std::unordered_map> symbolBuckets; std::unique_ptr collisionTile; + optional glyphAtlasImage; + optional iconAtlasImage; uint64_t correlationID; }; void onPlacement(PlacementResult); @@ -106,10 +113,17 @@ private: std::unique_ptr featureIndex; std::unique_ptr data; + optional glyphAtlasImage; + optional iconAtlasImage; + std::unordered_map> symbolBuckets; std::unique_ptr collisionTile; util::Throttler placementThrottler; + +public: + optional glyphAtlasTexture; + optional iconAtlasTexture; }; } // namespace mbgl diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index d67cbca76b..a5a82a7920 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -386,14 +386,24 @@ void GeometryTileWorker::attemptPlacement() { auto collisionTile = std::make_unique(*placementConfig); std::unordered_map> buckets; + optional glyphAtlasImage; + optional iconAtlasImage; + for (auto& symbolLayout : symbolLayouts) { if (obsolete) { return; } if (symbolLayout->state == SymbolLayout::Pending) { - symbolLayout->prepare(glyphMap, imageMap); + GlyphAtlas glyphAtlas = makeGlyphAtlas(glyphMap); + ImageAtlas imageAtlas = makeImageAtlas(imageMap); + + symbolLayout->prepare(glyphMap, glyphAtlas.positions, + imageMap, imageAtlas.positions); symbolLayout->state = SymbolLayout::Placed; + + glyphAtlasImage = std::move(glyphAtlas.image); + iconAtlasImage = std::move(imageAtlas.image); } if (!symbolLayout->hasSymbolInstances()) { @@ -409,6 +419,8 @@ void GeometryTileWorker::attemptPlacement() { parent.invoke(&GeometryTile::onPlacement, GeometryTile::PlacementResult { std::move(buckets), std::move(collisionTile), + std::move(glyphAtlasImage), + std::move(iconAtlasImage), correlationID }); } diff --git a/test/tile/annotation_tile.test.cpp b/test/tile/annotation_tile.test.cpp index 8aa91b1517..707f118fd5 100644 --- a/test/tile/annotation_tile.test.cpp +++ b/test/tile/annotation_tile.test.cpp @@ -55,9 +55,9 @@ TEST(AnnotationTile, Issue8289) { // Simulate layout and placement of a symbol layer. tile.onLayout(GeometryTile::LayoutResult { {}, - std::make_unique(), - std::move(data), - 0 + std::make_unique(), + std::move(data), + 0 }); auto collisionTile = std::make_unique(PlacementConfig()); @@ -69,16 +69,18 @@ TEST(AnnotationTile, Issue8289) { tile.onPlacement(GeometryTile::PlacementResult { {}, - std::move(collisionTile), - 0 + std::move(collisionTile), + {}, + {}, + 0 }); // Simulate a second layout with empty data. tile.onLayout(GeometryTile::LayoutResult { {}, - std::make_unique(), - std::make_unique(), - 0 + std::make_unique(), + std::make_unique(), + 0 }); std::unordered_map> result; diff --git a/test/tile/vector_tile.test.cpp b/test/tile/vector_tile.test.cpp index c427a1a869..f24733dc9b 100644 --- a/test/tile/vector_tile.test.cpp +++ b/test/tile/vector_tile.test.cpp @@ -81,6 +81,8 @@ TEST(VectorTile, Issue7615) { symbolBucket }}, nullptr, + {}, + {}, 0 }); diff --git a/test/util/image.test.cpp b/test/util/image.test.cpp index f4031f1bc1..f4a6473040 100644 --- a/test/util/image.test.cpp +++ b/test/util/image.test.cpp @@ -86,33 +86,49 @@ TEST(Image, WebPTile) { } #endif // !defined(__ANDROID__) && !defined(__APPLE__) && !defined(QT_IMAGE_DECODERS) +TEST(Image, Resize) { + AlphaImage image({0, 0}); + + image.resize({1, 1}); + EXPECT_EQ(image.size, Size({1, 1})); + + image.fill(100); + image.resize({2, 1}); + EXPECT_EQ(image.size, Size({2, 1})); + EXPECT_EQ(image.data[0], 100); + EXPECT_EQ(image.data[1], 0); + + image.resize({0, 0}); + EXPECT_EQ(image.size, Size({0, 0})); +} + TEST(Image, Copy) { PremultipliedImage src5({5, 5}); PremultipliedImage dst5({5, 5}); PremultipliedImage src10({10, 10}); PremultipliedImage dst10({10, 10}); - EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {0, 0}, {0, 0}, {6, 0}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {0, 0}, {0, 0}, {0, 6}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {1, 1}, {0, 0}, {5, 0}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {1, 1}, {0, 0}, {0, 5}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {0, 0}, {0, 0}, {6, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {0, 0}, {0, 0}, {1, 6}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {1, 1}, {0, 0}, {5, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {1, 1}, {0, 0}, {1, 5}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {0, 0}, {6, 0}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {0, 0}, {0, 6}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {1, 1}, {5, 0}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {1, 1}, {0, 5}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {0, 0}, {6, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {0, 0}, {1, 6}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {1, 1}, {5, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {1, 1}, {1, 5}), std::out_of_range); const uint32_t max = std::numeric_limits::max(); - EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {max, 0}, {0, 0}, {1, 0}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, max}, {0, 0}, {0, 1}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {max, 0}, {1, 0}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {0, max}, {0, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {max, 0}, {0, 0}, {1, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, max}, {0, 0}, {1, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {max, 0}, {1, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {0, max}, {1, 1}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {1, 0}, {0, 0}, {max, 0}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 1}, {0, 0}, {0, max}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {1, 0}, {max, 0}), std::out_of_range); - EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {0, 1}, {0, max}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {1, 0}, {0, 0}, {max, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 1}, {0, 0}, {1, max}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {1, 0}, {max, 1}), std::out_of_range); + EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {0, 1}, {1, max}), std::out_of_range); } TEST(Image, Move) { -- cgit v1.2.1