1/* 2 * Copyright 2011 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 "include/core/SkCanvas.h" 9#include "include/core/SkColorFilter.h" 10#include "include/core/SkColorPriv.h" 11#include "include/core/SkContourMeasure.h" 12#include "include/core/SkGraphics.h" 13#include "include/core/SkPath.h" 14#include "include/core/SkRegion.h" 15#include "include/core/SkShader.h" 16#include "include/core/SkStream.h" 17#include "include/core/SkTime.h" 18#include "include/core/SkTypeface.h" 19#include "include/core/SkVertices.h" 20#include "include/effects/SkGradientShader.h" 21#include "include/effects/SkOpPathEffect.h" 22#include "include/private/SkTDArray.h" 23#include "include/utils/SkRandom.h" 24#include "samplecode/DecodeFile.h" 25#include "samplecode/Sample.h" 26#include "src/core/SkGeometry.h" 27#include "src/core/SkOSFile.h" 28#include "src/utils/SkUTF.h" 29#include "tools/Resources.h" 30#include "tools/timer/TimeUtils.h" 31 32namespace { 33static sk_sp<SkShader> make_shader0(SkIPoint* size) { 34 SkBitmap bm; 35 decode_file(GetResourceAsData("images/dog.jpg"), &bm); 36 *size = SkIPoint{bm.width(), bm.height()}; 37 return bm.makeShader(SkSamplingOptions(SkFilterMode::kLinear)); 38} 39 40static sk_sp<SkShader> make_shader1(const SkIPoint& size) { 41 SkPoint pts[] = { { 0, 0, }, 42 { SkIntToScalar(size.fX), SkIntToScalar(size.fY) } }; 43 SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED }; 44 return SkGradientShader::MakeLinear(pts, colors, nullptr, 45 SK_ARRAY_COUNT(colors), SkTileMode::kMirror); 46} 47 48class Patch { 49public: 50 Patch() { sk_bzero(fPts, sizeof(fPts)); } 51 ~Patch() {} 52 53 void setPatch(const SkPoint pts[12]) { 54 memcpy(fPts, pts, 12 * sizeof(SkPoint)); 55 fPts[12] = pts[0]; // the last shall be first 56 } 57 void setBounds(int w, int h) { fW = w; fH = h; } 58 59 void draw(SkCanvas*, const SkPaint&, int segsU, int segsV, 60 bool doTextures, bool doColors); 61 62private: 63 SkPoint fPts[13]; 64 int fW, fH; 65}; 66 67static void eval_patch_edge(const SkPoint cubic[], SkPoint samples[], int segs) { 68 SkScalar t = 0; 69 SkScalar dt = SK_Scalar1 / segs; 70 71 samples[0] = cubic[0]; 72 for (int i = 1; i < segs; i++) { 73 t += dt; 74 SkEvalCubicAt(cubic, t, &samples[i], nullptr, nullptr); 75 } 76} 77 78static void eval_sheet(const SkPoint edge[], int nu, int nv, int iu, int iv, 79 SkPoint* pt) { 80 const int TL = 0; 81 const int TR = nu; 82 const int BR = TR + nv; 83 const int BL = BR + nu; 84 85 SkScalar u = SkIntToScalar(iu) / nu; 86 SkScalar v = SkIntToScalar(iv) / nv; 87 88 SkScalar uv = u * v; 89 SkScalar Uv = (1 - u) * v; 90 SkScalar uV = u * (1 - v); 91 SkScalar UV = (1 - u) * (1 - v); 92 93 SkScalar x0 = UV * edge[TL].fX + uV * edge[TR].fX + Uv * edge[BL].fX + uv * edge[BR].fX; 94 SkScalar y0 = UV * edge[TL].fY + uV * edge[TR].fY + Uv * edge[BL].fY + uv * edge[BR].fY; 95 96 SkScalar x = (1 - v) * edge[TL+iu].fX + u * edge[TR+iv].fX + 97 v * edge[BR+nu-iu].fX + (1 - u) * edge[BL+nv-iv].fX - x0; 98 SkScalar y = (1 - v) * edge[TL+iu].fY + u * edge[TR+iv].fY + 99 v * edge[BR+nu-iu].fY + (1 - u) * edge[BL+nv-iv].fY - y0; 100 pt->set(x, y); 101} 102 103static SkColor make_color(SkScalar s, SkScalar t) { 104 return SkColorSetARGB(0xFF, SkUnitScalarClampToByte(s), SkUnitScalarClampToByte(t), 0); 105} 106 107void Patch::draw(SkCanvas* canvas, const SkPaint& paint, int nu, int nv, 108 bool doTextures, bool doColors) { 109 if (nu < 1 || nv < 1) { 110 return; 111 } 112 113 int i, npts = (nu + nv) * 2; 114 SkAutoSTMalloc<16, SkPoint> storage(npts + 1); 115 SkPoint* edge0 = storage.get(); 116 SkPoint* edge1 = edge0 + nu; 117 SkPoint* edge2 = edge1 + nv; 118 SkPoint* edge3 = edge2 + nu; 119 120 // evaluate the edge points 121 eval_patch_edge(fPts + 0, edge0, nu); 122 eval_patch_edge(fPts + 3, edge1, nv); 123 eval_patch_edge(fPts + 6, edge2, nu); 124 eval_patch_edge(fPts + 9, edge3, nv); 125 edge3[nv] = edge0[0]; // the last shall be first 126 127 for (i = 0; i < npts; i++) { 128// canvas->drawLine(edge0[i].fX, edge0[i].fY, edge0[i+1].fX, edge0[i+1].fY, paint); 129 } 130 131 int row, vertCount = (nu + 1) * (nv + 1); 132 SkAutoTMalloc<SkPoint> vertStorage(vertCount); 133 SkPoint* verts = vertStorage.get(); 134 135 // first row 136 memcpy(verts, edge0, (nu + 1) * sizeof(SkPoint)); 137 // rows 138 SkPoint* r = verts; 139 for (row = 1; row < nv; row++) { 140 r += nu + 1; 141 r[0] = edge3[nv - row]; 142 for (int col = 1; col < nu; col++) { 143 eval_sheet(edge0, nu, nv, col, row, &r[col]); 144 } 145 r[nu] = edge1[row]; 146 } 147 // last row 148 SkPoint* last = verts + nv * (nu + 1); 149 for (i = 0; i <= nu; i++) { 150 last[i] = edge2[nu - i]; 151 } 152 153// canvas->drawPoints(verts, vertCount, paint); 154 155 int stripCount = (nu + 1) * 2; 156 SkAutoTMalloc<SkPoint> stripStorage(stripCount * 2); 157 SkAutoTMalloc<SkColor> colorStorage(stripCount); 158 SkPoint* strip = stripStorage.get(); 159 SkPoint* tex = strip + stripCount; 160 SkColor* colors = colorStorage.get(); 161 SkScalar t = 0; 162 const SkScalar ds = SK_Scalar1 * fW / nu; 163 const SkScalar dt = SK_Scalar1 * fH / nv; 164 r = verts; 165 for (row = 0; row < nv; row++) { 166 SkPoint* upper = r; 167 SkPoint* lower = r + nu + 1; 168 r = lower; 169 SkScalar s = 0; 170 for (i = 0; i <= nu; i++) { 171 strip[i*2 + 0] = *upper++; 172 strip[i*2 + 1] = *lower++; 173 tex[i*2 + 0].set(s, t); 174 tex[i*2 + 1].set(s, t + dt); 175 colors[i*2 + 0] = make_color(s/fW, t/fH); 176 colors[i*2 + 1] = make_color(s/fW, (t + dt)/fH); 177 s += ds; 178 } 179 t += dt; 180 canvas->drawVertices(SkVertices::MakeCopy(SkVertices::kTriangleStrip_VertexMode, stripCount, 181 strip, doTextures ? tex : nullptr, 182 doColors ? colors : nullptr), 183 SkBlendMode::kModulate, paint); 184 } 185} 186 187static void drawpatches(SkCanvas* canvas, const SkPaint& paint, int nu, int nv, 188 Patch* patch) { 189 SkAutoCanvasRestore ar(canvas, true); 190 191 patch->draw(canvas, paint, nu, nv, false, false); 192 canvas->translate(SkIntToScalar(180), 0); 193 patch->draw(canvas, paint, nu, nv, true, false); 194 canvas->translate(SkIntToScalar(180), 0); 195 patch->draw(canvas, paint, nu, nv, false, true); 196 canvas->translate(SkIntToScalar(180), 0); 197 patch->draw(canvas, paint, nu, nv, true, true); 198} 199 200static constexpr SkScalar DX = 20; 201static constexpr SkScalar DY = 0; 202static constexpr SkScalar kS = 50; 203static constexpr SkScalar kT = 40; 204 205struct PatchView : public Sample { 206 sk_sp<SkShader> fShader0; 207 sk_sp<SkShader> fShader1; 208 SkScalar fAngle = 0; 209 SkIPoint fSize0 = {0, 0}, 210 fSize1 = {0, 0}; 211 SkPoint fPts[12] = { 212 {kS * 0, kT * 1}, 213 {kS * 1, kT * 1}, 214 {kS * 2, kT * 1}, 215 {kS * 3, kT * 1}, 216 {kS * 3, kT * 2}, 217 {kS * 3, kT * 3}, 218 {kS * 3, kT * 4}, 219 {kS * 2, kT * 4}, 220 {kS * 1, kT * 4}, 221 {kS * 0, kT * 4}, 222 {kS * 0, kT * 3}, 223 {kS * 0, kT * 2}, 224 }; 225 226 void onOnceBeforeDraw() override { 227 fShader0 = make_shader0(&fSize0); 228 fSize1 = fSize0; 229 if (fSize0.fX == 0 || fSize0.fY == 0) { 230 fSize1.set(2, 2); 231 } 232 fShader1 = make_shader1(fSize1); 233 this->setBGColor(SK_ColorGRAY); 234 } 235 236 SkString name() override { return SkString("Patch"); } 237 238 void onDrawContent(SkCanvas* canvas) override { 239 const int nu = 10; 240 const int nv = 10; 241 242 SkPaint paint; 243 paint.setDither(true); 244 245 canvas->translate(DX, DY); 246 247 Patch patch; 248 249 paint.setShader(fShader0); 250 if (fSize0.fX == 0) { 251 fSize0.fX = 1; 252 } 253 if (fSize0.fY == 0) { 254 fSize0.fY = 1; 255 } 256 patch.setBounds(fSize0.fX, fSize0.fY); 257 258 patch.setPatch(fPts); 259 drawpatches(canvas, paint, nu, nv, &patch); 260 261 paint.setShader(nullptr); 262 paint.setAntiAlias(true); 263 paint.setStrokeWidth(SkIntToScalar(5)); 264 canvas->drawPoints(SkCanvas::kPoints_PointMode, SK_ARRAY_COUNT(fPts), fPts, paint); 265 266 canvas->translate(0, SkIntToScalar(300)); 267 268 paint.setAntiAlias(false); 269 paint.setShader(fShader1); 270 { 271 SkMatrix m; 272 m.setSkew(1, 0); 273 paint.setShader(paint.getShader()->makeWithLocalMatrix(m)); 274 } 275 { 276 SkMatrix m; 277 m.setRotate(fAngle); 278 paint.setShader(paint.getShader()->makeWithLocalMatrix(m)); 279 } 280 patch.setBounds(fSize1.fX, fSize1.fY); 281 drawpatches(canvas, paint, nu, nv, &patch); 282 } 283 284 bool onAnimate(double nanos) override { 285 fAngle = TimeUtils::Scaled(1e-9 * nanos, 60, 360); 286 return true; 287 } 288 289 class PtClick : public Click { 290 public: 291 int fIndex; 292 PtClick(int index) : fIndex(index) {} 293 }; 294 295 static bool hittest(const SkPoint& pt, SkScalar x, SkScalar y) { 296 return SkPoint::Length(pt.fX - x, pt.fY - y) < SkIntToScalar(5); 297 } 298 299 Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override { 300 x -= DX; 301 y -= DY; 302 for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); i++) { 303 if (hittest(fPts[i], x, y)) { 304 return new PtClick((int)i); 305 } 306 } 307 return nullptr; 308 } 309 310 bool onClick(Click* click) override { 311 fPts[((PtClick*)click)->fIndex].set(click->fCurr.fX - DX, click->fCurr.fY - DY); 312 return true; 313 } 314 315private: 316 using INHERITED = Sample; 317}; 318} // namespace 319DEF_SAMPLE( return new PatchView(); ) 320 321////////////////////////////////////////////////////////////////////////////// 322 323namespace { 324static sk_sp<SkVertices> make_verts(const SkPath& path, SkScalar width) { 325 auto meas = SkContourMeasureIter(path, false).next(); 326 if (!meas) { 327 return nullptr; 328 } 329 330 const SkPoint src[2] = { 331 { 0, -width/2 }, { 0, width/2 }, 332 }; 333 SkTDArray<SkPoint> pts; 334 335 const SkScalar step = 2; 336 for (SkScalar distance = 0; distance < meas->length(); distance += step) { 337 SkMatrix mx; 338 if (!meas->getMatrix(distance, &mx)) { 339 continue; 340 } 341 SkPoint* dst = pts.append(2); 342 mx.mapPoints(dst, src, 2); 343 } 344 345 int vertCount = pts.count(); 346 int indexCount = 0; // no texture 347 unsigned flags = SkVertices::kHasColors_BuilderFlag; 348 SkVertices::Builder builder(SkVertices::kTriangleStrip_VertexMode, 349 vertCount, indexCount, flags); 350 memcpy(builder.positions(), pts.begin(), vertCount * sizeof(SkPoint)); 351 SkRandom rand; 352 for (int i = 0; i < vertCount; ++i) { 353 builder.colors()[i] = rand.nextU() | 0xFF000000; 354 } 355 SkDebugf("vert count = %d\n", vertCount); 356 357 return builder.detach(); 358} 359 360class PseudoInkView : public Sample { 361 enum { N = 100 }; 362 SkPath fPath; 363 sk_sp<SkVertices> fVertices[N]; 364 SkPaint fSkeletonP, fStrokeP, fVertsP; 365 bool fDirty = true; 366 367public: 368 PseudoInkView() { 369 fSkeletonP.setStyle(SkPaint::kStroke_Style); 370 fSkeletonP.setAntiAlias(true); 371 372 fStrokeP.setStyle(SkPaint::kStroke_Style); 373 fStrokeP.setStrokeWidth(30); 374 fStrokeP.setColor(0x44888888); 375 } 376 377protected: 378 SkString name() override { return SkString("PseudoInk"); } 379 380 bool onAnimate(double nanos) override { return true; } 381 382 void onDrawContent(SkCanvas* canvas) override { 383 if (fDirty) { 384 for (int i = 0; i < N; ++i) { 385 fVertices[i] = make_verts(fPath, 30); 386 } 387 fDirty = false; 388 } 389 for (int i = 0; i < N; ++i) { 390 canvas->drawVertices(fVertices[i], SkBlendMode::kSrc, fVertsP); 391 canvas->translate(1, 1); 392 } 393// canvas->drawPath(fPath, fStrokeP); 394 // canvas->drawPath(fPath, fSkeletonP); 395 } 396 397 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override { 398 Click* click = new Click(); 399 fPath.reset(); 400 fPath.moveTo(x, y); 401 return click; 402 } 403 404 bool onClick(Click* click) override { 405 switch (click->fState) { 406 case skui::InputState::kMove: 407 fPath.lineTo(click->fCurr); 408 fDirty = true; 409 break; 410 default: 411 break; 412 } 413 return true; 414 } 415 416private: 417 using INHERITED = Sample; 418}; 419} // namespace 420DEF_SAMPLE( return new PseudoInkView(); ) 421 422namespace { 423// Show stroking options using patheffects (and pathops) 424// and why strokeandfill is a hacks 425class ManyStrokesView : public Sample { 426 SkPath fPath; 427 sk_sp<SkPathEffect> fPE[6]; 428 429public: 430 ManyStrokesView() { 431 fPE[0] = SkStrokePathEffect::Make(20, SkPaint::kRound_Join, SkPaint::kRound_Cap); 432 433 auto p0 = SkStrokePathEffect::Make(25, SkPaint::kRound_Join, SkPaint::kRound_Cap); 434 auto p1 = SkStrokePathEffect::Make(20, SkPaint::kRound_Join, SkPaint::kRound_Cap); 435 fPE[1] = SkMergePathEffect::Make(p0, p1, SkPathOp::kDifference_SkPathOp); 436 437 fPE[2] = SkMergePathEffect::Make(nullptr, p1, SkPathOp::kDifference_SkPathOp); 438 fPE[3] = SkMergePathEffect::Make(nullptr, p1, SkPathOp::kUnion_SkPathOp); 439 fPE[4] = SkMergePathEffect::Make(p0, nullptr, SkPathOp::kDifference_SkPathOp); 440 fPE[5] = SkMergePathEffect::Make(p0, nullptr, SkPathOp::kIntersect_SkPathOp); 441 } 442 443protected: 444 SkString name() override { return SkString("ManyStrokes"); } 445 446 bool onAnimate(double nanos) override { return true; } 447 448 void dodraw(SkCanvas* canvas, sk_sp<SkPathEffect> pe, SkScalar x, SkScalar y, 449 const SkPaint* ptr = nullptr) { 450 SkPaint paint; 451 paint.setAntiAlias(true); 452 paint.setPathEffect(pe); 453 canvas->save(); 454 canvas->translate(x, y); 455 canvas->drawPath(fPath, ptr ? *ptr : paint); 456 457 paint.setPathEffect(nullptr); 458 paint.setStyle(SkPaint::kStroke_Style); 459 paint.setColor(SK_ColorGREEN); 460 canvas->drawPath(fPath, paint); 461 462 canvas->restore(); 463 } 464 465 void onDrawContent(SkCanvas* canvas) override { 466 SkPaint p; 467 p.setColor(0); 468 this->dodraw(canvas, nullptr, 0, 0, &p); 469 470 this->dodraw(canvas, fPE[0], 300, 0); 471 this->dodraw(canvas, fPE[1], 0, 300); 472 this->dodraw(canvas, fPE[2], 300, 300); 473 this->dodraw(canvas, fPE[3], 600, 300); 474 this->dodraw(canvas, fPE[4], 900, 0); 475 this->dodraw(canvas, fPE[5], 900, 300); 476 477 p.setColor(SK_ColorBLACK); 478 p.setStyle(SkPaint::kStrokeAndFill_Style); 479 p.setStrokeJoin(SkPaint::kRound_Join); 480 p.setStrokeCap(SkPaint::kRound_Cap); 481 p.setStrokeWidth(20); 482 this->dodraw(canvas, nullptr, 600, 0, &p); 483 } 484 485 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override { 486 Click* click = new Click(); 487 fPath.reset(); 488 fPath.moveTo(x, y); 489 return click; 490 } 491 492 bool onClick(Click* click) override { 493 switch (click->fState) { 494 case skui::InputState::kMove: 495 fPath.lineTo(click->fCurr); 496 break; 497 default: 498 break; 499 } 500 return true; 501 } 502 503private: 504 using INHERITED = Sample; 505}; 506} // namespace 507DEF_SAMPLE( return new ManyStrokesView(); ) 508