1 /* 2 * Copyright 2015 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "sk_tool_utils.h" 9 10 #include "SkCanvas.h" 11 #include "SkFontMgr.h" 12 #include "SkGraphics.h" 13 #include "SkPaint.h" 14 #include "SkPoint.h" 15 #include "SkRandomScalerContext.h" 16 #include "SkSurface.h" 17 #include "SkTextBlob.h" 18 #include "SkTypeface.h" 19 20 #ifdef SK_BUILD_FOR_WIN 21 #include "SkTypeface_win.h" 22 #endif 23 24 #include "Test.h" 25 26 #if SK_SUPPORT_GPU 27 #include "GrContext.h" 28 #include "GrTest.h" 29 30 static void draw(SkCanvas* canvas, int redraw, const SkTArray<sk_sp<SkTextBlob>>& blobs) { 31 int yOffset = 0; 32 for (int r = 0; r < redraw; r++) { 33 for (int i = 0; i < blobs.count(); i++) { 34 const auto& blob = blobs[i]; 35 const SkRect& bounds = blob->bounds(); 36 yOffset += SkScalarCeilToInt(bounds.height()); 37 SkPaint paint; 38 canvas->drawTextBlob(blob, 0, SkIntToScalar(yOffset), paint); 39 } 40 } 41 } 42 43 static const int kWidth = 1024; 44 static const int kHeight = 768; 45 46 // This test hammers the GPU textblobcache and font atlas 47 static void text_blob_cache_inner(skiatest::Reporter* reporter, GrContext* context, 48 int maxTotalText, int maxGlyphID, int maxFamilies, bool normal, 49 bool stressTest) { 50 // setup surface 51 uint32_t flags = 0; 52 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); 53 54 // configure our context for maximum stressing of cache and atlas 55 if (stressTest) { 56 GrTest::SetupAlwaysEvictAtlas(context); 57 context->setTextBlobCacheLimit_ForTesting(0); 58 } 59 60 SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, kN32_SkColorType, kPremul_SkAlphaType); 61 auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 0, &props)); 62 REPORTER_ASSERT(reporter, surface); 63 if (!surface) { 64 return; 65 } 66 67 SkCanvas* canvas = surface->getCanvas(); 68 69 sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); 70 71 int count = SkMin32(fm->countFamilies(), maxFamilies); 72 73 // make a ton of text 74 SkAutoTArray<uint16_t> text(maxTotalText); 75 for (int i = 0; i < maxTotalText; i++) { 76 text[i] = i % maxGlyphID; 77 } 78 79 // generate textblobs 80 SkTArray<sk_sp<SkTextBlob>> blobs; 81 for (int i = 0; i < count; i++) { 82 SkPaint paint; 83 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 84 paint.setTextSize(48); // draw big glyphs to really stress the atlas 85 86 SkString familyName; 87 fm->getFamilyName(i, &familyName); 88 sk_sp<SkFontStyleSet> set(fm->createStyleSet(i)); 89 for (int j = 0; j < set->count(); ++j) { 90 SkFontStyle fs; 91 set->getStyle(j, &fs, nullptr); 92 93 // We use a typeface which randomy returns unexpected mask formats to fuzz 94 sk_sp<SkTypeface> orig(set->createTypeface(j)); 95 if (normal) { 96 paint.setTypeface(orig); 97 } else { 98 paint.setTypeface(sk_make_sp<SkRandomTypeface>(orig, paint, true)); 99 } 100 101 SkTextBlobBuilder builder; 102 for (int aa = 0; aa < 2; aa++) { 103 for (int subpixel = 0; subpixel < 2; subpixel++) { 104 for (int lcd = 0; lcd < 2; lcd++) { 105 paint.setAntiAlias(SkToBool(aa)); 106 paint.setSubpixelText(SkToBool(subpixel)); 107 paint.setLCDRenderText(SkToBool(lcd)); 108 if (!SkToBool(lcd)) { 109 paint.setTextSize(160); 110 } 111 const SkTextBlobBuilder::RunBuffer& run = builder.allocRun(paint, 112 maxTotalText, 113 0, 0, 114 nullptr); 115 memcpy(run.glyphs, text.get(), maxTotalText * sizeof(uint16_t)); 116 } 117 } 118 } 119 blobs.emplace_back(builder.make()); 120 } 121 } 122 123 // create surface where LCD is impossible 124 info = SkImageInfo::MakeN32Premul(kWidth, kHeight); 125 SkSurfaceProps propsNoLCD(0, kUnknown_SkPixelGeometry); 126 auto surfaceNoLCD(canvas->makeSurface(info, &propsNoLCD)); 127 REPORTER_ASSERT(reporter, surface); 128 if (!surface) { 129 return; 130 } 131 132 SkCanvas* canvasNoLCD = surfaceNoLCD->getCanvas(); 133 134 // test redraw 135 draw(canvas, 2, blobs); 136 draw(canvasNoLCD, 2, blobs); 137 138 // test draw after free 139 context->freeGpuResources(); 140 draw(canvas, 1, blobs); 141 142 context->freeGpuResources(); 143 draw(canvasNoLCD, 1, blobs); 144 145 // test draw after abandon 146 context->abandonContext(); 147 draw(canvas, 1, blobs); 148 } 149 150 DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobCache, reporter, ctxInfo) { 151 text_blob_cache_inner(reporter, ctxInfo.grContext(), 1024, 256, 30, true, false); 152 } 153 154 DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobStressCache, reporter, ctxInfo) { 155 text_blob_cache_inner(reporter, ctxInfo.grContext(), 256, 256, 10, true, true); 156 } 157 158 DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobAbnormal, reporter, ctxInfo) { 159 text_blob_cache_inner(reporter, ctxInfo.grContext(), 256, 256, 10, false, false); 160 } 161 162 DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobStressAbnormal, reporter, ctxInfo) { 163 text_blob_cache_inner(reporter, ctxInfo.grContext(), 256, 256, 10, false, true); 164 } 165 #endif 166