private ConsoleBitmapDiffFrame PrepareDiffFrame(ConsoleBitmap bitmap)
        {
            ConsoleBitmapDiffFrame diff = new ConsoleBitmapDiffFrame();

            diff.Diffs = new List <ConsoleBitmapPixelDiff>();
            int changes = 0;

            for (int y = 0; y < bitmap.Height; y++)
            {
                for (int x = 0; x < bitmap.Width; x++)
                {
                    var pixel = bitmap.GetPixel(x, y);
                    if (pixel.HasChanged)
                    {
                        changes++;
                        if (pixel.Value.HasValue)
                        {
                            diff.Diffs.Add(new ConsoleBitmapPixelDiff()
                            {
                                X     = x,
                                Y     = y,
                                Value = pixel.Value.Value
                            });
                        }
                    }
                }
            }

            return(diff);
        }
Example #2
0
        private ConsoleBitmapDiffFrame PrepareDiffFrame(ConsoleBitmapRawFrame previous, ConsoleBitmap bitmap)
        {
            ConsoleBitmapDiffFrame diff = new ConsoleBitmapDiffFrame();

            diff.Diffs = new List <ConsoleBitmapPixelDiff>();
            int changes = 0;

            for (int y = 0; y < GetEffectiveHeight(bitmap); y++)
            {
                for (int x = 0; x < GetEffectiveWidth(bitmap); x++)
                {
                    var pixel            = bitmap.GetPixel(GetEffectiveLeft + x, GetEffectiveTop + y);
                    var hasPreviousPixel = previous.Pixels.Length == GetEffectiveWidth(bitmap) && previous.Pixels[0].Length == GetEffectiveHeight(bitmap);
                    var previousPixel    = hasPreviousPixel ? previous.Pixels[x][y] : default(ConsoleCharacter);

                    if (pixel.HasChanged || hasPreviousPixel == false || (pixel.Value.HasValue && pixel.Value.Value.Equals(previousPixel) == false))
                    {
                        changes++;
                        if (pixel.Value.HasValue)
                        {
                            diff.Diffs.Add(new ConsoleBitmapPixelDiff()
                            {
                                X     = x,
                                Y     = y,
                                Value = pixel.Value.Value
                            });
                        }
                    }
                }
            }

            return(diff);
        }
Example #3
0
        /// <summary>
        /// Serializes the given diff frame.
        ///
        /// A serialized diff frame is always a single line with this structure:
        ///
        /// All data values are surrounded in square brackets like [dataValue]
        ///
        /// Segment1 - Timestamp in the format: [$timestampInTicks$] where $timestampInTicks$ represents a 64 bit non-negative integer
        /// Segment2 - The type of frame, in this case [Diff]
        /// Segment3 - The diff data
        ///
        ///     The first pixel will be preceded by color markers for foreground (e.g. [F=Red]) and background (e.g. [B=Red]) which means that subsequence characters have those color characteristics.
        ///     If the next pixel is a different foreground and/or background color then there will be color markers for those changes in between the pixel data values
        ///     If the next pixel shares the same foreground and background then there will be no color markers in between those pixels. This saves space.
        ///     Diff pixels are surrounded in square brackets in this format: [xCoordinate,yCoordinate,pixelValue].
        ///     pixelValue is generally a single character, but square brackets are encoded a OB for the opening bracket and CB for the closing bracket
        ///
        /// </summary>
        /// <param name="frame">a raw frame</param>
        /// <returns>a serialized string</returns>
        public string SerializeFrame(ConsoleBitmapDiffFrame frame)
        {
            StringBuilder builder = new StringBuilder();

            builder.Append($"[{frame.Timestamp.Ticks}]");
            builder.Append("[Diff]");

            ConsoleColor?lastFg = null;
            ConsoleColor?lastBg = null;

            foreach (var diff in frame.Diffs)
            {
                if (lastFg.HasValue == false || lastFg.Value != diff.Value.ForegroundColor)
                {
                    lastFg = diff.Value.ForegroundColor;
                    builder.Append($"[F={lastFg}]");
                }

                if (lastBg.HasValue == false || lastBg.Value != diff.Value.BackgroundColor)
                {
                    lastBg = diff.Value.BackgroundColor;
                    builder.Append($"[B={lastBg}]");
                }

                string appendValue;
                var    pixelCharValue = diff.Value.Value;
                if (pixelCharValue == '[')
                {
                    appendValue = "OB";
                }
                else if (pixelCharValue == ']')
                {
                    appendValue = "CB";
                }
                else
                {
                    appendValue = pixelCharValue + "";
                }

                builder.Append($"[{diff.X},{diff.Y},{appendValue}]");
            }
            builder.AppendLine();
            var ret = builder.ToString();

            return(ret);
        }
