1 module skia.SKBitmap;
2 
3 import skia.Exceptions;
4 import skia.SkiaApi;
5 import skia.SKCodec;
6 import skia.SKImageInfo;
7 import skia.SKObject;
8 import skia.MathTypes;
9 import skia.SKColorTable;
10 import skia.SKColor;
11 import skia.SKPMColor;
12 import skia.SKColorSpace;
13 import skia.SKPaint;
14 import skia.Definitions;
15 import skia.SKStream;
16 import skia.SKData;
17 import skia.SKPixmap;
18 import skia.SkiaApi;
19 import skia.SKImageInfo;
20 import skia.SKObject;
21 import skia.SKImage;
22 import skia.SKShader;
23 import skia.SKMatrix;
24 import skia.SKCanvas;
25 import skia.DelegateProxies;
26 import skia.SKMask;
27 
28 
29 import std.format;
30 
31 enum SKBitmapResizeMethod
32 {
33 	Box,
34 	Triangle,
35 	Lanczos3,
36 	Hamming,
37 	Mitchell
38 }
39 
40 SKFilterQuality ToFilterQuality (SKBitmapResizeMethod method)
41 {
42 	switch (method) {
43 		case SKBitmapResizeMethod.Box:
44 		case SKBitmapResizeMethod.Triangle:
45 			return SKFilterQuality.Low;
46 		case SKBitmapResizeMethod.Lanczos3:
47 			return SKFilterQuality.Medium;
48 		case SKBitmapResizeMethod.Hamming:
49 		case SKBitmapResizeMethod.Mitchell:
50 			return SKFilterQuality.High;
51 		default:
52 			return SKFilterQuality.Medium;
53 	}
54 }
55 	
56 
57 // TODO: keep in mind SKBitmap may be going away (according to Google)
58 // TODO: `ComputeIsOpaque` may be useful
59 // TODO: `GenerationID` may be useful
60 // TODO: `GetAddr` and `GetPixel` are confusing
61 
62 class SKBitmap : SKObject, ISKSkipObjectRegistration
63 {
64 	private enum string UnsupportedColorTypeMessage = "Setting the ColorTable is only supported for bitmaps with ColorTypes of Index8.";
65 	private enum string UnableToAllocatePixelsMessage = "Unable to allocate pixels for the bitmap.";
66 
67 	this (void* handle, bool owns)
68 	{
69 		super (handle, owns);
70 	}
71 
72 	this () {
73 		this (SkiaApi.sk_bitmap_new (), true);
74 		if (Handle is null) {
75 			throw new InvalidOperationException ("Unable to create a new SKBitmap instance.");
76 		}
77 	}
78 
79 	this (int width, int height, bool isOpaque = false) {
80 		this (width, height, SKImageInfo.PlatformColorType, isOpaque ? SKAlphaType.Opaque : SKAlphaType.Premul);
81 	}
82 
83 	this (int width, int height, SKColorType colorType, SKAlphaType alphaType) {
84 		this ( SKImageInfo (width, height, colorType, alphaType));
85 	}
86 
87 	this (int width, int height, SKColorType colorType, SKAlphaType alphaType, SKColorSpace colorspace) {
88 		this ( SKImageInfo (width, height, colorType, alphaType, colorspace));
89 	}
90 
91 	this (SKImageInfo info) {
92 		this (info, info.RowBytes);
93 	}
94 
95 	this (SKImageInfo info, int rowBytes) {
96 		this ();
97 		if (!TryAllocPixels (info, rowBytes)) {
98 			throw new Exception (UnableToAllocatePixelsMessage);
99 		}
100 	}
101 
102 	this (SKImageInfo info, SKColorTable ctable, SKBitmapAllocFlags flags) {
103 		this (info, SKBitmapAllocFlags.None);
104 	}
105 
106 	this (SKImageInfo info, SKBitmapAllocFlags flags) {
107 		this ();
108 		if (!TryAllocPixels (info, flags)) {
109 			throw new Exception (UnableToAllocatePixelsMessage);
110 		}
111 	}
112 
113 	this (SKImageInfo info, SKColorTable ctable) {
114 		this (info, SKBitmapAllocFlags.None);
115 	}
116 
117 	protected override void Dispose (bool disposing) 
118 	{
119 		return super.Dispose (disposing);
120 	}
121 
122   override void Dispose()
123   {
124     return super.Dispose();
125   }
126 
127 	protected override void DisposeNative () 
128 	{
129 		return SkiaApi.sk_bitmap_destructor (cast(sk_bitmap_t*)Handle);
130 	}
131 
132 	// TryAllocPixels
133 
134 	bool TryAllocPixels (SKImageInfo info)
135 	{
136 		return TryAllocPixels (info, info.RowBytes);
137 	}
138 
139 	bool TryAllocPixels (SKImageInfo info, int rowBytes)
140 	{
141 		auto cinfo = SKImageInfo.FromManaged (info);
142 		return SkiaApi.sk_bitmap_try_alloc_pixels (cast(sk_bitmap_t*)Handle, &cinfo, rowBytes);
143 	}
144 
145 	bool TryAllocPixels (SKImageInfo info, SKBitmapAllocFlags flags)
146 	{
147 		auto cinfo = SKImageInfo.FromManaged (info);
148 		return SkiaApi.sk_bitmap_try_alloc_pixels_with_flags (cast(sk_bitmap_t*)Handle, &cinfo, cast(uint)flags);
149 	}
150 
151 	// Reset
152 
153 	void Reset ()
154 	{
155 		SkiaApi.sk_bitmap_reset (cast(sk_bitmap_t*)Handle);
156 	}
157 
158 	// SetImmutable
159 
160 	void SetImmutable ()
161 	{
162 		SkiaApi.sk_bitmap_set_immutable (cast(sk_bitmap_t*)Handle);
163 	}
164 
165 	// Erase
166 
167 	void Erase (SKColor color)
168 	{
169 		SkiaApi.sk_bitmap_erase (cast(sk_bitmap_t*)Handle, cast(uint)color);
170 	}
171 
172 	void Erase (SKColor color, SKRectI rect)
173 	{
174 		SkiaApi.sk_bitmap_erase_rect (cast(sk_bitmap_t*)Handle, cast(uint)color, &rect);
175 	}
176 
177 	// GetAddr*
178 
179 	byte GetAddr8 (int x, int y) { return *SkiaApi.sk_bitmap_get_addr_8 (cast(sk_bitmap_t*)Handle, x, y); }
180 
181 	ushort GetAddr16 (int x, int y) { return *SkiaApi.sk_bitmap_get_addr_16 (cast(sk_bitmap_t*)Handle, x, y); }
182 
183 	uint GetAddr32 (int x, int y) { return *SkiaApi.sk_bitmap_get_addr_32 (cast(sk_bitmap_t*)Handle, x, y); }
184 
185 	void* GetAddr (int x, int y) { return GetAddress (x, y); }
186 
187 	// GetAddress
188 
189 	void* GetAddress (int x, int y)
190 	{
191 		return cast(void*)SkiaApi.sk_bitmap_get_addr (cast(sk_bitmap_t*)Handle, x, y);
192 	}
193 
194 	// Pixels (color)
195 
196 	// SKPMColor GetIndex8Color (int x, int y)
197 	// {
198 	// 	return GetPixel (x, y);
199 	// }
200 
201 	SKColor GetPixel (int x, int y)
202 	{
203 		return cast(SKColor)SkiaApi.sk_bitmap_get_pixel_color (cast(sk_bitmap_t*)Handle, x, y);
204 	}
205 
206 	void SetPixel (int x, int y, SKColor color)
207 	{
208 		SKImageInfo info = Info();
209 		if (x < 0 || x >= info.Width)
210 			throw new ArgumentOutOfRangeException (x.stringof);
211 		if (y < 0 || y >= info.Height)
212 			throw new ArgumentOutOfRangeException (y.stringof);
213 
214 		SKCanvas canvas = new SKCanvas (this);
215 		scope(exit) {
216 			canvas.Dispose();
217 		}
218 		canvas.DrawPoint (x, y, color);
219 	}
220 
221 	// Copy
222 
223 	bool CanCopyTo (SKColorType colorType)
224 	{
225 		// TODO: optimize as this does more work that we really want
226 
227 		if (colorType == SKColorType.Unknown)
228 			return false;
229 
230 		SKBitmap bmp = new SKBitmap();
231 		scope(exit) {
232 		    bmp.Dispose();
233 		}
234 
235 		auto info = Info
236 			.WithColorType (colorType)
237 			.WithSize (1, 1);
238 		return bmp.TryAllocPixels (info);
239 	}
240 
241 	SKBitmap Copy ()
242 	{
243 		return Copy (ColorType);
244 	}
245 
246 	SKBitmap Copy (SKColorType colorType)
247 	{
248 		auto destination = new SKBitmap ();
249 		if (!CopyTo (destination, colorType)) {
250 			destination.Dispose ();
251 			destination = null;
252 		}
253 		return destination;
254 	}
255 
256 	bool CopyTo (SKBitmap destination)
257 	{
258 		if (destination is null) {
259 			throw new ArgumentNullException (destination.stringof);
260 		}
261 		return CopyTo (destination, ColorType);
262 	}
263 
264 	bool CopyTo (SKBitmap destination, SKColorType colorType)
265 	{
266 		if (destination is null)
267 			throw new ArgumentNullException (destination.stringof);
268 
269 		if (colorType == SKColorType.Unknown)
270 			return false;
271 
272 		auto srcPixmap = PeekPixels();
273 		scope(exit) {
274 		    srcPixmap.Dispose();
275 		}
276 		if (srcPixmap is null)
277 			return false;
278 
279 		SKBitmap temp = new SKBitmap();
280 		scope(exit) {
281 		    temp.Dispose();
282 		}
283 
284 		auto dstInfo = srcPixmap.Info.WithColorType (colorType);
285 		if (!temp.TryAllocPixels (dstInfo))
286 			return false;
287 
288 		SKCanvas canvas = new SKCanvas(temp);
289 		scope(exit) {
290 		    canvas.Dispose();
291 		}
292 
293 		SKPaint paint = new SKPaint();
294 		scope(exit) {
295 			paint.Dispose();
296 		}
297 
298 		paint.Shader = ToShader (),
299 		paint.BlendMode = SKBlendMode.Src;
300 
301 		canvas.DrawPaint (paint);
302 
303 		destination.Swap (temp);
304 		return true;
305 	}
306 
307 	// ExtractSubset
308 
309 	bool ExtractSubset (SKBitmap destination, SKRectI subset)
310 	{
311 		if (destination is null) {
312 			throw new ArgumentNullException (destination.stringof);
313 		}
314 		return SkiaApi.sk_bitmap_extract_subset (cast(sk_bitmap_t*)Handle, cast(sk_bitmap_t*)destination.Handle, &subset);
315 	}
316 
317 	// ExtractAlpha
318 
319 	bool ExtractAlpha (SKBitmap destination)
320 	{
321 		SKPointI offset;
322 		return ExtractAlpha (destination, null, offset);
323 	}
324 
325 	bool ExtractAlpha (SKBitmap destination, ref SKPointI offset)
326 	{
327 		return ExtractAlpha (destination, null, offset);
328 	}
329 
330 	bool ExtractAlpha (SKBitmap destination, SKPaint paint)
331 	{
332 		SKPointI offset;
333 		return ExtractAlpha (destination, paint, offset);
334 	}
335 
336 	bool ExtractAlpha (SKBitmap destination, SKPaint paint, ref SKPointI offset)
337 	{
338 		if (destination is null) {
339 			throw new ArgumentNullException (destination.stringof);
340 		}
341 		SKPointI* o = &offset;
342 		return SkiaApi.sk_bitmap_extract_alpha (cast(sk_bitmap_t*)Handle, cast(sk_bitmap_t*)destination.Handle, cast(sk_paint_t*)(paint is null ? null : paint.Handle), o);
343 	}
344 
345 	// properties
346 
347 	bool ReadyToDraw()
348 	{
349 		return SkiaApi.sk_bitmap_ready_to_draw (cast(sk_bitmap_t*)Handle);
350 	}
351 
352 	SKImageInfo Info() {
353 		SKImageInfoNative cinfo;
354 		SkiaApi.sk_bitmap_get_info (cast(sk_bitmap_t*)Handle, &cinfo);
355 		return SKImageInfo.ToManaged (cinfo);
356 	}
357 
358 	int Width() {
359 		return Info.Width;
360 	}
361 
362 	int Height() {
363 		return Info.Height;
364 	}
365 
366 	SKColorType ColorType() {
367 		return Info.ColorType;
368 	}
369 
370 	SKAlphaType AlphaType() {
371 		return Info.AlphaType;
372 	}
373 
374 	SKColorSpace ColorSpace() {
375 		return Info.ColorSpace;
376 	}
377 
378 	int BytesPerPixel() {
379 		return Info.BytesPerPixel;
380 	}
381 
382 	int RowBytes() {
383 		return cast(int)SkiaApi.sk_bitmap_get_row_bytes (cast(sk_bitmap_t*)Handle);
384 	}
385 
386 	int ByteCount() {
387 		return cast(int)SkiaApi.sk_bitmap_get_byte_count (cast(sk_bitmap_t*)Handle);
388 	}
389 
390 	// *Pixels*
391 
392 	void* GetPixels ()
393 	{
394 		size_t length;
395 		return GetPixels (length);
396 	}
397 
398 	// ReadOnlySpan!byte GetPixelSpan ()
399 	// {
400 	// 	size_t length;
401 	// 	return new ReadOnlySpan!byte (GetPixels (length), cast(int)length);
402 	// }
403 
404 	void* GetPixels (ref size_t length)
405 	{
406 		size_t* l = &length;
407 
408 		return cast(void*)SkiaApi.sk_bitmap_get_pixels (cast(sk_bitmap_t*)Handle, l);
409 	}
410 
411 	void SetPixels (void* pixels)
412 	{
413 		SkiaApi.sk_bitmap_set_pixels (cast(sk_bitmap_t*)Handle, cast(void*)pixels);
414 	}
415 
416 	void SetPixels (void* pixels, SKColorTable ct)
417 	{
418 		SetPixels (pixels);
419 	}
420 
421 	// SetColorTable
422 
423 	void SetColorTable (SKColorTable ct)
424 	{
425 		// no-op due to unsupperted action
426 	}
427 
428 	// more properties
429 
430 	// byte[] Bytes() {
431 	// 	byte[] array = GetPixelSpan ().ToArray ();
432 	// 	// GC.KeepAlive (this);
433 	// 	return array;
434 	// }
435 
436 	SKColor[] Pixels() {
437 		auto info = Info;
438 		SKColor[] pixels = new SKColor[info.Width * info.Height];
439 		SKColor* p = pixels.ptr;
440 		SkiaApi.sk_bitmap_get_pixel_colors (cast(sk_bitmap_t*)Handle, cast(uint*)p);
441 
442 		return pixels;
443 	}
444 	
445 	void Pixels(SKColor[] value) {
446 		if (value is null)
447 			throw new ArgumentNullException (value.stringof);
448 
449 		SKImageInfo info = Info();
450 		if (info.Width * info.Height != value.length) {
451 			string str = format("The number of pixels must equal Width x Height, or {%d * %d}.", info.Width, info.Height);
452 			throw new ArgumentException (str ~ " Argument: " ~ value.stringof);
453 		}
454 
455 		SKColor* v = value.ptr;
456 		SKImageInfo tempInfo =  SKImageInfo (info.Width, info.Height, SKColorType.Bgra8888, SKAlphaType.Unpremul);
457 		SKBitmap temp = new SKBitmap();
458 		scope(exit) {
459 			temp.Dispose();
460 		}
461 		temp.InstallPixels (tempInfo, cast(void*)v);
462 
463 		auto shader = temp.ToShader ();
464 		scope(exit) {
465 			shader.Dispose();
466 		}
467 
468 		SKCanvas canvas = new SKCanvas(this);
469 		scope(exit) {
470 			canvas.Dispose();
471 		}
472 
473 		SKPaint paint = new SKPaint();
474 		scope(exit) {
475 			paint.Dispose();
476 		}
477 		
478 		paint.Shader = shader;
479 		paint.BlendMode = SKBlendMode.Src;
480 		
481 		canvas.DrawPaint (paint);
482 	}
483 
484 	bool IsEmpty() {
485 		return Info.IsEmpty;
486 	}
487 
488 	bool IsNull() {
489 		return SkiaApi.sk_bitmap_is_null (cast(sk_bitmap_t*)Handle);
490 	}
491 
492 	bool DrawsNothing() {
493 		return IsEmpty || IsNull;
494 	}
495 
496 	bool IsImmutable() {
497 		return SkiaApi.sk_bitmap_is_immutable (cast(sk_bitmap_t*)Handle);
498 	}
499 
500 	bool IsVolatile() {
501 		return SkiaApi.sk_bitmap_is_volatile (cast(sk_bitmap_t*)Handle);
502 	}
503 
504 	void IsVolatile(bool value) {
505 		SkiaApi.sk_bitmap_set_volatile (cast(sk_bitmap_t*)Handle, value); 
506 	}
507 
508 	SKColorTable ColorTable = null;
509 
510 	// DecodeBounds
511 
512 	// static SKImageInfo DecodeBounds (Stream stream)
513 	// {
514 	// 	if (stream is null) {
515 	// 		throw new ArgumentNullException (stream.stringof);
516 	// 	}
517 
518 	// 	SKCodec codec = SKCodec.Create (stream);
519 	// 	scope(exit) {
520 	// 		codec.Dispose();
521 	// 	}
522 
523 	// 	if(codec !is null) {
524 	// 		SKImageInfo imageInfo = codec.Info();
525 	// 		if(imageInfo !is null) return imageInfo;
526 	// 	}
527 
528 	// 	return SKImageInfo.Empty;
529 	// }
530 
531 	static SKImageInfo DecodeBounds (SKStream stream)
532 	{
533 		if (stream is null) {
534 			throw new ArgumentNullException (stream.stringof);
535 		}
536 		SKCodec codec = SKCodec.Create (stream);
537 		scope(exit) {
538 			codec.Dispose();
539 		}
540 
541 		if(codec !is null) {
542 			SKImageInfo imageInfo = codec.Info();
543 			if(&imageInfo !is null) return imageInfo;
544 		}
545 
546 		return SKImageInfo.Empty;
547 	}
548 
549 	static SKImageInfo DecodeBounds (SKData data)
550 	{
551 		if (data is null) {
552 			throw new ArgumentNullException (data.stringof);
553 		}
554 
555 		SKCodec codec = SKCodec.Create (data);
556 		scope(exit) {
557 			codec.Dispose();
558 		}
559 
560 		if(codec !is null) {
561 			SKImageInfo imageInfo = codec.Info();
562 			if(&imageInfo !is null) return imageInfo;
563 		}
564 
565 		return SKImageInfo.Empty;
566 	}
567 
568 	static SKImageInfo DecodeBounds (string filename)
569 	{
570 		if (filename is null) {
571 			throw new ArgumentNullException (filename.stringof);
572 		}
573 
574 		SKCodec codec = SKCodec.Create (filename);
575 		scope(exit) {
576 			codec.Dispose();
577 		}
578 
579 		if(codec !is null) {
580 			SKImageInfo imageInfo = codec.Info();
581 			if(&imageInfo !is null) return imageInfo;
582 		}
583 
584 		return SKImageInfo.Empty;
585 	}
586 
587 	// static SKImageInfo DecodeBounds (byte[] buffer)
588 	// {
589 	// 	// return DecodeBounds (buffer.AsSpan ());
590 	// 	byte* b = buffer.ptr;
591 
592 	// 	// using var skdata = SKData.Create (cast(void*)b, buffer.Length);
593 	// 	using var codec = SKCodec.Create (skdata);
594 	// 	return codec?.Info ?? SKImageInfo.Empty;
595 	// }
596 
597 	// static SKImageInfo DecodeBounds (ReadOnlySpan!byte buffer)
598 	// {
599 	// 	byte* b = buffer; {
600 	// 		using var skdata = SKData.Create ((void*)b, buffer.Length);
601 	// 		using var codec = SKCodec.Create (skdata);
602 	// 		return codec?.Info ?? SKImageInfo.Empty;
603 	// 	}
604 	// }
605 
606 	// Decode
607 
608 	static SKBitmap Decode (SKCodec codec)
609 	{
610 		if (codec is null) {
611 			throw new ArgumentNullException (codec.stringof);
612 		}
613 
614 		SKImageInfo info = codec.Info;
615 		if (info.AlphaType == SKAlphaType.Unpremul) {
616 			info.AlphaType = SKAlphaType.Premul;
617 		}
618 		// for backwards compatibility, remove the colorspace
619 		info.ColorSpace = null;
620 		return Decode (codec, info);
621 	}
622 
623 	static SKBitmap Decode (SKCodec codec, SKImageInfo bitmapInfo)
624 	{
625 		if (codec is null) {
626 			throw new ArgumentNullException (codec.stringof);
627 		}
628 
629 		SKBitmap bitmap = new SKBitmap (bitmapInfo);
630 		size_t length;
631 		auto result = codec.GetPixels (bitmapInfo, bitmap.GetPixels (length));
632 		if (result != SKCodecResult.Success && result != SKCodecResult.IncompleteInput) {
633 			bitmap.Dispose ();
634 			bitmap = null;
635 		}
636 		return bitmap;
637 	}
638 
639 	// static SKBitmap Decode (Stream stream)
640 	// {
641 	// 	if (stream is null) {
642 	// 		throw new ArgumentNullException (stream.stringof);
643 	// 	}
644 
645 	// 	SKCodec codec = SKCodec.Create (stream);
646 	// 	if (codec is null) {
647 	// 		return null;
648 	// 	}
649 
650 	// 	scope(exit) {
651 	// 		codec.Dispose();
652 	// 	}
653 	// 	return Decode (codec);
654 	// }
655 
656 	// static SKBitmap Decode (Stream stream, SKImageInfo bitmapInfo)
657 	// {
658 	// 	if (stream is null) {
659 	// 		throw new ArgumentNullException (stream.stringof);
660 	// 	}
661 
662 	// 	SKCodec codec = SKCodec.Create (stream);
663 	// 	if (codec is null) {
664 	// 		return null;
665 	// 	}
666 
667 	// 	scope(exit) {
668 	// 		codec.Dispose();
669 	// 	}
670 	// 	return Decode (codec, bitmapInfo);
671 	// }
672 
673 	// static SKBitmap Decode (SKStream stream)
674 	// {
675 	// 	if (stream is null) {
676 	// 		throw new ArgumentNullException (stream.stringof);
677 	// 	}
678 
679 	// 	SKCodec codec = SKCodec.Create (stream);
680 	// 	if (codec is null) {
681 	// 		return null;
682 	// 	}
683 
684 	// 	scope(exit) {
685 	// 		codec.Dispose();
686 	// 	}        
687 				
688 	// 	return Decode (codec);
689 	// }
690 
691 	// static SKBitmap Decode (SKStream stream, SKImageInfo bitmapInfo)
692 	// {
693 	// 	if (stream is null) {
694 	// 		throw new ArgumentNullException (stream.stringof);
695 	// 	}
696 		
697 	// 	SKCodec codec = SKCodec.Create (stream);
698 	// 	if (codec is null) {
699 	// 		return null;
700 	// 	}
701 
702 	// 	scope(exit) {
703 	// 		codec.Dispose();
704 	// 	}
705 		
706 	// 	return Decode (codec, bitmapInfo);
707 	// }
708 
709 	// static SKBitmap Decode (SKData data)
710 	// {
711 	// 	if (data is null) {
712 	// 		throw new ArgumentNullException (data.stringof);
713 	// 	}
714 
715 	// 	SKCodec codec = SKCodec.Create (data);
716 	// 	if (codec is null) {
717 	// 		return null;
718 	// 	}
719 
720 	// 	scope(exit) {
721 	// 		codec.Dispose();
722 	// 	}
723 
724 	// 	return Decode (codec);
725 	// }
726 
727 	// static SKBitmap Decode (SKData data, SKImageInfo bitmapInfo)
728 	// {
729 	// 	if (data is null) {
730 	// 		throw new ArgumentNullException (data.stringof);
731 	// 	}
732 
733 	// 	SKCodec codec = SKCodec.Create (data);
734 	// 	if (codec is null) {
735 	// 		return null;
736 	// 	}
737 
738 	// 	scope(exit) {
739 	// 		codec.Dispose();
740 	// 	}
741 
742 	// 	return Decode (codec, bitmapInfo);
743 	// }
744 
745 	static SKBitmap Decode (string filename)
746 	{
747 		if (filename is null) {
748 			throw new ArgumentNullException (filename.stringof);
749 		}
750 
751 		SKCodec codec = SKCodec.Create (filename);
752 		if (codec is null) {
753 			return null;
754 		}
755 
756 		scope(exit) {
757 			codec.Dispose();
758 		}
759 		
760 		return Decode (codec);
761 	}
762 
763 	static SKBitmap Decode (string filename, SKImageInfo bitmapInfo)
764 	{
765 		if (filename is null) {
766 			throw new ArgumentNullException (filename.stringof);
767 		}
768 
769 		SKCodec codec = SKCodec.Create (filename);
770 		if (codec is null) {
771 			return null;
772 		}
773 
774 		scope(exit) {
775 			codec.Dispose();
776 		}
777 
778 		return Decode (codec, bitmapInfo);
779 	}
780 
781 	static SKBitmap Decode (byte[] buffer)
782 	{
783 		// return Decode (buffer.AsSpan ());
784 
785 
786 		// byte* b = buffer.ptr;
787 		// auto skdata =  SKData.Create (cast(void*)b, buffer.length);
788 		// scope(exit) {
789 		//     skdata.Dispose();
790 		// }
791 
792 		// auto codec =  SKCodec.Create (skdata);
793 		// scope(exit) {
794 		//     codec.Dispose();
795 		// }
796 
797 		// return Decode (codec);				
798 
799     return null;
800 	}
801 
802 	static SKBitmap Decode (byte[] buffer, SKImageInfo bitmapInfo)
803 	{
804 		// return Decode (buffer.AsSpan (), bitmapInfo);
805 		byte* b = buffer.ptr;
806 		auto skdata =  SKData.Create (cast(void*)b, buffer.length);
807 		scope(exit) {
808 		    skdata.Dispose();
809 		}
810 
811 		auto codec =  SKCodec.Create (skdata);
812 		scope(exit) {
813 		    codec.Dispose();
814 		}
815 
816 		return Decode (codec, bitmapInfo);
817 	}
818 
819 	// static SKBitmap Decode (ReadOnlySpan!byte buffer)
820 	// {
821 	// 	byte* b = buffer;
822 	// 	using var skdata = SKData.Create (cast(void*)b, buffer.Length);
823 	// 	using var codec = SKCodec.Create (skdata);
824 	// 	return Decode (codec);
825 	// }
826 
827 	// static SKBitmap Decode (ReadOnlySpan!byte buffer, SKImageInfo bitmapInfo)
828 	// {
829 	// 	byte* b = buffer; {
830 	// 		using var skdata = SKData.Create ((void*)b, buffer.Length);
831 	// 		using var codec = SKCodec.Create (skdata);
832 	// 		return Decode (codec, bitmapInfo);
833 	// 	}
834 	// }
835 
836 	// InstallPixels
837 
838 	bool InstallPixels (SKImageInfo info, void* pixels)
839 	{
840 		return InstallPixels (info, pixels, info.RowBytes, null, null);
841 	}
842 
843 	bool InstallPixels (SKImageInfo info, void* pixels, int rowBytes)
844 	{
845 		return InstallPixels (info, pixels, rowBytes, null, null);
846 	}
847 
848 	bool InstallPixels (SKImageInfo info, void* pixels, int rowBytes, SKColorTable ctable)
849 	{
850 		return InstallPixels (info, pixels, rowBytes, null, null);
851 	}
852 
853 	bool InstallPixels (SKImageInfo info, void* pixels, int rowBytes, SKColorTable ctable, SKBitmapReleaseDelegate releaseProc, void* context)
854 	{
855 		return InstallPixels (info, pixels, rowBytes, releaseProc, context);
856 	}
857 
858 	bool InstallPixels (SKImageInfo info, void* pixels, int rowBytes, SKBitmapReleaseDelegate releaseProc)
859 	{
860 		// return InstallPixels (info, pixels, rowBytes, releaseProc, null);
861     return true;
862 	}
863 
864 	bool InstallPixels (SKImageInfo info, void* pixels, int rowBytes, SKBitmapReleaseDelegate releaseProc, void* context)
865 	{
866 		// var cinfo = SKImageInfo.FromManaged (ref info);
867 		// var del = releaseProc !is null && context !is null
868 		// 	? new SKBitmapReleaseDelegate ((addr, _) => releaseProc (addr, context))
869 		// 	: releaseProc;
870 		// var proxy = DelegateProxies.Create (del, DelegateProxies.SKBitmapReleaseDelegateProxy, out _, out var ctx);
871 		// return SkiaApi.sk_bitmap_install_pixels (Handle, &cinfo, (void*)pixels, cast(void*)rowBytes, proxy, (void*)ctx);
872 
873     return true;
874 	}
875 
876 	bool InstallPixels (SKPixmap pixmap)
877 	{
878 		return SkiaApi.sk_bitmap_install_pixels_with_pixmap (cast(sk_bitmap_t*)Handle, cast(sk_pixmap_t*)pixmap.Handle);
879 	}
880 
881 	// InstallMaskPixels
882 
883 	bool InstallMaskPixels (SKMask mask)
884 	{
885 		return SkiaApi.sk_bitmap_install_mask_pixels (cast(sk_bitmap_t*)Handle, &mask);
886 	}
887 
888 	// NotifyPixelsChanged
889 
890 	void NotifyPixelsChanged ()
891 	{
892 		SkiaApi.sk_bitmap_notify_pixels_changed (cast(sk_bitmap_t*)Handle);
893 	}
894 
895 	// PeekPixels
896 
897 	SKPixmap PeekPixels ()
898 	{
899 		SKPixmap pixmap = new SKPixmap ();
900 		auto result = PeekPixels (pixmap);
901 		if (result) {
902 			return pixmap;
903 		} else {
904 			pixmap.Dispose ();
905 			return null;
906 		}
907 	}
908 
909 	bool PeekPixels (SKPixmap pixmap)
910 	{
911 		if (pixmap is null) {
912 			throw new ArgumentNullException (pixmap.stringof);
913 		}
914 		auto result = SkiaApi.sk_bitmap_peek_pixels (cast(sk_bitmap_t*)Handle, cast(sk_pixmap_t*)pixmap.Handle);
915 		if (result)
916 			pixmap.pixelSource = this;
917 		return result;
918 	}
919 
920 	// Resize
921 
922 	SKBitmap Resize (SKImageInfo info, SKBitmapResizeMethod method)
923 	{
924 		return Resize (info, method.ToFilterQuality ());
925 	}
926 
927 	bool Resize (SKBitmap dst, SKBitmapResizeMethod method)
928 	{
929 		return ScalePixels (dst, method.ToFilterQuality ());
930 	}
931 
932 	static bool Resize (SKBitmap dst, SKBitmap src, SKBitmapResizeMethod method)
933 	{
934 		return src.ScalePixels (dst, method.ToFilterQuality ());
935 	}
936 
937 	SKBitmap Resize (SKImageInfo info, SKFilterQuality quality)
938 	{
939 		auto dst = new SKBitmap (info);
940 		if (ScalePixels (dst, quality)) {
941 			return dst;
942 		} else {
943 			dst.Dispose ();
944 			return null;
945 		}
946 	}
947 
948 	SKBitmap Resize (SKSizeI size, SKFilterQuality quality)
949 	{
950 		return Resize (Info.WithSize (size), quality);
951 	}
952 
953 	// ScalePixels
954 
955 	bool ScalePixels (SKBitmap destination, SKFilterQuality quality)
956 	{
957 		if (destination is null) {
958 			throw new ArgumentNullException (destination.stringof);
959 		}
960 
961 		SKPixmap dstPix = destination.PeekPixels ();
962 		scope(exit) {
963 			dstPix.Dispose();
964 		}
965 
966 		return ScalePixels (dstPix, quality);
967 	}
968 
969 	bool ScalePixels (SKPixmap destination, SKFilterQuality quality)
970 	{
971 		if (destination is null) {
972 			throw new ArgumentNullException (destination.stringof);
973 		}
974 
975 		SKPixmap srcPix = PeekPixels ();
976 		scope(exit) {
977 			srcPix.Dispose();
978 		}
979 		return srcPix.ScalePixels (destination, quality);
980 	}
981 
982 	// From/ToImage
983 
984 	static SKBitmap FromImage (SKImage image)
985 	{
986 		if (image is null) {
987 			throw new ArgumentNullException (image.stringof);
988 		}
989 
990 		SKImageInfo info =  SKImageInfo (image.Width(),image.Height(), SKImageInfo.PlatformColorType, image.AlphaType);
991 		SKBitmap bmp = new SKBitmap (info);
992 		if (!image.ReadPixels (info, cast(void*)bmp.GetPixels (), info.RowBytes, 0, 0)) {
993 			bmp.Dispose ();
994 			bmp = null;
995 		}
996 		return bmp;
997 	}
998 
999 	// Encode
1000 
1001 	SKData Encode (SKEncodedImageFormat format, int quality)
1002 	{
1003 		auto pixmap = PeekPixels();
1004 		scope(exit) {
1005 		    pixmap.Dispose();
1006 		}
1007 		return pixmap.Encode (format, quality);
1008 	}
1009 
1010 	// bool Encode (Stream dst, SKEncodedImageFormat format, int quality)
1011 	// {
1012 	// 	SKManagedWStream wrapped = new SKManagedWStream(dst);
1013 	// 	scope(exit) {
1014 	// 	    wrapped.Dispose();
1015 	// 	}
1016 	// 	return Encode (wrapped, format, quality);
1017 	// }
1018 
1019 	bool Encode (SKWStream dst, SKEncodedImageFormat format, int quality)
1020 	{
1021 		if (dst is null)
1022 			throw new ArgumentNullException (dst.stringof);
1023 
1024 		auto pixmap = PeekPixels();
1025 		scope(exit) {
1026 		    pixmap.Dispose();
1027 		}
1028 
1029 		if(pixmap !is null) {
1030 			return pixmap.Encode (dst, format, quality);
1031 		}
1032 		
1033 		return false;
1034 	}
1035 
1036 	// Swap
1037 
1038 	private void Swap (SKBitmap other)
1039 	{
1040 		SkiaApi.sk_bitmap_swap (cast(sk_bitmap_t*)Handle, cast(sk_bitmap_t*)other.Handle);
1041 	}
1042 
1043 	// ToShader
1044 
1045 	SKShader ToShader ()
1046 	{
1047 		return ToShader (SKShaderTileMode.Clamp, SKShaderTileMode.Clamp);
1048 	}
1049 
1050 	SKShader ToShader (SKShaderTileMode tmx, SKShaderTileMode tmy)
1051 	{
1052 		return SKShader.GetObject (SkiaApi.sk_bitmap_make_shader (cast(sk_bitmap_t*)Handle, tmx, tmy, null));
1053 	}
1054 
1055 	SKShader ToShader (SKShaderTileMode tmx, SKShaderTileMode tmy, SKMatrix localMatrix)
1056 	{
1057 		return SKShader.GetObject (SkiaApi.sk_bitmap_make_shader (cast(sk_bitmap_t*)Handle, tmx, tmy, &localMatrix));
1058 	}
1059 }