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: ParameterSpace(const T & data)77 explicit ParameterSpace(const T& data) : fData(data) {} ParameterSpace(T && data)78 explicit ParameterSpace(T&& data) : fData(std::move(data)) {} 79 80 explicit operator const T&() const { return fData; } 81 82 private: 83 T fData; 84 }; 85 86 // DeviceSpace is a data-only wrapper around Skia's geometric types. It is similar to 87 // 'ParameterSpace' except that it is used to represent geometry that has been transformed or 88 // defined in the root device space (i.e. the final pixels of drawn content). Much of what SkCanvas 89 // tracks, such as its clip bounds are defined in this space and DeviceSpace provides a 90 // type-enforced mechanism for the canvas to pass that information into the image filtering system, 91 // using the Mapping of the filtering context. 92 template<typename T> 93 class DeviceSpace { 94 public: DeviceSpace(const T & data)95 explicit DeviceSpace(const T& data) : fData(data) {} DeviceSpace(T && data)96 explicit DeviceSpace(T&& data) : fData(std::move(data)) {} 97 98 explicit operator const T&() const { return fData; } 99 100 private: 101 T fData; 102 }; 103 104 // LayerSpace is a geometric wrapper that specifies the geometry is defined in the shared layer 105 // space where image filters are evaluated. For a given Context (and its Mapping), the image filter 106 // DAG operates in the same coordinate space. This space may be different from the local coordinate 107 // space that defined the image filter parameters (such as blur sigma), and it may be different 108 // from the total CTM of the SkCanvas. 109 // 110 // To encourage correct filter use and implementation, the bulk of filter logic should be performed 111 // in layer space (e.g. determining what portion of an input image to read, or what the output 112 // region is). LayerSpace specializations for the six common Skia math types (Sk[I]Rect, Sk[I]Point, 113 // and Sk[I]Size), and skif::[I]Vector (to allow vectors to be specialized separately from points)) 114 // are provided that mimic their APIs but preserve the coordinate space and enforce type semantics. 115 template<typename T> 116 class LayerSpace {}; 117 118 // Layer-space specialization for integerized direction vectors. 119 template<> 120 class LayerSpace<IVector> { 121 public: 122 LayerSpace() = default; LayerSpace(const IVector & geometry)123 explicit LayerSpace(const IVector& geometry) : fData(geometry) {} LayerSpace(IVector && geometry)124 explicit LayerSpace(IVector&& geometry) : fData(std::move(geometry)) {} 125 explicit operator const IVector&() const { return fData; } 126 SkIVector()127 explicit operator SkIVector() const { return SkIVector::Make(fData.fX, fData.fY); } 128 x()129 int32_t x() const { return fData.fX; } y()130 int32_t y() const { return fData.fY; } 131 132 LayerSpace<IVector> operator-() const { return LayerSpace<IVector>({-fData.fX, -fData.fY}); } 133 134 LayerSpace<IVector> operator+(const LayerSpace<IVector>& v) const { 135 LayerSpace<IVector> sum = *this; 136 sum += v; 137 return sum; 138 } 139 LayerSpace<IVector> operator-(const LayerSpace<IVector>& v) const { 140 LayerSpace<IVector> diff = *this; 141 diff -= v; 142 return diff; 143 } 144 145 void operator+=(const LayerSpace<IVector>& v) { 146 fData.fX += v.fData.fX; 147 fData.fY += v.fData.fY; 148 } 149 void operator-=(const LayerSpace<IVector>& v) { 150 fData.fX -= v.fData.fX; 151 fData.fY -= v.fData.fY; 152 } 153 154 private: 155 IVector fData; 156 }; 157 158 // Layer-space specialization for floating point direction vectors. 159 template<> 160 class LayerSpace<Vector> { 161 public: 162 LayerSpace() = default; LayerSpace(const Vector & geometry)163 explicit LayerSpace(const Vector& geometry) : fData(geometry) {} LayerSpace(Vector && geometry)164 explicit LayerSpace(Vector&& geometry) : fData(std::move(geometry)) {} 165 explicit operator const Vector&() const { return fData; } 166 SkVector()167 explicit operator SkVector() const { return SkVector::Make(fData.fX, fData.fY); } 168 x()169 SkScalar x() const { return fData.fX; } y()170 SkScalar y() const { return fData.fY; } 171 length()172 SkScalar length() const { return SkVector::Length(fData.fX, fData.fY); } 173 174 LayerSpace<Vector> operator-() const { return LayerSpace<Vector>({-fData.fX, -fData.fY}); } 175 176 LayerSpace<Vector> operator*(SkScalar s) const { 177 LayerSpace<Vector> scaled = *this; 178 scaled *= s; 179 return scaled; 180 } 181 182 LayerSpace<Vector> operator+(const LayerSpace<Vector>& v) const { 183 LayerSpace<Vector> sum = *this; 184 sum += v; 185 return sum; 186 } 187 LayerSpace<Vector> operator-(const LayerSpace<Vector>& v) const { 188 LayerSpace<Vector> diff = *this; 189 diff -= v; 190 return diff; 191 } 192 193 void operator*=(SkScalar s) { 194 fData.fX *= s; 195 fData.fY *= s; 196 } 197 void operator+=(const LayerSpace<Vector>& v) { 198 fData.fX += v.fData.fX; 199 fData.fY += v.fData.fY; 200 } 201 void operator-=(const LayerSpace<Vector>& v) { 202 fData.fX -= v.fData.fX; 203 fData.fY -= v.fData.fY; 204 } 205 206 friend LayerSpace<Vector> operator*(SkScalar s, const LayerSpace<Vector>& b) { 207 return b * s; 208 } 209 210 private: 211 Vector fData; 212 }; 213 214 // Layer-space specialization for integer 2D coordinates (treated as positions, not directions). 215 template<> 216 class LayerSpace<SkIPoint> { 217 public: 218 LayerSpace() = default; LayerSpace(const SkIPoint & geometry)219 explicit LayerSpace(const SkIPoint& geometry) : fData(geometry) {} LayerSpace(SkIPoint && geometry)220 explicit LayerSpace(SkIPoint&& geometry) : fData(std::move(geometry)) {} 221 explicit operator const SkIPoint&() const { return fData; } 222 223 // Parrot the SkIPoint API while preserving coordinate space. x()224 int32_t x() const { return fData.fX; } y()225 int32_t y() const { return fData.fY; } 226 227 // Offsetting by direction vectors produce more points 228 LayerSpace<SkIPoint> operator+(const LayerSpace<IVector>& v) { 229 return LayerSpace<SkIPoint>(fData + SkIVector(v)); 230 } 231 LayerSpace<SkIPoint> operator-(const LayerSpace<IVector>& v) { 232 return LayerSpace<SkIPoint>(fData - SkIVector(v)); 233 } 234 235 void operator+=(const LayerSpace<IVector>& v) { 236 fData += SkIVector(v); 237 } 238 void operator-=(const LayerSpace<IVector>& v) { 239 fData -= SkIVector(v); 240 } 241 242 // Subtracting another point makes a direction between them 243 LayerSpace<IVector> operator-(const LayerSpace<SkIPoint>& p) { 244 return LayerSpace<IVector>(IVector(fData - p.fData)); 245 } 246 247 private: 248 SkIPoint fData; 249 }; 250 251 // Layer-space specialization for floating point 2D coordinates (treated as positions) 252 template<> 253 class LayerSpace<SkPoint> { 254 public: 255 LayerSpace() = default; LayerSpace(const SkPoint & geometry)256 explicit LayerSpace(const SkPoint& geometry) : fData(geometry) {} LayerSpace(SkPoint && geometry)257 explicit LayerSpace(SkPoint&& geometry) : fData(std::move(geometry)) {} 258 explicit operator const SkPoint&() const { return fData; } 259 260 // Parrot the SkPoint API while preserving coordinate space. x()261 SkScalar x() const { return fData.fX; } y()262 SkScalar y() const { return fData.fY; } 263 distanceToOrigin()264 SkScalar distanceToOrigin() const { return fData.distanceToOrigin(); } 265 266 // Offsetting by direction vectors produce more points 267 LayerSpace<SkPoint> operator+(const LayerSpace<Vector>& v) { 268 return LayerSpace<SkPoint>(fData + SkVector(v)); 269 } 270 LayerSpace<SkPoint> operator-(const LayerSpace<Vector>& v) { 271 return LayerSpace<SkPoint>(fData - SkVector(v)); 272 } 273 274 void operator+=(const LayerSpace<Vector>& v) { 275 fData += SkVector(v); 276 } 277 void operator-=(const LayerSpace<Vector>& v) { 278 fData -= SkVector(v); 279 } 280 281 // Subtracting another point makes a direction between them 282 LayerSpace<Vector> operator-(const LayerSpace<SkPoint>& p) { 283 return LayerSpace<Vector>(Vector(fData - p.fData)); 284 } 285 286 private: 287 SkPoint fData; 288 }; 289 290 // Layer-space specialization for integer dimensions 291 template<> 292 class LayerSpace<SkISize> { 293 public: 294 LayerSpace() = default; LayerSpace(const SkISize & geometry)295 explicit LayerSpace(const SkISize& geometry) : fData(geometry) {} LayerSpace(SkISize && geometry)296 explicit LayerSpace(SkISize&& geometry) : fData(std::move(geometry)) {} 297 explicit operator const SkISize&() const { return fData; } 298 width()299 int32_t width() const { return fData.width(); } height()300 int32_t height() const { return fData.height(); } 301 isEmpty()302 bool isEmpty() const { return fData.isEmpty(); } 303 304 private: 305 SkISize fData; 306 }; 307 308 // Layer-space specialization for floating point dimensions 309 template<> 310 class LayerSpace<SkSize> { 311 public: 312 LayerSpace() = default; LayerSpace(const SkSize & geometry)313 explicit LayerSpace(const SkSize& geometry) : fData(geometry) {} LayerSpace(SkSize && geometry)314 explicit LayerSpace(SkSize&& geometry) : fData(std::move(geometry)) {} 315 explicit operator const SkSize&() const { return fData; } 316 width()317 SkScalar width() const { return fData.width(); } height()318 SkScalar height() const { return fData.height(); } 319 isEmpty()320 bool isEmpty() const { return fData.isEmpty(); } isZero()321 bool isZero() const { return fData.isZero(); } 322 round()323 LayerSpace<SkISize> round() const { return LayerSpace<SkISize>(fData.toRound()); } ceil()324 LayerSpace<SkISize> ceil() const { return LayerSpace<SkISize>(fData.toCeil()); } floor()325 LayerSpace<SkISize> floor() const { return LayerSpace<SkISize>(fData.toFloor()); } 326 327 private: 328 SkSize fData; 329 }; 330 331 // Layer-space specialization for axis-aligned integer bounding boxes. 332 template<> 333 class LayerSpace<SkIRect> { 334 public: 335 LayerSpace() = default; LayerSpace(const SkIRect & geometry)336 explicit LayerSpace(const SkIRect& geometry) : fData(geometry) {} LayerSpace(SkIRect && geometry)337 explicit LayerSpace(SkIRect&& geometry) : fData(std::move(geometry)) {} 338 explicit operator const SkIRect&() const { return fData; } 339 340 // Parrot the SkIRect API while preserving coord space left()341 int32_t left() const { return fData.fLeft; } top()342 int32_t top() const { return fData.fTop; } right()343 int32_t right() const { return fData.fRight; } bottom()344 int32_t bottom() const { return fData.fBottom; } 345 width()346 int32_t width() const { return fData.width(); } height()347 int32_t height() const { return fData.height(); } 348 topLeft()349 LayerSpace<SkIPoint> topLeft() const { return LayerSpace<SkIPoint>(fData.topLeft()); } size()350 LayerSpace<SkISize> size() const { return LayerSpace<SkISize>(fData.size()); } 351 intersect(const LayerSpace<SkIRect> & r)352 bool intersect(const LayerSpace<SkIRect>& r) { return fData.intersect(r.fData); } join(const LayerSpace<SkIRect> & r)353 void join(const LayerSpace<SkIRect>& r) { fData.join(r.fData); } offset(const LayerSpace<IVector> & v)354 void offset(const LayerSpace<IVector>& v) { fData.offset(SkIVector(v)); } outset(const LayerSpace<SkISize> & delta)355 void outset(const LayerSpace<SkISize>& delta) { fData.outset(delta.width(), delta.height()); } 356 357 private: 358 SkIRect fData; 359 }; 360 361 // Layer-space specialization for axis-aligned float bounding boxes. 362 template<> 363 class LayerSpace<SkRect> { 364 public: 365 LayerSpace() = default; LayerSpace(const SkRect & geometry)366 explicit LayerSpace(const SkRect& geometry) : fData(geometry) {} LayerSpace(SkRect && geometry)367 explicit LayerSpace(SkRect&& geometry) : fData(std::move(geometry)) {} 368 explicit operator const SkRect&() const { return fData; } 369 370 // Parrot the SkRect API while preserving coord space and usage left()371 SkScalar left() const { return fData.fLeft; } top()372 SkScalar top() const { return fData.fTop; } right()373 SkScalar right() const { return fData.fRight; } bottom()374 SkScalar bottom() const { return fData.fBottom; } 375 width()376 SkScalar width() const { return fData.width(); } height()377 SkScalar height() const { return fData.height(); } 378 topLeft()379 LayerSpace<SkPoint> topLeft() const { 380 return LayerSpace<SkPoint>(SkPoint::Make(fData.fLeft, fData.fTop)); 381 } size()382 LayerSpace<SkSize> size() const { 383 return LayerSpace<SkSize>(SkSize::Make(fData.width(), fData.height())); 384 } roundOut()385 LayerSpace<SkIRect> roundOut() const { return LayerSpace<SkIRect>(fData.roundOut()); } 386 intersect(const LayerSpace<SkRect> & r)387 bool intersect(const LayerSpace<SkRect>& r) { return fData.intersect(r.fData); } join(const LayerSpace<SkRect> & r)388 void join(const LayerSpace<SkRect>& r) { fData.join(r.fData); } offset(const LayerSpace<Vector> & v)389 void offset(const LayerSpace<Vector>& v) { fData.offset(SkVector(v)); } outset(const LayerSpace<SkSize> & delta)390 void outset(const LayerSpace<SkSize>& delta) { fData.outset(delta.width(), delta.height()); } 391 392 private: 393 SkRect fData; 394 }; 395 396 // Mapping is the primary definition of the shared layer space used when evaluating an image filter 397 // DAG. It encapsulates any needed decomposition of the total CTM into the parameter-to-layer matrix 398 // (that filters use to map their parameters to the layer space), and the layer-to-device matrix 399 // (that canvas uses to map the output layer-space image into its root device space). Mapping 400 // defines functions to transform ParameterSpace and DeviceSpace types to and from their LayerSpace 401 // variants, which can then be used and reasoned about by SkImageFilter implementations. 402 class Mapping { 403 public: 404 // This constructor allows the decomposition to be explicitly provided Mapping(const SkMatrix & layerToDev,const SkMatrix & paramToLayer)405 Mapping(const SkMatrix& layerToDev, const SkMatrix& paramToLayer) 406 : fLayerToDevMatrix(layerToDev) 407 , fParamToLayerMatrix(paramToLayer) {} 408 409 // Make the default decomposition Mapping, given the total CTM and the root image filter. 410 static Mapping DecomposeCTM(const SkMatrix& ctm, const SkImageFilter* filter, 411 const skif::ParameterSpace<SkPoint>& representativePoint); 412 413 // Return a new Mapping object whose parameter-to-layer matrix is equal to this->layerMatrix() * 414 // local, but both share the same layer-to-device matrix. concatLocal(const SkMatrix & local)415 Mapping concatLocal(const SkMatrix& local) const { 416 return Mapping(fLayerToDevMatrix, SkMatrix::Concat(fParamToLayerMatrix, local)); 417 } 418 deviceMatrix()419 const SkMatrix& deviceMatrix() const { return fLayerToDevMatrix; } layerMatrix()420 const SkMatrix& layerMatrix() const { return fParamToLayerMatrix; } totalMatrix()421 SkMatrix totalMatrix() const { 422 return SkMatrix::Concat(fLayerToDevMatrix, fParamToLayerMatrix); 423 } 424 425 template<typename T> paramToLayer(const ParameterSpace<T> & paramGeometry)426 LayerSpace<T> paramToLayer(const ParameterSpace<T>& paramGeometry) const { 427 return LayerSpace<T>(map(static_cast<const T&>(paramGeometry), fParamToLayerMatrix)); 428 } 429 430 template<typename T> deviceToLayer(const DeviceSpace<T> & devGeometry)431 LayerSpace<T> deviceToLayer(const DeviceSpace<T>& devGeometry) const { 432 // The mapping from device space to layer space is defined by the inverse of the 433 // layer-to-device matrix 434 SkMatrix devToLayerMatrix; 435 if (!fLayerToDevMatrix.invert(&devToLayerMatrix)) { 436 // Punt and just pass through the geometry unmodified... 437 return LayerSpace<T>(static_cast<const T&>(devGeometry)); 438 } else { 439 return LayerSpace<T>(map(static_cast<const T&>(devGeometry), devToLayerMatrix)); 440 } 441 } 442 443 template<typename T> layerToDevice(const LayerSpace<T> & layerGeometry)444 DeviceSpace<T> layerToDevice(const LayerSpace<T>& layerGeometry) const { 445 return DeviceSpace<T>(map(static_cast<const T&>(layerGeometry), fLayerToDevMatrix)); 446 } 447 448 private: 449 // The image filter process decomposes the total CTM into layerToDev * paramToLayer and uses the 450 // param-to-layer matrix to define the layer-space coordinate system. Depending on how it's 451 // decomposed, either the layer matrix or the device matrix could be the identity matrix (but 452 // sometimes neither). 453 SkMatrix fLayerToDevMatrix; 454 SkMatrix fParamToLayerMatrix; 455 456 // Actual geometric mapping operations that work on coordinates and matrices w/o the type 457 // safety of the coordinate space wrappers (hence these are private). 458 template<typename T> 459 static T map(const T& geom, const SkMatrix& matrix); 460 }; 461 462 // Usage is a template tag to improve the readability of filter implementations. It is attached to 463 // images and geometry to group a collection of related variables and ensure that moving from one 464 // use case to another is explicit. 465 // NOTE: This can be aliased as 'For' when using the fluent type names. 466 // TODO (michaelludwig) - If the primary motivation for Usage--enforcing layer to image space 467 // transformations safely when multiple images are involved--can be handled entirely by helper 468 // functions on FilterResult, then Usage can go away and FilterResult will not need to be templated 469 enum class Usage { 470 // Designates the semantic purpose of the bounds, coordinate, or image as being an input 471 // to the image filter calculations. When this usage is used, it denotes a generic input, 472 // such as the current input in a dynamic loop, or some aggregate of all inputs. Because 473 // most image filters consume 1 or 2 filters only, the related kInput0 and kInput1 are 474 // also defined. 475 kInput, 476 // A more specific version of kInput, this marks the tagged variable as attached to the 477 // image filter of SkImageFilter_Base::getInput(0). 478 kInput0, 479 // A more specific version of kInput, this marks the tagged variable as attached to the 480 // image filter of SkImageFilter_Base::getInput(1). 481 kInput1, 482 // Designates the purpose of the bounds, coordinate, or image as being the output of the 483 // current image filter calculation. There is only ever one output for an image filter. 484 kOutput, 485 }; 486 487 // Convenience macros to add 'using' declarations that rename the above enums to provide a more 488 // fluent and readable API. This should only be used in a private or local scope to prevent leakage 489 // of the names. Use the IN_CLASS variant at the start of a class declaration in those scenarios. 490 // These macros enable the following simpler type names: 491 // skif::Image<skif::Usage::kInput> -> Image<For::kInput> 492 #define SK_USE_FLUENT_IMAGE_FILTER_TYPES \ 493 using For = skif::Usage; 494 495 #define SK_USE_FLUENT_IMAGE_FILTER_TYPES_IN_CLASS \ 496 protected: SK_USE_FLUENT_IMAGE_FILTER_TYPES public: 497 498 // Wraps an SkSpecialImage and tags it with a corresponding usage, either as generic input (e.g. the 499 // source image), or a specific input image from a filter's connected inputs. It also includes the 500 // origin of the image in the layer space. This origin is used to draw the image in the correct 501 // location. The 'layerBounds' rectangle of the filtered image is the layer-space bounding box of 502 // the image. It has its top left corner at 'origin' and has the same dimensions as the underlying 503 // special image subset. Transforming 'layerBounds' by the Context's layer matrix and painting it 504 // with the subset rectangle will display the filtered results in the appropriate device-space 505 // region. 506 // 507 // When filter implementations are processing intermediate FilterResult results, it can be assumed 508 // that all FilterResult' layerBounds are in the same layer coordinate space defined by the shared 509 // skif::Context. 510 // 511 // NOTE: This is named FilterResult since most instances will represent the output of an image 512 // filter (even if that is then cast to be the input to the next filter). The main exception is the 513 // source input used when an input filter is null, but from a data-standpoint it is the same since 514 // it is equivalent to the result of an identity filter. 515 template<Usage kU> 516 class FilterResult { 517 public: FilterResult()518 FilterResult() : fImage(nullptr), fOrigin(SkIPoint::Make(0, 0)) {} 519 FilterResult(sk_sp<SkSpecialImage> image,const LayerSpace<SkIPoint> & origin)520 FilterResult(sk_sp<SkSpecialImage> image, const LayerSpace<SkIPoint>& origin) 521 : fImage(std::move(image)) 522 , fOrigin(origin) {} FilterResult(sk_sp<SkSpecialImage> image)523 explicit FilterResult(sk_sp<SkSpecialImage> image) 524 : fImage(std::move(image)) 525 , fOrigin{{0, 0}} {} 526 527 // Allow explicit moves/copies in order to cast from one use type to another, except kInput0 528 // and kInput1 can only be cast to kOutput (e.g. as part of a noop image filter). 529 template<Usage kI> FilterResult(FilterResult<kI> && image)530 explicit FilterResult(FilterResult<kI>&& image) 531 : fImage(std::move(image.fImage)) 532 , fOrigin(image.fOrigin) { 533 static_assert((kU != Usage::kInput) || (kI != Usage::kInput0 && kI != Usage::kInput1), 534 "kInput0 and kInput1 cannot be moved to more generic kInput usage."); 535 static_assert((kU != Usage::kInput0 && kU != Usage::kInput1) || 536 (kI == kU || kI == Usage::kInput || kI == Usage::kOutput), 537 "Can only move to specific input from the generic kInput or kOutput usage."); 538 } 539 540 template<Usage kI> FilterResult(const FilterResult<kI> & image)541 explicit FilterResult(const FilterResult<kI>& image) 542 : fImage(image.fImage) 543 , fOrigin(image.fOrigin) { 544 static_assert((kU != Usage::kInput) || (kI != Usage::kInput0 && kI != Usage::kInput1), 545 "kInput0 and kInput1 cannot be copied to more generic kInput usage."); 546 static_assert((kU != Usage::kInput0 && kU != Usage::kInput1) || 547 (kI == kU || kI == Usage::kInput || kI == Usage::kOutput), 548 "Can only copy to specific input from the generic kInput usage."); 549 } 550 image()551 const SkSpecialImage* image() const { return fImage.get(); } refImage()552 sk_sp<SkSpecialImage> refImage() const { return fImage; } 553 554 // Get the layer-space bounds of the result. This will have the same dimensions as the 555 // image and its top left corner will be 'origin()'. layerBounds()556 LayerSpace<SkIRect> layerBounds() const { 557 return LayerSpace<SkIRect>(SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), 558 fImage->width(), fImage->height())); 559 } 560 561 // Get the layer-space coordinate of this image's top left pixel. layerOrigin()562 const LayerSpace<SkIPoint>& layerOrigin() const { return fOrigin; } 563 564 // Extract image and origin, safely when the image is null. 565 // TODO (michaelludwig) - This is intended for convenience until all call sites of 566 // SkImageFilter_Base::filterImage() have been updated to work in the new type system 567 // (which comes later as SkDevice, SkCanvas, etc. need to be modified, and coordinate space 568 // tagging needs to be added). imageAndOffset(SkIPoint * offset)569 sk_sp<SkSpecialImage> imageAndOffset(SkIPoint* offset) const { 570 if (fImage) { 571 *offset = SkIPoint(fOrigin); 572 return fImage; 573 } else { 574 *offset = {0, 0}; 575 return nullptr; 576 } 577 } 578 579 private: 580 // Allow all FilterResult templates access to each others members 581 template<Usage kO> 582 friend class FilterResult; 583 584 sk_sp<SkSpecialImage> fImage; 585 LayerSpace<SkIPoint> fOrigin; 586 }; 587 588 // The context contains all necessary information to describe how the image filter should be 589 // computed (i.e. the current layer matrix and clip), and the color information of the output of a 590 // filter DAG. For now, this is just the color space (of the original requesting device). This is 591 // used when constructing intermediate rendering surfaces, so that we ensure we land in a surface 592 // that's similar/compatible to the final consumer of the DAG's output. 593 class Context { 594 public: 595 SK_USE_FLUENT_IMAGE_FILTER_TYPES_IN_CLASS 596 597 // Creates a context with the given layer matrix and destination clip, reading from 'source' 598 // with an origin of (0,0). Context(const SkMatrix & layerMatrix,const SkIRect & clipBounds,SkImageFilterCache * cache,SkColorType colorType,SkColorSpace * colorSpace,const SkSpecialImage * source)599 Context(const SkMatrix& layerMatrix, const SkIRect& clipBounds, SkImageFilterCache* cache, 600 SkColorType colorType, SkColorSpace* colorSpace, const SkSpecialImage* source) 601 : fMapping(SkMatrix::I(), layerMatrix) 602 , fDesiredOutput(clipBounds) 603 , fCache(cache) 604 , fColorType(colorType) 605 , fColorSpace(colorSpace) 606 , fSource(sk_ref_sp(source), LayerSpace<SkIPoint>({0, 0})) {} 607 Context(const Mapping & mapping,const LayerSpace<SkIRect> & desiredOutput,SkImageFilterCache * cache,SkColorType colorType,SkColorSpace * colorSpace,const FilterResult<For::kInput> & source)608 Context(const Mapping& mapping, const LayerSpace<SkIRect>& desiredOutput, 609 SkImageFilterCache* cache, SkColorType colorType, SkColorSpace* colorSpace, 610 const FilterResult<For::kInput>& source) 611 : fMapping(mapping) 612 , fDesiredOutput(desiredOutput) 613 , fCache(cache) 614 , fColorType(colorType) 615 , fColorSpace(colorSpace) 616 , fSource(source) {} 617 618 // The mapping that defines the transformation from local parameter space of the filters to the 619 // layer space where the image filters are evaluated, as well as the remaining transformation 620 // from the layer space to the final device space. The layer space defined by the returned 621 // Mapping may be the same as the root device space, or be an intermediate space that is 622 // supported by the image filter DAG (depending on what it returns from canHandleComplexCTM()). 623 // If a node returns false from canHandleComplexCTM(), the layer matrix of the mapping will be 624 // at most a scale + translate, and the remaining matrix will be appropriately set to transform 625 // the layer space to the final device space (applied by the SkCanvas when filtering is 626 // finished). mapping()627 const Mapping& mapping() const { return fMapping; } 628 // DEPRECATED: Use mapping() and its coordinate-space types instead ctm()629 const SkMatrix& ctm() const { return fMapping.layerMatrix(); } 630 // The bounds, in the layer space, that the filtered image will be clipped to. The output 631 // from filterImage() must cover these clip bounds, except in areas where it will just be 632 // transparent black, in which case a smaller output image can be returned. desiredOutput()633 const LayerSpace<SkIRect>& desiredOutput() const { return fDesiredOutput; } 634 // DEPRECATED: Use desiredOutput() instead clipBounds()635 const SkIRect& clipBounds() const { return static_cast<const SkIRect&>(fDesiredOutput); } 636 // The cache to use when recursing through the filter DAG, in order to avoid repeated 637 // calculations of the same image. cache()638 SkImageFilterCache* cache() const { return fCache; } 639 // The output device's color type, which can be used for intermediate images to be 640 // compatible with the eventual target of the filtered result. colorType()641 SkColorType colorType() const { return fColorType; } 642 #if SK_SUPPORT_GPU grColorType()643 GrColorType grColorType() const { return SkColorTypeToGrColorType(fColorType); } 644 #endif 645 // The output device's color space, so intermediate images can match, and so filtering can 646 // be performed in the destination color space. colorSpace()647 SkColorSpace* colorSpace() const { return fColorSpace; } refColorSpace()648 sk_sp<SkColorSpace> refColorSpace() const { return sk_ref_sp(fColorSpace); } 649 // The default surface properties to use when making transient surfaces during filtering. surfaceProps()650 const SkSurfaceProps& surfaceProps() const { return fSource.image()->props(); } 651 652 // This is the image to use whenever an expected input filter has been set to null. In the 653 // majority of cases, this is the original source image for the image filter DAG so it comes 654 // from the SkDevice that holds either the saveLayer or the temporary rendered result. The 655 // exception is composing two image filters (via SkImageFilters::Compose), which must use 656 // the output of the inner DAG as the "source" for the outer DAG. source()657 const FilterResult<For::kInput>& source() const { return fSource; } 658 // DEPRECATED: Use source() instead to get both the image and its origin. sourceImage()659 const SkSpecialImage* sourceImage() const { return fSource.image(); } 660 661 // True if image filtering should occur on the GPU if possible. gpuBacked()662 bool gpuBacked() const { return fSource.image()->isTextureBacked(); } 663 // The recording context to use when computing the filter with the GPU. getContext()664 GrRecordingContext* getContext() const { return fSource.image()->getContext(); } 665 666 /** 667 * Since a context can be built directly, its constructor has no chance to "return null" if 668 * it's given invalid or unsupported inputs. Call this to know of the the context can be 669 * used. 670 * 671 * The SkImageFilterCache Key, for example, requires a finite ctm (no infinities or NaN), 672 * so that test is part of isValid. 673 */ isValid()674 bool isValid() const { return fSource.image() != nullptr && fMapping.layerMatrix().isFinite(); } 675 676 // Create a surface of the given size, that matches the context's color type and color space 677 // as closely as possible, and uses the same backend of the device that produced the source 678 // image. 679 sk_sp<SkSpecialSurface> makeSurface(const SkISize& size, 680 const SkSurfaceProps* props = nullptr) const { 681 if (!props) { 682 props = &this->surfaceProps(); 683 } 684 return fSource.image()->makeSurface(fColorType, fColorSpace, size, 685 kPremul_SkAlphaType, *props); 686 } 687 688 // Create a new context that matches this context, but with an overridden layer space. withNewMapping(const Mapping & mapping)689 Context withNewMapping(const Mapping& mapping) const { 690 return Context(mapping, fDesiredOutput, fCache, fColorType, fColorSpace, fSource); 691 } 692 // Create a new context that matches this context, but with an overridden desired output rect. withNewDesiredOutput(const LayerSpace<SkIRect> & desiredOutput)693 Context withNewDesiredOutput(const LayerSpace<SkIRect>& desiredOutput) const { 694 return Context(fMapping, desiredOutput, fCache, fColorType, fColorSpace, fSource); 695 } 696 697 private: 698 Mapping fMapping; 699 LayerSpace<SkIRect> fDesiredOutput; 700 SkImageFilterCache* fCache; 701 SkColorType fColorType; 702 // The pointed-to object is owned by the device controlling the filter process, and our lifetime 703 // is bounded by the device, so this can be a bare pointer. 704 SkColorSpace* fColorSpace; 705 FilterResult<For::kInput> fSource; 706 }; 707 708 } // end namespace skif 709 710 #endif // SkImageFilterTypes_DEFINED 711