public FileModel Demux(IImageWrapper source)
        {
            Pixel[,] dataSet = new Pixel[source.Width, source.Height];

            for (var x = 0; x < source.Width; ++x)
            {
                for (var y = 0; y < source.Height; ++y)
                {
                    Pixel p = new Pixel();
                    p.R = source.GetRedAtPosition(x, y);
                    p.G = source.GetGreenAtPosition(x, y);
                    p.B = source.GetBlueAtPosition(x, y);

                    dataSet[x, y] = p;
                }
            }

            return(demuxxImage(dataSet));
        }
        private Pixel[,] muxxImageAndFile(FileModel fileModel, IImageWrapper container)
        {
            // warning, this is very verbose code.
            // i could have written this in linq much more elegantly,
            // however i wrote this so that others can pick it up without needing to know c# / linq / lambdas

            if (!this.encryptionFeasible(fileModel, container))
            {
                throw new Exception("image is too small to hide this file in");
            }

            int byteSpread    = this.getByteSpread();
            int bitShiftCount = (BITS_PER_CHANNEL - BITS_USED_PER_CHANNEL);
            int maxValue      = (0x01 << BITS_PER_CHANNEL) - 0x01;
            int partMask      = maxValue >> bitShiftCount;
            int visibleMask   = (byte)(maxValue << (0x08 - bitShiftCount));

            var header = new EncryptionHeader();

            header.FileName = fileModel.FileName;
            header.FileSize = (int)fileModel.FileContents.Length;

            var headerBytesUnspread = header.ToBytes();
            var fileBytesUnspread   = new byte[fileModel.FileContents.Length];

            fileModel.FileContents.Read(fileBytesUnspread, 0, (int)fileModel.FileContents.Length);

            var unspreadBytes = new byte[headerBytesUnspread.Length + fileBytesUnspread.Length];
            var spreadBytes   = new byte[unspreadBytes.Length * byteSpread];

            int unspreadIndex = 0;

            for (var i = 0; i < headerBytesUnspread.Length; ++i)
            {
                unspreadBytes [unspreadIndex] = headerBytesUnspread [i];
                ++unspreadIndex;
            }

            for (var i = 0; i < fileBytesUnspread.Length; ++i)
            {
                unspreadBytes [unspreadIndex] = fileBytesUnspread [i];
                ++unspreadIndex;
            }

            int spreadIndex = 0;

            for (var i = 0; i < unspreadBytes.Length; ++i)
            {
                var toSpread = unspreadBytes [i];

                for (var s = 0; s < byteSpread; ++s)                 // going least significant bit to most
                {
                    var spreadShift = s * BITS_USED_PER_CHANNEL;
                    var shifted     = toSpread >> spreadShift;
                    var masked      = shifted & partMask;

                    spreadBytes [spreadIndex] = (byte)masked;
                    ++spreadIndex;
                }
            }

            // sanity check
            var maxSpreadValue = (0x01 << (BITS_USED_PER_CHANNEL)) - 1;

            for (var i = 0; i < spreadBytes.Length; ++i)
            {
                var sb = spreadBytes [i];
                if (sb > maxSpreadValue)
                {
                    throw new Exception("the author is a moron");
                }
            }

            Pixel[,] dataSet = new Pixel[container.Width, container.Height];

            var numberOfChannels = Enum.GetValues(typeof(LosslessFileMuxxer.ByteOrder)).GetLength(0);
            var channelIndex     = 0;

            spreadIndex = 0;
            var rand = new Random(Guid.NewGuid().GetHashCode());
            var randomiseNonDatasetBytes = false;

            for (var x = 0; x < container.Width; ++x)
            {
                for (var y = 0; y < container.Height; ++y)
                {
                    Pixel p = new Pixel();
                    p.R = container.GetRedAtPosition(x, y);
                    p.G = container.GetGreenAtPosition(x, y);
                    p.B = container.GetBlueAtPosition(x, y);

                    if (spreadIndex < spreadBytes.Length)
                    {
                        for (var c = 0; c < numberOfChannels; ++c)
                        {
                            if (spreadIndex < spreadBytes.Length)
                            {
                                var byteToHide   = spreadBytes [spreadIndex];
                                var whichChannel = (ByteOrder)channelIndex;
                                switch (whichChannel)
                                {
                                case ByteOrder.Red:
                                    p.R = (byte)(((int)p.R & visibleMask) + byteToHide);
                                    break;

                                case ByteOrder.Green:
                                    p.G = (byte)(((int)p.G & visibleMask) + byteToHide);
                                    break;

                                case ByteOrder.Blue:
                                    p.B = (byte)(((int)p.B & visibleMask) + byteToHide);
                                    break;
                                }

                                channelIndex = channelIndex == numberOfChannels - 1 ? 0 : channelIndex + 1;
                                ++spreadIndex;
                            }
                            else
                            {
                                if (randomiseNonDatasetBytes)
                                {
                                    // not a data pixel, give it a random value
                                    var newVal = rand.Next(0, maxSpreadValue);
                                    p.R    = (byte)(((int)p.R & visibleMask) + newVal);
                                    newVal = rand.Next(0, maxSpreadValue);
                                    p.G    = (byte)(((int)p.G & visibleMask) + newVal);
                                    newVal = rand.Next(0, maxSpreadValue);
                                    p.B    = (byte)(((int)p.B & visibleMask) + newVal);
                                }
                            }
                        }
                    }
                    else
                    {
                        if (randomiseNonDatasetBytes)
                        {
                            // not a data pixel, give it a random value
                            var newVal = rand.Next(0, maxSpreadValue);
                            p.R    = (byte)(((int)p.R & visibleMask) + newVal);
                            newVal = rand.Next(0, maxSpreadValue);
                            p.G    = (byte)(((int)p.G & visibleMask) + newVal);
                            newVal = rand.Next(0, maxSpreadValue);
                            p.B    = (byte)(((int)p.B & visibleMask) + newVal);
                        }
                    }

                    dataSet [x, y] = p;
                }
            }

            return(dataSet);
        }