7

I have a ASUS Nexus 7 running Android 4.2.2 My application is generatng a SIGSEGV in sk_malloc_flags when running the following code:

static Picture createDrawing() {

    Path firstPath = new Path();
    firstPath.moveTo(3058, 12365);
    firstPath.lineTo(8499, 3038);
    firstPath.lineTo(9494, 3619);
    firstPath.lineTo(4053, 12946);
    firstPath.close();

    Path fourthPath = new Path();
    fourthPath.moveTo(3065, 12332);
    fourthPath.lineTo(4053, 12926);
    fourthPath.lineTo(9615, 3669);
    fourthPath.lineTo(8628, 3075);
    fourthPath.close();

    Picture picture = new Picture();
    Canvas canvas = picture.beginRecording(12240, 15840);
    canvas.clipPath(firstPath);
    canvas.clipPath(fourthPath); << SIGSEGV occurs here
    picture.endRecording();
    return picture;
}

The SIGSEGV is reported as follows:

    I/DEBUG   (  124): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr deadbaad
    I/DEBUG   (  124):     r0 00000027  r1 deadbaad  r2 4017f258  r3 00000000
    I/DEBUG   (  124):     r4 00000000  r5 bed72434  r6 bed72508  r7 1be773bc
    I/DEBUG   (  124):     r8 1be730f9  r9 000042c3  sl 00000001  fp 67185010
    I/DEBUG   (  124):     ip 40443f3c  sp bed72430  lr 401522f9  pc 4014e992  cpsr 60000030
...
    I/DEBUG   (  124): backtrace:
    I/DEBUG   (  124):     #00  pc 0001a992  /system/lib/libc.so
    I/DEBUG   (  124):     #01  pc 00018070  /system/lib/libc.so (abort+4)
    I/DEBUG   (  124):     #02  pc 000be4b4  /system/lib/libskia.so (sk_malloc_flags(unsigned int, unsigned int)+28)
    I/DEBUG   (  124):     #03  pc 0008afc0  /system/lib/libskia.so (SkRegion::op(SkRegion const&, SkRegion const&, SkRegion::Op)+1716)
    I/DEBUG   (  124):     #04  pc 00089448  /system/lib/libskia.so (SkRasterClip::op(SkRasterClip const&, SkRegion::Op)+128)

I have obviously simplified the code to that shown above, the full application uses transforms, etc based on some input data to generate the values. Are they any suggestions as to how to fix this without implementing my own code for clipping in the general case?

Winnie
  • 71
  • 2
  • What happens, if you leave only `canvas.clipPath(firstPath);`, removing call for `fourthPath`? – David Jashi Jun 25 '13 at 13:11
  • Try if putting `canvas.clipPath(fourthPath, , Region.Op.REPLACE);` and see if that's the cause, if not, then, as David Jashi said, try removing the call to see if it's the problem. – g00dy Jun 27 '13 at 07:52
  • Another thing I can think of, is if you draw them separately: `Picture picture = new Picture(); Canvas canvas = picture.beginRecording(12240, 15840); canvas.clipPath(firstPath); picture.endRecording(); canvas = picture.beginRecording(12240, 15840); canvas.clipPath(fourthPath, Region.Op.INTERSECT);` /*You can also try without `Region.Op.INTERSECT`*/ `picture.endRecording();` – g00dy Jun 27 '13 at 07:57

3 Answers3

5

This looks like an ill fated corner case for clipPath handling.

canvas.clipPath(fourthPath);

causes a merge with previous firstPath however since these are complex (non-rectangular) shapes system tries to draw them as scanlines and merge it afterwards. To make this merge, it needs to allocate some memory but as you can see in SkRegion.cpp, it goes for heuristic worst case.

static int compute_worst_case_count(int a_count, int b_count) {
    int a_intervals = count_to_intervals(a_count);
    int b_intervals = count_to_intervals(b_count);
    // Our heuristic worst case is ai * (bi + 1) + bi * (ai + 1)
    int intervals = 2 * a_intervals * b_intervals + a_intervals + b_intervals;
    // convert back to number of RunType values
    return intervals_to_count(intervals);
}

For your paths this worst_case_count becomes close to 2GB and you get an abort due to not getting that big memory from malloc.

I couldn't see any way out of it using different parameters. Anything which avoids merging clipPaths must help, like calling clipPath with Region.Op.REPLACE. Region.Op.INTERSECT should fail too.

I would concentrate on avoiding calling clipPath with a complex path on top of a complex path.

If it suits your use case, you can use same Path object for setting canvas.clipPath(). For example:

Picture picture = new Picture();
Canvas canvas = picture.beginRecording(12240, 15840);
Path path = new Path();
path.moveTo(3058, 12365);
path.lineTo(8499, 3038);
path.lineTo(9494, 3619);
path.lineTo(4053, 12946);
path.close();
canvas.clipPath(path);
// do stuff with canvas
path.moveTo(3065, 12332);
path.lineTo(4053, 12926);
path.lineTo(9615, 3669);
path.lineTo(8628, 3075);
path.close();
canvas.clipPath(path, Region.Op.REPLACE);
// do more stuff with canvas
picture.endRecording();
return picture;

Since path contains previous drawings, you can just continue updating it. If this is not applicable to your case, you either need to make those numbers smaller or partition your complex regions into smaller ones to avoid worst case heuristic to become too big.

auselen
  • 27,577
  • 7
  • 73
  • 114
1

Ok, let me put this in an answer, since it looks quite logical to me:

To see if the problem is comming from the consequtive calls to clipPath(Path), try removing the call at first or putting canvas.clipPath(fourthPath, Region.Op.REPLACE); in the place of canvas.clipPath(fourthPath); and see if that's the cause.

Another thing I can think of, is if you draw them separately:

Picture picture = new Picture();
Canvas canvas = picture.beginRecording(12240, 15840);
canvas.clipPath(firstPath);
picture.endRecording();

canvas = picture.beginRecording(12240, 15840);
canvas.clipPath(fourthPath);
picture.endRecording();
g00dy
  • 6,752
  • 2
  • 30
  • 43
0

It looks like Canvas.clipPath() is not supported with hardware acceleration, at least doc says that: http://developer.android.com/guide/topics/graphics/hardware-accel.html#unsupported

The only workaround that comes to my mind is to turn off hardware acceleration.

You can do it on:

  • Application level
    <application android:hardwareAccelerated="true" ...>
    in manifest

  • Activity level
    android:hardwareAccelerated="false"
    for activity in manifest

  • View level
    view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    for an individual view at runtime

Docs:
http://developer.android.com/guide/topics/graphics/hardware-accel.html#controlling

pawelzieba
  • 16,082
  • 3
  • 46
  • 72
  • Turning on or off hardware acceleration does not affect the crash. The Canvas is not associated with a view. – Winnie Jun 25 '13 at 09:40
  • Is `12240 x 15840` not a little to big for a picture? Have you tried with smaller dimensions? – pawelzieba Jun 25 '13 at 13:13