Example #4
0
        /// <summary>
        /// Deserializes the given frame given a known width and height.
        /// </summary>
        /// <param name="serializedFrame">the frame data</param>
        /// <param name="width">the known width of the frame</param>
        /// <param name="height">the known height of the frame</param>
        /// <returns>a deserialized frame that's either a raw frame or a diff frame, depending on what was in the serialized string</returns>
        public ConsoleBitmapFrame DeserializeFrame(string serializedFrame, int width, int height)
        {
            var tokens = tokenizer.Tokenize(serializedFrame);
            var reader = new TokenReader<Token>(tokens);

            reader.Expect("[");
            var timestampToken = reader.Advance();
            var timestamp = new TimeSpan(long.Parse(timestampToken.Value));
            reader.Expect("]");

            reader.Expect("[");
            reader.Advance();
            var isDiff = reader.Current.Value == "Diff";
            reader.Expect("]");

            if (isDiff)
            {
                var diffFrame = new ConsoleBitmapDiffFrame()
                {
                    Timestamp = timestamp,
                    Diffs = new System.Collections.Generic.List<ConsoleBitmapPixelDiff>()
                };

                var lastBackground = ConsoleString.DefaultBackgroundColor;
                var lastForeground = ConsoleString.DefaultForegroundColor;
                while (reader.CanAdvance(skipWhitespace: true))
                {
                    reader.Expect("[", skipWhiteSpace: true);
                    if (reader.Peek().Value.StartsWith("F=") || reader.Peek().Value.StartsWith("B="))
                    {
                        reader.Advance();
                        var match = ColorSpecifierRegex.Match(reader.Current.Value);
                        if (match.Success == false) throw new FormatException($"Unexpected token {reader.Current.Value} at position {reader.Current.Position} ");

                        var isForeground = match.Groups["ForB"].Value == "F";

                        if (isForeground)
                        {
                            if (Enum.TryParse(match.Groups["color"].Value, out ConsoleColor c))
                            {
                                lastForeground = (RGB)c;
                            }
                            else if (RGB.TryParse(match.Groups["color"].Value, out lastForeground) == false)
                            {
                                throw new ArgumentException($"Expected a color @ {reader.Position}");
                            }
                        }
                        else
                        {
                            if (Enum.TryParse(match.Groups["color"].Value, out ConsoleColor c))
                            {
                                lastBackground = (RGB)c;
                            }
                            else if (RGB.TryParse(match.Groups["color"].Value, out lastBackground) == false)
                            {
                                throw new ArgumentException($"Expected a color @ {reader.Position}");
                            }
                        }

                        reader.Expect("]");
                    }
                    else
                    {
                        var match = PixelDiffRegex.Match(reader.Advance().Value);
                        if (match.Success == false) throw new FormatException("Could not parse pixel diff");

                        var valGroup = match.Groups["val"].Value;

                        char? nextChar = valGroup.Length == 1 ? valGroup[0] : valGroup == "OB" ? '[' : valGroup == "CB" ? ']' : new char?();
                        if (nextChar.HasValue == false) throw new FormatException($"Unexpected token {nextChar} @ {reader.Position}");

                        diffFrame.Diffs.Add(new ConsoleBitmapPixelDiff()
                        {
                            X = int.Parse(match.Groups["x"].Value),
                            Y = int.Parse(match.Groups["y"].Value),
                            Value = new ConsoleCharacter(nextChar.Value, lastForeground, lastBackground),
                        });

                        reader.Expect("]");
                    }
                }

                return diffFrame;
            }
            else
            {
                var rawFrame = new ConsoleBitmapRawFrame()
                {
                    Timestamp = timestamp,
                    Pixels = new ConsoleCharacter[width][]
                };

                for (var i = 0; i < width; i++)
                {
                    rawFrame.Pixels[i] = new ConsoleCharacter[height];
                }

                var x = 0;
                var y = 0;
                var lastFg = ConsoleString.DefaultForegroundColor;
                var lastBg = ConsoleString.DefaultBackgroundColor;
                while (reader.CanAdvance(skipWhitespace:true))
                {
                    reader.Expect("[", skipWhiteSpace:true);
                    var next = reader.Advance();
                    var match = ColorSpecifierRegex.Match(next.Value);
                    if (match.Success)
                    {  
                        var isForeground = match.Groups["ForB"].Value == "F";

                        if (isForeground)
                        {

                            if(Enum.TryParse<ConsoleColor>(match.Groups["color"].Value, out ConsoleColor c))
                            {
                                lastFg = c;
                            }
                            else if(RGB.TryParse(match.Groups["color"].Value, out lastFg) == false)
                            {
                                throw new ArgumentException($"Expected a color @ {reader.Position}");
                            }
                        }
                        else
                        {
                            if (Enum.TryParse<ConsoleColor>(match.Groups["color"].Value, out ConsoleColor c))
                            {
                                lastBg = c;
                            }
                            else if (RGB.TryParse(match.Groups["color"].Value, out lastBg) == false)
                            {
                                throw new ArgumentException($"Expected a color @ {reader.Position}");
                            }
                        }
                    }
                    else
                    {
                        char? nextChar = next.Value.Length == 1 ? next.Value[0] : next.Value == "OB" ? '[' : next.Value == "CB" ? ']' : new char?();
                        if (nextChar.HasValue == false) throw new FormatException($"Unexpected token {nextChar} @ {next.Position}");
                        rawFrame.Pixels[x][y++] = new ConsoleCharacter(nextChar.Value, lastFg, lastBg);
                        if (y == height)
                        {
                            y = 0;
                            x++;
                        }
                    }

                    reader.Expect("]");
                }

                return rawFrame;
            }
        }