|
@@ -62,31 +62,9 @@ namespace PixiEditor.Models.Layers
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private int _offsetX;
|
|
|
+ public int OffsetX => (int) Offset.Left;
|
|
|
|
|
|
- public int OffsetX
|
|
|
- {
|
|
|
- get => _offsetX;
|
|
|
- set
|
|
|
- {
|
|
|
- _offsetX = value;
|
|
|
- Offset = new Thickness(value,Offset.Top, 0, 0);
|
|
|
- RaisePropertyChanged("OffsetX");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private int _offsetY;
|
|
|
-
|
|
|
- public int OffsetY
|
|
|
- {
|
|
|
- get => _offsetY;
|
|
|
- set
|
|
|
- {
|
|
|
- _offsetY = value;
|
|
|
- Offset = new Thickness(Offset.Left, value,0,0);
|
|
|
- RaisePropertyChanged("OffsetY");
|
|
|
- }
|
|
|
- }
|
|
|
+ public int OffsetY => (int) Offset.Top;
|
|
|
|
|
|
private Thickness _offset;
|
|
|
|
|
@@ -107,6 +85,7 @@ namespace PixiEditor.Models.Layers
|
|
|
private WriteableBitmap _layerBitmap;
|
|
|
|
|
|
private string _name;
|
|
|
+ private bool _clipRequested;
|
|
|
|
|
|
public int MaxWidth { get; set; } = int.MaxValue;
|
|
|
public int MaxHeight { get; set; } = int.MaxValue;
|
|
@@ -128,25 +107,35 @@ namespace PixiEditor.Models.Layers
|
|
|
Height = (int) layerBitmap.Height;
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Returns pixel color by x and y coordinates relative to document using (x - OffsetX + 1) formula.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="x">Viewport relative X</param>
|
|
|
+ /// <param name="y">Viewport relative Y</param>
|
|
|
+ /// <returns>Color of a pixel</returns>
|
|
|
+ public Color GetPixelWithOffset(int x, int y)
|
|
|
+ {
|
|
|
+ return LayerBitmap.GetPixel(x - OffsetX + 1, y - OffsetY + 1);
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Applies pixels to layer
|
|
|
/// </summary>
|
|
|
/// <param name="pixels"></param>
|
|
|
public void ApplyPixels(BitmapPixelChanges pixels)
|
|
|
{
|
|
|
- if (pixels.ChangedPixels == null) return;
|
|
|
+ if (pixels.ChangedPixels == null || pixels.ChangedPixels.Count == 0) return;
|
|
|
DynamicResize(pixels);
|
|
|
pixels.ChangedPixels = ApplyOffset(pixels.ChangedPixels);
|
|
|
using (var ctx = LayerBitmap.GetBitmapContext())
|
|
|
{
|
|
|
foreach (var coords in pixels.ChangedPixels)
|
|
|
{
|
|
|
- if (coords.Key.X < 0 || coords.Key.Y < 0) continue;
|
|
|
- ctx.WriteableBitmap.SetPixel(Math.Clamp(coords.Key.X, 0, Width - 1),
|
|
|
- Math.Clamp(coords.Key.Y, 0, Height - 1),
|
|
|
- coords.Value);
|
|
|
+ if (OutOfBounds(coords.Key)) continue;
|
|
|
+ ctx.WriteableBitmap.SetPixel(coords.Key.X, coords.Key.Y, coords.Value);
|
|
|
}
|
|
|
}
|
|
|
+ ClipIfNecessary();
|
|
|
}
|
|
|
|
|
|
private Dictionary<Coordinates, Color> ApplyOffset(Dictionary<Coordinates, Color> changedPixels)
|
|
@@ -155,41 +144,125 @@ namespace PixiEditor.Models.Layers
|
|
|
d => d.Value);
|
|
|
}
|
|
|
|
|
|
+ public Coordinates[] ConvertToRelativeCoordinates(Coordinates[] nonRelativeCords)
|
|
|
+ {
|
|
|
+ Coordinates[] result = new Coordinates[nonRelativeCords.Length];
|
|
|
+ for (int i = 0; i < result.Length; i++)
|
|
|
+ {
|
|
|
+ result[i] = new Coordinates(nonRelativeCords[i].X - OffsetX, nonRelativeCords[i].Y - OffsetY);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
- /// Resizes canvas to fit pixels outside current bounds. Clamped to MaxHeight and MaxWidth
|
|
|
+ /// Resizes canvas to fit pixels outside current bounds. Clamped to MaxHeight and MaxWidth
|
|
|
/// </summary>
|
|
|
/// <param name="pixels"></param>
|
|
|
- private void DynamicResize(BitmapPixelChanges pixels)
|
|
|
+ public void DynamicResize(BitmapPixelChanges pixels)
|
|
|
{
|
|
|
- RecalculateOffset(pixels);
|
|
|
+ ResetOffset(pixels);
|
|
|
int newMaxX = pixels.ChangedPixels.Max(x => x.Key.X) - OffsetX;
|
|
|
int newMaxY = pixels.ChangedPixels.Max(x => x.Key.Y) - OffsetY;
|
|
|
- if (newMaxX + 1 > Width || newMaxY + 1 > Height)
|
|
|
+ int newMinX = pixels.ChangedPixels.Min(x => x.Key.X) - OffsetX;
|
|
|
+ int newMinY = pixels.ChangedPixels.Min(x => x.Key.Y) - OffsetY;
|
|
|
+
|
|
|
+ if (pixels.ChangedPixels.Any(x => x.Value.A != 0))
|
|
|
{
|
|
|
- newMaxX = Math.Clamp(Math.Max(newMaxX + 1, Width), 0, MaxWidth);
|
|
|
- newMaxY = Math.Clamp(Math.Max(newMaxY + 1, Height), 0, MaxHeight);
|
|
|
- ResizeCanvas(0, 0, 0, 0, Width, Height, newMaxX, newMaxY);
|
|
|
- RecalculateOffset(pixels);
|
|
|
- LayerBitmap.Clear(System.Windows.Media.Colors.Blue);
|
|
|
+ if (newMaxX + 1 > Width || newMaxY + 1 > Height)
|
|
|
+ {
|
|
|
+ IncreaseSizeToBottom(newMaxX, newMaxY);
|
|
|
+ }
|
|
|
+ else if (newMinX < 0 || newMinY < 0)
|
|
|
+ {
|
|
|
+ IncreaseSizeToTop(newMinX, newMinY);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(pixels.ChangedPixels.Any(x=> IsBorderPixel(x.Key) && x.Value.A == 0))
|
|
|
+ {
|
|
|
+ _clipRequested = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void RecalculateOffset(BitmapPixelChanges pixels)
|
|
|
+ private bool IsBorderPixel(Coordinates cords)
|
|
|
{
|
|
|
- if (Width == 0 || Height == 0)
|
|
|
+ return cords.X - OffsetX == 0 || cords.Y - OffsetY == 0 || cords.X - OffsetX == Width - 1 ||
|
|
|
+ cords.Y - OffsetY == Height - 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ private bool OutOfBounds(Coordinates cords)
|
|
|
+ {
|
|
|
+ return cords.X < 0 || cords.X > Width - 1 || cords.Y < 0 || cords.Y > Height - 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void ClipIfNecessary()
|
|
|
+ {
|
|
|
+ if (_clipRequested)
|
|
|
{
|
|
|
- OffsetX = pixels.ChangedPixels.Min(x => x.Key.X);
|
|
|
- OffsetY = pixels.ChangedPixels.Min(x => x.Key.Y);
|
|
|
+ ClipCanvas();
|
|
|
+ _clipRequested = false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private Coordinates FindOffsetForNewSize(BitmapPixelChanges changes)
|
|
|
+ public void ClipCanvas()
|
|
|
+ {
|
|
|
+ var points = GetEdgePoints();
|
|
|
+ int smallestX = points.Coords1.X;
|
|
|
+ int smallestY = points.Coords1.Y;
|
|
|
+ int biggestX = points.Coords2.X;
|
|
|
+ int biggestY = points.Coords2.Y;
|
|
|
+
|
|
|
+ if (smallestX < 0 && smallestY < 0 && biggestX < 0 && biggestY < 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ int width = biggestX - smallestX + 1;
|
|
|
+ int height = biggestY - smallestY + 1;
|
|
|
+ ResizeCanvas(0,0, smallestX, smallestY, width, height);
|
|
|
+ Offset = new Thickness(OffsetX + smallestX, OffsetY + smallestY, 0, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void IncreaseSizeToBottom(int newMaxX, int newMaxY)
|
|
|
+ {
|
|
|
+ newMaxX = Math.Clamp(Math.Max(newMaxX + 1, Width), 0, MaxWidth - OffsetX);
|
|
|
+ newMaxY = Math.Clamp(Math.Max(newMaxY + 1, Height), 0, MaxHeight - OffsetY);
|
|
|
+
|
|
|
+ ResizeCanvas(0, 0, 0, 0, newMaxX, newMaxY);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void IncreaseSizeToTop(int newMinX, int newMinY)
|
|
|
{
|
|
|
- int newMaxX = changes.ChangedPixels.Max(x => x.Key.X);
|
|
|
- int newMaxY = changes.ChangedPixels.Max(x => x.Key.Y);
|
|
|
- int newMinX = changes.ChangedPixels.Min(x => x.Key.X);
|
|
|
- int newMinY = changes.ChangedPixels.Min(x => x.Key.Y);
|
|
|
- return new Coordinates(0,0);
|
|
|
+ newMinX = Math.Clamp(Math.Min(newMinX, Width), -OffsetX, 0);
|
|
|
+ newMinY = Math.Clamp(Math.Min(newMinY, Height), -OffsetY, 0);
|
|
|
+
|
|
|
+ Offset = new Thickness(Math.Clamp(OffsetX + newMinX, 0, MaxWidth),
|
|
|
+ Math.Clamp(OffsetY + newMinY, 0, MaxHeight), 0, 0);
|
|
|
+
|
|
|
+ int newWidth = Math.Clamp(Width - newMinX, 0, MaxWidth);
|
|
|
+ int newHeight = Math.Clamp(Height - newMinY, 0, MaxHeight);
|
|
|
+
|
|
|
+ int offsetX = Math.Abs(newWidth - Width);
|
|
|
+ int offsetY = Math.Abs(newHeight - Height);
|
|
|
+
|
|
|
+ ResizeCanvas(offsetX, offsetY, 0, 0, newWidth, newHeight);
|
|
|
+ }
|
|
|
+
|
|
|
+ private DoubleCords GetEdgePoints()
|
|
|
+ {
|
|
|
+ Coordinates smallestPixel = CoordinatesCalculator.FindMinEdgeNonTransparentPixel(LayerBitmap);
|
|
|
+ Coordinates biggestPixel = CoordinatesCalculator.FindMostEdgeNonTransparentPixel(LayerBitmap);
|
|
|
+
|
|
|
+ return new DoubleCords(smallestPixel, biggestPixel);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void ResetOffset(BitmapPixelChanges pixels)
|
|
|
+ {
|
|
|
+ if (Width == 0 || Height == 0)
|
|
|
+ {
|
|
|
+ int offsetX = pixels.ChangedPixels.Min(x => x.Key.X);
|
|
|
+ int offsetY = pixels.ChangedPixels.Min(x => x.Key.Y);
|
|
|
+ Offset = new Thickness(offsetX, offsetY, 0,0);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
public void Clear()
|
|
@@ -205,12 +278,11 @@ namespace PixiEditor.Models.Layers
|
|
|
return byteArray;
|
|
|
}
|
|
|
|
|
|
- public void ResizeCanvas(int offsetX, int offsetY, int offsetXSrc, int offsetYSrc, int oldWidth, int oldHeight,
|
|
|
- int newWidth, int newHeight)
|
|
|
+ public void ResizeCanvas(int offsetX, int offsetY, int offsetXSrc, int offsetYSrc, int newWidth, int newHeight)
|
|
|
{
|
|
|
int sizeOfArgb = 4;
|
|
|
- int iteratorHeight = oldHeight > newHeight ? newHeight : oldHeight;
|
|
|
- int count = oldWidth > newWidth ? newWidth : oldWidth;
|
|
|
+ int iteratorHeight = Height > newHeight ? newHeight : Height;
|
|
|
+ int count = Width > newWidth ? newWidth : Width;
|
|
|
|
|
|
using (var srcContext = LayerBitmap.GetBitmapContext(ReadWriteMode.ReadOnly))
|
|
|
{
|
|
@@ -219,7 +291,7 @@ namespace PixiEditor.Models.Layers
|
|
|
{
|
|
|
for (int line = 0; line < iteratorHeight; line++)
|
|
|
{
|
|
|
- var srcOff = ((offsetYSrc + line) * oldWidth + offsetXSrc) * sizeOfArgb;
|
|
|
+ var srcOff = ((offsetYSrc + line) * Width + offsetXSrc) * sizeOfArgb;
|
|
|
var dstOff = ((offsetY + line) * newWidth + offsetX) * sizeOfArgb;
|
|
|
BitmapContext.BlockCopy(srcContext, srcOff, destContext, dstOff, count * sizeOfArgb);
|
|
|
}
|