Skip to content

Heatmap: Add independent rendering cell flags (#4950)#5035

Merged
swharden merged 7 commits intoScottPlot:mainfrom
CoderPM2011:fix/Heatmap-SVG
Aug 23, 2025
Merged

Heatmap: Add independent rendering cell flags (#4950)#5035
swharden merged 7 commits intoScottPlot:mainfrom
CoderPM2011:fix/Heatmap-SVG

Conversation

@CoderPM2011
Copy link
Contributor

The issue described in #4950 is similar to that in #5020.
SVG does not fully support pixel-based images, but the Heatmap rendering currently relies on Drawing.BitmapFromArgbs().

To address this, a new optional flag IndependentCellRendering has been added.
When set to true, each cell is rendered independently using Drawing.FillRectangle().

⚠️ Note that enabling this option can lead to performance degradation and significantly larger SVG file sizes when rendering many cells.
However, when the number of cells is large, the difference is hardly noticeable in the SVG output, so it is generally unnecessary to use IndependentCellRendering in such cases. 🤔

For this reason, the feature is disabled by default.
🧐 Users should carefully evaluate their needs before enabling it.

demo result (SVG)

demo

demo code

var plot = formsPlot1.Plot;

double[,] data = {
        { 1, 2, 3 },
        { 4, 5, 6 },
        { 7, 8, 9 },
    };

Heatmap heatmap = plot.Add.Heatmap(data);

heatmap.IndependentCellRendering = true;
formsPlot1.Plot.SaveSvg("demo.svg", 400, 300);
heatmap.IndependentCellRendering = false;

Copy link
Member

@bclehmann bclehmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm of two minds about this, on one hand this render method may well be a good feature, it likely is more efficient (if we avoid building the cached bitmap) for small heatmaps, especially if they're updated more often then they're panned/zoomed.

But also I'd really rather just find a way to set image-rendering: pixelated on the generated SVGs and let this be handled by the user's image viewer. It will be drastically better on file size and it should look pixel-identical between renderers.
Unfortunately I don't believe this is supported in Skia for whatever reason, though it would be a relatively easy add. So our only option would be to manually edit the generated XML, which is not an idea that sparks joy for me.

Edit: I went all the way to drafting a patch for Skia upstream (see bclehmann/skia@717b241) before I realized that while image-rendering: pixelated is part of the CSS draft, and is thus supported on all SVGs rendered by browsers, it is not in the mainline SVG draft and so we would be relying on behaviour not required by the spec. It appears that W3 is considering it, but it's not yet there.

In the SVG spec we can rely on image-rendering: optimizeSpeed existing (though it's not in the CSS spec) but it is merely a hint and the spec states that implementers who can achieve their performance goals with methods other than nearest neighbour should do so. i.e. a conforming implementation is free to completely ignore this property and use bilinear or bicubic across the board.

@swharden
Copy link
Member

Hey @CoderPM2011, thanks for this PR!

... and @bclehmann you're my hero for starting with a scottplot feature and trying to fix it by considering submitting a C++ patch to fix Skia 🤣

The strategy pattern would be easier given our current API

I'll implement this quickly and merge now, but I we can refine it in subsequent PRs as needed. I'm curious if new strategies will get added in the future. It seems like a new way to get custom rendering functionality! ... although it's not that different than inheriting overriding the virtual Render method, though I guess Update isn't virtual so I'll change that while I'm in there

@swharden
Copy link
Member

swharden commented Aug 23, 2025

The strategy pattern

var heatmap = plot.Add.Heatmap(data);
plot.Title("default");
plot.SaveTestSvg(400, 300, "default");

heatmap.RenderStrategy = new ScottPlot.Plottables.Heatmap.RenderStrategies.Rectangles();
plot.Title("RenderCellsAsRectangles");
plot.SaveTestSvg(400, 300, "enabled");
default rectangles
Test_Heatmap_SvgGradient_default Test_Heatmap_SvgGradient_enabled

@swharden
Copy link
Member

Hey @CoderPM2011 and @bclehmann, thanks for this PR! I think my implementation of strategy pattern was effective here, but I'll merge now and continue to welcome input on how to improve it.

Thanks again! 🚀

@swharden swharden merged commit 83eab49 into ScottPlot:main Aug 23, 2025
3 checks passed
@swharden swharden linked an issue Aug 23, 2025 that may be closed by this pull request
@CoderPM2011 CoderPM2011 deleted the fix/Heatmap-SVG branch August 23, 2025 04:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Exporting a heatmap as SVG outputs a smoothed svg.

3 participants