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阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容