グラデーション
Quartz は、CGShadingRef と CGGradientRef でグラデーションを作成するための 2 つの不透明なデータ型を提供します。これらのいずれかを使用して、軸方向または放射状のグラデーションを作成できます。グラデーション は、ある色から別の色に変化する塗りつぶしです。
軸のグラデーション(線形グラデーション とも呼ばれる) は、2 つの定義された終点間の軸に沿って変化します。軸に垂直な線上にあるすべての点は同じ色値を持ちます。
放射状のグラデーション は、定義された 2 つの終点の間の軸に沿って放射状に変化する塗りつぶしで、通常は両方とも円です。中心点が軸上にある円の円周上にある場合、点は同じ色値を共有します。グラデーションの円形部分の半径は、端の円の半径によって定義されます。各中間円の半径は端から端まで直線的に変化します。
軸方向および放射状グラデーションの例
Quartz 関数は、グラデーション効果を作成するための豊富な表現を提供します。この節では、達成可能ないくつかの結果を示します。図 8-1 の軸方向のグラデーションは、オレンジ色のシェードの終点と黄色のシェードの終点で変化します。この場合、軸は原点に対して 45 度の角度になります。
図 8-1   45 度の軸に沿った軸方向のグラデーション
Quartz では、図 8-2 に示すように、軸に沿って色や位置を指定して、より複雑な軸方向のグラデーションを作成することもできます。開始点の色は赤のシェードで、終点の色は紫のシェードです。ただし、色はオレンジ色、黄色、緑色、青色、および藍色にそれぞれ設定されている軸上に 5 つの場所があります。結果は、同じ軸に沿った 6 つの連続した線形グラデーションと考えることができます。ここで使用される軸は、図 8-1 (45度の角度) で使用されている軸と同じですが、必ずしもそうである必要はありません。軸の角度は、指定した開始点と終点によって定義されます。
図 8-2   7 つの位置と色で作成された軸方向のグラデーション
図 8-3 は、小さくて明るい赤い円と大きな黒い円の間で変化する放射状のグラデーションを示しています。
図 8-3 2 つの円の間で変化する放射状グラデーション
Quartz では、色の変化に基づいてグラデーションを作成するだけでなく、アルファ値のみを変更することも、他の色成分とともにアルファ値を変更することもできます。図 8-4 は、アルファ値が 1.0 から 0.1 まで変化するとき、赤、緑、青の成分が一定のままであるグラデーションを示しています。
図 8-4 アルファ成分のみを変化させることによって生成される放射状グラデーション
放射状グラデーションで円を配置すると、さまざまな図形を作成できます。1 つの円が部分的にまたは完全に他の円の外側にある場合、Quartz は等しくない円周を持つ円の円錐面と、等しい円周を持つ円の円面を作成します。放射状グラデーションの一般的な使用法は、図 8-5 に示すように、陰影のついた球体を作成することです。この場合、単一の点(半径が 0 の円) が大きな円の中にあります。
図 8-5 点と円の間で変化する放射状グラデーション
図 8-6 に示すように放射状グラデーションをいくつか入れ子にすると、より複雑な効果を作成できます。形状のドーナツ状部分は、同心円を使用して作成されます。
図 8-6 入れ子にした放射状グラデーション
CGShading と CGGradient オブジェクトの比較
グラデーションを作成するために使用できる 2 つの型のオブジェクトでは、どちらを使用するのが最善か疑問に思うかもしれません。この節ではその質問に答えるのを助けます。
CGShadingRef の不透明データ型を使用すると、グラデーションの各点での色の計算方法を制御できます。CGShading オブジェクトを作成する前に、グラデーション内の色を計算する関数を定義する CGFunction オブジェクト(CGFunctionRef) を作成しなければなりません。カスタム関数を書くと、図 8-1、図 8-3、及び 図 8-5 に示すような滑らかなグラデーションを作成することができます。また、図 8-12 に示すような特殊な効果もあります。
CGShading オブジェクトを作成するときは、軸方向(線形) であるか放射状であるかを指定します。グラデーション計算関数(CGFunction オブジェクトとしてカプセル化されている) に加えて、軸方向のグラデーションか放射状グラデーションかに応じて、色空間と開始点と終点または半径も指定します。描画時には、描画コンテキストとともに CGShading オブジェクトを関数 CGContextDrawShading に渡すだけです。Quartz は、グラデーションの各ポイントのグラデーション計算関数を呼び出します。
CGGradient オブジェクトは、使いやすさを考慮して設計された CGShading オブジェクトのサブセットです。CGGradientRef の不透明データ型は、Quartz がグラデーションの各点で色を計算するため、あなたがグラデーション計算関数を提供しないため、使用するのが簡単です。グラデーションオブジェクトを作成するときは、位置と色の配列を指定して下さい。Quartz は、各位置にグラデーションの終点として割り当てる色を使用して、連続した位置の各セットのグラデーションを計算します。図 8-1 に示すように、開始位置と終了位置を1つずつ使用するようにグラデーションオブジェクトを設定することも、図 8-2 に示すような効果を作成するためにいくつかの点を与えることもできます。2 つ以上の位置を提供できるのは、2 つの位置に限られている CGShading オブジェクトを使用することよりも利点があります。
CGGradient オブジェクトを作成するときは、各位置の色空間、位置、および色を設定するだけです。グラデーションオブジェクトを使用してコンテキストに描画する場合は、Quartz が軸方向または放射状のグラデーションを描画するかどうかを指定します。描画時ではなく、作成時に図形が定義されている CGShading オブジェクトとは対照的に、軸方向または放射状のグラデーションを描画するかどうかによって、描画時に開始点と終了点または半径を指定します。
表 8-1 は、2 つの不透明なデータ型の違いをまとめたものです。
表 8-1 CGShadingオ ブジェクトと CGGradient オブジェクトの違い
CGGradient | CGShading |
---|---|
軸方向と放射状のグラデーションを描画するのに同じオブジェクトを使用できます。 | 軸方向および放射状のグラデーションのために別々のオブジェクトを作成する必要あり。 |
描画時にグラデーションの図形を設定します。 | オブジェクト作成時にグラデーションの図形を設定します。 |
Quartz が、グラデーション内の各点の色を計算します。 | グラデーション内の各点の色を計算する呼び出し関数を用意しなければなりません。 |
2 つ以上の位置と色を簡単に定義できます。 | 2 つ以上の位置と色を使用するように呼び出し関数を設計する必要があり、あなたにもう少し作業が必要です。 |
グラデーションの端を超えて色を拡張
グラデーションを作成するときは、グラデーションの端を超えて空間を単色で塗りつぶすことができるオプションがあります。Quartz は、グラデーションの境界で定義された色を塗りつぶしの色として使用します。グラデーションの開始点、グラデーションの終点、またはその両方を超えて拡張できます。オプションは、CGShading オブジェクトまたは CGGradient オブジェクトのいずれかを使用して作成された軸方向または放射状のグラデーションに適用できます。各型のオブジェクトは、CGGradient オブジェクトの使用 と CGShading オブジェクトの使用 で示すように、拡張オプションを設定するために使用できる定数を提供します。
図 8-7 は、開始位置と終点位置を超えて延びる軸方向のグラデーションを示しています。図の線はグラデーションの軸を示しています。ご覧のように、塗りつぶしの色は開始点と終点の色に対応しています。
図 8-7 軸方向のグラデーションの拡張
図 8-8 は、拡張オプションを使用しない放射状グラデーションと、開始位置と終了位置の両方に拡張オプションを使用する放射状グラデーションを比較しています。Quartz は開始と終了の色値を取り、それらの無地の色を使用して表面を図のように延長します。図は、開始と終了の円とグラデーションの軸を示しています。
図 8-8 放射状グラデーションの拡張
CGGradient オブジェクトの使用
CGGradient オブジェクトはグラデーションの抽象的な定義です。単に色と位置を指定しますが、図形は指定しません。軸方向および放射状図形の両方でこの同じオブジェクトを使用できます。抽象的な定義として、CGGradient オブジェクトは、おそらくその相手役、CGShading オブジェクトよりも容易に再利用可能です。CGGradient オブジェクトに図形がロックされていないと、複数の CGGradient オブジェクトにメモリリソースを割り当てる必要もなく、同じカラースキームに基づいてグラデーションを繰り返しペイントする可能性があります。
Quartz はあなたのためにグラデーションを計算するので、CGGradient オブジェクトを使ってグラデーションを作成し描画するのはかなり簡単で、以下の手順を取ります。
- 色空間、2 つ以上の色成分の配列、2 つ以上の位置の配列、2 つの配列のそれぞれの項目の数を提供する CGGradient オブジェクトを作成します。
- CGContextDrawLinearGradient または CGContextDrawRadialGradient のいずれかを呼び出し、コンテキスト、CGGradient オブジェクト、描画オプション、開始および終了地形(放射状グラデーションの軸勾配または円中心および半径の点) を指定して、グラデーションをペイントします。
- 不要になったら CGGradient オブジェクトを解放します。
位置は、グラデーションの軸に沿った正規化距離を指定する 0.0〜1.0 の範囲の CGFloat 値です。0.0 の値は軸の始点を指定し、1.0 は軸の終点を指定します。他の値は、始点からの割合を示し、例えば 0.25 は始点からの距離の 1/4、軸の半分の点の 0.5などです。最低限、Quartz は2つの位置を使用します。位置の配列に NULL を渡すと、Quartz は最初の位置には 0 を使用し、2 番目には 1 を使用します。
1 色あたりの色成分の数は、色空間に依存します。オンスクリーン描画の場合は、RGB 色空間を使用します。Quartz はアルファ値を使用して描画するため、各オンスクリーンの色には赤、緑、青、アルファ値の 4 つの成分があります。したがって、オンスクリーンの描画では、指定した色成分配列の要素数に 4 を掛けた数の位置が含まれていなければなりません。Quartz の RGBA 色成分の値は、0.0〜1.0 の範囲で変更できます。
リスト 8-1 は、CGGradient オブジェクトを作成するコードの一部です。必要な変数を宣言した後、コードは位置と必要な数の色成分を設定します(この例では 2 X 4 = 8)。汎用 RGB 色空間を作成します。(iOS では、汎用 RGB 色空間が利用できないので、代わりにコードは CGColorSpaceCreateDeviceRGB を呼び出す必要があります)。次に、CGGradientCreateWithColorComponents 関数に必要なパラメータを渡します。アプリケーションが CGColor オブジェクトを設定する場合に便利な、CGGradientCreateWithColors 関数を使用することもできます。
リスト 8-1 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) の取得方法は示していません。
リスト 8-2   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 に示した放射状グラデーションを描画するコードの一部です。この例では、グラデーションの領域を単色で塗りつぶすことによって、グラデーションの領域を拡張した結果を示しています。
リスト 8-3   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 に示す変数を使用して作成されました。
リスト 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 に示した灰色のグラデーションを作成するための変数を示します。この変数には 3 つの位置があります。
リスト 8-5 灰色のグラデーションを作成するための変数
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 3 つの位置を持つ軸方向のグラデーション
CGShading オブジェクトの使用
CGShadingCreateAxial または CGShadingCreateRadial 関数を呼び出す CGShading オブジェクトを作成し、以下のパラメータを指定してグラデーションを設定します。
- 呼び出し関数が提供する色成分値を解釈するときに使用する Quartz の色空間を記述する CGColorSpace オブジェクト。
- 開始点と終了点。軸方向のグラデーションの場合、これらは軸の開始座標と終了座標(ユーザー空間内の) です。 放射状グラデーションの場合、これらは開始円と終了円の中心の座標です。
- グラデーション領域を定義するために使用される円の開始半径と終了半径(放射状グラデーションのみ)。
- この節の後半で説明する CGFunctionCreate 関数を呼び出して取得する CGFunction オブジェクト。この呼び出しルーチンは、特定の点で描画する色を返さなければなりません。
- 始点または終点を超える領域を単色で塗りつぶすかどうかを指定するブール値。
CGShading 作成関数に提供する CGFunction オブジェクトには、呼び出し関数構造体と、呼び出し関数を実装するために Quartz に必要なすべての情報が含まれています。おそらく、CGShading オブジェクトを設定するのに最も難しいのは、CGFunction オブジェクトを作成することです。関数 CGFunctionCreate を呼び出すと、あなたは以下の情報を提供します。
- 呼び出し関数が必要とするデータへのポインタ。
- 呼び出し関数への入力値の数。Quartz では、呼び出し関数に 1 つの入力値が必要です。
- 浮動小数点値の配列。Quartz は、この配列内の 1 つの要素だけで呼び出し関数を提供します。入力値の範囲は、グラデーションの始点の色の 0 から、グラデーションの終点の色の 1 までです。
- 呼び出し関数によって提供される出力値の数。各入力値に対して、呼び出し関数は各色成分の値と不透明度を指定するアルファ値を供給しなければなりません。色成分値は、作成した色空間内の Quartz によって解釈され、CGShading 作成関数に供給されます。たとえば、RGB 色空間を使用している場合は、出力値の数(R、G、B、および A) として 4 つの値を指定します。
- 各色成分とアルファ値を指定する浮動小数点値の配列。
- 構造体のバージョンを含む呼び出し関数データ構造体(このフィールドを 0 に設定します)、色成分値を生成するための呼び出し関数、および info パラメータで呼び出し関数に提供されたデータを解放するオプションの呼び出し関数。呼び出し関数の名前を myCalculateShadingValues だすると、以下のようになります。
void myCalculateShadingValues (void *info, const CGFloat *in, CGFloat *out)
CGShading オブジェクトを作成した後、必要に応じて追加のクリッピングを設定することができます。次に、CGContextDrawShading 関数を呼び出して、グラデーションでコンテキストのクリッピング領域をペイントします。この関数を呼び出すと、Quartz はあなたの呼び出し関数を呼び出して、開始点から終了点までの範囲にわたる色値を取得します。
CGShading オブジェクトが不要になったら、CGShadingRelease 関数を呼び出して CGShading オブジェクトを解放します。
CGShading オブジェクトを使用して軸方向のグラデーションをペイント と、CGShading オブジェクトを使用して放射状グラデーションをペイント では、CGShading オブジェクトを使用してグラデーションを描画するコードを記述する手順を説明します。
CGShading オブジェクトを使用して軸方向のグラデーションをペイント
軸方向および放射状のグラデーションでは、同様の手順を実行する必要があります。この例では、CGShading オブジェクトを使用して軸方向のグラデーションを描画し、グラフィックスコンテキストで半円形のクリッピングパスを作成し、次にクリッピングされたコンテキストにグラデーションをペイントして図 8-11 の出力を得る方法を示します。
図 8-11 クリップされペイントされた軸方向のグラデーション
図に示す軸方向のグラデーションをペイントするには、以下の節で説明する手順に従って下さい。
- 色値を計算する CGFunction オブジェクトの設定
- 軸方向のグラデーション用に CGShading オブジェクトを作成
- コンテキストのクリップ
- CGShading オブジェクトを使って軸方向のグラデーションをペイント
- オブジェクトの解放
色値を計算する CGFunction オブジェクトの設定
色値計算関数が 3 つのパラメータを取る限り、好きなように色値を計算できます:
- void *info。これは NULL または CGShading 作成関数に渡すデータへのポインタです。
- const CGFloat *in。Quartz は in 配列を呼び出し関数に渡します。配列の値は、CGFunction オブジェクト用に定義された入力値の範囲内でなければなりません。この例では、入力範囲は 0〜1 です。リスト 8-7 を参照してください。
- CGFloat *out。呼び出し関数は out 配列を Quartz に渡します。これは、色空間内の各色成分ごとに 1 つの要素とアルファ値を含んでいます。出力値は、CGFunction オブジェクト用に定義された出力値の範囲内にある必要があります。この例では、出力範囲は 0〜1 です。リスト 8-7 を参照してください。
これらのパラメータの詳細については、CGFunctionEvaluateCallback を参照してください。
リスト 8-6 は、定数配列で定義された値に入力値を乗算して色成分値を計算する関数を示しています。入力値の範囲は 0 から 1 までであるため、出力値は黒(RGB の場合は 0、0、0) から紫の色相(1、0、.5) の範囲です。最後の成分は常に 1 に設定されているため、色は常に完全に不透明です。
リスト 8-6 色成分値の計算
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 オブジェクトを作成する関数を示しています。番号付きコード行の詳細な説明は、リストの後に示します。
リスト 8-7   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 }
コードの動作は以下の通りです。
- パラメータとして色空間をとります。
- 呼び出し関数構造体を宣言し、構造体のバージョン(0)、色成分の計算呼び出し関数へのポインタ、およびオプションの解放関数の NULL で満たします。
- 色空間内の色成分の数を計算し、アルファ値を考慮に入れて値を 1 だけ増分します。
- numComponents 値へのポインタを渡します。この値は、呼び出し関数 myCalculateShadingValues によって、計算する成分の数を決定するために使用されます。
- 1 が呼び出し関数の入力値の数であることを指定します。
- 入力の有効な間隔を指定する配列を提供します。この配列には 0 と 1 が含まれます。
- 色成分の数にアルファ値を加えた数の出力値を渡します。
- 各出力値の有効な間隔を指定する配列を提供します。この配列は、各成分の間隔 0 と 1 を指定します。4 つの成分があるため、この配列には 8 つの要素があります。
- 以前に宣言されていた呼び出し関数構造体へのポインタを渡します。
軸方向のグラデーション用に CGShading オブジェクトを作成
CGShading オブジェクトを作成するには、リスト 8-8 に示すように、CGShadingCreateAxial 関数を呼び出し、色空間、開始点と終点、CGFunction オブジェクト、およびグラデーションの開始点と終点を超えて領域を塗りつぶすかどうかを指定するブール値を渡します。
リスト 8-8 軸方向のグラデーション用に 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 にはあります。
リスト 8-9 グラフィックスコンテキストに半円クリップを追加する
CGContextBeginPath (myContext); CGContextAddArc (myContext, .5, .5, .3, 0, my_convert_to_radians (180), 0); CGContextClosePath (myContext); CGContextClip (myContext);
CGShading オブジェクトを使って軸方向のグラデーションをペイント
CGShading オブジェクトで指定された色グラデーションを使用して現在のコンテキストを塗りつぶすには、CGContextDrawShading 関数を呼び出します。
CGContextDrawShading(myContext、myShading);
オブジェクトの解放
CGShading オブジェクトが不要になったら、関数 CGShadingRelease を呼び出します。リスト 8-10 に示すように、CGColorSpace オブジェクトと CGFunction オブジェクトも解放する必要があります。
リスト 8-10 オブジェクトの解放
CGShadingRelease (myShading); CGColorSpaceRelease (colorspace); CGFunctionRelease (myFunctionObject);
CGShading オブジェクトを使った軸方向のグラデーション用の完全なルーチン
リスト 8-11 のコードは、リスト 8-7 で設定した CGFunction オブジェクトと リスト 8-6 で示した呼び出し関数を使用して、軸方向のグラデーションをペイントする完全なルーチンを示しています。番号付きコード行の詳細な説明は、リストの後に示します。
リスト 8-11 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 }
コードの動作は以下の通りです
- そこに描画するグラフィックスコンテキストと長方形をパラメータとして取ります。
- 開始点に値を割り当てます。このルーチンは、0 から 1 まで変化するユーザー空間に基づいて値を計算します。Quartz が描画するウィンドウの空間を後で拡大縮小します。この座標位置は、x は左端にあり y は下端から 50% と考えることができます。
- 終点に値を割り当てます。この座標位置は、x が右端にあり、y は下から 50% と考えることができます。ご覧のように、グラデーションの軸は水平です。
- このルーチンはディスプレイに描画するため、デバイス RGB の色空間を作成します。
- リスト 8-7 に示したルーチンを呼び出して CGFunction オブジェクトを作成し、今作成した色空間を渡します。
- 軸方向のグラデーションの CGShading オブジェクトを作成します。Quartz が開始点と終了点を超えて領域を埋めるべきでないことを示すために、最後の 2 つのパラメータは false です。
- 描画に使用されるウィンドウの高さと幅に合わせて拡大縮小されたアフィン変換を設定します。高さは必ずしも幅と同じではないことに注意してください。この例では、2 つが等しくないため、最終結果は円形ではなく楕円です。
- 今設定した変形を、ルーチンに渡されたグラフィックスコンテキストと連結します。
- 後でこの状態を復元できるようにグラフィックス状態を保存します。
- クリッピング領域を設定します。この行と次の 2 行は、白で塗りつぶされた長方形にコンテキストをクリップします。グラデーションは白い背景のウィンドウに描画されます。
- パスを作成します。この行と次の 3 行は円の半分の円弧を設定し、それをグラフィックスコンテキストにクリッピング領域として追加します。効果は、グラデーションが半円の領域に描画されることです。ただし、円はウィンドウの高さと幅(ステップ 8 を参照) によって変形され、半分の楕円に描画されたグラデーションの最終的な効果をもたらします。ユーザがウィンドウのサイズを変更すると、クリッピング領域のサイズが変更されます。
- グラデーションをグラフィックスコンテキストにペイントし、前述のようにグラデーションを変形し、クリップします。
- オブジェクトを解放します。この行と次の 2 行は、作成したすべてのオブジェクトを解放します。
- 塗りつぶされた背景を設定して半円にクリップする前の状態にグラフィックス状態を復元します。復元された状態は、ウィンドウの幅と高さによって変形します。
CGShading オブジェクトを使用して放射状グラデーションをペイント
この例では、CGShading オブジェクトを使用して図 8-12 に示す出力を生成する方法を示します。
図 8-12   CGShading オブジェクトを使用して作成された放射状グラデーション
放射状グラデーションをペイントするには、以下の節で説明する手順に従います。
- 色値を計算する CGFunction オブジェクトの設定
- 放射状グラデーション用の CGShading オブジェクトの作成
- CGShading オブジェクトを使った放射状グラデーションのペイント
- オブジェクトの解放
色値を計算する CGFunction オブジェクトの設定
放射状のグラデーションと軸方向のグラデーションの色値を計算する関数を書くことには違いはありません。実際には、色値を計算する CGFunction オブジェクトの設定 での、軸方向のグラデーションについて説明した指示に従うことができます。リスト 8-12 では、色成分が関数で宣言された頻度値に基づいた周期で正弦波状に変化するように色を計算します。図8-12 に示す結果は、図 8-11 に示す色とはまったく異なります。色出力の違いにもかかわらず、リスト 8-12 のコードは リスト 8-6 に似ていますが、各関数は同じプロトタイプに従っています。各関数は 1 つの入力値を取り、色空間の各色成分に 1 つプラスアルファ値を加えた N 値を計算します。
リスト 8-12 色成分値の計算
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 オブジェクトまたは放射状グラデーションを作成するには、リスト 8-13 に示すように、CGShadingCreateRadial 関数を呼び出し、色空間、開始点と終点、開始半径と終了半径、CGFunction オブジェクト、およびブール値を渡して グラデーションの開始点と終了点を超えて領域を塗りつぶすかどうか指定します。
図 8-13 放射状グラデーション用の 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 を呼び出します。リスト 8-14 に示すように、CGColorSpace オブジェクトと CGFunction オブジェクトも解放する必要があります。
リスト 8-14 オブジェクトを解放するリスト
CGShadingRelease (myShading); CGColorSpaceRelease (colorspace); CGFunctionRelease (myFunctionObject);
CGShading オブジェクトを使用した放射状グラデーションペイントのための完全なルーチン
リスト 8-15 のコードは、リスト 8-7 で設定した CGFunction オブジェクトと リスト 8-12 で示した呼び出し関数を使用して放射状グラデーションをペイントする完全なルーチンを示しています。番号付きコード行の詳細な説明は、リストの後に示します。
リスト 8-15   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 }
コードの動作は以下の通りです
- その中に描画するグラフィックスコンテキストと長方形をパラメータとして取ります。
- 開始円の中心に値を割り当てます。このルーチンは、0 から 1 まで変化するユーザー空間に基づいて値を計算します。Quartz がその中に描画するウィンドウの空間を後で拡大縮小します。この座標位置は、x が左から 25%、y が下から 30% と考えることができます。
- 開始円の半径を割り当てます。これをユーザー空間の幅の 10% と考えることができます。
- 終了円の中心に値を割り当てます。この座標位置は x が左から 70%、y が下から 70% と考えることができます。
- 終了円の半径を割り当てます。これをユーザー空間の幅の 25% と考えることができます。終了円は開始円より大きくなります。円錐形は、左から右に向かって上向きに傾いています。
- このルーチンはディスプレイに描画されるため、デバイス RGB の色空間を作成します。
- リスト 8-7 に示したルーチンを呼び出して、CGFunctionObject を作成し、今作成した色空間を渡してます。ただし、リスト 8-12 に示した色計算関数を使用することを思い出してください。
- 放射状グラデーション用の CGShading オブジェクトを作成します。最後の 2 つのパラメータは、Quartz がグラデーションの開始点と終了点を超えて領域を塗りつぶすべきでないことを示すために false です。
- 描画に使用されるウィンドウの高さと幅に合わせて拡大縮小されるアフィン変換を設定します。高さは必ずしも幅と同じではないことに注意してください。実際には、ユーザーがウィンドウのサイズを変更するたびに変換は変更されます。
- 今設定した変換を、ルーチンに渡されたグラフィックスコンテキストと連結します。
- 後でこの状態を復元できるようにグラフィックスの状態を保存します。
- クリッピング領域を設定します。この行と次の 2 行は、白で塗りつぶされた長方形にコンテキストをクリップします。グラデーションは白い背景のウィンドウに描画されます。
- 前述のようにグラデーションをグラフィックスコンテキストに変換してグラデーションをペイントします。
- オブジェクトを解放します。この行と次の 2 行は、作成したすべてのオブジェクトを解放します。
- 塗りつぶした背景を設定する前の状態にグラフィックス状態を復元します。復元された状態は、ウィンドウの幅と高さによって変わります。
以下も見よ
- CGGradient リファレンス では、CGGradient オブジェクトを作成する関数について説明します。
- CGShading リファレンス では、CGShading オブジェクトを作成する関数について説明しています。
- CGFunction リファレンス では、CGShading オブジェクト用のグラデーションカラーを計算するために必要な関数について説明します。
- CGContext リファレンス では、CGGradient オブジェクトと CGShading オブジェクトでコンテキストに描画する関数について説明します。