天天看点

Quartz 2D3. 路径的创建和绘画

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坐标系

Quartz 2D3. 路径的创建和绘画
Quartz 2D3. 路径的创建和绘画

2.1 在iOS中获取画布

  • 在UIView中实现drawRect:方法,视图会自动配置绘画环境,你可以直接在这个方法里进行绘画,系统已经自动将坐标系转换与UIKit相同的坐标系
  • 使用UIGraphicsGetCurrentContext获取CGContectRef对象,拿到画布进行绘画

2.2 在MacOS中获取画布

这里需要自己转换坐标系

2.3 创建PDF画布

**有两种方式创建PDF画布

  1. 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, , , , , , );
           
Quartz 2D3. 路径的创建和绘画
//使用CGContextAddQuadCurveToPoint创建曲线要有起点终点和一个控制点
    CGContextMoveToPoint(ctx, , );
    CGContextAddQuadCurveToPoint(ctx, , , , );
           
Quartz 2D3. 路径的创建和绘画

闭合路径:使用CGContextClosePath方法可以将当前路径封闭,系统会从当前点画一条直线指向路径起始点

椭圆:调用

CGContextAddEllipseInRect

方法,指定一个矩形的的frame,创建矩形的内切圆

矩形:调用

CGContextAddRect

方法即可

——————— 创建路径 ———————

  1. 创建一个路径之前需要先调用

    CGContextBeginPath

  2. 指定路径的起点,必须调用

    CGContextMoveToPoint

    方法指定起点,让后才可以添加直线、弧线和曲线等
  3. 如果想要闭合路径需要调用

    CGContextClosePath

    方法,调用该方法后,默认后面所画的就是一条新的路径
  4. 画弧线是,系统会自动在当前点和弧线的起始点之间画一条直线,弧线的终点变为当前点
  5. 添加椭圆或者矩形就是添加一个封闭的路径
  6. 创建路径并不是绘画路径,想要绘画路径必须调用用于绘画的方法,填充或者画线

注:如果想要重复绘画路径就需要把路径保存,这是可以使用

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
Quartz 2D3. 路径的创建和绘画
Round join
Quartz 2D3. 路径的创建和绘画
Bevel join
Quartz 2D3. 路径的创建和绘画

Line cap styles

Style Appearance
Butt cap
Quartz 2D3. 路径的创建和绘画
Round cap
Quartz 2D3. 路径的创建和绘画
Projecting square cap
Quartz 2D3. 路径的创建和绘画

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里
           
Quartz 2D3. 路径的创建和绘画
CGFloat lengths[] = {,,};
CGContextSetLineDash(ctx, , lengths, );//lengths中前两个数字有效
           
Quartz 2D3. 路径的创建和绘画
CGFloat lengths[] = {,,};
CGContextSetLineDash(ctx, , lengths, );
//30实现,10跳过,5实线,30跳过,10实线,5跳过,如此虚幻,所有虚线最好设置lengths个数为偶数比较合适
           
Quartz 2D3. 路径的创建和绘画
CGFloat lengths[] = {,,};
CGContextSetLineDash(ctx, , lengths, );
//设置了phase,虚线想做以15,后面自动补全
           
Quartz 2D3. 路径的创建和绘画

描绘路径的方法

Function Description

CGContextStrokePath

描绘当前路径

CGContextStrokeRect

描绘一个指定的矩形

CGContextStrokeRectWithWidth

描绘一个指定的矩形并指定线的宽度

CGContextStrokeEllipseInRect

描绘一个指定的矩形的内切椭圆

CGContextStrokeLineSegments

描绘几个线段,数组必须是偶数,每一对包括线段的起点和终点

CGContextDrawPath

设置描绘模式,比如划线、填充等

——————— 填充路径 ————————

Quartz 2D3. 路径的创建和绘画

填充有两种规则:

  • 上图左边图形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, , , , , M_PI*, );
CGContextEOClip(ctx);

CGContextFillRect(ctx, rect);
           
Function Description

CGContextClip

使用Winding-number规则剪切

CGContextEOClip

使用Even-odd规则剪切

CGContextClipToRect

画一个矩形,并使用Winding-number规则jin进行剪切

CGContextClipToRects

画若干矩形,并使用Winding-number规则剪切

CGContextClipToMask

使用图片作为遮罩剪切