[Drawing API] Add ModifyCTM (Matrix m) to Xwt.Drawing.Context API
This provides the link between the Matrix transforms facilities and Drawing.Contexts An sample of using this for specialised transforms has been added to DrawingTransforms.cs and unit tests hve been aded to Testing/Tests/TransformTests.cs
This commit is contained in:
Родитель
ff4ad28bca
Коммит
c98579a302
|
@ -50,7 +50,8 @@ namespace Samples
|
|||
public virtual void Transforms (Xwt.Drawing.Context ctx, double x, double y)
|
||||
{
|
||||
Rotate (ctx, x, y);
|
||||
Scale (ctx, x + 120, y);
|
||||
Scale (ctx, x + 140, y);
|
||||
Reflect (ctx, x+20, y + 100);
|
||||
}
|
||||
|
||||
public virtual void Rotate (Xwt.Drawing.Context ctx, double x, double y)
|
||||
|
@ -126,6 +127,51 @@ namespace Samples
|
|||
|
||||
ctx.Restore ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Illustrates the use of matrix transforms to skew and reflect text
|
||||
/// </summary>
|
||||
public void Reflect (Context ctx, double x, double y)
|
||||
{
|
||||
ctx.Save ();
|
||||
ctx.SetLineWidth (1);
|
||||
|
||||
TextLayout layout = new TextLayout ();
|
||||
layout.Text = "Reflected and Skewed Text";
|
||||
layout.Font = Font.WithSize (16);
|
||||
Size size = layout.GetSize ();
|
||||
Rectangle r = new Rectangle (Point.Zero, size);
|
||||
|
||||
// Draw text with no transformations at (x+0.5, y+0.5)
|
||||
ctx.Translate (x+0.5, y+0.5); // final move to specified location
|
||||
ctx.SetColor (Colors.Blue);
|
||||
ctx.DrawTextLayout (layout, 0, 0);
|
||||
|
||||
// Use Matrix transforms to reflect Y-values and skew X-values by -0.5*Y
|
||||
// Note that transforms are prepended, so are actioned in reverse order
|
||||
// This is the same order that Backend Context transforms are applied
|
||||
|
||||
Matrix m = new Matrix (); // Identity matrix
|
||||
Matrix s = new Matrix (1.0, 0.0, // new skew matrix
|
||||
-0.5, 1.0,
|
||||
0.0, 0.0);
|
||||
|
||||
m.TranslatePrepend (0, size.Height); // Shift text back to place
|
||||
m.Prepend (s); // Skew X-values
|
||||
m.ScalePrepend (1, -1); // Reflect text Y-values
|
||||
m.TranslatePrepend (0, -size.Height); // Shift text base to (0,0)
|
||||
|
||||
ctx.ModifyCTM (m); // NB ctx.Translate (x+0.5, y+0.5) still active
|
||||
|
||||
ctx.SetColor (Colors.DarkGray);
|
||||
ctx.Rectangle (r);
|
||||
ctx.Fill ();
|
||||
ctx.SetColor (Colors.LightBlue);
|
||||
ctx.DrawTextLayout (layout, 0, 0);
|
||||
|
||||
ctx.Restore ();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,9 @@ namespace Xwt
|
|||
[TestFixture]
|
||||
public class TransformTests
|
||||
{
|
||||
// There is only a single context used in all tests,
|
||||
// so it MUST be Saved and Restored for each test.
|
||||
|
||||
const double DELTA = 0.000000001d;
|
||||
|
||||
ImageBuilder ib = null;
|
||||
|
@ -49,8 +52,11 @@ namespace Xwt
|
|||
[TestFixtureSetUp]
|
||||
public void Init ()
|
||||
{
|
||||
ib = new ImageBuilder (10, 10);
|
||||
ib = new ImageBuilder (1, 1);
|
||||
context = ib.Context;
|
||||
Matrix m1 = Matrix.Identity;
|
||||
Matrix m2 = context.GetCTM ();
|
||||
CheckMatrix (m1, m2);
|
||||
}
|
||||
|
||||
[TestFixtureTearDown]
|
||||
|
@ -58,12 +64,11 @@ namespace Xwt
|
|||
{
|
||||
if (context != null)
|
||||
context.Dispose ();
|
||||
|
||||
if (ib != null)
|
||||
ib.Dispose ();
|
||||
}
|
||||
|
||||
private Context NewContext {
|
||||
private Context Context {
|
||||
get { return context; }
|
||||
}
|
||||
|
||||
|
@ -77,19 +82,14 @@ namespace Xwt
|
|||
Assert.AreEqual (expected.OffsetY, actual.OffsetY, DELTA);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NewCTM ()
|
||||
{
|
||||
Matrix mI = Matrix.Identity;
|
||||
Context ctx = NewContext;
|
||||
CheckMatrix (mI, ctx.GetCTM ());
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void Translate ()
|
||||
{
|
||||
Matrix m1, m2;
|
||||
Context ctx = NewContext;
|
||||
Context ctx = Context;
|
||||
ctx.Save ();
|
||||
|
||||
// Check with a range of -ve and +ve offsets
|
||||
for (double tx = -1; tx <= 1; tx += 0.25) {
|
||||
|
@ -110,13 +110,15 @@ namespace Xwt
|
|||
CheckMatrix (m1, m2);
|
||||
ctx.Restore ();
|
||||
}
|
||||
ctx.Restore ();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Scale ()
|
||||
{
|
||||
Matrix m1, m2;
|
||||
Context ctx = NewContext;
|
||||
Context ctx = Context;
|
||||
ctx.Save ();
|
||||
|
||||
// Scaling with a zero scale-factor results in a non-invertible matrix
|
||||
// This fails with Cairo, so avoid zero as one of the test values
|
||||
|
@ -138,13 +140,15 @@ namespace Xwt
|
|||
CheckMatrix (m1, m2);
|
||||
ctx.Restore ();
|
||||
}
|
||||
ctx.Restore ();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Rotate ()
|
||||
{
|
||||
Matrix m1, m2;
|
||||
Context ctx = NewContext;
|
||||
Context ctx = Context;
|
||||
ctx.Save ();
|
||||
|
||||
for (double theta = 0; theta <= 360; theta += 30) {
|
||||
ctx.Save ();
|
||||
|
@ -155,6 +159,76 @@ namespace Xwt
|
|||
CheckMatrix (m1, m2);
|
||||
ctx.Restore ();
|
||||
}
|
||||
ctx.Restore ();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RotateAndTranslate ()
|
||||
{
|
||||
// Transforms are Prepended to the CTM, so they are done in reverse order.
|
||||
// At present, this must be stated explicitly in the Matrix calls, but may
|
||||
// be worth changing so that Prepend is the default (to match the Backends).
|
||||
|
||||
Matrix m1, m2;
|
||||
Context ctx = Context;
|
||||
ctx.Save ();
|
||||
|
||||
for (double theta = 30; theta <= 360; theta += 30) {
|
||||
ctx.Save ();
|
||||
ctx.Translate (100, 0); // done last
|
||||
ctx.Rotate (theta); // done first (about the origin)
|
||||
|
||||
m1 = Matrix.Identity;
|
||||
m1.TranslatePrepend (100, 0);
|
||||
m1.RotatePrepend (theta);
|
||||
m2 = ctx.GetCTM ();
|
||||
|
||||
CheckMatrix (m1, m2);
|
||||
ctx.Restore ();
|
||||
}
|
||||
ctx.Restore ();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ModifyCTM ()
|
||||
{
|
||||
// Checks that Matrix and Context transforms match, and that applying
|
||||
// a matrix transform to the Context CTM produces the expected result
|
||||
|
||||
double theta = 30;
|
||||
double x = 50;
|
||||
double y = 30;
|
||||
|
||||
Context ctx = Context;
|
||||
ctx.Save ();
|
||||
|
||||
Matrix m1 = Matrix.Identity;
|
||||
Matrix ctm;
|
||||
|
||||
// Apply and compare several transforms
|
||||
ctx.Save ();
|
||||
ctx.Scale (2, 3);
|
||||
m1.ScalePrepend (2, 3);
|
||||
ctm = ctx.GetCTM ();
|
||||
CheckMatrix (m1, ctm);
|
||||
|
||||
ctx.Translate (x, y);
|
||||
m1.TranslatePrepend (x, y);
|
||||
ctm = ctx.GetCTM ();
|
||||
CheckMatrix (m1, ctm);
|
||||
|
||||
ctx.Rotate (theta);
|
||||
m1.RotatePrepend (theta);
|
||||
ctm = ctx.GetCTM ();
|
||||
CheckMatrix (m1, ctm);
|
||||
|
||||
// Check ModifyCTM matches combined Matrix Transform
|
||||
ctx.Restore ();
|
||||
ctx.ModifyCTM (m1);
|
||||
ctm = ctx.GetCTM ();
|
||||
CheckMatrix (m1, ctm);
|
||||
|
||||
ctx.Restore ();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -339,13 +339,20 @@ namespace Xwt.CairoBackend
|
|||
CairoContextBackend gc = (CairoContextBackend)backend;
|
||||
gc.Context.Scale (scaleX, scaleY);
|
||||
}
|
||||
|
||||
|
||||
public override void Translate (object backend, double tx, double ty)
|
||||
{
|
||||
CairoContextBackend gc = (CairoContextBackend)backend;
|
||||
gc.Context.Translate (tx, ty);
|
||||
}
|
||||
|
||||
public override void ModifyCTM (object backend, Matrix m)
|
||||
{
|
||||
CairoContextBackend gc = (CairoContextBackend)backend;
|
||||
Cairo.Matrix t = new Cairo.Matrix (m.M11, m.M12, m.M21, m.M22, m.OffsetX, m.OffsetY);
|
||||
gc.Context.Transform (t);
|
||||
}
|
||||
|
||||
public override Matrix GetCTM (object backend)
|
||||
{
|
||||
Cairo.Matrix t = ((CairoContextBackend)backend).Context.Matrix;
|
||||
|
|
|
@ -319,6 +319,14 @@ namespace Xwt.Mac
|
|||
((CGContextBackend)backend).Context.TranslateCTM ((float)tx, (float)ty);
|
||||
}
|
||||
|
||||
public override void ModifyCTM (object backend, Matrix m)
|
||||
{
|
||||
CGAffineTransform t = new CGAffineTransform ((float)m.M11, (float)m.M12,
|
||||
(float)m.M21, (float)m.M22,
|
||||
(float)m.OffsetX, (float)m.OffsetY);
|
||||
((CGContextBackend)backend).Context.ConcatCTM (t);
|
||||
}
|
||||
|
||||
public override Matrix GetCTM (object backend)
|
||||
{
|
||||
CGAffineTransform t = GetContextTransform ((CGContextBackend)backend);
|
||||
|
|
|
@ -297,6 +297,13 @@ namespace Xwt.WPFBackend
|
|||
c.PushTransform (t);
|
||||
}
|
||||
|
||||
public override void ModifyCTM (object backend, Drawing.Matrix m)
|
||||
{
|
||||
var c = (DrawingContext)backend;
|
||||
MatrixTransform t = new MatrixTransform (m.M11, m.M12, m.M21, m.M22, m.OffsetX, m.OffsetY);
|
||||
c.PushTransform (t);
|
||||
}
|
||||
|
||||
public override Drawing.Matrix GetCTM (object backend)
|
||||
{
|
||||
var c = (DrawingContext)backend;
|
||||
|
|
|
@ -70,6 +70,8 @@ namespace Xwt.Backends
|
|||
|
||||
public abstract void Translate (object backend, double tx, double ty);
|
||||
|
||||
public abstract void ModifyCTM (object backend, Matrix transform);
|
||||
|
||||
public abstract Matrix GetCTM (object backend);
|
||||
|
||||
public abstract bool IsPointInStroke (object backend, double x, double y);
|
||||
|
|
|
@ -222,6 +222,18 @@ namespace Xwt.Drawing
|
|||
handler.Translate (Backend, p.X, p.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies the Current Transformation Matrix (CTM) by prepending the Matrix transform specified
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This enables any 'non-standard' transforms (eg skew, reflection) to be used for drawing,
|
||||
/// and provides the link to the extra transform capabilities provided by Xwt.Drawing.Matrix
|
||||
/// </remarks>
|
||||
public void ModifyCTM (Matrix transform)
|
||||
{
|
||||
handler.ModifyCTM (Backend, transform);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the current transformation matrix (CTM)
|
||||
/// </summary>
|
||||
|
@ -229,7 +241,7 @@ namespace Xwt.Drawing
|
|||
{
|
||||
return handler.GetCTM (Backend);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the point (x, y) by the current transformation matrix (CTM)
|
||||
/// </summary>
|
||||
|
|
|
@ -548,6 +548,13 @@ namespace Xwt.Drawing
|
|||
ctx.NativeContextHandler.Translate (ctx.NativeBackend, tx, ty);
|
||||
}
|
||||
|
||||
public override void ModifyCTM (object backend, Matrix t)
|
||||
{
|
||||
var ctx = (VectorBackend)backend;
|
||||
CreateNativePathBackend (ctx);
|
||||
ctx.NativeContextHandler.ModifyCTM (ctx.NativeBackend, t);
|
||||
}
|
||||
|
||||
public override Matrix GetCTM (object backend)
|
||||
{
|
||||
var ctx = (VectorBackend)backend;
|
||||
|
|
Загрузка…
Ссылка в новой задаче