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坐标系
2.1 在iOS中获取画布
在UIView中实现drawRect:方法,视图会自动配置绘画环境,你可以直接在这个方法里进行绘画,系统已经自动将坐标系转换与UIKit相同的坐标系
使用UIGraphicsGetCurrentContext获取CGContectRef对象,拿到画布进行绘画
CGContextRef ctxRef = UIGraphicsGetCurrentContext();
2.2 在MacOS中获取画布
CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];
这里需要自己转换坐标系
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 (0, 0, myPageWidth, myPageHeight);
myPDFContext = MyPDFContextCreate (&mediaBox, CFSTR("test.pdf"));
CFStringRef myKeys[1];
CFTypeRef myValues[1];
myKeys[0] = kCGPDFContextMediaBox;
myValues[0] = (CFTypeRef) CFDataCreate(NULL,(const UInt8 *)&mediaBox, sizeof (CGRect));
CFDictionaryRef pageDictionary = CFDictionaryCreate(NULL, (const void **) myKeys,(const void **) myValues,1,&kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks);
CGPDFContextBeginPage(myPDFContext, &pageDictionary);
// ********** Your drawing code here **********
CGContextSetRGBFillColor (myPDFContext, 1, 0, 0, 1);
CGContextFillRect (myPDFContext, CGRectMake (0, 0, 200, 100 ));
CGContextSetRGBFillColor (myPDFContext, 0, 0, 1, .5);
CGContextFillRect (myPDFContext, CGRectMake (0, 0, 100, 200 ));
CGPDFContextEndPage(myPDFContext);
CFRelease(pageDictionary);
CFRelease(myValues[0]);
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 * 4);
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,
8, // 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, 0, 0);
线:画一条线需要有一个起点和一个终点,代用方法 CGContextAddLineToPoint
来制定一个终点,也可以调用 CGContextAddLines
添加多条线,数组中第一个点是起点,其他的都是终点
//这是画一条线的完整代码
CGContextMoveToPoint(ctx, 0, 0); //指定起点
CGContextAddLineToPoint(ctx, 10, 10); //指定终点
//这是添加多条线
CGPoint points[] = {CGPointMake(50, 50),CGPointMake(100, 50),CGPointMake(100, 100),CGPointMake(50, 100)};
CGContextAddLines(ctx, points, 4);
**弧:**Quartz提供了两个方法创建弧, CGContextAddArc
和CGContextAddArcToPoint
//需要给一个圆的中心点x、y坐标和圆的半径,还有圆的起始角度和结束角度,以及一个顺时针逆时针参数,默认情况下1代表顺时针,0代表逆时针,但是在UIView的drawRect方法里由于系统自动旋转了坐标系,此时0代表顺时针,1代表逆时针
CGContextAddArc(ctx, 100, 100, 50, 0, M_PI_4*3, 1);
//CGContextAddArcToPoint需要一个当前点和两个弧线的点以及一个圆半径,不建议使用此方法创建弧线,计算难度大
CGContextMoveToPoint(ctx, 80, 50);
CGContextAddArcToPoint(ctx, 50, 50, 80, 100, 100);
曲线:
//使用CGContextAddCurveToPoint创建需要有一个起点一个终点还有两个控制点,示意图如下
CGContextMoveToPoint(ctx, 50, 100);
CGContextAddCurveToPoint(ctx, 60, 30, 80, 150, 90, 100);
//使用CGContextAddQuadCurveToPoint创建曲线要有起点终点和一个控制点
CGContextMoveToPoint(ctx, 50, 100);
CGContextAddQuadCurveToPoint(ctx, 80, 50, 100, 100);
闭合路径:使用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[] = {30,10};
CGContextSetLineDash(ctx, 0, lengths, 2);
//lengths是里是虚线实现交替,30实线,10跳过,30实线,10跳过,如此反复,count表示使用lengths里
CGFloat lengths[] = {30,10,5};
CGContextSetLineDash(ctx, 0, lengths, 2);//lengths中前两个数字有效
CGFloat lengths[] = {30,10,5};
CGContextSetLineDash(ctx, 0, lengths, 3);
//30实现,10跳过,5实线,30跳过,10实线,5跳过,如此虚幻,所有虚线最好设置lengths个数为偶数比较合适
CGFloat lengths[] = {30,10,5};
CGContextSetLineDash(ctx, 15, lengths, 3);
//设置了phase,虚线想做以15,后面自动补全
描绘路径的方法
Function | Description |
---|---|
CGContextStrokePath | 描绘当前路径 |
CGContextStrokeRect | 描绘一个指定的矩形 |
CGContextStrokeRectWithWidth | 描绘一个指定的矩形并指定线的宽度 |
CGContextStrokeEllipseInRect | 描绘一个指定的矩形的内切椭圆 |
CGContextStrokeLineSegments | 描绘几个线段,数组必须是偶数,每一对包括线段的起点和终点 |
CGContextDrawPath | 设置描绘模式,比如划线、填充等 |
——————— 填充路径 ————————
填充有两种规则:
- 上图左边图形Winding-number规则,假设逆时针方向圈数加1,顺时针防线减1,看不中不同封闭区域的圈数,圈数为0的区域不填充,圈数不为0的区域填充。
- 上图右边Even-odd规则,圈数是奇数就填充,圈数是偶数就不填充,方向不会影响填充结果
Function | Description |
---|---|
CGContextEOFillPath | 使用Even-odd规则填充 |
CGContextFillPath | 使用Winding-number规则填充 |
CGContextFillRect | 画一个矩形,并使用Winding-number规则填充改矩形 |
CGContextFillRects | 画若干矩形,并使用Winding-number规则填充这些矩形 |
CGContextFillEllipseInRect | 画一个内切圆,并使用Winding-number规则填充改圆 |
CGContextDrawPath | 指定模式绘画,如果选择 kCGPathFillStroke 则使用 Winding-number规则填充,如果选择kCGPathEOFillStroke ,则使用Even-odd规则 |
————————— 剪切路径 —————————
你可以通过设置剪切区域来显示某个特定区域的图像,多用于图片剪裁
//需要先设置剪切区域在绘图,剪切区域是绘制属性,和填充颜色、线条宽度一样,需要先设置再使用
CGContextAddArc(ctx, 150, 200, 80, 0, M_PI*2, 0);
CGContextEOClip(ctx);
CGContextFillRect(ctx, rect);
Function | Description |
---|---|
CGContextClip | 使用Winding-number规则剪切 |
CGContextEOClip | 使用Even-odd规则剪切 |
CGContextClipToRect | 画一个矩形,并使用Winding-number规则jin进行剪切 |
CGContextClipToRects | 画若干矩形,并使用Winding-number规则剪切 |
CGContextClipToMask | 使用图片作为遮罩剪切 |