Gradients

Quartz为创建渐变提供了CGShadingRef和CGGradientRef两种不透明的数据类型。您可以使用其中任一种创建轴向或径向渐变。渐变是从一种颜色到另一种颜色变化的填充。

轴向梯度(也称为线性梯度)沿着两个限定的端点之间的轴线变化。位于与轴垂直的线上的所有点具有相同的颜色值。

径向梯度是沿两个限定端之间的轴径向变化的填充,其通常都是圆。如果点位于中心点落在轴上的圆的圆周上,则点共享相同的颜色值。梯度的圆形部分的半径由端圆的半径限定;每个中间圆的半径从一端到另一端线性地变化。

本章提供了可以使用Quartz创建的线性和径向渐变的示例,比较可以对绘制渐变进行影响的两种方法,然后展示如何使用每个不透明数据类型创建渐变。

轴向和径向梯度示例

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

8-1 沿45度轴的轴向梯度

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

8-2 用七个位置和颜色创建的轴向渐变

图8-3显示了一个小的,明亮的红色圆圈和一个较大的黑色圆圈之间变化的径向梯度。

8-3 在两个圆之间变化的径向梯度

使用Quartz,您不仅限于基于颜色更改创建渐变; 您可以只改变alpha,或者你可以改变alpha与其他颜色组件。 图8-4显示了一个梯度,其中红色,绿色和蓝色分量保持恒定,因为alpha值从1.0变化到0.1。

注:如果使用alpha更改渐变,则在绘制到PDF内容时将无法捕获该渐变。 因此,不能打印这种梯度。 如果你需要绘制一个梯度到PDF,使用1.0的alpha。

8-4 通过仅改变alpha分量创建的径向梯度

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

8-5 在点和圆之间变化的径向梯度

您可以通过嵌套多个径向渐变创建更复杂的效果,类似于图8-6所示的形状。 使用同心圆创建形状的环形部分。

8-6 嵌套径向渐变

CGShading和CGGradient对象的比较

有两种类型的对象可用于创建渐变,你可能想知道最好使用哪一个。本节有助于回答这个问题。

CGShadingRef opaque数据类型使您可以控制如何计算渐变中每个点的颜色。在创建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对象的几何是在创建时定义的,而不是在绘制时。

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

CGShading和CGGradient对象之间的差异

Extending Color Beyond the End of a Gradient

当您创建渐变时,您可以选择用纯色填充超出渐变末端的空间。 Quartz使用在渐变边界定义的颜色作为填充颜色。 您可以延伸超出渐变的开始,渐变的结束,或两者。 您可以将选项应用于使用CGShading对象或CGGradient对象创建的轴向或径向渐变。 每种类型的对象都提供常量,您可以使用它来设置扩展选项,您将在使用CGGradient对象和使用CGShading对象中看到。

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

8-7 延伸轴向梯度

图8-8比较了不使用扩展选项的径向渐变和对开始和结束位置使用扩展选项的渐变。 Quartz获取开始和结束颜色值,并使用这些纯色来扩展曲面,如图所示。 该图显示了起始和结束圆形以及渐变的轴。

8-8 扩展径向渐变

使用CGGradient对象

CGGradient对象是渐变的抽象定义 - 它仅仅指定颜色和位置,而不是几何。您可以将这个相同的对象用于轴向和径向几何。作为抽象定义,CGGradient对象可能比它们的对应CGShading对象更容易重用。没有在CGGradient对象中锁定几何体允许基于相同的颜色方案迭代地绘制梯度的可能性,而不需要也在多个CGGradient对象中占用存储器资源。

因为Quartz为你计算梯度,使用CGGradient对象来创建和绘制梯度是相当简单的,需要以下步骤:

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

位置是在0.0到1.0(包括1和0.0)范围内的CGFloat值,其指定沿着梯度的轴的归一化距离。值0.0指定轴的起点,而1.0指定轴的终点。其他值指定距离的一部分,例如,距起点的四分之一距离为0.25,轴上的中间点为0.5。至少,Quartz使用两个位置。如果为locations数组传递NULL,Quartz对第一个位置使用0,对第二个位置使用1。

每种颜色的颜色分量的数量取决于颜色空间。对于屏幕上的绘图,您将使用RGB颜色空间。因为Quartz使用alpha绘制,每个屏幕上的颜色有四个组件 - 红色,绿色,蓝色和alpha。因此,对于屏幕上的绘图,您提供的颜色组件数组中的元素数量必须包含位置数量的四倍。石英RGBA颜色分量的值可以从0.0到1.0(含)不等。

