2
// ContextBackendHandler.cs
5
// Lluis Sanchez <lluis@xamarin.com>
6
// Alex Corrado <corrado@xamarin.com>
8
// Copyright (c) 2011 Xamarin Inc
10
// Permission is hereby granted, free of charge, to any person obtaining a copy
11
// of this software and associated documentation files (the "Software"), to deal
12
// in the Software without restriction, including without limitation the rights
13
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
// copies of the Software, and to permit persons to whom the Software is
15
// furnished to do so, subject to the following conditions:
17
// The above copyright notice and this permission notice shall be included in
18
// all copies or substantial portions of the Software.
20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
33
using MonoMac.Foundation;
34
using MonoMac.CoreGraphics;
39
class CGContextBackend {
40
public CGContext Context;
42
public GradientInfo Gradient;
43
public CGAffineTransform? InverseViewTransform;
46
public class ContextBackendHandler: IContextBackendHandler
48
const double degrees = System.Math.PI / 180d;
50
public void Save (object backend)
52
((CGContextBackend)backend).Context.SaveState ();
55
public void Restore (object backend)
57
((CGContextBackend)backend).Context.RestoreState ();
60
public void SetGlobalAlpha (object backend, double alpha)
62
((CGContextBackend)backend).Context.SetAlpha ((float)alpha);
65
public void Arc (object backend, double xc, double yc, double radius, double angle1, double angle2)
67
CGContext ctx = ((CGContextBackend)backend).Context;
68
ctx.AddArc ((float)xc, (float)yc, (float)radius, (float)(angle1 * degrees), (float)(angle2 * degrees), false);
71
public void ArcNegative (object backend, double xc, double yc, double radius, double angle1, double angle2)
73
CGContext ctx = ((CGContextBackend)backend).Context;
74
ctx.AddArc ((float)xc, (float)yc, (float)radius, (float)(angle1 * degrees), (float)(angle2 * degrees), true);
77
public void Clip (object backend)
79
((CGContextBackend)backend).Context.Clip ();
82
public void ClipPreserve (object backend)
84
CGContext ctx = ((CGContextBackend)backend).Context;
85
using (CGPath oldPath = ctx.CopyPath ()) {
87
ctx.AddPath (oldPath);
91
public void ClosePath (object backend)
93
((CGContextBackend)backend).Context.ClosePath ();
96
public void CurveTo (object backend, double x1, double y1, double x2, double y2, double x3, double y3)
98
((CGContextBackend)backend).Context.AddCurveToPoint ((float)x1, (float)y1, (float)x2, (float)y2, (float)x3, (float)y3);
101
public void Fill (object backend)
103
CGContextBackend gc = (CGContextBackend)backend;
104
CGContext ctx = gc.Context;
105
SetupContextForDrawing (ctx);
107
if (gc.Gradient != null)
108
GradientBackendHandler.Draw (ctx, gc.Gradient);
110
ctx.DrawPath (CGPathDrawingMode.Fill);
113
public void FillPreserve (object backend)
115
CGContext ctx = ((CGContextBackend)backend).Context;
116
using (CGPath oldPath = ctx.CopyPath ()) {
118
ctx.AddPath (oldPath);
122
public void LineTo (object backend, double x, double y)
124
((CGContextBackend)backend).Context.AddLineToPoint ((float)x, (float)y);
127
public void MoveTo (object backend, double x, double y)
129
((CGContextBackend)backend).Context.MoveTo ((float)x, (float)y);
132
public void NewPath (object backend)
134
((CGContextBackend)backend).Context.BeginPath ();
137
public void Rectangle (object backend, double x, double y, double width, double height)
139
((CGContextBackend)backend).Context.AddRect (new RectangleF ((float)x, (float)y, (float)width, (float)height));
142
public void RelCurveTo (object backend, double dx1, double dy1, double dx2, double dy2, double dx3, double dy3)
144
CGContext ctx = ((CGContextBackend)backend).Context;
145
PointF p = ctx.GetPathCurrentPoint ();
146
ctx.AddCurveToPoint ((float)(p.X + dx1), (float)(p.Y + dy1), (float)(p.X + dx2), (float)(p.Y + dy2), (float)(p.X + dx3), (float)(p.Y + dy3));
149
public void RelLineTo (object backend, double dx, double dy)
151
CGContext ctx = ((CGContextBackend)backend).Context;
152
PointF p = ctx.GetPathCurrentPoint ();
153
ctx.AddLineToPoint ((float)(p.X + dx), (float)(p.Y + dy));
156
public void RelMoveTo (object backend, double dx, double dy)
158
CGContext ctx = ((CGContextBackend)backend).Context;
159
PointF p = ctx.GetPathCurrentPoint ();
160
ctx.MoveTo ((float)(p.X + dx), (float)(p.Y + dy));
163
public void Stroke (object backend)
165
CGContext ctx = ((CGContextBackend)backend).Context;
166
SetupContextForDrawing (ctx);
167
ctx.DrawPath (CGPathDrawingMode.Stroke);
170
public void StrokePreserve (object backend)
172
CGContext ctx = ((CGContextBackend)backend).Context;
173
SetupContextForDrawing (ctx);
174
using (CGPath oldPath = ctx.CopyPath ()) {
175
ctx.DrawPath (CGPathDrawingMode.Stroke);
176
ctx.AddPath (oldPath);
180
public void SetColor (object backend, Xwt.Drawing.Color color)
182
CGContextBackend gc = (CGContextBackend)backend;
184
CGContext ctx = gc.Context;
185
ctx.SetFillColorSpace (Util.DeviceRGBColorSpace);
186
ctx.SetStrokeColorSpace (Util.DeviceRGBColorSpace);
187
ctx.SetFillColor ((float)color.Red, (float)color.Green, (float)color.Blue, (float)color.Alpha);
188
ctx.SetStrokeColor ((float)color.Red, (float)color.Green, (float)color.Blue, (float)color.Alpha);
191
public void SetLineWidth (object backend, double width)
193
((CGContextBackend)backend).Context.SetLineWidth ((float)width);
196
public void SetLineDash (object backend, double offset, params double[] pattern)
198
float[] array = new float[pattern.Length];
199
for (int n=0; n<pattern.Length; n++)
200
array [n] = (float) pattern[n];
201
if (array.Length == 0)
202
array = new float [] { 1 };
203
((CGContextBackend)backend).Context.SetLineDash ((float)offset, array);
206
public void SetPattern (object backend, object p)
208
CGContextBackend gc = (CGContextBackend)backend;
209
gc.Gradient = p as GradientInfo;
210
if (gc.Gradient != null || !(p is CGPattern))
212
CGContext ctx = gc.Context;
213
CGPattern pattern = (CGPattern)p;
214
float[] alpha = new[] { 1.0f };
215
ctx.SetFillColorSpace (Util.PatternColorSpace);
216
ctx.SetStrokeColorSpace (Util.PatternColorSpace);
217
ctx.SetFillPattern (pattern, alpha);
218
ctx.SetStrokePattern (pattern, alpha);
221
public void SetFont (object backend, Xwt.Drawing.Font font)
223
((CGContextBackend)backend).Context.SelectFont (font.Family, (float)font.Size, CGTextEncoding.FontSpecific);
226
public void DrawTextLayout (object backend, TextLayout layout, double x, double y)
228
CGContext ctx = ((CGContextBackend)backend).Context;
229
SetupContextForDrawing (ctx);
230
TextLayoutBackendHandler.Draw (ctx, WidgetRegistry.GetBackend (layout), x, y);
233
public void DrawImage (object backend, object img, double x, double y, double alpha)
235
CGContext ctx = ((CGContextBackend)backend).Context;
236
NSImage image = (NSImage)img;
237
var rect = new RectangleF (PointF.Empty, image.Size);
239
ctx.SetAlpha ((float)alpha);
240
ctx.TranslateCTM ((float)x, (float)y + rect.Height);
241
ctx.ScaleCTM (1f, -1f);
242
ctx.DrawImage (rect, image.AsCGImage (RectangleF.Empty, null, null));
246
public void DrawImage (object backend, object img, double x, double y, double width, double height, double alpha)
248
var srcRect = new Rectangle (Point.Zero, ((NSImage)img).Size.ToXwtSize ());
249
var destRect = new Rectangle (x, y, width, height);
250
DrawImage (backend, img, srcRect, destRect, alpha);
253
public void DrawImage (object backend, object img, Rectangle srcRect, Rectangle destRect, double alpha)
255
CGContext ctx = ((CGContextBackend)backend).Context;
256
NSImage image = (NSImage) img;
257
var rect = new RectangleF (0, 0, (float)destRect.Width, (float)destRect.Height);
259
ctx.SetAlpha ((float)alpha);
260
ctx.TranslateCTM ((float)destRect.X, (float)destRect.Y + rect.Height);
261
ctx.ScaleCTM (1f, -1f);
262
ctx.DrawImage (rect, image.AsCGImage (RectangleF.Empty, null, null).WithImageInRect (srcRect.ToRectangleF ()));
266
public void Rotate (object backend, double angle)
268
((CGContextBackend)backend).Context.RotateCTM ((float)(angle * degrees));
271
public void Scale (object backend, double scaleX, double scaleY)
273
((CGContextBackend)backend).Context.ScaleCTM ((float)scaleX, (float)scaleY);
276
public void Translate (object backend, double tx, double ty)
278
((CGContextBackend)backend).Context.TranslateCTM ((float)tx, (float)ty);
281
public void TransformPoint (object backend, ref double x, ref double y)
283
CGAffineTransform t = GetContextTransform ((CGContextBackend)backend);
285
PointF p = t.TransformPoint (new PointF ((float)x, (float)y));
290
public void TransformDistance (object backend, ref double dx, ref double dy)
292
CGAffineTransform t = GetContextTransform ((CGContextBackend)backend);
293
// remove translational elements from CTM
297
PointF p = t.TransformPoint (new PointF ((float)dx, (float)dy));
302
public void TransformPoints (object backend, Point[] points)
304
CGAffineTransform t = GetContextTransform ((CGContextBackend)backend);
307
for (int i = 0; i < points.Length; ++i) {
308
p = t.TransformPoint (new PointF ((float)points[i].X, (float)points[i].Y));
314
public void TransformDistances (object backend, Distance[] vectors)
316
CGAffineTransform t = GetContextTransform ((CGContextBackend)backend);
320
for (int i = 0; i < vectors.Length; ++i) {
321
p = t.TransformPoint (new PointF ((float)vectors[i].Dx, (float)vectors[i].Dy));
327
public void Dispose (object backend)
329
((CGContextBackend)backend).Context.Dispose ();
332
static CGAffineTransform GetContextTransform (CGContextBackend gc)
334
CGAffineTransform t = gc.Context.GetCTM ();
336
// The CTM returned above actually includes the full view transform.
337
// We only want the transform that is applied to the context, so concat
338
// the inverse of the view transform to nullify that part.
339
if (gc.InverseViewTransform.HasValue)
340
t.Multiply (gc.InverseViewTransform.Value);
345
static void SetupContextForDrawing (CGContext ctx)
347
if (ctx.IsPathEmpty ())
350
// setup pattern drawing to better match the behavior of Cairo
351
var drawPoint = ctx.GetCTM ().TransformPoint (ctx.GetPathBoundingBox ().Location);
352
var patternPhase = new SizeF (drawPoint.X, drawPoint.Y);
353
if (patternPhase != SizeF.Empty)
354
ctx.SetPatternPhase (patternPhase);