/// <inheritdoc />
        public ValueTask InvokeAsync(TiffImageDecoderContext context, ITiffImageDecoderPipelineNode next)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (next is null)
            {
                throw new ArgumentNullException(nameof(next));
            }

            TiffYCbCrConverter8 converter = _converter;

            int                 bytesPerScanline = 3 * context.SourceImageSize.Width;
            Memory <byte>       source           = context.UncompressedData.Slice(context.SourceReadOffset.Y * bytesPerScanline);
            ReadOnlySpan <byte> sourceSpan       = source.Span;

            using TiffPixelBufferWriter <TiffRgba32> writer = context.GetWriter <TiffRgba32>();

            int rows = context.ReadSize.Height;
            int cols = context.ReadSize.Width;

            for (int row = 0; row < rows; row++)
            {
                using TiffPixelSpanHandle <TiffRgba32> pixelSpanHandle = writer.GetRowSpan(row);
                ReadOnlySpan <byte> rowSourceSpan      = sourceSpan.Slice(3 * context.SourceReadOffset.X, 3 * context.ReadSize.Width);
                Span <TiffRgba32>   rowDestinationSpan = pixelSpanHandle.GetSpan();

                converter.ConvertToRgba32(rowSourceSpan, rowDestinationSpan, cols);
                sourceSpan = sourceSpan.Slice(bytesPerScanline);
            }

            return(next.RunAsync(context));
        }
 /// <summary>
 /// Initialize the middleware.
 /// </summary>
 /// <param name="coefficients">The YCbCrCoefficients tag.</param>
 /// <param name="referenceBlackWhite">The ReferenceBlackWhite tag.</param>
 public TiffChunkyYCbCr888Interpreter(TiffValueCollection <TiffRational> coefficients, TiffValueCollection <TiffRational> referenceBlackWhite)
 {
     if (!coefficients.IsEmpty && coefficients.Count != 3)
     {
         throw new ArgumentException("coefficient should have 3 none-zero elements.");
     }
     if (!referenceBlackWhite.IsEmpty && referenceBlackWhite.Count != 6)
     {
         throw new ArgumentException("referenceWhiteBlack should have 6 elements.");
     }
     _converter = TiffYCbCrConverter8.Create(coefficients.GetOrCreateArray(), referenceBlackWhite.GetOrCreateArray());
 }
        /// <inheritdoc />
        public ValueTask InvokeAsync(TiffImageDecoderContext context, ITiffImageDecoderPipelineNode next)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (next is null)
            {
                throw new ArgumentNullException(nameof(next));
            }

            TiffYCbCrConverter8 converter = _converter;

            int skippedRowOffset = context.SourceImageSize.Width * context.SourceReadOffset.Y;
            int planarByteCount  = context.SourceImageSize.Width * context.SourceImageSize.Height;

            ReadOnlySpan <byte> sourceSpan = context.UncompressedData.Span;
            ReadOnlySpan <byte> sourceY    = sourceSpan.Slice(0, planarByteCount);
            ReadOnlySpan <byte> sourceCb   = sourceSpan.Slice(planarByteCount, planarByteCount);
            ReadOnlySpan <byte> sourceCr   = sourceSpan.Slice(2 * planarByteCount, planarByteCount);

            using TiffPixelBufferWriter <TiffRgba32> writer = context.GetWriter <TiffRgba32>();

            int rows = context.ReadSize.Height;
            int cols = context.ReadSize.Width;

            for (int row = 0; row < rows; row++)
            {
                using TiffPixelSpanHandle <TiffRgba32> pixelSpanHandle = writer.GetRowSpan(row);
                Span <TiffRgba32> rowDestinationSpan = pixelSpanHandle.GetSpan();
                int rowOffset = skippedRowOffset + row * context.SourceImageSize.Width + context.SourceReadOffset.X;
                for (int col = 0; col < cols; col++)
                {
                    int componentOffset = rowOffset + col;
                    rowDestinationSpan[col] = converter.ConvertToRgba32(sourceY[componentOffset], sourceCb[componentOffset], sourceCr[componentOffset]);
                }
            }

            return(next.RunAsync(context));
        }