BitmapUtils.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. using PixiEditor.Models.DataHolders;
  2. using PixiEditor.Models.Layers;
  3. using PixiEditor.Models.Layers.Utils;
  4. using PixiEditor.Parser;
  5. using PixiEditor.Parser.Skia;
  6. using SkiaSharp;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. using System.Windows;
  11. using System.Windows.Media.Imaging;
  12. namespace PixiEditor.Models.ImageManipulation
  13. {
  14. public static class BitmapUtils
  15. {
  16. public static Surface CombineLayers(Int32Rect portion, IEnumerable<Layer> layers, LayerStructure structure = null)
  17. {
  18. Surface finalSurface = new(portion.Width, portion.Height);
  19. using SKPaint paint = new();
  20. for (int i = 0; i < layers.Count(); i++)
  21. {
  22. Layer layer = layers.ElementAt(i);
  23. if (structure != null && !LayerStructureUtils.GetFinalLayerIsVisible(layer, structure))
  24. continue;
  25. float layerOpacity = structure == null ? layer.Opacity : LayerStructureUtils.GetFinalLayerOpacity(layer, structure);
  26. paint.Color = new(255, 255, 255, (byte)(layerOpacity * 255));
  27. if (layer.OffsetX < 0 || layer.OffsetY < 0 ||
  28. layer.Width + layer.OffsetX > layer.MaxWidth ||
  29. layer.Height + layer.OffsetY > layer.MaxHeight)
  30. {
  31. throw new InvalidOperationException("Layers must not extend beyond canvas borders");
  32. }
  33. using SKImage snapshot = layer.LayerBitmap.SkiaSurface.Snapshot();
  34. int x = portion.X - layer.OffsetX;
  35. int y = portion.Y - layer.OffsetY;
  36. finalSurface.SkiaSurface.Canvas.DrawImage(
  37. snapshot,
  38. new SKRect(x, y, portion.Width + x, portion.Height + y),
  39. new SKRect(0, 0, portion.Width, portion.Height),
  40. paint);
  41. }
  42. return finalSurface;
  43. }
  44. public static Surface[] ExtractSelectedPortions(Layer selLayer, Layer[] extractFrom, bool eraceFromLayers)
  45. {
  46. using var selSnap = selLayer.LayerBitmap.SkiaSurface.Snapshot();
  47. Surface[] output = new Surface[extractFrom.Length];
  48. int count = 0;
  49. foreach (Layer layer in extractFrom)
  50. {
  51. Surface portion = new Surface(selLayer.Width, selLayer.Height);
  52. SKRect selLayerRect = new SKRect(0, 0, selLayer.Width, selLayer.Height);
  53. int x = selLayer.OffsetX - layer.OffsetX;
  54. int y = selLayer.OffsetY - layer.OffsetY;
  55. using (var layerSnap = layer.LayerBitmap.SkiaSurface.Snapshot())
  56. portion.SkiaSurface.Canvas.DrawImage(layerSnap, new SKRect(x, y, x + selLayer.Width, y + selLayer.Height), selLayerRect, Surface.ReplacingPaint);
  57. portion.SkiaSurface.Canvas.DrawImage(selSnap, 0, 0, Surface.MaskingPaint);
  58. output[count] = portion;
  59. count++;
  60. if (eraceFromLayers)
  61. {
  62. layer.LayerBitmap.SkiaSurface.Canvas.DrawImage(selSnap, new SKRect(0, 0, selLayer.Width, selLayer.Height),
  63. new SKRect(selLayer.OffsetX - layer.OffsetX, selLayer.OffsetY - layer.OffsetY, selLayer.OffsetX - layer.OffsetX + selLayer.Width, selLayer.OffsetY - layer.OffsetY + selLayer.Height),
  64. Surface.InverseMaskingPaint);
  65. layer.InvokeLayerBitmapChange(new Int32Rect(selLayer.OffsetX, selLayer.OffsetY, selLayer.Width, selLayer.Height));
  66. }
  67. }
  68. return output;
  69. }
  70. /// <summary>
  71. /// Generates simplified preview from Document, very fast, great for creating small previews. Creates uniform streched image.
  72. /// </summary>
  73. /// <param name="document">Document which be used to generate preview.</param>
  74. /// <param name="maxPreviewWidth">Max width of preview.</param>
  75. /// <param name="maxPreviewHeight">Max height of preview.</param>
  76. /// <returns>WriteableBitmap image.</returns>
  77. public static WriteableBitmap GeneratePreviewBitmap(Document document, int maxPreviewWidth, int maxPreviewHeight)
  78. {
  79. var opacityLayers = document.Layers.Where(x => x.IsVisible && x.Opacity > 0.8f);
  80. return GeneratePreviewBitmap(
  81. opacityLayers.Select(x => x.LayerBitmap),
  82. opacityLayers.Select(x => x.OffsetX),
  83. opacityLayers.Select(x => x.OffsetY),
  84. document.Width,
  85. document.Height,
  86. maxPreviewWidth,
  87. maxPreviewHeight);
  88. }
  89. public static WriteableBitmap GeneratePreviewBitmap(IEnumerable<Layer> layers, int width, int height, int maxPreviewWidth, int maxPreviewHeight)
  90. {
  91. var opacityLayers = layers.Where(x => x.IsVisible && x.Opacity > 0.8f);
  92. return GeneratePreviewBitmap(
  93. opacityLayers.Select(x => x.LayerBitmap),
  94. opacityLayers.Select(x => x.OffsetX),
  95. opacityLayers.Select(x => x.OffsetY),
  96. width,
  97. height,
  98. maxPreviewWidth,
  99. maxPreviewHeight);
  100. }
  101. public static WriteableBitmap GeneratePreviewBitmap(IEnumerable<SerializableLayer> layers, int width, int height, int maxPreviewWidth, int maxPreviewHeight)
  102. {
  103. var opacityLayers = layers.Where(x => x.IsVisible && x.Opacity > 0.8f
  104. && x.Height > 0 && x.Width > 0);
  105. return GeneratePreviewBitmap(
  106. opacityLayers.Select(x => new Surface(x.ToSKImage())),
  107. opacityLayers.Select(x => x.OffsetX),
  108. opacityLayers.Select(x => x.OffsetY),
  109. width,
  110. height,
  111. maxPreviewWidth,
  112. maxPreviewHeight);
  113. }
  114. public static SKColor BlendColors(SKColor bottomColor, SKColor topColor)
  115. {
  116. if ((topColor.Alpha < 255 && topColor.Alpha > 0))
  117. {
  118. byte r = (byte)((topColor.Red * topColor.Alpha / 255) + (bottomColor.Red * bottomColor.Alpha * (255 - topColor.Alpha) / (255 * 255)));
  119. byte g = (byte)((topColor.Green * topColor.Alpha / 255) + (bottomColor.Green * bottomColor.Alpha * (255 - topColor.Alpha) / (255 * 255)));
  120. byte b = (byte)((topColor.Blue * topColor.Alpha / 255) + (bottomColor.Blue * bottomColor.Alpha * (255 - topColor.Alpha) / (255 * 255)));
  121. byte a = (byte)(topColor.Alpha + (bottomColor.Alpha * (255 - topColor.Alpha) / 255));
  122. return new SKColor(r, g, b, a);
  123. }
  124. return topColor.Alpha == 255 ? topColor : bottomColor;
  125. }
  126. private static WriteableBitmap GeneratePreviewBitmap(
  127. IEnumerable<Surface> layerBitmaps,
  128. IEnumerable<int> offsetsX,
  129. IEnumerable<int> offsetsY,
  130. int width,
  131. int height,
  132. int maxPreviewWidth,
  133. int maxPreviewHeight)
  134. {
  135. int count = layerBitmaps.Count();
  136. if (count != offsetsX.Count() || count != offsetsY.Count())
  137. {
  138. throw new ArgumentException("There were not the same amount of bitmaps and offsets", nameof(layerBitmaps));
  139. }
  140. using Surface previewSurface = new Surface(width, height);
  141. var layerBitmapsEnumerator = layerBitmaps.GetEnumerator();
  142. var offsetsXEnumerator = offsetsX.GetEnumerator();
  143. var offsetsYEnumerator = offsetsY.GetEnumerator();
  144. while (layerBitmapsEnumerator.MoveNext())
  145. {
  146. offsetsXEnumerator.MoveNext();
  147. offsetsYEnumerator.MoveNext();
  148. var bitmap = layerBitmapsEnumerator.Current.SkiaSurface.Snapshot();
  149. var offsetX = offsetsXEnumerator.Current;
  150. var offsetY = offsetsYEnumerator.Current;
  151. previewSurface.SkiaSurface.Canvas.DrawImage(
  152. bitmap,
  153. offsetX, offsetY, Surface.BlendingPaint);
  154. }
  155. int newWidth = width >= height ? maxPreviewWidth : (int)Math.Ceiling(width / ((float)height / maxPreviewHeight));
  156. int newHeight = height > width ? maxPreviewHeight : (int)Math.Ceiling(height / ((float)width / maxPreviewWidth));
  157. return previewSurface.ResizeNearestNeighbor(newWidth, newHeight).ToWriteableBitmap();
  158. }
  159. }
  160. }