private static Dictionary <string, byte>[] GetHuffmanCodeValueMapsArray(DhtHeader dhtHeader)
        {
            int count = dhtHeader.HuffmanTables.Length;

            Dictionary <string, byte>[] huffmanCodeValueMapsArray = new Dictionary <string, byte> [count];
            int tableIndex = 0;

            for (int i = 0; i < count; i++)
            {
                huffmanCodeValueMapsArray[i] = dhtHeader.HuffmanTables[tableIndex].GetHuffmanCodeValueMap();
                tableIndex++;
            }
            return(huffmanCodeValueMapsArray);
        }
        public static IntegerMatrix ReadScanData(byte[] scanDataBytes, DhtHeader dhtHeader, Sof3Header sof3Header, SosHeader sosHeader, UInt16[] slices)
        {
            int           columnsCount = GetImageWidth(slices);
            int           rowsCount = sof3Header.LinesNumber;
            int           rowIndex = 0, columnIndex = 0;
            IntegerMatrix resMatrix = new IntegerMatrix(rowsCount, columnsCount);

            ushort normalSliceWidth = slices[1];

            List <int> decompressionValueList = new List <int>();

            Dictionary <string, byte>[] huffmanCodeValueMapsArray = RawReader.GetHuffmanCodeValueMapsArray(dhtHeader);

            BitArray scanDataBitArray = RawReader.GetScanDataBitArray(scanDataBytes);

            int      index = 0, huffmanCodeIndex = 0;
            BitArray huffmanCodeBitArray = new BitArray(1);
            string   huffmanCodeString   = null;
            byte     huffmanValue        = 0;

            int componentIndex = 0;
            int componentValueIndex = 0, componentValue = 0;

            int[] previousComponentValues = RawReader.GetInitialComponentValues(sof3Header);

            int lastIndex = GetLastDecompressionValueIndex(sof3Header, slices);
            int lastSliceFirstDecompressionValueIndex = GetLastSliceFirstDecompressionValueIndex(sof3Header, slices);
            int sliceSize = slices[1] * sof3Header.LinesNumber;

            Dictionary <string, byte> huffmanCodeValueMap = huffmanCodeValueMapsArray[GetHuffmanTableIndex(rowIndex, columnIndex, sosHeader)];

            while (componentValueIndex <= lastIndex && index < scanDataBitArray.Length)
            {
                huffmanCodeBitArray[huffmanCodeIndex] = scanDataBitArray[index];
                huffmanCodeString = huffmanCodeBitArray.ToBitString();

                if (huffmanCodeValueMap.TryGetValue(huffmanCodeString, out huffmanValue))
                {
                    int differenceCodeValue = 0;

                    index++;
                    if (huffmanValue > 0)
                    {
                        //Read difference code
                        int finalIndex = index + huffmanValue - 1;
                        differenceCodeValue = GetDifferenceCodeValue(scanDataBitArray, index, finalIndex, huffmanValue);
                        index = finalIndex + 1;
                    }
                    else
                    {
                        differenceCodeValue = 0;
                    }

                    componentValue = previousComponentValues[componentIndex] + differenceCodeValue;
                    previousComponentValues[componentIndex] = componentValue;

                    decompressionValueList.Add(componentValue);
                    resMatrix[rowIndex, columnIndex] = componentValue;
                    componentValueIndex++;

                    huffmanCodeBitArray = new BitArray(1);
                    huffmanCodeIndex    = 0;

                    rowIndex    = GetNextRowIndex(componentValueIndex, lastSliceFirstDecompressionValueIndex, sliceSize, slices);
                    columnIndex = GetNextColumnIndex(componentValueIndex, lastSliceFirstDecompressionValueIndex, sliceSize, slices);

                    huffmanCodeValueMap = huffmanCodeValueMapsArray[GetHuffmanTableIndex(rowIndex, columnIndex, sosHeader)];

                    bool initComponentValuesRequired =
                        (columnIndex == 0) ||
                        ((columnIndex > 0) && (columnIndex % normalSliceWidth == 0));

                    if (initComponentValuesRequired)
                    {
                        previousComponentValues = GetInitialComponentValues(sof3Header);
                    }

                    componentIndex = GetComponentIndex(rowIndex, columnIndex);
                }
                else
                {
                    //Read huffman code
                    index++;
                    huffmanCodeIndex++;
                    huffmanCodeBitArray.Length++;
                }
            }

            return(resMatrix);
        }
        public static IntegerMatrix ReadImageFromFile(string filePath)
        {
            byte[] fileBytes = File.ReadAllBytes(filePath);

            List <HeaderItemDescriptor> headerItemList = HeaderItemDescriptor.GetHeaderItemDescriptorList();

            #region Byte Order

            //Byte order
            HeaderItemDescriptor headerItemByteOrder =
                headerItemList.FirstOrDefault(item => item.Name == HeaderItemDescriptor.ITEM_BYTE_ORDER);
            byte[] headerItemByteOrderBytes = RawReader.ReadHeaderItem(fileBytes, headerItemByteOrder);

            #endregion

            //First IFD
            HeaderItemDescriptor headerItemFirstIFD =
                headerItemList.FirstOrDefault(item => item.Name == HeaderItemDescriptor.ITEM_TIFF_OFFSET);
            byte[] headerItemFirstIFDBytes = RawReader.ReadHeaderItem(fileBytes, headerItemFirstIFD);

            List <IFD_0_EntryDescriptor> firstIFDEntryDescriptorList = IFD_0_EntryDescriptor.GetEntryDescriptorList();

            //Compression
            int    compressionEntryIndex             = firstIFDEntryDescriptorList.FindIndex(item => item.Name == IFD_0_EntryDescriptor.COMPRESSION);
            UInt32 localCompressionOffset            = ImageFileDirectory.GetImageFileDirectoryEntryOffset(Convert.ToUInt32(compressionEntryIndex));
            UInt32 compressionOffset                 = BitConverter.ToUInt32(headerItemFirstIFDBytes, 0) + localCompressionOffset;
            ImageFileDirectoryEntry compressionEntry = RawReader.ReadImageFileDirectoryEntry(fileBytes, compressionOffset);
            UInt16 compressionValue = compressionEntry.GetUInt16Value();

            //Make
            int    makeEntryIndex             = firstIFDEntryDescriptorList.FindIndex(item => item.Name == IFD_0_EntryDescriptor.MAKE);
            UInt32 localMakeOffset            = ImageFileDirectory.GetImageFileDirectoryEntryOffset(Convert.ToUInt32(makeEntryIndex));
            UInt32 makeOffset                 = BitConverter.ToUInt32(headerItemFirstIFDBytes, 0) + localMakeOffset;
            ImageFileDirectoryEntry makeEntry = RawReader.ReadImageFileDirectoryEntry(fileBytes, makeOffset);
            UInt32 makeOffseValue             = makeEntry.GetUInt32Value();
            byte[] makeBytes = RawReader.ReadBytesUntilEndingZero(fileBytes, makeOffseValue);
            string makeValue = ConvertHelper.ConvertToString(makeBytes);

            //Model
            int    modelEntryIndex             = firstIFDEntryDescriptorList.FindIndex(item => item.Name == IFD_0_EntryDescriptor.MODEL);
            UInt32 localModelOffset            = ImageFileDirectory.GetImageFileDirectoryEntryOffset(Convert.ToUInt32(modelEntryIndex));
            UInt32 modelOffset                 = BitConverter.ToUInt32(headerItemFirstIFDBytes, 0) + localModelOffset;
            ImageFileDirectoryEntry modelEntry = RawReader.ReadImageFileDirectoryEntry(fileBytes, modelOffset);
            UInt32 modelOffsetValue            = modelEntry.GetUInt32Value();
            byte[] modelBytes = RawReader.ReadBytesUntilEndingZero(fileBytes, modelOffsetValue);
            string modelValue = ConvertHelper.ConvertToString(modelBytes);

            //X Resolution
            int    xResolutionEntryIndex             = firstIFDEntryDescriptorList.FindIndex(item => item.Name == IFD_0_EntryDescriptor.X_RESOLUTION);
            UInt32 localXResolutionOffset            = ImageFileDirectory.GetImageFileDirectoryEntryOffset(Convert.ToUInt32(xResolutionEntryIndex));
            UInt32 xResolutionOffset                 = BitConverter.ToUInt32(headerItemFirstIFDBytes, 0) + localXResolutionOffset;
            ImageFileDirectoryEntry xResolutionEntry = RawReader.ReadImageFileDirectoryEntry(fileBytes, xResolutionOffset);
            UInt32 xResolutionOffsetValue            = xResolutionEntry.GetUInt32Value();
            byte[] xResolutionBytes = RawReader.ReadBytes(fileBytes, xResolutionOffsetValue, 4);
            UInt32 xResolutionValue = ConvertHelper.ConvertToUInt32(xResolutionBytes);

            //DateTime
            int    dateTimeEntryIndex             = firstIFDEntryDescriptorList.FindIndex(item => item.Name == IFD_0_EntryDescriptor.DATE_TIME);
            UInt32 localDateTimeOffset            = ImageFileDirectory.GetImageFileDirectoryEntryOffset(Convert.ToUInt32(dateTimeEntryIndex));
            UInt32 dateTimeOffset                 = BitConverter.ToUInt32(headerItemFirstIFDBytes, 0) + localDateTimeOffset;
            ImageFileDirectoryEntry dateTimeEntry = RawReader.ReadImageFileDirectoryEntry(fileBytes, dateTimeOffset);
            UInt32   dateTimeOffsetValue          = dateTimeEntry.GetUInt32Value();
            byte[]   dateTimeBytes                = RawReader.ReadBytesUntilEndingZero(fileBytes, dateTimeOffsetValue);
            string   dateTimeValue                = ConvertHelper.ConvertToString(dateTimeBytes);
            DateTime dateTimeStruct               = DateTime.ParseExact(dateTimeValue, "yyyy:MM:dd HH:mm:ss", CultureInfo.InvariantCulture);

            //EXIF bytes
            IFD_0_EntryDescriptor exifEntryDescriptor =
                firstIFDEntryDescriptorList.Where(item => item.Name == IFD_0_EntryDescriptor.EXIF).FirstOrDefault();
            uint exifOffset = RawReader.GetEntryOffsetByTagID(fileBytes, exifEntryDescriptor.TagID);
            ImageFileDirectoryEntry exifOffsetEntry = RawReader.ReadImageFileDirectoryEntry(fileBytes, exifOffset);
            UInt32 exifOffsetValue = exifOffsetEntry.GetUInt32Value();
            byte[] exifBytes       = RawReader.ReadBytes(fileBytes, exifOffsetValue);

            //Makernote bytes
            List <EXIF_EntryDescriptor> exifEntryDescriptorList  = EXIF_EntryDescriptor.GetEntryDescriptorList();
            EXIF_EntryDescriptor        makerNoteEntryDescriptor =
                exifEntryDescriptorList.Where(item => item.Name == EXIF_EntryDescriptor.MAKER_NOTE).FirstOrDefault();
            uint makerNoteOffset = RawReader.GetEntryOffsetByTagID(fileBytes, makerNoteEntryDescriptor.TagID);
            ImageFileDirectoryEntry makerNoteOffsetEntry = RawReader.ReadImageFileDirectoryEntry(fileBytes, makerNoteOffset);
            UInt32 makerNoteOffsetValue = makerNoteOffsetEntry.GetUInt32Value();
            byte[] makerNoteBytes       = RawReader.ReadBytes(fileBytes, makerNoteOffsetValue);

            //Color Balance
            List <Makernote_EntryDescriptor> makernoteEntryDescriptorList = Makernote_EntryDescriptor.GetEntryDescriptorList();
            Makernote_EntryDescriptor        colorBalanceEntryDescriptor  =
                makernoteEntryDescriptorList.Where(item => item.Name == Makernote_EntryDescriptor.COLOR_BALANCE).FirstOrDefault();
            uint colorBalanceOffset = RawReader.GetEntryOffsetByTagID(makerNoteBytes, colorBalanceEntryDescriptor.TagID);
            ImageFileDirectoryEntry colorBalanceEntry = RawReader.ReadImageFileDirectoryEntry(makerNoteBytes, colorBalanceOffset);
            UInt32 colorBalanceOffsetValue            = colorBalanceEntry.GetUInt32Value();


            //Raw Data IFD
            HeaderItemDescriptor headerItemRawIFD =
                headerItemList.FirstOrDefault(item => item.Name == HeaderItemDescriptor.ITEM_RAW_IFD_OFFSET);
            byte[] headerItemRawBytes = RawReader.ReadHeaderItem(fileBytes, headerItemRawIFD);
            List <IFD_3_EntryDescriptor> rawEntryDescriptorList = IFD_3_EntryDescriptor.GetEntryDescriptorList();

            //Slices
            int    slicesIndex  = rawEntryDescriptorList.FindIndex(item => item.Name == IFD_3_EntryDescriptor.CR2_SLICE);
            UInt32 slicesOffset = BitConverter.ToUInt32(headerItemRawBytes, 0) + ImageFileDirectory.GetImageFileDirectoryEntryOffset(Convert.ToUInt32(slicesIndex));
            ImageFileDirectoryEntry slicesEntry = RawReader.ReadImageFileDirectoryEntry(fileBytes, slicesOffset);
            UInt16[] slices = RawReader.ReadUInt16Array(fileBytes, slicesEntry.GetUInt32Value(), slicesEntry.NumberOfValue);

            //Headers and compressed raw bytes
            int    rawOffsetEntryIndex = rawEntryDescriptorList.FindIndex(item => item.Name == IFD_3_EntryDescriptor.STRIP_OFFSET);
            UInt32 localStripOffset    = ImageFileDirectory.GetImageFileDirectoryEntryOffset(Convert.ToUInt32(rawOffsetEntryIndex));
            UInt32 stripOffset         = BitConverter.ToUInt32(headerItemRawBytes, 0) + localStripOffset;
            ImageFileDirectoryEntry stripOffsetEntry = RawReader.ReadImageFileDirectoryEntry(fileBytes, stripOffset);
            UInt32 stripOffsetValue = stripOffsetEntry.GetUInt32Value();
            byte[] rawBytes         = RawReader.ReadBytes(fileBytes, stripOffsetValue);

            DhtHeader  dhtHeader  = RawReader.ReadDhtHeader(rawBytes);
            Sof3Header sof3Header = RawReader.ReadSof3Header(rawBytes);
            SosHeader  sosHeader  = RawReader.ReadSosHeader(rawBytes);

            Dictionary <string, byte> huffmanCodeValueDictionaryOne = dhtHeader.HuffmanTables[0].GetHuffmanCodeValueMap();
            Dictionary <string, byte> huffmanCodeValueDictionaryTwo = dhtHeader.HuffmanTables[1].GetHuffmanCodeValueMap();

            //SCAN DATA
            UInt32 scanDataOffset = sosHeader.Offset + sosHeader.HeaderLength + 2;
            byte[] scanDataBytes  = RawReader.ReadBytes(rawBytes, scanDataOffset);

            return(RawReader.ReadScanData(scanDataBytes, dhtHeader, sof3Header, sosHeader, slices));
        }