天天看點

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

使用圖檔作為遮罩剪切