1 module skia.SKCanvas;
2 
3 import skia.Exceptions;
4 import skia.SkiaApi;
5 import skia.SKObject;
6 import skia.SKTextBlob;
7 import skia.MathTypes;
8 import skia.Definitions;
9 import skia.SKPath;
10 import skia.SKBitmap;
11 import skia.SKPaint;
12 import skia.SKMatrix;
13 import skia.SKColor;
14 import skia.SKImage;
15 import skia.SKFont;
16 import skia.SKRegion;
17 import skia.SKData;
18 import skia.SKRoundRect;
19 import skia.SKPicture;
20 import skia.SKDrawable;
21 import skia.SKSurface;
22 import skia.SKVertices;
23 import skia.IDisposable;
24 import skia.SKColors;
25 
26 import std.typecons;
27 
28 import std.experimental.logger;
29 
30 // TODO: carefully consider the `PeekPixels`, `ReadPixels`
31 
32 class SKCanvas : SKObject
33 {
34 	private enum PatchCornerCount = 4;
35 	private enum PatchCubicsCount = 12;
36 
37 	this (void* handle, bool owns)
38 	{
39 		super(handle, owns);
40 	}
41 
42 	this (SKBitmap bitmap)
43 	{
44     this (null, true);
45 		if (bitmap is null)
46 			throw new ArgumentNullException (bitmap.stringof);
47 		Handle = SkiaApi.sk_canvas_new_from_bitmap (cast(sk_bitmap_t*)bitmap.Handle);
48 	}
49 
50 // 	protected override void Dispose (bool disposing)
51 // 	{
52 // 		return super.Dispose (disposing);
53 // 	}
54 
55 //   override void Dispose()
56 //   {
57 //     return super.Dispose();
58 //   }
59 
60 	protected override void DisposeNative ()
61 	{
62 		return SkiaApi.sk_canvas_destroy (cast(sk_canvas_t*)Handle);
63 	}
64 
65 	void Discard ()
66 	{
67 		return SkiaApi.sk_canvas_discard (cast(sk_canvas_t*)Handle);
68 	}
69 
70 	// QuickReject
71 
72 	bool QuickReject (SKRect rect)
73 	{
74 		return SkiaApi.sk_canvas_quick_reject (cast(sk_canvas_t*)Handle, &rect);
75 	}
76 
77 	bool QuickReject (SKPath path)
78 	{
79 		if (path is null)
80 			throw new ArgumentNullException (path.stringof);
81 		return path.IsEmpty || QuickReject (path.Bounds);
82 	}
83 
84 	// Save*
85 
86 	int Save ()
87 	{
88 		if (Handle is null)
89 			throw new ObjectDisposedException ("SKCanvas");
90 		return SkiaApi.sk_canvas_save (cast(sk_canvas_t*)Handle);
91 	}
92 
93 	int SaveLayer (SKRect limit, SKPaint paint)
94 	{
95 		return SkiaApi.sk_canvas_save_layer (cast(sk_canvas_t*)Handle, &limit, cast(sk_paint_t*)(paint is null ? null : paint.Handle));
96 	}
97 
98 	int SaveLayer (SKPaint paint)
99 	{
100 		return SkiaApi.sk_canvas_save_layer (cast(sk_canvas_t*)Handle, null, cast(sk_paint_t*)(paint is null ? null : paint.Handle));
101 	}
102 
103 	int SaveLayer ()
104 	{
105 		return SaveLayer (null);
106 	}
107 
108 	// DrawColor
109 
110 	void DrawColor (SKColor color, SKBlendMode mode = SKBlendMode.Src)
111 	{
112 		SkiaApi.sk_canvas_draw_color (cast(sk_canvas_t*)Handle, cast(uint)color, mode);
113 	}
114 
115 	// DrawLine
116 
117 	void DrawLine (SKPoint p0, SKPoint p1, SKPaint paint)
118 	{
119 		DrawLine (p0.X, p0.Y, p1.X, p1.Y, paint);
120 	}
121 
122 	void DrawLine (float x0, float y0, float x1, float y1, SKPaint paint)
123 	{
124 		if (paint is null)
125 			throw new ArgumentNullException (paint.stringof);
126 		SkiaApi.sk_canvas_draw_line (cast(sk_canvas_t*)Handle, x0, y0, x1, y1, cast(sk_paint_t*)paint.Handle);
127 	}
128 
129 	// Clear
130 
131 	void Clear ()
132 	{
133 		DrawColor (SKColors.Empty, SKBlendMode.Src);
134 	}
135 
136 	void Clear (SKColor color)
137 	{
138 		DrawColor (color, SKBlendMode.Src);
139 	}
140 
141 	// Restore*
142 
143 	void Restore ()
144 	{
145 		SkiaApi.sk_canvas_restore (cast(sk_canvas_t*)Handle);
146 	}
147 
148 	void RestoreToCount (int count)
149 	{
150 		SkiaApi.sk_canvas_restore_to_count (cast(sk_canvas_t*)Handle, count);
151 	}
152 
153 	// Translate
154 
155 	void Translate (float dx, float dy)
156 	{
157 		SkiaApi.sk_canvas_translate (cast(sk_canvas_t*)Handle, dx, dy);
158 	}
159 
160 	void Translate (SKPoint point)
161 	{
162 		SkiaApi.sk_canvas_translate (cast(sk_canvas_t*)Handle, point.X, point.Y);
163 	}
164 
165 	// Scale
166 
167 	void Scale (float s)
168 	{
169 		SkiaApi.sk_canvas_scale (cast(sk_canvas_t*)Handle, s, s);
170 	}
171 
172 	void Scale (float sx, float sy)
173 	{
174 		SkiaApi.sk_canvas_scale (cast(sk_canvas_t*)Handle, sx, sy);
175 	}
176 
177 	void Scale (SKPoint size)
178 	{
179 		SkiaApi.sk_canvas_scale (cast(sk_canvas_t*)Handle, size.X, size.Y);
180 	}
181 
182 	void Scale (float sx, float sy, float px, float py)
183 	{
184 		Translate (px, py);
185 		Scale (sx, sy);
186 		Translate (-px, -py);
187 	}
188 
189 	// Rotate*
190 
191 	void RotateDegrees (float degrees)
192 	{
193 		SkiaApi.sk_canvas_rotate_degrees (cast(sk_canvas_t*)Handle, degrees);
194 	}
195 
196 	void RotateRadians (float radians)
197 	{
198 		SkiaApi.sk_canvas_rotate_radians (cast(sk_canvas_t*)Handle, radians);
199 	}
200 
201 	void RotateDegrees (float degrees, float px, float py)
202 	{
203 		Translate (px, py);
204 		RotateDegrees (degrees);
205 		Translate (-px, -py);
206 	}
207 
208 	void RotateRadians (float radians, float px, float py)
209 	{
210 		Translate (px, py);
211 		RotateRadians (radians);
212 		Translate (-px, -py);
213 	}
214 
215 	// Skew
216 
217 	void Skew (float sx, float sy)
218 	{
219 		SkiaApi.sk_canvas_skew (cast(sk_canvas_t*)Handle, sx, sy);
220 	}
221 
222 	void Skew (SKPoint skew)
223 	{
224 		SkiaApi.sk_canvas_skew (cast(sk_canvas_t*)Handle, skew.X, skew.Y);
225 	}
226 
227 	// Concat
228 
229 	void Concat (ref SKMatrix m)
230 	{
231 		SKMatrix* ptr = &m;
232         SkiaApi.sk_canvas_concat (cast(sk_canvas_t*)Handle, ptr);
233 	}
234 
235 	// Clip*
236 
237 	void ClipRect (SKRect rect, SKClipOperation operation = SKClipOperation.Intersect, bool antialias = false)
238 	{
239 		SkiaApi.sk_canvas_clip_rect_with_operation (cast(sk_canvas_t*)Handle, &rect, operation, antialias);
240 	}
241 
242 	void ClipRoundRect (SKRoundRect rect, SKClipOperation operation = SKClipOperation.Intersect, bool antialias = false)
243 	{
244 		if (rect is null)
245 			throw new ArgumentNullException (rect.stringof);
246 
247 		SkiaApi.sk_canvas_clip_rrect_with_operation (cast(sk_canvas_t*)Handle, cast(sk_rrect_t*)rect.Handle, operation, antialias);
248 	}
249 
250 	void ClipPath (SKPath path, SKClipOperation operation = SKClipOperation.Intersect, bool antialias = false)
251 	{
252 		if (path is null)
253 			throw new ArgumentNullException (path.stringof);
254 
255 		SkiaApi.sk_canvas_clip_path_with_operation (cast(sk_canvas_t*)Handle,cast(sk_path_t*) path.Handle, operation, antialias);
256 	}
257 
258 	void ClipRegion (SKRegion region, SKClipOperation operation = SKClipOperation.Intersect)
259 	{
260 		if (region is null)
261 			throw new ArgumentNullException (region.stringof);
262 
263 		SkiaApi.sk_canvas_clip_region (cast(sk_canvas_t*)Handle, cast(sk_region_t*)region.Handle, operation);
264 	}
265 
266 	SKRect LocalClipBounds() {
267         SKRect bounds;
268         GetLocalClipBounds (bounds);
269         return bounds;
270 	}
271 
272 	SKRectI DeviceClipBounds() {
273         SKRectI bounds;
274         GetDeviceClipBounds (bounds);
275         return bounds;
276 	}
277 
278 	bool IsClipEmpty() { return SkiaApi.sk_canvas_is_clip_empty (cast(sk_canvas_t*)Handle); }
279 
280 	bool IsClipRect() { return SkiaApi.sk_canvas_is_clip_rect (cast(sk_canvas_t*)Handle); }
281 
282 	bool GetLocalClipBounds (ref SKRect bounds)
283 	{
284 		SKRect* b = &bounds;
285 		return SkiaApi.sk_canvas_get_local_clip_bounds (cast(sk_canvas_t*)Handle, b);
286 	}
287 
288 	bool GetDeviceClipBounds (ref SKRectI bounds)
289 	{
290 		SKRectI* b = &bounds;
291 		return SkiaApi.sk_canvas_get_device_clip_bounds (cast(sk_canvas_t*)Handle, b);
292 	}
293 
294 	// DrawPaint
295 
296 	void DrawPaint (SKPaint paint)
297 	{
298 		if (paint is null)
299 			throw new ArgumentNullException (paint.stringof);
300 		SkiaApi.sk_canvas_draw_paint (cast(sk_canvas_t*)Handle, cast(sk_paint_t*)paint.Handle);
301 	}
302 
303 	// DrawRegion
304 
305 	void DrawRegion (SKRegion region, SKPaint paint)
306 	{
307 		if (region is null)
308 			throw new ArgumentNullException (region.stringof);
309 		if (paint is null)
310 			throw new ArgumentNullException (paint.stringof);
311 		SkiaApi.sk_canvas_draw_region (cast(sk_canvas_t*)Handle, cast(sk_region_t*)region.Handle, cast(sk_paint_t*)paint.Handle);
312 	}
313 
314 	// DrawRect
315 
316 	void DrawRect (float x, float y, float w, float h, SKPaint paint)
317 	{
318 		DrawRect (SKRect.Create (x, y, w, h), paint);
319 	}
320 
321 	void DrawRect (SKRect rect, SKPaint paint)
322 	{
323 		if (paint is null)
324 			throw new ArgumentNullException (paint.stringof);
325 		SkiaApi.sk_canvas_draw_rect (cast(sk_canvas_t*)Handle, &rect, cast(sk_paint_t*)paint.Handle);
326 	}
327 
328 	// DrawRoundRect
329 
330 	void DrawRoundRect (SKRoundRect rect, SKPaint paint)
331 	{
332 		if (rect is null)
333 			throw new ArgumentNullException (rect.stringof);
334 		if (paint is null)
335 			throw new ArgumentNullException (paint.stringof);
336 		SkiaApi.sk_canvas_draw_rrect (cast(sk_canvas_t*)Handle, cast(sk_rrect_t*)rect.Handle, cast(sk_paint_t*)paint.Handle);
337 	}
338 
339 	void DrawRoundRect (float x, float y, float w, float h, float rx, float ry, SKPaint paint)
340 	{
341 		DrawRoundRect (SKRect.Create (x, y, w, h), rx, ry, paint);
342 	}
343 
344 	void DrawRoundRect (SKRect rect, float rx, float ry, SKPaint paint)
345 	{
346 		if (paint is null)
347 			throw new ArgumentNullException (paint.stringof);
348 		SkiaApi.sk_canvas_draw_round_rect (cast(sk_canvas_t*)Handle, &rect, rx, ry, cast(sk_paint_t*)paint.Handle);
349 	}
350 
351 	void DrawRoundRect (SKRect rect, SKSize r, SKPaint paint)
352 	{
353 		DrawRoundRect (rect, r.Width, r.Height, paint);
354 	}
355 
356 	// DrawOval
357 
358 	void DrawOval (float cx, float cy, float rx, float ry, SKPaint paint)
359 	{
360 		DrawOval (SKRect (cx - rx, cy - ry, cx + rx, cy + ry), paint);
361 	}
362 
363 	void DrawOval (SKPoint c, SKSize r, SKPaint paint)
364 	{
365 		DrawOval (c.X, c.Y, r.Width, r.Height, paint);
366 	}
367 
368 	void DrawOval (SKRect rect, SKPaint paint)
369 	{
370 		if (paint is null)
371 			throw new ArgumentNullException (paint.stringof);
372 		SkiaApi.sk_canvas_draw_oval (cast(sk_canvas_t*)Handle, &rect, cast(sk_paint_t*)paint.Handle);
373 	}
374 
375 	// DrawCircle
376 
377 	void DrawCircle (float cx, float cy, float radius, SKPaint paint)
378 	{
379 		if (paint is null)
380 			throw new ArgumentNullException (paint.stringof);
381 		SkiaApi.sk_canvas_draw_circle (cast(sk_canvas_t*)Handle, cx, cy, radius, cast(sk_paint_t*)paint.Handle);
382 	}
383 
384 	void DrawCircle (SKPoint c, float radius, SKPaint paint)
385 	{
386 		DrawCircle (c.X, c.Y, radius, paint);
387 	}
388 
389 	// DrawPath
390 
391 	void DrawPath (SKPath path, SKPaint paint)
392 	{
393 		if (paint is null)
394 			throw new ArgumentNullException (paint.stringof);
395 		if (path is null)
396 			throw new ArgumentNullException (path.stringof);
397 		SkiaApi.sk_canvas_draw_path (cast(sk_canvas_t*)Handle, cast(sk_path_t*)path.Handle, cast(sk_paint_t*)paint.Handle);
398 	}
399 
400 	// DrawPoints
401 
402 	void DrawPoints (SKPointMode mode, SKPoint[] points, SKPaint paint)
403 	{
404 		if (paint is null)
405 			throw new ArgumentNullException (paint.stringof);
406 		if (points is null)
407 			throw new ArgumentNullException (points.stringof);
408 		SKPoint* p = points.ptr;
409 		SkiaApi.sk_canvas_draw_points (cast(sk_canvas_t*)Handle, mode, cast(void*)points.length, p, cast(sk_paint_t*)paint.Handle);
410 	}
411 
412 	// DrawPoint
413 
414 	void DrawPoint (SKPoint p, SKPaint paint)
415 	{
416 		DrawPoint (p.X, p.Y, paint);
417 	}
418 
419 	void DrawPoint (float x, float y, SKPaint paint)
420 	{
421 		if (paint is null)
422 			throw new ArgumentNullException (paint.stringof);
423 		SkiaApi.sk_canvas_draw_point (cast(sk_canvas_t*)Handle, x, y, cast(sk_paint_t*)paint.Handle);
424 	}
425 
426 	void DrawPoint (SKPoint p, SKColor color)
427 	{
428 		DrawPoint (p.X, p.Y, color);
429 	}
430 
431 	void DrawPoint (float x, float y, SKColor color)
432 	{
433         SKPaint paint = new SKPaint();
434 		scope(exit) {
435 			paint.Dispose();
436 		}
437 
438         paint.Color = color;
439         paint.BlendMode = SKBlendMode.Src;
440 
441         DrawPoint (x, y, paint);
442 	}
443 
444 	// DrawImage
445 
446 	void DrawImage (SKImage image, SKPoint p, SKPaint paint = null)
447 	{
448 		DrawImage (image, p.X, p.Y, paint);
449 	}
450 
451 	void DrawImage (SKImage image, float x, float y, SKPaint paint = null)
452 	{
453 		if (image is null)
454 			throw new ArgumentNullException (image.stringof);
455 		SkiaApi.sk_canvas_draw_image (cast(sk_canvas_t*)Handle, cast(sk_image_t*)image.Handle, x, y, cast(sk_paint_t*)(paint is null ? null : paint.Handle));
456 	}
457 
458 	void DrawImage (SKImage image, SKRect dest, SKPaint paint = null)
459 	{
460 		if (image is null)
461 			throw new ArgumentNullException (image.stringof);
462 		SkiaApi.sk_canvas_draw_image_rect (cast(sk_canvas_t*)Handle, cast(sk_image_t*)image.Handle, null, &dest, cast(sk_paint_t*)(paint is null ? null : paint.Handle));
463 	}
464 
465 	void DrawImage (SKImage image, SKRect source, SKRect dest, SKPaint paint = null)
466 	{
467 		if (image is null)
468 			throw new ArgumentNullException (image.stringof);
469 		SkiaApi.sk_canvas_draw_image_rect (cast(sk_canvas_t*)Handle, cast(sk_image_t*)image.Handle, &source, &dest, cast(sk_paint_t*)(paint is null ? null : paint.Handle));
470 	}
471 
472 	// DrawPicture
473 
474 	void DrawPicture (SKPicture picture, float x, float y, SKPaint paint = null)
475 	{
476 		auto matrix = SKMatrix.CreateTranslation (x, y);
477 		DrawPicture (picture, matrix, paint);
478 	}
479 
480 	void DrawPicture (SKPicture picture, SKPoint p, SKPaint paint = null)
481 	{
482 		DrawPicture (picture, p.X, p.Y, paint);
483 	}
484 
485 	void DrawPicture (SKPicture picture, ref SKMatrix matrix, SKPaint paint = null)
486 	{
487 		if (picture is null)
488 			throw new ArgumentNullException (picture.stringof);
489 		SKMatrix* m = &matrix;
490 		SkiaApi.sk_canvas_draw_picture (cast(sk_canvas_t*)Handle, cast(sk_picture_t*)picture.Handle, m, cast(sk_paint_t*)(paint is null ? null : paint.Handle));
491 	}
492 
493 	void DrawPicture (SKPicture picture, SKPaint paint = null)
494 	{
495 		if (picture is null)
496 			throw new ArgumentNullException (picture.stringof);
497 		SkiaApi.sk_canvas_draw_picture (cast(sk_canvas_t*)Handle, cast(sk_picture_t*)picture.Handle, null, cast(sk_paint_t*)(paint is null ? null : paint.Handle));
498 	}
499 
500 	// DrawDrawable
501 
502 	void DrawDrawable (SKDrawable drawable, ref SKMatrix matrix)
503 	{
504 		if (drawable is null)
505 			throw new ArgumentNullException (drawable.stringof);
506 		SKMatrix* m = &matrix;
507 		SkiaApi.sk_canvas_draw_drawable (cast(sk_canvas_t*)Handle, cast(sk_drawable_t*)drawable.Handle, m);
508 	}
509 
510 	void DrawDrawable (SKDrawable drawable, float x, float y)
511 	{
512 		if (drawable is null)
513 			throw new ArgumentNullException (drawable.stringof);
514 		auto matrix = SKMatrix.CreateTranslation (x, y);
515 		DrawDrawable (drawable, matrix);
516 	}
517 
518 	void DrawDrawable (SKDrawable drawable, SKPoint p)
519 	{
520 		if (drawable is null)
521 			throw new ArgumentNullException (drawable.stringof);
522 		auto matrix = SKMatrix.CreateTranslation (p.X, p.Y);
523 		DrawDrawable (drawable, matrix);
524 	}
525 
526 	// DrawBitmap
527 
528 	void DrawBitmap (SKBitmap bitmap, SKPoint p, SKPaint paint = null)
529 	{
530 		DrawBitmap (bitmap, p.X, p.Y, paint);
531 	}
532 
533 	void DrawBitmap (SKBitmap bitmap, float x, float y, SKPaint paint = null)
534 	{
535 		if (bitmap is null)
536 			throw new ArgumentNullException (bitmap.stringof);
537 		SkiaApi.sk_canvas_draw_bitmap (cast(sk_canvas_t*)Handle, cast(sk_bitmap_t*)bitmap.Handle, x, y, cast(sk_paint_t*)(paint is null ? null : paint.Handle));
538 	}
539 
540 	void DrawBitmap (SKBitmap bitmap, SKRect dest, SKPaint paint = null)
541 	{
542 		if (bitmap is null)
543 			throw new ArgumentNullException (bitmap.stringof);
544 		SkiaApi.sk_canvas_draw_bitmap_rect (cast(sk_canvas_t*)Handle, cast(sk_bitmap_t*)bitmap.Handle, null, &dest, cast(sk_paint_t*)(paint is null ? null : paint.Handle));
545 	}
546 
547 	void DrawBitmap (SKBitmap bitmap, SKRect source, SKRect dest, SKPaint paint = null)
548 	{
549 		if (bitmap is null)
550 			throw new ArgumentNullException (bitmap.stringof);
551 		SkiaApi.sk_canvas_draw_bitmap_rect (cast(sk_canvas_t*)Handle, cast(sk_bitmap_t*)bitmap.Handle, &source, &dest, cast(sk_paint_t*)(paint is null ? null : paint.Handle));
552 	}
553 
554 	// DrawSurface
555 
556 	void DrawSurface (SKSurface surface, SKPoint p, SKPaint paint = null)
557 	{
558 		DrawSurface (surface, p.X, p.Y, paint);
559 	}
560 
561 	void DrawSurface (SKSurface surface, float x, float y, SKPaint paint = null)
562 	{
563 		if (surface is null)
564 			throw new ArgumentNullException (surface.stringof);
565 
566 		surface.Draw (this, x, y, paint);
567 	}
568 
569 	// DrawText (SKTextBlob)
570 
571 	void DrawText (SKTextBlob text, float x, float y, SKPaint paint)
572 	{
573 		if (text is null)
574 			throw new ArgumentNullException (text.stringof);
575 		if (paint is null)
576 			throw new ArgumentNullException (paint.stringof);
577 
578 		SkiaApi.sk_canvas_draw_text_blob (cast(sk_canvas_t*)Handle, cast(sk_textblob_t*)text.Handle, x, y, cast(sk_paint_t*)paint.Handle);
579 	}
580 
581 	// DrawText
582 
583 	void DrawText (string text, SKPoint p, SKPaint paint)
584 	{
585 		DrawText (text, p.X, p.Y, paint);
586 	}
587 
588 	void DrawText (string text, float x, float y, SKPaint paint)
589 	{
590 		DrawText (text, x, y, paint.GetFont (), paint);
591 	}
592 
593 	void DrawText (string text, float x, float y, SKFont font, SKPaint paint)
594 	{
595 		if (text is null)
596 			throw new ArgumentNullException (text.stringof);
597 		if (font is null)
598 			throw new ArgumentNullException (font.stringof);
599 		if (paint is null)
600 			throw new ArgumentNullException (paint.stringof);
601 
602 		if (paint.TextAlign != SKTextAlign.Left) {
603 			auto width = font.MeasureText (text);
604 			if (paint.TextAlign == SKTextAlign.Center)
605 				width *= 0.5f;
606 			x -= width;
607 		}
608 
609 		SKTextBlob blob = SKTextBlob.Create (text, font);
610 		if (blob is null)
611 			return;
612 
613 		scope(exit) {
614 			blob.Dispose();
615 		}
616 
617 		DrawText (blob, x, y, paint);
618 	}
619 
620 	void DrawText (byte[] text, SKPoint p, SKPaint paint)
621 	{
622 		DrawText (text, p.X, p.Y, paint);
623 	}
624 
625 	void DrawText (byte[] text, float x, float y, SKPaint paint)
626 	{
627 		if (text is null)
628 			throw new ArgumentNullException (text.stringof);
629 		if (paint is null)
630 			throw new ArgumentNullException (paint.stringof);
631 
632 		if (paint.TextAlign != SKTextAlign.Left) {
633 			auto width = paint.MeasureText (text);
634 			if (paint.TextAlign == SKTextAlign.Center)
635 				width *= 0.5f;
636 			x -= width;
637 		}
638 
639 		SKTextBlob blob = SKTextBlob.Create (text, paint.TextEncoding, paint.GetFont ());
640 		if (blob is null)
641 			return;
642 
643 		scope(exit) {
644 			blob.Dispose();
645 		}
646 
647 		DrawText (blob, x, y, paint);
648 	}
649 
650 	void DrawText (void* buffer, int length, SKPoint p, SKPaint paint)
651 	{
652 		DrawText (buffer, length, p.X, p.Y, paint);
653 	}
654 
655 	void DrawText (void* buffer, int length, float x, float y, SKPaint paint)
656 	{
657 		if (buffer is null && length != 0)
658 			throw new ArgumentNullException (buffer.stringof);
659 		if (paint is null)
660 			throw new ArgumentNullException (paint.stringof);
661 
662 		if (paint.TextAlign != SKTextAlign.Left) {
663 			auto width = paint.MeasureText (buffer, length);
664 			if (paint.TextAlign == SKTextAlign.Center)
665 				width *= 0.5f;
666 			x -= width;
667 		}
668 
669 		SKTextBlob blob = SKTextBlob.Create (buffer, cast(int)length, paint.TextEncoding, paint.GetFont ());
670 		if (blob is null)
671 			return;
672 
673 		scope(exit) {
674 			blob.Dispose();
675 		}
676 
677 		DrawText (blob, x, y, paint);
678 	}
679 
680 	// DrawPositionedText
681 
682 	void DrawPositionedText (string text, SKPoint[] points, SKPaint paint)
683 	{
684 		if (text is null)
685 			throw new ArgumentNullException (text.stringof);
686 		if (paint is null)
687 			throw new ArgumentNullException (paint.stringof);
688 		if (points is null)
689 			throw new ArgumentNullException (points.stringof);
690 
691 		SKTextBlob blob = SKTextBlob.CreatePositioned (text, paint.GetFont (), points);
692 		if (blob is null)
693 			return;
694 
695 		scope(exit) {
696 			blob.Dispose();
697 		}
698 
699 		DrawText (blob, 0, 0, paint);
700 	}
701 
702 	void DrawPositionedText (byte[] text, SKPoint[] points, SKPaint paint)
703 	{
704 		if (text is null)
705 			throw new ArgumentNullException (text.stringof);
706 		if (paint is null)
707 			throw new ArgumentNullException (paint.stringof);
708 		if (points is null)
709 			throw new ArgumentNullException (points.stringof);
710 
711 		SKTextBlob blob = SKTextBlob.CreatePositioned (text, paint.TextEncoding, paint.GetFont (), points);
712 		if (blob is null)
713 			return;
714 
715 		scope(exit) {
716 			blob.Dispose();
717 		}
718 
719 		DrawText (blob, 0, 0, paint);
720 	}
721 
722 	void DrawPositionedText (void* buffer, int length, SKPoint[] points, SKPaint paint)
723 	{
724 		if (buffer is null && length != 0)
725 			throw new ArgumentNullException (buffer.stringof);
726 		if (paint is null)
727 			throw new ArgumentNullException (paint.stringof);
728 		if (points is null)
729 			throw new ArgumentNullException (points.stringof);
730 
731 		SKTextBlob blob = SKTextBlob.CreatePositioned (buffer, length, paint.TextEncoding, paint.GetFont (), points);
732 		if (blob is null)
733 			return;
734 
735 		scope(exit) {
736 			blob.Dispose();
737 		}
738 
739 		DrawText (blob, 0, 0, paint);
740 	}
741 
742 	// DrawTextOnPath
743 
744 	void DrawTextOnPath (string text, SKPath path, SKPoint offset, SKPaint paint)
745 	{
746 		DrawTextOnPath (text, path, offset, true, paint);
747 	}
748 
749 	void DrawTextOnPath (string text, SKPath path, float hOffset, float vOffset, SKPaint paint)
750 	{
751 		DrawTextOnPath (text, path, SKPoint (hOffset, vOffset), true, paint);
752 	}
753 
754 	void DrawTextOnPath (string text, SKPath path, SKPoint offset, bool warpGlyphs, SKPaint paint)
755 	{
756 		if (paint is null)
757 			throw new ArgumentNullException (paint.stringof);
758 
759 		DrawTextOnPath (text, path, offset, warpGlyphs, paint.GetFont (), paint);
760 	}
761 
762 	void DrawTextOnPath (string text, SKPath path, SKPoint offset, bool warpGlyphs, SKFont font, SKPaint paint)
763 	{
764 		if (text is null)
765 			throw new ArgumentNullException (text.stringof);
766 		if (path is null)
767 			throw new ArgumentNullException (path.stringof);
768 		if (font is null)
769 			throw new ArgumentNullException (font.stringof);
770 		if (paint is null)
771 			throw new ArgumentNullException (paint.stringof);
772 
773 		if (warpGlyphs) {
774 			auto textPath =  font.GetTextPathOnPath (text, path, paint.TextAlign, offset);
775 			scope(exit) {
776 			    textPath.Dispose();
777 			}
778 
779 			DrawPath (textPath, paint);
780 		} else {
781 			SKTextBlob blob = SKTextBlob.CreatePathPositioned (text, font, path, paint.TextAlign, offset);
782 			if (blob !is null) {
783                 scope(exit) {
784                     blob.Dispose();
785                 }       
786 				DrawText (blob, 0, 0, paint);
787             }
788 		}
789 	}
790 
791 	void DrawTextOnPath (byte[] text, SKPath path, SKPoint offset, SKPaint paint)
792 	{
793 		DrawTextOnPath (text, path, offset.X, offset.Y, paint);
794 	}
795 
796 	void DrawTextOnPath (byte[] text, SKPath path, float hOffset, float vOffset, SKPaint paint)
797 	{
798 		if (text is null)
799 			throw new ArgumentNullException (text.stringof);
800 		if (path is null)
801 			throw new ArgumentNullException (path.stringof);
802 		if (paint is null)
803 			throw new ArgumentNullException (paint.stringof);
804 
805 		byte* t = text.ptr;
806 		DrawTextOnPath (cast(void*)t, cast(int)text.length, path, hOffset, vOffset, paint);
807 	}
808 
809 	void DrawTextOnPath (void* buffer, int length, SKPath path, SKPoint offset, SKPaint paint)
810 	{
811 		DrawTextOnPath (buffer, length, path, offset.X, offset.Y, paint);
812 	}
813 
814 	void DrawTextOnPath (void* buffer, int length, SKPath path, float hOffset, float vOffset, SKPaint paint)
815 	{
816 		if (buffer is null && length != 0)
817 			throw new ArgumentNullException (buffer.stringof);
818 		if (path is null)
819 			throw new ArgumentNullException (path.stringof);
820 		if (paint is null)
821 			throw new ArgumentNullException (paint.stringof);
822 
823 		auto font = paint.GetFont ();
824 
825 		auto textPath =  font.GetTextPathOnPath (buffer, length, paint.TextEncoding, path, paint.TextAlign, SKPoint (hOffset, vOffset));
826 		scope(exit) {
827 		    textPath.Dispose();
828 		}
829 
830 		DrawPath (textPath, paint);
831 	}
832 
833 	// Flush
834 
835 	void Flush ()
836 	{
837 		SkiaApi.sk_canvas_flush (cast(sk_canvas_t*)Handle);
838 	}
839 
840 	// Draw*Annotation
841 
842 	void DrawAnnotation (SKRect rect, string key, SKData value)
843 	{
844 		// auto bytes = StringUtilities.GetEncodedText (key, SKTextEncoding.Utf8, true);
845 		// byte* b = bytes.ptr;
846 		// SkiaApi.sk_canvas_draw_annotation (cast(sk_canvas_t*)Handle, &rect, b.stringof, cast(sk_data_t*)(value is null ? null : value.Handle));
847 	}
848 
849 	void DrawUrlAnnotation (SKRect rect, SKData value)
850 	{
851 		SkiaApi.sk_canvas_draw_url_annotation (cast(sk_canvas_t*)Handle, &rect,  cast(sk_data_t*)(value is null ? null : value.Handle));
852 	}
853 
854 	SKData DrawUrlAnnotation (SKRect rect, string value)
855 	{
856 		auto data = SKData.FromCString (value);
857 		DrawUrlAnnotation (rect, data);
858 		return data;
859 	}
860 
861 	void DrawNamedDestinationAnnotation (SKPoint point, SKData value)
862 	{
863 		SkiaApi.sk_canvas_draw_named_destination_annotation (cast(sk_canvas_t*)Handle, &point,  cast(sk_data_t*)(value is null ? null : value.Handle));
864 	}
865 
866 	SKData DrawNamedDestinationAnnotation (SKPoint point, string value)
867 	{
868 		auto data = SKData.FromCString (value);
869 		DrawNamedDestinationAnnotation (point, data);
870 		return data;
871 	}
872 
873 	void DrawLinkDestinationAnnotation (SKRect rect, SKData value)
874 	{
875 		SkiaApi.sk_canvas_draw_link_destination_annotation (cast(sk_canvas_t*)Handle, &rect,  cast(sk_data_t*)(value is null ? null : value.Handle));
876 	}
877 
878 	SKData DrawLinkDestinationAnnotation (SKRect rect, string value)
879 	{
880 		auto data = SKData.FromCString (value);
881 		DrawLinkDestinationAnnotation (rect, data);
882 		return data;
883 	}
884 
885 	// Draw*NinePatch
886 
887 	void DrawBitmapNinePatch (SKBitmap bitmap, SKRectI center, SKRect dst, SKPaint paint = null)
888 	{
889 		if (bitmap is null)
890 			throw new ArgumentNullException (bitmap.stringof);
891 		// the "center" rect must fit inside the bitmap "rect"
892 		if (!SKRect.Create (cast(SKSize)bitmap.Info.Size).Contains (cast(SKRect)center))
893 			throw new ArgumentException ("Center rectangle must be contained inside the bitmap bounds.", center.stringof);
894 
895 		SkiaApi.sk_canvas_draw_bitmap_nine (cast(sk_canvas_t*)Handle, cast(sk_bitmap_t*)bitmap.Handle, &center, &dst, cast(sk_paint_t*)(paint is null ? null : paint.Handle));
896 	}
897 
898 	void DrawImageNinePatch (SKImage image, SKRectI center, SKRect dst, SKPaint paint = null)
899 	{
900 		if (image is null)
901 			throw new ArgumentNullException (image.stringof);
902 		// the "center" rect must fit inside the image "rect"
903 		if (!SKRect.Create (image.Width(), image.Height()).Contains (cast(SKRect)center))
904 			throw new ArgumentException ("Center rectangle must be contained inside the image bounds.", center.stringof);
905 
906 		SkiaApi.sk_canvas_draw_image_nine (cast(sk_canvas_t*)Handle, cast(sk_image_t*)image.Handle, &center, &dst, cast(sk_paint_t*)(paint is null ? null : paint.Handle));
907 	}
908 
909 	// Draw*Lattice
910 
911 	void DrawBitmapLattice (SKBitmap bitmap, int[] xDivs, int[] yDivs, SKRect dst, SKPaint paint = null)
912 	{
913 		SKLattice lattice =  SKLattice ();
914 		lattice.XDivs = xDivs;
915 		lattice.YDivs = yDivs;
916 		DrawBitmapLattice (bitmap, lattice, dst, paint);
917 	}
918 
919 	void DrawImageLattice (SKImage image, int[] xDivs, int[] yDivs, SKRect dst, SKPaint paint = null)
920 	{
921 		SKLattice lattice =  SKLattice();
922 		lattice.XDivs = xDivs;
923 		lattice.YDivs = yDivs;
924 		DrawImageLattice (image, lattice, dst, paint);
925 	}
926 
927 	void DrawBitmapLattice (SKBitmap bitmap, SKLattice lattice, SKRect dst, SKPaint paint = null)
928 	{
929 		if (bitmap is null)
930 			throw new ArgumentNullException (bitmap.stringof);
931 		if (lattice.XDivs is null)
932 			throw new ArgumentNullException (lattice.XDivs.stringof);
933 		if (lattice.YDivs is null)
934 			throw new ArgumentNullException (lattice.YDivs.stringof);
935 
936 		int* x = lattice.XDivs.ptr;
937 		int* y = lattice.YDivs.ptr;
938 		SKLatticeRectType* r = lattice.RectTypes.ptr;
939 		SKColor* c = lattice.Colors.ptr;
940 
941         SKLatticeInternal nativeLattice =  SKLatticeInternal();
942         
943 		nativeLattice.fBounds = null;
944 		nativeLattice.fRectTypes = r;
945 		nativeLattice.fXCount = cast(int)lattice.XDivs.length;
946 		nativeLattice.fXDivs = x;
947 		nativeLattice.fYCount = cast(int)lattice.YDivs.length;
948 		nativeLattice.fYDivs = y;
949 		nativeLattice.fColors = cast(uint*)c;
950 
951 		Nullable!SKRectI nullableBounds = lattice.Bounds;
952       
953         if (!nullableBounds.isNull) {
954             SKRectI bounds = nullableBounds.get();
955             nativeLattice.fBounds = &bounds;
956         }
957         SkiaApi.sk_canvas_draw_bitmap_lattice (cast(sk_canvas_t*)Handle, cast(sk_bitmap_t*)bitmap.Handle, &nativeLattice, &dst, cast(sk_paint_t*)(paint is null ? null : paint.Handle));
958 	}
959 
960 	void DrawImageLattice (SKImage image, SKLattice lattice, SKRect dst, SKPaint paint = null)
961 	{
962 		if (image is null)
963 			throw new ArgumentNullException (image.stringof);
964 		if (lattice.XDivs is null)
965 			throw new ArgumentNullException (lattice.XDivs.stringof);
966 		if (lattice.YDivs is null)
967 			throw new ArgumentNullException (lattice.YDivs.stringof);
968 
969 		int* x = lattice.XDivs.ptr;
970 		int* y = lattice.YDivs.ptr;
971 		SKLatticeRectType* r = lattice.RectTypes.ptr;
972 		SKColor* c = lattice.Colors.ptr; 
973 
974 		SKLatticeInternal nativeLattice =  SKLatticeInternal();
975 	
976 		nativeLattice.fBounds = null;
977 		nativeLattice.fRectTypes = r;
978 		nativeLattice.fXCount = cast(int)lattice.XDivs.length;
979 		nativeLattice.fXDivs = x;
980 		nativeLattice.fYCount = cast(int)lattice.YDivs.length;
981 		nativeLattice.fYDivs = y;
982 		nativeLattice.fColors = cast(uint*)c;
983 		
984 		Nullable!SKRectI nullableBounds = lattice.Bounds;
985       
986         if (!nullableBounds.isNull) {
987             SKRectI bounds = nullableBounds.get();
988 			nativeLattice.fBounds = &bounds;
989 		}
990 		SkiaApi.sk_canvas_draw_image_lattice (cast(sk_canvas_t*)Handle, cast(sk_image_t*)image.Handle, &nativeLattice, &dst, cast(sk_paint_t*)(paint is null ? null : paint.Handle));
991 	}
992 
993 	// *Matrix
994 
995 	void ResetMatrix ()
996 	{
997 		SkiaApi.sk_canvas_reset_matrix (cast(sk_canvas_t*)Handle);
998 	}
999 
1000 	void SetMatrix (SKMatrix matrix)
1001 	{
1002 		SkiaApi.sk_canvas_set_matrix (cast(sk_canvas_t*)Handle, &matrix);
1003 	}
1004 
1005 	SKMatrix TotalMatrix() {
1006         SKMatrix matrix;
1007         SkiaApi.sk_canvas_get_total_matrix (cast(sk_canvas_t*)Handle, &matrix);
1008         return matrix;
1009 	}
1010 
1011 	// SaveCount
1012 
1013 	int SaveCount() { return SkiaApi.sk_canvas_get_save_count (cast(sk_canvas_t*)Handle); }
1014 
1015 	// DrawVertices
1016 
1017 	void DrawVertices (SKVertexMode vmode, SKPoint[] vertices, SKColor[] colors, SKPaint paint)
1018 	{
1019 		auto vert = SKVertices.CreateCopy (vmode, vertices, colors);
1020 		DrawVertices (vert, SKBlendMode.Modulate, paint);
1021 	}
1022 
1023 	void DrawVertices (SKVertexMode vmode, SKPoint[] vertices, SKPoint[] texs, SKColor[] colors, SKPaint paint)
1024 	{
1025 		auto vert = SKVertices.CreateCopy (vmode, vertices, texs, colors);
1026 		DrawVertices (vert, SKBlendMode.Modulate, paint);
1027 	}
1028 
1029 	void DrawVertices (SKVertexMode vmode, SKPoint[] vertices, SKPoint[] texs, SKColor[] colors, ushort[] indices, SKPaint paint)
1030 	{
1031 		auto vert = SKVertices.CreateCopy (vmode, vertices, texs, colors, indices);
1032 		DrawVertices (vert, SKBlendMode.Modulate, paint);
1033 	}
1034 
1035 	void DrawVertices (SKVertexMode vmode, SKPoint[] vertices, SKPoint[] texs, SKColor[] colors, SKBlendMode mode, ushort[] indices, SKPaint paint)
1036 	{
1037 		auto vert = SKVertices.CreateCopy (vmode, vertices, texs, colors, indices);
1038 		DrawVertices (vert, mode, paint);
1039 	}
1040 
1041 	void DrawVertices (SKVertices vertices, SKBlendMode mode, SKPaint paint)
1042 	{
1043 		if (vertices is null)
1044 			throw new ArgumentNullException (vertices.stringof);
1045 		if (paint is null)
1046 			throw new ArgumentNullException (paint.stringof);
1047 		SkiaApi.sk_canvas_draw_vertices (cast(sk_canvas_t*)Handle, cast(sk_vertices_t*)vertices.Handle, mode, cast(sk_paint_t*)paint.Handle);
1048 	}
1049 
1050 	// DrawArc
1051 
1052 	void DrawArc (SKRect oval, float startAngle, float sweepAngle, bool useCenter, SKPaint paint)
1053 	{
1054 		if (paint is null)
1055 			throw new ArgumentNullException (paint.stringof);
1056 		SkiaApi.sk_canvas_draw_arc (cast(sk_canvas_t*)Handle, &oval, startAngle, sweepAngle, useCenter, cast(sk_paint_t*)paint.Handle);
1057 	}
1058 
1059 	// DrawRoundRectDifference
1060 
1061 	void DrawRoundRectDifference (SKRoundRect outer, SKRoundRect inner, SKPaint paint)
1062 	{
1063 		if (outer is null)
1064 			throw new ArgumentNullException (outer.stringof);
1065 		if (inner is null)
1066 			throw new ArgumentNullException (inner.stringof);
1067 		if (paint is null)
1068 			throw new ArgumentNullException (paint.stringof);
1069 
1070 		SkiaApi.sk_canvas_draw_drrect (cast(sk_canvas_t*)Handle, cast(sk_rrect_t*)outer.Handle, cast(sk_rrect_t*)inner.Handle, cast(sk_paint_t*)paint.Handle);
1071 	}
1072 
1073 	// DrawAtlas
1074 
1075 	void DrawAtlas (SKImage atlas, SKRect[] sprites, SKRotationScaleMatrix[] transforms, SKPaint paint)
1076 	{
1077 		return DrawAtlas (atlas, sprites, transforms, null, SKBlendMode.Dst, null, paint);
1078 	}
1079 
1080 	void DrawAtlas (SKImage atlas, SKRect[] sprites, SKRotationScaleMatrix[] transforms, SKColor[] colors, SKBlendMode mode, SKPaint paint)
1081 	{
1082 		return DrawAtlas (atlas, sprites, transforms, colors, mode, null, paint);
1083 	}
1084 
1085 	void DrawAtlas (SKImage atlas, SKRect[] sprites, SKRotationScaleMatrix[] transforms, SKColor[] colors, SKBlendMode mode, SKRect cullRect, SKPaint paint)
1086 	{
1087 		return DrawAtlas (atlas, sprites, transforms, colors, mode, &cullRect, paint);
1088 	}
1089 
1090 	private void DrawAtlas (SKImage atlas, SKRect[] sprites, SKRotationScaleMatrix[] transforms, SKColor[] colors, SKBlendMode mode, SKRect* cullRect, SKPaint paint)
1091 	{
1092 		if (atlas is null)
1093 			throw new ArgumentNullException (atlas.stringof);
1094 		if (sprites is null)
1095 			throw new ArgumentNullException (sprites.stringof);
1096 		if (transforms is null)
1097 			throw new ArgumentNullException (transforms.stringof);
1098 
1099 		if (transforms.length != sprites.length)
1100 			throw new ArgumentException ("The number of transforms must match the number of sprites.", transforms.stringof);
1101 		if (colors !is null && colors.length != sprites.length)
1102 			throw new ArgumentException ("The number of colors must match the number of sprites.", colors.stringof);
1103 
1104 		SKRect* s = sprites.ptr;
1105 		SKRotationScaleMatrix* t = transforms.ptr;
1106 		SKColor* c = colors.ptr;
1107 		SkiaApi.sk_canvas_draw_atlas (cast(sk_canvas_t*)Handle, cast(sk_image_t*)atlas.Handle, t, s, cast(uint*)c, cast(int)transforms.length, mode,cullRect,  cast(sk_paint_t*)paint.Handle);
1108 	}
1109 
1110 	// DrawPatch
1111 
1112 	void DrawPatch (SKPoint[] cubics, SKColor[] colors, SKPoint[] texCoords, SKPaint paint)
1113 	{
1114 		return DrawPatch (cubics, colors, texCoords, SKBlendMode.Modulate, paint);
1115 	}
1116 
1117 	void DrawPatch (SKPoint[] cubics, SKColor[] colors, SKPoint[] texCoords, SKBlendMode mode, SKPaint paint)
1118 	{
1119 		if (cubics is null)
1120 			throw new ArgumentNullException (cubics.stringof);
1121 		if (cubics.length != PatchCubicsCount)
1122 			throw new ArgumentException ("Cubics must have a length of {PatchCubicsCount}."~cubics.stringof);
1123 
1124 		if (colors !is null && colors.length != PatchCornerCount)
1125 			throw new ArgumentException ("Colors must have a length of {PatchCornerCount}."~colors.stringof);
1126 
1127 		if (texCoords !is null && texCoords.length != PatchCornerCount)
1128 			throw new ArgumentException ("Texture coordinates must have a length of {PatchCornerCount}."~texCoords.stringof);
1129 
1130 		if (paint is null)
1131 			throw new ArgumentNullException (paint.stringof);
1132 
1133 		SKPoint* cubes = cubics.ptr;
1134 		SKColor* cols = colors.ptr;
1135 		SKPoint* coords = texCoords.ptr;
1136 		SkiaApi.sk_canvas_draw_patch (cast(sk_canvas_t*)Handle, cubes, cast(uint*)cols, coords, mode, cast(sk_paint_t*)paint.Handle);
1137 	}
1138 
1139 	static SKCanvas GetObject (void* handle, bool owns = true, bool unrefExisting = true)
1140 	{
1141 		  return GetOrAddObject!(SKCanvas)(handle, owns, unrefExisting, 
1142 		  		(h,o) { 
1143 			  		return new SKCanvas (h, o);
1144 			  	});
1145 	}
1146 }
1147 
1148 class SKAutoCanvasRestore : IDisposable
1149 {
1150 	private SKCanvas canvas;
1151 	private int saveCount;
1152 
1153 	this (SKCanvas canvas)
1154 	{
1155     this (canvas, true);
1156   }
1157 
1158 	this (SKCanvas canvas, bool doSave)
1159 	{
1160 		this.canvas = canvas;
1161 		this.saveCount = 0;
1162 
1163 		if (canvas !is null) {
1164 			saveCount = canvas.SaveCount();
1165 			if (doSave) {
1166 				canvas.Save ();
1167 			}
1168 		}
1169 	}
1170 
1171 	void Dispose ()
1172 	{
1173 		Restore ();
1174 	}
1175 
1176 	/// <summary>
1177 	/// Perform the restore now, instead of waiting for the Dispose.
1178 	/// Will only do this once.
1179 	/// </summary>
1180 	void Restore ()
1181 	{
1182 		if (canvas !is null) {
1183 			canvas.RestoreToCount (saveCount);
1184 			canvas = null;
1185 		}
1186 	}
1187 }