1. 程式人生 > >Quartz 2D Programming Guide筆記

Quartz 2D Programming Guide筆記

Graphics Contexts圖形上下文

圖形上下文(graphics context)是繪製目標,可以理解為畫布,包含著繪圖時的引數和裝置資訊。型別為CGContextRef。獲取graphics context後,呼叫Quartz 2D的函式進行繪製、旋轉等操作,還可以修改如線寬、填充顏色等繪畫引數。

獲取圖形上下文

當view出現在螢幕上以及檢視將要更新時,系統會呼叫view的drawRect:方法。我們建立UIView子類,並重寫drawRect:方法,此時呼叫UIKit提供的方法UIGraphicsGetCurrentContext,來獲取系統為我們準備好的當前繪圖環境的graphics context物件,。

路徑

路徑構成一個或多個圖形或子路徑。子路徑可以由直線、曲線或兩者相結合組成。如圖:

這裡寫圖片描述

呼叫CGContextMoveToPoint,傳入表示x和y座標的兩個float值來確定路徑的起始點。

CGContextMoveToPoint(context, 50, 50);

線段

兩個點確定一條線,線的起點為當前點,所以只需要確定終點。
1、呼叫CGContextAddLineToPoint,傳入終點座標來新增一條線。

CGContextAddLineToPoint(context, 100, 50);

2、呼叫CGContextAddLines可以新增多條線,傳入點的陣列,Quartz以第一個點為起點,然後用直線依次連線剩下的點。

GPoint aPoints[3];//座標點
aPoints[0] = CGPointMake(100, 80);//座標1
aPoints[1] = CGPointMake(130, 150);//座標2
aPoints[2] = CGPointMake(130, 200);//座標3
CGContextAddLines(context, aPoints, 3);

【例項1】
Quartz 2D簡單繪製分割線:https://www.jianshu.com/p/52700532f854

弧是圓的一部分,Quartz提供兩個方法來建立弧。
1、CGContextAddArc,傳入圓的中心點座標(x,y)、半徑、起始角度、結束角度、方向(1為時鐘方向)。

CGContextAddArc(context, 100, 100, 50, M_PI_2 , 0, 1);

2、CGContextAddArcToPoint,傳入兩個終點的座標值以及半徑長度,Quartz以兩個終點確定兩條切線,從而確定弧。

【例項2】
可拖動的環形進度:http://blog.csdn.net/dolacmeng/article/details/46617517

曲線

二次和三次貝塞爾曲線可以確定各種各樣的曲線形狀,如圖:

這裡寫圖片描述

1、呼叫CGContextAddCurveToPoint函式,傳入兩個控制點、結束點,來確定一條三次貝塞爾曲線。

這裡寫圖片描述

2、通過呼叫CGContextAddQuadCurveToPoint,傳入控制點和結束點,確定一條二次貝塞爾曲線

這裡寫圖片描述

【例項】貝塞爾曲線動畫demo(仿美人相機效果):http://blog.csdn.net/dolacmeng/article/details/79276039

**例項中使用CAShapeLayer與UIBezierPath結合繪製,而非通過重寫drawRect。UIBezierPath類可以建立基於向量的路徑,這個類在UIKit中,是Core Graphics框架關於path的一個封裝。
CAShapeLayer和drawRect的比較:
- 1.drawRect:屬於CoreGraphics框架,佔用CPU,效能消耗大。
- 2.CAShapeLayer:屬於CoreAnimation框架,通過GPU來渲染圖形,節省效能。動畫渲染直接提交給手機GPU,不消耗記憶體。
參考連結:https://www.jianshu.com/p/b1c38a3a67a9

閉合路徑

呼叫CGContextClosePath,會新增一條從當前點到起始點的線來閉合當前的形狀。

橢圓

橢圓是一個被壓扁的圓形,呼叫CGContextAddEllipseInRect方法,傳入一個用以確定橢圓邊界的矩形。

CGContextAddEllipseInRect(context, CGRectMake(0, 0, 200, 100));

矩形

呼叫CGContextAddRect來新增一個矩形,傳入一個包含原點座標、寬度和高度的CGRect結構體。

CGContextAddRect(context, CGRectMake(0, 0, 200, 100));

可以一次新增多個矩形,只需呼叫CGContextAddRects方法,並傳入CGRect結構體陣列。

這裡寫圖片描述

CGRect rects[4];
rects[0] = (CGRect){{0,0}, {50, 50}};
rects[1] = (CGRect){{100, 100}, {30, 30}};
rects[2] = (CGRect){{0,100}, {20, 20}};
rects[3] = (CGRect){{100,0}, {10, 10}};

