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 30 struct TextBlobWrapper { 31 // This class assumes it 'owns' the textblob it wraps, and thus does not need to take a ref 32 explicit TextBlobWrapper(const SkTextBlob* blob) : fBlob(blob) {} 33 TextBlobWrapper(const TextBlobWrapper& blob) : fBlob(SkRef(blob.fBlob.get())) {} 34 35 SkAutoTUnref<const SkTextBlob> fBlob; 36 }; 37 38 static void draw(SkCanvas* canvas, int redraw, const SkTArray<TextBlobWrapper>& blobs) { 39 int yOffset = 0; 40 for (int r = 0; r < redraw; r++) { 41 for (int i = 0; i < blobs.count(); i++) { 42 const SkTextBlob* blob = blobs[i].fBlob.get(); 43 const SkRect& bounds = blob->bounds(); 44 yOffset += SkScalarCeilToInt(bounds.height()); 45 SkPaint paint; 46 canvas->drawTextBlob(blob, 0, SkIntToScalar(yOffset), paint); 47 } 48 } 49 } 50 51 static const int kWidth = 1024; 52 static const int kHeight = 768; 53 54 // This test hammers the GPU textblobcache and font atlas 55 static void text_blob_cache_inner(skiatest::Reporter* reporter, GrContext* context, 56 int maxTotalText, int maxGlyphID, int maxFamilies, bool normal, 57 bool stressTest) { 58 // setup surface 59 uint32_t flags = 0; 60 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); 61 62 // configure our context for maximum stressing of cache and atlas 63 if (stressTest) { 64 GrTest::SetupAlwaysEvictAtlas(context); 65 context->setTextBlobCacheLimit_ForTesting(0); 66 } 67 68 SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, kN32_SkColorType, kPremul_SkAlphaType); 69 SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(context, SkBudgeted::kNo, info, 70 0, &props)); 71 REPORTER_ASSERT(reporter, surface); 72 if (!surface) { 73 return; 74 } 75 76 SkCanvas* canvas = surface->getCanvas(); 77 78 SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault()); 79 80 int count = SkMin32(fm->countFamilies(), maxFamilies); 81 82 // make a ton of text 83 SkAutoTArray<uint16_t> text(maxTotalText); 84 for (int i = 0; i < maxTotalText; i++) { 85 text[i] = i % maxGlyphID; 86 } 87 88 // generate textblobs 89 SkTArray<TextBlobWrapper> blobs; 90 for (int i = 0; i < count; i++) { 91 SkPaint paint; 92 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 93 paint.setTextSize(48); // draw big glyphs to really stress the atlas 94 95 SkString familyName; 96 fm->getFamilyName(i, &familyName); 97 SkAutoTUnref<SkFontStyleSet> set(fm->createStyleSet(i)); 98 for (int j = 0; j < set->count(); ++j) { 99 SkFontStyle fs; 100 set->getStyle(j, &fs, nullptr); 101 102 // We use a typeface which randomy returns unexpected mask formats to fuzz 103 SkAutoTUnref<SkTypeface> orig(set->createTypeface(j)); 104 if (normal) { 105 paint.setTypeface(orig); 106 } else { 107 SkAutoTUnref<SkTypeface> typeface(new SkRandomTypeface(orig, paint, true)); 108 paint.setTypeface(typeface); 109 } 110 111 SkTextBlobBuilder builder; 112 for (int aa = 0; aa < 2; aa++) { 113 for (int subpixel = 0; subpixel < 2; subpixel++) { 114 for (int lcd = 0; lcd < 2; lcd++) { 115 paint.setAntiAlias(SkToBool(aa)); 116 paint.setSubpixelText(SkToBool(subpixel)); 117 paint.setLCDRenderText(SkToBool(lcd)); 118 if (!SkToBool(lcd)) { 119 paint.setTextSize(160); 120 } 121 const SkTextBlobBuilder::RunBuffer& run = builder.allocRun(paint, 122 maxTotalText, 123 0, 0, 124 nullptr); 125 memcpy(run.glyphs, text.get(), maxTotalText * sizeof(uint16_t)); 126 } 127 } 128 } 129 blobs.emplace_back(builder.build()); 130 } 131 } 132 133 // create surface where LCD is impossible 134 info = SkImageInfo::MakeN32Premul(kWidth, kHeight); 135 SkSurfaceProps propsNoLCD(0, kUnknown_SkPixelGeometry); 136 SkAutoTUnref<SkSurface> surfaceNoLCD(canvas->newSurface(info, &propsNoLCD)); 137 REPORTER_ASSERT(reporter, surface); 138 if (!surface) { 139 return; 140 } 141 142 SkCanvas* canvasNoLCD = surfaceNoLCD->getCanvas(); 143 144 // test redraw 145 draw(canvas, 2, blobs); 146 draw(canvasNoLCD, 2, blobs); 147 148 // test draw after free 149 context->freeGpuResources(); 150 draw(canvas, 1, blobs); 151 152 context->freeGpuResources(); 153 draw(canvasNoLCD, 1, blobs); 154 155 // test draw after abandon 156 context->abandonContext(); 157 draw(canvas, 1, blobs); 158 } 159 160 DEF_GPUTEST_FOR_NULL_CONTEXT(TextBlobCache, reporter, context) { 161 text_blob_cache_inner(reporter, context, 1024, 256, 30, true, false); 162 } 163 164 DEF_GPUTEST_FOR_NULL_CONTEXT(TextBlobStressCache, reporter, context) { 165 text_blob_cache_inner(reporter, context, 256, 256, 10, true, true); 166 } 167 168 DEF_GPUTEST_FOR_NULL_CONTEXT(TextBlobAbnormal, reporter, context) { 169 text_blob_cache_inner(reporter, context, 256, 256, 10, false, false); 170 } 171 172 DEF_GPUTEST_FOR_NULL_CONTEXT(TextBlobStressAbnormal, reporter, context) { 173 text_blob_cache_inner(reporter, context, 256, 256, 10, false, true); 174 } 175 #endif 176