Painting

什么是paint绘制的基础?

  • Path describes a sequence of potentially disjoint movements on a plane. Paths tracks a current point as well as one or more subpaths (created via Path.moveTo). Subpaths may be closed (i.e., the first and last points are coincident), open (i.e., the first and last points are distinct), or self intersecting (i.e., movements within the path intersect). Paths incorporate lines, arcs, beziers, and more; each operation begins at the current point and, once complete, defines the new current point. The current point begins at the origin. Paths can be queried (via Path.contains), transformed (via Path.transform), and merged (via Path.combine, which accepts a PathOperation).

    • PathFillType defines the criteria determining whether a point is contained by the path. PathFillType.evenOdd casts a ray from the point outward, summing the number of edge crossings; an odd count indicates that the point is internal. PathFillType.nonZero considers the path’s directionality. Again casting a ray from the point outward, this method sums the number of clockwise and counterclockwise crossings. If the counts aren’t equal, the point is considered to be internal.

  • Canvas represents a graphical context supporting a number of drawing operations. These operations are captured by an associated PictureRecorder and, once finalized, transformed into a Picture. The Canvas is associated with a clip region (i.e., an area within which painting will be visible), and a current transform (i.e., a matrix to be applied to any drawing), both managed using a stack (i.e., clip regions and transforms can be pushed and popped as drawing proceeds). Any drawing outside of the canvas’s culling box (“cullRect”) may be discarded; by default, however, affected pixels are retained. Many operations accept a Paint parameter which describes how the drawing will be composited (e.g., the fill, stroke, blending, etc).

    • Canvas exposes a rich API for drawing. The majority of these operations are implemented within the engine.

      • Canvas.clipPath, Canvas.clipRect, etc., refine (i.e., reduce) the clip region. These operations compute the intersection of the current clip region and the provided geometry to define a new clip region. The clip region can be anti-aliased to provide a gradual blending.

      • Canvas.translate, Canvas.scale, Canvas.transform, etc., alter the current transformation matrix (i.e., by multiplying it by an additional transform). The former methods apply standard transformations, whereas the latter applies an arbitrary 4x4 matrix (specified in column-major order).

      • Canvas.drawRect, Canvas.drawLine, Canvas.drawPath, etc., perform fundamental drawing operations.

      • Canvas.drawImage, Canvas.drawAtlas, Canvas.drawPicture, etc., copy pixels from a rendered image or recorded picture into the current canvas.

      • Canvas.drawParagraph paints text into the canvas (via Paragraph._paint).

      • Canvas.drawVertices, Canvas.drawPoints, etc., describe solids using a collection of points. The former constructs triangles from a set of vertices (Vertices) and a vertex mode (VertexMode); this mode describes how vertices are composed into triangles (e.g., VertexMode.triangles specifies that each sequence of three points defines a new triangle). The resulting triangles are filled and composited using the provided Paint and BlendMode. The latter paints a set of points using a PointMode describing how the collection of points is to be interpreted (e.g., as defining line segments or disconnected points).

    • The save stack tracks the current transformation and clip region. New entries can be pushed (via Canvas.save or Canvas.saveLayer) and popped (via Canvas.restore). The number of items in this stack can also be queried (via Canvas.getSaveCount); there is always at least one item on the stack. All drawing operations are subject to the transform and clip at the top of the stack.

      • All drawing operations are performed sequentially (by default or when using Canvas.save/Canvas.restore). If the operation utilizes blending, it will be blended immediately after completing.

      • Canvas.saveLayer allows drawing operations to be grouped together and composited as a whole. Each individual operation will still be blended within the saved layer; however, once the layer is completed, the composite drawing will be blended as a whole using the provided Paint and bounds.

        • For example, an arbitrary drawing can be made consistently translucent by first painting it using an opaque fill, and then blending the resulting layer with the canvas. If instead each component of the drawing were individually blended, overlapping regions would appear darker.

        • This is particularly useful for antialiased clip regions (i.e., regions that aren’t pixel aligned). Without layers, any operations intersecting the clip would needed to be antialiased (i.e., blended with the background). If a subsequent operation intersects the clip at this same point, it would be blended with both the background and the previous operation; this produces visual artifacts (e.g., color bleed). If both operations were combined into a layer and composited as a whole, only the final operation would be blended.

        • Note that though this doesn’t introduce a new framework layer, it does cause the engine to switch to a new rendering target. This is fairly expensive as it flushes the GPU’s command buffer and requires data to be shuffled.

  • Paint describes how a drawing operation is to be applied to the canvas. In particular, it specifies a number of graphical parameters including the color to use when filling or stroking lines (Paint.color, Paint.colorFilter, Paint.shader), how new painting is to be blended with old painting (Paint.blendMode, Paint.isAntiAlias, Paint.maskFilter), and how edges are to be drawn (Paint.strokeWidth, Paint.strokeJoin, Paint.strokeCap). Fundamental to most drawing is whether the result is to be stroked (e.g., drawn as an outline) or filled; Paint.style exposes a PaintingStyle instance specifying the mode to be used.

    • If stroking, Paint.strokeWidth is measured in logical pixels orthogonal to the path being painted. A value of 0.0 will cause the line to be rendered as thin as possible (“hairline rendering”).

      • Any lines that are drawn will be capped at their endpoints according to a StrokeCap value (via Paint.strokeCap; StrokeCap.butt is the default and does not paint a cap). Caps extend the overall length of lines in proportion to the stroke width.

      • Discrete segments are joined according to a StrokeJoin value (via Paint.strokeJoin; StrokeJoin.miter is the default and extends the original line such that the next can be drawn directly from it). A limit may be specified to prevent the original line from extending too far (via Paint.strokeMiterLimit; once exceeded, the join reverts to StrokeJoin.bevel).

    • ColorFilter describes a function mapping from two input colors (e.g., the paint’s color and the destination’s color) to a final output color (e.g., the final composited color). If a ColorFilter is provided, it overrides both the paint color and shader; otherwise, the shader overrides the color.

    • MaskFilter applies a filter (e.g., a blur) to the drawing once it is complete but before it is composited. Currently, this is limited to a Gaussian blur.

  • Shader是引擎使用的Skia着色器的手柄。框架中包含多个控件,包括GradientImageShader。这俩是类似的,前者通过平滑地混合颜色来生成像素,而后者则直接从图像中读取它们。两者都支持平铺,以便可以将原始像素扩展到其边界之外(可以在任一方向上指定不同的TileMode);ImageShader还支持将任意矩阵应用于源图像。

最后更新于