BitmapUtils.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. using PixiEditor.Models.DataHolders;
  2. using PixiEditor.Models.Layers;
  3. using PixiEditor.Models.Layers.Utils;
  4. using PixiEditor.Models.Position;
  5. using PixiEditor.Parser;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Linq;
  9. using System.Runtime.CompilerServices;
  10. using System.Windows;
  11. using System.Windows.Media;
  12. using System.Windows.Media.Imaging;
  13. namespace PixiEditor.Models.ImageManipulation
  14. {
  15. public static class BitmapUtils
  16. {
  17. /// <summary>
  18. /// Converts pixel bytes to WriteableBitmap.
  19. /// </summary>
  20. /// <param name="currentBitmapWidth">Width of bitmap.</param>
  21. /// <param name="currentBitmapHeight">Height of bitmap.</param>
  22. /// <param name="byteArray">Bitmap byte array.</param>
  23. /// <returns>WriteableBitmap.</returns>
  24. public static WriteableBitmap BytesToWriteableBitmap(int currentBitmapWidth, int currentBitmapHeight, byte[] byteArray)
  25. {
  26. WriteableBitmap bitmap = BitmapFactory.New(currentBitmapWidth, currentBitmapHeight);
  27. if (byteArray != null)
  28. {
  29. bitmap.FromByteArray(byteArray);
  30. }
  31. return bitmap;
  32. }
  33. /// <summary>
  34. /// Converts layers bitmaps into one bitmap.
  35. /// </summary>
  36. /// <param name="width">Width of final bitmap.</param>
  37. /// <param name="height">Height of final bitmap.</param>.
  38. /// <param name="layers">Layers to combine.</param>
  39. /// <returns>WriteableBitmap of layered bitmaps.</returns>
  40. public static WriteableBitmap CombineLayers(int width, int height, IEnumerable<Layer> layers, LayerStructure structure = null)
  41. {
  42. WriteableBitmap finalBitmap = BitmapFactory.New(width, height);
  43. using (finalBitmap.GetBitmapContext())
  44. {
  45. for (int i = 0; i < layers.Count(); i++)
  46. {
  47. Layer layer = layers.ElementAt(i);
  48. float layerOpacity = structure == null ? layer.Opacity : LayerStructureUtils.GetFinalLayerOpacity(layer, structure);
  49. if (layer.OffsetX < 0 || layer.OffsetY < 0 ||
  50. layer.Width + layer.OffsetX > layer.MaxWidth ||
  51. layer.Height + layer.OffsetY > layer.MaxHeight)
  52. {
  53. throw new InvalidOperationException("Layers must not extend beyond canvas borders");
  54. }
  55. for (int y = 0; y < layer.Height; y++)
  56. {
  57. for (int x = 0; x < layer.Width; x++)
  58. {
  59. Color previousColor = finalBitmap.GetPixel(x + layer.OffsetX, y + layer.OffsetY);
  60. Color color = layer.GetPixel(x, y);
  61. finalBitmap.SetPixel(x + layer.OffsetX, y + layer.OffsetY, BlendColor(previousColor, color, layerOpacity));
  62. }
  63. }
  64. }
  65. }
  66. return finalBitmap;
  67. }
  68. public static Color GetColorAtPointCombined(int x, int y, params Layer[] layers)
  69. {
  70. Color prevColor = Color.FromArgb(0, 0, 0, 0);
  71. for (int i = 0; i < layers.Length; i++)
  72. {
  73. Color color = layers[i].GetPixelWithOffset(x, y);
  74. float layerOpacity = layers[i].Opacity;
  75. prevColor = BlendColor(prevColor, color, layerOpacity);
  76. }
  77. return prevColor;
  78. }
  79. /// <summary>
  80. /// Generates simplified preview from Document, very fast, great for creating small previews. Creates uniform streched image.
  81. /// </summary>
  82. /// <param name="document">Document which be used to generate preview.</param>
  83. /// <param name="maxPreviewWidth">Max width of preview.</param>
  84. /// <param name="maxPreviewHeight">Max height of preview.</param>
  85. /// <returns>WriteableBitmap image.</returns>
  86. public static WriteableBitmap GeneratePreviewBitmap(Document document, int maxPreviewWidth, int maxPreviewHeight)
  87. {
  88. var opacityLayers = document.Layers.Where(x => x.IsVisible && x.Opacity > 0.8f);
  89. return GeneratePreviewBitmap(
  90. opacityLayers.Select(x => x.LayerBitmap),
  91. opacityLayers.Select(x => x.OffsetX),
  92. opacityLayers.Select(x => x.OffsetY),
  93. document.Width,
  94. document.Height,
  95. maxPreviewWidth,
  96. maxPreviewHeight);
  97. }
  98. public static WriteableBitmap GeneratePreviewBitmap(IEnumerable<Layer> layers, int width, int height, int maxPreviewWidth, int maxPreviewHeight)
  99. {
  100. var opacityLayers = layers.Where(x => x.IsVisible && x.Opacity > 0.8f);
  101. return GeneratePreviewBitmap(
  102. opacityLayers.Select(x => x.LayerBitmap),
  103. opacityLayers.Select(x => x.OffsetX),
  104. opacityLayers.Select(x => x.OffsetY),
  105. width,
  106. height,
  107. maxPreviewWidth,
  108. maxPreviewHeight);
  109. }
  110. public static WriteableBitmap GeneratePreviewBitmap(IEnumerable<SerializableLayer> layers, int width, int height, int maxPreviewWidth, int maxPreviewHeight)
  111. {
  112. var opacityLayers = layers.Where(x => x.IsVisible && x.Opacity > 0.8f);
  113. return GeneratePreviewBitmap(
  114. opacityLayers.Select(x => BytesToWriteableBitmap(x.Width, x.Height, x.BitmapBytes)),
  115. opacityLayers.Select(x => x.OffsetX),
  116. opacityLayers.Select(x => x.OffsetY),
  117. width,
  118. height,
  119. maxPreviewWidth,
  120. maxPreviewHeight);
  121. }
  122. public static Dictionary<Guid, Color[]> GetPixelsForSelection(Layer[] layers, Coordinates[] selection)
  123. {
  124. Dictionary<Guid, Color[]> result = new();
  125. foreach (Layer layer in layers)
  126. {
  127. Color[] pixels = new Color[selection.Length];
  128. using (layer.LayerBitmap.GetBitmapContext())
  129. {
  130. for (int j = 0; j < pixels.Length; j++)
  131. {
  132. Coordinates position = layer.GetRelativePosition(selection[j]);
  133. if (position.X < 0 || position.X > layer.Width - 1 || position.Y < 0 ||
  134. position.Y > layer.Height - 1)
  135. {
  136. continue;
  137. }
  138. pixels[j] = layer.GetPixel(position.X, position.Y);
  139. }
  140. }
  141. result[layer.LayerGuid] = pixels;
  142. }
  143. return result;
  144. }
  145. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  146. private static Color BlendColor(Color previousColor, Color color, float opacity)
  147. {
  148. if ((color.A < 255 && color.A > 0) || (opacity < 1f && opacity > 0 && color.A > 0))
  149. {
  150. byte pixelA = (byte)(color.A * opacity);
  151. byte r = (byte)((color.R * pixelA / 255) + (previousColor.R * previousColor.A * (255 - pixelA) / (255 * 255)));
  152. byte g = (byte)((color.G * pixelA / 255) + (previousColor.G * previousColor.A * (255 - pixelA) / (255 * 255)));
  153. byte b = (byte)((color.B * pixelA / 255) + (previousColor.B * previousColor.A * (255 - pixelA) / (255 * 255)));
  154. byte a = (byte)(pixelA + (previousColor.A * (255 - pixelA) / 255));
  155. color = Color.FromArgb(a, r, g, b);
  156. }
  157. else
  158. {
  159. color = Color.FromArgb(color.A, color.R, color.G, color.B);
  160. }
  161. if (color.A > 0)
  162. {
  163. return color;
  164. }
  165. return previousColor;
  166. }
  167. private static WriteableBitmap GeneratePreviewBitmap(
  168. IEnumerable<WriteableBitmap> layerBitmaps,
  169. IEnumerable<int> offsetsX,
  170. IEnumerable<int> offsetsY,
  171. int width,
  172. int height,
  173. int maxPreviewWidth,
  174. int maxPreviewHeight)
  175. {
  176. int count = layerBitmaps.Count();
  177. if (count != offsetsX.Count() || count != offsetsY.Count())
  178. {
  179. throw new ArgumentException("There were not the same amount of bitmaps and offsets", nameof(layerBitmaps));
  180. }
  181. WriteableBitmap previewBitmap = BitmapFactory.New(width, height);
  182. var layerBitmapsEnumerator = layerBitmaps.GetEnumerator();
  183. var offsetsXEnumerator = offsetsX.GetEnumerator();
  184. var offsetsYEnumerator = offsetsY.GetEnumerator();
  185. while (layerBitmapsEnumerator.MoveNext())
  186. {
  187. offsetsXEnumerator.MoveNext();
  188. offsetsYEnumerator.MoveNext();
  189. var bitmap = layerBitmapsEnumerator.Current;
  190. var offsetX = offsetsXEnumerator.Current;
  191. var offsetY = offsetsYEnumerator.Current;
  192. previewBitmap.Blit(
  193. new Rect(offsetX, offsetY, bitmap.Width, bitmap.Height),
  194. bitmap,
  195. new Rect(0, 0, bitmap.Width, bitmap.Height));
  196. }
  197. int newWidth = width >= height ? maxPreviewWidth : (int)Math.Ceiling(width / ((float)height / maxPreviewHeight));
  198. int newHeight = height > width ? maxPreviewHeight : (int)Math.Ceiling(height / ((float)width / maxPreviewWidth));
  199. return previewBitmap.Resize(newWidth, newHeight, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
  200. }
  201. }
  202. }