Quartz 2D編程指南 (十) —— 漸變(一)

版本記錄

版本號 時間
V1.0 2018.09.06

前言

Quartz 2D框架相信大家都知道,也都一直在使用。Quartz 2D的API是純C語言的,它是一個二維繪圖引擎,同時支持iOS和Mac系統。Quartz 2D的API來自于Core Graphics框架,數據類型和函數基本都以CG作為前綴,接下來幾篇我們就一起來看一下這個框架。感興趣可以看上面幾篇文章。
1. Quartz 2D編程指南 (一) —— 簡介(一)
2. Quartz 2D編程指南 (二) —— Quartz 2D概覽(二)
3. Quartz 2D編程指南 (三) —— 圖形上下文(三)
4. Quartz 2D編程指南 (四) —— Paths路徑(一)
5. Quartz 2D編程指南 (五) —— Paths路徑(二)
6. Quartz 2D編程指南 (六) —— 顏色和顏色空間(一)
7. Quartz 2D編程指南 (七) —— 變換(一)
8. Quartz 2D編程指南 (八) —— Patterns圖案樣式(一)
9. Quartz 2D編程指南 (九) —— 陰影(一)

Gradients - 漸變

Quartz提供了兩種不透明的數據類型來創建漸變 - CGShadingRefCGGradientRef。您可以使用其中任何一個來創建軸向或徑向漸變。漸變是從一種顏色到另一種顏色不同的填充。

軸向梯度(也稱為線性梯度)(axial gradient (also called a linear gradient))沿著兩個限定的端點之間的軸變化。位于垂直于軸的直線上的所有點具有相同的顏色值。

徑向梯度(radial gradient)是沿兩個限定端之間的軸徑向變化的填充,其通常是兩個圓。如果點位于中心點落在軸上的圓周上,則它們共享相同的顏色值。梯度的圓形截面的半徑由端圓的半徑限定;每個中間圓的半徑從一端到另一端線性變化。

本章提供了可以使用Quartz創建的各種線性和徑向漸變的示例,比較了繪制漸變時可以采用的兩種方法,然后展示了如何使用每種不透明數據類型來創建漸變。


Axial and Radial Gradient Examples - 軸向和徑向漸變示例

Quartz函數提供了豐富的詞匯表來創建漸變效果。 本節介紹了您可以實現的一些結果。 圖8-1中的軸向漸變在一個端點(橙色陰影)和另一個端點(黃色陰影)之間變化。 在這種情況下,軸相對于原點成45度角。

Figure 8-1 An axial gradient along a 45 degree axis

Quartz還允許您沿軸指定顏色和位置,以創建更復雜的軸向漸變,如圖8-2所示。 起點處的顏色為紅色,終點處的顏色為紫色。 但是,軸上還有五個位置,其顏色分別設置為橙色,黃色,綠色,藍色和靛藍。 您可以將結果視為沿同一軸的六個連續線性漸變。 雖然這里使用的軸與圖8-1(45度角)中使用的軸相同,但并非必須如此。 軸的角度由您提供的起點和終點定義。

Figure 8-2 An axial gradient created with seven locations and colors

圖8-3顯示了一個徑向漸變,它在一個小的亮紅色圓圈和一個較大的黑色圓圈之間變化。

Figure 8-3 A radial gradient that varies between two circles

使用Quartz,您不僅可以根據顏色變化創建漸變;您也可以根據alpha改變去創建,或者可以改變alpha和其他顏色組件。 圖8-4顯示了一個漸變,當alpha值從1.0到0.1變化時,紅色,綠色和藍色分量保持不變。

注意:如果使用alpha更改漸變,則在繪制到PDF內容時將無法捕獲該漸變。 因此,不能打印這樣的漸變。 如果需要為PDF繪制漸變,請使用1.0的alpha。

Figure 8-4 A radial gradient created by varying only the alpha component

您可以將圓放置在徑向漸變中以創建各種形狀。 如果一個圓部分或完全位于另一個圓之外,則Quartz為具有不等圓周的圓形創建圓錐曲面,為圓周具有相等圓周的圓柱曲面創建圓錐曲面。 徑向漸變的常見用法是創建陰影球體,如圖8-5所示。 在這種情況下,單個點(半徑為0的圓)位于較大的圓內。

Figure 8-5 A radial gradient that varies between a point and a circle

