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/gm.h" 9 #include "include/core/SkCanvas.h" 10 #include "include/core/SkColor.h" 11 #include "include/core/SkColorSpace.h" 12 #include "include/core/SkFont.h" 13 #include "include/core/SkFontStyle.h" 14 #include "include/core/SkFontTypes.h" 15 #include "include/core/SkImageInfo.h" 16 #include "include/core/SkPaint.h" 17 #include "include/core/SkRect.h" 18 #include "include/core/SkRefCnt.h" 19 #include "include/core/SkScalar.h" 20 #include "include/core/SkSize.h" 21 #include "include/core/SkString.h" 22 #include "include/core/SkSurface.h" 23 #include "include/core/SkSurfaceProps.h" 24 #include "include/core/SkTextBlob.h" 25 #include "include/core/SkTypeface.h" 26 #include "include/gpu/GrContext.h" 27 #include "tools/ToolUtils.h" 28 #include "tools/fonts/RandomScalerContext.h" 29 30 #include <string.h> 31 #include <utility> 32 33 class GrRenderTargetContext; 34 35 namespace skiagm { 36 class TextBlobRandomFont : public GpuGM { 37 public: 38 // This gm tests that textblobs can be translated and scaled with a font that returns random 39 // but deterministic masks TextBlobRandomFont()40 TextBlobRandomFont() { } 41 42 protected: onOnceBeforeDraw()43 void onOnceBeforeDraw() override { 44 SkTextBlobBuilder builder; 45 46 const char* text = "The quick brown fox jumps over the lazy dog."; 47 48 SkPaint paint; 49 paint.setAntiAlias(true); 50 paint.setColor(SK_ColorMAGENTA); 51 52 // make textbloben 53 SkFont font; 54 font.setSize(32); 55 font.setEdging(SkFont::Edging::kSubpixelAntiAlias); 56 57 // Setup our random scaler context 58 auto typeface = ToolUtils::create_portable_typeface("sans-serif", SkFontStyle::Bold()); 59 if (!typeface) { 60 typeface = SkTypeface::MakeDefault(); 61 } 62 font.setTypeface(sk_make_sp<SkRandomTypeface>(std::move(typeface), paint, false)); 63 64 SkScalar y = 0; 65 SkRect bounds; 66 font.measureText(text, strlen(text), SkTextEncoding::kUTF8, &bounds); 67 y -= bounds.fTop; 68 ToolUtils::add_to_text_blob(&builder, text, font, 0, y); 69 y += bounds.fBottom; 70 71 // A8 72 const char* bigtext1 = "The quick brown fox"; 73 const char* bigtext2 = "jumps over the lazy dog."; 74 font.setSize(160); 75 font.setSubpixel(false); 76 font.setEdging(SkFont::Edging::kAntiAlias); 77 font.measureText(bigtext1, strlen(bigtext1), SkTextEncoding::kUTF8, &bounds); 78 y -= bounds.fTop; 79 ToolUtils::add_to_text_blob(&builder, bigtext1, font, 0, y); 80 y += bounds.fBottom; 81 82 font.measureText(bigtext2, strlen(bigtext2), SkTextEncoding::kUTF8, &bounds); 83 y -= bounds.fTop; 84 ToolUtils::add_to_text_blob(&builder, bigtext2, font, 0, y); 85 y += bounds.fBottom; 86 87 // color emoji 88 if (sk_sp<SkTypeface> origEmoji = ToolUtils::emoji_typeface()) { 89 font.setTypeface(sk_make_sp<SkRandomTypeface>(origEmoji, paint, false)); 90 const char* emojiText = ToolUtils::emoji_sample_text(); 91 font.measureText(emojiText, strlen(emojiText), SkTextEncoding::kUTF8, &bounds); 92 y -= bounds.fTop; 93 ToolUtils::add_to_text_blob(&builder, emojiText, font, 0, y); 94 y += bounds.fBottom; 95 } 96 97 // build 98 fBlob = builder.make(); 99 } 100 onShortName()101 SkString onShortName() override { 102 return SkString("textblobrandomfont"); 103 } 104 onISize()105 SkISize onISize() override { 106 return SkISize::Make(kWidth, kHeight); 107 } 108 onDraw(GrContext * context,GrRenderTargetContext *,SkCanvas * canvas,SkString * errorMsg)109 DrawResult onDraw(GrContext* context, GrRenderTargetContext*, SkCanvas* canvas, 110 SkString* errorMsg) override { 111 // This GM exists to test a specific feature of the GPU backend. 112 // This GM uses ToolUtils::makeSurface which doesn't work well with vias. 113 // This GM uses SkRandomTypeface which doesn't work well with serialization. 114 canvas->drawColor(SK_ColorWHITE); 115 116 SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, canvas->imageInfo().colorType(), 117 kPremul_SkAlphaType, 118 canvas->imageInfo().refColorSpace()); 119 SkSurfaceProps props(0, kUnknown_SkPixelGeometry); 120 auto surface(ToolUtils::makeSurface(canvas, info, &props)); 121 if (!surface) { 122 *errorMsg = "This test requires a surface"; 123 return DrawResult::kFail; 124 } 125 126 SkPaint paint; 127 paint.setAntiAlias(true); 128 129 SkCanvas* surfaceCanvas = surface->getCanvas(); 130 131 SkScalar stride = SkScalarCeilToScalar(fBlob->bounds().height()); 132 SkScalar yOffset = 5; 133 134 canvas->save(); 135 // Originally we would alternate between rotating and not to force blob regeneration, 136 // but that code seems to have rotted. Keeping the rotate to match the old GM as 137 // much as possible, and it seems like a reasonable stress test for transformed 138 // color emoji. 139 canvas->rotate(-0.05f); 140 canvas->drawTextBlob(fBlob, 10, yOffset, paint); 141 yOffset += stride; 142 canvas->restore(); 143 144 // Rotate in the surface canvas, not the final canvas, to avoid aliasing 145 surfaceCanvas->rotate(-0.05f); 146 surfaceCanvas->drawTextBlob(fBlob, 10, yOffset, paint); 147 surface->draw(canvas, 0, 0, nullptr); 148 yOffset += stride; 149 150 // free gpu resources and verify 151 context->freeGpuResources(); 152 153 canvas->rotate(-0.05f); 154 canvas->drawTextBlob(fBlob, 10, yOffset, paint); 155 yOffset += stride; 156 return DrawResult::kOk; 157 } 158 159 private: 160 sk_sp<SkTextBlob> fBlob; 161 162 static constexpr int kWidth = 2000; 163 static constexpr int kHeight = 1600; 164 165 typedef GM INHERITED; 166 }; 167 168 ////////////////////////////////////////////////////////////////////////////// 169 170 DEF_GM(return new TextBlobRandomFont;) 171 } 172