SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google's Skia Graphics Library. It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.
When using SKPixmap to get the raw pixel data via GetPixelSpan(), the length that is used to create the span is incorrectly calculated when a pixmap is derived from ExtractSubset on another pixmap/bitmap (or manually created with a larger-than-normal row size).
Code
// A sample PNG image that is larger than the subset required below (25x25).
// This sample image is sized: 111x33
byte[] imageData = [
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x21,
0x08, 0x02, 0x00, 0x00, 0x00, 0x58, 0xF5, 0x62, 0x2B, 0x00, 0x00, 0x00,
0x03, 0x73, 0x42, 0x49, 0x54, 0x08, 0x08, 0x08, 0xDB, 0xE1, 0x4F, 0xE0,
0x00, 0x00, 0x00, 0x7B, 0x49, 0x44, 0x41, 0x54, 0x68, 0x81, 0xED, 0xDA,
0xB1, 0x0D, 0x02, 0x51, 0x0C, 0x05, 0xC1, 0x6F, 0x44, 0xFF, 0x2D, 0x9B,
0x80, 0x13, 0x0D, 0xB0, 0xD2, 0x09, 0x34, 0x13, 0x39, 0xB4, 0x36, 0x7E,
0xB3, 0xBB, 0x87, 0x8A, 0x9A, 0x95, 0xDD, 0x7D, 0x7E, 0xAE, 0x7B, 0x5F,
0xF9, 0x75, 0x33, 0x73, 0xCE, 0x79, 0xDC, 0xFD, 0xC6, 0x5F, 0x51, 0xB3,
0xA4, 0x66, 0x49, 0xCD, 0x92, 0x9A, 0x25, 0x35, 0x4B, 0x6A, 0x96, 0xD4,
0x2C, 0xA9, 0x59, 0x52, 0xB3, 0xA4, 0x66, 0x49, 0xCD, 0x92, 0x9A, 0x25,
0x35, 0x4B, 0x6A, 0x96, 0xD4, 0x2C, 0xA9, 0x59, 0x52, 0xB3, 0xA4, 0x66,
0x49, 0xCD, 0x92, 0x9A, 0x25, 0x35, 0x4B, 0x6A, 0x96, 0xD4, 0x2C, 0xA9,
0x59, 0x52, 0xB3, 0xA4, 0x66, 0x49, 0xCD, 0xD2, 0xB5, 0xEA, 0x7A, 0x8F,
0x92, 0xF8, 0xD2, 0x58, 0xC7, 0x85, 0x5E, 0x3B, 0x1C, 0x0F, 0x3A, 0x22,
0x47, 0x3D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE,
0x42, 0x60, 0x82
];
using var bitmap = SKBitmap.Decode(imageData);
// Get a smaller portion of the bitmap (bottom-right corner--to exemplify the last row requiring less than the
// pixmap's full row bytes).
using var bitmapSubset = new SKBitmap();
if (!bitmap.ExtractSubset(bitmapSubset, SKRectI.Create(bitmap.Width - 25, bitmap.Height - 25, 25, 25)))
throw new InvalidOperationException("Unable to create subset!");
// Get the subset bitmap's pixmap.
var pixmap = bitmapSubset.PeekPixels();
var pixmapInfo = pixmap.Info;
var pixmapBytes = pixmap.GetPixelSpan();
// Calculate the actual byte length of the pixmap data.
var pixmapActualByteLength = pixmapInfo.Height > 0
? ((pixmap.RowBytes * (pixmapInfo.Height - 1)) + pixmapInfo.RowBytes)
: 0;
// Validate pixmap span's length.
if (pixmapBytes.Length < pixmapActualByteLength)
throw new InvalidOperationException("Pixel data not fully specified!");
Expected Behavior
No exception should be thrown and SKPixmap.GetPixelSpan() should return a span with a length of 11200 bytes (in this particular example).
The span's length should match the native Skia API: SkPixmap::computeByteSize() which in turn calls SkImageInfo::computeByteSize(pixmap->rowBytes()). Neither of these APIs appear to be available in the exposed/referenced C library. Though it is easy to calculate as seen in the example code above.
Actual Behavior
InvalidOperationException is thrown with the message: Pixel data not fully specified!
Version of SkiaSharp
Other (Please indicate in the description)
Last Known Good Version of SkiaSharp
Other (Please indicate in the description)
IDE / Editor
Visual Studio (Windows)
Platform / Operating System
Windows
Platform / Operating System Version
Tested on Windows 10.0.19045.0 x64, .NET 8.0.1 x64, SkiaSharp 2.88.7.
Devices
In theory, bug should occur on all devices.
Relevant Screenshots
No response
Relevant Log Output
No response
Code of Conduct
[X] I agree to follow this project's Code of Conduct
Description
When using
SKPixmap
to get the raw pixel data viaGetPixelSpan()
, the length that is used to create the span is incorrectly calculated when a pixmap is derived fromExtractSubset
on another pixmap/bitmap (or manually created with a larger-than-normal row size).Code
Expected Behavior
No exception should be thrown and
SKPixmap.GetPixelSpan()
should return a span with a length of 11200 bytes (in this particular example).The span's length should match the native Skia API:
SkPixmap::computeByteSize()
which in turn callsSkImageInfo::computeByteSize(pixmap->rowBytes())
. Neither of these APIs appear to be available in the exposed/referenced C library. Though it is easy to calculate as seen in the example code above.Actual Behavior
InvalidOperationException
is thrown with the message: Pixel data not fully specified!Version of SkiaSharp
Other (Please indicate in the description)
Last Known Good Version of SkiaSharp
Other (Please indicate in the description)
IDE / Editor
Visual Studio (Windows)
Platform / Operating System
Windows
Platform / Operating System Version
Tested on Windows 10.0.19045.0 x64, .NET 8.0.1 x64, SkiaSharp 2.88.7.
Devices
In theory, bug should occur on all devices.
Relevant Screenshots
No response
Relevant Log Output
No response
Code of Conduct