1 module skia.SKImage;
2 
3 // TODO: `MakeCrossContextFromEncoded`
4 // TODO: `MakeFromYUVTexturesCopy` and `MakeFromNV12TexturesCopy`
5 // TODO: `FromPicture` with bit depth and color space
6 // TODO: `GetTextureHandle`
7 // TODO: `MakeColorSpace`
8 
9 import skia.Definitions;
10 import skia.EnumMappings;
11 import skia.Exceptions;
12 import skia.DelegateProxies;
13 import skia.MathTypes;
14 import skia.GRContext;
15 import skia.GRDefinitions;
16 import skia.GRBackendTexture;
17 import skia.SKObject;
18 import skia.SKImageInfo;
19 import skia.SKStream;
20 import skia.SKPixmap;
21 import skia.SKColorTable;
22 import skia.SKData;
23 import skia.SKBitmap;
24 import skia.SKColorSpace;
25 import skia.SKPicture;
26 import skia.SKPaint;
27 import skia.SKShader;
28 import skia.SKPixelSerializer;
29 import skia.SKImageFilter;
30 import skia.SKMatrix;
31 import skia.SkiaApi;
32 
33 class SKImage : SKObject {
34 	this(void* x, bool owns) {
35 		super(x, owns);
36 	}
37 
38 	// protected override void Dispose(bool disposing) {
39 	// 	return super.Dispose(disposing);
40 	// }
41 
42 	// create brand new image
43 
44 	// static SKImage Create(SKImageInfo info) {
45 	// 	auto pixels = Marshal.AllocCoTaskMem(info.BytesSize);
46 	// 	SKPixmap pixmap = new SKPixmap(info, pixels);
47 
48 	// 	// don't use the managed version as that is just extra overhead which isn't necessary
49 	// 	return GetObject(SkiaApi.sk_image_new_raster(cast(sk_pixmap_t*)pixmap.Handle,
50 	// 			DelegateProxies.SKImageRasterReleaseDelegateProxyForCoTaskMem, null));
51 
52 	// }
53 
54 	// create a new image from a copy of pixel data
55 
56 	static SKImage FromPixelCopy(SKImageInfo info, SKStream pixels) {
57 		return FromPixelCopy(info, pixels, info.RowBytes);
58 	}
59 
60 	static SKImage FromPixelCopy(SKImageInfo info, SKStream pixels, int rowBytes) {
61 		if (pixels is null)
62 			throw new ArgumentNullException(pixels.stringof);
63 
64 		SKData data = SKData.Create(pixels);
65 		return FromPixels(info, data, rowBytes);
66 	}
67 
68 	// static SKImage FromPixelCopy (SKImageInfo info, Stream pixels)
69 	// {
70 	// 	return FromPixelCopy (info, pixels, info.RowBytes);
71 	// }
72 
73 	// static SKImage FromPixelCopy (SKImageInfo info, Stream pixels, int rowBytes)
74 	// {
75 	// 	if (pixels is null)
76 	// 		throw new ArgumentNullException (pixels.stringof);
77 
78 	//   SKData data = SKData.Create (pixels);
79 	//   return FromPixels (info, data, rowBytes);
80 	// }
81 
82 	static SKImage FromPixelCopy(SKImageInfo info, byte[] pixels) {
83 		return FromPixelCopy(info, pixels, info.RowBytes);
84 	}
85 
86 	// static SKImage FromPixelCopy(SKImageInfo info, byte[] pixels, int rowBytes) {
87 	// 	if (pixels is null)
88 	// 		throw new ArgumentNullException(pixels.stringof);
89 
90 	// 	SKData data = SKData.CreateCopy(pixels);
91 	// 	return FromPixels(info, data, rowBytes);
92 	// }
93 
94 	static SKImage FromPixelCopy(SKImageInfo info, void* pixels) {
95 		return FromPixelCopy(info, pixels, info.RowBytes);
96 	}
97 
98 	static SKImage FromPixelCopy(SKImageInfo info, void* pixels, int rowBytes) {
99 		if (pixels is null)
100 			throw new ArgumentNullException(pixels.stringof);
101 
102 		auto nInfo = SKImageInfo.FromManaged(info);
103 		return GetObject(SkiaApi.sk_image_new_raster_copy(&nInfo,
104 				cast(void*) pixels, cast(void*) rowBytes));
105 	}
106 
107 	static SKImage FromPixelCopy(SKImageInfo info, void* pixels, int rowBytes, SKColorTable ctable) {
108 		return FromPixelCopy(info, pixels, rowBytes);
109 	}
110 
111 	static SKImage FromPixelCopy(SKPixmap pixmap) {
112 		if (pixmap is null)
113 			throw new ArgumentNullException(pixmap.stringof);
114 		return GetObject(SkiaApi.sk_image_new_raster_copy_with_pixmap(cast(sk_pixmap_t*)pixmap.Handle));
115 	}
116 
117 	static SKImage FromPixelCopy(SKImageInfo info, const(byte)[] pixels) {
118 		return FromPixelCopy(info, cast(void*)pixels, info.RowBytes);
119 	}
120 
121 	static SKImage FromPixelCopy(SKImageInfo info, byte[] pixels, int rowBytes) {
122 		if (pixels is null)
123 			throw new ArgumentNullException(pixels.stringof);
124 
125 		SKData data = SKData.CreateCopy(pixels);
126 		return FromPixels(info, data, rowBytes);
127 	}
128 
129 	// create a new image around existing pixel data
130 
131 	static SKImage FromPixelData(SKImageInfo info, SKData data, int rowBytes) {
132 		if (data is null)
133 			throw new ArgumentNullException(data.stringof);
134 		auto cinfo = SKImageInfo.FromManaged(info);
135 		return GetObject(SkiaApi.sk_image_new_raster_data(&cinfo, cast(sk_data_t*)data.Handle,
136 				cast(void*) rowBytes));
137 	}
138 
139 	static SKImage FromPixels(SKImageInfo info, SKData data) {
140 		return FromPixels(info, data, info.RowBytes);
141 	}
142 
143 	static SKImage FromPixels(SKImageInfo info, SKData data, int rowBytes) {
144 		if (data is null)
145 			throw new ArgumentNullException(data.stringof);
146 		auto cinfo = SKImageInfo.FromManaged(info);
147 		return GetObject(SkiaApi.sk_image_new_raster_data(&cinfo, cast(sk_data_t*)data.Handle,
148 				cast(void*) rowBytes));
149 	}
150 
151 	static SKImage FromPixels(SKImageInfo info, void* pixels) {
152 		SKPixmap pixmap = new SKPixmap(info, pixels, info.RowBytes);
153 		return FromPixels(pixmap);
154 	}
155 
156 	static SKImage FromPixels(SKImageInfo info, void* pixels, int rowBytes) {
157 
158 		SKPixmap pixmap = new SKPixmap(info, pixels, rowBytes);
159 		return FromPixels(pixmap);
160 	}
161 
162 	static SKImage FromPixels(SKPixmap pixmap) {
163 		return FromPixels(pixmap);
164 	}
165 
166 	static SKImage FromPixels(SKPixmap pixmap, SKImageRasterReleaseDelegate releaseProc) {
167 		return FromPixels(pixmap, releaseProc);
168 	}
169 
170 	// static SKImage FromPixels (SKPixmap pixmap, SKImageRasterReleaseDelegate releaseProc, object releaseContext)
171 	// {
172 	// 	if (pixmap is null)
173 	// 		throw new ArgumentNullException (pixmap.stringof);
174 
175 	// 	auto del = releaseProc !is null && releaseContext !is null
176 	// 		? new SKImageRasterReleaseDelegate ((addr, _) {return releaseProc (addr, releaseContext);})
177 	// 		: releaseProc;
178 	// 	auto proxy = DelegateProxies.Create (del, DelegateProxies.SKImageRasterReleaseDelegateProxy,  _, out var ctx);
179 	// 	return GetObject (SkiaApi.sk_image_new_raster (pixmap.Handle, proxy, cast(void*)ctx));
180 	// }
181 
182 	// create a new image from encoded data
183 
184 	static SKImage FromEncodedData(SKData data, SKRectI subset) {
185 		if (data is null)
186 			throw new ArgumentNullException(data.stringof);
187 
188 		auto handle = SkiaApi.sk_image_new_from_encoded(cast(sk_data_t*)data.Handle, &subset);
189 		return GetObject(handle);
190 	}
191 
192 	static SKImage FromEncodedData(SKData data) {
193 		if (data is null)
194 			throw new ArgumentNullException(data.stringof);
195 
196 		auto handle = SkiaApi.sk_image_new_from_encoded(cast(sk_data_t*)data.Handle, null);
197 		return GetObject(handle);
198 	}
199 
200 	static SKImage FromEncodedData(byte[] data) {
201 		if (data is null)
202 			throw new ArgumentNullException(data.stringof);
203 		if (data.length == 0)
204 			throw new ArgumentException("The data buffer was empty.");
205 
206 		SKData skdata = SKData.CreateCopy(data);
207 		return FromEncodedData(skdata);
208 	}
209 
210 	static SKImage FromEncodedData(SKStream data) {
211 		if (data is null)
212 			throw new ArgumentNullException(data.stringof);
213 
214 		SKData skdata = SKData.Create(data);
215 		if (skdata is null)
216 			return null;
217 		return FromEncodedData(skdata);
218 	}
219 
220 	// static SKImage FromEncodedData (Stream data)
221 	// {
222 	// 	if (data is null)
223 	// 		throw new ArgumentNullException (data.stringof);
224 
225 	// 	  SKData skdata = SKData.Create (data);
226 	// 		if (skdata is null)
227 	// 			return null;
228 	// 		return FromEncodedData (skdata);
229 
230 	// }
231 
232 	static SKImage FromEncodedData(string filename) {
233 		if (filename is null)
234 			throw new ArgumentNullException(filename.stringof);
235 
236 		SKData skdata = SKData.Create(filename);
237 		if (skdata is null)
238 			return null;
239 		return FromEncodedData(skdata);
240 	}
241 
242 	// create a new image from a bitmap
243 
244 	static SKImage FromBitmap(SKBitmap bitmap) {
245 		if (bitmap is null)
246 			throw new ArgumentNullException(bitmap.stringof);
247 
248 		auto image = GetObject(SkiaApi.sk_image_new_from_bitmap(cast(sk_bitmap_t*)bitmap.Handle));
249 		// GC.KeepAlive(bitmap);
250 		return image;
251 	}
252 
253 	// create a new image from a GPU texture
254 
255 	static SKImage FromTexture(GRContext context, GRBackendTextureDesc desc) {
256 		return FromTexture(context, desc, SKAlphaType.Premul);
257 	}
258 
259 	static SKImage FromTexture(GRContext context, GRBackendTextureDesc desc, SKAlphaType alpha) {
260 		return FromTexture(context, desc, alpha);
261 	}
262 
263 	static SKImage FromTexture(GRContext context, GRBackendTextureDesc desc,
264 			SKAlphaType alpha, SKImageTextureReleaseDelegate releaseProc) {
265 		return FromTexture(context, desc, alpha, releaseProc);
266 	}
267 
268 	// static SKImage FromTexture (GRContext context, GRBackendTextureDesc desc, SKAlphaType alpha, SKImageTextureReleaseDelegate releaseProc, object releaseContext)
269 	// {
270 	// 	if (context is null)
271 	// 		throw new ArgumentNullException (context.stringof);
272 
273 	// 	auto texture = new GRBackendTexture (desc);
274 	// 	return FromTexture (context, texture, desc.Origin, desc.Config.ToColorType (), alpha, null, releaseProc, releaseContext);
275 	// }
276 
277 	static SKImage FromTexture(GRContext context, GRGlBackendTextureDesc desc) {
278 		return FromTexture(context, desc, SKAlphaType.Premul);
279 	}
280 
281 	static SKImage FromTexture(GRContext context, GRGlBackendTextureDesc desc, SKAlphaType alpha) {
282 		return FromTexture(context, desc, alpha);
283 	}
284 
285 	static SKImage FromTexture(GRContext context, GRGlBackendTextureDesc desc,
286 			SKAlphaType alpha, SKImageTextureReleaseDelegate releaseProc) {
287 		return FromTexture(context, desc, alpha, releaseProc);
288 	}
289 
290 	// static SKImage FromTexture (GRContext context, GRGlBackendTextureDesc desc, SKAlphaType alpha, SKImageTextureReleaseDelegate releaseProc, object releaseContext)
291 	// {
292 	// 	auto texture = new GRBackendTexture (desc);
293 	// 	return FromTexture (context, texture, desc.Origin, desc.Config.ToColorType (), alpha, null, releaseProc, releaseContext);
294 	// }
295 
296 	static SKImage FromTexture(GRContext context, GRBackendTexture texture, SKColorType colorType) {
297 		return FromTexture(context, texture, GRSurfaceOrigin.BottomLeft,
298 				colorType, SKAlphaType.Premul);
299 	}
300 
301 	static SKImage FromTexture(GRContext context, GRBackendTexture texture,
302 			GRSurfaceOrigin origin, SKColorType colorType) {
303 		return FromTexture(context, texture, origin, colorType,
304 				SKAlphaType.Premul);
305 	}
306 
307 	static SKImage FromTexture(GRContext context, GRBackendTexture texture,
308 			GRSurfaceOrigin origin, SKColorType colorType, SKAlphaType alpha) {
309 		return FromTexture(context, texture, origin, colorType, alpha);
310 	}
311 
312 	static SKImage FromTexture(GRContext context, GRBackendTexture texture,
313 			GRSurfaceOrigin origin, SKColorType colorType, SKAlphaType alpha,
314 			SKColorSpace colorspace) {
315 		return FromTexture(context, texture, origin, colorType, alpha, colorspace);
316 	}
317 
318 	static SKImage FromTexture(GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, SKColorType colorType,
319 			SKAlphaType alpha, SKColorSpace colorspace, SKImageTextureReleaseDelegate releaseProc) {
320 		return FromTexture(context, texture, origin, colorType, alpha,
321 				colorspace, releaseProc);
322 	}
323 
324 	// static SKImage FromTexture (GRContext context, GRBackendTexture texture, GRSurfaceOrigin origin, SKColorType colorType, SKAlphaType alpha, SKColorSpace colorspace, SKImageTextureReleaseDelegate releaseProc, object releaseContext)
325 	// {
326 	// 	if (context is null)
327 	// 		throw new ArgumentNullException (context.stringof);
328 	// 	if (texture is null)
329 	// 		throw new ArgumentNullException (texture.stringof);
330 
331 	// 	auto cs = colorspace is null ? null : colorspace.Handle;
332 	// 	auto del = releaseProc !is null && releaseContext !is null
333 	// 		? new SKImageTextureReleaseDelegate ((_){return  releaseProc (releaseContext);})
334 	// 		: releaseProc;
335 	// 	auto proxy = DelegateProxies.Create (del, DelegateProxies.SKImageTextureReleaseDelegateProxy, _, out var ctx);
336 	// 	return GetObject (SkiaApi.sk_image_new_from_texture (context.Handle, texture.Handle, origin, colorType.ToNative (), alpha, cs, proxy, (void*)ctx));
337 	// }
338 
339 	static SKImage FromAdoptedTexture(GRContext context, GRBackendTextureDesc desc) {
340 		return FromAdoptedTexture(context, desc, SKAlphaType.Premul);
341 	}
342 
343 	static SKImage FromAdoptedTexture(GRContext context, GRBackendTextureDesc desc,
344 			SKAlphaType alpha) {
345 		GRBackendTexture texture = new GRBackendTexture(desc);
346 		return FromAdoptedTexture(context, texture, desc.Origin,
347 				desc.Config.ToColorType(), alpha, null);
348 	}
349 
350 	static SKImage FromAdoptedTexture(GRContext context, GRGlBackendTextureDesc desc) {
351 		return FromAdoptedTexture(context, desc, SKAlphaType.Premul);
352 	}
353 
354 	static SKImage FromAdoptedTexture(GRContext context,
355 			GRGlBackendTextureDesc desc, SKAlphaType alpha) {
356 		GRBackendTexture texture = new GRBackendTexture(desc);
357 		return FromAdoptedTexture(context, texture, desc.Origin,
358 				desc.Config.ToColorType(), alpha, null);
359 	}
360 
361 	static SKImage FromAdoptedTexture(GRContext context,
362 			GRBackendTexture texture, SKColorType colorType) {
363 		return FromAdoptedTexture(context, texture, GRSurfaceOrigin.BottomLeft,
364 				colorType, SKAlphaType.Premul, null);
365 	}
366 
367 	static SKImage FromAdoptedTexture(GRContext context,
368 			GRBackendTexture texture, GRSurfaceOrigin origin, SKColorType colorType) {
369 		return FromAdoptedTexture(context, texture, origin, colorType, SKAlphaType.Premul, null);
370 	}
371 
372 	static SKImage FromAdoptedTexture(GRContext context, GRBackendTexture texture,
373 			GRSurfaceOrigin origin, SKColorType colorType, SKAlphaType alpha) {
374 		return FromAdoptedTexture(context, texture, origin, colorType, alpha, null);
375 	}
376 
377 	static SKImage FromAdoptedTexture(GRContext context, GRBackendTexture texture,
378 			GRSurfaceOrigin origin, SKColorType colorType, SKAlphaType alpha,
379 			SKColorSpace colorspace) {
380 		if (context is null)
381 			throw new ArgumentNullException(context.stringof);
382 		if (texture is null)
383 			throw new ArgumentNullException(texture.stringof);
384 
385 		auto cs = colorspace is null ? null : colorspace.Handle;
386 		return GetObject(SkiaApi.sk_image_new_from_adopted_texture(cast(gr_context_t*)context.Handle,
387 				cast(gr_backendtexture_t*)texture.Handle, origin, colorType.ToNative(), alpha, cast(sk_colorspace_t*)cs));
388 	}
389 
390 	// create a new image from a picture
391 
392 	static SKImage FromPicture(SKPicture picture, SKSizeI dimensions) {
393 		return FromPicture(picture, dimensions, null, null);
394 	}
395 
396 	static SKImage FromPicture(SKPicture picture, SKSizeI dimensions, SKMatrix matrix) {
397 		return FromPicture(picture, dimensions, &matrix, null);
398 	}
399 
400 	static SKImage FromPicture(SKPicture picture, SKSizeI dimensions, SKPaint paint) {
401 		return FromPicture(picture, dimensions, null, paint);
402 	}
403 
404 	static SKImage FromPicture(SKPicture picture, SKSizeI dimensions, SKMatrix matrix, SKPaint paint) {
405 		return FromPicture(picture, dimensions, &matrix, paint);
406 	}
407 
408 	private static SKImage FromPicture(SKPicture picture, SKSizeI dimensions,
409 			SKMatrix* matrix, SKPaint paint) {
410 		if (picture is null)
411 			throw new ArgumentNullException(picture.stringof);
412 
413 		auto p = paint.Handle ? paint.Handle : null;
414 		return GetObject(SkiaApi.sk_image_new_from_picture(cast(sk_picture_t*)picture.Handle, &dimensions, matrix, cast(sk_paint_t*)p));
415 	}
416 
417 	SKData Encode() {
418 		return SKData.GetObject(SkiaApi.sk_image_encode(cast(sk_image_t*)Handle));
419 	}
420 
421 	SKData Encode(SKPixelSerializer serializer) {
422 		if (serializer is null)
423 			throw new ArgumentNullException(serializer.stringof);
424 
425 		// try old data
426 		auto encoded = EncodedData;
427 		if (encoded !is null) {
428 			if (serializer.UseEncodedData(encoded.Data, cast(ulong) encoded.Size)) {
429 				return encoded;
430 			} else {
431 				encoded.Dispose();
432 				encoded = null;
433 			}
434 		}
435 
436 		// get new data (raster)
437 		if (!IsTextureBacked) {
438 			auto pixmap = PeekPixels();
439 			return serializer.Encode(pixmap);
440 
441 		}
442 
443 		// get new data (texture / gpu)
444 		// this involves a copy from gpu to cpu first
445 		if (IsTextureBacked) {
446 			SKImageInfo info = SKImageInfo(Width, Height, ColorType, AlphaType, ColorSpace);
447 			SKBitmap temp = new SKBitmap(info);
448 			SKPixmap pixmap = temp.PeekPixels();
449 			if (pixmap !is null && ReadPixels(pixmap, 0, 0)) {
450 				return serializer.Encode(pixmap);
451 			}
452 
453 		}
454 
455 		// some error
456 		return null;
457 	}
458 
459 	SKData Encode(SKEncodedImageFormat format, int quality) {
460 		return SKData.GetObject(SkiaApi.sk_image_encode_specific(cast(sk_image_t*)Handle, format, quality));
461 	}
462 
463 	int Width() {
464 		return SkiaApi.sk_image_get_width(cast(sk_image_t*)Handle);
465 	}
466 
467 	int Height() {
468 		return SkiaApi.sk_image_get_height(cast(sk_image_t*)Handle);
469 	}
470 
471 	uint UniqueId() {
472 		return SkiaApi.sk_image_get_unique_id(cast(sk_image_t*)Handle);
473 	}
474 
475 	SKAlphaType AlphaType() {
476 		return SkiaApi.sk_image_get_alpha_type(cast(sk_image_t*)Handle);
477 	}
478 
479 	SKColorType ColorType() {
480 		// return SkiaApi.sk_image_get_color_type(cast(sk_image_t*)Handle).FromNative();
481     return SKColorType.Rgba8888;
482 	}
483 
484 	SKColorSpace ColorSpace() {
485 		return SKColorSpace.GetObject(SkiaApi.sk_image_get_colorspace(cast(sk_image_t*)Handle));
486 	}
487 
488 	bool IsAlphaOnly() {
489 		return SkiaApi.sk_image_is_alpha_only(cast(sk_image_t*)Handle);
490 	}
491 
492 	SKData EncodedData() {
493 		return SKData.GetObject(SkiaApi.sk_image_ref_encoded(cast(sk_image_t*)Handle));
494 	}
495 
496 	// ToShader
497 
498 	SKShader ToShader() {
499 		return ToShader(SKShaderTileMode.Clamp, SKShaderTileMode.Clamp);
500 	}
501 
502 	SKShader ToShader(SKShaderTileMode tileX, SKShaderTileMode tileY) {
503 		return SKShader.GetObject(SkiaApi.sk_image_make_shader(cast(sk_image_t*)Handle, tileX, tileY, null));
504 	}
505 
506 	SKShader ToShader(SKShaderTileMode tileX, SKShaderTileMode tileY, SKMatrix localMatrix) {
507 		return SKShader.GetObject(SkiaApi.sk_image_make_shader(cast(sk_image_t*)Handle, tileX,
508 				tileY, &localMatrix));
509 	}
510 
511 	// PeekPixels
512 
513 	bool PeekPixels(SKPixmap pixmap) {
514 		if (pixmap is null)
515 			throw new ArgumentNullException(pixmap.stringof);
516 
517 		auto result = SkiaApi.sk_image_peek_pixels(cast(sk_image_t*)Handle, cast(sk_pixmap_t*)pixmap.Handle);
518 		if (result)
519 			pixmap.pixelSource = this;
520 		return result;
521 	}
522 
523 	SKPixmap PeekPixels() {
524 		auto pixmap = new SKPixmap();
525 		if (!PeekPixels(pixmap)) {
526 			pixmap.Dispose();
527 			pixmap = null;
528 		}
529 		return pixmap;
530 	}
531 
532 	bool IsTextureBacked() {
533 		return SkiaApi.sk_image_is_texture_backed(cast(sk_image_t*)Handle);
534 	}
535 
536 	bool IsLazyGenerated() {
537 		return SkiaApi.sk_image_is_lazy_generated(cast(sk_image_t*)Handle);
538 	}
539 
540 	bool IsValid(GRContext context) {
541 		return SkiaApi.sk_image_is_valid(cast(sk_image_t*)Handle, cast(gr_context_t*)(context.Handle ? context.Handle : null));
542 	}
543 
544 	// ReadPixels
545 
546 	bool ReadPixels(SKImageInfo dstInfo, void* dstPixels) {
547 		return ReadPixels(dstInfo, dstPixels, dstInfo.RowBytes, 0, 0, SKImageCachingHint.Allow);
548 	}
549 
550 	bool ReadPixels(SKImageInfo dstInfo, void* dstPixels, int dstRowBytes) {
551 		return ReadPixels(dstInfo, dstPixels, dstRowBytes, 0, 0, SKImageCachingHint.Allow);
552 	}
553 
554 	bool ReadPixels(SKImageInfo dstInfo, void* dstPixels, int dstRowBytes, int srcX, int srcY) {
555 		return ReadPixels(dstInfo, dstPixels, dstRowBytes, srcX, srcY, SKImageCachingHint.Allow);
556 	}
557 
558 	bool ReadPixels(SKImageInfo dstInfo, void* dstPixels, int dstRowBytes,
559 			int srcX, int srcY, SKImageCachingHint cachingHint) {
560 		auto cinfo = SKImageInfo.FromManaged(dstInfo);
561 		auto result = SkiaApi.sk_image_read_pixels(cast(sk_image_t*)Handle, &cinfo,
562 				cast(void*) dstPixels, cast(void*) dstRowBytes, srcX, srcY, cachingHint);
563 		// GC.KeepAlive(this);
564 		return result;
565 	}
566 
567 	bool ReadPixels(SKPixmap pixmap) {
568 		return ReadPixels(pixmap, 0, 0, SKImageCachingHint.Allow);
569 	}
570 
571 	bool ReadPixels(SKPixmap pixmap, int srcX, int srcY) {
572 		return ReadPixels(pixmap, srcX, srcY, SKImageCachingHint.Allow);
573 	}
574 
575 	bool ReadPixels(SKPixmap pixmap, int srcX, int srcY, SKImageCachingHint cachingHint) {
576 		if (pixmap is null)
577 			throw new ArgumentNullException(pixmap.stringof);
578 
579 		auto result = SkiaApi.sk_image_read_pixels_into_pixmap(cast(sk_image_t*)Handle,
580 				cast(sk_pixmap_t*)pixmap.Handle, srcX, srcY, cachingHint);
581 		// GC.KeepAlive(this);
582 		return result;
583 	}
584 
585 	// ScalePixels
586 
587 	bool ScalePixels(SKPixmap dst, SKFilterQuality quality) {
588 		return ScalePixels(dst, quality, SKImageCachingHint.Allow);
589 	}
590 
591 	bool ScalePixels(SKPixmap dst, SKFilterQuality quality, SKImageCachingHint cachingHint) {
592 		if (dst is null)
593 			throw new ArgumentNullException(dst.stringof);
594 		return SkiaApi.sk_image_scale_pixels(cast(sk_image_t*)Handle, cast(sk_pixmap_t*)dst.Handle, quality, cachingHint);
595 	}
596 
597 	// Subset
598 
599 	SKImage Subset(SKRectI subset) {
600 		return GetObject(SkiaApi.sk_image_make_subset(cast(sk_image_t*)Handle, &subset));
601 	}
602 
603 	// ToRasterImage
604 
605 	SKImage ToRasterImage() {
606 		return ToRasterImage(false);
607 	}
608 
609 	SKImage ToRasterImage(bool ensurePixelData) {
610 		return ensurePixelData ? GetObject(SkiaApi.sk_image_make_raster_image(cast(sk_image_t*)Handle)) : GetObject(
611 				SkiaApi.sk_image_make_non_texture_image(cast(sk_image_t*)Handle));
612 	}
613 
614 	// ToTextureImage
615 
616 	SKImage ToTextureImage(GRContext context) {
617 		return ToTextureImage(context, false);
618 	}
619 
620 	SKImage ToTextureImage(GRContext context, SKColorSpace colorspace) {
621 		return ToTextureImage(context, false);
622 	}
623 
624 	SKImage ToTextureImage(GRContext context, bool mipmapped) {
625 		if (context is null)
626 			throw new ArgumentNullException(context.stringof);
627 
628 		return GetObject(SkiaApi.sk_image_make_texture_image(cast(sk_image_t*)Handle, cast(gr_context_t*)context.Handle, mipmapped));
629 	}
630 
631 	// ApplyImageFilter
632 
633 	SKImage ApplyImageFilter(SKImageFilter filter, SKRectI subset,
634 			SKRectI clipBounds, out SKRectI outSubset, out SKPoint outOffset) {
635 		SKPointI outOffsetActual;
636 		auto image = ApplyImageFilter(filter, subset, clipBounds, outSubset, outOffsetActual);
637 		// outOffset = outOffsetActual;
638 		return image;
639 	}
640 
641 	SKImage ApplyImageFilter(SKImageFilter filter, SKRectI subset,
642 			SKRectI clipBounds, out SKRectI outSubset, out SKPointI outOffset) {
643 		if (filter is null)
644 			throw new ArgumentNullException(filter.stringof);
645 
646 		SKRectI* os = &outSubset;
647 		SKPointI* oo = &outOffset;
648 		return GetObject(SkiaApi.sk_image_make_with_filter(cast(sk_image_t*)Handle,
649 				cast(sk_imagefilter_t*)filter.Handle, &subset, &clipBounds, os, oo));
650 	}
651 
652 	static SKImage GetObject(void* handle) {
653     	return GetOrAddObject!(SKImage)(handle, delegate SKImage (h, o) { return new SKImage (h, o);});
654 		// return GetOrAddObject(handle, (h, o) { return new SKImage(h, o); });
655 	}
656 
657 }