From 24d4a05d40b29773be342fdc36c9cbbd28158470 Mon Sep 17 00:00:00 2001 From: freezy Date: Sun, 11 Jun 2023 23:56:19 +0200 Subject: [PATCH] refactor: Use byte array instead of bit planes in colored frame. --- LibDmd.Test/Frame/FrameTests.cs | 3 +- .../RenderGraph/Gray6ConverterSourceTests.cs | 2 +- LibDmd.Test/TestBase.cs | 6 +- LibDmd/AlphaNumericFrame.cs | 117 +++++++++++- LibDmd/Common/FrameUtil.cs | 82 +++------ LibDmd/Converter/Pin2Color/Animation.cs | 2 +- .../Pin2Color/Pin2ColorGray2Colorizer.cs | 23 ++- .../Pin2Color/Pin2ColorGray4Colorizer.cs | 20 ++- LibDmd/Converter/Plugin/ColorizationPlugin.cs | 14 +- LibDmd/Converter/Serum/Serum.cs | 44 +---- LibDmd/Frame/ColoredFrame.cs | 170 +++++++++--------- LibDmd/Frame/DmdFrame.cs | 91 +++++++--- LibDmd/Input/FileSystem/ImageSource.cs | 6 +- LibDmd/Input/Network/WebsocketServer.cs | 36 ++-- .../PassthroughAlphaNumericSource.cs | 8 + .../Passthrough/PassthroughGray2Source.cs | 10 ++ .../Passthrough/PassthroughGray4Source.cs | 7 + LibDmd/Input/PinballFX/PinballFXGrabber.cs | 2 +- LibDmd/Output/Network/BrowserStream.cs | 4 +- LibDmd/Output/Network/NetworkStream.cs | 4 +- LibDmd/Output/Network/VpdbStream.cs | 18 +- LibDmd/Output/Network/WebsocketSerializer.cs | 8 +- LibDmd/Output/Pin2Dmd/Pin2Dmd.cs | 24 +-- LibDmd/Output/PinDmd1/PinDmd1.cs | 9 +- LibDmd/Output/PinDmd2/PinDmd2.cs | 9 +- LibDmd/Output/PinDmd3/PinDmd3.cs | 42 ++--- LibDmd/Output/PinUp/PinUpOutput.cs | 9 +- .../Virtual/Dmd/VirtualDmdControl.xaml.cs | 2 +- LibDmd/Output/ZeDMD/ZeDMD.cs | 83 ++++----- LibDmd/RenderGraph.cs | 4 +- 30 files changed, 458 insertions(+), 401 deletions(-) diff --git a/LibDmd.Test/Frame/FrameTests.cs b/LibDmd.Test/Frame/FrameTests.cs index 7dd3d72b..6ce7cff3 100644 --- a/LibDmd.Test/Frame/FrameTests.cs +++ b/LibDmd.Test/Frame/FrameTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Reactive.Linq; using System.Threading.Tasks; using FluentAssertions; diff --git a/LibDmd.Test/RenderGraph/Gray6ConverterSourceTests.cs b/LibDmd.Test/RenderGraph/Gray6ConverterSourceTests.cs index 7b3d22a7..351a992a 100644 --- a/LibDmd.Test/RenderGraph/Gray6ConverterSourceTests.cs +++ b/LibDmd.Test/RenderGraph/Gray6ConverterSourceTests.cs @@ -167,7 +167,7 @@ 00 00 00 00 00 00 00 00 00 00 00 00 22 22 22 22 22 22 22 22 22 22 22 22 00000000"); var coloredFrame = FrameGenerator.FromString(@" - 3F 2E 1D 0C 1B 2A 39 08 + 3F 2E 1D 0C 1B 2A 39 28 0A 0A 0A 0A 30 30 30 30 00 00 00 00 22 22 22 22 00 11 22 33 04 15 26 37", diff --git a/LibDmd.Test/TestBase.cs b/LibDmd.Test/TestBase.cs index fc66590d..6720e40d 100644 --- a/LibDmd.Test/TestBase.cs +++ b/LibDmd.Test/TestBase.cs @@ -104,10 +104,8 @@ protected static async Task AssertFrame(ITestSource source, ITes Print(expectedFrame, "Expected: "); - receivedFrame.Planes.Length.Should().Be(expectedFrame.Planes.Length); - for (var i = 0; i < receivedFrame.Planes.Length; i++) { - receivedFrame.Planes[i].Should().BeEquivalentTo(expectedFrame.Planes[i]); - } + receivedFrame.Data.Length.Should().Be(expectedFrame.Data.Length); + receivedFrame.Data.Should().BeEquivalentTo(expectedFrame.Data); receivedFrame.Palette.Should().BeEquivalentTo(expectedFrame.Palette); receivedFrame.Dimensions.Should().Be(expectedFrame.Dimensions); } diff --git a/LibDmd/AlphaNumericFrame.cs b/LibDmd/AlphaNumericFrame.cs index 43e78ff3..ec76c861 100644 --- a/LibDmd/AlphaNumericFrame.cs +++ b/LibDmd/AlphaNumericFrame.cs @@ -1,24 +1,32 @@ using System; +using System.Collections.Generic; using LibDmd.DmdDevice; namespace LibDmd { - public class AlphaNumericFrame : ICloneable + public class AlphaNumericFrame : ICloneable, IEqualityComparer { /// /// The segment data /// - public ushort[] SegmentData { get; } + public ushort[] SegmentData { get; private set; } /// /// The extended segment data /// - public ushort[] SegmentDataExtended { get; } + public ushort[] SegmentDataExtended { get; private set; } /// /// The segment type /// - public NumericalLayout SegmentLayout { get; } + public NumericalLayout SegmentLayout { get; private set; } + + public static bool operator == (AlphaNumericFrame x, AlphaNumericFrame y) => Equals(x, y); + public static bool operator != (AlphaNumericFrame x, AlphaNumericFrame y) => !Equals(x, y); + + public AlphaNumericFrame() + { + } public AlphaNumericFrame(NumericalLayout layout, ushort[] segData) { @@ -33,6 +41,107 @@ public AlphaNumericFrame(NumericalLayout layout, ushort[] segData, ushort[] segD SegmentLayout = layout; } + + public void Update(AlphaNumericFrame frame) + { + SegmentData = frame.SegmentData; + SegmentDataExtended = frame.SegmentDataExtended; + SegmentLayout = frame.SegmentLayout; + } + public object Clone() => new AlphaNumericFrame(SegmentLayout, SegmentData, SegmentDataExtended); + + #region Equality + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) { + return false; + } + + if (ReferenceEquals(this, obj)) { + return true; + } + + if (obj.GetType() != this.GetType()) { + return false; + } + + return Equals(this, (AlphaNumericFrame)obj); + } + + protected bool Equals(AlphaNumericFrame other) => Equals(this, other); + + public override int GetHashCode() + { + unchecked + { + var hashCode = (SegmentData != null ? SegmentData.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (SegmentDataExtended != null ? SegmentDataExtended.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (int)SegmentLayout; + return hashCode; + } + } + + public static bool Equals(AlphaNumericFrame x, AlphaNumericFrame y) + { + if (ReferenceEquals(x, y)) { + return true; + } + + if (ReferenceEquals(x, null)) { + return false; + } + + if (ReferenceEquals(y, null)) { + return false; + } + + if (x.GetType() != y.GetType()) { + return false; + } + + return Compare(x.SegmentData, y.SegmentData) + && Compare(x.SegmentDataExtended, y.SegmentDataExtended) + && x.SegmentLayout == y.SegmentLayout; + } + + bool IEqualityComparer.Equals(AlphaNumericFrame x, AlphaNumericFrame y) => Equals(x, y); + + private static bool Compare(IReadOnlyList a, IReadOnlyList b) + { + if (a == null && b == null) { + return true; + } + + if (a == null || b == null) { + return false; + } + + if (a.Count != b.Count) { + return false; + } + + for (var i = 0; i < a.Count; i++) { + if (a[i] != b[i]) { + return false; + } + } + + return true; + } + + public int GetHashCode(AlphaNumericFrame obj) + { + unchecked + { + var hashCode = (obj.SegmentData != null ? obj.SegmentData.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (obj.SegmentDataExtended != null ? obj.SegmentDataExtended.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (int)obj.SegmentLayout; + return hashCode; + } + } + + #endregion } } diff --git a/LibDmd/Common/FrameUtil.cs b/LibDmd/Common/FrameUtil.cs index 801251c1..41ff7ae6 100644 --- a/LibDmd/Common/FrameUtil.cs +++ b/LibDmd/Common/FrameUtil.cs @@ -1,35 +1,38 @@ using System; using System.Collections; -using System.Linq; +using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; using LibDmd.Frame; using NLog; -using Color = System.Windows.Media.Color; namespace LibDmd.Common { /// - /// Wärchziig zum hin- und härkonvertiärä vo Biud-Datä. + /// Tools for dealing with frame data. /// - public class FrameUtil + public static class FrameUtil { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); /// - /// Tuät es Biud i sini Bitahteiu uifteilä. + /// Splits a pixel array into separate bit planes. /// /// /// - /// Mr chas so gseh dass für äs Biud mit viar Graiteen zwe Ebänä fir - /// jedes Bit uisächemid + /// A bit plane is a byte array with the same dimensions as the original frame, + /// but since it's bits, a pixel can be either one or zero, so they are packed + /// into bytes. + /// + /// This makes it more efficient to transfer than one byte per pixel, where only + /// 2 or 4 bits are used. /// /// - /// Dimensionä vom Biud - /// Mit wefu Bits pro Pixu s Biud konstruiärt isch - /// D datä vom Biud - /// Bruich das bim zruggäh wenn definiärt. - /// Än Ebini fir jedes Bit + /// Frame dimensions + /// How many bits per pixel, i.e. how many bit planes + /// Frame data, from top left to bottom right + /// If set, write the bit planes into this. + /// Array of bit plans public static byte[][] Split(Dimensions dim, int bitlen, byte[] frame, byte[][] destPlanes = null) { using (Profiler.Start("FrameUtil.Split")) { @@ -89,16 +92,16 @@ public static byte[][] Split(Dimensions dim, int bitlen, byte[] frame, byte[][] } /// - /// Tuät mehreri Bit-Ebänä widr zämäfiägä. + /// Joins an array of bit planes back into one single byte array where one byte represents one pixel. /// - /// Dimensionä vom Biud - /// Ä Lischtä vo Ebänä zum zämäfiägä - /// Äs Graistuifäbiud mit sefu Bittiäfi wiä Ebänä gä wordä sind + /// Frame dimensions + /// Array of bit planes + /// Byte array from top left to bottom right public static byte[] Join(Dimensions dim, byte[][] bitPlanes) { using (Profiler.Start("FrameUtil.Join")) { - var frame = new byte[dim.Surface]; + var frame = new byte[dim.Surface]; if (bitPlanes.Length == 2) { unsafe { @@ -135,7 +138,7 @@ public static byte[] Join(Dimensions dim, byte[][] bitPlanes) { var pfEnd = pFrame + frame.Length; - System.Diagnostics.Debug.Assert(bitPlanes.Length == 4); + Debug.Assert(bitPlanes.Length == 4); fixed (byte* plane0 = &bitPlanes[0][0], plane1 = &bitPlanes[1][0], plane2 = &bitPlanes[2][0], plane3 = &bitPlanes[3][0]) { @@ -238,7 +241,7 @@ public static void SplitIntoRgbPlanes(char[] rgb565, int width, int numLogicalRo | (r1 & 1) << 2 | (g1 & 1) << 1 | (b1 & 1) << 0; - var indexWithinSubframe = mapAdafruitIndex(x, y, width, height, numLogicalRows); + var indexWithinSubframe = MapAdafruitIndex(x, y, width, height, numLogicalRows); var indexWithinOutput = subframe * subframeSize + indexWithinSubframe; dest[indexWithinOutput] = (byte)dotPair; r0 >>= 1; @@ -252,7 +255,7 @@ public static void SplitIntoRgbPlanes(char[] rgb565, int width, int numLogicalRo } } - private static int mapAdafruitIndex(int x, int y, int width, int height, int numLogicalRows) + private static int MapAdafruitIndex(int x, int y, int width, int height, int numLogicalRows) { var pairOffset = 16; var logicalRowLengthPerMatrix = 32 * 32 / 2 / numLogicalRows; @@ -269,24 +272,6 @@ private static int mapAdafruitIndex(int x, int y, int width, int height, int num return index; } - /// - /// Merges an array of bit planes into one single array. - /// - /// Source planes - /// Destination array - /// Where to start copying at destination - /// True if destination array changed, false otherwise. - public static bool Copy(byte[][] planes, byte[] frame, int offset) - { - var identical = true; - foreach (var plane in planes) { - identical = identical && CompareBuffers(plane, 0, frame, offset, plane.Length); - Buffer.BlockCopy(plane, 0, frame, offset, plane.Length); - offset += plane.Length; - } - return !identical; - } - /// /// Copies a byte array to another byte array. /// @@ -302,7 +287,6 @@ public static bool Copy(byte[] frame, byte[] dest, int offset) } //Scale planes by doubling the pixels in each byte - public static readonly ushort[] doublePixel = { 0x0000,0x0003,0x000C,0x000F,0x0030,0x0033,0x003C,0x003F,0x00C0,0x00C3,0x00CC,0x00CF,0x00F0,0x00F3,0x00FC,0x00FF, 0x0300,0x0303,0x030C,0x030F,0x0330,0x0333,0x033C,0x033F,0x03C0,0x03C3,0x03CC,0x03CF,0x03F0,0x03F3,0x03FC,0x03FF, @@ -410,7 +394,7 @@ public static byte[] ScaleDoubleRgb(Dimensions dim, byte[] frame) /// /// scaled frame planes [Obsolete("Use Scale2x which uses more obvious parameters.")] - public static byte[] Scale2xUgh(Dimensions dim, byte[] data) + public static byte[] Scale2xObsolete(Dimensions dim, byte[] data) { byte[] scaledData = new byte[dim.Surface]; @@ -542,10 +526,11 @@ public static byte[] Scale2XRgb(Dimensions dim, byte[] data) /// /// /// - public static byte[][] Scale2x(Dimensions dim, byte[][] data) + [Obsolete] + public static byte[][] Scale2xObsolete(Dimensions dim, byte[][] data) { var joinData = Join(dim, data); - var frameData = Scale2xUgh(dim, joinData); + var frameData = Scale2xObsolete(dim, joinData); return Split(dim, data.Length, frameData); } @@ -603,12 +588,6 @@ public static byte[] ConvertGrayToGray(byte[] srcFrame, params byte[] mapping) } } - public static DmdFrame ConvertToRgb24(Dimensions dim, byte[][] planes, Color[] palette) - { - var frame = Join(dim, planes); - return ColorUtil.ColorizeObsolete(dim, frame, palette); - } - public static byte[] NewPlane(Dimensions dim) { var count = dim.Width / 8 * dim.Height; @@ -661,7 +640,7 @@ public static void OrPlane(byte[] plane, byte[] target) public static byte[] CombinePlaneWithMask(byte[] planeA, byte[] planeB, byte[] mask) { var length = planeA.Length; - System.Diagnostics.Debug.Assert(length == planeB.Length && length == mask.Length); + Debug.Assert(length == planeB.Length && length == mask.Length); byte[] outPlane = new byte[length]; unchecked @@ -881,11 +860,6 @@ public static unsafe bool CompareBuffers(byte[] buffer1, int offset1, byte[] buf } } - public static bool CompareBuffersSlow(byte[] a, byte[] b) - { - return a != null && CompareBuffers(a, 0, b, 0, a.Length); - } - /// /// Fast byte array comparison, courtesy to https://stackoverflow.com/questions/43289/comparing-two-byte-arrays-in-net/8808245#8808245 /// diff --git a/LibDmd/Converter/Pin2Color/Animation.cs b/LibDmd/Converter/Pin2Color/Animation.cs index 99a5addc..f7aeb7f0 100644 --- a/LibDmd/Converter/Pin2Color/Animation.cs +++ b/LibDmd/Converter/Pin2Color/Animation.cs @@ -194,7 +194,7 @@ private byte[][] RenderLCM(byte[][] vpmFrame) } else { - vpmFrame = FrameUtil.Scale2x(Size, vpmFrame); + vpmFrame = FrameUtil.Scale2xObsolete(Size, vpmFrame); } } diff --git a/LibDmd/Converter/Pin2Color/Pin2ColorGray2Colorizer.cs b/LibDmd/Converter/Pin2Color/Pin2ColorGray2Colorizer.cs index 5f60edcd..5d465207 100644 --- a/LibDmd/Converter/Pin2Color/Pin2ColorGray2Colorizer.cs +++ b/LibDmd/Converter/Pin2Color/Pin2ColorGray2Colorizer.cs @@ -280,24 +280,35 @@ private void Render(Dimensions dim, byte[][] planes) { // Scale2 Algorithm (http://www.scale2x.it/algorithm) var colorData = FrameUtil.Join(dim / 2, planes); - var scaledData = FrameUtil.Scale2xUgh(dim, colorData); + var scaledData = FrameUtil.Scale2xObsolete(dim, colorData); planes = FrameUtil.Split(dim, planes.Length, scaledData); } } // Wenns kä Erwiiterig gä hett, de gäbemer eifach d Planes mit dr Palettä zrugg if (planes.Length == 2) { - ColoredGray2AnimationFrames.OnNext(new ColoredFrame(dim, planes, ColorUtil.GetPalette(_palette.GetColors((int)(Math.Log(_palette.Colors.Length) / Math.Log(2))), (int)Math.Pow(2, planes.Length)), _paletteIndex)); + ColoredGray2AnimationFrames.OnNext(new ColoredFrame( + dim, + FrameUtil.Join(dim, planes), + ColorUtil.GetPalette(_palette.GetColors((int)(Math.Log(_palette.Colors.Length) / Math.Log(2))), (int)Math.Pow(2, planes.Length)), + _paletteIndex)); } // Faus scho, de schickermr s Frame uifd entsprächendi Uisgab faus diä gsetzt isch if (planes.Length == 4) { - ColoredGray4AnimationFrames.OnNext(new ColoredFrame(dim, planes, ColorUtil.GetPalette(_palette.GetColors((int)(Math.Log(_palette.Colors.Length) / Math.Log(2))), (int)Math.Pow(2, planes.Length)), _paletteIndex)); + ColoredGray4AnimationFrames.OnNext(new ColoredFrame( + dim, + FrameUtil.Join(dim, planes), + ColorUtil.GetPalette(_palette.GetColors((int)(Math.Log(_palette.Colors.Length) / Math.Log(2))), (int)Math.Pow(2, planes.Length)), + _paletteIndex)); } - if (planes.Length == 6) - { - ColoredGray6AnimationFrames.OnNext(new ColoredFrame(dim, planes, ColorUtil.GetPalette(_palette.GetColors((int)(Math.Log(_palette.Colors.Length) / Math.Log(2))), (int)Math.Pow(2, planes.Length)), _paletteIndex)); + if (planes.Length == 6) { + ColoredGray6AnimationFrames.OnNext(new ColoredFrame( + dim, + FrameUtil.Join(dim, planes), + ColorUtil.GetPalette(_palette.GetColors((int)(Math.Log(_palette.Colors.Length) / Math.Log(2))), (int)Math.Pow(2, planes.Length)), + _paletteIndex)); } } diff --git a/LibDmd/Converter/Pin2Color/Pin2ColorGray4Colorizer.cs b/LibDmd/Converter/Pin2Color/Pin2ColorGray4Colorizer.cs index 01d10189..2056e8b5 100644 --- a/LibDmd/Converter/Pin2Color/Pin2ColorGray4Colorizer.cs +++ b/LibDmd/Converter/Pin2Color/Pin2ColorGray4Colorizer.cs @@ -319,7 +319,7 @@ private void Render(Dimensions dim, byte[][] planes) { // Scale2 Algorithm (http://www.scale2x.it/algorithm) var colorData = FrameUtil.Join(dim / 2, planes); - var scaledData = FrameUtil.Scale2xUgh(dim, colorData); + var scaledData = FrameUtil.Scale2xObsolete(dim, colorData); planes = FrameUtil.Split(dim, planes.Length, scaledData); } } @@ -327,18 +327,30 @@ private void Render(Dimensions dim, byte[][] planes) // Wenns kä Erwiiterig gä hett, de gäbemer eifach d Planes mit dr Palettä zrugg if (planes.Length == 2) { - ColoredGray2AnimationFrames.OnNext(new ColoredFrame(dim, planes, ColorUtil.GetPalette(_palette.GetColors((int)(Math.Log(_palette.Colors.Length) / Math.Log(2))), (int)Math.Pow(2, planes.Length)), _paletteIndex)); + ColoredGray2AnimationFrames.OnNext(new ColoredFrame( + dim, + FrameUtil.Join(dim, planes), + ColorUtil.GetPalette(_palette.GetColors((int)(Math.Log(_palette.Colors.Length) / Math.Log(2))), (int)Math.Pow(2, planes.Length)), + _paletteIndex)); } // Faus scho, de schickermr s Frame uifd entsprächendi Uisgab faus diä gsetzt isch if (planes.Length == 4) { - ColoredGray4AnimationFrames.OnNext(new ColoredFrame(dim, planes, ColorUtil.GetPalette(_palette.GetColors((int)(Math.Log(_palette.Colors.Length)/Math.Log(2))), (int)Math.Pow(2, planes.Length)), _paletteIndex)); + ColoredGray4AnimationFrames.OnNext(new ColoredFrame( + dim, + FrameUtil.Join(dim, planes), + ColorUtil.GetPalette(_palette.GetColors((int)(Math.Log(_palette.Colors.Length)/Math.Log(2))), (int)Math.Pow(2, planes.Length)), + _paletteIndex)); } if (planes.Length == 6) { - ColoredGray6AnimationFrames.OnNext(new ColoredFrame(dim, planes, ColorUtil.GetPalette(_palette.GetColors((int)(Math.Log(_palette.Colors.Length) / Math.Log(2))), (int)Math.Pow(2, planes.Length)), _paletteIndex)); + ColoredGray6AnimationFrames.OnNext(new ColoredFrame( + dim, + FrameUtil.Join(dim, planes), + ColorUtil.GetPalette(_palette.GetColors((int)(Math.Log(_palette.Colors.Length) / Math.Log(2))), (int)Math.Pow(2, planes.Length)), + _paletteIndex)); } } diff --git a/LibDmd/Converter/Plugin/ColorizationPlugin.cs b/LibDmd/Converter/Plugin/ColorizationPlugin.cs index b86eff24..77e80068 100644 --- a/LibDmd/Converter/Plugin/ColorizationPlugin.cs +++ b/LibDmd/Converter/Plugin/ColorizationPlugin.cs @@ -205,7 +205,7 @@ public void SetPinUpOutput(PinUpOutput puo) #region Conversion - private byte[] _frame; + private byte[] _frameData; private readonly Dictionary _colorIndex = new Dictionary(); private readonly Color[] _palette = new Color[64]; @@ -294,8 +294,8 @@ private void EmitFrame(Dimensions dim, IntPtr rgb24FramePtr) var rgb24Frame = new byte[frameSize]; Marshal.Copy(rgb24FramePtr, rgb24Frame, 0, frameSize); - if (_frame == null || _frame.Length != dim.Surface) { - _frame = new byte[dim.Surface]; + if (_frameData == null || _frameData.Length != dim.Surface) { + _frameData = new byte[dim.Surface]; } _colorIndex.Clear(); for (var k = 0; k < 64; k++) { @@ -320,18 +320,18 @@ private void EmitFrame(Dimensions dim, IntPtr rgb24FramePtr) index = _colorIndex[color]; } - _frame[j++] = (byte)index; + _frameData[j++] = (byte)index; } // split and send if (lastIndex < 4) { - _coloredGray2Frames.OnNext(new ColoredFrame(dim, FrameUtil.Split(dim, 2, _frame), _palette.Take(4).ToArray())); + _coloredGray2Frames.OnNext(new ColoredFrame(dim, _frameData, _palette.Take(4).ToArray())); } else if (lastIndex < 16) { - _coloredGray4Frames.OnNext(new ColoredFrame(dim, FrameUtil.Split(dim, 4, _frame), _palette.Take(16).ToArray())); + _coloredGray4Frames.OnNext(new ColoredFrame(dim, _frameData, _palette.Take(16).ToArray())); } else if (lastIndex < 64) { - _coloredGray6Frames.OnNext(new ColoredFrame(dim, FrameUtil.Split(dim, 6, _frame), _palette)); + _coloredGray6Frames.OnNext(new ColoredFrame(dim, _frameData, _palette)); } else { _rgb24Frames.OnNext(new DmdFrame(dim, rgb24Frame, 24)); diff --git a/LibDmd/Converter/Serum/Serum.cs b/LibDmd/Converter/Serum/Serum.cs index b6614be9..f099c93a 100644 --- a/LibDmd/Converter/Serum/Serum.cs +++ b/LibDmd/Converter/Serum/Serum.cs @@ -40,7 +40,6 @@ public class Serum : AbstractConverter, IColoredGray6Source private readonly Color[] _colorPalette = new Color[64]; private readonly byte[] _bytePalette = new byte[64 * 3]; private readonly DmdFrame _frame; - private readonly byte[][] _planes; private readonly byte[] _rotations; private readonly Subject _coloredGray6AnimationFrames = new Subject(); @@ -79,11 +78,6 @@ public Serum(string altcolorPath, string romName) NumTriggersAvailable = numTriggers; IsLoaded = true; _frame = new DmdFrame(_dimensions, ((int)NumColors).GetBitLength()); - - _planes = new byte[6][]; - for (uint ti = 0; ti < 6; ti++) { - _planes[ti] = new byte[_dimensions.Surface / 8]; - } _rotations = new byte[MAX_COLOR_ROTATIONS * 3]; } @@ -133,11 +127,8 @@ public override void Convert(DmdFrame frame) _activePupOutput.SendTriggerId((ushort)triggerId); } - // convert to planes - FrameUtil.Split(frame.Dimensions, _planes.Length, _frame.Data, _planes); - // send the colored frame - _coloredGray6AnimationFrames.OnNext(new ColoredFrame(_dimensions, _planes, ConvertPalette(), _rotations)); + _coloredGray6AnimationFrames.OnNext(new ColoredFrame(_dimensions, _frame.Data, ConvertPalette(), _rotations)); } public static string GetVersion() @@ -147,39 +138,6 @@ public static string GetVersion() return str; } - private byte[][] ConvertToPlanes(byte colorBitDepth) - { - byte bitMask = 1; - var tj = 0; - for (var tk = 0; tk < colorBitDepth; tk++) { - _planes[tk][tj] = 0; - } - - var len = _dimensions.Surface; - for (var ti = 0; ti < len; ti++) { - byte tl = 1; - for (var tk = 0; tk < colorBitDepth; tk++) { - if ((_frame.Data[ti] & tl) > 0) { - _planes[tk][tj] |= bitMask; - } - tl <<= 1; - } - if (bitMask == 0x80) { - bitMask = 1; - tj++; - if (tj < len / 8) { - for (var tk = 0; tk < colorBitDepth; tk++) { - _planes[tk][tj] = 0; - } - } - } else { - bitMask <<= 1; - } - } - - return _planes; - } - private Color[] ConvertPalette() { for (int ti = 0; ti < 64; ti++) { diff --git a/LibDmd/Frame/ColoredFrame.cs b/LibDmd/Frame/ColoredFrame.cs index 69ed8a80..c94f776f 100644 --- a/LibDmd/Frame/ColoredFrame.cs +++ b/LibDmd/Frame/ColoredFrame.cs @@ -10,25 +10,21 @@ namespace LibDmd { - public class ColoredFrame : BaseFrame, ICloneable, IEqualityComparer + public class ColoredFrame : DmdFrame, ICloneable, IEqualityComparer { - /// - /// Frame data, split into bit planes - /// - public byte[][] Planes { get; private set; } - /// /// Color palette /// public Color[] Palette { get; private set; } /// - /// Palette index from animation or -1 if not set. + /// Palette index from animation or -1 if not set. This references a palette + /// that was already loaded onto a device. /// public int PaletteIndex { get; private set; } /// - /// Colour Rotation descriptions. + /// Color Rotation descriptions. /// /// /// Size: 8*3 bytes: 8 colour rotations available per frame, 1 byte for the first colour, @@ -37,14 +33,10 @@ public class ColoredFrame : BaseFrame, ICloneable, IEqualityComparer - /// If set, colors defined in + /// If set, colors defined in are rotated. /// public bool RotateColors; - public int BitLength => Planes.Length; - - private byte[] Data => FrameUtil.Join(Dimensions, Planes); - public static bool operator == (ColoredFrame x, ColoredFrame y) => Equals(x, y); public static bool operator != (ColoredFrame x, ColoredFrame y) => !Equals(x, y); @@ -54,37 +46,42 @@ public ColoredFrame() { } - public ColoredFrame(Dimensions dim, byte[][] planes, Color[] palette, int paletteIndex = -1, byte[] rotations = null) + public ColoredFrame(Dimensions dim, byte[] data, Color[] palette, int paletteIndex = -1, byte[] rotations = null) { Dimensions = dim; - Planes = planes; + Data = data; + BitLength = palette.Length.GetBitLength(); Palette = palette; PaletteIndex = paletteIndex; Rotations = rotations; RotateColors = rotations != null && rotations.Length > 0; #if DEBUG - if (planes.Length != palette.Length.GetBitLength()) { - throw new ArgumentException($"Number of planes must match palette size ({planes.Length} != {palette.Length.GetBitLength()})"); - } + AssertData(); #endif } - public ColoredFrame(Dimensions dim, byte[][] planes, Color[] palette, byte[] rotations) - : this(dim, planes, palette, -1, rotations) { } + public ColoredFrame(Dimensions dim, byte[] data, Color[] palette, byte[] rotations) + : this(dim, data, palette, -1, rotations) { } public ColoredFrame(DmdFrame frame, Color color) { Dimensions = frame.Dimensions; - Planes = FrameUtil.Split(Dimensions, frame.BitLength, frame.Data); + Data = frame.Data; + BitLength = frame.BitLength; Palette = ColorUtil.GetPalette(new[] { Colors.Black, color }, frame.NumColors); RotateColors = false; + + #if DEBUG + AssertData(); + #endif } public ColoredFrame(DmdFrame frame, params Color[] palette) { Dimensions = frame.Dimensions; - Planes = FrameUtil.Split(frame.Dimensions, frame.BitLength, frame.Data); + Data = frame.Data; + BitLength = frame.BitLength; Palette = palette; RotateColors = false; } @@ -93,35 +90,26 @@ public ColoredFrame(DmdFrame frame, params Color[] palette) #region Updates - public ColoredFrame Update(byte[][] planes, Color[] palette) + public ColoredFrame Update(byte[] data, Color[] palette) { - Planes = planes; + Data = data; + BitLength = palette.Length.GetBitLength(); Palette = palette; - - #if DEBUG - if (planes.Length != Palette.Length.GetBitLength()) { - throw new ArgumentException("Number of planes must match palette size"); - } - #endif return this; } - private ColoredFrame Update(byte[][] planes) + private ColoredFrame Update(byte[] data) { - Planes = planes; - - #if DEBUG - if (planes.Length != Palette.Length.GetBitLength()) { - throw new ArgumentException("Number of planes must match palette size"); - } - #endif + Data = data; return this; } + [Obsolete("Use byte data updates")] private ColoredFrame Update(Dimensions dim, byte[][] planes) { Dimensions = dim; - Planes = planes; + Data = FrameUtil.Join(Dimensions, planes); + BitLength = planes.Length; #if DEBUG if (planes.Length != Palette.Length.GetBitLength()) { @@ -134,7 +122,8 @@ private ColoredFrame Update(Dimensions dim, byte[][] planes) public void Update(ColoredFrame frame) { Dimensions = frame.Dimensions; - Planes = frame.Planes; + Data = frame.Data; + BitLength = frame.BitLength; Palette = frame.Palette; PaletteIndex = frame.PaletteIndex; Rotations = frame.Rotations; @@ -151,13 +140,12 @@ public void Update(ColoredFrame frame) /// New DMD frame public DmdFrame ConvertToGray() { - return new DmdFrame(Dimensions, FrameUtil.Join(Dimensions, Planes), Planes.Length); + return new DmdFrame(Dimensions, Data, BitLength); } public DmdFrame ConvertToGray(params byte[] mapping) { - var data = FrameUtil.Join(Dimensions, Planes); - return new DmdFrame(Dimensions, FrameUtil.ConvertGrayToGray(data, mapping), mapping.Length.GetBitLength()); + return new DmdFrame(Dimensions, FrameUtil.ConvertGrayToGray(Data, mapping), mapping.Length.GetBitLength()); } /// @@ -170,11 +158,11 @@ public DmdFrame ConvertToGray(params byte[] mapping) /// Converts this colored frame to a RGB24 frame. /// /// Converted RGB24 frame - public DmdFrame ConvertToRgb24() => new DmdFrame(Dimensions, ColorUtil.ColorizeRgb24(Dimensions, FrameUtil.Join(Dimensions, Planes), Palette), 24); + public DmdFrame ConvertToRgb24() => new DmdFrame(Dimensions, ColorUtil.ColorizeRgb24(Dimensions, Data, Palette), 24); private BitmapSource ConvertToBitmapWithColors() { - var rgb24 = ColorUtil.ColorizeRgb24(Dimensions, FrameUtil.Join(Dimensions, Planes), Palette); + var rgb24 = ColorUtil.ColorizeRgb24(Dimensions, Data, Palette); return ImageUtil.ConvertFromRgb24(Dimensions, rgb24); } @@ -231,20 +219,22 @@ public ColoredFrame Transform(RenderGraph renderGraph, IFixedSizeDestination fix // for dynamic or equal target dimensions, just flip if (targetDim == Dimensions.Dynamic || targetDim == Dimensions) { - return Update(TransformationUtil.Flip(Dimensions, Planes, renderGraph.FlipHorizontally, renderGraph.FlipVertically)); + return Update(TransformationUtil.Flip(Dimensions, BytesPerPixel, Data, renderGraph.FlipHorizontally, renderGraph.FlipVertically)); } - // if we need to scale down by factor 2, do it here more efficiently - if (Dimensions.IsDoubleSizeOf(targetDim) && !renderGraph.FlipHorizontally && !renderGraph.FlipVertically) { - return Update(targetDim, FrameUtil.ScaleDown(targetDim, Planes)); - } + // // if we need to scale down by factor 2, do it here more efficiently + // if (Dimensions.IsDoubleSizeOf(targetDim) && !renderGraph.FlipHorizontally && !renderGraph.FlipVertically) { + // return Update(targetDim, FrameUtil.ScaleDown(targetDim, Data)); + // } // otherwise, convert to grayscale bitmap, transform, convert back. var bmp = ConvertToBitmapWithoutColors(); var transformedBmp = TransformationUtil.Transform(bmp, targetDim, renderGraph.Resize, renderGraph.FlipHorizontally, renderGraph.FlipVertically); var transformedData = ConvertFromBitmapWithoutColors(transformedBmp); - return Update(targetDim, FrameUtil.Split(targetDim, BitLength, transformedData)); + Update(targetDim, transformedData); + + return this; } /// @@ -254,7 +244,7 @@ public ColoredFrame Transform(RenderGraph renderGraph, IFixedSizeDestination fix /// If and how to scale /// Updated frame instance /// - public ColoredFrame TransformHdScaling(IFixedSizeDestination fixedDest, ScalerMode scalerMode) + public new ColoredFrame TransformHdScaling(IFixedSizeDestination fixedDest, ScalerMode scalerMode) { // skip if disabled @@ -279,47 +269,15 @@ public ColoredFrame TransformHdScaling(IFixedSizeDestination fixedDest, ScalerMo // resize var data = scalerMode == ScalerMode.Doubler - ? FrameUtil.ScaleDouble(Dimensions, FrameUtil.Join(Dimensions, Planes)) - : FrameUtil.Scale2X(Dimensions, FrameUtil.Join(Dimensions, Planes)); + ? FrameUtil.ScaleDouble(Dimensions, Data) + : FrameUtil.Scale2X(Dimensions, Data); - return Update(Dimensions * 2, FrameUtil.Split(Dimensions * 2, BitLength, data)); + Update(Dimensions * 2, data); + return this; } #endregion - public object Clone() => new ColoredFrame(Dimensions, Planes, Palette, PaletteIndex, Rotations); - public override string ToString() - { - var bitLength = Planes.Length; - var data = FrameUtil.Join(Dimensions, Planes); - var sb = new StringBuilder(); - sb.AppendLine($"Colored Frame {Dimensions}@{bitLength}, {Palette.Length} colors ({data.Length} bytes):"); - if (bitLength <= 4) { - for (var y = 0; y < Dimensions.Height; y++) { - for (var x = 0; x < Dimensions.Width; x++) { - sb.Append(data[y * Dimensions.Width + x].ToString("X")); - } - sb.AppendLine(); - } - - } else if (bitLength <= 8) { - for (var y = 0; y < Dimensions.Height; y++) { - for (var x = 0; x < Dimensions.Width; x++) { - sb.Append(Data[y * Dimensions.Width + x].ToString("X2") + " "); - } - sb.AppendLine(); - } - } else { - throw new ArgumentException("Cannot print frame with bit length " + bitLength); - } - - sb.AppendLine("Palette: ["); - sb.Append(string.Join(", ", Palette.Select(c => c.ToString()))); - sb.AppendLine("]"); - - return sb.ToString(); - } - #region Equality public override bool Equals(object obj) @@ -410,5 +368,41 @@ private static bool PaletteEquals(IReadOnlyList a, IReadOnlyList b int IEqualityComparer.GetHashCode(ColoredFrame obj) => obj.GetHashCode(); #endregion + + #region Overrides + + public new object Clone() => new ColoredFrame(Dimensions, Data, Palette, PaletteIndex, Rotations); + + public override string ToString() + { + var sb = new StringBuilder(); + sb.AppendLine($"Colored Frame {Dimensions}@{BitLength}, {Palette.Length} colors ({Data.Length} bytes):"); + if (BitLength <= 4) { + for (var y = 0; y < Dimensions.Height; y++) { + for (var x = 0; x < Dimensions.Width; x++) { + sb.Append(Data[y * Dimensions.Width + x].ToString("X")); + } + sb.AppendLine(); + } + + } else if (BitLength <= 8) { + for (var y = 0; y < Dimensions.Height; y++) { + for (var x = 0; x < Dimensions.Width; x++) { + sb.Append(Data[y * Dimensions.Width + x].ToString("X2") + " "); + } + sb.AppendLine(); + } + } else { + throw new ArgumentException("Cannot print frame with bit length " + BitLength); + } + + sb.AppendLine("Palette: ["); + sb.Append(string.Join(", ", Palette.Select(c => c.ToString()))); + sb.AppendLine("]"); + + return sb.ToString(); + } + + #endregion } } diff --git a/LibDmd/Frame/DmdFrame.cs b/LibDmd/Frame/DmdFrame.cs index dc53c12c..af66b011 100644 --- a/LibDmd/Frame/DmdFrame.cs +++ b/LibDmd/Frame/DmdFrame.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Runtime.InteropServices; using System.Text; using System.Windows.Media; using LibDmd.Common; @@ -13,10 +12,33 @@ namespace LibDmd.Frame /// RGB24 buffer must be divisible by 3 public class DmdFrame : BaseFrame, ICloneable, IEqualityComparer { - public byte[] Data { get; private set; } + /// + /// The frame data, from top left to bottom right. + /// It's usually one byte per pixel, but three bytes for RGB24. + /// + public byte[] Data { get; protected set; } + + /// + /// The bit length of each pixel. + /// public int BitLength; + + /// + /// The number of colors resulting from . + /// public int NumColors => (int)Math.Pow(2, BitLength); - + + /// + /// Converts to bit planes, which is an array for each color, + /// where each each pixel is either 0 or 1, 8 pixels per byte. + /// + public byte[][] BitPlanes => FrameUtil.Split(Dimensions, BitLength, Data); + + /// + /// The length of every bit plane. + /// + public int BitPlaneLength => Dimensions.Surface / 8; + public static bool operator == (DmdFrame x, DmdFrame y) => Equals(x, y); public static bool operator != (DmdFrame x, DmdFrame y) => !Equals(x, y); @@ -35,7 +57,7 @@ public FrameFormat Format { private bool IsGray => BitLength <= 8; private bool IsRgb24 => BitLength == 24; - private int BytesPerPixel => BitLength <= 8 ? 1 : BitLength / 8; + protected int BytesPerPixel => BitLength <= 8 ? 1 : BitLength / 8; #region Constructors @@ -124,6 +146,24 @@ public DmdFrame Update(Dimensions dim, byte[] data) #endregion + #region Transfers + + public void CopyPlanesTo(byte[] dest, int offset) + { + var planes = BitPlanes; + foreach (var plane in planes) { + Buffer.BlockCopy(plane, 0, dest, offset, plane.Length); + offset += plane.Length; + } + } + + public void CopyDataTo(byte[] dest, int offset) + { + Buffer.BlockCopy(Data, 0, dest, offset, Data.Length); + } + + #endregion + #region Utilities public DmdFrame Resize(Dimensions dim, int bitLength) @@ -140,6 +180,25 @@ public DmdFrame Clear() return this; } + private static string PlaneName(int p) + { + switch (p) { + case 0: return "RED"; + case 1: return "GREEN"; + case 2: return "BLUE"; + default: return "PLANE " + p; + } + } + +#if DEBUG + protected void AssertData() + { + if (Dimensions.Surface * BytesPerPixel != Data.Length) { + throw new ArgumentException($"Data length does not match dimensions and bit length: {Dimensions} * {BytesPerPixel} = {Dimensions.Surface} * {BytesPerPixel} != {Data.Length}."); + } + } +#endif + #endregion #region Conversions @@ -249,8 +308,6 @@ public DmdFrame ConvertToRgb24(Color[] palette) #region Transformations - - /// /// Up- or downscales image, and flips if necessary. /// @@ -440,6 +497,8 @@ private static bool Equals(DmdFrame a, DmdFrame b) #endregion + #region Overrides + /// /// Flat-clones the frame (i.e. the data is still the same, but now you /// can replace it without affecting other references of the frame). @@ -484,24 +543,6 @@ public override string ToString() return sb.ToString(); } - private static string PlaneName(int p) - { - switch (p) { - case 0: return "RED"; - case 1: return "GREEN"; - case 2: return "BLUE"; - default: return "PLANE " + p; - } - } - -#if DEBUG - private void AssertData() - { - if (Dimensions.Surface * BytesPerPixel != Data.Length) { - throw new ArgumentException($"Data length does not match dimensions and bit length: {Dimensions} * {BytesPerPixel} = {Dimensions.Surface} * {BytesPerPixel} != {Data.Length}."); - } - } -#endif - + #endregion } } diff --git a/LibDmd/Input/FileSystem/ImageSource.cs b/LibDmd/Input/FileSystem/ImageSource.cs index c86de4d5..6a8e782c 100644 --- a/LibDmd/Input/FileSystem/ImageSource.cs +++ b/LibDmd/Input/FileSystem/ImageSource.cs @@ -47,7 +47,7 @@ public ImageSourceColoredGray2(BitmapSource bmp) { var frame = new ColoredFrame( bmp.Dimensions(), - FrameUtil.Split(bmp.Dimensions(), 2, ImageUtil.ConvertToGray2(bmp).Data), + ImageUtil.ConvertToGray2(bmp).Data, new [] { Colors.Black, Colors.Red, Colors.Green, Colors.Blue } ); _frames = new BehaviorSubject(frame); @@ -65,7 +65,7 @@ public ImageSourceColoredGray4(BitmapSource bmp) var pixelDim = bmp.Dimensions(); var frame = new ColoredFrame( pixelDim, - FrameUtil.Split(pixelDim, 4, ImageUtil.ConvertToGray4(bmp)), + ImageUtil.ConvertToGray4(bmp), new[] { Colors.Black, Colors.Blue, Colors.Purple, Colors.DimGray, Colors.Green, Colors.Brown, Colors.Red, Colors.Gray, @@ -88,7 +88,7 @@ public ImageSourceColoredGray6(BitmapSource bmp) var pixelDim = bmp.Dimensions(); var frame = new ColoredFrame( pixelDim, - FrameUtil.Split(pixelDim, 6, ImageUtil.ConvertToGray6(bmp)), + ImageUtil.ConvertToGray6(bmp), new[] { Colors.AntiqueWhite, Colors.Aqua, Colors.BlueViolet, Colors.BurlyWood, Colors.Chartreuse, Colors.Crimson, Colors.DarkGreen, Colors.DeepPink, diff --git a/LibDmd/Input/Network/WebsocketServer.cs b/LibDmd/Input/Network/WebsocketServer.cs index db4e8182..976a9482 100644 --- a/LibDmd/Input/Network/WebsocketServer.cs +++ b/LibDmd/Input/Network/WebsocketServer.cs @@ -15,17 +15,17 @@ namespace LibDmd.Input.Network { public class WebsocketServer : ISocketAction { - internal WebsocketGray2Source Gray2Source = new WebsocketGray2Source(); - internal WebsocketGray4Source Gray4Source = new WebsocketGray4Source(); - internal WebsocketColoredGray2Source ColoredGray2Source = new WebsocketColoredGray2Source(); - internal WebsocketColoredGray4Source ColoredGray4Source = new WebsocketColoredGray4Source(); - internal WebsocketRgb24Source Rgb24Source = new WebsocketRgb24Source(); + private readonly WebsocketGray2Source _gray2Source = new WebsocketGray2Source(); + private readonly WebsocketGray4Source _gray4Source = new WebsocketGray4Source(); + private readonly WebsocketColoredGray2Source _coloredGray2Source = new WebsocketColoredGray2Source(); + private readonly WebsocketColoredGray4Source _coloredGray4Source = new WebsocketColoredGray4Source(); + private readonly WebsocketRgb24Source _rgb24Source = new WebsocketRgb24Source(); private readonly HttpServer _server; private readonly List _sockets = new List(); private RenderGraphCollection _graphs; private readonly DmdFrame _dmdFrame = new DmdFrame(); - private ColoredFrame _coloredFrame = new ColoredFrame(); + private readonly ColoredFrame _coloredFrame = new ColoredFrame(); private static readonly NLog.Logger Logger = LogManager.GetCurrentClassLogger(); @@ -61,27 +61,27 @@ public void SetupGraphs(RenderGraphCollection graphs, List rendere _graphs = graphs; graphs.Add(new RenderGraph { Name = "2-bit Websocket Graph", - Source = Gray2Source, + Source = _gray2Source, Destinations = renderers, }); graphs.Add(new RenderGraph { Name = "4-bit Websocket Graph", - Source = Gray4Source, + Source = _gray4Source, Destinations = renderers, }); graphs.Add(new RenderGraph { Name = "Colored 2-bit Websocket Graph", - Source = ColoredGray2Source, + Source = _coloredGray2Source, Destinations = renderers, }); graphs.Add(new RenderGraph { Name = "Colored 4-bit Websocket Graph", - Source = ColoredGray4Source, + Source = _coloredGray4Source, Destinations = renderers, }); graphs.Add(new RenderGraph { Name = "24-bit RGB Websocket Graph", - Source = Rgb24Source, + Source = _rgb24Source, Destinations = renderers, }); } @@ -110,17 +110,17 @@ public void OnGameName(string gameName) Logger.Info("OnGameName: {0}", gameName); } - public void OnRgb24(uint timestamp, byte[] frame) => Rgb24Source.FramesRgb24.OnNext(_dmdFrame.Update(frame, 24)); + public void OnRgb24(uint timestamp, byte[] frame) => _rgb24Source.FramesRgb24.OnNext(_dmdFrame.Update(frame, 24)); - public void OnColoredGray4(uint timestamp, Color[] palette, byte[][] planes) - => ColoredGray4Source.FramesColoredGray4.OnNext(_coloredFrame.Update(planes, palette)); + public void OnColoredGray4(uint timestamp, Color[] palette, byte[] data) + => _coloredGray4Source.FramesColoredGray4.OnNext(_coloredFrame.Update(data, palette)); - public void OnColoredGray2(uint timestamp, Color[] palette, byte[][] planes) - => ColoredGray4Source.FramesColoredGray4.OnNext(_coloredFrame.Update(planes, palette)); + public void OnColoredGray2(uint timestamp, Color[] palette, byte[] data) + => _coloredGray4Source.FramesColoredGray4.OnNext(_coloredFrame.Update(data, palette)); - public void OnGray4(uint timestamp, byte[] frame) => Gray4Source.FramesGray4.OnNext(_dmdFrame.Update(frame, 4)); + public void OnGray4(uint timestamp, byte[] frame) => _gray4Source.FramesGray4.OnNext(_dmdFrame.Update(frame, 4)); - public void OnGray2(uint timestamp, byte[] frame) => Gray2Source.FramesGray2.OnNext(_dmdFrame.Update(frame, 2)); + public void OnGray2(uint timestamp, byte[] frame) => _gray2Source.FramesGray2.OnNext(_dmdFrame.Update(frame, 2)); } public class DmdSocket : WebSocketBehavior diff --git a/LibDmd/Input/Passthrough/PassthroughAlphaNumericSource.cs b/LibDmd/Input/Passthrough/PassthroughAlphaNumericSource.cs index 509b85b9..31a52dfb 100644 --- a/LibDmd/Input/Passthrough/PassthroughAlphaNumericSource.cs +++ b/LibDmd/Input/Passthrough/PassthroughAlphaNumericSource.cs @@ -20,6 +20,8 @@ public class PassthroughAlphaNumericSource : AbstractSource, IAlphaNumericSource private readonly ISubject _framesAlphaNumeric; private readonly ISubject _gameName = new Subject(); + + private readonly AlphaNumericFrame _lastFrame = new AlphaNumericFrame(); private readonly BehaviorSubject _lastFrameFormat; public PassthroughAlphaNumericSource(AlphaNumericFrame initialFrame) @@ -35,6 +37,12 @@ public PassthroughAlphaNumericSource(BehaviorSubject lastFrameForma public void NextFrame(AlphaNumericFrame frame) { + // de-dupe frame + if (_lastFrameFormat.Value == FrameFormat.AlphaNumeric && _lastFrame == frame) { + return; + } + + _lastFrame.Update(frame); _framesAlphaNumeric.OnNext(frame); _lastFrameFormat.OnNext(FrameFormat.AlphaNumeric); } diff --git a/LibDmd/Input/Passthrough/PassthroughGray2Source.cs b/LibDmd/Input/Passthrough/PassthroughGray2Source.cs index 401dc862..62e82b28 100644 --- a/LibDmd/Input/Passthrough/PassthroughGray2Source.cs +++ b/LibDmd/Input/Passthrough/PassthroughGray2Source.cs @@ -2,6 +2,7 @@ using System.Reactive; using System.Reactive.Subjects; using LibDmd.Frame; +using NLog; namespace LibDmd.Input.Passthrough { @@ -22,8 +23,11 @@ public class PassthroughGray2Source : AbstractSource, IGray2Source, IGameNameSou private readonly Subject _framesGray2 = new Subject(); private readonly ISubject _gameName = new Subject(); + private readonly DmdFrame _lastFrame = new DmdFrame(); private readonly BehaviorSubject _lastFrameFormat; + protected static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + public PassthroughGray2Source(BehaviorSubject lastFrameFormat, string name) { _lastFrameFormat = lastFrameFormat; @@ -32,6 +36,12 @@ public PassthroughGray2Source(BehaviorSubject lastFrameFormat, stri public void NextFrame(DmdFrame frame) { + // de-dupe frame + if (_lastFrameFormat.Value == FrameFormat.Gray2 && _lastFrame == frame) { + return; + } + + _lastFrame.Update(frame); _lastFrameFormat.OnNext(FrameFormat.Gray2); _framesGray2.OnNext(frame); } diff --git a/LibDmd/Input/Passthrough/PassthroughGray4Source.cs b/LibDmd/Input/Passthrough/PassthroughGray4Source.cs index e73abdfc..5fbed39a 100644 --- a/LibDmd/Input/Passthrough/PassthroughGray4Source.cs +++ b/LibDmd/Input/Passthrough/PassthroughGray4Source.cs @@ -22,6 +22,7 @@ public class PassthroughGray4Source : AbstractSource, IGray4Source, IGameNameSou private readonly Subject _framesGray4 = new Subject(); private readonly ISubject _gameName = new Subject(); + private readonly DmdFrame _lastFrame = new DmdFrame(); private readonly BehaviorSubject _lastFrameFormat; public PassthroughGray4Source(BehaviorSubject lastFrameFormat, string name) @@ -32,6 +33,12 @@ public PassthroughGray4Source(BehaviorSubject lastFrameFormat, stri public void NextFrame(DmdFrame frame) { + // de-dupe frame + if (_lastFrameFormat.Value == FrameFormat.Gray4 && _lastFrame == frame) { + return; + } + + _lastFrame.Update(frame); _lastFrameFormat.OnNext(FrameFormat.Gray4); _framesGray4.OnNext(frame); } diff --git a/LibDmd/Input/PinballFX/PinballFXGrabber.cs b/LibDmd/Input/PinballFX/PinballFXGrabber.cs index db8a7788..3a17943e 100644 --- a/LibDmd/Input/PinballFX/PinballFXGrabber.cs +++ b/LibDmd/Input/PinballFX/PinballFXGrabber.cs @@ -126,7 +126,7 @@ public IObservable GetColoredGray2Frames() palette = ColorUtil.GetPalette(new[]{ Colors.Black, color }, 4); lastHue = hue; } - return new ColoredFrame(bmp.Dimensions(), FrameUtil.Split(new Dimensions(bmp.PixelWidth, bmp.PixelHeight), 2, frame), palette, index); + return new ColoredFrame(bmp.Dimensions(), frame, palette, index); }) .Publish(); diff --git a/LibDmd/Output/Network/BrowserStream.cs b/LibDmd/Output/Network/BrowserStream.cs index 07fa453e..ec120f07 100644 --- a/LibDmd/Output/Network/BrowserStream.cs +++ b/LibDmd/Output/Network/BrowserStream.cs @@ -102,12 +102,12 @@ public void RenderGray4(DmdFrame frame) public void RenderColoredGray2(ColoredFrame frame) { - _sockets.ForEach(s => s.SendColoredGray2(frame.Planes, frame.Palette)); + _sockets.ForEach(s => s.SendColoredGray2(frame.BitPlanes, frame.Palette)); } public void RenderColoredGray4(ColoredFrame frame) { - _sockets.ForEach(s => s.SendColoredGray4(frame.Planes, frame.Palette)); + _sockets.ForEach(s => s.SendColoredGray4(frame.BitPlanes, frame.Palette)); } public void RenderRgb24(DmdFrame frame) diff --git a/LibDmd/Output/Network/NetworkStream.cs b/LibDmd/Output/Network/NetworkStream.cs index d0ecbd81..021e04b6 100644 --- a/LibDmd/Output/Network/NetworkStream.cs +++ b/LibDmd/Output/Network/NetworkStream.cs @@ -131,14 +131,14 @@ public void RenderGray4(DmdFrame frame) public void RenderColoredGray2(ColoredFrame frame) { if (IsAvailable) { - _client.Send(_serializer.SerializeColoredGray2(frame.Planes, frame.Palette)); + _client.Send(_serializer.SerializeColoredGray2(frame.BitPlanes, frame.Palette)); } } public void RenderColoredGray4(ColoredFrame frame) { if (IsAvailable) { - _client.Send(_serializer.SerializeColoredGray4(frame.Planes, frame.Palette)); + _client.Send(_serializer.SerializeColoredGray4(frame.BitPlanes, frame.Palette)); } } diff --git a/LibDmd/Output/Network/VpdbStream.cs b/LibDmd/Output/Network/VpdbStream.cs index f7fb8ec2..ca21234a 100644 --- a/LibDmd/Output/Network/VpdbStream.cs +++ b/LibDmd/Output/Network/VpdbStream.cs @@ -102,39 +102,33 @@ public void SetDimensions(Dimensions dim) public void RenderGray2(DmdFrame frame) { - EmitTimestampedData("gray2planes", frame.Data.Length / 4, (data, offset) => FrameUtil.Copy(FrameUtil.Split(_dimensions, 2, frame.Data), data, offset)); + EmitTimestampedData("gray2planes", frame.Data.Length / 4, frame.CopyDataTo); } public void RenderGray4(DmdFrame frame) { - EmitTimestampedData("gray4planes", frame.Data.Length / 2, (data, offset) => FrameUtil.Copy(FrameUtil.Split(_dimensions, 4, frame.Data), data, offset)); + EmitTimestampedData("gray4planes", frame.Data.Length / 2, frame.CopyDataTo); } public void RenderColoredGray2(ColoredFrame frame) { - if (frame.Planes.Length == 0) { - return; - } const int numColors = 4; const int bytesPerColor = 3; - var dataLength = bytesPerColor * numColors + frame.Planes[0].Length * frame.Planes.Length; + var dataLength = bytesPerColor * numColors + frame.BitPlaneLength * frame.BitLength; EmitTimestampedData("coloredgray2", dataLength, (data, offset) => { Buffer.BlockCopy(ColorUtil.ToByteArray(frame.Palette), 0, data, offset, bytesPerColor * numColors); - FrameUtil.Copy(frame.Planes, data, offset + bytesPerColor * numColors); + frame.CopyPlanesTo(data, offset + bytesPerColor * numColors); }); } public void RenderColoredGray4(ColoredFrame frame) { - if (frame.Planes.Length == 0) { - return; - } const int numColors = 16; const int bytesPerColor = 3; - var dataLength = bytesPerColor * numColors + frame.Planes[0].Length * frame.Planes.Length; + var dataLength = bytesPerColor * numColors + frame.BitPlaneLength * frame.BitLength; EmitTimestampedData("coloredgray4", dataLength, (data, offset) => { Buffer.BlockCopy(ColorUtil.ToByteArray(frame.Palette), 0, data, offset, bytesPerColor * numColors); - FrameUtil.Copy(frame.Planes, data, offset + bytesPerColor * numColors); + frame.CopyPlanesTo(data, offset + bytesPerColor * numColors); }); } diff --git a/LibDmd/Output/Network/WebsocketSerializer.cs b/LibDmd/Output/Network/WebsocketSerializer.cs index ea79144e..4fcceaa5 100644 --- a/LibDmd/Output/Network/WebsocketSerializer.cs +++ b/LibDmd/Output/Network/WebsocketSerializer.cs @@ -17,8 +17,8 @@ internal interface ISocketAction void OnClearPalette(); void OnGameName(string gameName); void OnRgb24(uint timestamp, byte[] frame); - void OnColoredGray4(uint timestamp, Color[] palette, byte[][] planes); - void OnColoredGray2(uint timestamp, Color[] palette, byte[][] planes); + void OnColoredGray4(uint timestamp, Color[] palette, byte[] data); + void OnColoredGray2(uint timestamp, Color[] palette, byte[] data); void OnGray4(uint timestamp, byte[] frame); void OnGray2(uint timestamp, byte[] frame); } @@ -92,7 +92,7 @@ public void Unserialize(byte[] data, ISocketAction action) { for (var i = 0; i < 4; i++) { planes[i] = reader.ReadBytes(planeSize); } - action.OnColoredGray4(timestamp, palette, planes); + action.OnColoredGray4(timestamp, palette, FrameUtil.Join(Dimensions, planes)); break; } case "coloredGray2": { @@ -107,7 +107,7 @@ public void Unserialize(byte[] data, ISocketAction action) { for (var i = 0; i < 2; i++) { planes[i] = reader.ReadBytes(planeSize); } - action.OnColoredGray2(timestamp, palette, planes); + action.OnColoredGray2(timestamp, palette, FrameUtil.Join(Dimensions, planes)); break; } case "gray4Planes": { diff --git a/LibDmd/Output/Pin2Dmd/Pin2Dmd.cs b/LibDmd/Output/Pin2Dmd/Pin2Dmd.cs index fc6243c4..6a1772e8 100644 --- a/LibDmd/Output/Pin2Dmd/Pin2Dmd.cs +++ b/LibDmd/Output/Pin2Dmd/Pin2Dmd.cs @@ -97,16 +97,11 @@ public void RenderGray2(DmdFrame frame) public void RenderGray4(DmdFrame frame) { - // convert to bit planes - var planes = FrameUtil.Split(FixedSize, 4, frame.Data); - // copy to buffer - var changed = FrameUtil.Copy(planes, _frameBufferGray4, 4); + frame.CopyPlanesTo(_frameBufferGray4, 4); // send frame buffer to device - if (changed) { - RenderRaw(_frameBufferGray4); - } + RenderRaw(_frameBufferGray4); } public void RenderRgb24(DmdFrame frame) @@ -125,13 +120,10 @@ public void RenderColoredGray6(ColoredFrame frame) SetPalette(frame.Palette, frame.PaletteIndex); // copy to buffer - var changed = FrameUtil.Copy(frame.Planes, _frameBufferGray6, 4); + frame.CopyPlanesTo(_frameBufferGray6, 4); // send frame buffer to device - if (changed) - { - RenderRaw(_frameBufferGray6); - } + RenderRaw(_frameBufferGray6); } public void RenderColoredGray4(ColoredFrame frame) @@ -139,20 +131,16 @@ public void RenderColoredGray4(ColoredFrame frame) SetPalette(frame.Palette, frame.PaletteIndex); // copy to buffer - var changed = FrameUtil.Copy(frame.Planes, _frameBufferGray4, 4); + frame.CopyPlanesTo(_frameBufferGray4, 4); // send frame buffer to device - if (changed) { - RenderRaw(_frameBufferGray4); - } + RenderRaw(_frameBufferGray4); } public void RenderColoredGray2(ColoredFrame frame) { SetPalette(frame.Palette, frame.PaletteIndex); - var joinedFrame = FrameUtil.Join(FixedSize, frame.Planes); - // send frame buffer to device RenderGray4(frame.ConvertToGray(0x0, 0x1, 0x4, 0xf)); } diff --git a/LibDmd/Output/PinDmd1/PinDmd1.cs b/LibDmd/Output/PinDmd1/PinDmd1.cs index 9c0d698d..e5acedb4 100644 --- a/LibDmd/Output/PinDmd1/PinDmd1.cs +++ b/LibDmd/Output/PinDmd1/PinDmd1.cs @@ -144,16 +144,11 @@ public static PinDmd1 GetInstance() public void RenderGray2(DmdFrame frame) { - // split frame into 2-bit planes - var planes = FrameUtil.Split(FixedSize, 2, frame.Data); - // copy planes into frame buffer - var changed = FrameUtil.Copy(planes, _frameBuffer, 4); + frame.CopyPlanesTo(_frameBuffer, 4); // send buffer to device - if (changed) { - RenderRaw(_frameBuffer); - } + RenderRaw(_frameBuffer); } public void RenderRaw(byte[] data) diff --git a/LibDmd/Output/PinDmd2/PinDmd2.cs b/LibDmd/Output/PinDmd2/PinDmd2.cs index 9934136f..1f485a0d 100644 --- a/LibDmd/Output/PinDmd2/PinDmd2.cs +++ b/LibDmd/Output/PinDmd2/PinDmd2.cs @@ -117,16 +117,11 @@ public void RenderGray2(DmdFrame frame) public void RenderGray4(DmdFrame frame) { - // convert to bit planes - var planes = FrameUtil.Split(FixedSize, 4, frame.Data); - // copy to buffer - var changed = FrameUtil.Copy(planes, _frameBuffer, 4); + frame.CopyPlanesTo(_frameBuffer, 4); // send frame buffer to device - if (changed) { - RenderRaw(_frameBuffer); - } + RenderRaw(_frameBuffer); } public void RenderRaw(byte[] data) diff --git a/LibDmd/Output/PinDmd3/PinDmd3.cs b/LibDmd/Output/PinDmd3/PinDmd3.cs index bb0dd132..fbcff96b 100644 --- a/LibDmd/Output/PinDmd3/PinDmd3.cs +++ b/LibDmd/Output/PinDmd3/PinDmd3.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text.RegularExpressions; using System.Windows.Media; +using System.Windows.Media.Animation; using LibDmd.Common; using LibDmd.Frame; using NLog; @@ -196,17 +197,12 @@ private bool Connect(string port, bool checkFirmware) public void RenderGray2(DmdFrame frame) { - // split to sub frames - var planes = FrameUtil.Split(FixedSize, 2, frame.Data); - // copy to frame buffer - var changed = FrameUtil.Copy(planes, _frameBufferGray2, 13); + frame.CopyPlanesTo(_frameBufferGray2, 13); // send frame buffer to device - if (changed) { - WritePalette(_currentPalette); - RenderRaw(_frameBufferGray2); - } + WritePalette(_currentPalette); + RenderRaw(_frameBufferGray2); } public void RenderColoredGray2(ColoredFrame frame) @@ -215,34 +211,26 @@ public void RenderColoredGray2(ColoredFrame frame) WritePalette(frame.Palette); // copy to frame buffer - var changed = FrameUtil.Copy(frame.Planes, _frameBufferGray2, 13); + frame.CopyPlanesTo(_frameBufferGray2, 13); // send frame buffer to device - if (changed) { - RenderRaw(_frameBufferGray2); - } + RenderRaw(_frameBufferGray2); } public void RenderGray4(DmdFrame frame) { - // split to sub frames - var planes = FrameUtil.Split(FixedSize, 4, frame.Data); - // copy to frame buffer - var changed = FrameUtil.Copy(planes, _frameBufferGray4, 13); + frame.CopyPlanesTo(_frameBufferGray2, 13); // send frame buffer to device - if (changed) { - RenderRaw(_frameBufferGray4); - } + RenderRaw(_frameBufferGray4); } public void RenderColoredGray4(ColoredFrame frame) { // fall back if firmware doesn't support colored gray 4 if (!_supportsColoredGray4) { - var rgb24Frame = ColorUtil.ColorizeObsolete(FixedSize, FrameUtil.Join(FixedSize, frame.Planes), frame.Palette); - RenderRgb24(rgb24Frame); + RenderRgb24(frame.ConvertToRgb24()); return; } @@ -258,23 +246,19 @@ public void RenderColoredGray4(ColoredFrame frame) } // copy frame - var frameChanged = FrameUtil.Copy(frame.Planes, _frameBufferColoredGray4, 49); + frame.CopyPlanesTo(_frameBufferColoredGray4, 49); // send frame buffer to device - if (frameChanged || paletteChanged) { - RenderRaw(_frameBufferColoredGray4); - } + RenderRaw(_frameBufferColoredGray4); } public void RenderRgb24(DmdFrame frame) { // copy data to frame buffer - var changed = FrameUtil.Copy(frame.Data, _frameBufferRgb24, 1); + frame.CopyDataTo(_frameBufferRgb24, 1); // can directly be sent to the device. - if (changed) { - RenderRaw(_frameBufferRgb24); - } + RenderRaw(_frameBufferRgb24); } public void RenderRaw(byte[] data) diff --git a/LibDmd/Output/PinUp/PinUpOutput.cs b/LibDmd/Output/PinUp/PinUpOutput.cs index 21358009..470f4449 100644 --- a/LibDmd/Output/PinUp/PinUpOutput.cs +++ b/LibDmd/Output/PinUp/PinUpOutput.cs @@ -126,13 +126,8 @@ public void RenderGray4(DmdFrame frame) } try { - // Render as orange palette (same as default with no PAL loaded) - var planes = FrameUtil.Split(FixedSize, 4, frame.Data); - - var orangeFrame = FrameUtil.ConvertToRgb24(FixedSize, planes, - ColorUtil.GetPalette(new[] {Colors.Black, Colors.OrangeRed}, 16)); - - Marshal.Copy(orangeFrame.Data, 0, _pnt, FixedSize.Surface * 3); + var rgbFrame = frame.ConvertToRgb24(ColorUtil.GetPalette(new[] { Colors.Black, Colors.OrangeRed }, 16)); + Marshal.Copy(rgbFrame.Data, 0, _pnt, rgbFrame.Data.Length); Render_RGB24((ushort) FixedSize.Width, (ushort) FixedSize.Height, _pnt); } catch (Exception e) { diff --git a/LibDmd/Output/Virtual/Dmd/VirtualDmdControl.xaml.cs b/LibDmd/Output/Virtual/Dmd/VirtualDmdControl.xaml.cs index a84531d8..87250f91 100644 --- a/LibDmd/Output/Virtual/Dmd/VirtualDmdControl.xaml.cs +++ b/LibDmd/Output/Virtual/Dmd/VirtualDmdControl.xaml.cs @@ -251,7 +251,7 @@ public void RenderColoredGray6(ColoredFrame frame) SetDimensions(frame.Dimensions); SetPalette(frame.Palette); - _frameData = FrameUtil.Join(_frameDimensions, frame.Planes); + _frameData = frame.Data; _frameType = FrameFormat.ColoredGray6; _framePalette = frame.Palette; diff --git a/LibDmd/Output/ZeDMD/ZeDMD.cs b/LibDmd/Output/ZeDMD/ZeDMD.cs index 3cfdac83..7780f97b 100644 --- a/LibDmd/Output/ZeDMD/ZeDMD.cs +++ b/LibDmd/Output/ZeDMD/ZeDMD.cs @@ -96,29 +96,22 @@ public void Init() public void RenderGray2(DmdFrame frame) { - var planes = FrameUtil.Split(RomDimensions, 2, frame.Data); - var changed = FrameUtil.Copy(planes, _frameBuffer, 13); + frame.CopyPlanesTo(_frameBuffer, 13); // send frame buffer to device - if (changed) - { - RenderRaw(_frameBuffer, Gray2, 1 + 12 + RomDimensions.Surface / 4); - } + RenderRaw(_frameBuffer, Gray2, 1 + 12 + RomDimensions.Surface / 4); } public void RenderColoredGray2(ColoredFrame frame) { // copy palette - var paletteChanged = CopyPalette(frame.Palette, _frameBuffer, 4); + CopyPalette(frame.Palette, _frameBuffer, 4); // copy frame - var frameChanged = FrameUtil.Copy(frame.Planes, _frameBuffer, 13); + frame.CopyPlanesTo(_frameBuffer, 13); // send frame buffer to device - if (frameChanged || paletteChanged) - { - RenderRaw(_frameBuffer, Gray2, 1 + 12 + RomDimensions.Surface / 4); - } + RenderRaw(_frameBuffer, Gray2, 1 + 12 + RomDimensions.Surface / 4); } private float CalcBrightness(float x) @@ -129,61 +122,55 @@ private float CalcBrightness(float x) public void RenderGray4(DmdFrame frame) { - var planes = FrameUtil.Split(RomDimensions, 4, frame.Data); - var changed = FrameUtil.Copy(planes, _frameBuffer, 49); + frame.CopyPlanesTo(_frameBuffer, 49); - // send frame buffer to device - if (changed) + // copy palette + for (int ti = 0; ti < 16; ti++) { - for (int ti = 0; ti < 16; ti++) - { - _frameBuffer[1 + ti * 3] = (byte)(255.0f * CalcBrightness(ti / 15.0f)); - _frameBuffer[1 + ti * 3 + 1] = (byte)(109.0f * CalcBrightness(ti / 15.0f)); - _frameBuffer[1 + ti * 3 + 2] = (byte)(0.0f * CalcBrightness(ti / 15.0f)); - } - RenderRaw(_frameBuffer, ColGray4, 1 + 48 + RomDimensions.Surface / 2); + _frameBuffer[1 + ti * 3] = (byte)(255.0f * CalcBrightness(ti / 15.0f)); + _frameBuffer[1 + ti * 3 + 1] = (byte)(109.0f * CalcBrightness(ti / 15.0f)); + _frameBuffer[1 + ti * 3 + 2] = (byte)(0.0f * CalcBrightness(ti / 15.0f)); } + + // send frame buffer to device + RenderRaw(_frameBuffer, ColGray4, 1 + 48 + RomDimensions.Surface / 2); } public void RenderColoredGray4(ColoredFrame frame) { // copy palette - var paletteChanged = CopyPalette(frame.Palette, _frameBuffer, 16); + CopyPalette(frame.Palette, _frameBuffer, 16); // copy frame - var frameChanged = FrameUtil.Copy(frame.Planes, _frameBuffer, 49); + frame.CopyPlanesTo(_frameBuffer, 49); // send frame buffer to device - if (frameChanged || paletteChanged) - { - RenderRaw(_frameBuffer, ColGray4, 1 + 48 + RomDimensions.Surface / 2); - } + RenderRaw(_frameBuffer, ColGray4, 1 + 48 + RomDimensions.Surface / 2); } public void RenderColoredGray6(ColoredFrame frame) { // copy palette - var paletteChanged = CopyPalette(frame.Palette, _frameBuffer, 64); + CopyPalette(frame.Palette, _frameBuffer, 64); // copy frame - var frameChanged = FrameUtil.Copy(frame.Planes, _frameBuffer, 193); + frame.CopyPlanesTo(_frameBuffer, 193); // send frame buffer to device - if (frameChanged || paletteChanged) - { - if (frame.RotateColors) - { - for (int ti = 0; ti < 3 * MAX_COLOR_ROTATIONS; ti++) _frameBuffer[ti + 1 + 192 + RomDimensions.Surface * 6 / 8] = frame.Rotations[ti]; + if (frame.RotateColors) { + for (int ti = 0; ti < 3 * MAX_COLOR_ROTATIONS; ti++) { + _frameBuffer[ti + 1 + 192 + RomDimensions.Surface * 6 / 8] = frame.Rotations[ti]; } - else - { - for (int ti = 0; ti < MAX_COLOR_ROTATIONS; ti++) _frameBuffer[ti * 3 + 1 + 192 + RomDimensions.Surface * 6 / 8] = 255; + + } else { + for (int ti = 0; ti < MAX_COLOR_ROTATIONS; ti++) { + _frameBuffer[ti * 3 + 1 + 192 + RomDimensions.Surface * 6 / 8] = 255; } - RenderRaw(_frameBuffer, ColGray6, 1 + 192 + 6 * RomDimensions.Surface / 8 + 3 * 8); } + RenderRaw(_frameBuffer, ColGray6, 1 + 192 + 6 * RomDimensions.Surface / 8 + 3 * 8); } - private bool CopyPalette(Color[] palette,byte[] framebuffer,int ncol) + private static void CopyPalette(Color[] palette,byte[] framebuffer,int ncol) { var paletteChanged = false; for (var i = 0; i < ncol; i++) @@ -195,25 +182,21 @@ private bool CopyPalette(Color[] palette,byte[] framebuffer,int ncol) framebuffer[j + 1] = color.G; framebuffer[j + 2] = color.B; } - return paletteChanged; } public void RenderRgb24(DmdFrame frame) { - bool changed; // can directly be sent to the device. _frameBuffer[0] = RGB24; + // copy data to frame buffer - changed = FrameUtil.Copy(frame.Data, _frameBuffer, 1); - if (changed) - { - pDMD.QueueFrame(_frameBuffer.Take(RomDimensions.Surface * 3 + 1).ToArray()); - } + frame.CopyDataTo(_frameBuffer, 1); + pDMD.QueueFrame(_frameBuffer.Take(RomDimensions.Surface * 3 + 1).ToArray()); } - public void RenderRaw(byte[] data, byte Mode, int length) + private void RenderRaw(byte[] data, byte mode, int length) { - data[0] = Mode; + data[0] = mode; if (pDMD.Opened) { pDMD.QueueFrame(data.Take(length).ToArray()); diff --git a/LibDmd/RenderGraph.cs b/LibDmd/RenderGraph.cs index 9057ae51..517f2cce 100644 --- a/LibDmd/RenderGraph.cs +++ b/LibDmd/RenderGraph.cs @@ -419,7 +419,7 @@ public IDisposable StartRendering(Action onCompleted, Action onError // start looking at the most performant combinations first. // // So first we try to match the source format with the destination format. Then - // we go on by looking at "upscaling" convertions, e.g. if a destination only + // we go on by looking at "up-scaling" conversions, e.g. if a destination only // supports RGB24, then convert 2-bit to RGB24. Lastly we check "downscaling" // conversions, e.g. convert an RGB24 frame to 2-bit for outputs like PinDMD1 // that can only render 4 shades. @@ -662,7 +662,7 @@ private void Connect(ISource source, IDestination dest, FrameFormat from, FrameF case FrameFormat.Gray2: AssertCompatibility(source, sourceGray2, dest, destGray2, from, to); Subscribe( - sourceGray2.GetGray2Frames().DistinctUntilChanged(), + sourceGray2.GetGray2Frames(), frame => frame .TransformHdScaling(destFixedSize, ScalerMode) .TransformGray(this, destFixedSize, destMultiSize),