bookmaker initial checkin

bookmaker is a tool that generates documentation
backends from a canonical markup. Documentation for
bookmaker itself is evolving at docs/usingBookmaker.bmh,
which is visible online at skia.org/user/api/bmh_usingBookmaker

Change-Id: Ic76ddf29134895b5c2ebfbc84603e40ff08caf09
Reviewed-on: https://skia-review.googlesource.com/28000
Commit-Queue: Cary Clark <caryclark@google.com>
Reviewed-by: Cary Clark <caryclark@google.com>
diff --git a/docs/SkCanvas.bmh b/docs/SkCanvas.bmh
new file mode 100644
index 0000000..aefbae4
--- /dev/null
+++ b/docs/SkCanvas.bmh
@@ -0,0 +1,5721 @@
+#Topic Canvas
+
+Canvas provides an interface for drawing, and how the drawing is clipped and transformed.
+Canvas contains a stack of Matrix and Clip values.
+
+Canvas and Paint together provide the state to draw into Surface or Device.
+Each Canvas draw call transforms the geometry of the object by the concatenation of all Matrix
+values in the stack. 
+The transformed geometry is clipped by the intersection of all of Clip values in the stack.
+The Canvas draw calls take Paint parameter for drawing state.
+Create Paint to supply the drawing state, such as Color,
+Typeface, Paint_Text_Size, Paint_Stroke_Width, Shader and so on.
+
+To draw to a pixel-based destination, create Raster_Surface or GPU_Surface. 
+Request Canvas from Surface to obtain the interface to draw. 
+Canvas generated by Raster_Surface draws to memory visible to the CPU. 
+Canvas generated by GPU_Surface uses Vulkan or OpenGL to draw to the GPU.
+
+Canvas can be constructed to draw to Bitmap without first creating Raster_Surface.
+This approach may be deprecated in the future.  
+
+To draw to a document, obtain Canvas from SVG_Canvas, Document_PDF, or Picture_Recorder.
+Document-based Canvas and other Canvas subclasses reference Device describing the destination.
+
+#Class SkCanvas
+
+#Topic Overview
+
+#Subtopic Subtopics
+#Table
+#Legend
+# topics                 # description                                 ##
+#Legend ##
+#ToDo generate a TOC here ##
+#Table ##
+#Subtopic ##
+
+#Subtopic Constants
+#Table
+#Legend
+# constants                      # description                                 ##
+#Legend ##
+# Lattice::Flags                 # Controls Lattice transparency. ##
+# PointMode                      # Sets drawPoints options. ##
+# SaveLayerFlags                 # Sets SaveLayerRec options. ##
+# SrcRectConstraint              # Sets drawImageRect options. ##
+#Table ##
+#Subtopic ##
+
+#Subtopic Structs
+#Table
+#Legend
+# struct                         # description                                 ##
+#Legend ##
+# Lattice                        # Divides Bitmap, Image into a rectangular grid. ##
+# SaveLayerRec                   # Contains state to create the layer offscreen. ##
+#Table ##
+#Subtopic ##
+
+#Subtopic Constructors
+
+Create the desired type of Surface to obtain its Canvas when possible. Constructors are useful
+when no Surface is required, and some helpers implicitly create Raster_Surface.
+
+#Table
+#Legend
+#                                                               # description                                     ##
+#Legend ##
+# SkCanvas()                                                    # No Surface, no dimensions.                      ##
+# SkCanvas(int width, int height, const SkSurfaceProps* = NULL) # No Surface, set dimensions, Surface_Properties. ##
+# SkCanvas(SkBaseDevice* device)                                # Existing Device. (SkBaseDevice is private.)     ##
+# SkCanvas(const SkBitmap& bitmap)                              # Uses existing Bitmap.                           ##
+# SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props) # Uses existing Bitmap and Surface_Properties.    ##
+# MakeRasterDirect                                              # Creates from SkImageInfo and Pixel_Storage.     ##
+# MakeRasterDirectN32                                           # Creates from image data and Pixel_Storage.      ##
+#ToDo incomplete ##
+#Table ##
+#Subtopic ##
+
+#Subtopic Member_Functions
+#Table
+#Legend
+# function                         # description                                                   ##
+#Legend ##
+# accessTopLayerPixels             # Returns writable pixel access if available. ##
+# accessTopRasterHandle            # Returns context that tracks Clip and Matrix. ##
+# clear()                          # Fills Clip with Color. ##
+# clipPath                         # Combines Clip with Path. ##
+# clipRRect                        # Combines Clip with Round_Rect. ##
+# clipRect                         # Combines Clip with Rect. ##
+# clipRegion                       # Combines Clip with Region. ##
+# concat()                         # Multiplies Matrix by Matrix. ##
+# discard()                        # Makes Canvas contents undefined. ##
+# drawAnnotation                   # Associates a Rect with a key-value pair.##
+# drawArc                          # Draws Arc using Clip, Matrix, and Paint.##
+# drawAtlas                        # Draws sprites using Clip, Matrix, and Paint.##
+# drawBitmap                       # Draws Bitmap at (x, y) position. ##
+# drawBitmapLattice                # Draws differentially stretched Bitmap. ##
+# drawBitmapNine                   # Draws Nine_Patch Bitmap. ##
+# drawBitmapRect                   # Draws Bitmap, source Rect to destination Rect. ##
+# drawCircle                       # Draws Circle using Clip, Matrix, and Paint. ##
+# drawColor                        # Fills Clip with Color and Blend_Mode. ##
+# drawDRRect                       # Draws double Round_Rect stroked or filled. ##
+# drawDrawable                     # Draws Drawable, encapsulated drawing commands. ##
+# drawIRect                        # Draws IRect using Clip, Matrix, and Paint. ##
+# drawImage                        # Draws Image at (x, y) position. ##
+# drawImageLattice                 # Draws differentially stretched Image. ##
+# drawImageNine                    # Draws Nine_Patch Image. ##
+# drawImageRect                    # Draws Image, source Rect to destination Rect. ##
+# drawLine                         # Draws line segment between two points.##
+# drawOval                         # Draws Oval using Clip, Matrix, and Paint. ##
+# drawPaint                        # Fills Clip with Paint. ##
+# drawPatch                        # Draws cubic Coons patch. ##
+# drawPath                         # Draws Path using Clip, Matrix, and Paint. ##
+# drawPicture                      # Draws Picture using Clip and Matrix. ##
+# drawPoint                        # Draws point at (x, y) position. ##
+# drawPoints                       # Draws array as points, lines, polygon. ##
+# drawPosText                      # Draws text at array of (x, y) positions. ##
+# drawPosTextH                     # Draws text at x positions with common baseline. ##
+# drawRRect                        # Draws Round_Rect using Clip, Matrix, and Paint. ##
+# drawRect                         # Draws Rect using Clip, Matrix, and Paint. ##
+# drawRegion                       # Draws Region using Clip, Matrix, and Paint. ##
+# drawRoundRect                    # Draws Round_Rect using Clip, Matrix, and Paint. ##
+# drawText                         # Draws text at (x, y), using font advance. ##
+# drawTextBlob                     # Draws text with arrays of positions and Paint. ##
+# drawTextOnPath                   # Draws text following Path contour. ##
+# drawTextOnPathHV                 # Draws text following Path with offsets. ##
+# drawTextRSXform                  # Draws text with array of RSXform. ##
+# drawString                       # Draws null terminated string at (x, y) using font advance. ##
+# drawVertices                     # Draws Vertices, a triangle mesh. ##
+# flush()                          # Triggers execution of all pending draw operations. ##
+# getBaseLayerSize                 # Gets size of base layer in global coordinates. ##
+# getDeviceClipBounds              # Returns IRect bounds of Clip. ##
+# getDrawFilter                    # Legacy; to be deprecated. ##
+# getGrContext                     # Returns GPU_Context of the GPU_Surface. ##
+# getLocalClipBounds               # Returns Clip bounds in source coordinates. ##
+# getMetaData                      # Associates additional data with the canvas. ##
+# getProps                         # Copies Surface_Properties if available. ##
+# getSaveCount                     # Returns depth of stack containing Clip and Matrix. ##
+# getTotalMatrix                   # Returns Matrix. ##
+# imageInfo                        # Returns Image_Info for Canvas. ##
+# isClipEmpty                      # Returns if Clip is empty. ##
+# isClipRect                       # Returns if Clip is Rect and not empty. ##
+# MakeRasterDirect                 # Creates Canvas from SkImageInfo and pixel data. ##
+# MakeRasterDirectN32              # Creates Canvas from image specifications and pixel data. ##
+# makeSurface                      # Creates Surface matching SkImageInfo and SkSurfaceProps. ##
+# peekPixels                       # Returns if Canvas has direct access to its pixels. ##
+# quickReject                      # Returns if Rect is outside Clip. ##
+# readPixels                       # Copies and converts rectangle of pixels from Canvas. ##
+# resetMatrix                      # Resets Matrix to identity. ##
+# restore()                        # Restores changes to Clip and Matrix, pops save stack. ##
+# restoreToCount                   # Restores changes to Clip and Matrix to given depth. ##
+# rotate()                         # Rotates Matrix. ##
+# save()                           # Saves Clip and Matrix on stack. ##
+# saveLayer                        # Saves Clip and Matrix on stack; creates offscreen. ##
+# saveLayerAlpha                   # Saves Clip and Matrix on stack; creates offscreen; sets opacity. ##
+# saveLayerPreserveLCDTextRequests # Saves Clip and Matrix on stack; creates offscreen for LCD text. ##
+# scale()                          # Scales Matrix. ##
+# setAllowSimplifyClip             # Experimental. ##
+# setDrawFilter                    # Legacy; to be deprecated. ##
+# setMatrix                        # Sets Matrix. ##
+# skew()                           # Skews Matrix. #
+# translate()                      # Translates Matrix. ##
+# writePixels                      # Copies and converts rectangle of pixels to Canvas. ##
+#Table ##
+#Subtopic ##
+
+#Topic Overview ##
+
+# ------------------------------------------------------------------------------
+
+#Method static std::unique_ptr<SkCanvas> MakeRasterDirect(const SkImageInfo& info,
+        void* pixels, size_t rowBytes)
+
+Allocates raster canvas that will draw directly into pixels.
+To access pixels after drawing, call flush() or peekPixels.
+
+#Param info  Width, height, Image_Color_Type, Image_Alpha_Type, Color_Space, of Raster_Surface.
+             Width, or height, or both, may be zero.
+##
+#Param pixels  Pointer to destination pixels buffer. Buffer size should be info height
+               times rowBytes times bytes required for Image_Color_Type.
+##
+#Param rowBytes  The interval from one Surface row to the next; equal to or greater than
+                 info width times bytes required for Image_Color_Type.
+##
+
+#Return  Canvas if all parameters are valid; otherwise, nullptr.
+         Valid parameters include: info dimensions must be zero or positive, and other checks;
+         info must contain Image_Color_Type and Image_Alpha_Type supported by Raster_Surface;
+         pixels must be not be nullptr;
+         rowBytes must be zero or large enough to contain width pixels of Image_Color_Type.
+##
+
+#Example
+    #Description
+        Allocates a three by three bitmap, clears it to white, and draws a black pixel
+        in the center.
+    ##
+void draw(SkCanvas* ) {

+    SkImageInfo info = SkImageInfo::MakeN32Premul(3, 3);  // device aligned, 32 bpp, premultipled

+    const size_t minRowBytes = info.minRowBytes();  // bytes used by one bitmap row

+    const size_t size = info.getSafeSize(minRowBytes);  // bytes used by all rows

+    SkAutoTMalloc<SkPMColor> storage(size);  // allocate storage for pixels

+    SkPMColor* pixels = storage.get();  // get pointer to allocated storage

+    // create a SkCanvas backed by a raster device, and delete it when the

+    // function goes out of scope.

+    std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(info, pixels, minRowBytes);

+    canvas->clear(SK_ColorWHITE);  // white is unpremultiplied, in ARGB order

+    canvas->flush();  // ensure that pixels are cleared

+    SkPMColor pmWhite = pixels[0];  // the premultiplied format may vary

+    SkPaint paint;  // by default, draws black

+    canvas->drawPoint(1, 1, paint);  // draw in the center

+    canvas->flush();  // ensure that point was drawn

+    for (int y = 0; y < info.height(); ++y) {

+        for (int x = 0; x < info.width(); ++x) {

+            SkDebugf("%c", *pixels++ == pmWhite ? '-' : 'x');

+        }

+        SkDebugf("\n");

+    }

+}
+    #StdOut
+        ---
+        -x-
+        ---
+    ##
+##
+
+#ToDo incomplete ##
+
+#SeeAlso MakeRasterDirectN32 SkSurface::MakeRasterDirect
+##
+
+# ------------------------------------------------------------------------------
+
+#Method static std::unique_ptr<SkCanvas> MakeRasterDirectN32(int width, int height, SkPMColor* pixels,
+                                                         size_t rowBytes) 
+
+Creates Canvas with Raster_Surface with inline image specification that draws into pixels.
+Image_Color_Type is set to kN32_SkColorType.
+Image_Alpha_Type is set to kPremul_SkAlphaType.
+To access pixels after drawing, call flush() or peekPixels.
+
+#Param width  Pixel column count on Raster_Surface created. Must be zero or greater. ##
+#Param height  Pixel row count on Raster_Surface created. Must be zero or greater. ##
+#Param pixels  Pointer to destination pixels buffer. Buffer size should be height
+               times rowBytes times four.
+##
+#Param rowBytes  The interval from one Surface row to the next; equal to or greater than
+                 width times four.
+##
+
+#Return  Canvas if all parameters are valid; otherwise, nullptr.
+         Valid parameters include: width and height must be zero or positive;
+         pixels must be not be nullptr;
+         rowBytes must be zero or large enough to contain width pixels of Image_Color_Type.
+##
+
+#Example
+    #Description
+        Allocates a three by three bitmap, clears it to white, and draws a black pixel
+        in the center.
+    ##
+void draw(SkCanvas* ) {
+    const int width = 3;
+    const int height = 3;
+    SkPMColor pixels[height][width];  // allocate a 3x3 premultiplied bitmap on the stack
+    // create a SkCanvas backed by a raster device, and delete it when the
+    // function goes out of scope.
+    std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirectN32(
+            width,
+            height,
+            pixels[0],  // top left of the bitmap
+            sizeof(pixels[0]));  // byte width of the each row
+    // write a pre-multiplied value for white into all pixels in the bitmap
+    canvas->clear(SK_ColorWHITE);
+    SkPMColor pmWhite = pixels[0][0];  // the premultiplied format may vary
+    SkPaint paint;  // by default, draws black
+    canvas->drawPoint(1, 1, paint);  // draw in the center
+    canvas->flush();  // ensure that pixels is ready to be read
+    for (int y = 0; y < height; ++y) {
+        for (int x = 0; x < width; ++x) {
+            SkDebugf("%c", pixels[y][x] == pmWhite ? '-' : 'x');
+        }
+        SkDebugf("\n");
+    }
+}
+    #StdOut
+        ---
+        -x-
+        ---
+    ##
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method SkCanvas()
+
+Creates an empty canvas with no backing device/pixels, and zero
+dimensions.
+
+#Return  An empty canvas. ##
+
+#Example
+
+#Description
+Passes a placeholder to a function that requires one.
+##
+
+#Function
+// Returns true if either the canvas rotates the text by 90 degrees, or the paint does.

+static void check_for_up_and_down_text(const SkCanvas* canvas, const SkPaint& paint) {

+    bool paintHasVertical = paint.isVerticalText();

+    const SkMatrix& matrix = canvas->getTotalMatrix();

+    bool matrixIsVertical = matrix.preservesRightAngles() && !matrix.isScaleTranslate();

+    SkDebugf("paint draws text %s\n", paintHasVertical != matrixIsVertical ?

+            "top to bottom" : "left to right");

+}

+

+static void check_for_up_and_down_text(const SkPaint& paint) {

+    SkCanvas canvas;  // placeholder only, does not have an associated device

+    check_for_up_and_down_text(&canvas, paint);

+}

+

+##
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    check_for_up_and_down_text(paint);  // paint draws text left to right

+    paint.setVerticalText(true);

+    check_for_up_and_down_text(paint);  // paint draws text top to bottom

+    paint.setVerticalText(false);

+    canvas->rotate(90);

+    check_for_up_and_down_text(canvas, paint);  // paint draws text top to bottom

+}

+
+    #StdOut
+        paint draws text left to right
+        paint draws text top to bottom
+        paint draws text top to bottom
+    ##
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method SkCanvas(int width, int height, const SkSurfaceProps* props = NULL)
+
+Creates Canvas of the specified dimensions without a Surface.
+Used by subclasses with custom implementations for draw methods.
+
+#Param width  Zero or greater. ##
+#Param height Zero or greater. ##
+#Param props  The LCD striping orientation and setting for device independent fonts.
+                     If nullptr, use Legacy_Font_Host settings. ##
+
+#Return       Canvas placeholder with dimensions. ##
+
+#Example
+    SkCanvas canvas(10, 20);  // 10 units wide, 20 units high
+    canvas.clipRect(SkRect::MakeXYWH(30, 40, 5, 10));  // clip is outside canvas' device
+    SkDebugf("canvas %s empty\n", canvas.getDeviceClipBounds().isEmpty() ? "is" : "is not");
+
+    #StdOut
+        canvas is empty
+    ##
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method explicit SkCanvas(SkBaseDevice* device)
+
+Construct a canvas that draws into device.
+Used by child classes of SkCanvas.
+
+#ToDo  Since SkBaseDevice is private, shouldn't this be private also? ##
+
+#Param device   Specifies a device for the canvas to draw into. ##
+
+#Return         Canvas that can be used to draw into device. ##
+
+#Example
+#Error "Unsure how to create a meaningful example."
+ SkPDFCanvas::SkPDFCanvas(const sk_sp<SkPDFDevice>& dev)
+    : SkCanvas(dev.get()) {}
+##
+
+#ToDo either remove doc of figure out a way to fiddle it ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method explicit SkCanvas(const SkBitmap& bitmap)
+
+Construct a canvas that draws into bitmap.
+Sets SkSurfaceProps::kLegacyFontHost_InitType in constructed Surface. 
+
+#ToDo Should be deprecated? ##
+
+#Param bitmap   Width, height, Image_Color_Type, Image_Alpha_Type, and pixel storage of Raster_Surface. 
+                Bitmap is copied so that subsequently editing bitmap will not affect
+                constructed Canvas.
+##
+
+#Return         Canvas that can be used to draw into bitmap. ##
+
+#Example
+#Description
+The actual output depends on the installed fonts.
+##
+    SkBitmap bitmap;
+    // create a bitmap 5 wide and 11 high
+    bitmap.allocPixels(SkImageInfo::MakeN32Premul(5, 11));
+    SkCanvas canvas(bitmap);
+    canvas.clear(SK_ColorWHITE);  // white is unpremultiplied, in ARGB order
+    SkPixmap pixmap;  // provides guaranteed access to the drawn pixels
+    if (!canvas.peekPixels(&pixmap)) {
+        SkDebugf("peekPixels should never fail.\n");
+    }
+    const SkPMColor* pixels = pixmap.addr32();  // points to top left of bitmap
+    SkPMColor pmWhite = pixels[0];  // the premultiplied format may vary
+    SkPaint paint;  // by default, draws black, 12 point text
+    canvas.drawString("!", 1, 10, paint);  // 1 char at baseline (1, 10)
+    for (int y = 0; y < bitmap.height(); ++y) {
+        for (int x = 0; x < bitmap.width(); ++x) {
+            SkDebugf("%c", *pixels++ == pmWhite ? '-' : 'x');
+        }
+        SkDebugf("\n");
+    }
+
+    #StdOut
+    -----
+    --x--
+    --x--
+    --x--
+    --x--
+    --x--
+    --x--
+    -----
+    --x--
+    --x--
+    -----
+    #StdOut ##
+##
+
+#ToDo incomplete ##
+
+##
+
+#Enum ColorBehavior
+
+#ToDo exclude this during build phase
+      (use SK_BUILD_FOR_ANDROID_FRAMEWORK as exclude directive)
+##
+
+#Private
+Android framework only.
+##
+
+#Code
+    enum class ColorBehavior {
+        kLegacy,
+    };
+##
+#Const kLegacy 0
+##
+##
+
+
+# ------------------------------------------------------------------------------
+
+#Method SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
+
+Construct a canvas that draws into bitmap.
+Use props to match the device characteristics, like LCD striping.
+
+#Param bitmap   Width, height, Image_Color_Type, Image_Alpha_Type, and pixel storage of Raster_Surface. 
+                Bitmap is copied so that subsequently editing bitmap will not affect
+                constructed Canvas.
+##
+#Param props    The order and orientation of RGB striping; and whether to use
+                device independent fonts.
+##
+
+#Return         Canvas that can be used to draw into bitmap. ##
+
+#Example
+#Description
+The actual output depends on the installed fonts.
+##
+    SkBitmap bitmap;
+    // create a bitmap 5 wide and 11 high
+    bitmap.allocPixels(SkImageInfo::MakeN32Premul(5, 11));
+    SkCanvas canvas(bitmap, SkSurfaceProps(0, kUnknown_SkPixelGeometry));
+    canvas.clear(SK_ColorWHITE);  // white is unpremultiplied, in ARGB order
+    SkPixmap pixmap;  // provides guaranteed access to the drawn pixels
+    if (!canvas.peekPixels(&pixmap)) {
+        SkDebugf("peekPixels should never fail.\n");
+    }
+    const SkPMColor* pixels = pixmap.addr32();  // points to top left of bitmap
+    SkPMColor pmWhite = pixels[0];  // the premultiplied format may vary
+    SkPaint paint;  // by default, draws black, 12 point text
+    canvas.drawString("!", 1, 10, paint);  // 1 char at baseline (1, 10)
+    for (int y = 0; y < bitmap.height(); ++y) {
+        for (int x = 0; x < bitmap.width(); ++x) {
+            SkDebugf("%c", *pixels++ == pmWhite ? '-' : 'x');
+        }
+        SkDebugf("\n");
+    }
+
+    #StdOut
+    -----
+    ---x-
+    ---x-
+    ---x-
+    ---x-
+    ---x-
+    ---x-
+    -----
+    ---x-
+    ---x-
+    -----
+    #StdOut ##
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method virtual ~SkCanvas()
+
+Draws State_Stack_Layer, if any.
+Free up resources used by Canvas.
+
+#Example
+#Error "Haven't thought of a useful example to put here."
+##
+
+#ToDo create example to show how draw happens when canvas goes out of scope ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method SkMetaData& getMetaData()
+
+Associates additional data with the canvas.
+The storage is freed when Canvas is deleted.
+
+#Return  storage that can be read from and written to. ##
+
+#Example
+    const char* kHelloMetaData = "HelloMetaData";
+    SkCanvas canvas;
+    SkMetaData& metaData = canvas.getMetaData();
+    SkDebugf("before: %s\n", metaData.findString(kHelloMetaData));
+    metaData.setString(kHelloMetaData, "Hello!");
+    SkDebugf("during: %s\n", metaData.findString(kHelloMetaData));
+    metaData.removeString(kHelloMetaData);
+    SkDebugf("after: %s\n", metaData.findString(kHelloMetaData));
+
+    #StdOut
+        before: (null)
+        during: Hello!
+        after: (null)
+    #StdOut ##
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method SkImageInfo imageInfo() const
+
+Returns Image_Info for Canvas. If Canvas is not associated with Raster_Surface or
+GPU_Surface, returns SkImageInfo::SkImageInfo() is returned Image_Color_Type is set to kUnknown_SkColorType.
+
+#Return  dimensions and Image_Color_Type of Canvas. ##
+
+#Example
+    SkCanvas canvas;
+    SkImageInfo canvasInfo = canvas.imageInfo();
+    SkImageInfo emptyInfo;
+    SkDebugf("emptyInfo %c= canvasInfo\n", emptyInfo == canvasInfo ? '=' : '!');
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method bool getProps(SkSurfaceProps* props) const
+
+If Canvas is associated with Raster_Surface or
+GPU_Surface, copies Surface_Properties and returns true. Otherwise,
+return false and leave props unchanged.
+
+#Param props  Pointer to writable SkSurfaceProps. ##
+
+#Return  true if Surface_Properties was copied. ##
+
+#ToDo This seems old style. Deprecate? ##
+
+#Example
+    SkBitmap bitmap;
+    SkCanvas canvas(bitmap, SkSurfaceProps(0, kRGB_V_SkPixelGeometry));
+    SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
+    SkDebugf("isRGB:%d\n", SkPixelGeometryIsRGB(surfaceProps.pixelGeometry()));
+    if (!canvas.getProps(&surfaceProps)) {
+        SkDebugf("getProps failed unexpectedly.\n");
+    }
+    SkDebugf("isRGB:%d\n", SkPixelGeometryIsRGB(surfaceProps.pixelGeometry()));
+
+    #StdOut
+        isRGB:0
+        isRGB:1
+    #StdOut ##
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void flush()
+
+Triggers the immediate execution of all pending draw operations. 
+If Canvas is associated with GPU_Surface, resolve all pending GPU operations.
+
+#Example
+#Error "haven't thought of a useful example to put here"
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method virtual SkISize getBaseLayerSize() const
+
+Gets the size of the base or root layer in global canvas coordinates. The
+origin of the base layer is always (0,0). The current drawable area may be
+smaller (due to clipping or saveLayer).
+
+#Return  Integral width and height of base layer. ##
+
+#Example
+    SkBitmap bitmap;
+    bitmap.allocPixels(SkImageInfo::MakeN32Premul(20, 30));
+    SkCanvas canvas(bitmap, SkSurfaceProps(0, kUnknown_SkPixelGeometry));
+    canvas.clipRect(SkRect::MakeWH(10, 40));
+    SkIRect clipDeviceBounds = canvas.getDeviceClipBounds();
+    if (clipDeviceBounds.isEmpty()) {
+        SkDebugf("Empty clip bounds is unexpected!\n");
+    }
+    SkDebugf("clip=%d,%d\n", clipDeviceBounds.width(), clipDeviceBounds.height());
+    SkISize baseLayerSize = canvas.getBaseLayerSize();
+    SkDebugf("size=%d,%d\n", baseLayerSize.width(), baseLayerSize.height());
+
+    #StdOut
+        clip=10,30
+        size=20,30
+    ##
+##
+
+#ToDo is this the same as the width and height of surface? ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method sk_sp<SkSurface> makeSurface(const SkImageInfo& info, const SkSurfaceProps* props = nullptr)
+
+Creates Surface matching info and props, and associates it with Canvas.
+If Canvas is already associated with Surface, it cannot create a new Surface.
+
+#Param info  Initialize Surface with width, height, Image_Color_Type, Image_Alpha_Type, and Color_Space. ##
+#Param props  Use to match if provided, or use the Surface_Properties in Canvas otherwise. ##
+
+#Return  Surface matching info and props, or nullptr if no match is available. ##
+
+#Example
+    sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(5, 6);
+    SkCanvas* smallCanvas = surface->getCanvas();
+    SkImageInfo imageInfo = SkImageInfo::MakeN32Premul(3, 4);
+    sk_sp<SkSurface> compatible = smallCanvas->makeSurface(imageInfo);
+    SkDebugf("compatible %c= nullptr\n", compatible == nullptr ? '=' : '!');
+    SkDebugf("size = %d, %d\n", compatible->width(), compatible->height());
+
+    #StdOut
+        compatible != nullptr

