1 module skia.SKMatrix;
2 
3 import skia.Definitions;
4 import skia.Exceptions;
5 import skia.MathTypes;
6 import skia.SkiaApi;
7 // import skia.SKPoint;
8 
9 import std.math;
10 
11 struct SKMatrix
12 {
13 	enum float DegreesToRadians = cast(float)PI / 180.0f;
14 
15 	enum SKMatrix Empty = SKMatrix();
16 
17 	// float scaleX
18 	private float scaleX;
19 	float ScaleX() {
20 		return scaleX;
21 	}
22 
23 	void ScaleX(float value) {
24 		scaleX = value;
25 	}
26 
27 	// float skewX
28 	private float skewX;
29 	float SkewX() {
30 		return skewX;
31 	}
32 
33 	void SkewX(float value) {
34 		skewX = value;
35 	}
36 
37 	// float transX
38 	private float transX;
39 	float TransX() {
40 		return transX;
41 	}
42 
43 	void TransX(float value) {
44 		transX = value;
45 	}
46 
47 	// float skewY
48 	private float skewY;
49 	float SkewY() {
50 		return skewY;
51 	}
52 
53 	void SkewY(float value) {
54 		skewY = value;
55 	}
56 
57 	// float scaleY
58 	private float scaleY;
59 	float ScaleY() {
60 		return scaleY;
61 	}
62 
63 	void ScaleY(float value) {
64 		scaleY = value;
65 	}
66 
67 	// float transY
68 	private float transY;
69 	float TransY() {
70 		return transY;
71 	}
72 
73 	void TransY(float value) {
74 		transY = value;
75 	}
76 
77 	// float persp0
78 	private float persp0;
79 	float Persp0() {
80 		return persp0;
81 	}
82 
83 	void Persp0(float value) {
84 		persp0 = value;
85 	}
86 
87 	// float persp1
88 	private float persp1;
89 	float Persp1() {
90 		return persp1;
91 	}
92 
93 	void Persp1(float value) {
94 		persp1 = value;
95 	}
96 
97 	// float persp2
98 	private float persp2;
99 	float Persp2() {
100 		return persp2;
101 	}
102 
103 	void Persp2(float value) {
104 		persp2 = value;
105 	}
106 
107 	// const bool Equals (SKMatrix obj) {
108 	// 	return scaleX == obj.scaleX && skewX == obj.skewX && transX == obj.transX && skewY == obj.skewY && scaleY == obj.scaleY && transY == obj.transY && persp0 == obj.persp0 && persp1 == obj.persp1 && persp2 == obj.persp2
109 	// }
110 
111 	// const override bool Equals (object obj) {
112 	// 	return obj is SKMatrix f && Equals (f)
113 	// }
114 
115 	// static bool operator == (SKMatrix left, SKMatrix right) {
116 	// 	return left.Equals (right)
117 	// }
118 
119 	// static bool operator != (SKMatrix left, SKMatrix right) {
120 	// 	return !left.Equals (right)
121 	// }
122 
123 	// const override int GetHashCode ()
124 	// {
125 	// 	var hash = new HashCode ();
126 	// 	hash.Add (scaleX);
127 	// 	hash.Add (skewX);
128 	// 	hash.Add (transX);
129 	// 	hash.Add (skewY);
130 	// 	hash.Add (scaleY);
131 	// 	hash.Add (transY);
132 	// 	hash.Add (persp0);
133 	// 	hash.Add (persp1);
134 	// 	hash.Add (persp2);
135 	// 	return hash.ToHashCode ();
136 	// }
137 
138 static SKMatrix Identity()
139   {
140     SKMatrix matrix;
141     matrix.scaleX = 1; 
142     matrix.scaleY = 1;
143     matrix.persp2 = 1;
144     return matrix;
145   } 
146 
147 	private static struct Indices
148 	{
149 		enum ScaleX = 0;
150 		enum SkewX = 1;
151 		enum TransX = 2;
152 		enum SkewY = 3;
153 		enum ScaleY = 4;
154 		enum TransY = 5;
155 		enum Persp0 = 6;
156 		enum Persp1 = 7;
157 		enum Persp2 = 8;
158 
159 		enum Count = 9;
160 	}
161 
162 	this (float[] values)
163 	{
164 		if (values is null)
165 			throw new ArgumentNullException (values.stringof);
166 		if (values.length != Indices.Count)
167 			throw new ArgumentException ("The matrix array must have a length of {Indices.Count}.", values.stringof);
168 
169 		scaleX = values[Indices.ScaleX];
170 		skewX = values[Indices.SkewX];
171 		transX = values[Indices.TransX];
172 
173 		skewY = values[Indices.SkewY];
174 		scaleY = values[Indices.ScaleY];
175 		transY = values[Indices.TransY];
176 
177 		persp0 = values[Indices.Persp0];
178 		persp1 = values[Indices.Persp1];
179 		persp2 = values[Indices.Persp2];
180 	}
181 
182 	this (
183 		float scaleX, float skewX, float transX,
184 		float skewY, float scaleY, float transY,
185 		float persp0, float persp1, float persp2)
186 	{
187 		this.scaleX = scaleX;
188 		this.skewX = skewX;
189 		this.transX = transX;
190 		this.skewY = skewY;
191 		this.scaleY = scaleY;
192 		this.transY = transY;
193 		this.persp0 = persp0;
194 		this.persp1 = persp1;
195 		this.persp2 = persp2;
196 	}
197 
198 	const bool IsIdentity()
199 	{
200 		return this == (Identity);
201 	} 
202 
203 	// Values
204 
205 	float[] Values() {
206 		return
207 			[
208 				scaleX, skewX, transX,
209 				skewY, scaleY, transY,
210 				persp0, persp1, persp2
211       ];
212 		
213 	}
214    void Values(float[] value) {
215 	
216 			if (value is null)
217 				throw new ArgumentNullException (Values.stringof);
218 			if (value.length != Indices.Count)
219 				throw new ArgumentException ("The matrix array must have a length of {Indices.Count}.", Values.stringof);
220 
221 			scaleX = value[Indices.ScaleX];
222 			skewX = value[Indices.SkewX];
223 			transX = value[Indices.TransX];
224 
225 			skewY = value[Indices.SkewY];
226 			scaleY = value[Indices.ScaleY];
227 			transY = value[Indices.TransY];
228 
229 			persp0 = value[Indices.Persp0];
230 			persp1 = value[Indices.Persp1];
231 			persp2 = value[Indices.Persp2];
232 		
233 	}
234 
235 	const void GetValues (float[] values)
236 	{
237 		if (values is null)
238 			throw new ArgumentNullException (values.stringof);
239 		if (values.length != Indices.Count)
240 			throw new ArgumentException ("The matrix array must have a length of {Indices.Count}.", values.stringof);
241 
242 		values[Indices.ScaleX] = scaleX;
243 		values[Indices.SkewX] = skewX;
244 		values[Indices.TransX] = transX;
245 
246 		values[Indices.SkewY] = skewY;
247 		values[Indices.ScaleY] = scaleY;
248 		values[Indices.TransY] = transY;
249 
250 		values[Indices.Persp0] = persp0;
251 		values[Indices.Persp1] = persp1;
252 		values[Indices.Persp2] = persp2;
253 	}
254 
255 	// Create*
256 
257 	static SKMatrix CreateIdentity ()
258     {
259       SKMatrix matrix = SKMatrix();
260       matrix.scaleX = 1;
261       matrix.scaleY = 1; 
262       matrix.persp2 = 1;
263       
264       return matrix;
265     }
266 		
267 	static SKMatrix CreateTranslation (float x, float y)
268 	{
269 		if (x == 0 && y == 0)
270 			return Identity;
271 
272     SKMatrix matrix =  SKMatrix();
273 		matrix.scaleX = 1;
274 		matrix.scaleY = 1;
275 		matrix.transX = x;
276 		matrix.transY = y;
277 		matrix.persp2 = 1;
278 		return matrix;
279 	}
280 
281 	static SKMatrix CreateScale (float x, float y)
282 	{
283 		if (x == 1 && y == 1)
284 			return Identity;
285 
286     SKMatrix matrix =  SKMatrix();
287 		matrix.scaleX = x;
288 		matrix.scaleY = y;
289 		matrix.persp2 = 1;
290     return matrix;
291 	}
292 
293 	static SKMatrix CreateScale (float x, float y, float pivotX, float pivotY)
294 	{
295 		if (x == 1 && y == 1)
296 			return Identity;
297 
298 		auto tx = pivotX - x * pivotX;
299 		auto ty = pivotY - y * pivotY;
300 
301     SKMatrix matrix =  SKMatrix();
302 		matrix.scaleX = x;
303 		matrix.scaleY = y;
304 		matrix.transX = tx;
305 		matrix.transY = ty;
306 		matrix.persp2 = 1;
307     return matrix;
308 	}
309 
310 	static SKMatrix CreateRotation (float radians)
311 	{
312 		if (radians == 0)
313 			return Identity;
314 
315 		auto sin = cast(float)sin (radians);
316 		auto cos = cast(float)cos (radians);
317 
318 		auto matrix = Identity;
319 		SetSinCos ( matrix, sin, cos);
320 		return matrix;
321 	}
322 
323 	static SKMatrix CreateRotation (float radians, float pivotX, float pivotY)
324 	{
325 		if (radians == 0)
326 			return Identity;
327 
328 		auto sin = cast(float)sin (radians);
329 		auto cos = cast(float)cos (radians);
330 
331 		auto matrix = Identity;
332 		SetSinCos (matrix, sin, cos, pivotX, pivotY);
333 		return matrix;
334 	}
335 
336 	static SKMatrix CreateRotationDegrees (float degrees)
337 	{
338 		if (degrees == 0)
339 			return Identity;
340 
341 		return CreateRotation (degrees * DegreesToRadians);
342 	}
343 
344 	static SKMatrix CreateRotationDegrees (float degrees, float pivotX, float pivotY)
345 	{
346 		if (degrees == 0)
347 			return Identity;
348 
349 		return CreateRotation (degrees * DegreesToRadians, pivotX, pivotY);
350 	}
351 
352 	static SKMatrix CreateSkew (float x, float y)
353 	{
354 		if (x == 0 && y == 0)
355 			return Identity;
356 
357     SKMatrix matrix =  SKMatrix();
358 
359 		matrix.scaleX = 1;
360 		matrix.skewX = x;
361 		matrix.skewY = y;
362 		matrix.scaleY = 1;
363 		matrix.persp2 = 1;
364     
365     return matrix;
366 	}
367 
368 	static SKMatrix CreateScaleTranslation (float sx, float sy, float tx, float ty)
369 	{
370 		if (sx == 0 && sy == 0 && tx == 0 && ty == 0)
371 			return Identity;
372 
373     SKMatrix matrix =  SKMatrix();
374 
375 		matrix.scaleX = sx;
376 		matrix.skewX = 0;
377 		matrix.transX = tx;
378 
379 		matrix.skewY = 0;
380 		matrix.scaleY = sy;
381 		matrix.transY = ty;
382 
383 		matrix.persp0 = 0;
384 		matrix.persp1 = 0;
385 		matrix.persp2 = 1;
386     
387     return matrix;
388 	}
389 
390 	// Make*
391 
392 	static SKMatrix MakeIdentity ()	
393   {
394 		return CreateIdentity ();
395 	}
396 
397 	static SKMatrix MakeScale (float sx, float sy)	
398   {
399 		return CreateScale (sx, sy);
400 	}
401 
402 	static SKMatrix MakeScale (float sx, float sy, float pivotX, float pivotY)	
403   {
404 		return CreateScale (sx, sy, pivotX, pivotY);
405 	}
406 
407 	static SKMatrix MakeTranslation (float dx, float dy)	
408   {
409 		return CreateTranslation (dx, dy);
410 	}
411 
412 	static SKMatrix MakeRotation (float radians)	
413   {
414 		return CreateRotation (radians);
415 	}
416 
417 	static SKMatrix MakeRotation (float radians, float pivotx, float pivoty)	
418   {
419 		return CreateRotation (radians, pivotx, pivoty);
420 	}
421 
422 	static SKMatrix MakeRotationDegrees (float degrees)	
423   {
424 		return CreateRotationDegrees (degrees);
425 	}
426 
427 	static SKMatrix MakeRotationDegrees (float degrees, float pivotx, float pivoty)	
428   {
429 		return CreateRotationDegrees (degrees, pivotx, pivoty);
430 	}
431 
432 	static SKMatrix MakeSkew (float sx, float sy)	
433   {
434 		return CreateSkew (sx, sy);
435 	}
436 
437 	// Set*
438 
439 	void SetScaleTranslate (float sx, float sy, float tx, float ty)
440 	{
441 		scaleX = sx;
442 		skewX = 0;
443 		transX = tx;
444 
445 		skewY = 0;
446 		scaleY = sy;
447 		transY = ty;
448 
449 		persp0 = 0;
450 		persp1 = 0;
451 		persp2 = 1;
452 	}
453 
454 	// Rotate
455 
456 	static void Rotate (ref SKMatrix matrix, float radians, float pivotx, float pivoty)
457 	{
458 		auto sin = cast(float)sin (radians);
459 		auto cos = cast(float)cos (radians);
460 		SetSinCos ( matrix, sin, cos, pivotx, pivoty);
461 	}
462 
463 	static void RotateDegrees (ref SKMatrix matrix, float degrees, float pivotx, float pivoty)
464 	{
465 		auto sin = cast(float)sin (degrees * DegreesToRadians);
466 		auto cos = cast(float)cos (degrees * DegreesToRadians);
467 		SetSinCos ( matrix, sin, cos, pivotx, pivoty);
468 	}
469 
470 	static void Rotate (ref SKMatrix matrix, float radians)
471 	{
472 		auto sin = cast(float)sin (radians);
473 		auto cos = cast(float)cos (radians);
474 		SetSinCos ( matrix, sin, cos);
475 	}
476 
477 	static void RotateDegrees (ref SKMatrix matrix, float degrees)
478 	{
479 		auto sin = cast(float)sin (degrees * DegreesToRadians);
480 		auto cos = cast(float)cos (degrees * DegreesToRadians);
481 		SetSinCos ( matrix, sin, cos);
482 	}
483 
484 	// Invert
485 
486 	const bool IsInvertible() {
487 		
488 		SKMatrix* t = cast(SKMatrix*)&this;
489 		return SkiaApi.sk_matrix_try_invert (t, null);
490 	}
491 
492 	const bool TryInvert (out SKMatrix inverse)
493 	{
494 		SKMatrix* i = &inverse;
495 		SKMatrix* t = cast(SKMatrix*)&this;
496 		return SkiaApi.sk_matrix_try_invert (t, i);
497 	}
498 
499 	SKMatrix Invert ()
500 	{
501      SKMatrix matrix;
502 		if (TryInvert ( matrix))
503 			return matrix;
504 
505 		return Empty;
506 	}
507 
508 	// *Concat
509 
510 	static SKMatrix Concat (SKMatrix first, SKMatrix second)
511 	{
512 		SKMatrix target;
513 		SkiaApi.sk_matrix_concat (&target, &first, &second);
514 		return target;
515 	}
516 
517 	SKMatrix PreConcat (SKMatrix matrix)
518 	{
519 		auto target = this;
520 		SkiaApi.sk_matrix_pre_concat (&target, &matrix);
521 		return target;
522 	}
523 
524 	SKMatrix PostConcat (SKMatrix matrix)
525 	{
526 		auto target = this;
527 		SkiaApi.sk_matrix_post_concat (&target, &matrix);
528 		return target;
529 	}
530 
531 	static void Concat (ref SKMatrix target, SKMatrix first, SKMatrix second)
532 	{
533 		SKMatrix* t = ⌖
534 		SkiaApi.sk_matrix_concat (t, &first, &second);
535 	}
536 
537 	static void Concat (ref SKMatrix target, ref SKMatrix first, ref SKMatrix second)
538 	{
539 		SKMatrix* t = ⌖
540 		SKMatrix* f = &first;
541 		SKMatrix* s = &second;
542 		SkiaApi.sk_matrix_concat (t, f, s);
543 	}
544 
545 	static void PreConcat (ref SKMatrix target, SKMatrix matrix)
546 	{
547 		SKMatrix* t = ⌖
548 		SkiaApi.sk_matrix_pre_concat (t, &matrix);
549 	}
550 
551 	static void PreConcat (ref SKMatrix target, ref SKMatrix matrix)
552 	{
553 		SKMatrix* t = ⌖
554 		SKMatrix* m = &matrix;
555 		SkiaApi.sk_matrix_pre_concat (t, m);
556 	}
557 
558 	static void PostConcat (ref SKMatrix target, SKMatrix matrix)
559 	{
560 		SKMatrix* t = ⌖
561 		SkiaApi.sk_matrix_post_concat (t, &matrix);
562 	}
563 
564 	static void PostConcat (ref SKMatrix target, ref SKMatrix matrix)
565 	{
566 		SKMatrix* t = ⌖
567 		SKMatrix* m = &matrix;
568 		SkiaApi.sk_matrix_post_concat (t, m);
569 	}
570 
571 	// MapRect
572 
573 	const SKRect MapRect (SKRect source)
574 	{
575 		SKRect dest;
576 		SKMatrix* m = cast(SKMatrix*)&this;
577 		SkiaApi.sk_matrix_map_rect (m, &dest, &source);
578 		return dest;
579 	}
580 
581 	static void MapRect (ref SKMatrix matrix, out SKRect dest, ref SKRect source)
582 	{
583 		SKMatrix* m = &matrix;
584 		SKRect* d = &dest;
585 		SKRect* s = &source;
586 		SkiaApi.sk_matrix_map_rect (m, d, s);
587 	}
588 
589 	// MapPoints
590 
591 	const SKPoint MapPoint (SKPoint point)
592 	{
593     	return MapPoint (point.X, point.Y);
594   	}
595 	
596 
597 	const SKPoint MapPoint (float x, float y)
598 	{
599 		SKPoint result;
600 		SKMatrix* t = cast(SKMatrix*)&this;
601 		SkiaApi.sk_matrix_map_xy (t, x, y, &result);
602 		return result;
603 	}
604 
605 	const void MapPoints (SKPoint[] result, SKPoint[] points)
606 	{
607 		if (result is null)
608 			throw new ArgumentNullException (result.stringof);
609 		if (points is null)
610 			throw new ArgumentNullException (points.stringof);
611 		if (result.length != points.length)
612 			throw new ArgumentException ("Buffers must be the same size.");
613 
614 		SKMatrix* t = cast(SKMatrix*)&this;
615 		SKPoint* rp = result.ptr;
616 		SKPoint* pp = points.ptr;
617 		SkiaApi.sk_matrix_map_points (t, rp, pp, cast(int)result.length);
618 	}
619 
620 	const SKPoint[] MapPoints (SKPoint[] points)
621 	{
622 		if (points is null)
623 			throw new ArgumentNullException (points.stringof);
624 
625 		auto res = new SKPoint[points.length];
626 		MapPoints (res, points);
627 		return res;
628 	}
629 
630 	// MapVectors
631 
632 	const SKPoint MapVector (SKPoint vector)
633   {
634     return MapVector (vector.X, vector.Y);
635   }
636 	
637 
638 	const SKPoint MapVector (float x, float y)
639 	{
640 		SKPoint result;
641 		SKMatrix* t = cast(SKMatrix*)&this;
642 		SkiaApi.sk_matrix_map_vector (t, x, y, &result);
643 		return result;
644 	}
645 
646 	const void MapVectors (SKPoint[] result, SKPoint[] vectors)
647 	{
648 		if (result is null)
649 			throw new ArgumentNullException (result.stringof);
650 		if (vectors is null)
651 			throw new ArgumentNullException (vectors.stringof);
652 		if (result.length != vectors.length)
653 			throw new ArgumentException ("Buffers must be the same size.");
654 
655 		SKMatrix* t = cast(SKMatrix*)&this;
656 		SKPoint* rp = result.ptr;
657 		SKPoint* pp = vectors.ptr;
658 		SkiaApi.sk_matrix_map_vectors (t, rp, pp, cast(int)result.length);
659 	}
660 
661 	const SKPoint[] MapVectors (SKPoint[] vectors)
662 	{
663 		if (vectors is null)
664 			throw new ArgumentNullException (vectors.stringof);
665 
666 		auto res = new SKPoint[vectors.length];
667 		MapVectors (res, vectors);
668 		return res;
669 	}
670 
671 	// MapRadius
672 
673 	const float MapRadius (float radius)
674 	{
675 		SKMatrix* t = cast(SKMatrix*)&this;
676 		return SkiaApi.sk_matrix_map_radius (t, radius);
677 	}
678 
679 	// private
680 
681 	private static void SetSinCos (ref SKMatrix matrix, float sin, float cos)
682 	{
683 		matrix.scaleX = cos;
684 		matrix.skewX = -sin;
685 		matrix.transX = 0;
686 		matrix.skewY = sin;
687 		matrix.scaleY = cos;
688 		matrix.transY = 0;
689 		matrix.persp0 = 0;
690 		matrix.persp1 = 0;
691 		matrix.persp2 = 1;
692 	}
693 
694 	private static void SetSinCos (ref SKMatrix matrix, float sin, float cos, float pivotx, float pivoty)
695 	{
696 		float oneMinusCos = 1 - cos;
697 
698 		matrix.scaleX = cos;
699 		matrix.skewX = -sin;
700 		matrix.transX = Dot (sin, pivoty, oneMinusCos, pivotx);
701 		matrix.skewY = sin;
702 		matrix.scaleY = cos;
703 		matrix.transY = Dot (-sin, pivotx, oneMinusCos, pivoty);
704 		matrix.persp0 = 0;
705 		matrix.persp1 = 0;
706 		matrix.persp2 = 1;
707 	}
708 
709 	private static float Dot (float a, float b, float c, float d)
710   {
711     return a * b + c * d;
712   }
713 		
714 
715 	private static float Cross (float a, float b, float c, float d)
716   {
717     return a * b - c * d;
718   }
719 		
720 }