X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftext%2Ftext-geometry.cpp;h=3635d0ce856f058e975edcd53c482ae82e22bd9c;hb=d4862a88ec4fba32908dba8c1a32d1467513e5ca;hp=e599922e416ba5ac70010a100614c0cbe38783f9;hpb=12fc433eba468fe62254c4ebfb04d57697632077;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git diff --git a/dali-toolkit/internal/text/text-geometry.cpp b/dali-toolkit/internal/text/text-geometry.cpp index e599922..3635d0c 100644 --- a/dali-toolkit/internal/text/text-geometry.cpp +++ b/dali-toolkit/internal/text/text-geometry.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -20,9 +20,13 @@ // EXTERNAL INCLUDES #include +#include // INTERNAL INCLUDES #include +#include +#include +#include using namespace Dali; @@ -32,13 +36,14 @@ namespace Toolkit { namespace Text { -bool GetNextLine(GlyphIndex index, LineIndex& lineIndex, LineRun*& lineRun, GlyphIndex& lastGlyphOfLine, Length numberOfLines) +bool GetNextLine(GlyphIndex index, LineIndex& lineIndex, LineRun*& lineRun, GlyphIndex& lastGlyphOfLine, Length numberOfLines, bool& isLastLine) { if(index == lastGlyphOfLine) { ++lineIndex; if(lineIndex < numberOfLines) { + isLastLine = (lineIndex + 1 == numberOfLines); ++lineRun; return true; } @@ -47,11 +52,11 @@ bool GetNextLine(GlyphIndex index, LineIndex& lineIndex, LineRun*& lineRun, Glyp return false; } -void UpdateLineInfo(const LineRun* lineRun, float& currentLineOffset, float& currentLineHeight, GlyphIndex& lastGlyphOfLine) +void UpdateLineInfo(const LineRun* lineRun, float& currentLineOffset, float& currentLineHeight, GlyphIndex& lastGlyphOfLine, bool isLastLine) { lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u; currentLineOffset = currentLineOffset + currentLineHeight; - currentLineHeight = GetLineHeight(*lineRun); + currentLineHeight = GetLineHeight(*lineRun, isLastLine); } void GetTextGeometry(ModelPtr textModel, CharacterIndex startIndex, CharacterIndex endIndex, Vector& sizesList, Vector& positionsList) @@ -67,20 +72,29 @@ void GetTextGeometry(ModelPtr textModel, CharacterIndex startIndex, CharacterInd const CharacterIndex* const glyphToCharacterBuffer = visualModel->mGlyphsToCharacters.Begin(); const CharacterDirection* const modelCharacterDirectionsBuffer = (0u != logicalModel->mCharacterDirections.Count()) ? logicalModel->mCharacterDirections.Begin() : NULL; - if((startIndex < 0 && endIndex < 0) || (startIndex >= logicalModel->mText.Count() && endIndex >= logicalModel->mText.Count())) - return; + //Clear the lists + sizesList.Clear(); + positionsList.Clear(); - if(startIndex < 0) - startIndex = 0; + if(charactersToGlyphBuffer == nullptr || glyphsPerCharacterBuffer == nullptr || charactersPerGlyphBuffer == nullptr || glyphToCharacterBuffer == nullptr) + { + return; + } - if(endIndex < 0) - endIndex = 0; + if(startIndex >= logicalModel->mText.Count() && endIndex >= logicalModel->mText.Count()) + { + return; + } if(startIndex >= logicalModel->mText.Count()) - startIndex = logicalModel->mText.Count() - 1; + { + startIndex = static_cast(logicalModel->mText.Count() - 1u); + } if(endIndex >= logicalModel->mText.Count()) - endIndex = logicalModel->mText.Count() - 1; + { + endIndex = static_cast(logicalModel->mText.Count() - 1u); + } if(startIndex > endIndex) { @@ -106,6 +120,7 @@ void GetTextGeometry(ModelPtr textModel, CharacterIndex startIndex, CharacterInd GlyphIndex glyphEnd = *(charactersToGlyphBuffer + endIndex) + ((numberOfGlyphs > 0) ? numberOfGlyphs - 1u : 0u); LineIndex lineIndex = visualModel->GetLineOfCharacter(startIndex); Length numberOfLines = visualModel->GetTotalNumberOfLines(); + bool isLastLine = lineIndex + 1 == numberOfLines; LineIndex firstLineIndex = lineIndex; Size textInLineSize; @@ -115,7 +130,7 @@ void GetTextGeometry(ModelPtr textModel, CharacterIndex startIndex, CharacterInd //get the first line and its vertical offset float currentLineOffset = CalculateLineOffset(visualModel->mLines, firstLineIndex); - float currentLineHeight = GetLineHeight(*lineRun); + float currentLineHeight = GetLineHeight(*lineRun, isLastLine); GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1; // Check if the first/last glyph is a ligature that needs be splitted like English fi or Arabic ï»». @@ -154,9 +169,9 @@ void GetTextGeometry(ModelPtr textModel, CharacterIndex startIndex, CharacterInd positionsList.PushBack(blockPos); } - if(GetNextLine(index, lineIndex, lineRun, lastGlyphOfLine, numberOfLines)) + if(GetNextLine(index, lineIndex, lineRun, lastGlyphOfLine, numberOfLines, isLastLine)) { - UpdateLineInfo(lineRun, currentLineOffset, currentLineHeight, lastGlyphOfLine); + UpdateLineInfo(lineRun, currentLineOffset, currentLineHeight, lastGlyphOfLine, isLastLine); } // Ignore any glyph that was removed continue; @@ -164,16 +179,16 @@ void GetTextGeometry(ModelPtr textModel, CharacterIndex startIndex, CharacterInd } else { - if((ellipsisPosition == DevelText::EllipsisPosition::END) && (index >= endIndexOfGlyphs)) + if((ellipsisPosition == DevelText::EllipsisPosition::END) && (index > endIndexOfGlyphs)) { //skip remaining elided glyphs break; } else if((ellipsisPosition == DevelText::EllipsisPosition::START) && (index <= startIndexOfGlyphs)) { - if(GetNextLine(index, lineIndex, lineRun, lastGlyphOfLine, numberOfLines)) + if(GetNextLine(index, lineIndex, lineRun, lastGlyphOfLine, numberOfLines, isLastLine)) { - UpdateLineInfo(lineRun, currentLineOffset, currentLineHeight, lastGlyphOfLine); + UpdateLineInfo(lineRun, currentLineOffset, currentLineHeight, lastGlyphOfLine, isLastLine); } continue; @@ -220,9 +235,9 @@ void GetTextGeometry(ModelPtr textModel, CharacterIndex startIndex, CharacterInd currentSize.y = currentLineHeight; // if there is next line to retrieve. - if(GetNextLine(index, lineIndex, lineRun, lastGlyphOfLine, numberOfLines)) + if(GetNextLine(index, lineIndex, lineRun, lastGlyphOfLine, numberOfLines, isLastLine)) { - UpdateLineInfo(lineRun, currentLineOffset, currentLineHeight, lastGlyphOfLine); + UpdateLineInfo(lineRun, currentLineOffset, currentLineHeight, lastGlyphOfLine, isLastLine); } } @@ -231,7 +246,7 @@ void GetTextGeometry(ModelPtr textModel, CharacterIndex startIndex, CharacterInd blockPos = currentPosition; blockSize = currentSize; } - else if((isPrevoiusRightToLeft != isCurrentRightToLeft) || (blockPos.y != currentPosition.y)) //new direction or new line + else if((isPrevoiusRightToLeft != isCurrentRightToLeft) || (!Dali::Equals(blockPos.y, currentPosition.y))) //new direction or new line { sizesList.PushBack(blockSize); positionsList.PushBack(blockPos); @@ -257,6 +272,249 @@ void GetTextGeometry(ModelPtr textModel, CharacterIndex startIndex, CharacterInd positionsList.PushBack(blockPos); } +float GetLineLeft(const LineRun& lineRun) +{ + return lineRun.alignmentOffset; +} + +float GetLineTop(const Vector& lines, const LineRun& lineRun) +{ + float lineTop = 0; + const int numberOfLines = (int)lines.Count(); + + int currentLineIndex = 0; + Vector::ConstIterator endIt = (&lineRun); + for(Vector::Iterator it = lines.Begin(); + it != endIt; + ++it, ++currentLineIndex) + { + LineRun& line = *it; + bool isLastLine = (currentLineIndex + 1) == numberOfLines; + lineTop += GetLineHeight(line, isLastLine); + } + + return lineTop; +} + +float GetLineWidth(const LineRun& lineRun) +{ + return lineRun.width; +} + +Rect GetLineBoundingRect(ModelPtr textModel, const uint32_t lineIndex) +{ + if(textModel->mVisualModel == nullptr) + { + return {0, 0, 0, 0}; + } + + Length numberOfLines = textModel->mVisualModel->GetTotalNumberOfLines(); + + if(lineIndex >= numberOfLines) + { + return {0, 0, 0, 0}; + } + + const Vector& lines = textModel->mVisualModel->mLines; + const LineRun& lineRun = lines[lineIndex]; + bool isFirstLine = lineIndex == 0; + bool isLastLine = (lineIndex + 1) == numberOfLines; + + // Calculate the Left(lineX) = X position. + float lineX = GetLineLeft(lineRun) + textModel->mScrollPosition.x; + + // Calculate the Top(lineY) = PreviousHeights. + // If the line is the first line of the text; its top = 0. + float lineY = (isFirstLine ? 0 : GetLineTop(lines, lineRun)) + textModel->mScrollPosition.y; + + // The rectangle contains the width and height: + float lineWidth = GetLineWidth(lineRun); + float lineHeight = GetLineHeight(lineRun, isLastLine); + + return {lineX, lineY, lineWidth, lineHeight}; +} + +float GetCharacterLeft(const GlyphInfo& glyph, const Vector2& characterPosition) +{ + return characterPosition.x - glyph.xBearing; +} + +float GetCharacterTop(const float& yPosition) +{ + return (-1 * yPosition); +} + +float GetCharacterHeight(const GlyphInfo& glyph) +{ + return glyph.height; +} + +float GetCharacterWidth(const GlyphInfo& glyph) +{ + return glyph.advance; +} + +Rect<> GetCharacterBoundingRect(ModelPtr textModel, const uint32_t charIndex) +{ + if(textModel->mVisualModel == nullptr) + { + return {0, 0, 0, 0}; + } + + VisualModelPtr& visualModel = textModel->mVisualModel; + LogicalModelPtr& logicalModel = textModel->mLogicalModel; + + if(charIndex >= logicalModel->mText.Count() || visualModel->mLines.Empty()) + { + return {0, 0, 0, 0}; + } + + const Vector& glyphPositions = visualModel->mGlyphPositions; + const Vector& glyphs = visualModel->mGlyphs; + const Vector& lines = visualModel->mLines; + + //For each character, the index of the first glyph. + const GlyphIndex glyphIndex = visualModel->mCharactersToGlyph[charIndex]; //took its glyphs + + const Vector2& characterPosition = glyphPositions[glyphIndex]; + const GlyphInfo& glyphInfo = glyphs[glyphIndex]; + + // GetLineOfCharacter function returns 0 if the lines are empty + const int lineIndex = visualModel->GetLineOfCharacter(charIndex); + const LineRun& lineRun = lines[lineIndex]; + + //Calculate the left: x position of the glyph + alignmentOffset of the line + mScrollPosition.x. + float characterX = lineRun.alignmentOffset + GetCharacterLeft(glyphInfo, characterPosition) + textModel->mScrollPosition.x; + + //Calculate the Top(characterY): position.Y + previouse lines height + mScrollPosition.y. + bool isFirstLine = lineIndex == 0; + + //If the line is the first line of the text; its top = 0. + float lineY = (isFirstLine ? 0 : GetLineTop(lines, lineRun)); + + float characterY = lineY + GetCharacterTop(characterPosition.y) + textModel->mScrollPosition.y; + + //The rectangle contains the width and height: + float characterWidth = GetCharacterWidth(glyphInfo); + float characterHeight = GetCharacterHeight(glyphInfo); + + return {characterX, characterY, characterWidth, characterHeight}; +} + +int GetCharIndexAtPosition(ModelPtr textModel, float visualX, float visualY) +{ + if(textModel == nullptr) + { + return -1; + } + + VisualModelPtr& visualModel = textModel->mVisualModel; + + const int totalNumberOfGlyphs = visualModel->mGlyphs.Count(); + const int totalNumberOfLines = visualModel->mLines.Count(); + + if((0 == totalNumberOfGlyphs) || + (0 == totalNumberOfLines)) + { + return -1; + } + + //The top point of the view = 0. + if(visualY < 0) + { + return -1; + } + + const Vector& lines = visualModel->mLines; + + float lineTop = 0.f; + int lineIndex = 0; + int high = totalNumberOfLines; + int low = -1; + int guess; + + // Searching for the correct line. + while(high - low > 1) + { + guess = (high + low) / 2; + Vector::ConstIterator it = lines.Begin() + guess; + + lineTop = GetLineTop(lines, *it); + + if(lineTop > visualY) + { + high = guess; + } + else + { + low = guess; + } + } + + if(low < 0) + { + lineIndex = 0; + } + else + { + lineIndex = low; + } + + bool isLastLine = lineIndex + 1 == totalNumberOfLines; + + if(isLastLine) + { + const LineRun& line = *(visualModel->mLines.Begin() + lineIndex); + float lineHeight = GetLineHeight(line, isLastLine); + + // If the visualY is placed after the last line. + if(visualY > lineTop + lineHeight) + { + return -1; + } + } + + // Start searching for the visualX + const LineRun& line = *(visualModel->mLines.Begin() + lineIndex); + + visualX -= line.alignmentOffset; + + // Positions of the glyphs + const Vector2* const positionsBuffer = visualModel->mGlyphPositions.Begin(); + + const CharacterIndex startCharacter = line.characterRun.characterIndex; + const CharacterIndex endCharacter = line.characterRun.characterIndex + line.characterRun.numberOfCharacters - 1; + + CharacterIndex characterIndexAtPosition = -1; + CharacterIndex characterIndex = startCharacter; + float characterPosition; + float rightMostCharacterPosition; + + for(; characterIndex != endCharacter; characterIndex++) + { + characterPosition = positionsBuffer[characterIndex].x; + rightMostCharacterPosition = positionsBuffer[characterIndex+1].x; + + if(visualX < rightMostCharacterPosition && visualX >= characterPosition) + { + characterIndexAtPosition = characterIndex; + break; + } + } + + if(characterIndex == endCharacter) + { + // If the visualX is within the last character position or it comes after the last character; we return the last character. + rightMostCharacterPosition = positionsBuffer[endCharacter].x + GetCharacterWidth(visualModel->mGlyphs[endCharacter]); + + if(visualX >= positionsBuffer[endCharacter].x && visualX < rightMostCharacterPosition) + { + characterIndexAtPosition = endCharacter; + } + } + + return characterIndexAtPosition; +} } // namespace Text } // namespace Toolkit