您可以通過嵌套幾個類似于圖8-6中所示形狀的徑向漸變來創建更復雜的效果。 形狀的環形部分使用同心圓形成。

Figure 8-6 Nested radial gradients

A Comparison of CGShading and CGGradient Objects - CGShading和CGGradient對象的比較

有兩種類型的對象可用于創建漸變,您可能想知道哪一個最適合使用。本節有助于回答這個問題。

CGShadingRef不透明數據類型使您可以控制如何計算漸變中每個點的顏色。在創建CGShading對象之前,必須創建一個CGFunction對象(CGFunctionRef),該對象定義用于計算漸變中顏色的函數。編寫自定義函數可讓您自由創建平滑漸變,如圖8-1,圖8-3和圖8-5所示,或更多非常規效果,如圖8-12所示。

創建CGShading對象時,可以指定它是軸向(線性)還是徑向。與漸變計算函數(封裝為CGFunction對象)一起,您還可以提供顏色空間,起點和終點,具體取決于您是繪制軸向還是徑向漸變。在繪圖時,您只需將CGShading對象與繪圖上下文一起傳遞給函數CGContextDrawShading。 Quartz為漸變中的每個點調用漸變計算函數。

CGGradient對象是CGShading對象的一個??子集,其設計考慮了易用性。 CGGradientRef不透明數據類型很容易使用,因為Quartz會為您計算漸變中每個點的顏色 - 您不提供漸變計算函數。創建漸變對象時,可以提供位置和顏色的數組。 Quartz使用您為每個位置指定的顏色作為漸變的終點,為每組連續位置計算漸變。您可以將漸變對象設置為使用單個起始和結束位置,如圖8-1所示,或者您可以提供許多點來創建類似于圖8-2中所示的效果。提供兩個以上位置的能力優于使用CGShading對象,后者僅限于兩個位置。

創建CGGradient對象時,只需為每個位置設置顏色空間,位置和顏色。使用漸變對象繪制上下文時,可以指定Quartz是應繪制軸向還是徑向漸變。在繪制時,您可以指定起始點和結束點或半徑,具體取決于您是繪制軸向還是徑向漸變,與CGShading對象不同,CGShading對象的幾何圖形是在創建時定義的,而不是在繪制時定義的。

表8-1總結了兩種不透明數據類型之間的差異。

Table 8-1 Differences between CGShading and CGGradient objects


Extending Color Beyond the End of a Gradient - 在漸變結束之外延伸顏色

創建漸變時,可以選擇使用純色填充漸變末端之外的空間。 Quartz使用在漸變邊界處定義的顏色作為填充顏色。 您可以延伸到漸變的開始,漸變的結束或兩者。 您可以將該選項應用于使用CGShading對象或CGGradient對象創建的軸向或徑向漸變。 每種類型的對象都提供了可用于設置擴展選項的常量,如Using a CGGradient ObjectUsing a CGShading Object中所示。

圖8-7顯示了在起始位置和結束位置都延伸的軸向梯度。 圖中的線顯示了漸變的軸。 如您所見,填充顏色對應于起點和終點的顏色。

Figure 8-7 Extending an axial gradient

圖8-8將不使用擴展選項的徑向漸變與使用擴展選項的徑向漸變比較起始位置和結束位置。 Quartz采用起始和結束顏色值,并使用這些純色來擴展表面,如圖所示。 該圖顯示了起始圓和結束圓,以及漸變的軸。

Figure 8-8 Extending a radial gradient

Using a CGGradient Object - 使用CGGradient對象

CGGradient對象是漸變的抽象定義 - 它只是指定顏色和位置,而不是幾何。您可以將同一個對象用于軸向和徑向幾何。作為一個抽象定義,CGGradient對象可能比其對應的CGShading對象更容易重用。沒有將幾何體鎖定在CGGradient對象中允許基于相同的顏色方案迭代地繪制漸變的可能性,而不需要在多個CGGradient對象中占用存儲器資源。

因為Quartz為您計算漸變,使用CGGradient對象創建和繪制漸變非常簡單,需要以下步驟:

  • 1) 創建一個CGGradient對象,提供一個顏色空間,一個包含兩個或多個顏色組件的數組,一個包含兩個或多個位置的數組,以及兩個或多個數組中每個數組中的項目數。
  • 2) 通過調用CGContextDrawLinearGradientCGContextDrawRadialGradient并提供上下文,CGGradient對象,繪圖選項以及說明和結束幾何(軸向漸變點或圓心和徑向漸變的半徑)來繪制漸變。
  • 3) 不再需要時釋放CGGradient對象。

