SVG fails when Width is high

  1. SDK Version: 36
  2. Platforms(Android/iOS/web/all): Android device

<Svg width="21489" >

The above code makes my phone crash. Is there any known reason for that?

I’m testing on a Nokia 9 PureView. Works fine on iPhone simulator.

Best,
Andy

It crashes with a stack trace mentioning:

java.lang.RuntimeException: Canvas: trying to draw too large(267666984bytes) bitmap.
	at android.view.DisplayListCanvas.throwIfCannotDraw(DisplayListCanvas.java:229)
	at android.view.RecordingCanvas.drawBitmap(RecordingCanvas.java:82)
	at abi36_0_0.host.exp.exponent.modules.api.components.svg.SvgView.onDraw(SvgView.java:6)
	at android.view.View.draw(View.java:21881)
	at android.view.View.updateDisplayListIfDirty(View.java:20754)
	at android.view.View.draw(View.java:21607)
	at android.view.ViewGroup.drawChild(ViewGroup.java:4558)
	at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4333)
	at abi36_0_0.com.facebook.react.views.view.ReactViewGroup.dispatchDraw(ReactViewGroup.java:2)
	at android.view.View.draw(View.java:21884)
	at android.view.View.updateDisplayListIfDirty(View.java:20754)
	at android.view.View.draw(View.java:21607)
	at android.view.ViewGroup.drawChild(ViewGroup.java:4558)
	at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4333)
[...]

So it seems that the SVG is rendered to a Canvas as a bitmap and there’s nothing that constrains the size of the bitmap. So if it’s too large, an exception is thrown (presumably to prevent the app from trying to use too much RAM).

I’m not sure how best to deal with something like this, assuming you do want to display such a large SVG in a ScrollView (based on one of your other posts.) I suppose some way of rendering just the part of the SVG that is within the screen (and a little past the edge of the screen) and updates on scroll would be the solution, similar to a VirtualizedList. Maybe using ClipPath will work for this.

Well funny thing is it does not matter how much content I put in. Like a polygon with 6000 sides. But the size does. I renders really fast, so I do not think it renders a bitmap. But it might think it does, and take precautions accordingly. If I go over 3300 in width it crashes. It is not that much given current screen resolutions? I’m not sure if all deveces fail at 3400 or if it is dependent on the device!?

I could make a smaller one and scale it it I guess. For now I just restricted the width to 3300 and so it “zooms out” when showing more than 50 km in distance.

Is there any way I can look into the issue, so I know how to set the limit? Like what the reason is for not being able to draw?

Best,
Andy

Here are some related issues:

App crashes on Android (release variant) with `Canvas: trying to draw too large` in logcat · Issue #23479 · facebook/react-native · GitHub (Not SVG, but the exception is the same).

The exception is thrown in Android code rather than React Native or Expo code. The line numbers/file names don’t match, but this looks basically like where it’s coming from. As you can see, if the calculated size is > MAX_BITMAP_SIZE then it throws the exception. In this code, MAX_BITMAP_SIZE is defined as 100MB.

If you search on GitHub you’ll find various similar versions of this code. For all the ones I looked at, the limit was 100MB.

What’s the height of your SVG? Something like 1000?
I think it uses 32 bits for each pixel (8 bits each for R, G, B and A), so:
3400 * 1000 * 32 = 108800000 > 104857600

eh… my calculations are not making sense. Sorry :sweat_smile:
Should be 32 bits, not 32 bytes!

Are you using something like 8000 for the height then?

3400 * 8000 * 4 = 108800000 > 104857600

Very strange. If I use height={1000} width={2912} it works. If I use height={1000} width={2913} it crashes saying that the size is 104868000 bytes.

That implies it’s using 36 bytes per pixel!?

EDIT: OK, so I think this explains things to some extent. Android is scaling up the canvas on phones with high DPI displays. I don’t know how you tell the resolution/how much scaling is being done which you would need to know to calculate the maximum size.

Height for the Svg is 635

Or 49 and 50 would both work if 3300 passes and 3400 fails

Used this one

But would require a bit depth of 400 to max out the 100 MB limit

(width * pixelRatio) * (height * pixelRatio) * bitDepth / 8 / 1024 / 1024 = MB

Oh yeah 2,267716535433071 pixel ratio

so a bit depth of 77,8

The size in bytes (before scaling) is width * height * 4 (because 32 bits = 4 bytes).

So looks like a scaling factor of 12.5 in your case.

Width of 3300 works out to about 99.92 MB.
3400 works out to about 102.95 MB.

I calculated it like this:
(3400 * 2,26771653543307) * (635 * 2,26771653543307) * 32 / 8 / 1024 / 1024
= 42,35359252

Yours like this:
(3400 * 4 * 2,26771653543307) * (635 * 4 * 2,26771653543307) / 8 / 1024 / 1024
= 21,17679626

Yours according to the way I calculate should 16 bits?

But not the most important …I still don’t get how to get to the 100 MB limit

OK, I implemented this one so far. Just looking at the amount of pixels in play. And it scales down on my phone after 55 kilometers.


This is about 800 kilometers

    getMaxWidth(proposalWidth, height) {
        if(Platform.OS === 'ios') {
            return proposalWidth;
        } else {
            var h = PixelRatio.getPixelSizeForLayoutSize(height);
            var w = PixelRatio.getPixelSizeForLayoutSize(proposalWidth);

            if(h * w > 10874155) {
                for (var i = proposalWidth; i >= 0; i--) {
                    if(h *  PixelRatio.getPixelSizeForLayoutSize(i) < 10874155) {
                        return i;
                    }
                }
            } else {
                return proposalWidth;
            }
        }
    }
1 Like

My calculation was already in bytes, so no need to divide by 8. Also you shouldn’t multiply by the bytes-per-pixel value twice. If you make those two changes then your modification of my calculation would be the same as yours.

Anyway, glad you worked this out :slight_smile:

Today is fails on different values …smaller …for no apparent reason :-/

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.