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:
parent
7dda8dfb9d
commit
419a94da02
@ -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",
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
|
@ -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:
|
||||
|
@ -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 = {
|
||||
|
94
src/gpu/ccpr/GrCCPRGeometry.cpp
Normal file
94
src/gpu/ccpr/GrCCPRGeometry.cpp
Normal 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;
|
||||
}
|
29
src/gpu/ccpr/GrCCPRGeometry.h
Normal file
29
src/gpu/ccpr/GrCCPRGeometry.h
Normal 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
|
Loading…
Reference in New Issue
Block a user