iOS成长之路

Core Graphics 之 Gradients (九)

2018-08-27  本文已影响0人  Niko_peng
Gradients

Quartz为创建gradient提供了两种不透明的数据类型——cgshadingref和CGGradientRef。您可以使用其中任何一种来创建轴向或径向梯度。渐变是一种不同颜色的填充。

轴向梯度(也称为线性梯度)在两个定义的端点之间沿轴线变化。所有与坐标轴垂直的点都有相同的颜色值。

径向梯度是指两个确定的端点之间沿轴线呈径向变化的填充,这两个端点通常都是圆。如果点位于圆心点落在轴上的圆周上,它们的颜色值是相同的。梯度的圆形截面的半径由末端圆的半径定义;每个中间圆的半径从一端到另一端呈线性变化。

轴向和径向梯度的例子

Quartz函数为创建渐变效果提供了丰富的词汇表。本节展示了一些您可以实现的结果。图8-1中的轴向梯度在一个橙色的端点和另一个黄色的端点之间变化。在这种情况下,轴与原点成45度角。


屏幕快照 2018-08-27 下午3.26.24.png

Quartz还允许您沿着轴指定颜色和位置,以创建更复杂的轴向渐变,如图8-2所示。起点的颜色是红色,终点的颜色是紫色。然而,轴上还有五个位置的颜色分别设置为橙色、黄色、绿色、蓝色和靛蓝。你可以把结果看作是沿着同一轴的六个连续的线性梯度。虽然这里使用的轴与图8-1(45度角)中使用的轴相同,但它不一定是。轴的角度是由你提供的起始点和结束点定义的。


屏幕快照 2018-08-27 下午3.27.23.png
对于Quartz,您不限于根据颜色变化创建渐变;你可以只改变alpha值,或者你可以改变alpha值和其他颜色的分量。图8-4显示了一个梯度,其红色、绿色和蓝色组件保持不变,因为alpha值在1.0到0.1之间变化。
注意:如果你使用alpha来改变一个梯度,你将不能在绘制PDF内容时捕获那个梯度。因此,这样的渐变无法打印出来。如果你需要绘制一个PDF梯度,使用alpha 1.0。
屏幕快照 2018-08-27 下午3.28.17.png

您可以将这些圆放置在径向渐变中,以创建各种形状。如果一个圆部分或完全在另一个圆之外,Quartz为具有不等环的圆创建一个圆锥面,为具有相等环的圆创建一个圆柱面。径向梯度的一个常见用法是创建一个阴影球体,如图8-5所示。在这种情况下,一个点(半径为0的圆)位于一个较大的圆内。


屏幕快照 2018-08-27 下午3.28.53.png
通过嵌套几个类似于图8-6所示形状的径向梯度,可以创建更复杂的效果。形状的环形部分是使用同心圆创建的。
屏幕快照 2018-08-27 下午3.28.57.png
比较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对象的优势,CGShading对象仅限于两个位置。
创建CGGradient对象时,只需为每个位置设置一个颜色空间、位置和颜色。当您使用渐变对象绘制上下文时,您指定Quartz应该绘制轴向梯度还是径向梯度。在绘制时,根据绘制的是轴向梯度还是径向梯度,您可以指定起始点和结束点或半径,而CGShading对象的几何形状是在创建时定义的,而不是在绘制时定义的。

表8-1总结了这两种不透明数据类型之间的差异。


屏幕快照 2018-08-27 下午3.31.09.png
把颜色延伸到渐变的末端以外

当你创建一个渐变时,你可以选择用纯色填充渐变结束之外的空间。Quartz使用在渐变边界处定义的颜色作为填充颜色。你可以扩展到一个梯度的开始,一个梯度的结束,或者两者兼而有之。您可以将此选项应用于使用CGShading对象或CGGradient对象创建的轴向或径向梯度。每种类型的对象都提供可用于设置扩展选项的常量,您将在使用CGGradient对象和使用CGShading对象时看到这一点。

图8-7显示了一个轴向梯度,在起始和结束位置都延伸。图中的直线显示了梯度的轴线。如您所见,填充色对应于起点和终点的颜色。


屏幕快照 2018-08-27 下午3.31.59.png

图8-8比较了不使用扩展选项的径向梯度和使用扩展选项的起始位置和结束位置。Quartz接受开始和结束的颜色值,并使用这些纯色来扩展表面,如图所示。图中显示了开始和结束的圆,以及渐变的轴。


屏幕快照 2018-08-27 下午3.32.35.png
Using a CGGradient Object

