diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter420{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter420{TPixel}.cs
new file mode 100644
index 0000000000..c831b611c8
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter420{TPixel}.cs
@@ -0,0 +1,95 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
+{
+ ///
+ /// On-stack worker struct to efficiently encapsulate the TPixel -> Rgb24 -> YCbCr conversion chain of 8x8 pixel blocks.
+ ///
+ /// The pixel type to work on
+ internal ref struct YCbCrForwardConverter420
+ where TPixel : unmanaged, IPixel
+ {
+ ///
+ /// The left Y component
+ ///
+ public Block8x8F YLeft;
+
+ ///
+ /// The left Y component
+ ///
+ public Block8x8F YRight;
+
+ ///
+ /// The Cb component
+ ///
+ public Block8x8F Cb;
+
+ ///
+ /// The Cr component
+ ///
+ public Block8x8F Cr;
+
+ ///
+ /// The color conversion tables
+ ///
+ private RgbToYCbCrConverterLut colorTables;
+
+ ///
+ /// Temporal 16x8 block to hold TPixel data
+ ///
+ private Span pixelSpan;
+
+ ///
+ /// Temporal RGB block
+ ///
+ private Span rgbSpan;
+
+ public static YCbCrForwardConverter420 Create()
+ {
+ var result = default(YCbCrForwardConverter420);
+
+ // TODO: this is subject to discuss
+ const int twoBlocksByteSizeWithPadding = 384 + 8; // converter.Convert comments for +8 padding
+ result.rgbSpan = MemoryMarshal.Cast(new byte[twoBlocksByteSizeWithPadding].AsSpan());
+
+ // TODO: this size should be configurable
+ result.pixelSpan = new TPixel[128].AsSpan();
+
+ // Avoid creating lookup tables, when vectorized converter is supported
+ if (!RgbToYCbCrConverterVectorized.IsSupported)
+ {
+ result.colorTables = RgbToYCbCrConverterLut.Create();
+ }
+
+ return result;
+ }
+
+ ///
+ /// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (, , )
+ ///
+ public void Convert(ImageFrame frame, int x, int y, ref RowOctet currentRows, int idx)
+ {
+ Memory.Buffer2D buffer = frame.PixelBuffer;
+ YCbCrForwardConverter.LoadAndStretchEdges(currentRows, this.pixelSpan, new Point(x, y), new Size(16, 8), new Size(buffer.Width, buffer.Height));
+
+ PixelOperations.Instance.ToRgb24(frame.GetConfiguration(), this.pixelSpan, this.rgbSpan);
+
+ if (RgbToYCbCrConverterVectorized.IsSupported)
+ {
+ RgbToYCbCrConverterVectorized.Convert420_16x8(this.rgbSpan, ref this.YLeft, ref this.YRight, ref this.Cb, ref this.Cr, idx);
+ }
+ else
+ {
+ throw new NotSupportedException("This is not yet implemented");
+ //this.colorTables.Convert(this.rgbSpan, ref yBlock, ref cbBlock, ref crBlock);
+ }
+ }
+ }
+}