diff --git a/README.md b/README.md index 45a238c..8c611fd 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ Have fun! - Phosphor in MIT License - Steve Schoger in PD License - Thewolfkit in CC Attribution License + - Uxaspects in Apache License + - Uxwb in GPL License - Wishforge.games in CC Attribution License - Yamatsum in MIT License - Zest in MIT License diff --git a/ZXBStudio/Dialogs/SplashScreen.axaml b/ZXBStudio/Dialogs/SplashScreen.axaml index 813d64a..e64bebd 100644 --- a/ZXBStudio/Dialogs/SplashScreen.axaml +++ b/ZXBStudio/Dialogs/SplashScreen.axaml @@ -13,7 +13,7 @@ - + diff --git a/ZXBStudio/Dialogs/SplashScreen.axaml.cs b/ZXBStudio/Dialogs/SplashScreen.axaml.cs index 5be7d11..0f58757 100644 --- a/ZXBStudio/Dialogs/SplashScreen.axaml.cs +++ b/ZXBStudio/Dialogs/SplashScreen.axaml.cs @@ -8,7 +8,7 @@ public partial class SplashScreen : Window public SplashScreen() { InitializeComponent(); - lblVersion.Content = $"v{Program.Version} | {Program.VersionDate}"; + lblVersion.Content=$"{Program.Version} | {Program.VersionDate}"; } } } diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/PaletteBuilderDialog.axaml b/ZXBStudio/DocumentEditors/ZXGraphics/PaletteBuilderDialog.axaml index 35e4920..2e60542 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/PaletteBuilderDialog.axaml +++ b/ZXBStudio/DocumentEditors/ZXGraphics/PaletteBuilderDialog.axaml @@ -47,7 +47,7 @@ Convert to current palette - + Append new colors to current palette @@ -78,7 +78,7 @@ - + @@ -95,10 +95,21 @@ - + + + + + + + + diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/PaletteBuilderDialog.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/PaletteBuilderDialog.axaml.cs index 123c6ed..251c34b 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/PaletteBuilderDialog.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/PaletteBuilderDialog.axaml.cs @@ -33,6 +33,7 @@ using Avalonia.Controls.Shapes; using static CommunityToolkit.Mvvm.ComponentModel.__Internals.__TaskExtensions.TaskAwaitableWithoutEndValidation; using FFmpeg.AutoGen; +using System.Diagnostics; namespace ZXBasicStudio.DocumentEditors.ZXGraphics { @@ -44,6 +45,7 @@ public partial class PaletteBuilderDialog : Window, IDisposable private Rectangle[] rectangulos = new Rectangle[256]; private PaletteColor[] palette = null; + private PaletteColor[] palette512 = null; private string sourceFile = null; private string convertedFile = null; private bool imgSourceLoaded = false; @@ -68,8 +70,9 @@ public PaletteBuilderDialog() // Set the palette palette = ServiceLayer.GetPalette(GraphicsModes.Next); - selectedColorIndex = 0; + selectedColorIndex = 0; DrawPalette(); + DrawColorPicker(); btnFileSource.Tapped += BtnFileSource_Tapped; btnResetPalette.Click += BtnResetPalette_Click; @@ -85,7 +88,10 @@ public PaletteBuilderDialog() btnRefresh.Click += BtnRefresh_Click; btnSaveImage.Click += BtnSaveImage_Click; + btnColorPicker.Click += BtnColorPicker_Click; + btnClose.Click += BtnClose_Click; + } @@ -893,5 +899,170 @@ private void CrearPaletaGPL9bits() } #endregion + + + #region Color Picker + + private int Circles = 8; //16; + private int Sectors = 32; + private double Radius = 200; + + private void BtnColorPicker_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) + { + grdColorPicker.IsVisible = true; + } + + + private void DrawColorPicker() + { + Create512Palette(); + + ColorWheelCanvas.Children.Clear(); + double ringWidth = Radius / Circles; + double angleStep = 360.0 / Sectors; + double cx = Radius; + double cy = Radius; + + for (int circle = 0; circle < Circles; circle++) + { + double r1 = ringWidth * circle; + double r2 = ringWidth * (circle + 1); + double value = (circle + 1) / (double)Circles; + + for (int sector = 0; sector < Sectors; sector++) + { + double hue = sector * angleStep; + Color color = FromHSV(hue, 1.0, value); + int idx=ServiceLayer.GetColor(color.R, color.G, color.B, palette512, 5); + var p = palette512[idx]; + var color512=Color.FromRgb(p.Red, p.Green, p.Blue); + DrawSegment(cx, cy, r1, r2, hue, angleStep, color512); + } + } + } + + private void DrawSegment(double cx, double cy, double r1, double r2, double startAngle, double angleSize, Color color) + { + var startRad = Math.PI * startAngle / 180.0; + var endRad = Math.PI * (startAngle + angleSize) / 180.0; + + var p1 = new Point(cx + r1 * Math.Cos(startRad), cy + r1 * Math.Sin(startRad)); + var p2 = new Point(cx + r2 * Math.Cos(startRad), cy + r2 * Math.Sin(startRad)); + var p3 = new Point(cx + r2 * Math.Cos(endRad), cy + r2 * Math.Sin(endRad)); + var p4 = new Point(cx + r1 * Math.Cos(endRad), cy + r1 * Math.Sin(endRad)); + + var path = new Avalonia.Controls.Shapes.Path + { + Fill = new SolidColorBrush(color), + StrokeThickness = 0, + Data = new PathGeometry + { + Figures = new PathFigures + { + new PathFigure + { + StartPoint = p1, + Segments = new PathSegments + { + new LineSegment { Point = p2 }, + new ArcSegment + { + Point = p3, + Size = new Size(r2, r2), + SweepDirection = SweepDirection.Clockwise, + IsLargeArc = angleSize > 180 + }, + new LineSegment { Point = p4 }, + new ArcSegment + { + Point = p1, + Size = new Size(r1, r1), + SweepDirection = SweepDirection.CounterClockwise, + IsLargeArc = angleSize > 180 + } + } + } + } + } + }; + + ColorWheelCanvas.Children.Add(path); + } + + private void ColorWheelCanvas_PointerPressed(object? sender, PointerPressedEventArgs e) + { + var pos = e.GetPosition(ColorWheelCanvas); + double dx = pos.X - Radius; + double dy = pos.Y - Radius; + double distance = Math.Sqrt(dx * dx + dy * dy); + if (distance > Radius) return; + + double angle = Math.Atan2(dy, dx) * 180 / Math.PI; + if (angle < 0) angle += 360; + + int sector = (int)(angle / (360.0 / Sectors)); + int circle = (int)(distance / (Radius / Circles)); + + double hue = sector * (360.0 / Sectors); + double value = (circle + 1) / (double)Circles; + Color color = FromHSV(hue, 1.0, value); + + SelectedColorPreview.Fill = new SolidColorBrush(color); + } + + private static Color FromHSV(double hue, double saturation, double value) + { + double c = value * saturation; + double x = c * (1 - Math.Abs((hue / 60.0 % 2) - 1)); + double m = value - c; + + double r = 0, g = 0, b = 0; + + if (hue < 60) { r = c; g = x; } + else if (hue < 120) { r = x; g = c; } + else if (hue < 180) { g = c; b = x; } + else if (hue < 240) { g = x; b = c; } + else if (hue < 300) { r = x; b = c; } + else { r = c; b = x; } + + byte R = (byte)((r + m) * 255); + byte G = (byte)((g + m) * 255); + byte B = (byte)((b + m) * 255); + + return Color.FromRgb(R, G, B); + } + + private void Create512Palette() + { + palette512 = new PaletteColor[512]; + for (int i = 0; i < 512; i++) + { + int r = ((i & 0b111000000) >> 6)*36; + int g = ((i & 0b000111000) >> 3)*36; + int b = (i & 0b000000111)*36; + if (r > 250) + { + r = 255; + } + if (g > 250) + { + g = 255; + } + if (b > 250) + { + b = 255; + } + + palette512[i] = new PaletteColor() + { + Red = (byte)r, + Green = (byte)g, + Blue = (byte)b, + HasPriority = false + }; + } + } + + #endregion } } diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml index 94da8c8..f38140e 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml +++ b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml @@ -27,10 +27,10 @@ - - @@ -103,14 +103,21 @@ - - + + diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml.cs index d3a2939..0168356 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml.cs @@ -245,7 +245,7 @@ public bool Initialize(string fileName) private void _Initialize(string fileName) { - _Modified = false; + _Modified = true; ServiceLayer.Initialize(); @@ -262,37 +262,7 @@ private void _Initialize(string fileName) Sprite[] sprites = dataS.Deserializar(); foreach (var sprite in sprites) - { - /* - // Check attributes for ZX Spectrum mode - if (sprite != null && sprite.GraphicMode == GraphicsModes.ZXSpectrum) - { - // Force ZX Spectrum palette - sprite.Palette = ServiceLayer.GetPalette(GraphicsModes.ZXSpectrum); - foreach (var pattern in sprite.Patterns) - { - int cW = sprite.Width / 8; - int cH = sprite.Height / 8; - int l = cW * cH; - if (pattern.Attributes == null) - { - pattern.Attributes = new AttributeColor[cW * cH]; - for (int n = 0; n < pattern.Attributes.Length; n++) - { - pattern.Attributes[n] = new AttributeColor() - { - Ink = 1, - Paper = 0 - }; - } - } - if (pattern.Attributes.Length != l) - { - pattern.Attributes = pattern.Attributes.Take(l).ToArray(); - } - } - } - */ + { // Check attributes for ZX Spectrum mode if (sprite != null && sprite.Patterns != null) { @@ -355,8 +325,11 @@ private void _Initialize(string fileName) btnUndo.Tapped += BtnUndo_Tapped; btnRedo.Tapped += BtnRedo_Tapped; + + btnViewAttributes.Tapped += BtnViewAttributes_Tapped; + btnColorPicker.Tapped += BtnColorPicker_Tapped; btnInvertColorsCell.Tapped += BtnInvertColorsCell_Tapped; - btnInvertPixelsCell.Tapped += BtnInvertPixelsCell_Tapped; + btnInvertPixelsCell.Tapped += BtnInvertPixelsCell_Tapped; Refresh(); @@ -739,6 +712,9 @@ private void Editor_Command(SpritePatternEditor sender, string command) ctrlProperties.PrimaryColor = sender.PrimaryColorIndex; ctrlProperties.SecondaryColor = sender.SecondaryColorIndex; ctrlPreview.SpriteData = sender.SpriteData; + + btnColorPicker.IsChecked = !ctrlEditor.ColorPicker; + UpdateColorPanel(); } } break; @@ -1048,27 +1024,49 @@ private void BtnImport_Tapped(object? sender, TappedEventArgs e) } + + private void BtnViewAttributes_Tapped(object? sender, TappedEventArgs e) + { + ctrlEditor.ViewAttributes = btnViewAttributes.IsChecked==true; + } + + private void BtnColorPicker_Tapped(object? sender, TappedEventArgs e) + { + ctrlEditor.ColorPicker = btnColorPicker.IsChecked == false; + } + + private void BtnInvertPixelsCell_Tapped(object? sender, TappedEventArgs e) { - + ctrlEditor.InvertPixelsCell = btnInvertPixelsCell.IsChecked == false; + if (ctrlEditor.InvertPixelsCell) + { + btnInvertColorsCell.IsChecked = true; + ctrlEditor.InvertColorsCell = false; + } } private void BtnInvertColorsCell_Tapped(object? sender, TappedEventArgs e) { - + ctrlEditor.InvertColorsCell = btnInvertColorsCell.IsChecked == false; + if (ctrlEditor.InvertColorsCell) + { + btnInvertPixelsCell.IsChecked = true; + ctrlEditor.InvertPixelsCell = false; + } } private void BtnRedo_Tapped(object? sender, TappedEventArgs e) { - + ctrlEditor.Redo(); } private void BtnUndo_Tapped(object? sender, TappedEventArgs e) { - + ctrlEditor.Undo(); } #endregion diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteImportDialog.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteImportDialog.axaml.cs index 3148690..47e7129 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteImportDialog.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteImportDialog.axaml.cs @@ -330,7 +330,7 @@ private void _UpdatePreview() var dirAttr = (cy * sw) + cx; var attr = pattern.Attributes[dirAttr]; attr.Bright = attr.Bright | (idxAttr > 7); - byte cCol = (byte)(idxAttr & 0b111); + byte cCol = (byte)(idxAttr /*& 0b111*/); if (paper == -1) { attr.Paper = cCol; @@ -406,9 +406,10 @@ private void _UpdatePreview() private int GetColor(byte r, byte g, byte b, PaletteColor[] palette) { - if (r > 0) + bool brigth = false; + if (r > 250 || g > 250 || b > 250) { - + brigth = true; } byte cr = GetColor_CutOff(r); byte cg = GetColor_CutOff(g); @@ -428,6 +429,10 @@ private int GetColor(byte r, byte g, byte b, PaletteColor[] palette) palColor.Green == p.Green && palColor.Blue == p.Blue) { + if (brigth && n<8) + { + return n + 8; + } return n; } } @@ -471,9 +476,9 @@ private byte GetColor_CutOff(byte c) private static double GetColorDistance(PaletteColor c1, PaletteColor c2) { - int rDiff = c1.Red - c2.Red; - int gDiff = c1.Green - c2.Green; - int bDiff = c1.Blue - c2.Blue; + int rDiff = Math.Abs(c1.Red - c2.Red); + int gDiff = Math.Abs(c1.Green - c2.Green); + int bDiff = Math.Abs(c1.Blue - c2.Blue); return Math.Sqrt(rDiff * rDiff + gDiff * gDiff + bDiff * bDiff); } diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SpritePatternEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/SpritePatternEditor.axaml.cs index dbc142c..1d1d99a 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/SpritePatternEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/SpritePatternEditor.axaml.cs @@ -10,6 +10,7 @@ using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; +using System.Threading.Tasks.Sources; using System.Xml.Schema; using ZXBasicStudio.Common; using ZXBasicStudio.DocumentEditors.ZXGraphics.log; @@ -46,6 +47,7 @@ public Sprite SpriteData lastId = _SpriteData.Id; } } + Undo_AddPoint(); Refresh(true); } } @@ -81,6 +83,28 @@ public int Zoom public bool Bright { get; set; } public bool Flash { get; set; } + public bool ViewAttributes + { + get + { + return _ViewAttributes; + } + set + { + _ViewAttributes = value; + aspect.ViewAttributes = value; + Refresh(false); + } + } + + private bool _ViewAttributes = true; + + public bool InvertPixelsCell { get; set; } = false; + + public bool InvertColorsCell { get; set; } = false; + + public bool ColorPicker { get; set; } = false; + #endregion @@ -124,6 +148,8 @@ public SpritePatternEditor() grdEditor.PointerPressed += GrdEditor_PointerPressed; grdEditor.PointerReleased += GrdEditor_PointerReleased; grdEditor.PointerExited += GrdEditor_PointerExited; + + aspect.ViewAttributes = true; } @@ -168,44 +194,100 @@ public void Refresh(bool callBack = false) #region Undo and Redo - private List operations = new List(); - private List operationsDeleted = new List(); + private List operations = new List(); + private int operationIndex = -1; public void Undo() { - // Get last operation - var op = operations.LastOrDefault(); + if (operationIndex > operations.Count - 1) + { + operationIndex = operations.Count - 1; + } + if (operationIndex < 0) + { + return; + } + var op = operations[operationIndex]; + if (Pattern_Equals(SpriteData.Patterns[SpriteData.CurrentFrame], op)) + { + operationIndex--; + Undo(); + return; + } if (op != null) { - // Move from operations to operationsDeleted - operations.Remove(op); - // Restore point value (Undo) - SetPoint(op.X, op.Y, op.ColorIndex); - // Remove new operation (Undo don't add operation) - var op2 = operations.LastOrDefault(); - if (op2 != null) - { - operations.Remove(op2); - operationsDeleted.Add(op2); - } + SpriteData.Patterns[SpriteData.CurrentFrame] = op; + Refresh(); } } public void Redo() { - // Get last undo operation - var op = operationsDeleted.LastOrDefault(); + if (operationIndex > operations.Count - 1) + { + return; + } + if (operationIndex < 0) + { + return; + } + var op = operations[operationIndex]; + if (Pattern_Equals(SpriteData.Patterns[SpriteData.CurrentFrame], op)) + { + operationIndex++; + Redo(); + return; + } if (op != null) { - // Delete from operationsDeleted - operationsDeleted.Remove(op); - // Set point value (Undo) - SetPoint(op.X, op.Y, op.ColorIndex); + SpriteData.Patterns[SpriteData.CurrentFrame] = op; + Refresh(); } } + private void Undo_AddPoint() + { + try + { + if (SpriteData == null || SpriteData.Patterns == null) + { + operations.Clear(); + return; + } + + if (operationIndex >= 0 && + operationIndex < operations.Count) + { + var lastOp = operations[operationIndex]; + if (Pattern_Equals(SpriteData.Patterns[SpriteData.CurrentFrame], lastOp)) + { + return; // No changes + } + } + operations = operations.Take(operationIndex+1).ToList(); + + var op = SpriteData.Patterns[SpriteData.CurrentFrame].Clonar(); + operations.Add(op); + operationIndex = operations.Count - 1; + } + catch (Exception ex) + { + Debug.WriteLine($"Error in Undo_AddPoint: {ex.Message}"); + } + } + + private bool Pattern_Equals(Pattern p1, Pattern p2) + { + if (p1 == null || p2 == null) + { + return false; + } + var pc1 = p1.Serializar(); + var pc2 = p2.Serializar(); + return pc1 == pc2; + } #endregion @@ -219,19 +301,43 @@ public void Redo() private void GrdEditor_PointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e) { var p = e.GetCurrentPoint(grdEditor); - if (p.Properties.IsLeftButtonPressed) + + if (ColorPicker) { - SetPoint(p.Position.X, p.Position.Y, PrimaryColorIndex); - MouseLeftPressed = true; - MouseRightPressed = false; + int x = (int)p.Position.X; + int y = (int)p.Position.Y; + x = x / (_Zoom + 1); + y = y / (_Zoom + 1); + var atr=GetAttribute(SpriteData.Patterns[SpriteData.CurrentFrame], x,y); + PrimaryColorIndex = atr.Ink; + SecondaryColorIndex = atr.Paper; + ColorPicker = false; + Refresh(true); } - else if (p.Properties.IsRightButtonPressed) + else if (InvertPixelsCell) { - SetPoint(p.Position.X, p.Position.Y, SecondaryColorIndex); - MouseLeftPressed = false; - MouseRightPressed = true; + GrdEditor_InvertPixelsCell(p.Position.X, p.Position.Y); } - } + else if (InvertColorsCell) + { + GrdEditor_InvertColorsCell(p.Position.X, p.Position.Y); + } + else + { + if (p.Properties.IsLeftButtonPressed) + { + SetPoint(p.Position.X, p.Position.Y, PrimaryColorIndex); + MouseLeftPressed = true; + MouseRightPressed = false; + } + else if (p.Properties.IsRightButtonPressed) + { + SetPoint(p.Position.X, p.Position.Y, SecondaryColorIndex); + MouseLeftPressed = false; + MouseRightPressed = true; + } + } + } /// @@ -302,13 +408,6 @@ private void SetPoint(double mx, double my, int value) } int dir = (SpriteData.Width * y) + x; - operations.Add(new Operation() - { - ColorIndex = SpriteData.Patterns[SpriteData.CurrentFrame].RawData[dir], - X = (int)mx, - Y = (int)my - }); - var sprite = SpriteData.Patterns[SpriteData.CurrentFrame]; switch (SpriteData.GraphicMode) @@ -331,6 +430,7 @@ private void SetPoint(double mx, double my, int value) break; } + Undo_AddPoint(); Refresh(false); if (tmr == null) @@ -409,6 +509,7 @@ public void Clear() { SpriteData.Patterns[SpriteData.CurrentFrame].RawData[n] = SecondaryColorIndex; } + Undo_AddPoint(); Refresh(true); } @@ -418,6 +519,7 @@ public void Clear() /// public void Cut() { + Undo_AddPoint(); Copy(); Clear(); } @@ -459,6 +561,7 @@ public async void Paste() else { SpriteData.Patterns[SpriteData.CurrentFrame].RawData = cbPatterns[0].RawData; + SpriteData.Patterns[SpriteData.CurrentFrame].Attributes = cbPatterns[0].Attributes; } } else @@ -514,6 +617,7 @@ public async void Paste() } } } + Undo_AddPoint(); Refresh(true); } @@ -536,6 +640,7 @@ public void HorizontalMirror() } } SpriteData.Patterns[SpriteData.CurrentFrame] = pat2; + Undo_AddPoint(); Refresh(true); } @@ -558,6 +663,7 @@ public void VerticalMirror() } } SpriteData.Patterns[SpriteData.CurrentFrame] = pat2; + Undo_AddPoint(); Refresh(true); } @@ -587,6 +693,7 @@ public void RotateLeft() } } SpriteData.Patterns[SpriteData.CurrentFrame] = pattern2; + Undo_AddPoint(); Refresh(true); } @@ -616,6 +723,7 @@ public void RotateRight() } } SpriteData.Patterns[SpriteData.CurrentFrame] = pattern2; + Undo_AddPoint(); Refresh(true); } @@ -644,6 +752,7 @@ public void ShiftUp() } } SpriteData.Patterns[SpriteData.CurrentFrame] = pattern2; + Undo_AddPoint(); Refresh(true); } @@ -672,6 +781,7 @@ public void ShiftRight() } } SpriteData.Patterns[SpriteData.CurrentFrame] = pattern2; + Undo_AddPoint(); Refresh(true); } @@ -700,6 +810,7 @@ public void ShiftDown() } } SpriteData.Patterns[SpriteData.CurrentFrame] = pattern2; + Undo_AddPoint(); Refresh(true); } @@ -728,6 +839,7 @@ public void ShiftLeft() } } SpriteData.Patterns[SpriteData.CurrentFrame] = pattern2; + Undo_AddPoint(); Refresh(true); } @@ -757,6 +869,7 @@ public void MoveUp() } } SpriteData.Patterns[SpriteData.CurrentFrame] = pattern2; + Undo_AddPoint(); Refresh(true); } @@ -786,6 +899,7 @@ public void MoveRight() } } SpriteData.Patterns[SpriteData.CurrentFrame] = pattern2; + Undo_AddPoint(); Refresh(true); } @@ -815,6 +929,7 @@ public void MoveDown() } } SpriteData.Patterns[SpriteData.CurrentFrame] = pattern2; + Undo_AddPoint(); Refresh(true); } @@ -844,6 +959,7 @@ public void MoveLeft() } } SpriteData.Patterns[SpriteData.CurrentFrame] = pattern2; + Undo_AddPoint(); Refresh(true); } @@ -879,6 +995,7 @@ public void Invert() } } SpriteData.Patterns[SpriteData.CurrentFrame] = pattern2; + Undo_AddPoint(); Refresh(true); } @@ -905,6 +1022,7 @@ public void Mask() _Mask(0, maxY, ref pattern, ref pattern2); _Mask(maxX, maxY, ref pattern, ref pattern2); SpriteData.Patterns[SpriteData.CurrentFrame] = pattern2; + Undo_AddPoint(); Refresh(true); } @@ -984,5 +1102,74 @@ private void SetPointValue(int x, int y, int colorIndex, ref Pattern pattern) } #endregion + + + #region Invert pixels and colors + + private void GrdEditor_InvertPixelsCell(double mx, double my) + { + if (SpriteData == null) + { + return; + } + + int x = (int)mx; + int y = (int)my; + + x = x / (_Zoom + 1); + y = y / (_Zoom + 1); + + x = x / 8; + y = y / 8; + + if (x < 0 || y < 0 || x >= SpriteData.Width || y >= SpriteData.Height) + { + return; + } + + for(int py = 0; py < 8; py++) + { + for (int px = 0; px < 8; px++) + { + int dir = ((y * 8 + py) * SpriteData.Width) + (x * 8 + px); + if (dir < SpriteData.Patterns[SpriteData.CurrentFrame].RawData.Length) + { + var value = SpriteData.Patterns[SpriteData.CurrentFrame].RawData[dir]; + if (value == PrimaryColorIndex) + { + SpriteData.Patterns[SpriteData.CurrentFrame].RawData[dir] = SecondaryColorIndex; + } + else if (value == SecondaryColorIndex) + { + SpriteData.Patterns[SpriteData.CurrentFrame].RawData[dir] = PrimaryColorIndex; + } + } + } + } + Refresh(); + } + + private void GrdEditor_InvertColorsCell(double mx, double my) + { + if (SpriteData == null) + { + return; + } + int x = (int)mx; + int y = (int)my; + x = x / (_Zoom + 1); + y = y / (_Zoom + 1); + + var inkBak = PrimaryColorIndex; + var paperBak = SecondaryColorIndex; + var attr=GetAttribute(SpriteData.Patterns[SpriteData.CurrentFrame], x, y); + PrimaryColorIndex = attr.Paper; + SecondaryColorIndex = attr.Ink; + SetAttribute(SpriteData.Patterns[SpriteData.CurrentFrame], x, y); + PrimaryColorIndex = inkBak; + SecondaryColorIndex=paperBak; + Refresh(); + } + #endregion } } diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/ZXGridImageView.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/ZXGridImageView.axaml.cs index 455cf3f..e063b0e 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/ZXGridImageView.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/ZXGridImageView.axaml.cs @@ -14,6 +14,19 @@ namespace ZXBasicStudio.DocumentEditors.ZXGraphics { public partial class ZXGridImageView : UserControl { + public bool ViewAttributes { + get + { + return _ViewAttributes; + } + set + { + _ViewAttributes = value; + InvalidateVisual(); + } + } + private bool _ViewAttributes = false; + private WriteableBitmap? gridImage; private IZXBitmap? backgroundImage; private SKColor gridColor = new SKColor(0x00, 0x00, 0x00, 0xFF); @@ -75,7 +88,9 @@ public ZXGridImageView() private void EnsureGrid() { if (backgroundImage == null) + { return; + } int w = (int)(backgroundImage.PixelSize.Width * Zoom + backgroundImage.PixelSize.Width + 1); int h = (int)(backgroundImage.PixelSize.Height * Zoom + backgroundImage.PixelSize.Height + 1); @@ -83,7 +98,9 @@ private void EnsureGrid() if (gridImage != null) { if (gridImage.PixelSize.Width == w && gridImage.PixelSize.Height == h) + { return; + } gridImage.Dispose(); diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/ZXSpriteImage.cs b/ZXBStudio/DocumentEditors/ZXGraphics/ZXSpriteImage.cs index 0f6a1e5..4b03213 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/ZXSpriteImage.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/ZXSpriteImage.cs @@ -17,7 +17,10 @@ public class ZXSpriteImage : IZXBitmap, IDisposable #endregion #region Public properties + public bool IsEmpty { get; private set; } + public bool ViewAttributes { get; set; } = true; + #endregion #region Constructors @@ -90,9 +93,27 @@ public unsafe void RenderSprite(Sprite Sprite, int FrameNumber) { var attr = GetAttribute(Sprite, frame, x, y); if (colorIndex == 0) - color = Sprite.Palette[attr.Paper]; + { + if (ViewAttributes) + { + color = Sprite.Palette[attr.Paper]; + } + else + { + color = Sprite.Palette[7]; + } + } else - color = Sprite.Palette[attr.Ink]; + { + if(ViewAttributes) + { + color = Sprite.Palette[attr.Ink]; + } + else + { + color = Sprite.Palette[0]; + } + } } break; case GraphicsModes.Monochrome: diff --git a/ZXBStudio/Program.cs b/ZXBStudio/Program.cs index 0a67f78..9231f67 100644 --- a/ZXBStudio/Program.cs +++ b/ZXBStudio/Program.cs @@ -9,8 +9,9 @@ namespace ZXBasicStudio internal class Program { public static string Version = "1.6.0 - beta 5"; - public static string VersionDate = "2025-10-05"; - + public static string VersionDate = "2025.10.05"; + + // Initialization code. Don't use any Avalonia, third-party APIs or any // SynchronizationContext-reliant code before AppMain is called: things aren't initialized // yet and stuff might break. diff --git a/ZXBStudio/Svg/hide.svg b/ZXBStudio/Svg/hide.svg new file mode 100644 index 0000000..60bcb77 --- /dev/null +++ b/ZXBStudio/Svg/hide.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/ZXBStudio/Svg/picker.svg b/ZXBStudio/Svg/picker.svg new file mode 100644 index 0000000..cf539f2 --- /dev/null +++ b/ZXBStudio/Svg/picker.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/ZXBStudio/Svg/select.svg b/ZXBStudio/Svg/select.svg new file mode 100644 index 0000000..c816e73 --- /dev/null +++ b/ZXBStudio/Svg/select.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ZXBStudio/ZXBasicStudio.csproj b/ZXBStudio/ZXBasicStudio.csproj index 2c38e46..baef0c0 100644 --- a/ZXBStudio/ZXBasicStudio.csproj +++ b/ZXBStudio/ZXBasicStudio.csproj @@ -170,6 +170,7 @@ + @@ -188,6 +189,7 @@ + @@ -195,6 +197,7 @@ + @@ -359,6 +362,9 @@ + + +