CGGradient对象是渐变的抽象定义——它只指定颜色和位置,而不是几何形状。在轴向和径向几何图形中都可以使用相同的对象。作为一个抽象的定义,CGGradient对象可能比它们的对等对象CGShading对象更容易被重用。没有将几何图形锁定在CGGradient对象中,就可以基于相同的配色方案迭代地绘制渐变,而不需要在多个CGGradient对象中绑定内存资源。
因为Quartz是为您计算梯度的,所以使用CGGradient对象来创建和绘制渐变是相当简单的,需要以下步骤:

1、创建一个CGGradient对象,提供一个颜色空间、一个由两个或多个颜色组件组成的数组、一个由两个或多个位置组成的数组以及两个数组中每个数组中的项数。
2、通过调用CGContextDrawLinearGradient或CGContextDrawRadialGradient来绘制梯度,并提供上下文、CGGradient对象、绘图选项以及声明和结束几何(轴向梯度点或圆中心,径向梯度半径)。
3、当不再需要CGGradient对象时,释放它。

location是一个CGFloat值,范围为0.0到1.0(包括0.0到1.0),它指定沿渐变轴的规范化距离。值0.0指定轴的起点,而1.0指定轴的终点。其他值指定距离的比例,例如从起点到中间点的距离的四分之一为0.25,中点为0.5。Quartz至少使用两个位置。如果为location数组传递NULL,则Quartz为第一个位置使用0,第二个位置使用1。
每个颜色的颜色组件的数量取决于颜色空间。对于屏幕绘图,您将使用RGB颜色空间。因为Quartz是用alpha绘制的,所以屏幕上的每一种颜色都有四个组成部分——红色、绿色、蓝色和alpha。因此,对于屏幕绘图,您提供的颜色组件数组中的元素数量必须是位置数量的4倍。Quartz RGBA颜色组件值可以从0.0变化到1.0,包括在内。

清单8-1是一个创建CGGradient对象的代码片段。在声明了必要的变量之后,代码设置了位置和所需的颜色组件数量(对于本例,2 X 4 = 8)。(在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对象之后,你可以用它来绘制一个轴向或线性渐变。清单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);

清单8-3是一个代码片段,它使用清单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);
屏幕快照 2018-08-27 下午3.36.23.png

图8-4所示的径向梯度使用清单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 };

清单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 };
屏幕快照 2018-08-27 下午3.44.35.png
Using a CGShading Object

通过创建一个CGShading对象,调用函数CGShadingCreateAxial或CGShadingCreateRadial,设置一个渐变,提供以下参数:

1、一个CGColorSpace对象,描述石英在解释回调提供的颜色组件值时使用的颜色空间。
2、开始和结束点。对于轴向梯度,这些是轴的起始坐标和结束坐标(在用户空间中)。对于径向梯度,这些是起点和终点圆中心的坐标。
3、用于定义梯度区域的圆的起始和结束半径(仅适用于径向梯度)。
4、通过调用函数CGFunctionCreate获得的CGFunction对象,将在本节后面讨论。这个回调例程必须返回一个颜色,以便在特定点上绘制。
5、布尔值,指定是否用纯色填充起始点或结束点以外的区域。

您提供给CGShading创建函数的CGFunction对象包含回调结构和石英实现回调所需的所有信息。也许设置CGShading对象最棘手的部分是创建CGFunction对象。当您调用函数CGFunctionCreate时,您将提供以下内容:

1、指向回调所需的任何数据的指针。
2、回调的输入值的数量。Quartz要求回调接受一个输入值。
3、浮点值的数组。Quartz在这个数组中仅使用一个元素提供回调。一个输入值的范围可以从0到1,0代表渐变开始的颜色,1代表渐变结束的颜色。
4、回调函数提供的输出值的数量。对于每个输入值,回调必须为每个颜色组件提供一个值,并提供一个alpha值来指定不透明度。颜色组件值由Quartz在您创建的颜色空间中解释,并提供给CGShading创建函数。例如,如果您使用RGB颜色空间,您可以提供值4作为输出值的数量(R、G、B和A)。
5、指定每个颜色组件和alpha值的浮点值数组。
6、回调数据结构,其中包含结构的版本(将此字段设置为0)、用于生成颜色组件值的回调,以及用于释放info参数中提供给回调的数据的可选回调。如果将回调函数命名为myCalculateShadingValues,它将如下所示:

void myCalculateShadingValues (void *info, const CGFloat *in, CGFloat *out)

在创建CGShading对象之后,如果需要的话,还可以设置额外的剪切。然后,调用CGContextDrawShading函数,用渐变绘制上下文的剪切区域。当您调用这个函数时,Quartz会调用您的回调函数来获取跨越从起点到终点范围的颜色值。

当您不再需要CGShading对象时,您可以通过调用CGShadingRelease函数来释放它。

使用CGShading对象绘制轴向渐变,使用CGShading对象绘制径向渐变,提供了编写使用CGShading对象绘制渐变的代码的逐步说明。

Painting an Axial Gradient Using a CGShading Object

