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 "SkAutoMalloc.h" 11 #include "SkCanvas.h" 12 #include "SkRSXform.h" 13 #include "SkSurface.h" 14 #include "SkTextBlob.h" 15 #include "sk_tool_utils.h" 16 17 class DrawAtlasGM : public skiagm::GM { 18 static sk_sp<SkImage> MakeAtlas(SkCanvas* caller, const SkRect& target) { 19 SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100); 20 auto surface(sk_tool_utils::makeSurface(caller, info)); 21 SkCanvas* canvas = surface->getCanvas(); 22 // draw red everywhere, but we don't expect to see it in the draw, testing the notion 23 // that drawAtlas draws a subset-region of the atlas. 24 canvas->clear(SK_ColorRED); 25 26 SkPaint paint; 27 paint.setBlendMode(SkBlendMode::kClear); 28 SkRect r(target); 29 r.inset(-1, -1); 30 // zero out a place (with a 1-pixel border) to land our drawing. 31 canvas->drawRect(r, paint); 32 paint.setBlendMode(SkBlendMode::kSrcOver); 33 paint.setColor(SK_ColorBLUE); 34 paint.setAntiAlias(true); 35 canvas->drawOval(target, paint); 36 return surface->makeImageSnapshot(); 37 } 38 39 public: 40 DrawAtlasGM() {} 41 42 protected: 43 44 SkString onShortName() override { 45 return SkString("draw-atlas"); 46 } 47 48 SkISize onISize() override { 49 return SkISize::Make(640, 480); 50 } 51 52 void onDraw(SkCanvas* canvas) override { 53 const SkRect target = { 50, 50, 80, 90 }; 54 auto atlas = MakeAtlas(canvas, target); 55 56 const struct { 57 SkScalar fScale; 58 SkScalar fDegrees; 59 SkScalar fTx; 60 SkScalar fTy; 61 62 void apply(SkRSXform* xform) const { 63 const SkScalar rad = SkDegreesToRadians(fDegrees); 64 xform->fSCos = fScale * SkScalarCos(rad); 65 xform->fSSin = fScale * SkScalarSin(rad); 66 xform->fTx = fTx; 67 xform->fTy = fTy; 68 } 69 } rec[] = { 70 { 1, 0, 10, 10 }, // just translate 71 { 2, 0, 110, 10 }, // scale + translate 72 { 1, 30, 210, 10 }, // rotate + translate 73 { 2, -30, 310, 30 }, // scale + rotate + translate 74 }; 75 76 const int N = SK_ARRAY_COUNT(rec); 77 SkRSXform xform[N]; 78 SkRect tex[N]; 79 SkColor colors[N]; 80 81 for (int i = 0; i < N; ++i) { 82 rec[i].apply(&xform[i]); 83 tex[i] = target; 84 colors[i] = 0x80FF0000 + (i * 40 * 256); 85 } 86 87 SkPaint paint; 88 paint.setFilterQuality(kLow_SkFilterQuality); 89 paint.setAntiAlias(true); 90 91 canvas->drawAtlas(atlas.get(), xform, tex, N, nullptr, &paint); 92 canvas->translate(0, 100); 93 canvas->drawAtlas(atlas.get(), xform, tex, colors, N, SkBlendMode::kSrcIn, nullptr, &paint); 94 } 95 96 private: 97 typedef GM INHERITED; 98 }; 99 DEF_GM( return new DrawAtlasGM; ) 100 101 /////////////////////////////////////////////////////////////////////////////////////////////////// 102 #include "SkFont.h" 103 #include "SkFontPriv.h" 104 #include "SkPath.h" 105 #include "SkPathMeasure.h" 106 107 static void draw_text_on_path(SkCanvas* canvas, const void* text, size_t length, 108 const SkPoint xy[], const SkPath& path, const SkFont& font, const SkPaint& paint, 109 float baseline_offset) { 110 SkPathMeasure meas(path, false); 111 112 int count = font.countText(text, length, kUTF8_SkTextEncoding); 113 size_t size = count * (sizeof(SkRSXform) + sizeof(SkScalar)); 114 SkAutoSMalloc<512> storage(size); 115 SkRSXform* xform = (SkRSXform*)storage.get(); 116 SkScalar* widths = (SkScalar*)(xform + count); 117 118 // Compute a conservative bounds so we can cull the draw 119 const SkRect fontb = SkFontPriv::GetFontBounds(font); 120 const SkScalar max = SkTMax(SkTMax(SkScalarAbs(fontb.fLeft), SkScalarAbs(fontb.fRight)), 121 SkTMax(SkScalarAbs(fontb.fTop), SkScalarAbs(fontb.fBottom))); 122 const SkRect bounds = path.getBounds().makeOutset(max, max); 123 124 SkAutoTArray<SkGlyphID> glyphs(count); 125 font.textToGlyphs(text, length, kUTF8_SkTextEncoding, glyphs.get(), count); 126 font.getWidths(glyphs.get(), count, widths); 127 128 for (int i = 0; i < count; ++i) { 129 // we want to position each character on the center of its advance 130 const SkScalar offset = SkScalarHalf(widths[i]); 131 SkPoint pos; 132 SkVector tan; 133 if (!meas.getPosTan(xy[i].x() + offset, &pos, &tan)) { 134 pos = xy[i]; 135 tan.set(1, 0); 136 } 137 pos += SkVector::Make(-tan.fY, tan.fX) * baseline_offset; 138 139 xform[i].fSCos = tan.x(); 140 xform[i].fSSin = tan.y(); 141 xform[i].fTx = pos.x() - tan.y() * xy[i].y() - tan.x() * offset; 142 xform[i].fTy = pos.y() + tan.x() * xy[i].y() - tan.y() * offset; 143 } 144 145 canvas->drawTextBlob(SkTextBlob::MakeFromRSXform(glyphs.get(), count * sizeof(SkGlyphID), 146 &xform[0], font, kGlyphID_SkTextEncoding), 147 0, 0, paint); 148 149 if (true) { 150 SkPaint p; 151 p.setStyle(SkPaint::kStroke_Style); 152 canvas->drawRect(bounds, p); 153 } 154 } 155 156 #include "SkGradientShader.h" 157 static sk_sp<SkShader> make_shader() { 158 SkPoint pts[2] = {{0, 0}, {220, 0}}; 159 SkColor colors[2] = {SK_ColorRED, SK_ColorBLUE}; 160 return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkShader::kMirror_TileMode); 161 } 162 163 static void drawTextPath(SkCanvas* canvas, bool doStroke) { 164 const char text0[] = "ABCDFGHJKLMNOPQRSTUVWXYZ"; 165 const int N = sizeof(text0) - 1; 166 SkPoint pos[N]; 167 168 SkFont font; 169 font.setSize(100); 170 171 SkPaint paint; 172 paint.setShader(make_shader()); 173 paint.setAntiAlias(true); 174 if (doStroke) { 175 paint.setStyle(SkPaint::kStroke_Style); 176 paint.setStrokeWidth(2.25f); 177 paint.setStrokeJoin(SkPaint::kRound_Join); 178 } 179 180 SkScalar x = 0; 181 for (int i = 0; i < N; ++i) { 182 pos[i].set(x, 0); 183 x += font.measureText(&text0[i], 1, kUTF8_SkTextEncoding, nullptr, &paint); 184 } 185 186 SkPath path; 187 const float baseline_offset = -5; 188 189 const SkPath::Direction dirs[] = { 190 SkPath::kCW_Direction, SkPath::kCCW_Direction, 191 }; 192 for (auto d : dirs) { 193 path.reset(); 194 path.addOval(SkRect::MakeXYWH(160, 160, 540, 540), d); 195 draw_text_on_path(canvas, text0, N, pos, path, font, paint, baseline_offset); 196 } 197 198 paint.reset(); 199 paint.setStyle(SkPaint::kStroke_Style); 200 canvas->drawPath(path, paint); 201 } 202 203 DEF_SIMPLE_GM(drawTextRSXform, canvas, 430, 860) { 204 canvas->scale(0.5f, 0.5f); 205 const bool doStroke[] = { false, true }; 206 for (auto st : doStroke) { 207 drawTextPath(canvas, st); 208 canvas->translate(0, 860); 209 } 210 } 211 212 // Exercise xform blob and its bounds 213 DEF_SIMPLE_GM(blob_rsxform, canvas, 500, 100) { 214 SkFont font; 215 font.setTypeface(sk_tool_utils::create_portable_typeface()); 216 font.setSize(50); 217 218 const char text[] = "CrazyXform"; 219 constexpr size_t len = sizeof(text) - 1; 220 221 SkRSXform xforms[len]; 222 SkScalar scale = 1; 223 SkScalar x = 0, y = 0; 224 for (size_t i = 0; i < len; ++i) { 225 scale = SkScalarSin(i * SK_ScalarPI / (len-1)) * 0.75f + 0.5f; 226 xforms[i] = SkRSXform::Make(scale, 0, x, y); 227 x += 50 * scale; 228 } 229 230 auto blob = SkTextBlob::MakeFromRSXform(text, len, xforms, font); 231 232 SkPoint offset = { 20, 70 }; 233 SkPaint paint; 234 paint.setColor(0xFFCCCCCC); 235 canvas->drawRect(blob->bounds().makeOffset(offset.fX, offset.fY), paint); 236 paint.setColor(SK_ColorBLACK); 237 canvas->drawTextBlob(blob, offset.fX, offset.fY, paint); 238 } 239 240 #include "Resources.h" 241 #include "SkColorFilter.h" 242 #include "SkVertices.h" 243 244 static sk_sp<SkVertices> make_vertices(sk_sp<SkImage> image, const SkRect& r, 245 SkColor color) { 246 SkPoint pos[4]; 247 r.toQuad(pos); 248 SkColor colors[4] = { color, color, color, color }; 249 return SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4, 250 pos, pos, colors); 251 } 252 253 /* 254 * drawAtlas and drawVertices have several things in common: 255 * - can create compound "shaders", combining texture and colors 256 * - these are combined via an explicit blendmode 257 * - like drawImage, they only respect parts of the paint 258 * - colorfilter, imagefilter, blendmode, alpha 259 * 260 * This GM produces a series of pairs of images (atlas | vertices). 261 * Each pair should look the same, and each set shows a different combination 262 * of alpha | colorFilter | mode 263 */ 264 DEF_SIMPLE_GM(compare_atlas_vertices, canvas, 560, 585) { 265 const SkRect tex = SkRect::MakeWH(128, 128); 266 const SkRSXform xform = SkRSXform::Make(1, 0, 0, 0); 267 const SkColor color = 0x884488CC; 268 269 auto image = GetResourceAsImage("images/mandrill_128.png"); 270 auto verts = make_vertices(image, tex, color); 271 const sk_sp<SkColorFilter> filters[] = { 272 nullptr, 273 SkColorFilter::MakeModeFilter(0xFF00FF88, SkBlendMode::kModulate), 274 }; 275 const SkBlendMode modes[] = { 276 SkBlendMode::kSrcOver, 277 SkBlendMode::kPlus, 278 }; 279 280 canvas->translate(10, 10); 281 SkPaint paint; 282 for (SkBlendMode mode : modes) { 283 for (float alpha : { 1.0f, 0.5f }) { 284 paint.setAlphaf(alpha); 285 canvas->save(); 286 for (auto cf : filters) { 287 paint.setColorFilter(cf); 288 canvas->drawAtlas(image, &xform, &tex, &color, 1, 289 mode, &tex, &paint); 290 canvas->translate(128, 0); 291 paint.setShader(image->makeShader()); 292 canvas->drawVertices(verts, mode, paint); 293 paint.setShader(nullptr); 294 canvas->translate(145, 0); 295 } 296 canvas->restore(); 297 canvas->translate(0, 145); 298 } 299 } 300 } 301