1 /*
2 * Copyright 2019 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/core/SkMatrix.h"
9 #include "src/core/SkImageFilterTypes.h"
10 #include "src/core/SkImageFilter_Base.h"
11 #include "src/core/SkMatrixPriv.h"
12
13 // Both [I]Vectors and Sk[I]Sizes are transformed as non-positioned values, i.e. go through
14 // mapVectors() not mapPoints().
map_as_vector(int32_t x,int32_t y,const SkMatrix & matrix)15 static SkIVector map_as_vector(int32_t x, int32_t y, const SkMatrix& matrix) {
16 SkVector v = SkVector::Make(SkIntToScalar(x), SkIntToScalar(y));
17 matrix.mapVectors(&v, 1);
18 return SkIVector::Make(SkScalarRoundToInt(v.fX), SkScalarRoundToInt(v.fY));
19 }
20
map_as_vector(SkScalar x,SkScalar y,const SkMatrix & matrix)21 static SkVector map_as_vector(SkScalar x, SkScalar y, const SkMatrix& matrix) {
22 SkVector v = SkVector::Make(x, y);
23 matrix.mapVectors(&v, 1);
24 return v;
25 }
26
27 namespace skif {
28
decomposeCTM(const SkMatrix & ctm,const SkImageFilter * filter,const skif::ParameterSpace<SkPoint> & representativePt)29 bool Mapping::decomposeCTM(const SkMatrix& ctm, const SkImageFilter* filter,
30 const skif::ParameterSpace<SkPoint>& representativePt) {
31 SkMatrix remainder, layer;
32 SkSize decomposed;
33 using MatrixCapability = SkImageFilter_Base::MatrixCapability;
34 MatrixCapability capability =
35 filter ? as_IFB(filter)->getCTMCapability() : MatrixCapability::kComplex;
36 if (capability == MatrixCapability::kTranslate) {
37 // Apply the entire CTM post-filtering
38 remainder = ctm;
39 layer = SkMatrix::I();
40 } else if (ctm.isScaleTranslate() || capability == MatrixCapability::kComplex) {
41 // Either layer space can be anything (kComplex) - or - it can be scale+translate, and the
42 // ctm is. In both cases, the layer space can be equivalent to device space.
43 remainder = SkMatrix::I();
44 layer = ctm;
45 } else if (ctm.decomposeScale(&decomposed, &remainder)) {
46 // This case implies some amount of sampling post-filtering, either due to skew or rotation
47 // in the original matrix. As such, keep the layer matrix as simple as possible.
48 layer = SkMatrix::Scale(decomposed.fWidth, decomposed.fHeight);
49 } else {
50 // Perspective, which has a non-uniform scaling effect on the filter. Pick a single scale
51 // factor that best matches where the filter will be evaluated.
52 SkScalar scale = SkMatrixPriv::DifferentialAreaScale(ctm, SkPoint(representativePt));
53 if (SkScalarIsFinite(scale) && !SkScalarNearlyZero(scale)) {
54 // Now take the sqrt to go from an area scale factor to a scaling per X and Y
55 // FIXME: It would be nice to be able to choose a non-uniform scale.
56 scale = SkScalarSqrt(scale);
57 } else {
58 // The representative point was behind the W = 0 plane, so don't factor out any scale.
59 // NOTE: This makes remainder and layer the same as the MatrixCapability::Translate case
60 scale = 1.f;
61 }
62
63 remainder = ctm;
64 remainder.preScale(SkScalarInvert(scale), SkScalarInvert(scale));
65 layer = SkMatrix::Scale(scale, scale);
66 }
67
68 SkMatrix invRemainder;
69 if (!remainder.invert(&invRemainder)) {
70 // Under floating point arithmetic, it's possible to decompose an invertible matrix into
71 // a scaling matrix and a remainder and have the remainder be non-invertible. Generally
72 // when this happens the scale factors are so large and the matrix so ill-conditioned that
73 // it's unlikely that any drawing would be reasonable, so failing to make a layer is okay.
74 return false;
75 } else {
76 fParamToLayerMatrix = layer;
77 fLayerToDevMatrix = remainder;
78 fDevToLayerMatrix = invRemainder;
79 return true;
80 }
81 }
82
adjustLayerSpace(const SkMatrix & layer)83 bool Mapping::adjustLayerSpace(const SkMatrix& layer) {
84 SkMatrix invLayer;
85 if (!layer.invert(&invLayer)) {
86 return false;
87 }
88 fParamToLayerMatrix.postConcat(layer);
89 fDevToLayerMatrix.postConcat(layer);
90 fLayerToDevMatrix.preConcat(invLayer);
91 return true;
92 }
93
94 // Instantiate map specializations for the 6 geometric types used during filtering
95 template<>
map(const SkRect & geom,const SkMatrix & matrix)96 SkRect Mapping::map<SkRect>(const SkRect& geom, const SkMatrix& matrix) {
97 return matrix.mapRect(geom);
98 }
99
100 template<>
map(const SkIRect & geom,const SkMatrix & matrix)101 SkIRect Mapping::map<SkIRect>(const SkIRect& geom, const SkMatrix& matrix) {
102 SkRect mapped = matrix.mapRect(SkRect::Make(geom));
103 mapped.inset(1e-3f, 1e-3f);
104 return mapped.roundOut();
105 }
106
107 template<>
map(const SkIPoint & geom,const SkMatrix & matrix)108 SkIPoint Mapping::map<SkIPoint>(const SkIPoint& geom, const SkMatrix& matrix) {
109 SkPoint p = SkPoint::Make(SkIntToScalar(geom.fX), SkIntToScalar(geom.fY));
110 matrix.mapPoints(&p, 1);
111 return SkIPoint::Make(SkScalarRoundToInt(p.fX), SkScalarRoundToInt(p.fY));
112 }
113
114 template<>
map(const SkPoint & geom,const SkMatrix & matrix)115 SkPoint Mapping::map<SkPoint>(const SkPoint& geom, const SkMatrix& matrix) {
116 SkPoint p;
117 matrix.mapPoints(&p, &geom, 1);
118 return p;
119 }
120
121 template<>
map(const IVector & geom,const SkMatrix & matrix)122 IVector Mapping::map<IVector>(const IVector& geom, const SkMatrix& matrix) {
123 return IVector(map_as_vector(geom.fX, geom.fY, matrix));
124 }
125
126 template<>
map(const Vector & geom,const SkMatrix & matrix)127 Vector Mapping::map<Vector>(const Vector& geom, const SkMatrix& matrix) {
128 return Vector(map_as_vector(geom.fX, geom.fY, matrix));
129 }
130
131 template<>
map(const SkISize & geom,const SkMatrix & matrix)132 SkISize Mapping::map<SkISize>(const SkISize& geom, const SkMatrix& matrix) {
133 SkIVector v = map_as_vector(geom.fWidth, geom.fHeight, matrix);
134 return SkISize::Make(v.fX, v.fY);
135 }
136
137 template<>
map(const SkSize & geom,const SkMatrix & matrix)138 SkSize Mapping::map<SkSize>(const SkSize& geom, const SkMatrix& matrix) {
139 SkVector v = map_as_vector(geom.fWidth, geom.fHeight, matrix);
140 return SkSize::Make(v.fX, v.fY);
141 }
142
resolveToBounds(const LayerSpace<SkIRect> & newBounds) const143 FilterResult FilterResult::resolveToBounds(const LayerSpace<SkIRect>& newBounds) const {
144 // NOTE(michaelludwig) - This implementation is based on the assumption that an image resolved
145 // to 'newBounds' will be decal tiled and that the current image is decal tiled. Because of this
146 // simplification, the resolved image is always a subset of 'fImage' that matches the
147 // intersection of 'newBounds' and 'layerBounds()' so no rendering/copying is needed.
148 LayerSpace<SkIRect> tightBounds = newBounds;
149 if (!fImage || !tightBounds.intersect(this->layerBounds())) {
150 return {}; // Fully transparent
151 }
152
153 // Calculate offset from old origin to new origin, representing the relative subset in the image
154 LayerSpace<IVector> originShift = tightBounds.topLeft() - fOrigin;
155
156 auto subsetImage = fImage->makeSubset(SkIRect::MakeXYWH(originShift.x(), originShift.y(),
157 tightBounds.width(), tightBounds.height()));
158 return {std::move(subsetImage), tightBounds.topLeft()};
159 }
160
161 } // end namespace skif
162