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 #include "SampleCode.h" 8 #include "SkBlurMask.h" 9 #include "SkCanvas.h" 10 #include "SkView.h" 11 #include "Sk1DPathEffect.h" 12 #include "Sk2DPathEffect.h" 13 #include "SkBlurMaskFilter.h" 14 #include "SkColorMatrixFilter.h" 15 #include "SkColorPriv.h" 16 #include "SkCornerPathEffect.h" 17 #include "SkDashPathEffect.h" 18 #include "SkDiscretePathEffect.h" 19 #include "SkEmbossMaskFilter.h" 20 #include "SkReadBuffer.h" 21 #include "SkWriteBuffer.h" 22 #include "SkGradientShader.h" 23 #include "SkLayerRasterizer.h" 24 #include "SkMath.h" 25 #include "SkPath.h" 26 #include "SkPictureRecorder.h" 27 #include "SkRegion.h" 28 #include "SkShader.h" 29 #include "SkCornerPathEffect.h" 30 #include "SkPathMeasure.h" 31 #include "SkPicture.h" 32 #include "SkRandom.h" 33 #include "SkTypeface.h" 34 #include "SkUtils.h" 35 36 #include <math.h> 37 #include "DecodeFile.h" 38 39 static inline SkPMColor rgb2gray(SkPMColor c) { 40 unsigned r = SkGetPackedR32(c); 41 unsigned g = SkGetPackedG32(c); 42 unsigned b = SkGetPackedB32(c); 43 44 unsigned x = (r * 5 + g * 7 + b * 4) >> 4; 45 46 return SkPackARGB32(0, x, x, x) | (c & (SK_A32_MASK << SK_A32_SHIFT)); 47 } 48 49 class SkGrayScaleColorFilter : public SkColorFilter { 50 public: 51 virtual void filterSpan(const SkPMColor src[], int count, 52 SkPMColor result[]) const override { 53 for (int i = 0; i < count; i++) 54 result[i] = rgb2gray(src[i]); 55 } 56 }; 57 58 class SkChannelMaskColorFilter : public SkColorFilter { 59 public: 60 SkChannelMaskColorFilter(U8CPU redMask, U8CPU greenMask, U8CPU blueMask) { 61 fMask = SkPackARGB32(0xFF, redMask, greenMask, blueMask); 62 } 63 64 virtual void filterSpan(const SkPMColor src[], int count, 65 SkPMColor result[]) const override { 66 SkPMColor mask = fMask; 67 for (int i = 0; i < count; i++) { 68 result[i] = src[i] & mask; 69 } 70 } 71 72 private: 73 SkPMColor fMask; 74 }; 75 76 /////////////////////////////////////////////////////////// 77 78 static void r0(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) { 79 p.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, 80 SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(3)), 81 SkBlurMaskFilter::kNone_BlurFlag)); 82 rastBuilder->addLayer(p, SkIntToScalar(3), SkIntToScalar(3)); 83 84 p.setMaskFilter(nullptr); 85 p.setStyle(SkPaint::kStroke_Style); 86 p.setStrokeWidth(SK_Scalar1); 87 rastBuilder->addLayer(p); 88 89 p.setAlpha(0x11); 90 p.setStyle(SkPaint::kFill_Style); 91 p.setBlendMode(SkBlendMode::kSrc); 92 rastBuilder->addLayer(p); 93 } 94 95 static void r1(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) { 96 rastBuilder->addLayer(p); 97 98 p.setAlpha(0x40); 99 p.setBlendMode(SkBlendMode::kSrc); 100 p.setStyle(SkPaint::kStroke_Style); 101 p.setStrokeWidth(SK_Scalar1*2); 102 rastBuilder->addLayer(p); 103 } 104 105 static void r2(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) { 106 p.setStyle(SkPaint::kStrokeAndFill_Style); 107 p.setStrokeWidth(SK_Scalar1*4); 108 rastBuilder->addLayer(p); 109 110 p.setStyle(SkPaint::kStroke_Style); 111 p.setStrokeWidth(SK_Scalar1*3/2); 112 p.setBlendMode(SkBlendMode::kClear); 113 rastBuilder->addLayer(p); 114 } 115 116 static void r3(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) { 117 p.setStyle(SkPaint::kStroke_Style); 118 p.setStrokeWidth(SK_Scalar1*3); 119 rastBuilder->addLayer(p); 120 121 p.setAlpha(0x20); 122 p.setStyle(SkPaint::kFill_Style); 123 p.setBlendMode(SkBlendMode::kSrc); 124 rastBuilder->addLayer(p); 125 } 126 127 static void r4(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) { 128 p.setAlpha(0x60); 129 rastBuilder->addLayer(p, SkIntToScalar(3), SkIntToScalar(3)); 130 131 p.setAlpha(0xFF); 132 p.setBlendMode(SkBlendMode::kClear); 133 rastBuilder->addLayer(p, SK_Scalar1*3/2, SK_Scalar1*3/2); 134 135 p.setBlendMode(SkBlendMode::kSrcOver); 136 rastBuilder->addLayer(p); 137 } 138 139 static void r5(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) { 140 rastBuilder->addLayer(p); 141 142 p.setPathEffect(SkDiscretePathEffect::Make(SK_Scalar1*4, SK_Scalar1*3)); 143 p.setBlendMode(SkBlendMode::kSrcOut); 144 rastBuilder->addLayer(p); 145 } 146 147 static void r6(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) { 148 rastBuilder->addLayer(p); 149 150 p.setAntiAlias(false); 151 SkLayerRasterizer::Builder rastBuilder2; 152 r5(&rastBuilder2, p); 153 p.setRasterizer(rastBuilder2.detach()); 154 p.setBlendMode(SkBlendMode::kClear); 155 rastBuilder->addLayer(p); 156 } 157 158 class Dot2DPathEffect : public Sk2DPathEffect { 159 public: 160 Dot2DPathEffect(SkScalar radius, const SkMatrix& matrix) 161 : Sk2DPathEffect(matrix), fRadius(radius) {} 162 163 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Dot2DPathEffect) 164 165 protected: 166 void next(const SkPoint& loc, int u, int v, SkPath* dst) const override { 167 dst->addCircle(loc.fX, loc.fY, fRadius); 168 } 169 170 void flatten(SkWriteBuffer& buffer) const override { 171 this->INHERITED::flatten(buffer); 172 buffer.writeScalar(fRadius); 173 } 174 175 private: 176 SkScalar fRadius; 177 178 typedef Sk2DPathEffect INHERITED; 179 }; 180 181 static void r7(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) { 182 SkMatrix lattice; 183 lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0); 184 lattice.postSkew(SK_Scalar1/3, 0, 0, 0); 185 p.setPathEffect(sk_make_sp<Dot2DPathEffect>(SK_Scalar1*4, lattice)); 186 rastBuilder->addLayer(p); 187 } 188 189 static void r8(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) { 190 rastBuilder->addLayer(p); 191 192 SkMatrix lattice; 193 lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0); 194 lattice.postSkew(SK_Scalar1/3, 0, 0, 0); 195 p.setPathEffect(sk_make_sp<Dot2DPathEffect>(SK_Scalar1*2, lattice)); 196 p.setBlendMode(SkBlendMode::kClear); 197 rastBuilder->addLayer(p); 198 199 p.setPathEffect(nullptr); 200 p.setBlendMode(SkBlendMode::kSrcOver); 201 p.setStyle(SkPaint::kStroke_Style); 202 p.setStrokeWidth(SK_Scalar1); 203 rastBuilder->addLayer(p); 204 } 205 206 static void r9(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) { 207 rastBuilder->addLayer(p); 208 209 SkMatrix lattice; 210 lattice.setScale(SK_Scalar1, SK_Scalar1*6, 0, 0); 211 lattice.postRotate(SkIntToScalar(30), 0, 0); 212 p.setPathEffect(SkLine2DPathEffect::Make(SK_Scalar1*2, lattice)); 213 p.setBlendMode(SkBlendMode::kClear); 214 rastBuilder->addLayer(p); 215 216 p.setPathEffect(nullptr); 217 p.setBlendMode(SkBlendMode::kSrcOver); 218 p.setStyle(SkPaint::kStroke_Style); 219 p.setStrokeWidth(SK_Scalar1); 220 rastBuilder->addLayer(p); 221 } 222 223 typedef void (*raster_proc)(SkLayerRasterizer::Builder*, SkPaint&); 224 225 static const raster_proc gRastProcs[] = { 226 r0, r1, r2, r3, r4, r5, r6, r7, r8, r9 227 }; 228 229 static const struct { 230 SkColor fMul, fAdd; 231 } gLightingColors[] = { 232 { 0x808080, 0x800000 }, // general case 233 { 0x707070, 0x707070 }, // no-pin case 234 { 0xFFFFFF, 0x800000 }, // just-add case 235 { 0x808080, 0x000000 }, // just-mul case 236 { 0xFFFFFF, 0x000000 } // identity case 237 }; 238 239 static void apply_shader(SkPaint* paint, int index) { 240 raster_proc proc = gRastProcs[index]; 241 if (proc) { 242 SkPaint p; 243 SkLayerRasterizer::Builder rastBuilder; 244 245 p.setAntiAlias(true); 246 proc(&rastBuilder, p); 247 paint->setRasterizer(rastBuilder.detach()); 248 } 249 250 #ifdef SK_SUPPORT_LEGACY_EMBOSSMASKFILTER 251 SkScalar dir[] = { SK_Scalar1, SK_Scalar1, SK_Scalar1 }; 252 paint->setMaskFilter(SkBlurMaskFilter::MakeEmboss( 253 SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(3)), dir, 254 SK_Scalar1/4, SkIntToScalar(4))); 255 paint->setColor(SK_ColorBLUE); 256 #endif 257 } 258 259 class DemoView : public SampleView { 260 public: 261 DemoView() {} 262 263 protected: 264 // overrides from SkEventSink 265 virtual bool onQuery(SkEvent* evt) { 266 if (SampleCode::TitleQ(*evt)) { 267 SampleCode::TitleR(evt, "Demo"); 268 return true; 269 } 270 return this->INHERITED::onQuery(evt); 271 } 272 273 virtual bool onClick(Click* click) { 274 return this->INHERITED::onClick(click); 275 } 276 277 void makePath(SkPath& path) { 278 path.addCircle(SkIntToScalar(20), SkIntToScalar(20), SkIntToScalar(20), 279 SkPath::kCCW_Direction); 280 for (int index = 0; index < 10; index++) { 281 SkScalar x = (float) cos(index / 10.0f * 2 * 3.1415925358f); 282 SkScalar y = (float) sin(index / 10.0f * 2 * 3.1415925358f); 283 x *= index & 1 ? 7 : 14; 284 y *= index & 1 ? 7 : 14; 285 x += SkIntToScalar(20); 286 y += SkIntToScalar(20); 287 if (index == 0) 288 path.moveTo(x, y); 289 else 290 path.lineTo(x, y); 291 } 292 path.close(); 293 } 294 295 virtual void onDrawContent(SkCanvas* canvas) { 296 canvas->save(); 297 this->drawPicture(canvas, 0); 298 canvas->restore(); 299 300 { 301 SkPictureRecorder recorder; 302 { 303 SkCanvas* record = recorder.beginRecording(320, 480, nullptr, 0); 304 this->drawPicture(record, 120); 305 } 306 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 307 308 canvas->translate(0, SkIntToScalar(120)); 309 310 SkRect clip; 311 clip.set(0, 0, SkIntToScalar(160), SkIntToScalar(160)); 312 do { 313 canvas->save(); 314 canvas->clipRect(clip); 315 picture->playback(canvas); 316 canvas->restore(); 317 if (clip.fRight < SkIntToScalar(320)) 318 clip.offset(SkIntToScalar(160), 0); 319 else if (clip.fBottom < SkIntToScalar(480)) 320 clip.offset(-SkIntToScalar(320), SkIntToScalar(160)); 321 else 322 break; 323 } while (true); 324 } 325 } 326 327 void drawPicture(SkCanvas* canvas, int spriteOffset) { 328 SkMatrix matrix; matrix.reset(); 329 SkPaint paint; 330 SkPath path; 331 SkPoint start = {0, 0}; 332 SkPoint stop = { SkIntToScalar(40), SkIntToScalar(40) }; 333 SkRect rect = {0, 0, SkIntToScalar(40), SkIntToScalar(40) }; 334 SkRect rect2 = {0, 0, SkIntToScalar(65), SkIntToScalar(20) }; 335 SkScalar left = 0, top = 0, x = 0, y = 0; 336 int index; 337 338 char ascii[] = "ascii..."; 339 int asciiLength = sizeof(ascii) - 1; 340 char utf8[] = "utf8" "\xe2\x80\xa6"; 341 short utf16[] = {'u', 't', 'f', '1', '6', 0x2026 }; 342 short utf16simple[] = {'u', 't', 'f', '1', '6', '!' }; 343 344 makePath(path); 345 SkTDArray<SkPoint>(pos); 346 pos.setCount(asciiLength); 347 for (index = 0; index < asciiLength; index++) 348 pos[index].set(SkIntToScalar((unsigned int)index * 10), 349 SkIntToScalar((unsigned int)index * 2)); 350 SkTDArray<SkPoint>(pos2); 351 pos2.setCount(asciiLength); 352 for (index = 0; index < asciiLength; index++) 353 pos2[index].set(SkIntToScalar((unsigned int)index * 10), 354 SkIntToScalar(20)); 355 356 // shaders 357 SkPoint linearPoints[] = { { 0, 0, }, { SkIntToScalar(40), SkIntToScalar(40) } }; 358 SkColor linearColors[] = { SK_ColorRED, SK_ColorBLUE }; 359 SkScalar* linearPos = nullptr; 360 int linearCount = 2; 361 SkShader::TileMode linearMode = SkShader::kMirror_TileMode; 362 auto linear = SkGradientShader::MakeLinear(linearPoints, 363 linearColors, linearPos, linearCount, linearMode); 364 365 SkPoint radialCenter = { SkIntToScalar(25), SkIntToScalar(25) }; 366 SkScalar radialRadius = SkIntToScalar(25); 367 SkColor radialColors[] = { SK_ColorGREEN, SK_ColorGRAY, SK_ColorRED }; 368 SkScalar radialPos[] = { 0, SkIntToScalar(3) / 5, SkIntToScalar(1)}; 369 int radialCount = 3; 370 SkShader::TileMode radialMode = SkShader::kRepeat_TileMode; 371 auto radial = SkGradientShader::MakeRadial(radialCenter, 372 radialRadius, radialColors, radialPos, radialCount, 373 radialMode); 374 375 SkEmbossMaskFilter::Light light; 376 light.fDirection[0] = SK_Scalar1/2; 377 light.fDirection[1] = SK_Scalar1/2; 378 light.fDirection[2] = SK_Scalar1/3; 379 light.fAmbient = 0x48; 380 light.fSpecular = 0x80; 381 382 auto lightingFilter = SkColorMatrixFilter::MakeLightingFilter( 383 0xff89bc45, 0xff112233); 384 385 canvas->save(); 386 canvas->translate(SkIntToScalar(0), SkIntToScalar(5)); 387 paint.setAntiAlias(true); 388 paint.setFilterQuality(kLow_SkFilterQuality); 389 // !!! draw through a clip 390 paint.setColor(SK_ColorLTGRAY); 391 paint.setStyle(SkPaint::kFill_Style); 392 SkRect clip = {0, 0, SkIntToScalar(320), SkIntToScalar(120)}; 393 canvas->clipRect(clip); 394 paint.setShader(SkShader::MakeBitmapShader(fTx, 395 SkShader::kMirror_TileMode, SkShader::kRepeat_TileMode)); 396 canvas->drawPaint(paint); 397 canvas->save(); 398 399 // line (exercises xfermode, colorShader, colorFilter, filterShader) 400 paint.setColor(SK_ColorGREEN); 401 paint.setStrokeWidth(SkIntToScalar(10)); 402 paint.setStyle(SkPaint::kStroke_Style); 403 paint.setBlendMode(SkBlendMode::kXor); 404 paint.setColorFilter(lightingFilter); 405 canvas->drawLine(start.fX, start.fY, stop.fX, stop.fY, paint); // should not be green 406 paint.setBlendMode(SkBlendMode::kSrcOver); 407 paint.setColorFilter(nullptr); 408 409 // rectangle 410 paint.setStyle(SkPaint::kFill_Style); 411 canvas->translate(SkIntToScalar(50), 0); 412 paint.setColor(SK_ColorYELLOW); 413 paint.setShader(linear); 414 paint.setPathEffect(pathEffectTest()); 415 canvas->drawRect(rect, paint); 416 paint.setPathEffect(nullptr); 417 418 // circle w/ emboss & transparent (exercises 3dshader) 419 canvas->translate(SkIntToScalar(50), 0); 420 paint.setMaskFilter(SkEmbossMaskFilter::Make( 421 SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(12)/5), light)); 422 canvas->drawOval(rect, paint); 423 canvas->translate(SkIntToScalar(10), SkIntToScalar(10)); 424 // paint.setShader(transparentShader)->unref(); 425 canvas->drawOval(rect, paint); 426 canvas->translate(0, SkIntToScalar(-10)); 427 428 // path 429 canvas->translate(SkIntToScalar(50), 0); 430 paint.setColor(SK_ColorRED); 431 paint.setStyle(SkPaint::kStroke_Style); 432 paint.setStrokeWidth(SkIntToScalar(5)); 433 paint.setShader(radial); 434 paint.setMaskFilter(nullptr); 435 canvas->drawPath(path, paint); 436 437 paint.setShader(nullptr); 438 // bitmap 439 canvas->translate(SkIntToScalar(50), 0); 440 paint.setStyle(SkPaint::kFill_Style); 441 canvas->drawBitmap(fBug, left, top, &paint); 442 443 canvas->translate(-SkIntToScalar(30), SkIntToScalar(30)); 444 paint.setShader(shaderTest()); // test compose shader 445 canvas->drawRect(rect2, paint); 446 paint.setShader(nullptr); 447 448 canvas->restore(); 449 // text 450 canvas->translate(0, SkIntToScalar(60)); 451 canvas->save(); 452 paint.setColor(SK_ColorGRAY); 453 canvas->drawPosText(ascii, asciiLength, pos.begin(), paint); 454 canvas->drawPosText(ascii, asciiLength, pos2.begin(), paint); 455 456 canvas->translate(SkIntToScalar(50), 0); 457 paint.setColor(SK_ColorCYAN); 458 canvas->drawText(utf8, sizeof(utf8) - 1, x, y, paint); 459 460 canvas->translate(SkIntToScalar(30), 0); 461 paint.setColor(SK_ColorMAGENTA); 462 paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); 463 matrix.setTranslate(SkIntToScalar(10), SkIntToScalar(10)); 464 canvas->drawTextOnPath((void*) utf16, sizeof(utf16), path, &matrix, paint); 465 canvas->translate(0, SkIntToScalar(20)); 466 canvas->drawTextOnPath((void*) utf16simple, sizeof(utf16simple), path, &matrix, paint); 467 canvas->restore(); 468 469 canvas->translate(0, SkIntToScalar(60)); 470 paint.setTextEncoding(SkPaint::kUTF8_TextEncoding); 471 canvas->restore(); 472 } 473 474 virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) { 475 fClickPt.set(x, y); 476 this->inval(nullptr); 477 return this->INHERITED::onFindClickHandler(x, y, modi); 478 } 479 480 sk_sp<SkPathEffect> pathEffectTest() { 481 static const int gXY[] = { 1, 0, 0, -1, 2, -1, 3, 0, 2, 1, 0, 1 }; 482 SkScalar gPhase = 0; 483 SkPath path; 484 path.moveTo(SkIntToScalar(gXY[0]), SkIntToScalar(gXY[1])); 485 for (unsigned i = 2; i < SK_ARRAY_COUNT(gXY); i += 2) 486 path.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1])); 487 path.close(); 488 path.offset(SkIntToScalar(-6), 0); 489 auto outer = SkPath1DPathEffect::Make(path, SkIntToScalar(12), 490 gPhase, SkPath1DPathEffect::kRotate_Style); 491 auto inner = SkDiscretePathEffect::Make(SkIntToScalar(2), 492 SkIntToScalar(1)/10); // SkCornerPathEffect(SkIntToScalar(2)); 493 return SkPathEffect::MakeCompose(outer, inner); 494 } 495 496 sk_sp<SkShader> shaderTest() { 497 SkPoint pts[] = { { 0, 0, }, { SkIntToScalar(100), 0 } }; 498 SkColor colors[] = { SK_ColorRED, SK_ColorBLUE }; 499 auto shaderA = SkGradientShader::MakeLinear(pts, colors, nullptr, 500 2, SkShader::kClamp_TileMode); 501 pts[1].set(0, SkIntToScalar(100)); 502 SkColor colors2[] = {SK_ColorBLACK, SkColorSetARGB(0x80, 0, 0, 0)}; 503 auto shaderB = SkGradientShader::MakeLinear(pts, colors2, nullptr, 504 2, SkShader::kClamp_TileMode); 505 return SkShader::MakeComposeShader(std::move(shaderA), std::move(shaderB), 506 SkBlendMode::kDstIn); 507 } 508 509 virtual void startTest() { 510 decode_file("/Users/caryclark/Desktop/bugcirc.gif", &fBug); 511 decode_file("/Users/caryclark/Desktop/tbcirc.gif", &fTb); 512 decode_file("/Users/caryclark/Desktop/05psp04.gif", &fTx); 513 } 514 515 void drawRaster(SkCanvas* canvas) { 516 for (size_t index = 0; index < SK_ARRAY_COUNT(gRastProcs); index++) 517 drawOneRaster(canvas); 518 } 519 520 void drawOneRaster(SkCanvas* canvas) { 521 canvas->save(); 522 523 SkScalar x = SkIntToScalar(20); 524 SkScalar y = SkIntToScalar(40); 525 SkPaint paint; 526 527 paint.setAntiAlias(true); 528 paint.setTextSize(SkIntToScalar(48)); 529 paint.setTypeface(SkTypeface::MakeFromName("sans-serif", 530 SkFontStyle::FromOldStyle(SkTypeface::kBold))); 531 532 SkString str("GOOGLE"); 533 534 for (size_t i = 0; i < SK_ARRAY_COUNT(gRastProcs); i++) { 535 apply_shader(&paint, (int)i); 536 537 // paint.setMaskFilter(nullptr); 538 // paint.setColor(SK_ColorBLACK); 539 540 #if 01 541 int index = i % SK_ARRAY_COUNT(gLightingColors); 542 paint.setColorFilter(SkColorMatrixFilter::MakeLightingFilter( 543 gLightingColors[index].fMul, 544 gLightingColors[index].fAdd)); 545 #endif 546 547 canvas->drawText(str.c_str(), str.size(), x, y, paint); 548 SkRect oval = { x, y - SkIntToScalar(40), x + SkIntToScalar(40), y }; 549 paint.setStyle(SkPaint::kStroke_Style); 550 canvas->drawOval(oval, paint); 551 paint.setStyle(SkPaint::kFill_Style); 552 553 y += paint.getFontSpacing(); 554 } 555 556 canvas->restore(); 557 } 558 559 private: 560 SkPoint fClickPt; 561 SkBitmap fBug, fTb, fTx; 562 typedef SampleView INHERITED; 563 }; 564 565 ////////////////////////////////////////////////////////////////////////////// 566 567 static SkView* MyFactory() { return new DemoView; } 568 static SkViewRegister reg(MyFactory); 569