位置是CGFloat值,范圍為0.0到1.0(包括0.0和1.0),用于指定沿梯度軸的標準化距離。值0.0指定軸的起點,而1.0指定軸的終點。其他值指定距離的比例,例如距離起點的距離的四分之一為0.25,對于軸的中間點為0.5。 Quartz至少使用兩個位置。如果為位置數組傳遞NULL,則Quartz對第一個位置使用0,對第二個位置使用1。

每種顏色的顏色分量數量取決于顏色空間。對于屏幕繪圖,您將使用RGB顏色空間。由于Quartz使用alpha繪制,因此每個屏幕顏色都有四個組件 - 紅色,綠色,藍色和alpha。因此,對于屏幕繪圖,您提供的顏色分量數組中的元素數必須包含四倍的位置數。 Quartz RGBA顏色分量的值可以在0.0到1.0之間變化,包括0.0和1.0。

Listing 8-1是一個創建CGGradient對象的代碼片段。在聲明必要的變量之后,代碼設置位置和必要數量的顏色分量(對于此示例,2 X 4 = 8)。它創建了一個通用的RGB色彩空間。 (在iOS中,通用RGB顏色空間不可用,您的代碼應該調用CGColorSpaceCreateDeviceRGB。)然后,它將必要的參數傳遞給函數CGGradientCreateWithColorComponents。您還可以使用函數CGGradientCreateWithColors,這在您的應用程序設置CGColor對象時很方便。

// Listing 8-1  Creating a CGGradient object

CGGradientRef myGradient;
CGColorSpaceRef myColorspace;
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGFloat components[8] = { 1.0, 0.5, 0.4, 1.0,  // Start color
                          0.8, 0.8, 0.3, 1.0 }; // End color
 
myColorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
myGradient = CGGradientCreateWithColorComponents (myColorspace, components,
                          locations, num_locations);

創建CGGradient對象后,可以使用它來繪制軸向或線性漸變。 Listing 8-2是一個代碼片段,它聲明并設置線性漸變的起點和終點,然后繪制漸變。 結果如圖8-1所示。 該代碼未顯示如何獲取CGContext對象(myContext)

// Listing 8-2  Painting an axial gradient using a CGGradient object

CGPoint myStartPoint, myEndPoint;
myStartPoint.x = 0.0;
myStartPoint.y = 0.0;
myEndPoint.x = 1.0;
myEndPoint.y = 1.0;
CGContextDrawLinearGradient (myContext, myGradient, myStartPoint, myEndPoint, 0);

Listing 8-3是一個代碼片段,它使用Listing 8-1中創建的CGGradient對象繪制圖8-9中所示的徑向漸變。 此示例說明了通過填充純色來擴展漸變區域的結果。

// Listing 8-3  Painting a radial gradient using a CGGradient object

CGPoint myStartPoint, myEndPoint;
CGFloat myStartRadius, myEndRadius;
myStartPoint.x = 0.15;
myStartPoint.y = 0.15;
myEndPoint.x = 0.5;
myEndPoint.y = 0.5;
myStartRadius = 0.1;
myEndRadius = 0.25;
CGContextDrawRadialGradient (myContext, myGradient, myStartPoint,
                         myStartRadius, myEndPoint, myEndRadius,
                         kCGGradientDrawsAfterEndLocation);
Figure 8-9 A radial gradient painted using a CGGradient object

圖8-4中顯示的徑向漸變是使用Listing 8-4中所示的變量創建的。

// Listing 8-4  The variables used to create a radial gradient by varying alpha

CGPoint myStartPoint, myEndPoint;
CGFloat myStartRadius, myEndRadius;
myStartPoint.x = 0.2;
myStartPoint.y = 0.5;
myEndPoint.x = 0.65;
myEndPoint.y = 0.5;
myStartRadius = 0.1;
myEndRadius = 0.25;
size_t num_locations = 2;
CGFloat locations[2] = { 0, 1.0 };
CGFloat components[8] = { 0.95, 0.3, 0.4, 1.0,
                          0.95, 0.3, 0.4, 0.1 };

