/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <dali/public-api/animation/constraints.h>
#include <dali/public-api/rendering/geometry.h>
#include <dali/public-api/rendering/renderer.h>
+#include <map>
// INTERNAL INCLUDES
#include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
+#include <dali-toolkit/internal/text/glyph-metrics-helper.h>
#include <dali-toolkit/internal/text/glyph-run.h>
#include <dali-toolkit/internal/text/rendering/atlas/atlas-glyph-manager.h>
#include <dali-toolkit/internal/text/rendering/atlas/atlas-mesh-factory.h>
+#include <dali-toolkit/internal/text/rendering/styles/strikethrough-helper-functions.h>
+#include <dali-toolkit/internal/text/rendering/styles/underline-helper-functions.h>
#include <dali-toolkit/internal/text/text-view.h>
using namespace Dali;
const float ZERO(0.0f);
const float HALF(0.5f);
const float ONE(1.0f);
-const uint32_t DOUBLE_PIXEL_PADDING = 4u;//Padding will be added twice to Atlas
+const float ONE_AND_A_HALF(1.5f);
+const uint32_t DOUBLE_PIXEL_PADDING = 4u; //Padding will be added twice to Atlas
const uint16_t NO_OUTLINE = 0u;
} // namespace
};
/**
- * brief Struct used to generate the underline mesh.
+ * brief Struct used to generate the underline/striketthrough mesh.
* There is one Extent per line of text.
*/
struct Extent
mLeft(0.0f),
mRight(0.0f),
mUnderlinePosition(0.0f),
- mUnderlineThickness(0.0f),
+ mLineThickness(0.0f),
mMeshRecordIndex(0u),
- mUnderlineChunkId(0u)
+ mUnderlineChunkId(0u),
+ mStrikethroughPosition(0.0f),
+ mStrikethroughChunkId(0u)
{
}
float mLeft;
float mRight;
float mUnderlinePosition;
- float mUnderlineThickness;
+ float mLineThickness;
uint32_t mMeshRecordIndex;
uint32_t mUnderlineChunkId;
+ float mStrikethroughPosition;
+ uint32_t mStrikethroughChunkId;
};
struct MaxBlockSize
mQuadVertexFormat["aColor"] = Property::VECTOR4;
}
- bool IsGlyphUnderlined(GlyphIndex index,
- const Vector<GlyphRun>& underlineRuns)
+ bool
+ doGlyphHaveStrikethrough(GlyphIndex index,
+ const Vector<StrikethroughGlyphRun>& strikethroughRuns,
+ Vector4& strikethroughColor)
{
- for(Vector<GlyphRun>::ConstIterator it = underlineRuns.Begin(),
- endIt = underlineRuns.End();
+ for(Vector<StrikethroughGlyphRun>::ConstIterator it = strikethroughRuns.Begin(),
+ endIt = strikethroughRuns.End();
it != endIt;
++it)
{
- const GlyphRun& run = *it;
+ const StrikethroughGlyphRun& run = *it;
- if((run.glyphIndex <= index) && (index < run.glyphIndex + run.numberOfGlyphs))
+ if((run.glyphRun.glyphIndex <= index) && (index < run.glyphRun.glyphIndex + run.glyphRun.numberOfGlyphs))
{
+ if(run.isColorSet)
+ {
+ strikethroughColor = run.color;
+ }
+
return true;
}
}
return false;
}
-
void CacheGlyph(const GlyphInfo& glyph, FontId lastFontId, const AtlasGlyphManager::GlyphStyle& style, AtlasManager::AtlasSlot& slot)
{
const Size& defaultTextAtlasSize = mFontClient.GetDefaultTextAtlasSize(); //Retrieve default size of text-atlas-block from font-client.
// If CheckAtlas in AtlasManager::Add can't fit the bitmap in the current atlas it will create a new atlas
// Setting the block size and size of new atlas does not mean a new one will be created. An existing atlas may still surffice.
- uint32_t default_width = defaultTextAtlasSize.width;
+ uint32_t default_width = defaultTextAtlasSize.width;
uint32_t default_height = defaultTextAtlasSize.height;
- while (
+ while(
(blockSize.mNeededBlockWidth >= (default_width - (DOUBLE_PIXEL_PADDING + 1u)) ||
- blockSize.mNeededBlockHeight >= (default_height - (DOUBLE_PIXEL_PADDING + 1u)))
- &&
+ blockSize.mNeededBlockHeight >= (default_height - (DOUBLE_PIXEL_PADDING + 1u))) &&
(default_width < maximumTextAtlasSize.width &&
default_height < maximumTextAtlasSize.height))
{
const Vector4& color,
uint16_t outline,
AtlasManager::AtlasSlot& slot,
- bool underlineGlyph,
+ bool decorationlineGlyph,
float currentUnderlinePosition,
- float currentUnderlineThickness,
+ float currentlineThickness,
std::vector<MeshRecord>& meshContainer,
Vector<TextCacheEntry>& newTextCache,
Vector<Extent>& extents,
- uint32_t underlineChunkId)
+ uint32_t underlineChunkId,
+ bool isGlyphCached,
+ uint32_t strikethroughChunkId)
{
// Generate mesh data for this quad, plugging in our supplied position
AtlasManager::Mesh2D newMesh;
mGlyphManager.GenerateMeshData(slot.mImageId, position, newMesh);
- TextCacheEntry textCacheEntry;
- textCacheEntry.mFontId = glyph.fontId;
- textCacheEntry.mImageId = slot.mImageId;
- textCacheEntry.mIndex = glyph.index;
- textCacheEntry.mOutlineWidth = outline;
- textCacheEntry.isItalic = glyph.isItalicRequired;
- textCacheEntry.isBold = glyph.isBoldRequired;
-
- newTextCache.PushBack(textCacheEntry);
+ if(!isGlyphCached)
+ {
+ TextCacheEntry textCacheEntry;
+ textCacheEntry.mFontId = glyph.fontId;
+ textCacheEntry.mImageId = slot.mImageId;
+ textCacheEntry.mIndex = glyph.index;
+ textCacheEntry.mOutlineWidth = outline;
+ textCacheEntry.isItalic = glyph.isItalicRequired;
+ textCacheEntry.isBold = glyph.isBoldRequired;
+
+ newTextCache.PushBack(textCacheEntry);
+ }
AtlasManager::Vertex2D* verticesBuffer = newMesh.mVertices.Begin();
newMesh,
extents,
position.y + glyph.yBearing,
- underlineGlyph,
+ decorationlineGlyph,
currentUnderlinePosition,
- currentUnderlineThickness,
+ currentlineThickness,
slot,
- underlineChunkId);
+ underlineChunkId,
+ position.y + (glyph.height * HALF),
+ strikethroughChunkId);
}
void CreateActors(const std::vector<MeshRecord>& meshContainer,
std::vector<MeshRecord> meshContainer;
std::vector<MeshRecord> meshContainerOutline;
Vector<Extent> extents;
+ Vector<Extent> strikethroughExtents;
mDepth = depth;
const Vector2& textSize(view.GetLayoutSize());
const Vector2& shadowOffset(view.GetShadowOffset());
const Vector4& shadowColor(view.GetShadowColor());
const bool underlineEnabled = view.IsUnderlineEnabled();
- const Vector4& underlineColor(view.GetUnderlineColor());
- const float underlineHeight = view.GetUnderlineHeight();
- const uint16_t outlineWidth = view.GetOutlineWidth();
+ const uint16_t outlineWidth = view.GetOutlineWidth();
const Vector4& outlineColor(view.GetOutlineColor());
- const bool isOutline = 0u != outlineWidth;
- const GlyphInfo* hyphens = view.GetHyphens();
- const Length* hyphenIndices = view.GetHyphenIndices();
- const Length hyphensCount = view.GetHyphensCount();
+ const bool isOutline = 0u != outlineWidth;
+ const GlyphInfo* hyphens = view.GetHyphens();
+ const Length* hyphenIndices = view.GetHyphenIndices();
+ const Length hyphensCount = view.GetHyphensCount();
+ const bool strikethroughEnabled = view.IsStrikethroughEnabled();
+ const Vector4& strikethroughColor(view.GetStrikethroughColor());
+ const float strikethroughHeight = view.GetStrikethroughHeight();
+ Vector4 currentStrikethroughColor;
+ const float characterSpacing(view.GetCharacterSpacing());
+
+ // Elided text info. Indices according to elided text.
+ const auto startIndexOfGlyphs = view.GetStartIndexOfElidedGlyphs();
+ const auto firstMiddleIndexOfElidedGlyphs = view.GetFirstMiddleIndexOfElidedGlyphs();
+ const auto secondMiddleIndexOfElidedGlyphs = view.GetSecondMiddleIndexOfElidedGlyphs();
const bool useDefaultColor = (NULL == colorsBuffer);
+ // Get a handle of the font client. Used to retrieve the bitmaps of the glyphs.
+ TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
+
// Get the underline runs.
- const Length numberOfUnderlineRuns = view.GetNumberOfUnderlineRuns();
- Vector<GlyphRun> underlineRuns;
+ const Length numberOfUnderlineRuns = view.GetNumberOfUnderlineRuns();
+ Vector<UnderlinedGlyphRun> underlineRuns;
underlineRuns.Resize(numberOfUnderlineRuns);
view.GetUnderlineRuns(underlineRuns.Begin(),
0u,
numberOfUnderlineRuns);
+ // Aggregate underline-style-properties from view
+ const UnderlineStyleProperties viewUnderlineProperties{view.GetUnderlineType(),
+ view.GetUnderlineColor(),
+ view.GetUnderlineHeight(),
+ view.GetDashedUnderlineGap(),
+ view.GetDashedUnderlineWidth(),
+ true,
+ true,
+ true,
+ true,
+ true};
+
+ float maxUnderlineHeight = viewUnderlineProperties.height;
+
+ // Get the strikethrough runs.
+ const Length numberOfStrikethroughRuns = view.GetNumberOfStrikethroughRuns();
+ Vector<StrikethroughGlyphRun> strikethroughRuns;
+ strikethroughRuns.Resize(numberOfStrikethroughRuns);
+ view.GetStrikethroughRuns(strikethroughRuns.Begin(), 0u, numberOfStrikethroughRuns);
+
bool thereAreUnderlinedGlyphs = false;
+ bool strikethroughGlyphsExist = false;
- float currentUnderlinePosition = ZERO;
- float currentUnderlineThickness = underlineHeight;
- FontId lastFontId = 0;
- FontId lastUnderlinedFontId = 0;
- Style style = STYLE_NORMAL;
+ float currentUnderlinePosition = ZERO;
+ float currentStrikethroughHeight = strikethroughHeight;
+ float maxStrikethroughHeight = currentStrikethroughHeight;
+ FontId lastFontId = 0;
+ Style style = STYLE_NORMAL;
if(fabsf(shadowOffset.x) > Math::MACHINE_EPSILON_1 || fabsf(shadowOffset.y) > Math::MACHINE_EPSILON_1)
{
uint32_t hyphenIndex = 0;
//For septated underlined chunks. (this is for Markup case)
- uint32_t underlineChunkId = 0u; // give id for each chunk.
- bool isPreUnderlined = false; // status of underlined for previous glyph.
+ uint32_t underlineChunkId = 0u; // give id for each chunk.
+ bool isPreUnderlined = false; // status of underlined for previous glyph.
+ std::map<uint32_t, UnderlineStyleProperties> mapUnderlineChunkIdWithProperties; // mapping underlineChunkId with UnderlineStyleProperties to get properties of underlined chunk
+ UnderlineStyleProperties preUnderlineProperties = viewUnderlineProperties; // the previous UnderlineStyleProperties
+
+ uint32_t strikethroughChunkId = 0u; // give id for each chunk.
+ bool isPrevGlyphStrikethrough = false; // status of strikethrough for previous glyph.
+ const Character* textBuffer = view.GetTextBuffer();
+ float calculatedAdvance = 0.f;
+ const Vector<CharacterIndex>& glyphToCharacterMap = view.GetGlyphsToCharacters();
+ const CharacterIndex* glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
+
+ //Skip hyphenIndices less than startIndexOfGlyphs or between two middle of elided text
+ if(hyphenIndices)
+ {
+ while((hyphenIndex < hyphensCount) && (hyphenIndices[hyphenIndex] < startIndexOfGlyphs ||
+ (hyphenIndices[hyphenIndex] > firstMiddleIndexOfElidedGlyphs && hyphenIndices[hyphenIndex] < secondMiddleIndexOfElidedGlyphs)))
+ {
+ ++hyphenIndex;
+ }
+ }
+ //To keep the last fontMetrics of lastDecorativeLinesFontId
+ FontId lastDecorativeLinesFontId = 0; // DecorativeLines like Undeline and Strikethrough
+ FontMetrics lastDecorativeLinesFontMetrics;
+ fontClient.GetFontMetrics(lastDecorativeLinesFontId, lastDecorativeLinesFontMetrics);
+
+ // Iteration on glyphs
for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
{
GlyphInfo glyph;
- bool addHyphen = ((hyphenIndex < hyphensCount) && hyphenIndices && (i == hyphenIndices[hyphenIndex]));
+ bool addHyphen = ((hyphenIndex < hyphensCount) && hyphenIndices && ((i + startIndexOfGlyphs) == hyphenIndices[hyphenIndex]));
if(addHyphen && hyphens)
{
glyph = hyphens[hyphenIndex];
glyph = *(glyphsBuffer + i);
}
- const bool isGlyphUnderlined = underlineEnabled || IsGlyphUnderlined(i, underlineRuns);
- thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || isGlyphUnderlined;
+ Vector<UnderlinedGlyphRun>::ConstIterator currentUnderlinedGlyphRunIt = underlineRuns.End();
+ const bool isGlyphUnderlined = underlineEnabled || IsGlyphUnderlined(i, underlineRuns, currentUnderlinedGlyphRunIt);
+ const UnderlineStyleProperties currentUnderlineProperties = GetCurrentUnderlineProperties(i, isGlyphUnderlined, underlineRuns, currentUnderlinedGlyphRunIt, viewUnderlineProperties);
+ float currentUnderlineHeight = currentUnderlineProperties.height;
+ thereAreUnderlinedGlyphs = thereAreUnderlinedGlyphs || isGlyphUnderlined;
+
+ currentStrikethroughColor = strikethroughColor;
+ const bool isStrikethroughGlyph = strikethroughEnabled || doGlyphHaveStrikethrough(i, strikethroughRuns, currentStrikethroughColor);
+ strikethroughGlyphsExist = strikethroughGlyphsExist || isStrikethroughGlyph;
// No operation for white space
if(glyph.width && glyph.height)
{
- // Are we still using the same fontId as previous
- if(isGlyphUnderlined && (glyph.fontId != lastUnderlinedFontId))
+ // Check and update decorative-lines informations
+ if((isGlyphUnderlined || isStrikethroughGlyph) &&
+ ((glyph.fontId != lastDecorativeLinesFontId) || !(currentUnderlineProperties.IsHeightEqualTo(preUnderlineProperties))))
{
- // We need to fetch fresh font underline metrics
- FontMetrics fontMetrics;
- mFontClient.GetFontMetrics(glyph.fontId, fontMetrics);
- currentUnderlinePosition = ceil(fabsf(fontMetrics.underlinePosition));
- const float descender = ceil(fabsf(fontMetrics.descender));
-
- if(fabsf(underlineHeight) < Math::MACHINE_EPSILON_1000)
+ bool isDecorativeLinesFontIdUpdated = false;
+ // Are we still using the same fontId as previous
+ if(glyph.fontId != lastDecorativeLinesFontId)
{
- currentUnderlineThickness = fontMetrics.underlineThickness;
+ // We need to fetch fresh font metrics
+ lastDecorativeLinesFontId = glyph.fontId;
+ isDecorativeLinesFontIdUpdated = true;
+ fontClient.GetFontMetrics(lastDecorativeLinesFontId, lastDecorativeLinesFontMetrics);
- // Ensure underline will be at least a pixel high
- if(currentUnderlineThickness < ONE)
- {
- currentUnderlineThickness = ONE;
- }
- else
+ if(isStrikethroughGlyph || isGlyphUnderlined)
{
- currentUnderlineThickness = ceil(currentUnderlineThickness);
+ //The currentUnderlinePosition will be used for both Underline and/or Strikethrough
+ currentUnderlinePosition = FetchUnderlinePositionFromFontMetrics(lastDecorativeLinesFontMetrics);
}
}
- // Clamp the underline position at the font descender and check for ( as EFL describes it ) a broken font
- if(currentUnderlinePosition > descender)
+ if(isGlyphUnderlined && (isDecorativeLinesFontIdUpdated || !(currentUnderlineProperties.IsHeightEqualTo(preUnderlineProperties))))
{
- currentUnderlinePosition = descender;
+ //If the Underline Height is changed then we need to recalculate height.
+ if(!(currentUnderlineProperties.IsHeightEqualTo(preUnderlineProperties)))
+ {
+ maxUnderlineHeight = currentUnderlineHeight;
+ }
+
+ CalcualteUnderlineHeight(lastDecorativeLinesFontMetrics, currentUnderlineHeight, maxUnderlineHeight);
}
- if(fabsf(currentUnderlinePosition) < Math::MACHINE_EPSILON_1000)
+ if(isDecorativeLinesFontIdUpdated && isStrikethroughGlyph)
{
- // Move offset down by one ( EFL behavior )
- currentUnderlinePosition = ONE;
+ CalcualteStrikethroughHeight(currentStrikethroughHeight, maxStrikethroughHeight);
}
-
- lastUnderlinedFontId = glyph.fontId;
-
- } // underline
+ } // decorative-lines
AtlasGlyphManager::GlyphStyle style;
style.isItalic = glyph.isItalicRequired;
if(addHyphen)
{
GlyphInfo tempInfo = *(glyphsBuffer + i);
- position.x = position.x + tempInfo.advance - tempInfo.xBearing + glyph.xBearing;
+ calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + i))), characterSpacing, tempInfo.advance);
+ position.x = position.x + calculatedAdvance - tempInfo.xBearing + glyph.xBearing;
position.y += tempInfo.yBearing - glyph.yBearing;
}
const ColorIndex colorIndex = useDefaultColor ? 0u : *(colorIndicesBuffer + i);
const Vector4& color = (useDefaultColor || (0u == colorIndex)) ? defaultColor : *(colorsBuffer + colorIndex - 1u);
+ //The new underlined chunk. Add new id if they are not consecutive indices (this is for Markup case)
+ // Examples: "Hello <u>World</u> Hello <u>World</u>", "<u>World</u> Hello <u>World</u>", "<u> World</u> Hello <u>World</u>"
+ if((!isPreUnderlined && isGlyphUnderlined) || (isGlyphUnderlined && (preUnderlineProperties != currentUnderlineProperties)))
+ {
+ underlineChunkId++;
+ mapUnderlineChunkIdWithProperties.insert(std::pair<uint32_t, UnderlineStyleProperties>(underlineChunkId, currentUnderlineProperties));
+ }
+
+ //Keep status of underlined for previous glyph to check consecutive indices
+ isPreUnderlined = isGlyphUnderlined;
+ preUnderlineProperties = currentUnderlineProperties;
+
GenerateMesh(glyph,
positionPlusOutlineOffset,
color,
slot,
isGlyphUnderlined,
currentUnderlinePosition,
- currentUnderlineThickness,
+ maxUnderlineHeight,
meshContainer,
newTextCache,
extents,
- underlineChunkId);
+ underlineChunkId,
+ false,
+ 0u);
+
+ if(isStrikethroughGlyph)
+ {
+ GenerateMesh(glyph,
+ positionPlusOutlineOffset,
+ color,
+ NO_OUTLINE,
+ slot,
+ strikethroughGlyphsExist,
+ 0.0f,
+ maxStrikethroughHeight,
+ meshContainer,
+ newTextCache,
+ strikethroughExtents,
+ 0u,
+ true,
+ strikethroughChunkId);
+ }
lastFontId = glyph.fontId; // Prevents searching for existing blocksizes when string of the same fontId.
}
slotOutline,
false,
currentUnderlinePosition,
- currentUnderlineThickness,
+ maxUnderlineHeight,
meshContainerOutline,
newTextCache,
extents,
+ 0u,
+ false,
0u);
}
-
- //The new underlined chunk. Add new id if they are not consecutive indices (this is for Markup case)
- // Examples: "Hello <u>World</u> Hello <u>World</u>", "<u>World</u> Hello <u>World</u>", "<u> World</u> Hello <u>World</u>"
- if( isPreUnderlined && (isPreUnderlined != isGlyphUnderlined))
+ if(isPrevGlyphStrikethrough && !isStrikethroughGlyph)
{
- underlineChunkId++;
+ strikethroughChunkId++;
}
- //Keep status of underlined for previous glyph to check consecutive indices
- isPreUnderlined = isGlyphUnderlined;
+
+ isPrevGlyphStrikethrough = isStrikethroughGlyph;
}
if(addHyphen)
if(thereAreUnderlinedGlyphs)
{
// Check to see if any of the text needs an underline
- GenerateUnderlines(meshContainer, extents, underlineColor);
+ GenerateUnderlines(meshContainer, extents, viewUnderlineProperties, mapUnderlineChunkIdWithProperties);
+ }
+
+ if(strikethroughGlyphsExist)
+ {
+ // Check to see if any of the text needs a strikethrough
+ GenerateStrikethrough(meshContainer, strikethroughExtents, currentStrikethroughColor);
}
// For each MeshData object, create a mesh actor and add to the renderable actor
AtlasManager::Mesh2D& newMesh,
Vector<Extent>& extents,
float baseLine,
- bool underlineGlyph,
+ bool decorationlineGlyph,
float underlinePosition,
- float underlineThickness,
+ float lineThickness,
AtlasManager::AtlasSlot& slot,
- uint32_t underlineChunkId)
+ uint32_t underlineChunkId,
+ float strikethroughPosition,
+ uint32_t strikethroughChunkId)
{
if(slot.mImageId)
{
// Append the mesh to the existing mesh and adjust any extents
Toolkit::Internal::AtlasMeshFactory::AppendMesh(mIt->mMesh, newMesh);
- if(underlineGlyph)
+ if(decorationlineGlyph)
{
AdjustExtents(extents,
meshContainer,
right,
baseLine,
underlinePosition,
- underlineThickness,
- underlineChunkId);
+ lineThickness,
+ underlineChunkId,
+ strikethroughPosition,
+ strikethroughChunkId);
}
return;
meshRecord.mMesh = newMesh;
meshContainer.push_back(meshRecord);
- if(underlineGlyph)
+ if(decorationlineGlyph)
{
// Adjust extents for this new meshrecord
AdjustExtents(extents,
right,
baseLine,
underlinePosition,
- underlineThickness,
- underlineChunkId);
+ lineThickness,
+ underlineChunkId,
+ strikethroughPosition,
+ strikethroughChunkId);
}
}
}
float right,
float baseLine,
float underlinePosition,
- float underlineThickness,
- uint32_t underlineChunkId)
+ float lineThickness,
+ uint32_t underlineChunkId,
+ float strikethroughPosition,
+ uint32_t strikethroughChunkId)
{
bool foundExtent = false;
for(Vector<Extent>::Iterator eIt = extents.Begin(),
eIt != eEndIt;
++eIt)
{
- if(Equals(baseLine, eIt->mBaseLine) && underlineChunkId == eIt->mUnderlineChunkId)
+ if(Equals(baseLine, eIt->mBaseLine) && underlineChunkId == eIt->mUnderlineChunkId && strikethroughChunkId == eIt->mStrikethroughChunkId)
{
foundExtent = true;
if(left < eIt->mLeft)
{
eIt->mUnderlinePosition = underlinePosition;
}
- if(underlineThickness > eIt->mUnderlineThickness)
+ if(lineThickness > eIt->mLineThickness)
{
- eIt->mUnderlineThickness = underlineThickness;
+ eIt->mLineThickness = lineThickness;
}
}
}
if(!foundExtent)
{
Extent extent;
- extent.mLeft = left;
- extent.mRight = right;
- extent.mBaseLine = baseLine;
- extent.mUnderlinePosition = underlinePosition;
- extent.mUnderlineThickness = underlineThickness;
- extent.mMeshRecordIndex = index;
- extent.mUnderlineChunkId = underlineChunkId;
+ extent.mLeft = left;
+ extent.mRight = right;
+ extent.mBaseLine = baseLine;
+ extent.mUnderlinePosition = underlinePosition;
+ extent.mMeshRecordIndex = index;
+ extent.mUnderlineChunkId = underlineChunkId;
+ extent.mLineThickness = lineThickness;
+ extent.mStrikethroughPosition = strikethroughPosition;
+ extent.mStrikethroughChunkId = strikethroughChunkId;
extents.PushBack(extent);
}
}
}
}
- void GenerateUnderlines(std::vector<MeshRecord>& meshRecords,
- Vector<Extent>& extents,
- const Vector4& underlineColor)
+ void GenerateUnderlines(std::vector<MeshRecord>& meshRecords,
+ Vector<Extent>& extents,
+ const UnderlineStyleProperties& viewUnderlineProperties,
+ const std::map<uint32_t, UnderlineStyleProperties>& mapUnderlineChunkIdWithProperties)
+ {
+ AtlasManager::Mesh2D newMesh;
+ unsigned short faceIndex = 0;
+
+ for(Vector<Extent>::ConstIterator eIt = extents.Begin(),
+ eEndIt = extents.End();
+ eIt != eEndIt;
+ ++eIt)
+ {
+ AtlasManager::Vertex2D vert;
+ uint32_t index = eIt->mMeshRecordIndex;
+ Vector2 uv = mGlyphManager.GetAtlasSize(meshRecords[index].mAtlasId);
+
+ auto pairUnderlineChunkIdWithProperties = mapUnderlineChunkIdWithProperties.find(eIt->mUnderlineChunkId);
+
+ const UnderlineStyleProperties underlineProperties = (pairUnderlineChunkIdWithProperties == mapUnderlineChunkIdWithProperties.end())
+ ? viewUnderlineProperties
+ : pairUnderlineChunkIdWithProperties->second;
+
+ const Vector4& underlineColor = underlineProperties.colorDefined ? underlineProperties.color : viewUnderlineProperties.color;
+ const Text::Underline::Type& underlineType = underlineProperties.typeDefined ? underlineProperties.type : viewUnderlineProperties.type;
+ const float& dashedUnderlineGap = underlineProperties.dashGapDefined ? underlineProperties.dashGap : viewUnderlineProperties.dashGap;
+ const float& dashedUnderlineWidth = underlineProperties.dashWidthDefined ? underlineProperties.dashWidth : viewUnderlineProperties.dashWidth;
+
+ // Make sure we don't hit texture edge for single pixel texture ( filled pixel is in top left of every atlas )
+ float u = HALF / uv.x;
+ float v = HALF / uv.y;
+ float thickness = eIt->mLineThickness;
+ float ShiftLineBy = (underlineType == Text::Underline::Type::DOUBLE) ? (thickness * ONE_AND_A_HALF) : (thickness * HALF);
+ float baseLine = eIt->mBaseLine + eIt->mUnderlinePosition - ShiftLineBy;
+ float tlx = eIt->mLeft;
+ float brx = eIt->mRight;
+
+ if(underlineType == Text::Underline::Type::DASHED)
+ {
+ float dashTlx = tlx;
+ float dashBrx = tlx;
+
+ while((dashTlx >= tlx) && (dashTlx < brx) && ((dashTlx + dashedUnderlineWidth) <= brx))
+ {
+ dashBrx = dashTlx + dashedUnderlineWidth;
+
+ //The top left edge of the underline
+ vert.mPosition.x = dashTlx;
+ vert.mPosition.y = baseLine;
+ vert.mTexCoords.x = ZERO;
+ vert.mTexCoords.y = ZERO;
+ vert.mColor = underlineColor;
+ newMesh.mVertices.PushBack(vert);
+
+ //The top right edge of the underline
+ vert.mPosition.x = dashBrx;
+ vert.mPosition.y = baseLine;
+ vert.mTexCoords.x = u;
+ vert.mColor = underlineColor;
+ newMesh.mVertices.PushBack(vert);
+
+ //The bottom left edge of the underline
+ vert.mPosition.x = dashTlx;
+ vert.mPosition.y = baseLine + thickness;
+ vert.mTexCoords.x = ZERO;
+ vert.mTexCoords.y = v;
+ vert.mColor = underlineColor;
+ newMesh.mVertices.PushBack(vert);
+
+ //The bottom right edge of the underline
+ vert.mPosition.x = dashBrx;
+ vert.mPosition.y = baseLine + thickness;
+ vert.mTexCoords.x = u;
+ vert.mColor = underlineColor;
+ newMesh.mVertices.PushBack(vert);
+
+ dashTlx = dashBrx + dashedUnderlineGap; // The next dash will start at the right of the current dash plus the gap
+
+ // Six indices in counter clockwise winding
+ newMesh.mIndices.PushBack(faceIndex + 1u);
+ newMesh.mIndices.PushBack(faceIndex);
+ newMesh.mIndices.PushBack(faceIndex + 2u);
+ newMesh.mIndices.PushBack(faceIndex + 2u);
+ newMesh.mIndices.PushBack(faceIndex + 3u);
+ newMesh.mIndices.PushBack(faceIndex + 1u);
+
+ faceIndex += 4;
+
+ Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
+ }
+ }
+ else
+ {
+ // It's either SOLID or DOUBLE so we need to generate the first solid underline anyway.
+ vert.mPosition.x = tlx;
+ vert.mPosition.y = baseLine;
+ vert.mTexCoords.x = ZERO;
+ vert.mTexCoords.y = ZERO;
+ vert.mColor = underlineColor;
+ newMesh.mVertices.PushBack(vert);
+
+ vert.mPosition.x = brx;
+ vert.mPosition.y = baseLine;
+ vert.mTexCoords.x = u;
+ vert.mColor = underlineColor;
+ newMesh.mVertices.PushBack(vert);
+
+ vert.mPosition.x = tlx;
+ vert.mPosition.y = baseLine + thickness;
+ vert.mTexCoords.x = ZERO;
+ vert.mTexCoords.y = v;
+ vert.mColor = underlineColor;
+ newMesh.mVertices.PushBack(vert);
+
+ vert.mPosition.x = brx;
+ vert.mPosition.y = baseLine + thickness;
+ vert.mTexCoords.x = u;
+ vert.mColor = underlineColor;
+ newMesh.mVertices.PushBack(vert);
+
+ // Six indices in counter clockwise winding
+ newMesh.mIndices.PushBack(faceIndex + 1u);
+ newMesh.mIndices.PushBack(faceIndex);
+ newMesh.mIndices.PushBack(faceIndex + 2u);
+ newMesh.mIndices.PushBack(faceIndex + 2u);
+ newMesh.mIndices.PushBack(faceIndex + 3u);
+ newMesh.mIndices.PushBack(faceIndex + 1u);
+ faceIndex += 4;
+
+ Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
+
+ if(underlineType == Text::Underline::Type::DOUBLE)
+ {
+ baseLine += 2 * thickness;
+
+ //The top left edge of the underline
+ vert.mPosition.x = tlx;
+ vert.mPosition.y = baseLine; // Vertical start of the second underline
+ vert.mTexCoords.x = ZERO;
+ vert.mTexCoords.y = ZERO;
+ vert.mColor = underlineColor;
+ newMesh.mVertices.PushBack(vert);
+
+ //The top right edge of the underline
+ vert.mPosition.x = brx;
+ vert.mPosition.y = baseLine;
+ vert.mTexCoords.x = u;
+ vert.mColor = underlineColor;
+ newMesh.mVertices.PushBack(vert);
+
+ //The bottom left edge of the underline
+ vert.mPosition.x = tlx;
+ vert.mPosition.y = baseLine + thickness; // Vertical End of the second underline
+ vert.mTexCoords.x = ZERO;
+ vert.mTexCoords.y = v;
+ vert.mColor = underlineColor;
+ newMesh.mVertices.PushBack(vert);
+
+ //The bottom right edge of the underline
+ vert.mPosition.x = brx;
+ vert.mPosition.y = baseLine + thickness;
+ vert.mTexCoords.x = u;
+ vert.mColor = underlineColor;
+ newMesh.mVertices.PushBack(vert);
+
+ // Six indices in counter clockwise winding
+ newMesh.mIndices.PushBack(faceIndex + 1u);
+ newMesh.mIndices.PushBack(faceIndex);
+ newMesh.mIndices.PushBack(faceIndex + 2u);
+ newMesh.mIndices.PushBack(faceIndex + 2u);
+ newMesh.mIndices.PushBack(faceIndex + 3u);
+ newMesh.mIndices.PushBack(faceIndex + 1u);
+
+ faceIndex += 4;
+
+ Toolkit::Internal::AtlasMeshFactory::AppendMesh(meshRecords[index].mMesh, newMesh);
+ }
+ }
+ }
+ }
+
+ void GenerateStrikethrough(std::vector<MeshRecord>& meshRecords,
+ Vector<Extent>& extents,
+ const Vector4& strikethroughColor)
{
AtlasManager::Mesh2D newMesh;
unsigned short faceIndex = 0;
Vector2 uv = mGlyphManager.GetAtlasSize(meshRecords[index].mAtlasId);
// Make sure we don't hit texture edge for single pixel texture ( filled pixel is in top left of every atlas )
- float u = HALF / uv.x;
- float v = HALF / uv.y;
- float thickness = eIt->mUnderlineThickness;
- float baseLine = eIt->mBaseLine + eIt->mUnderlinePosition - (thickness * HALF);
- float tlx = eIt->mLeft;
- float brx = eIt->mRight;
+ float u = HALF / uv.x;
+ float v = HALF / uv.y;
+ float thickness = eIt->mLineThickness;
+ float tlx = eIt->mLeft;
+ float brx = eIt->mRight;
+ float strikethroughPosition = eIt->mStrikethroughPosition;
vert.mPosition.x = tlx;
- vert.mPosition.y = baseLine;
+ vert.mPosition.y = strikethroughPosition;
vert.mTexCoords.x = ZERO;
vert.mTexCoords.y = ZERO;
- vert.mColor = underlineColor;
+ vert.mColor = strikethroughColor;
newMesh.mVertices.PushBack(vert);
vert.mPosition.x = brx;
- vert.mPosition.y = baseLine;
+ vert.mPosition.y = strikethroughPosition;
vert.mTexCoords.x = u;
- vert.mColor = underlineColor;
+ vert.mColor = strikethroughColor;
newMesh.mVertices.PushBack(vert);
vert.mPosition.x = tlx;
- vert.mPosition.y = baseLine + thickness;
+ vert.mPosition.y = strikethroughPosition + thickness;
vert.mTexCoords.x = ZERO;
vert.mTexCoords.y = v;
- vert.mColor = underlineColor;
+ vert.mColor = strikethroughColor;
newMesh.mVertices.PushBack(vert);
vert.mPosition.x = brx;
- vert.mPosition.y = baseLine + thickness;
+ vert.mPosition.y = strikethroughPosition + thickness;
vert.mTexCoords.x = u;
- vert.mColor = underlineColor;
+ vert.mColor = strikethroughColor;
newMesh.mVertices.PushBack(vert);
// Six indices in counter clockwise winding