轴向和径向梯度要求您执行类似的步骤。这个示例展示了如何使用CGShading对象绘制轴向渐变,在图形上下文中创建半圆形剪切路径,然后将渐变绘制到剪切上下文,以实现图8-11所示的输出。


屏幕快照 2018-08-27 下午3.53.15.png

要绘制如图所示的轴向梯度,请按照以下步骤进行解释:

1、设置一个CGFunction对象来计算颜色值
2、创建一个轴向渐变的CGShading对象
3、剪辑的上下文
4、使用CGShading对象绘制轴向渐变
5、释放对象

Set Up a CGFunction Object to Compute Color Values

你可以任意计算颜色值,只要你的颜色计算函数有三个参数:

1、void *info。NULL,或者指向传递给CGShading创建函数的数据的指针。
const CGFloat * in。Quartz将in数组传递给回调函数。数组中的值必须在为CGFunction对象定义的输入值范围内。对于本例,输入范围为0到1;请参见清单以8:7。
3、CGFloat *out.。回调将out数组传递给Quartz。它包含颜色空间中每个颜色组件的一个元素和一个alpha值。输出值应该在为CGFunction对象定义的输出值范围内。对于本例,输出范围为0到1;请参见清单以8:7。
有关这些参数的更多信息,请参阅CGFunctionEvaluateCallback。

清单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对象。清单8-7显示了一个创建包含清单8-6回调的CGFunction对象的函数。清单后面显示了每一行代码的详细说明。
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、声明回调结构,并用结构的版本(0)、颜色组件计算回调的指针和可选释放函数的NULL来填充它。
3、计算颜色空间中颜色组件的数量,并将值增加1以表示alpha值。
4、传递一个指向numComponents值的指针。回调myCalculateShadingValues使用这个值来确定要计算的组件的数量。
5、指定1是回调的输入值的数量。
6、提供一个数组,该数组指定输入的有效间隔。这个数组包含0和1。
7、传递输出值的数量,即颜色组件的数量加上。
8、提供一个数组,该数组指定每个输出值的有效间隔。这个数组为每个组件指定了间隔0和1。因为有4个组件,所以这个数组中有8个元素。
9、传递一个指向之前声明和填充的回调结构的指针。

Create a CGShading Object for an Axial Gradient

要创建CGShading对象,您可以调用函数CGShadingCreateAxial,如清单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);
Clip the Context

当您绘制渐变时,Quartz会填充当前上下文。绘制渐变与使用颜色和图案不同,颜色和图案用于描边和填充路径对象。因此,如果你想让你的渐变出现在一个特定的形状,你需要相应地剪辑上下文。清单8-9中的代码向当前上下文添加了一个半圆形,以便将渐变绘制到该剪辑区域,如图8-11所示。

如果仔细观察,您会注意到代码应该是半圆,而图中显示的是半个椭圆。为什么?您将看到,当您使用CGShading对象查看轴向渐变的完整例程中的整个例程时,上下文也被缩放了。后面会更详细地谈到。虽然您可能不需要在应用程序中应用缩放或剪辑,但是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);

Paint the Axial Gradient Using a CGShading Object

调用CGContextDrawShading函数,使用CGShading对象中指定的颜色渐变填充当前上下文:

CGContextDrawShading (myContext, myShading);
Release Objects

当不再需要CGShading对象时,调用函数CGShadingRelease。您还需要释放CGColorSpace对象和CGFunction对象,如清单8-10所示。
Listing 8-10 Releasing objects

CGShadingRelease (myShading);
CGColorSpaceRelease (colorspace);
CGFunctionRelease (myFunctionObject)
一个使用CGShading对象的轴向渐变的完整例程

清单8-11中的代码使用清单8-7中设置的CGFunction对象和清单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);

下面是代码的作用:

1、将图形上下文和要绘制的矩形作为参数。
2、为起始点赋值。例程根据从0到1的用户空间计算值。稍后您将缩放Quartz拉入的窗口的空间。你可以把这个坐标位置看成x在最左边,y在最下面50%
3、为结束点赋值。你可以把这个坐标位置看成x在最右边,y在最下面50%如你所见,渐变的轴是一条水平线。
4、为设备RGB创建一个颜色空间,因为这个例程将绘制到显示器。
5、通过调用清单8-7所示的例程并传递刚才创建的颜色空间,创建CGFunction对象。
6、为轴向渐变创建一个CGShading对象。最后两个参数为假,表示Quartz不应填充起始点和结束点以外的区域。
7、设置一个仿射变换,将其缩放到用于绘图的窗口的高度和宽度。注意,高度不一定等于宽度。在这个例子中,由于两者不相等,最终的结果是椭圆形的而不是圆形的。
8、将刚才设置的转换与传递给例程的图形上下文连接起来。
9、保存图形状态,以便稍后恢复该状态。
10、设置剪切区域。这一行和接下来的两行将上下文剪辑到一个用白色填充的矩形上。效果是渐变被绘制到一个带有白色背景的窗口。
11、创建一个路径。这一行和接下来的三行创建了一个半圆的圆弧,并将其作为裁剪区域添加到图形上下文中。结果是梯度绘制到一个半圆的区域。然而,圆形会被窗口的高度和宽度所改变(参见步骤8),最终产生一个绘制为半个椭圆的渐变效果。当窗口被用户调整大小时,剪切区域也被调整大小。
12、绘制梯度到图形上下文,转换和剪切梯度如前所述。
13、释放对象。这一行和接下来的两行将释放您创建的所有对象。
14、将图形状态恢复到设置填充背景和剪切到半圆之前的状态。恢复后的状态仍然由窗口的宽度和高度进行转换。