Listing 8-5顯示了用于創建圖8-10中所示的灰色漸變的變量,它具有三個位置。

// Listing 8-5  The variables used to create a gray gradient

size_t num_locations = 3;
CGFloat locations[3] = { 0.0, 0.5, 1.0};
CGFloat components[12] = {  1.0, 1.0, 1.0, 1.0,
                            0.5, 0.5, 0.5, 1.0,
                            1.0, 1.0, 1.0, 1.0 };
Figure 8-10 An axial gradient with three locations

Using a CGShading Object - 使用CGShading對象

您可以通過創建調用函數CGShadingCreateAxialCGShadingCreateRadialCGShading對象來設置漸變,并提供以下參數:

  • 一個CGColorSpace對象,描述Quartz在解釋回調提供的顏色分量值時使用的顏色空間。
  • 起點和終點。對于軸向梯度,這些是軸的起始坐標和結束坐標(在用戶空間中)。對于徑向漸變,這些是起始圓和結束圓的中心坐標。用于定義漸變區域的圓的起點和終點半徑(僅適用于徑向漸變)。
  • 一個CGFunction對象,可以通過調用函數CGFunctionCreate獲得,本節稍后將對此進行討論。此回調例程必須返回一個顏色以在特定點繪制。
  • 布爾值,指定是否使用純色填充起點或終點之外的區域。

您提供給CGShading創建函數的CGFunction對象包含一個回調結構以及Quartz實現回調所需的所有信息。也許設置CGShading對象最棘手的部分是創建CGFunction對象。當您調用函數CGFunctionCreate時,您提供以下內容:

  • 指向回調所需數據的指針。
  • 回調的輸入值數。 Quartz要求你的回調需要一個輸入值。
  • 一組浮點值。 Quartz只為此數組中的一個元素提供回調。對于漸變開始時的顏色,輸入值的范圍可以是0,對于漸變結束時的顏色,輸入值的范圍可以是1。
  • 回調提供的輸出值的數量。對于每個輸入值,回調必須為每個顏色分量提供一個值,并為要指定不透明度的alpha值提供。顏色組件值由Quartz在您創建的顏色空間中解釋并提供給CGShading創建函數。例如,如果使用RGB顏色空間,則提供值4作為輸出值(R,G,B和A)的數量。
  • 一組浮點值,用于指定每個顏色分量和alpha值。
  • 一個回調數據結構,包含結構的版本(將此字段設置為0),用于生成顏色組件值的回調,以及用于釋放info參數中提供給回調的數據的可選回調。如果你要將你的回調命名為myCalculateShadingValues,它將如下所示:
void myCalculateShadingValues(void * info,const CGFloat * in,CGFloat * out)

創建CGShading對象后,如果需要,可以設置其他剪輯。然后,調用函數CGContextDrawShading以使用漸變繪制上下文的剪切區域。當您調用此函數時,Quartz會調用您的回調以獲取跨越從起點到終點范圍的顏色值。

當您不再需要CGShading對象時,可以通過調用函數CGShadingRelease來釋放它。

Painting an Axial Gradient Using a CGShading ObjectPainting a Radial Gradient Using a CGShading Object提供有關編寫使用CGShading對象繪制漸變的代碼的分步說明。

1. Painting an Axial Gradient Using a CGShading Object - 使用CGShading對象繪制軸向漸變

軸向和徑向漸變需要您執行類似的步驟。 此示例顯示如何使用CGShading對象繪制軸向漸變,在圖形上下文中創建半圓形剪切路徑,然后將漸變繪制到剪切的上下文以實現圖8-11中所示的輸出。

Figure 8-11 An axial gradient that is clipped and painted

要繪制圖中所示的軸向漸變,請按照以下部分中說明的步驟操作:

(1) Set Up a CGFunction Object to Compute Color Values - 設置CGFunction對象以計算顏色值

您可以按照自己喜歡的方式計算顏色值,只要您的顏色計算函數有三個參數:

  • void *info。這是NULL或指向傳遞給CGShading創建函數的數據的指針。
  • const CGFloat * in。 Quartz將in數組傳遞給你的回調。數組中的值必須位于為CGFunction對象定義的輸入值范圍內。對于此示例,輸入范圍是0到1;請參見Listing 8-7
  • CGFloat * out。你的回調將out數組傳遞給Quartz。它包含顏色空間中每個顏色分量的一個元素和一個alpha值。輸出值應該在為CGFunction對象定義的輸出值范圍內。對于此示例,輸出范圍是0到1;請參見Listing 8-7

