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

[core] Introduce descender, ascender in glyphMetrics + Fix fonts dis-alignment #15676

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
22 changes: 12 additions & 10 deletions src/mbgl/text/glyph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,16 @@ using GlyphIDs = std::set<GlyphID>;
GlyphRange getGlyphRange(GlyphID glyph);

struct GlyphMetrics {
uint32_t width = 0;
uint32_t height = 0;
uint32_t width = 0U;
uint32_t height = 0U;
int32_t left = 0;
int32_t top = 0;
uint32_t advance = 0;
uint32_t advance = 0U;
};

inline bool operator==(const GlyphMetrics& lhs, const GlyphMetrics& rhs) {
return lhs.width == rhs.width &&
lhs.height == rhs.height &&
lhs.left == rhs.left &&
lhs.top == rhs.top &&
lhs.advance == rhs.advance;
return lhs.width == rhs.width && lhs.height == rhs.height && lhs.left == rhs.left && lhs.top == rhs.top &&
lhs.advance == rhs.advance;
}

class Glyph {
Expand All @@ -55,7 +52,11 @@ class Glyph {
GlyphMetrics metrics;
};

using Glyphs = std::map<GlyphID, optional<Immutable<Glyph>>>;
struct Glyphs {
std::map<GlyphID, optional<Immutable<Glyph>>> glyphs{};
optional<int32_t> ascender{nullopt};
optional<int32_t> descender{nullopt};
};
using GlyphMap = std::map<FontStackHash, Glyphs>;

class PositionedGlyph {
Expand All @@ -82,7 +83,7 @@ class Shaping {
Shaping() = default;
explicit Shaping(float x, float y, WritingModeType writingMode_, std::size_t lineCount_)
: top(y), bottom(y), left(x), right(x), writingMode(writingMode_), lineCount(lineCount_) {}
std::vector<PositionedGlyph> positionedGlyphs;
std::unordered_map<uint32_t, std::vector<PositionedGlyph>> positionedGlyphs;
float top = 0;
float bottom = 0;
float left = 0;
Expand All @@ -92,6 +93,7 @@ class Shaping {
explicit operator bool() const { return !positionedGlyphs.empty(); }
// The y offset *should* be part of the font metadata.
static constexpr int32_t yOffset = -17;
bool hasBaseline{false};
};

enum class WritingModeType : uint8_t {
Expand Down
23 changes: 10 additions & 13 deletions src/mbgl/text/glyph_atlas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ GlyphAtlas makeGlyphAtlas(const GlyphMap& glyphs) {

for (const auto& glyphMapEntry : glyphs) {
FontStackHash fontStack = glyphMapEntry.first;
GlyphPositionMap& positions = result.positions[fontStack];

for (const auto& entry : glyphMapEntry.second) {
GlyphPositionData& positions = result.positions[fontStack];
positions.ascender = glyphMapEntry.second.ascender;
positions.descender = glyphMapEntry.second.descender;
for (const auto& entry : glyphMapEntry.second.glyphs) {
if (entry.second && (*entry.second)->bitmap.valid()) {
const Glyph& glyph = **entry.second;

Expand All @@ -39,16 +40,12 @@ GlyphAtlas makeGlyphAtlas(const GlyphMap& glyphs) {
},
glyph.bitmap.size);

positions.emplace(glyph.id,
GlyphPosition {
Rect<uint16_t> {
static_cast<uint16_t>(bin.x),
static_cast<uint16_t>(bin.y),
static_cast<uint16_t>(bin.w),
static_cast<uint16_t>(bin.h)
},
glyph.metrics
});
positions.glyphPositionMap.emplace(glyph.id,
GlyphPosition{Rect<uint16_t>{static_cast<uint16_t>(bin.x),
static_cast<uint16_t>(bin.y),
static_cast<uint16_t>(bin.w),
static_cast<uint16_t>(bin.h)},
glyph.metrics});
}
}
}
Expand Down
9 changes: 7 additions & 2 deletions src/mbgl/text/glyph_atlas.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ struct GlyphPosition {
GlyphMetrics metrics;
};

using GlyphPositionMap = std::map<GlyphID, GlyphPosition>;
using GlyphPositions = std::map<FontStackHash, GlyphPositionMap>;
struct GlyphPositionData {
std::map<GlyphID, GlyphPosition> glyphPositionMap{};
optional<int32_t> ascender{nullopt};
optional<int32_t> descender{nullopt};
};

using GlyphPositions = std::map<FontStackHash, GlyphPositionData>;

class GlyphAtlas {
public:
Expand Down
16 changes: 11 additions & 5 deletions src/mbgl/text/glyph_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ void GlyphManager::processResponse(const Response& res, const FontStack& fontSta

if (!res.noContent) {
std::vector<Glyph> glyphs;

int32_t ascender{0}, descender{0};
try {
glyphs = parseGlyphPBF(range, *res.data);
std::tie(glyphs, ascender, descender) = parseGlyphPBF(range, *res.data);
} catch (...) {
observer->onGlyphsError(fontStack, range, std::current_exception());
return;
Expand All @@ -104,6 +104,10 @@ void GlyphManager::processResponse(const Response& res, const FontStack& fontSta
entry.glyphs.emplace(id, makeMutable<Glyph>(std::move(glyph)));
}
}
if (ascender != 0 || descender != 0) {
entry.ascender = ascender;
entry.descender = descender;
}
}

request.parsed = true;
Expand Down Expand Up @@ -134,18 +138,20 @@ void GlyphManager::notify(GlyphRequestor& requestor, const GlyphDependencies& gl

Glyphs& glyphs = response[FontStackHasher()(fontStack)];
Entry& entry = entries[fontStack];
glyphs.ascender = entry.ascender;
glyphs.descender = entry.descender;

for (const auto& glyphID : glyphIDs) {
auto it = entry.glyphs.find(glyphID);
if (it != entry.glyphs.end()) {
glyphs.emplace(*it);
glyphs.glyphs.emplace(*it);
} else {
glyphs.emplace(glyphID, std::experimental::nullopt);
glyphs.glyphs.emplace(glyphID, nullopt);
}
}
}

requestor.onGlyphsAvailable(response);
requestor.onGlyphsAvailable(std::move(response));
}

void GlyphManager::removeRequestor(GlyphRequestor& requestor) {
Expand Down
2 changes: 2 additions & 0 deletions src/mbgl/text/glyph_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ class GlyphManager {
struct Entry {
std::map<GlyphRange, GlyphRequest> ranges;
std::map<GlyphID, Immutable<Glyph>> glyphs;
optional<int32_t> ascender;
optional<int32_t> descender;
};

std::unordered_map<FontStack, Entry, FontStackHasher> entries;
Expand Down
132 changes: 83 additions & 49 deletions src/mbgl/text/glyph_pbf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@

namespace mbgl {

std::vector<Glyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::string& data) {
std::vector<Glyph> result;
result.reserve(256);
std::tuple<std::vector<Glyph>, int32_t, int32_t> parseGlyphPBF(const GlyphRange& glyphRange, const std::string& data) {
std::vector<Glyph> glyphs;
glyphs.reserve(256);
int32_t ascender{0}, descender{0};
bool ascenderSet{false}, descenderSet{false};

protozero::pbf_reader glyphs_pbf(data);

while (glyphs_pbf.next(1)) {
auto fontstack_pbf = glyphs_pbf.get_message();
while (fontstack_pbf.next(3)) {
auto readGlyphMetrics = [glyphRange, &glyphs](protozero::pbf_reader& fontstack_pbf) {
auto glyph_pbf = fontstack_pbf.get_message();

Glyph glyph;
protozero::data_view glyphData;

Expand All @@ -23,72 +23,106 @@ std::vector<Glyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::string

while (glyph_pbf.next()) {
switch (glyph_pbf.tag()) {
case 1: // id
glyph.id = glyph_pbf.get_uint32();
hasID = true;
break;
case 2: // bitmap
glyphData = glyph_pbf.get_view();
break;
case 3: // width
glyph.metrics.width = glyph_pbf.get_uint32();
hasWidth = true;
break;
case 4: // height
glyph.metrics.height = glyph_pbf.get_uint32();
hasHeight = true;
break;
case 5: // left
glyph.metrics.left = glyph_pbf.get_sint32();
hasLeft = true;
break;
case 6: // top
glyph.metrics.top = glyph_pbf.get_sint32();
hasTop = true;
break;
case 7: // advance
glyph.metrics.advance = glyph_pbf.get_uint32();
hasAdvance = true;
break;
default:
glyph_pbf.skip();
break;
case 1: // id
glyph.id = glyph_pbf.get_uint32();
hasID = true;
break;
case 2: // bitmap
glyphData = glyph_pbf.get_view();
break;
case 3: // width
glyph.metrics.width = glyph_pbf.get_uint32();
hasWidth = true;
break;
case 4: // height
glyph.metrics.height = glyph_pbf.get_uint32();
hasHeight = true;
break;
case 5: // left
glyph.metrics.left = glyph_pbf.get_sint32();
hasLeft = true;
break;
case 6: // top
glyph.metrics.top = glyph_pbf.get_sint32();
hasTop = true;
break;
case 7: // advance
glyph.metrics.advance = glyph_pbf.get_uint32();
hasAdvance = true;
break;
default:
glyph_pbf.skip();
break;
}
}

// 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 ||
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;
return;
}

// 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.
if (glyph.metrics.width && glyph.metrics.height) {
const Size size {
glyph.metrics.width + 2 * Glyph::borderSize,
glyph.metrics.height + 2 * Glyph::borderSize
};
const Size size{glyph.metrics.width + 2 * Glyph::borderSize,
glyph.metrics.height + 2 * Glyph::borderSize};

if (size.area() != glyphData.size()) {
continue;
return;
}

glyph.bitmap = AlphaImage(size, reinterpret_cast<const uint8_t*>(glyphData.data()), glyphData.size());
}

result.push_back(std::move(glyph));
glyphs.push_back(std::move(glyph));
};

auto fontstack_pbf = glyphs_pbf.get_message();
while (fontstack_pbf.next()) {
zmiao marked this conversation as resolved.
Show resolved Hide resolved
switch (fontstack_pbf.tag()) {
case 3: {
readGlyphMetrics(fontstack_pbf);
break;
}
case 4: {
// ascender value for one fontstack shall keep the same, if different values appear, set ascender to
// be 0/invalid.
const auto value = fontstack_pbf.get_sint32();
if (!ascenderSet) {
ascender = value;
ascenderSet = true;
} else if (ascender != value) {
ascender = 0;
}
break;
}
case 5: {
// descender value for one fontstack shall keep the same, if different values appear, set descender
// to be 0/invalid.
const auto value = fontstack_pbf.get_sint32();
if (!descenderSet) {
descender = value;
descenderSet = true;
} else if (descender != value) {
descender = 0;
}
break;
}
default: {
fontstack_pbf.skip();
break;
}
}
}
}

return result;
return std::make_tuple(std::move(glyphs), ascender, descender);
}

} // namespace mbgl
3 changes: 2 additions & 1 deletion src/mbgl/text/glyph_pbf.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
#include <mbgl/text/glyph_range.hpp>

#include <string>
#include <tuple>
#include <vector>

namespace mbgl {

std::vector<Glyph> parseGlyphPBF(const GlyphRange&, const std::string& data);
std::tuple<std::vector<Glyph>, int32_t, int32_t> parseGlyphPBF(const GlyphRange&, const std::string& data);

} // namespace mbgl
Loading