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 "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