清单8-1是一个创建CGGradient对象的代码片段。在声明必要的变量之后,代码设置位置和必需数量的颜色分量(在本例中为2×4 = 8)。它创建一个通用的RGB颜色空间。 (在iOS中,通用的RGB颜色空间不可用,你的代码应该调用CGColorSpaceCreateDeviceRGB)然后,它传递必要的参数到CGGradientCreateWithColorComponents函数。你也可以使用函数CGGradientCreateWithColors,如果你的应用程序设置CGColor对象,这很方便。

// 创建一个CGGradient对象
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)。

// 使用CGGradient对象绘制轴向渐变
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所示的径向渐变。 本示例说明了通过用纯色填充来扩展渐变区域的结果。

// 使用CGGradient对象绘制径向渐变
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);
8-9 使用CGGradient对象绘制的径向渐变

图8-4中所示的径向梯度是使用如清单8-4中所示的变量创建的。

// 用于通过改变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所示的灰度梯度的变量,它有三个位置。

// 用于创建灰色渐变的变量
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 };
8-10 具有三个位置的轴向梯度

使用CGShading对象

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

  • 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来释放它。

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

使用CGShading对象绘制轴向渐变

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

8-11 一个轴向渐变,被剪切和绘制

要绘制图中所示的轴向梯度,请按照这些部分中介绍的步骤操作:

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

您可以以任何方式计算颜色值,只要您的颜色计算函数有三个参数:

  • void * info。 这是NULL或指向传递给CGShading创建函数的数据的指针。
  • const CGFloat * in。 Quartz将in数组传递给你的回调函数。 数组中的值必须在为CGFunction对象定义的输入值范围内。 对于本示例,输入范围为0到1; 见清单8-7。
  • CGFloat * out。 你的回调将out数组传递给Quartz。 它在颜色空间中为每个颜色分量包含一个元素,以及一个alpha值。 输出值应在为CGFunction对象定义的输出值范围内。 对于本示例,输出范围为0到1; 见清单8-7。

有关这些参数的更多信息,请参阅CGFunctionEvaluateCallback。

清单8-6显示了一个函数,它通过将常量数组中定义的值乘以输入值来计算颜色分量值。 因为输入值范围从0到1,所以输出值的范围从黑色(对于RGB,值0,0,0)到通过(1,0,.5),它是紫色色调。 请注意,最后一个组件始终设置为1,因此颜色始终完全不透明。

// 计算颜色分量值
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显示了一个创建CGFunction对象的函数,该对象包含清单8-6中的回调。 每个编号的代码行的详细说明显示在列表之后。

// 创建CGFunction对象
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. 传递输出值的数量,这是颜色分量的数量加上alpha。
  8. 提供一个数组,指定每个输出值的有效间隔。此数组为每个组件指定间隔0和1.因为有四个组件,所以此数组中有八个元素。
  9. 传递一个指针到之前声明和填充的回调结构。
为轴向渐变创建一个CGShading对象

要创建一个CGShading对象,可以调用函数CGShadingCreateAxial,如清单8-8所示,传递一个颜色空间,起始点和结束点,一个CGFunction对象和一个布尔值,它指定是否填充开始和结束区域 点的渐变。

// 为轴向渐变创建CGShading对象
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)
剪辑上下文

当你绘制一个渐变,Quartz填充当前上下文。 绘制渐变与使用颜色和图案不同,后者用于描画和填充路径对象。 因此,如果您希望渐变以特定形状显示,则需要相应地剪裁上下文。 清单8-9中的代码为当前上下文添加了一个半圆,以便将渐变绘制到该剪裁区域中,如图8-11所示。

如果你仔细看,你会注意到代码应该导致一个半圆,而图中显示一个半椭圆。 为什么? 你会看到,当你看一下使用CGShading对象的轴向渐变的完整例程中的整个例程时,上下文也被缩放。 更多关于后面。 虽然您可能不需要在应用程序中应用缩放或剪辑,但Quartz 2D中存在这些和许多其他选项,以帮助您实现有趣的效果。

// 向图形上下文中添加半圆剪辑
CGContextBeginPath (myContext);
    CGContextAddArc (myContext, .5, .5, .3, 0,
                    my_convert_to_radians (180), 0);
    CGContextClosePath (myContext);
    CGContextClip (myContext);
使用CGShading对象绘制轴向渐变

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

CGContextDrawShading (myContext, myShading);
释放对象

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

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

