Skip to content

Add Surface.Resize, smooth image resampling for documents, and profile-driven optimizations - #47

Merged
Malcolmnixon merged 16 commits into
mainfrom
feature/image-resize
Oct 11, 2026
Merged

Malcolmnixon merged 16 commits into
mainfrom
feature/image-resize

Conversation

@Malcolmnixon

Copy link
Copy Markdown
Member

Pull Request

Description

Adds public image resizing and uses the same high-quality resampler for images embedded in documents, followed by profile-driven performance work.

  • Public API: ResamplingMode (Auto, Nearest, Bilinear, Bicubic, Lanczos3, Area), Surface.Resize and Surface.ResizeToFit. This makes load/crop/resize/save straightforward. Alpha is handled premultiplied.
  • Documents: PDF images, PPTX pictures, SVG <image>, feImage, SVG pattern tiles and PDF tile paint now use the shared engine. This is area averaging when shrinking and bicubic when enlarging. SVG image-rendering: pixelated|crisp-edges|optimizeSpeed selects Nearest. PDF /SMask resizing is now bilinear.
  • Performance: rasterizer empty-row and zero-column skipping, TensorPrimitives clip multiply, a per-thread compositing scratch cache, pooled resampler buffers, a glyph outline cache and PathBuilder scratch reuse. Warm 96 dpi renders: BB6 page 1 123 to 66 ms, BB6 page 0 56-77 to 36 ms, issue 9 repro 24.8 to 20.7 ms. Allocation on the issue 9 repro fell from 62.8 to 15.4 MB. A golden-pixel diff was byte-identical. Candidates the profile did not justify were rejected: the opaque compositing fast path, vectorized coverage resolve, frozen collections and parallel resampler strips.
  • Docs: user guide and README example, design, verification, requirements, review sets.

Known limits: PDF /ImageMask and /Interpolate are not implemented. About 40 existing exact-value test assertions were loosened to tolerances because smoothing changes edge pixels (justified in the tests and verification docs).

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code quality improvement

Related Issues

Closes #

Pre-Submission Checklist

Before submitting this pull request, ensure you have completed the following:

Build and Test

  • Code builds successfully and all tests pass: pwsh ./build.ps1 (passed, 13821 tests, 0 failed, net8/net9/net10)
  • Code produces zero warnings

Code Quality

  • New code has appropriate XML documentation comments
  • Static analyzer warnings have been addressed

Quality Checks

Please run the following checks before submitting:

  • All linters pass: pwsh ./lint.ps1 (exit 0)

Testing

  • Added unit tests for new functionality
  • Updated existing tests if behavior changed
  • All tests follow the AAA (Arrange, Act, Assert) pattern
  • Test coverage is maintained or improved

Documentation

  • Updated README.md (if applicable)
  • Updated docs/ documentation (if applicable)
  • Added code examples for new features (if applicable)
  • Updated requirements.yaml (if applicable)

Additional Notes

Reviewed locally before pushing: code review, formal review of all affected ReviewMark sets including system level, traceability check (no orphaned requirements), lint-fix pass. Existing tests contain no timing assertions, and none were added.

Malcolm Nixon and others added 6 commits October 11, 2026 09:14
Add ResamplingMode and a separable, premultiplied-alpha resampling engine; route
PDF, PPTX, SVG images and tile paints through it, with tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…thing; doc fixes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rendering parsing, shared tile classification, tests and docs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ache and resampler allocations

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings October 11, 2026 16:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

An extreme but finite image scale can stall rendering, and the glyph-cache size limit is not enforced under concurrent calls.

3 open findings
What changed in this PR

This PR adds public image resizing to CanvasNet and shares its resampling engine with document renderers. It also targets rendering and allocation costs identified by profiling.

Changes:

  • Add Surface.Resize, ResizeToFit, and selectable filters.
  • Smooth raster images and tiles in PDF, PPTX, and SVG rendering.
  • Add rendering optimizations, tests, and supporting documentation and requirements.
