| /* | 
 |  * Copyright 2018 Google Inc. | 
 |  * | 
 |  * Use of this source code is governed by a BSD-style license that can be | 
 |  * found in the LICENSE file. | 
 |  */ | 
 |  | 
 | #include "SkGraphics.h" | 
 | #include "SkMutex.h" | 
 | #include "SkRemoteGlyphCache.h" | 
 | #include "SkStrikeCache.h" | 
 | #include "SkSurface.h" | 
 | #include "SkTextBlob.h" | 
 | #include "SkTypeface_remote.h" | 
 | #include "Test.h" | 
 |  | 
 | class DiscardableManager : public SkStrikeServer::DiscardableHandleManager, | 
 |                            public SkStrikeClient::DiscardableHandleManager { | 
 | public: | 
 |     DiscardableManager() = default; | 
 |     ~DiscardableManager() override = default; | 
 |  | 
 |     // Server implementation. | 
 |     SkDiscardableHandleId createHandle() override { | 
 |         // Handles starts as locked. | 
 |         fLockedHandles.add(++fNextHandleId); | 
 |         return fNextHandleId; | 
 |     } | 
 |     bool lockHandle(SkDiscardableHandleId id) override { | 
 |         if (id <= fLastDeletedHandleId) return false; | 
 |         fLockedHandles.add(id); | 
 |         return true; | 
 |     } | 
 |  | 
 |     // Client implementation. | 
 |     bool deleteHandle(SkDiscardableHandleId id) override { return id <= fLastDeletedHandleId; } | 
 |  | 
 |     void unlockAll() { fLockedHandles.reset(); } | 
 |     void unlockAndDeleteAll() { | 
 |         unlockAll(); | 
 |         fLastDeletedHandleId = fNextHandleId; | 
 |     } | 
 |     const SkTHashSet<SkDiscardableHandleId>& lockedHandles() const { return fLockedHandles; } | 
 |     SkDiscardableHandleId handleCount() { return fNextHandleId; } | 
 |  | 
 | private: | 
 |     SkDiscardableHandleId fNextHandleId = 0u; | 
 |     SkDiscardableHandleId fLastDeletedHandleId = 0u; | 
 |     SkTHashSet<SkDiscardableHandleId> fLockedHandles; | 
 | }; | 
 |  | 
 | sk_sp<SkTextBlob> buildTextBlob(sk_sp<SkTypeface> tf, int glyphCount) { | 
 |     SkPaint font; | 
 |     font.setTypeface(tf); | 
 |     font.setTextEncoding(SkPaint::kGlyphID_TextEncoding); | 
 |     font.setTextAlign(SkPaint::kLeft_Align); | 
 |     font.setStyle(SkPaint::kFill_Style); | 
 |     font.setHinting(SkPaint::kNormal_Hinting); | 
 |     font.setTextSize(1u); | 
 |  | 
 |     SkTextBlobBuilder builder; | 
 |     SkRect bounds = SkRect::MakeWH(10, 10); | 
 |     const auto& runBuffer = builder.allocRunPosH(font, glyphCount, 0, &bounds); | 
 |     SkASSERT(runBuffer.utf8text == nullptr); | 
 |     SkASSERT(runBuffer.clusters == nullptr); | 
 |  | 
 |     for (int i = 0; i < glyphCount; i++) { | 
 |         runBuffer.glyphs[i] = static_cast<SkGlyphID>(i); | 
 |         runBuffer.pos[i] = SkIntToScalar(i); | 
 |     } | 
 |     return builder.make(); | 
 | } | 
 |  | 
 | SkBitmap RasterBlob(sk_sp<SkTextBlob> blob, int width, int height) { | 
 |     auto surface = SkSurface::MakeRasterN32Premul(width, height); | 
 |     SkPaint paint; | 
 |     surface->getCanvas()->drawTextBlob(blob.get(), 0u, 0u, paint); | 
 |     SkBitmap bitmap; | 
 |     bitmap.allocN32Pixels(width, height); | 
 |     surface->readPixels(bitmap, 0, 0); | 
 |     return bitmap; | 
 | } | 
 |  | 
 | DEF_TEST(SkRemoteGlyphCache_TypefaceSerialization, reporter) { | 
 |     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); | 
 |     SkStrikeServer server(discardableManager.get()); | 
 |     SkStrikeClient client(discardableManager); | 
 |  | 
 |     auto server_tf = SkTypeface::MakeDefault(); | 
 |     auto tf_data = server.serializeTypeface(server_tf.get()); | 
 |  | 
 |     auto client_tf = client.deserializeTypeface(tf_data->data(), tf_data->size()); | 
 |     REPORTER_ASSERT(reporter, client_tf); | 
 |     REPORTER_ASSERT(reporter, SkTypefaceProxy::DownCast(client_tf.get())->remoteTypefaceID() == | 
 |                                       server_tf->uniqueID()); | 
 | } | 
 |  | 
 | DEF_TEST(SkRemoteGlyphCache_StrikeSerialization, reporter) { | 
 |     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); | 
 |     SkStrikeServer server(discardableManager.get()); | 
 |     SkStrikeClient client(discardableManager); | 
 |  | 
 |     // 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); | 
 |     SkPaint paint; | 
 |     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); | 
 |     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)); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer, reporter) { | 
 |     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); | 
 |     SkStrikeServer server(discardableManager.get()); | 
 |     SkStrikeClient client(discardableManager); | 
 |  | 
 |     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle()); | 
 |     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); | 
 |     SkPaint paint; | 
 |     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint); | 
 |  | 
 |     // The strike from the blob should be locked after it has been drawn on the canvas. | 
 |     REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u); | 
 |     REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u); | 
 |  | 
 |     // Write the strike data and unlock everything. Re-analyzing the blob should lock the handle | 
 |     // again. | 
 |     std::vector<uint8_t> fontData; | 
 |     server.writeStrikeData(&fontData); | 
 |     discardableManager->unlockAll(); | 
 |     REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 0u); | 
 |  | 
 |     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint); | 
 |     REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u); | 
 |     REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u); | 
 | } | 
 |  | 
 | DEF_TEST(SkRemoteGlyphCache_StrikeDeletionServer, reporter) { | 
 |     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); | 
 |     SkStrikeServer server(discardableManager.get()); | 
 |     SkStrikeClient client(discardableManager); | 
 |  | 
 |     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle()); | 
 |     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); | 
 |     SkPaint paint; | 
 |     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint); | 
 |     REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u); | 
 |  | 
 |     // Write the strike data and delete all the handles. Re-analyzing the blob should create new | 
 |     // handles. | 
 |     std::vector<uint8_t> fontData; | 
 |     server.writeStrikeData(&fontData); | 
 |     discardableManager->unlockAndDeleteAll(); | 
 |     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint); | 
 |     REPORTER_ASSERT(reporter, discardableManager->handleCount() == 2u); | 
 | } | 
 |  | 
 | DEF_TEST(SkRemoteGlyphCache_StrikePinningClient, reporter) { | 
 |     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); | 
 |     SkStrikeServer server(discardableManager.get()); | 
 |     SkStrikeClient client(discardableManager); | 
 |  | 
 |     // 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); | 
 |     SkPaint paint; | 
 |     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint); | 
 |  | 
 |     std::vector<uint8_t> serverStrikeData; | 
 |     server.writeStrikeData(&serverStrikeData); | 
 |  | 
 |     // Client. | 
 |     REPORTER_ASSERT(reporter, | 
 |                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size())); | 
 |     auto* clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size()).get(); | 
 |  | 
 |     // The cache remains alive until it is pinned in the discardable manager. | 
 |     SkGraphics::PurgeFontCache(); | 
 |     REPORTER_ASSERT(reporter, !clientTf->unique()); | 
 |  | 
 |     // Once the strike is unpinned and purged, SkStrikeClient should be the only owner of the | 
 |     // clientTf. | 
 |     discardableManager->unlockAndDeleteAll(); | 
 |     SkGraphics::PurgeFontCache(); | 
 |     REPORTER_ASSERT(reporter, clientTf->unique()); | 
 | } | 
 |  | 
 | DEF_TEST(SkRemoteGlyphCache_ClientMemoryAccounting, reporter) { | 
 |     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); | 
 |     SkStrikeServer server(discardableManager.get()); | 
 |     SkStrikeClient client(discardableManager); | 
 |  | 
 |     // 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); | 
 |     SkPaint paint; | 
 |     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint); | 
 |  | 
 |     std::vector<uint8_t> serverStrikeData; | 
 |     server.writeStrikeData(&serverStrikeData); | 
 |  | 
 |     // Client. | 
 |     REPORTER_ASSERT(reporter, | 
 |                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size())); | 
 |     SkStrikeCache::Validate(); | 
 | } |