CGContextAddRects(context, rects, 4);
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillPath(context);

Creating a Path

建立路徑:
在圖形上下文中建立路徑,先呼叫CGContextBeginPath.然後通過呼叫CGContextMoveToPoint設定起點。之後就繪製新增線、弧或曲線。總之記住:
* 在開始新路徑前,呼叫CGContextBeginPath.
* 線、弧、曲線從當前點開始繪製,所以必須呼叫CGContextMoveToPoint來設定當前起始點或者呼叫對應的便捷方法。
* 當你想要閉合當前路徑,呼叫 CGContextClosePath方法來連線上起始點。
* 當你繪製弧時,Quartz會在當前點和弧的起始點之間繪製一條直線。
* 最後需呼叫繪製函式來填充(fill)或畫(stroke)出路徑。
如:

CGContextMoveToPoint(context, 100, 100);
CGContextAddLineToPoint(context, 200, 200);
CGContextAddLineToPoint(context, 50, 300);
CGContextClosePath(context);
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextStrokePath(context);

當路徑被繪製出來後,路徑會被清除。如果想要儲存路徑,Quartz提供了兩種資料型別來建立可重複利用的路徑:CGPathRef 和 CGMutablePathRef。但操作的是CGPath而不是graphics context:
* CGPathCreateMutable 代替 CGContextBeginPath
* CGPathMoveToPoint 代替 CGContextMoveToPoint
* CGPathAddLineToPoint 代替 CGContextAddLineToPoint
* CGPathAddCurveToPoint 代替 CGContextAddCurveToPoint
* CGPathAddEllipseInRect 代替 CGContextAddEllipseInRect
* CGPathAddArc 代替 CGContextAddArc
* CGPathAddRect 代替 CGContextAddRect
* CGPathCloseSubpath 代替 CGContextClosePath

Painting a Path

Quartz提供方法畫出(畫線)或填充路徑,或者同時畫出(畫線)並填充路徑。繪製的線條特性(寬度、顏色等)、填充顏色和計算填充區域的方式都是graphics state的一部分。

可以通過下表的屬性,修改畫出的路徑特性。一旦修改這些屬性,會作用於之後的所有繪製中。

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 CGContextSetStrokeColorCGContextSetStrokeColorWithColor
Stroke pattern CGContextSetStrokePattern

line width是線條的寬度,路徑兩邊各佔線寬的一半。
line join表示Quartz怎麼處理線條的連線處。Quartz支援的連線樣式 如下:

這裡寫圖片描述

line cap表示線的兩個端點的樣式:
這裡寫圖片描述

line dash線條的樣式:
這裡寫圖片描述
Quartz提供的畫線方法:

Function Description
CGContextStrokePath Strokes the current path.
CGContextStrokeRect Strokes the specified rectangle.
CGContextStrokeRectWithWidth Strokes the specified rectangle, using the specified line width.
CGContextStrokeEllipseInRect Strokes an ellipse that fits inside the specified rectangle.
CGContextStrokeLineSegments Strokes a sequence of lines.
CGContextDrawPath If you pass the constant kCGPathStroke, strokes the current path. See Filling a Path if you want to both fill and stroke a path.

Filling a Path

當填充路徑時,Quartz認為每一個路徑都是閉合的,然後利用這些閉合的路徑計算填充區域。一共有兩種計算填充區域的方式。橢圓形、矩形等簡單的路徑,很好確定填充區域。但是如果有重疊的路徑或者路徑內包含多個路徑時(如下圖),有兩種規則來確定填充區域。

這裡寫圖片描述

預設的填充規則為非零繞組規則:給定一條曲線C和一個點P,構造一條從P點出發射向無窮遠的射線。找出所有該射線和曲線的交點,並按如下規則統計繞組數量(winding number):每一個順時針方向(曲線從左向右通過射線)上的交點減1,每一個逆時針方向(曲線從右向左通過射線)上的交點加1。如果繞組總數為0,表示該點在曲線外;否則,該點在曲線內。

