using System; using MonoTouch.UIKit; using MonoTouch.Foundation; using System.Drawing; using MonoTouch.CoreGraphics; using QuartzSample; public class GradientDrawingView : QuartzView { CGGradient gradient; public GradientDrawingView () : base () { using (var rgb = CGColorSpace.CreateDeviceRGB()){ float [] colors = { 204f / 255f, 224f / 255f, 244f / 255f, 10f, 29f / 255f, 156f / 255f, 215f / 255f, 10f, 0f / 255f, 50f / 255f, 126f / 255f, 10f, }; gradient = new CGGradient (rgb, colors, null); } } // Returns an appropriate starting point for the demonstration of a linear gradient static PointF demoLGStart (RectangleF bounds) { return new PointF (bounds.X, bounds.Y + bounds.Height * 0.25f); } // Returns an appropriate ending point for the demonstration of a linear gradient PointF demoLGEnd(RectangleF bounds) { return new PointF (bounds.X, bounds.Y + bounds.Height * 0.75f); } // Returns the center point for for the demonstration of the radial gradient PointF demoRGCenter(RectangleF bounds) { return new PointF (bounds.X + bounds.Width/2, bounds.Y + bounds.Height/2); } // Returns an appropriate inner radius for the demonstration of the radial gradient float demoRGInnerRadius(RectangleF bounds) { float r = bounds.Width < bounds.Height ? bounds.Width : bounds.Height; return r * 0.125f; } // Returns an appropriate outer radius for the demonstration of the radial gradient float demoRGOuterRadius(RectangleF bounds) { float r = bounds.Width < bounds.Height ? bounds.Width : bounds.Height; return r * 0.5f; } public override void DrawInContext (CGContext context) { // The clipping rects we plan to use, which also defines the location and span of each gradient var clips = new RectangleF [] { new RectangleF(10, 30, 60, 90), new RectangleF(90, 30, 60, 90), new RectangleF(170, 30, 60, 90), new RectangleF(250, 30, 60, 90), new RectangleF(30, 140, 120, 120), new RectangleF(170, 140, 120, 120), new RectangleF(30, 280, 120, 120), new RectangleF(170, 280, 120, 120), }; // Linear Gradients PointF start, end; // Clip to area to draw the gradient, and draw it. Since we are clipping, we save the graphics state // so that we can revert to the previous larger area. context.SaveState(); context.ClipToRect(clips[0]); // A linear gradient requires only a starting & ending point. // The colors of the gradient are linearly interpolated along the line segment connecting these two points // A gradient location of 0 means that color is expressed fully at the 'start' point // a location of 1 means that color is expressed fully at the 'end' point. // The gradient fills outwards perpendicular to the line segment connectiong start & end points // (which is why we need to clip the context, or the gradient would fill beyond where we want it to). // The gradient options (last) parameter determines what how to fill the clip area that is "before" and "after" // the line segment connecting start & end. start = demoLGStart(clips[0]); end = demoLGEnd(clips[0]); context.DrawLinearGradient(gradient, start, end, 0); context.RestoreState(); // Same as above for each combination of CGGradientDrawingOptions.DrawsBeforeStartLocation & CGGradientDrawingOptions.DrawsAfterEndLocation context.SaveState(); context.ClipToRect(clips[1]); start = demoLGStart(clips[1]); end = demoLGEnd(clips[1]); context.DrawLinearGradient(gradient, start, end, CGGradientDrawingOptions.DrawsBeforeStartLocation); context.RestoreState(); context.SaveState(); context.ClipToRect(clips[2]); start = demoLGStart(clips[2]); end = demoLGEnd(clips[2]); context.DrawLinearGradient(gradient, start, end, CGGradientDrawingOptions.DrawsAfterEndLocation); context.RestoreState(); context.SaveState(); context.ClipToRect(clips[3]); start = demoLGStart(clips[3]); end = demoLGEnd(clips[3]); context.DrawLinearGradient(gradient, start, end, CGGradientDrawingOptions.DrawsBeforeStartLocation | CGGradientDrawingOptions.DrawsAfterEndLocation); context.RestoreState(); // Radial Gradients float startRadius, endRadius; // Clip to area to draw the gradient, and draw it. Since we are clipping, we save the graphics state // so that we can revert to the previous larger area. context.SaveState(); context.ClipToRect(clips[4]); // A radial gradient requires a start & end point as well as a start & end radius. // Logically a radial gradient is created by linearly interpolating the center, radius and color of each // circle using the start and end point for the center, start and end radius for the radius, and the color ramp // inherant to the gradient to create a set of stroked circles that fill the area completely. // The gradient options specify if this interpolation continues past the start or end points as it does with // linear gradients. start = end = demoRGCenter(clips[4]); startRadius = demoRGInnerRadius(clips[4]); endRadius = demoRGOuterRadius(clips[4]); context.DrawRadialGradient(gradient, start, startRadius, end, endRadius, 0); context.RestoreState(); // Same as above for each combination of CGGradientDrawingOptions.DrawsBeforeStartLocation & CGGradientDrawingOptions.DrawsAfterEndLocation context.SaveState(); context.ClipToRect(clips[5]); start = end = demoRGCenter(clips[5]); startRadius = demoRGInnerRadius(clips[5]); endRadius = demoRGOuterRadius(clips[5]); context.DrawRadialGradient(gradient, start, startRadius, end, endRadius, CGGradientDrawingOptions.DrawsBeforeStartLocation); context.RestoreState(); context.SaveState(); context.ClipToRect(clips[6]); start = end = demoRGCenter(clips[6]); startRadius = demoRGInnerRadius(clips[6]); endRadius = demoRGOuterRadius(clips[6]); context.DrawRadialGradient(gradient, start, startRadius, end, endRadius, CGGradientDrawingOptions.DrawsAfterEndLocation); context.RestoreState(); context.SaveState(); context.ClipToRect(clips[7]); start = end = demoRGCenter(clips[7]); startRadius = demoRGInnerRadius(clips[7]); endRadius = demoRGOuterRadius(clips[7]); context.DrawRadialGradient(gradient, start, startRadius, end, endRadius, CGGradientDrawingOptions.DrawsBeforeStartLocation | CGGradientDrawingOptions.DrawsAfterEndLocation); context.RestoreState(); // Show the clipping areas context.SetLineWidth(2); context.SetRGBStrokeColor(1, 0, 0, 1); context.AddRects(clips); context.StrokePath(); } } [Register] public class PatternDrawingView : QuartzView { CGColor coloredPatternColor; CGPattern uncoloredPattern; CGColorSpace uncoloredPatternColorSpace; static void DrawColored (CGContext context) { // Dark Blue context.SetRGBFillColor(29 / 255f, 156 / 255f, 215 / 255f, 10); context.FillRect(new RectangleF(0, 0, 8, 8)); context.FillRect(new RectangleF(8, 8, 8, 8)); // Light Blue context.SetRGBFillColor(204 / 255f, 224 / 255f, 244 / 255f, 10); context.FillRect(new RectangleF(8, 0, 8, 8)); context.FillRect(new RectangleF(0, 8, 8, 8)); } // Uncolored patterns take their color from the given context static void DrawUncolored (CGContext context) { context.FillRect(new RectangleF(0, 0, 8, 8)); context.FillRect(new RectangleF(8, 8, 8, 8)); } public PatternDrawingView () : base () { // First we need to create a CGPattern that specifies the qualities of our pattern. using (var coloredPattern = new CGPattern ( new RectangleF(0, 0, 16, 16), // the pattern coordinate space, drawing is clipped to this rectangle CGAffineTransform.MakeIdentity (), // a transform on the pattern coordinate space used before it is drawn. 16, 16, // the spacing (horizontal, vertical) of the pattern - how far to move after drawing each cell CGPatternTiling.NoDistortion, true, // this is a colored pattern, which means that you only specify an alpha value when drawing it DrawColored)){ // To draw a pattern, you need a pattern colorspace. // Since this is an colored pattern, the parent colorspace is NULL, indicating that it only has an alpha value. using (var coloredPatternColorSpace = CGColorSpace.CreatePattern (null)){ float alpha = 1; // Since this pattern is colored, we'll create a CGColor for it to make drawing it easier and more efficient. // From here on, the colored pattern is referenced entirely via the associated CGColor rather than the // originally created CGPatternRef. coloredPatternColor = new CGColor (coloredPatternColorSpace, coloredPattern, new float [] { alpha }); } } // Uncolored Pattern setup // As above, we create a CGPattern that specifies the qualities of our pattern uncoloredPattern = new CGPattern ( new RectangleF(0, 0, 16, 16), // coordinate space CGAffineTransform.MakeIdentity (), // transform 16, 16, // spacing CGPatternTiling.NoDistortion, false, // this is an uncolored pattern, thus to draw it we need to specify both color and alpha DrawUncolored); // callbacks for this pattern // With an uncolored pattern we still need to create a pattern colorspace, but now we need a parent colorspace // We'll use the DeviceRGB colorspace here. We'll need this colorspace along with the CGPatternRef to draw this pattern later. using (var deviceRGB = CGColorSpace.CreateDeviceRGB()){ uncoloredPatternColorSpace = CGColorSpace.CreatePattern(deviceRGB); } } public override void DrawInContext (CGContext context) { // Draw the colored pattern. Since we have a CGColorRef for this pattern, we just set // that color current and draw. context.SetFillColorWithColor(coloredPatternColor); context.FillRect(new RectangleF(10, 10, 90, 90)); // You can also stroke with a pattern. context.SetStrokeColorWithColor(coloredPatternColor); context.StrokeRectWithWidth(new RectangleF(120, 10, 90, 90), 8); // Since we aren't encapsulating our pattern in a CGColor for the uncolored pattern case, setup requires two steps. // First you have to set the context's current colorspace (fill or stroke) to a pattern colorspace, // indicating to Quartz that you want to draw a pattern. context.SetFillColorSpace(uncoloredPatternColorSpace); // Next you set the pattern and the color that you want the pattern to draw with. var color1 = new float [] {1, 0, 0, 1}; context.SetFillPattern(uncoloredPattern, color1); // And finally you draw! context.FillRect(new RectangleF(10, 120, 90, 90)); // As long as the current colorspace is a pattern colorspace, you are free to change the pattern or pattern color var color2 = new float [] {0, 1, 0, 1}; context.SetFillPattern(uncoloredPattern, color2); context.FillRect(new RectangleF(10, 230, 90, 90)); // And of course, just like the colored case, you can stroke with a pattern as well. context.SetStrokeColorSpace(uncoloredPatternColorSpace); context.SetStrokePattern(uncoloredPattern, color1); context.StrokeRectWithWidth(new RectangleF(120, 120, 90, 90), 8); // As long as the current colorspace is a pattern colorspace, you are free to change the pattern or pattern color context.SetStrokePattern(uncoloredPattern, color2); context.StrokeRectWithWidth(new RectangleF(120, 230, 90, 90), 8); } }