• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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