Commit 5aa6215
committed
NumSharp.Bitmap: add [SupportedOSPlatform], comprehensive tests, and OpenBugs
Source changes (np_.extensions.cs, NumSharp.Bitmap.csproj):
- Add [SupportedOSPlatform("windows")] attribute to np_ class, marking the
library as Windows-only since System.Drawing.Common wraps GDI+ which is
only available on Windows since .NET 6. This enables CA1416 analyzer
warnings for cross-platform consumers.
- Update package description to clarify Windows-only + GDI+ dependency.
New test coverage (BitmapExtensionsTests.cs — 52 tests):
- ToNDArray: dtype, contiguity, size consistency, discardAlpha (copy/no-copy),
flat output, pixel data correctness, odd width handling, copy vs no-copy
data equality, multiple embedded resources (captcha-a through captcha-d,
odd-width).
- ToBitmap: 32bpp round-trip, auto format detection (24bpp/32bpp), flat
input reshaping, error handling (null, wrong ndim, format mismatch,
multiple pictures), size property verification, specific pixel value
round-trip fidelity.
- Image.ToNDArray: delegation to Bitmap overload.
- AsNDArray: BitmapData zero-copy wrapper (shaped, with discardAlpha).
- ToBytesPerPixel: all supported pixel formats, unsupported formats,
DontCare rejection.
- Synthetic images: zeros/ones images at various sizes.
OpenBugs.Bitmap.cs (8 bugs, 11 failing tests):
Bug 1 (GH #396): ToNDArray(copy:true) computes bytes-per-pixel as
stride/width — wrong when GDI+ stride includes 4-byte alignment
padding. 3px wide 24bpp: stride=12, stride/width=4 instead of 3.
5px wide: stride=16, 32 bytes copied but reshape expects 30 →
IncorrectShapeException.
Bug 2 (GH #440): Same root cause on 2px wide 24bpp: stride=8,
stride/width=4 instead of 3, producing (1,H,W,4) shape.
Bug 3: AsNDArray(flat:true) accesses shape[3] on the 1-d flat array
before reshaping to 4-d → IndexOutOfRangeException. Both with and
without discardAlpha.
Bug 4 (GH #475): ToBitmap on sliced (non-contiguous) NDArray fails
in MultiIterator.Assign — flat vector can't broadcast against the
sliced 4-d shape → IncorrectShapeException.
Bug 5 (GH #491): ToBitmap with non-byte dtype (e.g. int32) throws
cryptic InvalidCastException from CopyTo<byte>. No dtype guard or
auto-conversion.
Bug 6 (GH #440): ToBitmap stride padding logic concatenates a zeros
column when stride != width*bpp. This is fundamentally wrong — GDI+
pads rows to 4-byte boundaries by bytes, not by adding pixel columns.
The concat inflates nd.size beyond the destination buffer →
ArgumentOutOfRangeException. Tested at 1px and 5px wide.
Bug 7: 24bpp 2px-wide round-trip produces corrupted pixel data because
Bug 2's wrong bpp shifts channel boundaries. SetPixel(1,0)=(40,50,60)
recovers as (0,40,50).
Bug 8: ToBitmap with unsupported channel count (e.g. 2) lets
extractFormatNumber() fall through with format=DontCare, producing
an unhelpful GDI+ "Parameter is not valid" error instead of a
descriptive ArgumentException.
Two architectural root causes explain most bugs:
1. Copy path uses stride/width for bpp instead of deriving from
PixelFormat — the no-copy path correctly uses ReshapeFlatData().
2. ToBitmap tries to handle stride padding by concatenating columns
instead of copying row-by-row with per-row padding.1 parent 587e73d commit 5aa6215
File tree
4 files changed
+1148
-1
lines changed- src/NumSharp.Bitmap
- test/NumSharp.UnitTest
- NumSharp.Bitmap
4 files changed
+1148
-1
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
| 8 | + | |
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| 5 | + | |
5 | 6 | | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
| 12 | + | |
11 | 13 | | |
12 | 14 | | |
13 | 15 | | |
| |||
0 commit comments