有關這些參數的更多信息,請參閱CGFunctionEvaluateCallback

Listing 8-6顯示了一個函數,它通過將常量數組中定義的值乘以輸入值來計算顏色分量值。因為輸入值的范圍是0到1,所以輸出值的范圍從黑色(對于RGB,值0,0,0)到通過(1,0,.5)是紫色色調。請注意,最后一個組件始終設置為1,因此顏色始終是完全不透明的。

// Listing 8-6  Computing color component values
 
static void myCalculateShadingValues (void *info,
                            const CGFloat *in,
                            CGFloat *out)
{
    CGFloat v;
    size_t k, components;
    static const CGFloat c[] = {1, 0, .5, 0 };
 
    components = (size_t)info;
 
    v = *in;
    for (k = 0; k < components -1; k++)
        *out++ = c[k] * v;
     *out++ = 1;
}

在編寫回調以計算顏色值之后,將其打包為CGFunction對象的一部分。 它是您在創建CGShading對象時提供給Quartz的CGFunction對象。 Listing 8-7顯示了一個創建CGFunction對象的函數,該對象包含Listing 8-6中的回調。 列表后面會顯示每個編號行代碼的詳細說明。

// Listing 8-7  Creating a CGFunction object
 
static CGFunctionRef myGetFunction (CGColorSpaceRef colorspace)// 1
{
    size_t numComponents;
    static const CGFloat input_value_range [2] = { 0, 1 };
    static const CGFloat output_value_ranges [8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
    static const CGFunctionCallbacks callbacks = { 0,// 2
                                &myCalculateShadingValues,
                                NULL };
 
    numComponents = 1 + CGColorSpaceGetNumberOfComponents (colorspace);// 3
    return CGFunctionCreate ((void *) numComponents, // 4
                                1, // 5
                                input_value_range, // 6
                                numComponents, // 7
                                output_value_ranges, // 8
                                &callbacks);// 9
}

這是代碼的作用:

  • 1)將顏色空間作為參數。
  • 2)聲明一個回調結構并用structure(0)的版本填充它,指向顏色組件計算回調的指針,以及可選釋放函數的NULL
  • 3)計算顏色空間中顏色分量的數量,并將值遞增1以計算alpha值。
  • 4)傳遞指向numComponents值的指針。回調myCalculateShadingValues使用此值來確定要計算的組件數。
  • 5)指定1是回調的輸入值的數量。
  • 6)提供一個數組,指定輸入的有效間隔。該數組包含0和1。
  • 7)傳遞輸出值的數量,即顏色分量加上alpha的數量。
  • 8)提供一個數組,指定每個輸出值的有效間隔。此數組為每個組件指定間隔0和1.因為有四個組件,所以此數組中有八個元素。
  • 9)傳遞指向先前聲明和填充的回調結構的指針。

(2) Create a CGShading Object for an Axial Gradient - 為軸向漸變創建CGShading對象

要創建CGShading對象,可以調用函數CGShadingCreateAxial,如Listing 8-8所示,傳遞顏色空間,起點和終點,CGFunction對象和布爾值,該值指定是否填充開始和結束之外的區域漸變點。

// Listing 8-8  Creating a CGShading object for an axial gradient

CGPoint     startPoint,
            endPoint;
CGFunctionRef myFunctionObject;
CGShadingRef myShading;
 
startPoint = CGPointMake(0,0.5);
endPoint = CGPointMake(1,0.5);
colorspace = CGColorSpaceCreateDeviceRGB();
myFunctionObject = myGetFunction (colorspace);
 
myShading = CGShadingCreateAxial (colorspace,
                        startPoint, endPoint,
                        myFunctionObject,
                        false, false);

(3) Clip the Context - 裁剪上下文

當您繪制漸變時,Quartz會填充當前上下文。 繪制漸變與使用顏色和圖案不同,顏色和圖案用于描邊和填充路徑對象。 因此,如果希望漸變顯示在特定形狀中,則需要相應地剪切上下文。 Listing 8-9中的代碼在當前上下文中添加了一個半圓,以便將漸變繪制到該剪輯區域中,如圖8-11所示。