File Description
test/​DemaConsulting.CanvasNet.Tests/​Geometry/​PathBuilderTests.cs Tests builder reuse.
test/​DemaConsulting.CanvasNet.Tests/​Fonts/​TrueTypeFontTests.cs Tests outline caching.
test/​DemaConsulting.CanvasNet.Tests/​Drawing/​TilePaintEvaluatorTests.cs Tests tile smoothing.
test/​DemaConsulting.CanvasNet.Tests/​Drawing/​ScanlineRasterizerTests.cs Tests windowed coverage.
test/​DemaConsulting.CanvasNet.Tests/​CanvasNetTests.cs Tests public resize workflows.
test/​DemaConsulting.CanvasNet.Tests/​Canvas/​SurfaceTests.cs Tests workspace reuse.
test/​DemaConsulting.CanvasNet.Tests/​Canvas/​SurfaceResizeCodecRoundTripTests.cs Tests resize and codec round trips.
test/​DemaConsulting.CanvasNet.Tests/​Canvas/​SurfaceResamplerTests.cs Tests resampling helpers.
test/​DemaConsulting.CanvasNet.Svg.Tests/​SvgPatternTileSmoothingTests.cs Tests SVG tile sampling.
test/​DemaConsulting.CanvasNet.Svg.Tests/​SvgImageResamplingTests.cs Tests SVG image filtering.
test/​DemaConsulting.CanvasNet.Pptx.Tests/​PptxImagesTests.cs Tests picture smoothing and crops.
test/​DemaConsulting.CanvasNet.Pdf.Tests/​PdfSystemIntegrationTests.cs Adjusts image-render assertions.
test/​DemaConsulting.CanvasNet.Pdf.Tests/​PdfImageResamplingTests.cs Tests grid lines at multiple sizes.
test/​DemaConsulting.CanvasNet.Pdf.Tests/​PdfDocumentTests.cs Adjusts image-render assertions.
test/​DemaConsulting.CanvasNet.Pdf.Tests/​PdfDocumentJpxTests.cs Adjusts mask and image assertions.
test/​DemaConsulting.CanvasNet.Pdf.Tests/​PdfDocumentImageDownscaleTests.cs Tests smooth enlargement.
test/​DemaConsulting.CanvasNet.Pdf.Tests/​PdfDocumentImageColorConversionTests.cs Tests pixel color conversion.
src/​DemaConsulting.CanvasNet/​Geometry/​PathBuilder.cs Reuses command scratch storage.
src/​DemaConsulting.CanvasNet/​Fonts/​TrueTypeFont.cs Caches decoded outlines.
src/​DemaConsulting.CanvasNet/​Drawing/​TilePaintEvaluator.cs Adds scale-aware tile sampling.
src/​DemaConsulting.CanvasNet/​Drawing/​ScanlineRasterizer.cs Trims coverage work and reuses scratch storage.
src/​DemaConsulting.CanvasNet/​DemaConsulting.CanvasNet.csproj Grants SVG access to resampling internals.
src/​DemaConsulting.CanvasNet/​Canvas/​SurfaceResampler.cs Connects existing helpers to shared resampling.
src/​DemaConsulting.CanvasNet/​Canvas/​Surface.cs Adds resizing and workspace caching.
src/​DemaConsulting.CanvasNet/​Canvas/​ResamplingMode.cs Defines public filter choices.
src/​DemaConsulting.CanvasNet.Svg/​SvgCodec.Patterns.cs Smooths scaled pattern tiles.
src/​DemaConsulting.CanvasNet.Svg/​SvgCodec.Image.cs Resamples SVG images.
src/​DemaConsulting.CanvasNet.Svg/​SvgCodec.Filters.FeImage.cs Uses smooth feImage sampling.
src/​DemaConsulting.CanvasNet.Svg/​SvgCodec.Elements.cs Parses inherited image-rendering settings.
src/​DemaConsulting.CanvasNet.Pptx/​PptxDocument.Images.cs Smooths cropped pictures.
src/​DemaConsulting.CanvasNet.Pdf/​PdfDocument.Images.cs Resamples images and soft masks.
src/​DemaConsulting.CanvasNet.Pdf/​PdfDocument.Color.cs Reduces color-conversion allocations.
README.md Adds a resize example.
docs/​verification/​canvas-net/​geometry/​path.md Records builder-reuse verification.
docs/​verification/​canvas-net/​fonts/​true-type-font.md Records outline-cache verification.
docs/​verification/​canvas-net/​drawing/​tile-paint.md Records tile-smoothing verification.
docs/​verification/​canvas-net/​drawing/​path-filler.md Records coverage-window verification.
docs/​verification/​canvas-net/​canvas/​surface.md Records resize and caching verification.
docs/​verification/​canvas-net/​canvas.md Updates canvas verification scope.
docs/​verification/​canvas-net.md Records system resize tests.
docs/​verification/​canvas-net-svg/​svg-codec.md Records SVG resampling tests.
docs/​verification/​canvas-net-pptx/​pptx-document.md Records picture-resampling tests.
docs/​verification/​canvas-net-pdf/​pdf-document.md Records PDF resampling tests.
docs/​user_guide/​introduction.md Documents resize APIs and examples.
docs/​reqstream/​canvas-net/​geometry/​path.yaml Links builder-reuse test.
docs/​reqstream/​canvas-net/​fonts/​true-type-font.yaml Adds outline-cache requirement.
docs/​reqstream/​canvas-net/​fonts.yaml Links font requirement.
docs/​reqstream/​canvas-net/​drawing/​tile-paint.yaml Adds tile-smoothing requirement.
docs/​reqstream/​canvas-net/​drawing/​path-filler.yaml Adds coverage-window requirement.
docs/​reqstream/​canvas-net/​drawing.yaml Links drawing requirements.
docs/​reqstream/​canvas-net/​canvas/​surface.yaml Adds resize and caching requirements.
docs/​reqstream/​canvas-net/​canvas.yaml Links canvas resize requirements.
docs/​reqstream/​canvas-net.yaml Adds system resize requirements.
docs/​reqstream/​canvas-net-svg/​svg-codec.yaml Adds SVG resampling requirements.
docs/​reqstream/​canvas-net-svg.yaml Links SVG requirements.
docs/​reqstream/​canvas-net-pptx/​pptx-document.yaml Adds picture-resampling requirement.
docs/​reqstream/​canvas-net-pptx.yaml Links PPTX requirement.
docs/​reqstream/​canvas-net-pdf/​pdf-document.yaml Updates image-render requirements.
docs/​reqstream/​canvas-net-pdf.yaml Links PDF requirements.
docs/​design/​introduction.md Maps resampling components.
docs/​design/​canvas-net/​geometry/​path.md Explains scratch-list reuse.
docs/​design/​canvas-net/​fonts/​true-type-font.md Explains outline caching.
docs/​design/​canvas-net/​drawing/​tile-paint.md Describes tile filtering.
docs/​design/​canvas-net/​drawing/​path-filler.md Describes coverage trimming.
docs/​design/​canvas-net/​canvas/​surface.md Describes resize and workspace design.
docs/​design/​canvas-net/​canvas.md Updates canvas unit design.
docs/​design/​canvas-net.md Documents public resize APIs.
docs/​design/​canvas-net-svg/​svg-codec.md Describes SVG image filtering.
docs/​design/​canvas-net-pptx/​pptx-document.md Describes picture filtering and crops.
docs/​design/​canvas-net-pdf/​pdf-document.md Describes PDF image filtering.
.reviewmark.yaml Adds new source and tests to review sets.
.cspell.yaml Adds resampling terminology.

