1.1 The Graphics Context
所有的東西都是畫在CGContextRef類型的對象上
Quartz 2D中一些資料類型
- CGPathRef:用于繪畫的線
- CGImageRef:一個矢量圖檔
- CGLayerRef:一個繪畫層,可以重複繪畫和離屏繪畫
- CGPatternRef:用于重複繪畫的
- CGShadingRef和CGFrandientRef:用于漸變繪畫
- CGFunctionRef:回調方法,一般使用者畫漸變圖形時使用
- CGColorRef和CGColorSpaceRef:顔色描述
- CGImageSourceRef和CGImageDestinationRef:将資料放到Quartz或取出
- CGFontRef:用于畫文字
- CGPDFDictionaryRef,CGPDFObjectRef,CGPDFPageRef,CGPDFStream,CGPDFStringRef和CGPDFArrayRef:用于通路PDF中繼資料
- CGPDFScannerRef和CGPDFContentStreamRef:用于解析PDF中繼資料
1.2 繪畫狀态
儲存目前繪畫狀态CGContextSaveGState
恢複恢複狀态CGContextSaveGState
1.3 Quartz 2D坐标系
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0NXYFhGd192UvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2Lc1zZIVmesdUZ1x2RlZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39DN4YjMyUzM1ETNxUDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
2.1 在iOS中擷取畫布
- 在UIView中實作drawRect:方法,視圖會自動配置繪畫環境,你可以直接在這個方法裡進行繪畫,系統已經自動将坐标系轉換與UIKit相同的坐标系
- 使用UIGraphicsGetCurrentContext擷取CGContectRef對象,拿到畫布進行繪畫
2.2 在MacOS中擷取畫布
這裡需要自己轉換坐标系
2.3 建立PDF畫布
**有兩種方式建立PDF畫布
- CGPDFContextCreateWithURL指定PDF輸出路徑
CGContextRef MyPDFContextCreate(const CGRect *inMediaBox,CFStringRef path) {
CGContextRef contextRef = NULL;
CFURLRef url;
//建立以儲存PDF的路徑
url = CFURLCreateWithFileSystemPath(NULL,
path,
kCFURLPOSIXPathStyle,
false);
if(url != NULL){
contextRef = CGPDFContextCreateWithURL(url,
inMediaBox,
NULL);
CFRelease(url);
}
return contextRef;
}
2.CGPDFContextCreate當你想講PDF資料發送給使用者是使用這個方法建立
CGContextRef MyPDFContextCreate (const CGRect *inMediaBox,
CFStringRef path)
{
CGContextRef myOutContext = NULL;
CFURLRef url;
CGDataConsumerRef dataConsumer;
url = CFURLCreateWithFileSystemPath (NULL,
path,
kCFURLPOSIXPathStyle,
false);
if (url != NULL)
{
dataConsumer = CGDataConsumerCreateWithURL (url);
if (dataConsumer != NULL)
{
myOutContext = CGPDFContextCreate (dataConsumer,
inMediaBox,
NULL);
CGDataConsumerRelease (dataConsumer);
}
CFRelease(url);
}
return myOutContext;
}
注:需要轉換坐标系
在PDF畫布上繪畫
CGRect mediaBox;
mediaBox = CGRectMake (, , myPageWidth, myPageHeight);
myPDFContext = MyPDFContextCreate (&mediaBox, CFSTR("test.pdf"));
CFStringRef myKeys[];
CFTypeRef myValues[];
myKeys[] = kCGPDFContextMediaBox;
myValues[] = (CFTypeRef) CFDataCreate(NULL,(const UInt8 *)&mediaBox, sizeof (CGRect));
CFDictionaryRef pageDictionary = CFDictionaryCreate(NULL, (const void **) myKeys,(const void **) myValues,,&kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks);
CGPDFContextBeginPage(myPDFContext, &pageDictionary);
// ********** Your drawing code here **********
CGContextSetRGBFillColor (myPDFContext, , , , );
CGContextFillRect (myPDFContext, CGRectMake (, , , ));
CGContextSetRGBFillColor (myPDFContext, , , , );
CGContextFillRect (myPDFContext, CGRectMake (, , , ));
CGPDFContextEndPage(myPDFContext);
CFRelease(pageDictionary);
CFRelease(myValues[]);
CGContextRelease(myPDFContext);
2.4 建立矢量圖畫布
//建立Bitmap Graphics Context
CGContextRef MyCreateBitmapContext (int pixelsWide,
int pixelsHigh)
{
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
void * bitmapData;
int bitmapByteCount;
int bitmapBytesPerRow;
bitmapBytesPerRow = (pixelsWide * );
bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
bitmapData = calloc( bitmapByteCount, sizeof(uint8_t) );
if (bitmapData == NULL)
{
fprintf (stderr, "Memory not allocated!");
return NULL;
}
//bitmapData可以傳NULL,系統會自動申請記憶體空間
context = CGBitmapContextCreate (bitmapData,
pixelsWide,
pixelsHigh,
, // bits per component
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedLast);
if (context== NULL)
{
free (bitmapData);
fprintf (stderr, "Context not created!");
return NULL;
}
CGColorSpaceRelease( colorSpace );
return context;
}
CGRect myBoundingBox;
myBoundingBox = CGRectMake (0, 0, myWidth, myHeight);
myBitmapContext = MyCreateBitmapContext (400, 300);
// ********** Your drawing code here **********
CGContextSetRGBFillColor (myBitmapContext, 1, 0, 0, 1);
CGContextFillRect (myBitmapContext, CGRectMake (0, 0, 200, 100 ));
CGContextSetRGBFillColor (myBitmapContext, 0, 0, 1, .5);
CGContextFillRect (myBitmapContext, CGRectMake (0, 0, 100, 200 ));
myImage = CGBitmapContextCreateImage (myBitmapContext);
CGContextDrawImage(myContext, myBoundingBox, myImage);
char *bitmapData = CGBitmapContextGetData(myBitmapContext);
CGContextRelease (myBitmapContext);
if (bitmapData) free(bitmapData);
CGImageRelease(myImage);
3. 路徑的建立和繪畫
點:使用
CGContextMoveToPoint
方法移動到一個點,作為一條路徑的七點
//第一個參數是畫布,第二三的事點的x,y坐标
CGContextMoveToPoint(ctx, , );
線:畫一條線需要有一個起點和一個終點,代用方法
CGContextAddLineToPoint
來制定一個終點,也可以調用
CGContextAddLines
添加多條線,數組中第一個點是起點,其他的都是終點
//這是畫一條線的完整代碼
CGContextMoveToPoint(ctx, , ); //指定起點
CGContextAddLineToPoint(ctx, , ); //指定終點
//這是添加多條線
CGPoint points[] = {CGPointMake(, ),CGPointMake(, ),CGPointMake(, ),CGPointMake(, )};
CGContextAddLines(ctx, points, );
**弧:**Quartz提供了兩個方法建立弧,
CGContextAddArc
和
CGContextAddArcToPoint
//需要給一個圓的中心點x、y坐标和圓的半徑,還有圓的起始角度和結束角度,以及一個順時針逆時針參數,預設情況下1代表順時針,0代表逆時針,但是在UIView的drawRect方法裡由于系統自動旋轉了坐标系,此時0代表順時針,1代表逆時針
CGContextAddArc(ctx, , , , , M_PI_4*, );
//CGContextAddArcToPoint需要一個目前點和兩個弧線的點以及一個圓半徑,不建議使用此方法建立弧線,計算難度大
CGContextMoveToPoint(ctx, , );
CGContextAddArcToPoint(ctx, , , , , );
曲線:
//使用CGContextAddCurveToPoint建立需要有一個起點一個終點還有兩個控制點,示意圖如下
CGContextMoveToPoint(ctx, , );
CGContextAddCurveToPoint(ctx, , , , , , );
//使用CGContextAddQuadCurveToPoint建立曲線要有起點終點和一個控制點
CGContextMoveToPoint(ctx, , );
CGContextAddQuadCurveToPoint(ctx, , , , );
閉合路徑:使用CGContextClosePath方法可以将目前路徑封閉,系統會從目前點畫一條直線指向路徑起始點
橢圓:調用
CGContextAddEllipseInRect
方法,指定一個矩形的的frame,建立矩形的内切圓
矩形:調用
CGContextAddRect
方法即可
——————— 建立路徑 ———————
- 建立一個路徑之前需要先調用
CGContextBeginPath
- 指定路徑的起點,必須調用
方法指定起點,讓後才可以添加直線、弧線和曲線等CGContextMoveToPoint
- 如果想要閉合路徑需要調用
方法,調用該方法後,預設後面所畫的就是一條新的路徑CGContextClosePath
- 畫弧線是,系統會自動在目前點和弧線的起始點之間畫一條直線,弧線的終點變為目前點
- 添加橢圓或者矩形就是添加一個封閉的路徑
- 建立路徑并不是繪畫路徑,想要繪畫路徑必須調用用于繪畫的方法,填充或者畫線
注:如果想要重複繪畫路徑就需要把路徑儲存,這是可以使用
CGPathRef
建立路徑,然後調用
CGContextAddPath
将路徑添加到畫布上,
CGPathRef
的繪畫方法如下
-
替換CGPathCreateMutable
CGContextBeginPath
-
CGPathMoveToPoint替換
CGContextMoveToPoint
-
CGPathAddLineToPoint替換
CGContextAddLineToPoint
-
CGPathAddCurveToPoint替換
CGContextAddCurveToPoint
-
CGPathAddEllipseInRect替換
CGContextAddEllipseInRect
-
CGPathAddArc替換
CGContextAddArc
-
CGPathAddRect替換
CGContextAddRect
-
CGPathCloseSubpath替換
CGContextClosePath
——————— 描繪路徑 ————————
一些影響劃線的屬性:
屬性 | 設定屬性的方法 |
---|---|
Line width | CGContextSetLineWidth |
Line join | CGContextSetLineJoin |
Line cap | CGContextSetLineCap |
Miter limit | CGContextSetMiterLimit |
Line dash pattern | CGContextSetLineDash |
Stroke color space | CGContextSetStrokeColorSpace |
Stroke color | CGContextSetStrokeColor“CGContextSetStrokeColorWithColor |
Stroke pattern | CGContextSetStrokePattern |
Line join styles
Style | Appearance |
---|---|
Miter join | |
Round join | |
Bevel join | |
Line cap styles
Style | Appearance |
---|---|
Butt cap | |
Round cap | |
Projecting square cap | |
Line dash pattern
//設定虛線的方法
void CGContextSetLineDash ( CGContextRef ctx,CGFloat phase,const CGFloat lengths[],size_t count);
//phase表示虛線往左移動的距離,和二進制的左移類似
//lengths表示虛線的展示方式
//count表示lengths中前幾個數字有效,count不能超過lengths中的資料量超過就不好控制
幾種情況的對比
CGFloat lengths[] = {,};
CGContextSetLineDash(ctx, , lengths, );
//lengths是裡是虛線實作交替,30實線,10跳過,30實線,10跳過,如此反複,count表示使用lengths裡
CGFloat lengths[] = {,,};
CGContextSetLineDash(ctx, , lengths, );//lengths中前兩個數字有效
CGFloat lengths[] = {,,};
CGContextSetLineDash(ctx, , lengths, );
//30實作,10跳過,5實線,30跳過,10實線,5跳過,如此虛幻,所有虛線最好設定lengths個數為偶數比較合适
CGFloat lengths[] = {,,};
CGContextSetLineDash(ctx, , lengths, );
//設定了phase,虛線想做以15,後面自動補全
描繪路徑的方法
Function | Description |
---|---|
| 描繪目前路徑 |
| 描繪一個指定的矩形 |
| 描繪一個指定的矩形并指定線的寬度 |
| 描繪一個指定的矩形的内切橢圓 |
| 描繪幾個線段,數組必須是偶數,每一對包括線段的起點和終點 |
| 設定描繪模式,比如劃線、填充等 |
——————— 填充路徑 ————————
填充有兩種規則:
- 上圖左邊圖形Winding-number規則,假設逆時針方向圈數加1,順時針防線減1,看不中不同封閉區域的圈數,圈數為0的區域不填充,圈數不為0的區域填充。
- 上圖右邊Even-odd規則,圈數是奇數就填充,圈數是偶數就不填充,方向不會影響填充結果
Function | Description |
---|---|
| 使用Even-odd規則填充 |
| 使用Winding-number規則填充 |
| 畫一個矩形,并使用Winding-number規則填充改矩形 |
| 畫若幹矩形,并使用Winding-number規則填充這些矩形 |
| 畫一個内切圓,并使用Winding-number規則填充改圓 |
| 指定模式繪畫,如果選擇 則使用 Winding-number規則填充,如果選擇 ,則使用Even-odd規則 |
————————— 剪切路徑 —————————
你可以通過設定剪切區域來顯示某個特定區域的圖像,多用于圖檔剪裁
//需要先設定剪切區域在繪圖,剪切區域是繪制屬性,和填充顔色、線條寬度一樣,需要先設定再使用
CGContextAddArc(ctx, , , , , M_PI*, );
CGContextEOClip(ctx);
CGContextFillRect(ctx, rect);
Function | Description |
---|---|
| 使用Winding-number規則剪切 |
| 使用Even-odd規則剪切 |
| 畫一個矩形,并使用Winding-number規則jin進行剪切 |
| 畫若幹矩形,并使用Winding-number規則剪切 |
| 使用圖檔作為遮罩剪切 |