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 #ifndef SkImageFilterTypes_DEFINED 9 #define SkImageFilterTypes_DEFINED 10 11 #include "include/core/SkMatrix.h" 12 #include "src/core/SkSpecialImage.h" 13 #include "src/core/SkSpecialSurface.h" 14 15 class GrRecordingContext; 16 class SkImageFilter; 17 class SkImageFilterCache; 18 class SkSpecialSurface; 19 class SkSurfaceProps; 20 21 // The skif (SKI[mage]F[ilter]) namespace contains types that are used for filter implementations. 22 // The defined types come in two groups: users of internal Skia types, and templates to help with 23 // readability. Image filters cannot be implemented without access to key internal types, such as 24 // SkSpecialImage. It is possible to avoid the use of the readability templates, although they are 25 // strongly encouraged. 26 namespace skif { 27 28 // skif::IVector and skif::Vector represent plain-old-data types for storing direction vectors, so 29 // that the coordinate-space templating system defined below can have a separate type id for 30 // directions vs. points, and specialize appropriately. As such, all operations with direction 31 // vectors are defined on the LayerSpace specialization, since that is the intended point of use. 32 struct IVector { 33 int32_t fX; 34 int32_t fY; 35 36 IVector() = default; IVectorIVector37 IVector(int32_t x, int32_t y) : fX(x), fY(y) {} IVectorIVector38 explicit IVector(const SkIVector& v) : fX(v.fX), fY(v.fY) {} 39 }; 40 41 struct Vector { 42 SkScalar fX; 43 SkScalar fY; 44 45 Vector() = default; VectorVector46 Vector(SkScalar x, SkScalar y) : fX(x), fY(y) {} VectorVector47 explicit Vector(const SkVector& v) : fX(v.fX), fY(v.fY) {} 48 }; 49 50 /////////////////////////////////////////////////////////////////////////////////////////////////// 51 // Coordinate Space Tagging 52 // - In order to enforce correct coordinate spaces in image filter implementations and use, 53 // geometry is wrapped by templated structs to declare in the type system what coordinate space 54 // the coordinates are defined in. 55 // - Currently there is ParameterSpace and DeviceSpace that are data-only wrappers around 56 // coordinates, and the primary LayerSpace that provides all operative functionality for image 57 // filters. It is intended that all logic about image bounds and access be conducted in the shared 58 // layer space. 59 // - The LayerSpace struct has type-safe specializations for SkIRect, SkRect, SkIPoint, SkPoint, 60 // skif::IVector (to distinguish SkIVector from SkIPoint), skif::Vector, SkISize, and SkSize. 61 // - A Mapping object provides type safe coordinate conversions between these spaces, and 62 // automatically does the "right thing" for each geometric type. 63 /////////////////////////////////////////////////////////////////////////////////////////////////// 64 65 // ParameterSpace is a data-only wrapper around Skia's geometric types such as SkIPoint, and SkRect. 66 // Parameter space is the same as the local coordinate space of an SkShader, or the coordinates 67 // passed into SkCanvas::drawX calls, but "local" is avoided due to the alliteration with layer 68 // space. SkImageFilters are defined in terms of ParameterSpace<T> geometry and must use the Mapping 69 // on Context to transform the parameters into LayerSpace to evaluate the filter in the shared 70 // coordinate space of the entire filter DAG. 71 // 72 // A value of ParameterSpace<SkIRect> implies that its wrapped SkIRect is defined in the local 73 // parameter space. 74 template<typename T> 75 class ParameterSpace { 76 public: 77 ParameterSpace() = default; ParameterSpace(const T & data)78 explicit ParameterSpace(const T& data) : fData(data) {} ParameterSpace(T && data)79 explicit ParameterSpace(T&& data) : fData(std::move(data)) {} 80 81 explicit operator const T&() const { return fData; } 82 Optional(const T * ptr)83 static const ParameterSpace<T>* Optional(const T* ptr) { 84 return static_cast<const ParameterSpace<T>*>(reinterpret_cast<const void*>(ptr)); 85 } 86 private: 87 T fData; 88 }; 89 90 // DeviceSpace is a data-only wrapper around Skia's geometric types. It is similar to 91 // 'ParameterSpace' except that it is used to represent geometry that has been transformed or 92 // defined in the root device space (i.e. the final pixels of drawn content). Much of what SkCanvas 93 // tracks, such as its clip bounds are defined in this space and DeviceSpace provides a 94 // type-enforced mechanism for the canvas to pass that information into the image filtering system, 95 // using the Mapping of the filtering context. 96 template<typename T> 97 class DeviceSpace { 98 public: 99 DeviceSpace() = default; DeviceSpace(const T & data)100 explicit DeviceSpace(const T& data) : fData(data) {} DeviceSpace(T && data)101 explicit DeviceSpace(T&& data) : fData(std::move(data)) {} 102 103 explicit operator const T&() const { return fData; } 104 105 private: 106 T fData; 107 }; 108 109 // LayerSpace is a geometric wrapper that specifies the geometry is defined in the shared layer 110 // space where image filters are evaluated. For a given Context (and its Mapping), the image filter 111 // DAG operates in the same coordinate space. This space may be different from the local coordinate 112 // space that defined the image filter parameters (such as blur sigma), and it may be different 113 // from the total CTM of the SkCanvas. 114 // 115 // To encourage correct filter use and implementation, the bulk of filter logic should be performed 116 // in layer space (e.g. determining what portion of an input image to read, or what the output 117 // region is). LayerSpace specializations for the six common Skia math types (Sk[I]Rect, Sk[I]Point, 118 // and Sk[I]Size), and skif::[I]Vector (to allow vectors to be specialized separately from points)) 119 // are provided that mimic their APIs but preserve the coordinate space and enforce type semantics. 120 template<typename T> 121 class LayerSpace {}; 122 123 // Layer-space specialization for integerized direction vectors. 124 template<> 125 class LayerSpace<IVector> { 126 public: 127 LayerSpace() = default; LayerSpace(const IVector & geometry)128 explicit LayerSpace(const IVector& geometry) : fData(geometry) {} LayerSpace(IVector && geometry)129 explicit LayerSpace(IVector&& geometry) : fData(std::move(geometry)) {} 130 explicit operator const IVector&() const { return fData; } 131 SkIVector()132 explicit operator SkIVector() const { return SkIVector::Make(fData.fX, fData.fY); } 133 x()134 int32_t x() const { return fData.fX; } y()135 int32_t y() const { return fData.fY; } 136 137 LayerSpace<IVector> operator-() const { return LayerSpace<IVector>({-fData.fX, -fData.fY}); } 138 139 LayerSpace<IVector> operator+(const LayerSpace<IVector>& v) const { 140 LayerSpace<IVector> sum = *this; 141 sum += v; 142 return sum; 143 } 144 LayerSpace<IVector> operator-(const LayerSpace<IVector>& v) const { 145 LayerSpace<IVector> diff = *this; 146 diff -= v; 147 return diff; 148 } 149 150 void operator+=(const LayerSpace<IVector>& v) { 151 fData.fX += v.fData.fX; 152 fData.fY += v.fData.fY; 153 } 154 void operator-=(const LayerSpace<IVector>& v) { 155 fData.fX -= v.fData.fX; 156 fData.fY -= v.fData.fY; 157 } 158 159 private: 160 IVector fData; 161 }; 162 163 // Layer-space specialization for floating point direction vectors. 164 template<> 165 class LayerSpace<Vector> { 166 public: 167 LayerSpace() = default; LayerSpace(const Vector & geometry)168 explicit LayerSpace(const Vector& geometry) : fData(geometry) {} LayerSpace(Vector && geometry)169 explicit LayerSpace(Vector&& geometry) : fData(std::move(geometry)) {} 170 explicit operator const Vector&() const { return fData; } 171 SkVector()172 explicit operator SkVector() const { return SkVector::Make(fData.fX, fData.fY); } 173 x()174 SkScalar x() const { return fData.fX; } y()175 SkScalar y() const { return fData.fY; } 176 length()177 SkScalar length() const { return SkVector::Length(fData.fX, fData.fY); } 178 179 LayerSpace<Vector> operator-() const { return LayerSpace<Vector>({-fData.fX, -fData.fY}); } 180 181 LayerSpace<Vector> operator*(SkScalar s) const { 182 LayerSpace<Vector> scaled = *this; 183 scaled *= s; 184 return scaled; 185 } 186 187 LayerSpace<Vector> operator+(const LayerSpace<Vector>& v) const { 188 LayerSpace<Vector> sum = *this; 189 sum += v; 190 return sum; 191 } 192 LayerSpace<Vector> operator-(const LayerSpace<Vector>& v) const { 193 LayerSpace<Vector> diff = *this; 194 diff -= v; 195 return diff; 196 } 197 198 void operator*=(SkScalar s) { 199 fData.fX *= s; 200 fData.fY *= s; 201 } 202 void operator+=(const LayerSpace<Vector>& v) { 203 fData.fX += v.fData.fX; 204 fData.fY += v.fData.fY; 205 } 206 void operator-=(const LayerSpace<Vector>& v) { 207 fData.fX -= v.fData.fX; 208 fData.fY -= v.fData.fY; 209 } 210 211 friend LayerSpace<Vector> operator*(SkScalar s, const LayerSpace<Vector>& b) { 212 return b * s; 213 } 214 215 private: 216 Vector fData; 217 }; 218 219 // Layer-space specialization for integer 2D coordinates (treated as positions, not directions). 220 template<> 221 class LayerSpace<SkIPoint> { 222 public: 223 LayerSpace() = default; LayerSpace(const SkIPoint & geometry)224 explicit LayerSpace(const SkIPoint& geometry) : fData(geometry) {} LayerSpace(SkIPoint && geometry)225 explicit LayerSpace(SkIPoint&& geometry) : fData(std::move(geometry)) {} 226 explicit operator const SkIPoint&() const { return fData; } 227 228 // Parrot the SkIPoint API while preserving coordinate space. x()229 int32_t x() const { return fData.fX; } y()230 int32_t y() const { return fData.fY; } 231 232 // Offsetting by direction vectors produce more points 233 LayerSpace<SkIPoint> operator+(const LayerSpace<IVector>& v) { 234 return LayerSpace<SkIPoint>(fData + SkIVector(v)); 235 } 236 LayerSpace<SkIPoint> operator-(const LayerSpace<IVector>& v) { 237 return LayerSpace<SkIPoint>(fData - SkIVector(v)); 238 } 239 240 void operator+=(const LayerSpace<IVector>& v) { 241 fData += SkIVector(v); 242 } 243 void operator-=(const LayerSpace<IVector>& v) { 244 fData -= SkIVector(v); 245 } 246 247 // Subtracting another point makes a direction between them 248 LayerSpace<IVector> operator-(const LayerSpace<SkIPoint>& p) { 249 return LayerSpace<IVector>(IVector(fData - p.fData)); 250 } 251 252 LayerSpace<IVector> operator-() const { return LayerSpace<IVector>({-fData.fX, -fData.fY}); } 253 254 private: 255 SkIPoint fData; 256 }; 257 258 // Layer-space specialization for floating point 2D coordinates (treated as positions) 259 template<> 260 class LayerSpace<SkPoint> { 261 public: 262 LayerSpace() = default; LayerSpace(const SkPoint & geometry)263 explicit LayerSpace(const SkPoint& geometry) : fData(geometry) {} LayerSpace(SkPoint && geometry)264 explicit LayerSpace(SkPoint&& geometry) : fData(std::move(geometry)) {} 265 explicit operator const SkPoint&() const { return fData; } 266 267 // Parrot the SkPoint API while preserving coordinate space. x()268 SkScalar x() const { return fData.fX; } y()269 SkScalar y() const { return fData.fY; } 270 distanceToOrigin()271 SkScalar distanceToOrigin() const { return fData.distanceToOrigin(); } 272 273 // Offsetting by direction vectors produce more points 274 LayerSpace<SkPoint> operator+(const LayerSpace<Vector>& v) { 275 return LayerSpace<SkPoint>(fData + SkVector(v)); 276 } 277 LayerSpace<SkPoint> operator-(const LayerSpace<Vector>& v) { 278 return LayerSpace<SkPoint>(fData - SkVector(v)); 279 } 280 281 void operator+=(const LayerSpace<Vector>& v) { 282 fData += SkVector(v); 283 } 284 void operator-=(const LayerSpace<Vector>& v) { 285 fData -= SkVector(v); 286 } 287 288 // Subtracting another point makes a direction between them 289 LayerSpace<Vector> operator-(const LayerSpace<SkPoint>& p) { 290 return LayerSpace<Vector>(Vector(fData - p.fData)); 291 } 292 293 LayerSpace<Vector> operator-() const { return LayerSpace<Vector>({-fData.fX, -fData.fY}); } 294 295 private: 296 SkPoint fData; 297 }; 298 299 // Layer-space specialization for integer dimensions 300 template<> 301 class LayerSpace<SkISize> { 302 public: 303 LayerSpace() = default; LayerSpace(const SkISize & geometry)304 explicit LayerSpace(const SkISize& geometry) : fData(geometry) {} LayerSpace(SkISize && geometry)305 explicit LayerSpace(SkISize&& geometry) : fData(std::move(geometry)) {} 306 explicit operator const SkISize&() const { return fData; } 307 width()308 int32_t width() const { return fData.width(); } height()309 int32_t height() const { return fData.height(); } 310 isEmpty()311 bool isEmpty() const { return fData.isEmpty(); } 312 313 private: 314 SkISize fData; 315 }; 316 317 // Layer-space specialization for floating point dimensions 318 template<> 319 class LayerSpace<SkSize> { 320 public: 321 LayerSpace() = default; LayerSpace(const SkSize & geometry)322 explicit LayerSpace(const SkSize& geometry) : fData(geometry) {} LayerSpace(SkSize && geometry)323 explicit LayerSpace(SkSize&& geometry) : fData(std::move(geometry)) {} 324 explicit operator const SkSize&() const { return fData; } 325 width()326 SkScalar width() const { return fData.width(); } height()327 SkScalar height() const { return fData.height(); } 328 isEmpty()329 bool isEmpty() const { return fData.isEmpty(); } isZero()330 bool isZero() const { return fData.isZero(); } 331 round()332 LayerSpace<SkISize> round() const { return LayerSpace<SkISize>(fData.toRound()); } ceil()333 LayerSpace<SkISize> ceil() const { return LayerSpace<SkISize>(fData.toCeil()); } floor()334 LayerSpace<SkISize> floor() const { return LayerSpace<SkISize>(fData.toFloor()); } 335 336 private: 337 SkSize fData; 338 }; 339 340 // Layer-space specialization for axis-aligned integer bounding boxes. 341 template<> 342 class LayerSpace<SkIRect> { 343 public: 344 LayerSpace() = default; LayerSpace(const SkIRect & geometry)345 explicit LayerSpace(const SkIRect& geometry) : fData(geometry) {} LayerSpace(SkIRect && geometry)346 explicit LayerSpace(SkIRect&& geometry) : fData(std::move(geometry)) {} 347 explicit operator const SkIRect&() const { return fData; } 348 Empty()349 static LayerSpace<SkIRect> Empty() { return LayerSpace<SkIRect>(SkIRect::MakeEmpty()); } 350 351 // Parrot the SkIRect API while preserving coord space isEmpty()352 bool isEmpty() const { return fData.isEmpty(); } 353 left()354 int32_t left() const { return fData.fLeft; } top()355 int32_t top() const { return fData.fTop; } right()356 int32_t right() const { return fData.fRight; } bottom()357 int32_t bottom() const { return fData.fBottom; } 358 width()359 int32_t width() const { return fData.width(); } height()360 int32_t height() const { return fData.height(); } 361 topLeft()362 LayerSpace<SkIPoint> topLeft() const { return LayerSpace<SkIPoint>(fData.topLeft()); } size()363 LayerSpace<SkISize> size() const { return LayerSpace<SkISize>(fData.size()); } 364 intersect(const LayerSpace<SkIRect> & r)365 bool intersect(const LayerSpace<SkIRect>& r) { return fData.intersect(r.fData); } join(const LayerSpace<SkIRect> & r)366 void join(const LayerSpace<SkIRect>& r) { fData.join(r.fData); } offset(const LayerSpace<IVector> & v)367 void offset(const LayerSpace<IVector>& v) { fData.offset(SkIVector(v)); } outset(const LayerSpace<SkISize> & delta)368 void outset(const LayerSpace<SkISize>& delta) { fData.outset(delta.width(), delta.height()); } 369 370 private: 371 SkIRect fData; 372 }; 373 374 // Layer-space specialization for axis-aligned float bounding boxes. 375 template<> 376 class LayerSpace<SkRect> { 377 public: 378 LayerSpace() = default; LayerSpace(const SkRect & geometry)379 explicit LayerSpace(const SkRect& geometry) : fData(geometry) {} LayerSpace(SkRect && geometry)380 explicit LayerSpace(SkRect&& geometry) : fData(std::move(geometry)) {} 381 explicit operator const SkRect&() const { return fData; } 382 Empty()383 static LayerSpace<SkRect> Empty() { return LayerSpace<SkRect>(SkRect::MakeEmpty()); } 384 385 // Parrot the SkRect API while preserving coord space and usage isEmpty()386 bool isEmpty() const { return fData.isEmpty(); } 387 left()388 SkScalar left() const { return fData.fLeft; } top()389 SkScalar top() const { return fData.fTop; } right()390 SkScalar right() const { return fData.fRight; } bottom()391 SkScalar bottom() const { return fData.fBottom; } 392 width()393 SkScalar width() const { return fData.width(); } height()394 SkScalar height() const { return fData.height(); } 395 topLeft()396 LayerSpace<SkPoint> topLeft() const { 397 return LayerSpace<SkPoint>(SkPoint::Make(fData.fLeft, fData.fTop)); 398 } size()399 LayerSpace<SkSize> size() const { 400 return LayerSpace<SkSize>(SkSize::Make(fData.width(), fData.height())); 401 } 402 round()403 LayerSpace<SkIRect> round() const { return LayerSpace<SkIRect>(fData.round()); } roundIn()404 LayerSpace<SkIRect> roundIn() const { return LayerSpace<SkIRect>(fData.roundIn()); } roundOut()405 LayerSpace<SkIRect> roundOut() const { return LayerSpace<SkIRect>(fData.roundOut()); } 406 intersect(const LayerSpace<SkRect> & r)407 bool intersect(const LayerSpace<SkRect>& r) { return fData.intersect(r.fData); } join(const LayerSpace<SkRect> & r)408 void join(const LayerSpace<SkRect>& r) { fData.join(r.fData); } offset(const LayerSpace<Vector> & v)409 void offset(const LayerSpace<Vector>& v) { fData.offset(SkVector(v)); } outset(const LayerSpace<SkSize> & delta)410 void outset(const LayerSpace<SkSize>& delta) { fData.outset(delta.width(), delta.height()); } 411 412 private: 413 SkRect fData; 414 }; 415 416 // Mapping is the primary definition of the shared layer space used when evaluating an image filter 417 // DAG. It encapsulates any needed decomposition of the total CTM into the parameter-to-layer matrix 418 // (that filters use to map their parameters to the layer space), and the layer-to-device matrix 419 // (that canvas uses to map the output layer-space image into its root device space). Mapping 420 // defines functions to transform ParameterSpace and DeviceSpace types to and from their LayerSpace 421 // variants, which can then be used and reasoned about by SkImageFilter implementations. 422 class Mapping { 423 public: 424 Mapping() = default; 425 426 // This constructor allows the decomposition to be explicitly provided, requires 427 // layerToDev to be invertible. Mapping(const SkMatrix & layerToDev,const SkMatrix & paramToLayer)428 Mapping(const SkMatrix& layerToDev, const SkMatrix& paramToLayer) 429 : fLayerToDevMatrix(layerToDev) 430 , fParamToLayerMatrix(paramToLayer) { 431 SkAssertResult(fLayerToDevMatrix.invert(&fDevToLayerMatrix)); 432 } 433 434 // Sets this Mapping to the default decomposition of the canvas's total transform, given the 435 // requirements of the 'filter'. Returns false if the decomposition failed or would produce an 436 // invalid device matrix. Assumes 'ctm' is invertible. 437 bool SK_WARN_UNUSED_RESULT decomposeCTM(const SkMatrix& ctm, 438 const SkImageFilter* filter, 439 const skif::ParameterSpace<SkPoint>& representativePt); 440 441 // Update the mapping's parameter-to-layer matrix to be pre-concatenated with the specified 442 // local space transformation. This changes the definition of parameter space, any 443 // skif::ParameterSpace<> values are interpreted anew. Layer space and device space are 444 // unchanged. concatLocal(const SkMatrix & local)445 void concatLocal(const SkMatrix& local) { fParamToLayerMatrix.preConcat(local); } 446 447 // Update the mapping's layer space coordinate system by post-concatenating the given matrix 448 // to it's parameter-to-layer transform, and pre-concatenating the inverse of the matrix with 449 // it's layer-to-device transform. The net effect is that neither the parameter nor device 450 // coordinate systems are changed, but skif::LayerSpace is adjusted. 451 // 452 // Returns false if the layer matrix cannot be inverted, and this mapping is left unmodified. 453 bool adjustLayerSpace(const SkMatrix& layer); 454 455 // Update the mapping's layer space so that the point 'origin' in the current layer coordinate 456 // space maps to (0, 0) in the adjusted coordinate space. applyOrigin(const LayerSpace<SkIPoint> & origin)457 void applyOrigin(const LayerSpace<SkIPoint>& origin) { 458 SkAssertResult(this->adjustLayerSpace(SkMatrix::Translate(-origin.x(), -origin.y()))); 459 } 460 deviceMatrix()461 const SkMatrix& deviceMatrix() const { return fLayerToDevMatrix; } layerMatrix()462 const SkMatrix& layerMatrix() const { return fParamToLayerMatrix; } totalMatrix()463 SkMatrix totalMatrix() const { 464 return SkMatrix::Concat(fLayerToDevMatrix, fParamToLayerMatrix); 465 } 466 467 template<typename T> paramToLayer(const ParameterSpace<T> & paramGeometry)468 LayerSpace<T> paramToLayer(const ParameterSpace<T>& paramGeometry) const { 469 return LayerSpace<T>(map(static_cast<const T&>(paramGeometry), fParamToLayerMatrix)); 470 } 471 472 template<typename T> deviceToLayer(const DeviceSpace<T> & devGeometry)473 LayerSpace<T> deviceToLayer(const DeviceSpace<T>& devGeometry) const { 474 return LayerSpace<T>(map(static_cast<const T&>(devGeometry), fDevToLayerMatrix)); 475 } 476 477 template<typename T> layerToDevice(const LayerSpace<T> & layerGeometry)478 DeviceSpace<T> layerToDevice(const LayerSpace<T>& layerGeometry) const { 479 return DeviceSpace<T>(map(static_cast<const T&>(layerGeometry), fLayerToDevMatrix)); 480 } 481 482 private: 483 // The image filter process decomposes the total CTM into layerToDev * paramToLayer and uses the 484 // param-to-layer matrix to define the layer-space coordinate system. Depending on how it's 485 // decomposed, either the layer matrix or the device matrix could be the identity matrix (but 486 // sometimes neither). 487 SkMatrix fLayerToDevMatrix; 488 SkMatrix fParamToLayerMatrix; 489 490 // Cached inverse of fLayerToDevMatrix 491 SkMatrix fDevToLayerMatrix; 492 493 // Actual geometric mapping operations that work on coordinates and matrices w/o the type 494 // safety of the coordinate space wrappers (hence these are private). 495 template<typename T> 496 static T map(const T& geom, const SkMatrix& matrix); 497 }; 498 499 // Wraps an SkSpecialImage and its location in the shared layer coordinate space. This origin is 500 // used to draw the image in the correct location. The 'layerBounds' rectangle of the filtered image 501 // is the layer-space bounding box of the image. It has its top left corner at 'origin' and has the 502 // same dimensions as the underlying special image subset. Transforming 'layerBounds' by the 503 // Context's layer-to-device matrix and painting it with the subset rectangle will display the 504 // filtered results in the appropriate device-space region. 505 // 506 // When filter implementations are processing intermediate FilterResult results, it can be assumed 507 // that all FilterResult' layerBounds are in the same layer coordinate space defined by the shared 508 // skif::Context. 509 // 510 // NOTE: This is named FilterResult since most instances will represent the output of an image 511 // filter (even if that is then used as an input to the next filter). The main exception is the 512 // source input used when an input filter is null, but from a data-standpoint it is the same since 513 // it is equivalent to the result of an identity filter. 514 class FilterResult { 515 public: FilterResult()516 FilterResult() : fImage(nullptr), fOrigin(SkIPoint::Make(0, 0)) {} 517 FilterResult(sk_sp<SkSpecialImage> image,const LayerSpace<SkIPoint> & origin)518 FilterResult(sk_sp<SkSpecialImage> image, const LayerSpace<SkIPoint>& origin) 519 : fImage(std::move(image)) 520 , fOrigin(origin) {} FilterResult(sk_sp<SkSpecialImage> image)521 explicit FilterResult(sk_sp<SkSpecialImage> image) 522 : fImage(std::move(image)) 523 , fOrigin{{0, 0}} {} 524 image()525 const SkSpecialImage* image() const { return fImage.get(); } refImage()526 sk_sp<SkSpecialImage> refImage() const { return fImage; } 527 528 // Get the layer-space bounds of the result. This will have the same dimensions as the 529 // image and its top left corner will be 'origin()'. layerBounds()530 LayerSpace<SkIRect> layerBounds() const { 531 return LayerSpace<SkIRect>(SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), 532 fImage->width(), fImage->height())); 533 } 534 535 // Get the layer-space coordinate of this image's top left pixel. layerOrigin()536 const LayerSpace<SkIPoint>& layerOrigin() const { return fOrigin; } 537 538 // Produce a new FilterResult that can correctly cover 'newBounds' when it's drawn with its 539 // tile mode at its origin. When possible, the returned FilterResult can reuse the same image 540 // data and adjust its access subset, origin, and tile mode. If 'forcePad' is true or if the 541 // 'newTileMode' that applies at the 'newBounds' geometry is incompatible with the current 542 // bounds and tile mode, then a new image is created that resolves this image's data and tiling. 543 // TODO (michaelludwig): All FilterResults are decal mode and there are no current usages that 544 // require force-padding a decal FilterResult so these arguments aren't implemented yet. 545 FilterResult resolveToBounds(const LayerSpace<SkIRect>& newBounds) const; 546 // SkTileMode newTileMode=SkTileMode::kDecal, 547 // bool forcePad=false) const; 548 549 // Extract image and origin, safely when the image is null. 550 // TODO (michaelludwig) - This is intended for convenience until all call sites of 551 // SkImageFilter_Base::filterImage() have been updated to work in the new type system 552 // (which comes later as SkDevice, SkCanvas, etc. need to be modified, and coordinate space 553 // tagging needs to be added). imageAndOffset(SkIPoint * offset)554 sk_sp<SkSpecialImage> imageAndOffset(SkIPoint* offset) const { 555 if (fImage) { 556 *offset = SkIPoint(fOrigin); 557 return fImage; 558 } else { 559 *offset = {0, 0}; 560 return nullptr; 561 } 562 } 563 564 private: 565 sk_sp<SkSpecialImage> fImage; 566 LayerSpace<SkIPoint> fOrigin; 567 // SkTileMode fTileMode = SkTileMode::kDecal; 568 }; 569 570 // The context contains all necessary information to describe how the image filter should be 571 // computed (i.e. the current layer matrix and clip), and the color information of the output of a 572 // filter DAG. For now, this is just the color space (of the original requesting device). This is 573 // used when constructing intermediate rendering surfaces, so that we ensure we land in a surface 574 // that's similar/compatible to the final consumer of the DAG's output. 575 class Context { 576 public: 577 // Creates a context with the given layer matrix and destination clip, reading from 'source' 578 // with an origin of (0,0). Context(const SkMatrix & layerMatrix,const SkIRect & clipBounds,SkImageFilterCache * cache,SkColorType colorType,SkColorSpace * colorSpace,const SkSpecialImage * source)579 Context(const SkMatrix& layerMatrix, const SkIRect& clipBounds, SkImageFilterCache* cache, 580 SkColorType colorType, SkColorSpace* colorSpace, const SkSpecialImage* source) 581 : fMapping(SkMatrix::I(), layerMatrix) 582 , fDesiredOutput(clipBounds) 583 , fCache(cache) 584 , fColorType(colorType) 585 , fColorSpace(colorSpace) 586 , fSource(sk_ref_sp(source), LayerSpace<SkIPoint>({0, 0})) {} 587 Context(const Mapping & mapping,const LayerSpace<SkIRect> & desiredOutput,SkImageFilterCache * cache,SkColorType colorType,SkColorSpace * colorSpace,const FilterResult & source)588 Context(const Mapping& mapping, const LayerSpace<SkIRect>& desiredOutput, 589 SkImageFilterCache* cache, SkColorType colorType, SkColorSpace* colorSpace, 590 const FilterResult& source) 591 : fMapping(mapping) 592 , fDesiredOutput(desiredOutput) 593 , fCache(cache) 594 , fColorType(colorType) 595 , fColorSpace(colorSpace) 596 , fSource(source) {} 597 598 // The mapping that defines the transformation from local parameter space of the filters to the 599 // layer space where the image filters are evaluated, as well as the remaining transformation 600 // from the layer space to the final device space. The layer space defined by the returned 601 // Mapping may be the same as the root device space, or be an intermediate space that is 602 // supported by the image filter DAG (depending on what it returns from getCTMCapability()). 603 // If a node returns something other than kComplex from getCTMCapability(), the layer matrix of 604 // the mapping will respect that return value, and the remaining matrix will be appropriately 605 // set to transform the layer space to the final device space (applied by the SkCanvas when 606 // filtering is finished). mapping()607 const Mapping& mapping() const { return fMapping; } 608 // DEPRECATED: Use mapping() and its coordinate-space types instead ctm()609 const SkMatrix& ctm() const { return fMapping.layerMatrix(); } 610 // The bounds, in the layer space, that the filtered image will be clipped to. The output 611 // from filterImage() must cover these clip bounds, except in areas where it will just be 612 // transparent black, in which case a smaller output image can be returned. desiredOutput()613 const LayerSpace<SkIRect>& desiredOutput() const { return fDesiredOutput; } 614 // DEPRECATED: Use desiredOutput() instead clipBounds()615 const SkIRect& clipBounds() const { return static_cast<const SkIRect&>(fDesiredOutput); } 616 // The cache to use when recursing through the filter DAG, in order to avoid repeated 617 // calculations of the same image. cache()618 SkImageFilterCache* cache() const { return fCache; } 619 // The output device's color type, which can be used for intermediate images to be 620 // compatible with the eventual target of the filtered result. colorType()621 SkColorType colorType() const { return fColorType; } 622 #if SK_SUPPORT_GPU grColorType()623 GrColorType grColorType() const { return SkColorTypeToGrColorType(fColorType); } 624 #endif 625 // The output device's color space, so intermediate images can match, and so filtering can 626 // be performed in the destination color space. colorSpace()627 SkColorSpace* colorSpace() const { return fColorSpace; } refColorSpace()628 sk_sp<SkColorSpace> refColorSpace() const { return sk_ref_sp(fColorSpace); } 629 // The default surface properties to use when making transient surfaces during filtering. surfaceProps()630 const SkSurfaceProps& surfaceProps() const { return fSource.image()->props(); } 631 632 // This is the image to use whenever an expected input filter has been set to null. In the 633 // majority of cases, this is the original source image for the image filter DAG so it comes 634 // from the SkDevice that holds either the saveLayer or the temporary rendered result. The 635 // exception is composing two image filters (via SkImageFilters::Compose), which must use 636 // the output of the inner DAG as the "source" for the outer DAG. source()637 const FilterResult& source() const { return fSource; } 638 // DEPRECATED: Use source() instead to get both the image and its origin. sourceImage()639 const SkSpecialImage* sourceImage() const { return fSource.image(); } 640 641 // True if image filtering should occur on the GPU if possible. gpuBacked()642 bool gpuBacked() const { return fSource.image()->isTextureBacked(); } 643 // The recording context to use when computing the filter with the GPU. getContext()644 GrRecordingContext* getContext() const { return fSource.image()->getContext(); } 645 646 /** 647 * Since a context can be built directly, its constructor has no chance to "return null" if 648 * it's given invalid or unsupported inputs. Call this to know of the the context can be 649 * used. 650 * 651 * The SkImageFilterCache Key, for example, requires a finite ctm (no infinities or NaN), 652 * so that test is part of isValid. 653 */ isValid()654 bool isValid() const { return fSource.image() != nullptr && fMapping.layerMatrix().isFinite(); } 655 656 // Create a surface of the given size, that matches the context's color type and color space 657 // as closely as possible, and uses the same backend of the device that produced the source 658 // image. 659 sk_sp<SkSpecialSurface> makeSurface(const SkISize& size, 660 const SkSurfaceProps* props = nullptr) const { 661 if (!props) { 662 props = &this->surfaceProps(); 663 } 664 return fSource.image()->makeSurface(fColorType, fColorSpace, size, 665 kPremul_SkAlphaType, *props); 666 } 667 668 // Create a new context that matches this context, but with an overridden layer space. withNewMapping(const Mapping & mapping)669 Context withNewMapping(const Mapping& mapping) const { 670 return Context(mapping, fDesiredOutput, fCache, fColorType, fColorSpace, fSource); 671 } 672 // Create a new context that matches this context, but with an overridden desired output rect. withNewDesiredOutput(const LayerSpace<SkIRect> & desiredOutput)673 Context withNewDesiredOutput(const LayerSpace<SkIRect>& desiredOutput) const { 674 return Context(fMapping, desiredOutput, fCache, fColorType, fColorSpace, fSource); 675 } 676 677 private: 678 Mapping fMapping; 679 LayerSpace<SkIRect> fDesiredOutput; 680 SkImageFilterCache* fCache; 681 SkColorType fColorType; 682 // The pointed-to object is owned by the device controlling the filter process, and our lifetime 683 // is bounded by the device, so this can be a bare pointer. 684 SkColorSpace* fColorSpace; 685 FilterResult fSource; 686 }; 687 688 } // end namespace skif 689 690 #endif // SkImageFilterTypes_DEFINED 691