Reland "Introduce SkGlyphSourceBuffer"
This is a reland of 77c53087c1ea92bc6c7e436f60746da7605cad98
Original change's description:
> Introduce SkGlyphSourceBuffer
>
> SkGlyphSourceBuffer provides a system for taking rejected glyphs from
> one glyph drawing stage, and turns them into the source for the next stage.
> It is rarely used, so it tries to conserve memory.
>
> Change-Id: I5275ffa3e804fc494eb2f5803e0cf2d148a755f7
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/248260
> Reviewed-by: Ben Wagner <bungeman@google.com>
> Commit-Queue: Herb Derby <herb@google.com>
Change-Id: Ib90508a21993b12c71ee86cbdeb51c4d4c2ec913
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/249128
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Herb Derby <herb@google.com>
diff --git a/src/core/SkGlyphBuffer.cpp b/src/core/SkGlyphBuffer.cpp
index a3ef7fb..e7ccf57 100644
--- a/src/core/SkGlyphBuffer.cpp
+++ b/src/core/SkGlyphBuffer.cpp
@@ -9,6 +9,11 @@
#include "src/core/SkGlyphRunPainter.h"
#include "src/core/SkStrikeForGPU.h"
+void SkSourceGlyphBuffer::reset() {
+ fRejectedGlyphIDs.reset();
+ fRejectedPositions.reset();
+}
+
void SkDrawableGlyphBuffer::ensureSize(size_t size) {
if (size > fMaxSize) {
fMultiBuffer.reset(size);
diff --git a/src/core/SkGlyphBuffer.h b/src/core/SkGlyphBuffer.h
index ca7541a..abfd617 100644
--- a/src/core/SkGlyphBuffer.h
+++ b/src/core/SkGlyphBuffer.h
@@ -14,6 +14,71 @@
class SkStrikeForGPU;
struct SkGlyphPositionRoundingSpec;
+// SkSourceGlyphBuffer is the source of glyphs between the different stages of character drawing.
+// It starts with the glyphs and positions from the SkGlyphRun as the first source. When glyphs
+// are reject by a stage they become the source for the next stage.
+class SkSourceGlyphBuffer {
+public:
+ SkSourceGlyphBuffer() = default;
+
+ void setSource(SkZip<const SkGlyphID, const SkPoint> source) {
+ this->~SkSourceGlyphBuffer();
+ new (this) SkSourceGlyphBuffer{source};
+ }
+
+ void reset();
+
+ void reject(size_t index) {
+ SkASSERT(index < fSource.size());
+ if (!this->sourceIsRejectBuffers()) {
+ // Need to expand the buffers for first use. All other reject sets will be fewer than
+ // this one.
+ SkGlyphID glyphID; SkPoint pos;
+ std::tie(glyphID, pos) = fSource[index];
+ fRejectedGlyphIDs.push_back(glyphID);
+ fRejectedPositions.push_back(pos);
+ fRejectSize++;
+ } else {
+ SkASSERT(fRejectSize < fRejects.size());
+ fRejects[fRejectSize++] = fSource[index];
+ }
+ }
+
+ void reject(size_t index, int rejectedMaxDimension) {
+ fRejectedMaxDimension = SkTMax(fRejectedMaxDimension, rejectedMaxDimension);
+ this->reject(index);
+ }
+
+ SkZip<const SkGlyphID, const SkPoint> flipRejectsToSource() {
+ fRejects = SkMakeZip(fRejectedGlyphIDs, fRejectedPositions).first(fRejectSize);
+ fSource = fRejects;
+ fRejectSize = 0;
+ fSourceMaxDimension = fRejectedMaxDimension;
+ fRejectedMaxDimension = 0;
+ return fSource;
+ }
+
+ SkZip<const SkGlyphID, const SkPoint> source() const { return fSource; }
+
+ int rejectedMaxDimension() const { return fSourceMaxDimension; }
+
+private:
+ SkSourceGlyphBuffer(const SkZip<const SkGlyphID, const SkPoint>& source) {
+ fSource = source;
+ }
+ bool sourceIsRejectBuffers() const {
+ return fSource.get<0>().data() == fRejectedGlyphIDs.data();
+ }
+
+ SkZip<const SkGlyphID, const SkPoint> fSource;
+ size_t fRejectSize{0};
+ int fSourceMaxDimension{0};
+ int fRejectedMaxDimension{0};
+ SkZip<SkGlyphID, SkPoint> fRejects;
+ SkSTArray<4, SkGlyphID> fRejectedGlyphIDs;
+ SkSTArray<4, SkPoint> fRejectedPositions;
+};
+
// A memory format that allows an SkPackedGlyphID, SkGlyph*, and SkPath* to occupy the same
// memory. This allows SkPackedGlyphIDs as input, and SkGlyph*/SkPath* as output using the same
// memory.
diff --git a/src/core/SkZip.h b/src/core/SkZip.h
index 1b38b41..1249fbb 100644
--- a/src/core/SkZip.h
+++ b/src/core/SkZip.h
@@ -8,7 +8,6 @@
#ifndef SkZip_DEFINED
#define SkZip_DEFINED
-#include <cstddef>
#include <iterator>
#include <tuple>
#include <type_traits>
@@ -48,10 +47,10 @@
};
template<typename T>
- using make_nullptr = std::integral_constant<std::nullptr_t, nullptr>;
+ static constexpr T* nullify = nullptr;
public:
- constexpr SkZip() : fPointers{make_nullptr<Ts*>::value...}, fSize{0} {}
+ constexpr SkZip() : fPointers{nullify<Ts>...}, fSize{0} {}
constexpr SkZip(size_t) = delete;
constexpr SkZip(size_t size, Ts*... ts)
: fPointers{ts...}
@@ -181,6 +180,10 @@
};
template<typename... Ts>
+template<typename T>
+constexpr T* SkZip<Ts...>::nullify;
+
+template<typename... Ts>
inline constexpr auto SkMakeZip(Ts&& ... ts) {
return SkMakeZipDetail::MakeZip(std::forward<Ts>(ts)...);
}
diff --git a/tests/SkGlyphBufferTest.cpp b/tests/SkGlyphBufferTest.cpp
index ea6c5d5..4ce5041 100644
--- a/tests/SkGlyphBufferTest.cpp
+++ b/tests/SkGlyphBufferTest.cpp
@@ -11,6 +11,72 @@
#include "src/core/SkScalerContext.h"
#include "tests/Test.h"
+
+DEF_TEST(SkSourceGlyphBufferBasic, reporter) {
+ SkSourceGlyphBuffer rejects;
+ // Positions are picked to avoid precision problems.
+ const SkPoint positions[] = {{10.25,10.25}, {20.5,10.25}, {30.75,10.25}, {40,10.25}};
+ const SkGlyphID glyphIDs[] = {1, 2, 3, 4};
+ auto source = SkMakeZip(glyphIDs, positions);
+
+ rejects.setSource(source);
+ for (auto t : SkMakeEnumerate(rejects.source())) {
+ size_t i; SkGlyphID glyphID; SkPoint pos;
+ std::forward_as_tuple(i, std::tie(glyphID, pos)) = t;
+ REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[i]));
+ REPORTER_ASSERT(reporter, pos == std::get<1>(source[i]));
+ }
+ // Reject a couple of glyphs.
+ rejects.reject(1);
+ rejects.reject(2, 100);
+ rejects.flipRejectsToSource();
+ REPORTER_ASSERT(reporter, rejects.rejectedMaxDimension() == 100);
+ for (auto t : SkMakeEnumerate(rejects.source())) {
+ size_t i; SkGlyphID glyphID; SkPoint pos;
+ std::forward_as_tuple(i, std::tie(glyphID, pos)) = t;
+ // This will index 1 and 2 from the original source.
+ size_t j = i + 1;
+ REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[j]));
+ REPORTER_ASSERT(reporter, pos == std::get<1>(source[j]));
+ }
+
+ // Reject an additional glyph
+ rejects.reject(0, 10);
+ rejects.flipRejectsToSource();
+ REPORTER_ASSERT(reporter, rejects.rejectedMaxDimension() == 10);
+ for (auto t : SkMakeEnumerate(rejects.source())) {
+ size_t i; SkGlyphID glyphID; SkPoint pos;
+ std::forward_as_tuple(i, std::tie(glyphID, pos)) = t;
+ // This will index 1 from the original source.
+ size_t j = i + 1;
+ REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[j]));
+ REPORTER_ASSERT(reporter, pos == std::get<1>(source[j]));
+ }
+
+ // Start all over
+ rejects.setSource(source);
+ for (auto t : SkMakeEnumerate(rejects.source())) {
+ size_t i; SkGlyphID glyphID; SkPoint pos;
+ std::forward_as_tuple(i, std::tie(glyphID, pos)) = t;
+ REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[i]));
+ REPORTER_ASSERT(reporter, pos == std::get<1>(source[i]));
+ }
+
+ // Check that everything is working after calling setSource.
+ rejects.reject(1);
+ rejects.reject(2, 100);
+ rejects.flipRejectsToSource();
+ REPORTER_ASSERT(reporter, rejects.rejectedMaxDimension() == 100);
+ for (auto t : SkMakeEnumerate(rejects.source())) {
+ size_t i; SkGlyphID glyphID; SkPoint pos;
+ std::forward_as_tuple(i, std::tie(glyphID, pos)) = t;
+ // This will index 1 and 2 from the original source.
+ size_t j = i + 1;
+ REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[j]));
+ REPORTER_ASSERT(reporter, pos == std::get<1>(source[j]));
+ }
+}
+
DEF_TEST(SkDrawableGlyphBufferBasic, reporter) {
// Positions are picked to avoid precision problems.
const SkPoint positions[] = {{10.25,10.25}, {20.5,10.25}, {30.75,10.25}, {40,10.25}};