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