Files
MapLibreParticleLab/include/mln/text/glyph.hpp
Bart Louwers 2a8ebc4906 core: rename mbgl directories to mln (#4511)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-08-22 20:50:14 +02:00

222 lines
6.1 KiB
C++

#pragma once
#include <mln/text/glyph_range.hpp>
#include <mln/util/bitmask_operations.hpp>
#include <mln/util/font_stack.hpp>
#include <mln/util/rect.hpp>
#include <mln/util/traits.hpp>
#include <mln/util/immutable.hpp>
#include <mln/util/image.hpp>
#include <mln/util/util.hpp>
#include <algorithm>
#include <cstdint>
#include <vector>
#include <string>
#include <map>
#include <set>
namespace mln {
union GlyphID {
char32_t hash;
struct {
char16_t code;
GlyphIDType type;
} complex;
GlyphID(int codepoint) {
complex.type = FontPBF;
complex.code = codepoint;
}
GlyphID(uint32_t codepoint) {
complex.type = FontPBF;
complex.code = codepoint;
}
GlyphID(char16_t codepoint) {
complex.type = FontPBF;
complex.code = codepoint;
}
GlyphID(char16_t index, GlyphIDType t) {
complex.type = t;
complex.code = index;
}
operator char16_t() { return complex.code; }
operator char32_t() { return hash; }
bool operator<(const GlyphID &other) const { return hash < other.hash; }
bool operator>(const GlyphID &other) const { return hash > other.hash; }
bool operator<(const uint16_t &other) const { return hash < other; }
bool operator>(const uint16_t &other) const { return hash > other; }
};
using GlyphIDs = std::set<GlyphID>;
// Note: this only works for the BMP
GlyphRange getGlyphRange(GlyphID glyph);
struct GlyphMetrics {
uint32_t width = 0;
uint32_t height = 0;
float left = 0;
float top = 0;
uint32_t advance = 0;
// True when the glyph bitmap is rendered at 2x texture resolution
// (e.g. local CJK glyphs).
bool isDoubleResolution = false;
};
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 && lhs.isDoubleResolution == rhs.isDoubleResolution;
}
class Glyph {
public:
// We're using this value throughout the Mapbox GL ecosystem. If this is
// different, the glyphs also need to be reencoded.
static constexpr const uint8_t borderSize = 3;
GlyphID id = 0;
// A signed distance field of the glyph with a border (see above).
AlphaImage bitmap;
// Glyph metrics
GlyphMetrics metrics;
};
using Glyphs = std::map<GlyphID, std::optional<Immutable<Glyph>>>;
using GlyphMap = std::map<FontStackHash, Glyphs>;
class PositionedGlyph {
public:
explicit PositionedGlyph(GlyphID glyph_,
float x_,
float y_,
bool vertical_,
FontStackHash font_,
float scale_,
Rect<uint16_t> rect_,
GlyphMetrics metrics_,
std::optional<std::string> imageID_,
std::size_t sectionIndex_ = 0)
: glyph(glyph_),
x(x_),
y(y_),
vertical(vertical_),
font(font_),
scale(scale_),
rect(rect_),
metrics(metrics_),
imageID(std::move(imageID_)),
sectionIndex(sectionIndex_) {}
GlyphID glyph = 0;
float x = 0;
float y = 0;
bool vertical = false;
FontStackHash font = 0;
float scale = 0.0;
Rect<uint16_t> rect;
GlyphMetrics metrics;
std::optional<std::string> imageID;
// Maps positioned glyph to TaggedString section
std::size_t sectionIndex;
};
enum class WritingModeType : uint8_t;
struct PositionedLine {
std::vector<PositionedGlyph> positionedGlyphs;
float lineOffset = 0.0;
};
class Shaping {
public:
Shaping() = default;
explicit Shaping(float x, float y, WritingModeType writingMode_)
: top(y),
bottom(y),
left(x),
right(x),
writingMode(writingMode_) {}
std::vector<PositionedLine> positionedLines;
float top = 0;
float bottom = 0;
float left = 0;
float right = 0;
WritingModeType writingMode;
explicit operator bool() const {
return std::ranges::any_of(positionedLines, [](const auto &line) { return !line.positionedGlyphs.empty(); });
}
// The y offset *should* be part of the font metadata.
static constexpr int32_t yOffset = -17;
bool verticalizable = false;
bool iconsInText = false;
};
enum class WritingModeType : uint8_t {
None = 0,
Horizontal = 1 << 0,
Vertical = 1 << 1,
};
// style defined faces
struct FontFace {
using Range = std::pair<uint32_t, uint32_t>;
GlyphIDType type; // an unique glyph id
std::string name; // font face name
std::string url; // font file url
std::vector<Range> ranges; // unicode ranges
FontFace() = default;
FontFace(const std::string &name_, const std::string &url_, const std::vector<Range> &ranges_)
: type(FontPBF),
name(name_),
url(url_),
ranges(ranges_) {}
FontFace(const std::string &name_, const std::string &url_, std::vector<Range> &&ranges_)
: type(FontPBF),
name(name_),
url(url_),
ranges(std::move(ranges_)) {}
auto valid() const -> bool { return !name.empty() && !url.empty() && !ranges.empty(); }
};
using FontFaces = std::vector<FontFace>;
struct HBShapeRequest {
std::u16string str;
FontStack fontStack;
GlyphIDType type;
HBShapeRequest(const std::u16string &str_, const FontStack &fontStack_, GlyphIDType type_)
: str(str_),
fontStack(fontStack_),
type(type_) {}
};
using HBShapeRequests = std::map<FontStack, std::map<GlyphIDType, std::set<std::u16string>>>;
struct GlyphDependencies {
std::map<FontStack, GlyphIDs> glyphs;
HBShapeRequests shapes;
};
using GlyphRangeDependencies = std::map<FontStack, std::unordered_set<GlyphRange>>;
struct GlyphPosition {
Rect<uint16_t> rect;
GlyphMetrics metrics;
};
using GlyphPositionMap = std::map<GlyphID, GlyphPosition>;
using GlyphPositions = std::map<FontStackHash, GlyphPositionMap>;
} // end namespace mln