Closed Bug 1546894 Opened 5 years ago Closed 5 years ago

canvas: arcTo after closePath does not create a tangent line to the arc

Categories

(Core :: Graphics: Canvas2D, defect, P3)

66 Branch
defect

Tracking

()

RESOLVED FIXED
mozilla69
Tracking Status
firefox69 --- fixed

People

(Reporter: qdirks, Assigned: nical)

Details

Attachments

(4 files)

User Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0

Steps to reproduce:

Make one ctx.moveTo(x,y) call and two ctx.lineTo(x,y) calls to form the first two sides of a triangle. Complete the triangle by calling ctx.closePath(). Then call ctx.arcTo(x1,y1,x2,y2,radius). Then call ctx.stroke().

Actual results:

The line drawn to the arc is not tangent to the arc. The last point of the previous subpath, as specified by the second lineTo call, is incorrectly used as the (x0, y0) control point of the arc. To clarify, it would be called the last point of the 'previous' subpath because of the supposed functioning of the closePath method preceding the call to arcTo.

Expected results:

The last point of the current subpath, as specified by the moveTo call because of the closePath call, should be used as the (x0, y0) control point of the arc.

Following is the relevant sections from https://www.w3.org/TR/2dcontext/#building-paths:

closePath: """
The closePath() method must do nothing if the object's path has no subpaths. Otherwise, it must mark the last subpath as closed, create a new subpath whose first point is the same as the previous subpath's first point, and finally add this new subpath to the path.
"""

arcTo: """
The arcTo(x1, y1, x2, y2, radius) method must first ensure there is a subpath for (x1, y1). Then, the behavior depends on the arguments and the last point in the subpath, as described below.

Negative values for radius must cause the implementation to throw an IndexSizeError exception.

Let the point (x0, y0) be the last point in the subpath.

If the point (x0, y0) is equal to the point (x1, y1), or if the point (x1, y1) is equal to the point (x2, y2), or if the radius radius is zero, then the method must add the point (x1, y1) to the subpath, and connect that point to the previous point (x0, y0) by a straight line.

Otherwise, if the points (x0, y0), (x1, y1), and (x2, y2) all lie on a single straight line, then the method must add the point (x1, y1) to the subpath, and connect that point to the previous point (x0, y0) by a straight line.

Otherwise, let The Arc be the shortest arc given by circumference of the circle that has radius radius, and that has one point tangent to the half-infinite line that crosses the point (x0, y0) and ends at the point (x1, y1), and that has a different point tangent to the half-infinite line that ends at the point (x1, y1), and crosses the point (x2, y2). The points at which this circle touches these two lines are called the start and end tangent points respectively. The method must connect the point (x0, y0) to the start tangent point by a straight line, adding the start tangent point to the subpath, and then must connect the start tangent point to the end tangent point by The Arc, adding the end tangent point to the subpath.
"""

Attached file arcToBug.html

Moving this over to the component. Unfortunately, I don't understand what is the actual problem but developers will know for sure.

Component: Untriaged → Canvas: 2D
Product: Firefox → Core

Thanks for the bug report and analysis. It appears that the way we track the current point in the skia backend doesn't properly take close events into account.

Assignee: nobody → nical.bugzilla
Priority: -- → P3
Status: UNCONFIRMED → ASSIGNED
Ever confirmed: true
Pushed by nsilva@mozilla.com:
https://hg.mozilla.org/integration/autoland/rev/5bc7f1dbafe5
Fix PathBuilderCapture::CurrentPoint when closing a path. r=lsalzman
https://hg.mozilla.org/integration/autoland/rev/b3176ad6f3a7
Fix PathBuilderSkia::CurrentPoint. r=lsalzman

The canvas implementation maintains user-space and a device-space path builder, and switch from user-space to the device-space one when a a transform is set in the middle of building a path. When that happens we "finish" the original builder and create the new one out of the resulting path, but as a result we lose the information about the current point and the previous MoveTo location.
All backends appear to get this wrong to some varying extent. For example the Cairo backend stores the the current point in the path just to avoid this problem but doesn't keep the previous MoveTo position which means we don't do the right thing if the we get a transform followed by a Close() and an edge without a MoveTo.

What would make sense here is to use the PathBuilder rather than the Path in TransformedCopyToBuilder. I didn't know why we do don't this in the first place (all of this dates from the original moz2d implementation).

Flags: needinfo?(nical.bugzilla)

Because of the way the canvas 2D implementation juggles between path builders and paths, we have to keep track of current and first points in the path, in case a builder is created out of it.

Pushed by nsilva@mozilla.com:
https://hg.mozilla.org/integration/mozilla-inbound/rev/c246c374ce11
Fix PathBuilderSkia::CurrentPoint. r=lsalzman
https://hg.mozilla.org/integration/mozilla-inbound/rev/cea22203c716
Fix PathBuilderCapture::CurrentPoint when closing a path. r=lsalzman
https://hg.mozilla.org/integration/mozilla-inbound/rev/45f8d9f8f333
Keep track of current and begin points in path objects in case we'll create a builder from them. r=lsalzman
Status: ASSIGNED → RESOLVED
Closed: 5 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla69
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: