/// <summary>
        /// Decode raw CFAPattern value
        /// </summary>
        /// <remarks>
        /// Converted from Exiftool version 10.33 created by Phil Harvey
        /// http://www.sno.phy.queensu.ca/~phil/exiftool/
        /// lib\Image\ExifTool\Exif.pm
        ///
        /// The value consists of:
        /// - Two short, being the grid width and height of the repeated pattern.
        /// - Next, for every pixel in that pattern, an identification code.
        /// </remarks>
        private int[] DecodeCFAPattern(int tagType)
        {
            int[] ret;

            var values = Directory.GetByteArray(tagType);

            if (values == null)
            {
                return(null);
            }

            if (values.Length < 4)
            {
                ret = new int[values.Length];
                for (var i = 0; i < values.Length; i++)
                {
                    ret[i] = values[i];
                }
                return(ret);
            }

            IndexedReader reader = new ByteArrayReader(values);

            // first two values should be read as 16-bits (2 bytes)
            var item0 = reader.GetInt16(0);
            var item1 = reader.GetInt16(2);

            ret = new int[values.Length - 2];

            var copyArray = false;
            var end       = 2 + item0 * item1;

            if (end > values.Length) // sanity check in case of byte order problems; calculated 'end' should be <= length of the values
            {
                // try swapping byte order (I have seen this order different than in EXIF)
                reader = reader.WithByteOrder(!reader.IsMotorolaByteOrder);
                item0  = reader.GetInt16(0);
                item1  = reader.GetInt16(2);

                if (values.Length >= 2 + item0 * item1)
                {
                    copyArray = true;
                }
            }
            else
            {
                copyArray = true;
            }

            if (copyArray)
            {
                ret[0] = item0;
                ret[1] = item1;

                for (var i = 4; i < values.Length; i++)
                {
                    ret[i - 2] = reader.GetByte(i);
                }
            }
            return(ret);
        }
        public void ProcessChunk(string fourCc, byte[] payload)
        {
            switch (fourCc)
            {
            case "EXIF":
            {
                // We have seen WebP images with and without the preamble here. It's likely that some software incorrectly
                // copied an entire JPEG segment into the WebP image. Regardless, we can handle it here.
                var reader = ExifReader.StartsWithJpegExifPreamble(payload)
                        ? new ByteArrayReader(payload, ExifReader.JpegSegmentPreambleLength)
                        : new ByteArrayReader(payload);
                _directories.AddRange(new ExifReader().Extract(reader));
                break;
            }

            case "ICCP":
            {
                _directories.Add(new IccReader().Extract(new ByteArrayReader(payload)));
                break;
            }

            case "XMP ":
            {
                _directories.Add(new XmpReader().Extract(payload));
                break;
            }

            case "VP8X":
            {
                if (payload.Length != 10)
                {
                    break;
                }

                string?error          = null;
                var    reader         = new ByteArrayReader(payload, isMotorolaByteOrder: false);
                var    isAnimation    = false;
                var    hasAlpha       = false;
                var    widthMinusOne  = -1;
                var    heightMinusOne = -1;
                try
                {
                    // Flags
                    // bit 0: has fragments
                    // bit 1: is animation
                    // bit 2: has XMP
                    // bit 3: has Exif
                    // bit 4: has alpha
                    // big 5: has ICC
                    isAnimation = reader.GetBit(1);
                    hasAlpha    = reader.GetBit(4);

                    // Image size
                    widthMinusOne  = reader.GetInt24(4);
                    heightMinusOne = reader.GetInt24(7);
                }
                catch (IOException e)
                {
                    error = "Exception reading WebpRiff chunk 'VP8X' : " + e.Message;
                }

                var directory = new WebPDirectory();
                if (error == null)
                {
                    directory.Set(WebPDirectory.TagImageWidth, widthMinusOne + 1);
                    directory.Set(WebPDirectory.TagImageHeight, heightMinusOne + 1);
                    directory.Set(WebPDirectory.TagHasAlpha, hasAlpha);
                    directory.Set(WebPDirectory.TagIsAnimation, isAnimation);
                }
                else
                {
                    directory.AddError(error);
                }
                _directories.Add(directory);
                break;
            }

            case "VP8L":
            {
                if (payload.Length < 5)
                {
                    break;
                }

                var reader = new ByteArrayReader(payload, isMotorolaByteOrder: false);

                string?error          = null;
                var    widthMinusOne  = -1;
                var    heightMinusOne = -1;
                try
                {
                    // https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification#2_riff_header

                    // Expect the signature byte
                    if (reader.GetByte(0) != 0x2F)
                    {
                        break;
                    }
                    var b1 = reader.GetByte(1);
                    var b2 = reader.GetByte(2);
                    var b3 = reader.GetByte(3);
                    var b4 = reader.GetByte(4);
                    // 14 bits for width
                    widthMinusOne = (b2 & 0x3F) << 8 | b1;
                    // 14 bits for height
                    heightMinusOne = (b4 & 0x0F) << 10 | b3 << 2 | (b2 & 0xC0) >> 6;
                }
                catch (IOException e)
                {
                    error = "Exception reading WebpRiff chunk 'VP8L' : " + e.Message;
                }

                var directory = new WebPDirectory();
                if (error == null)
                {
                    directory.Set(WebPDirectory.TagImageWidth, widthMinusOne + 1);
                    directory.Set(WebPDirectory.TagImageHeight, heightMinusOne + 1);
                }
                else
                {
                    directory.AddError(error);
                }
                _directories.Add(directory);
                break;
            }

            case "VP8 ":
            {
                if (payload.Length < 10)
                {
                    break;
                }

                var reader = new ByteArrayReader(payload, isMotorolaByteOrder: false);

                string?error  = null;
                var    width  = 0;
                var    height = 0;
                try
                {
                    // https://tools.ietf.org/html/rfc6386#section-9.1
                    // https://github.com/webmproject/libwebp/blob/master/src/enc/syntax.c#L115

                    // Expect the signature bytes
                    if (reader.GetByte(3) != 0x9D ||
                        reader.GetByte(4) != 0x01 ||
                        reader.GetByte(5) != 0x2A)
                    {
                        break;
                    }
                    width  = reader.GetUInt16(6);
                    height = reader.GetUInt16(8);
                }
                catch (IOException e)
                {
                    error = "Exception reading WebpRiff chunk 'VP8' : " + e.Message;
                }

                var directory = new WebPDirectory();
                if (error == null)
                {
                    directory.Set(WebPDirectory.TagImageWidth, width);
                    directory.Set(WebPDirectory.TagImageHeight, height);
                }
                else
                {
                    directory.AddError(error);
                }
                _directories.Add(directory);
                break;
            }
            }
        }
        public void ProcessChunk(string fourCc, byte[] payload)
        {
            switch (fourCc)
            {
            case "EXIF":
            {
                _directories.AddRange(new ExifReader().Extract(new ByteArrayReader(payload)));
                break;
            }

            case "ICCP":
            {
                _directories.Add(new IccReader().Extract(new ByteArrayReader(payload)));
                break;
            }

            case "XMP ":
            {
                _directories.Add(new XmpReader().Extract(payload));
                break;
            }

            case "VP8X":
            {
                if (payload.Length != 10)
                {
                    break;
                }

                string error          = null;
                var    reader         = new ByteArrayReader(payload, isMotorolaByteOrder: false);
                var    isAnimation    = false;
                var    hasAlpha       = false;
                var    widthMinusOne  = -1;
                var    heightMinusOne = -1;
                try
                {
                    // Flags
//                      var hasFragments = reader.GetBit(0);
                    isAnimation = reader.GetBit(1);
//                      var hasXmp = reader.GetBit(2);
//                      var hasExif = reader.GetBit(3);
                    hasAlpha = reader.GetBit(4);
//                      var hasIcc = reader.GetBit(5);
                    // Image size
                    widthMinusOne  = reader.GetInt24(4);
                    heightMinusOne = reader.GetInt24(7);
                }
                catch (IOException e)
                {
                    error = "Exception reading WebpRiff chunk 'VP8X' : " + e.Message;
                }

                var directory = new WebPDirectory();
                if (error == null)
                {
                    directory.Set(WebPDirectory.TagImageWidth, widthMinusOne + 1);
                    directory.Set(WebPDirectory.TagImageHeight, heightMinusOne + 1);
                    directory.Set(WebPDirectory.TagHasAlpha, hasAlpha);
                    directory.Set(WebPDirectory.TagIsAnimation, isAnimation);
                }
                else
                {
                    directory.AddError(error);
                }
                _directories.Add(directory);
                break;
            }

            case "VP8L":
            {
                if (payload.Length < 5)
                {
                    break;
                }

                var reader = new ByteArrayReader(payload, isMotorolaByteOrder: false);

                string error          = null;
                var    widthMinusOne  = -1;
                var    heightMinusOne = -1;
                try
                {
                    // https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification#2_riff_header

                    // Expect the signature byte
                    if (reader.GetByte(0) != 0x2F)
                    {
                        break;
                    }
                    var b1 = reader.GetByte(1);
                    var b2 = reader.GetByte(2);
                    var b3 = reader.GetByte(3);
                    var b4 = reader.GetByte(4);
                    // 14 bits for width
                    widthMinusOne = (b2 & 0x3F) << 8 | b1;
                    // 14 bits for height
                    heightMinusOne = (b4 & 0x0F) << 10 | b3 << 2 | (b2 & 0xC0) >> 6;
                }
                catch (IOException e)
                {
                    error = "Exception reading WebpRiff chunk 'VP8L' : " + e.Message;
                }

                var directory = new WebPDirectory();
                if (error == null)
                {
                    directory.Set(WebPDirectory.TagImageWidth, widthMinusOne + 1);
                    directory.Set(WebPDirectory.TagImageHeight, heightMinusOne + 1);
                }
                else
                {
                    directory.AddError(error);
                }
                _directories.Add(directory);
                break;
            }

            case "VP8 ":
            {
                if (payload.Length < 10)
                {
                    break;
                }

                var reader = new ByteArrayReader(payload, isMotorolaByteOrder: false);

                string error  = null;
                var    width  = 0;
                var    height = 0;
                try
                {
                    // https://tools.ietf.org/html/rfc6386#section-9.1
                    // https://github.com/webmproject/libwebp/blob/master/src/enc/syntax.c#L115

                    // Expect the signature bytes
                    if (reader.GetByte(3) != 0x9D ||
                        reader.GetByte(4) != 0x01 ||
                        reader.GetByte(5) != 0x2A)
                    {
                        break;
                    }
                    width  = reader.GetUInt16(6);
                    height = reader.GetUInt16(8);
                }
                catch (IOException e)
                {
                    error = "Exception reading WebpRiff chunk 'VP8' : " + e.Message;
                }

                var directory = new WebPDirectory();
                if (error == null)
                {
                    directory.Set(WebPDirectory.TagImageWidth, width);
                    directory.Set(WebPDirectory.TagImageHeight, height);
                }
                else
                {
                    directory.AddError(error);
                }
                _directories.Add(directory);
                break;
            }
            }
        }