Open petsuter opened 3 months ago
The exception is thrown here in WritePixelsImpl
:
because the calculation is done in checked
context.
However, even if you cast the multiplication to be in uint
, then upon creating Bitmap from source (e.g. Cloning), you reach:
int bufferSize = checked(_backBufferStride.Value * source.PixelHeight);
source.CriticalCopyPixels(rcFull, _backBuffer, bufferSize, _backBufferStride.Value);
And the last one is in CriticalCopyPixels
where minRequiredDestSize
is again calculated in checked context as int
.
If you fix those 3, it works as expected and the bitmap is generated with the proper rectangle in the bottom corner.
Since the IWICBitmapSource_CopyPixels_Proxy
accepts uint
as size as well (documented), I do not see any other reason for this than a bug. Rest of the source would also indicate so.
However, the problem though is being 2GB limitation for e.g. byte[]
, which means that unless you would init a bitmap source that big (30k*30k*4
) from an unmanaged array, you're not able to init it.
So in the end I guess the solutions are either 1) throw on constructor 2) different behaviour for unmanaged buffers.
As per 2), funny enough, IWICImagingFactory_CreateBitmapFromMemory_Proxy
fails for buffers larger than Int32.MaxValue
with 0x80070057
, so no dice I guess. (That's for BitmapSource
but it inherits, yet WriteableBitmap
uses Mil)
Could it not be left uninitialized and filled in parts with WritePixels? No full sized byte[]
array is required for that. Or is it used internally?
(What is Mil?)
It is possible to fix the few calcs in WriteableBitmap
and it will be possible to write pixels with your code into the bitmaps with size over 23xxx * 23xxx and 4 channels. It is also possible to clone and save it afterwards.
However, initializing BitmapSource
from either managed array or unmanaged array (which uses IWICImagingFactory_CreateBitmapFromMemory_Proxy
fails with 0x80070057
for buffers bigger than Int32.MaxValue
.
I do not know what the call-chain and magic is behind wpfgfx
right now as I've never needed it, neither have the time to do the necessary research on why that works currently but given the fact it uses two buffers, it is probably not using the WIC call/interface.
related #5125
2GB limitation for e.g. byte[]
1) throw on constructor
Not thrilled about that, it's the WritePixels
operation that fails, not creating the bitmap.
What is Mil?
Media Integration Layer
it is probably not using the WIC call/interface
Doesn't the WritePixels just call
But yes, looks like WIC does not support >2GB bitmaps, at least for the standard pixel formats.
Still doesn't bypass the single-dim array length limit, it just allows the underlying data to be bigger in size than 2GB, see remarks: The maximum size in any single dimension is 2,147,483,591 (0x7FFFFFC7) for byte arrays and arrays of single-byte structures, and 2,146,435,071 (0X7FEFFFFF) for arrays containing other types.
Not thrilled about that, it's the
WritePixels
operation that fails, not creating the bitmap.
That is true, we could fix this particular thing with WriteableBitmap
and document the rest with BitmapSource
. I can do it as I've already spent time on researching this and have it basically ready, just wanna run some tests.
Doesn't the WritePixels just call https://github.com/dotnet/wpf/blob/0310ea93a4ea4af6123dc5de3da1e3e3ee582100/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/common/exports.cpp#L291
Yeah, WritePixels -> WritePixelsImpl -> MilUtility_CopyPixelBuffer
, I had BitmapSource
in mind there which uses the IWICBitmapSource_CopyPixels_Proxy
which works with 4GB, but you cannot create it.
I'm worried it won't work on many graphics cards...
Still doesn't bypass the single-dim array length limit, it just allows the underlying data to be bigger in size than 2GB, see remarks: The maximum size in any single dimension is 2,147,483,591 (0x7FFFFFC7) for byte arrays and arrays of single-byte structures, and 2,146,435,071 (0X7FEFFFFF) for arrays containing other types.
Not thrilled about that, it's the
WritePixels
operation that fails, not creating the bitmap.That is true, we could fix this particular thing with
WriteableBitmap
and document the rest withBitmapSource
. I can do it as I've already spent time on researching this and have it basically ready, just wanna run some tests.Doesn't the WritePixels just call https://github.com/dotnet/wpf/blob/0310ea93a4ea4af6123dc5de3da1e3e3ee582100/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/common/exports.cpp#L291
Yeah, WritePixels -> WritePixelsImpl ->
MilUtility_CopyPixelBuffer
, I hadBitmapSource
in mind there which uses theIWICBitmapSource_CopyPixels_Proxy
which works with 4GB, but you cannot create it.
If the single-dim array limit is the issue, using a larger underlying type like a double array instead of a byte array would quadruple the size limit. But I don't know if that would play well with all the native underlying functions.
I'm worried it won't work on many graphics cards...
I mean, you could create it previously, you just couldn't write to the entirety of its dimension. So that's a bug imho. As a developer using this class/interface, you should be aware of the implications of creating such a large bitmap. That's just common sense. It's like using raw pointers in C#, don't do it if you don't know what it means and how to interact with GC.
If the single-dim array limit is the issue, using a larger underlying type like a double array instead of a byte array would quadruple the size limit. But I don't know if that would play well with all the native underlying functions.
As noted in the PR and here as well, WIC
only allows creation of 2GB buffers bitmaps in size, which fits perfectly for single-dim byte array (for BitmapSource
). But for WriteableBitmap
, that works just fine after the PR as the layer supports it.
such a large bitmap
(2-3GB is not really large today. Large scientific images can be 2-3TB. Nobody expects to be able to handle such large images directly yet. (Even with machines that have TBs of RAM today!) But would be nice to not crash on comparably much smaller 2-3GB images.)
I'm worried it won't work on many graphics cards...
A valid concern unfortunately. But could be tested maybe.
Description
WriteableBitmap
can not exceed 4 GB I think (WriteableBitmap
constructor fails) but even for sizes below 4 GB (but above 2GB) there are overflow exceptions inWriteableBitmap.WritePixels
, even when only writing a tiny amount of data.Reproduction Steps
Expected behavior
No exception. (Or if >2GB is considered unsupported, the constructor should already fail?)
Actual behavior
Exception:
Regression?
I don't think so.
Known Workarounds
None
Impact
Unknown
Configuration
.NET 8, Windows 11, x64. I don't think this is specific.
Other information
None.
Maybe uint should be used somewhere instead of int in the internal offset calculations?