如果你仔細觀察,你會注意到代碼應該產生一個半圓,而圖中顯示的是半橢圓。 為什么? 當您在A Complete Routine for an Axial Gradient Using a CGShading Object中查看整個例程時,您將看到上下文也被縮放。 稍后會詳細介紹。 雖然您可能不需要在應用程序中應用縮放或剪輯,但Quartz 2D中存在這些和許多其他選項可幫助您實現有趣的效果。

// Listing 8-9  Adding a semicircle clip to the graphics context

    CGContextBeginPath (myContext);
    CGContextAddArc (myContext, .5, .5, .3, 0,
                    my_convert_to_radians (180), 0);
    CGContextClosePath (myContext);
    CGContextClip (myContext);

(4)Paint the Axial Gradient Using a CGShading Object - 使用CGShading對象繪制軸向漸變

調用函數CGContextDrawShading以使用CGShading對象中指定的顏色漸變填充當前上下文:

CGContextDrawShading (myContext, myShading);

(5) Release Objects - 釋放對象

當您不再需要CGShading對象時,可以調用函數CGShadingRelease。 您還需要釋放CGColorSpace對象和CGFunction對象,如Listing 8-10所示。

// Listing 8-10  Releasing objects

CGShadingRelease (myShading);
CGColorSpaceRelease (colorspace);
CGFunctionRelease (myFunctionObject);

3. A Complete Routine for an Axial Gradient Using a CGShading Object - 使用CGShading對象的軸向漸變的完整例程

Listing 8-11中的代碼顯示了一個完整的例程,該例程使用Listing 8-7中設置的CGFunction對象和Listing 8-6中顯示的回調來繪制軸向漸變。 列表后面會顯示每個編號行代碼的詳細說明。

// Listing 8-11  Painting an axial gradient using a CGShading object

void myPaintAxialShading (CGContextRef myContext,// 1
                            CGRect bounds)
{
    CGPoint     startPoint,
                endPoint;
    CGAffineTransform myTransform;
    CGFloat width = bounds.size.width;
    CGFloat height = bounds.size.height;
 
 
    startPoint = CGPointMake(0,0.5); // 2
    endPoint = CGPointMake(1,0.5);// 3
 
    colorspace = CGColorSpaceCreateDeviceRGB();// 4
    myShadingFunction = myGetFunction(colorspace);// 5
 
    shading = CGShadingCreateAxial (colorspace, // 6
                                 startPoint, endPoint,
                                 myShadingFunction,
                                 false, false);
 
    myTransform = CGAffineTransformMakeScale (width, height);// 7
    CGContextConcatCTM (myContext, myTransform);// 8
    CGContextSaveGState (myContext);// 9
 
    CGContextClipToRect (myContext, CGRectMake(0, 0, 1, 1));// 10
    CGContextSetRGBFillColor (myContext, 1, 1, 1, 1);
    CGContextFillRect (myContext, CGRectMake(0, 0, 1, 1));
 
    CGContextBeginPath (myContext);// 11
    CGContextAddArc (myContext, .5, .5, .3, 0,
                        my_convert_to_radians (180), 0);
    CGContextClosePath (myContext);
    CGContextClip (myContext);
 
    CGContextDrawShading (myContext, shading);// 12
    CGColorSpaceRelease (colorspace);// 13
    CGShadingRelease (shading);
    CGFunctionRelease (myShadingFunction);
 
    CGContextRestoreGState (myContext); // 14
}

