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 #ifndef DMSrcSink_DEFINED 9 #define DMSrcSink_DEFINED 10 11 #include "gm/gm.h" 12 #include "include/core/SkBBHFactory.h" 13 #include "include/core/SkBitmap.h" 14 #include "include/core/SkCanvas.h" 15 #include "include/core/SkData.h" 16 #include "include/core/SkPicture.h" 17 #include "src/utils/SkMultiPictureDocument.h" 18 #include "tools/flags/CommonFlagsConfig.h" 19 #include "tools/gpu/MemoryCache.h" 20 21 #include <functional> 22 23 //#define TEST_VIA_SVG 24 25 namespace skiagm { 26 namespace verifiers { 27 class VerifierList; 28 } // namespace verifiers 29 } // namespace skiagm 30 31 namespace DM { 32 33 // This is just convenience. It lets you use either return "foo" or return SkStringPrintf(...). 34 struct ImplicitString : public SkString { 35 template <typename T> ImplicitStringImplicitString36 ImplicitString(const T& s) : SkString(s) {} ImplicitStringImplicitString37 ImplicitString() : SkString("") {} 38 }; 39 typedef ImplicitString Name; 40 typedef ImplicitString Path; 41 42 class Result { 43 public: 44 enum class Status : int { Ok, Fatal, Skip }; Result(Status status,const SkString & s)45 Result(Status status, const SkString& s) : fMsg(s), fStatus(status) {} Result(Status status,const char * s)46 Result(Status status, const char* s) : fMsg(s), fStatus(status) {} Result(Status status,const char * s,Args...args)47 template <typename... Args> Result (Status status, const char* s, Args... args) 48 : fMsg(SkStringPrintf(s, args...)), fStatus(status) {} 49 50 Result(const Result&) = default; 51 Result& operator=(const Result&) = default; 52 Ok()53 static Result Ok() { return Result(Status::Ok, nullptr); } 54 Fatal(const SkString & s)55 static Result Fatal(const SkString& s) { return Result(Status::Fatal, s); } Fatal(const char * s)56 static Result Fatal(const char* s) { return Result(Status::Fatal, s); } Fatal(const char * s,Args...args)57 template <typename... Args> static Result Fatal(const char* s, Args... args) { 58 return Result(Status::Fatal, s, args...); 59 } 60 Skip(const SkString & s)61 static Result Skip(const SkString& s) { return Result(Status::Skip, s); } Skip(const char * s)62 static Result Skip(const char* s) { return Result(Status::Skip, s); } Skip(const char * s,Args...args)63 template <typename... Args> static Result Skip(const char* s, Args... args) { 64 return Result(Status::Skip, s, args...); 65 } 66 isOk()67 bool isOk() { return fStatus == Status::Ok; } isFatal()68 bool isFatal() { return fStatus == Status::Fatal; } isSkip()69 bool isSkip() { return fStatus == Status::Skip; } 70 c_str()71 const char* c_str() const { return fMsg.c_str(); } status()72 Status status() const { return fStatus; } 73 74 private: 75 SkString fMsg; 76 Status fStatus; 77 }; 78 79 struct SinkFlags { 80 enum Type { kNull, kGPU, kVector, kRaster } type; 81 enum Approach { kDirect, kIndirect } approach; 82 enum Multisampled { kNotMultisampled, kMultisampled } multisampled; 83 SinkFlags(Type t, Approach a, Multisampled ms = kNotMultisampled) typeSinkFlags84 : type(t), approach(a), multisampled(ms) {} 85 }; 86 87 struct Src { ~SrcSrc88 virtual ~Src() {} 89 virtual Result SK_WARN_UNUSED_RESULT draw(GrDirectContext*, SkCanvas*) const = 0; 90 virtual SkISize size() const = 0; 91 virtual Name name() const = 0; modifyGrContextOptionsSrc92 virtual void modifyGrContextOptions(GrContextOptions* options) const {} vetoSrc93 virtual bool veto(SinkFlags) const { return false; } 94 pageCountSrc95 virtual int pageCount() const { return 1; } drawSrc96 virtual Result SK_WARN_UNUSED_RESULT draw(int, GrDirectContext* context, 97 SkCanvas* canvas) const { 98 return this->draw(context, canvas); 99 } sizeSrc100 virtual SkISize size(int) const { return this->size(); } 101 // Force Tasks using this Src to run on the main thread? serialSrc102 virtual bool serial() const { return false; } 103 104 /** Return a list of verifiers for the src, or null if no verifiers should be run .*/ getVerifiersSrc105 virtual std::unique_ptr<skiagm::verifiers::VerifierList> getVerifiers() const { 106 return nullptr; 107 } 108 }; 109 110 struct Sink { ~SinkSink111 virtual ~Sink() {} 112 // You may write to either the bitmap or stream. If you write to log, we'll print that out. 113 virtual Result SK_WARN_UNUSED_RESULT draw(const Src&, SkBitmap*, SkWStream*, SkString* log) 114 const = 0; 115 116 // Force Tasks using this Sink to run on the main thread? serialSink117 virtual bool serial() const { return false; } 118 119 // File extension for the content draw() outputs, e.g. "png", "pdf". 120 virtual const char* fileExtension() const = 0; 121 122 virtual SinkFlags flags() const = 0; 123 124 /** Returns the color type and space used by the sink. */ colorInfoSink125 virtual SkColorInfo colorInfo() const { return SkColorInfo(); } 126 }; 127 128 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 129 130 class GMSrc : public Src { 131 public: 132 explicit GMSrc(skiagm::GMFactory); 133 134 Result draw(GrDirectContext*, SkCanvas*) const override; 135 SkISize size() const override; 136 Name name() const override; 137 void modifyGrContextOptions(GrContextOptions* options) const override; 138 139 std::unique_ptr<skiagm::verifiers::VerifierList> getVerifiers() const override; 140 141 private: 142 skiagm::GMFactory fFactory; 143 }; 144 145 class CodecSrc : public Src { 146 public: 147 enum Mode { 148 kCodec_Mode, 149 // We choose to test only one mode with zero initialized memory. 150 // This will exercise all of the interesting cases in SkSwizzler 151 // without doubling the size of our test suite. 152 kCodecZeroInit_Mode, 153 kScanline_Mode, 154 kStripe_Mode, // Tests the skipping of scanlines 155 kCroppedScanline_Mode, // Tests (jpeg) cropped scanline optimization 156 kSubset_Mode, // For codecs that support subsets directly. 157 kAnimated_Mode, // For codecs that support animation. 158 }; 159 enum DstColorType { 160 kGetFromCanvas_DstColorType, 161 kGrayscale_Always_DstColorType, 162 kNonNative8888_Always_DstColorType, 163 }; 164 CodecSrc(Path, Mode, DstColorType, SkAlphaType, float); 165 166 Result draw(GrDirectContext*, SkCanvas*) const override; 167 SkISize size() const override; 168 Name name() const override; veto(SinkFlags)169 bool veto(SinkFlags) const override; 170 bool serial() const override { return fRunSerially; } 171 private: 172 Path fPath; 173 Mode fMode; 174 DstColorType fDstColorType; 175 SkAlphaType fDstAlphaType; 176 float fScale; 177 bool fRunSerially; 178 }; 179 180 class AndroidCodecSrc : public Src { 181 public: 182 AndroidCodecSrc(Path, CodecSrc::DstColorType, SkAlphaType, int sampleSize); 183 184 Result draw(GrDirectContext*, SkCanvas*) const override; 185 SkISize size() const override; 186 Name name() const override; veto(SinkFlags)187 bool veto(SinkFlags) const override; 188 bool serial() const override { return fRunSerially; } 189 private: 190 Path fPath; 191 CodecSrc::DstColorType fDstColorType; 192 SkAlphaType fDstAlphaType; 193 int fSampleSize; 194 bool fRunSerially; 195 }; 196 197 #ifdef SK_ENABLE_ANDROID_UTILS 198 // Allows for testing of various implementations of Android's BitmapRegionDecoder 199 class BRDSrc : public Src { 200 public: 201 enum Mode { 202 // Decode the entire image as one region. 203 kFullImage_Mode, 204 // Splits the image into multiple regions using a divisor and decodes the regions 205 // separately. Also, this test adds a border of a few pixels to each of the regions 206 // that it is decoding. This tests the behavior when a client asks for a region that 207 // does not fully fit in the image. 208 kDivisor_Mode, 209 }; 210 211 BRDSrc(Path, Mode, CodecSrc::DstColorType, uint32_t); 212 213 Result draw(GrDirectContext*, SkCanvas*) const override; 214 SkISize size() const override; 215 Name name() const override; 216 bool veto(SinkFlags) const override; 217 private: 218 Path fPath; 219 Mode fMode; 220 CodecSrc::DstColorType fDstColorType; 221 uint32_t fSampleSize; 222 }; 223 #endif 224 225 class ImageGenSrc : public Src { 226 public: 227 enum Mode { 228 kCodec_Mode, // Use CodecImageGenerator 229 kPlatform_Mode, // Uses CG or WIC 230 }; 231 ImageGenSrc(Path, Mode, SkAlphaType, bool); 232 233 Result draw(GrDirectContext*, SkCanvas*) const override; 234 SkISize size() const override; 235 Name name() const override; veto(SinkFlags)236 bool veto(SinkFlags) const override; 237 bool serial() const override { return fRunSerially; } 238 private: 239 Path fPath; 240 Mode fMode; 241 SkAlphaType fDstAlphaType; 242 bool fIsGpu; 243 bool fRunSerially; 244 }; 245 246 class ColorCodecSrc : public Src { 247 public: 248 ColorCodecSrc(Path, bool decode_to_dst); 249 250 Result draw(GrDirectContext*, SkCanvas*) const override; 251 SkISize size() const override; 252 Name name() const override; 253 bool veto(SinkFlags) const override; 254 private: 255 Path fPath; 256 bool fDecodeToDst; 257 }; 258 259 class SKPSrc : public Src { 260 public: 261 explicit SKPSrc(Path path); 262 263 Result draw(GrDirectContext*, SkCanvas*) const override; 264 SkISize size() const override; 265 Name name() const override; 266 private: 267 Path fPath; 268 }; 269 270 // This class extracts all the paths from an SKP and then removes unwanted paths according to the 271 // provided l/r trail. It then just draws the remaining paths. (Non-path draws are thrown out.) It 272 // is useful for finding a reduced repo case for path drawing bugs. 273 class BisectSrc : public SKPSrc { 274 public: 275 explicit BisectSrc(Path path, const char* trail); 276 277 Result draw(GrDirectContext*, SkCanvas*) const override; 278 279 private: 280 SkString fTrail; 281 282 using INHERITED = SKPSrc; 283 }; 284 285 #if defined(SK_ENABLE_SKOTTIE) 286 class SkottieSrc final : public Src { 287 public: 288 explicit SkottieSrc(Path path); 289 290 Result draw(GrDirectContext*, SkCanvas*) const override; 291 SkISize size() const override; 292 Name name() const override; 293 bool veto(SinkFlags) const override; 294 295 private: 296 // Generates a kTileCount x kTileCount filmstrip with evenly distributed frames. 297 static constexpr int kTileCount = 5; 298 299 // Fit kTileCount x kTileCount frames to a 1000x1000 film strip. 300 static constexpr SkScalar kTargetSize = 1000; 301 static constexpr SkScalar kTileSize = kTargetSize / kTileCount; 302 303 Path fPath; 304 }; 305 #endif 306 307 #if defined(SK_ENABLE_SKRIVE) 308 class SkRiveSrc final : public Src { 309 public: 310 explicit SkRiveSrc(Path path); 311 312 Result draw(GrDirectContext*, SkCanvas*) const override; 313 SkISize size() const override; 314 Name name() const override; 315 bool veto(SinkFlags) const override; 316 317 private: 318 // Generates a kTileCount x kTileCount filmstrip with evenly distributed frames. 319 static constexpr int kTileCount = 5; 320 321 // Fit kTileCount x kTileCount frames to a 1000x1000 film strip. 322 static constexpr SkScalar kTargetSize = 1000; 323 static constexpr SkScalar kTileSize = kTargetSize / kTileCount; 324 325 const Path fPath; 326 }; 327 #endif 328 329 #if defined(SK_XML) 330 } // namespace DM 331 332 class SkSVGDOM; 333 334 namespace DM { 335 336 class SVGSrc : public Src { 337 public: 338 explicit SVGSrc(Path path); 339 340 Result draw(GrDirectContext*, SkCanvas*) const override; 341 SkISize size() const override; 342 Name name() const override; 343 bool veto(SinkFlags) const override; 344 345 private: 346 Name fName; 347 sk_sp<SkSVGDOM> fDom; 348 SkScalar fScale; 349 350 using INHERITED = Src; 351 }; 352 #endif // SK_XML 353 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 354 355 class MSKPSrc : public Src { 356 public: 357 explicit MSKPSrc(Path path); 358 359 int pageCount() const override; 360 Result draw(GrDirectContext*, SkCanvas* c) const override; 361 Result draw(int, GrDirectContext*, SkCanvas*) const override; 362 SkISize size() const override; 363 SkISize size(int) const override; 364 Name name() const override; 365 366 private: 367 Path fPath; 368 mutable SkTArray<SkDocumentPage> fPages; 369 }; 370 371 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 372 373 class NullSink : public Sink { 374 public: NullSink()375 NullSink() {} 376 377 Result draw(const Src& src, SkBitmap*, SkWStream*, SkString*) const override; fileExtension()378 const char* fileExtension() const override { return ""; } flags()379 SinkFlags flags() const override { return SinkFlags{ SinkFlags::kNull, SinkFlags::kDirect }; } 380 }; 381 382 class GPUSink : public Sink { 383 public: 384 GPUSink(const SkCommandLineConfigGpu*, const GrContextOptions&); 385 386 Result draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; 387 Result onDraw(const Src&, SkBitmap*, SkWStream*, SkString*, 388 const GrContextOptions& baseOptions, 389 std::function<void(GrDirectContext*)> initContext = nullptr) const; 390 contextType()391 sk_gpu_test::GrContextFactory::ContextType contextType() const { return fContextType; } contextOverrides()392 const sk_gpu_test::GrContextFactory::ContextOverrides& contextOverrides() const { 393 return fContextOverrides; 394 } surfType()395 SkCommandLineConfigGpu::SurfType surfType() const { return fSurfType; } serial()396 bool serial() const override { return true; } fileExtension()397 const char* fileExtension() const override { return "png"; } flags()398 SinkFlags flags() const override { 399 SinkFlags::Multisampled ms = fSampleCount > 1 ? SinkFlags::kMultisampled 400 : SinkFlags::kNotMultisampled; 401 return SinkFlags{ SinkFlags::kGPU, SinkFlags::kDirect, ms }; 402 } baseContextOptions()403 const GrContextOptions& baseContextOptions() const { return fBaseContextOptions; } colorInfo()404 SkColorInfo colorInfo() const override { 405 return SkColorInfo(fColorType, fAlphaType, fColorSpace); 406 } 407 408 protected: 409 sk_sp<SkSurface> createDstSurface(GrDirectContext*, SkISize size) const; 410 bool readBack(SkSurface*, SkBitmap* dst) const; 411 412 private: 413 sk_gpu_test::GrContextFactory::ContextType fContextType; 414 sk_gpu_test::GrContextFactory::ContextOverrides fContextOverrides; 415 SkCommandLineConfigGpu::SurfType fSurfType; 416 int fSampleCount; 417 uint32_t fSurfaceFlags; 418 SkColorType fColorType; 419 SkAlphaType fAlphaType; 420 sk_sp<SkColorSpace> fColorSpace; 421 GrContextOptions fBaseContextOptions; 422 sk_gpu_test::MemoryCache fMemoryCache; 423 }; 424 425 class GPUThreadTestingSink : public GPUSink { 426 public: 427 GPUThreadTestingSink(const SkCommandLineConfigGpu*, const GrContextOptions&); 428 429 Result draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; 430 fileExtension()431 const char* fileExtension() const override { 432 // Suppress writing out results from this config - we just want to do our matching test 433 return nullptr; 434 } 435 436 private: 437 std::unique_ptr<SkExecutor> fExecutor; 438 439 using INHERITED = GPUSink; 440 }; 441 442 class GPUPersistentCacheTestingSink : public GPUSink { 443 public: 444 GPUPersistentCacheTestingSink(const SkCommandLineConfigGpu*, const GrContextOptions&); 445 446 Result draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; 447 fileExtension()448 const char* fileExtension() const override { 449 // Suppress writing out results from this config - we just want to do our matching test 450 return nullptr; 451 } 452 453 private: 454 int fCacheType; 455 456 using INHERITED = GPUSink; 457 }; 458 459 class GPUPrecompileTestingSink : public GPUSink { 460 public: 461 GPUPrecompileTestingSink(const SkCommandLineConfigGpu*, const GrContextOptions&); 462 463 Result draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; 464 fileExtension()465 const char* fileExtension() const override { 466 // Suppress writing out results from this config - we just want to do our matching test 467 return nullptr; 468 } 469 470 private: 471 using INHERITED = GPUSink; 472 }; 473 474 // This sink attempts to emulate Chrome's OOP-R behavior. It: 475 // doesn't use promise images 476 // uses only a single thread for both DDL creation & drawing 477 class GPUOOPRSink : public GPUSink { 478 public: 479 GPUOOPRSink(const SkCommandLineConfigGpu*, const GrContextOptions&); 480 481 Result draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; 482 483 private: 484 Result ooprDraw(const Src&, sk_sp<SkSurface> dstSurface, GrDirectContext*) const; 485 486 using INHERITED = GPUSink; 487 }; 488 489 // This sink attempts to better simulate the Chrome DDL use-case. It: 490 // creates the DDLs on separate recording threads 491 // performs all the GPU work on a separate GPU thread 492 // In the future this should be expanded to: 493 // upload on a utility thread w/ access to a shared context 494 // compile the programs on the utility thread 495 // perform fine grained scheduling of gpu tasks based on their image and program prerequisites 496 // create a single "compositing" DDL that is replayed last 497 class GPUDDLSink : public GPUSink { 498 public: 499 GPUDDLSink(const SkCommandLineConfigGpu*, const GrContextOptions&); 500 501 Result draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; 502 503 private: 504 Result ddlDraw(const Src&, 505 sk_sp<SkSurface> dstSurface, 506 SkTaskGroup* recordingTaskGroup, 507 SkTaskGroup* gpuTaskGroup, 508 sk_gpu_test::TestContext* gpuTestCtx, 509 GrDirectContext* gpuThreadCtx) const; 510 511 std::unique_ptr<SkExecutor> fRecordingExecutor; 512 std::unique_ptr<SkExecutor> fGPUExecutor; 513 514 using INHERITED = GPUSink; 515 }; 516 517 class PDFSink : public Sink { 518 public: PDFSink(bool pdfa,SkScalar rasterDpi)519 PDFSink(bool pdfa, SkScalar rasterDpi) : fPDFA(pdfa), fRasterDpi(rasterDpi) {} 520 Result draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; fileExtension()521 const char* fileExtension() const override { return "pdf"; } flags()522 SinkFlags flags() const override { return SinkFlags{ SinkFlags::kVector, SinkFlags::kDirect }; } 523 bool fPDFA; 524 SkScalar fRasterDpi; 525 }; 526 527 class XPSSink : public Sink { 528 public: 529 XPSSink(); 530 531 Result draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; fileExtension()532 const char* fileExtension() const override { return "xps"; } flags()533 SinkFlags flags() const override { return SinkFlags{ SinkFlags::kVector, SinkFlags::kDirect }; } 534 }; 535 536 class RasterSink : public Sink { 537 public: 538 explicit RasterSink(SkColorType, sk_sp<SkColorSpace> = nullptr); 539 540 Result draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; fileExtension()541 const char* fileExtension() const override { return "png"; } flags()542 SinkFlags flags() const override { return SinkFlags{ SinkFlags::kRaster, SinkFlags::kDirect }; } 543 544 private: 545 SkColorType fColorType; 546 sk_sp<SkColorSpace> fColorSpace; 547 }; 548 549 class ThreadedSink : public RasterSink { 550 public: 551 explicit ThreadedSink(SkColorType, sk_sp<SkColorSpace> = nullptr); 552 Result draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; 553 }; 554 555 class SKPSink : public Sink { 556 public: 557 SKPSink(); 558 559 Result draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; fileExtension()560 const char* fileExtension() const override { return "skp"; } flags()561 SinkFlags flags() const override { return SinkFlags{ SinkFlags::kVector, SinkFlags::kDirect }; } 562 }; 563 564 class DebugSink : public Sink { 565 public: 566 Result draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; fileExtension()567 const char* fileExtension() const override { return "json"; } flags()568 SinkFlags flags() const override { return SinkFlags{ SinkFlags::kVector, SinkFlags::kDirect }; } 569 }; 570 571 class SVGSink : public Sink { 572 public: 573 SVGSink(int pageIndex = 0); 574 575 Result draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; fileExtension()576 const char* fileExtension() const override { return "svg"; } flags()577 SinkFlags flags() const override { return SinkFlags{ SinkFlags::kVector, SinkFlags::kDirect }; } 578 579 private: 580 int fPageIndex; 581 }; 582 583 584 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 585 586 class Via : public Sink { 587 public: Via(Sink * sink)588 explicit Via(Sink* sink) : fSink(sink) {} fileExtension()589 const char* fileExtension() const override { return fSink->fileExtension(); } serial()590 bool serial() const override { return fSink->serial(); } flags()591 SinkFlags flags() const override { 592 SinkFlags flags = fSink->flags(); 593 flags.approach = SinkFlags::kIndirect; 594 return flags; 595 } 596 protected: 597 std::unique_ptr<Sink> fSink; 598 }; 599 600 class ViaMatrix : public Via { 601 public: 602 ViaMatrix(SkMatrix, Sink*); 603 Result draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; 604 private: 605 const SkMatrix fMatrix; 606 }; 607 608 class ViaUpright : public Via { 609 public: 610 ViaUpright(SkMatrix, Sink*); 611 Result draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; 612 private: 613 const SkMatrix fMatrix; 614 }; 615 616 class ViaSerialization : public Via { 617 public: ViaSerialization(Sink * sink)618 explicit ViaSerialization(Sink* sink) : Via(sink) {} 619 Result draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; 620 }; 621 622 class ViaPicture : public Via { 623 public: ViaPicture(Sink * sink)624 explicit ViaPicture(Sink* sink) : Via(sink) {} 625 Result draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; 626 }; 627 628 class ViaSVG : public Via { 629 public: ViaSVG(Sink * sink)630 explicit ViaSVG(Sink* sink) : Via(sink) {} 631 Result draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override; 632 }; 633 634 } // namespace DM 635 636 #endif//DMSrcSink_DEFINED 637