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 }