Reland "SkParagraph"

This is a reland of 10ad0b9b01e4b8a4721ae2ec1adee9ca7d0fe534

Original change's description:
> SkParagraph
>
> Change-Id: I0a4be75fd0c18021c201bcc1edfdfad8556edeff
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/192100
> Reviewed-by: Ben Wagner <bungeman@google.com>
> Reviewed-by: Mike Reed <reed@google.com>
> Commit-Queue: Julia Lavrova <jlavrova@google.com>

Change-Id: I46cf43eae693edf68e45345acd0eb39e04e02bfc
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/219863
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Julia Lavrova <jlavrova@google.com>
diff --git a/modules/skparagraph/src/Run.cpp b/modules/skparagraph/src/Run.cpp
new file mode 100644
index 0000000..1773ec5
--- /dev/null
+++ b/modules/skparagraph/src/Run.cpp
@@ -0,0 +1,246 @@
+// Copyright 2019 Google LLC.
+#include "modules/skparagraph/src/Run.h"
+#include <unicode/brkiter.h>
+#include "include/core/SkFontMetrics.h"
+
+namespace skia {
+namespace textlayout {
+
+Run::Run(SkSpan<const char> text,
+         const SkShaper::RunHandler::RunInfo& info,
+         SkScalar lineHeight,
+         size_t index,
+         SkScalar offsetX) {
+    TRACE_EVENT0("skia", TRACE_FUNC);
+    fFont = info.fFont;
+    fHeightMultiplier = lineHeight;
+    fBidiLevel = info.fBidiLevel;
+    fAdvance = info.fAdvance;
+    fText = SkSpan<const char>(text.begin() + info.utf8Range.begin(), info.utf8Range.size());
+
+    fIndex = index;
+    fUtf8Range = info.utf8Range;
+    fOffset = SkVector::Make(offsetX, 0);
+    fGlyphs.push_back_n(info.glyphCount);
+    fPositions.push_back_n(info.glyphCount + 1);
+    fOffsets.push_back_n(info.glyphCount + 1, SkScalar(0));
+    fClusterIndexes.push_back_n(info.glyphCount + 1);
+    info.fFont.getMetrics(&fFontMetrics);
+    fSpaced = false;
+    // To make edge cases easier:
+    fPositions[info.glyphCount] = fOffset + fAdvance;
+    fClusterIndexes[info.glyphCount] = info.utf8Range.end();
+}
+
+SkShaper::RunHandler::Buffer Run::newRunBuffer() {
+    TRACE_EVENT0("skia", TRACE_FUNC);
+    return {fGlyphs.data(), fPositions.data(), nullptr, fClusterIndexes.data(), fOffset};
+}
+
+SkScalar Run::calculateWidth(size_t start, size_t end, bool clip) const {
+    TRACE_EVENT0("skia", TRACE_FUNC);
+    SkASSERT(start <= end);
+    // clip |= end == size();  // Clip at the end of the run?
+    SkScalar offset = 0;
+    if (fSpaced && end > start) {
+        offset = fOffsets[clip ? end - 1 : end] - fOffsets[start];
+    }
+    return fPositions[end].fX - fPositions[start].fX + offset;
+}
+
+void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size, SkVector offset) const {
+    TRACE_EVENT0("skia", TRACE_FUNC);
+    SkASSERT(pos + size <= this->size());
+    const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size));
+    sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
+
+    if (fSpaced || offset.fX != 0 || offset.fY != 0) {
+        for (size_t i = 0; i < size; ++i) {
+            auto point = fPositions[i + pos];
+            if (fSpaced) {
+                point.fX += fOffsets[i + pos];
+            }
+            blobBuffer.points()[i] = point + offset;
+        }
+    } else {
+        // Good for the first line
+        sk_careful_memcpy(blobBuffer.points(), fPositions.data() + pos, size * sizeof(SkPoint));
+    }
+}
+
+// TODO: Make the search more effective
+std::tuple<bool, Cluster*, Cluster*> Run::findLimitingClusters(SkSpan<const char> text) {
+    if (text.empty()) {
+        Cluster* found = nullptr;
+        for (auto& cluster : fClusters) {
+            if (cluster.contains(text.begin())) {
+                found = &cluster;
+                break;
+            }
+        }
+        return std::make_tuple(found != nullptr, found, found);
+    }
+
+    auto first = text.begin();
+    auto last = text.end() - 1;
+
+    Cluster* start = nullptr;
+    Cluster* end = nullptr;
+    for (auto& cluster : fClusters) {
+        if (cluster.contains(first)) start = &cluster;
+        if (cluster.contains(last)) end = &cluster;
+    }
+    if (!leftToRight()) {
+        std::swap(start, end);
+    }
+
+    return std::make_tuple(start != nullptr && end != nullptr, start, end);
+}
+
+void Run::iterateThroughClustersInTextOrder(const ClusterVisitor& visitor) {
+    TRACE_EVENT0("skia", TRACE_FUNC);
+    // Can't figure out how to do it with one code for both cases without 100 ifs
+    // Can't go through clusters because there are no cluster table yet
+    if (leftToRight()) {
+        size_t start = 0;
+        size_t cluster = this->clusterIndex(start);
+        for (size_t glyph = 1; glyph <= this->size(); ++glyph) {
+            auto nextCluster = this->clusterIndex(glyph);
+            if (nextCluster == cluster) {
+                continue;
+            }
+
+            visitor(start,
+                    glyph,
+                    cluster,
+                    nextCluster,
+                    this->calculateWidth(start, glyph, glyph == size()),
+                    this->calculateHeight());
+
+            start = glyph;
+            cluster = nextCluster;
+        }
+    } else {
+        size_t glyph = this->size();
+        size_t cluster = this->fUtf8Range.begin();
+        for (int32_t start = this->size() - 1; start >= 0; --start) {
+            size_t nextCluster =
+                    start == 0 ? this->fUtf8Range.end() : this->clusterIndex(start - 1);
+            if (nextCluster == cluster) {
+                continue;
+            }
+
+            visitor(start,
+                    glyph,
+                    cluster,
+                    nextCluster,
+                    this->calculateWidth(start, glyph, glyph == 0),
+                    this->calculateHeight());
+
+            glyph = start;
+            cluster = nextCluster;
+        }
+    }
+}
+
+SkScalar Run::addSpacesAtTheEnd(SkScalar space, Cluster* cluster) {
+    TRACE_EVENT0("skia", TRACE_FUNC);
+    if (cluster->endPos() == cluster->startPos()) {
+        return 0;
+    }
+
+    fOffsets[cluster->endPos() - 1] += space;
+    // Increment the run width
+    fSpaced = true;
+    fAdvance.fX += space;
+    // Increment the cluster width
+    cluster->space(space, space);
+
+    return space;
+}
+
+SkScalar Run::addSpacesEvenly(SkScalar space, Cluster* cluster) {
+    TRACE_EVENT0("skia", TRACE_FUNC);
+    // Offset all the glyphs in the cluster
+    SkScalar shift = 0;
+    for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
+        fOffsets[i] += shift;
+        shift += space;
+    }
+    if (this->size() == cluster->endPos()) {
+        // To make calculations easier
+        fOffsets[cluster->endPos()] += shift;
+    }
+    // Increment the run width
+    fSpaced = true;
+    fAdvance.fX += shift;
+    // Increment the cluster width
+    cluster->space(shift, space);
+
+    return shift;
+}
+
+void Run::shift(const Cluster* cluster, SkScalar offset) {
+    TRACE_EVENT0("skia", TRACE_FUNC);
+    if (offset == 0) {
+        return;
+    }
+
+    fSpaced = true;
+    for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
+        fOffsets[i] += offset;
+    }
+    if (this->size() == cluster->endPos()) {
+        // To make calculations easier
+        fOffsets[cluster->endPos()] += offset;
+    }
+}
+
+void Cluster::setIsWhiteSpaces() {
+    TRACE_EVENT0("skia", TRACE_FUNC);
+    auto pos = fText.end();
+    while (--pos >= fText.begin()) {
+        auto ch = *pos;
+        if (!u_isspace(ch) && u_charType(ch) != U_CONTROL_CHAR &&
+            u_charType(ch) != U_NON_SPACING_MARK) {
+            return;
+        }
+    }
+    fWhiteSpaces = true;
+}
+
+SkScalar Cluster::sizeToChar(const char* ch) const {
+    TRACE_EVENT0("skia", TRACE_FUNC);
+    if (ch < fText.begin() || ch >= fText.end()) {
+        return 0;
+    }
+    auto shift = ch - fText.begin();
+    auto ratio = shift * 1.0 / fText.size();
+
+    return SkDoubleToScalar(fWidth * ratio);
+}
+
+SkScalar Cluster::sizeFromChar(const char* ch) const {
+    TRACE_EVENT0("skia", TRACE_FUNC);
+    if (ch < fText.begin() || ch >= fText.end()) {
+        return 0;
+    }
+    auto shift = fText.end() - ch - 1;
+    auto ratio = shift * 1.0 / fText.size();
+
+    return SkDoubleToScalar(fWidth * ratio);
+}
+
+size_t Cluster::roundPos(SkScalar s) const {
+    TRACE_EVENT0("skia", TRACE_FUNC);
+    auto ratio = (s * 1.0) / fWidth;
+    return sk_double_floor2int(ratio * size());
+}
+
+SkScalar Cluster::trimmedWidth(size_t pos) const {
+    // Find the width until the pos and return the min between trimmedWidth and the width(pos)
+    return SkTMin(this->run()->positionX(pos) - this->run()->positionX(fStart), fWidth - fSpacing);
+}
+
+}  // namespace textlayout
+}  // namespace skia