|
@@ -1,5 +1,10 @@
|
|
using System;
|
|
using System;
|
|
|
|
+using System.Collections.Generic;
|
|
|
|
+using System.Linq;
|
|
|
|
+using System.Windows;
|
|
|
|
+using System.Windows.Media;
|
|
using System.Windows.Media.Imaging;
|
|
using System.Windows.Media.Imaging;
|
|
|
|
+using PixiEditor.Models.Position;
|
|
using PixiEditor.Models.Tools;
|
|
using PixiEditor.Models.Tools;
|
|
|
|
|
|
namespace PixiEditor.Models.Layers
|
|
namespace PixiEditor.Models.Layers
|
|
@@ -57,6 +62,44 @@ namespace PixiEditor.Models.Layers
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ private int _offsetX;
|
|
|
|
+
|
|
|
|
+ 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");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private Thickness _offset;
|
|
|
|
+
|
|
|
|
+ public Thickness Offset
|
|
|
|
+ {
|
|
|
|
+ get => _offset;
|
|
|
|
+ set
|
|
|
|
+ {
|
|
|
|
+ _offset = value;
|
|
|
|
+ RaisePropertyChanged("Offset");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
private bool _isActive;
|
|
private bool _isActive;
|
|
|
|
|
|
private bool _isRenaming;
|
|
private bool _isRenaming;
|
|
@@ -65,6 +108,9 @@ namespace PixiEditor.Models.Layers
|
|
|
|
|
|
private string _name;
|
|
private string _name;
|
|
|
|
|
|
|
|
+ public int MaxWidth { get; set; } = int.MaxValue;
|
|
|
|
+ public int MaxHeight { get; set; } = int.MaxValue;
|
|
|
|
+
|
|
public Layer(string name, int width, int height)
|
|
public Layer(string name, int width, int height)
|
|
{
|
|
{
|
|
Name = name;
|
|
Name = name;
|
|
@@ -89,12 +135,13 @@ namespace PixiEditor.Models.Layers
|
|
public void ApplyPixels(BitmapPixelChanges pixels)
|
|
public void ApplyPixels(BitmapPixelChanges pixels)
|
|
{
|
|
{
|
|
if (pixels.ChangedPixels == null) return;
|
|
if (pixels.ChangedPixels == null) return;
|
|
|
|
+ DynamicResize(pixels);
|
|
|
|
+ pixels.ChangedPixels = ApplyOffset(pixels.ChangedPixels);
|
|
using (var ctx = LayerBitmap.GetBitmapContext())
|
|
using (var ctx = LayerBitmap.GetBitmapContext())
|
|
{
|
|
{
|
|
foreach (var coords in pixels.ChangedPixels)
|
|
foreach (var coords in pixels.ChangedPixels)
|
|
{
|
|
{
|
|
- if (coords.Key.X > Width - 1 || coords.Key.X < 0 || coords.Key.Y < 0 ||
|
|
|
|
- coords.Key.Y > Height - 1) continue;
|
|
|
|
|
|
+ if (coords.Key.X < 0 || coords.Key.Y < 0) continue;
|
|
ctx.WriteableBitmap.SetPixel(Math.Clamp(coords.Key.X, 0, Width - 1),
|
|
ctx.WriteableBitmap.SetPixel(Math.Clamp(coords.Key.X, 0, Width - 1),
|
|
Math.Clamp(coords.Key.Y, 0, Height - 1),
|
|
Math.Clamp(coords.Key.Y, 0, Height - 1),
|
|
coords.Value);
|
|
coords.Value);
|
|
@@ -102,6 +149,49 @@ namespace PixiEditor.Models.Layers
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ private Dictionary<Coordinates, Color> ApplyOffset(Dictionary<Coordinates, Color> changedPixels)
|
|
|
|
+ {
|
|
|
|
+ return changedPixels.ToDictionary(d => new Coordinates(d.Key.X - OffsetX, d.Key.Y - OffsetY),
|
|
|
|
+ d => d.Value);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Resizes canvas to fit pixels outside current bounds. Clamped to MaxHeight and MaxWidth
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="pixels"></param>
|
|
|
|
+ private void DynamicResize(BitmapPixelChanges pixels)
|
|
|
|
+ {
|
|
|
|
+ RecalculateOffset(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)
|
|
|
|
+ {
|
|
|
|
+ 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);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void RecalculateOffset(BitmapPixelChanges pixels)
|
|
|
|
+ {
|
|
|
|
+ if (Width == 0 || Height == 0)
|
|
|
|
+ {
|
|
|
|
+ OffsetX = pixels.ChangedPixels.Min(x => x.Key.X);
|
|
|
|
+ OffsetY = pixels.ChangedPixels.Min(x => x.Key.Y);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private Coordinates FindOffsetForNewSize(BitmapPixelChanges changes)
|
|
|
|
+ {
|
|
|
|
+ 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);
|
|
|
|
+ }
|
|
|
|
+
|
|
public void Clear()
|
|
public void Clear()
|
|
{
|
|
{
|
|
LayerBitmap.Clear();
|
|
LayerBitmap.Clear();
|
|
@@ -115,19 +205,30 @@ namespace PixiEditor.Models.Layers
|
|
return byteArray;
|
|
return byteArray;
|
|
}
|
|
}
|
|
|
|
|
|
- public byte[] ConvertBitmapToBytes(WriteableBitmap bitmap)
|
|
|
|
|
|
+ public void ResizeCanvas(int offsetX, int offsetY, int offsetXSrc, int offsetYSrc, int oldWidth, int oldHeight,
|
|
|
|
+ int newWidth, int newHeight)
|
|
{
|
|
{
|
|
- bitmap.Lock();
|
|
|
|
- byte[] byteArray = bitmap.ToByteArray();
|
|
|
|
- bitmap.Unlock();
|
|
|
|
- return byteArray;
|
|
|
|
- }
|
|
|
|
|
|
+ int sizeOfArgb = 4;
|
|
|
|
+ int iteratorHeight = oldHeight > newHeight ? newHeight : oldHeight;
|
|
|
|
+ int count = oldWidth > newWidth ? newWidth : oldWidth;
|
|
|
|
|
|
- public void Resize(int newWidth, int newHeight)
|
|
|
|
- {
|
|
|
|
- LayerBitmap.Resize(newWidth, newHeight, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
|
|
|
|
- Height = newHeight;
|
|
|
|
- Width = newWidth;
|
|
|
|
|
|
+ using (var srcContext = LayerBitmap.GetBitmapContext(ReadWriteMode.ReadOnly))
|
|
|
|
+ {
|
|
|
|
+ var result = BitmapFactory.New(newWidth, newHeight);
|
|
|
|
+ using (var destContext = result.GetBitmapContext())
|
|
|
|
+ {
|
|
|
|
+ for (int line = 0; line < iteratorHeight; line++)
|
|
|
|
+ {
|
|
|
|
+ var srcOff = ((offsetYSrc + line) * oldWidth + offsetXSrc) * sizeOfArgb;
|
|
|
|
+ var dstOff = ((offsetY + line) * newWidth + offsetX) * sizeOfArgb;
|
|
|
|
+ BitmapContext.BlockCopy(srcContext, srcOff, destContext, dstOff, count * sizeOfArgb);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ LayerBitmap = result;
|
|
|
|
+ Width = newWidth;
|
|
|
|
+ Height = newHeight;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|