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规则剪切 |
| 使用图片作为遮罩剪切 |