🧠 Review effort: Lite


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/DemaConsulting.CanvasNet/Canvas/SurfaceResampler.Engine.cs
Comment thread src/DemaConsulting.CanvasNet/Drawing/ScanlineRasterizer.cs Outdated
Comment thread src/DemaConsulting.CanvasNet/Fonts/TrueTypeFont.cs Outdated
…ache cap atomic

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings October 11, 2026 17:35

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The shared engine and multiple renderers need final human validation, and the remaining rasterizer and test findings are unresolved.

0 open findings

3 resolved since last review
Previously missed (2)

In code that hasn't changed since last review

Medium severity Trim EvenOdd rows without clips and handle empty coverage

src/​DemaConsulting.CanvasNet/​Drawing/​ScanlineRasterizer.cs:1066

For an EvenOdd row with multiple vertical breakpoints and no dense clip, this sets the window to the full clip width, then ApplyClip returns before trimming because _clip is null. Even a narrow shape or an all-zero row is passed to paint evaluation and compositing across the entire width, undoing the new skip/window optimization on this path. Trim the resolved row even when there is no clip, and set the length to zero when the row has no coverage.

Medium severity Prevent resize alpha-edge tests from passing without visible pixels

test/​DemaConsulting.CanvasNet.Tests/​CanvasNetTests.cs:125

