Home | History | Annotate | Download | only in tests
      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