Add a GrCCPRGeometry file

Enough ccpr-specific geometry code is in flight that it feels like it
should have its own file.

Bug: skia:
Change-Id: I99ef620a7dc35178cf774b3a4ec6159d46f401c7
Reviewed-on: https://skia-review.googlesource.com/39162
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Chris Dalton 2017-08-28 10:24:22 -06:00 committed by Skia Commit-Bot
parent 7dda8dfb9d
commit 419a94da02
7 changed files with 132 additions and 77 deletions

View File

@ -295,6 +295,8 @@ skia_gpu_sources = [
"$_src/gpu/ccpr/GrCCPRCoverageProcessor.h",
"$_src/gpu/ccpr/GrCCPRCubicProcessor.cpp",
"$_src/gpu/ccpr/GrCCPRCubicProcessor.h",
"$_src/gpu/ccpr/GrCCPRGeometry.cpp",
"$_src/gpu/ccpr/GrCCPRGeometry.h",
"$_src/gpu/ccpr/GrCCPRPathProcessor.cpp",
"$_src/gpu/ccpr/GrCCPRPathProcessor.h",
"$_src/gpu/ccpr/GrCCPRQuadraticProcessor.cpp",

View File

@ -10,7 +10,6 @@
#if SK_SUPPORT_GPU
#include "GrContextPriv.h"
#include "GrPathUtils.h"
#include "GrRenderTargetContext.h"
#include "GrRenderTargetContextPriv.h"
#include "GrResourceProvider.h"
@ -22,6 +21,7 @@
#include "SkPath.h"
#include "SkView.h"
#include "ccpr/GrCCPRCoverageProcessor.h"
#include "ccpr/GrCCPRGeometry.h"
#include "gl/GrGLGpu.cpp"
#include "ops/GrDrawOp.h"
@ -202,10 +202,9 @@ void CCPRGeometryView::updateGpuData() {
fGpuInstances.push_back().fCubicData = {controlPointsIdx + i * 4, i};
}
} else if (is_curve(fMode)) {
SkPoint P[3] = {fPoints[0], fPoints[1], fPoints[3]};
SkPoint chopped[5];
fGpuPoints.push_back(P[0]);
if (GrPathUtils::chopMonotonicQuads(P, chopped)) {
fGpuPoints.push_back(fPoints[0]);
if (GrCCPRChopMonotonicQuadratics(fPoints[0], fPoints[1], fPoints[3], chopped)) {
// Endpoints.
fGpuPoints.push_back(chopped[2]);
fGpuPoints.push_back(chopped[4]);
@ -215,8 +214,8 @@ void CCPRGeometryView::updateGpuData() {
fGpuInstances.push_back().fQuadraticData = {3, 0};
fGpuInstances.push_back().fQuadraticData = {4, 1};
} else {
fGpuPoints.push_back(P[2]);
fGpuPoints.push_back(P[1]);
fGpuPoints.push_back(fPoints[3]);
fGpuPoints.push_back(fPoints[1]);
fGpuInstances.push_back().fQuadraticData = {2, 0};
}
} else {

View File

@ -567,66 +567,6 @@ void GrPathUtils::convertCubicToQuadsConstrainToTangents(const SkPoint p[4],
}
}
static inline Sk2f normalize(const Sk2f& n) {
Sk2f nn = n*n;
return n * (nn + SkNx_shuffle<1,0>(nn)).rsqrt();
}
bool GrPathUtils::chopMonotonicQuads(const SkPoint p[3], SkPoint dst[5]) {
GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT);
GR_STATIC_ASSERT(2 * sizeof(float) == sizeof(SkPoint));
GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX));
Sk2f p0 = Sk2f::Load(&p[0]);
Sk2f p1 = Sk2f::Load(&p[1]);
Sk2f p2 = Sk2f::Load(&p[2]);
Sk2f tan0 = p1 - p0;
Sk2f tan1 = p2 - p1;
Sk2f v = p2 - p0;
// Check if the curve is already monotonic (i.e. (tan0 dot v) >= 0 and (tan1 dot v) >= 0).
// This should almost always be this case for well-behaved curves in the real world.
float dot0[2], dot1[2];
(tan0 * v).store(dot0);
(tan1 * v).store(dot1);
if (dot0[0] + dot0[1] >= 0 && dot1[0] + dot1[1] >= 0) {
return false;
}
// Chop the curve into two segments with equal curvature. To do this we find the T value whose
// tangent is perpendicular to the vector that bisects tan0 and -tan1.
Sk2f n = normalize(tan0) - normalize(tan1);
// This tangent can be found where (dQ(t) dot n) = 0:
//
// 0 = (dQ(t) dot n) = | 2*t 1 | * | p0 - 2*p1 + p2 | * | n |
// | -2*p0 + 2*p1 | | . |
//
// = | 2*t 1 | * | tan1 - tan0 | * | n |
// | 2*tan0 | | . |
//
// = 2*t * ((tan1 - tan0) dot n) + (2*tan0 dot n)
//
// t = (tan0 dot n) / ((tan0 - tan1) dot n)
Sk2f dQ1n = (tan0 - tan1) * n;
Sk2f dQ0n = tan0 * n;
Sk2f t = (dQ0n + SkNx_shuffle<1,0>(dQ0n)) / (dQ1n + SkNx_shuffle<1,0>(dQ1n));
t = Sk2f::Min(Sk2f::Max(t, 0), 1); // Clamp for FP error.
Sk2f p01 = SkNx_fma(t, tan0, p0);
Sk2f p12 = SkNx_fma(t, tan1, p1);
Sk2f p012 = SkNx_fma(t, p12 - p01, p01);
p0.store(&dst[0]);
p01.store(&dst[1]);
p012.store(&dst[2]);
p12.store(&dst[3]);
p2.store(&dst[4]);
return true;
}
////////////////////////////////////////////////////////////////////////////////
/**

View File

@ -124,14 +124,6 @@ namespace GrPathUtils {
SkPathPriv::FirstDirection dir,
SkTArray<SkPoint, true>* quads);
// Ensures that a quadratic bezier is monotonic with respect to its vector [P2 - P0] (the vector
// between its endpoints). In the event that the curve is not monotonic, it is chopped into two
// segments that are monotonic. This should be rare for well-behaved curves in the real world.
//
// Returns false if the curve was already monotonic.
// true if it was chopped into two monotonic segments, now contained in dst.
bool chopMonotonicQuads(const SkPoint p[3], SkPoint dst[5]);
// Computes the KLM linear functionals for the cubic implicit form. The "right" side of the
// curve (when facing in the direction of increasing parameter values) will be the area that
// satisfies:

View File

@ -11,7 +11,6 @@
#include "GrGpuCommandBuffer.h"
#include "GrOnFlushResourceProvider.h"
#include "GrOpFlushState.h"
#include "GrPathUtils.h"
#include "SkGeometry.h"
#include "SkMakeUnique.h"
#include "SkMathPriv.h"
@ -19,6 +18,7 @@
#include "SkPathPriv.h"
#include "SkPoint.h"
#include "SkNx.h"
#include "ccpr/GrCCPRGeometry.h"
#include "ops/GrDrawOp.h"
#include "../pathops/SkPathOpsCubic.h"
#include <numeric>
@ -311,9 +311,8 @@ void GrCCPRCoverageOpsBuilder::fanTo(const SkPoint& pt) {
void GrCCPRCoverageOpsBuilder::quadraticTo(SkPoint controlPt, SkPoint endPt) {
SkASSERT(fCurrPathIndices.fQuadratics+2 <= fBaseInstances[(int)fCurrScissorMode].fSerpentines);
SkPoint P[3] = {fCurrFanPoint, controlPt, endPt};
SkPoint chopped[5];
if (GrPathUtils::chopMonotonicQuads(P, chopped)) {
if (GrCCPRChopMonotonicQuadratics(fCurrFanPoint, controlPt, endPt, chopped)) {
this->fanTo(chopped[2]);
fPointsData[fControlPtsIdx++] = chopped[1];
fInstanceData[fCurrPathIndices.fQuadratics++].fQuadraticData = {

View File

@ -0,0 +1,94 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrCCPRGeometry.h"
#include "GrTypes.h"
#include "SkPoint.h"
#include "SkNx.h"
#include <algorithm>
#include <cmath>
#include <cstdlib>
// We convert between SkPoint and Sk2f freely throughout this file.
GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT);
GR_STATIC_ASSERT(2 * sizeof(float) == sizeof(SkPoint));
GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX));
static inline Sk2f normalize(const Sk2f& n) {
Sk2f nn = n*n;
return n * (nn + SkNx_shuffle<1,0>(nn)).rsqrt();
}
static inline float dot(const Sk2f& a, const Sk2f& b) {
float product[2];
(a * b).store(product);
return product[0] + product[1];
}
// Returns whether the (convex) curve segment is monotonic with respect to [endPt - startPt].
static inline bool is_convex_curve_monotonic(const Sk2f& startPt, const Sk2f& startTan,
const Sk2f& endPt, const Sk2f& endTan) {
Sk2f v = endPt - startPt;
float dot0 = dot(startTan, v);
float dot1 = dot(endTan, v);
// A small, negative tolerance handles floating-point error in the case when one tangent
// approaches 0 length, meaning the (convex) curve segment is effectively a flat line.
float tolerance = -std::max(std::abs(dot0), std::abs(dot1)) * SK_ScalarNearlyZero;
return dot0 >= tolerance && dot1 >= tolerance;
}
static inline Sk2f lerp(const Sk2f& a, const Sk2f& b, const Sk2f& t) {
return SkNx_fma(t, b - a, a);
}
bool GrCCPRChopMonotonicQuadratics(const SkPoint& startPt, const SkPoint& controlPt,
const SkPoint& endPt, SkPoint dst[5]) {
Sk2f p0 = Sk2f::Load(&startPt);
Sk2f p1 = Sk2f::Load(&controlPt);
Sk2f p2 = Sk2f::Load(&endPt);
Sk2f tan0 = p1 - p0;
Sk2f tan1 = p2 - p1;
// This should almost always be this case for well-behaved curves in the real world.
if (is_convex_curve_monotonic(p0, tan0, p2, tan1)) {
return false;
}
// Chop the curve into two segments with equal curvature. To do this we find the T value whose
// tangent is perpendicular to the vector that bisects tan0 and -tan1.
Sk2f n = normalize(tan0) - normalize(tan1);
// This tangent can be found where (dQ(t) dot n) = 0:
//
// 0 = (dQ(t) dot n) = | 2*t 1 | * | p0 - 2*p1 + p2 | * | n |
// | -2*p0 + 2*p1 | | . |
//
// = | 2*t 1 | * | tan1 - tan0 | * | n |
// | 2*tan0 | | . |
//
// = 2*t * ((tan1 - tan0) dot n) + (2*tan0 dot n)
//
// t = (tan0 dot n) / ((tan0 - tan1) dot n)
Sk2f dQ1n = (tan0 - tan1) * n;
Sk2f dQ0n = tan0 * n;
Sk2f t = (dQ0n + SkNx_shuffle<1,0>(dQ0n)) / (dQ1n + SkNx_shuffle<1,0>(dQ1n));
t = Sk2f::Min(Sk2f::Max(t, 0), 1); // Clamp for FP error.
Sk2f p01 = SkNx_fma(t, tan0, p0);
Sk2f p12 = SkNx_fma(t, tan1, p1);
Sk2f p012 = lerp(p01, p12, t);
p0.store(&dst[0]);
p01.store(&dst[1]);
p012.store(&dst[2]);
p12.store(&dst[3]);
p2.store(&dst[4]);
return true;
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrGrCCPRGeometry_DEFINED
#define GrGrCCPRGeometry_DEFINED
#include "SkTypes.h"
struct SkPoint;
/*
* Ensures that a quadratic bezier is monotonic with respect to the vector between its endpoints
* [P2 - P0]. In the event that the curve is not monotonic, it is chopped into two segments that
* are. This should be rare for well-behaved curves in the real world.
*
* NOTE: This must be done in device space, since an affine transformation can change whether a
* curve is monotonic.
*
* Returns false if the curve was already monotonic.
* true if it was chopped into two monotonic segments, now contained in dst.
*/
bool GrCCPRChopMonotonicQuadratics(const SkPoint& startPt, const SkPoint& controlPt,
const SkPoint& endPt, SkPoint dst[5]);
#endif