FloatPoint Path::currentPoint() const { if (hasCurrentPoint()) { SkPoint lastPt; m_path->getLastPt(&lastPt); return lastPt; } float quietNaN = std::numeric_limits<float>::quiet_NaN(); return FloatPoint(quietNaN, quietNaN); }
void Path::addArc(const FloatPoint& center, float radius, float startAngle, float endAngle, bool anticlockwise) { // The OpenVG spec says nothing about inf as radius or start/end angle. // WebKit seems to pass those (e.g. https://bugs.webkit.org/show_bug.cgi?id=16449), // so abort instead of risking undefined behavior. if (!isfinite(radius) || !isfinite(startAngle) || !isfinite(endAngle)) return; // For some reason, the HTML 5 spec defines the angle as going clockwise // from the positive X axis instead of going standard anticlockwise. // So let's make it a proper angle in order to keep sanity. startAngle = fmod((2.0 * piDouble) - startAngle, 2.0 * piDouble); endAngle = fmod((2.0 * piDouble) - endAngle, 2.0 * piDouble); // Make it so that endAngle > startAngle. fmod() above takes care of // keeping the difference below 360 degrees. if (endAngle <= startAngle) endAngle += 2.0 * piDouble; m_path->makeCompatibleContextCurrent(); const VGfloat angleDelta = anticlockwise ? (endAngle - startAngle) : (startAngle - endAngle + (2.0 * piDouble)); // OpenVG uses endpoint parameterization while this method receives its // values in center parameterization. It lacks an ellipse rotation // parameter so we use 0 for that, and also the radius is only a single // value which makes for rh == rv. In order to convert from endpoint to // center parameterization, we use the formulas from the OpenVG/SVG specs: // (x,y) = (cos rot, -sin rot; sin rot, -cos rot) * (rh * cos angle, rv * sin angle) + (center.x, center.y) // rot is 0, which simplifies this a bit: // (x,y) = (1, 0; 0, -1) * (rh * cos angle, rv * sin angle) + (center.x, center.y) // = (1 * rh * cos angle + 0 * rv * sin angle, 0 * rh * cos angle + -1 * rv * sin angle) + (center.x, center.y) // = (rh * cos angle, -rv * sin angle) + (center.x, center.y) // (Set angle = {startAngle, endAngle} to retrieve the respective endpoints.) const VGfloat startX = radius * cos(startAngle) + center.x(); const VGfloat startY = -radius * sin(startAngle) + center.y(); const VGfloat endX = radius * cos(endAngle) + center.x(); const VGfloat endY = -radius * sin(endAngle) + center.y(); if (angleDelta > 2.0 * piDouble - 0.05) { VGUErrorCode error = vguEllipse(m_path->vgPath(), center.x(), center.y(), radius * 2, radius * 2); ASSERT(error == VGU_NO_ERROR); } else { // Fa: large arc flag, makes the difference between SCWARC_TO and // LCWARC_TO, respectively SCCWARC_TO and LCCWARC_TO arcs. const bool largeArc = (angleDelta > piDouble); // Fs: sweep flag, specifying whether the arc is drawn in increasing // (true) or decreasing (0) direction. No need to calculate this // value, as it we already get it passed as a parameter // (Fs == !anticlockwise). // Translate the large arc and sweep flags into an OpenVG segment // command. As OpenVG thinks of everything upside down, we need to // reverse the anticlockwise parameter in order to get the specified // rotation. const VGubyte segmentCommand = !anticlockwise ? (largeArc ? VG_LCCWARC_TO_ABS : VG_SCCWARC_TO_ABS) : (largeArc ? VG_LCWARC_TO_ABS : VG_SCWARC_TO_ABS); // So now, we've got all the parameters in endpoint parameterization // format as OpenVG requires it. Which means we can just pass it // like this. const VGubyte pathSegments[] = { hasCurrentPoint() ? VG_LINE_TO_ABS : VG_MOVE_TO_ABS, segmentCommand }; const VGfloat pathData[] = { startX, startY, radius, radius, 0, endX, endY }; m_path->appendPathData(2, pathSegments, pathData); } m_path->m_currentPoint.setX(endX); m_path->m_currentPoint.setY(endY); }