Many children's shoes know Core Graphics, but they have not heard of Quartz 2D. The relationship between them can be seen in the previous article (and so on, in fact, the difference between potatoes and potatoes). Searching for Core Graphics in Duniang is mostly the operation of CGPathRef, without detailed description and explanation of API, which is not easy to understand. Fang naturally did not explain.
This paper is based on the basic concept of drawing Path and API, and combines with the actual combat to complete the graphics drawing.
This article is a summary of official documents and practical warfare.
Paths (Path) Principle
A path defines one or more shapes or subpaths. A subpath may consist of a straight line, a curve, or both. It can be either open or closed.
Creating and drawing paths are separate tasks. 1. Creating a path first. 2. When we need to render a path, we use Quartz to draw it.
Create and draw paths
Subpaths are composed of points, lines, arcs and curves. Quartz also provides functions for drawing rectangles and ellipses. Points define the starting point and end point of drawing. Of course, some pictures can be drawn only by selecting the origin, such as arcs, ellipses and so on.
Note: Complex graphics are mostly composed of n sub-paths.
For path rendering, Quartz provides "two" sets of APIs (which allow me to understand), CGPath and CGContext, respectively.
- CGContext is a direct operation context, drawing subpaths in the context, closing the current subpath by CGContextClosePath(context), and then continuing to draw the next path, then how to continue to modify or draw the previous path is unlikely to be achieved. Therefore..
- When CGPath comes out, it operates on the CGPathRef object. Every time this sub-path is drawn, CGContextAddPath (context, curve Path) is added to the current context, and then the next sub-path is drawn, at which time all the sub-paths can be saved for reuse.
Therefore, the author suggests that CGPath be used to draw complex composite graphics and CGContext to draw simple graphics.
0. context
//Get the context, UIKit coordinate system. CGContextRef context = UIGraphicsGetCurrentContext(); .. /* Begin a new path. The old path is discarded. */ /* So it's important to note that the sub-paths are not created here, if CGContext is used to plot the paths, * Calling CGContextBeginPath(context) again when you start drawing the next subpath overrides * The last path, so this function can be called only once*/ CGContextBeginPath(context);
1. point Point
CGContextMoveToPoint(context, p1.x, p1.y); //perhaps CGPathMoveToPoint(path, nil, fromPoint.x, fromPoint.y);
2. line Lines
CGContextMoveToPoint(context, p1.x, p1.y); CGContextAddLineToPoint(context, x, y); CGContextClosePath(context); //perhaps CGMutablePathRef path = CGPathCreateMutable(); CGPathMoveToPoint(path, nil, fromPoint.x, fromPoint.y); CGPathAddLineToPoint(path, nil, toPoint.x, toPoint.y); CGPathCloseSubpath(path); CGContextAddPath(context, path); CGPathRelease(path);
3. circular arc Arcs
CGContextAddArc(context, center.x, center.y, radius, radians(0.0f), radians(360.0f), 0); CGContextClosePath(context); //perhaps CGMutablePathRef path = CGPathCreateMutable(); CGPathAddArc(path, nil, center.x, center.y, 50, radians(-90.0f), radians((- 180.0f)), YES); CGPathCloseSubpath(path); CGContextAddPath(context, path); CGPathRelease(path); //Other drawing methods CGContextAddArcToPoint; CGContextAddQuadCurveToPoint;
4. curve Curves
CGContextAddCurveToPoint(context, refPoint1.x, refPoint1.y, refPoint2.x, refPoint2.y, toPoint.x, toPoint.y); CGContextMoveToPoint(context, fromPoint.x, fromPoint.y); CGContextAddQuadCurveToPoint(context, refPoint1.x, refPoint1.y, toPoint.x, toPoint.y); CGContextClosePath(context); //perhaps CGMutablePathRef path = CGPathCreateMutable(); CGPathMoveToPoint(path, nil, fromPoint.x, fromPoint.y); CGPathAddCurveToPoint(path, nil,refPoint1.x, refPoint1.y, refPoint2.x, refPoint2.y, toPoint.x, toPoint.y); CGPathCloseSubpath(path); CGContextAddPath(context, path); CGPathRelease(path);
5. rectangle Rectangle
CGContextAddRect(context, rect); CGContextClosePath(context); //perhaps CGPathAddRect(path, nil, rect); CGPathCloseSubpath(path); CGContextAddPath(context, path); CGPathRelease(path); //Add multiple rectangles together CGContextAddRects(context, rects, 3);
6. Ellipses
CGContextAddEllipseInRect(context, rect); CGContextClosePath(context); //perhaps CGPathAddEllipseInRect(path, nil, rect); CGPathCloseSubpath(path); CGContextAddPath(context, path); CGPathRelease(path);
Summary of Drawing Path Rules
- Before drawing the path, call the function CGContextBeginPath.
- Lines, arcs and curves begin at the current point. An empty path has no current point; we must use CGContext- MoveToPoint to set the starting point of the first subpath, or call a convenience function to implicitly complete the task.
- To close the current subpath, call the function CGContextClosePath. Then the path will start a new subpath, even if we do not show setting a new starting point.
- When drawing an arc, Quartz draws a straight line between the current point and the starting point of the arc.
- The Quartz program that adds ellipses and rectangles adds a new closed subpath to the path.
- We must call the drawing function to fill or edge a path, because the path is not drawn when it is created.
Deep characterization (edge drawing, filling)
Attributes Affecting Edge Stroke
The following table shows some attributes of context, and modifying these attributes will affect subsequent edge-tracing operations.
Parameter | Function to set parameter value |
---|---|
Line width | CGContextSetLineWidth |
Line join | CGContextSetLineJoin |
Line cap | CGContextSetLineCap |
Miter limit | CGContextSetMiterLimit |
Line dash pattern | CGContextSetLineDash |
Stroke color space | CGContextSetStrokeColorSpace |
Stroke color | CGContextSetStrokeColorCG... |
Stroke pattern | CGContextSetStrokePattern |
Stroke function
Quartz provides the functions in the following table to trace the current path.
Function | Description |
---|---|
CGContextStrokePath | Edge the current path |
CGContextStrokeRect | Note: Create a rectangular subpath and edge it |
CGContextStrokeRectWithWidth | Note: Create a rectangular subpath, set the stroke width, and trace it. |
CGContextStrokeEllipseInRect | Note: Create an ellipse subpath and trace it |
CGContextStrokeLineSegments | Draw edges on a set of straight lines |
CGContextDrawPath | Note: To draw the current subpath in the context, you need to specify CGPathDrawingMode (see table below) |
CGPathDrawingMode has the following types:
- kCGPathFill, fill only
- kCGPathEOFill, parity filling
- kCGPathStroke, only edge tracing
- KCGPathFill Stroke, both filling and edge-tracing
- kCGPathEOFillStroke, Parity Stroke
Fill path
Quartz considers every subpath included in the path as closed. Then, these closed paths are used and filled pixels are calculated. Paths such as ellipses and rectangles have distinct regions. But if the path is composed of several overlapping parts or the path contains multiple sub-paths, we have two rules to define the filling area.
Non-zero winding number rule:
To determine whether a point needs to be drawn, we begin by drawing a straight line across the drawing boundary. Starting from 0, the count is added to 1 for each path fragment passing through a straight line from left to right, and minus 1 for each path fragment passing through a straight line from right to left. If the result is 0, the point is not drawn, otherwise it is drawn. The direction of path fragment drawing will affect the result.Even-odd rule: In order to determine whether a point is drawn, we start from that point and draw a straight line through the drawing boundary. Calculate the number of path fragments passing through the line. If it is odd, the point is drawn, and if it is even, the point is not drawn. The direction of path fragment drawing does not affect the result.
To sum up: the default rule is non-zero winding number. The filling rule of non-zero winding number is related to the direction of drawing, while the even-odd rule is independent of the direction.
See: Official documents.
Quartz provides the functions in the following table to fill in the current path.
Function | Description |
---|---|
CGContextEOFillPath | Fill the current path with parity rules |
CGContextFillPath | Filling current path with non-zero winding rule |
CGContextFillRect | Note: Create a rectangular subpath and fill it. |
CGContextFillRects | Note: Create a set of rectangular paths and fill them in |
CGContextFillEllipseInRect | Note: Create an elliptical path and fill it in |
CGContextDrawPath | Note: To draw the current subpath in the context, you need to specify CGPathDrawingMode |
Set up mixed mode
Mixed mode specifies how Quartz draws the drawing to the background. Quartz defaults to normal blend mode, which uses the following formula to calculate how foreground and background drawings are blended:
result = (alpha * foreground) + (1 - alpha) *background
Call CGContextSetBlendMode(context, kCGBlendModeNormal) to set round mode.
You can be there. Quartz 2D Programming Guide - Chapter Paths See the concrete effect of mixed mode.
Note: CGContext save and restore functions
When constructing complex paths, which are composed of n sub-paths and each sub-path has different styles, we need to make different modifications to the context attributes of the current context. In order to ensure that the context parameter values of the next sub-path remain the original values (i.e., not inherit the parameter values of the previous sub-path), we need to be in each sub-path. Call CGContextSaveGState(context) to save the current context state before drawing a subpath, and restore the settings after drawing the current subpath, that is, call CGContextRestoreGState(context).
Paths in action, drawing graphics.
In actual combat, we draw a graph, which is composed of several sub-paths with different shapes.
Implementation of drawRect
In order to use Core Graphics to plot, the easiest way is to customize a class that inherits from UIView and override the drawRect method of a subclass. Drawing graphics in this method.
Core Graphics has to have a canvas to paint things on it. In the drawRect method, we can get the Context of the current stack top directly.
- (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); CGContextBeginPath(context); //1. line drawing [self beginLinesDemo:context]; //2. arc drawing [self beginArcsDemo:context]; //3. curve drawing [self beginCurvesDemo:context]; //4. ellipse plotting [self beginEllipsesDemo:context]; //5. rectangle drawing [self beginRectangleDemo:context]; //6. The Attributes Affecting the Stroke Edge // CGContextSet... //7. Functions of Path Edge // CGContextStroke... //8. fill path // CGContextFill... }
Line Drawing Pentagon
This article focuses not on how to calculate the Pentagon, but on how to use CG. So if you are interested in computing, I think Du Niang will give you the answer, go on.
- (void)beginLinesDemo:(CGContextRef)context { //1.Line line CGContextSaveGState(context); cg_drawPentagramByLine(context, CGPointMake(150, 150), 100); //Setting stroke attributes CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor); CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor); CGContextSetLineWidth(context, 2); CGContextDrawPath(context, kCGPathFillStroke); CGContextRestoreGState(context); } void cg_drawPentagramByLine(CGContextRef context, CGPoint center,CGFloat radius) { CGPoint p1 = CGPointMake(center.x, center.y - radius); CGContextMoveToPoint(context, p1.x, p1.y); CGFloat angle=4 * M_PI / 5.0; for (int i=1; i<=5; i++) { CGFloat x = center.x -sinf(i*angle)*radius; CGFloat y = center.y -cosf(i*angle)*radius; CGContextAddLineToPoint(context, x, y); } CGContextClosePath(context); }
The drawing is as follows:
Arc Link Pentagon Vertex
- (void)beginArcsDemo:(CGContextRef)context { //2.Arcs curve CGContextSaveGState(context); cd_drawCircleByArc(context, CGPointMake(150, 150), 102); //Setting stroke attributes CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor); CGContextSetLineWidth(context, 2); CGContextDrawPath(context, kCGPathStroke); CGContextRestoreGState(context); } void cd_drawCircleByArc(CGContextRef context, CGPoint center, CGFloat radius) { CGContextAddArc(context, center.x, center.y, radius, radians(0.0f), radians(360.0f), 0); //The following two uses can be studied on their own // CGContextAddArcToPoint; // CGContextAddQuadCurveToPoint; }
The drawing is as follows:
Curve Drawing Windmill
- (void)beginCurvesDemo:(CGContextRef)context { //3.Curve curve CGContextSaveGState(context); cd_drawPinwheelByCurve(context, CGPointMake(300, 100), CGPointMake(0, 200), CGPointMake(0, 0), CGPointMake(300, 300)); CGContextSetRGBFillColor(context, 246 / 255.0f, 122 / 255.0f , 180 / 255.0f, 0.6); CGContextSetStrokeColorWithColor(context, [UIColor orangeColor].CGColor); CGContextDrawPath(context, kCGPathFillStroke); CGMutablePathRef curvePath = cd_drawPinwheelCurveByPath(CGPointMake(300, 200), CGPointMake(0, 100), CGPointMake(300, 0), CGPointMake(0, 300)); CGContextAddPath(context, curvePath); CGPathRelease(curvePath); CGContextDrawPath(context, kCGPathFillStroke); CGContextRestoreGState(context); } void cd_drawPinwheelByCurve(CGContextRef context,CGPoint refPoint1, CGPoint refPoint2, CGPoint fromPoint, CGPoint toPoint) { CGContextMoveToPoint(context, fromPoint.x, fromPoint.y); CGContextAddCurveToPoint(context, refPoint1.x, refPoint1.y, refPoint2.x, refPoint2.y, toPoint.x, toPoint.y); CGContextAddQuadCurveToPoint(context, refPoint1.x, refPoint1.y, toPoint.x, toPoint.y); } CGMutablePathRef cd_drawPinwheelCurveByPath(CGPoint refPoint1, CGPoint refPoint2, CGPoint fromPoint, CGPoint toPoint) { CGMutablePathRef path = CGPathCreateMutable(); CGPathMoveToPoint(path, nil, fromPoint.x, fromPoint.y); CGPathAddCurveToPoint(path, nil,refPoint1.x, refPoint1.y, refPoint2.x, refPoint2.y, toPoint.x, toPoint.y); CGPathCloseSubpath(path); //Other addCurve s create curve methods with context. return path; }
The drawing is as follows:
So far, so much has been written about Paths trunk in Quartz, as well as the edge and corner knowledge points and API s, which are not described in detail here.
Detailed view Official document .
This is Core Graphic's second article, the actual combat details, and then CG beautification, if you are interested, to a compliment and attention. We'll see you next time.