protected override TgaFooter Extract(Stream stream, int offset)
        {
            var reader = new SequentialStreamReader(stream, isMotorolaByteOrder: false);

            return(new TgaFooter(
                       extOffset: reader.GetInt32(),
                       devOffset: reader.GetInt32(),
                       signature: reader.GetBytes(_footerSignature.Length)));
        }
        protected override void Populate(Stream stream, int offset, TgaExtensionDirectory directory)
        {
            var reader = new SequentialStreamReader(stream, isMotorolaByteOrder: false);

            var size = reader.GetUInt16();

            if (size < ExtensionSize)
            {
                throw new ImageProcessingException("Invalid TGA extension size");
            }
            var authorName = GetString(41);

            if (authorName.Length > 0)
            {
                directory.Set(TgaExtensionDirectory.TagAuthorName, authorName);
            }
            var comments = GetString(324);

            if (comments.Length > 0)
            {
                directory.Set(TgaExtensionDirectory.TagComments, comments);
            }
            if (TryGetDateTime(out var dateTime))
            {
                directory.Set(TgaExtensionDirectory.TagDateTime, dateTime);
            }
            var jobName = GetString(41);

            if (jobName.Length > 0)
            {
                directory.Set(TgaExtensionDirectory.TagJobName, jobName);
            }
            if (TryGetTimeSpan(out var jobTime))
            {
                directory.Set(TgaExtensionDirectory.TagJobTime, jobTime);
            }
            var softwareName = GetString(41);

            if (softwareName.Length > 0)
            {
                directory.Set(TgaExtensionDirectory.TagSoftwareName, softwareName);
            }
            var softwareVersion = GetSoftwareVersion(softwareName);

            if (softwareVersion.Length > 0)
            {
                directory.Set(TgaExtensionDirectory.TagSoftwareVersion, softwareVersion);
            }
            var keyColor = reader.GetUInt32();

            if (keyColor != 0)
            {
                directory.Set(TgaExtensionDirectory.TagKeyColor, keyColor);
            }
            if (TryGetRational(out var aspectRatio))
            {
                directory.Set(TgaExtensionDirectory.TagAspectRatio, aspectRatio);
            }
            if (TryGetRational(out var gamma))
            {
                directory.Set(TgaExtensionDirectory.TagGamma, gamma);
            }
            var colorCorrectionOffset = reader.GetInt32();

            if (colorCorrectionOffset != 0)
            {
                directory.Set(TgaExtensionDirectory.TagColorCorrectionOffset, colorCorrectionOffset);
            }
            var thumbnailOffset = reader.GetInt32();

            if (thumbnailOffset != 0)
            {
                directory.Set(TgaExtensionDirectory.TagThumbnailOffset, thumbnailOffset);
            }
            var scanLineOffset = reader.GetInt32();

            if (scanLineOffset != 0)
            {
                directory.Set(TgaExtensionDirectory.TagScanLineOffset, scanLineOffset);
            }
            var attributesType = reader.GetByte();

            directory.Set(TgaExtensionDirectory.TagAttributesType, attributesType);

            string GetString(int length)
            {
                var buffer = new byte[length];

                reader.GetBytes(buffer, 0, length);
                int i = 0;

                while (i < buffer.Length && buffer[i] != '\0')
                {
                    ++i;
                }
                return(Encoding.ASCII.GetString(buffer, 0, i).TrimEnd());
            }

            bool TryGetDateTime(out DateTime dateTime)
            {
                var month  = reader.GetInt16();
                var day    = reader.GetInt16();
                var year   = reader.GetInt16();
                var hour   = reader.GetInt16();
                var minute = reader.GetInt16();
                var second = reader.GetInt16();

                if (month == 0 && day == 0 && year == 0)
                {
                    dateTime = DateTime.MinValue;
                    return(false);
                }
                dateTime = new DateTime(year, month, day, hour, minute, second);
                return(true);
            }

            bool TryGetTimeSpan(out TimeSpan timeSpan)
            {
                var hours   = reader.GetInt16();
                var minutes = reader.GetInt16();
                var seconds = reader.GetInt16();

                if (hours == 0 && minutes == 0 && seconds == 0)
                {
                    timeSpan = TimeSpan.Zero;
                    return(false);
                }
                timeSpan = new TimeSpan(hours, minutes, seconds);
                return(true);
            }

            string GetSoftwareVersion(string softwareName)
            {
                var number = reader.GetUInt16();
                var letter = reader.GetByte();

                if (number == 0)
                {
                    return(string.Empty);
                }
                var sb    = new StringBuilder();
                var denom = softwareName != "Paint Shop Pro" ? 100 : 0x100;

                sb.Append(number / denom);
                sb.Append('.');
                sb.Append(number % denom);
                if (letter != 0 && letter != 0x20)
                {
                    sb.Append((char)letter);
                }
                return(sb.ToString());
            }

            bool TryGetRational(out Rational value)
            {
                var num   = reader.GetUInt16();
                var denom = reader.GetUInt16();

                if (denom == 0)
                {
                    value = default;
                    return(false);
                }
                value = new Rational(num, denom);
                return(true);
            }
        }