另一個為奇偶規則。平面內的任何一點P,引出一條射線,注意不要經過多邊形的頂點,如果射線與多邊形的交點的個數為奇數,則點P在多邊形的內部,如果交點的個數為偶數,則點P在多邊形的外部。
(參考:http://blog.csdn.net/wodownload2/article/details/52151714
【例項】中間透明的引導蒙層
http://blog.csdn.net/dolacmeng/article/details/79285679

Quartz提供的填充函式如下3-5:

Function Description
CGContextEOFillPath Fills the current path using the even-odd rule.
CGContextFillPath Fills the current path using the nonzero winding number rule.
CGContextFillRect Fills the area that fits inside the specified rectangle.
CGContextFillRects Fills the areas that fits inside the specified rectangles.
CGContextFillEllipseInRect Fills an ellipse that fits inside the specified rectangle.
CGContextDrawPath Fills the current path if you pass kCGPathFill (nonzero winding number rule) or kCGPathEOFill (even-odd rule). Fills and strokes the current path if you pass kCGPathFillStroke or kCGPathEOFillStroke.

Setting Blend Modes

重疊模式,兩個繪製重疊時,重疊部分預設的顏色為:
result = (alpha * foreground) + (1 - alpha) * background

Clipping to a Path

路徑可以作為遮罩,進行裁剪。例如有一張大圖,但只希望顯示其中一部分,可以設定裁剪區域,即只顯示在閉合路徑內部的部分:

CGContextBeginPath (context);
CGContextAddArc (context, w/2, h/2, ((w>h) ? h : w)/2, 0, 2*PI, 0);
CGContextClosePath (context);
CGContextClip (context);

提供的裁剪方法:

Function Description
CGContextClip Uses the nonzero winding number rule to calculate the intersection of the current path with the current clipping path.
CGContextEOClip Uses the even-odd rule to calculate the intersection of the current path with the current clipping path.
CGContextClipToRect Sets the clipping area to the area that intersects both the current clipping path and the specified rectangle.
CGContextClipToRects Sets the clipping area to the area that intersects both the current clipping path and region within the specified rectangles.
CGContextClipToMask Maps a mask into the specif

Transforms

在繪圖前,可以通過變換矩陣(CTM)進行旋轉、縮放、移動(可以理解為對座標系操作),從而實現對即將繪製的影象進行變換。
下面的程式碼會把一張圖片繪製到上下文:

CGContextDrawImage (myContext, rect, myImage);

這裡寫圖片描述

呼叫CGContextTranslateCTM函式,改變座標空間原點的x和y,如在x軸上移動100個單位,在y軸上移動50個單位:

CGContextTranslateCTM (myContext, 100, 50);

這裡寫圖片描述

呼叫CGContextRotateCTM來旋轉座標系。如,旋轉-45度:

CGContextRotateCTM (myContext, radians(–45.));

這裡寫圖片描述

此時部分圖片內容被裁剪了,因為這部分被移到圖形上下文之外了。另外需要將角度轉換為弧度:

include <math.h>

static inline double radians (double degrees) {return degrees * M_PI/180;}

呼叫CGContextScaleCTM可以在x軸和y軸上對座標系進行縮放,如果為負值,可以進行翻轉。例如在x軸上縮放0.5倍,在y軸上縮放0.75倍:

這裡寫圖片描述

可以結合使用多個變換,通過CGContextConcatCTM方法可將多個變換組合成一個變換。

也可以直接依次執行多個變換。如:

CGContextTranslateCTM (myContext, w/4, 0);
CGContextScaleCTM (myContext, .25,  .5);
CGContextRotateCTM (myContext, radians ( 22.));

這裡寫圖片描述

Creating Affine Transforms

仿射變換(affine transform)是作用於矩陣而非CTM。可以使用這些函式構造一個矩陣,然後通過函式CGContextConcatCTM應用於CTM。
仿射變換和CTM變換作用相同,都可以進行平移、旋轉、縮放操作。方法和作用如下表:

Function Use
CGAffineTransformMakeTranslation To construct a new translation matrix from x and y values that specify how much to move the origin.
CGAffineTransformTranslate To apply a translation operation to an existing affine transform.
CGAffineTransformMakeRotation To construct a new rotation matrix from a value that specifies in radians how much to rotate the coordinate system.
CGAffineTransformRotate To apply a rotation operation to an existing affine transform.
CGAffineTransformMakeScale To construct a new scaling matrix from x and y values that specify how much to stretch or shrink coordinates.
CGAffineTransformScale To apply a scaling operation to an existing affine transform.

.
Quartz提供了CGAffineTransformInvert方法來撤消一個矩陣。但一般可以通過儲存和還原graphics state實現類似的需求。

如果想要只對某個點或某部分進行變換,可以通過CGPointApplyAffineTransform方法對點,通過CGSizeApplyAffineTransform對某部分進行操作。

【待續…】

參考:
https://developer.apple.com/library/content/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_overview/dq_overview.html