| 1 | /* |
| 2 | * Copyright 2017 Google Inc. |
| 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 SkShaderBase_DEFINED |
| 9 | #define SkShaderBase_DEFINED |
| 10 | |
| 11 | #include "include/core/SkColor.h" |
| 12 | #include "include/core/SkFlattenable.h" |
| 13 | #include "include/core/SkMatrix.h" |
| 14 | #include "include/core/SkPoint.h" |
| 15 | #include "include/core/SkRefCnt.h" |
| 16 | #include "include/core/SkScalar.h" |
| 17 | #include "include/core/SkShader.h" |
| 18 | #include "include/core/SkSurfaceProps.h" |
| 19 | #include "include/core/SkTypes.h" |
| 20 | #include "include/private/base/SkNoncopyable.h" |
| 21 | |
| 22 | #include <cstddef> |
| 23 | #include <cstdint> |
| 24 | #include <optional> |
| 25 | #include <tuple> |
| 26 | |
| 27 | class SkArenaAlloc; |
| 28 | class SkColorSpace; |
| 29 | class SkImage; |
| 30 | class SkRuntimeEffect; |
| 31 | class SkWriteBuffer; |
| 32 | enum SkColorType : int; |
| 33 | enum class SkTileMode; |
| 34 | struct SkDeserialProcs; |
| 35 | struct SkStageRec; |
| 36 | |
| 37 | namespace SkShaders { |
| 38 | /** |
| 39 | * This is used to accumulate matrices, starting with the CTM, when building up |
| 40 | * SkRasterPipeline or GrFragmentProcessor by walking the SkShader tree. It avoids |
| 41 | * adding a matrix multiply for each individual matrix. It also handles the reverse matrix |
| 42 | * concatenation order required by Android Framework, see b/256873449. |
| 43 | * |
| 44 | * This also tracks the dubious concept of a "total matrix", in the legacy Context/shadeSpan system. |
| 45 | * That includes all the matrices encountered during traversal to the current shader, including ones |
| 46 | * that have already been applied. The total matrix represents the transformation from the current |
| 47 | * shader's coordinate space to device space. It is dubious because it doesn't account for SkShaders |
| 48 | * that manipulate the coordinates passed to their children, which may not even be representable by |
| 49 | * a matrix. |
| 50 | * |
| 51 | * The total matrix is used for mipmap level selection and a filter downgrade optimizations in |
| 52 | * SkImageShader and sizing of the SkImage created by SkPictureShader. If we can remove usages |
| 53 | * of the "total matrix" and if Android Framework could be updated to not use backwards local |
| 54 | * matrix concatenation this could just be replaced by a simple SkMatrix or SkM44 passed down |
| 55 | * during traversal. |
| 56 | */ |
| 57 | class MatrixRec { |
| 58 | public: |
| 59 | MatrixRec() = default; |
| 60 | |
| 61 | explicit MatrixRec(const SkMatrix& ctm); |
| 62 | |
| 63 | /** |
| 64 | * Returns a new MatrixRec that represents the existing total and pending matrix |
| 65 | * pre-concat'ed with m. |
| 66 | */ |
| 67 | [[nodiscard]] MatrixRec concat(const SkMatrix& m) const; |
| 68 | |
| 69 | /** |
| 70 | * Appends a mul by the inverse of the pending local matrix to the pipeline. 'postInv' is an |
| 71 | * additional matrix to post-apply to the inverted pending matrix. If the pending matrix is |
| 72 | * not invertible the std::optional result won't have a value and the pipeline will be |
| 73 | * unmodified. |
| 74 | */ |
| 75 | [[nodiscard]] std::optional<MatrixRec> apply(const SkStageRec& rec, |
| 76 | const SkMatrix& postInv = {}) const; |
| 77 | |
| 78 | /** |
| 79 | * FP matrices work differently than SkRasterPipeline. The starting coordinates provided to the |
| 80 | * root SkShader's FP are already in local space. So we never apply the inverse CTM. This |
| 81 | * returns the inverted pending local matrix with the provided postInv matrix applied after it. |
| 82 | * If the pending local matrix cannot be inverted, the boolean is false. |
| 83 | */ |
| 84 | std::tuple<SkMatrix, bool> applyForFragmentProcessor(const SkMatrix& postInv) const; |
| 85 | |
| 86 | /** |
| 87 | * A parent FP may need to create a FP for its child by calling |
| 88 | * SkShaderBase::asFragmentProcessor() and then pass the result to the apply() above. |
| 89 | * This comes up when the parent needs to ensure pending matrices are applied before the |
| 90 | * child because the parent is going to manipulate the coordinates *after* any pending |
| 91 | * matrix and pass the resulting coords to the child. This function gets a MatrixRec that |
| 92 | * reflects the state after this MatrixRec has bee applied but it does not apply it! |
| 93 | * Example: |
| 94 | * auto childFP = fChild->asFragmentProcessor(args, mrec.applied()); |
| 95 | * childFP = MakeAWrappingFPThatModifiesChildsCoords(std::move(childFP)); |
| 96 | * auto [success, parentFP] = mrec.apply(std::move(childFP)); |
| 97 | */ |
| 98 | MatrixRec applied() const; |
| 99 | |
| 100 | /** Call to indicate that the mapping from shader to device space is not known. */ |
| 101 | void markTotalMatrixInvalid() { fTotalMatrixIsValid = false; } |
| 102 | |
| 103 | /** Marks the CTM as already applied; can avoid re-seeding the shader unnecessarily. */ |
| 104 | void markCTMApplied() { fCTMApplied = true; } |
| 105 | |
| 106 | /** |
| 107 | * Indicates whether the total matrix of a MatrixRec passed to a SkShader actually |
| 108 | * represents the full transform between that shader's coordinate space and device space. |
| 109 | */ |
| 110 | bool totalMatrixIsValid() const { return fTotalMatrixIsValid; } |
| 111 | |
| 112 | /** |
| 113 | * Gets the total transform from the current shader's space to device space. This may or |
| 114 | * may not be valid. Shaders should avoid making decisions based on this matrix if |
| 115 | * totalMatrixIsValid() is false. |
| 116 | */ |
| 117 | SkMatrix totalMatrix() const { return SkMatrix::Concat(a: fCTM, b: fTotalLocalMatrix); } |
| 118 | |
| 119 | /** Gets the inverse of totalMatrix(), if invertible. */ |
| 120 | [[nodiscard]] bool totalInverse(SkMatrix* out) const { |
| 121 | return this->totalMatrix().invert(inverse: out); |
| 122 | } |
| 123 | |
| 124 | /** Is there a transform that has not yet been applied by a parent shader? */ |
| 125 | bool hasPendingMatrix() const { |
| 126 | return (!fCTMApplied && !fCTM.isIdentity()) || !fPendingLocalMatrix.isIdentity(); |
| 127 | } |
| 128 | |
| 129 | /** When generating raster pipeline, have the device coordinates been seeded? */ |
| 130 | bool rasterPipelineCoordsAreSeeded() const { return fCTMApplied; } |
| 131 | |
| 132 | private: |
| 133 | MatrixRec(const SkMatrix& ctm, |
| 134 | const SkMatrix& totalLocalMatrix, |
| 135 | const SkMatrix& pendingLocalMatrix, |
| 136 | bool totalIsValid, |
| 137 | bool ctmApplied) |
| 138 | : fCTM(ctm) |
| 139 | , fTotalLocalMatrix(totalLocalMatrix) |
| 140 | , fPendingLocalMatrix(pendingLocalMatrix) |
| 141 | , fTotalMatrixIsValid(totalIsValid) |
| 142 | , fCTMApplied(ctmApplied) {} |
| 143 | |
| 144 | const SkMatrix fCTM; |
| 145 | |
| 146 | // Concatenation of all local matrices, including those already applied. |
| 147 | const SkMatrix fTotalLocalMatrix; |
| 148 | |
| 149 | // The accumulated local matrices from walking down the shader hierarchy that have NOT yet |
| 150 | // been incorporated into the SkRasterPipeline. |
| 151 | const SkMatrix fPendingLocalMatrix; |
| 152 | |
| 153 | bool fTotalMatrixIsValid = true; |
| 154 | |
| 155 | // Tracks whether the CTM has already been applied (and in raster pipeline whether the |
| 156 | // device coords have been seeded.) |
| 157 | bool fCTMApplied = false; |
| 158 | }; |
| 159 | |
| 160 | } // namespace SkShaders |
| 161 | |
| 162 | #define SK_ALL_SHADERS(M) \ |
| 163 | M(Blend) \ |
| 164 | M(CTM) \ |
| 165 | M(Color) \ |
| 166 | M(Color4) \ |
| 167 | M(ColorFilter) \ |
| 168 | M(CoordClamp) \ |
| 169 | M(Empty) \ |
| 170 | M(GradientBase) \ |
| 171 | M(Image) \ |
| 172 | M(LocalMatrix) \ |
| 173 | M(PerlinNoise) \ |
| 174 | M(Picture) \ |
| 175 | M(Runtime) \ |
| 176 | M(Transform) \ |
| 177 | M(TriColor) |
| 178 | |
| 179 | #define SK_ALL_GRADIENTS(M) \ |
| 180 | M(Conical) \ |
| 181 | M(Linear) \ |
| 182 | M(Radial) \ |
| 183 | M(Sweep) |
| 184 | |
| 185 | class SkShaderBase : public SkShader { |
| 186 | public: |
| 187 | ~SkShaderBase() override; |
| 188 | |
| 189 | sk_sp<SkShader> makeInvertAlpha() const; |
| 190 | sk_sp<SkShader> makeWithCTM(const SkMatrix&) const; // owns its own ctm |
| 191 | |
| 192 | /** |
| 193 | * Returns true if the shader is guaranteed to produce only a single color. |
| 194 | * Subclasses can override this to allow loop-hoisting optimization. |
| 195 | */ |
| 196 | virtual bool isConstant() const { return false; } |
| 197 | |
| 198 | enum class ShaderType { |
| 199 | #define M(type) k##type, |
| 200 | SK_ALL_SHADERS(M) |
| 201 | #undef M |
| 202 | }; |
| 203 | |
| 204 | virtual ShaderType type() const = 0; |
| 205 | |
| 206 | enum class GradientType { |
| 207 | kNone, |
| 208 | #define M(type) k##type, |
| 209 | SK_ALL_GRADIENTS(M) |
| 210 | #undef M |
| 211 | }; |
| 212 | |
| 213 | /** |
| 214 | * If the shader subclass can be represented as a gradient, asGradient |
| 215 | * returns the matching GradientType enum (or GradientType::kNone if it |
| 216 | * cannot). Also, if info is not null, asGradient populates info with |
| 217 | * the relevant (see below) parameters for the gradient. fColorCount |
| 218 | * is both an input and output parameter. On input, it indicates how |
| 219 | * many entries in fColors and fColorOffsets can be used, if they are |
| 220 | * non-NULL. After asGradient has run, fColorCount indicates how |
| 221 | * many color-offset pairs there are in the gradient. If there is |
| 222 | * insufficient space to store all of the color-offset pairs, fColors |
| 223 | * and fColorOffsets will not be altered. fColorOffsets specifies |
| 224 | * where on the range of 0 to 1 to transition to the given color. |
| 225 | * The meaning of fPoint and fRadius is dependent on the type of gradient. |
| 226 | * |
| 227 | * None: |
| 228 | * info is ignored. |
| 229 | * Color: |
| 230 | * fColorOffsets[0] is meaningless. |
| 231 | * Linear: |
| 232 | * fPoint[0] and fPoint[1] are the end-points of the gradient |
| 233 | * Radial: |
| 234 | * fPoint[0] and fRadius[0] are the center and radius |
| 235 | * Conical: |
| 236 | * fPoint[0] and fRadius[0] are the center and radius of the 1st circle |
| 237 | * fPoint[1] and fRadius[1] are the center and radius of the 2nd circle |
| 238 | * Sweep: |
| 239 | * fPoint[0] is the center of the sweep. |
| 240 | */ |
| 241 | struct GradientInfo { |
| 242 | int fColorCount = 0; //!< In-out parameter, specifies passed size |
| 243 | // of fColors/fColorOffsets on input, and |
| 244 | // actual number of colors/offsets on |
| 245 | // output. |
| 246 | SkColor* fColors = nullptr; //!< The colors in the gradient. |
| 247 | SkScalar* fColorOffsets = nullptr; //!< The unit offset for color transitions. |
| 248 | SkPoint fPoint[2]; //!< Type specific, see above. |
| 249 | SkScalar fRadius[2]; //!< Type specific, see above. |
| 250 | SkTileMode fTileMode; |
| 251 | uint32_t fGradientFlags = 0; //!< see SkGradientShader::Flags |
| 252 | }; |
| 253 | |
| 254 | virtual GradientType asGradient(GradientInfo* info = nullptr, |
| 255 | SkMatrix* localMatrix = nullptr) const { |
| 256 | return GradientType::kNone; |
| 257 | } |
| 258 | |
| 259 | enum Flags { |
| 260 | //!< set if all of the colors will be opaque |
| 261 | kOpaqueAlpha_Flag = 1 << 0, |
| 262 | }; |
| 263 | |
| 264 | /** |
| 265 | * ContextRec acts as a parameter bundle for creating Contexts. |
| 266 | */ |
| 267 | struct ContextRec { |
| 268 | ContextRec(SkAlpha paintAlpha, |
| 269 | const SkShaders::MatrixRec& matrixRec, |
| 270 | SkColorType dstColorType, |
| 271 | SkColorSpace* dstColorSpace, |
| 272 | SkSurfaceProps props) |
| 273 | : fMatrixRec(matrixRec) |
| 274 | , fDstColorType(dstColorType) |
| 275 | , fDstColorSpace(dstColorSpace) |
| 276 | , fProps(props) |
| 277 | , fPaintAlpha(paintAlpha) {} |
| 278 | |
| 279 | static ContextRec Concat(const ContextRec& parentRec, const SkMatrix& localM) { |
| 280 | return {parentRec.fPaintAlpha, |
| 281 | parentRec.fMatrixRec.concat(m: localM), |
| 282 | parentRec.fDstColorType, |
| 283 | parentRec.fDstColorSpace, |
| 284 | parentRec.fProps}; |
| 285 | } |
| 286 | |
| 287 | const SkShaders::MatrixRec fMatrixRec; |
| 288 | SkColorType fDstColorType; // the color type of the dest surface |
| 289 | SkColorSpace* fDstColorSpace; // the color space of the dest surface (if any) |
| 290 | SkSurfaceProps fProps; // props of the dest surface |
| 291 | SkAlpha fPaintAlpha; |
| 292 | |
| 293 | bool isLegacyCompatible(SkColorSpace* shadersColorSpace) const; |
| 294 | }; |
| 295 | |
| 296 | class Context : public ::SkNoncopyable { |
| 297 | public: |
| 298 | Context(const SkShaderBase& shader, const ContextRec&); |
| 299 | |
| 300 | virtual ~Context(); |
| 301 | |
| 302 | /** |
| 303 | * Called sometimes before drawing with this shader. Return the type of |
| 304 | * alpha your shader will return. The default implementation returns 0. |
| 305 | * Your subclass should override if it can (even sometimes) report a |
| 306 | * non-zero value, since that will enable various blitters to perform |
| 307 | * faster. |
| 308 | */ |
| 309 | virtual uint32_t getFlags() const { return 0; } |
| 310 | |
| 311 | /** |
| 312 | * Called for each span of the object being drawn. Your subclass should |
| 313 | * set the appropriate colors (with premultiplied alpha) that correspond |
| 314 | * to the specified device coordinates. |
| 315 | */ |
| 316 | virtual void shadeSpan(int x, int y, SkPMColor[], int count) = 0; |
| 317 | |
| 318 | protected: |
| 319 | // Reference to shader, so we don't have to dupe information. |
| 320 | const SkShaderBase& fShader; |
| 321 | |
| 322 | uint8_t getPaintAlpha() const { return fPaintAlpha; } |
| 323 | const SkMatrix& getTotalInverse() const { return fTotalInverse; } |
| 324 | |
| 325 | private: |
| 326 | SkMatrix fTotalInverse; |
| 327 | uint8_t fPaintAlpha; |
| 328 | }; |
| 329 | |
| 330 | /** |
| 331 | * Make a context using the memory provided by the arena. |
| 332 | * |
| 333 | * @return pointer to context or nullptr if can't be created |
| 334 | */ |
| 335 | Context* makeContext(const ContextRec&, SkArenaAlloc*) const; |
| 336 | |
| 337 | /** |
| 338 | * If the shader can represent its "average" luminance in a single color, return true and |
| 339 | * if color is not NULL, return that color. If it cannot, return false and ignore the color |
| 340 | * parameter. |
| 341 | * |
| 342 | * Note: if this returns true, the returned color will always be opaque, as only the RGB |
| 343 | * components are used to compute luminance. |
| 344 | */ |
| 345 | bool asLuminanceColor(SkColor*) const; |
| 346 | |
| 347 | /** |
| 348 | * If this returns false, then we draw nothing (do not fall back to shader context). This should |
| 349 | * only be called on a root-level effect. It assumes that the initial device coordinates have |
| 350 | * not yet been seeded. |
| 351 | */ |
| 352 | [[nodiscard]] bool appendRootStages(const SkStageRec& rec, const SkMatrix& ctm) const; |
| 353 | |
| 354 | /** |
| 355 | * Adds stages to implement this shader. To ensure that the correct input coords are present |
| 356 | * in r,g MatrixRec::apply() must be called (unless the shader doesn't require it's input |
| 357 | * coords). The default impl creates shadercontext and calls that (not very efficient). |
| 358 | */ |
| 359 | virtual bool appendStages(const SkStageRec&, const SkShaders::MatrixRec&) const; |
| 360 | |
| 361 | virtual SkImage* onIsAImage(SkMatrix*, SkTileMode[2]) const { |
| 362 | return nullptr; |
| 363 | } |
| 364 | |
| 365 | virtual SkRuntimeEffect* asRuntimeEffect() const { return nullptr; } |
| 366 | |
| 367 | static Type GetFlattenableType() { return kSkShader_Type; } |
| 368 | Type getFlattenableType() const override { return GetFlattenableType(); } |
| 369 | |
| 370 | static sk_sp<SkShaderBase> Deserialize(const void* data, size_t size, |
| 371 | const SkDeserialProcs* procs = nullptr) { |
| 372 | return sk_sp<SkShaderBase>(static_cast<SkShaderBase*>( |
| 373 | SkFlattenable::Deserialize(GetFlattenableType(), data, length: size, procs).release())); |
| 374 | } |
| 375 | static void RegisterFlattenables(); |
| 376 | |
| 377 | /** DEPRECATED. skbug.com/8941 |
| 378 | * If this shader can be represented by another shader + a localMatrix, return that shader and |
| 379 | * the localMatrix. If not, return nullptr and ignore the localMatrix parameter. |
| 380 | */ |
| 381 | virtual sk_sp<SkShader> makeAsALocalMatrixShader(SkMatrix* localMatrix) const; |
| 382 | |
| 383 | static SkMatrix ConcatLocalMatrices(const SkMatrix& parentLM, const SkMatrix& childLM) { |
| 384 | #if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK) // b/256873449 |
| 385 | return SkMatrix::Concat(childLM, parentLM); |
| 386 | #endif |
| 387 | return SkMatrix::Concat(a: parentLM, b: childLM); |
| 388 | } |
| 389 | |
| 390 | protected: |
| 391 | SkShaderBase(); |
| 392 | |
| 393 | void flatten(SkWriteBuffer&) const override; |
| 394 | |
| 395 | #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT |
| 396 | /** |
| 397 | * Specialize creating a SkShader context using the supplied allocator. |
| 398 | * @return pointer to context owned by the arena allocator. |
| 399 | */ |
| 400 | virtual Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const { |
| 401 | return nullptr; |
| 402 | } |
| 403 | #endif |
| 404 | |
| 405 | virtual bool onAsLuminanceColor(SkColor*) const { |
| 406 | return false; |
| 407 | } |
| 408 | |
| 409 | friend class SkShaders::MatrixRec; |
| 410 | }; |
| 411 | inline SkShaderBase* as_SB(SkShader* shader) { |
| 412 | return static_cast<SkShaderBase*>(shader); |
| 413 | } |
| 414 | |
| 415 | inline const SkShaderBase* as_SB(const SkShader* shader) { |
| 416 | return static_cast<const SkShaderBase*>(shader); |
| 417 | } |
| 418 | |
| 419 | inline const SkShaderBase* as_SB(const sk_sp<SkShader>& shader) { |
| 420 | return static_cast<SkShaderBase*>(shader.get()); |
| 421 | } |
| 422 | |
| 423 | void SkRegisterBlendShaderFlattenable(); |
| 424 | void SkRegisterColor4ShaderFlattenable(); |
| 425 | void SkRegisterColorShaderFlattenable(); |
| 426 | void SkRegisterCoordClampShaderFlattenable(); |
| 427 | void SkRegisterEmptyShaderFlattenable(); |
| 428 | void SkRegisterPerlinNoiseShaderFlattenable(); |
| 429 | |
| 430 | #endif // SkShaderBase_DEFINED |
| 431 | |