Skip to content

Commit 24d4a05

Browse files
committed
refactor: Use byte array instead of bit planes in colored frame.
1 parent aed88ed commit 24d4a05

30 files changed

+458
-401
lines changed

LibDmd.Test/Frame/FrameTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.Reactive.Linq;
34
using System.Threading.Tasks;
45
using FluentAssertions;

LibDmd.Test/RenderGraph/Gray6ConverterSourceTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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
167167
00000000");
168168

169169
var coloredFrame = FrameGenerator.FromString(@"
170-
3F 2E 1D 0C 1B 2A 39 08
170+
3F 2E 1D 0C 1B 2A 39 28
171171
0A 0A 0A 0A 30 30 30 30
172172
00 00 00 00 22 22 22 22
173173
00 11 22 33 04 15 26 37",

LibDmd.Test/TestBase.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,8 @@ protected static async Task AssertFrame<TFrame>(ITestSource<TFrame> source, ITes
104104

105105
Print(expectedFrame, "Expected: ");
106106

107-
receivedFrame.Planes.Length.Should().Be(expectedFrame.Planes.Length);
108-
for (var i = 0; i < receivedFrame.Planes.Length; i++) {
109-
receivedFrame.Planes[i].Should().BeEquivalentTo(expectedFrame.Planes[i]);
110-
}
107+
receivedFrame.Data.Length.Should().Be(expectedFrame.Data.Length);
108+
receivedFrame.Data.Should().BeEquivalentTo(expectedFrame.Data);
111109
receivedFrame.Palette.Should().BeEquivalentTo(expectedFrame.Palette);
112110
receivedFrame.Dimensions.Should().Be(expectedFrame.Dimensions);
113111
}

LibDmd/AlphaNumericFrame.cs

Lines changed: 113 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,32 @@
11
using System;
2+
using System.Collections.Generic;
23
using LibDmd.DmdDevice;
34

45
namespace LibDmd
56
{
6-
public class AlphaNumericFrame : ICloneable
7+
public class AlphaNumericFrame : ICloneable, IEqualityComparer<AlphaNumericFrame>
78
{
89
/// <summary>
910
/// The segment data
1011
/// </summary>
11-
public ushort[] SegmentData { get; }
12+
public ushort[] SegmentData { get; private set; }
1213

1314
/// <summary>
1415
/// The extended segment data
1516
/// </summary>
16-
public ushort[] SegmentDataExtended { get; }
17+
public ushort[] SegmentDataExtended { get; private set; }
1718

1819
/// <summary>
1920
/// The segment type
2021
/// </summary>
21-
public NumericalLayout SegmentLayout { get; }
22+
public NumericalLayout SegmentLayout { get; private set; }
23+
24+
public static bool operator == (AlphaNumericFrame x, AlphaNumericFrame y) => Equals(x, y);
25+
public static bool operator != (AlphaNumericFrame x, AlphaNumericFrame y) => !Equals(x, y);
26+
27+
public AlphaNumericFrame()
28+
{
29+
}
2230

2331
public AlphaNumericFrame(NumericalLayout layout, ushort[] segData)
2432
{
@@ -33,6 +41,107 @@ public AlphaNumericFrame(NumericalLayout layout, ushort[] segData, ushort[] segD
3341
SegmentLayout = layout;
3442
}
3543

44+
45+
public void Update(AlphaNumericFrame frame)
46+
{
47+
SegmentData = frame.SegmentData;
48+
SegmentDataExtended = frame.SegmentDataExtended;
49+
SegmentLayout = frame.SegmentLayout;
50+
}
51+
3652
public object Clone() => new AlphaNumericFrame(SegmentLayout, SegmentData, SegmentDataExtended);
53+
54+
#region Equality
55+
56+
public override bool Equals(object obj)
57+
{
58+
if (ReferenceEquals(null, obj)) {
59+
return false;
60+
}
61+
62+
if (ReferenceEquals(this, obj)) {
63+
return true;
64+
}
65+
66+
if (obj.GetType() != this.GetType()) {
67+
return false;
68+
}
69+
70+
return Equals(this, (AlphaNumericFrame)obj);
71+
}
72+
73+
protected bool Equals(AlphaNumericFrame other) => Equals(this, other);
74+
75+
public override int GetHashCode()
76+
{
77+
unchecked
78+
{
79+
var hashCode = (SegmentData != null ? SegmentData.GetHashCode() : 0);
80+
hashCode = (hashCode * 397) ^ (SegmentDataExtended != null ? SegmentDataExtended.GetHashCode() : 0);
81+
hashCode = (hashCode * 397) ^ (int)SegmentLayout;
82+
return hashCode;
83+
}
84+
}
85+
86+
public static bool Equals(AlphaNumericFrame x, AlphaNumericFrame y)
87+
{
88+
if (ReferenceEquals(x, y)) {
89+
return true;
90+
}
91+
92+
if (ReferenceEquals(x, null)) {
93+
return false;
94+
}
95+
96+
if (ReferenceEquals(y, null)) {
97+
return false;
98+
}
99+
100+
if (x.GetType() != y.GetType()) {
101+
return false;
102+
}
103+
104+
return Compare(x.SegmentData, y.SegmentData)
105+
&& Compare(x.SegmentDataExtended, y.SegmentDataExtended)
106+
&& x.SegmentLayout == y.SegmentLayout;
107+
}
108+
109+
bool IEqualityComparer<AlphaNumericFrame>.Equals(AlphaNumericFrame x, AlphaNumericFrame y) => Equals(x, y);
110+
111+
private static bool Compare(IReadOnlyList<ushort> a, IReadOnlyList<ushort> b)
112+
{
113+
if (a == null && b == null) {
114+
return true;
115+
}
116+
117+
if (a == null || b == null) {
118+
return false;
119+
}
120+
121+
if (a.Count != b.Count) {
122+
return false;
123+
}
124+
125+
for (var i = 0; i < a.Count; i++) {
126+
if (a[i] != b[i]) {
127+
return false;
128+
}
129+
}
130+
131+
return true;
132+
}
133+
134+
public int GetHashCode(AlphaNumericFrame obj)
135+
{
136+
unchecked
137+
{
138+
var hashCode = (obj.SegmentData != null ? obj.SegmentData.GetHashCode() : 0);
139+
hashCode = (hashCode * 397) ^ (obj.SegmentDataExtended != null ? obj.SegmentDataExtended.GetHashCode() : 0);
140+
hashCode = (hashCode * 397) ^ (int)obj.SegmentLayout;
141+
return hashCode;
142+
}
143+
}
144+
145+
#endregion
37146
}
38147
}

LibDmd/Common/FrameUtil.cs

Lines changed: 28 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,38 @@
11
using System;
22
using System.Collections;
3-
using System.Linq;
3+
using System.Diagnostics;
44
using System.Runtime.InteropServices;
55
using System.Text;
66
using LibDmd.Frame;
77
using NLog;
8-
using Color = System.Windows.Media.Color;
98

109
namespace LibDmd.Common
1110
{
1211
/// <summary>
13-
/// Wärchziig zum hin- und härkonvertiärä vo Biud-Datä.
12+
/// Tools for dealing with frame data.
1413
/// </summary>
15-
public class FrameUtil
14+
public static class FrameUtil
1615
{
1716
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
1817

1918
/// <summary>
20-
/// Tuät es Biud i sini Bitahteiu uifteilä.
19+
/// Splits a pixel array into separate bit planes.
2120
/// </summary>
2221
///
2322
/// <remarks>
24-
/// Mr chas so gseh dass für äs Biud mit viar Graiteen zwe Ebänä fir
25-
/// jedes Bit uisächemid
23+
/// A bit plane is a byte array with the same dimensions as the original frame,
24+
/// but since it's bits, a pixel can be either one or zero, so they are packed
25+
/// into bytes.
26+
///
27+
/// This makes it more efficient to transfer than one byte per pixel, where only
28+
/// 2 or 4 bits are used.
2629
/// </remarks>
2730
///
28-
/// <param name="dim">Dimensionä vom Biud</param>
29-
/// <param name="bitlen">Mit wefu Bits pro Pixu s Biud konstruiärt isch</param>
30-
/// <param name="frame">D datä vom Biud</param>
31-
/// <param name="destPlanes">Bruich das bim zruggäh wenn definiärt.</param>
32-
/// <returns>Än Ebini fir jedes Bit</returns>
31+
/// <param name="dim">Frame dimensions</param>
32+
/// <param name="bitlen">How many bits per pixel, i.e. how many bit planes</param>
33+
/// <param name="frame">Frame data, from top left to bottom right</param>
34+
/// <param name="destPlanes">If set, write the bit planes into this.</param>
35+
/// <returns>Array of bit plans</returns>
3336
public static byte[][] Split(Dimensions dim, int bitlen, byte[] frame, byte[][] destPlanes = null)
3437
{
3538
using (Profiler.Start("FrameUtil.Split")) {
@@ -89,16 +92,16 @@ public static byte[][] Split(Dimensions dim, int bitlen, byte[] frame, byte[][]
8992
}
9093

9194
/// <summary>
92-
/// Tuät mehreri Bit-Ebänä widr zämäfiägä.
95+
/// Joins an array of bit planes back into one single byte array where one byte represents one pixel.
9396
/// </summary>
94-
/// <param name="dim">Dimensionä vom Biud</param>
95-
/// <param name="bitPlanes">Ä Lischtä vo Ebänä zum zämäfiägä</param>
96-
/// <returns>Äs Graistuifäbiud mit sefu Bittiäfi wiä Ebänä gä wordä sind</returns>
97+
/// <param name="dim">Frame dimensions</param>
98+
/// <param name="bitPlanes">Array of bit planes</param>
99+
/// <returns>Byte array from top left to bottom right</returns>
97100
public static byte[] Join(Dimensions dim, byte[][] bitPlanes)
98101
{
99102
using (Profiler.Start("FrameUtil.Join")) {
100-
var frame = new byte[dim.Surface];
101103

104+
var frame = new byte[dim.Surface];
102105
if (bitPlanes.Length == 2) {
103106
unsafe
104107
{
@@ -135,7 +138,7 @@ public static byte[] Join(Dimensions dim, byte[][] bitPlanes)
135138
{
136139
var pfEnd = pFrame + frame.Length;
137140

138-
System.Diagnostics.Debug.Assert(bitPlanes.Length == 4);
141+
Debug.Assert(bitPlanes.Length == 4);
139142
fixed (byte* plane0 = &bitPlanes[0][0], plane1 = &bitPlanes[1][0],
140143
plane2 = &bitPlanes[2][0], plane3 = &bitPlanes[3][0])
141144
{
@@ -238,7 +241,7 @@ public static void SplitIntoRgbPlanes(char[] rgb565, int width, int numLogicalRo
238241
| (r1 & 1) << 2
239242
| (g1 & 1) << 1
240243
| (b1 & 1) << 0;
241-
var indexWithinSubframe = mapAdafruitIndex(x, y, width, height, numLogicalRows);
244+
var indexWithinSubframe = MapAdafruitIndex(x, y, width, height, numLogicalRows);
242245
var indexWithinOutput = subframe * subframeSize + indexWithinSubframe;
243246
dest[indexWithinOutput] = (byte)dotPair;
244247
r0 >>= 1;
@@ -252,7 +255,7 @@ public static void SplitIntoRgbPlanes(char[] rgb565, int width, int numLogicalRo
252255
}
253256
}
254257

255-
private static int mapAdafruitIndex(int x, int y, int width, int height, int numLogicalRows)
258+
private static int MapAdafruitIndex(int x, int y, int width, int height, int numLogicalRows)
256259
{
257260
var pairOffset = 16;
258261
var logicalRowLengthPerMatrix = 32 * 32 / 2 / numLogicalRows;
@@ -269,24 +272,6 @@ private static int mapAdafruitIndex(int x, int y, int width, int height, int num
269272
return index;
270273
}
271274

272-
/// <summary>
273-
/// Merges an array of bit planes into one single array.
274-
/// </summary>
275-
/// <param name="planes">Source planes</param>
276-
/// <param name="frame">Destination array</param>
277-
/// <param name="offset">Where to start copying at destination</param>
278-
/// <returns>True if destination array changed, false otherwise.</returns>
279-
public static bool Copy(byte[][] planes, byte[] frame, int offset)
280-
{
281-
var identical = true;
282-
foreach (var plane in planes) {
283-
identical = identical && CompareBuffers(plane, 0, frame, offset, plane.Length);
284-
Buffer.BlockCopy(plane, 0, frame, offset, plane.Length);
285-
offset += plane.Length;
286-
}
287-
return !identical;
288-
}
289-
290275
/// <summary>
291276
/// Copies a byte array to another byte array.
292277
/// </summary>
@@ -302,7 +287,6 @@ public static bool Copy(byte[] frame, byte[] dest, int offset)
302287
}
303288

304289
//Scale planes by doubling the pixels in each byte
305-
306290
public static readonly ushort[] doublePixel = {
307291
0x0000,0x0003,0x000C,0x000F,0x0030,0x0033,0x003C,0x003F,0x00C0,0x00C3,0x00CC,0x00CF,0x00F0,0x00F3,0x00FC,0x00FF,
308292
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)
410394
/// <param name="data"></param>
411395
/// <returns>scaled frame planes</returns>
412396
[Obsolete("Use Scale2x which uses more obvious parameters.")]
413-
public static byte[] Scale2xUgh(Dimensions dim, byte[] data)
397+
public static byte[] Scale2xObsolete(Dimensions dim, byte[] data)
414398
{
415399
byte[] scaledData = new byte[dim.Surface];
416400

@@ -542,10 +526,11 @@ public static byte[] Scale2XRgb(Dimensions dim, byte[] data)
542526
/// <param name="dim"></param>
543527
/// <param name="data"></param>
544528
/// <returns></returns>
545-
public static byte[][] Scale2x(Dimensions dim, byte[][] data)
529+
[Obsolete]
530+
public static byte[][] Scale2xObsolete(Dimensions dim, byte[][] data)
546531
{
547532
var joinData = Join(dim, data);
548-
var frameData = Scale2xUgh(dim, joinData);
533+
var frameData = Scale2xObsolete(dim, joinData);
549534
return Split(dim, data.Length, frameData);
550535
}
551536

@@ -603,12 +588,6 @@ public static byte[] ConvertGrayToGray(byte[] srcFrame, params byte[] mapping)
603588
}
604589
}
605590

606-
public static DmdFrame ConvertToRgb24(Dimensions dim, byte[][] planes, Color[] palette)
607-
{
608-
var frame = Join(dim, planes);
609-
return ColorUtil.ColorizeObsolete(dim, frame, palette);
610-
}
611-
612591
public static byte[] NewPlane(Dimensions dim)
613592
{
614593
var count = dim.Width / 8 * dim.Height;
@@ -661,7 +640,7 @@ public static void OrPlane(byte[] plane, byte[] target)
661640
public static byte[] CombinePlaneWithMask(byte[] planeA, byte[] planeB, byte[] mask)
662641
{
663642
var length = planeA.Length;
664-
System.Diagnostics.Debug.Assert(length == planeB.Length && length == mask.Length);
643+
Debug.Assert(length == planeB.Length && length == mask.Length);
665644
byte[] outPlane = new byte[length];
666645

667646
unchecked
@@ -881,11 +860,6 @@ public static unsafe bool CompareBuffers(byte[] buffer1, int offset1, byte[] buf
881860
}
882861
}
883862

884-
public static bool CompareBuffersSlow(byte[] a, byte[] b)
885-
{
886-
return a != null && CompareBuffers(a, 0, b, 0, a.Length);
887-
}
888-
889863
/// <summary>
890864
/// Fast byte array comparison, courtesy to https://stackoverflow.com/questions/43289/comparing-two-byte-arrays-in-net/8808245#8808245
891865
///

LibDmd/Converter/Pin2Color/Animation.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ private byte[][] RenderLCM(byte[][] vpmFrame)
194194
}
195195
else
196196
{
197-
vpmFrame = FrameUtil.Scale2x(Size, vpmFrame);
197+
vpmFrame = FrameUtil.Scale2xObsolete(Size, vpmFrame);
198198
}
199199
}
200200

0 commit comments

Comments
 (0)