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 "SkPaint.h" 12#include "SkPoint.h" 13#include "SkTextBlob.h" 14#include "SkFontMgr.h" 15#include "SkGraphics.h" 16#include "SkSurface.h" 17#include "SkTypeface.h" 18#include "../src/fonts/SkRandomScalerContext.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 30static 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 43static const int kWidth = 1024; 44static const int kHeight = 768; 45 46// This test hammers the GPU textblobcache and font atlas 47static 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 150DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobCache, reporter, ctxInfo) { 151 text_blob_cache_inner(reporter, ctxInfo.grContext(), 1024, 256, 30, true, false); 152} 153 154DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobStressCache, reporter, ctxInfo) { 155 text_blob_cache_inner(reporter, ctxInfo.grContext(), 256, 256, 10, true, true); 156} 157 158DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobAbnormal, reporter, ctxInfo) { 159 text_blob_cache_inner(reporter, ctxInfo.grContext(), 256, 256, 10, false, false); 160} 161 162DEF_GPUTEST_FOR_NULLGL_CONTEXT(TextBlobStressAbnormal, reporter, ctxInfo) { 163 text_blob_cache_inner(reporter, ctxInfo.grContext(), 256, 256, 10, false, true); 164} 165#endif 166