Detect when perspective is really affine, and update the matrix as we handoff
the ctm to the device. This catches cases where the matrix bottom-row might look like [ 0, 0, not_one ] That would get categorized as perspective, but in reality that matrix behaves like affine. If we can detect that pattern, and scale the entire matrix by 1/not_one, we don't change its behavior, but it will now be categorized as affine (seen as simpler/faster). bug: skia:9698 Change-Id: Ib77b647c1d32f73538b1c0d8e9e49ec533610b3a Reviewed-on: https://skia-review.googlesource.com/c/skia/+/260776 Commit-Queue: Mike Reed <reed@google.com> Reviewed-by: Mike Klein <mtklein@google.com> Reviewed-by: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
parent
8fec4140f6
commit
c880346ba5
@ -500,20 +500,8 @@ public:
|
||||
@param persp2 perspective scale factor to store
|
||||
*/
|
||||
SkMatrix& setAll(SkScalar scaleX, SkScalar skewX, SkScalar transX,
|
||||
SkScalar skewY, SkScalar scaleY, SkScalar transY,
|
||||
SkScalar persp0, SkScalar persp1, SkScalar persp2) {
|
||||
fMat[kMScaleX] = scaleX;
|
||||
fMat[kMSkewX] = skewX;
|
||||
fMat[kMTransX] = transX;
|
||||
fMat[kMSkewY] = skewY;
|
||||
fMat[kMScaleY] = scaleY;
|
||||
fMat[kMTransY] = transY;
|
||||
fMat[kMPersp0] = persp0;
|
||||
fMat[kMPersp1] = persp1;
|
||||
fMat[kMPersp2] = persp2;
|
||||
this->setTypeMask(kUnknown_Mask);
|
||||
return *this;
|
||||
}
|
||||
SkScalar skewY, SkScalar scaleY, SkScalar transY,
|
||||
SkScalar persp0, SkScalar persp1, SkScalar persp2);
|
||||
|
||||
/** Copies nine scalar values contained by SkMatrix into buffer, in member value
|
||||
ascending order: kMScaleX, kMSkewX, kMTransX, kMSkewY, kMScaleY, kMTransY,
|
||||
@ -1235,6 +1223,19 @@ public:
|
||||
*/
|
||||
SkMatrix& setAffine(const SkScalar affine[6]);
|
||||
|
||||
/**
|
||||
* A matrix is categorized as 'perspective' if the bottom row is not [0, 0, 1].
|
||||
* However, for most uses (e.g. mapPoints) a bottom row of [0, 0, X] behaves like a
|
||||
* non-perspective matrix, though it will be categorized as perspective. Calling
|
||||
* normalizePerspective() will change the matrix such that, if its bottom row was [0, 0, X],
|
||||
* it will be changed to [0, 0, 1] by scaling the rest of the matrix by 1/X.
|
||||
*
|
||||
* | A B C | | A/X B/X C/X |
|
||||
* | D E F | -> | D/X E/X F/X | for X != 0
|
||||
* | 0 0 X | | 0 0 1 |
|
||||
*/
|
||||
void normalizePerspective();
|
||||
|
||||
/** Maps src SkPoint array of length count to dst SkPoint array of equal or greater
|
||||
length. SkPoint are mapped by multiplying each SkPoint by SkMatrix. Given:
|
||||
|
||||
|
@ -670,7 +670,7 @@ class HalfPlaneCoons : public SampleCameraView {
|
||||
bool onChar(SkUnichar uni) override {
|
||||
switch (uni) {
|
||||
case 'h': fShowHandles = !fShowHandles; return true;
|
||||
case 's': fShowSkeleton = !fShowSkeleton; return true;
|
||||
case 'k': fShowSkeleton = !fShowSkeleton; return true;
|
||||
case 't': fShowTex = !fShowTex; return true;
|
||||
default: break;
|
||||
}
|
||||
|
@ -42,11 +42,13 @@ SkBaseDevice::SkBaseDevice(const SkImageInfo& info, const SkSurfaceProps& surfac
|
||||
void SkBaseDevice::setOrigin(const SkMatrix& globalCTM, int x, int y) {
|
||||
fOrigin.set(x, y);
|
||||
fLocalToDevice = globalCTM;
|
||||
fLocalToDevice.normalizePerspective();
|
||||
fLocalToDevice.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
|
||||
}
|
||||
|
||||
void SkBaseDevice::setGlobalCTM(const SkMatrix& ctm) {
|
||||
fLocalToDevice = ctm;
|
||||
fLocalToDevice.normalizePerspective();
|
||||
if (fOrigin.fX | fOrigin.fY) {
|
||||
fLocalToDevice.postTranslate(-SkIntToScalar(fOrigin.fX), -SkIntToScalar(fOrigin.fY));
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ texture_to_matrix(const VertState& state, const SkPoint verts[], const SkPoint t
|
||||
|
||||
class SkTriColorShader : public SkShaderBase {
|
||||
public:
|
||||
SkTriColorShader(bool isOpaque) : fIsOpaque(isOpaque) {}
|
||||
SkTriColorShader(bool isOpaque, bool usePersp) : fIsOpaque(isOpaque), fUsePersp(usePersp) {}
|
||||
|
||||
// This gets called for each triangle, without re-calling onAppendStages.
|
||||
bool update(const SkMatrix& ctmInv, const SkPoint pts[], const SkPMColor4f colors[],
|
||||
@ -142,7 +142,7 @@ protected:
|
||||
#endif
|
||||
bool onAppendStages(const SkStageRec& rec) const override {
|
||||
rec.fPipeline->append(SkRasterPipeline::seed_shader);
|
||||
if (rec.fCTM.hasPerspective()) {
|
||||
if (fUsePersp) {
|
||||
rec.fPipeline->append(SkRasterPipeline::matrix_perspective, &fM33);
|
||||
}
|
||||
rec.fPipeline->append(SkRasterPipeline::matrix_4x3, &fM43);
|
||||
@ -155,12 +155,13 @@ private:
|
||||
Factory getFactory() const override { return nullptr; }
|
||||
const char* getTypeName() const override { return nullptr; }
|
||||
|
||||
// If ctm has perspective, we need both of these matrices,
|
||||
// If fUsePersp, we need both of these matrices,
|
||||
// otherwise we can combine them, and only use fM43
|
||||
|
||||
Matrix43 fM43;
|
||||
SkMatrix fM33;
|
||||
const bool fIsOpaque;
|
||||
const bool fUsePersp; // controls our stages, and what we do in update()
|
||||
|
||||
typedef SkShaderBase INHERITED;
|
||||
};
|
||||
@ -189,7 +190,7 @@ bool SkTriColorShader::update(const SkMatrix& ctmInv, const SkPoint pts[],
|
||||
(c2 - c0).store(&fM43.fMat[4]);
|
||||
c0.store(&fM43.fMat[8]);
|
||||
|
||||
if (!fM33.hasPerspective()) {
|
||||
if (!fUsePersp) {
|
||||
fM43.setConcat(fM43, fM33);
|
||||
}
|
||||
return true;
|
||||
@ -316,10 +317,20 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int vertexCount,
|
||||
vertices = deformed;
|
||||
}
|
||||
|
||||
/* We need to know if we have perspective or not, so we can know what stage(s) we will need,
|
||||
and how to prep our "uniforms" before each triangle in the tricolorshader.
|
||||
|
||||
We could just check the matrix on each triangle to decide, but we have to be sure to always
|
||||
make the same decision, since we create 1 or 2 stages only once for the entire patch.
|
||||
|
||||
To be safe, we just make that determination here, and pass it into the tricolorshader.
|
||||
*/
|
||||
const bool usePerspective = fMatrix->hasPerspective();
|
||||
|
||||
SkPoint* devVerts = nullptr;
|
||||
SkPoint3* dev3 = nullptr;
|
||||
|
||||
if (fMatrix->hasPerspective()) {
|
||||
if (usePerspective) {
|
||||
dev3 = outerAlloc.makeArray<SkPoint3>(vertexCount);
|
||||
fMatrix->mapHomogeneousPoints(dev3, vertices, vertexCount);
|
||||
} else {
|
||||
@ -379,7 +390,8 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int vertexCount,
|
||||
|
||||
if (colors) {
|
||||
dstColors = convert_colors(colors, vertexCount, fDst.colorSpace(), &outerAlloc);
|
||||
triShader = outerAlloc.make<SkTriColorShader>(compute_is_opaque(colors, vertexCount));
|
||||
triShader = outerAlloc.make<SkTriColorShader>(compute_is_opaque(colors, vertexCount),
|
||||
usePerspective);
|
||||
if (shader) {
|
||||
shader = outerAlloc.make<SkShader_Blend>(bmode,
|
||||
sk_ref_sp(triShader), sk_ref_sp(shader),
|
||||
|
@ -21,27 +21,24 @@
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
static void normalize_perspective(SkScalar mat[9]) {
|
||||
// If it was interesting to never store the last element, we could divide all 8 other
|
||||
// elements here by the 9th, making it 1.0...
|
||||
void SkMatrix::normalizePerspective() {
|
||||
// If the bottom row of the matrix is [0, 0, not_one], we will treat the matrix as if it
|
||||
// is in perspective, even though it stills behaves like its affine. If we divide everything
|
||||
// by the not_one value, then it will behave the same, but will be treated as affine,
|
||||
// and therefore faster (e.g. clients can forward-difference calculations).
|
||||
//
|
||||
// When SkScalar was SkFixed, we would sometimes rescale the entire matrix to keep its
|
||||
// component values from getting too large. This is not a concern when using floats/doubles,
|
||||
// so we do nothing now.
|
||||
|
||||
// Disable this for now, but it could be enabled.
|
||||
#if 0
|
||||
if (0 == mat[SkMatrix::kMPersp0] && 0 == mat[SkMatrix::kMPersp1]) {
|
||||
SkScalar p2 = mat[SkMatrix::kMPersp2];
|
||||
if (0 == fMat[SkMatrix::kMPersp0] && 0 == fMat[SkMatrix::kMPersp1]) {
|
||||
SkScalar p2 = fMat[SkMatrix::kMPersp2];
|
||||
if (p2 != 0 && p2 != 1) {
|
||||
double inv = 1.0 / p2;
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
mat[i] = SkDoubleToScalar(mat[i] * inv);
|
||||
fMat[i] = SkDoubleToScalar(fMat[i] * inv);
|
||||
}
|
||||
mat[SkMatrix::kMPersp2] = 1;
|
||||
fMat[SkMatrix::kMPersp2] = 1;
|
||||
}
|
||||
this->setTypeMask(kUnknown_Mask);
|
||||
(void)this->getType(); // trigger computing the new mask
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// In a few places, we performed the following
|
||||
@ -68,7 +65,22 @@ SkMatrix& SkMatrix::reset() { *this = SkMatrix(); return *this; }
|
||||
|
||||
SkMatrix& SkMatrix::set9(const SkScalar buffer[]) {
|
||||
memcpy(fMat, buffer, 9 * sizeof(SkScalar));
|
||||
normalize_perspective(fMat);
|
||||
this->setTypeMask(kUnknown_Mask);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SkMatrix& SkMatrix::setAll(SkScalar scaleX, SkScalar skewX, SkScalar transX,
|
||||
SkScalar skewY, SkScalar scaleY, SkScalar transY,
|
||||
SkScalar persp0, SkScalar persp1, SkScalar persp2) {
|
||||
fMat[kMScaleX] = scaleX;
|
||||
fMat[kMSkewX] = skewX;
|
||||
fMat[kMTransX] = transX;
|
||||
fMat[kMSkewY] = skewY;
|
||||
fMat[kMScaleY] = scaleY;
|
||||
fMat[kMTransY] = transY;
|
||||
fMat[kMPersp0] = persp0;
|
||||
fMat[kMPersp1] = persp1;
|
||||
fMat[kMPersp2] = persp2;
|
||||
this->setTypeMask(kUnknown_Mask);
|
||||
return *this;
|
||||
}
|
||||
@ -641,7 +653,6 @@ SkMatrix& SkMatrix::setConcat(const SkMatrix& a, const SkMatrix& b) {
|
||||
tmp.fMat[kMPersp1] = rowcol3(&a.fMat[6], &b.fMat[1]);
|
||||
tmp.fMat[kMPersp2] = rowcol3(&a.fMat[6], &b.fMat[2]);
|
||||
|
||||
normalize_perspective(tmp.fMat);
|
||||
tmp.setTypeMask(kUnknown_Mask);
|
||||
} else {
|
||||
tmp.fMat[kMScaleX] = muladdmul(a.fMat[kMScaleX],
|
||||
|
Loading…
Reference in New Issue
Block a user