+        size = 3, 4
+    ##
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method virtual GrContext* getGrContext()
+
+Returns GPU_Context of the GPU_Surface associated with Canvas.
+
+#Return GPU_Context, if available; nullptr otherwise. ##
+
+#Example
+void draw(SkCanvas* canvas) {
+    if (canvas->getGrContext()) {
+         canvas->clear(SK_ColorRED);
+    } else {
+         canvas->clear(SK_ColorBLUE);
+    }
+}
+##
+
+#ToDo fiddle should show both CPU and GPU out ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void* accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin = NULL)
+
+Returns the pixel base address, Image_Info, rowBytes, and origin if the pixels
+can be read directly.
+The returned address is only valid
+while Canvas is in scope and unchanged. Any Canvas call or Surface call
+may invalidate the returned address and other returned values.
+
+If pixels are inaccessible, info, rowBytes, and origin are unchanged.
+
+#Param info  If not nullptr, copies writable pixels' Image_Info. ##
+#Param rowBytes  If not nullptr, copies writable pixels' row bytes. ##
+#Param origin  If not nullptr, copies Canvas top layer origin, its top left corner. ##
+
+#Return  Address of pixels, or nullptr if inaccessible. ##
+
+#Example
+void draw(SkCanvas* canvas) {
+    if (canvas->accessTopLayerPixels(nullptr, nullptr)) {
+         canvas->clear(SK_ColorRED);
+    } else {
+         canvas->clear(SK_ColorBLUE);
+    }
+}
+##
+
+#Example
+#Description
+Draws "ABC" on the device. Then draws "DEF" in an offscreen layer, and reads the
+offscreen to add a large dotted "DEF". Finally blends the offscreen with the
+device. 
+
+The offscreen and blended result appear on the CPU and GPU but the large dotted
+"DEF" appear only on the CPU.
+##
+void draw(SkCanvas* canvas) {
+  SkPaint paint;

+  paint.setTextSize(100);

+  canvas->drawString("ABC", 20, 160, paint);

+  SkRect layerBounds = SkRect::MakeXYWH(32, 32, 192, 192);

+  canvas->saveLayerAlpha(&layerBounds, 128);

+  canvas->clear(SK_ColorWHITE);

+  canvas->drawString("DEF", 20, 160, paint);

+  SkImageInfo imageInfo;

+  size_t rowBytes;

+  SkIPoint origin;

+  uint32_t* access = (uint32_t*) canvas->accessTopLayerPixels(&imageInfo, &rowBytes, &origin);

+  if (access) {

+    int h = imageInfo.height();

+    int v = imageInfo.width();

+    int rowWords = rowBytes / sizeof(uint32_t);

+    for (int y = 0; y < h; ++y) {

+        int newY = (y - h / 2) * 2 + h / 2;

+        if (newY < 0 || newY >= h) {

+            continue;

+        }

+        for (int x = 0; x < v; ++x) {

+            int newX = (x - v / 2) * 2 + v / 2;

+            if (newX < 0 || newX >= v) {

+                continue;

+            }

+            if (access[y * rowWords + x] == SK_ColorBLACK) {

+                access[newY * rowWords + newX] = SK_ColorGRAY;

+            }

+        }

+    }

+

+  }

+  canvas->restore();
+}
+##
+
+#ToDo there are no callers of this that I can find. Deprecate? ##
+#ToDo fiddle should show both CPU and GPU out ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method SkRasterHandleAllocator::Handle accessTopRasterHandle() const
+
+Returns custom context that tracks the Matrix and Clip.
+
+Use Raster_Handle_Allocator to blend Skia drawing with custom drawing, typically performed
+by the host platform's user interface. This accessor returns the custom context created
+when SkRasterHandleAllocator::MakeCanvas creates a custom canvas with raster storage for
+the drawing destination.
+
+#Return  Context of custom allocator. ##
+
+#Example
+#Description
+#ToDo ##
+##
+#Function
+    static void DeleteCallback(void*, void* context) {
+        delete (char*) context;
+    }
+
+    class CustomAllocator : public SkRasterHandleAllocator {
+    public:
+        bool allocHandle(const SkImageInfo& info, Rec* rec) override {
+            char* context = new char[4]{'s', 'k', 'i', 'a'};
+            rec->fReleaseProc = DeleteCallback;
+            rec->fReleaseCtx = context;
+            rec->fHandle = context;
+            rec->fPixels = context;
+            rec->fRowBytes = 4;
+            return true;
+        }
+
+        void updateHandle(Handle handle, const SkMatrix& ctm, const SkIRect& clip_bounds) override {
+            // apply canvas matrix and clip to custom environment
+        }
+    };
+
+##
+    void draw(SkCanvas* canvas) {
+        const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
+        std::unique_ptr<SkCanvas> c2 =
+                SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<CustomAllocator>(
+                new CustomAllocator()), info);
+        char* context = (char*) c2->accessTopRasterHandle();
+        SkDebugf("context = %.4s\n", context);
+
+    }
+    #StdOut
+        context = skia
+    ##
+    #ToDo skstd::make_unique could not be used because def is private -- note to fix in c++14? ##
+##
+
+#SeeAlso SkRasterHandleAllocator
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method bool peekPixels(SkPixmap* pixmap)
+
+Returns true if Canvas has direct access to its pixels.
+
+Pixels are readable when Device is raster. Pixels are not readable when SkCanvas is returned from
+GPU_Surface, returned by SkDocument::beginPage, returned by SkPictureRecorder::beginRecording,
+or SkCanvas is the base of a utility class like SkDumpCanvas.
+
+pixmap pixel address is only valid while Canvas is in scope and unchanged. Any Canvas or Surface call may
+invalidate the pixmap values.
+
+#Param pixmap  storage for Canvas pixel state if Canvas pixels are readable; otherwise, ignored. ##
+
+#Return  true if Canvas has direct access to pixels. ##
+
+#Example
+    SkPixmap pixmap;
+    if (canvas->peekPixels(&pixmap)) {
+        SkDebugf("width=%d height=%d\n", pixmap.bounds().width(), pixmap.bounds().height());
+    }
+    #StdOut
+        width=256 height=256
+    ##
+##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
+                    int srcX, int srcY)
+
+Copies rectangle of pixels from Canvas into dstPixels, converting their Image_Color_Type and Image_Alpha_Type.
+Pixels are readable when Device is raster. Pixels are not readable when SkCanvas is returned from
+GPU_Surface, returned by SkDocument::beginPage, returned by SkPictureRecorder::beginRecording,
+or SkCanvas is the base of a utility class like SkDumpCanvas.
+
+Pixel values are converted only if Canvas Image_Color_Type and Image_Alpha_Type does not match dstInfo.
+Only pixels within the rectangle that intersect Canvas pixels are copied.
+dstPixels outside the rectangle intersection are unchanged.
+
+#Table
+#Legend
+# source rectangle # value ##
+##
+# left # srcX ##
+# top # srcY ##
+# width # dstInfo.width() ##
+# height # dstInfo.height() ##
+##
+
+ #Table
+#Legend
+# canvas pixel bounds # value ##
+##
+# left # 0 ##
+# top # 0 ##
+# width # imageInfo().width() ##
+# height # imageInfo().height() ##
+##
+
+Does not copy, and returns false if:
+
+#List
+# Source rectangle and canvas pixel bounds do not intersect. ##
+# Canvas pixels could not be converted to dstInfo Image_Color_Type or dstInfo Image_Alpha_Type. ##
+# Canvas pixels are not readable; for instance, Canvas is not raster, or is document-based. ##
+# dstRowBytes is too small to contain one row of pixels. ##
+##
+
+#Param dstInfo  Dimensions, Image_Color_Type, and Image_Alpha_Type of dstPixels. ##
+#Param dstPixels  Storage for pixels, of size dstInfo.height() times dstRowBytes. ##
+#Param dstRowBytes  Size of one destination row, dstInfo.width() times pixel size. ##
+#Param srcX  Offset into readable pixels in x. ##
+#Param srcY  Offset into readable pixels in y. ##
+
+#Return  true if pixels were copied. ##
+
+#Example
+#Description
+    Canvas returned by Raster_Surface has premultiplied pixel values.
+    clear() takes unpremultiplied input with Color_Alpha equal 0x80
+    and Color_RGB equal 0x55, 0xAA, 0xFF. Color_RGB is multipled by Color_Alpha
+    to generate premultipled value 0x802B5580. readPixels converts pixel back
+    to unpremultipled value 0x8056A9FF, introducing error.
+##
+    canvas->clear(0x8055aaff);
+    for (SkAlphaType alphaType : { kPremul_SkAlphaType, kUnpremul_SkAlphaType } ) {
+        uint32_t pixel = 0;
+        SkImageInfo info = SkImageInfo::Make(1, 1, kBGRA_8888_SkColorType, alphaType);
+        if (canvas->readPixels(info, &pixel, 4, 0, 0)) {
+            SkDebugf("pixel = %08x\n", pixel);
+        }
+    }
+
+    #StdOut
+        pixel = 802b5580
+        pixel = 8056a9ff
+    ##
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method bool readPixels(const SkPixmap& pixmap, int srcX, int srcY)
+
+Copies rectangle of pixels from Canvas into Pixmap, converting their Image_Color_Type and Image_Alpha_Type.
+Pixels are readable when Device is raster. Pixels are not readable when SkCanvas is returned from
+GPU_Surface, returned by SkDocument::beginPage, returned by SkPictureRecorder::beginRecording,
+or SkCanvas is the base of a utility class like SkDumpCanvas.
+
+Pixel values are converted only if Canvas Image_Color_Type and Image_Alpha_Type does not match bitmap Image_Info.
+Only Pixmap pixels within the rectangle that intersect Canvas pixels are copied.
+Pixmap pixels outside the rectangle intersection are unchanged.
+
+#Table
+#Legend
+# source rectangle # value ##
+##
+# left # srcX ##
+# top # srcY ##
+# width # bitmap.width() ##
+# height # bitmap.height() ##
+##
+
+ #Table
+#Legend
+# canvas pixel bounds # value ##
+##
+# left # 0 ##
+# top # 0 ##
+# width # imageInfo().width() ##
+# height # imageInfo().height() ##
+##
+
+Does not copy, and returns false if:
+
+#List
+# Source rectangle and canvas pixel bounds do not intersect. ##
+# Canvas pixels could not be converted to bitmap Image_Color_Type or bitmap Image_Alpha_Type. ##
+# Canvas pixels are not readable; for instance, Canvas is not raster, or is document-based. ##
+# bitmap pixels could not be allocated. ##
+# Bitmap_Row_Bytes is too small to contain one row of pixels. ##
+##
+
+#Param pixmap  Receives pixels copied from Canvas. ##
+#Param srcX  Offset into readable pixels in x. ##
+#Param srcY  Offset into readable pixels in y. ##
+
+#Return  true if pixels were copied. ##
+
+#Example
+void draw(SkCanvas* canvas) {
+    canvas->clear(0x8055aaff);
+    uint32_t pixels[1] = { 0 };
+    SkPixmap pixmap(SkImageInfo::MakeN32Premul(1, 1), pixels, 4);

+    canvas->readPixels(pixmap, 0, 0);
+    SkDebugf("pixel = %08x\n", pixels[0]);
+}
+    #StdOut
+        pixel = 802b5580
+    ##
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method bool readPixels(const SkBitmap& bitmap, int srcX, int srcY)
+
+Copies pixels enclosed by bitmap offset to (x, y) from Canvas into bitmap, converting their Image_Color_Type and Image_Alpha_Type.
+Pixels are readable when Device is raster. Pixels are not readable when SkCanvas is returned from
+GPU_Surface, returned by SkDocument::beginPage, returned by SkPictureRecorder::beginRecording,
+or SkCanvas is the base of a utility class like SkDumpCanvas.
+Allocates pixel storage in bitmap if needed.
+
+Pixel values are converted only if Canvas Image_Color_Type and Image_Alpha_Type does not match bitmap Image_Info.
+Only pixels within the rectangle that intersect Canvas pixels are copied.
+Bitamp pixels outside the rectangle intersection are unchanged.
+
+ #Table
+#Legend
+# canvas pixel bounds # value ##
+##
+# left # 0 ##
+# top # 0 ##
+# width # imageInfo().width() ##
+# height # imageInfo().height() ##
+##
+
+Does not copy, and returns false if:
+
+#List
+# Bounds formed by (x, y) and bitmap (width, height) and canvas pixel bounds do not intersect. ##
+# Canvas pixels could not be converted to bitmap Image_Color_Type or bitmap Image_Alpha_Type. ##
+# Canvas pixels are not readable; for instance, Canvas is not raster, or is document-based. ##
+# bitmap pixels could not be allocated. ##
+# Bitmap_Row_Bytes is too small to contain one row of pixels. ##
+##
+
+#Param bitmap  Receives pixels copied from Canvas. ##
+#Param srcX  Offset into readable pixels in x. ##
+#Param srcY  Offset into readable pixels in y. ##
+
+#Return  true if pixels were copied. ##
+
+#Example
+void draw(SkCanvas* canvas) {
+    canvas->clear(0x8055aaff);
+    SkBitmap bitmap;
+    bitmap.allocPixels(SkImageInfo::MakeN32Premul(1, 1));
+    canvas->readPixels(bitmap, 0, 0);
+    SkDebugf("pixel = %08x\n", bitmap.getAddr32(0, 0)[0]);
+}
+    #StdOut
+        pixel = 802b5580
+    ##
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method bool writePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes, int x, int y)
+
+Copies to Canvas pixels, ignoring the Matrix and Clip, converting to match
+info Image_Color_Type and info Image_Alpha_Type.
+
+Pixel values are converted only if Canvas Image_Color_Type and Image_Alpha_Type does not match info.
+Only pixels within the source rectangle that intersect Canvas pixel bounds are copied.
+Canvas pixels outside the rectangle intersection are unchanged.
+
+#Table
+#Legend
+# source rectangle # value ##
+##
+# left # x ##
+# top # y ##
+# width # info.width() ##
+# height # info.height() ##
+##
+
+ #Table
+#Legend
+# canvas pixel bounds # value ##
+##
+# left # 0 ##
+# top # 0 ##
+# width # imageInfo().width() ##
+# height # imageInfo().height() ##
+##
+
+Does not copy, and returns false if:
+
+#List
+# Source rectangle and canvas pixel bounds do not intersect. ##
+# pixels could not be converted to Canvas Image_Color_Type or Canvas Image_Alpha_Type. ##
+# Canvas pixels are not writable; for instance, Canvas is document-based. ##
+# rowBytes is too small to contain one row of pixels. ##
+##
+
+#Param info    Dimensions, Image_Color_Type, and Image_Alpha_Type of pixels. ##
+#Param pixels  Pixels to copy, of size info.height() times rowBytes. ##
+#Param rowBytes  Offset from one row to the next, usually info.width() times pixel size. ##
+#Param x  Offset into Canvas writable pixels in x. ##
+#Param y  Offset into Canvas writable pixels in y. ##
+
+#Return  true if pixels were written to Canvas. ##
+
+#Example
+    SkImageInfo imageInfo = SkImageInfo::MakeN32(256, 1, kPremul_SkAlphaType);
+    for (int y = 0; y < 256; ++y) {
+        uint32_t pixels[256];
+        for (int x = 0; x < 256; ++x) {
+            pixels[x] = SkColorSetARGB(x, x + y, x, x - y);
+        }
+        canvas->writePixels(imageInfo, &pixels, sizeof(pixels), 0, y);
+    }
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method bool writePixels(const SkBitmap& bitmap, int x, int y)
+
+Writes to Canvas pixels, ignoring the Matrix and Clip, converting to match
+bitmap Image_Color_Type and bitmap Image_Alpha_Type.
+
+Pixel values are converted only if Canvas Image_Color_Type and Image_Alpha_Type does not match bitmap.
+Only pixels within the source rectangle that intersect Canvas pixel bounds are copied.
+Canvas pixels outside the rectangle intersection are unchanged.
+
+#Table
+#Legend
+# source rectangle # value ##
+##
+# left # x ##
+# top # y ##
+# width # bitmap.width() ##
+# height # bitmap.height() ##
+##
+
+ #Table
+#Legend
+# canvas pixel bounds # value ##
+##
+# left # 0 ##
+# top # 0 ##
+# width # imageInfo().width() ##
+# height # imageInfo().height() ##
+##
+
+Does not copy, and returns false if:
+
+#List
+# Source rectangle and Canvas pixel bounds do not intersect. ##
+# bitmap does not have allocated pixels. ##
+# bitmap pixels could not be converted to Canvas Image_Color_Type or Canvas Image_Alpha_Type. ##
+# Canvas pixels are not writable; for instance, Canvas is document-based. ##
+# bitmap pixels are inaccessible; for instance, bitmap wraps a texture. ##
+##
+
+#Param bitmap  Provides pixels copied to Canvas. ##
+#Param x  Offset into Canvas writable pixels in x. ##
+#Param y  Offset into Canvas writable pixels in y. ##
+
+#Return  true if pixels were written to Canvas. ##
+
+#Example
+void draw(SkCanvas* canvas) {
+    SkImageInfo imageInfo = SkImageInfo::MakeN32Premul(2, 2);
+    SkBitmap bitmap;
+    bitmap.setInfo(imageInfo);
+    uint32_t pixels[4];
+    bitmap.setPixels(pixels);
+    for (int y = 0; y < 256; y += 2) {
+        for (int x = 0; x < 256;  x += 2) {
+            pixels[0] = SkColorSetRGB(x, y, x | y);
+            pixels[1] = SkColorSetRGB(x ^ y, y, x);
+            pixels[2] = SkColorSetRGB(x, x & y, y);
+            pixels[3] = SkColorSetRGB(~x, ~y, x);
+            canvas->writePixels(bitmap, x, y);
+        }
+    }
+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+#Topic State_Stack
+
+Canvas maintains a stack of state that allows hierarchical drawing, commonly used
+to implement windows and views. The initial state has an identity matrix and and an infinite clip.
+Even with a wide-open clip, drawing is constrained by the bounds of the
+Canvas Surface or Device.
+
+Canvas savable state consists of Clip, Matrix, and Draw_Filter.
+Clip describes the area that may be drawn to.
+Matrix transforms the geometry.
+Draw_Filter (deprecated on most platforms) modifies the paint before drawing.
+
+save(), saveLayer, saveLayerPreserveLCDTextRequests, and saveLayerAlpha
+save state and return the depth of the stack.
+
+restore() and restoreToCount revert state to its value when saved.
+
+Each state on the stack intersects Clip with the previous Clip,
+and concatenates Matrix with the previous Matrix.
+The intersected Clip makes the drawing area the same or smaller;
+the concatenated Matrix may move the origin and potentially scale or rotate
+the coordinate space.
+
+Canvas does not require balancing the state stack but it is a good idea
+to do so. Calling save() without restore() will eventually cause Skia to fail;
+mismatched save() and restore() create hard to find bugs.
+
+It is not possible to use state to draw outside of the clip defined by the
+previous state.
+
+#Example
+#Description
+Draw to ever smaller clips; then restore drawing to full canvas.
+Note that the second clipRect is not permitted to enlarge Clip.
+##
+#Height 160
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    canvas->save();                             // records stack depth to restore  

+    canvas->clipRect(SkRect::MakeWH(100, 100)); // constrains drawing to clip

+    canvas->clear(SK_ColorRED);                 // draws to limit of clip

+    canvas->save();                             // records stack depth to restore 

+    canvas->clipRect(SkRect::MakeWH(50, 150));  // Rect below 100 is ignored

+    canvas->clear(SK_ColorBLUE);                // draws to smaller clip

+    canvas->restore();                          // enlarges clip

+    canvas->drawLine(20, 20, 150, 150, paint);  // line below 100 is not drawn

+    canvas->restore();                          // enlarges clip

+    canvas->drawLine(150, 20, 50, 120, paint);  // line below 100 is drawn

+}
+## 
+
+Each Clip uses the current Matrix for its coordinates.
+
+#Example
+#Description
+While clipRect is given the same rectangle twice, Matrix makes the second
+clipRect draw at half the size of the first.
+##
+#Height 128
+void draw(SkCanvas* canvas) {

+    canvas->clipRect(SkRect::MakeWH(100, 100));

+    canvas->clear(SK_ColorRED);

+    canvas->scale(.5, .5);

+    canvas->clipRect(SkRect::MakeWH(100, 100));

+    canvas->clear(SK_ColorBLUE);

+}
+##
+
+#SeeAlso save() saveLayer saveLayerPreserveLCDTextRequests saveLayerAlpha restore() restoreToCount
+
+#Method int save()
+
+Saves Matrix, Clip, and Draw_Filter (Draw_Filter deprecated on most platforms).
+Calling restore() discards changes to Matrix, Clip, and Draw_Filter,
+restoring the Matrix, Clip, and Draw_Filter to their state when save() was called.
+
+Matrix may be changed by translate(), scale(), rotate(), skew(), concat(), setMatrix, and resetMatrix.
+Clip may be changed by clipRect, clipRRect, clipPath, clipRegion.
+
+Saved Canvas state is put on a stack; multiple calls to save() should be balance by an equal number of
+calls to restore().
+
+Call restoreToCount with result to restore this and subsequent saves.
+
+#Return Depth of saved stack. ##
+
+#Example
+#Description 
+The black square is translated 50 pixels down and to the right.
+Restoring Canvas state removes translate() from Canvas stack;
+the red square is not translated, and is drawn at the origin.
+##
+#Height 100
+void draw(SkCanvas* canvas) {
+    SkPaint paint;
+    SkRect rect = { 0, 0, 25, 25 };
+    canvas->drawRect(rect, paint);
+    canvas->save();
+    canvas->translate(50, 50);
+    canvas->drawRect(rect, paint);
+    canvas->restore();
+    paint.setColor(SK_ColorRED);
+    canvas->drawRect(rect, paint);
+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+#Subtopic Layer
+
+Layer allocates a temporary offscreen Bitmap to draw into. When the drawing is complete,
+the Bitmap is drawn into the Canvas. 
+
+Layer is saved in a stack along with other saved state. When state with a Layer
+is restored, the offscreen Bitmap is drawn into the previous layer.
+
+Layer may be initialized with the contents of the previous layer. When Layer is
+restored, its Bitmap can be modified by Paint passed to Layer to apply Color_Alpha,
+Color_Filter, Image_Filter, and Blend_Mode.
+
+#Method int saveLayer(const SkRect* bounds, const SkPaint* paint)
+
+Saves Matrix, Clip, and Draw_Filter (Draw_Filter deprecated on most platforms),
+and allocates an offscreen Bitmap for subsequent drawing.
+Calling restore() discards changes to Matrix, Clip, and Draw_Filter,
+and draws the offscreen bitmap.
+The Matrix, Clip, and Draw_Filter are restored to their state when save() was called. 
+
+Matrix may be changed by translate(), scale(), rotate(), skew(), concat(), setMatrix, and resetMatrix.
+Clip may be changed by clipRect, clipRRect, clipPath, clipRegion.
+
+Rect bounds suggests but does not define the offscreen size. To clip drawing to a specific rectangle,
+use clipRect.
+
+Optional Paint paint applies Color_Alpha, Color_Filter, Image_Filter, and Blend_Mode when restore() is called.
+
+Call restoreToCount with result to restore this and subsequent saves.
+
+#Param bounds  Used as a hint to limit the size of the offscreen; may be nullptr. ##
+#Param paint  Used when restore() is called to draw the offscreen; may be nullptr. ##
+
+#Return  Depth of saved stack. ##
+
+#Example
+#Description
+Rectangles are blurred by Image_Filter when restore() draws offscreen to main Canvas.
+##
+#Height 128
+void draw(SkCanvas* canvas) {
+    SkPaint paint, blur;
+    blur.setImageFilter(SkImageFilter::MakeBlur(3, 3, nullptr));
+    canvas->saveLayer(nullptr, &blur);
+    SkRect rect = { 25, 25, 50, 50};
+    canvas->drawRect(rect, paint);
+    canvas->translate(50, 50);
+    paint.setColor(SK_ColorRED);
+    canvas->drawRect(rect, paint);
+    canvas->restore();
+}
+##
+
+#ToDo incomplete ##
+
+##
+
+#Method int saveLayer(const SkRect& bounds, const SkPaint* paint) 
+
+Saves Matrix, Clip, and Draw_Filter (Draw_Filter deprecated on most platforms),
+and allocates an offscreen Bitmap for subsequent drawing.
+Calling restore() discards changes to Matrix, Clip, and Draw_Filter,
+and draws the offscreen Bitmap.
+The Matrix, Clip, and Draw_Filter are restored to their state when save() was called. 
+
+Matrix may be changed by translate(), scale(), rotate(), skew(), concat(), setMatrix, and resetMatrix.
+Clip may be changed by clipRect, clipRRect, clipPath, clipRegion.
+
+bounds suggests but does not define the offscreen size. To clip drawing to a specific rectangle,
+use clipRect.
+
+Optional Paint paint applies Color_Alpha, Color_Filter, Image_Filter, and Blend_Mode when restore() is called.
+
+Call restoreToCount with result to restore this and subsequent saves.
+
+#Param bounds  Used as a hint to limit the size of the offscreen; may be nullptr. ##
+#Param paint  Used when restore() is called to draw the offscreen; may be nullptr. ##
+
+#Return  Depth of saved stack. ##
+
+#Example
+#Description
+Rectangles are blurred by Image_Filter when restore() draws offscreen to main Canvas.
+The red rectangle is clipped; it does not fully fit on the offscreen Canvas. 
+Image_Filter blurs past edge of offscreen so red rectangle is blurred on all sides.
+##
+#Height 128
+void draw(SkCanvas* canvas) {
+    SkPaint paint, blur;
+    blur.setImageFilter(SkImageFilter::MakeBlur(3, 3, nullptr));
+    canvas->saveLayer(SkRect::MakeWH(90, 90), &blur);
+    SkRect rect = { 25, 25, 50, 50};
+    canvas->drawRect(rect, paint);
+    canvas->translate(50, 50);
+    paint.setColor(SK_ColorRED);
+    canvas->drawRect(rect, paint);
+    canvas->restore();
+}
+##
+
+#ToDo incomplete ##
+
+##
+
+#Method int saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint)
+
+Saves Matrix, Clip, and Draw_Filter (Draw_Filter deprecated on most platforms),
+and allocates an offscreen bitmap for subsequent drawing.
+LCD_Text is preserved when the offscreen is drawn to the prior layer.
+
+Draw text on an opaque background so that LCD_Text blends correctly with the prior layer.
+
+Calling restore() discards changes to Matrix, Clip, and Draw_Filter,
+and draws the offscreen bitmap.
+The Matrix, Clip, and Draw_Filter are restored to their state when save() was called. 
+
+Matrix may be changed by translate(), scale(), rotate(), skew(), concat(), setMatrix, and resetMatrix.
+Clip may be changed by clipRect, clipRRect, clipPath, clipRegion.
+  
+Draw LCD_Text on an opaque background to get good results.
+
+bounds suggests but does not define the offscreen size. To clip drawing to a specific rectangle,
+use clipRect.
+
+paint modifies how the offscreen overlays the prior layer. Color_Alpha, Blend_Mode,
+Color_Filter, Draw_Looper, Image_Filter, and Mask_Filter, affect the offscreen draw.
+
+Call restoreToCount with result to restore this and subsequent saves.
+
+#Param bounds  Used as a hint to limit the size of the offscreen; may be nullptr. ##
+#Param paint  Used when restore() is called to draw the offscreen; may be nullptr. ##
+
+#Return  Depth of saved stack. ##
+
+#Example
+    SkPaint paint;

+    paint.setAntiAlias(true);

+    paint.setLCDRenderText(true);

+    paint.setTextSize(20);

+    for (auto preserve : { false, true } ) {

+        preserve ? canvas->saveLayerPreserveLCDTextRequests(nullptr, nullptr)

+                 : canvas->saveLayer(nullptr, nullptr);

+        SkPaint p;

+        p.setColor(SK_ColorWHITE);

+        // Comment out the next line to draw on a non-opaque background.

+        canvas->drawRect(SkRect::MakeLTRB(25, 40, 200, 70), p);

+        canvas->drawString("Hamburgefons", 30, 60, paint);

+

+        p.setColor(0xFFCCCCCC);

+        canvas->drawRect(SkRect::MakeLTRB(25, 70, 200, 100), p);

+        canvas->drawString("Hamburgefons", 30, 90, paint);

+

+        canvas->restore();

+        canvas->translate(0, 80);

+    }
+    ##
+
+#ToDo incomplete ##
+
+##
+
+#Method int saveLayerAlpha(const SkRect* bounds, U8CPU alpha)
+
+Saves Matrix, Clip, and Draw_Filter (Draw_Filter deprecated on most platforms),
+and allocates an offscreen bitmap for subsequent drawing.
+
+Calling restore() discards changes to Matrix, Clip, and Draw_Filter,
+and blends the offscreen bitmap with alpha opacity onto the prior layer.
+The Matrix, Clip, and Draw_Filter are restored to their state when save() was called. 
+
+Matrix may be changed by translate(), scale(), rotate(), skew(), concat(), setMatrix, and resetMatrix.
+Clip may be changed by clipRect, clipRRect, clipPath, clipRegion.
+
+bounds suggests but does not define the offscreen size. To clip drawing to a specific rectangle,
+use clipRect.
+
+Call restoreToCount with result to restore this and subsequent saves.
+
+#Param bounds  Used as a hint to limit the size of the offscreen; may be nullptr. ##
+#Param alpha  The opacity of the offscreen; zero is fully transparent, 255 is fully opaque. ##
+
+#Return  Depth of saved stack. ##
+
+#Example
+    SkPaint paint;

+    paint.setColor(SK_ColorRED);

+    canvas->drawCircle(50, 50, 50, paint);

+    canvas->saveLayerAlpha(nullptr, 128);

+    paint.setColor(SK_ColorBLUE);

+    canvas->drawCircle(100, 50, 50, paint);

+    paint.setColor(SK_ColorGREEN);

+    paint.setAlpha(128);

+    canvas->drawCircle(75, 90, 50, paint);

+    canvas->restore();
+##
+
+#ToDo incomplete ##
+
+##
+
+#Enum SaveLayerFlags
+
+#Code
+    enum {
+        kIsOpaque_SaveLayerFlag = 1 << 0,
+        kPreserveLCDText_SaveLayerFlag = 1 << 1,
+        kInitWithPrevious_SaveLayerFlag = 1 << 2,
+    };
+
+    typedef uint32_t SaveLayerFlags;
+##
+
+SaveLayerFlags provides options that may be used in any combination in SaveLayerRec,
+defining how the offscreen allocated by saveLayer operates.
+
+#Const kIsOpaque_SaveLayerFlag 1
+  Creates offscreen without transparency. Flag is ignored if layer Paint contains
+  Image_Filter or Color_Filter.
+##
+
+#Const kPreserveLCDText_SaveLayerFlag 2
+  Creates offscreen for LCD text. Flag is ignored if layer Paint contains
+  Image_Filter or Color_Filter.
+##
+
+#Const kInitWithPrevious_SaveLayerFlag 4
+  Initializes offscreen with the contents of the previous layer.
+##
+
+#Example
+#Height 160
+#Description
+Canvas layer captures red and blue circles scaled up by four.
+scalePaint blends offscreen back with transparency. 
+##
+void draw(SkCanvas* canvas) {

+    SkPaint redPaint, bluePaint, scalePaint;

+    redPaint.setColor(SK_ColorRED);

+    canvas->drawCircle(21, 21, 8, redPaint);

+    bluePaint.setColor(SK_ColorBLUE);

+    canvas->drawCircle(31, 21, 8, bluePaint);

+    SkMatrix matrix;

+    matrix.setScale(4, 4);

+    scalePaint.setAlpha(0x40);

+    scalePaint.setImageFilter(

+            SkImageFilter::MakeMatrixFilter(matrix, kNone_SkFilterQuality, nullptr));

+    SkCanvas::SaveLayerRec saveLayerRec(nullptr, &scalePaint,

+            SkCanvas::kInitWithPrevious_SaveLayerFlag); 

+    canvas->saveLayer(saveLayerRec);

+    canvas->restore();

+}
+##
+
+#ToDo incomplete ##
+
+#Enum ##
+
+#Struct SaveLayerRec
+
+#Code
+    struct SaveLayerRec {
+        SaveLayerRec*(...
+
+        const SkRect*           fBounds;
+        const SkPaint*          fPaint;
+        const SkImageFilter*    fBackdrop;
+        SaveLayerFlags          fSaveLayerFlags;
+    };
+##
+
+SaveLayerRec contains the state used to create the layer offscreen. 
+
+#Member const SkRect*           fBounds
+    fBounds is used as a hint to limit the size of the offscreen; may be nullptr.
+    fBounds suggests but does not define the offscreen size. To clip drawing to a specific rectangle,
+    use clipRect.
+##
+
+#Member const SkPaint*          fPaint
+    fPaint modifies how the offscreen overlays the prior layer; may be nullptr. Color_Alpha, Blend_Mode,
+    Color_Filter, Draw_Looper, Image_Filter, and Mask_Filter affect the offscreen draw.
+##
+
+#Member const SkImageFilter*    fBackdrop
+    fBackdrop applies Image_Filter to the prior layer when copying to the layer offscreen; may be nullptr.
+    Use kInitWithPrevious_SaveLayerFlag to copy the prior layer without a Image_Filter.
+##
+
+#Member const SkImage*          fClipMask
+#ToDo header documentation is incomplete ##
+    may be nullptr.
+##
+
+#Member const SkMatrix*         fClipMatrix
+#ToDo header documentation is incomplete ##
+   may be nullptr.
+##
+
+#Member SaveLayerFlags          fSaveLayerFlags
+    fSaveLayerFlags are used to create layer offscreen without transparency, create layer offscreen for
+    LCD text, and to create layer offscreen with the contents of the previous layer.
+##
+
+#Example
+#Height 160
+#Description
+Canvas layer captures a red anti-aliased circle and a blue aliased circle scaled up by four.
+After drawing another unscaled red circle on top, the offscreen is transferred to the main canvas. 
+##
+void draw(SkCanvas* canvas) {

+    SkPaint redPaint, bluePaint;

+    redPaint.setAntiAlias(true);

+    redPaint.setColor(SK_ColorRED);

+    canvas->drawCircle(21, 21, 8, redPaint);

+    bluePaint.setColor(SK_ColorBLUE);

+    canvas->drawCircle(31, 21, 8, bluePaint);

+    SkMatrix matrix;

+    matrix.setScale(4, 4);

+    auto scaler = SkImageFilter::MakeMatrixFilter(matrix, kNone_SkFilterQuality, nullptr);

+    SkCanvas::SaveLayerRec saveLayerRec(nullptr, nullptr, scaler.get(), 0); 

+    canvas->saveLayer(saveLayerRec);

+    canvas->drawCircle(125, 85, 8, redPaint);

+    canvas->restore();

+}
+##
+
+#Method SaveLayerRec()
+
+Sets fBounds, fPaint, and fBackdrop to nullptr. Clears fSaveLayerFlags.
+
+#Return  empty SaveLayerRec. ##
+
+#Example
+    SkCanvas::SaveLayerRec rec1;

+    rec1.fSaveLayerFlags = SkCanvas::kIsOpaque_SaveLayerFlag;

+    SkCanvas::SaveLayerRec rec2(nullptr, nullptr, SkCanvas::kIsOpaque_SaveLayerFlag);

+    SkDebugf("rec1 %c= rec2\n", rec1.fBounds == rec2.fBounds

+            && rec1.fPaint == rec2.fPaint

+            && rec1.fBackdrop == rec2.fBackdrop

+            && rec1.fSaveLayerFlags == rec2.fSaveLayerFlags ? '=' : '!');
+    #StdOut
+        rec1 == rec2
+    ##
+##
+
+##
+
+#Method SaveLayerRec(const SkRect* bounds, const SkPaint* paint, SaveLayerFlags saveLayerFlags = 0)
+
+Sets fBounds, fPaint, and fSaveLayerFlags; sets fBackdrop to nullptr.
+
+#Param bounds  Offscreen dimensions; may be nullptr. ##
+#Param paint  Applied to offscreen when overlaying prior layer; may be nullptr. ##
+#Param saveLayerFlags  SaveLayerRec options to modify offscreen. ##
+
+#Return  SaveLayerRec with empty backdrop. ##
+
+#Example
+    SkCanvas::SaveLayerRec rec1;

+    SkCanvas::SaveLayerRec rec2(nullptr, nullptr);

+    SkDebugf("rec1 %c= rec2\n", rec1.fBounds == rec2.fBounds

+            && rec1.fPaint == rec2.fPaint

+            && rec1.fBackdrop == rec2.fBackdrop

+            && rec1.fSaveLayerFlags == rec2.fSaveLayerFlags ? '=' : '!');
+    #StdOut
+        rec1 == rec2
+    ##
+##
+
+##
+
+#Method SaveLayerRec(const SkRect* bounds, const SkPaint* paint, const SkImageFilter* backdrop,
+                     SaveLayerFlags saveLayerFlags)
+
+Sets fBounds, fPaint, fBackdrop, and fSaveLayerFlags.
+
+#Param bounds  Offscreen dimensions; may be nullptr. ##
+#Param paint  Applied to offscreen when overlaying prior layer; may be nullptr. ##
+#Param backdrop  Copies prior layer to offscreen with Image_Filter; may be nullptr. ##
+#Param saveLayerFlags  SaveLayerRec options to modify offscreen. ##
+
+#Return  SaveLayerRec fully specified. ##
+
+#Example
+    SkCanvas::SaveLayerRec rec1;

+    SkCanvas::SaveLayerRec rec2(nullptr, nullptr, nullptr, 0);

+    SkDebugf("rec1 %c= rec2\n", rec1.fBounds == rec2.fBounds

+            && rec1.fPaint == rec2.fPaint

+            && rec1.fBackdrop == rec2.fBackdrop

+            && rec1.fSaveLayerFlags == rec2.fSaveLayerFlags ? '=' : '!');
+    #StdOut
+        rec1 == rec2
+    ##
+##
+
+##
+
+#Method SaveLayerRec(const SkRect* bounds, const SkPaint* paint, const SkImageFilter* backdrop,
+                     const SkImage* clipMask, const SkMatrix* clipMatrix,
+                     SaveLayerFlags saveLayerFlags)
+
+#Experimental
+Not ready for general use.
+##
+
+#Param bounds  Offscreen dimensions; may be nullptr. ##
+#Param paint  Applied to offscreen when overlaying prior layer; may be nullptr. ##
+#Param backdrop  Copies prior layer to offscreen with Image_Filter; may be nullptr. ##
+#Param clipMask  May be nullptr. ##
+#Param clipMatrix  May be nullptr. ##
+#Param saveLayerFlags  SaveLayerRec options to modify offscreen. ##
+
+#Return  SaveLayerRec fully specified. ##
+
+#ToDo  incomplete ##
+
+##
+
+#Struct ##
+
+#Method int saveLayer(const SaveLayerRec& layerRec)
+
+Saves Matrix, Clip, and Draw_Filter (Draw_Filter deprecated on most platforms),
+and allocates an offscreen bitmap for subsequent drawing.
+
+Calling restore() discards changes to Matrix, Clip, and Draw_Filter,
+and blends the offscreen bitmap with alpha opacity onto the prior layer.
+The Matrix, Clip, and Draw_Filter are restored to their state when save() was called. 
+
+Matrix may be changed by translate(), scale(), rotate(), skew(), concat(), setMatrix, and resetMatrix.
+Clip may be changed by clipRect, clipRRect, clipPath, clipRegion.
+
+SaveLayerRec contains the state used to create the layer offscreen.
+
+Call restoreToCount with result to restore this and subsequent saves.
+
+#Param layerRec  offscreen state. ##
+
+#Return          depth of save state stack. ##
+
+#Example
+#Description
+The example draws an image, and saves it into a layer with kInitWithPrevious_SaveLayerFlag.
+Next it punches a hole in the layer and restore with SkBlendMode::kPlus.
+Where the layer was cleared, the original image will draw unchanged.
+Outside of the circle the mandrill is brightened.
+##
+    #Image 3
+    // sk_sp<SkImage> image = GetResourceAsImage("mandrill_256.png");
+    canvas->drawImage(image, 0, 0, nullptr);
+    SkCanvas::SaveLayerRec rec;
+    SkPaint paint;
+    paint.setBlendMode(SkBlendMode::kPlus);
+    rec.fSaveLayerFlags = SkCanvas::kInitWithPrevious_SaveLayerFlag;
+    rec.fPaint = &paint;
+    canvas->saveLayer(rec);
+    paint.setBlendMode(SkBlendMode::kClear);
+    canvas->drawCircle(128, 128, 96, paint);
+    canvas->restore();
+##
+
+#ToDo above example needs to replace GetResourceAsImage with way to select image in fiddle ##
+
+##
+
+#Subtopic Layer ##
+
+# ------------------------------------------------------------------------------
+
+#Method void restore()
+
+Removes changes to Matrix, Clip, and Draw_Filter since Canvas state was
+last saved. The state is removed from the stack. 
+
+Does nothing if the stack is empty. 
+
+#Example
+void draw(SkCanvas* canvas) {

+    SkCanvas simple;

+    SkDebugf("depth = %d\n", simple.getSaveCount());

+    simple.restore();

+    SkDebugf("depth = %d\n", simple.getSaveCount());

+}
+##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method int getSaveCount() const
+
+Returns the number of saved states, each containing: Matrix, Clip, and Draw_Filter.
+Equals the number of save() calls less the number of restore() calls plus one. 
+The save count of a new canvas is one.
+
+#Return  depth of save state stack. ##
+
+#Example
+void draw(SkCanvas* canvas) {

+    SkCanvas simple;

+    SkDebugf("depth = %d\n", simple.getSaveCount());

+    simple.save();

+    SkDebugf("depth = %d\n", simple.getSaveCount());

+    simple.restore();

+    SkDebugf("depth = %d\n", simple.getSaveCount());

+}
+#StdOut
+depth = 1

