Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[core] Per-tile glyph/icon atlases
Browse files Browse the repository at this point in the history
  • Loading branch information
jfirebaugh committed Jun 9, 2017
1 parent c858aa2 commit f73f451
Show file tree
Hide file tree
Showing 20 changed files with 223 additions and 161 deletions.
17 changes: 17 additions & 0 deletions include/mbgl/util/image.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,27 @@ class Image : private util::noncopyable {
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<uint32_t>& srcPt, const Point<uint32_t>& dstPt, const Size& size) {
if (size.isEmpty()) {
return;
}

if (!srcImg.valid()) {
throw std::invalid_argument("invalid source for image copy");
}
Expand Down
6 changes: 3 additions & 3 deletions src/mbgl/layout/symbol_instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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());
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/mbgl/layout/symbol_instance.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class SymbolInstance {
const float iconBoxScale,
const float iconPadding,
style::SymbolPlacementType iconPlacement,
const GlyphPositions& face,
const GlyphPositionMap&,
const IndexedSubfeature&,
const std::size_t featureIndex);

Expand Down
81 changes: 39 additions & 42 deletions src/mbgl/layout/symbol_layout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -220,12 +221,13 @@ void SymbolLayout::prepare(const GlyphMap& glyphs, const ImageMap& images) {
const bool textAlongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map &&
layout.get<SymbolPlacement>() == SymbolPlacementType::Line;

auto glyphPositionsIt = glyphs.find(layout.get<TextFont>());
if (glyphPositionsIt != glyphs.end()) {
glyphAtlas = makeGlyphAtlas(glyphPositionsIt->second);
}
auto glyphMapIt = glyphMap.find(layout.get<TextFont>());
const Glyphs& glyphs = glyphMapIt != glyphMap.end()
? glyphMapIt->second : Glyphs();

imageAtlas = makeImageAtlas(images);
auto glyphPositionsIt = glyphPositions.find(layout.get<TextFont>());
const GlyphPositionMap& glyphPositionMap = glyphPositionsIt != glyphPositions.end()
? glyphPositionsIt->second : GlyphPositionMap();

for (auto it = features.begin(); it != features.end(); ++it) {
auto& feature = *it;
Expand All @@ -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<TextFont>());
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<SymbolPlacement>() != SymbolPlacementType::Line ?
layout.get<TextMaxWidth>() * oneEm : 0,
/* lineHeight: ems */ layout.get<TextLineHeight>() * oneEm,
/* horizontalAlign */ horizontalAlign,
/* verticalAlign */ verticalAlign,
/* justify */ justify,
/* spacing: ems */ util::i18n::allowsLetterSpacing(*feature.text) ? layout.get<TextLetterSpacing>() * oneEm : 0.0f,
/* translate */ Point<float>(layout.evaluate<TextOffset>(zoom, feature)[0] * oneEm, layout.evaluate<TextOffset>(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<SymbolPlacement>() != SymbolPlacementType::Line ?
layout.get<TextMaxWidth>() * oneEm : 0,
/* lineHeight: ems */ layout.get<TextLineHeight>() * oneEm,
/* horizontalAlign */ horizontalAlign,
/* verticalAlign */ verticalAlign,
/* justify */ justify,
/* spacing: ems */ util::i18n::allowsLetterSpacing(*feature.text) ? layout.get<TextLetterSpacing>() * oneEm : 0.0f,
/* translate */ Point<float>(layout.evaluate<TextOffset>(zoom, feature)[0] * oneEm, layout.evaluate<TextOffset>(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<IconOffset>(zoom, feature),
layout.evaluate<IconRotate>(zoom, feature) * util::DEG2RAD);
if (image->second->sdf) {
Expand All @@ -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();
Expand All @@ -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<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon) {
optional<PositionedIcon> shapedIcon,
const GlyphPositionMap& glyphPositionMap) {
const float minScale = 0.5f;
const float glyphSize = 24.0f;

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -430,9 +430,6 @@ bool SymbolLayout::anchorIsTooClose(const std::u16string& text, const float repe
std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) {
auto bucket = std::make_unique<SymbolBucket>(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.

Expand Down
9 changes: 4 additions & 5 deletions src/mbgl/layout/symbol_layout.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ class SymbolLayout {
ImageDependencies&,
GlyphDependencies&);

void prepare(const GlyphMap& glyphs, const ImageMap& icons);
void prepare(const GlyphMap&, const GlyphPositions&,
const ImageMap&, const ImagePositions&);

std::unique_ptr<SymbolBucket> place(CollisionTile&);

Expand All @@ -53,7 +54,8 @@ class SymbolLayout {
void addFeature(const size_t,
const SymbolFeature&,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon);
optional<PositionedIcon> shapedIcon,
const GlyphPositionMap&);

bool anchorIsTooClose(const std::u16string& text, const float repeatDistance, const Anchor&);
std::map<std::u16string, std::vector<Anchor>> compareText;
Expand Down Expand Up @@ -93,9 +95,6 @@ class SymbolLayout {
std::vector<SymbolInstance> symbolInstances;
std::vector<SymbolFeature> 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
};

Expand Down
2 changes: 0 additions & 2 deletions src/mbgl/renderer/buckets/symbol_bucket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
3 changes: 0 additions & 3 deletions src/mbgl/renderer/buckets/symbol_bucket.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,9 @@ class SymbolBucket : public Bucket {
gl::VertexVector<SymbolLayoutVertex> vertices;
gl::IndexVector<gl::Triangles> triangles;
gl::SegmentVector<SymbolTextAttributes> segments;
AlphaImage atlasImage;

optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer;
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
optional<gl::Texture> atlasTexture;
} text;

std::unique_ptr<SymbolSizeBinder> iconSizeBinder;
Expand All @@ -63,7 +61,6 @@ class SymbolBucket : public Bucket {

optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer;
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
optional<gl::Texture> atlasTexture;
} icon;

struct CollisionBoxBuffer {
Expand Down
34 changes: 13 additions & 21 deletions src/mbgl/renderer/image_atlas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,17 @@ ImageAtlas makeImageAtlas(const ImageMap& images) {
options.autoResize = true;
mapbox::ShelfPack pack(0, 0, options);

std::vector<const style::Image::Impl*> pointers;
pointers.reserve(images.size());

std::vector<mapbox::Bin> 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<uint32_t>(pack.width()),
static_cast<uint32_t>(pack.height())
});

for (const auto& bin : bins) {
const style::Image::Impl& image = *pointers.at(bin.id);
result.image.resize({
static_cast<uint32_t>(pack.width()),
static_cast<uint32_t>(pack.height())
});

PremultipliedImage::copy(image.image,
result.image,
Expand All @@ -62,6 +48,12 @@ ImageAtlas makeImageAtlas(const ImageMap& images) {
ImagePosition { bin, image });
}

// pack.shrink();
// result.image.resize({
// static_cast<uint32_t>(pack.width()),
// static_cast<uint32_t>(pack.height())
// });

return result;
}

Expand Down
9 changes: 1 addition & 8 deletions src/mbgl/renderer/image_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,7 @@ optional<ImagePosition> 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;

Expand Down
13 changes: 8 additions & 5 deletions src/mbgl/renderer/painters/painter_symbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include <mbgl/programs/symbol_program.hpp>
#include <mbgl/programs/collision_box_program.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/tile/tile.hpp>
#include <mbgl/tile/geometry_tile.hpp>

#include <cmath>

Expand Down Expand Up @@ -62,18 +62,21 @@ void Painter::renderSymbol(PaintParameters& parameters,
);
};

assert(dynamic_cast<GeometryTile*>(&tile.tile));
GeometryTile& geometryTile = static_cast<GeometryTile&>(tile.tile);

if (bucket.hasIconData()) {
auto values = layer.iconPropertyValues(layout);
auto paintPropertyValues = layer.iconPaintProperties();

const bool iconScaled = layout.get<IconSize>().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) {
Expand Down Expand Up @@ -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,
Expand Down
Loading

0 comments on commit f73f451

Please sign in to comment.