[core] Shape images in labels and create image quads
This commit is contained in:
@@ -23,8 +23,7 @@ public:
|
||||
const bool,
|
||||
const bool) = 0;
|
||||
|
||||
virtual void prepareSymbols(const GlyphMap&, const GlyphPositions&,
|
||||
const ImageMap&, const ImagePositions&) {};
|
||||
virtual void prepareSymbols(const GlyphMap&, const GlyphPositions&, const ImageMap&, const ImagePositions&){};
|
||||
|
||||
virtual bool hasSymbolInstances() const {
|
||||
return true;
|
||||
|
||||
@@ -25,8 +25,9 @@ SymbolInstanceSharedData::SymbolInstanceSharedData(GeometryCoordinates line_,
|
||||
const style::SymbolLayoutProperties::Evaluated& layout,
|
||||
const style::SymbolPlacementType textPlacement,
|
||||
const std::array<float, 2>& textOffset,
|
||||
const GlyphPositions& positions,
|
||||
bool allowVerticalPlacement) : line(std::move(line_)) {
|
||||
const ImageMap& imageMap,
|
||||
bool allowVerticalPlacement)
|
||||
: line(std::move(line_)) {
|
||||
// Create the quads used for rendering the icon and glyphs.
|
||||
if (shapedIcon) {
|
||||
iconQuad = getIconQuad(*shapedIcon, getAnyShaping(shapedTextOrientations).writingMode);
|
||||
@@ -38,11 +39,12 @@ SymbolInstanceSharedData::SymbolInstanceSharedData(GeometryCoordinates line_,
|
||||
bool singleLineInitialized = false;
|
||||
const auto initHorizontalGlyphQuads = [&] (SymbolQuads& quads, const Shaping& shaping) {
|
||||
if (!shapedTextOrientations.singleLine) {
|
||||
quads = getGlyphQuads(shaping, textOffset, layout, textPlacement, positions, allowVerticalPlacement);
|
||||
quads = getGlyphQuads(shaping, textOffset, layout, textPlacement, imageMap, allowVerticalPlacement);
|
||||
return;
|
||||
}
|
||||
if (!singleLineInitialized) {
|
||||
rightJustifiedGlyphQuads = getGlyphQuads(shaping, textOffset, layout, textPlacement, positions, allowVerticalPlacement);
|
||||
rightJustifiedGlyphQuads =
|
||||
getGlyphQuads(shaping, textOffset, layout, textPlacement, imageMap, allowVerticalPlacement);
|
||||
singleLineInitialized = true;
|
||||
}
|
||||
};
|
||||
@@ -60,7 +62,8 @@ SymbolInstanceSharedData::SymbolInstanceSharedData(GeometryCoordinates line_,
|
||||
}
|
||||
|
||||
if (shapedTextOrientations.vertical) {
|
||||
verticalGlyphQuads = getGlyphQuads(shapedTextOrientations.vertical, textOffset, layout, textPlacement, positions, allowVerticalPlacement);
|
||||
verticalGlyphQuads = getGlyphQuads(
|
||||
shapedTextOrientations.vertical, textOffset, layout, textPlacement, imageMap, allowVerticalPlacement);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,14 +23,14 @@ struct ShapedTextOrientations {
|
||||
|
||||
struct SymbolInstanceSharedData {
|
||||
SymbolInstanceSharedData(GeometryCoordinates line,
|
||||
const ShapedTextOrientations& shapedTextOrientations,
|
||||
const optional<PositionedIcon>& shapedIcon,
|
||||
const optional<PositionedIcon>& verticallyShapedIcon,
|
||||
const style::SymbolLayoutProperties::Evaluated& layout,
|
||||
const style::SymbolPlacementType textPlacement,
|
||||
const std::array<float, 2>& textOffset,
|
||||
const GlyphPositions& positions,
|
||||
bool allowVerticalPlacement);
|
||||
const ShapedTextOrientations& shapedTextOrientations,
|
||||
const optional<PositionedIcon>& shapedIcon,
|
||||
const optional<PositionedIcon>& verticallyShapedIcon,
|
||||
const style::SymbolLayoutProperties::Evaluated& layout,
|
||||
const style::SymbolPlacementType textPlacement,
|
||||
const std::array<float, 2>& textOffset,
|
||||
const ImageMap& imageMap,
|
||||
bool allowVerticalPlacement);
|
||||
bool empty() const;
|
||||
GeometryCoordinates line;
|
||||
// Note: When singleLine == true, only `rightJustifiedGlyphQuads` is populated.
|
||||
|
||||
@@ -340,8 +340,10 @@ std::array<float, 2> SymbolLayout::evaluateVariableOffset(style::SymbolAnchorTyp
|
||||
return result;
|
||||
}
|
||||
|
||||
void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions& glyphPositions,
|
||||
const ImageMap& imageMap, const ImagePositions& imagePositions) {
|
||||
void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap,
|
||||
const GlyphPositions& glyphPositions,
|
||||
const ImageMap& imageMap,
|
||||
const ImagePositions& imagePositions) {
|
||||
const bool isPointPlacement = layout->get<SymbolPlacement>() == SymbolPlacementType::Point;
|
||||
const bool textAlongLine = layout->get<TextRotationAlignment>() == AlignmentType::Map && !isPointPlacement;
|
||||
|
||||
@@ -375,7 +377,8 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions
|
||||
/* translate */ textOffset,
|
||||
/* writingMode */ writingMode,
|
||||
/* bidirectional algorithm object */ bidi,
|
||||
/* glyphs */ glyphMap,
|
||||
glyphMap,
|
||||
/* glyphs */ glyphPositions,
|
||||
/* images */ imagePositions,
|
||||
layoutTextSize,
|
||||
allowVerticalPlacement);
|
||||
@@ -430,7 +433,7 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions
|
||||
Shaping shaping = applyShaping(*feature.formattedText, WritingModeType::Horizontal, SymbolAnchorType::Center, justification);
|
||||
if (shaping) {
|
||||
shapingForJustification = std::move(shaping);
|
||||
if (shapingForJustification.lineCount == 1u) {
|
||||
if (shapingForJustification.positionedLines.size() == 1u) {
|
||||
shapedTextOrientations.singleLine = true;
|
||||
break;
|
||||
}
|
||||
@@ -488,7 +491,7 @@ void SymbolLayout::prepareSymbols(const GlyphMap& glyphMap, const GlyphPositions
|
||||
feature,
|
||||
shapedTextOrientations,
|
||||
std::move(shapedIcon),
|
||||
glyphPositions,
|
||||
imageMap,
|
||||
textOffset,
|
||||
layoutTextSize,
|
||||
layoutIconSize,
|
||||
@@ -505,7 +508,7 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
|
||||
const SymbolFeature& feature,
|
||||
const ShapedTextOrientations& shapedTextOrientations,
|
||||
optional<PositionedIcon> shapedIcon,
|
||||
const GlyphPositions& glyphPositions,
|
||||
const ImageMap& imageMap,
|
||||
std::array<float, 2> textOffset,
|
||||
float layoutTextSize,
|
||||
float layoutIconSize,
|
||||
@@ -585,10 +588,16 @@ void SymbolLayout::addFeature(const std::size_t layoutFeatureIndex,
|
||||
}
|
||||
};
|
||||
|
||||
const auto createSymbolInstanceSharedData = [&] (GeometryCoordinates line) {
|
||||
const auto createSymbolInstanceSharedData = [&](GeometryCoordinates line) {
|
||||
return std::make_shared<SymbolInstanceSharedData>(std::move(line),
|
||||
shapedTextOrientations, shapedIcon, verticallyShapedIcon, evaluatedLayoutProperties,
|
||||
textPlacement, textOffset, glyphPositions, allowVerticalPlacement);
|
||||
shapedTextOrientations,
|
||||
shapedIcon,
|
||||
verticallyShapedIcon,
|
||||
evaluatedLayoutProperties,
|
||||
textPlacement,
|
||||
textOffset,
|
||||
imageMap,
|
||||
allowVerticalPlacement);
|
||||
};
|
||||
|
||||
const auto& type = feature.getType();
|
||||
|
||||
@@ -31,8 +31,10 @@ public:
|
||||
|
||||
~SymbolLayout() final = default;
|
||||
|
||||
void prepareSymbols(const GlyphMap&, const GlyphPositions&,
|
||||
const ImageMap&, const ImagePositions&) override;
|
||||
void prepareSymbols(const GlyphMap& glyphMap,
|
||||
const GlyphPositions&,
|
||||
const ImageMap&,
|
||||
const ImagePositions&) override;
|
||||
|
||||
void createBucket(const ImagePositions&, std::unique_ptr<FeatureIndex>&, std::unordered_map<std::string, LayerRenderData>&, const bool firstLoad, const bool showCollisionBoxes) override;
|
||||
|
||||
@@ -60,7 +62,7 @@ private:
|
||||
const SymbolFeature&,
|
||||
const ShapedTextOrientations& shapedTextOrientations,
|
||||
optional<PositionedIcon> shapedIcon,
|
||||
const GlyphPositions&,
|
||||
const ImageMap&,
|
||||
std::array<float, 2> textOffset,
|
||||
float layoutTextSize,
|
||||
float layoutIconSize,
|
||||
|
||||
@@ -20,6 +20,7 @@ class ImagePosition {
|
||||
public:
|
||||
ImagePosition(const mapbox::Bin&, const style::Image::Impl&, uint32_t version = 0);
|
||||
|
||||
static constexpr const uint16_t padding = 1u;
|
||||
float pixelRatio;
|
||||
Rect<uint16_t> textureRect;
|
||||
uint32_t version;
|
||||
@@ -50,6 +51,13 @@ public:
|
||||
textureRect.h / pixelRatio,
|
||||
}};
|
||||
}
|
||||
|
||||
Rect<uint16_t> paddedTextureRect() const {
|
||||
return {static_cast<uint16_t>(textureRect.x - padding),
|
||||
static_cast<uint16_t>(textureRect.y - padding),
|
||||
static_cast<uint16_t>(textureRect.w + padding * 2),
|
||||
static_cast<uint16_t>(textureRect.h + padding * 2)};
|
||||
}
|
||||
};
|
||||
|
||||
using ImagePositions = std::map<std::string, ImagePosition>;
|
||||
|
||||
@@ -60,38 +60,66 @@ using GlyphMap = std::map<FontStackHash, Glyphs>;
|
||||
|
||||
class PositionedGlyph {
|
||||
public:
|
||||
explicit PositionedGlyph(GlyphID glyph_, float x_, float y_, bool vertical_, FontStackHash font_, float scale_, std::size_t sectionIndex_ = 0)
|
||||
: glyph(glyph_), x(x_), y(y_), vertical(vertical_), font(font_), scale(scale_), sectionIndex(sectionIndex_)
|
||||
{}
|
||||
explicit PositionedGlyph(GlyphID glyph_,
|
||||
float x_,
|
||||
float y_,
|
||||
bool vertical_,
|
||||
FontStackHash font_,
|
||||
float scale_,
|
||||
Rect<uint16_t> rect_,
|
||||
GlyphMetrics metrics_,
|
||||
optional<std::string> imageID_,
|
||||
std::size_t sectionIndex_ = 0)
|
||||
: glyph(glyph_),
|
||||
x(x_),
|
||||
y(y_),
|
||||
vertical(vertical_),
|
||||
font(font_),
|
||||
scale(scale_),
|
||||
rect(std::move(rect_)),
|
||||
metrics(std::move(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;
|
||||
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_, std::size_t lineCount_)
|
||||
: top(y), bottom(y), left(x), right(x), writingMode(writingMode_), lineCount(lineCount_) {}
|
||||
std::vector<PositionedGlyph> positionedGlyphs;
|
||||
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;
|
||||
std::size_t lineCount = 0u;
|
||||
explicit operator bool() const { return !positionedGlyphs.empty(); }
|
||||
explicit operator bool() const {
|
||||
return std::any_of(positionedLines.begin(), positionedLines.end(), [](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;
|
||||
};
|
||||
|
||||
enum class WritingModeType : uint8_t {
|
||||
|
||||
@@ -20,7 +20,7 @@ SymbolQuad getIconQuad(const PositionedIcon& shapedIcon,
|
||||
// If you have a 10px icon that isn't perfectly aligned to the pixel grid it will cover 11 actual
|
||||
// pixels. The quad needs to be padded to account for this, otherwise they'll look slightly clipped
|
||||
// on one edge in some cases.
|
||||
constexpr const float border = 1.0f;
|
||||
const float border = ImagePosition::padding;
|
||||
|
||||
// Expand the box to respect the 1 pixel border in the atlas image. We're using `image.paddedRect - border`
|
||||
// instead of image.displaySize because we only pad with one pixel for retina images as well, and the
|
||||
@@ -77,96 +77,110 @@ SymbolQuads getGlyphQuads(const Shaping& shapedText,
|
||||
const std::array<float, 2> textOffset,
|
||||
const SymbolLayoutProperties::Evaluated& layout,
|
||||
const style::SymbolPlacementType placement,
|
||||
const GlyphPositions& positions,
|
||||
const ImageMap& imageMap,
|
||||
bool allowVerticalPlacement) {
|
||||
const float textRotate = layout.get<TextRotate>() * util::DEG2RAD;
|
||||
const bool alongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map && placement != SymbolPlacementType::Point;
|
||||
|
||||
SymbolQuads quads;
|
||||
|
||||
for (const PositionedGlyph &positionedGlyph: shapedText.positionedGlyphs) {
|
||||
auto fontPositions = positions.find(positionedGlyph.font);
|
||||
if (fontPositions == positions.end())
|
||||
continue;
|
||||
|
||||
auto positionsIt = fontPositions->second.find(positionedGlyph.glyph);
|
||||
if (positionsIt == fontPositions->second.end())
|
||||
continue;
|
||||
for (const auto& line : shapedText.positionedLines) {
|
||||
for (const auto& positionedGlyph : line.positionedGlyphs) {
|
||||
if (!positionedGlyph.rect.hasArea()) continue;
|
||||
|
||||
const GlyphPosition& glyph = positionsIt->second;
|
||||
const Rect<uint16_t>& rect = glyph.rect;
|
||||
// The rects have an addditional buffer that is not included in their size;
|
||||
const float glyphPadding = 1.0f;
|
||||
float rectBuffer = 3.0f + glyphPadding;
|
||||
float pixelRatio = 1.0f;
|
||||
float lineOffset = 0.0f;
|
||||
const bool rotateVerticalGlyph = (alongLine || allowVerticalPlacement) && positionedGlyph.vertical;
|
||||
const float halfAdvance = positionedGlyph.metrics.advance * positionedGlyph.scale / 2.0;
|
||||
const Rect<uint16_t>& rect = positionedGlyph.rect;
|
||||
|
||||
// The rects have an addditional buffer that is not included in their size;
|
||||
const float glyphPadding = 1.0f;
|
||||
const float rectBuffer = 3.0f + glyphPadding;
|
||||
// Align images and scaled glyphs in the middle of a vertical line.
|
||||
if (allowVerticalPlacement && shapedText.verticalizable) {
|
||||
const float scaledGlyphOffset = (positionedGlyph.scale - 1) * util::ONE_EM;
|
||||
const float imageOffset = (util::ONE_EM - positionedGlyph.metrics.width * positionedGlyph.scale) / 2.0f;
|
||||
lineOffset = line.lineOffset / 2.0f - (positionedGlyph.imageID ? -imageOffset : scaledGlyphOffset);
|
||||
}
|
||||
|
||||
const float halfAdvance = glyph.metrics.advance * positionedGlyph.scale / 2.0;
|
||||
if (positionedGlyph.imageID) {
|
||||
auto image = imageMap.find(*positionedGlyph.imageID);
|
||||
if (image == imageMap.end()) {
|
||||
continue;
|
||||
}
|
||||
pixelRatio = image->second->pixelRatio;
|
||||
rectBuffer = ImagePosition::padding / pixelRatio;
|
||||
}
|
||||
|
||||
const Point<float> glyphOffset = alongLine ?
|
||||
Point<float>{ positionedGlyph.x + halfAdvance, positionedGlyph.y } :
|
||||
Point<float>{ 0.0f, 0.0f };
|
||||
const Point<float> glyphOffset =
|
||||
alongLine ? Point<float>{positionedGlyph.x + halfAdvance, positionedGlyph.y} : Point<float>{0.0f, 0.0f};
|
||||
|
||||
Point<float> builtInOffset = alongLine ?
|
||||
Point<float>{ 0.0f, 0.0f } :
|
||||
Point<float>{ positionedGlyph.x + halfAdvance + textOffset[0], positionedGlyph.y + textOffset[1] };
|
||||
Point<float> builtInOffset = alongLine ? Point<float>{0.0f, 0.0f}
|
||||
: Point<float>{positionedGlyph.x + halfAdvance + textOffset[0],
|
||||
positionedGlyph.y + textOffset[1] - lineOffset};
|
||||
|
||||
Point<float> verticalizedLabelOffset = { 0.0f, 0.0f };
|
||||
const bool rotateVerticalGlyph = (alongLine || allowVerticalPlacement) && positionedGlyph.vertical;
|
||||
if (rotateVerticalGlyph) {
|
||||
// Vertical POI labels, that are rotated 90deg CW and whose glyphs must preserve upright orientation
|
||||
// need to be rotated 90deg CCW. After quad is rotated, it is translated to the original built-in offset.
|
||||
verticalizedLabelOffset = builtInOffset;
|
||||
builtInOffset = { 0.0f, 0.0f };
|
||||
Point<float> verticalizedLabelOffset = {0.0f, 0.0f};
|
||||
if (rotateVerticalGlyph) {
|
||||
// Vertical POI labels, that are rotated 90deg CW and whose glyphs must preserve upright orientation
|
||||
// need to be rotated 90deg CCW. After quad is rotated, it is translated to the original built-in
|
||||
// offset.
|
||||
verticalizedLabelOffset = builtInOffset;
|
||||
builtInOffset = {0.0f, 0.0f};
|
||||
}
|
||||
|
||||
const float x1 =
|
||||
(positionedGlyph.metrics.left - rectBuffer) * positionedGlyph.scale - halfAdvance + builtInOffset.x;
|
||||
const float y1 = (-positionedGlyph.metrics.top - rectBuffer) * positionedGlyph.scale + builtInOffset.y;
|
||||
const float x2 = x1 + rect.w * positionedGlyph.scale / pixelRatio;
|
||||
const float y2 = y1 + rect.h * positionedGlyph.scale / pixelRatio;
|
||||
|
||||
Point<float> tl{x1, y1};
|
||||
Point<float> tr{x2, y1};
|
||||
Point<float> bl{x1, y2};
|
||||
Point<float> br{x2, y2};
|
||||
|
||||
if (rotateVerticalGlyph) {
|
||||
// Vertical-supporting glyphs are laid out in 24x24 point boxes (1 square em)
|
||||
// In horizontal orientation, the y values for glyphs are below the midline
|
||||
// and we use a "yOffset" of -17 to pull them up to the middle.
|
||||
// By rotating counter-clockwise around the point at the center of the left
|
||||
// edge of a 24x24 layout box centered below the midline, we align the center
|
||||
// of the glyphs with the horizontal midline, so the yOffset is no longer
|
||||
// necessary, but we also pull the glyph to the left along the x axis.
|
||||
// The y coordinate includes baseline yOffset, therefore, needs to be accounted
|
||||
// for when glyph is rotated and translated.
|
||||
|
||||
const Point<float> center{-halfAdvance, halfAdvance - Shaping::yOffset};
|
||||
const float verticalRotation = -M_PI_2;
|
||||
|
||||
// xHalfWidhtOffsetcorrection is a difference between full-width and half-width
|
||||
// advance, should be 0 for full-width glyphs and will pull up half-width glyphs.
|
||||
const float xHalfWidhtOffsetcorrection = util::ONE_EM / 2 - halfAdvance;
|
||||
const float yImageOffsetCorrection = positionedGlyph.imageID ? xHalfWidhtOffsetcorrection : 0.0f;
|
||||
const Point<float> xOffsetCorrection{5.0f - Shaping::yOffset - xHalfWidhtOffsetcorrection,
|
||||
-yImageOffsetCorrection};
|
||||
|
||||
tl = util::rotate(tl - center, verticalRotation) + center + xOffsetCorrection + verticalizedLabelOffset;
|
||||
tr = util::rotate(tr - center, verticalRotation) + center + xOffsetCorrection + verticalizedLabelOffset;
|
||||
bl = util::rotate(bl - center, verticalRotation) + center + xOffsetCorrection + verticalizedLabelOffset;
|
||||
br = util::rotate(br - center, verticalRotation) + center + xOffsetCorrection + verticalizedLabelOffset;
|
||||
}
|
||||
|
||||
if (textRotate) {
|
||||
// Compute the transformation matrix.
|
||||
float angle_sin = std::sin(textRotate);
|
||||
float angle_cos = std::cos(textRotate);
|
||||
std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}};
|
||||
|
||||
tl = util::matrixMultiply(matrix, tl);
|
||||
tr = util::matrixMultiply(matrix, tr);
|
||||
bl = util::matrixMultiply(matrix, bl);
|
||||
br = util::matrixMultiply(matrix, br);
|
||||
}
|
||||
|
||||
quads.emplace_back(tl, tr, bl, br, rect, shapedText.writingMode, glyphOffset, positionedGlyph.sectionIndex);
|
||||
}
|
||||
|
||||
const float x1 = (glyph.metrics.left - rectBuffer) * positionedGlyph.scale - halfAdvance + builtInOffset.x;
|
||||
const float y1 = (-glyph.metrics.top - rectBuffer) * positionedGlyph.scale + builtInOffset.y;
|
||||
const float x2 = x1 + rect.w * positionedGlyph.scale;
|
||||
const float y2 = y1 + rect.h * positionedGlyph.scale;
|
||||
|
||||
Point<float> tl{x1, y1};
|
||||
Point<float> tr{x2, y1};
|
||||
Point<float> bl{x1, y2};
|
||||
Point<float> br{x2, y2};
|
||||
|
||||
if (rotateVerticalGlyph) {
|
||||
// Vertical-supporting glyphs are laid out in 24x24 point boxes (1 square em)
|
||||
// In horizontal orientation, the y values for glyphs are below the midline
|
||||
// and we use a "yOffset" of -17 to pull them up to the middle.
|
||||
// By rotating counter-clockwise around the point at the center of the left
|
||||
// edge of a 24x24 layout box centered below the midline, we align the center
|
||||
// of the glyphs with the horizontal midline, so the yOffset is no longer
|
||||
// necessary, but we also pull the glyph to the left along the x axis.
|
||||
// The y coordinate includes baseline yOffset, therefore, needs to be accounted
|
||||
// for when glyph is rotated and translated.
|
||||
|
||||
const Point<float> center{ -halfAdvance, halfAdvance - Shaping::yOffset };
|
||||
const float verticalRotation = -M_PI_2;
|
||||
|
||||
// xHalfWidhtOffsetcorrection is a difference between full-width and half-width
|
||||
// advance, should be 0 for full-width glyphs and will pull up half-width glyphs.
|
||||
const float xHalfWidhtOffsetcorrection = util::ONE_EM / 2 - halfAdvance;
|
||||
const Point<float> xOffsetCorrection{ 5.0f - Shaping::yOffset - xHalfWidhtOffsetcorrection, 0.0f };
|
||||
|
||||
tl = util::rotate(tl - center, verticalRotation) + center + xOffsetCorrection + verticalizedLabelOffset;
|
||||
tr = util::rotate(tr - center, verticalRotation) + center + xOffsetCorrection + verticalizedLabelOffset;
|
||||
bl = util::rotate(bl - center, verticalRotation) + center + xOffsetCorrection + verticalizedLabelOffset;
|
||||
br = util::rotate(br - center, verticalRotation) + center + xOffsetCorrection + verticalizedLabelOffset;
|
||||
}
|
||||
|
||||
if (textRotate) {
|
||||
// Compute the transformation matrix.
|
||||
float angle_sin = std::sin(textRotate);
|
||||
float angle_cos = std::cos(textRotate);
|
||||
std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}};
|
||||
|
||||
tl = util::matrixMultiply(matrix, tl);
|
||||
tr = util::matrixMultiply(matrix, tr);
|
||||
bl = util::matrixMultiply(matrix, bl);
|
||||
br = util::matrixMultiply(matrix, br);
|
||||
}
|
||||
|
||||
quads.emplace_back(tl, tr, bl, br, rect, shapedText.writingMode, glyphOffset, positionedGlyph.sectionIndex);
|
||||
}
|
||||
|
||||
return quads;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/text/glyph_atlas.hpp>
|
||||
#include <mbgl/style/types.hpp>
|
||||
#include <mbgl/style/image_impl.hpp>
|
||||
#include <mbgl/style/layers/symbol_layer_properties.hpp>
|
||||
#include <mbgl/style/types.hpp>
|
||||
#include <mbgl/text/glyph_atlas.hpp>
|
||||
#include <mbgl/tile/geometry_tile_data.hpp>
|
||||
|
||||
#include <vector>
|
||||
@@ -50,7 +51,7 @@ SymbolQuads getGlyphQuads(const Shaping& shapedText,
|
||||
const std::array<float, 2> textOffset,
|
||||
const style::SymbolLayoutProperties::Evaluated&,
|
||||
style::SymbolPlacementType placement,
|
||||
const GlyphPositions& positions,
|
||||
const ImageMap& imageMap,
|
||||
bool allowVerticalPlacement);
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -122,44 +122,43 @@ void PositionedIcon::fitIconToText(const Shaping& shapedText,
|
||||
}
|
||||
|
||||
void align(Shaping& shaping,
|
||||
const float justify,
|
||||
const float horizontalAlign,
|
||||
const float verticalAlign,
|
||||
const float maxLineLength,
|
||||
const float lineHeight,
|
||||
const std::size_t lineCount) {
|
||||
float justify,
|
||||
float horizontalAlign,
|
||||
float verticalAlign,
|
||||
float maxLineLength,
|
||||
float maxLineHeight,
|
||||
float lineHeight,
|
||||
float blockHeight,
|
||||
std::size_t lineCount) {
|
||||
const float shiftX = (justify - horizontalAlign) * maxLineLength;
|
||||
const float shiftY = (-verticalAlign * lineCount + 0.5) * lineHeight;
|
||||
|
||||
for (auto& glyph : shaping.positionedGlyphs) {
|
||||
glyph.x += shiftX;
|
||||
glyph.y += shiftY;
|
||||
float shiftY = 0.0f;
|
||||
|
||||
if (maxLineHeight != lineHeight) {
|
||||
shiftY = -blockHeight * verticalAlign - Shaping::yOffset;
|
||||
} else {
|
||||
shiftY = (-verticalAlign * lineCount + 0.5) * lineHeight;
|
||||
}
|
||||
|
||||
for (auto& line : shaping.positionedLines) {
|
||||
for (auto& positionedGlyph : line.positionedGlyphs) {
|
||||
positionedGlyph.x += shiftX;
|
||||
positionedGlyph.y += shiftY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// justify left = 0, right = 1, center = .5
|
||||
void justifyLine(std::vector<PositionedGlyph>& positionedGlyphs,
|
||||
const GlyphMap& glyphMap,
|
||||
std::size_t start,
|
||||
std::size_t end,
|
||||
float justify) {
|
||||
if (!justify) {
|
||||
void justifyLine(std::vector<PositionedGlyph>& positionedGlyphs, float justify, float lineOffset) {
|
||||
if (!justify && !lineOffset) {
|
||||
return;
|
||||
}
|
||||
|
||||
PositionedGlyph& glyph = positionedGlyphs[end];
|
||||
auto glyphs = glyphMap.find(glyph.font);
|
||||
if (glyphs == glyphMap.end()) {
|
||||
return;
|
||||
}
|
||||
auto it = glyphs->second.find(glyph.glyph);
|
||||
if (it != glyphs->second.end() && it->second) {
|
||||
const float lastAdvance = (*it->second)->metrics.advance * glyph.scale;
|
||||
const float lineIndent = float(glyph.x + lastAdvance) * justify;
|
||||
|
||||
for (std::size_t j = start; j <= end; j++) {
|
||||
positionedGlyphs[j].x -= lineIndent;
|
||||
}
|
||||
|
||||
PositionedGlyph& lastGlyph = positionedGlyphs.back();
|
||||
const float lastAdvance = lastGlyph.metrics.advance * lastGlyph.scale;
|
||||
const float lineIndent = float(lastGlyph.x + lastAdvance) * justify;
|
||||
for (auto& positionedGlyph : positionedGlyphs) {
|
||||
positionedGlyph.x -= lineIndent;
|
||||
positionedGlyph.y += lineOffset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,17 +171,17 @@ float getGlyphAdvance(char16_t codePoint,
|
||||
if (!section.imageID) {
|
||||
auto glyphs = glyphMap.find(section.fontStackHash);
|
||||
if (glyphs == glyphMap.end()) {
|
||||
return 0.0;
|
||||
return 0.0f;
|
||||
}
|
||||
auto it = glyphs->second.find(codePoint);
|
||||
if (it == glyphs->second.end() || !it->second) {
|
||||
return 0.0;
|
||||
return 0.0f;
|
||||
}
|
||||
return (*it->second)->metrics.advance * section.scale + spacing;
|
||||
} else {
|
||||
auto image = imagePositions.find(*section.imageID);
|
||||
if (image == imagePositions.end()) {
|
||||
return 0.0;
|
||||
return 0.0f;
|
||||
}
|
||||
return image->second.displaySize()[0] * section.scale * util::ONE_EM / layoutTextSize + spacing;
|
||||
}
|
||||
@@ -348,83 +347,169 @@ void shapeLines(Shaping& shaping,
|
||||
const style::TextJustifyType textJustify,
|
||||
const WritingModeType writingMode,
|
||||
const GlyphMap& glyphMap,
|
||||
const ImagePositions& /*imagePositions*/,
|
||||
float /*layoutTextSize*/,
|
||||
const GlyphPositions& glyphPositions,
|
||||
const ImagePositions& imagePositions,
|
||||
float layoutTextSize,
|
||||
bool allowVerticalPlacement) {
|
||||
float x = 0;
|
||||
float x = 0.0f;
|
||||
float y = Shaping::yOffset;
|
||||
|
||||
float maxLineLength = 0;
|
||||
|
||||
float maxLineLength = 0.0f;
|
||||
double maxLineHeight = 0.0f;
|
||||
|
||||
const float justify =
|
||||
textJustify == style::TextJustifyType::Right ? 1.0f : textJustify == style::TextJustifyType::Left ? 0.0f : 0.5f;
|
||||
|
||||
const float justify = textJustify == style::TextJustifyType::Right ? 1 :
|
||||
textJustify == style::TextJustifyType::Left ? 0 :
|
||||
0.5;
|
||||
|
||||
for (TaggedString& line : lines) {
|
||||
// Collapse whitespace so it doesn't throw off justification
|
||||
line.trim();
|
||||
|
||||
const double lineMaxScale = line.getMaxScale();
|
||||
|
||||
const double maxLineOffset = (lineMaxScale - 1.0) * util::ONE_EM;
|
||||
double lineOffset = 0.0;
|
||||
shaping.positionedLines.emplace_back();
|
||||
auto& positionedLine = shaping.positionedLines.back();
|
||||
auto& positionedGlyphs = positionedLine.positionedGlyphs;
|
||||
|
||||
if (line.empty()) {
|
||||
y += lineHeight; // Still need a line feed after empty line
|
||||
continue;
|
||||
}
|
||||
|
||||
std::size_t lineStartIndex = shaping.positionedGlyphs.size();
|
||||
for (std::size_t i = 0; i < line.length(); i++) {
|
||||
const std::size_t sectionIndex = line.getSectionIndex(i);
|
||||
const SectionOptions& section = line.sectionAt(sectionIndex);
|
||||
char16_t codePoint = line.getCharCodeAt(i);
|
||||
auto glyphs = glyphMap.find(section.fontStackHash);
|
||||
if (glyphs == glyphMap.end()) {
|
||||
continue;
|
||||
}
|
||||
auto it = glyphs->second.find(codePoint);
|
||||
if (it == glyphs->second.end() || !it->second) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We don't know the baseline, but since we're laying out
|
||||
// at 24 points, we can calculate how much it will move when
|
||||
// we scale up or down.
|
||||
const double baselineOffset = (lineMaxScale - section.scale) * util::ONE_EM;
|
||||
|
||||
const Glyph& glyph = **it->second;
|
||||
double baselineOffset = 0.0;
|
||||
Rect<uint16_t> rect;
|
||||
GlyphMetrics metrics;
|
||||
float advance = 0.0f;
|
||||
float verticalAdvance = util::ONE_EM;
|
||||
double sectionScale = section.scale;
|
||||
assert(sectionScale);
|
||||
|
||||
if (writingMode == WritingModeType::Horizontal ||
|
||||
// Don't verticalize glyphs that have no upright orientation if vertical placement is disabled.
|
||||
(!allowVerticalPlacement && !util::i18n::hasUprightVerticalOrientation(codePoint)) ||
|
||||
// If vertical placement is ebabled, don't verticalize glyphs that
|
||||
// are from complex text layout script, or whitespaces.
|
||||
(allowVerticalPlacement && (util::i18n::isWhitespace(codePoint) || util::i18n::isCharInComplexShapingScript(codePoint)))) {
|
||||
shaping.positionedGlyphs.emplace_back(codePoint, x, y + baselineOffset, false, section.fontStackHash, section.scale, sectionIndex);
|
||||
x += glyph.metrics.advance * section.scale + spacing;
|
||||
const bool vertical =
|
||||
!(writingMode == WritingModeType::Horizontal ||
|
||||
// Don't verticalize glyphs that have no upright orientation if vertical placement is disabled.
|
||||
(!allowVerticalPlacement && !util::i18n::hasUprightVerticalOrientation(codePoint)) ||
|
||||
// If vertical placement is ebabled, don't verticalize glyphs that
|
||||
// are from complex text layout script, or whitespaces.
|
||||
(allowVerticalPlacement &&
|
||||
(util::i18n::isWhitespace(codePoint) || util::i18n::isCharInComplexShapingScript(codePoint))));
|
||||
|
||||
if (!section.imageID) {
|
||||
auto glyphPositionMap = glyphPositions.find(section.fontStackHash);
|
||||
if (glyphPositionMap == glyphPositions.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto glyphPosition = glyphPositionMap->second.find(codePoint);
|
||||
if (glyphPosition != glyphPositionMap->second.end()) {
|
||||
rect = glyphPosition->second.rect;
|
||||
metrics = glyphPosition->second.metrics;
|
||||
} else {
|
||||
auto glyphs = glyphMap.find(section.fontStackHash);
|
||||
if (glyphs == glyphMap.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto glyph = glyphs->second.find(codePoint);
|
||||
if (glyph == glyphs->second.end() || !glyph->second) {
|
||||
continue;
|
||||
}
|
||||
metrics = (*glyph->second)->metrics;
|
||||
}
|
||||
advance = metrics.advance;
|
||||
// We don't know the baseline, but since we're laying out
|
||||
// at 24 points, we can calculate how much it will move when
|
||||
// we scale up or down.
|
||||
baselineOffset = (lineMaxScale - sectionScale) * util::ONE_EM;
|
||||
} else {
|
||||
shaping.positionedGlyphs.emplace_back(codePoint, x, y + baselineOffset, true, section.fontStackHash, section.scale, sectionIndex);
|
||||
x += util::ONE_EM * section.scale + spacing;
|
||||
auto image = imagePositions.find(*section.imageID);
|
||||
if (image == imagePositions.end()) {
|
||||
continue;
|
||||
}
|
||||
const auto& displaySize = image->second.displaySize();
|
||||
metrics.width = displaySize[0];
|
||||
metrics.height = displaySize[1];
|
||||
metrics.left = ImagePosition::padding;
|
||||
metrics.top = -Glyph::borderSize;
|
||||
metrics.advance = vertical ? displaySize[1] : displaySize[0];
|
||||
rect = image->second.paddedTextureRect();
|
||||
|
||||
// If needed, allow to set scale factor for an image using
|
||||
// alias "image-scale" that could be alias for "font-scale"
|
||||
// when FormattedSection is an image section.
|
||||
sectionScale = sectionScale * util::ONE_EM / layoutTextSize;
|
||||
|
||||
// Difference between one EM and an image size.
|
||||
// Aligns bottom of an image to a baseline level.
|
||||
float imageOffset = util::ONE_EM - displaySize[1] * sectionScale;
|
||||
baselineOffset = maxLineOffset + imageOffset;
|
||||
advance = verticalAdvance = metrics.advance;
|
||||
|
||||
// Difference between height of an image and one EM at max line scale.
|
||||
// Pushes current line down if an image size is over 1 EM at max line scale.
|
||||
double offset =
|
||||
(vertical ? displaySize[0] : displaySize[1]) * sectionScale - util::ONE_EM * lineMaxScale;
|
||||
if (offset > 0.0 && offset > lineOffset) {
|
||||
lineOffset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vertical) {
|
||||
positionedGlyphs.emplace_back(codePoint,
|
||||
x,
|
||||
y + baselineOffset,
|
||||
vertical,
|
||||
section.fontStackHash,
|
||||
sectionScale,
|
||||
rect,
|
||||
metrics,
|
||||
section.imageID,
|
||||
sectionIndex);
|
||||
x += advance * sectionScale + spacing;
|
||||
} else {
|
||||
positionedGlyphs.emplace_back(codePoint,
|
||||
x,
|
||||
y + baselineOffset,
|
||||
vertical,
|
||||
section.fontStackHash,
|
||||
sectionScale,
|
||||
rect,
|
||||
metrics,
|
||||
section.imageID,
|
||||
sectionIndex);
|
||||
x += verticalAdvance * sectionScale + spacing;
|
||||
shaping.verticalizable |= true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Only justify if we placed at least one glyph
|
||||
if (shaping.positionedGlyphs.size() != lineStartIndex) {
|
||||
if (positionedGlyphs.size() != 0) {
|
||||
float lineLength = x - spacing; // Don't count trailing spacing
|
||||
maxLineLength = util::max(lineLength, maxLineLength);
|
||||
|
||||
justifyLine(shaping.positionedGlyphs, glyphMap, lineStartIndex,
|
||||
shaping.positionedGlyphs.size() - 1, justify);
|
||||
justifyLine(positionedGlyphs, justify, lineOffset);
|
||||
}
|
||||
|
||||
x = 0;
|
||||
y += lineHeight * lineMaxScale;
|
||||
|
||||
double currentLineHeight = lineHeight * lineMaxScale + lineOffset;
|
||||
x = 0.0f;
|
||||
y += currentLineHeight;
|
||||
positionedLine.lineOffset = std::max(lineOffset, maxLineOffset);
|
||||
maxLineHeight = std::max(currentLineHeight, maxLineHeight);
|
||||
}
|
||||
|
||||
auto anchorAlign = AnchorAlignment::getAnchorAlignment(textAnchor);
|
||||
|
||||
align(shaping, justify, anchorAlign.horizontalAlign, anchorAlign.verticalAlign, maxLineLength,
|
||||
lineHeight, lines.size());
|
||||
const float height = y - Shaping::yOffset;
|
||||
align(shaping,
|
||||
justify,
|
||||
anchorAlign.horizontalAlign,
|
||||
anchorAlign.verticalAlign,
|
||||
maxLineLength,
|
||||
maxLineHeight,
|
||||
lineHeight,
|
||||
height,
|
||||
lines.size());
|
||||
|
||||
// Calculate the bounding box
|
||||
shaping.top += -anchorAlign.verticalAlign * height;
|
||||
@@ -442,27 +527,29 @@ const Shaping getShaping(const TaggedString& formattedString,
|
||||
const std::array<float, 2>& translate,
|
||||
const WritingModeType writingMode,
|
||||
BiDi& bidi,
|
||||
const GlyphMap& glyphs,
|
||||
const GlyphMap& glyphMap,
|
||||
const GlyphPositions& glyphPositions,
|
||||
const ImagePositions& imagePositions,
|
||||
float layoutTextSize,
|
||||
bool allowVerticalPlacement) {
|
||||
assert(layoutTextSize);
|
||||
std::vector<TaggedString> reorderedLines;
|
||||
if (formattedString.sectionCount() == 1) {
|
||||
auto untaggedLines = bidi.processText(
|
||||
formattedString.rawText(),
|
||||
determineLineBreaks(formattedString, spacing, maxWidth, glyphs, imagePositions, layoutTextSize));
|
||||
determineLineBreaks(formattedString, spacing, maxWidth, glyphMap, imagePositions, layoutTextSize));
|
||||
for (const auto& line : untaggedLines) {
|
||||
reorderedLines.emplace_back(line, formattedString.sectionAt(0));
|
||||
}
|
||||
} else {
|
||||
auto processedLines = bidi.processStyledText(
|
||||
formattedString.getStyledText(),
|
||||
determineLineBreaks(formattedString, spacing, maxWidth, glyphs, imagePositions, layoutTextSize));
|
||||
determineLineBreaks(formattedString, spacing, maxWidth, glyphMap, imagePositions, layoutTextSize));
|
||||
for (const auto& line : processedLines) {
|
||||
reorderedLines.emplace_back(line, formattedString.getSections());
|
||||
}
|
||||
}
|
||||
Shaping shaping(translate[0], translate[1], writingMode, reorderedLines.size());
|
||||
Shaping shaping(translate[0], translate[1], writingMode);
|
||||
shapeLines(shaping,
|
||||
reorderedLines,
|
||||
spacing,
|
||||
@@ -470,7 +557,8 @@ const Shaping getShaping(const TaggedString& formattedString,
|
||||
textAnchor,
|
||||
textJustify,
|
||||
writingMode,
|
||||
glyphs,
|
||||
glyphMap,
|
||||
glyphPositions,
|
||||
imagePositions,
|
||||
layoutTextSize,
|
||||
allowVerticalPlacement);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
#include <mbgl/text/tagged_string.hpp>
|
||||
#include <mbgl/renderer/image_atlas.hpp>
|
||||
#include <mbgl/style/types.hpp>
|
||||
#include <mbgl/style/layers/symbol_layer_properties.hpp>
|
||||
#include <mbgl/style/types.hpp>
|
||||
#include <mbgl/text/glyph.hpp>
|
||||
#include <mbgl/text/glyph_atlas.hpp>
|
||||
#include <mbgl/text/tagged_string.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
@@ -68,7 +69,8 @@ const Shaping getShaping(const TaggedString& string,
|
||||
const std::array<float, 2>& translate,
|
||||
const WritingModeType,
|
||||
BiDi& bidi,
|
||||
const GlyphMap& glyphs,
|
||||
const GlyphMap& glyphMap,
|
||||
const GlyphPositions& glyphPositions,
|
||||
const ImagePositions& imagePositions,
|
||||
float layoutTextSize,
|
||||
bool allowVerticalPlacement);
|
||||
|
||||
@@ -460,8 +460,7 @@ void GeometryTileWorker::finalizeLayout() {
|
||||
return;
|
||||
}
|
||||
|
||||
layout->prepareSymbols(glyphMap, glyphAtlas.positions,
|
||||
imageMap, iconAtlas.iconPositions);
|
||||
layout->prepareSymbols(glyphMap, glyphAtlas.positions, imageMap, iconAtlas.iconPositions);
|
||||
|
||||
if (!layout->hasSymbolInstances()) {
|
||||
continue;
|
||||
|
||||
@@ -6,7 +6,7 @@ using namespace mbgl;
|
||||
|
||||
SymbolInstance makeSymbolInstance(float x, float y, std::u16string key) {
|
||||
GeometryCoordinates line;
|
||||
GlyphPositions positions;
|
||||
ImageMap imageMap;
|
||||
const ShapedTextOrientations shaping{};
|
||||
style::SymbolLayoutProperties::Evaluated layout_;
|
||||
IndexedSubfeature subfeature(0, "", "", 0);
|
||||
@@ -16,9 +16,8 @@ SymbolInstance makeSymbolInstance(float x, float y, std::u16string key) {
|
||||
std::array<float, 2> variableTextOffset{{0.0f, 0.0f}};
|
||||
style::SymbolPlacementType placementType = style::SymbolPlacementType::Point;
|
||||
|
||||
auto sharedData = std::make_shared<SymbolInstanceSharedData>(std::move(line),
|
||||
shaping, nullopt, nullopt, layout_, placementType,
|
||||
textOffset, positions, false);
|
||||
auto sharedData = std::make_shared<SymbolInstanceSharedData>(
|
||||
std::move(line), shaping, nullopt, nullopt, layout_, placementType, textOffset, imageMap, false);
|
||||
return SymbolInstance(anchor, std::move(sharedData), shaping, nullopt, nullopt, 0, 0, placementType, textOffset, 0, 0, iconOffset, subfeature, 0, 0, key, 0.0f, 0.0f, 0.0f, variableTextOffset, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,10 @@ TEST(getIconQuads, style) {
|
||||
shapedText.bottom = 30.0f;
|
||||
shapedText.left = -60.0f;
|
||||
shapedText.right = 20.0f;
|
||||
shapedText.positionedGlyphs.emplace_back(PositionedGlyph(32, 0.0f, 0.0f, false, 0, 1.0));
|
||||
// shapedText.positionedGlyphs.emplace_back(PositionedGlyph(32, 0.0f, 0.0f, false, 0, 1.0));
|
||||
shapedText.positionedLines.emplace_back();
|
||||
shapedText.positionedLines.back().positionedGlyphs.emplace_back(
|
||||
PositionedGlyph(32, 0.0f, 0.0f, false, 0, 1.0, /*texRect*/ {}, /*metrics*/ {}, /*imageID*/ nullopt));
|
||||
|
||||
// none
|
||||
{
|
||||
|
||||
@@ -10,13 +10,16 @@ using namespace mbgl;
|
||||
using namespace util;
|
||||
|
||||
TEST(Shaping, ZWSP) {
|
||||
GlyphPosition glyphPosition;
|
||||
glyphPosition.metrics.width = 18;
|
||||
glyphPosition.metrics.height = 18;
|
||||
glyphPosition.metrics.left = 2;
|
||||
glyphPosition.metrics.top = -8;
|
||||
glyphPosition.metrics.advance = 21;
|
||||
|
||||
Glyph glyph;
|
||||
glyph.id = u'中';
|
||||
glyph.metrics.width = 18;
|
||||
glyph.metrics.height = 18;
|
||||
glyph.metrics.left = 2;
|
||||
glyph.metrics.top = -8;
|
||||
glyph.metrics.advance = 21;
|
||||
glyph.metrics = glyphPosition.metrics;
|
||||
|
||||
BiDi bidi;
|
||||
auto immutableGlyph = Immutable<Glyph>(makeMutable<Glyph>(std::move(glyph)));
|
||||
@@ -25,7 +28,7 @@ TEST(Shaping, ZWSP) {
|
||||
GlyphMap glyphs = {
|
||||
{ FontStackHasher()(fontStack), {{u'中', std::move(immutableGlyph)}} }
|
||||
};
|
||||
|
||||
GlyphPositions glyphPositions = {{FontStackHasher()(fontStack), {{u'中', std::move(glyphPosition)}}}};
|
||||
ImagePositions imagePositions;
|
||||
|
||||
const auto testGetShaping = [&](const TaggedString& string, unsigned maxWidthInChars) {
|
||||
@@ -39,6 +42,7 @@ TEST(Shaping, ZWSP) {
|
||||
WritingModeType::Horizontal,
|
||||
bidi,
|
||||
glyphs,
|
||||
glyphPositions,
|
||||
imagePositions,
|
||||
16.0,
|
||||
/*allowVerticalPlacement*/ false);
|
||||
@@ -51,7 +55,7 @@ TEST(Shaping, ZWSP) {
|
||||
{
|
||||
TaggedString string(u"中中\u200b中中\u200b中中\u200b中中中中中中\u200b中中", sectionOptions);
|
||||
auto shaping = testGetShaping(string, 5);
|
||||
ASSERT_EQ(shaping.lineCount, 3);
|
||||
ASSERT_EQ(shaping.positionedLines.size(), 3);
|
||||
ASSERT_EQ(shaping.top, -36);
|
||||
ASSERT_EQ(shaping.bottom, 36);
|
||||
ASSERT_EQ(shaping.left, -63);
|
||||
@@ -65,7 +69,7 @@ TEST(Shaping, ZWSP) {
|
||||
{
|
||||
TaggedString string(u"中中\u200b中", sectionOptions);
|
||||
auto shaping = testGetShaping(string, 1);
|
||||
ASSERT_EQ(shaping.lineCount, 2);
|
||||
ASSERT_EQ(shaping.positionedLines.size(), 2);
|
||||
ASSERT_EQ(shaping.top, -24);
|
||||
ASSERT_EQ(shaping.bottom, 24);
|
||||
ASSERT_EQ(shaping.left, -21);
|
||||
@@ -78,7 +82,7 @@ TEST(Shaping, ZWSP) {
|
||||
{
|
||||
TaggedString string(u"中中\u200b", sectionOptions);
|
||||
auto shaping = testGetShaping(string, 2);
|
||||
ASSERT_EQ(shaping.lineCount, 1);
|
||||
ASSERT_EQ(shaping.positionedLines.size(), 1);
|
||||
ASSERT_EQ(shaping.top, -12);
|
||||
ASSERT_EQ(shaping.bottom, 12);
|
||||
ASSERT_EQ(shaping.left, -21);
|
||||
@@ -90,7 +94,7 @@ TEST(Shaping, ZWSP) {
|
||||
{
|
||||
TaggedString string(u"\u200b\u200b\u200b\u200b\u200b", sectionOptions);
|
||||
auto shaping = testGetShaping(string, 1);
|
||||
ASSERT_EQ(shaping.lineCount, 5);
|
||||
ASSERT_EQ(shaping.positionedLines.size(), 5);
|
||||
ASSERT_EQ(shaping.top, -60);
|
||||
ASSERT_EQ(shaping.bottom, 60);
|
||||
ASSERT_EQ(shaping.left, 0);
|
||||
|
||||
Reference in New Issue
Block a user