Painting a Radial Gradient Using a CGShading Object

This example shows how to use a CGShading object to produce the output shown in Figure 8-12.


屏幕快照 2018-08-27 下午4.04.56.png

要绘制径向梯度,请遵循以下部分中解释的步骤:

1、设置一个CGFunction对象来计算颜色值。
3、为径向渐变创建一个CGShading对象
3、使用CGShading对象绘制径向渐变
4、释放对象

Set Up a CGFunction Object to Compute Color Values

编写函数来计算径向和轴向梯度的颜色值之间没有区别。实际上,您可以按照在设置CGFunction对象以计算颜色值时列出的轴向梯度的说明进行操作。清单8-12计算颜色,使颜色组件以正弦曲线的方式变化,其周期基于函数中声明的频率值。图8-12所示的结果与图8-11所示的颜色有很大不同。尽管颜色输出有所不同,清单8-12中的代码与清单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对象,正如在设置CGFunction对象以计算颜色值时所描述的轴向值一样。

为径向渐变创建一个CGShading对象

创建一个CGShading对象或径向渐变,你CGShadingCreateRadial调用这个函数,见清单8日至13日,通过颜色空间,开始和结束点,起点和终点的半径CGFunction对象和布尔值指定是否要填补该地区以外的开始和结束点的梯度。
Listing 8-13 Creating a CGShading object for a radial gradient

GPoint 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);
使用CGShading对象绘制径向渐变

调用函数CGContextDrawShading使用CGShading对象中指定的指定颜色渐变填充当前上下文。

CGContextDrawShading (myContext, shading);

请注意,无论梯度是轴向的还是径向的,都使用相同的函数绘制梯度。

Release Objects

当不再需要CGShading对象时,调用函数CGShadingRelease。您还需要释放CGColorSpace对象和CGFunction对象,如清单8-14所示。
Listing 8-14 Code that releases objects

CGShadingRelease (myShading);
CGColorSpaceRelease (colorspace);
CGFunctionRelease (myFunctionObject);
使用CGShading对象绘制径向渐变的一个完整例程

清单8-15中的代码显示了一个完整的例程,它使用清单8-7中设置的CGFunction对象和清单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的用户空间计算值。稍后您将按比例缩放石英吸入窗口的空间。你可以把这个坐标位置想象成x在左边25% y在下面30%
3、指定起始圆的半径。你可以把它想象成用户空间宽度的10%。
4、为结束圆的中心指定一个值。你可以把这个坐标位置想象成x在左边70% y在下面70%
5、指定结束圆的半径。您可以将其看作是用户空间宽度的25%。结束圆将比开始圆大。圆锥的形状将从左到右,倾斜向上。
6、为设备RGB创建一个颜色空间,因为这个例程将绘制到显示器。
7、通过调用清单8-7所示的例程并传递刚才创建的颜色空间,创建CGFunctionObject。但是,回想一下,您将使用清单8-12所示的颜色计算函数。
为径向渐变创建一个CGShading对象。最后两个参数为假,表示石英不应填充梯度起点和终点以外的区域。
8、设置一个仿射变换,将其缩放到用于绘图的窗口的高度和宽度。注意,高度不一定等于宽度。事实上,每当用户调整窗口大小时,转换就会发生变化。
9、将刚才设置的转换与传递给例程的图形上下文连接起来。
10、保存图形状态,以便稍后恢复该状态。
11、设置剪切区域。这一行和接下来的两行将上下文剪辑到一个用白色填充的矩形上。效果是渐变被绘制到一个带有白色背景的窗口。
12、绘制梯度到图形上下文转换梯度如前所述。
13、释放对象。这一行和接下来的两行将释放您创建的所有对象。
14、将图形状态恢复到设置填充背景之前的状态。恢复后的状态仍然由窗口的宽度和高度进行转换。

上一篇 下一篇

猜你喜欢

热点阅读