This loop checks RGB only when p.A > 0, so it passes without any color assertions if resizing makes every pixel transparent. The per-filter Resize_AlphaEdge_HasNoColorFringe test in SurfaceResizeTests.cs has the same gap. Assert nonzero or expected alpha at a known visible pixel in both tests, then check the colors of visible pixels; that makes a dropped alpha edge fail instead of passing vacuously.

🧠 Review effort: Lite

…pha-edge tests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings October 11, 2026 17:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The cross-renderer change needs human validation, and unresolved extreme-scale and glyph-cache memory risks remain.

0 open findings

Previously missed (3)

In code that hasn't changed since last review

Medium severity Prevent source footprint and tap-bound overflow for tiny placements

src/​DemaConsulting.CanvasNet/​Canvas/​SurfaceResampler.Engine.cs:549

The finite/nonzero check still lets an axis-aligned placement produce a source footprint beyond long range. For example, an 8192-pixel-wide image placed at x=0.5 with scale 1e-19 selects device pixel 0, but this call passes a source extent of 1e19 to BuildAxis, which casts its tap bounds to long; the resulting weights can select an edge pixel instead of averaging the image. At less extreme scales, the bounded-tail bucket calculations can also overflow long for Bilinear/Bicubic/Lanczos3. Bound or reject these footprints before the integer tap math, and make tail bucket arithmetic overflow-safe so tiny placements cannot silently produce wrong pixels.

Medium severity Limit cached glyph geometry size, not just glyph entry count

src/​DemaConsulting.CanvasNet/​Fonts/​TrueTypeFont.cs:529

This admission check caps the number of glyphs but not the size of their retained outlines. GlyfLocaReader allows up to 200,000 points/commands per decoded glyph, so an untrusted font with thousands of distinct large glyphs can leave a very large amount of geometry resident as long as the TrueTypeFont is retained, even though the cache remains below 4096 entries. Limit the total cached geometry cost (or skip/evict oversized outlines), not just the entry count.

Low severity Update SysML2 unit models and artifact paths for resampling changes

src/​DemaConsulting.CanvasNet/​Canvas/​Surface.cs:474

The SysML2 unit model was not updated alongside the new APIs and renderer behavior. docs/sysml2/model/canvas-net/canvas/surface.sysml still describes only cropping/compositing and omits ResamplingMode.cs, SurfaceResampler.Engine.cs, and the resize test files from its artifact comments; drawing/tile-paint.sysml still says tiles are sampled only by nearest neighbor, and the PDF/SVG unit test artifact lists omit their new resampling test files. These files are the repository's architecture and artifact lookup records, so queries now return stale behavior and incomplete test locations. Update the affected unit descriptions and artifact paths to match this change.

🧠 Review effort: Lite

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings October 11, 2026 18:23

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Shared resampling changes rendering across several document formats, and rotated-image sampling still needs correction and human validation.

2 open findings

🧠 Review effort: Lite

Comment thread src/DemaConsulting.CanvasNet.Pptx/PptxDocument.Images.cs Outdated
Comment thread src/DemaConsulting.CanvasNet/Canvas/Surface.cs Outdated
…eature guarantee

Map fallback sample coordinates by block factor (x / factor) instead of
uniform normalization so a partial last block no longer shifts samples.
Applies to PPTX, SVG and PDF. Qualify the thin-feature guarantee to
filtering modes other than Nearest.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings October 11, 2026 19:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Cross-renderer resampling changes need human validation, and the workspace-cache disposal issue remains unresolved.

