BitmapTests.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. using System;
  2. using System.IO;
  3. using System.Runtime.CompilerServices;
  4. using System.Runtime.InteropServices;
  5. using Avalonia.Controls;
  6. using Avalonia.Controls.Platform.Surfaces;
  7. using Avalonia.Controls.Shapes;
  8. using Avalonia.Layout;
  9. using Avalonia.Media;
  10. using Avalonia.Media.Imaging;
  11. using Avalonia.Platform;
  12. using Xunit;
  13. using Path = System.IO.Path;
  14. #pragma warning disable CS0649
  15. #if AVALONIA_SKIA
  16. namespace Avalonia.Skia.RenderTests
  17. #else
  18. namespace Avalonia.Direct2D1.RenderTests.Media
  19. #endif
  20. {
  21. public class BitmapTests : TestBase
  22. {
  23. public BitmapTests()
  24. : base(@"Media\Bitmap")
  25. {
  26. Directory.CreateDirectory(OutputPath);
  27. }
  28. class Framebuffer : ILockedFramebuffer, IFramebufferPlatformSurface
  29. {
  30. public Framebuffer(PixelFormat fmt, PixelSize size)
  31. {
  32. Format = fmt;
  33. var bpp = fmt == PixelFormat.Rgb565 ? 2 : 4;
  34. Size = size;
  35. RowBytes = bpp * size.Width;
  36. Address = Marshal.AllocHGlobal(size.Height * RowBytes);
  37. }
  38. public IntPtr Address { get; }
  39. public Vector Dpi { get; } = new Vector(96, 96);
  40. public PixelFormat Format { get; }
  41. public PixelSize Size { get; }
  42. public int RowBytes { get; }
  43. public void Dispose()
  44. {
  45. //no-op
  46. }
  47. public ILockedFramebuffer Lock()
  48. {
  49. return this;
  50. }
  51. public void Deallocate() => Marshal.FreeHGlobal(Address);
  52. }
  53. [Theory]
  54. [InlineData(PixelFormatEnum.Rgba8888), InlineData(PixelFormatEnum.Bgra8888),
  55. #if AVALONIA_SKIA
  56. InlineData(PixelFormatEnum.Rgb565)
  57. #endif
  58. ]
  59. internal void FramebufferRenderResultsShouldBeUsableAsBitmap(PixelFormatEnum fmte)
  60. {
  61. var fmt = new PixelFormat(fmte);
  62. var testName = nameof(FramebufferRenderResultsShouldBeUsableAsBitmap) + "_" + fmt;
  63. var fb = new Framebuffer(fmt, new PixelSize(80, 80));
  64. var r = Avalonia.AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>();
  65. using(var cpuContext = r.CreateBackendContext(null))
  66. using (var target = cpuContext.CreateRenderTarget(new object[] { fb }))
  67. using (var ctx = target.CreateDrawingContext())
  68. {
  69. ctx.Clear(Colors.Transparent);
  70. ctx.PushOpacity(0.8, new Rect(0, 0, 80, 80));
  71. ctx.DrawRectangle(Brushes.Chartreuse, null, new Rect(0, 0, 20, 100));
  72. ctx.DrawRectangle(Brushes.Crimson, null, new Rect(20, 0, 20, 100));
  73. ctx.DrawRectangle(Brushes.Gold,null, new Rect(40, 0, 20, 100));
  74. ctx.PopOpacity();
  75. }
  76. var bmp = new Bitmap(fmt, AlphaFormat.Premul, fb.Address, fb.Size, new Vector(96, 96), fb.RowBytes);
  77. fb.Deallocate();
  78. using (var rtb = new RenderTargetBitmap(new PixelSize(100, 100), new Vector(96, 96)))
  79. {
  80. using (var ctx = rtb.CreateDrawingContext())
  81. {
  82. ctx.DrawRectangle(Brushes.Blue, null, new Rect(0, 0, 100, 100));
  83. ctx.DrawRectangle(Brushes.Pink, null, new Rect(0, 20, 100, 10));
  84. var rc = new Rect(0, 0, 60, 60);
  85. ctx.DrawBitmap(bmp.PlatformImpl, 1, rc, rc);
  86. }
  87. rtb.Save(System.IO.Path.Combine(OutputPath, testName + ".out.png"));
  88. }
  89. CompareImagesNoRenderer(testName);
  90. }
  91. [Theory]
  92. [InlineData(PixelFormatEnum.Bgra8888), InlineData(PixelFormatEnum.Rgba8888)]
  93. internal void WriteableBitmapShouldBeUsable(PixelFormatEnum fmte)
  94. {
  95. var fmt = new PixelFormat(fmte);
  96. var writeableBitmap = new WriteableBitmap(new PixelSize(256, 256), new Vector(96, 96), fmt);
  97. var data = new int[256 * 256];
  98. for (int y = 0; y < 256; y++)
  99. for (int x = 0; x < 256; x++)
  100. data[y * 256 + x] =(int)((uint)(x + (y << 8)) | 0xFF000000u);
  101. using (var l = writeableBitmap.Lock())
  102. {
  103. for(var r = 0; r<256; r++)
  104. {
  105. Marshal.Copy(data, r * 256, new IntPtr(l.Address.ToInt64() + r * l.RowBytes), 256);
  106. }
  107. }
  108. var name = nameof(WriteableBitmapShouldBeUsable) + "_" + fmt;
  109. writeableBitmap.Save(System.IO.Path.Combine(OutputPath, name + ".out.png"));
  110. CompareImagesNoRenderer(name);
  111. }
  112. struct RawHeader
  113. {
  114. public int Width, Height, Stride;
  115. }
  116. [Theory,
  117. InlineData(PixelFormatEnum.BlackWhite),
  118. InlineData(PixelFormatEnum.Gray2),
  119. InlineData(PixelFormatEnum.Gray4),
  120. InlineData(PixelFormatEnum.Gray8),
  121. InlineData(PixelFormatEnum.Gray16),
  122. InlineData(PixelFormatEnum.Gray32Float),
  123. InlineData(PixelFormatEnum.Rgba64),
  124. InlineData(PixelFormatEnum.Rgba64, AlphaFormat.Premul),
  125. ]
  126. internal unsafe void BitmapsShouldSupportTranscoders_Lenna(PixelFormatEnum format, AlphaFormat alphaFormat = AlphaFormat.Unpremul)
  127. {
  128. var relativeFilesDir = "../../../PixelFormats/Lenna";
  129. var filesDir = Path.Combine(OutputPath, relativeFilesDir);
  130. var formatName = format.ToString();
  131. if (alphaFormat == AlphaFormat.Premul)
  132. formatName = "P" + formatName.ToLowerInvariant();
  133. var bitsData = File.ReadAllBytes(Path.Combine(filesDir, formatName + ".bits")).AsSpan();
  134. var header = MemoryMarshal.Cast<byte, RawHeader>(bitsData.Slice(0, Unsafe.SizeOf<RawHeader>()))[0];
  135. var data = bitsData.Slice(Unsafe.SizeOf<RawHeader>());
  136. var size = new PixelSize(header.Width, header.Height);
  137. var stride = header.Stride;
  138. string expectedName = Path.Combine(relativeFilesDir, formatName);
  139. if (!File.Exists(Path.Combine(OutputPath, expectedName + ".expected.png")))
  140. expectedName = Path.Combine(relativeFilesDir, "Default");
  141. var names = new[]
  142. {
  143. "_Writeable",
  144. "_WriteableInitialized",
  145. "_Normal"
  146. };
  147. foreach (var step in new[] { 0,1,2 })
  148. {
  149. var testName = nameof(BitmapsShouldSupportTranscoders_Lenna) + "_" + formatName + names[step];
  150. var path = System.IO.Path.Combine(OutputPath, testName + ".out.png");
  151. fixed (byte* pData = data)
  152. {
  153. Bitmap? b = null;
  154. try
  155. {
  156. if (step == 0)
  157. {
  158. var bmp = new WriteableBitmap(size, new Vector(96, 96), new PixelFormat(format),
  159. alphaFormat);
  160. using (var l = bmp.Lock())
  161. {
  162. var minStride = (l.Size.Width * l.Format.BitsPerPixel + 7) / 8;
  163. for (var y = 0; y < size.Height; y++)
  164. {
  165. Unsafe.CopyBlock((l.Address + y * l.RowBytes).ToPointer(), pData + y * stride,
  166. (uint)minStride);
  167. }
  168. }
  169. b = bmp;
  170. }
  171. else if (step == 1)
  172. b = new WriteableBitmap(new PixelFormat(format), alphaFormat, new IntPtr(pData),
  173. size, new Vector(96, 96), stride);
  174. else
  175. b = new Bitmap(new PixelFormat(format), alphaFormat, new IntPtr(pData),
  176. size, new Vector(96, 96), stride);
  177. if (step < 2)
  178. {
  179. var copyTo = new byte[data.Length];
  180. fixed (byte* pCopyTo = copyTo)
  181. b.CopyPixels(default, new IntPtr(pCopyTo), copyTo.Length, stride);
  182. Assert.Equal(data.ToArray(), copyTo);
  183. }
  184. b.Save(path);
  185. CompareImagesNoRenderer(testName, expectedName);
  186. }
  187. finally
  188. {
  189. b?.Dispose();
  190. }
  191. }
  192. }
  193. }
  194. [Fact]
  195. public unsafe void CopyPixelsShouldWorkForNonTranscodedBitmaps()
  196. {
  197. var stride = 32 * 4;
  198. var data = new byte[32 * stride];
  199. new Random().NextBytes(data);
  200. for (var c = 0; c < data.Length; c++)
  201. if (data[c] == 0)
  202. data[c] = 1;
  203. Bitmap bmp;
  204. fixed (byte* pData = data)
  205. bmp = new Bitmap(PixelFormat.Bgra8888, AlphaFormat.Unpremul, new IntPtr(pData), new PixelSize(32, 32),
  206. new Vector(96, 96), 32 * 4);
  207. var copyTo = new byte[data.Length];
  208. fixed (byte* pCopyTo = copyTo)
  209. bmp.CopyPixels(default, new IntPtr(pCopyTo), data.Length, stride);
  210. Assert.Equal(data, copyTo);
  211. }
  212. }
  213. }