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 "gm.h" 9 10#include "Resources.h" 11#include "SkBlurMask.h" 12#include "SkBlurMaskFilter.h" 13#include "SkCanvas.h" 14#include "SkGradientShader.h" 15#include "SkImage.h" 16#include "SkRandom.h" 17#include "SkStream.h" 18#include "SkSurface.h" 19#include "SkTextBlob.h" 20#include "SkTypeface.h" 21 22namespace skiagm { 23class TextBlobMixedSizes : public GM { 24public: 25 // This gm tests that textblobs of mixed sizes with a large glyph will render properly 26 TextBlobMixedSizes(bool useDFT) : fUseDFT(useDFT) {} 27 28protected: 29 void onOnceBeforeDraw() override { 30 SkAutoTUnref<SkTypeface> typeface(GetResourceAsTypeface("/fonts/HangingS.ttf")); 31 SkTextBlobBuilder builder; 32 33 // make textblob. To stress distance fields, we choose sizes appropriately 34 SkPaint paint; 35 paint.setAntiAlias(true); 36 paint.setSubpixelText(true); 37 paint.setLCDRenderText(true); 38 paint.setTypeface(typeface); 39 40 const char* text = "Skia"; 41 42 // extra large 43 paint.setTextSize(262); 44 45 sk_tool_utils::add_to_text_blob(&builder, text, paint, 0, 0); 46 47 // large 48 SkRect bounds; 49 paint.measureText(text, strlen(text), &bounds); 50 SkScalar yOffset = bounds.height(); 51 paint.setTextSize(162); 52 53 sk_tool_utils::add_to_text_blob(&builder, text, paint, 0, yOffset); 54 55 // Medium 56 paint.measureText(text, strlen(text), &bounds); 57 yOffset += bounds.height(); 58 paint.setTextSize(72); 59 60 sk_tool_utils::add_to_text_blob(&builder, text, paint, 0, yOffset); 61 62 // Small 63 paint.measureText(text, strlen(text), &bounds); 64 yOffset += bounds.height(); 65 paint.setTextSize(32); 66 67 sk_tool_utils::add_to_text_blob(&builder, text, paint, 0, yOffset); 68 69 // micro (will fall out of distance field text even if distance field text is enabled) 70 paint.measureText(text, strlen(text), &bounds); 71 yOffset += bounds.height(); 72 paint.setTextSize(14); 73 74 sk_tool_utils::add_to_text_blob(&builder, text, paint, 0, yOffset); 75 76 // build 77 fBlob.reset(builder.build()); 78 } 79 80 SkString onShortName() override { 81 SkString name("textblobmixedsizes"); 82 if (fUseDFT) { 83 name.appendf("_df"); 84 } 85 return name; 86 } 87 88 SkISize onISize() override { 89 return SkISize::Make(kWidth, kHeight); 90 } 91 92 void onDraw(SkCanvas* inputCanvas) override { 93 SkCanvas* canvas = inputCanvas; 94 SkAutoTUnref<SkSurface> surface; 95 if (fUseDFT) { 96#if SK_SUPPORT_GPU 97 // Create a new Canvas to enable DFT 98 GrContext* ctx = inputCanvas->getGrContext(); 99 SkImageInfo info = SkImageInfo::MakeN32Premul(onISize()); 100 SkSurfaceProps props(SkSurfaceProps::kUseDeviceIndependentFonts_Flag, 101 SkSurfaceProps::kLegacyFontHost_InitType); 102 surface.reset(SkSurface::NewRenderTarget(ctx, SkBudgeted::kNo, info, 0, 103 &props)); 104 canvas = surface.get() ? surface->getCanvas() : inputCanvas; 105 // init our new canvas with the old canvas's matrix 106 canvas->setMatrix(inputCanvas->getTotalMatrix()); 107#endif 108 } 109 canvas->drawColor(sk_tool_utils::color_to_565(SK_ColorWHITE)); 110 111 SkRect bounds = fBlob->bounds(); 112 113 static const int kPadX = SkScalarFloorToInt(bounds.width() / 3); 114 static const int kPadY = SkScalarFloorToInt(bounds.height() / 3); 115 116 int rowCount = 0; 117 canvas->translate(SkIntToScalar(kPadX), SkIntToScalar(kPadY)); 118 canvas->save(); 119 SkRandom random; 120 121 SkPaint paint; 122 if (!fUseDFT) { 123 paint.setColor(sk_tool_utils::color_to_565(SK_ColorWHITE)); 124 } 125 paint.setAntiAlias(false); 126 127 static const SkScalar kSigma = SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(8)); 128 129 // setup blur paint 130 SkPaint blurPaint(paint); 131 blurPaint.setColor(sk_tool_utils::color_to_565(SK_ColorBLACK)); 132 SkAutoTUnref<SkMaskFilter> mf(SkBlurMaskFilter::Create(kNormal_SkBlurStyle, kSigma)); 133 blurPaint.setMaskFilter(mf); 134 135 for (int i = 0; i < 4; i++) { 136 canvas->save(); 137 switch (i % 2) { 138 case 0: 139 canvas->rotate(random.nextF() * 45.f); 140 break; 141 case 1: 142 canvas->rotate(-random.nextF() * 45.f); 143 break; 144 } 145 if (!fUseDFT) { 146 canvas->drawTextBlob(fBlob, 0, 0, blurPaint); 147 } 148 canvas->drawTextBlob(fBlob, 0, 0, paint); 149 canvas->restore(); 150 canvas->translate(bounds.width() + SK_Scalar1 * kPadX, 0); 151 ++rowCount; 152 if ((bounds.width() + 2 * kPadX) * rowCount > kWidth) { 153 canvas->restore(); 154 canvas->translate(0, bounds.height() + SK_Scalar1 * kPadY); 155 canvas->save(); 156 rowCount = 0; 157 } 158 } 159 canvas->restore(); 160 161#if SK_SUPPORT_GPU 162 // render offscreen buffer 163 if (surface) { 164 SkAutoCanvasRestore acr(inputCanvas, true); 165 // since we prepended this matrix already, we blit using identity 166 inputCanvas->resetMatrix(); 167 SkImage* image = surface->newImageSnapshot(); 168 inputCanvas->drawImage(image, 0, 0, nullptr); 169 image->unref(); 170 } 171#endif 172 } 173 174private: 175 SkAutoTUnref<const SkTextBlob> fBlob; 176 177 static const int kWidth = 2000; 178 static const int kHeight = 2000; 179 180 bool fUseDFT; 181 182 typedef GM INHERITED; 183}; 184 185////////////////////////////////////////////////////////////////////////////// 186 187DEF_GM( return new TextBlobMixedSizes(false); ) 188#if SK_SUPPORT_GPU 189DEF_GM( return new TextBlobMixedSizes(true); ) 190#endif 191} 192