2 open findings
1 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Add high-contrast feImage resampling regression test

src/​DemaConsulting.CanvasNet.Svg/​SvgCodec.Filters.FeImage.cs:100

nearest: false gives raster feImage its own smooth-resampling path, but the new SVG resampling tests exercise only <image> elements. The existing feImage data-URI test uses a solid blue source, which renders the same with nearest and smooth sampling. Add a high-contrast feImage enlargement or reduction that checks an interpolated boundary or area-averaged pixel, so a regression to nearest sampling is detected.

🧠 Review effort: Lite

Comment thread src/DemaConsulting.CanvasNet/Canvas/Surface.cs
…g tests; qualify Nearest thin-feature docs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings October 11, 2026 19:43

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Two image-edge regressions remain in a broad change spanning the public resampler and three document renderers.

0 open findings

2 resolved since last review
Previously missed (2)

In code that hasn't changed since last review

Medium severity Negative crop edges leave transparent bands

src/​DemaConsulting.CanvasNet.Pptx/​PptxDocument.Images.cs:736

ResolveSrcRect accepts negative crop edges (including the l="-4697" value in PptxRenderTests.cs). For a negative left or top edge, clamping windowX0/windowY0 to zero here and mapping only that source window into the shape makes TryResampleToDevice skip the overhanging strip. Previously the inverse-mapped lookup painted that strip using the source edge pixel. Axis-aligned pictures can therefore show a transparent band where they used to show image content. Keep the full shape footprint and replicate the source edge into overhanging crop regions, or route those crops through an edge-clamped fallback.

Medium severity Incorrect sampling at partial trailing reduction blocks

src/​DemaConsulting.CanvasNet/​Canvas/​SurfaceResampler.cs:105

When BoxReduce makes a partial last block (for example, width 5 and factor 2), SampleBilinearReduced divides every source coordinate by the full factor. At the last source pixel's center (x = 4.5), that produces reduced coordinate 2.25; SampleBilinear then blends the preceding block into the last block instead of sampling the last block at its center. This shifts edge colors in rotated or skewed PDF, PPTX, and SVG images that use the prefiltered fallback. Account for the actual width and center of a partial trailing block when mapping source coordinates to reduced pixels, and cover a non-divisible reduction in a fallback-path test.

🧠 Review effort: Lite

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings October 11, 2026 20:15

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Shared rendering changes span several document formats, and the unresolved PPTX crop-boundary defect needs correction and human validation.

0 open findings

Previously missed (1)

In code that hasn't changed since last review

Medium severity Prevent fractional crop bounds from including an extra source column

src/​DemaConsulting.CanvasNet.Pptx/​PptxDocument.Images.cs:720

A valid PPTX crop can include a source column that lies entirely outside its boundary. For a 45-pixel-wide image with a 40% right crop, cropWidth is computed as 0.6000000238f, so the exclusive pixel endpoint becomes 27.00000107; subtracting 1e-6 still leaves it above 27, and Ceiling(...)-1 includes column 27 instead of ending at 26. When that column differs in color, area or bicubic resampling can pull it into the cropped picture's right edge. Compute the window bounds with precision or a tolerance appropriate to the image size, and test a non-binary fractional crop that lands at an integer pixel boundary.

🧠 Review effort: Lite

Compute crop fractions in double and snap near-integer pixel boundaries before Floor/Ceiling.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings October 11, 2026 21:16

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Public resizing, three document renderers, and pooled rendering buffers change together and warrant final human review.

1 open finding
Previously missed (1)

In code that hasn't changed since last review

Low severity Document supported smooth image-rendering keywords

docs/​design/​canvas-net-svg/​svg-codec.md:170

This out-of-scope statement contradicts both ParseImageRendering in SvgCodec.Elements.cs and the later image design section: optimizeQuality, smooth, and high-quality are recognized and select smooth sampling, including when overriding an inherited pixelated value. List those supported keywords here, reserving the out-of-scope statement for genuinely unsupported values.

🧠 Review effort: Lite

Comment thread docs/reqstream/canvas-net-pptx/pptx-document.yaml
Copilot AI lite review requested due to automatic review settings October 11, 2026 21:26
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Shared resampling changes affect several renderers, and skewed image placement has an unresolved correctness issue.

