private CompressedFrame CalculateFrameChanges(Frame[] frames, FrameType type, int frameNumber)
        {
            int x_inc = 0;
            int y_inc = 0;
            switch (type)
            {
                case FrameType.TransitionalLeft:
                    x_inc = -1;
                    break;
                case FrameType.TransitionalRight:
                    x_inc = 1;
                    break;
                case FrameType.TransitionalTop:
                    y_inc = -1;
                    break;
                case FrameType.TransitionalBottom:
                    y_inc = 1;
                    break;
            }

            var currentFrame = frames[frameNumber];
            var prevFrame = frames[frameNumber - 1];
            var changesPos = new List<Tuple<int, int>>();

            for (int y = 0; y < FrameHeight; y++)
            {
                if (y + y_inc >= 0 && y + y_inc < FrameHeight)
                {
                    var line = currentFrame.Lines[y + y_inc];
                    var prevLine = prevFrame.Lines[y];
                    for (int x = 0; x < FrameWidth; x++)
                    {
                        if (x + x_inc >= 0 && x + x_inc < FrameWidth)
                        {
                            if (line[x + x_inc] != prevLine[x])
                                changesPos.Add(new Tuple<int, int>(x + x_inc, y + y_inc));
                        }
                    }
                }
            }

            var frameChanges = new List<FrameChange>();

            var markedChanges = new bool[FrameHeight, FrameWidth];
            if (type == FrameType.TransitionalLeft || type == FrameType.TransitionalRight)
            {
                int x = type == FrameType.TransitionalLeft ? FrameWidth - 1 : 0;
                var chars = new List<char>();
                for (int y = 0; y < FrameHeight; y++)
                {
                    markedChanges[y, x] = true;
                    chars.Add(currentFrame.Lines[y][x]);
                }
                frameChanges.Add(new FrameChange { Type = FrameChangeType.Vertical, X = x, Y = 0, Chars = chars, Length = FrameHeight });
            }
            else if (type == FrameType.TransitionalTop || type == FrameType.TransitionalBottom)
            {
                int y = type == FrameType.TransitionalTop ? FrameHeight - 1 : 0;
                var chars = new List<char>();
                for (int x = 0; x < FrameWidth; x++)
                {
                    markedChanges[y, x] = true;
                    chars.Add(currentFrame.Lines[y][x]);
                }
                frameChanges.Add(new FrameChange { Type = FrameChangeType.Horizontal, X = 0, Y = y, Chars = chars, Length = FrameWidth });
            }

            foreach (var changePos in changesPos)
            {
                if (!markedChanges[changePos.Item2, changePos.Item1])
                {
                    int lineXInd = FrameWidth;
                    for (int x = changePos.Item1 + 1; x < FrameWidth; x++)
                        if (changesPos.FirstOrDefault(pos => pos.Item1 == x && pos.Item2 == changePos.Item2) == null)
                        {
                            lineXInd = x;
                            break;
                        }

                    int lineYInd = FrameHeight;
                    for (int y = changePos.Item2 + 1; y < FrameHeight; y++)
                        if (changesPos.FirstOrDefault(pos => pos.Item1 == changePos.Item1 && pos.Item2 == y) == null)
                        {
                            lineYInd = y;
                            break;
                        }

                    var frameChange = new FrameChange
                    {
                        X = changePos.Item1,
                        Y = changePos.Item2
                    };
                    if (lineXInd - changePos.Item1 >= lineYInd - changePos.Item2)
                    {
                        if (lineXInd - changePos.Item1 == 1)
                        {
                            frameChange.Type = FrameChangeType.One;
                            frameChange.Length = 1;
                            frameChange.Chars = new List<char>()
                                {
                                    currentFrame.Lines[changePos.Item2][changePos.Item1],
                                };
                            markedChanges[changePos.Item2, changePos.Item1] = true;
                        }
                        else
                        {
                            frameChange.Type = FrameChangeType.Horizontal;
                            frameChange.Length = lineXInd - changePos.Item1;
                            frameChange.Chars = new List<char>();
                            for (int x = 0; x < frameChange.Length; x++)
                            {
                                frameChange.Chars.Add(currentFrame.Lines[changePos.Item2][changePos.Item1 + x]);
                                markedChanges[changePos.Item2, changePos.Item1 + x] = true;
                            }
                        }
                    }
                    else
                    {
                        frameChange.Type = FrameChangeType.Vertical;
                        frameChange.Length = lineYInd - changePos.Item2;
                        frameChange.Chars = new List<char>();
                        for (int y = 0; y < frameChange.Length; y++)
                        {
                            frameChange.Chars.Add(currentFrame.Lines[changePos.Item2 + y][changePos.Item1]);
                            markedChanges[changePos.Item2 + y, changePos.Item1] = true;
                        }
                    }

                    frameChanges.Add(frameChange);
                }
            }

            return new CompressedFrame { FrameChanges = frameChanges, FrameType = type };
        }
        private byte[] GetCompressedFrameBytes(HuffmanTree tree, Frame frame, CompressedFrame compressedFrame)
        {
            int transBitPos = 0;
            var transZipBytes = new byte[frame.Bytes.Length * 10];
            Utils.AddInt(transZipBytes, ref transBitPos, frame.RepeatCount, 7);
            Utils.AddInt(transZipBytes, ref transBitPos, (int)compressedFrame.FrameType, 3);
            Utils.AddInt(transZipBytes, ref transBitPos, compressedFrame.FrameChanges.Count, 7);
            for (int i = 0; i < compressedFrame.FrameChanges.Count; i++)
            {
                var change = compressedFrame.FrameChanges[i];
                Utils.AddInt(transZipBytes, ref transBitPos, (int)change.Type, 2);
                Utils.AddInt(transZipBytes, ref transBitPos, GetPos(change.X, change.Y), 10);

                if (change.Type == FrameChangeType.One)
                    Utils.AddIntReversed(transZipBytes, ref transBitPos, tree.CompressedBytes[change.Chars[0]]);
                else
                {
                    Utils.AddInt(transZipBytes, ref transBitPos, change.Length,
                        change.Type == FrameChangeType.Horizontal ? 7 : 4);
                    //HuffmanRle.Encode(tree, change.Chars.Select(c => (byte)c).ToArray(), ref transBitPos, transZipBytes, 5, 4);
                    for (int j = 0; j < change.Chars.Count; j++)
                        Utils.AddIntReversed(transZipBytes, ref transBitPos, tree.CompressedBytes[change.Chars[j]]);
                    /*if (i == 0 && compressedFrame.FrameType != FrameType.Basic && compressedFrame.FrameType != FrameType.Transitional)
                        HuffmanRle.Encode(tree, change.Chars.Select(c => (byte)c).ToArray(), ref transBitPos, transZipBytes, 5, 4);
                    else
                        for (int j = 0; j < change.Chars.Count; j++)
                            Utils.AddIntReversed(transZipBytes, ref transBitPos, tree.CompressedBytes[change.Chars[j]]);*/
                }
            }
            return transZipBytes.Take((transBitPos + 7) / 8).ToArray();
        }
 private Dictionary<FrameType, CompressedFrame> CalculateChangesForDifferentChangeTypes(Frame[] frames, int frameNumber)
 {
     var changes = new Dictionary<FrameType, CompressedFrame>();
     changes.Add(FrameType.Transitional, CalculateFrameChanges(Frames, FrameType.Transitional, frameNumber));
     changes.Add(FrameType.TransitionalLeft, CalculateFrameChanges(Frames, FrameType.TransitionalLeft, frameNumber));
     changes.Add(FrameType.TransitionalRight, CalculateFrameChanges(Frames, FrameType.TransitionalRight, frameNumber));
     changes.Add(FrameType.TransitionalTop, CalculateFrameChanges(Frames, FrameType.TransitionalTop, frameNumber));
     changes.Add(FrameType.TransitionalBottom, CalculateFrameChanges(Frames, FrameType.TransitionalBottom, frameNumber));
     return changes;
 }