這是代碼的作用:

  • 1)將參數作為圖形上下文和要繪制的矩形。
  • 2)為起點指定一個值。例程根據用戶空間計算值,該用戶空間從0到1不等。您將稍后為Quartz繪制的窗口縮放空間。您可以將此坐標位置視為最左側的x和距離底部50%的y。
  • 3)為結束點指定一個值。您可以將此坐標位置視為最右側的x,y是底部的50%。如您所見,漸變的軸是水平線。
  • 4)為設備RGB創建顏色空間,因為此例程將繪制到顯示器。
  • 5)通過調用Listing 8-7中所示的例程并傳遞剛剛創建的顏色空間來創建CGFunction對象。
  • 6)為軸向漸變創建CGShading對象。最后兩個參數為false,表示Quartz不應填充起點和終點之外的區域。
  • 7)設置仿射變換,該變換縮放到用于繪制的窗口的高度和寬度。請注意,高度不一定等于寬度。在這個例子中,因為兩者不相等,最終結果是橢圓形而不是圓形。
  • 8)將剛剛設置的轉換與傳遞給例程的圖形上下文連接起來。
  • 9)保存圖形狀態,以便稍后恢復此狀態。
  • 10)設置剪切區域。此行和接下來的兩行將上下文剪切為填充白色的矩形。效果是將漸變繪制到具有白色背景的窗口。
  • 11)創建一個路徑。此行和接下來的三行設置一個半圓的圓弧,并將其作為剪切區域添加到圖形上下文中。效果是將漸變繪制到半個圓的區域。但是,圓圈??將通過窗口的高度和寬度進行變換(參見步驟8),從而產生繪制為半橢圓的漸變的最終效果。當窗口由用戶調整大小時,剪裁區域被調整大小。
  • 12)如前所述,將漸變繪制到圖形上下文,轉換和剪切漸變。
  • 13)釋放對象。此行和接下來的兩行將釋放您創建的所有對象。
  • 14)將圖形狀態恢復為設置填充背景之前存在的狀態并剪切為半圈。恢復的狀態仍然由窗口的寬度和高度轉換。

4. Painting a Radial Gradient Using a CGShading Object - 使用CGShading對象繪制徑向漸變

此示例顯示如何使用CGShading對象生成如圖8-12所示的輸出。

Figure 8-12 A radial gradient created using a CGShading object

要繪制徑向漸變,請按照以下各節中說明的步驟操作:

(1) Set Up a CGFunction Object to Compute Color Values - 設置CGFunction對象以計算顏色值

寫入函數以計算徑向和軸向梯度的顏色值之間沒有區別。 實際上,您可以按照Set Up a CGFunction Object to Compute Color Values設置軸向漸變。 Listing 8-12計算顏色,使顏色分量正弦變化,周期基于函數中聲明的頻率值。 圖8-12中顯示的結果與圖8-11中顯示的顏色完全不同。 盡管顏色輸出存在差異,但Listing 8-12中的代碼與Listing 8-6類似,因為每個函數都遵循相同的原型。 每個函數接受一個輸入值并計算N個值,一個用于顏色空間的每個顏色分量加上一個alpha值。

// Listing 8-12  Computing color component values

static void  myCalculateShadingValues (void *info,
                                const CGFloat *in,
                                CGFloat *out)
{
    size_t k, components;
    double frequency[4] = { 55, 220, 110, 0 };
    components = (size_t)info;
    for (k = 0; k < components - 1; k++)
        *out++ = (1 + sin(*in * frequency[k]))/2;
     *out++ = 1; // alpha
}

回想一下,在編寫顏色計算函數之后,需要創建一個CGFunction對象,如Set Up a CGFunction Object to Compute Color Values中的軸向值所述。

(2) Create a CGShading Object for a Radial Gradient - 為徑向漸變創建CGShading對象

要創建CGShading對象或徑向漸變,可以調用函數CGShadingCreateRadial,如Listing 8-13所示,傳遞顏色空間,起點和終點,開始和結束半徑,CGFunction對象和布爾值,以指定是否填充漸變的起點和終點之外的區域。

// Listing 8-13  Creating a CGShading object for a radial gradient

    CGPoint startPoint, endPoint;
    CGFloat startRadius, endRadius;
 
    startPoint = CGPointMake(0.25,0.3);
    startRadius = .1;
    endPoint = CGPointMake(.7,0.7);
    endRadius = .25;
    colorspace = CGColorSpaceCreateDeviceRGB();
    myShadingFunction = myGetFunction (colorspace);
    CGShadingCreateRadial (colorspace,
                    startPoint,
                    startRadius,
                    endPoint,
                    endRadius,
                    myShadingFunction,
                    false,
                    false);

(3) Paint a Radial Gradient Using a CGShading Object - 使用CGShading對象繪制徑向漸變

調用函數CGContextDrawShading使用CGShading對象中指定的指定顏色漸變填充當前上下文。

CGContextDrawShading(myContext,shading);

請注意,無論漸變是軸向還是徑向,都使用相同的函數繪制漸變。

(4) Release Objects - 釋放對象

當您不再需要CGShading對象時,可以調用函數CGShadingRelease。 您還需要釋放CGColorSpace對象和CGFunction對象,如Listing 8-14所示。

