aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Khushal <khushalsagar@chromium.org>2018-05-17 10:41:40 -0700
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-05-17 20:50:43 +0000
commit51371a43397424ee4d3057fd5a82ec7c01eff705 (patch)
tree0ae5f93ab2ee57ddb73a3ed32633b9774c1d00c7
parente6c0fe0b8fb4fb66b0c8d3b3abac6257c3201f00 (diff)
fonts: Handle fallback to using paths for text rendering for remoting.
SkRemoteGlyphCache only sends images for glyphs, even for cases where the gpu falls back to drawing text as paths. This includes cases in SkDraw::ShouldDrawTextAsPaths and when the glyph exceeds the max bounds that can fit on the atlas. Fix this by identifying these cases in the renderer and sending paths instead. Note: We still don't handle distance field text correctly. R=herb@google.com, bsalomon@google.com Bug: skia:7913 Change-Id: I17d4eccbeaa2e995ae67b61c76cebd27f8280329 Reviewed-on: https://skia-review.googlesource.com/128203 Reviewed-by: Herb Derby <herb@google.com> Commit-Queue: Khusal Sagar <khushalsagar@chromium.org>
-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();
+}