+depth = 2

+depth = 1
+##
+##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void restoreToCount(int saveCount)
+
+Restores state to Matrix, Clip, and Draw_Filter
+values when save(), saveLayer, saveLayerPreserveLCDTextRequests, or saveLayerAlpha
+returned saveCount.
+
+Does nothing if saveCount is greater than state stack count. 
+Restores state to initial values if saveCount is less than or equal to one.
+
+#Param saveCount    The depth of state stack to restore. ##
+
+#Example
+void draw(SkCanvas* canvas) {

+    SkDebugf("depth = %d\n", canvas->getSaveCount());

+    canvas->save();

+    canvas->save();

+    SkDebugf("depth = %d\n", canvas->getSaveCount());

+    canvas->restoreToCount(0);

+    SkDebugf("depth = %d\n", canvas->getSaveCount());

+}
+#StdOut
+depth = 1

+depth = 3

+depth = 1
+##
+##
+
+##
+
+#Topic State_Stack ##
+
+# ------------------------------------------------------------------------------
+#Topic Matrix
+
+#Method void translate(SkScalar dx, SkScalar dy)
+
+Translate Matrix by dx along the x-axis and dy along the y-axis.
+
+Mathematically, replace Matrix with a translation matrix
+pre-multiplied with Matrix. 
+
+This has the effect of moving the drawing by (dx, dy) before transforming
+the result with Matrix.
+
+#Param  dx   The distance to translate in x. ##
+#Param  dy   The distance to translate in y. ##
+
+#Example
+#Height 128
+#Description
+scale() followed by translate() produces different results from translate() followed
+by scale(). 
+
+The blue stroke follows translate of (50, 50); a black
+fill follows scale of (2, 1/2.f). After restoring the clip, which resets 
+Matrix, a red frame follows the same scale of (2, 1/2.f); a gray fill
+follows translate of (50, 50).
+##
+void draw(SkCanvas* canvas) {

+    SkPaint filledPaint;

+    SkPaint outlinePaint;

+    outlinePaint.setStyle(SkPaint::kStroke_Style);

+    outlinePaint.setColor(SK_ColorBLUE);

+    canvas->save();

+    canvas->translate(50, 50);

+    canvas->drawCircle(28, 28, 15, outlinePaint);  // blue center: (50+28, 50+28)

+    canvas->scale(2, 1/2.f);

+    canvas->drawCircle(28, 28, 15, filledPaint);   // black center: (50+(28*2), 50+(28/2))

+    canvas->restore();

+    filledPaint.setColor(SK_ColorGRAY);

+    outlinePaint.setColor(SK_ColorRED);

+    canvas->scale(2, 1/2.f);

+    canvas->drawCircle(28, 28, 15, outlinePaint);  // red center: (28*2, 28/2)

+    canvas->translate(50, 50);

+    canvas->drawCircle(28, 28, 15, filledPaint);   // gray center: ((50+28)*2, (50+28)/2)

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void scale(SkScalar sx, SkScalar sy)
+
+Scale Matrix by sx on the x-axis and sy on the y-axis.
+
+Mathematically, replace Matrix with a scale matrix
+pre-multiplied with Matrix. 
+
+This has the effect of scaling the drawing by (sx, sy) before transforming
+the result with Matrix.
+
+#Param  sx   The amount to scale in x. ##
+#Param  sy   The amount to scale in y. ##
+
+#Example
+#Height 160
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    SkRect rect = { 10, 20, 60, 120 };

+    canvas->translate(20, 20);

+    canvas->drawRect(rect, paint);

+    canvas->scale(2, .5f);

+    paint.setColor(SK_ColorGRAY);

+    canvas->drawRect(rect, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void rotate(SkScalar degrees)
+
+Rotate Matrix by degrees. Positive degrees rotates clockwise.
+
+Mathematically, replace Matrix with a rotation matrix
+pre-multiplied with Matrix. 
+
+This has the effect of rotating the drawing by degrees before transforming
+the result with Matrix.
+
+#Param  degrees  The amount to rotate, in degrees. ##
+
+#Example
+#Description
+Draw clock hands at time 5:10. The hour hand and minute hand point up and
+are rotated clockwise.
+##
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    paint.setStyle(SkPaint::kStroke_Style);

+    canvas->translate(128, 128);

+    canvas->drawCircle(0, 0, 60, paint);

+    canvas->save();

+    canvas->rotate(10 * 360 / 60);   // 10 minutes of 60 scaled to 360 degrees

+    canvas->drawLine(0, 0, 0, -50, paint); 

+    canvas->restore();

+    canvas->rotate((5 + 10.f/60) * 360 / 12); // 5 and 10/60 hours of 12 scaled to 360 degrees

+    canvas->drawLine(0, 0, 0, -30, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void rotate(SkScalar degrees, SkScalar px, SkScalar py)
+
+Rotate Matrix by degrees about a point at (px, py). Positive degrees rotates clockwise.
+
+Mathematically, construct a rotation matrix. Pre-multiply the rotation matrix by
+a translation matrix, then replace Matrix with the resulting matrix
+pre-multiplied with Matrix. 
+
+This has the effect of rotating the drawing about a given point before transforming
+the result with Matrix.
+
+#Param  degrees  The amount to rotate, in degrees. ##
+#Param  px  The x coordinate of the point to rotate about. ##
+#Param  py  The y coordinate of the point to rotate about. ##
+
+#Example
+#Height 192
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    paint.setTextSize(96);

+    canvas->drawString("A1", 130, 100, paint);

+    canvas->rotate(180, 130, 100);

+    canvas->drawString("A1", 130, 100, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void skew(SkScalar sx, SkScalar sy)
+
+Skew Matrix by sx on the x-axis and sy on the y-axis. A positive value of sx skews the
+drawing right as y increases; a positive value of sy skews the drawing down as x increases.
+
+Mathematically, replace Matrix with a skew matrix
+pre-multiplied with Matrix. 
+
+Preconcat the current matrix with the specified skew.
+#Param  sx   The amount to skew in x. ##
+#Param  sy   The amount to skew in y. ##
+
+This has the effect of scaling the drawing by (sx, sy) before transforming
+the result with Matrix.
+
+#Example
+    #Description 
+        Black text mimics an oblique text style by using a negative skew in x that
+        shifts the geometry to the right as the y values decrease.
+        Red text uses a positive skew in y to shift the geometry down as the x values
+        increase.
+        Blue text combines x and y skew to rotate and scale.
+    ##
+    SkPaint paint;

+    paint.setTextSize(128);

+    canvas->translate(30, 130);

+    canvas->save();

+    canvas->skew(-.5, 0);

+    canvas->drawString("A1", 0, 0, paint);

+    canvas->restore();

+    canvas->save();

+    canvas->skew(0, .5);

+    paint.setColor(SK_ColorRED);

+    canvas->drawString("A1", 0, 0, paint);

+    canvas->restore();

+    canvas->skew(-.5, .5);

+    paint.setColor(SK_ColorBLUE);

+    canvas->drawString("A1", 0, 0, paint);
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void concat(const SkMatrix& matrix)
+
+Replace Matrix with matrix pre-multiplied with Matrix.
+
+This has the effect of transforming the drawn geometry by matrix, before transforming
+the result with Matrix.
+
+#Param  matrix   Pre-multiply with Matrix. ##
+
+#Example
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    paint.setTextSize(80);

+    paint.setTextScaleX(.3);

+    SkMatrix matrix;

+    SkRect rect[2] = {{ 10, 20, 90, 110 }, { 40, 130, 140, 180 }};

+    matrix.setRectToRect(rect[0], rect[1], SkMatrix::kFill_ScaleToFit);

+    canvas->drawRect(rect[0], paint);

+    canvas->drawRect(rect[1], paint);

+    paint.setColor(SK_ColorWHITE);

+    canvas->drawString("Here", rect[0].fLeft + 10, rect[0].fBottom - 10, paint);

+    canvas->concat(matrix);

+    canvas->drawString("There", rect[0].fLeft + 10, rect[0].fBottom - 10, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void setMatrix(const SkMatrix& matrix)
+
+Replace Matrix with matrix.
+Unlike concat(), any prior matrix state is overwritten.
+
+#Param  matrix  Copied into Matrix. ##
+
+#Example
+#Height 128
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    canvas->scale(4, 6);

+    canvas->drawString("truth", 2, 10, paint);

+    SkMatrix matrix;

+    matrix.setScale(2.8f, 6);

+    canvas->setMatrix(matrix);

+    canvas->drawString("consequences", 2, 20, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void resetMatrix()
+
+Sets Matrix to the identity matrix.
+Any prior matrix state is overwritten.
+
+#Example
+#Height 128
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    canvas->scale(4, 6);

+    canvas->drawString("truth", 2, 10, paint);

+    canvas->resetMatrix();

+    canvas->scale(2.8f, 6);

+    canvas->drawString("consequences", 2, 20, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method const SkMatrix& getTotalMatrix() const
+
+Returns Matrix.
+This does not account for translation by Device or Surface.
+
+#Return Matrix on Canvas. ##
+
+#Example
+    SkDebugf("isIdentity %s\n", canvas->getTotalMatrix().isIdentity() ? "true" : "false");

+    #StdOut

+        isIdentity true

+    ##

+##
+
+#ToDo incomplete ##
+
+##
+
+#Topic Matrix ##
+
+# ------------------------------------------------------------------------------
+#Topic Clip
+
+Clip is built from a stack of clipping paths. Each Path in the
+stack can be constructed from one or more Path_Contour elements. The 
+Path_Contour may be composed of any number of Path_Verb segments. Each
+Path_Contour forms a closed area; Path_Fill_Type defines the area enclosed
+by Path_Contour.
+
+Clip stack of Path elements successfully restrict the Path area. Each
+Path is transformed by Matrix, then intersected with or subtracted from the 
+prior Clip to form the replacement Clip. Use SkClipOp::kDifference
+to subtract Path from Clip; use SkClipOp::kIntersect to intersect Path
+with Clip.
+
+A clipping Path may be anti-aliased; if Path, after transformation, is
+composed of horizontal and vertical lines, clearing Anti-alias allows whole pixels
+to either be inside or outside the clip. The fastest drawing has a aliased,
+rectanglar clip.
+
+If clipping Path has Anti-alias set, clip may partially clip a pixel, requiring
+that drawing blend partially with the destination along the edge. A rotated 
+rectangular anti-aliased clip looks smoother but draws slower.
+
+Clip can combine with Rect and Round_Rect primitives; like
+Path, these are transformed by Matrix before they are combined with Clip.
+
+Clip can combine with Region. Region is assumed to be in Device coordinates
+and is unaffected by Matrix.
+
+#Example
+#Height 90
+    #Description
+        Draw a red circle with an aliased clip and an anti-aliased clip.
+        Use an image filter to zoom into the pixels drawn.
+        The edge of the aliased clip fully draws pixels in the red circle.
+        The edge of the anti-aliased clip partially draws pixels in the red circle.
+    ##
+    SkPaint redPaint, scalePaint;

+    redPaint.setAntiAlias(true);

+    redPaint.setColor(SK_ColorRED);

+    canvas->save();

+    for (bool antialias : { false, true } ) {

+        canvas->save();

+        canvas->clipRect(SkRect::MakeWH(19.5f, 11.5f), antialias);

+        canvas->drawCircle(17, 11, 8, redPaint);

+        canvas->restore();

+        canvas->translate(16, 0);

+    }

+    canvas->restore();

+    SkMatrix matrix;

+    matrix.setScale(6, 6);

+    scalePaint.setImageFilter(

+            SkImageFilter::MakeMatrixFilter(matrix, kNone_SkFilterQuality, nullptr));

+    SkCanvas::SaveLayerRec saveLayerRec(

+            nullptr, &scalePaint, SkCanvas::kInitWithPrevious_SaveLayerFlag); 

+    canvas->saveLayer(saveLayerRec);

+    canvas->restore();
+##
+
+#Method void clipRect(const SkRect& rect, SkClipOp op, bool doAntiAlias)
+
+Replace Clip with the intersection or difference of Clip and rect,
+with an aliased or anti-aliased clip edge. rect is transformed by Matrix
+before it is combined with Clip.
+
+#Param  rect  Rectangle to combine with Clip. ##
+#Param  op    Clip_Op to apply to Clip. ##
+#Param  doAntiAlias  true if Clip is to be anti-aliased. ##
+
+#Example
+#Height 128
+void draw(SkCanvas* canvas) {

+    canvas->rotate(10);

+    SkPaint paint;

+    paint.setAntiAlias(true);

+    for (auto alias: { false, true } ) {

+        canvas->save();

+        canvas->clipRect(SkRect::MakeWH(90, 80), SkClipOp::kIntersect, alias);

+        canvas->drawCircle(100, 60, 60, paint);

+        canvas->restore();

+        canvas->translate(80, 0);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+#Method void clipRect(const SkRect& rect, SkClipOp op) 
+
+Replace Clip with the intersection or difference of Clip and rect.
+Resulting Clip is aliased; pixels are fully contained by the clip.
+rect is transformed by Matrix
+before it is combined with Clip.
+
+#Param  rect  Rectangle to combine with Clip. ##
+#Param  op    Clip_Op to apply to Clip. ##
+
+#Example
+#Height 192
+#Width 280
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    for (SkClipOp op: { SkClipOp::kIntersect, SkClipOp::kDifference } ) {

+        canvas->save();

+        canvas->clipRect(SkRect::MakeWH(90, 120), op, false);

+        canvas->drawCircle(100, 100, 60, paint);

+        canvas->restore();

+        canvas->translate(80, 0);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+#Method void clipRect(const SkRect& rect, bool doAntiAlias = false) 
+
+Replace Clip with the intersection of Clip and rect.
+Resulting Clip is aliased; pixels are fully contained by the clip.
+rect is transformed by Matrix
+before it is combined with Clip.
+
+#Param  rect  Rectangle to combine with Clip. ##
+#Param  doAntiAlias  true if Clip is to be anti-aliased. ##
+
+#Example
+#Height 133
+    #Description
+        A circle drawn in pieces looks uniform when drawn aliased.
+        The same circle pieces blend with pixels more than once when anti-aliased,
+        visible as a thin pair of lines through the right circle.
+    ##
+void draw(SkCanvas* canvas) {

+    canvas->clear(SK_ColorWHITE);

+    SkPaint paint;

+    paint.setAntiAlias(true);

+    paint.setColor(0x8055aaff);

+    SkRect clipRect = { 0, 0, 87.4f, 87.4f };

+    for (auto alias: { false, true } ) {

+        canvas->save();

+        canvas->clipRect(clipRect, SkClipOp::kIntersect, alias);

+        canvas->drawCircle(67, 67, 60, paint);

+        canvas->restore();

+        canvas->save();

+        canvas->clipRect(clipRect, SkClipOp::kDifference, alias);

+        canvas->drawCircle(67, 67, 60, paint);

+        canvas->restore();

+        canvas->translate(120, 0);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+#Method void androidFramework_setDeviceClipRestriction(const SkIRect& rect)
+
+Sets the max clip rectangle, which can be set by clipRect, clipRRect and
+clipPath and intersect the current clip with the specified rect.
+The max clip affects only future ops (it is not retroactive).
+The clip restriction is not recorded in pictures.
+
+#Private
+This is private API to be used only by Android framework.
+##
+
+#Param  rect   The maximum allowed clip in device coordinates. 
+Empty rect means max clip is not enforced. ##
+
+##
+
+#Method void clipRRect(const SkRRect& rrect, SkClipOp op, bool doAntiAlias)
+
+Replace Clip with the intersection or difference of Clip and rrect,
+with an aliased or anti-aliased clip edge.
+rrect is transformed by Matrix
+before it is combined with Clip.
+
+#Param  rrect  Round_Rect to combine with Clip. ##
+#Param  op  Clip_Op to apply to Clip. ##
+#Param  doAntiAlias  true if Clip is to be antialiased. ##
+
+#Example
+#Height 128
+void draw(SkCanvas* canvas) {

+    canvas->clear(SK_ColorWHITE);

+    SkPaint paint;

+    paint.setAntiAlias(true);

+    paint.setColor(0x8055aaff);

+    SkRRect oval;

+    oval.setOval({10, 20, 90, 100});

+    canvas->clipRRect(oval, SkClipOp::kIntersect, true);

+    canvas->drawCircle(70, 100, 60, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+#Method void clipRRect(const SkRRect& rrect, SkClipOp op) 
+
+Replace Clip with the intersection or difference of Clip and rrect.
+Resulting Clip is aliased; pixels are fully contained by the clip.
+rrect is transformed by Matrix
+before it is combined with Clip.
+
+#Param  rrect  Round_Rect to combine with Clip. ##
+#Param  op  Clip_Op to apply to Clip. ##
+
+#Example
+#Height 128
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    paint.setColor(0x8055aaff);

+    auto oval = SkRRect::MakeOval({10, 20, 90, 100});

+    canvas->clipRRect(oval, SkClipOp::kIntersect);

+    canvas->drawCircle(70, 100, 60, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+#Method void clipRRect(const SkRRect& rrect, bool doAntiAlias = false) 
+
+Replace Clip with the intersection of Clip and rrect,
+with an aliased or anti-aliased clip edge.
+rrect is transformed by Matrix
+before it is combined with Clip.
+
+#Param  rrect  Round_Rect to combine with Clip. ##
+#Param  doAntiAlias  true if Clip is to be antialiased. ##
+
+#Example
+#Height 128
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    paint.setAntiAlias(true);

+    auto oval = SkRRect::MakeRectXY({10, 20, 90, 100}, 9, 13);

+    canvas->clipRRect(oval, true);

+    canvas->drawCircle(70, 100, 60, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+#Method void clipPath(const SkPath& path, SkClipOp op, bool doAntiAlias)
+
+Replace Clip with the intersection or difference of Clip and path,
+with an aliased or anti-aliased clip edge. Path_Fill_Type determines if path
+describes the area inside or outside its contours; and if Path_Contour overlaps
+itself or another Path_Contour, whether the overlaps form part of the area.
+path is transformed by Matrix
+before it is combined with Clip.
+
+#Param  path  Path to combine with Clip. ##
+#Param  op  Clip_Op to apply to Clip. ##
+#Param  doAntiAlias  true if Clip is to be antialiased. ##
+
+#Example
+#Description
+Top figure uses SkPath::kInverseWinding_FillType and SkClipOp::kDifference;
+area outside clip is subtracted from circle.
+
+Bottom figure uses SkPath::kWinding_FillType and SkClipOp::kIntersect;
+area inside clip is intersected with circle.
+##
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    paint.setAntiAlias(true);

+    SkPath path;

+    path.addRect({20, 30, 100, 110});

+    path.setFillType(SkPath::kInverseWinding_FillType);

+    canvas->save();

+    canvas->clipPath(path, SkClipOp::kDifference, false);

+    canvas->drawCircle(70, 100, 60, paint);

+    canvas->restore();

+    canvas->translate(100, 100);

+    path.setFillType(SkPath::kWinding_FillType);

+    canvas->clipPath(path, SkClipOp::kIntersect, false);

+    canvas->drawCircle(70, 100, 60, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+#Method void clipPath(const SkPath& path, SkClipOp op) 
+
+Replace Clip with the intersection or difference of Clip and path.
+Resulting Clip is aliased; pixels are fully contained by the clip.
+Path_Fill_Type determines if path
+describes the area inside or outside its contours; and if Path_Contour overlaps
+itself or another Path_Contour, whether the overlaps form part of the area.
+path is transformed by Matrix
+before it is combined with Clip.
+
+#Param  path  Path to combine with Clip. ##
+#Param  op  Clip_Op to apply to Clip. ##
+
+#Example
+#Description
+Overlapping Rects form a clip. When clip's Path_Fill_Type is set to
+SkPath::kWinding_FillType, the overlap is included. Set to 
+SkPath::kEvenOdd_FillType, the overlap is excluded and forms a hole.
+##
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    paint.setAntiAlias(true);

+    SkPath path;

+    path.addRect({20, 15, 100, 95});

+    path.addRect({50, 65, 130, 135});

+    path.setFillType(SkPath::kWinding_FillType);

+    canvas->save();

+    canvas->clipPath(path, SkClipOp::kIntersect);

+    canvas->drawCircle(70, 85, 60, paint);

+    canvas->restore();

+    canvas->translate(100, 100);

+    path.setFillType(SkPath::kEvenOdd_FillType);

+    canvas->clipPath(path, SkClipOp::kIntersect);

+    canvas->drawCircle(70, 85, 60, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+#Method void clipPath(const SkPath& path, bool doAntiAlias = false) 
+
+Replace Clip with the intersection of Clip and path.
+Resulting Clip is aliased; pixels are fully contained by the clip.
+Path_Fill_Type determines if path
+describes the area inside or outside its contours; and if Path_Contour overlaps
+itself or another Path_Contour, whether the overlaps form part of the area.
+path is transformed by Matrix
+before it is combined with Clip.
+
+#Param  path  Path to combine with Clip. ##
+#Param  doAntiAlias  true if Clip is to be antialiased. ##
+
+#Example
+#Height 212
+#Description
+Clip loops over itself covering its center twice. When clip's Path_Fill_Type 
+is set to SkPath::kWinding_FillType, the overlap is included. Set to 
+SkPath::kEvenOdd_FillType, the overlap is excluded and forms a hole.
+##
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    paint.setAntiAlias(true);

+    SkPath path;

+    SkPoint poly[] = {{20, 20}, { 80, 20}, { 80,  80}, {40,  80},

+                      {40, 40}, {100, 40}, {100, 100}, {20, 100}};

+    path.addPoly(poly, SK_ARRAY_COUNT(poly), true);

+    path.setFillType(SkPath::kWinding_FillType);

+    canvas->save();

+    canvas->clipPath(path, SkClipOp::kIntersect);

+    canvas->drawCircle(50, 50, 45, paint);

+    canvas->restore();

+    canvas->translate(100, 100);

+    path.setFillType(SkPath::kEvenOdd_FillType);

+    canvas->clipPath(path, SkClipOp::kIntersect);

+    canvas->drawCircle(50, 50, 45, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void setAllowSimplifyClip(bool allow) 
+
+#Experimental
+Only used for testing.
+##
+
+Set to simplify clip stack using path ops.
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void clipRegion(const SkRegion& deviceRgn, SkClipOp op = SkClipOp::kIntersect)
+
+Replace Clip with the intersection or difference of Clip and Region deviceRgn.
+Resulting Clip is aliased; pixels are fully contained by the clip.
+deviceRgn is unaffected by Matrix.
+
+#Param  deviceRgn    Region to combine with Clip. ##
+#Param  op  Clip_Op to apply to Clip. ##
+
+#Example
+#Description

+    region is unaffected by canvas rotation; rect is affected by canvas rotation.

+    Both clips are aliased; this is unnoticable on Region clip because it

+    aligns to pixel boundaries.

+##

+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    paint.setAntiAlias(true);

+    SkIRect iRect = {30, 40, 120, 130 };

+    SkRegion region(iRect);

+    canvas->rotate(10);

+    canvas->save();

+    canvas->clipRegion(region, SkClipOp::kIntersect);

+    canvas->drawCircle(50, 50, 45, paint);

+    canvas->restore();

+    canvas->translate(100, 100);

+    canvas->clipRect(SkRect::Make(iRect), SkClipOp::kIntersect);

+    canvas->drawCircle(50, 50, 45, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+#Method bool quickReject(const SkRect& rect) const
+
+Return true if Rect rect, transformed by Matrix, can be quickly determined to be
+outside of Clip. May return false even though rect is outside of Clip.
+
+Use to check if an area to be drawn is clipped out, to skip subsequent draw calls.
+
+#Param  rect  Rect to compare with Clip. ##
+
+#Return  true if rect, transformed by Matrix, does not intersect Clip. ##
+
+#Example
+void draw(SkCanvas* canvas) {

+    SkRect testRect = {30, 30, 120, 129 }; 

+    SkRect clipRect = {30, 130, 120, 230 }; 

+    canvas->save();

+    canvas->clipRect(clipRect);

+    SkDebugf("quickReject %s\n", canvas->quickReject(testRect) ? "true" : "false");

+    canvas->restore();

+    canvas->rotate(10);

+    canvas->clipRect(clipRect);

+    SkDebugf("quickReject %s\n", canvas->quickReject(testRect) ? "true" : "false");

+}
+    #StdOut
+        quickReject true

+        quickReject false
+    ##
+##
+
+#ToDo incomplete ##
+
+##
+
+#Method bool quickReject(const SkPath& path) const
+
+Return true if path, transformed by Matrix, can be quickly determined to be
+outside of Clip. May return false even though path is outside of Clip.
+
+Use to check if an area to be drawn is clipped out, to skip subsequent draw calls.
+
+#Param path  Path to compare with Clip. ##
+
+#Return  true if path, transformed by Matrix, does not intersect Clip. ##
+
+#Example
+void draw(SkCanvas* canvas) {

+    SkPoint testPoints[] = {{30,  30}, {120,  30}, {120, 129} }; 

+    SkPoint clipPoints[] = {{30, 130}, {120, 130}, {120, 230} }; 

+    SkPath testPath, clipPath;

+    testPath.addPoly(testPoints, SK_ARRAY_COUNT(testPoints), true);

+    clipPath.addPoly(clipPoints, SK_ARRAY_COUNT(clipPoints), true);

+    canvas->save();

+    canvas->clipPath(clipPath);

+    SkDebugf("quickReject %s\n", canvas->quickReject(testPath) ? "true" : "false");

+    canvas->restore();

+    canvas->rotate(10);

+    canvas->clipPath(clipPath);

+    SkDebugf("quickReject %s\n", canvas->quickReject(testPath) ? "true" : "false");

+    #StdOut
+        quickReject true

+        quickReject false
+    ##
+}
+##
+
+#ToDo incomplete ##
+
+##
+
+#Method SkRect getLocalClipBounds() const 
+
+Return bounds of Clip, transformed by inverse of Matrix. If Clip is empty,
+return SkRect::MakeEmpty, where all Rect sides equal zero.
+
+Rect returned is outset by one to account for partial pixel coverage if Clip
+is anti-aliased.
+
+#Return  bounds of Clip in local coordinates. ##
+
+#Example
+    #Description 
+        Initial bounds is device bounds outset by 1 on all sides.
+        Clipped bounds is clipPath bounds outset by 1 on all sides.
+        Scaling the canvas by two in x and y scales the local bounds by 1/2 in x and y.
+    ##
+    SkCanvas local(256, 256);

+    canvas = &local;

+    SkRect bounds = canvas->getLocalClipBounds();

+    SkDebugf("left:%g  top:%g  right:%g  bottom:%g\n",

+            bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);

+    SkPoint clipPoints[]  = {{30, 130}, {120, 130}, {120, 230} }; 

+    SkPath clipPath;

+    clipPath.addPoly(clipPoints, SK_ARRAY_COUNT(clipPoints), true);

+    canvas->clipPath(clipPath);

+    bounds = canvas->getLocalClipBounds();

+    SkDebugf("left:%g  top:%g  right:%g  bottom:%g\n",

+            bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);

+    canvas->scale(2, 2);

+    bounds = canvas->getLocalClipBounds();

+    SkDebugf("left:%g  top:%g  right:%g  bottom:%g\n",

+            bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);

+    #StdOut

+        left:-1  top:-1  right:257  bottom:257

+        left:29  top:129  right:121  bottom:231

+        left:14.5  top:64.5  right:60.5  bottom:115.5

+    ##

+##
+
+# local canvas in example works around bug in fiddle ##
+#Bug 6524  ##
+
+##
+
+#Method bool getLocalClipBounds(SkRect* bounds) const 
+
+Return bounds of Clip, transformed by inverse of Matrix. If Clip is empty,
+return false, and set bounds to SkRect::MakeEmpty, where all Rect sides equal zero.
+
+bounds is outset by one to account for partial pixel coverage if Clip
+is anti-aliased.
+
+#Param bounds  Rect of Clip in local coordinates. ##
+
+#Return  true if Clip bounds is not empty. ##
+
+#Example
+    void draw(SkCanvas* canvas) {

+        SkCanvas local(256, 256);

+        canvas = &local;

+        SkRect bounds;

+        SkDebugf("local bounds empty = %s\n", canvas->getLocalClipBounds(&bounds)

+                 ? "false" : "true");

+        SkPath path;

+        canvas->clipPath(path);

+        SkDebugf("local bounds empty = %s\n", canvas->getLocalClipBounds(&bounds)

+                 ? "false" : "true");

+    }
+    #StdOut
+        local bounds empty = false

+        local bounds empty = true
+    ##
+##
+
+# local canvas in example works around bug in fiddle ##
+#Bug 6524  ##
+
+##
+
+#Method SkIRect getDeviceClipBounds() const 
+
+Return IRect bounds of Clip, unaffected by Matrix. If Clip is empty,
+return SkRect::MakeEmpty, where all Rect sides equal zero.
+
+Unlike getLocalClipBounds, returned IRect is not outset. 
+
+#Return  bounds of Clip in Device coordinates. ##
+
+#Example
+void draw(SkCanvas* canvas) {

+    #Description

+        Initial bounds is device bounds, not outset.
+        Clipped bounds is clipPath bounds, not outset.
+        Scaling the canvas by 1/2 in x and y scales the device bounds by 1/2 in x and y.
+    ##

+    SkCanvas device(256, 256);

+    canvas = &device;

+    SkIRect bounds = canvas->getDeviceClipBounds();

+    SkDebugf("left:%d  top:%d  right:%d  bottom:%d\n",

+            bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);

+    SkPoint clipPoints[]  = {{30, 130}, {120, 130}, {120, 230} }; 

+    SkPath clipPath;

+    clipPath.addPoly(clipPoints, SK_ARRAY_COUNT(clipPoints), true);

+    canvas->save();

+    canvas->clipPath(clipPath);

+    bounds = canvas->getDeviceClipBounds();

+    SkDebugf("left:%d  top:%d  right:%d  bottom:%d\n",

+            bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);

+    canvas->restore();

+    canvas->scale(1.f/2, 1.f/2);

+    canvas->clipPath(clipPath);

+    bounds = canvas->getDeviceClipBounds();

+    SkDebugf("left:%d  top:%d  right:%d  bottom:%d\n",

+            bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);

+    #StdOut
+        left:0  top:0  right:256  bottom:256

+        left:30  top:130  right:120  bottom:230

+        left:15  top:65  right:60  bottom:115
+    ##
+}
+##
+
+#ToDo some confusion on why with an identity Matrix local and device are different ##
+
+# device canvas in example works around bug in fiddle ##
+#Bug 6524  ##
+
+##
+
+#Method bool getDeviceClipBounds(SkIRect* bounds) const 
+
+Return IRect bounds of Clip, unaffected by Matrix. If Clip is empty,
+return false, and set bounds to SkRect::MakeEmpty, where all Rect sides equal zero.
+
+Unlike getLocalClipBounds, bounds is not outset. 
+
+#Param bounds  Rect of Clip in device coordinates. ##
+
+#Return  true if Clip bounds is not empty. ##
+
+#Example
+    void draw(SkCanvas* canvas) {

+        SkIRect bounds;

+        SkDebugf("device bounds empty = %s\n", canvas->getDeviceClipBounds(&bounds)

+                 ? "false" : "true");

+        SkPath path;

+        canvas->clipPath(path);

+        SkDebugf("device bounds empty = %s\n", canvas->getDeviceClipBounds(&bounds)

+                 ? "false" : "true");

+    }
+    #StdOut
+        device bounds empty = false

+        device bounds empty = true
+    ##
+##
+
+#ToDo incomplete ##
+
+##
+
+#Topic Clip ##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawColor(SkColor color, SkBlendMode mode = SkBlendMode::kSrcOver)
+
+Fill Clip with Color color.
+mode determines how Color_ARGB is combined with destination.
+
+#Param color    Unpremultiplied Color_ARGB. ##
+#Param mode  SkBlendMode used to combine source color and destination. ##
+
+#Example
+    canvas->drawColor(SK_ColorRED);

+    canvas->clipRect(SkRect::MakeWH(150, 150));

+    canvas->drawColor(SkColorSetARGB(0x80, 0x00, 0xFF, 0x00), SkBlendMode::kPlus);

+    canvas->clipRect(SkRect::MakeWH(75, 75));

+    canvas->drawColor(SkColorSetARGB(0x80, 0x00, 0x00, 0xFF), SkBlendMode::kPlus);

+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void clear(SkColor color) 
+
+Fill Clip with Color color using SkBlendMode::kSrc. 
+This has the effect of replacing all pixels contained by Clip with color.
+
+#Param color    Unpremultiplied Color_ARGB. ##
+
+#Example
+void draw(SkCanvas* canvas) {

+    canvas->save();

+    canvas->clipRect(SkRect::MakeWH(256, 128));

+    canvas->clear(SkColorSetARGB(0x80, 0xFF, 0x00, 0x00)); 

+    canvas->restore();

+    canvas->save();

+    canvas->clipRect(SkRect::MakeWH(150, 192));

+    canvas->clear(SkColorSetARGB(0x80, 0x00, 0xFF, 0x00));

+    canvas->restore();

+    canvas->clipRect(SkRect::MakeWH(75, 256));

+    canvas->clear(SkColorSetARGB(0x80, 0x00, 0x00, 0xFF));

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void discard() 
+
+Make Canvas contents undefined. Subsequent calls that read Canvas pixels,
+such as drawing with SkBlendMode, return undefined results. discard() does
+not change Clip or Matrix.
+
+discard() may do nothing, depending on the implementation of Surface or Device
+that created Canvas.
+
+discard() allows optimized performance on subsequent draws by removing
+cached data associated with Surface or Device.
+It is not necessary to call discard() once done with Canvas;
+any cached data is deleted when owning Surface or Device is deleted.
+
+#ToDo example? not sure how to make this meaningful w/o more implementation detail ##
+
+#NoExample 
+##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawPaint(const SkPaint& paint)
+
+Fill Clip with Paint paint. drawPaint is affected by  Paint components  
+Rasterizer, Mask_Filter, Shader, Color_Filter, Image_Filter, and Blend_Mode; but not by
+Path_Effect.
+
+# can Path_Effect in paint ever alter drawPaint?
+
+#Param  paint    Used to fill the canvas. ##
+
+#Example
+void draw(SkCanvas* canvas) {

+    SkColor     colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE };

+    SkScalar    pos[] = { 0, SK_Scalar1/2, SK_Scalar1 };

+    SkPaint     paint;

+    paint.setShader(SkGradientShader::MakeSweep(256, 256, colors, pos, SK_ARRAY_COUNT(colors)));

+    canvas->drawPaint(paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Enum PointMode
+
+#Code
+    enum PointMode {
+        kPoints_PointMode,
+        kLines_PointMode,
+        kPolygon_PointMode 
+    };
+##
+
+Selects if an array of points are drawn as discrete points, as lines, or as
+an open polygon.
+
+#Const kPoints_PointMode 0
+    Draw each point separately.
+##
+
+#Const kLines_PointMode 1
+    Draw each pair of points as a line segment.
+##
+
+#Const kPolygon_PointMode 2
+    Draw the array of points as a open polygon.
+##
+
+#Example
+    #Description 
+        The upper left corner shows three squares when drawn as points.
+        The upper right corner shows one line; when drawn as lines, two points are required per line.
+        The lower right corner shows two lines; when draw as polygon, no miter is drawn at the corner.
+        The lower left corner shows two lines with a miter when path contains polygon.
+    ##
+void draw(SkCanvas* canvas) {

+  SkPaint paint;

+  paint.setStyle(SkPaint::kStroke_Style);

+  paint.setStrokeWidth(10);

+  SkPoint points[] = {{64, 32}, {96, 96}, {32, 96}};

+  canvas->drawPoints(SkCanvas::kPoints_PointMode, 3, points, paint);

+  canvas->translate(128, 0);

+  canvas->drawPoints(SkCanvas::kLines_PointMode, 3, points, paint);

+  canvas->translate(0, 128);

+  canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, points, paint);

+  SkPath path;

+  path.addPoly(points, 3, false);

+  canvas->translate(-128, 0);

+  canvas->drawPath(path, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint)
+
+Draw pts using Clip, Matrix and Paint paint.
+count is the number of points; if count is less than one, drawPoints has no effect.
+mode may be one of: kPoints_PointMode, kLines_PointMode, or kPolygon_PointMode.
+
+If mode is kPoints_PointMode, the shape of point drawn depends on paint Paint_Stroke_Cap.
+If paint is set to SkPaint::kRound_Cap, each point draws a circle of diameter Paint_Stroke_Width.
+If paint is set to SkPaint::kSquare_Cap or SkPaint::kButt_Cap, 
+each point draws a square of width and height Paint_Stroke_Width.
+
+If mode is kLines_PointMode, each pair of points draws a line segment.
+One line is drawn for every two points; each point is used once. If count is odd,
+the final point is ignored. 
+
+If mode is kPolygon_PointMode, each adjacent pair of points draws a line segment.
+count minus one lines are drawn; the first and last point are used once.
+
+Each line segment respects paint Paint_Stroke_Cap and Paint_Stroke_Width.
+Paint_Style is ignored, as if were set to SkPaint::kStroke_Style.
+
+drawPoints always draws each element one at a time; drawPoints is not affected by
+Paint_Stroke_Join, and unlike drawPath, does not create a mask from all points and lines
+before drawing. 
+
+#Param  mode     Whether pts draws points or lines. ##
+#Param  count    The number of points in the array. ##
+#Param  pts      Array of points to draw. ##
+#Param  paint    Stroke, blend, color, and so on, used to draw. ##
+
+#Example
+#Height 200
+    #Description 
+    #List
+    # The first column draws points. ##
+    # The second column draws points as lines. ##
+    # The third column draws points as a polygon. ##
+    # The fourth column draws points as a polygonal path. ##
+    # The first row uses a round cap and round join. ##
+    # The second row uses a square cap and a miter join. ##
+    # The third row uses a butt cap and a bevel join. ##
+    ##
+    The transparent color makes multiple line draws visible;
+    the path is drawn all at once.
+    ##
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    paint.setAntiAlias(true);

+    paint.setStyle(SkPaint::kStroke_Style);

+    paint.setStrokeWidth(10);

+    paint.setColor(0x80349a45);

+    const SkPoint points[] = {{32, 16}, {48, 48}, {16, 32}};

+    const SkPaint::Join join[] = { SkPaint::kRound_Join, 

+                                   SkPaint::kMiter_Join,

+                                   SkPaint::kBevel_Join };

+    int joinIndex = 0;

+    SkPath path;

+    path.addPoly(points, 3, false);

+    for (const auto cap : { SkPaint::kRound_Cap, SkPaint::kSquare_Cap, SkPaint::kButt_Cap } ) {

+        paint.setStrokeCap(cap);

+        paint.setStrokeJoin(join[joinIndex++]);

+        for (const auto mode : { SkCanvas::kPoints_PointMode,

+                                 SkCanvas::kLines_PointMode,

+                                 SkCanvas::kPolygon_PointMode } ) {

+            canvas->drawPoints(mode, 3, points, paint);

+            canvas->translate(64, 0);

+        }

+        canvas->drawPath(path, paint);

+        canvas->translate(-192, 64);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawPoint(SkScalar x, SkScalar y, const SkPaint& paint)
+
+Draw point at (x, y) using Clip, Matrix and Paint paint.
+
+The shape of point drawn depends on paint Paint_Stroke_Cap.
+If paint is set to SkPaint::kRound_Cap, draw a circle of diameter Paint_Stroke_Width.
+If paint is set to SkPaint::kSquare_Cap or SkPaint::kButt_Cap, 
+draw a square of width and height Paint_Stroke_Width.
+Paint_Style is ignored, as if were set to SkPaint::kStroke_Style.
+
+#Param  x        Left edge of circle or square. ##
+#Param  y        Top edge of circle or square. ##
+#Param  paint    Stroke, blend, color, and so on, used to draw. ##
+
+#Example
+void draw(SkCanvas* canvas) {

+  SkPaint paint;

+  paint.setAntiAlias(true);

+  paint.setColor(0x80349a45);

+  paint.setStyle(SkPaint::kStroke_Style);

+  paint.setStrokeWidth(100);

+  paint.setStrokeCap(SkPaint::kRound_Cap);

+  canvas->scale(1, 1.2f);

+  canvas->drawPoint(64, 96, paint);

+  canvas->scale(.6f, .8f);

+  paint.setColor(SK_ColorWHITE);

+  canvas->drawPoint(106, 120, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint)
+
+Draw line segment from (x0, y0) to (x1, y1) using Clip, Matrix, and Paint paint.
+In paint: Paint_Stroke_Width describes the line thickness; Paint_Stroke_Cap draws the end rounded or square;
+Paint_Style is ignored, as if were set to SkPaint::kStroke_Style.
+
+#Param  x0    Start of line segment on x-axis. ##
+#Param  y0    Start of line segment on y-axis. ##
+#Param  x1    End of line segment on x-axis. ##
+#Param  y1    End of line segment on y-axis. ##
+#Param  paint  Stroke, blend, color, and so on, used to draw. ##
+
+#Example
+  SkPaint paint;

+  paint.setAntiAlias(true);

+  paint.setColor(0xFF9a67be);

+  paint.setStrokeWidth(20);

+  canvas->skew(1, 0);

+  canvas->drawLine(32, 96, 32, 160, paint);

+  canvas->skew(-2, 0);

+  canvas->drawLine(288, 96, 288, 160, paint);

+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawRect(const SkRect& rect, const SkPaint& paint)
+
+Draw Rect rect using Clip, Matrix, and Paint paint.
+In paint: Paint_Style determines if rectangle is stroked or filled; 
+if stroked, Paint_Stroke_Width describes the line thickness, and
+Paint_Stroke_Join draws the corners rounded or square.
+
+#Param  rect     The rectangle to be drawn. ##
+#Param  paint    Stroke or fill, blend, color, and so on, used to draw. ##
+
+#Example
+void draw(SkCanvas* canvas) {

+    SkPoint rectPts[] = { {64, 48}, {192, 160} };

+    SkPaint paint;

+    paint.setAntiAlias(true);

+    paint.setStyle(SkPaint::kStroke_Style);

+    paint.setStrokeWidth(20);

+    paint.setStrokeJoin(SkPaint::kRound_Join);

+    SkMatrix rotator;

+    rotator.setRotate(30, 128, 128);

+    for (auto color : { SK_ColorRED, SK_ColorBLUE, SK_ColorYELLOW, SK_ColorMAGENTA } ) {

+        paint.setColor(color);

+        SkRect rect;

+        rect.set(rectPts[0], rectPts[1]);

+        canvas->drawRect(rect, paint);

+        rotator.mapPoints(rectPts, 2);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawIRect(const SkIRect& rect, const SkPaint& paint) 
+
+Draw IRect rect using Clip, Matrix, and Paint paint.
+In paint: Paint_Style determines if rectangle is stroked or filled; 
+if stroked, Paint_Stroke_Width describes the line thickness, and
+Paint_Stroke_Join draws the corners rounded or square.
+
+#Param  rect     The rectangle to be drawn. ##
+#Param  paint    Stroke or fill, blend, color, and so on, used to draw. ##
+
+#Example
+    SkIRect rect = { 64, 48, 192, 160 };

+    SkPaint paint;

+    paint.setAntiAlias(true);

+    paint.setStyle(SkPaint::kStroke_Style);

+    paint.setStrokeWidth(20);

+    paint.setStrokeJoin(SkPaint::kRound_Join);

+    for (auto color : { SK_ColorRED, SK_ColorBLUE, SK_ColorYELLOW, SK_ColorMAGENTA } ) {

+        paint.setColor(color);

+        canvas->drawIRect(rect, paint);

+        canvas->rotate(30, 128, 128);

+    }

+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawRegion(const SkRegion& region, const SkPaint& paint)
+
+Draw Region region using Clip, Matrix, and Paint paint.
+In paint: Paint_Style determines if rectangle is stroked or filled; 
+if stroked, Paint_Stroke_Width describes the line thickness, and
+Paint_Stroke_Join draws the corners rounded or square.
+
+#Param  region   The region to be drawn. ##
+#Param  paint    Paint stroke or fill, blend, color, and so on, used to draw. ##
+
+#Example
+void draw(SkCanvas* canvas) {

+    SkRegion region;

+    region.op( 10, 10, 50, 50, SkRegion::kUnion_Op);

+    region.op( 10, 50, 90, 90, SkRegion::kUnion_Op);

+    SkPaint paint;

+    paint.setAntiAlias(true);

+    paint.setStyle(SkPaint::kStroke_Style);

+    paint.setStrokeWidth(20);

+    paint.setStrokeJoin(SkPaint::kRound_Join);

+    canvas->drawRegion(region, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawOval(const SkRect& oval, const SkPaint& paint)
+
+Draw Oval oval using Clip, Matrix, and Paint.
+In paint: Paint_Style determines if Oval is stroked or filled; 
+if stroked, Paint_Stroke_Width describes the line thickness.
+
+#Param  oval     Rect bounds of Oval. ##
+#Param  paint    Paint stroke or fill, blend, color, and so on, used to draw. ##
+
+#Example
+void draw(SkCanvas* canvas) {

+    canvas->clear(0xFF3f5f9f);

+    SkColor  kColor1 = SkColorSetARGB(0xff, 0xff, 0x7f, 0);

+    SkColor  g1Colors[] = { kColor1, SkColorSetA(kColor1, 0x20) };

+    SkPoint  g1Points[] = { { 0, 0 }, { 0, 100 } };

+    SkScalar pos[] = { 0.2f, 1.0f };

+    SkRect bounds = SkRect::MakeWH(80, 70);

+    SkPaint paint;

+    paint.setAntiAlias(true);

+    paint.setShader(SkGradientShader::MakeLinear(g1Points, g1Colors, pos, SK_ARRAY_COUNT(g1Colors),

+            SkShader::kClamp_TileMode));

+    canvas->drawOval(bounds , paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawRRect(const SkRRect& rrect, const SkPaint& paint)
+
+Draw Round_Rect rrect using Clip, Matrix, and Paint paint.
+In paint: Paint_Style determines if rrect is stroked or filled; 
+if stroked, Paint_Stroke_Width describes the line thickness.
+
+rrect may represent a rectangle, circle, oval, uniformly rounded rectangle, or may have
+any combination of positive non-square radii for the four corners.
+
+#Param  rrect    Round_Rect with up to eight corner radii to draw. ##
+#Param  paint    Paint stroke or fill, blend, color, and so on, used to draw. ##
+
+#Example
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    paint.setAntiAlias(true);

+    SkRect outer = {30, 40, 210, 220};

+    SkRect radii = {30, 50, 70, 90 };

+    SkRRect rRect;

+    rRect.setNinePatch(outer, radii.fLeft, radii.fTop, radii.fRight, radii.fBottom);

+    canvas->drawRRect(rRect, paint);

+    paint.setColor(SK_ColorWHITE);

+    canvas->drawLine(outer.fLeft + radii.fLeft, outer.fTop,

+                     outer.fLeft + radii.fLeft, outer.fBottom, paint);

+    canvas->drawLine(outer.fRight - radii.fRight, outer.fTop, 

+                     outer.fRight - radii.fRight, outer.fBottom, paint);

+    canvas->drawLine(outer.fLeft,  outer.fTop + radii.fTop, 

+                     outer.fRight, outer.fTop + radii.fTop, paint);

+    canvas->drawLine(outer.fLeft,  outer.fBottom - radii.fBottom, 

+                     outer.fRight, outer.fBottom - radii.fBottom, paint);

+}

+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint)
+
+Draw Round_Rect outer and inner
+using Clip, Matrix, and Paint paint. 
+outer must contain inner or the drawing is undefined.
+In paint: Paint_Style determines if rrect is stroked or filled; 
+if stroked, Paint_Stroke_Width describes the line thickness.
+If stroked and Round_Rect corner has zero length radii, Paint_Stroke_Join can draw
+corners rounded or square. 
+
+GPU-backed platforms take advantage of drawDRRect since both outer and inner are
+concave and outer contains inner. These platforms may not be able to draw
+Path built with identical data as fast. 
+
+#Param  outer    Round_Rect outer bounds to draw. ##
+#Param  inner    Round_Rect inner bounds to draw. ##
+#Param  paint    Paint stroke or fill, blend, color, and so on, used to draw. ##
+
+#Example
+void draw(SkCanvas* canvas) {

+   SkRRect outer = SkRRect::MakeRect({20, 40, 210, 200});

+   SkRRect inner = SkRRect::MakeOval({60, 70, 170, 160});

+   SkPaint paint;

+   canvas->drawDRRect(outer, inner, paint);

+}
+##
+
+#Example
+#Description
+    Outer Rect has no corner radii, but stroke join is rounded.
+    Inner Round_Rect has corner radii; outset stroke increases radii of corners.
+    Stroke join does not affect inner Round_Rect since it has no sharp corners.
+##
+void draw(SkCanvas* canvas) {

+   SkRRect outer = SkRRect::MakeRect({20, 40, 210, 200});

+   SkRRect inner = SkRRect::MakeRectXY({60, 70, 170, 160}, 10, 10);

+   SkPaint paint;

+   paint.setAntiAlias(true);

+   paint.setStyle(SkPaint::kStroke_Style);

+   paint.setStrokeWidth(20);

+   paint.setStrokeJoin(SkPaint::kRound_Join);

+   canvas->drawDRRect(outer, inner, paint);

+   paint.setStrokeWidth(1);

+   paint.setColor(SK_ColorWHITE);

+   canvas->drawDRRect(outer, inner, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint)
+
+Draw Circle at (cx, cy) with radius using Clip, Matrix, and Paint paint.
+If radius is zero or less, nothing is drawn.
+In paint: Paint_Style determines if Circle is stroked or filled; 
+if stroked, Paint_Stroke_Width describes the line thickness.
+
+#Param  cx       Circle center on the x-axis. ##
+#Param  cy       Circle center on the y-axis. ##
+#Param  radius   Half the diameter of Circle. ##
+#Param  paint    Paint stroke or fill, blend, color, and so on, used to draw. ##
+
+#Example
+    void draw(SkCanvas* canvas) {

+        SkPaint paint;

+        paint.setAntiAlias(true);

+        canvas->drawCircle(128, 128, 90, paint);

+        paint.setColor(SK_ColorWHITE);

+        canvas->drawCircle(86, 86, 20, paint);

+        canvas->drawCircle(160, 76, 20, paint);

+        canvas->drawCircle(140, 150, 35, paint);

+    }

+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
+                 bool useCenter, const SkPaint& paint)
+
+Draw Arc using Clip, Matrix, and Paint paint.
+Arc is part of Oval bounded by oval, sweeping from startAngle to startAngle plus
+sweepAngle. startAngle and sweepAngle are in degrees.
+startAngle of zero places start point at the right middle edge of oval.
+A positive sweepAngle places Arc end point clockwise from start point;
+a negative sweepAngle places Arc end point counterclockwise from start point.
+sweepAngle may exceed 360 degrees, a full circle.
+If useCenter is true, draw a wedge that includes lines from oval
+center to Arc end points. If useCenter is false, draw Arc between end points.
+
+If Rect oval is empty or sweepAngle is zero, nothing is drawn.
+
+#Param  oval     Rect bounds of Oval containing Arc to draw. ##
+#Param  startAngle Angle in degrees where Arc begins. ##
+#Param  sweepAngle Sweep angle in degrees; positive is clockwise. ##
+#Param  useCenter If true include the center of the oval. ##
+#Param  paint    Paint stroke or fill, blend, color, and so on, used to draw. ##
+
+#Example
+    void draw(SkCanvas* canvas) {

+        SkPaint paint;

+        paint.setAntiAlias(true);

+        SkRect oval = { 4, 4, 60, 60};

+        for (auto useCenter : { false, true } ) {

+            for (auto style : { SkPaint::kFill_Style, SkPaint::kStroke_Style } ) {

+                paint.setStyle(style);

+                for (auto degrees : { 45, 90, 180, 360} ) {

+                    canvas->drawArc(oval, 0, degrees , useCenter, paint);

+                    canvas->translate(64, 0);

+                }

+                canvas->translate(-256, 64);

+            }

+        }

+    }
+##
+
+#Example
+#Height 64
+    void draw(SkCanvas* canvas) {

+        SkPaint paint;

+        paint.setAntiAlias(true);

+        paint.setStyle(SkPaint::kStroke_Style);

+        paint.setStrokeWidth(4);

+        SkRect oval = { 4, 4, 60, 60};

+        float intervals[] = { 5, 5 };

+        paint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 2.5f));

+        for (auto degrees : { 270, 360, 540, 720 } ) {

+            canvas->drawArc(oval, 0, degrees, false, paint);

+            canvas->translate(64, 0);

+        }

+    }
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, const SkPaint& paint)
+
+Draw Round_Rect bounded by Rect rect, with corner radii (rx, ry) using Clip, Matrix,
+and Paint paint.
+In paint: Paint_Style determines if Round_Rect is stroked or filled; 
+if stroked, Paint_Stroke_Width describes the line thickness.
+If rx or ry are less than zero, they are treated as if they are zero.
+If rx plus ry exceeds rect width or rect height, radii are scaled down to fit.
+If rx and ry are zero, Round_Rect is drawn as Rect and if stroked is affected by Paint_Stroke_Join.
+
+#Param  rect     Rect bounds of Round_Rect to draw. ##
+#Param  rx       Semiaxis length in x of oval describing rounded corners. ##
+#Param  ry       Semiaxis length in y of oval describing rounded corners. ##
+#Param  paint    Stroke, blend, color, and so on, used to draw. ##
+
+#Example
+#Description
+    Top row has a zero radius a generates a rectangle.
+    Second row radii sum to less than sides.
+    Third row radii sum equals sides.
+    Fourth row radii sum exceeds sides; radii are scaled to fit.
+##
+    void draw(SkCanvas* canvas) {

+        SkVector radii[] = { {0, 20}, {10, 10}, {10, 20}, {10, 40} };

+        SkPaint paint;

+        paint.setStrokeWidth(15);

+        paint.setStrokeJoin(SkPaint::kRound_Join);

+        paint.setAntiAlias(true);

+        for (auto style : { SkPaint::kStroke_Style, SkPaint::kFill_Style  } ) {

+            paint.setStyle(style );

+            for (size_t i = 0; i < SK_ARRAY_COUNT(radii); ++i) {

+               canvas->drawRoundRect({10, 10, 60, 40}, radii[i].fX, radii[i].fY, paint);

+               canvas->translate(0, 60);

+            }

+            canvas->translate(80, -240);

+        }

+    }
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawPath(const SkPath& path, const SkPaint& paint)
+
+Draw Path path using Clip, Matrix, and Paint paint.
+Path contains an array of Path_Contour, each of which may be open or closed.
+
+In paint: Paint_Style determines if Round_Rect is stroked or filled:
+if filled, Path_Fill_Type determines whether Path_Contour describes inside or outside of fill;
+if stroked, Paint_Stroke_Width describes the line thickness, Paint_Stroke_Cap describes line ends,
+and Paint_Stroke_Join describes how corners are drawn.
+
+#Param  path     Path to draw. ##
+#Param  paint    Stroke, blend, color, and so on, used to draw. ##
+
+#Example
+#Description
+    Top rows draw stroked path with combinations of joins and caps. The open contour
+    is affected by caps; the closed contour is affected by joins.
+    Bottom row draws fill the same for open and closed contour. 
+    First bottom column shows winding fills overlap.
+    Second bottom column shows even odd fills exclude overlap.
+    Third bottom column shows inverse winding fills area outside both contours.
+##
+void draw(SkCanvas* canvas) {

+    SkPath path;

+    path.moveTo(20, 20);

+    path.quadTo(60, 20, 60, 60);

+    path.close();

+    path.moveTo(60, 20);

+    path.quadTo(60, 60, 20, 60);

+    SkPaint paint;

+    paint.setStrokeWidth(10);

+    paint.setAntiAlias(true);

+    paint.setStyle(SkPaint::kStroke_Style);

+    for (auto join: { SkPaint::kBevel_Join, SkPaint::kRound_Join, SkPaint::kMiter_Join } ) {

+        paint.setStrokeJoin(join);

+        for (auto cap: { SkPaint::kButt_Cap, SkPaint::kSquare_Cap, SkPaint::kRound_Cap  } ) {

+            paint.setStrokeCap(cap);

+            canvas->drawPath(path, paint);

+            canvas->translate(80, 0);

+        }

+        canvas->translate(-240, 60);

+    }

+    paint.setStyle(SkPaint::kFill_Style);

+    for (auto fill : { SkPath::kWinding_FillType, 

+                       SkPath::kEvenOdd_FillType, 

+                       SkPath::kInverseWinding_FillType } ) {

+        path.setFillType(fill);

+        canvas->save();

+        canvas->clipRect({0, 10, 80, 70});

+        canvas->drawPath(path, paint);

+        canvas->restore();

+        canvas->translate(80, 0);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+#Topic Draw_Image
+
+drawImage, drawImageRect, and drawImageNine can be called with a bare pointer or a smart pointer as a convenience.
+The pairs of calls are otherwise identical.
+
+
+#Method void drawImage(const SkImage* image, SkScalar left, SkScalar top, const SkPaint* paint = NULL)
+
+Draw Image image, with its top-left corner at (left, top),
+using Clip, Matrix, and optional Paint paint.
+
+If paint is supplied, apply Color_Filter, Color_Alpha, Image_Filter, Blend_Mode, and Draw_Looper.
+If image is kAlpha_8_SkColorType, apply Shader.
+if paint contains Mask_Filter, generate mask from image bounds. If generated mask extends
+beyond image bounds, replicate image edge colors, just as Shader made from 
+SkImage::makeShader with SkShader::kClamp_TileMode set replicates the image's edge
+color when it samples outside of its bounds. 
+
+#Param  image    Uncompressed rectangular map of pixels. ##
+#Param  left     Left side of image. ##
+#Param  top      Top side of image. ##
+#Param  paint    Paint containing Blend_Mode, Color_Filter, Image_Filter, and so on; or nullptr. ##
+
+#Example
+#Height 64
+#Image 4
+void draw(SkCanvas* canvas) {

+   // sk_sp<SkImage> image;

+   SkImage* imagePtr = image.get();

+   canvas->drawImage(imagePtr, 0, 0);

+   SkPaint paint;

+   canvas->drawImage(imagePtr, 80, 0, &paint);

+   paint.setAlpha(0x80);

+   canvas->drawImage(imagePtr, 160, 0, &paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawImage(const sk_sp<SkImage>& image, SkScalar left, SkScalar top,
+                   const SkPaint* paint = NULL) 
+
+Draw Image image, with its top-left corner at (left, top),
+using Clip, Matrix, and optional Paint paint.
+
+If Paint paint is supplied, apply Color_Filter, Color_Alpha, Image_Filter, Blend_Mode, and Draw_Looper.
+If image is kAlpha_8_SkColorType, apply Shader.
+if paint contains Mask_Filter, generate mask from image bounds. If generated mask extends
+beyond image bounds, replicate image edge colors, just as Shader made from 
+SkImage::makeShader with SkShader::kClamp_TileMode set replicates the image's edge
+color when it samples outside of its bounds. 
+
+#Param  image    Uncompressed rectangular map of pixels. ##
+#Param  left     Left side of image. ##
+#Param  top      Top side of image. ##
+#Param  paint    Paint containing Blend_Mode, Color_Filter, Image_Filter, and so on; or nullptr. ##
+
+#Example
+#Height 64
+#Image 4
+void draw(SkCanvas* canvas) {

+   // sk_sp<SkImage> image;

+   canvas->drawImage(image, 0, 0);

+   SkPaint paint;

+   canvas->drawImage(image, 80, 0, &paint);

+   paint.setAlpha(0x80);

+   canvas->drawImage(image, 160, 0, &paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Enum SrcRectConstraint
+
+#Code
+    enum SrcRectConstraint {
+        kStrict_SrcRectConstraint, 
+        kFast_SrcRectConstraint, 
+    };
+##
+
+SrcRectConstraint controls the behavior at the edge of the Rect src, provided to drawImageRect,
+trading off speed for precision.
+
+Image_Filter in Paint may sample multiple pixels in the image.
+Rect src restricts the bounds of pixels that may be read. Image_Filter may slow
+down if it cannot read outside the bounds, when sampling near the edge of Rect src. 
+SrcRectConstraint specifies whether an Image_Filter is allowed to read pixels
+outside Rect src.
+
+#Const kStrict_SrcRectConstraint
+    Requires Image_Filter to respect Rect src,
+    sampling only inside of its bounds, possibly with a performance penalty.
+##
+
+#Const kFast_SrcRectConstraint
+    Permits Image_Filter to sample outside of Rect src
+    by half the width of Image_Filter, permitting it to run faster but with
+    error at the image edges.
+##
+
+#Example
+#Height 64
+#Description
+    redBorder contains a black and white checkerboard bordered by red.
+    redBorder is drawn scaled by 16 on the left.
+    The middle and right bitmaps are filtered checkboards.
+    Drawing the checkerboard with kStrict_SrcRectConstraint shows only a blur of black and white.
+    Drawing the checkerboard with kFast_SrcRectConstraint allows red to bleed in the corners.
+##
+void draw(SkCanvas* canvas) {

+    SkBitmap redBorder;

+    redBorder.allocPixels(SkImageInfo::MakeN32Premul(4, 4));

+    SkCanvas checkRed(redBorder);

+    checkRed.clear(SK_ColorRED);

+    uint32_t checkers[][2] = { { SK_ColorBLACK, SK_ColorWHITE },

+                               { SK_ColorWHITE, SK_ColorBLACK } };

+    checkRed.writePixels(

+            SkImageInfo::MakeN32Premul(2, 2), (void*) checkers, sizeof(checkers[0]), 1, 1);

+    canvas->scale(16, 16);

+    canvas->drawBitmap(redBorder, 0, 0, nullptr);

+    canvas->resetMatrix();

+    sk_sp<SkImage> image = SkImage::MakeFromBitmap(redBorder);

+    SkPaint lowPaint;

+    lowPaint.setFilterQuality(kLow_SkFilterQuality);

+    for (auto constraint : { SkCanvas::kStrict_SrcRectConstraint,

+                             SkCanvas::kFast_SrcRectConstraint } ) {

+        canvas->translate(80, 0);

+        canvas->drawImageRect(image.get(), SkRect::MakeLTRB(1, 1, 3, 3),

+                SkRect::MakeLTRB(16, 16, 48, 48), &lowPaint, constraint);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
+                       const SkPaint* paint,
+                       SrcRectConstraint constraint = kStrict_SrcRectConstraint)
+
+Draw Rect src of Image image, scaled and translated to fill Rect dst.
+Additionally transform draw using Clip, Matrix, and optional Paint paint.
+If Paint paint is supplied, apply Color_Filter, Color_Alpha, Image_Filter, Blend_Mode, and Draw_Looper.
+If image is kAlpha_8_SkColorType, apply Shader.
+if paint contains Mask_Filter, generate mask from image bounds. If generated mask extends
+beyond image bounds, replicate image edge colors, just as Shader made from 
+SkImage::makeShader with SkShader::kClamp_TileMode set replicates the image's edge
+color when it samples outside of its bounds. 
+constraint set to kStrict_SrcRectConstraint limits Paint Filter_Quality to sample within src;
+set to kFast_SrcRectConstraint allows sampling outside to improve performance.
+
+#Param  image      Image containing pixels, dimensions, and format. ##
+#Param  src        Source Rect of image to draw from. ##
+#Param  dst        Destination Rect of image to draw to. ##
+#Param  paint      Paint containing Blend_Mode, Color_Filter, Image_Filter, and so on; or nullptr. ##
+#Param  constraint Filter strictly within src or draw faster. ##
+
+#Example
+#Height 64
+#Description
+    The left bitmap draws with Paint default kNone_SkFilterQuality, and stays within
+    its bounds; there's no bleeding with kFast_SrcRectConstraint.
+    the middle and right bitmaps draw with kLow_SkFilterQuality; with
+    kStrict_SrcRectConstraint, the filter remains within the checkerboard, and
+    with kFast_SrcRectConstraint red bleeds on the edges.
+##
+void draw(SkCanvas* canvas) {

+    uint32_t pixels[][4] = { 

+            { 0xFFFF0000, 0xFFFF0000, 0xFFFF0000, 0xFFFF0000 },

+            { 0xFFFF0000, 0xFF000000, 0xFFFFFFFF, 0xFFFF0000 },

+            { 0xFFFF0000, 0xFFFFFFFF, 0xFF000000, 0xFFFF0000 },

+            { 0xFFFF0000, 0xFFFF0000, 0xFFFF0000, 0xFFFF0000 } };

+    SkBitmap redBorder;

+    redBorder.installPixels(SkImageInfo::MakeN32Premul(4, 4), 

+            (void*) pixels, sizeof(pixels[0]));

+    sk_sp<SkImage> image = SkImage::MakeFromBitmap(redBorder);

+    SkPaint lowPaint;

+    for (auto constraint : {

+            SkCanvas::kFast_SrcRectConstraint,

+            SkCanvas::kStrict_SrcRectConstraint,

+            SkCanvas::kFast_SrcRectConstraint } ) {

+        canvas->drawImageRect(image.get(), SkRect::MakeLTRB(1, 1, 3, 3),

+                SkRect::MakeLTRB(16, 16, 48, 48), &lowPaint, constraint);

+        lowPaint.setFilterQuality(kLow_SkFilterQuality);

+        canvas->translate(80, 0);

+    }

+}

+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
+                       const SkPaint* paint, SrcRectConstraint constraint = kStrict_SrcRectConstraint)
+
+Draw IRect isrc of Image image, scaled and translated to fill Rect dst.
+Note that isrc is on integer pixel boundaries; dst may include fractional boundaries.
+Additionally transform draw using Clip, Matrix, and optional Paint paint.
+If Paint paint is supplied, apply Color_Filter, Color_Alpha, Image_Filter, Blend_Mode, and Draw_Looper.
+If image is kAlpha_8_SkColorType, apply Shader.
+if paint contains Mask_Filter, generate mask from image bounds. If generated mask extends
+beyond image bounds, replicate image edge colors, just as Shader made from 
+SkImage::makeShader with SkShader::kClamp_TileMode set replicates the image's edge
+color when it samples outside of its bounds. 
+constraint set to kStrict_SrcRectConstraint limits Paint Filter_Quality to sample within src;
+set to kFast_SrcRectConstraint allows sampling outside to improve performance.
+
+#Param  image      Image containing pixels, dimensions, and format. ##
+#Param  isrc       Source IRect of image to draw from. ##
+#Param  dst        Destination Rect of image to draw to. ##
+#Param  paint      Paint containing Blend_Mode, Color_Filter, Image_Filter, and so on; or nullptr. ##
+#Param  constraint Filter strictly within src or draw faster. ##
+
+#Example
+#Image 4
+void draw(SkCanvas* canvas) {

+    // sk_sp<SkImage> image;

+    for (auto i : { 1, 2, 4, 8 } ) {

+        canvas->drawImageRect(image.get(), SkIRect::MakeLTRB(0, 0, 100, 100), 

+                SkRect::MakeXYWH(i * 20, i * 20, i * 20, i * 20), nullptr);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
+                       SrcRectConstraint constraint = kStrict_SrcRectConstraint)
+
+Draw Image image, scaled and translated to fill Rect dst,
+using Clip, Matrix, and optional Paint paint.
+If Paint paint is supplied, apply Color_Filter, Color_Alpha, Image_Filter, Blend_Mode, and Draw_Looper.
+If image is kAlpha_8_SkColorType, apply Shader.
+if paint contains Mask_Filter, generate mask from image bounds. If generated mask extends
+beyond image bounds, replicate image edge colors, just as Shader made from 
+SkImage::makeShader with SkShader::kClamp_TileMode set replicates the image's edge
+color when it samples outside of its bounds. 
+Use constaint to choose kStrict_SrcRectConstraint or kFast_SrcRectConstraint.
+
+#Param  image      Image containing pixels, dimensions, and format. ##
+#Param  dst        Destination Rect of image to draw to. ##
+#Param  paint      Paint containing Blend_Mode, Color_Filter, Image_Filter, and so on; or nullptr. ##
+#Param  constraint Filter strictly within src or draw faster. ##
+
+#Example
+#Image 4
+void draw(SkCanvas* canvas) {

+    // sk_sp<SkImage> image;

+    for (auto i : { 20, 40, 80, 160 } ) {

+        canvas->drawImageRect(image.get(), SkRect::MakeXYWH(i, i, i, i), nullptr);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawImageRect(const sk_sp<SkImage>& image, const SkRect& src, const SkRect& dst,
+                       const SkPaint* paint,
+                       SrcRectConstraint constraint = kStrict_SrcRectConstraint) 
+
+Draw Rect src of Image image, scaled and translated to fill Rect dst.
+Additionally transform draw using Clip, Matrix, and optional Paint paint.
+If Paint paint is supplied, apply Color_Filter, Color_Alpha, Image_Filter, Blend_Mode, and Draw_Looper.
+If image is kAlpha_8_SkColorType, apply Shader.
+if paint contains Mask_Filter, generate mask from image bounds. If generated mask extends
+beyond image bounds, replicate image edge colors, just as Shader made from 
+SkImage::makeShader with SkShader::kClamp_TileMode set replicates the image's edge
+color when it samples outside of its bounds. 
+constraint set to kStrict_SrcRectConstraint limits Paint Filter_Quality to sample within src;
+set to kFast_SrcRectConstraint allows sampling outside to improve performance.
+
+#Param  image      Image containing pixels, dimensions, and format. ##
+#Param  src        Source Rect of image to draw from. ##
+#Param  dst        Destination Rect of image to draw to. ##
+#Param  paint      Paint containing Blend_Mode, Color_Filter, Image_Filter, and so on; or nullptr. ##
+#Param  constraint Filter strictly within src or draw faster. ##
+
+#Example
+#Height 64
+#Description
+    Canvas scales and translates; transformation from src to dst also scales.
+    The two matrices are concatenated to create the final transformation.
+##
+void draw(SkCanvas* canvas) {

+    uint32_t pixels[][2] = { { SK_ColorBLACK, SK_ColorWHITE },

+                             { SK_ColorWHITE, SK_ColorBLACK } };

+    SkBitmap bitmap;

+    bitmap.installPixels(SkImageInfo::MakeN32Premul(2, 2), 

+            (void*) pixels, sizeof(pixels[0]));

+    sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);

+    SkPaint paint;

+    canvas->scale(4, 4);

+    for (auto alpha : { 50, 100, 150, 255 } ) {

+        paint.setAlpha(alpha);

+        canvas->drawImageRect(image, SkRect::MakeWH(2, 2), SkRect::MakeWH(8, 8), &paint);

+        canvas->translate(8, 0);

+    }

+}

+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawImageRect(const sk_sp<SkImage>& image, const SkIRect& isrc, const SkRect& dst,
+                       const SkPaint* paint, SrcRectConstraint constraint = kStrict_SrcRectConstraint) 
+
+Draw IRect isrc of Image image, scaled and translated to fill Rect dst.
+Note that isrc is on integer pixel boundaries; dst may include fractional boundaries.
+Additionally transform draw using Clip, Matrix, and optional Paint paint.
+If Paint paint is supplied, apply Color_Filter, Color_Alpha, Image_Filter, Blend_Mode, and Draw_Looper.
+If image is kAlpha_8_SkColorType, apply Shader.
+if paint contains Mask_Filter, generate mask from image bounds. If generated mask extends
+beyond image bounds, replicate image edge colors, just as Shader made from 
+SkShader::MakeBitmapShader with SkShader::kClamp_TileMode set replicates the image's edge
+color when it samples outside of its bounds. 
+cons set to kStrict_SrcRectConstraint limits Paint Filter_Quality to sample within src;
+set to kFast_SrcRectConstraint allows sampling outside to improve performance.
+
+#Param  image      Image containing pixels, dimensions, and format. ##
+#Param  isrc       Source IRect of image to draw from. ##
+#Param  dst        Destination Rect of image to draw to. ##
+#Param  paint      Paint containing Blend_Mode, Color_Filter, Image_Filter, and so on; or nullptr. ##
+#Param  constraint Filter strictly within src or draw faster. ##
+
+#Example
+#Height 64
+void draw(SkCanvas* canvas) {

+    uint32_t pixels[][2] = { { 0x00000000, 0x55555555},

+                             { 0xAAAAAAAA, 0xFFFFFFFF} };

+    SkBitmap bitmap;

+    bitmap.installPixels(SkImageInfo::MakeN32Premul(2, 2), 

+            (void*) pixels, sizeof(pixels[0]));

+    sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);

+    SkPaint paint;

+    canvas->scale(4, 4);

+    for (auto color : { SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN } ) {

+        paint.setColorFilter(SkColorFilter::MakeModeFilter(color, SkBlendMode::kPlus));

+        canvas->drawImageRect(image, SkIRect::MakeWH(2, 2), SkRect::MakeWH(8, 8), &paint);

+        canvas->translate(8, 0);

+    }

+}
+##
+
+#ToDo incomplete ##
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawImageRect(const sk_sp<SkImage>& image, const SkRect& dst, const SkPaint* paint,
+                       SrcRectConstraint constraint = kStrict_SrcRectConstraint) 
+
+Draw Image image, scaled and translated to fill Rect dst,
+using Clip, Matrix, and optional Paint paint.
+If Paint paint is supplied, apply Color_Filter, Color_Alpha, Image_Filter, Blend_Mode, and Draw_Looper.
+If image is kAlpha_8_SkColorType, apply Shader.
+if paint contains Mask_Filter, generate mask from image bounds. If generated mask extends
+beyond image bounds, replicate image edge colors, just as Shader made from 
+SkImage::makeShader with SkShader::kClamp_TileMode set replicates the image's edge
+color when it samples outside of its bounds. 
+constraint set to kStrict_SrcRectConstraint limits Paint Filter_Quality to sample within src;
+set to kFast_SrcRectConstraint allows sampling outside to improve performance.
+
+#Param  image      Image containing pixels, dimensions, and format. ##
+#Param  dst        Destination Rect of image to draw to. ##
+#Param  paint      Paint containing Blend_Mode, Color_Filter, Image_Filter, and so on; or nullptr. ##
+#Param  constraint Filter strictly within src or draw faster. ##
+
+#Example
+#Height 64
+void draw(SkCanvas* canvas) {

+    uint32_t pixels[][2] = { { 0x00000000, 0x55550000},

+                             { 0xAAAA0000, 0xFFFF0000} };

+    SkBitmap bitmap;

+    bitmap.installPixels(SkImageInfo::MakeN32Premul(2, 2), 

+            (void*) pixels, sizeof(pixels[0]));

+    sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);

+    SkPaint paint;

+    canvas->scale(4, 4);

+    for (auto color : { SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN } ) {

+        paint.setColorFilter(SkColorFilter::MakeModeFilter(color, SkBlendMode::kPlus));

+        canvas->drawImageRect(image, SkRect::MakeWH(8, 8), &paint);

+        canvas->translate(8, 0);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
+                       const SkPaint* paint = nullptr)
+
+Draw Image image stretched differentially to fit into Rect dst.
+IRect center divides the image into nine sections: four sides, four corners, and the center.
+corners are unscaled or scaled down proportionately if their sides are larger than dst;
+center and four sides are scaled to fit remaining space, if any.
+Additionally transform draw using Clip, Matrix, and optional Paint paint.
+If paint is supplied, apply Color_Filter, Color_Alpha, Image_Filter, Blend_Mode, and Draw_Looper.
+If image is kAlpha_8_SkColorType, apply Shader.
+if paint contains Mask_Filter, generate mask from image bounds. If generated mask extends
+beyond image bounds, replicate image edge colors, just as Shader made from 
+SkShader::MakeBitmapShader with SkShader::kClamp_TileMode set replicates the image's edge
+color when it samples outside of its bounds. 
+
+#Param  image      Image containing pixels, dimensions, and format. ##
+#Param  center     IRect edge of image corners and sides. ##
+#Param  dst        Destination Rect of image to draw to. ##
+#Param  paint      Paint containing Blend_Mode, Color_Filter, Image_Filter, and so on; or nullptr. ##
+
+#Example
+#Height 128
+#Description
+    The leftmost image is smaller than center; only corners are drawn, all scaled to fit.
+    The second image equals the size of center; only corners are drawn, unscaled.
+    The remaining images are larger than center. All corners draw unscaled. The sides
+    and center are scaled if needed to take up the remaining space.
+##
+void draw(SkCanvas* canvas) {

+    SkIRect center = { 20, 10, 50, 40 };

+    SkBitmap bitmap;

+    bitmap.allocPixels(SkImageInfo::MakeN32Premul(60, 60));

+    SkCanvas bitCanvas(bitmap);

+    SkPaint paint;

+    SkColor gray = 0xFF000000;

+    int left = 0;

+    for (auto right: { center.fLeft, center.fRight, bitmap.width() } ) {

+        int top = 0;

+        for (auto bottom: { center.fTop, center.fBottom, bitmap.height() } ) {

+            paint.setColor(gray);

+            bitCanvas.drawIRect(SkIRect::MakeLTRB(left, top, right, bottom), paint);

+            gray += 0x001f1f1f;

+            top = bottom;

+        }

+        left = right; 

+    }

+    sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);

+    SkImage* imagePtr = image.get();

+    for (auto dest: { 20, 30, 40, 60, 90 } ) {

+        canvas->drawImageNine(imagePtr, center, SkRect::MakeWH(dest, dest), nullptr);

+        canvas->translate(dest + 4, 0);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawImageNine(const sk_sp<SkImage>& image, const SkIRect& center, const SkRect& dst,
+                       const SkPaint* paint = nullptr) 
+
+Draw Image image stretched differentially to fit into Rect dst.
+IRect center divides the image into nine sections: four sides, four corners, and the center.
+corners are unscaled or scaled down proportionately if their sides are larger than dst;
+center and four sides are scaled to fit remaining space, if any.
+Additionally transform draw using Clip, Matrix, and optional Paint paint.
+If Paint paint is supplied, apply Color_Filter, Color_Alpha, Image_Filter, Blend_Mode, and Draw_Looper.
+If image is kAlpha_8_SkColorType, apply Shader.
+if paint contains Mask_Filter, generate mask from image bounds. If generated mask extends
+beyond image bounds, replicate image edge colors, just as Shader made from 
+SkShader::MakeBitmapShader with SkShader::kClamp_TileMode set replicates the image's edge
+color when it samples outside of its bounds. 
+
+#Param  image      Image containing pixels, dimensions, and format. ##
+#Param  center     IRect edge of image corners and sides. ##
+#Param  dst        Destination Rect of image to draw to. ##
+#Param  paint      Paint containing Blend_Mode, Color_Filter, Image_Filter, and so on; or nullptr. ##
+
+#Example
+#Height 128
+#Description
+    The two leftmost images has four corners and sides to the left and right of center.
+    The leftmost image scales the width of corners proportionately to fit.
+    The third and fourth image corners are unscaled; the sides and center are scaled to 
+    fill the remaining space.
+    The rightmost image has four corners scaled vertically to fit, and uses sides above
+    and below center to fill the remaining space.
+##
+void draw(SkCanvas* canvas) {

+    SkIRect center = { 20, 10, 50, 40 };

+    SkBitmap bitmap;

+    bitmap.allocPixels(SkImageInfo::MakeN32Premul(60, 60));

+    SkCanvas bitCanvas(bitmap);

+    SkPaint paint;

+    SkColor gray = 0xFF000000;

+    int left = 0;

+    for (auto right: { center.fLeft, center.fRight, bitmap.width() } ) {

+        int top = 0;

+        for (auto bottom: { center.fTop, center.fBottom, bitmap.height() } ) {

+            paint.setColor(gray);

+            bitCanvas.drawIRect(SkIRect::MakeLTRB(left, top, right, bottom), paint);

+            gray += 0x001f1f1f;

+            top = bottom;

+        }

+        left = right; 

+    }

+    sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);

+    for (auto dest: { 20, 30, 40, 60, 90 } ) {

+        canvas->drawImageNine(image, center, SkRect::MakeWH(dest, 110 - dest), nullptr);

+        canvas->translate(dest + 4, 0);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+                    const SkPaint* paint = NULL)
+
+Draw Bitmap bitmap, with its top-left corner at (left, top),
+using Clip, Matrix, and optional Paint paint.
+If paint is supplied, apply Color_Filter, Color_Alpha, Image_Filter, Blend_Mode, and Draw_Looper.
+If bitmap is kAlpha_8_SkColorType, apply Shader.
+if paint contains Mask_Filter, generate mask from bitmap bounds. If generated mask extends
+beyond bitmap bounds, replicate bitmap edge colors, just as Shader made from
+SkShader::MakeBitmapShader with SkShader::kClamp_TileMode set replicates the bitmap's edge
+color when it samples outside of its bounds. 
+
+#Param  bitmap   Bitmap containing pixels, dimensions, and format. ##
+#Param  left     Left side of bitmap. ##
+#Param  top      Top side of bitmap. ##
+#Param  paint    Paint containing Blend_Mode, Color_Filter, Image_Filter, and so on; or nullptr. ##
+
+#Example
+#Height 64
+void draw(SkCanvas* canvas) {

+    uint8_t pixels[][8] = { { 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00},

+                            { 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00},

+                            { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00},

+                            { 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF},

+                            { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},

+                            { 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00},

+                            { 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00},

+                            { 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF} };

+    SkBitmap bitmap;

+    bitmap.installPixels(SkImageInfo::MakeA8(8, 8), 

+            (void*) pixels, sizeof(pixels[0]));

+    SkPaint paint;

+    canvas->scale(4, 4);

+    for (auto color : { SK_ColorRED, SK_ColorBLUE, 0xFF007F00} ) {

+        paint.setColor(color);

+        canvas->drawBitmap(bitmap, 0, 0, &paint);

+        canvas->translate(12, 0);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
+                        const SkPaint* paint, SrcRectConstraint constraint = kStrict_SrcRectConstraint)
+
+Draw Rect src of Bitmap bitmap, scaled and translated to fill Rect dst.
+Additionally transform draw using Clip, Matrix, and optional Paint paint.
+If paint is supplied, apply Color_Filter, Color_Alpha, Image_Filter, Blend_Mode, and Draw_Looper.
+If bitmap is kAlpha_8_SkColorType, apply Shader.
+if paint contains Mask_Filter, generate mask from bitmap bounds. If generated mask extends
+beyond bitmap bounds, replicate bitmap edge colors, just as Shader made from 
+SkShader::MakeBitmapShader with SkShader::kClamp_TileMode set replicates the bitmap's edge
+color when it samples outside of its bounds. 
+constraint set to kStrict_SrcRectConstraint limits Paint Filter_Quality to sample within src;
+set to kFast_SrcRectConstraint allows sampling outside to improve performance.
+
+#Param  bitmap   Bitmap containing pixels, dimensions, and format. ##
+#Param  src      Source Rect of image to draw from. ##
+#Param  dst      Destination Rect of image to draw to. ##
+#Param  paint    Paint containing Blend_Mode, Color_Filter, Image_Filter, and so on; or nullptr. ##
+#Param  constraint Filter strictly within src or draw faster. ##
+
+#Example
+#Height 64
+void draw(SkCanvas* canvas) {

+    uint8_t pixels[][8] = { { 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00},

+                            { 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00},

+                            { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00},

+                            { 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF},

+                            { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},

+                            { 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00},

+                            { 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00},

+                            { 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00} };

+    SkBitmap bitmap;

+    bitmap.installPixels(SkImageInfo::MakeA8(8, 8), 

+            (void*) pixels, sizeof(pixels[0]));

+    SkPaint paint;

+    paint.setMaskFilter(SkBlurMaskFilter::Make(kSolid_SkBlurStyle, 6));

+    for (auto color : { SK_ColorRED, SK_ColorBLUE, 0xFF007F00} ) {

+        paint.setColor(color);

+        canvas->drawBitmapRect(bitmap, SkRect::MakeWH(8, 8), SkRect::MakeWH(32, 32), &paint);

+        canvas->translate(48, 0);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
+                        const SkPaint* paint, SrcRectConstraint constraint = kStrict_SrcRectConstraint)
+
+Draw IRect isrc of Bitmap bitmap, scaled and translated to fill Rect dst.
+Note that isrc is on integer pixel boundaries; dst may include fractional boundaries.
+Additionally transform draw using Clip, Matrix, and optional Paint paint.
+If paint is supplied, apply Color_Filter, Color_Alpha, Image_Filter, Blend_Mode, and Draw_Looper.
+If bitmap is kAlpha_8_SkColorType, apply Shader.
+if paint contains Mask_Filter, generate mask from bitmap bounds. If generated mask extends
+beyond bitmap bounds, replicate bitmap edge colors, just as Shader made from 
+SkShader::MakeBitmapShader with SkShader::kClamp_TileMode set replicates the bitmap's edge
+color when it samples outside of its bounds. 
+constraint set to kStrict_SrcRectConstraint limits Paint Filter_Quality to sample within src;
+set to kFast_SrcRectConstraint allows sampling outside to improve performance.
+
+#Param  bitmap   Bitmap containing pixels, dimensions, and format. ##
+#Param  isrc     Source IRect of image to draw from. ##
+#Param  dst      Destination Rect of image to draw to. ##
+#Param  paint    Paint containing Blend_Mode, Color_Filter, Image_Filter, and so on; or nullptr. ##
+#Param  constraint Filter strictly within src or draw faster. ##
+
+#Example
+#Height 64
+void draw(SkCanvas* canvas) {

+    uint8_t pixels[][8] = { { 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00},

+                            { 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00},

+                            { 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF},

+                            { 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF},

+                            { 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF},

+                            { 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF},

+                            { 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00},

+                            { 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00} };

+    SkBitmap bitmap;

+    bitmap.installPixels(SkImageInfo::MakeA8(8, 8), 

+            (void*) pixels, sizeof(pixels[0]));

+    SkPaint paint;

+    paint.setFilterQuality(kHigh_SkFilterQuality);

+    for (auto color : { SK_ColorRED, SK_ColorBLUE, 0xFF007F00, 0xFF7f007f} ) {

+        paint.setColor(color);

+        canvas->drawBitmapRect(bitmap, SkIRect::MakeWH(8, 8), SkRect::MakeWH(32, 32), &paint);

+        canvas->translate(48.25f, 0);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
+                        SrcRectConstraint constraint = kStrict_SrcRectConstraint)
+
+Draw Bitmap bitmap, scaled and translated to fill Rect dst.
+Note that isrc is on integer pixel boundaries; dst may include fractional boundaries.
+Additionally transform draw using Clip, Matrix, and optional Paint paint.
+If paint is supplied, apply Color_Filter, Color_Alpha, Image_Filter, Blend_Mode, and Draw_Looper.
+If bitmap is kAlpha_8_SkColorType, apply Shader.
+if paint contains Mask_Filter, generate mask from bitmap bounds. If generated mask extends
+beyond bitmap bounds, replicate bitmap edge colors, just as Shader made from 
+SkShader::MakeBitmapShader with SkShader::kClamp_TileMode set replicates the bitmap's edge
+color when it samples outside of its bounds. 
+constraint set to kStrict_SrcRectConstraint limits Paint Filter_Quality to sample within src;
+set to kFast_SrcRectConstraint allows sampling outside to improve performance.
+
+#Param  bitmap   Bitmap containing pixels, dimensions, and format. ##
+#Param  dst      Destination Rect of image to draw to. ##
+#Param  paint    Paint containing Blend_Mode, Color_Filter, Image_Filter, and so on; or nullptr. ##
+#Param  constraint Filter strictly within src or draw faster. ##
+
+#Example
+#Height 64
+void draw(SkCanvas* canvas) {

+    uint32_t pixels[][2] = { { 0x00000000, 0x55550000},

+                             { 0xAAAA0000, 0xFFFF0000} };

+    SkBitmap bitmap;

+    bitmap.installPixels(SkImageInfo::MakeN32Premul(2, 2), 

+            (void*) pixels, sizeof(pixels[0]));

+    SkPaint paint;

+    canvas->scale(4, 4);

+    for (auto color : { SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN } ) {

+        paint.setColorFilter(SkColorFilter::MakeModeFilter(color, SkBlendMode::kPlus));

+        canvas->drawBitmapRect(bitmap, SkRect::MakeWH(8, 8), &paint);

+        canvas->translate(8, 0);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
+                        const SkPaint* paint = NULL)
+
+Draw Bitmap bitmap stretched differentially to fit into Rect dst.
+IRect center divides the bitmap into nine sections: four sides, four corners, and the center.
+corners are unscaled or scaled down proportionately if their sides are larger than dst;
+center and four sides are scaled to fit remaining space, if any.
+Additionally transform draw using Clip, Matrix, and optional Paint paint.
+If Paint paint is supplied, apply Color_Filter, Color_Alpha, Image_Filter, Blend_Mode, and Draw_Looper.
+If bitmap is kAlpha_8_SkColorType, apply Shader.
+if paint contains Mask_Filter, generate mask from bitmap bounds. If generated mask extends
+beyond bitmap bounds, replicate bitmap edge colors, just as Shader made from 
+SkShader::MakeBitmapShader with SkShader::kClamp_TileMode set replicates the bitmap's edge
+color when it samples outside of its bounds. 
+
+#Param  bitmap     Bitmap containing pixels, dimensions, and format. ##
+#Param  center     IRect edge of image corners and sides. ##
+#Param  dst        Destination Rect of image to draw to. ##
+#Param  paint      Paint containing Blend_Mode, Color_Filter, Image_Filter, and so on; or nullptr. ##
+
+#Example
+#Height 128
+#Description
+    The two leftmost bitmap draws has four corners and sides to the left and right of center.
+    The leftmost bitmap draw scales the width of corners proportionately to fit.
+    The third and fourth draw corners are unscaled; the sides and center are scaled to 
+    fill the remaining space.
+    The rightmost bitmap draw has four corners scaled vertically to fit, and uses sides above
+    and below center to fill the remaining space.
+##
+void draw(SkCanvas* canvas) {

+    SkIRect center = { 20, 10, 50, 40 };

+    SkBitmap bitmap;

+    bitmap.allocPixels(SkImageInfo::MakeN32Premul(60, 60));

+    SkCanvas bitCanvas(bitmap);

+    SkPaint paint;

+    SkColor gray = 0xFF000000;

+    int left = 0;

+    for (auto right: { center.fLeft, center.fRight, bitmap.width() } ) {

+        int top = 0;

+        for (auto bottom: { center.fTop, center.fBottom, bitmap.height() } ) {

+            paint.setColor(gray);

+            bitCanvas.drawIRect(SkIRect::MakeLTRB(left, top, right, bottom), paint);

+            gray += 0x001f1f1f;

+            top = bottom;

+        }

+        left = right; 

+    }

+    for (auto dest: { 20, 30, 40, 60, 90 } ) {

+        canvas->drawBitmapNine(bitmap, center, SkRect::MakeWH(dest, 110 - dest), nullptr);

+        canvas->translate(dest + 4, 0);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+#Struct Lattice
+
+    Lattice divides Bitmap or Image into a rectangular grid.
+    Grid entries on even columns and even rows are fixed; these entries are
+    always drawn at their original size if the destination is large enough.
+    If the destination side is too small to hold the fixed entries, all fixed
+    entries are proportionately scaled down to fit.
+    The grid entries not on even columns and rows are scaled to fit the
+    remaining space, if any.
+
+#Code
+    struct Lattice {
+        enum Flags {...
+
+        const int*     fXDivs;
+        const int*     fYDivs;
+        const Flags*   fFlags;
+        int            fXCount;
+        int            fYCount;
+        const SkIRect* fBounds;
+    };
+##
+
+    #Enum Flags
+        #Code
+            enum Flags : uint8_t {
+                kTransparent_Flags = 1 << 0,
+            };
+        ##
+
+        Optional setting per rectangular grid entry to make it transparent.
+
+        #Const kTransparent_Flags 1
+            Set to skip lattice rectangle by making it transparent.
+        ##
+    ##
+
+    #Member const int*   fXDivs
+        Array of x-coordinates that divide the bitmap vertically.
+        Array entries must be unique, increasing, greater than or equal to fBounds left edge,
+        and less than fBounds right edge.
+        Set the first element to fBounds left to collapse the left column of fixed grid entries.
+    ##
+
+    #Member const int*   fYDivs
+        Array of y-coordinates that divide the bitmap horizontally.
+        Array entries must be unique, increasing, greater than or equal to fBounds top edge, 
+        and less than fBounds bottom edge.
+        Set the first element to fBounds top to collapse the top row of fixed grid entries.
+    ##
+
+    #Member const Flags*  fFlags
+        Optional array of Flags, one per rectangular grid entry:
+        array length must be (fXCount + 1) * (fYCount + 1).
+        Array entries correspond to the rectangular grid entries, ascending
+        left to right and then top to bottom.
+    ##
+
+    #Member int   fXCount
+        Number of entries in fXDivs array; one less than the number of horizontal divisions.
+    ##
+
+    #Member int   fYCount
+        Number of entries in fYDivs array; one less than the number of vertical divisions.
+    ##
+
+    #Member const SkIRect*   fBounds
+       Optional subset IRect source to draw from.
+       If nullptr, source bounds is dimensions of Bitmap or Image.
+    ##
+
+#Struct Lattice ##
+
+#Method void drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
+                           const SkPaint* paint = nullptr)
+
+Draw Bitmap bitmap stretched differentially to fit into Rect dst.
+
+Lattice lattice divides bitmap into a rectangular grid.
+Each intersection of an even-numbered row and column is fixed; like the corners
+of drawBitmapNine, fixed lattice elements never scale larger than their initial size
+and shrink proportionately when all fixed elements exceed the bitmap's dimension.
+All other grid elements scale to fill the available space, if any.
+
+Additionally transform draw using Clip, Matrix, and optional Paint paint.
+If Paint paint is supplied, apply Color_Filter, Color_Alpha, Image_Filter, Blend_Mode, and Draw_Looper.
+If bitmap is kAlpha_8_SkColorType, apply Shader.
+if paint contains Mask_Filter, generate mask from bitmap bounds. If generated mask extends
+beyond bitmap bounds, replicate bitmap edge colors, just as Shader made from 
+SkShader::MakeBitmapShader with SkShader::kClamp_TileMode set replicates the bitmap's edge
+color when it samples outside of its bounds. 
+
+#Param  bitmap     Bitmap containing pixels, dimensions, and format. ##
+#Param  lattice    Division of bitmap into fixed and variable rectangles. ##
+#Param  dst        Destination Rect of image to draw to. ##
+#Param  paint      Paint containing Blend_Mode, Color_Filter, Image_Filter, and so on; or nullptr. ##
+
+#Example
+#Height 128
+#Description
+    The two leftmost bitmap draws has four corners and sides to the left and right of center.
+    The leftmost bitmap draw scales the width of corners proportionately to fit.
+    The third and fourth draw corners are unscaled; the sides are scaled to 
+    fill the remaining space; the center is transparent.
+    The rightmost bitmap draw has four corners scaled vertically to fit, and uses sides above
+    and below center to fill the remaining space.
+##
+void draw(SkCanvas* canvas) {

+    SkIRect center = { 20, 10, 50, 40 };

+    SkBitmap bitmap;

+    bitmap.allocPixels(SkImageInfo::MakeN32Premul(60, 60));

+    SkCanvas bitCanvas(bitmap);

+    SkPaint paint;

+    SkColor gray = 0xFF000000;

+    int left = 0;

+    for (auto right: { center.fLeft, center.fRight, bitmap.width() } ) {

+        int top = 0;

+        for (auto bottom: { center.fTop, center.fBottom, bitmap.height() } ) {

+            paint.setColor(gray);

+            bitCanvas.drawIRect(SkIRect::MakeLTRB(left, top, right, bottom), paint);

+            gray += 0x001f1f1f;

+            top = bottom;

+        }

+        left = right; 

+    }

+    const int xDivs[] = { center.fLeft, center.fRight };

+    const int yDivs[] = { center.fTop, center.fBottom };

+    SkCanvas::Lattice::Flags flags[3][3];

+    memset(flags, 0, sizeof(flags));  

+    flags[1][1] = SkCanvas::Lattice::kTransparent_Flags;

+    SkCanvas::Lattice lattice = { xDivs, yDivs, flags[0], SK_ARRAY_COUNT(xDivs),

+         SK_ARRAY_COUNT(yDivs), nullptr };

+    for (auto dest: { 20, 30, 40, 60, 90 } ) {

+        canvas->drawBitmapLattice(bitmap, lattice , SkRect::MakeWH(dest, 110 - dest), nullptr);

+        canvas->translate(dest + 4, 0);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
+                          const SkPaint* paint = nullptr)
+
+Draw Image image stretched differentially to fit into Rect dst.
+
+Lattice lattice divides image into a rectangular grid.
+Each intersection of an even-numbered row and column is fixed; like the corners
+of drawImageNine, fixed lattice elements never scale larger than their initial size
+and shrink proportionately when all fixed elements exceed the bitmap's dimension.
+All other grid elements scale to fill the available space, if any.
+
+Additionally transform draw using Clip, Matrix, and optional Paint paint.
+If Paint paint is supplied, apply Color_Filter, Color_Alpha, Image_Filter, Blend_Mode, and Draw_Looper.
+If image is kAlpha_8_SkColorType, apply Shader.
+if paint contains Mask_Filter, generate mask from image bounds. If generated mask extends
+beyond image bounds, replicate image edge colors, just as Shader made from 
+SkShader::MakeBitmapShader with SkShader::kClamp_TileMode set replicates the image's edge
+color when it samples outside of its bounds. 
+
+#Param  image      Image containing pixels, dimensions, and format. ##
+#Param  lattice    Division of bitmap into fixed and variable rectangles. ##
+#Param  dst        Destination Rect of image to draw to. ##
+#Param  paint      Paint containing Blend_Mode, Color_Filter, Image_Filter, and so on; or nullptr. ##
+
+#Example
+#Height 128
+#Description
+    The leftmost image is smaller than center; only corners are drawn, all scaled to fit.
+    The second image equals the size of center; only corners are drawn, unscaled.
+    The remaining images are larger than center. All corners draw unscaled. The sides
+    are scaled if needed to take up the remaining space; the center is transparent.
+##
+void draw(SkCanvas* canvas) {

+    SkIRect center = { 20, 10, 50, 40 };

+    SkBitmap bitmap;

+    bitmap.allocPixels(SkImageInfo::MakeN32Premul(60, 60));

+    SkCanvas bitCanvas(bitmap);

+    SkPaint paint;

+    SkColor gray = 0xFF000000;

+    int left = 0;

+    for (auto right: { center.fLeft, center.fRight, bitmap.width() } ) {

+        int top = 0;

+        for (auto bottom: { center.fTop, center.fBottom, bitmap.height() } ) {

+            paint.setColor(gray);

+            bitCanvas.drawIRect(SkIRect::MakeLTRB(left, top, right, bottom), paint);

+            gray += 0x001f1f1f;

+            top = bottom;

+        }

+        left = right; 

+    }

+    const int xDivs[] = { center.fLeft, center.fRight };

+    const int yDivs[] = { center.fTop, center.fBottom };

+    SkCanvas::Lattice::Flags flags[3][3];

+    memset(flags, 0, sizeof(flags));  

+    flags[1][1] = SkCanvas::Lattice::kTransparent_Flags;

+    SkCanvas::Lattice lattice = { xDivs, yDivs, flags[0], SK_ARRAY_COUNT(xDivs),

+         SK_ARRAY_COUNT(yDivs), nullptr };

+    sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);

+    SkImage* imagePtr = image.get();

+    for (auto dest: { 20, 30, 40, 60, 90 } ) {

+        canvas->drawImageNine(imagePtr, center, SkRect::MakeWH(dest, dest), nullptr);

+        canvas->translate(dest + 4, 0);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+#Topic Draw_Image ##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+                  const SkPaint& paint)
+
+Draw text, with origin at (x, y), using Clip, Matrix, and Paint paint.
+text's meaning depends on Paint_Text_Encoding; by default, text encoding is UTF-8.
+x and y meaning depends on Paint_Text_Align and Paint_Vertical_Text; by default text
+draws left to right, positioning the first glyph's left side bearing at x and its
+baseline at y. Text size is affected by Matrix and Paint_Text_Size. 
+
+All elements of paint: Path_Effect, Rasterizer, Mask_Filter, Shader, Color_Filter, 
+Image_Filter, and Draw_Looper; apply to text. By default, drawText draws filled 12 point black
+glyphs.
+
+#Param  text     Character code points or glyphs drawn. ##
+#Param  byteLength   Byte length of text array. ##
+#Param  x        Start of text on x-axis. ##
+#Param  y        Start of text on y-axis. ##
+#Param  paint    Text size, blend, color, and so on, used to draw. ##
+
+#Example
+#Height 200
+#Description
+    The same text is drawn varying Paint_Text_Size and varying
+    Matrix. 
+##
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    paint.setAntiAlias(true);

+    float textSizes[] = { 12, 18, 24, 36 };

+    for (auto size: textSizes ) {

+        paint.setTextSize(size);

+        canvas->drawText("Aa", 2, 10, 20, paint);

+        canvas->translate(0, size * 2);

+    }

+    paint.reset();

+    paint.setAntiAlias(true);

+    float yPos = 20;

+    for (auto size: textSizes ) {

+        float scale = size / 12.f;

+        canvas->resetMatrix();

+        canvas->translate(100, 0);

+        canvas->scale(scale, scale);

+        canvas->drawText("Aa", 2, 10 / scale, yPos / scale, paint);

+        yPos += size * 2; 

+    }

+}

+##
+
+#ToDo incomplete ##
+
+##
+
+#Method void drawString(const char* string, SkScalar x, SkScalar y, const SkPaint& paint)
+
+Draw null terminated string, with origin at (x, y), using Clip, Matrix, and Paint paint.
+string's meaning depends on Paint_Text_Encoding; by default, string encoding is UTF-8.
+Other values of Paint_Text_Encoding are unlikely to produce the desired results, since
+zero bytes may be embedded in the string.
+x and y meaning depends on Paint_Text_Align and Paint_Vertical_Text; by default string
+draws left to right, positioning the first glyph's left side bearing at x and its
+baseline at y. Text size is affected by Matrix and Paint_Text_Size. 
+
+All elements of paint: Path_Effect, Rasterizer, Mask_Filter, Shader, Color_Filter, 
+Image_Filter, and Draw_Looper; apply to string. By default, drawString draws filled 12 point black
+glyphs.
+
+#Param  string   Character code points or glyphs drawn, ending with a char value of zero. ##
+#Param  x        Start of string on x-axis. ##
+#Param  y        Start of string on y-axis. ##
+#Param  paint    Text size, blend, color, and so on, used to draw. ##
+
+#Example
+   SkPaint paint;
+   canvas->drawString("a small hello", 20, 20, paint);
+##
+
+#SeeAlso drawText
+
+##
+
+#Method void drawString(const SkString& string, SkScalar x, SkScalar y, const SkPaint& paint)
+
+Draw null terminated string, with origin at (x, y), using Clip, Matrix, and Paint paint.
+string's meaning depends on Paint_Text_Encoding; by default, string encoding is UTF-8.
+Other values of Paint_Text_Encoding are unlikely to produce the desired results, since
+zero bytes may be embedded in the string.
+x and y meaning depends on Paint_Text_Align and Paint_Vertical_Text; by default string
+draws left to right, positioning the first glyph's left side bearing at x and its
+baseline at y. Text size is affected by Matrix and Paint_Text_Size. 
+
+All elements of paint: Path_Effect, Rasterizer, Mask_Filter, Shader, Color_Filter, 
+Image_Filter, and Draw_Looper; apply to string. By default, drawString draws filled 12 point black
+glyphs.
+
+#Param  string   Character code points or glyphs drawn, ending with a char value of zero. ##
+#Param  x        Start of string on x-axis. ##
+#Param  y        Start of string on y-axis. ##
+#Param  paint    Text size, blend, color, and so on, used to draw. ##
+
+#Example
+   SkPaint paint;
+   SkString string("a small hello");
+   canvas->drawString(string, 20, 20, paint);
+##
+
+#SeeAlso drawText
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
+                     const SkPaint& paint)
+
+Draw each glyph in text with the origin in pos array, using Clip, Matrix, and Paint paint.
+The number of entries in pos array must match the number of glyphs described by byteLength of text.
+text's meaning depends on Paint_Text_Encoding; by default, text encoding is UTF-8.
+pos elements' meaning depends on Paint_Text_Align and Paint_Vertical_Text; by default each
+glyph's left side bearing is positioned at x and its
+baseline is positioned at y. Text size is affected by Matrix and Paint_Text_Size. 
+
+All elements of paint: Path_Effect, Rasterizer, Mask_Filter, Shader, Color_Filter, 
+Image_Filter, and Draw_Looper; apply to text. By default, drawPosText draws filled 12 point black
+glyphs.
+
+Layout engines such as Harfbuzz typically use drawPosText to position each glyph
+rather than using the font's advance widths.
+
+#Param  text     Character code points or glyphs drawn. ##
+#Param  byteLength   Byte length of text array. ##
+#Param  pos      Array of glyph origins. ##
+#Param  paint    Text size, blend, color, and so on, used to draw. ##
+
+#Example
+#Height 120
+void draw(SkCanvas* canvas) {

+  const char hello[] = "HeLLo!";

+  const SkPoint pos[] = { {40, 100}, {82, 95}, {115, 110}, {130, 95}, {145, 85},

+    {172, 100} };

+  SkPaint paint;

+  paint.setTextSize(60);

+  canvas->drawPosText(hello, strlen(hello), pos, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY,
+                      const SkPaint& paint)
+
+Draw each glyph in text with its (x, y) origin composed from xpos array and constY, using Clip, Matrix, and Paint paint.
+The number of entries in xpos array must match the number of glyphs described by byteLength of text.
+text's meaning depends on Paint_Text_Encoding; by default, text encoding is UTF-8.
+pos elements' meaning depends on Paint_Text_Align and Paint_Vertical_Text; by default each
+glyph's left side bearing is positioned at an xpos element and its
+baseline is positioned at constY. Text size is affected by Matrix and Paint_Text_Size. 
+
+All elements of paint: Path_Effect, Rasterizer, Mask_Filter, Shader, Color_Filter, 
+Image_Filter, and Draw_Looper; apply to text. By default, drawPosTextH draws filled 12 point black
+glyphs.
+
+Layout engines such as Harfbuzz typically use drawPosTextH to position each glyph
+rather than using the font's advance widths if all glyphs share the same baseline.
+
+#Param  text     Character code points or glyphs drawn. ##
+#Param  byteLength   Byte length of text array. ##
+#Param  xpos     Array of x positions, used to position each glyph. ##
+#Param  constY   Shared y coordinate for all of x positions. ##
+#Param  paint    Text size, blend, color, and so on, used to draw. ##
+
+#Example
+#Height 40
+    void draw(SkCanvas* canvas) {

+        SkScalar xpos[] = { 20, 40, 80, 160 };

+        SkPaint paint;

+        canvas->drawPosTextH("XXXX", 4, xpos, 20, paint);

+    }

+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawTextOnPathHV(const void* text, size_t byteLength, const SkPath& path, SkScalar hOffset,
+                          SkScalar vOffset, const SkPaint& paint)
+
+Draw text on Path path, using Clip, Matrix, and Paint paint.
+Origin of text is at distance hOffset along the path, offset by a perpendicular vector of
+length vOffset. If the path section corresponding the glyph advance is curved, the glyph
+is drawn curved to match; control points in the glyph are mapped to projected points parallel
+to the path. If the text's advance is larger than the path length, the excess text is clipped.
+
+text's meaning depends on Paint_Text_Encoding; by default, text encoding is UTF-8.
+Origin meaning depends on Paint_Text_Align and Paint_Vertical_Text; by default text
+positions the first glyph's left side bearing at origin x and its
+baseline at origin y. Text size is affected by Matrix and Paint_Text_Size. 
+
+All elements of paint: Path_Effect, Rasterizer, Mask_Filter, Shader, Color_Filter, 
+Image_Filter, and Draw_Looper; apply to text. By default, drawTextOnPathHV draws filled 12 point black
+glyphs.
+
+#Param  text         Character code points or glyphs drawn. ##
+#Param  byteLength   Byte length of text array. ##
+#Param  path         Path providing text baseline. ##
+#Param  hOffset      Distance along path to offset origin. ##
+#Param  vOffset      Offset of text above (if negative) or below (if positive) the path. ##
+#Param  paint        Text size, blend, color, and so on, used to draw. ##
+
+#Example
+    void draw(SkCanvas* canvas) { 

+        const char aero[] = "correo a" "\xC3" "\xA9" "reo";

+        const size_t len = sizeof(aero) - 1;

+        SkPath path;

+        path.addOval({43-26, 43-26, 43+26, 43+26}, SkPath::kCW_Direction, 3);

+        SkPaint paint;

+        paint.setTextSize(24);

+        for (auto offset : { 0, 10, 20 } ) {

+            canvas->drawTextOnPathHV(aero, len, path, 0, -offset, paint);

+            canvas->translate(70 + offset, 70 + offset);

+        }

+    }

+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
+                        const SkMatrix* matrix, const SkPaint& paint)
+
+Draw text on Path path, using Clip, Matrix, and Paint paint.
+Origin of text is at beginning of path offset by matrix, if provided, before it is mapped to path.
+If the path section corresponding the glyph advance is curved, the glyph
+is drawn curved to match; control points in the glyph are mapped to projected points parallel
+to the path. If the text's advance is larger than the path length, the excess text is clipped.
+
+text's meaning depends on Paint_Text_Encoding; by default, text encoding is UTF-8.
+Origin meaning depends on Paint_Text_Align and Paint_Vertical_Text; by default text
+positions the first glyph's left side bearing at origin x and its
+baseline at origin y. Text size is affected by Matrix and Paint_Text_Size. 
+
+All elements of paint: Path_Effect, Rasterizer, Mask_Filter, Shader, Color_Filter, 
+Image_Filter, and Draw_Looper; apply to text. By default, drawTextOnPath draws filled 12 point black
+glyphs.
+
+#Param  text         Character code points or glyphs drawn. ##
+#Param  byteLength   Byte length of text array. ##
+#Param  path         Path providing text baseline. ##
+#Param  matrix       Optional transform of glyphs before mapping to path; or nullptr. ##
+#Param  paint        Text size, blend, color, and so on, used to draw. ##
+
+#Example
+    void draw(SkCanvas* canvas) { 

+        const char roller[] = "rollercoaster";

+        const size_t len = sizeof(roller) - 1;

+        SkPath path;

+        path.cubicTo(40, -80, 120, 80, 160, -40);

+        SkPaint paint;

+        paint.setTextSize(32);

+        paint.setStyle(SkPaint::kStroke_Style);

+        SkMatrix matrix;

+        matrix.setIdentity();

+        for (int i = 0; i < 3; ++i) {

+            canvas->translate(25, 60);

+            canvas->drawPath(path, paint);

+            canvas->drawTextOnPath(roller, len, path, &matrix, paint);

+            matrix.preTranslate(0, 10);

+        }

+    }

+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
+                         const SkRect* cullRect, const SkPaint& paint)
+
+Draw text, transforming each glyph by the corresponding SkRSXform,
+using Clip, Matrix, and Paint paint.
+RSXform array specifies a separate square scale, rotation, and translation for 
+each glyph.
+Optional Rect cullRect is a conservative bounds of text,
+taking into account RSXform and paint. If cullrect is outside of Clip, canvas can
+skip drawing.
+
+All elements of paint: Path_Effect, Rasterizer, Mask_Filter, Shader, Color_Filter, 
+Image_Filter, and Draw_Looper; apply to text. By default, drawTextRSXform draws filled 12 point black
+glyphs.
+
+#Param  text         Character code points or glyphs drawn. ##
+#Param  byteLength   Byte length of text array. ##
+#Param  xform        RSXform rotates, scales, and translates each glyph individually. ##
+#Param  cullRect     Rect bounds of text for efficient clipping; or nullptr. ##
+#Param  paint        Text size, blend, color, and so on, used to draw. ##
+
+#Example
+void draw(SkCanvas* canvas) {  

+    const int iterations = 26;

+    SkRSXform transforms[iterations];

+    char alphabet[iterations];

+    SkScalar angle = 0;

+    SkScalar scale = 1;

+    for (size_t i = 0; i < SK_ARRAY_COUNT(transforms); ++i) {

+        const SkScalar s = SkScalarSin(angle) * scale;

+        const SkScalar c = SkScalarCos(angle) * scale;

+        transforms[i] = SkRSXform::Make(-c, -s, -s * 16, c * 16);

+        angle += .45;

+        scale += .2;

+        alphabet[i] = 'A' + i;

+    }

+    SkPaint paint;

+    paint.setTextAlign(SkPaint::kCenter_Align);

+    canvas->translate(110, 138);

+    canvas->drawTextRSXform(alphabet, sizeof(alphabet), transforms, nullptr, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint)
+
+Draw Text_Blob blob at (x, y), using Clip, Matrix, and Paint paint.
+blob contains glyphs, their positions, and paint attributes specific to text:
+Typeface, Paint_Text_Size, Paint_Text_Scale_X, Paint_Text_Skew_X, Paint_Text_Align,
+Paint_Hinting, Anti-alias, Paint_Fake_Bold, Font_Embedded_Bitmaps, Full_Hinting_Spacing, 
+LCD_Text, Linear_Text, Subpixel_Text, and Paint_Vertical_Text.
+
+Elements of paint: Path_Effect, Rasterizer, Mask_Filter, Shader, Color_Filter, 
+Image_Filter, and Draw_Looper; apply to blob.
+
+#Param  blob     Glyphs, positions, and their paints' text size, typeface, and so on. ##
+#Param  x        Horizontal offset applied to blob. ##
+#Param  y        Vertical offset applied to blob. ##
+#Param  paint    Blend, color, stroking, and so on, used to draw. ##
+
+#Example
+#Height 120
+    void draw(SkCanvas* canvas) {

+        SkTextBlobBuilder textBlobBuilder;

+        const char bunny[] = "/(^x^)\\";

+        const int len = sizeof(bunny) - 1;

+        uint16_t glyphs[len];

+        SkPaint paint;

+        paint.textToGlyphs(bunny, len, glyphs);

+        int runs[] = { 3, 1, 3 };

+        SkPoint textPos = { 20, 100 };

+        int glyphIndex = 0;

+        for (auto runLen : runs) {

+            paint.setTextSize(1 == runLen ? 20 : 50);

+            const SkTextBlobBuilder::RunBuffer& run = 

+                    textBlobBuilder.allocRun(paint, runLen, textPos.fX, textPos.fY);

+            memcpy(run.glyphs, &glyphs[glyphIndex], sizeof(glyphs[0]) * runLen);

+            textPos.fX += paint.measureText(&bunny[glyphIndex], runLen, nullptr);

+            glyphIndex += runLen;

+        }

+        sk_sp<const SkTextBlob> blob = textBlobBuilder.make();

+        paint.reset();

+        canvas->drawTextBlob(blob.get(), 0, 0, paint);

+    }
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawTextBlob(const sk_sp<SkTextBlob>& blob, SkScalar x, SkScalar y, const SkPaint& paint) 
+
+Draw Text_Blob blob at (x, y), using Clip, Matrix, and Paint paint.
+blob contains glyphs, their positions, and paint attributes specific to text:
+Typeface, Paint_Text_Size, Paint_Text_Scale_X, Paint_Text_Skew_X, Paint_Text_Align,
+Paint_Hinting, Anti-alias, Paint_Fake_Bold, Font_Embedded_Bitmaps, Full_Hinting_Spacing, 
+LCD_Text, Linear_Text, Subpixel_Text, and Paint_Vertical_Text.
+
+Elements of paint: Path_Effect, Rasterizer, Mask_Filter, Shader, Color_Filter, 
+Image_Filter, and Draw_Looper; apply to blob.
+
+#Param  blob     Glyphs, positions, and their paints' text size, typeface, and so on. ##
+#Param  x        Horizontal offset applied to blob. ##
+#Param  y        Vertical offset applied to blob. ##
+#Param  paint    Blend, color, stroking, and so on, used to draw. ##
+
+#Example
+#Height 120
+#Description
+Paint attributes unrelated to text, like color, have no effect on paint in allocated Text_Blob.
+Paint attributes related to text, like text size, have no effect on paint passed to drawTextBlob.
+##
+    void draw(SkCanvas* canvas) {

+        SkTextBlobBuilder textBlobBuilder;

+        SkPaint paint;

+        paint.setTextSize(50);

+        paint.setColor(SK_ColorRED);

+        const SkTextBlobBuilder::RunBuffer& run = 

+                textBlobBuilder.allocRun(paint, 1, 20, 100);

+        run.glyphs[0] = 20;

+        sk_sp<const SkTextBlob> blob = textBlobBuilder.make();

+        paint.setTextSize(10);

+        paint.setColor(SK_ColorBLUE);

+        canvas->drawTextBlob(blob.get(), 0, 0, paint);

+    }

+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawPicture(const SkPicture* picture) 
+
+Draw Picture picture, using Clip and Matrix.
+Clip and Matrix are unchanged by picture contents, as if
+save() was called before and restore() was called after drawPicture.
+
+Picture records a series of draw commands for later playback.
+
+#Param  picture  Recorded drawing commands to play. ##
+
+#Example
+void draw(SkCanvas* canvas) {  

+    SkPictureRecorder recorder;

+    SkCanvas* recordingCanvas = recorder.beginRecording(50, 50);

+    for (auto color : { SK_ColorRED, SK_ColorBLUE, 0xff007f00 } ) {

+        SkPaint paint;

+        paint.setColor(color);

+        recordingCanvas->drawRect({10, 10, 30, 40}, paint);

+        recordingCanvas->translate(10, 10);

+        recordingCanvas->scale(1.2f, 1.4f);

+    }

+    sk_sp<SkPicture> playback = recorder.finishRecordingAsPicture();

+    const SkPicture* playbackPtr = playback.get();

+    canvas->drawPicture(playback);

+    canvas->scale(2, 2);

+    canvas->translate(50, 0);

+    canvas->drawPicture(playback);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawPicture(const sk_sp<SkPicture>& picture) 
+
+Draw Picture picture, using Clip and Matrix.
+Clip and Matrix are unchanged by picture contents, as if
+save() was called before and restore() was called after drawPicture.
+
+Picture records a series of draw commands for later playback.
+
+#Param  picture  Recorded drawing commands to play. ##
+
+#Example
+void draw(SkCanvas* canvas) {  

+    SkPictureRecorder recorder;

+    SkCanvas* recordingCanvas = recorder.beginRecording(50, 50);

+    for (auto color : { SK_ColorRED, SK_ColorBLUE, 0xff007f00 } ) {

+        SkPaint paint;

+        paint.setColor(color);

+        recordingCanvas->drawRect({10, 10, 30, 40}, paint);

+        recordingCanvas->translate(10, 10);

+        recordingCanvas->scale(1.2f, 1.4f);

+    }

+    sk_sp<SkPicture> playback = recorder.finishRecordingAsPicture();

+    canvas->drawPicture(playback);

+    canvas->scale(2, 2);

+    canvas->translate(50, 0);

+    canvas->drawPicture(playback);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint)
+
+Draw Picture picture, using Clip and Matrix;
+transforming picture with Matrix matrix, if provided;
+and use Paint paint Color_Alpha, Color_Filter, Image_Filter, and Blend_Mode, if provided.
+
+matrix transformation is equivalent to: save(), concat(), drawPicture, restore().
+paint use is equivalent to: saveLayer, drawPicture, restore().
+
+#Param  picture  Recorded drawing commands to play. ##
+#Param  matrix   Optional Matrix to rotate, scale, translate, and so on; or nullptr. ##
+#Param  paint    Optional Paint to apply transparency, filtering, and so on; or nullptr. ##
+
+#Example
+void draw(SkCanvas* canvas) {  

+    SkPaint paint;

+    SkPictureRecorder recorder;

+    SkCanvas* recordingCanvas = recorder.beginRecording(50, 50);

+    for (auto color : { SK_ColorRED, SK_ColorBLUE, 0xff007f00 } ) {

+        paint.setColor(color);

+        recordingCanvas->drawRect({10, 10, 30, 40}, paint);

+        recordingCanvas->translate(10, 10);

+        recordingCanvas->scale(1.2f, 1.4f);

+    }

+    sk_sp<SkPicture> playback = recorder.finishRecordingAsPicture();

+    const SkPicture* playbackPtr = playback.get();

+    SkMatrix matrix;

+    matrix.reset();

+    for (auto alpha : { 70, 140, 210 } ) {

+    paint.setAlpha(alpha);

+    canvas->drawPicture(playbackPtr, &matrix, &paint);

+    matrix.preTranslate(70, 70);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawPicture(const sk_sp<SkPicture>& picture, const SkMatrix* matrix, const SkPaint* paint) 
+
+Draw Picture picture, using Clip and Matrix;
+transforming picture with Matrix matrix, if provided;
+and use Paint paint Color_Alpha, Color_Filter, Image_Filter, and Blend_Mode, if provided.
+
+matrix transformation is equivalent to: save(), concat(), drawPicture, restore().
+paint use is equivalent to: saveLayer, drawPicture, restore().
+
+#Param  picture  Recorded drawing commands to play. ##
+#Param  matrix   Optional Matrix to rotate, scale, translate, and so on; or nullptr. ##
+#Param  paint    Optional Paint to apply transparency, filtering, and so on; or nullptr. ##
+
+#Example
+void draw(SkCanvas* canvas) {  

+    SkPaint paint;

+    SkPictureRecorder recorder;

+    SkCanvas* recordingCanvas = recorder.beginRecording(50, 50);

+    for (auto color : { SK_ColorRED, SK_ColorBLUE, 0xff007f00 } ) {

+        paint.setColor(color);

+        recordingCanvas->drawRect({10, 10, 30, 40}, paint);

+        recordingCanvas->translate(10, 10);

+        recordingCanvas->scale(1.2f, 1.4f);

+    }

+    sk_sp<SkPicture> playback = recorder.finishRecordingAsPicture();

+    SkMatrix matrix;

+    matrix.reset();

+    for (auto alpha : { 70, 140, 210 } ) {

+    paint.setAlpha(alpha);

+    canvas->drawPicture(playback, &matrix, &paint);

+    matrix.preTranslate(70, 70);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint)
+
+Draw Vertices vertices, a triangle mesh, using Clip and Matrix.
+If Vertices_Texs and Vertices_Colors are defined in vertices, and Paint paint contains Shader,
+Blend_Mode mode combines Vertices_Colors with Shader.
+
+#Param  vertices  The triangle mesh to draw. ##
+#Param  mode      Combines Vertices_Colors with Shader, if both are present. ##
+#Param  paint     Specifies the Shader, used as Vertices texture, if present. ##
+
+#Example
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    SkPoint points[] = { { 0, 0 }, { 250, 0 }, { 100, 100 }, { 0, 250 } };

+    SkColor colors[] = { SK_ColorRED, SK_ColorBLUE, SK_ColorYELLOW, SK_ColorCYAN };

+    auto vertices = SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode,

+            SK_ARRAY_COUNT(points), points, nullptr, colors);

+    canvas->drawVertices(vertices.get(), SkBlendMode::kSrc, paint);

+}

+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode, const SkPaint& paint)
+
+Draw Vertices vertices, a triangle mesh, using Clip and Matrix.
+If Vertices_Texs and Vertices_Colors are defined in vertices, and Paint paint contains Shader,
+Blend_Mode mode combines Vertices_Colors with Shader.
+
+#Param  vertices  The triangle mesh to draw. ##
+#Param  mode      Combines Vertices_Colors with Shader, if both are present. ##
+#Param  paint     Specifies the Shader, used as Vertices texture, if present. ##
+
+#Example
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    SkPoint points[] = { { 0, 0 }, { 250, 0 }, { 100, 100 }, { 0, 250 } };

+    SkPoint texs[] = { { 0, 0 }, { 0, 250 }, { 250, 250 }, { 250, 0 } };

+    SkColor colors[] = { SK_ColorRED, SK_ColorBLUE, SK_ColorYELLOW, SK_ColorCYAN };

+    paint.setShader(SkGradientShader::MakeLinear(points, colors, nullptr, 4,

+            SkShader::kClamp_TileMode));

+    auto vertices = SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode,

+            SK_ARRAY_COUNT(points), points, texs, colors);

+    canvas->drawVertices(vertices.get(), SkBlendMode::kDarken, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawPatch(const SkPoint cubics[12], const SkColor colors[4],
+                   const SkPoint texCoords[4], SkBlendMode mode, const SkPaint& paint)
+
+Draw a cubic Coons patch: the interpolation of four cubics with shared corners, 
+associating a color, and optionally a texture coordinate, with each corner.
+
+#ToDo can patch use image filter? ##
+
+The Coons patch uses Clip and Matrix, Paint paint's Shader, Color_Filter, Color_Alpha,
+Image_Filter, and Blend_Mode. If Shader is provided it is treated as the Coons
+patch texture; Blend_Mode mode combines Color colors and Shader if both are provided.
+
+#Param cubics     Point array cubics specifying the four cubics starting at the top left corner, 
+in clockwise order, sharing every fourth point. The last cubic ends at the first point. ##
+#Param colors     Color array color associating colors with corners in top left, top right, bottom right,
+bottom left order. ##
+#Param texCoords  Point array texCoords mapping Shader as texture to corners in same order, if paint
+contains Shader; or nullptr. ##
+#Param mode       Blend_Mode for colors and Shader if present. ##
+#Param paint      Shader, Color_Filter, Blend_Mode, used to draw. ##
+
+#Example
+#Image 5
+void draw(SkCanvas* canvas) {

+    // SkBitmap source = cmbkygk;

+    SkPaint paint;

+    paint.setFilterQuality(kLow_SkFilterQuality);

+    paint.setAntiAlias(true);

+    SkPoint cubics[] = { { 3, 1 },    { 4, 2 }, { 5, 1 },    { 7, 3 },

+                      /* { 7, 3 }, */ { 6, 4 }, { 7, 5 },    { 5, 7 },

+                      /* { 5, 7 }, */ { 4, 6 }, { 3, 7 },    { 1, 5 },

+                      /* { 1, 5 }, */ { 2, 4 }, { 1, 3 }, /* { 3, 1 } */ };

+    SkColor colors[] = { 0xbfff0000, 0xbf0000ff, 0xbfff00ff, 0xbf00ffff };

+    SkPoint texCoords[] = { { -30, -30 }, { 162, -30}, { 162, 162}, { -30, 162} };

+    paint.setShader(SkShader::MakeBitmapShader(source, SkShader::kClamp_TileMode,

+                                                       SkShader::kClamp_TileMode, nullptr));

+    canvas->scale(15, 15);

+    for (auto blend : { SkBlendMode::kSrcOver, SkBlendMode::kModulate, SkBlendMode::kXor } ) {

+        canvas->drawPatch(cubics, colors, texCoords, blend, paint);

+        canvas->translate(4, 4);

+    }

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawPatch(const SkPoint cubics[12], const SkColor colors[4],
+                   const SkPoint texCoords[4], const SkPaint& paint) 
+
+Draw a cubic Coons patch: the interpolation of four cubics with shared corners, 
+associating a color, a texture coordinate, or both, with each corner.
+
+The Coons patch uses Clip and Matrix, Paint paint's Shader, Color_Filter, Color_Alpha,
+Image_Filter, (?) and Blend_Mode. If Shader is provided it is treated as the Coons
+patch texture.
+
+#Param cubics     Point array cubics specifying the four cubics starting at the top left corner, 
+in clockwise order, sharing every fourth point. The last cubic ends at the first point. ##
+#Param colors     Color array color associating colors with corners in top left, top right, bottom right,
+bottom left order; or nullptr. ##
+#Param texCoords  Point array texCoords mapping Shader as texture to corners in same order, if paint
+contains Shader; or nullptr. ##
+#Param paint      Shader, Color_Filter, Blend_Mode, used to draw. ##
+
+#Example
+void draw(SkCanvas* canvas) {

+    SkPaint paint;

+    paint.setAntiAlias(true);

+    SkPoint cubics[] = { { 3, 1 },    { 4, 2 }, { 5, 1 },    { 7, 3 },

+                      /* { 7, 3 }, */ { 6, 4 }, { 7, 5 },    { 5, 7 },

+                      /* { 5, 7 }, */ { 4, 6 }, { 3, 7 },    { 1, 5 },

+                      /* { 1, 5 }, */ { 2, 4 }, { 1, 3 }, /* { 3, 1 } */ };

+    SkColor colors[] = { SK_ColorRED, SK_ColorBLUE, SK_ColorYELLOW, SK_ColorCYAN };

+    canvas->scale(30, 30);

+    canvas->drawPatch(cubics, colors, nullptr, paint);

+    SkPoint text[] = { {3,0.9f}, {4,2.5f}, {5,0.9f}, {7.5f,3.2f}, {5.5f,4.2f},

+            {7.5f,5.2f}, {5,7.5f}, {4,5.9f}, {3,7.5f}, {0.5f,5.2f}, {2.5f,4.2f},

+            {0.5f,3.2f} };

+    paint.setTextSize(18.f / 30);

+    paint.setTextAlign(SkPaint::kCenter_Align);

+    for (int i = 0; i< 10; ++i) {

+       char digit = '0' + i;

+       canvas->drawText(&digit, 1, text[i].fX, text[i].fY, paint);

+    }

+    canvas->drawString("10", text[10].fX, text[10].fY, paint);

+    canvas->drawString("11", text[11].fX, text[11].fY, paint);

+    paint.setStyle(SkPaint::kStroke_Style);

+    canvas->drawPoints(SkCanvas::kPolygon_PointMode, 12, cubics, paint);

+    canvas->drawLine(cubics[11].fX, cubics[11].fY, cubics[0].fX, cubics[0].fY, paint);

+}
+##
+
+#Example
+#Image 6
+void draw(SkCanvas* canvas) {

+    // SkBitmap source = checkerboard;

+    SkPaint paint;

+    paint.setFilterQuality(kLow_SkFilterQuality);

+    paint.setAntiAlias(true);

+    SkPoint cubics[] = { { 3, 1 },    { 4, 2 }, { 5, 1 },    { 7, 3 },

+                      /* { 7, 3 }, */ { 6, 4 }, { 7, 5 },    { 5, 7 },

+                      /* { 5, 7 }, */ { 4, 6 }, { 3, 7 },    { 1, 5 },

+                      /* { 1, 5 }, */ { 2, 4 }, { 1, 3 }, /* { 3, 1 } */ };

+    SkPoint texCoords[] = { { 0, 0 }, { 0, 62}, { 62, 62}, { 62, 0 } };

+    paint.setShader(SkShader::MakeBitmapShader(source, SkShader::kClamp_TileMode,

+                                                       SkShader::kClamp_TileMode, nullptr));

+    canvas->scale(30, 30);

+    canvas->drawPatch(cubics, nullptr, texCoords, paint);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
+                   const SkColor colors[], int count, SkBlendMode mode, const SkRect* cullRect,
+                   const SkPaint* paint)
+
+Draw a set of sprites from atlas, using Clip, Matrix, and optional Paint paint.
+paint uses Anti-alias, Color_Alpha, Color_Filter, Image_Filter, and Blend_Mode to draw, if present.
+For each entry in the array, Rect tex locates sprite in atlas, and RSXform xform transforms it
+into destination space.
+xform, text, and colors if present, must contain count entries.
+Optional colors is applied for each sprite using Blend_Mode.
+Optional cullRect is a conservative bounds of all transformed sprites. 
+If cullrect is outside of Clip, canvas can skip drawing.
+
+#Param atlas  Image containing sprites. ##
+#Param xform  RSXform mappings for sprites in atlas. ##
+#Param tex    Rect locations of sprites in atlas. ##
+#Param colors  Color, one per sprite, blended with sprite using Blend_Mode; or nullptr. ##
+#Param count  Number of sprites to draw. ##
+#Param mode   Blend_Mode combining colors and sprites. ##
+#Param cullRect  Rect bounds of transformed sprites for efficient clipping; or nullptr. ##
+#Param paint  Paint Color_Filter, Image_Filter, Blend_Mode, and so on; or nullptr. ##
+
+#Example
+#Image 3
+void draw(SkCanvas* canvas) {

+  // SkBitmap source = mandrill;

+  SkRSXform xforms[] = { { .5f, 0, 0, 0 }, {0, .5f, 200, 100 } };

+  SkRect tex[] = { { 0, 0, 250, 250 }, { 0, 0, 250, 250 } };

+  SkColor colors[] = { 0x7f55aa00, 0x7f3333bf };

+  const SkImage* imagePtr = image.get();

+  canvas->drawAtlas(imagePtr, xforms, tex, colors, 2, SkBlendMode::kSrcOver, nullptr, nullptr);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawAtlas(const sk_sp<SkImage>& atlas, const SkRSXform xform[], const SkRect tex[],
+                   const SkColor colors[], int count, SkBlendMode mode, const SkRect* cullRect,
+                   const SkPaint* paint) 
+
+Draw a set of sprites from atlas, using Clip, Matrix, and optional Paint paint.
+paint uses Anti-alias, Color_Alpha, Color_Filter, Image_Filter, and Blend_Mode to draw, if present.
+For each entry in the array, Rect tex locates sprite in atlas, and RSXform xform transforms it
+into destination space.
+xform, text, and colors if present, must contain count entries.
+Optional colors is applied for each sprite using Blend_Mode.
+Optional cullRect is a conservative bounds of all transformed sprites. 
+If cullrect is outside of Clip, canvas can skip drawing.
+
+#Param atlas  Image containing sprites. ##
+#Param xform  RSXform mappings for sprites in atlas. ##
+#Param tex    Rect locations of sprites in atlas. ##
+#Param colors  Color, one per sprite, blended with sprite using Blend_Mode; or nullptr. ##
+#Param count  Number of sprites to draw. ##
+#Param mode   Blend_Mode combining colors and sprites. ##
+#Param cullRect  Rect bounds of transformed sprites for efficient clipping; or nullptr. ##
+#Param paint  Paint Color_Filter, Image_Filter, Blend_Mode, and so on; or nullptr. ##
+
+#Example
+#Image 3
+void draw(SkCanvas* canvas) {

+  // SkBitmap source = mandrill;

+  SkRSXform xforms[] = { { .5f, 0, 0, 0 }, {0, .5f, 200, 100 } };

+  SkRect tex[] = { { 0, 0, 250, 250 }, { 0, 0, 250, 250 } };

+  SkColor colors[] = { 0x7f55aa00, 0x7f3333bf };

+  SkPaint paint;

+  paint.setAlpha(127);

+  canvas->drawAtlas(image, xforms, tex, colors, 2, SkBlendMode::kPlus, nullptr, &paint);

+}
+##
+
+#ToDo bug in example on cpu side, gpu looks ok ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[], int count,
+                   const SkRect* cullRect, const SkPaint* paint) 
+
+Draw a set of sprites from atlas, using Clip, Matrix, and optional Paint paint.
+paint uses Anti-alias, Color_Alpha, Color_Filter, Image_Filter, and Blend_Mode to draw, if present.
+For each entry in the array, Rect tex locates sprite in atlas, and RSXform xform transforms it
+into destination space.
+xform and text must contain count entries.
+Optional cullRect is a conservative bounds of all transformed sprites. 
+If cullrect is outside of Clip, canvas can skip drawing.
+
+#Param atlas  Image containing sprites. ##
+#Param xform  RSXform mappings for sprites in atlas. ##
+#Param tex    Rect locations of sprites in atlas. ##
+#Param count  Number of sprites to draw. ##
+#Param cullRect  Rect bounds of transformed sprites for efficient clipping; or nullptr. ##
+#Param paint  Paint Color_Filter, Image_Filter, Blend_Mode, and so on; or nullptr. ##
+
+#Example
+#Image 3
+void draw(SkCanvas* canvas) {

+  // sk_sp<SkImage> image = mandrill;

+  SkRSXform xforms[] = { { .5f, 0, 0, 0 }, {0, .5f, 200, 100 } };

+  SkRect tex[] = { { 0, 0, 250, 250 }, { 0, 0, 250, 250 } };

+  const SkImage* imagePtr = image.get();

+  canvas->drawAtlas(imagePtr, xforms, tex, 2, nullptr, nullptr);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawAtlas(const sk_sp<SkImage>& atlas, const SkRSXform xform[], const SkRect tex[],
+                   int count, const SkRect* cullRect, const SkPaint* paint) 
+
+Draw a set of sprites from atlas, using Clip, Matrix, and optional Paint paint.
+paint uses Anti-alias, Color_Alpha, Color_Filter, Image_Filter, and Blend_Mode to draw, if present.
+For each entry in the array, Rect tex locates sprite in atlas, and RSXform xform transforms it
+into destination space.
+xform and text must contain count entries.
+Optional cullRect is a conservative bounds of all transformed sprites. 
+If cullrect is outside of Clip, canvas can skip drawing.
+
+#Param atlas  Image containing sprites. ##
+#Param xform  RSXform mappings for sprites in atlas. ##
+#Param tex    Rect locations of sprites in atlas. ##
+#Param count  Number of sprites to draw. ##
+#Param cullRect  Rect bounds of transformed sprites for efficient clipping; or nullptr. ##
+#Param paint  Paint Color_Filter, Image_Filter, Blend_Mode, and so on; or nullptr. ##
+
+#Example
+#Image 3
+void draw(SkCanvas* canvas) {

+  // sk_sp<SkImage> image = mandrill;

+  SkRSXform xforms[] = { { 1, 0, 0, 0 }, {0, 1, 300, 100 } };

+  SkRect tex[] = { { 0, 0, 200, 200 }, { 200, 0, 400, 200 } };

+  canvas->drawAtlas(image, xforms, tex, 2, nullptr, nullptr);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawDrawable(SkDrawable* drawable, const SkMatrix* matrix = NULL)
+
+Draw Drawable drawable using Clip and Matrix, concatenated with 
+optional matrix.
+
+If Canvas has an asynchronous implementation, as is the case 
+when it is recording into Picture, then drawable will be referenced,
+so that SkDrawable::draw() can be called when the operation is finalized. To force
+immediate drawing, call SkDrawable::draw() instead.
+
+#Param drawable  Custom struct encapsulating drawing commands. ##
+#Param matrix    Transformation applied to drawing; or nullptr.  ##
+
+#Example
+#Height 100
+#Function
+struct MyDrawable : public SkDrawable {

+    SkRect onGetBounds() override { return SkRect::MakeWH(50, 100);  }

+

+    void onDraw(SkCanvas* canvas) override {

+       SkPath path;

+       path.conicTo(10, 90, 50, 90, 0.9f);

+       SkPaint paint;

+       paint.setColor(SK_ColorBLUE);

+       canvas->drawRect(path.getBounds(), paint);

+       paint.setAntiAlias(true);

+       paint.setColor(SK_ColorWHITE);

+       canvas->drawPath(path, paint);

+    }

+};

+

+#Function ##

+void draw(SkCanvas* canvas) {

+    sk_sp<SkDrawable> drawable(new MyDrawable);

+  SkMatrix matrix;

+  matrix.setTranslate(10, 10);

+  canvas->drawDrawable(drawable.get(), &matrix);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawDrawable(SkDrawable* drawable, SkScalar x, SkScalar y)
+
+Draw Drawable drawable using Clip and Matrix, offset by (x, y).
+
+If Canvas has an asynchronous implementation, as is the case 
+when it is recording into Picture, then drawable will be referenced,
+so that SkDrawable::draw() can be called when the operation is finalized. To force
+immediate drawing, call SkDrawable::draw() instead.
+
+#Param drawable  Custom struct encapsulating drawing commands. ##
+#Param x  Offset into Canvas writable pixels in x. ##
+#Param y  Offset into Canvas writable pixels in y. ##
+
+#Example
+#Height 100
+#Function
+struct MyDrawable : public SkDrawable {

+    SkRect onGetBounds() override { return SkRect::MakeWH(50, 100);  }

+

+    void onDraw(SkCanvas* canvas) override {

+       SkPath path;

+       path.conicTo(10, 90, 50, 90, 0.9f);

+       SkPaint paint;

+       paint.setColor(SK_ColorBLUE);

+       canvas->drawRect(path.getBounds(), paint);

+       paint.setAntiAlias(true);

+       paint.setColor(SK_ColorWHITE);

+       canvas->drawPath(path, paint);

+    }

+};

+

+#Function ##

+void draw(SkCanvas* canvas) {

+    sk_sp<SkDrawable> drawable(new MyDrawable);

+  canvas->drawDrawable(drawable.get(), 10, 10);

+}
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawAnnotation(const SkRect& rect, const char key[], SkData* value)
+
+Associate Rect on Canvas when an annotation; a key-value pair, where the key is
+a null-terminated utf8 string, and optional value is stored as Data.
+
+Only some canvas implementations, such as recording to Picture, or drawing to 
+Document_PDF, use annotations.
+
+#Param rect    Rect extent of canvas to annotate. ##
+#Param key     String used for lookup. ##
+#Param value   Data holding value stored in annotation. ##
+
+#Example
+    #Height 1
+    const char text[] = "Click this link!";
+    SkRect bounds;
+    SkPaint paint;
+    paint.setTextSize(40);
+    (void)paint.measureText(text, strlen(text), &bounds);
+    const char url[] = "https://www.google.com/";
+    sk_sp<SkData> urlData(SkData::MakeWithCString(url));
+    canvas->drawAnnotation(bounds, "url_key", urlData.get());
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method void drawAnnotation(const SkRect& rect, const char key[], const sk_sp<SkData>& value) 
+
+Associate Rect on Canvas when an annotation; a key-value pair, where the key is
+a null-terminated utf8 string, and optional value is stored as Data.
+
+Only some canvas implementations, such as recording to Picture, or drawing to 
+Document_PDF, use annotations.
+
+#Param rect    Rect extent of canvas to annotate. ##
+#Param key     String used for lookup. ##
+#Param value   Data holding value stored in annotation. ##
+
+#Example
+#Height 1
+    const char text[] = "Click this link!";
+    SkRect bounds;
+    SkPaint paint;
+    paint.setTextSize(40);
+    (void)paint.measureText(text, strlen(text), &bounds);
+    const char url[] = "https://www.google.com/";
+    sk_sp<SkData> urlData(SkData::MakeWithCString(url));
+    canvas->drawAnnotation(bounds, "url_key", urlData.get());
+##
+
+#ToDo incomplete ##
+
+##
+
+#Method SkDrawFilter* getDrawFilter() const
+
+Legacy call to be deprecated.
+
+#Deprecated 
+##
+
+##
+
+#Method virtual SkDrawFilter* setDrawFilter(SkDrawFilter* filter)
+
+Legacy call to be deprecated.
+
+#Deprecated 
+##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method virtual bool isClipEmpty() const
+
+Returns true if Clip is empty; that is, nothing will draw.
+
+isClipEmpty may do work when called; it should not be called
+more often than needed. However, once called, subsequent calls perform no
+work until Clip changes.
+
+#Return  true if Clip is empty. ##
+
+#Example
+    void draw(SkCanvas* canvas) {

+        SkDebugf("clip is%s empty\n", canvas->isClipEmpty() ? "" : " not");

+        SkPath path;

+        canvas->clipPath(path);

+        SkDebugf("clip is%s empty\n", canvas->isClipEmpty() ? "" : " not");

+    }
+    #StdOut
+        clip is not empty

+        clip is empty
+    ##
+##
+
+#ToDo incomplete ##
+
+##
+
+# ------------------------------------------------------------------------------
+
+#Method virtual bool isClipRect() const
+
+Returns true if Clip is Rect and not empty.
+Returns false if the clip is empty, or if it is not Rect.
+
+#Return  true if Clip is Rect and not empty. ##
+
+#Example
+    void draw(SkCanvas* canvas) {

+        SkDebugf("clip is%s rect\n", canvas->isClipRect() ? "" : " not");

+        canvas->clipRect({0, 0, 0, 0});

+        SkDebugf("clip is%s rect\n", canvas->isClipRect() ? "" : " not");

+    }
+    #StdOut
+        clip is rect

+        clip is not rect
+    ##
+##
+
+#ToDo incomplete ##
+
+##
+
+#Class SkCanvas ##
+#Topic Canvas ##