// Listing 8-14  Code that releases objects

CGShadingRelease (myShading);
CGColorSpaceRelease (colorspace);
CGFunctionRelease (myFunctionObject);

5. A Complete Routine for Painting a Radial Gradient Using a CGShading Object - 使用CGShading對象繪制徑向漸變的完整例程

Listing 8-15中的代碼顯示了一個完整的例程,它使用Listing 8-7中設置的CGFunction對象和Listing 8-12中顯示的回調繪制徑向漸變。 列表后面會顯示每個編號行代碼的詳細說明。

// Listing 8-15  A routine that paints a radial gradient using a CGShading object

void myPaintRadialShading (CGContextRef myContext,// 1
                            CGRect bounds);
{
    CGPoint startPoint,
            endPoint;
    CGFloat startRadius,
            endRadius;
    CGAffineTransform myTransform;
    CGFloat width = bounds.size.width;
    CGFloat height = bounds.size.height;
 
    startPoint = CGPointMake(0.25,0.3); // 2
    startRadius = .1;  // 3
    endPoint = CGPointMake(.7,0.7); // 4
    endRadius = .25; // 5
 
    colorspace = CGColorSpaceCreateDeviceRGB(); // 6
    myShadingFunction = myGetFunction (colorspace); // 7
 
    shading = CGShadingCreateRadial (colorspace, // 8
                            startPoint, startRadius,
                            endPoint, endRadius,
                            myShadingFunction,
                            false, false);
 
    myTransform = CGAffineTransformMakeScale (width, height); // 9
    CGContextConcatCTM (myContext, myTransform); // 10
    CGContextSaveGState (myContext); // 11
 
    CGContextClipToRect (myContext, CGRectMake(0, 0, 1, 1)); // 12
    CGContextSetRGBFillColor (myContext, 1, 1, 1, 1);
    CGContextFillRect (myContext, CGRectMake(0, 0, 1, 1));
 
    CGContextDrawShading (myContext, shading); // 13
    CGColorSpaceRelease (colorspace); // 14
    CGShadingRelease (shading);
    CGFunctionRelease (myShadingFunction);
 
    CGContextRestoreGState (myContext); // 15
}

這是代碼的作用:

  • 1)將參數作為圖形上下文和要繪制的矩形。
  • 2)為起始圓的中心指定一個值。例程根據用戶空間計算值,該用戶空間從0到1不等。您將稍后為Quartz繪制的窗口縮放空間。您可以將- 此坐標位置視為左側25%處的x和底部30%處的y。
  • 3)指定起始圓的半徑。您可以將其視為用戶空間寬度的10%。
  • 4)為結束圓的中心指定一個值。您可以將此坐標位置視為左側70%的x和底部70%的y。
  • 5)指定結束圓的半徑。您可以將其視為用戶空間寬度的25%。結束圓圈將大于起始圓圈。圓錐形狀將從左向右定向,向上傾斜。
  • 6)為設備RGB創建顏色空間,因為此例程將繪制到顯示器。
  • 7)通過調用Listing 8-7中所示的例程并傳遞剛剛創建的顏色空間來創建CGFunctionObject。但是,請記住,您將使用Listing 8-12中所示的顏色計算函數。
  • 8)為徑向漸變創建CGShading對象。最后兩個參數為false,表示Quartz不應填充漸變起點和終點之外的區域。
  • 9)設置仿射變換,該變換縮放到用于繪制的窗口的高度和寬度。請注意,高度不一定等于寬度。實際上,只要用戶調整窗口大小,轉換就會改變。
  • 10)將剛剛設置的轉換與傳遞給例程的圖形上下文連接起來。
  • 11)保存圖形狀態,以便稍后恢復此狀態。
  • 12)設置剪切區域。此行和接下來的兩行將上下文剪切為填充白色的矩形。效果是將漸變繪制到具有白色背景的窗口。
  • 13)如前所述,將漸變繪制到圖形上下文轉換漸變。
  • 14)釋放對象。此行和接下來的兩行將釋放您創建的所有對象。
  • 15)將圖形狀態恢復為設置填充背景之前存在的狀態。恢復的狀態仍然由窗口的寬度和高度轉換。

See Also - 同可參考

后記

本篇主要講述了漸變,感興趣的給個贊或者關注~~~

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容