清单8-11中的代码显示了一个使用清单8-7中设置的CGFunction对象和清单8-6中所示的回调来绘制轴向渐变的完整例程。 每个编号的代码行的详细说明显示在列表之后。

// 使用CGShading对象绘制轴向渐变
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和距离底部50%的y。如您所见,渐变的轴是水平线。
  4. 为设备RGB创建颜色空间,因为此例程绘制到显示。
  5. 通过调用如清单8-7所示的例程并传递刚刚创建的颜色空间来创建CGFunction对象。
  6. 为轴向渐变创建一个CGShading对象。最后两个参数为false,表示Quartz不应填充起始点和结束点以外的区域。
  7. 设置仿射变换,其缩放到用于绘制的窗口的高度和宽度。注意,高度不一定等于宽度。在这个例子中,因为两个不相等,所以最终结果是椭圆形而不是圆形。
  8. 将您刚刚设置的变换与传递给例程的图形上下文相连接。
  9. 保存图形状态,以便稍后恢复此状态。
  10. 设置剪切区域。此行和下两行将上下文剪切为以白色填充的矩形。效果是,渐变绘制到具有白色背景的窗口。
  11. 创建路径。该线和接下来的三条线设置了半圆弧,并将其作为剪裁区域添加到图形上下文中。效果是,渐变绘制到半圆的区域。然而,圆将被窗口的高度和宽度变换(参见步骤8),导致最终的效果是绘制到半椭圆的梯度。当窗口由用户调整大小时,剪切区域被调整大小。
  12. 将渐变绘制到图形上下文,如前所述转换和裁剪渐变。
  13. 释放对象。这一行和接下来的两行释放你创建的所有对象。
  14. 将图形状态恢复为在设置填充背景并剪切为半圈之前存在的状态。恢复的状态仍然由窗口的宽度和高度变换。

使用CGShading对象绘制径向渐变

此示例显示如何使用CGShading对象来产生如图8-12所示的输出。

8-12 使用CGShading对象创建的径向渐变

要绘制径向渐变,请按照以下部分中介绍的步骤操作:

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

写入函数用于计算径向和轴向梯度的颜色值没有区别。 事实上,您可以按照设置CGFunction对象以计算颜色值中的轴向渐变的说明进行操作。 清单8-12计算颜色,使颜色分量按正弦变化,一个周期基于函数中声明的频率值。 图8-12中显示的结果与图8-11中显示的颜色有很大不同。 尽管颜色输出有所不同,但是代码8-12中的代码与代码8-6相似,因为每个函数都遵循相同的原型。 每个函数接受一个输入值并计算N个值,一个用于颜色空间的每个颜色分量,另一个用于Alpha值。

// 计算颜色分量值
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对象和布尔值,以指定是否 填充超出渐变的起点和终点的区域。

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

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

CGContextDrawShading (myContext, shading);

请注意,您使用相同的函数绘制渐变,无论渐变是轴向还是径向。

释放对象

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

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

清单8-15中的代码显示了一个完整的例程,使用清单8-7中设置的CGFunction对象和清单8-12中所示的回调来绘制径向渐变。 每个编号的代码行的详细说明显示在列表之后。

// 使用CGShading对象绘制径向渐变的例程
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. 通过调用如清单8-7所示的例程并传递刚刚创建的颜色空间来创建CGFunctionObject。但是,请记住,您将使用颜色计算函数,如清单8-12所示。
  8. 为径向渐变创建一个CGShading对象。最后两个参数是false,表示Quartz不应该填充渐变的起点和终点以外的区域。
  9. 设置仿射变换,其缩放到用于绘制的窗口的高度和宽度。注意,高度不一定等于宽度。事实上,只要用户调整窗口大小,转换就会改变。
  10. 将您刚刚设置的变换与传递给例程的图形上下文相连接。
  11. 保存图形状态,以便稍后恢复此状态。
  12. 设置剪切区域。此行和下两行将上下文剪切为以白色填充的矩形。效果是,渐变绘制到具有白色背景的窗口。
  13. 将渐变绘制到图形上下文,如前所述转换渐变。
  14. 释放对象。这一行和接下来的两行释放你创建的所有对象。
  15. 将图形状态恢复为在设置填充背景之前存在的状态。恢复的状态仍然由窗口的宽度和高度变换。

See Also

  • CGGradient Reference描述了创建CGGradient对象的函数。
  • CGShading Reference描述了创建CGShading对象的函数。
  • CGFunction Reference描述了计算CGShading对象的渐变颜色所需的函数。
  • CGContext Reference描述了使用CGGradient和CGShading对象绘制上下文的函数。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容