1 open finding
Previously missed (1)

In code that hasn't changed since last review

Medium severity Shear check uses incorrect source dimensions

src/​DemaConsulting.CanvasNet/​Canvas/​SurfaceResampler.Engine.cs:549

The shear check multiplies the off-diagonal terms by the wrong source dimensions. In Matrix3x2, M12 shifts device Y across the source width, and M21 shifts device X across the source height. For example, a 1000×10 image with M12 = 0.001 shifts one device pixel vertically, but this check sees only 0.01 and accepts the axis-aligned path; that path ignores the shear and can paint the wrong pixels. Compare abs(M12) * source.Width and abs(M21) * source.Height against the tolerance, and test a non-square, slightly sheared image so it takes the inverse-mapped path.

🧠 Review effort: Lite

Copilot AI lite review requested due to automatic review settings October 11, 2026 21:35
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The axis-alignment check can misclassify skewed images, and fractional PPTX crops can paint outside the picture footprint.

0 open findings

1 resolved since last review
Previously missed (2)

In code that hasn't changed since last review

Medium severity Clip fractional crop output to picture footprint

src/​DemaConsulting.CanvasNet.Pptx/​PptxDocument.Images.cs:760

With a fractional srcRect, source includes whole border pixels beyond the crop. pixelToSurface maps that entire window to device coordinates, and CompositeWindow paints all of it without restricting pixels to the picture's unit-square footprint. For example, a 4-pixel image cropped 12.5% on the left and placed at x=10 with width 40 generates a window starting near x=4.3, painting pixels left of x=10. Keep the extra source pixels for filter taps, but clip the axis-aligned output to the actual picture footprint before compositing; the inverse-mapped fallback already checks u and v against [0,1).

Medium severity Correct Matrix3x2 axis checks for resampling

src/​DemaConsulting.CanvasNet/​Canvas/​SurfaceResampler.Engine.cs:549

Matrix3x2 applies M12 to the source x coordinate and M21 to the source y coordinate. This check uses the opposite dimensions, so, for example, a 1000×1 image with M12 = 0.001 passes as axis-aligned even though its far edge moves one device pixel vertically. TryResampleToDevice then drops that rotation/skew and paints the wrong pixels. Compare abs(M12) * source.Width and abs(M21) * source.Height with the tolerance, and correct the matching tolerance description in the design document.

🧠 Review effort: Lite

…nce axes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings October 11, 2026 21:53

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The shared resampler changes several rendering pipelines, and the new cache-safety tests still need consistent requirement and verification traces.

0 open findings

Previously missed (2)

In code that hasn't changed since last review

Low severity Trace workspace eviction and disposal safety test

docs/​reqstream/​canvas-net/​canvas/​surface.yaml:370

The new CompositeSpanWorkspace_DisposeEvictDispose_ReturnsArraysAtMostOnce test (SurfaceTests.cs:1471) checks that evicting a cached workspace and disposing a stale reference cannot return its arrays twice. It is missing from both this requirement's tests list and the matching scenario in docs/verification/canvas-net/canvas/surface.md:394-397. Add it to both lists and describe the eviction/disposal check in the verification scenario so this cache-safety regression is traceable.

Low severity Add outline budget test to GlyphOutlineMemoized requirement

docs/​reqstream/​canvas-net/​fonts/​true-type-font.yaml:344

The new GlyphOutlineMemoized requirement lists the other cache tests but omits TrueTypeFont_GetGlyphOutline_OutlineExceedingCostBudget_IsNotCached. That test is already listed in docs/verification/canvas-net/fonts/true-type-font.md:261-264 and checks that an outline exceeding the retained-geometry budget is decoded but not cached. Add it to this requirement's tests list so the budget behavior has a consistent ReqStream trace.

🧠 Review effort: Lite

@Malcolmnixon
Malcolmnixon merged commit f2f441b into main Oct 11, 2026
7 checks passed
@Malcolmnixon
Malcolmnixon deleted the feature/image-resize branch October 11, 2026 22:15
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.

2 participants