From a0feb216aecc78cfcdee578380bac221a43accff Mon Sep 17 00:00:00 2001 From: winscripter <142818255+winscripter@users.noreply.github.com> Date: Wed, 2 Sep 2026 20:50:24 +0400 Subject: [PATCH] Add Y'Cb'Cr stage, make Epf1Stage class sealed --- .../Processing/RenderPipeline/Epf1Stage.cs | 2 +- .../RenderPipeline/WriteToOutputStage.cs | 2 + .../Processing/RenderPipeline/YCbCrStage.cs | 67 +++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/YCbCrStage.cs diff --git a/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/Epf1Stage.cs b/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/Epf1Stage.cs index b42513898c..5dc19b6626 100644 --- a/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/Epf1Stage.cs +++ b/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/Epf1Stage.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Formats.Jxl.Processing.RenderPipeline; /// /// Edge Preserving Filter (type 1) stage. /// -internal class Epf1Stage : RenderPipelineStageBase +internal sealed class Epf1Stage : RenderPipelineStageBase { private readonly JxlLoopFilter loopFilter; private readonly JxlImageF sigma; diff --git a/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/WriteToOutputStage.cs b/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/WriteToOutputStage.cs index e07be6ec06..ad790ed10a 100644 --- a/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/WriteToOutputStage.cs +++ b/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/WriteToOutputStage.cs @@ -1,8 +1,10 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Numerics; using SixLabors.ImageSharp.Formats.Jxl.IO.Metadata; using SixLabors.ImageSharp.Formats.Jxl.Processing.Image; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Jxl.Processing.RenderPipeline; diff --git a/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/YCbCrStage.cs b/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/YCbCrStage.cs new file mode 100644 index 0000000000..6de34c61bf --- /dev/null +++ b/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/YCbCrStage.cs @@ -0,0 +1,67 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Jxl.Processing.RenderPipeline; + +/// +/// SIMD-based conversion from Y'Cb'Cr to RGB pixel buffers. +/// +internal sealed class YCbCrStage : RenderPipelineStageBase +{ + public YCbCrStage(Configuration configuration) + : base(configuration) + { + } + + /// + public override string Name => "YCbCr"; + + /// + public override void ProcessRow(Buffer2D> inputRows, Buffer2D> outputRows, int xExtraLeft, int xExtraRight, int width, int xPos, int yPos) + { + // Vectors for conversion, defined by the ITU + Vector c128 = Vector.Create(128.0f / 255); + Vector crcr = Vector.Create(1.402f); + Vector cgcb = Vector.Create(-0.114f * 1.772f / 0.587f); + Vector cgcr = Vector.Create(-0.299f * 1.402f / 0.587f); + Vector cbcb = Vector.Create(1.772f); + + Span row0 = this.GetInputRow(inputRows, 0, 0); + Span row1 = this.GetInputRow(inputRows, 1, 0); + Span row2 = this.GetInputRow(inputRows, 2, 0); + + // Using refs for better performance + ref float row0Ref = ref MemoryMarshal.GetReference(row0); + ref float row1Ref = ref MemoryMarshal.GetReference(row1); + ref float row2Ref = ref MemoryMarshal.GetReference(row2); + + for (int x = 0; x < width; x += Vector.Count) + { + // Y'Cb'Cr input vectors + Vector yVec = Vector.LoadUnsafe(ref Unsafe.Add(ref row1Ref, x)) + c128; + Vector cbVec = Vector.LoadUnsafe(ref Unsafe.Add(ref row0Ref, x)); + Vector crVec = Vector.LoadUnsafe(ref Unsafe.Add(ref row2Ref, x)); + + // RGB output vectors + Vector rVec = (crcr * crVec) + yVec; + Vector gVec = (cgcr * crVec) + ((cgcb * cbVec) + yVec); + Vector bVec = (cbcb * cbVec) + yVec; + + // Copying to the output... + rVec.StoreUnsafe(ref Unsafe.Add(ref row0Ref, x)); + gVec.StoreUnsafe(ref Unsafe.Add(ref row1Ref, x)); + bVec.StoreUnsafe(ref Unsafe.Add(ref row2Ref, x)); + } + } + + /// + public override RenderPipelineChannelMode GetChannelMode(int channel) => + channel < 3 + ? RenderPipelineChannelMode.InPlace + : RenderPipelineChannelMode.Ignored; +}