blob: 0a263443580aca81ed1b7c35702b3ab1f1b79555 [file] [log] [blame]
// Copyright 2019 Google LLC.
#ifndef TextLine_DEFINED
#define TextLine_DEFINED
#include "include/core/SkPoint.h"
#include "include/core/SkRect.h"
#include "include/core/SkScalar.h"
#include "include/private/SkTArray.h"
#include "modules/skparagraph/include/DartTypes.h"
#include "modules/skparagraph/include/Metrics.h"
#include "modules/skparagraph/include/TextStyle.h"
#include "modules/skparagraph/src/Run.h"
#include <stddef.h>
#include <functional>
#include <memory>
#include <vector>
class SkCanvas;
class SkString;
namespace skia {
namespace textlayout {
class ParagraphImpl;
class TextLine {
struct ClipContext {
const Run* run;
size_t pos;
size_t size;
SkScalar fTextShift; // Shifts the text inside the run so it's placed at the right position
SkRect clip;
bool clippingNeeded;
TextLine() = default;
TextLine(const TextLine&) = delete;
TextLine& operator=(const TextLine&) = delete;
TextLine(TextLine&&) = default;
TextLine& operator=(TextLine&&) = default;
~TextLine() = default;
TextLine(ParagraphImpl* owner,
SkVector offset,
SkVector advance,
BlockRange blocks,
TextRange text,
TextRange textWithSpaces,
ClusterRange clusters,
ClusterRange clustersWithGhosts,
SkScalar widthWithSpaces,
InternalLineMetrics sizes);
TextRange trimmedText() const { return fTextRange; }
TextRange textWithSpaces() const { return fTextWithWhitespacesRange; }
ClusterRange clusters() const { return fClusterRange; }
ClusterRange clustersWithSpaces() { return fGhostClusterRange; }
Run* ellipsis() const { return fEllipsis.get(); }
InternalLineMetrics sizes() const { return fSizes; }
bool empty() const { return fTextRange.empty(); }
SkScalar spacesWidth() { return fWidthWithSpaces - width(); }
SkScalar height() const { return fAdvance.fY; }
SkScalar width() const {
return fAdvance.fX + (fEllipsis != nullptr ? fEllipsis->fAdvance.fX : 0);
SkScalar shift() const { return fShift; }
SkVector offset() const;
SkScalar alphabeticBaseline() const { return fSizes.alphabeticBaseline(); }
SkScalar ideographicBaseline() const { return fSizes.ideographicBaseline(); }
SkScalar baseline() const { return fSizes.baseline(); }
using RunVisitor = std::function<bool(const Run* run, SkScalar runOffset, TextRange textRange, SkScalar* width)>;
void iterateThroughVisualRuns(bool includingGhostSpaces, const RunVisitor& runVisitor) const;
using RunStyleVisitor = std::function<void(TextRange textRange, const TextStyle& style, const ClipContext& context)>;
SkScalar iterateThroughSingleRunByStyles(const Run* run, SkScalar runOffset, TextRange textRange,
StyleType styleType, const RunStyleVisitor& visitor) const;
using ClustersVisitor = std::function<bool(const Cluster* cluster, bool ghost)>;
void iterateThroughClustersInGlyphsOrder(bool reverse, bool includeGhosts, const ClustersVisitor& visitor) const;
void format(TextAlign align, SkScalar maxWidth);
SkRect paint(SkCanvas* canvas, SkScalar x, SkScalar y);
void visit(SkScalar x, SkScalar y);
void createEllipsis(SkScalar maxWidth, const SkString& ellipsis, bool ltr);
// For testing internal structures
void scanStyles(StyleType style, const RunStyleVisitor& visitor);
void setMaxRunMetrics(const InternalLineMetrics& metrics) { fMaxRunMetrics = metrics; }
InternalLineMetrics getMaxRunMetrics() const { return fMaxRunMetrics; }
bool isFirstLine();
bool isLastLine();
void getRectsForRange(TextRange textRange, RectHeightStyle rectHeightStyle, RectWidthStyle rectWidthStyle, std::vector<TextBox>& boxes);
void getRectsForPlaceholders(std::vector<TextBox>& boxes);
PositionWithAffinity getGlyphPositionAtCoordinate(SkScalar dx);
ClipContext measureTextInsideOneRun(TextRange textRange,
const Run* run,
SkScalar runOffsetInLine,
SkScalar textOffsetInRunInLine,
bool includeGhostSpaces,
bool limitToGraphemes) const;
LineMetrics getMetrics() const;
SkRect extendHeight(const ClipContext& context) const;
SkScalar metricsWithoutMultiplier(TextHeightBehavior correction);
void shiftVertically(SkScalar shift) { fOffset.fY += shift; }
bool endsWithHardLineBreak() const;
std::unique_ptr<Run> shapeEllipsis(const SkString& ellipsis, const Run& run);
void justify(SkScalar maxWidth);
void buildTextBlob(SkCanvas* canvas, SkScalar x, SkScalar y, TextRange textRange, const TextStyle& style, const ClipContext& context);
void paintBackground(SkCanvas* canvas, SkScalar x, SkScalar y, TextRange textRange, const TextStyle& style, const ClipContext& context) const;
SkRect paintShadow(SkCanvas* canvas, SkScalar x, SkScalar y, TextRange textRange, const TextStyle& style, const ClipContext& context) const;
void paintDecorations(SkCanvas* canvas, SkScalar x, SkScalar y, TextRange textRange, const TextStyle& style, const ClipContext& context) const;
void shiftCluster(const Cluster* cluster, SkScalar shift, SkScalar prevShift);
ParagraphImpl* fOwner;
BlockRange fBlockRange;
TextRange fTextRange;
TextRange fTextWithWhitespacesRange;
ClusterRange fClusterRange;
ClusterRange fGhostClusterRange;
// Avoid the malloc/free in the common case of one run per line
SkSTArray<1, size_t, true> fRunsInVisualOrder;
SkVector fAdvance; // Text size
SkVector fOffset; // Text position
SkScalar fShift; // Let right
SkScalar fWidthWithSpaces;
std::unique_ptr<Run> fEllipsis; // In case the line ends with the ellipsis
InternalLineMetrics fSizes; // Line metrics as a max of all run metrics and struts
InternalLineMetrics fMaxRunMetrics; // No struts - need it for GetRectForRange(max height)
bool fHasBackground;
bool fHasShadows;
bool fHasDecorations;
LineMetricStyle fAscentStyle;
LineMetricStyle fDescentStyle;
struct TextBlobRecord {
void paint(SkCanvas* canvas, SkScalar x, SkScalar y);
sk_sp<SkTextBlob> fBlob;
SkPoint fOffset = SkPoint::Make(0.0f, 0.0f);
SkPaint fPaint;
SkRect fBounds = SkRect::MakeEmpty();
bool fClippingNeeded = false;
SkRect fClipRect = SkRect::MakeEmpty();
// Extra fields only used for the (experimental) visitor
const Run* fVisitor_Run;
size_t fVisitor_Pos;
bool fTextBlobCachePopulated;
std::vector<TextBlobRecord> fTextBlobCache;
} // namespace textlayout
} // namespace skia
#endif // TextLine_DEFINED