aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/core/SkPaint.h1
-rw-r--r--src/core/SkGlyph.cpp5
-rw-r--r--src/core/SkGlyph.h1
-rw-r--r--src/core/SkGlyphCache.cpp25
-rw-r--r--src/core/SkGlyphCache.h10
-rw-r--r--src/core/SkRemoteGlyphCache.cpp324
-rw-r--r--src/core/SkRemoteGlyphCache.h20
-rw-r--r--tests/SkRemoteGlyphCacheTest.cpp57
8 files changed, 297 insertions, 146 deletions
diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h
index 3cc63b8fe8..85c2e3b2f7 100644
--- a/include/core/SkPaint.h
+++ b/include/core/SkPaint.h
@@ -1690,6 +1690,7 @@ private:
friend class SkPDFDevice;
friend class SkScalerContext; // for computeLuminanceColor()
friend class SkTextBaseIter;
+ friend class SkTextBlobCacheDiffCanvas;
};
#endif
diff --git a/src/core/SkGlyph.cpp b/src/core/SkGlyph.cpp
index 836a47d608..9e040e39a4 100644
--- a/src/core/SkGlyph.cpp
+++ b/src/core/SkGlyph.cpp
@@ -60,6 +60,11 @@ static size_t format_rowbytes(int width, SkMask::Format format) {
: width * format_alignment(format);
}
+size_t SkGlyph::formatAlignment() const {
+ auto format = static_cast<SkMask::Format>(fMaskFormat);
+ return format_alignment(format);
+}
+
size_t SkGlyph::allocImage(SkArenaAlloc* alloc) {
auto size = this->computeImageSize();
auto format = static_cast<SkMask::Format>(fMaskFormat);
diff --git a/src/core/SkGlyph.h b/src/core/SkGlyph.h
index dfb51cb35a..307042f724 100644
--- a/src/core/SkGlyph.h
+++ b/src/core/SkGlyph.h
@@ -155,6 +155,7 @@ public:
void initWithGlyphID(SkPackedGlyphID glyph_id);
+ size_t formatAlignment() const;
size_t allocImage(SkArenaAlloc* alloc);
size_t rowBytes() const;
diff --git a/src/core/SkGlyphCache.cpp b/src/core/SkGlyphCache.cpp
index d9d8d41eb1..bf5f77c817 100644
--- a/src/core/SkGlyphCache.cpp
+++ b/src/core/SkGlyphCache.cpp
@@ -194,7 +194,9 @@ const void* SkGlyphCache::findImage(const SkGlyph& glyph) {
return glyph.fImage;
}
-void SkGlyphCache::initializeImage(const volatile void* data, size_t size, SkGlyph* glyph) {
+bool SkGlyphCache::initializeImage(const volatile void* data, size_t size, SkGlyph* glyph) {
+ if (glyph->fImage) return false;
+
if (glyph->fWidth > 0 && glyph->fWidth < kMaxGlyphWidth) {
size_t allocSize = glyph->allocImage(&fAlloc);
// check that alloc() actually succeeded
@@ -204,6 +206,8 @@ void SkGlyphCache::initializeImage(const volatile void* data, size_t size, SkGly
fMemoryUsed += size;
}
}
+
+ return true;
}
const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
@@ -225,6 +229,25 @@ const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) {
return glyph.fPathData ? glyph.fPathData->fPath : nullptr;
}
+bool SkGlyphCache::initializePath(SkGlyph* glyph, const volatile void* data, size_t size) {
+ if (glyph->fPathData) return false;
+
+ if (glyph->fWidth) {
+ SkGlyph::PathData* pathData = fAlloc.make<SkGlyph::PathData>();
+ glyph->fPathData = pathData;
+ pathData->fIntercept = nullptr;
+ SkPath* path = new SkPath;
+ if (!path->readFromMemory(const_cast<const void*>(data), size)) {
+ delete path;
+ return false;
+ }
+ pathData->fPath = path;
+ fMemoryUsed += compute_path_size(*path);
+ }
+
+ return true;
+}
+
#include "../pathops/SkPathOpsCubic.h"
#include "../pathops/SkPathOpsQuad.h"
diff --git a/src/core/SkGlyphCache.h b/src/core/SkGlyphCache.h
index e069efd905..2e7355779a 100644
--- a/src/core/SkGlyphCache.h
+++ b/src/core/SkGlyphCache.h
@@ -88,9 +88,10 @@ public:
*/
const void* findImage(const SkGlyph&);
- /** Initializes the image associated with the glyph with |data|.
+ /** Initializes the image associated with the glyph with |data|. Returns false if an image
+ * already exists.
*/
- void initializeImage(const volatile void* data, size_t size, SkGlyph*);
+ bool initializeImage(const volatile void* data, size_t size, SkGlyph*);
/** If the advance axis intersects the glyph's path, append the positions scaled and offset
to the array (if non-null), and set the count to the updated array length.
@@ -103,6 +104,11 @@ public:
*/
const SkPath* findPath(const SkGlyph&);
+ /** Initializes the path associated with the glyph with |data|. Returns false if a path
+ * already exits or data is invalid.
+ */
+ bool initializePath(SkGlyph*, const volatile void* data, size_t size);
+
/** Return the vertical metrics for this strike.
*/
const SkPaint::FontMetrics& getFontMetrics() const {
diff --git a/src/core/SkRemoteGlyphCache.cpp b/src/core/SkRemoteGlyphCache.cpp
index 985c0655e6..c7f1f4497b 100644
--- a/src/core/SkRemoteGlyphCache.cpp
+++ b/src/core/SkRemoteGlyphCache.cpp
@@ -13,12 +13,18 @@
#include <tuple>
#include "SkDevice.h"
+#include "SkDraw.h"
#include "SkFindAndPlaceGlyph.h"
+#include "SkPathEffect.h"
#include "SkStrikeCache.h"
#include "SkTextBlobRunIterator.h"
#include "SkTraceEvent.h"
#include "SkTypeface_remote.h"
+#if SK_SUPPORT_GPU
+#include "GrDrawOpAtlas.h"
+#endif
+
static SkDescriptor* auto_descriptor_from_desc(const SkDescriptor* source_desc,
SkFontID font_id,
SkAutoDescriptor* ad) {
@@ -54,33 +60,6 @@ static SkDescriptor* auto_descriptor_from_desc(const SkDescriptor* source_desc,
return desc;
}
-template <typename T>
-class ArraySlice final : public std::tuple<const T*, size_t> {
-public:
- // Additional constructors as needed.
- ArraySlice(const T* data, size_t size) : fData{data}, fSize{size} { }
- ArraySlice() : ArraySlice<T>(nullptr, 0) { }
-
- const T* begin() {
- return this->data();
- }
- const T* end() {
- return &this->data()[this->size()];
- }
-
- const T* data() const {
- return fData;
- }
-
- size_t size() const {
- return fSize;
- }
-
-private:
- const T* fData;
- size_t fSize;
-};
-
// -- Serializer ----------------------------------------------------------------------------------
size_t pad(size_t size, size_t alignment) { return (size + (alignment - 1)) & ~(alignment - 1); }
@@ -113,19 +92,13 @@ public:
memcpy(result, &desc, desc.getLength());
}
- template <typename T>
- T* allocateArray(int count) {
- auto result = allocate(sizeof(T) * count, alignof(T));
- return new (result) T[count];
- }
-
-private:
void* allocate(size_t size, size_t alignment) {
size_t aligned = pad(fBuffer->size(), alignment);
fBuffer->resize(aligned + size);
return &(*fBuffer)[aligned];
}
+private:
std::vector<uint8_t>* fBuffer;
};
@@ -157,14 +130,8 @@ public:
return true;
}
- template <typename T>
- ArraySlice<T> readArray(int count) {
- size_t size = count * sizeof(T);
- const T* base = (const T*)this->ensureAtLeast(size, alignof(T));
- if (!base) return ArraySlice<T>();
-
- ArraySlice<T> result = ArraySlice<T>{base, (uint32_t)count};
- return result;
+ const volatile void* read(size_t size, size_t alignment) {
+ return this->ensureAtLeast(size, alignment);
}
private:
@@ -185,6 +152,21 @@ private:
size_t fBytesRead = 0u;
};
+// Paths use a SkWriter32 which requires 4 byte alignment.
+static const size_t kPathAlignment = 4u;
+
+bool read_path(Deserializer* deserializer, SkGlyph* glyph, SkGlyphCache* cache) {
+ size_t pathSize = 0u;
+ if (!deserializer->read<size_t>(&pathSize)) return false;
+
+ if (pathSize == 0u) return true;
+
+ auto* path = deserializer->read(pathSize, kPathAlignment);
+ if (!path) return false;
+
+ return cache->initializePath(glyph, path, pathSize);
+}
+
size_t SkDescriptorMapOperators::operator()(const SkDescriptor* key) const {
return key->getChecksum();
}
@@ -281,15 +263,27 @@ void SkTextBlobCacheDiffCanvas::processGlyphRun(
FAIL_AND_RETURN
}
+ if (it.positioning() == SkTextBlob::kDefault_Positioning) {
+ // Default positioning needs advances. Can't do that.
+ TRACE_EVENT0("skia", "kDefault_Positioning");
+ FAIL_AND_RETURN
+ }
+
+ SkMatrix runMatrix{fDeviceMatrix};
+ runMatrix.preConcat(this->getTotalMatrix());
+ runMatrix.preTranslate(position.x(), position.y());
+ runMatrix.preTranslate(it.offset().x(), it.offset().y());
+
+ // If the matrix has perspective, we fall back to using distance field text or paths.
+ // TODO: Add distance field text support, and FallbackTextHelper logic from GrAtlasTextContext.
+ if (SkDraw::ShouldDrawTextAsPaths(runPaint, runMatrix)) {
+ this->processGlyphRunForPaths(it, runPaint);
+ return;
+ }
+
using PosFn = SkPoint(*)(int index, const SkScalar* pos);
PosFn posFn;
switch (it.positioning()) {
- case SkTextBlob::kDefault_Positioning: {
- // Default positioning needs advances. Can't do that.
- TRACE_EVENT0("skia", "kDefault_Positioning");
- FAIL_AND_RETURN
- }
-
case SkTextBlob::kHorizontal_Positioning:
posFn = [](int index, const SkScalar* pos) {
return SkPoint{pos[index], 0};
@@ -308,17 +302,6 @@ void SkTextBlobCacheDiffCanvas::processGlyphRun(
SK_ABORT("unhandled positioning mode");
}
- SkMatrix blobMatrix{fDeviceMatrix};
- blobMatrix.preConcat(this->getTotalMatrix());
- if (blobMatrix.hasPerspective()) {
- TRACE_EVENT0("skia", "hasPerspective");
- FAIL_AND_RETURN
- }
- blobMatrix.preTranslate(position.x(), position.y());
-
- SkMatrix runMatrix{blobMatrix};
- runMatrix.preTranslate(it.offset().x(), it.offset().y());
-
using MapFn = SkPoint(*)(const SkMatrix& m, SkPoint pt);
MapFn mapFn;
switch ((int)runMatrix.getType()) {
@@ -350,35 +333,13 @@ void SkTextBlobCacheDiffCanvas::processGlyphRun(
}
SkScalerContextRec deviceSpecificRec;
- SkScalerContextRec keyRec;
SkScalerContextEffects effects;
-
- SkScalerContext::MakeRecAndEffects(runPaint, &fSurfaceProps, &runMatrix,
- SkScalerContextFlags::kFakeGammaAndBoostContrast,
- &deviceSpecificRec,
- &effects, true);
-
- SkScalerContext::MakeRecAndEffects(runPaint, &fSurfaceProps, &runMatrix,
- SkScalerContextFlags::kFakeGammaAndBoostContrast,
- &keyRec,
- &effects, false);
-
-
- TRACE_EVENT1("skia", "RecForDesc", "rec", TRACE_STR_COPY(keyRec.dump().c_str()));
-
- // TODO: possible perf improvement - move descriptor calculation into getOrCreateCache.
- auto deviceDescriptor =
- SkScalerContext::DescriptorGivenRecAndEffects(deviceSpecificRec, effects);
- auto keyDescriptor =
- SkScalerContext::DescriptorGivenRecAndEffects(keyRec, effects);
- auto* glyphCacheState =
- static_cast<SkStrikeServer*>(fStrikeServer)
- ->getOrCreateCache(
- runPaint.getTypeface(),
- std::move(deviceDescriptor),
- std::move(keyDescriptor));
+ auto* glyphCacheState = static_cast<SkStrikeServer*>(fStrikeServer)
+ ->getOrCreateCache(runPaint, &fSurfaceProps, &runMatrix,
+ &deviceSpecificRec, &effects);
SkASSERT(glyphCacheState);
+ const bool asPath = false;
bool isSubpixel =
SkToBool(deviceSpecificRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag);
SkAxisAlignment axisAlignment = deviceSpecificRec.computeAxisAlignmentForHText();
@@ -393,18 +354,43 @@ void SkTextBlobCacheDiffCanvas::processGlyphRun(
glyphCacheState->addGlyph(runPaint.getTypeface(),
effects,
- SkPackedGlyphID(glyphs[index], subPixelPos.x(), subPixelPos.y()));
+ SkPackedGlyphID(glyphs[index], subPixelPos.x(), subPixelPos.y()),
+ asPath);
+ }
+}
+
+void SkTextBlobCacheDiffCanvas::processGlyphRunForPaths(const SkTextBlobRunIterator& it,
+ const SkPaint& runPaint) {
+ TRACE_EVENT0("skia", "SkTextBlobCacheDiffCanvas::processGlyphRunForPaths");
+
+ // The code below borrowed from GrAtlasTextContext::DrawBmpPosTextAsPaths.
+ SkPaint pathPaint(runPaint);
+ pathPaint.setupForAsPaths();
+ pathPaint.setStyle(SkPaint::kFill_Style);
+ pathPaint.setPathEffect(nullptr);
+
+ SkScalerContextRec deviceSpecificRec;
+ SkScalerContextEffects effects;
+ auto* glyphCacheState = static_cast<SkStrikeServer*>(fStrikeServer)
+ ->getOrCreateCache(pathPaint, &fSurfaceProps, nullptr,
+ &deviceSpecificRec, &effects);
+
+ const bool asPath = true;
+ const SkIPoint subPixelPos{0, 0};
+ const uint16_t* glyphs = it.glyphs();
+ for (uint32_t index = 0; index < it.glyphCount(); index++) {
+ glyphCacheState->addGlyph(runPaint.getTypeface(),
+ effects,
+ SkPackedGlyphID(glyphs[index], subPixelPos.x(), subPixelPos.y()),
+ asPath);
}
}
struct StrikeSpec {
StrikeSpec() {}
- StrikeSpec(SkFontID typefaceID_, size_t glyphCount_, SkDiscardableHandleId discardableHandleId_)
- : typefaceID{typefaceID_}
- , glyphCount{glyphCount_}
- , discardableHandleId(discardableHandleId_) {}
+ StrikeSpec(SkFontID typefaceID_, SkDiscardableHandleId discardableHandleId_)
+ : typefaceID{typefaceID_}, discardableHandleId(discardableHandleId_) {}
SkFontID typefaceID = 0u;
- size_t glyphCount = 0u;
SkDiscardableHandleId discardableHandleId = 0u;
/* desc */
/* n X (glyphs ids) */
@@ -462,11 +448,23 @@ void SkStrikeServer::writeStrikeData(std::vector<uint8_t>* memory) {
}
SkStrikeServer::SkGlyphCacheState* SkStrikeServer::getOrCreateCache(
- SkTypeface* tf,
- std::unique_ptr<SkDescriptor> deviceDesc,
- std::unique_ptr<SkDescriptor> keyDesc) {
- SkASSERT(deviceDesc);
- SkASSERT(keyDesc);
+ const SkPaint& paint,
+ const SkSurfaceProps* props,
+ const SkMatrix* matrix,
+ SkScalerContextRec* deviceRec,
+ SkScalerContextEffects* effects) {
+ SkScalerContextRec keyRec;
+ SkScalerContext::MakeRecAndEffects(paint, props, matrix,
+ SkScalerContextFlags::kFakeGammaAndBoostContrast, deviceRec,
+ effects, true);
+ SkScalerContext::MakeRecAndEffects(paint, props, matrix,
+ SkScalerContextFlags::kFakeGammaAndBoostContrast, &keyRec,
+ effects, false);
+ TRACE_EVENT1("skia", "RecForDesc", "rec", TRACE_STR_COPY(keyRec.dump().c_str()));
+
+ // TODO: possible perf improvement - don't recompute the device desc on cache hit.
+ auto deviceDesc = SkScalerContext::DescriptorGivenRecAndEffects(*deviceRec, *effects);
+ auto keyDesc = SkScalerContext::DescriptorGivenRecAndEffects(keyRec, *effects);
// Already locked.
if (fLockedDescs.find(keyDesc.get()) != fLockedDescs.end()) {
@@ -490,6 +488,7 @@ SkStrikeServer::SkGlyphCacheState* SkStrikeServer::getOrCreateCache(
fRemoteGlyphStateMap.erase(it);
}
+ auto* tf = paint.getTypeface();
const SkFontID typefaceId = tf->uniqueID();
if (!fCachedTypefaces.contains(typefaceId)) {
fCachedTypefaces.add(typefaceId);
@@ -499,10 +498,8 @@ SkStrikeServer::SkGlyphCacheState* SkStrikeServer::getOrCreateCache(
auto* keyDescPtr = keyDesc.get();
auto newHandle = fDiscardableHandleManager->createHandle();
- auto cacheState = skstd::make_unique<SkGlyphCacheState>(
- std::move(deviceDesc),
- std::move(keyDesc),
- newHandle);
+ auto cacheState = skstd::make_unique<SkGlyphCacheState>(std::move(deviceDesc),
+ std::move(keyDesc), newHandle);
auto* cacheStatePtr = cacheState.get();
fLockedDescs.insert(keyDescPtr);
@@ -510,10 +507,10 @@ SkStrikeServer::SkGlyphCacheState* SkStrikeServer::getOrCreateCache(
return cacheStatePtr;
}
-SkStrikeServer::SkGlyphCacheState::SkGlyphCacheState(
- std::unique_ptr<SkDescriptor> deviceDescriptor,
- std::unique_ptr<SkDescriptor> keyDescriptor,
- uint32_t discardable_handle_id)
+SkStrikeServer::SkGlyphCacheState::SkGlyphCacheState(std::unique_ptr<SkDescriptor> deviceDescriptor,
+ std::unique_ptr<SkDescriptor>
+ keyDescriptor,
+ uint32_t discardable_handle_id)
: fDeviceDescriptor(std::move(deviceDescriptor))
, fKeyDescriptor(std::move(keyDescriptor))
, fDiscardableHandleId(discardable_handle_id) {
@@ -525,51 +522,95 @@ SkStrikeServer::SkGlyphCacheState::~SkGlyphCacheState() = default;
void SkStrikeServer::SkGlyphCacheState::addGlyph(SkTypeface* typeface,
const SkScalerContextEffects& effects,
- SkPackedGlyphID glyph) {
+ SkPackedGlyphID glyph,
+ bool asPath) {
+ auto* cache = asPath ? &fCachedGlyphPaths : &fCachedGlyphImages;
+ auto* pending = asPath ? &fPendingGlyphPaths : &fPendingGlyphImages;
+
// Already cached.
- if (fCachedGlyphs.contains(glyph)) return;
+ if (cache->contains(glyph)) return;
// Serialize and cache. Also create the scalar context to use when serializing
// this glyph.
- fCachedGlyphs.add(glyph);
- fPendingGlyphs.push_back(glyph);
- if (!fContext) fContext = typeface->createScalerContext(effects, fDeviceDescriptor.get(), false);
+ cache->add(glyph);
+ pending->push_back(glyph);
+ if (!fContext) {
+ fContext = typeface->createScalerContext(effects, fDeviceDescriptor.get(), false);
+ }
}
void SkStrikeServer::SkGlyphCacheState::writePendingGlyphs(Serializer* serializer) {
// Write the desc.
- serializer->emplace<StrikeSpec>(fContext->getTypeface()->uniqueID(), fPendingGlyphs.size(),
- fDiscardableHandleId);
+ serializer->emplace<StrikeSpec>(fContext->getTypeface()->uniqueID(), fDiscardableHandleId);
serializer->writeDescriptor(*fKeyDescriptor.get());
// Write FontMetrics.
+ // TODO(khushalsagar): Do we need to re-send each time?
SkPaint::FontMetrics fontMetrics;
fContext->getFontMetrics(&fontMetrics);
serializer->write<SkPaint::FontMetrics>(fontMetrics);
- // Write Glyphs.
- for (const auto& glyphID : fPendingGlyphs) {
+ // Write glyphs images.
+ serializer->emplace<size_t>(fPendingGlyphImages.size());
+ for (const auto& glyphID : fPendingGlyphImages) {
auto glyph = serializer->emplace<SkGlyph>();
glyph->initWithGlyphID(glyphID);
fContext->getMetrics(glyph);
- auto imageSize = glyph->computeImageSize();
glyph->fPathData = nullptr;
glyph->fImage = nullptr;
- if (imageSize > 0) {
- // Since the allocateArray can move glyph, make one that stays in one place.
- SkGlyph stationaryGlyph = *glyph;
- stationaryGlyph.fImage = serializer->allocateArray<uint8_t>(imageSize);
- fContext->getImage(stationaryGlyph);
+ bool tooLargeForAtlas = false;
+#if SK_SUPPORT_GPU
+ tooLargeForAtlas = GrDrawOpAtlas::GlyphTooLargeForAtlas(glyph->fWidth, glyph->fHeight);
+#endif
+ if (tooLargeForAtlas) {
+ // Add this to the path cache, since we will always fall back to using paths
+ // for this glyph.
+ fCachedGlyphPaths.add(glyphID);
+ writeGlyphPath(glyphID, serializer);
+ continue;
}
+
+ auto imageSize = glyph->computeImageSize();
+ if (imageSize == 0u) continue;
+
+ // Since the allocate can move glyph, make one that stays in one place.
+ SkGlyph stationaryGlyph = *glyph;
+ stationaryGlyph.fImage = serializer->allocate(imageSize, stationaryGlyph.formatAlignment());
+ fContext->getImage(stationaryGlyph);
}
+ fPendingGlyphImages.clear();
+
+ // Write glyphs paths.
+ serializer->emplace<size_t>(fPendingGlyphPaths.size());
+ for (const auto& glyphID : fPendingGlyphPaths) {
+ auto glyph = serializer->emplace<SkGlyph>();
+ glyph->initWithGlyphID(glyphID);
+ fContext->getMetrics(glyph);
+ glyph->fPathData = nullptr;
+ glyph->fImage = nullptr;
+ writeGlyphPath(glyphID, serializer);
+ }
+ fPendingGlyphPaths.clear();
// Note that we reset the context after serializing pending glyphs since we
// don't want to extend the lifetime of the typeface.
- fPendingGlyphs.clear();
fContext.reset();
}
+void SkStrikeServer::SkGlyphCacheState::writeGlyphPath(const SkPackedGlyphID& glyphID,
+ Serializer* serializer) const {
+ SkPath path;
+ if (!fContext->getPath(glyphID, &path)) {
+ serializer->write<size_t>(0u);
+ return;
+ }
+
+ size_t pathSize = path.writeToMemory(nullptr);
+ serializer->write<size_t>(pathSize);
+ path.writeToMemory(serializer->allocate(pathSize, kPathAlignment));
+}
+
// SkStrikeClient -----------------------------------------
class SkStrikeClient::DiscardableStrikePinner : public SkStrikePinner {
@@ -656,20 +697,49 @@ bool SkStrikeClient::readStrikeData(const volatile void* memory, size_t memorySi
fDiscardableHandleManager));
}
- for (size_t j = 0; j < spec.glyphCount; j++) {
+ size_t glyphImagesCount = 0u;
+ if (!deserializer.read<size_t>(&glyphImagesCount)) READ_FAILURE
+ for (size_t j = 0; j < glyphImagesCount; j++) {
SkGlyph glyph;
if (!deserializer.read<SkGlyph>(&glyph)) READ_FAILURE
SkGlyph* allocatedGlyph = strike->getRawGlyphByID(glyph.getPackedID());
+ // Don't override the path, if the glyph has one.
+ auto* glyphPath = allocatedGlyph->fPathData;
*allocatedGlyph = glyph;
+ allocatedGlyph->fPathData = glyphPath;
+
+ bool tooLargeForAtlas = false;
+#if SK_SUPPORT_GPU
+ tooLargeForAtlas = GrDrawOpAtlas::GlyphTooLargeForAtlas(glyph.fWidth, glyph.fHeight);
+#endif
+ if (tooLargeForAtlas) {
+ if (!read_path(&deserializer, allocatedGlyph, strike.get())) READ_FAILURE
+ continue;
+ }
- ArraySlice<uint8_t> image;
auto imageSize = glyph.computeImageSize();
- if (imageSize != 0) {
- image = deserializer.readArray<uint8_t>(imageSize);
- if (!image.data()) READ_FAILURE
- strike->initializeImage(image.data(), image.size(), allocatedGlyph);
- }
+ if (imageSize == 0u) continue;
+
+ auto* image = deserializer.read(imageSize, allocatedGlyph->formatAlignment());
+ if (!image ||
+ !strike->initializeImage(image, imageSize, allocatedGlyph))
+ READ_FAILURE
+ }
+
+ size_t glyphPathsCount = 0u;
+ if (!deserializer.read<size_t>(&glyphPathsCount)) READ_FAILURE
+ for (size_t j = 0; j < glyphPathsCount; j++) {
+ SkGlyph glyph;
+ if (!deserializer.read<SkGlyph>(&glyph)) READ_FAILURE
+
+ SkGlyph* allocatedGlyph = strike->getRawGlyphByID(glyph.getPackedID());
+ // Don't override the image, if the glyph has one.
+ auto* glyphImage = allocatedGlyph->fImage;
+ *allocatedGlyph = glyph;
+ allocatedGlyph->fImage = glyphImage;
+
+ if (!read_path(&deserializer, allocatedGlyph, strike.get())) READ_FAILURE
}
}
diff --git a/src/core/SkRemoteGlyphCache.h b/src/core/SkRemoteGlyphCache.h
index 36fe59306b..60d880791f 100644
--- a/src/core/SkRemoteGlyphCache.h
+++ b/src/core/SkRemoteGlyphCache.h
@@ -69,6 +69,7 @@ private:
void processGlyphRun(const SkPoint& position,
const SkTextBlobRunIterator& it,
const SkPaint& runPaint);
+ void processGlyphRunForPaths(const SkTextBlobRunIterator& it, const SkPaint& runPaint);
const SkMatrix fDeviceMatrix;
const SkSurfaceProps fSurfaceProps;
@@ -122,9 +123,11 @@ public:
SkDiscardableHandleId discardableHandleId);
~SkGlyphCacheState();
- void addGlyph(SkTypeface*, const SkScalerContextEffects&, SkPackedGlyphID);
+ void addGlyph(SkTypeface*, const SkScalerContextEffects&, SkPackedGlyphID, bool pathOnly);
void writePendingGlyphs(Serializer* serializer);
- bool has_pending_glyphs() const { return !fPendingGlyphs.empty(); }
+ bool has_pending_glyphs() const {
+ return !fPendingGlyphImages.empty() || !fPendingGlyphPaths.empty();
+ }
SkDiscardableHandleId discardable_handle_id() const { return fDiscardableHandleId; }
const SkDescriptor& getDeviceDescriptor() {
return *fDeviceDescriptor;
@@ -135,12 +138,16 @@ public:
}
private:
+ void writeGlyphPath(const SkPackedGlyphID& glyphID, Serializer* serializer) const;
+
// The set of glyphs cached on the remote client.
- SkTHashSet<SkPackedGlyphID> fCachedGlyphs;
+ SkTHashSet<SkPackedGlyphID> fCachedGlyphImages;
+ SkTHashSet<SkPackedGlyphID> fCachedGlyphPaths;
// The set of glyphs which has not yet been serialized and sent to the
// remote client.
- std::vector<SkPackedGlyphID> fPendingGlyphs;
+ std::vector<SkPackedGlyphID> fPendingGlyphImages;
+ std::vector<SkPackedGlyphID> fPendingGlyphPaths;
// The device descriptor is used to create the scaler context. The glyphs to have the
// correct device rendering. The key descriptor is used for communication. The GPU side will
@@ -153,8 +160,9 @@ public:
std::unique_ptr<SkScalerContext> fContext;
};
- SkGlyphCacheState* getOrCreateCache(
- SkTypeface*, std::unique_ptr<SkDescriptor>, std::unique_ptr<SkDescriptor>);
+ SkGlyphCacheState* getOrCreateCache(const SkPaint&, const SkSurfaceProps*, const SkMatrix*,
+ SkScalerContextRec* deviceRec,
+ SkScalerContextEffects* effects);
private:
SkDescriptorMap<std::unique_ptr<SkGlyphCacheState>> fRemoteGlyphStateMap;
diff --git a/tests/SkRemoteGlyphCacheTest.cpp b/tests/SkRemoteGlyphCacheTest.cpp
index dbe36d1e6a..811bf1be50 100644
--- a/tests/SkRemoteGlyphCacheTest.cpp
+++ b/tests/SkRemoteGlyphCacheTest.cpp
@@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
+#include "SkDraw.h"
#include "SkGraphics.h"
#include "SkMutex.h"
#include "SkRemoteGlyphCache.h"
@@ -71,9 +72,15 @@ sk_sp<SkTextBlob> buildTextBlob(sk_sp<SkTypeface> tf, int glyphCount) {
return builder.make();
}
-SkBitmap RasterBlob(sk_sp<SkTextBlob> blob, int width, int height) {
+#define COMPARE_BLOBS(expected, actual, reporter) \
+ for (int i = 0; i < expected.width(); ++i) { \
+ for (int j = 0; j < expected.height(); ++j) { \
+ REPORTER_ASSERT(reporter, expected.getColor(i, j) == actual.getColor(i, j)); \
+ } \
+ }
+
+SkBitmap RasterBlob(sk_sp<SkTextBlob> blob, int width, int height, const SkPaint& paint) {
auto surface = SkSurface::MakeRasterN32Premul(width, height);
- SkPaint paint;
surface->getCanvas()->drawTextBlob(blob.get(), 0u, 0u, paint);
SkBitmap bitmap;
bitmap.allocN32Pixels(width, height);
@@ -99,6 +106,7 @@ DEF_TEST(SkRemoteGlyphCache_StrikeSerialization, reporter) {
sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
SkStrikeServer server(discardableManager.get());
SkStrikeClient client(discardableManager);
+ const SkPaint paint;
// Server.
auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
@@ -108,7 +116,6 @@ DEF_TEST(SkRemoteGlyphCache_StrikeSerialization, reporter) {
auto serverBlob = buildTextBlob(serverTf, glyphCount);
const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
- SkPaint paint;
cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
std::vector<uint8_t> serverStrikeData;
@@ -120,13 +127,9 @@ DEF_TEST(SkRemoteGlyphCache_StrikeSerialization, reporter) {
client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
auto clientBlob = buildTextBlob(clientTf, glyphCount);
- SkBitmap expected = RasterBlob(serverBlob, 10, 10);
- SkBitmap actual = RasterBlob(clientBlob, 10, 10);
- for (int i = 0; i < expected.width(); ++i) {
- for (int j = 0; j < expected.height(); ++j) {
- REPORTER_ASSERT(reporter, expected.getColor(i, j) == actual.getColor(i, j));
- }
- }
+ SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint);
+ SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint);
+ COMPARE_BLOBS(expected, actual, reporter);
}
DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer, reporter) {
@@ -246,3 +249,37 @@ DEF_TEST(SkRemoteGlyphCache_ClientMemoryAccounting, reporter) {
client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
SkStrikeCache::Validate();
}
+
+DEF_TEST(SkRemoteGlyphCache_DrawTextAsPath, reporter) {
+ sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
+ SkStrikeServer server(discardableManager.get());
+ SkStrikeClient client(discardableManager);
+ SkPaint paint;
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(0);
+ REPORTER_ASSERT(reporter, SkDraw::ShouldDrawTextAsPaths(paint, SkMatrix::I()));
+
+ // Server.
+ auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
+ auto serverTfData = server.serializeTypeface(serverTf.get());
+
+ int glyphCount = 10;
+ auto serverBlob = buildTextBlob(serverTf, glyphCount);
+ const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
+ SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
+ cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
+
+ std::vector<uint8_t> serverStrikeData;
+ server.writeStrikeData(&serverStrikeData);
+
+ // Client.
+ auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
+ REPORTER_ASSERT(reporter,
+ client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
+ auto clientBlob = buildTextBlob(clientTf, glyphCount);
+
+ SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint);
+ SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint);
+ COMPARE_BLOBS(expected, actual, reporter);
+ SkStrikeCache::Validate();
+}