Merge 4b638dc6c46212ff3efffc1d746e72dd62502d9a into 06df71c51446e96939c6a615b7c34ce9123806ba

This commit is contained in:
wm-jenildgohel 2026-02-20 00:17:29 +08:00 committed by GitHub
commit ef9dc100f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 141 additions and 0 deletions

View File

@ -8498,6 +8498,34 @@ base class _NativePicture extends NativeFieldWrapperClass1 implements Picture {
if (width <= 0 || height <= 0) {
throw Exception('Invalid image dimensions.');
}
// Check for dimensions that commonly exceed GPU texture limits.
// Many Android devices have max texture sizes between 2048-4096 pixels.
// While some modern GPUs support up to 8192 or 16384, we warn for
// dimensions above 4096 as they may fail on mid-range and older devices.
const int warningSizeThreshold = 4096;
const int hardLimitThreshold = 8192;
if (width > hardLimitThreshold || height > hardLimitThreshold) {
throw Exception(
'Image dimensions ($width x $height) exceed the maximum supported size '
'of $hardLimitThreshold pixels. The image cannot be rasterized.\n'
'Consider:\n'
' - Reducing the SVG/Picture dimensions\n'
' - Splitting into smaller segments\n'
' - Using a raster image format (PNG/JPEG) instead'
);
}
if (width > warningSizeThreshold || height > warningSizeThreshold) {
// Print warning but allow the operation to proceed
// ignore: avoid_print
print(
'Warning: Picture.toImage dimensions ($width x $height) exceed $warningSizeThreshold pixels.\n'
'This may fail on devices with limited GPU capabilities (particularly older Android devices).\n'
'If rendering fails, consider reducing dimensions or splitting into smaller images.'
);
}
return _futurize(
(_Callback<Image?> callback) => _toImage(width, height, (_Image? image) {
if (image == null) {
@ -8523,6 +8551,34 @@ base class _NativePicture extends NativeFieldWrapperClass1 implements Picture {
throw Exception('Invalid image dimensions.');
}
// Check for dimensions that commonly exceed GPU texture limits.
// Many Android devices have max texture sizes between 2048-4096 pixels.
// While some modern GPUs support up to 8192 or 16384, we warn for
// dimensions above 4096 as they may fail on mid-range and older devices.
const int warningSizeThreshold = 4096;
const int hardLimitThreshold = 8192;
if (width > hardLimitThreshold || height > hardLimitThreshold) {
throw Exception(
'Image dimensions ($width x $height) exceed the maximum supported size '
'of $hardLimitThreshold pixels. The image cannot be rasterized.\n'
'Consider:\n'
' - Reducing the SVG/Picture dimensions\n'
' - Splitting into smaller segments\n'
' - Using a raster image format (PNG/JPEG) instead'
);
}
if (width > warningSizeThreshold || height > warningSizeThreshold) {
// Print warning but allow the operation to proceed
// ignore: avoid_print
print(
'Warning: Picture.toImageSync dimensions ($width x $height) exceed $warningSizeThreshold pixels.\n'
'This may fail on devices with limited GPU capabilities (particularly older Android devices).\n'
'If rendering fails, consider reducing dimensions or splitting into smaller images.'
);
}
final image = _Image._();
_toImageSync(width, height, targetFormat.index, image);
return Image._(image, image.width, image.height);

View File

@ -172,6 +172,17 @@ Dart_Handle Picture::DoRasterizeToImage(const sk_sp<DisplayList>& display_list,
return tonic::ToDart("Image dimensions for scene were invalid.");
}
// Warn about dimensions that may exceed common GPU texture limits.
// This provides a better error message than a silent failure or crash.
constexpr uint32_t kWarningSizeThreshold = 4096;
if (width > kWarningSizeThreshold || height > kWarningSizeThreshold) {
FML_LOG(WARNING) << "Picture.toImage dimensions (" << width << " x "
<< height << ") exceed " << kWarningSizeThreshold
<< " pixels. This may fail on devices with limited GPU "
<< "capabilities. Consider reducing dimensions or "
<< "splitting into smaller images.";
}
auto* dart_state = UIDartState::Current();
auto image_callback = std::make_unique<tonic::DartPersistentValue>(
dart_state, raw_image_callback);

View File

@ -58,6 +58,80 @@ void main() {
Picture.onDispose = null;
});
test('toImage throws for dimensions exceeding hard limit', () async {
final picture = _createPicture();
// Test exceeding hard limit (8192 pixels)
expect(
() => picture.toImage(8193, 100),
throwsA(isA<Exception>().having(
(e) => e.toString(),
'message',
contains('exceed the maximum supported size'),
)),
);
expect(
() => picture.toImage(100, 8193),
throwsA(isA<Exception>().having(
(e) => e.toString(),
'message',
contains('exceed the maximum supported size'),
)),
);
picture.dispose();
});
test('toImageSync throws for dimensions exceeding hard limit', () async {
final picture = _createPicture();
// Test exceeding hard limit (8192 pixels)
expect(
() => picture.toImageSync(8193, 100),
throwsA(isA<Exception>().having(
(e) => e.toString(),
'message',
contains('exceed the maximum supported size'),
)),
);
expect(
() => picture.toImageSync(100, 8193),
throwsA(isA<Exception>().having(
(e) => e.toString(),
'message',
contains('exceed the maximum supported size'),
)),
);
picture.dispose();
});
test('toImage succeeds for normal dimensions', () async {
final picture = _createPicture();
// Should not throw for normal dimensions
final image = await picture.toImage(100, 100);
expect(image.width, 100);
expect(image.height, 100);
image.dispose();
picture.dispose();
});
test('toImageSync succeeds for normal dimensions', () async {
final picture = _createPicture();
// Should not throw for normal dimensions
final image = picture.toImageSync(100, 100);
expect(image.width, 100);
expect(image.height, 100);
image.dispose();
picture.dispose();
});
}
Picture _createPicture() {