From 4476abd507ccecee159ad1a5f5e510c11b4bf2d2 Mon Sep 17 00:00:00 2001 From: winscripter <142818255+winscripter@users.noreply.github.com> Date: Wed, 2 Sep 2026 23:40:18 +0400 Subject: [PATCH] Add 3 missing methods to JxlFrameDecoder, add spline and spotcolor render pipeline stages --- .../Jxl/Processing/Decoder/JxlFrameDecoder.cs | 25 +++++++++++++ .../Processing/RenderPipeline/SplineStage.cs | 21 +++++++++++ .../RenderPipeline/SpotColorStage.cs | 37 +++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/SplineStage.cs create mode 100644 src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/SpotColorStage.cs diff --git a/src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlFrameDecoder.cs b/src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlFrameDecoder.cs index 9b8e9b0f28..5e0eefc9ef 100644 --- a/src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlFrameDecoder.cs +++ b/src/ImageSharp/Formats/Jxl/Processing/Decoder/JxlFrameDecoder.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Jxl.IO; using SixLabors.ImageSharp.Formats.Jxl.IO.FrameHeader; using SixLabors.ImageSharp.Formats.Jxl.IO.Metadata; using SixLabors.ImageSharp.Formats.Jxl.Processing.Image; @@ -133,4 +134,28 @@ internal sealed class JxlFrameDecoder frameDecoder.FinalizeFrame(); } + + private static int BytesPerChannel(JxlDataType dataType) => + dataType == JxlDataType.Byte ? 1 + : dataType == JxlDataType.Single + ? 4 + : 2; + + private int GetStorageLocation(int thread, int task) => this.useTaskId ? task : thread; + + private void PrepareStorage(int numThreads, int numTasks) + { + int storageSize = Math.Min(numThreads, numTasks); + if (storageSize > this.groupDecoderCaches.Count) + { + this.groupDecoderCaches = [.. this.groupDecoderCaches.Take(storageSize)]; + } + + this.useTaskId = numThreads > numTasks; + bool useNoise = (this.frameHeader.Flags & (int)JxlFrameHeaderFlags.Noise) != 0; + bool useGroupIds = this.modularFrameDecoder.UsesFullImage && (this.frameHeader.Encoding == JxlFrameEncoding.VarDct || useNoise); + + this.decoderState.RenderPipeline?.PrepareForThreads(storageSize, useGroupIds); + this.decoderState.Upsampler8x.PrepareForThreads(numThreads); + } } diff --git a/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/SplineStage.cs b/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/SplineStage.cs new file mode 100644 index 0000000000..39864265d0 --- /dev/null +++ b/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/SplineStage.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Jxl.Processing.RenderPipeline; + +internal sealed class SplineStage(Configuration configuration, JxlSplines splines) : RenderPipelineStageBase(configuration) +{ + public override string Name => "Splines"; + + public override void ProcessRow(Buffer2D> inputRows, Buffer2D> outputRows, int xExtraLeft, int xExtraRight, int width, int xPos, int yPos) + { + Span rowX = this.GetInputRow(inputRows, 0, 0, xExtraLeft); + Span rowY = this.GetInputRow(inputRows, 1, 0, xExtraLeft); + Span rowB = this.GetInputRow(inputRows, 2, 0, xExtraLeft); + splines.AddToRow(rowX, rowY, rowB, yPos, xPos - xExtraLeft, xPos + width + xExtraRight); + } + + public override RenderPipelineChannelMode GetChannelMode(int channel) => channel < 3 ? RenderPipelineChannelMode.InPlace : RenderPipelineChannelMode.Ignored; +} diff --git a/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/SpotColorStage.cs b/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/SpotColorStage.cs new file mode 100644 index 0000000000..6b0fc97ed8 --- /dev/null +++ b/src/ImageSharp/Formats/Jxl/Processing/RenderPipeline/SpotColorStage.cs @@ -0,0 +1,37 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Jxl.Processing.RenderPipeline; + +internal sealed class SpotColorStage(Configuration configuration, int spotColorOffset, Memory spotColor) + : RenderPipelineStageBase(configuration) +{ + private readonly int spotC = 3 + spotColorOffset; + + public override string Name => "Spot"; + + public override void ProcessRow(Buffer2D> inputRows, Buffer2D> outputRows, int xExtraLeft, int xExtraRight, int width, int xPos, int yPos) + { + Span spotColors = spotColor.Span; + + float scale = 0; + for (int c = 0; c < 3; c++) + { + Span p = this.GetInputRow(inputRows, c, 0); + Span s = this.GetInputRow(inputRows, this.spotC, 0); + + for (int x = 0; x < width; x++) + { + float mix = scale * s[x]; + p[x] = (mix * spotColors[c]) + ((1.0f - mix) * p[x]); + } + } + } + + public override RenderPipelineChannelMode GetChannelMode(int channel) + => channel < 3 ? RenderPipelineChannelMode.InPlace + : channel == this.spotC ? RenderPipelineChannelMode.Input + : RenderPipelineChannelMode.Ignored; +}