Decouple SkShaders from Ganesh backend
This moves all asFragmentProcessor methods from the src/shaders
and src/shaders/gradients into GrFragmentProcessors. As a requirement
to do so, all shaders have had a public header made with the class
declaration. Many classes required new getters to expose some
fields for the construction of the GrFragmentProcessor. I briefly
toyed with the idea of using friend functions, but decided against
it for 2 reasons: 1) the syntax was verbose and gnarly; 2) the
syntax would make the functions of GrFragmentProcessors callable
from other places and not be static / contained to just
GrFragmentProcessors.cpp.
Other notable changes:
- SkShaderBase::GradientType::kColor removed, as it has been
superseded by SkShaderBase::ShaderType::kColor for detecting
"is this a color shader?" Callsites that needed the color
now cast it to SkColorShader
- SkPerlinNoiseShader class removed from public API
(Chromium migrated in https://crrev.com/c/4591270) and
used internally instead of SkPerlinNoiseShaderImpl for closer
alignment with the ShaderType enum.
- SkPerlinNoiseShader::PaintingData no longer uses #ifdefs
to make the bitmaps for the GPU backends, but this is now
explicitly done in a function generateBitmaps().
- SkShaderBase::MatrixRec has been moved to the SkShaders
namespace and had the Ganesh-specific apply() changed to
applyForFragmentProcessor(). The Ganesh-parts (e.g. the creation
of the GrMatrixEffect) were made the responsibility of
the caller (e.g. in GrFragmentProcessors.cpp).
- I thought it was strange that SkBitmapProcShader.h (but not
the .cpp) was in public.bzl. I made that not necessary by
#ifdef'ing the one include that was necessary removing
other unnecessary ones. This is because G3 sets
SK_DISABLE_LEGACY_SHADERCONTEXT
The following shader subclasses did not appear to have Ganesh
implementations and that is still the case:
- SkEmptyShader
- SpriteShader (appears SkVM only)
- DitherShader (appears SkVM only)
- SkTransformShader
- SkTriColorShader
- SkUpdatableColorShader
I enforced IWYU on src/shaders and all other new files I created
in this CL. This had some impact in wider files, but I do not
expect it to impact clients as no public #includes had #includes
removed.
Suggested Review Order:
- SkShaderBase.h/.cpp, noting that much of the .cpp came from
SkShader.cpp
- GrFragmentProcessors.cpp, glancing at the relevant subclass
.h and .cpp changes as necessary
- SkPerlinNoise* and GrPerlinNoise* to make sure I didn't overlook
something in the refactor/extraction
- Another pass through other changes in src/shaders and
src/shaders/gradients
- All other files in any order.
Change-Id: Icf3db955d0653f97d7c793b167463162fb70d9f2
Bug: skia:14317
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/706216
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Kevin Lubick <kjlubick@google.com>
diff --git a/bazel/exporter_tool/main.go b/bazel/exporter_tool/main.go
index a818410..73dd7619 100644
--- a/bazel/exporter_tool/main.go
+++ b/bazel/exporter_tool/main.go
@@ -67,6 +67,8 @@
"//src/opts:private_hdrs",
"//src/shaders:shader_hdrs",
"//src/shaders:shader_srcs",
+ "//src/shaders:sksl_hdrs",
+ "//src/shaders:sksl_srcs",
"//src/text:text_hdrs",
"//src/text:text_srcs",
}},
diff --git a/bench/PerlinNoiseBench.cpp b/bench/PerlinNoiseBench.cpp
index 6c93e46..a809836 100644
--- a/bench/PerlinNoiseBench.cpp
+++ b/bench/PerlinNoiseBench.cpp
@@ -42,9 +42,8 @@
float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed,
bool stitchTiles) {
SkPaint paint;
- paint.setShader(SkPerlinNoiseShader::MakeFractalNoise(baseFrequencyX, baseFrequencyY,
- numOctaves, seed,
- stitchTiles ? &fSize : nullptr));
+ paint.setShader(SkShaders::MakeFractalNoise(
+ baseFrequencyX, baseFrequencyY, numOctaves, seed, stitchTiles ? &fSize : nullptr));
for (int i = 0; i < loops; i++) {
this->drawClippedRect(canvas, x, y, paint);
}
diff --git a/docs/examples/skpaint_compose.cpp b/docs/examples/skpaint_compose.cpp
index 6f624c4..31f9052 100644
--- a/docs/examples/skpaint_compose.cpp
+++ b/docs/examples/skpaint_compose.cpp
@@ -5,11 +5,16 @@
void draw(SkCanvas* canvas) {
SkColor colors[2] = {SK_ColorBLUE, SK_ColorYELLOW};
SkPaint paint;
- paint.setShader(SkShaders::Blend(
- SkBlendMode::kDifference,
- SkGradientShader::MakeRadial(SkPoint::Make(128.0f, 128.0f), 180.0f, colors, nullptr, 2,
- SkTileMode::kClamp, 0, nullptr),
- SkPerlinNoiseShader::MakeTurbulence(0.025f, 0.025f, 2, 0.0f, nullptr)));
+ paint.setShader(SkShaders::Blend(SkBlendMode::kDifference,
+ SkGradientShader::MakeRadial(SkPoint::Make(128.0f, 128.0f),
+ 180.0f,
+ colors,
+ nullptr,
+ 2,
+ SkTileMode::kClamp,
+ 0,
+ nullptr),
+ SkShaders::MakeTurbulence(0.025f, 0.025f, 2, 0.0f, nullptr)));
canvas->drawPaint(paint);
}
} // END FIDDLE
diff --git a/docs/examples/skpaint_compose_shader.cpp b/docs/examples/skpaint_compose_shader.cpp
index 671d103..c9f03fa 100644
--- a/docs/examples/skpaint_compose_shader.cpp
+++ b/docs/examples/skpaint_compose_shader.cpp
@@ -6,11 +6,16 @@
void draw(SkCanvas* canvas) {
SkColor colors[2] = {SK_ColorBLUE, SK_ColorYELLOW};
SkPaint paint;
- paint.setShader(SkShaders::Blend(
- SkBlendMode::kDifference,
- SkGradientShader::MakeRadial(SkPoint::Make(128.0f, 128.0f), 180.0f, colors, nullptr,
- 2, SkTileMode::kClamp, 0, nullptr),
- SkPerlinNoiseShader::MakeTurbulence(0.025f, 0.025f, 2, 0.0f, nullptr)));
+ paint.setShader(SkShaders::Blend(SkBlendMode::kDifference,
+ SkGradientShader::MakeRadial(SkPoint::Make(128.0f, 128.0f),
+ 180.0f,
+ colors,
+ nullptr,
+ 2,
+ SkTileMode::kClamp,
+ 0,
+ nullptr),
+ SkShaders::MakeTurbulence(0.025f, 0.025f, 2, 0.0f, nullptr)));
canvas->drawPaint(paint);
}
} // END FIDDLE
diff --git a/docs/examples/skpaint_perlin.cpp b/docs/examples/skpaint_perlin.cpp
index e390841..08991d4 100644
--- a/docs/examples/skpaint_perlin.cpp
+++ b/docs/examples/skpaint_perlin.cpp
@@ -5,7 +5,7 @@
void draw(SkCanvas* canvas) {
canvas->clear(SK_ColorWHITE);
SkPaint paint;
- paint.setShader(SkPerlinNoiseShader::MakeFractalNoise(0.05f, 0.05f, 4, 0.0f, nullptr));
+ paint.setShader(SkShaders::MakeFractalNoise(0.05f, 0.05f, 4, 0.0f, nullptr));
canvas->drawPaint(paint);
}
} // END FIDDLE
diff --git a/docs/examples/skpaint_turb.cpp b/docs/examples/skpaint_turb.cpp
index 9557959..b652c0c 100644
--- a/docs/examples/skpaint_turb.cpp
+++ b/docs/examples/skpaint_turb.cpp
@@ -5,7 +5,7 @@
void draw(SkCanvas* canvas) {
canvas->clear(SK_ColorWHITE);
SkPaint paint;
- paint.setShader(SkPerlinNoiseShader::MakeTurbulence(0.05f, 0.05f, 4, 0.0f, nullptr));
+ paint.setShader(SkShaders::MakeTurbulence(0.05f, 0.05f, 4, 0.0f, nullptr));
canvas->drawPaint(paint);
}
} // END FIDDLE
diff --git a/fuzz/FuzzCanvas.cpp b/fuzz/FuzzCanvas.cpp
index 7f28e15..0e9d794 100644
--- a/fuzz/FuzzCanvas.cpp
+++ b/fuzz/FuzzCanvas.cpp
@@ -328,13 +328,17 @@
}
fuzz->nextRange(&numOctaves, 2, 7);
if (turbulence) {
- return SkPerlinNoiseShader::MakeTurbulence(baseFrequencyX, baseFrequencyY,
- numOctaves, seed,
- useTileSize ? &tileSize : nullptr);
+ return SkShaders::MakeTurbulence(baseFrequencyX,
+ baseFrequencyY,
+ numOctaves,
+ seed,
+ useTileSize ? &tileSize : nullptr);
} else {
- return SkPerlinNoiseShader::MakeFractalNoise(baseFrequencyX, baseFrequencyY,
- numOctaves, seed,
- useTileSize ? &tileSize : nullptr);
+ return SkShaders::MakeFractalNoise(baseFrequencyX,
+ baseFrequencyY,
+ numOctaves,
+ seed,
+ useTileSize ? &tileSize : nullptr);
}
}
default:
diff --git a/gm/composecolorfilter.cpp b/gm/composecolorfilter.cpp
index ca5c574..2a47ae0 100644
--- a/gm/composecolorfilter.cpp
+++ b/gm/composecolorfilter.cpp
@@ -108,7 +108,7 @@
// ways (direct and via ::Compose). This ensures the use (or non-use in this case) of the source
// image is the same across both means of composition.
auto cf = MakeTintColorFilter(0xff300000, 0xffa00000, /*useSkSL=*/false);
- auto shader = SkPerlinNoiseShader::MakeTurbulence(0.01f, 0.01f, 2, 0.f);
+ auto shader = SkShaders::MakeTurbulence(0.01f, 0.01f, 2, 0.f);
auto shaderIF = SkImageFilters::Shader(shader, SkImageFilters::Dither::kNo);
auto directCompose = SkImageFilters::ColorFilter(cf, shaderIF);
diff --git a/gm/constcolorprocessor.cpp b/gm/constcolorprocessor.cpp
index 32c10ac..1aeb0be 100644
--- a/gm/constcolorprocessor.cpp
+++ b/gm/constcolorprocessor.cpp
@@ -32,6 +32,7 @@
#include "src/gpu/ganesh/GrColor.h"
#include "src/gpu/ganesh/GrFPArgs.h"
#include "src/gpu/ganesh/GrFragmentProcessor.h"
+#include "src/gpu/ganesh/GrFragmentProcessors.h"
#include "src/gpu/ganesh/GrPaint.h"
#include "src/gpu/ganesh/SkGr.h"
#include "src/gpu/ganesh/SurfaceDrawContext.h"
@@ -115,7 +116,7 @@
GrColorInfo colorInfo;
SkSurfaceProps props;
GrFPArgs args(rContext, &colorInfo, props);
- baseFP = as_SB(fShader)->asRootFragmentProcessor(args, SkMatrix::I());
+ baseFP = GrFragmentProcessors::Make(fShader.get(), args, SkMatrix::I());
} else {
baseFP = GrFragmentProcessor::MakeColor(
SkPMColor4f::FromBytes_RGBA(kPaintColors[paintType]));
diff --git a/gm/imagefiltersclipped.cpp b/gm/imagefiltersclipped.cpp
index 8a8140d..8ef27a3 100644
--- a/gm/imagefiltersclipped.cpp
+++ b/gm/imagefiltersclipped.cpp
@@ -136,8 +136,8 @@
}
canvas->restore();
- sk_sp<SkImageFilter> rectFilter(SkImageFilters::Shader(
- SkPerlinNoiseShader::MakeFractalNoise(0.1f, 0.05f, 1, 0)));
+ sk_sp<SkImageFilter> rectFilter(
+ SkImageFilters::Shader(SkShaders::MakeFractalNoise(0.1f, 0.05f, 1, 0)));
canvas->translate(std::size(filters)*(r.width() + margin), 0);
for (int xOffset = 0; xOffset < 80; xOffset += 16) {
bounds.fLeft = SkIntToScalar(xOffset);
diff --git a/gm/imagefiltersscaled.cpp b/gm/imagefiltersscaled.cpp
index df40f19..ad8247d 100644
--- a/gm/imagefiltersscaled.cpp
+++ b/gm/imagefiltersscaled.cpp
@@ -97,19 +97,28 @@
resizeMatrix.setScale(RESIZE_FACTOR, RESIZE_FACTOR);
sk_sp<SkImageFilter> filters[] = {
- SkImageFilters::Blur(SkIntToScalar(4), SkIntToScalar(4), nullptr),
- SkImageFilters::DropShadow(5, 10, 3, 3, SK_ColorYELLOW, nullptr),
- SkImageFilters::DisplacementMap(SkColorChannel::kR, SkColorChannel::kR, 12,
- std::move(gradient), checkerboard),
- SkImageFilters::Dilate(1, 1, checkerboard),
- SkImageFilters::Erode(1, 1, checkerboard),
- SkImageFilters::Offset(SkIntToScalar(32), 0, nullptr),
- SkImageFilters::MatrixTransform(resizeMatrix, SkSamplingOptions(), nullptr),
- SkImageFilters::Shader(SkPerlinNoiseShader::MakeFractalNoise(
- SkDoubleToScalar(0.1), SkDoubleToScalar(0.05), 1, 0)),
- SkImageFilters::PointLitDiffuse(pointLocation, white, surfaceScale, kd, nullptr),
- SkImageFilters::SpotLitDiffuse(spotLocation, spotTarget, spotExponent,
- cutoffAngle, white, surfaceScale, kd, nullptr),
+ SkImageFilters::Blur(SkIntToScalar(4), SkIntToScalar(4), nullptr),
+ SkImageFilters::DropShadow(5, 10, 3, 3, SK_ColorYELLOW, nullptr),
+ SkImageFilters::DisplacementMap(SkColorChannel::kR,
+ SkColorChannel::kR,
+ 12,
+ std::move(gradient),
+ checkerboard),
+ SkImageFilters::Dilate(1, 1, checkerboard),
+ SkImageFilters::Erode(1, 1, checkerboard),
+ SkImageFilters::Offset(SkIntToScalar(32), 0, nullptr),
+ SkImageFilters::MatrixTransform(resizeMatrix, SkSamplingOptions(), nullptr),
+ SkImageFilters::Shader(SkShaders::MakeFractalNoise(
+ SkDoubleToScalar(0.1), SkDoubleToScalar(0.05), 1, 0)),
+ SkImageFilters::PointLitDiffuse(pointLocation, white, surfaceScale, kd, nullptr),
+ SkImageFilters::SpotLitDiffuse(spotLocation,
+ spotTarget,
+ spotExponent,
+ cutoffAngle,
+ white,
+ surfaceScale,
+ kd,
+ nullptr),
};
SkVector scales[] = {
diff --git a/gm/perlinnoise.cpp b/gm/perlinnoise.cpp
index 0b8c48c..18879db 100644
--- a/gm/perlinnoise.cpp
+++ b/gm/perlinnoise.cpp
@@ -35,10 +35,16 @@
bool stitchTiles,
SkISize size) {
return (type == Type::kFractalNoise)
- ? SkPerlinNoiseShader::MakeFractalNoise(baseFrequencyX, baseFrequencyY, numOctaves,
- seed, stitchTiles ? &size : nullptr)
- : SkPerlinNoiseShader::MakeTurbulence(baseFrequencyX, baseFrequencyY, numOctaves,
- seed, stitchTiles ? &size : nullptr);
+ ? SkShaders::MakeFractalNoise(baseFrequencyX,
+ baseFrequencyY,
+ numOctaves,
+ seed,
+ stitchTiles ? &size : nullptr)
+ : SkShaders::MakeTurbulence(baseFrequencyX,
+ baseFrequencyY,
+ numOctaves,
+ seed,
+ stitchTiles ? &size : nullptr);
}
class PerlinNoiseGM : public skiagm::GM {
diff --git a/gn/core.gni b/gn/core.gni
index 89edcc9..de72d62 100644
--- a/gn/core.gni
+++ b/gn/core.gni
@@ -144,6 +144,8 @@
# //src/opts:private_hdrs
# //src/shaders:shader_hdrs
# //src/shaders:shader_srcs
+# //src/shaders:sksl_hdrs
+# //src/shaders:sksl_srcs
# //src/text:text_hdrs
# //src/text:text_srcs
skia_core_sources = [
@@ -627,22 +629,32 @@
"$_src/opts/SkVM_opts.h",
"$_src/shaders/SkBitmapProcShader.cpp",
"$_src/shaders/SkBitmapProcShader.h",
+ "$_src/shaders/SkBlendShader.cpp",
+ "$_src/shaders/SkBlendShader.h",
"$_src/shaders/SkColorFilterShader.cpp",
"$_src/shaders/SkColorFilterShader.h",
"$_src/shaders/SkColorShader.cpp",
- "$_src/shaders/SkComposeShader.cpp",
+ "$_src/shaders/SkColorShader.h",
"$_src/shaders/SkCoordClampShader.cpp",
+ "$_src/shaders/SkCoordClampShader.h",
"$_src/shaders/SkEmptyShader.cpp",
+ "$_src/shaders/SkEmptyShader.h",
"$_src/shaders/SkGainmapShader.cpp",
"$_src/shaders/SkImageShader.cpp",
"$_src/shaders/SkImageShader.h",
"$_src/shaders/SkLocalMatrixShader.cpp",
"$_src/shaders/SkLocalMatrixShader.h",
- "$_src/shaders/SkPerlinNoiseShader.cpp",
+ "$_src/shaders/SkPerlinNoiseShaderImpl.cpp",
+ "$_src/shaders/SkPerlinNoiseShaderImpl.h",
+ "$_src/shaders/SkRuntimeShader.cpp",
+ "$_src/shaders/SkRuntimeShader.h",
"$_src/shaders/SkShader.cpp",
+ "$_src/shaders/SkShaderBase.cpp",
"$_src/shaders/SkShaderBase.h",
"$_src/shaders/SkTransformShader.cpp",
"$_src/shaders/SkTransformShader.h",
+ "$_src/shaders/SkTriColorShader.cpp",
+ "$_src/shaders/SkTriColorShader.h",
"$_src/text/GlyphRun.cpp",
"$_src/text/GlyphRun.h",
"$_src/text/StrikeForGPU.cpp",
diff --git a/gn/effects.gni b/gn/effects.gni
index ea41a34..5236594 100644
--- a/gn/effects.gni
+++ b/gn/effects.gni
@@ -65,14 +65,16 @@
"$_src/effects/SkTableMaskFilter.cpp",
"$_src/effects/SkTrimPE.h",
"$_src/effects/SkTrimPathEffect.cpp",
- "$_src/shaders/gradients/SkGradientShader.cpp",
- "$_src/shaders/gradients/SkGradientShaderBase.cpp",
- "$_src/shaders/gradients/SkGradientShaderBase.h",
+ "$_src/shaders/gradients/SkConicalGradient.cpp",
+ "$_src/shaders/gradients/SkConicalGradient.h",
+ "$_src/shaders/gradients/SkGradientBaseShader.cpp",
+ "$_src/shaders/gradients/SkGradientBaseShader.h",
"$_src/shaders/gradients/SkLinearGradient.cpp",
"$_src/shaders/gradients/SkLinearGradient.h",
"$_src/shaders/gradients/SkRadialGradient.cpp",
+ "$_src/shaders/gradients/SkRadialGradient.h",
"$_src/shaders/gradients/SkSweepGradient.cpp",
- "$_src/shaders/gradients/SkTwoPointConicalGradient.cpp",
+ "$_src/shaders/gradients/SkSweepGradient.h",
]
# List generated by Bazel rules:
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 5dd77b8..74bda32 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -386,6 +386,8 @@
"$_src/gpu/ganesh/effects/GrModulateAtlasCoverageEffect.h",
"$_src/gpu/ganesh/effects/GrOvalEffect.cpp",
"$_src/gpu/ganesh/effects/GrOvalEffect.h",
+ "$_src/gpu/ganesh/effects/GrPerlinNoise2Effect.cpp",
+ "$_src/gpu/ganesh/effects/GrPerlinNoise2Effect.h",
"$_src/gpu/ganesh/effects/GrPorterDuffXferProcessor.cpp",
"$_src/gpu/ganesh/effects/GrPorterDuffXferProcessor.h",
"$_src/gpu/ganesh/effects/GrRRectEffect.cpp",
diff --git a/include/effects/SkPerlinNoiseShader.h b/include/effects/SkPerlinNoiseShader.h
index 2fd20d7..7ef2f1f 100644
--- a/include/effects/SkPerlinNoiseShader.h
+++ b/include/effects/SkPerlinNoiseShader.h
@@ -50,38 +50,4 @@
const SkISize* tileSize = nullptr);
} // namespace SkShaders
-// TODO(kjlubick) Remove this class when clients have been migrated
-class SK_API SkPerlinNoiseShader {
-public:
- /**
- * This will construct Perlin noise of the given type (Fractal Noise or Turbulence).
- *
- * Both base frequencies (X and Y) have a usual range of (0..1) and must be non-negative.
- *
- * The number of octaves provided should be fairly small, with a limit of 255 enforced.
- * Each octave doubles the frequency, so 10 octaves would produce noise from
- * baseFrequency * 1, * 2, * 4, ..., * 512, which quickly yields insignificantly small
- * periods and resembles regular unstructured noise rather than Perlin noise.
- *
- * If tileSize isn't NULL or an empty size, the tileSize parameter will be used to modify
- * the frequencies so that the noise will be tileable for the given tile size. If tileSize
- * is NULL or an empty size, the frequencies will be used as is without modification.
- */
- static sk_sp<SkShader> MakeFractalNoise(SkScalar baseFrequencyX, SkScalar baseFrequencyY,
- int numOctaves, SkScalar seed,
- const SkISize* tileSize = nullptr) {
- return SkShaders::MakeFractalNoise(baseFrequencyX, baseFrequencyY,
- numOctaves, seed, tileSize);
- }
- static sk_sp<SkShader> MakeTurbulence(SkScalar baseFrequencyX, SkScalar baseFrequencyY,
- int numOctaves, SkScalar seed,
- const SkISize* tileSize = nullptr) {
- return SkShaders::MakeTurbulence(baseFrequencyX, baseFrequencyY,
- numOctaves, seed, tileSize);
- }
-
-private:
- SkPerlinNoiseShader() = delete;
-};
-
#endif
diff --git a/include/effects/SkRuntimeEffect.h b/include/effects/SkRuntimeEffect.h
index 41918ed..291a70f 100644
--- a/include/effects/SkRuntimeEffect.h
+++ b/include/effects/SkRuntimeEffect.h
@@ -324,7 +324,7 @@
friend class GrGLSLSkSLFP; //
#endif
- friend class SkRTShader; // fBaseProgram, fMain, fSampleUsages, getRPProgram()
+ friend class SkRuntimeShader; // fBaseProgram, fMain, fSampleUsages, getRPProgram()
friend class SkRuntimeBlender; //
friend class SkRuntimeColorFilter; //
diff --git a/include/private/SkGainmapShader.h b/include/private/SkGainmapShader.h
index f490ab9..bc53112 100644
--- a/include/private/SkGainmapShader.h
+++ b/include/private/SkGainmapShader.h
@@ -9,6 +9,7 @@
#define SkGainmapShader_DEFINED
#include "include/core/SkRefCnt.h"
+#include "include/private/base/SkAPI.h"
class SkColorSpace;
class SkShader;
diff --git a/modules/canvaskit/canvaskit_bindings.cpp b/modules/canvaskit/canvaskit_bindings.cpp
index a34b548..69ba375 100644
--- a/modules/canvaskit/canvaskit_bindings.cpp
+++ b/modules/canvaskit/canvaskit_bindings.cpp
@@ -1935,8 +1935,7 @@
int tileW, int tileH)->sk_sp<SkShader> {
// if tileSize is empty (e.g. tileW <= 0 or tileH <= 0, it will be ignored.
SkISize tileSize = SkISize::Make(tileW, tileH);
- return SkPerlinNoiseShader::MakeFractalNoise(baseFreqX, baseFreqY,
- numOctaves, seed, &tileSize);
+ return SkShaders::MakeFractalNoise(baseFreqX, baseFreqY, numOctaves, seed, &tileSize);
}))
// Here and in other gradient functions, cPtr is a pointer to an array of data
// representing colors. whether this is an array of SkColor or SkColor4f is indicated
@@ -2015,8 +2014,7 @@
int tileW, int tileH)->sk_sp<SkShader> {
// if tileSize is empty (e.g. tileW <= 0 or tileH <= 0, it will be ignored.
SkISize tileSize = SkISize::Make(tileW, tileH);
- return SkPerlinNoiseShader::MakeTurbulence(baseFreqX, baseFreqY,
- numOctaves, seed, &tileSize);
+ return SkShaders::MakeTurbulence(baseFreqX, baseFreqY, numOctaves, seed, &tileSize);
}))
.class_function("_MakeTwoPointConicalGradient", optional_override([](
WASMPointerF32 fourFloatsPtr,
@@ -2037,11 +2035,17 @@
colors, colorSpace, positions, count, mode,
flags, &localMatrix);
} else if (colorType == SkColorType::kRGBA_8888_SkColorType) {
- const SkColor* colors = reinterpret_cast<const SkColor*>(cPtr);
- return SkGradientShader::MakeTwoPointConical(startAndEnd[0], startRadius,
- startAndEnd[1], endRadius,
- colors, positions, count, mode,
- flags, &localMatrix);
+ const SkColor* colors = reinterpret_cast<const SkColor*>(cPtr);
+ return SkGradientShader::MakeTwoPointConical(startAndEnd[0],
+ startRadius,
+ startAndEnd[1],
+ endRadius,
+ colors,
+ positions,
+ count,
+ mode,
+ flags,
+ &localMatrix);
}
SkDebugf("%d is not an accepted colorType\n", colorType);
return nullptr;
diff --git a/modules/svg/src/SkSVGFeTurbulence.cpp b/modules/svg/src/SkSVGFeTurbulence.cpp
index 885ef4a..8357e6e 100644
--- a/modules/svg/src/SkSVGFeTurbulence.cpp
+++ b/modules/svg/src/SkSVGFeTurbulence.cpp
@@ -65,11 +65,11 @@
sk_sp<SkShader> shader;
switch (fTurbulenceType.fType) {
case SkSVGFeTurbulenceType::Type::kTurbulence:
- shader = SkPerlinNoiseShader::MakeTurbulence(
+ shader = SkShaders::MakeTurbulence(
fBaseFrequency.freqX(), fBaseFrequency.freqY(), fNumOctaves, fSeed, tileSize);
break;
case SkSVGFeTurbulenceType::Type::kFractalNoise:
- shader = SkPerlinNoiseShader::MakeFractalNoise(
+ shader = SkShaders::MakeFractalNoise(
fBaseFrequency.freqX(), fBaseFrequency.freqY(), fNumOctaves, fSeed, tileSize);
break;
}
diff --git a/public.bzl b/public.bzl
index 3a2873e..3fdfef4 100644
--- a/public.bzl
+++ b/public.bzl
@@ -374,7 +374,7 @@
"src/core/SkBitmapCache.h",
"src/core/SkBitmapDevice.cpp",
"src/core/SkBitmapDevice.h",
- "src/core/SkBitmapProcState.h",
+ "src/core/SkBitmapProcState.h", # needed for src/opts/SkBitmapProcState_opts.h
"src/core/SkBlendMode.cpp",
"src/core/SkBlendModeBlender.cpp",
"src/core/SkBlendModeBlender.h",
@@ -1076,6 +1076,8 @@
"src/gpu/ganesh/effects/GrModulateAtlasCoverageEffect.h",
"src/gpu/ganesh/effects/GrOvalEffect.cpp",
"src/gpu/ganesh/effects/GrOvalEffect.h",
+ "src/gpu/ganesh/effects/GrPerlinNoise2Effect.cpp",
+ "src/gpu/ganesh/effects/GrPerlinNoise2Effect.h",
"src/gpu/ganesh/effects/GrPorterDuffXferProcessor.cpp",
"src/gpu/ganesh/effects/GrPorterDuffXferProcessor.h",
"src/gpu/ganesh/effects/GrRRectEffect.cpp",
@@ -1426,33 +1428,44 @@
"src/sfnt/SkPanose.h",
"src/sfnt/SkSFNTHeader.h",
"src/sfnt/SkTTCFHeader.h",
- "src/shaders/SkBitmapProcShader.h",
+ "src/shaders/SkBlendShader.cpp",
+ "src/shaders/SkBlendShader.h",
"src/shaders/SkColorFilterShader.cpp",
"src/shaders/SkColorFilterShader.h",
"src/shaders/SkColorShader.cpp",
- "src/shaders/SkComposeShader.cpp",
+ "src/shaders/SkColorShader.h",
"src/shaders/SkCoordClampShader.cpp",
+ "src/shaders/SkCoordClampShader.h",
"src/shaders/SkEmptyShader.cpp",
+ "src/shaders/SkEmptyShader.h",
"src/shaders/SkGainmapShader.cpp",
"src/shaders/SkImageShader.cpp",
"src/shaders/SkImageShader.h",
"src/shaders/SkLocalMatrixShader.cpp",
"src/shaders/SkLocalMatrixShader.h",
- "src/shaders/SkPerlinNoiseShader.cpp",
+ "src/shaders/SkPerlinNoiseShaderImpl.cpp",
+ "src/shaders/SkPerlinNoiseShaderImpl.h",
"src/shaders/SkPictureShader.cpp",
"src/shaders/SkPictureShader.h",
- "src/shaders/SkShader.cpp",
+ "src/shaders/SkRuntimeShader.cpp",
+ "src/shaders/SkRuntimeShader.h",
+ "src/shaders/SkShaderBase.cpp",
"src/shaders/SkShaderBase.h",
+ "src/shaders/SkShader.cpp",
"src/shaders/SkTransformShader.cpp",
"src/shaders/SkTransformShader.h",
- "src/shaders/gradients/SkGradientShader.cpp",
- "src/shaders/gradients/SkGradientShaderBase.cpp",
- "src/shaders/gradients/SkGradientShaderBase.h",
+ "src/shaders/SkTriColorShader.cpp",
+ "src/shaders/SkTriColorShader.h",
+ "src/shaders/gradients/SkConicalGradient.cpp",
+ "src/shaders/gradients/SkConicalGradient.h",
+ "src/shaders/gradients/SkGradientBaseShader.cpp",
+ "src/shaders/gradients/SkGradientBaseShader.h",
"src/shaders/gradients/SkLinearGradient.cpp",
"src/shaders/gradients/SkLinearGradient.h",
"src/shaders/gradients/SkRadialGradient.cpp",
+ "src/shaders/gradients/SkRadialGradient.h",
"src/shaders/gradients/SkSweepGradient.cpp",
- "src/shaders/gradients/SkTwoPointConicalGradient.cpp",
+ "src/shaders/gradients/SkSweepGradient.h",
"src/sksl/GLSL.std.450.h",
"src/sksl/SkSLAnalysis.cpp",
"src/sksl/SkSLAnalysis.h",
diff --git a/src/BUILD.bazel b/src/BUILD.bazel
index 4ead2a0..c196107 100644
--- a/src/BUILD.bazel
+++ b/src/BUILD.bazel
@@ -159,6 +159,7 @@
"src/codec/SkPixmapUtilsPriv.h",
"src/core/SkAdvancedTypefaceMetrics.h",
"src/core/SkColorSpacePriv.h",
+ "src/core/SkCoreBlitters.h",
"src/core/SkDrawProcs.h",
"src/core/SkMatrixPriv.h",
"src/core/SkPathPriv.h",
diff --git a/src/android/SkAndroidFrameworkUtils.cpp b/src/android/SkAndroidFrameworkUtils.cpp
index 39b941a..a70c8f7 100644
--- a/src/android/SkAndroidFrameworkUtils.cpp
+++ b/src/android/SkAndroidFrameworkUtils.cpp
@@ -8,6 +8,7 @@
#include "include/android/SkAndroidFrameworkUtils.h"
#include "include/core/SkCanvas.h"
#include "include/utils/SkPaintFilterCanvas.h"
+#include "src/base/SkTLazy.h"
#include "src/core/SkDevice.h"
#include "src/image/SkSurface_Base.h"
#include "src/shaders/SkShaderBase.h"
diff --git a/src/core/SkCoreBlitters.h b/src/core/SkCoreBlitters.h
index 17f3c70..f00e76e 100644
--- a/src/core/SkCoreBlitters.h
+++ b/src/core/SkCoreBlitters.h
@@ -8,14 +8,25 @@
#ifndef SkCoreBlitters_DEFINED
#define SkCoreBlitters_DEFINED
+#include "include/core/SkColor.h"
#include "include/core/SkPaint.h"
+#include "include/core/SkPixmap.h"
+#include "include/core/SkRefCnt.h"
+#include "include/private/base/SkAssert.h"
+#include "include/private/base/SkCPUTypes.h"
#include "src/core/SkBlitRow.h"
#include "src/core/SkBlitter.h"
-#include "src/core/SkBlitter_A8.h"
-#include "src/shaders/SkBitmapProcShader.h"
#include "src/shaders/SkShaderBase.h"
+#include <cstdint>
+
+class SkArenaAlloc;
+class SkMatrix;
+class SkRasterPipeline;
+class SkShader;
class SkSurfaceProps;
+struct SkIRect;
+struct SkMask;
class SkRasterBlitter : public SkBlitter {
public:
diff --git a/src/core/SkDraw_atlas.cpp b/src/core/SkDraw_atlas.cpp
index 4d82a46..0fd201b 100644
--- a/src/core/SkDraw_atlas.cpp
+++ b/src/core/SkDraw_atlas.cpp
@@ -36,6 +36,7 @@
#include "src/core/SkScan.h"
#include "src/core/SkSurfacePriv.h"
#include "src/core/SkVMBlitter.h"
+#include "src/shaders/SkColorShader.h"
#include "src/shaders/SkShaderBase.h"
#include "src/shaders/SkTransformShader.h"
@@ -44,7 +45,6 @@
#include <utility>
class SkBlitter;
-class SkColorSpace;
enum class SkBlendMode;
#if defined(SK_ENABLE_SKVM)
@@ -77,48 +77,6 @@
ctx->rgba[3] = SkScalarRoundToInt(rgba[3]*255); ctx->a = rgba[3];
}
-class UpdatableColorShader : public SkShaderBase {
-public:
- explicit UpdatableColorShader(SkColorSpace* cs)
- : fSteps{sk_srgb_singleton(), kUnpremul_SkAlphaType, cs, kUnpremul_SkAlphaType} {}
-#if defined(SK_ENABLE_SKVM)
- skvm::Color program(skvm::Builder* builder,
- skvm::Coord device,
- skvm::Coord local,
- skvm::Color paint,
- const MatrixRec&,
- const SkColorInfo& dst,
- skvm::Uniforms* uniforms,
- SkArenaAlloc* alloc) const override {
- skvm::Uniform color = uniforms->pushPtr(fValues);
- skvm::F32 r = builder->arrayF(color, 0);
- skvm::F32 g = builder->arrayF(color, 1);
- skvm::F32 b = builder->arrayF(color, 2);
- skvm::F32 a = builder->arrayF(color, 3);
-
- return {r, g, b, a};
- }
-#endif
-
- void updateColor(SkColor c) const {
- SkColor4f c4 = SkColor4f::FromColor(c);
- fSteps.apply(c4.vec());
- auto cp4 = c4.premul();
- fValues[0] = cp4.fR;
- fValues[1] = cp4.fG;
- fValues[2] = cp4.fB;
- fValues[3] = cp4.fA;
- }
-
-private:
- // For serialization. This will never be called.
- Factory getFactory() const override { return nullptr; }
- const char* getTypeName() const override { return nullptr; }
-
- SkColorSpaceXformSteps fSteps;
- mutable float fValues[4];
-};
-
void SkDraw::drawAtlas(const SkRSXform xform[],
const SkRect textures[],
const SkColor colors[],
@@ -203,10 +161,10 @@
};
if (!rpblit()) {
- UpdatableColorShader* colorShader = nullptr;
+ SkUpdatableColorShader* colorShader = nullptr;
sk_sp<SkShader> shader;
if (colors) {
- colorShader = alloc.make<UpdatableColorShader>(fDst.colorSpace());
+ colorShader = alloc.make<SkUpdatableColorShader>(fDst.colorSpace());
shader = SkShaders::Blend(std::move(blender),
sk_ref_sp(colorShader),
sk_ref_sp(transformShader));
diff --git a/src/core/SkDraw_vertices.cpp b/src/core/SkDraw_vertices.cpp
index 4c7b036..4e48f65 100644
--- a/src/core/SkDraw_vertices.cpp
+++ b/src/core/SkDraw_vertices.cpp
@@ -29,16 +29,12 @@
#include "include/private/base/SkTo.h"
#include "src/base/SkArenaAlloc.h"
#include "src/base/SkTLazy.h"
-#include "src/base/SkVx.h"
#include "src/core/SkBlenderBase.h"
#include "src/core/SkConvertPixels.h"
#include "src/core/SkCoreBlitters.h"
#include "src/core/SkDraw.h"
-#include "src/core/SkEffectPriv.h"
#include "src/core/SkMatrixProvider.h"
#include "src/core/SkRasterClip.h"
-#include "src/core/SkRasterPipeline.h"
-#include "src/core/SkRasterPipelineOpList.h"
#include "src/core/SkScan.h"
#include "src/core/SkSurfacePriv.h"
#include "src/core/SkVMBlitter.h"
@@ -46,6 +42,7 @@
#include "src/core/SkVerticesPriv.h"
#include "src/shaders/SkShaderBase.h"
#include "src/shaders/SkTransformShader.h"
+#include "src/shaders/SkTriColorShader.h"
#include <cstddef>
#include <cstdint>
@@ -58,41 +55,6 @@
#include "src/core/SkVM.h"
#endif
-struct Matrix43 {
- float fMat[12]; // column major
-
- skvx::float4 map(float x, float y) const {
- return skvx::float4::Load(&fMat[0]) * x +
- skvx::float4::Load(&fMat[4]) * y +
- skvx::float4::Load(&fMat[8]);
- }
-
- // Pass a by value, so we don't have to worry about aliasing with this
- void setConcat(const Matrix43 a, const SkMatrix& b) {
- SkASSERT(!b.hasPerspective());
-
- fMat[ 0] = a.dot(0, b.getScaleX(), b.getSkewY());
- fMat[ 1] = a.dot(1, b.getScaleX(), b.getSkewY());
- fMat[ 2] = a.dot(2, b.getScaleX(), b.getSkewY());
- fMat[ 3] = a.dot(3, b.getScaleX(), b.getSkewY());
-
- fMat[ 4] = a.dot(0, b.getSkewX(), b.getScaleY());
- fMat[ 5] = a.dot(1, b.getSkewX(), b.getScaleY());
- fMat[ 6] = a.dot(2, b.getSkewX(), b.getScaleY());
- fMat[ 7] = a.dot(3, b.getSkewX(), b.getScaleY());
-
- fMat[ 8] = a.dot(0, b.getTranslateX(), b.getTranslateY()) + a.fMat[ 8];
- fMat[ 9] = a.dot(1, b.getTranslateX(), b.getTranslateY()) + a.fMat[ 9];
- fMat[10] = a.dot(2, b.getTranslateX(), b.getTranslateY()) + a.fMat[10];
- fMat[11] = a.dot(3, b.getTranslateX(), b.getTranslateY()) + a.fMat[11];
- }
-
-private:
- float dot(int index, float x, float y) const {
- return fMat[index + 0] * x + fMat[index + 4] * y;
- }
-};
-
static bool SK_WARN_UNUSED_RESULT
texture_to_matrix(const VertState& state, const SkPoint verts[], const SkPoint texs[],
SkMatrix* matrix) {
@@ -107,129 +69,6 @@
return matrix->setPolyToPoly(src, dst, 3);
}
-class SkTriColorShader : public SkShaderBase {
-public:
- SkTriColorShader(bool isOpaque, bool usePersp) : fIsOpaque(isOpaque), fUsePersp(usePersp) {}
-
- // This gets called for each triangle, without re-calling appendStages.
- bool update(const SkMatrix& ctmInv, const SkPoint pts[], const SkPMColor4f colors[],
- int index0, int index1, int index2);
-
-protected:
- bool appendStages(const SkStageRec& rec, const MatrixRec&) const override {
- rec.fPipeline->append(SkRasterPipelineOp::seed_shader);
- if (fUsePersp) {
- rec.fPipeline->append(SkRasterPipelineOp::matrix_perspective, &fM33);
- }
- rec.fPipeline->append(SkRasterPipelineOp::matrix_4x3, &fM43);
- return true;
- }
-
-#if defined(SK_ENABLE_SKVM)
- skvm::Color program(skvm::Builder*,
- skvm::Coord,
- skvm::Coord,
- skvm::Color,
- const MatrixRec&,
- const SkColorInfo&,
- skvm::Uniforms*,
- SkArenaAlloc*) const override;
-#endif
-
-private:
- bool isOpaque() const override { return fIsOpaque; }
- // For serialization. This will never be called.
- Factory getFactory() const override { return nullptr; }
- const char* getTypeName() const override { return nullptr; }
-
- // If fUsePersp, we need both of these matrices,
- // otherwise we can combine them, and only use fM43
-
- Matrix43 fM43;
- SkMatrix fM33;
- const bool fIsOpaque;
- const bool fUsePersp; // controls our stages, and what we do in update()
-#if defined(SK_ENABLE_SKVM)
- mutable skvm::Uniform fColorMatrix;
- mutable skvm::Uniform fCoordMatrix;
-#endif
-
- using INHERITED = SkShaderBase;
-};
-
-#if defined(SK_ENABLE_SKVM)
-skvm::Color SkTriColorShader::program(skvm::Builder* b,
- skvm::Coord device,
- skvm::Coord local,
- skvm::Color,
- const MatrixRec&,
- const SkColorInfo&,
- skvm::Uniforms* uniforms,
- SkArenaAlloc* alloc) const {
- fColorMatrix = uniforms->pushPtr(&fM43);
-
- skvm::F32 x = local.x,
- y = local.y;
-
- if (fUsePersp) {
- fCoordMatrix = uniforms->pushPtr(&fM33);
- auto dot = [&, x, y](int row) {
- return b->mad(x, b->arrayF(fCoordMatrix, row),
- b->mad(y, b->arrayF(fCoordMatrix, row + 3),
- b->arrayF(fCoordMatrix, row + 6)));
- };
-
- x = dot(0);
- y = dot(1);
- x = x * (1.0f / dot(2));
- y = y * (1.0f / dot(2));
- }
-
- auto colorDot = [&, x, y](int row) {
- return b->mad(x, b->arrayF(fColorMatrix, row),
- b->mad(y, b->arrayF(fColorMatrix, row + 4),
- b->arrayF(fColorMatrix, row + 8)));
- };
-
- skvm::Color color;
- color.r = colorDot(0);
- color.g = colorDot(1);
- color.b = colorDot(2);
- color.a = colorDot(3);
- return color;
-}
-#endif
-
-bool SkTriColorShader::update(const SkMatrix& ctmInv, const SkPoint pts[],
- const SkPMColor4f colors[], int index0, int index1, int index2) {
- SkMatrix m, im;
- m.reset();
- m.set(0, pts[index1].fX - pts[index0].fX);
- m.set(1, pts[index2].fX - pts[index0].fX);
- m.set(2, pts[index0].fX);
- m.set(3, pts[index1].fY - pts[index0].fY);
- m.set(4, pts[index2].fY - pts[index0].fY);
- m.set(5, pts[index0].fY);
- if (!m.invert(&im)) {
- return false;
- }
-
- fM33.setConcat(im, ctmInv);
-
- auto c0 = skvx::float4::Load(colors[index0].vec()),
- c1 = skvx::float4::Load(colors[index1].vec()),
- c2 = skvx::float4::Load(colors[index2].vec());
-
- (c1 - c0).store(&fM43.fMat[0]);
- (c2 - c0).store(&fM43.fMat[4]);
- c0.store(&fM43.fMat[8]);
-
- if (!fUsePersp) {
- fM43.setConcat(fM43, fM33);
- }
- return true;
-}
-
// Convert the SkColors into float colors. The conversion depends on some conditions:
// - If the pixmap has a dst colorspace, we have to be "color-correct".
// Do we map into dst-colorspace before or after we interpolate?
diff --git a/src/core/SkRasterPipelineBlitter.cpp b/src/core/SkRasterPipelineBlitter.cpp
index a0b55ac..cb8d32b 100644
--- a/src/core/SkRasterPipelineBlitter.cpp
+++ b/src/core/SkRasterPipelineBlitter.cpp
@@ -17,6 +17,8 @@
#include "src/core/SkBlitter.h"
#include "src/core/SkColorSpacePriv.h"
#include "src/core/SkColorSpaceXformSteps.h"
+#include "src/core/SkEffectPriv.h"
+#include "src/core/SkMask.h"
#include "src/core/SkMatrixProvider.h"
#include "src/core/SkOpts.h"
#include "src/core/SkRasterPipeline.h"
diff --git a/src/core/SkRuntimeBlender.cpp b/src/core/SkRuntimeBlender.cpp
index e2a3912..c4c0850 100644
--- a/src/core/SkRuntimeBlender.cpp
+++ b/src/core/SkRuntimeBlender.cpp
@@ -81,7 +81,7 @@
/*alwaysCopyIntoAlloc=*/false,
rec.fDstCS,
rec.fAlloc);
- SkShaderBase::MatrixRec matrix(SkMatrix::I());
+ SkShaders::MatrixRec matrix(SkMatrix::I());
matrix.markCTMApplied();
RuntimeEffectRPCallbacks callbacks(rec, matrix, fChildren, fEffect->fSampleUsages);
bool success = program->appendStages(rec.fPipeline, rec.fAlloc, &callbacks, uniforms);
@@ -110,7 +110,7 @@
colorInfo.colorSpace());
SkASSERT(inputs);
- SkShaderBase::MatrixRec mRec(SkMatrix::I());
+ SkShaders::MatrixRec mRec(SkMatrix::I());
mRec.markTotalMatrixInvalid();
RuntimeEffectVMCallbacks callbacks(p, uniforms, alloc, fChildren, mRec, src, colorInfo);
std::vector<skvm::Val> uniform = SkRuntimeEffectPriv::MakeSkVMUniforms(p,
diff --git a/src/core/SkRuntimeEffect.cpp b/src/core/SkRuntimeEffect.cpp
index d409c64..fb611b1 100644
--- a/src/core/SkRuntimeEffect.cpp
+++ b/src/core/SkRuntimeEffect.cpp
@@ -25,7 +25,6 @@
#include "include/private/base/SkTArray.h"
#include "src/base/SkArenaAlloc.h"
#include "src/base/SkNoDestructor.h"
-#include "src/base/SkTLazy.h"
#include "src/core/SkBlenderBase.h"
#include "src/core/SkChecksum.h"
#include "src/core/SkColorSpacePriv.h"
@@ -33,7 +32,6 @@
#include "src/core/SkEffectPriv.h"
#include "src/core/SkFilterColorProgram.h"
#include "src/core/SkLRUCache.h"
-#include "src/core/SkPicturePriv.h"
#include "src/core/SkRasterPipeline.h"
#include "src/core/SkRasterPipelineOpList.h"
#include "src/core/SkReadBuffer.h"
@@ -44,6 +42,7 @@
#include "src/effects/colorfilters/SkColorFilterBase.h"
#include "src/effects/colorfilters/SkRuntimeColorFilter.h"
#include "src/shaders/SkLocalMatrixShader.h"
+#include "src/shaders/SkRuntimeShader.h"
#include "src/shaders/SkShaderBase.h"
#include "src/sksl/SkSLAnalysis.h"
#include "src/sksl/SkSLBuiltinTypes.h"
@@ -66,7 +65,6 @@
#include "src/sksl/tracing/SkSLDebugTracePriv.h"
#include <algorithm>
-#include <tuple>
class SkColorSpace;
struct SkIPoint;
@@ -77,10 +75,6 @@
#include "include/gpu/GrTypes.h"
#include "include/gpu/ganesh/SkSurfaceGanesh.h"
#include "src/gpu/ganesh/GrCaps.h"
-#include "src/gpu/ganesh/GrColorInfo.h"
-#include "src/gpu/ganesh/GrFPArgs.h"
-#include "src/gpu/ganesh/GrFragmentProcessor.h"
-#include "src/gpu/ganesh/GrFragmentProcessors.h"
#include "src/gpu/ganesh/GrRecordingContextPriv.h"
#endif
@@ -293,7 +287,7 @@
return as_SB(shader)->appendStages(fStage, fMatrix);
}
// For a non-passthrough sample, we need to explicitly mark the total-matrix as invalid.
- SkShaderBase::MatrixRec nonPassthroughMatrix = fMatrix;
+ SkShaders::MatrixRec nonPassthroughMatrix = fMatrix;
nonPassthroughMatrix.markTotalMatrixInvalid();
return as_SB(shader)->appendStages(fStage, nonPassthroughMatrix);
}
@@ -384,14 +378,6 @@
SkASSERT(flattenable_is_valid_as_child(fChild.get()));
}
-static sk_sp<SkSL::DebugTracePriv> make_debug_trace(SkRuntimeEffect* effect,
- const SkIPoint& coord) {
- auto debugTrace = sk_make_sp<SkSL::DebugTracePriv>();
- debugTrace->setSource(effect->source());
- debugTrace->setTraceCoord(coord);
- return debugTrace;
-}
-
static ChildType child_type(const SkSL::Type& type) {
switch (type.typeKind()) {
case SkSL::Type::TypeKind::kBlender: return ChildType::kBlender;
@@ -947,248 +933,6 @@
#endif // defined(SK_ENABLE_SKVM)
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-using UniformsCallback = SkRuntimeEffectPriv::UniformsCallback;
-
-class SkRTShader : public SkShaderBase {
-public:
- SkRTShader(sk_sp<SkRuntimeEffect> effect,
- sk_sp<SkSL::DebugTracePriv> debugTrace,
- sk_sp<const SkData> uniforms,
- SkSpan<SkRuntimeEffect::ChildPtr> children)
- : fEffect(std::move(effect))
- , fDebugTrace(std::move(debugTrace))
- , fUniformData(std::move(uniforms))
- , fChildren(children.begin(), children.end()) {}
-
- SkRTShader(sk_sp<SkRuntimeEffect> effect,
- sk_sp<SkSL::DebugTracePriv> debugTrace,
- UniformsCallback uniformsCallback,
- SkSpan<SkRuntimeEffect::ChildPtr> children)
- : fEffect(std::move(effect))
- , fDebugTrace(std::move(debugTrace))
- , fUniformsCallback(std::move(uniformsCallback))
- , fChildren(children.begin(), children.end()) {}
-
- SkRuntimeEffect::TracedShader makeTracedClone(const SkIPoint& coord) {
- sk_sp<SkRuntimeEffect> unoptimized = fEffect->makeUnoptimizedClone();
- sk_sp<SkSL::DebugTracePriv> debugTrace = make_debug_trace(unoptimized.get(), coord);
- auto debugShader = sk_make_sp<SkRTShader>(
- unoptimized, debugTrace, this->uniformData(nullptr), SkSpan(fChildren));
-
- return SkRuntimeEffect::TracedShader{std::move(debugShader), std::move(debugTrace)};
- }
-
- bool isOpaque() const override { return fEffect->alwaysOpaque(); }
-
-#if defined(SK_GANESH)
- std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs& args,
- const MatrixRec& mRec) const override {
- if (!SkRuntimeEffectPriv::CanDraw(args.fContext->priv().caps(), fEffect.get())) {
- return nullptr;
- }
-
- sk_sp<const SkData> uniforms = SkRuntimeEffectPriv::TransformUniforms(
- fEffect->uniforms(),
- this->uniformData(args.fDstColorInfo->colorSpace()),
- args.fDstColorInfo->colorSpace());
- SkASSERT(uniforms);
-
- bool success;
- std::unique_ptr<GrFragmentProcessor> fp;
- std::tie(success, fp) = GrFragmentProcessors::make_effect_fp(fEffect,
- "runtime_shader",
- std::move(uniforms),
- /*inputFP=*/nullptr,
- /*destColorFP=*/nullptr,
- SkSpan(fChildren),
- args);
- if (!success) {
- return nullptr;
- }
-
- std::tie(success, fp) = mRec.apply(std::move(fp));
- if (!success) {
- return nullptr;
- }
- return fp;
- }
-#endif
-
-#if defined(SK_GRAPHITE)
- void addToKey(const skgpu::graphite::KeyContext& keyContext,
- skgpu::graphite::PaintParamsKeyBuilder* builder,
- skgpu::graphite::PipelineDataGatherer* gatherer) const override {
- using namespace skgpu::graphite;
-
- sk_sp<const SkData> uniforms = SkRuntimeEffectPriv::TransformUniforms(
- fEffect->uniforms(),
- this->uniformData(keyContext.dstColorInfo().colorSpace()),
- keyContext.dstColorInfo().colorSpace());
- SkASSERT(uniforms);
-
- RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer,
- { fEffect, std::move(uniforms) });
-
- SkRuntimeEffectPriv::AddChildrenToKey(fChildren, fEffect->children(), keyContext, builder,
- gatherer);
-
- builder->endBlock();
- }
-#endif
-
- bool appendStages(const SkStageRec& rec, const MatrixRec& mRec) const override {
-#ifdef SK_ENABLE_SKSL_IN_RASTER_PIPELINE
- if (!SkRuntimeEffectPriv::CanDraw(SkCapabilities::RasterBackend().get(), fEffect.get())) {
- // SkRP has support for many parts of #version 300 already, but for now, we restrict its
- // usage in runtime effects to just #version 100.
- return false;
- }
- if (const SkSL::RP::Program* program = fEffect->getRPProgram(fDebugTrace.get())) {
- std::optional<MatrixRec> newMRec = mRec.apply(rec);
- if (!newMRec.has_value()) {
- return false;
- }
- SkSpan<const float> uniforms = SkRuntimeEffectPriv::UniformsAsSpan(
- fEffect->uniforms(),
- this->uniformData(rec.fDstCS),
- /*alwaysCopyIntoAlloc=*/fUniformData == nullptr,
- rec.fDstCS,
- rec.fAlloc);
- RuntimeEffectRPCallbacks callbacks(rec, *newMRec, fChildren, fEffect->fSampleUsages);
- bool success = program->appendStages(rec.fPipeline, rec.fAlloc, &callbacks, uniforms);
- return success;
- }
-#endif
- return false;
- }
-
-#if defined(SK_ENABLE_SKVM)
- skvm::Color program(skvm::Builder* p,
- skvm::Coord device,
- skvm::Coord local,
- skvm::Color paint,
- const MatrixRec& mRec,
- const SkColorInfo& colorInfo,
- skvm::Uniforms* uniforms,
- SkArenaAlloc* alloc) const override {
- if (!SkRuntimeEffectPriv::CanDraw(SkCapabilities::RasterBackend().get(), fEffect.get())) {
- return {};
- }
-
- sk_sp<const SkData> inputs =
- SkRuntimeEffectPriv::TransformUniforms(fEffect->uniforms(),
- this->uniformData(colorInfo.colorSpace()),
- colorInfo.colorSpace());
- SkASSERT(inputs);
-
- // Ensure any pending transform is applied before running the runtime shader's code, which
- // gets to use and manipulate the coordinates.
- std::optional<MatrixRec> newMRec = mRec.apply(p, &local, uniforms);
- if (!newMRec.has_value()) {
- return {};
- }
- // We could omit this for children that are only sampled with passthrough coords.
- newMRec->markTotalMatrixInvalid();
-
- RuntimeEffectVMCallbacks callbacks(p,
- uniforms,
- alloc,
- fChildren,
- *newMRec,
- paint,
- colorInfo);
- std::vector<skvm::Val> uniform = SkRuntimeEffectPriv::MakeSkVMUniforms(
- p, uniforms, fEffect->uniformSize(), *inputs);
-
- return SkSL::ProgramToSkVM(*fEffect->fBaseProgram, fEffect->fMain, p, fDebugTrace.get(),
- SkSpan(uniform), device, local, paint, paint, &callbacks);
- }
-#endif
-
- void flatten(SkWriteBuffer& buffer) const override {
- buffer.writeString(fEffect->source().c_str());
- buffer.writeDataAsByteArray(this->uniformData(nullptr).get());
- SkRuntimeEffectPriv::WriteChildEffects(buffer, fChildren);
- }
-
- SkRuntimeEffect* asRuntimeEffect() const override { return fEffect.get(); }
-
- SK_FLATTENABLE_HOOKS(SkRTShader)
-
-private:
- enum Flags {
- kHasLegacyLocalMatrix_Flag = 1 << 1,
- };
-
- sk_sp<const SkData> uniformData(const SkColorSpace* dstCS) const {
- if (fUniformData) {
- return fUniformData;
- }
-
- // We want to invoke the uniforms-callback each time a paint occurs.
- SkASSERT(fUniformsCallback);
- sk_sp<const SkData> uniforms = fUniformsCallback({dstCS});
- SkASSERT(uniforms && uniforms->size() == fEffect->uniformSize());
- return uniforms;
- }
-
- sk_sp<SkRuntimeEffect> fEffect;
- sk_sp<SkSL::DebugTracePriv> fDebugTrace;
- sk_sp<const SkData> fUniformData;
- UniformsCallback fUniformsCallback;
- std::vector<SkRuntimeEffect::ChildPtr> fChildren;
-};
-
-sk_sp<SkFlattenable> SkRTShader::CreateProc(SkReadBuffer& buffer) {
- if (!buffer.validate(buffer.allowSkSL())) {
- return nullptr;
- }
-
- SkString sksl;
- buffer.readString(&sksl);
- sk_sp<SkData> uniforms = buffer.readByteArrayAsData();
-
- SkTLazy<SkMatrix> localM;
- if (buffer.isVersionLT(SkPicturePriv::kNoShaderLocalMatrix)) {
- uint32_t flags = buffer.read32();
- if (flags & kHasLegacyLocalMatrix_Flag) {
- buffer.readMatrix(localM.init());
- }
- }
-
- auto effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForShader, std::move(sksl));
-#if !SK_LENIENT_SKSL_DESERIALIZATION
- if (!buffer.validate(effect != nullptr)) {
- return nullptr;
- }
-#endif
-
- STArray<4, SkRuntimeEffect::ChildPtr> children;
- if (!SkRuntimeEffectPriv::ReadChildEffects(buffer, effect.get(), &children)) {
- return nullptr;
- }
-
-#if SK_LENIENT_SKSL_DESERIALIZATION
- if (!effect) {
- // If any children were SkShaders, return the first one. This is a reasonable fallback.
- for (int i = 0; i < children.size(); i++) {
- if (children[i].shader()) {
- SkDebugf("Serialized SkSL failed to compile. Replacing shader with child %d.\n", i);
- return sk_ref_sp(children[i].shader());
- }
- }
-
- // We don't know what to do, so just return nullptr (but *don't* poison the buffer).
- SkDebugf("Serialized SkSL failed to compile. Ignoring/dropping SkSL shader.\n");
- return nullptr;
- }
-#endif
-
- return effect->makeShader(std::move(uniforms), SkSpan(children), localM.getMaybeNull());
-}
-
sk_sp<SkShader> SkRuntimeEffectPriv::MakeDeferredShader(const SkRuntimeEffect* effect,
UniformsCallback uniformsCallback,
SkSpan<SkRuntimeEffect::ChildPtr> children,
@@ -1202,11 +946,11 @@
if (!uniformsCallback) {
return nullptr;
}
- return SkLocalMatrixShader::MakeWrapped<SkRTShader>(localMatrix,
- sk_ref_sp(effect),
- /*debugTrace=*/nullptr,
- std::move(uniformsCallback),
- children);
+ return SkLocalMatrixShader::MakeWrapped<SkRuntimeShader>(localMatrix,
+ sk_ref_sp(effect),
+ /*debugTrace=*/nullptr,
+ std::move(uniformsCallback),
+ children);
}
sk_sp<SkShader> SkRuntimeEffect::makeShader(sk_sp<const SkData> uniforms,
@@ -1235,11 +979,11 @@
if (uniforms->size() != this->uniformSize()) {
return nullptr;
}
- return SkLocalMatrixShader::MakeWrapped<SkRTShader>(localMatrix,
- sk_ref_sp(this),
- /*debugTrace=*/nullptr,
- std::move(uniforms),
- children);
+ return SkLocalMatrixShader::MakeWrapped<SkRuntimeShader>(localMatrix,
+ sk_ref_sp(this),
+ /*debugTrace=*/nullptr,
+ std::move(uniforms),
+ children);
}
sk_sp<SkImage> SkRuntimeEffect::makeImage(GrRecordingContext* rContext,
@@ -1340,8 +1084,8 @@
if (!effect) {
return TracedShader{nullptr, nullptr};
}
- // An SkShader with an attached SkRuntimeEffect must be an SkRTShader.
- SkRTShader* rtShader = static_cast<SkRTShader*>(shader.get());
+ // An SkShader with an attached SkRuntimeEffect must be an SkRuntimeShader.
+ SkRuntimeShader* rtShader = static_cast<SkRuntimeShader*>(shader.get());
return rtShader->makeTracedClone(traceCoord);
}
@@ -1384,9 +1128,12 @@
///////////////////////////////////////////////////////////////////////////////////////////////////
void SkRuntimeEffect::RegisterFlattenables() {
- SK_REGISTER_FLATTENABLE(SkRuntimeColorFilter);
- SK_REGISTER_FLATTENABLE(SkRTShader);
SK_REGISTER_FLATTENABLE(SkRuntimeBlender);
+ SK_REGISTER_FLATTENABLE(SkRuntimeColorFilter);
+ SK_REGISTER_FLATTENABLE(SkRuntimeShader);
+
+ // Previous name
+ SkFlattenable::Register("SkRTShader", SkRuntimeShader::CreateProc);
}
SkRuntimeShaderBuilder::SkRuntimeShaderBuilder(sk_sp<SkRuntimeEffect> effect)
diff --git a/src/core/SkRuntimeEffectPriv.h b/src/core/SkRuntimeEffectPriv.h
index 47d3c4f..16c968c 100644
--- a/src/core/SkRuntimeEffectPriv.h
+++ b/src/core/SkRuntimeEffectPriv.h
@@ -15,7 +15,6 @@
#include "include/private/base/SkAssert.h"
#include "include/private/base/SkSpan_impl.h"
#include "include/private/base/SkTArray.h"
-#include "src/shaders/SkShaderBase.h"
#include <cstddef>
#include <cstdint>
@@ -31,26 +30,38 @@
#endif
#ifdef SK_ENABLE_SKVM
+#include "include/core/SkImageInfo.h"
#include "src/sksl/codegen/SkSLVMCodeGenerator.h"
#endif
class SkArenaAlloc;
+class SkCapabilities;
class SkColorSpace;
class SkData;
class SkMatrix;
class SkReadBuffer;
class SkShader;
class SkWriteBuffer;
+struct SkColorSpaceXformSteps;
struct SkStageRec;
+namespace SkShaders {
+class MatrixRec;
+}
+
namespace SkSL {
class Context;
class Variable;
struct Program;
}
-class SkCapabilities;
-struct SkColorSpaceXformSteps;
+#if defined(SK_GRAPHITE)
+namespace skgpu::graphite {
+class KeyContext;
+class PaintParamsKeyBuilder;
+class PipelineDataGatherer;
+} // namespace skgpu::graphite
+#endif
class SkRuntimeEffectPriv {
public:
@@ -190,7 +201,7 @@
class RuntimeEffectRPCallbacks : public SkSL::RP::Callbacks {
public:
RuntimeEffectRPCallbacks(const SkStageRec& s,
- const SkShaderBase::MatrixRec& m,
+ const SkShaders::MatrixRec& m,
SkSpan<const SkRuntimeEffect::ChildPtr> c,
SkSpan<const SkSL::SampleUsage> u)
: fStage(s), fMatrix(m), fChildren(c), fSampleUsages(u) {}
@@ -209,7 +220,7 @@
void applyColorSpaceXform(const SkColorSpaceXformSteps& tempXform, const void* color);
const SkStageRec& fStage;
- const SkShaderBase::MatrixRec& fMatrix;
+ const SkShaders::MatrixRec& fMatrix;
SkSpan<const SkRuntimeEffect::ChildPtr> fChildren;
SkSpan<const SkSL::SampleUsage> fSampleUsages;
};
@@ -222,7 +233,7 @@
skvm::Uniforms* uniforms,
SkArenaAlloc* alloc,
const std::vector<SkRuntimeEffect::ChildPtr>& children,
- const SkShaderBase::MatrixRec& mRec,
+ const SkShaders::MatrixRec& mRec,
skvm::Color inColor,
const SkColorInfo& colorInfo)
: fBuilder(builder)
@@ -247,7 +258,7 @@
skvm::Uniforms* fUniforms;
SkArenaAlloc* fAlloc;
const std::vector<SkRuntimeEffect::ChildPtr>& fChildren;
- const SkShaderBase::MatrixRec& fMRec;
+ const SkShaders::MatrixRec& fMRec;
const skvm::Color fInColor;
const SkColorInfo& fColorInfo;
};
diff --git a/src/core/SkTextBlob.cpp b/src/core/SkTextBlob.cpp
index b1dadfd..fd3d29d 100644
--- a/src/core/SkTextBlob.cpp
+++ b/src/core/SkTextBlob.cpp
@@ -10,6 +10,7 @@
#include "include/core/SkRSXform.h"
#include "include/core/SkTypeface.h"
#include "src/base/SkSafeMath.h"
+#include "src/base/SkTLazy.h"
#include "src/core/SkFontPriv.h"
#include "src/core/SkPaintPriv.h"
#include "src/core/SkReadBuffer.h"
diff --git a/src/core/SkTextBlobTrace.cpp b/src/core/SkTextBlobTrace.cpp
index 0e90c57..cb51825 100644
--- a/src/core/SkTextBlobTrace.cpp
+++ b/src/core/SkTextBlobTrace.cpp
@@ -4,6 +4,7 @@
#include "src/core/SkTextBlobTrace.h"
#include "include/core/SkTextBlob.h"
+#include "src/base/SkTLazy.h"
#include "src/core/SkFontPriv.h"
#include "src/core/SkPtrRecorder.h"
#include "src/core/SkReadBuffer.h"
diff --git a/src/core/SkVMBlitter.cpp b/src/core/SkVMBlitter.cpp
index a8ffda5..4121fe8 100644
--- a/src/core/SkVMBlitter.cpp
+++ b/src/core/SkVMBlitter.cpp
@@ -5,6 +5,9 @@
* found in the LICENSE file.
*/
+#include "src/core/SkVMBlitter.h"
+
+#include "include/core/SkBlender.h"
#include "include/private/base/SkMacros.h"
#include "src/base/SkArenaAlloc.h"
#include "src/core/SkBlendModePriv.h"
@@ -15,12 +18,13 @@
#include "src/core/SkCoreBlitters.h"
#include "src/core/SkImageInfoPriv.h"
#include "src/core/SkLRUCache.h"
+#include "src/core/SkMask.h"
#include "src/core/SkMatrixProvider.h"
#include "src/core/SkPaintPriv.h"
#include "src/core/SkVM.h"
-#include "src/core/SkVMBlitter.h"
#include "src/effects/colorfilters/SkColorFilterBase.h"
#include "src/shaders/SkColorFilterShader.h"
+#include "src/shaders/SkEmptyShader.h"
#include <cinttypes>
@@ -62,7 +66,7 @@
const char* getTypeName() const override { return "NoopColorFilter"; }
};
- struct SpriteShader : public SkShaderBase {
+ struct SpriteShader : public SkEmptyShader {
explicit SpriteShader(SkPixmap sprite) : fSprite(sprite) {}
SkPixmap fSprite;
@@ -77,7 +81,7 @@
skvm::Coord /*device*/,
skvm::Coord /*local*/,
skvm::Color /*paint*/,
- const MatrixRec&,
+ const SkShaders::MatrixRec&,
const SkColorInfo& dst,
skvm::Uniforms* uniforms,
SkArenaAlloc*) const override {
@@ -91,7 +95,7 @@
}
};
- struct DitherShader : public SkShaderBase {
+ struct DitherShader : public SkEmptyShader {
explicit DitherShader(sk_sp<SkShader> shader) : fShader(std::move(shader)) {}
sk_sp<SkShader> fShader;
@@ -106,7 +110,7 @@
skvm::Coord device,
skvm::Coord local,
skvm::Color paint,
- const MatrixRec& mRec,
+ const SkShaders::MatrixRec& mRec,
const SkColorInfo& dst,
skvm::Uniforms* uniforms,
SkArenaAlloc* alloc) const override {
diff --git a/src/core/SkVMBlitter.h b/src/core/SkVMBlitter.h
index 9cf0eb7..c1cf7ff 100644
--- a/src/core/SkVMBlitter.h
+++ b/src/core/SkVMBlitter.h
@@ -8,6 +8,8 @@
#ifndef SkVMBlitter_DEFINED
#define SkVMBlitter_DEFINED
+#include "include/core/SkBlender.h"
+#include "include/core/SkMatrix.h"
#include "include/core/SkPixmap.h"
#include "src/base/SkArenaAlloc.h"
#include "src/base/SkTLazy.h"
diff --git a/src/effects/colorfilters/SkColorFilterBase.h b/src/effects/colorfilters/SkColorFilterBase.h
index 4f6cc23..998654d 100644
--- a/src/effects/colorfilters/SkColorFilterBase.h
+++ b/src/effects/colorfilters/SkColorFilterBase.h
@@ -47,16 +47,6 @@
M(Table) \
M(WorkingFormat)
-#define SK_ALL_COLOR_FILTERS(M) \
- M(BlendMode) \
- M(ColorSpaceXform) \
- M(Compose) \
- M(Gaussian) \
- M(Matrix) \
- M(Runtime) \
- M(Table) \
- M(WorkingFormat)
-
class SkColorFilterBase : public SkColorFilter {
public:
SK_WARN_UNUSED_RESULT
diff --git a/src/effects/colorfilters/SkRuntimeColorFilter.cpp b/src/effects/colorfilters/SkRuntimeColorFilter.cpp
index 1270e67..c5d9ae9 100644
--- a/src/effects/colorfilters/SkRuntimeColorFilter.cpp
+++ b/src/effects/colorfilters/SkRuntimeColorFilter.cpp
@@ -90,7 +90,7 @@
/*alwaysCopyIntoAlloc=*/false,
rec.fDstCS,
rec.fAlloc);
- SkShaderBase::MatrixRec matrix(SkMatrix::I());
+ SkShaders::MatrixRec matrix(SkMatrix::I());
matrix.markCTMApplied();
RuntimeEffectRPCallbacks callbacks(rec, matrix, fChildren, fEffect->fSampleUsages);
bool success = program->appendStages(rec.fPipeline, rec.fAlloc, &callbacks, uniforms);
@@ -112,7 +112,7 @@
fEffect->uniforms(), fUniforms, colorInfo.colorSpace());
SkASSERT(inputs);
- SkShaderBase::MatrixRec mRec(SkMatrix::I());
+ SkShaders::MatrixRec mRec(SkMatrix::I());
mRec.markTotalMatrixInvalid();
RuntimeEffectVMCallbacks callbacks(p, uniforms, alloc, fChildren, mRec, c, colorInfo);
std::vector<skvm::Val> uniform =
diff --git a/src/gpu/ganesh/ClipStack.cpp b/src/gpu/ganesh/ClipStack.cpp
index 96a279a..f85afa1 100644
--- a/src/gpu/ganesh/ClipStack.cpp
+++ b/src/gpu/ganesh/ClipStack.cpp
@@ -20,6 +20,7 @@
#include "src/gpu/ganesh/GrDirectContextPriv.h"
#include "src/gpu/ganesh/GrFPArgs.h"
#include "src/gpu/ganesh/GrFragmentProcessor.h"
+#include "src/gpu/ganesh/GrFragmentProcessors.h"
#include "src/gpu/ganesh/GrProxyProvider.h"
#include "src/gpu/ganesh/GrRecordingContextPriv.h"
#include "src/gpu/ganesh/GrSWMaskHelper.h"
@@ -1301,8 +1302,7 @@
static const GrColorInfo kCoverageColorInfo{GrColorType::kUnknown, kPremul_SkAlphaType,
nullptr};
GrFPArgs args(rContext, &kCoverageColorInfo, sdc->surfaceProps());
- clipFP = as_SB(cs.shader())->asRootFragmentProcessor(args,
- fMatrixProvider->localToDevice());
+ clipFP = GrFragmentProcessors::Make(cs.shader(), args, fMatrixProvider->localToDevice());
if (clipFP) {
// The initial input is the coverage from the geometry processor, so this ensures it
// is multiplied properly with the alpha of the clip shader.
diff --git a/src/gpu/ganesh/Device_drawTexture.cpp b/src/gpu/ganesh/Device_drawTexture.cpp
index daeaadb..db0a759 100644
--- a/src/gpu/ganesh/Device_drawTexture.cpp
+++ b/src/gpu/ganesh/Device_drawTexture.cpp
@@ -361,7 +361,8 @@
std::move(fp), image->imageInfo().colorInfo(), sdc->colorInfo());
if (image->isAlphaOnly()) {
if (const auto* shader = as_SB(paint.getShader())) {
- auto shaderFP = shader->asRootFragmentProcessor(
+ auto shaderFP = GrFragmentProcessors::Make(
+ shader,
GrFPArgs(rContext, &sdc->colorInfo(), sdc->surfaceProps()),
matrixProvider.localToDevice());
if (!shaderFP) {
diff --git a/src/gpu/ganesh/GrFragmentProcessors.cpp b/src/gpu/ganesh/GrFragmentProcessors.cpp
index cd2d5ee..1af581e 100644
--- a/src/gpu/ganesh/GrFragmentProcessors.cpp
+++ b/src/gpu/ganesh/GrFragmentProcessors.cpp
@@ -11,15 +11,27 @@
#include "include/core/SkBlendMode.h"
#include "include/core/SkColor.h"
#include "include/core/SkColorSpace.h"
+#include "include/core/SkColorType.h"
#include "include/core/SkData.h"
+#include "include/core/SkImage.h"
+#include "include/core/SkImageInfo.h"
#include "include/core/SkMatrix.h"
+#include "include/core/SkPicture.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
+#include "include/core/SkSize.h"
#include "include/effects/SkRuntimeEffect.h"
+#include "include/gpu/GpuTypes.h"
#include "include/gpu/GrRecordingContext.h"
+#include "include/gpu/GrTypes.h"
+#include "include/gpu/ganesh/SkSurfaceGanesh.h"
#include "include/private/SkColorData.h"
#include "include/private/base/SkAssert.h"
#include "include/private/base/SkDebug.h"
#include "include/private/base/SkTArray.h"
+#include "include/private/gpu/ganesh/GrTypesPriv.h"
+#include "src/base/SkTLazy.h"
#include "src/core/SkBlendModeBlender.h"
#include "src/core/SkBlenderBase.h"
#include "src/core/SkColorSpacePriv.h"
@@ -37,22 +49,58 @@
#include "src/effects/colorfilters/SkRuntimeColorFilter.h"
#include "src/effects/colorfilters/SkTableColorFilter.h"
#include "src/effects/colorfilters/SkWorkingFormatColorFilter.h"
+#include "src/gpu/ResourceKey.h"
+#include "src/gpu/Swizzle.h"
#include "src/gpu/ganesh/GrCaps.h"
#include "src/gpu/ganesh/GrColorInfo.h"
#include "src/gpu/ganesh/GrColorSpaceXform.h"
#include "src/gpu/ganesh/GrFPArgs.h"
#include "src/gpu/ganesh/GrFragmentProcessor.h"
+#include "src/gpu/ganesh/GrProxyProvider.h"
#include "src/gpu/ganesh/GrRecordingContextPriv.h"
+#include "src/gpu/ganesh/GrSamplerState.h"
+#include "src/gpu/ganesh/GrShaderCaps.h"
+#include "src/gpu/ganesh/GrSurfaceProxy.h"
+#include "src/gpu/ganesh/GrSurfaceProxyView.h"
+#include "src/gpu/ganesh/GrTextureProxy.h"
+#include "src/gpu/ganesh/SkGr.h"
#include "src/gpu/ganesh/effects/GrBlendFragmentProcessor.h"
#include "src/gpu/ganesh/effects/GrColorTableEffect.h"
+#include "src/gpu/ganesh/effects/GrMatrixEffect.h"
+#include "src/gpu/ganesh/effects/GrPerlinNoise2Effect.h"
#include "src/gpu/ganesh/effects/GrSkSLFP.h"
+#include "src/gpu/ganesh/effects/GrTextureEffect.h"
+#include "src/gpu/ganesh/gradients/GrGradientShader.h"
+#include "src/gpu/ganesh/image/GrImageUtils.h"
+#include "src/shaders/SkBlendShader.h"
+#include "src/shaders/SkColorFilterShader.h"
+#include "src/shaders/SkColorShader.h"
+#include "src/shaders/SkCoordClampShader.h"
+#include "src/shaders/SkEmptyShader.h"
+#include "src/shaders/SkImageShader.h"
+#include "src/shaders/SkLocalMatrixShader.h"
+#include "src/shaders/SkPerlinNoiseShaderImpl.h"
+#include "src/shaders/SkPictureShader.h"
+#include "src/shaders/SkRuntimeShader.h"
#include "src/shaders/SkShaderBase.h"
+#include "src/shaders/SkTransformShader.h"
+#include "src/shaders/SkTriColorShader.h"
+#include "src/shaders/gradients/SkConicalGradient.h"
+#include "src/shaders/gradients/SkGradientBaseShader.h"
+#include "src/shaders/gradients/SkLinearGradient.h"
+#include "src/shaders/gradients/SkRadialGradient.h"
+#include "src/shaders/gradients/SkSweepGradient.h"
+#include <cstdint>
+#include <cstring>
#include <memory>
#include <optional>
#include <utility>
#include <vector>
+class SkBitmap;
+enum class SkTileMode;
+
namespace GrFragmentProcessors {
static std::unique_ptr<GrFragmentProcessor>
make_fp_from_shader_mask_filter(const SkMaskFilterBase* maskfilter,
@@ -60,7 +108,7 @@
const SkMatrix& ctm) {
SkASSERT(maskfilter);
auto shaderMF = static_cast<const SkShaderMaskFilterImpl*>(maskfilter);
- auto fp = as_SB(shaderMF->shader())->asFragmentProcessor(args, SkShaderBase::MatrixRec(ctm));
+ auto fp = Make(shaderMF->shader().get(), args, ctm);
return GrFragmentProcessor::MulInputByChildAlpha(std::move(fp));
}
@@ -83,6 +131,23 @@
SkUNREACHABLE;
}
+bool IsSupported(const SkMaskFilter* maskfilter) {
+ if (!maskfilter) {
+ return false;
+ }
+ auto mfb = as_MFB(maskfilter);
+ switch (mfb->type()) {
+ case SkMaskFilterBase::Type::kShader:
+ return true;
+ case SkMaskFilterBase::Type::kBlur:
+ case SkMaskFilterBase::Type::kEmboss:
+ case SkMaskFilterBase::Type::kSDF:
+ case SkMaskFilterBase::Type::kTable:
+ return false;
+ }
+ SkUNREACHABLE;
+}
+
using ChildType = SkRuntimeEffect::ChildType;
GrFPResult make_effect_fp(sk_sp<SkRuntimeEffect> effect,
@@ -97,9 +162,9 @@
std::optional<ChildType> type = child.type();
if (type == ChildType::kShader) {
// Convert a SkShader into a child FP.
- SkShaderBase::MatrixRec mRec(SkMatrix::I());
+ SkShaders::MatrixRec mRec(SkMatrix::I());
mRec.markTotalMatrixInvalid();
- auto childFP = as_SB(child.shader())->asFragmentProcessor(childArgs, mRec);
+ auto childFP = Make(child.shader(), childArgs, mRec);
if (!childFP) {
return GrFPFailure(std::move(inputFP));
}
@@ -418,20 +483,598 @@
SkUNREACHABLE;
}
-bool IsSupported(const SkMaskFilter* maskfilter) {
- if (!maskfilter) {
- return false;
+static std::unique_ptr<GrFragmentProcessor> make_shader_fp(const SkBlendShader* shader,
+ const GrFPArgs& args,
+ const SkShaders::MatrixRec& mRec) {
+ auto fpA = Make(shader->dst().get(), args, mRec);
+ auto fpB = Make(shader->src().get(), args, mRec);
+ if (!fpA || !fpB) {
+ // This is unexpected. Both src and dst shaders should be valid. Just fail.
+ SkDEBUGFAIL("Both src and dst shaders in blend should be valid but are not.");
+ return nullptr;
}
- auto mfb = as_MFB(maskfilter);
- switch (mfb->type()) {
- case SkMaskFilterBase::Type::kShader:
- return true;
- case SkMaskFilterBase::Type::kBlur:
- case SkMaskFilterBase::Type::kEmboss:
- case SkMaskFilterBase::Type::kSDF:
- case SkMaskFilterBase::Type::kTable:
- return false;
+ return GrBlendFragmentProcessor::Make(std::move(fpB), std::move(fpA), shader->mode());
+}
+
+static std::unique_ptr<GrFragmentProcessor> make_shader_fp(const SkColorFilterShader* shader,
+ const GrFPArgs& args,
+ const SkShaders::MatrixRec& mRec) {
+ auto shaderFP = Make(shader->shader().get(), args, mRec);
+ if (!shaderFP) {
+ return nullptr;
+ }
+
+ // TODO I guess, but it shouldn't come up as used today.
+ SkASSERT(shader->alpha() == 1.0f);
+
+ auto [success, fp] = Make(args.fContext,
+ shader->filter().get(),
+ std::move(shaderFP),
+ *args.fDstColorInfo,
+ args.fSurfaceProps);
+ // If the filter FP could not be created, we still want to return the shader FP, so checking
+ // success can be omitted here.
+ return std::move(fp);
+}
+
+static std::unique_ptr<GrFragmentProcessor> make_shader_fp(const SkColorShader* shader,
+ const GrFPArgs& args,
+ const SkShaders::MatrixRec& mRec) {
+ return GrFragmentProcessor::MakeColor(SkColorToPMColor4f(shader->color(), *args.fDstColorInfo));
+}
+
+static std::unique_ptr<GrFragmentProcessor> make_shader_fp(const SkColor4Shader* shader,
+ const GrFPArgs& args,
+ const SkShaders::MatrixRec& mRec) {
+ SkColorSpaceXformSteps steps{shader->colorSpace().get(),
+ kUnpremul_SkAlphaType,
+ args.fDstColorInfo->colorSpace(),
+ kUnpremul_SkAlphaType};
+ SkColor4f color = shader->color();
+ steps.apply(color.vec());
+ return GrFragmentProcessor::MakeColor(color.premul());
+}
+
+static std::unique_ptr<GrFragmentProcessor> make_shader_fp(const SkCoordClampShader* shader,
+ const GrFPArgs& args,
+ const SkShaders::MatrixRec& mRec) {
+ static const SkRuntimeEffect* effect =
+ SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
+ "uniform shader c;"
+ "uniform float4 s;"
+ "half4 main(float2 p) {"
+ "return c.eval(clamp(p, s.LT, s.RB));"
+ "}");
+
+ auto fp = Make(shader->shader().get(), args, mRec.applied());
+ if (!fp) {
+ return nullptr;
+ }
+
+ GrSkSLFP::OptFlags flags = GrSkSLFP::OptFlags::kNone;
+ if (fp->compatibleWithCoverageAsAlpha()) {
+ flags |= GrSkSLFP::OptFlags::kCompatibleWithCoverageAsAlpha;
+ }
+ if (fp->preservesOpaqueInput()) {
+ flags |= GrSkSLFP::OptFlags::kPreservesOpaqueInput;
+ }
+ fp = GrSkSLFP::Make(effect,
+ "clamp_fp",
+ /*inputFP=*/nullptr,
+ flags,
+ "c",
+ std::move(fp),
+ "s",
+ shader->subset());
+
+ auto [total, ok] = mRec.applyForFragmentProcessor({});
+ if (!ok) {
+ return nullptr;
+ }
+ return GrMatrixEffect::Make(total, std::move(fp));
+}
+
+static std::unique_ptr<GrFragmentProcessor> make_shader_fp(const SkCTMShader* shader,
+ const GrFPArgs& args,
+ const SkShaders::MatrixRec& mRec) {
+ SkMatrix ctmInv;
+ if (!shader->ctm().invert(&ctmInv)) {
+ return nullptr;
+ }
+
+ auto base = Make(shader->proxyShader().get(), args, shader->ctm());
+ if (!base) {
+ return nullptr;
+ }
+
+ // In order for the shader to be evaluated with the original CTM, we explicitly evaluate it
+ // at sk_FragCoord, and pass that through the inverse of the original CTM. This avoids requiring
+ // local coords for the shader and mapping from the draw's local to device and then back.
+ return GrFragmentProcessor::DeviceSpace(GrMatrixEffect::Make(ctmInv, std::move(base)));
+}
+
+static std::unique_ptr<GrFragmentProcessor> make_shader_fp(const SkEmptyShader* shader,
+ const GrFPArgs&,
+ const SkShaders::MatrixRec&) {
+ return nullptr;
+}
+
+static bool needs_subset(sk_sp<const SkImage> img, const SkRect& subset) {
+ return subset != SkRect::Make(img->dimensions());
+}
+
+static std::unique_ptr<GrFragmentProcessor> make_shader_fp(const SkImageShader* shader,
+ const GrFPArgs& args,
+ const SkShaders::MatrixRec& mRec) {
+ SkTileMode tileModes[2] = {shader->tileModeX(), shader->tileModeY()};
+ const SkRect shaderSubset = shader->subset();
+ const SkRect* subset = needs_subset(shader->image(), shaderSubset) ? &shaderSubset : nullptr;
+ auto fp = skgpu::ganesh::AsFragmentProcessor(
+ args.fContext, shader->image(), shader->sampling(), tileModes, SkMatrix::I(), subset);
+ if (!fp) {
+ return nullptr;
+ }
+
+ auto [total, ok] = mRec.applyForFragmentProcessor({});
+ if (!ok) {
+ return nullptr;
+ }
+ fp = GrMatrixEffect::Make(total, std::move(fp));
+
+ if (!shader->isRaw()) {
+ fp = GrColorSpaceXformEffect::Make(std::move(fp),
+ shader->image()->colorSpace(),
+ shader->image()->alphaType(),
+ args.fDstColorInfo->colorSpace(),
+ kPremul_SkAlphaType);
+
+ if (shader->image()->isAlphaOnly()) {
+ fp = GrBlendFragmentProcessor::Make<SkBlendMode::kDstIn>(std::move(fp), nullptr);
+ }
+ }
+
+ return fp;
+}
+
+static std::unique_ptr<GrFragmentProcessor> make_shader_fp(const SkLocalMatrixShader* shader,
+ const GrFPArgs& args,
+ const SkShaders::MatrixRec& mRec) {
+ return Make(shader->wrappedShader().get(), args, mRec.concat(shader->localMatrix()));
+}
+
+static std::unique_ptr<GrFragmentProcessor> make_shader_fp(const SkPerlinNoiseShader* shader,
+ const GrFPArgs& args,
+ const SkShaders::MatrixRec& mRec) {
+ SkASSERT(args.fContext);
+ SkASSERT(shader->numOctaves());
+
+ const SkMatrix& totalMatrix = mRec.totalMatrix();
+
+ // Either we don't stitch tiles, or we have a valid tile size
+ SkASSERT(!shader->stitchTiles() || !shader->tileSize().isEmpty());
+
+ auto paintingData = shader->getPaintingData(totalMatrix);
+ paintingData->generateBitmaps();
+
+ // Like shadeSpan, we start from device space. We will account for that below with a device
+ // space effect.
+
+ auto context = args.fContext;
+
+ const SkBitmap& permutationsBitmap = paintingData->getPermutationsBitmap();
+ const SkBitmap& noiseBitmap = paintingData->getNoiseBitmap();
+
+ auto permutationsView = std::get<0>(GrMakeCachedBitmapProxyView(
+ context,
+ permutationsBitmap,
+ /*label=*/"PerlinNoiseShader_FragmentProcessor_PermutationsView"));
+ auto noiseView = std::get<0>(GrMakeCachedBitmapProxyView(
+ context, noiseBitmap, /*label=*/"PerlinNoiseShader_FragmentProcessor_NoiseView"));
+
+ if (permutationsView && noiseView) {
+ return GrFragmentProcessor::DeviceSpace(
+ GrMatrixEffect::Make(SkMatrix::Translate(1 - totalMatrix.getTranslateX(),
+ 1 - totalMatrix.getTranslateY()),
+ GrPerlinNoise2Effect::Make(shader->noiseType(),
+ shader->numOctaves(),
+ shader->stitchTiles(),
+ std::move(paintingData),
+ std::move(permutationsView),
+ std::move(noiseView),
+ *context->priv().caps())));
+ }
+ return nullptr;
+}
+
+static std::unique_ptr<GrFragmentProcessor> make_shader_fp(const SkPictureShader* shader,
+ const GrFPArgs& args,
+ const SkShaders::MatrixRec& mRec) {
+ auto ctx = args.fContext;
+ SkColorType dstColorType = GrColorTypeToSkColorType(args.fDstColorInfo->colorType());
+ if (dstColorType == kUnknown_SkColorType) {
+ dstColorType = kRGBA_8888_SkColorType;
+ }
+ sk_sp<SkColorSpace> dstCS = SkColorSpace::MakeSRGB();
+ if (args.fDstColorInfo->colorSpace()) {
+ dstCS = sk_ref_sp(args.fDstColorInfo->colorSpace());
+ }
+
+ auto info = SkPictureShader::CachedImageInfo::Make(shader->tile(),
+ mRec.totalMatrix(),
+ dstColorType,
+ dstCS.get(),
+ ctx->priv().caps()->maxTextureSize(),
+ args.fSurfaceProps);
+ if (!info.success) {
+ return nullptr;
+ }
+
+ // Gotta be sure the GPU can support our requested colortype (might be FP16)
+ if (!ctx->colorTypeSupportedAsSurface(info.imageInfo.colorType())) {
+ info.imageInfo = info.imageInfo.makeColorType(kRGBA_8888_SkColorType);
+ }
+
+ static const skgpu::UniqueKey::Domain kDomain = skgpu::UniqueKey::GenerateDomain();
+ skgpu::UniqueKey key;
+ std::tuple keyData = {dstCS->toXYZD50Hash(),
+ dstCS->transferFnHash(),
+ static_cast<uint32_t>(dstColorType),
+ shader->picture()->uniqueID(),
+ shader->tile(),
+ info.tileScale,
+ info.props};
+ skgpu::UniqueKey::Builder builder(
+ &key, kDomain, sizeof(keyData) / sizeof(uint32_t), "Picture Shader Image");
+ memcpy(&builder[0], &keyData, sizeof(keyData));
+ builder.finish();
+
+ GrProxyProvider* provider = ctx->priv().proxyProvider();
+ GrSurfaceProxyView view;
+ if (auto proxy = provider->findOrCreateProxyByUniqueKey(key)) {
+ view = GrSurfaceProxyView(proxy, kTopLeft_GrSurfaceOrigin, skgpu::Swizzle());
+ } else {
+ const int msaaSampleCount = 0;
+ const bool createWithMips = false;
+ auto image = info.makeImage(SkSurfaces::RenderTarget(ctx,
+ skgpu::Budgeted::kYes,
+ info.imageInfo,
+ msaaSampleCount,
+ kTopLeft_GrSurfaceOrigin,
+ &info.props,
+ createWithMips),
+ shader->picture().get());
+ if (!image) {
+ return nullptr;
+ }
+
+ auto [v, ct] = skgpu::ganesh::AsView(ctx, image, GrMipmapped::kNo);
+ view = std::move(v);
+ provider->assignUniqueKeyToProxy(key, view.asTextureProxy());
+ }
+
+ const GrSamplerState sampler(static_cast<GrSamplerState::WrapMode>(shader->tileModeX()),
+ static_cast<GrSamplerState::WrapMode>(shader->tileModeY()),
+ shader->filter());
+ auto fp = GrTextureEffect::Make(
+ std::move(view), kPremul_SkAlphaType, SkMatrix::I(), sampler, *ctx->priv().caps());
+ SkMatrix scale = SkMatrix::Scale(info.tileScale.width(), info.tileScale.height());
+ auto [total, ok] = mRec.applyForFragmentProcessor(scale);
+ if (!ok) {
+ return nullptr;
+ }
+ return GrMatrixEffect::Make(total, std::move(fp));
+}
+
+static std::unique_ptr<GrFragmentProcessor> make_shader_fp(const SkRuntimeShader* shader,
+ const GrFPArgs& args,
+ const SkShaders::MatrixRec& mRec) {
+ if (!SkRuntimeEffectPriv::CanDraw(args.fContext->priv().caps(), shader->asRuntimeEffect())) {
+ return nullptr;
+ }
+
+ sk_sp<const SkData> uniforms = SkRuntimeEffectPriv::TransformUniforms(
+ shader->asRuntimeEffect()->uniforms(),
+ shader->uniformData(args.fDstColorInfo->colorSpace()),
+ args.fDstColorInfo->colorSpace());
+ SkASSERT(uniforms);
+
+ auto children = shader->children();
+ bool success;
+ std::unique_ptr<GrFragmentProcessor> fp;
+ std::tie(success, fp) = make_effect_fp(shader->effect(),
+ "runtime_shader",
+ std::move(uniforms),
+ /*inputFP=*/nullptr,
+ /*destColorFP=*/nullptr,
+ SkSpan(children),
+ args);
+ if (!success) {
+ return nullptr;
+ }
+
+ auto [total, ok] = mRec.applyForFragmentProcessor({});
+ if (!ok) {
+ return nullptr;
+ }
+ return GrMatrixEffect::Make(total, std::move(fp));
+}
+
+static std::unique_ptr<GrFragmentProcessor> make_shader_fp(const SkTransformShader* shader,
+ const GrFPArgs&,
+ const SkShaders::MatrixRec&) {
+ return nullptr;
+}
+
+static std::unique_ptr<GrFragmentProcessor> make_shader_fp(const SkTriColorShader* shader,
+ const GrFPArgs&,
+ const SkShaders::MatrixRec&) {
+ return nullptr;
+}
+
+static std::unique_ptr<GrFragmentProcessor> make_shader_fp(const SkUpdatableColorShader* shader,
+ const GrFPArgs&,
+ const SkShaders::MatrixRec&) {
+ return nullptr;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+static std::unique_ptr<GrFragmentProcessor> make_gradient_fp(const SkConicalGradient* shader,
+ const GrFPArgs& args,
+ const SkShaders::MatrixRec& mRec) {
+ // The 2 point conical gradient can reject a pixel so it does change opacity even if the input
+ // was opaque. Thus, all of these layout FPs disable that optimization.
+ std::unique_ptr<GrFragmentProcessor> fp;
+ SkTLazy<SkMatrix> matrix;
+ switch (shader->getType()) {
+ case SkConicalGradient::Type::kStrip: {
+ static const SkRuntimeEffect* kEffect =
+ SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
+ "uniform half r0_2;"
+ "half4 main(float2 p) {"
+ // validation flag, set to negative to discard fragment later.
+ "half v = 1;"
+ "float t = r0_2 - p.y * p.y;"
+ "if (t >= 0) {"
+ "t = p.x + sqrt(t);"
+ "} else {"
+ "v = -1;"
+ "}"
+ "return half4(half(t), v, 0, 0);"
+ "}"
+ );
+ float r0 = shader->getStartRadius() / shader->getCenterX1();
+ fp = GrSkSLFP::Make(kEffect,
+ "TwoPointConicalStripLayout",
+ /*inputFP=*/nullptr,
+ GrSkSLFP::OptFlags::kNone,
+ "r0_2",
+ r0 * r0);
+ } break;
+
+ case SkConicalGradient::Type::kRadial: {
+ static const SkRuntimeEffect* kEffect =
+ SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
+ "uniform half r0;"
+ "uniform half lengthScale;"
+ "half4 main(float2 p) {"
+ // validation flag, set to negative to discard fragment later
+ "half v = 1;"
+ "float t = length(p) * lengthScale - r0;"
+ "return half4(half(t), v, 0, 0);"
+ "}"
+ );
+ float dr = shader->getDiffRadius();
+ float r0 = shader->getStartRadius() / dr;
+ bool isRadiusIncreasing = dr >= 0;
+ fp = GrSkSLFP::Make(kEffect,
+ "TwoPointConicalRadialLayout",
+ /*inputFP=*/nullptr,
+ GrSkSLFP::OptFlags::kNone,
+ "r0",
+ r0,
+ "lengthScale",
+ isRadiusIncreasing ? 1.0f : -1.0f);
+
+ // GPU radial matrix is different from the original matrix, since we map the diff radius
+ // to have |dr| = 1, so manually compute the final gradient matrix here.
+
+ // Map center to (0, 0)
+ matrix.set(SkMatrix::Translate(-shader->getStartCenter().fX,
+ -shader->getStartCenter().fY));
+ // scale |diffRadius| to 1
+ matrix->postScale(1 / dr, 1 / dr);
+ } break;
+
+ case SkConicalGradient::Type::kFocal: {
+ static const SkRuntimeEffect* kEffect =
+ SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
+ // Optimization flags, all specialized:
+ "uniform int isRadiusIncreasing;"
+ "uniform int isFocalOnCircle;"
+ "uniform int isWellBehaved;"
+ "uniform int isSwapped;"
+ "uniform int isNativelyFocal;"
+
+ "uniform half invR1;" // 1/r1
+ "uniform half fx;" // focalX = r0/(r0-r1)
+
+ "half4 main(float2 p) {"
+ "float t = -1;"
+ "half v = 1;" // validation flag,set to negative to discard fragment later
+
+ "float x_t = -1;"
+ "if (bool(isFocalOnCircle)) {"
+ "x_t = dot(p, p) / p.x;"
+ "} else if (bool(isWellBehaved)) {"
+ "x_t = length(p) - p.x * invR1;"
+ "} else {"
+ "float temp = p.x * p.x - p.y * p.y;"
+
+ // Only do sqrt if temp >= 0; this is significantly slower than
+ // checking temp >= 0 in the if statement that checks r(t) >= 0.
+ // But GPU may break if we sqrt a negative float. (Although I
+ // haven't observed that on any devices so far, and the old
+ // approach also does sqrt negative value without a check.) If
+ // the performance is really critical, maybe we should just
+ // compute the area where temp and x_t are always valid and drop
+ // all these ifs.
+ "if (temp >= 0) {"
+ "if (bool(isSwapped) || !bool(isRadiusIncreasing)) {"
+ "x_t = -sqrt(temp) - p.x * invR1;"
+ "} else {"
+ "x_t = sqrt(temp) - p.x * invR1;"
+ "}"
+ "}"
+ "}"
+
+ // The final calculation of t from x_t has lots of static
+ // optimizations but only do them when x_t is positive (which
+ // can be assumed true if isWellBehaved is true)
+ "if (!bool(isWellBehaved)) {"
+ // This will still calculate t even though it will be ignored
+ // later in the pipeline to avoid a branch
+ "if (x_t <= 0.0) {"
+ "v = -1;"
+ "}"
+ "}"
+ "if (bool(isRadiusIncreasing)) {"
+ "if (bool(isNativelyFocal)) {"
+ "t = x_t;"
+ "} else {"
+ "t = x_t + fx;"
+ "}"
+ "} else {"
+ "if (bool(isNativelyFocal)) {"
+ "t = -x_t;"
+ "} else {"
+ "t = -x_t + fx;"
+ "}"
+ "}"
+
+ "if (bool(isSwapped)) {"
+ "t = 1 - t;"
+ "}"
+
+ "return half4(half(t), v, 0, 0);"
+ "}"
+ );
+
+ const SkConicalGradient::FocalData& focalData = shader->getFocalData();
+ bool isRadiusIncreasing = (1 - focalData.fFocalX) > 0,
+ isFocalOnCircle = focalData.isFocalOnCircle(),
+ isWellBehaved = focalData.isWellBehaved(), isSwapped = focalData.isSwapped(),
+ isNativelyFocal = focalData.isNativelyFocal();
+
+ fp = GrSkSLFP::Make(kEffect, "TwoPointConicalFocalLayout", /*inputFP=*/nullptr,
+ GrSkSLFP::OptFlags::kNone,
+ "isRadiusIncreasing", GrSkSLFP::Specialize<int>(isRadiusIncreasing),
+ "isFocalOnCircle", GrSkSLFP::Specialize<int>(isFocalOnCircle),
+ "isWellBehaved", GrSkSLFP::Specialize<int>(isWellBehaved),
+ "isSwapped", GrSkSLFP::Specialize<int>(isSwapped),
+ "isNativelyFocal", GrSkSLFP::Specialize<int>(isNativelyFocal),
+ "invR1", 1.0f / focalData.fR1,
+ "fx", focalData.fFocalX);
+ } break;
+ }
+ return GrGradientShader::MakeGradientFP(
+ *shader, args, mRec, std::move(fp), matrix.getMaybeNull());
+}
+
+static std::unique_ptr<GrFragmentProcessor> make_gradient_fp(const SkLinearGradient* shader,
+ const GrFPArgs& args,
+ const SkShaders::MatrixRec& mRec) {
+ return GrGradientShader::MakeLinear(*shader, args, mRec);
+}
+
+static std::unique_ptr<GrFragmentProcessor> make_gradient_fp(const SkRadialGradient* shader,
+ const GrFPArgs& args,
+ const SkShaders::MatrixRec& mRec) {
+ static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(
+ SkRuntimeEffect::MakeForShader,
+ "half4 main(float2 coord) {"
+ "return half4(half(length(coord)), 1, 0, 0);" // y = 1 for always valid
+ "}");
+ // The radial gradient never rejects a pixel so it doesn't change opacity
+ auto fp = GrSkSLFP::Make(
+ effect, "RadialLayout", /*inputFP=*/nullptr, GrSkSLFP::OptFlags::kPreservesOpaqueInput);
+ return GrGradientShader::MakeGradientFP(*shader, args, mRec, std::move(fp));
+}
+
+static std::unique_ptr<GrFragmentProcessor> make_gradient_fp(const SkSweepGradient* shader,
+ const GrFPArgs& args,
+ const SkShaders::MatrixRec& mRec) {
+ // On some devices they incorrectly implement atan2(y,x) as atan(y/x). In actuality it is
+ // atan2(y,x) = 2 * atan(y / (sqrt(x^2 + y^2) + x)). So to work around this we pass in (sqrt(x^2
+ // + y^2) + x) as the second parameter to atan2 in these cases. We let the device handle the
+ // undefined behavior of the second paramenter being 0 instead of doing the divide ourselves and
+ // using atan instead.
+ int useAtanWorkaround =
+ args.fContext->priv().caps()->shaderCaps()->fAtan2ImplementedAsAtanYOverX;
+ static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
+ "uniform half bias;"
+ "uniform half scale;"
+ "uniform int useAtanWorkaround;" // specialized
+
+ "half4 main(float2 coord) {"
+ "half angle = bool(useAtanWorkaround)"
+ "? half(2 * atan(-coord.y, length(coord) - coord.x))"
+ ": half(atan(-coord.y, -coord.x));"
+
+ // 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi]
+ "half t = (angle * 0.1591549430918 + 0.5 + bias) * scale;"
+ "return half4(t, 1, 0, 0);" // y = 1 for always valid
+ "}"
+ );
+
+ // The sweep gradient never rejects a pixel so it doesn't change opacity
+ auto fp = GrSkSLFP::Make(effect, "SweepLayout", /*inputFP=*/nullptr,
+ GrSkSLFP::OptFlags::kPreservesOpaqueInput,
+ "bias", shader->tBias(),
+ "scale", shader->tScale(),
+ "useAtanWorkaround", GrSkSLFP::Specialize(useAtanWorkaround));
+ return GrGradientShader::MakeGradientFP(*shader, args, mRec, std::move(fp));
+}
+
+static std::unique_ptr<GrFragmentProcessor> make_shader_fp(const SkGradientBaseShader* shader,
+ const GrFPArgs& args,
+ const SkShaders::MatrixRec& mRec) {
+ SkASSERT(shader);
+
+ switch (shader->asGradient()) {
+#define M(type) \
+ case SkShaderBase::GradientType::k##type: \
+ return make_gradient_fp(static_cast<const Sk##type##Gradient*>(shader), args, mRec);
+ SK_ALL_GRADIENTS(M)
+#undef M
+ case SkShaderBase::GradientType::kNone:
+ SkDEBUGFAIL("Gradient shader says its type is none");
+ return nullptr;
}
SkUNREACHABLE;
}
+
+std::unique_ptr<GrFragmentProcessor> Make(const SkShader* shader,
+ const GrFPArgs& args,
+ const SkMatrix& ctm) {
+ return Make(shader, args, SkShaders::MatrixRec(ctm));
+}
+
+std::unique_ptr<GrFragmentProcessor> Make(const SkShader* shader,
+ const GrFPArgs& args,
+ const SkShaders::MatrixRec& mRec) {
+ if (!shader) {
+ return nullptr;
+ }
+ auto base = as_SB(shader);
+ switch (base->type()) {
+#define M(type) \
+ case SkShaderBase::ShaderType::k##type: \
+ return make_shader_fp(static_cast<const Sk##type##Shader*>(base), args, mRec);
+ SK_ALL_SHADERS(M)
+#undef M
+ }
+ SkUNREACHABLE;
+}
+
} // namespace GrFragmentProcessors
diff --git a/src/gpu/ganesh/GrFragmentProcessors.h b/src/gpu/ganesh/GrFragmentProcessors.h
index 460c90e..5593569 100644
--- a/src/gpu/ganesh/GrFragmentProcessors.h
+++ b/src/gpu/ganesh/GrFragmentProcessors.h
@@ -24,10 +24,15 @@
class SkMaskFilter;
class SkMatrix;
class SkSurfaceProps;
+class SkShader;
struct GrFPArgs;
using GrFPResult = std::tuple<bool, std::unique_ptr<GrFragmentProcessor>>;
+namespace SkShaders {
+class MatrixRec;
+}
+
namespace GrFragmentProcessors {
/**
* Returns a GrFragmentProcessor that implements this blend for the Ganesh GPU backend.
@@ -58,6 +63,17 @@
bool IsSupported(const SkMaskFilter*);
+/**
+ * Call on the root SkShader to produce a GrFragmentProcessor.
+ *
+ * The returned GrFragmentProcessor expects an unpremultiplied input color and produces a
+ * premultiplied output.
+ */
+std::unique_ptr<GrFragmentProcessor> Make(const SkShader*, const GrFPArgs&, const SkMatrix& ctm);
+std::unique_ptr<GrFragmentProcessor> Make(const SkShader*,
+ const GrFPArgs&,
+ const SkShaders::MatrixRec&);
+
// TODO(kjlubick, brianosman) remove this after all related effects have been migrated
GrFPResult make_effect_fp(sk_sp<SkRuntimeEffect> effect,
const char* name,
@@ -66,6 +82,6 @@
std::unique_ptr<GrFragmentProcessor> destColorFP,
SkSpan<const SkRuntimeEffect::ChildPtr> children,
const GrFPArgs& childArgs);
-}
+} // namespace GrFragmentProcessors
#endif
diff --git a/src/gpu/ganesh/GrProcessorUnitTest.h b/src/gpu/ganesh/GrProcessorUnitTest.h
index 79c6c0d..fa23508 100644
--- a/src/gpu/ganesh/GrProcessorUnitTest.h
+++ b/src/gpu/ganesh/GrProcessorUnitTest.h
@@ -20,13 +20,14 @@
#include <tuple>
-class SkMatrix;
class GrCaps;
-class GrProxyProvider;
+class GrFragmentProcessor;
+class GrGeometryProcessor;
class GrProcessorTestData;
+class GrProxyProvider;
class GrTexture;
class GrXPFactory;
-class GrGeometryProcessor;
+class SkMatrix;
namespace GrProcessorUnitTest {
diff --git a/src/gpu/ganesh/SkGr.cpp b/src/gpu/ganesh/SkGr.cpp
index f4812ce..c134215 100644
--- a/src/gpu/ganesh/SkGr.cpp
+++ b/src/gpu/ganesh/SkGr.cpp
@@ -373,7 +373,7 @@
paintFP = std::move(*shaderFP);
} else {
if (const SkShaderBase* shader = as_SB(skPaint.getShader())) {
- paintFP = shader->asFragmentProcessor(fpArgs, SkShaderBase::MatrixRec(ctm));
+ paintFP = GrFragmentProcessors::Make(shader, fpArgs, ctm);
if (paintFP == nullptr) {
return false;
}
diff --git a/src/gpu/ganesh/effects/BUILD.bazel b/src/gpu/ganesh/effects/BUILD.bazel
index c00129c..d9c8224 100644
--- a/src/gpu/ganesh/effects/BUILD.bazel
+++ b/src/gpu/ganesh/effects/BUILD.bazel
@@ -36,6 +36,8 @@
"GrModulateAtlasCoverageEffect.h",
"GrOvalEffect.cpp",
"GrOvalEffect.h",
+ "GrPerlinNoise2Effect.cpp",
+ "GrPerlinNoise2Effect.h",
"GrPorterDuffXferProcessor.cpp",
"GrPorterDuffXferProcessor.h",
"GrRRectEffect.cpp",
diff --git a/src/gpu/ganesh/effects/GrConvexPolyEffect.h b/src/gpu/ganesh/effects/GrConvexPolyEffect.h
index e96ba28..7392959 100644
--- a/src/gpu/ganesh/effects/GrConvexPolyEffect.h
+++ b/src/gpu/ganesh/effects/GrConvexPolyEffect.h
@@ -11,7 +11,6 @@
#include "include/core/SkScalar.h"
#include "src/gpu/ganesh/GrFragmentProcessor.h"
#include "src/gpu/ganesh/GrProcessorUnitTest.h"
-#include "src/shaders/SkShaderBase.h"
#include <array>
#include <memory>
diff --git a/src/gpu/ganesh/effects/GrPerlinNoise2Effect.cpp b/src/gpu/ganesh/effects/GrPerlinNoise2Effect.cpp
new file mode 100644
index 0000000..394f3a4
--- /dev/null
+++ b/src/gpu/ganesh/effects/GrPerlinNoise2Effect.cpp
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/gpu/ganesh/effects/GrPerlinNoise2Effect.h"
+
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkSize.h"
+#include "include/effects/SkPerlinNoiseShader.h"
+#include "include/private/gpu/ganesh/GrTypesPriv.h"
+#include "src/base/SkRandom.h"
+#include "src/core/SkSLTypeShared.h"
+#include "src/gpu/KeyBuilder.h"
+#include "src/gpu/ganesh/GrFragmentProcessor.h"
+#include "src/gpu/ganesh/GrFragmentProcessors.h"
+#include "src/gpu/ganesh/GrProcessorUnitTest.h"
+#include "src/gpu/ganesh/GrShaderCaps.h"
+#include "src/gpu/ganesh/GrShaderVar.h"
+#include "src/gpu/ganesh/GrTestUtils.h"
+#include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
+#include "src/gpu/ganesh/glsl/GrGLSLProgramDataManager.h"
+#include "src/gpu/ganesh/glsl/GrGLSLUniformHandler.h"
+
+#include <cstdint>
+#include <iterator>
+
+class SkShader;
+
+/////////////////////////////////////////////////////////////////////
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrPerlinNoise2Effect)
+
+#if GR_TEST_UTILS
+std::unique_ptr<GrFragmentProcessor> GrPerlinNoise2Effect::TestCreate(GrProcessorTestData* d) {
+ int numOctaves = d->fRandom->nextRangeU(2, 10);
+ bool stitchTiles = d->fRandom->nextBool();
+ SkScalar seed = SkIntToScalar(d->fRandom->nextU());
+ SkISize tileSize;
+ tileSize.fWidth = d->fRandom->nextRangeU(4, 4096);
+ tileSize.fHeight = d->fRandom->nextRangeU(4, 4096);
+ SkScalar baseFrequencyX = d->fRandom->nextRangeScalar(0.01f, 0.99f);
+ SkScalar baseFrequencyY = d->fRandom->nextRangeScalar(0.01f, 0.99f);
+
+ sk_sp<SkShader> shader(d->fRandom->nextBool()
+ ? SkShaders::MakeFractalNoise(baseFrequencyX,
+ baseFrequencyY,
+ numOctaves,
+ seed,
+ stitchTiles ? &tileSize : nullptr)
+ : SkShaders::MakeTurbulence(baseFrequencyX,
+ baseFrequencyY,
+ numOctaves,
+ seed,
+ stitchTiles ? &tileSize : nullptr));
+
+ GrTest::TestAsFPArgs asFPArgs(d);
+ return GrFragmentProcessors::Make(
+ shader.get(), asFPArgs.args(), GrTest::TestMatrix(d->fRandom));
+}
+#endif
+
+SkString GrPerlinNoise2Effect::Impl::emitHelper(EmitArgs& args) {
+ const GrPerlinNoise2Effect& pne = args.fFp.cast<GrPerlinNoise2Effect>();
+
+ GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+
+ // Add noise function
+ const GrShaderVar gPerlinNoiseArgs[] = {{"chanCoord", SkSLType::kHalf},
+ {"noiseVec ", SkSLType::kHalf2}};
+
+ const GrShaderVar gPerlinNoiseStitchArgs[] = {{"chanCoord", SkSLType::kHalf},
+ {"noiseVec", SkSLType::kHalf2},
+ {"stitchData", SkSLType::kHalf2}};
+
+ SkString noiseCode;
+
+ noiseCode.append(
+ "half4 floorVal;"
+ "floorVal.xy = floor(noiseVec);"
+ "floorVal.zw = floorVal.xy + half2(1);"
+ "half2 fractVal = fract(noiseVec);"
+
+ // smooth curve : t^2*(3 - 2*t)
+ "half2 noiseSmooth = fractVal*fractVal*(half2(3) - 2*fractVal);"
+ );
+
+ // Adjust frequencies if we're stitching tiles
+ if (pne.stitchTiles()) {
+ noiseCode.append("floorVal -= step(stitchData.xyxy, floorVal) * stitchData.xyxy;");
+ }
+
+ // NOTE: We need to explicitly pass half4(1) as input color here, because the helper function
+ // can't see fInputColor (which is "_input" in the FP's outer function). skbug.com/10506
+ SkString sampleX = this->invokeChild(0, "half4(1)", args, "half2(floorVal.x, 0.5)");
+ SkString sampleY = this->invokeChild(0, "half4(1)", args, "half2(floorVal.z, 0.5)");
+ noiseCode.appendf("half2 latticeIdx = half2(%s.a, %s.a);", sampleX.c_str(), sampleY.c_str());
+
+ if (args.fShaderCaps->fPerlinNoiseRoundingFix) {
+ // Android rounding for Tegra devices, like, for example: Xoom (Tegra 2), Nexus 7 (Tegra 3).
+ // The issue is that colors aren't accurate enough on Tegra devices. For example, if an
+ // 8 bit value of 124 (or 0.486275 here) is entered, we can get a texture value of
+ // 123.513725 (or 0.484368 here). The following rounding operation prevents these precision
+ // issues from affecting the result of the noise by making sure that we only have multiples
+ // of 1/255. (Note that 1/255 is about 0.003921569, which is the value used here).
+ noiseCode.append(
+ "latticeIdx = floor(latticeIdx * half2(255.0) + half2(0.5)) * half2(0.003921569);");
+ }
+
+ // Get (x,y) coordinates with the permuted x
+ noiseCode.append("half4 bcoords = 256*latticeIdx.xyxy + floorVal.yyww;");
+
+ noiseCode.append("half2 uv;");
+
+ // This is the math to convert the two 16bit integer packed into rgba 8 bit input into a
+ // [-1,1] vector and perform a dot product between that vector and the provided vector.
+ // Save it as a string because we will repeat it 4x.
+ static constexpr const char* inc8bit = "0.00390625"; // 1.0 / 256.0
+ SkString dotLattice =
+ SkStringPrintf("dot((lattice.ga + lattice.rb*%s)*2 - half2(1), fractVal)", inc8bit);
+
+ SkString sampleA = this->invokeChild(1, "half4(1)", args, "half2(bcoords.x, chanCoord)");
+ SkString sampleB = this->invokeChild(1, "half4(1)", args, "half2(bcoords.y, chanCoord)");
+ SkString sampleC = this->invokeChild(1, "half4(1)", args, "half2(bcoords.w, chanCoord)");
+ SkString sampleD = this->invokeChild(1, "half4(1)", args, "half2(bcoords.z, chanCoord)");
+
+ // Compute u, at offset (0,0)
+ noiseCode.appendf("half4 lattice = %s;", sampleA.c_str());
+ noiseCode.appendf("uv.x = %s;", dotLattice.c_str());
+
+ // Compute v, at offset (-1,0)
+ noiseCode.append("fractVal.x -= 1.0;");
+ noiseCode.appendf("lattice = %s;", sampleB.c_str());
+ noiseCode.appendf("uv.y = %s;", dotLattice.c_str());
+
+ // Compute 'a' as a linear interpolation of 'u' and 'v'
+ noiseCode.append("half2 ab;");
+ noiseCode.append("ab.x = mix(uv.x, uv.y, noiseSmooth.x);");
+
+ // Compute v, at offset (-1,-1)
+ noiseCode.append("fractVal.y -= 1.0;");
+ noiseCode.appendf("lattice = %s;", sampleC.c_str());
+ noiseCode.appendf("uv.y = %s;", dotLattice.c_str());
+
+ // Compute u, at offset (0,-1)
+ noiseCode.append("fractVal.x += 1.0;");
+ noiseCode.appendf("lattice = %s;", sampleD.c_str());
+ noiseCode.appendf("uv.x = %s;", dotLattice.c_str());
+
+ // Compute 'b' as a linear interpolation of 'u' and 'v'
+ noiseCode.append("ab.y = mix(uv.x, uv.y, noiseSmooth.x);");
+ // Compute the noise as a linear interpolation of 'a' and 'b'
+ noiseCode.append("return mix(ab.x, ab.y, noiseSmooth.y);");
+
+ SkString noiseFuncName = fragBuilder->getMangledFunctionName("noiseFuncName");
+ if (pne.stitchTiles()) {
+ fragBuilder->emitFunction(SkSLType::kHalf,
+ noiseFuncName.c_str(),
+ {gPerlinNoiseStitchArgs, std::size(gPerlinNoiseStitchArgs)},
+ noiseCode.c_str());
+ } else {
+ fragBuilder->emitFunction(SkSLType::kHalf,
+ noiseFuncName.c_str(),
+ {gPerlinNoiseArgs, std::size(gPerlinNoiseArgs)},
+ noiseCode.c_str());
+ }
+
+ return noiseFuncName;
+}
+
+void GrPerlinNoise2Effect::Impl::emitCode(EmitArgs& args) {
+ SkString noiseFuncName = this->emitHelper(args);
+
+ const GrPerlinNoise2Effect& pne = args.fFp.cast<GrPerlinNoise2Effect>();
+
+ GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+ GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
+
+ fBaseFrequencyUni = uniformHandler->addUniform(
+ &pne, kFragment_GrShaderFlag, SkSLType::kHalf2, "baseFrequency");
+ const char* baseFrequencyUni = uniformHandler->getUniformCStr(fBaseFrequencyUni);
+
+ const char* stitchDataUni = nullptr;
+ if (pne.stitchTiles()) {
+ fStitchDataUni = uniformHandler->addUniform(
+ &pne, kFragment_GrShaderFlag, SkSLType::kHalf2, "stitchData");
+ stitchDataUni = uniformHandler->getUniformCStr(fStitchDataUni);
+ }
+
+ // There are rounding errors if the floor operation is not performed here
+ fragBuilder->codeAppendf(
+ "half2 noiseVec = half2(floor(%s.xy) * %s);", args.fSampleCoord, baseFrequencyUni);
+
+ // Clear the color accumulator
+ fragBuilder->codeAppendf("half4 color = half4(0);");
+
+ if (pne.stitchTiles()) {
+ fragBuilder->codeAppendf("half2 stitchData = %s;", stitchDataUni);
+ }
+
+ fragBuilder->codeAppendf("half ratio = 1.0;");
+
+ // Loop over all octaves
+ fragBuilder->codeAppendf("for (int octave = 0; octave < %d; ++octave) {", pne.numOctaves());
+ fragBuilder->codeAppendf("color += ");
+ if (pne.type() != SkPerlinNoiseShader::kFractalNoise_Type) {
+ fragBuilder->codeAppend("abs(");
+ }
+
+ // There are 4 lines, put y coords at center of each.
+ static constexpr const char* chanCoordR = "0.5";
+ static constexpr const char* chanCoordG = "1.5";
+ static constexpr const char* chanCoordB = "2.5";
+ static constexpr const char* chanCoordA = "3.5";
+ if (pne.stitchTiles()) {
+ fragBuilder->codeAppendf(
+ "half4(%s(%s, noiseVec, stitchData), %s(%s, noiseVec, stitchData),"
+ "%s(%s, noiseVec, stitchData), %s(%s, noiseVec, stitchData))",
+ noiseFuncName.c_str(),
+ chanCoordR,
+ noiseFuncName.c_str(),
+ chanCoordG,
+ noiseFuncName.c_str(),
+ chanCoordB,
+ noiseFuncName.c_str(),
+ chanCoordA);
+ } else {
+ fragBuilder->codeAppendf(
+ "half4(%s(%s, noiseVec), %s(%s, noiseVec),"
+ "%s(%s, noiseVec), %s(%s, noiseVec))",
+ noiseFuncName.c_str(),
+ chanCoordR,
+ noiseFuncName.c_str(),
+ chanCoordG,
+ noiseFuncName.c_str(),
+ chanCoordB,
+ noiseFuncName.c_str(),
+ chanCoordA);
+ }
+ if (pne.type() != SkPerlinNoiseShader::kFractalNoise_Type) {
+ fragBuilder->codeAppend(")"); // end of "abs("
+ }
+ fragBuilder->codeAppend(" * ratio;");
+
+ fragBuilder->codeAppend(
+ "noiseVec *= half2(2.0);"
+ "ratio *= 0.5;");
+
+ if (pne.stitchTiles()) {
+ fragBuilder->codeAppend("stitchData *= half2(2.0);");
+ }
+ fragBuilder->codeAppend("}"); // end of the for loop on octaves
+
+ if (pne.type() == SkPerlinNoiseShader::kFractalNoise_Type) {
+ // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2
+ // by fractalNoise and (turbulenceFunctionResult) by turbulence.
+ fragBuilder->codeAppendf("color = color * half4(0.5) + half4(0.5);");
+ }
+
+ // Clamp values
+ fragBuilder->codeAppendf("color = saturate(color);");
+
+ // Pre-multiply the result
+ fragBuilder->codeAppendf("return half4(color.rgb * color.aaa, color.a);");
+}
+
+void GrPerlinNoise2Effect::Impl::onSetData(const GrGLSLProgramDataManager& pdman,
+ const GrFragmentProcessor& processor) {
+ const GrPerlinNoise2Effect& turbulence = processor.cast<GrPerlinNoise2Effect>();
+
+ const SkVector& baseFrequency = turbulence.baseFrequency();
+ pdman.set2f(fBaseFrequencyUni, baseFrequency.fX, baseFrequency.fY);
+
+ if (turbulence.stitchTiles()) {
+ const SkPerlinNoiseShader::StitchData& stitchData = turbulence.stitchData();
+ pdman.set2f(fStitchDataUni,
+ SkIntToScalar(stitchData.fWidth),
+ SkIntToScalar(stitchData.fHeight));
+ }
+}
+
+void GrPerlinNoise2Effect::onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const {
+ uint32_t key = fNumOctaves;
+ key = key << 3; // Make room for next 3 bits
+ switch (fType) {
+ case SkPerlinNoiseShader::kFractalNoise_Type:
+ key |= 0x1;
+ break;
+ case SkPerlinNoiseShader::kTurbulence_Type:
+ key |= 0x2;
+ break;
+ default:
+ // leave key at 0
+ break;
+ }
+ if (fStitchTiles) {
+ key |= 0x4; // Flip the 3rd bit if tile stitching is on
+ }
+ b->add32(key);
+}
diff --git a/src/gpu/ganesh/effects/GrPerlinNoise2Effect.h b/src/gpu/ganesh/effects/GrPerlinNoise2Effect.h
new file mode 100644
index 0000000..916052c
--- /dev/null
+++ b/src/gpu/ganesh/effects/GrPerlinNoise2Effect.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrPerlinNoise2Effect_DEFINED
+#define GrPerlinNoise2Effect_DEFINED
+
+#include "include/core/SkAlphaType.h"
+#include "include/core/SkMatrix.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkSamplingOptions.h"
+#include "include/core/SkString.h"
+#include "include/private/SkSLSampleUsage.h"
+#include "src/gpu/ganesh/GrCaps.h"
+#include "src/gpu/ganesh/GrFragmentProcessor.h"
+#include "src/gpu/ganesh/GrProcessorUnitTest.h"
+#include "src/gpu/ganesh/GrSamplerState.h"
+#include "src/gpu/ganesh/GrSurfaceProxyView.h"
+#include "src/gpu/ganesh/effects/GrTextureEffect.h"
+#include "src/gpu/ganesh/glsl/GrGLSLProgramDataManager.h"
+#include "src/shaders/SkPerlinNoiseShaderImpl.h"
+
+#include <memory>
+#include <utility>
+
+namespace skgpu {
+class KeyBuilder;
+}
+struct GrShaderCaps;
+
+class GrPerlinNoise2Effect : public GrFragmentProcessor {
+public:
+ static std::unique_ptr<GrFragmentProcessor> Make(
+ SkPerlinNoiseShader::Type type,
+ int numOctaves,
+ bool stitchTiles,
+ std::unique_ptr<SkPerlinNoiseShader::PaintingData> paintingData,
+ GrSurfaceProxyView permutationsView,
+ GrSurfaceProxyView noiseView,
+ const GrCaps& caps) {
+ static constexpr GrSamplerState kRepeatXSampler = {GrSamplerState::WrapMode::kRepeat,
+ GrSamplerState::WrapMode::kClamp,
+ GrSamplerState::Filter::kNearest};
+ auto permutationsFP = GrTextureEffect::Make(std::move(permutationsView),
+ kPremul_SkAlphaType,
+ SkMatrix::I(),
+ kRepeatXSampler,
+ caps);
+ auto noiseFP = GrTextureEffect::Make(
+ std::move(noiseView), kPremul_SkAlphaType, SkMatrix::I(), kRepeatXSampler, caps);
+
+ return std::unique_ptr<GrFragmentProcessor>(
+ new GrPerlinNoise2Effect(type,
+ numOctaves,
+ stitchTiles,
+ std::move(paintingData),
+ std::move(permutationsFP),
+ std::move(noiseFP)));
+ }
+
+ const char* name() const override { return "PerlinNoise"; }
+
+ std::unique_ptr<GrFragmentProcessor> clone() const override {
+ return std::unique_ptr<GrFragmentProcessor>(new GrPerlinNoise2Effect(*this));
+ }
+
+ const SkPerlinNoiseShader::StitchData& stitchData() const {
+ return fPaintingData->fStitchDataInit;
+ }
+
+ SkPerlinNoiseShader::Type type() const { return fType; }
+ bool stitchTiles() const { return fStitchTiles; }
+ const SkVector& baseFrequency() const { return fPaintingData->fBaseFrequency; }
+ int numOctaves() const { return fNumOctaves; }
+
+private:
+ class Impl : public ProgramImpl {
+ public:
+ SkString emitHelper(EmitArgs& args);
+ void emitCode(EmitArgs&) override;
+
+ private:
+ void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
+
+ GrGLSLProgramDataManager::UniformHandle fStitchDataUni;
+ GrGLSLProgramDataManager::UniformHandle fBaseFrequencyUni;
+ };
+
+ std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override {
+ return std::make_unique<Impl>();
+ }
+
+ void onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const override;
+
+ bool onIsEqual(const GrFragmentProcessor& sBase) const override {
+ const GrPerlinNoise2Effect& s = sBase.cast<GrPerlinNoise2Effect>();
+ return fType == s.fType &&
+ fPaintingData->fBaseFrequency == s.fPaintingData->fBaseFrequency &&
+ fNumOctaves == s.fNumOctaves && fStitchTiles == s.fStitchTiles &&
+ fPaintingData->fStitchDataInit == s.fPaintingData->fStitchDataInit;
+ }
+
+ GrPerlinNoise2Effect(SkPerlinNoiseShader::Type type,
+ int numOctaves,
+ bool stitchTiles,
+ std::unique_ptr<SkPerlinNoiseShader::PaintingData> paintingData,
+ std::unique_ptr<GrFragmentProcessor> permutationsFP,
+ std::unique_ptr<GrFragmentProcessor> noiseFP)
+ : GrFragmentProcessor(kGrPerlinNoise2Effect_ClassID, kNone_OptimizationFlags)
+ , fType(type)
+ , fNumOctaves(numOctaves)
+ , fStitchTiles(stitchTiles)
+ , fPaintingData(std::move(paintingData)) {
+ this->registerChild(std::move(permutationsFP), SkSL::SampleUsage::Explicit());
+ this->registerChild(std::move(noiseFP), SkSL::SampleUsage::Explicit());
+ this->setUsesSampleCoordsDirectly();
+ }
+
+ GrPerlinNoise2Effect(const GrPerlinNoise2Effect& that)
+ : GrFragmentProcessor(that)
+ , fType(that.fType)
+ , fNumOctaves(that.fNumOctaves)
+ , fStitchTiles(that.fStitchTiles)
+ , fPaintingData(new SkPerlinNoiseShader::PaintingData(*that.fPaintingData)) {}
+
+ GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+
+ SkPerlinNoiseShader::Type fType;
+ int fNumOctaves;
+ bool fStitchTiles;
+
+ std::unique_ptr<SkPerlinNoiseShader::PaintingData> fPaintingData;
+};
+
+#endif
diff --git a/src/gpu/ganesh/effects/GrRRectEffect.cpp b/src/gpu/ganesh/effects/GrRRectEffect.cpp
index 55c157b..ac92f82 100644
--- a/src/gpu/ganesh/effects/GrRRectEffect.cpp
+++ b/src/gpu/ganesh/effects/GrRRectEffect.cpp
@@ -27,7 +27,6 @@
#include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
#include "src/gpu/ganesh/glsl/GrGLSLProgramDataManager.h"
#include "src/gpu/ganesh/glsl/GrGLSLUniformHandler.h"
-#include "src/shaders/SkShaderBase.h"
#include <algorithm>
#include <cstdint>
diff --git a/src/gpu/ganesh/gradients/GrGradientBitmapCache.cpp b/src/gpu/ganesh/gradients/GrGradientBitmapCache.cpp
index 9717862..0bbd49c 100644
--- a/src/gpu/ganesh/gradients/GrGradientBitmapCache.cpp
+++ b/src/gpu/ganesh/gradients/GrGradientBitmapCache.cpp
@@ -13,7 +13,7 @@
#include "include/private/base/SkTemplates.h"
#include "src/base/SkHalf.h"
#include "src/core/SkRasterPipeline.h"
-#include "src/shaders/gradients/SkGradientShaderBase.h"
+#include "src/shaders/gradients/SkGradientBaseShader.h"
#include <functional>
@@ -135,7 +135,7 @@
p.append(SkRasterPipelineOp::seed_shader);
p.append_matrix(&alloc, SkMatrix::Scale(1.0f / bitmap->width(), 1.0f));
- SkGradientShaderBase::AppendGradientFillStages(&p, &alloc, colors, positions, count);
+ SkGradientBaseShader::AppendGradientFillStages(&p, &alloc, colors, positions, count);
p.append_store(bitmap->colorType(), &ctx);
p.run(0, 0, bitmap->width(), 1);
}
diff --git a/src/gpu/ganesh/gradients/GrGradientShader.cpp b/src/gpu/ganesh/gradients/GrGradientShader.cpp
index c0b2706..98a2fe7 100644
--- a/src/gpu/ganesh/gradients/GrGradientShader.cpp
+++ b/src/gpu/ganesh/gradients/GrGradientShader.cpp
@@ -450,7 +450,7 @@
// and removing these stops at the beginning, it makes optimizing the remaining color stops
// simpler.
- // SkGradientShaderBase guarantees that pos[0] == 0 by adding a default value.
+ // SkGradientBaseShader guarantees that pos[0] == 0 by adding a default value.
bool bottomHardStop = SkScalarNearlyEqual(positions[0], positions[1]);
// The same is true for pos[end] == 1
bool topHardStop = SkScalarNearlyEqual(positions[count - 2], positions[count - 1]);
@@ -733,7 +733,7 @@
// Our final goal is to emit premul colors, but under certain conditions we don't need to do
// anything to achieve that: i.e. its interpolating already premul colors (inputPremul) or
// all the colors have a = 1, in which case premul is a no op. Note that this allOpaque check
- // is more permissive than SkGradientShaderBase's isOpaque(), since we can optimize away the
+ // is more permissive than SkGradientBaseShader's isOpaque(), since we can optimize away the
// make-premul op for two point conical gradients (which report false for isOpaque).
SkAlphaType intermediateAlphaType = inputPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
SkAlphaType dstAlphaType = kPremul_SkAlphaType;
@@ -752,11 +752,29 @@
namespace GrGradientShader {
+/**
+ * Produces an FP that muls its input coords by the inverse of the pending matrix and then
+ * samples the passed FP with those coordinates. 'postInv' is an additional matrix to
+ * post-apply to the inverted pending matrix. If the pending matrix is not invertible the
+ * GrFPResult's bool will be false and the passed FP will be returned to the caller in the
+ * GrFPResult.
+ */
+static GrFPResult apply_matrix(std::unique_ptr<GrFragmentProcessor> fp,
+ const SkShaders::MatrixRec& rec,
+ const SkMatrix& postInv) {
+ auto [total, ok] = rec.applyForFragmentProcessor(postInv);
+ if (!ok) {
+ return {false, std::move(fp)};
+ }
+ // GrMatrixEffect returns 'fp' if total worked out to identity.
+ return {true, GrMatrixEffect::Make(total, std::move(fp))};
+}
+
// Combines the colorizer and layout with an appropriately configured top-level effect based on the
// gradient's tile mode
-std::unique_ptr<GrFragmentProcessor> MakeGradientFP(const SkGradientShaderBase& shader,
+std::unique_ptr<GrFragmentProcessor> MakeGradientFP(const SkGradientBaseShader& shader,
const GrFPArgs& args,
- const SkShaderBase::MatrixRec& mRec,
+ const SkShaders::MatrixRec& mRec,
std::unique_ptr<GrFragmentProcessor> layout,
const SkMatrix* overrideMatrix) {
// No shader is possible if a layout couldn't be created, e.g. a layout-specific Make() returned
@@ -766,12 +784,12 @@
}
// Some two-point conical gradients use a custom matrix here. Otherwise, use
- // SkGradientShaderBase's matrix;
+ // SkGradientBaseShader's matrix;
if (!overrideMatrix) {
overrideMatrix = &shader.getGradientMatrix();
}
bool success;
- std::tie(success, layout) = mRec.apply(std::move(layout), *overrideMatrix);
+ std::tie(success, layout) = apply_matrix(std::move(layout), mRec, *overrideMatrix);
if (!success) {
return nullptr;
}
@@ -825,7 +843,7 @@
break;
case SkTileMode::kClamp:
// For the clamped mode, the border colors are the first and last colors, corresponding
- // to t=0 and t=1, because SkGradientShaderBase enforces that by adding color stops as
+ // to t=0 and t=1, because SkGradientBaseShader enforces that by adding color stops as
// appropriate. If there is a hard stop, this grabs the expected outer colors for the
// border.
gradient = make_clamped_gradient(std::move(colorizer), std::move(layout),
@@ -852,7 +870,7 @@
std::unique_ptr<GrFragmentProcessor> MakeLinear(const SkLinearGradient& shader,
const GrFPArgs& args,
- const SkShaderBase::MatrixRec& mRec) {
+ const SkShaders::MatrixRec& mRec) {
// We add a tiny delta to t. When gradient stops are set up so that a hard stop in a vertically
// or horizontally oriented gradient falls exactly at a column or row of pixel centers we can
// get slightly different interpolated t values along the column/row. By adding the delta
diff --git a/src/gpu/ganesh/gradients/GrGradientShader.h b/src/gpu/ganesh/gradients/GrGradientShader.h
index b35c037..ad7d151 100644
--- a/src/gpu/ganesh/gradients/GrGradientShader.h
+++ b/src/gpu/ganesh/gradients/GrGradientShader.h
@@ -10,7 +10,7 @@
#include "src/gpu/ganesh/GrFPArgs.h"
#include "src/gpu/ganesh/GrFragmentProcessor.h"
-#include "src/shaders/gradients/SkGradientShaderBase.h"
+#include "src/shaders/gradients/SkGradientBaseShader.h"
#include "src/shaders/gradients/SkLinearGradient.h"
#if GR_TEST_UTILS
@@ -18,15 +18,15 @@
#endif
namespace GrGradientShader {
- std::unique_ptr<GrFragmentProcessor> MakeGradientFP(const SkGradientShaderBase& shader,
- const GrFPArgs& args,
- const SkShaderBase::MatrixRec&,
- std::unique_ptr<GrFragmentProcessor> layout,
- const SkMatrix* overrideMatrix = nullptr);
-
- std::unique_ptr<GrFragmentProcessor> MakeLinear(const SkLinearGradient& shader,
+std::unique_ptr<GrFragmentProcessor> MakeGradientFP(const SkGradientBaseShader& shader,
const GrFPArgs& args,
- const SkShaderBase::MatrixRec&);
+ const SkShaders::MatrixRec&,
+ std::unique_ptr<GrFragmentProcessor> layout,
+ const SkMatrix* overrideMatrix = nullptr);
+
+std::unique_ptr<GrFragmentProcessor> MakeLinear(const SkLinearGradient& shader,
+ const GrFPArgs& args,
+ const SkShaders::MatrixRec&);
#if GR_TEST_UTILS
/** Helper struct that stores (and populates) parameters to construct a random gradient.
diff --git a/src/gpu/graphite/KeyHelpers.cpp b/src/gpu/graphite/KeyHelpers.cpp
index 4954fd6..4f0696b 100644
--- a/src/gpu/graphite/KeyHelpers.cpp
+++ b/src/gpu/graphite/KeyHelpers.cpp
@@ -320,10 +320,9 @@
add_conical_gradient_uniform_data(dict, codeSnippetID, gradData, gatherer);
}
break;
- case SkShaderBase::GradientType::kColor:
case SkShaderBase::GradientType::kNone:
default:
- SkASSERT(0);
+ SkDEBUGFAIL("Expected a gradient shader, but it wasn't one.");
break;
}
diff --git a/src/opts/SkRasterPipeline_opts.h b/src/opts/SkRasterPipeline_opts.h
index 0bf1da6..15345b7 100644
--- a/src/opts/SkRasterPipeline_opts.h
+++ b/src/opts/SkRasterPipeline_opts.h
@@ -2185,7 +2185,7 @@
// Skia stores all polar colors with hue in the first component, so this "LCH -> Lab" transform
// actually takes "HCL". This is also used to do the same polar transform for OkHCL to OkLAB.
-// See similar comments & logic in SkGradientShaderBase.cpp.
+// See similar comments & logic in SkGradientBaseShader.cpp.
STAGE(css_hcl_to_lab, NoCtx) {
F H = r,
C = g,
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index ca3cc12..540974d 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -48,6 +48,7 @@
#include "src/pdf/SkPDFShader.h"
#include "src/pdf/SkPDFTypes.h"
#include "src/pdf/SkPDFUtils.h"
+#include "src/shaders/SkColorShader.h"
#include "src/text/GlyphRun.h"
#include "src/utils/SkClipStackUtils.h"
@@ -1174,18 +1175,11 @@
if (shader) {
// note: we always present the alpha as 1 for the shader, knowing that it will be
// accounted for when we create our newGraphicsState (below)
- if (as_SB(shader)->asGradient() == SkShaderBase::GradientType::kColor) {
+ if (as_SB(shader)->type() == SkShaderBase::ShaderType::kColor) {
+ auto colorShader = static_cast<SkColorShader*>(shader);
// We don't have to set a shader just for a color.
- SkShaderBase::GradientInfo gradientInfo;
- SkColor gradientColor = SK_ColorBLACK;
- gradientInfo.fColors = &gradientColor;
- gradientInfo.fColorOffsets = nullptr;
- gradientInfo.fColorCount = 1;
- SkAssertResult(as_SB(shader)->asGradient(&gradientInfo) ==
- SkShaderBase::GradientType::kColor);
- color = SkColor4f::FromColor(gradientColor);
- entry->fColor ={color.fR, color.fG, color.fB, 1};
-
+ color = SkColor4f::FromColor(colorShader->color());
+ entry->fColor = {color.fR, color.fG, color.fB, 1};
} else {
// PDF positions patterns relative to the initial transform, so
// we need to apply the current transform to the shader parameters.
diff --git a/src/pdf/SkPDFGradientShader.cpp b/src/pdf/SkPDFGradientShader.cpp
index 54cb5cd..3ec2f86 100644
--- a/src/pdf/SkPDFGradientShader.cpp
+++ b/src/pdf/SkPDFGradientShader.cpp
@@ -758,7 +758,6 @@
transformPoints[1] = transformPoints[0];
transformPoints[1].fX += SK_Scalar1;
break;
- case SkShaderBase::GradientType::kColor:
case SkShaderBase::GradientType::kNone:
default:
return SkPDFIndirectReference();
diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp
index 71e9d16..e0f238a 100644
--- a/src/ports/SkGlobalInitialization_default.cpp
+++ b/src/ports/SkGlobalInitialization_default.cpp
@@ -21,11 +21,13 @@
#include "include/effects/Sk2DPathEffect.h"
#include "include/effects/SkCornerPathEffect.h"
#include "include/effects/SkDiscretePathEffect.h"
+ #include "include/effects/SkImageFilters.h"
#include "include/effects/SkOverdrawColorFilter.h"
#include "include/effects/SkPerlinNoiseShader.h"
#include "include/effects/SkShaderMaskFilter.h"
#include "src/core/SkBlendModeBlender.h"
#include "src/core/SkImageFilter_Base.h"
+ #include "src/core/SkLocalMatrixImageFilter.h"
#include "src/core/SkRecordedDrawable.h"
#include "src/effects/SkDashImpl.h"
#include "src/effects/SkEmbossMaskFilter.h"
@@ -37,10 +39,7 @@
#include "src/shaders/SkLocalMatrixShader.h"
#include "src/shaders/SkPictureShader.h"
#include "src/shaders/SkShaderBase.h"
- #include "src/shaders/gradients/SkGradientShaderBase.h"
-
- #include "include/effects/SkImageFilters.h"
- #include "src/core/SkLocalMatrixImageFilter.h"
+ #include "src/shaders/gradients/SkGradientBaseShader.h"
#ifdef SK_ENABLE_SKSL
#include "include/effects/SkRuntimeEffect.h"
@@ -59,18 +58,18 @@
*/
void SkFlattenable::PrivateInitializer::InitEffects() {
// Shaders.
+ SkRegisterBlendShaderFlattenable();
SkRegisterColor4ShaderFlattenable();
SK_REGISTER_FLATTENABLE(SkColorFilterShader);
SkRegisterColorShaderFlattenable();
- SkRegisterComposeShaderFlattenable();
SkRegisterCoordClampShaderFlattenable();
SkRegisterEmptyShaderFlattenable();
SK_REGISTER_FLATTENABLE(SkLocalMatrixShader);
SK_REGISTER_FLATTENABLE(SkPictureShader);
+ SkRegisterConicalGradientShaderFlattenable();
SkRegisterLinearGradientShaderFlattenable();
SkRegisterRadialGradientShaderFlattenable();
SkRegisterSweepGradientShaderFlattenable();
- SkRegisterTwoPointConicalGradientShaderFlattenable();
SkRegisterPerlinNoiseShaderFlattenable();
SkShaderBase::RegisterFlattenables();
diff --git a/src/shaders/BUILD.bazel b/src/shaders/BUILD.bazel
index dca5db4..5db1548 100644
--- a/src/shaders/BUILD.bazel
+++ b/src/shaders/BUILD.bazel
@@ -8,22 +8,30 @@
SHADER_FILES = [
"SkBitmapProcShader.cpp",
"SkBitmapProcShader.h",
- "SkCoordClampShader.cpp",
+ "SkBlendShader.cpp",
+ "SkBlendShader.h",
"SkColorFilterShader.cpp",
"SkColorFilterShader.h",
"SkColorShader.cpp",
- "SkComposeShader.cpp",
+ "SkColorShader.h",
+ "SkCoordClampShader.cpp",
+ "SkCoordClampShader.h",
"SkEmptyShader.cpp",
+ "SkEmptyShader.h",
"SkGainmapShader.cpp",
"SkImageShader.cpp",
"SkImageShader.h",
"SkLocalMatrixShader.cpp",
"SkLocalMatrixShader.h",
- "SkPerlinNoiseShader.cpp",
+ "SkPerlinNoiseShaderImpl.cpp",
+ "SkPerlinNoiseShaderImpl.h",
"SkShader.cpp",
+ "SkShaderBase.cpp",
"SkShaderBase.h",
"SkTransformShader.cpp",
"SkTransformShader.h",
+ "SkTriColorShader.cpp",
+ "SkTriColorShader.h",
]
split_srcs_and_hdrs(
@@ -49,12 +57,25 @@
)
skia_filegroup(
+ name = "sksl_srcs",
+ srcs = ["SkRuntimeShader.cpp"],
+)
+
+skia_filegroup(
+ name = "sksl_hdrs",
+ srcs = ["SkRuntimeShader.h"],
+)
+
+skia_filegroup(
name = "srcs",
srcs = [
":shader_srcs",
":skpicture_srcs",
"//src/shaders/gradients:srcs",
- ],
+ ] + select({
+ "//src/sksl:needs_sksl": [":sksl_srcs"],
+ "//conditions:default": [],
+ }),
visibility = ["//src:__pkg__"],
)
@@ -64,6 +85,9 @@
":shader_hdrs",
":skpicture_hdrs",
"//src/shaders/gradients:private_hdrs",
- ],
+ ] + select({
+ "//src/sksl:needs_sksl": [":sksl_hdrs"],
+ "//conditions:default": [],
+ }),
visibility = ["//src:__pkg__"],
)
diff --git a/src/shaders/SkBitmapProcShader.cpp b/src/shaders/SkBitmapProcShader.cpp
index 159c0c4..dd4df85 100644
--- a/src/shaders/SkBitmapProcShader.cpp
+++ b/src/shaders/SkBitmapProcShader.cpp
@@ -7,9 +7,17 @@
#include "src/shaders/SkBitmapProcShader.h"
+#include "include/core/SkColor.h"
+#include "include/core/SkMatrix.h"
+#include "include/core/SkPixmap.h"
+#include "include/private/base/SkAssert.h"
#include "src/base/SkArenaAlloc.h"
#include "src/core/SkBitmapProcState.h"
-#include "src/core/SkPaintPriv.h"
+
+#include <algorithm>
+#include <cstdint>
+
+enum class SkTileMode;
class BitmapProcShaderContext : public SkShaderBase::Context {
public:
diff --git a/src/shaders/SkBitmapProcShader.h b/src/shaders/SkBitmapProcShader.h
index 763f304..72769739 100644
--- a/src/shaders/SkBitmapProcShader.h
+++ b/src/shaders/SkBitmapProcShader.h
@@ -7,10 +7,12 @@
#ifndef SkBitmapProcShader_DEFINED
#define SkBitmapProcShader_DEFINED
-#include "src/core/SkImagePriv.h"
+#include "include/core/SkSamplingOptions.h"
#include "src/shaders/SkShaderBase.h"
+class SkArenaAlloc;
class SkImage_Base;
+enum class SkTileMode;
class SkBitmapProcLegacyShader : public SkShaderBase {
private:
diff --git a/src/shaders/SkBlendShader.cpp b/src/shaders/SkBlendShader.cpp
new file mode 100644
index 0000000..e5358d3
--- /dev/null
+++ b/src/shaders/SkBlendShader.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/shaders/SkBlendShader.h"
+
+#include "include/core/SkBlendMode.h"
+#include "include/core/SkBlender.h"
+#include "include/core/SkData.h"
+#include "include/core/SkFlattenable.h"
+#include "include/effects/SkRuntimeEffect.h"
+#include "src/base/SkArenaAlloc.h"
+#include "src/core/SkBlendModePriv.h"
+#include "src/core/SkBlenderBase.h"
+#include "src/core/SkEffectPriv.h"
+#include "src/core/SkRasterPipeline.h"
+#include "src/core/SkRasterPipelineOpContexts.h"
+#include "src/core/SkRasterPipelineOpList.h"
+#include "src/core/SkReadBuffer.h"
+#include "src/core/SkRuntimeEffectPriv.h"
+#include "src/core/SkWriteBuffer.h"
+#include "src/shaders/SkShaderBase.h"
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/Blend.h"
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif
+
+#include <optional>
+
+sk_sp<SkFlattenable> SkBlendShader::CreateProc(SkReadBuffer& buffer) {
+ sk_sp<SkShader> dst(buffer.readShader());
+ sk_sp<SkShader> src(buffer.readShader());
+ if (!buffer.validate(dst && src)) {
+ return nullptr;
+ }
+
+ unsigned mode = buffer.read32();
+
+ if (mode == kCustom_SkBlendMode) {
+ sk_sp<SkBlender> blender = buffer.readBlender();
+ if (buffer.validate(blender != nullptr)) {
+ return SkShaders::Blend(std::move(blender), std::move(dst), std::move(src));
+ }
+ } else {
+ if (buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode)) {
+ return SkShaders::Blend(static_cast<SkBlendMode>(mode), std::move(dst), std::move(src));
+ }
+ }
+ return nullptr;
+}
+
+void SkBlendShader::flatten(SkWriteBuffer& buffer) const {
+ buffer.writeFlattenable(fDst.get());
+ buffer.writeFlattenable(fSrc.get());
+ buffer.write32((int)fMode);
+}
+
+// Returns the output of e0, and leaves the output of e1 in r,g,b,a
+static float* append_two_shaders(const SkStageRec& rec,
+ const SkShaders::MatrixRec& mRec,
+ SkShader* s0,
+ SkShader* s1) {
+ struct Storage {
+ float fCoords[2 * SkRasterPipeline_kMaxStride];
+ float fRes0[4 * SkRasterPipeline_kMaxStride];
+ };
+ auto storage = rec.fAlloc->make<Storage>();
+
+ // Note we cannot simply apply mRec here and then unconditionally store the coordinates. When
+ // building for Android Framework it would interrupt the backwards local matrix concatenation if
+ // mRec had a pending local matrix and either of the children also had a local matrix.
+ // b/256873449
+ if (mRec.rasterPipelineCoordsAreSeeded()) {
+ rec.fPipeline->append(SkRasterPipelineOp::store_src_rg, storage->fCoords);
+ }
+ if (!as_SB(s0)->appendStages(rec, mRec)) {
+ return nullptr;
+ }
+ rec.fPipeline->append(SkRasterPipelineOp::store_src, storage->fRes0);
+
+ if (mRec.rasterPipelineCoordsAreSeeded()) {
+ rec.fPipeline->append(SkRasterPipelineOp::load_src_rg, storage->fCoords);
+ }
+ if (!as_SB(s1)->appendStages(rec, mRec)) {
+ return nullptr;
+ }
+ return storage->fRes0;
+}
+
+bool SkBlendShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec& mRec) const {
+ float* res0 = append_two_shaders(rec, mRec, fDst.get(), fSrc.get());
+ if (!res0) {
+ return false;
+ }
+
+ rec.fPipeline->append(SkRasterPipelineOp::load_dst, res0);
+ SkBlendMode_AppendStages(fMode, rec.fPipeline);
+ return true;
+}
+
+#if defined(SK_ENABLE_SKVM)
+skvm::Color SkBlendShader::program(skvm::Builder* p,
+ skvm::Coord device,
+ skvm::Coord local,
+ skvm::Color paint,
+ const SkShaders::MatrixRec& mRec,
+ const SkColorInfo& cinfo,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc* alloc) const {
+ skvm::Color d, s;
+ if ((d = as_SB(fDst)->program(p, device, local, paint, mRec, cinfo, uniforms, alloc)) &&
+ (s = as_SB(fSrc)->program(p, device, local, paint, mRec, cinfo, uniforms, alloc))) {
+ return p->blend(fMode, s, d);
+ }
+ return {};
+}
+#endif
+
+#if defined(SK_GRAPHITE)
+void SkBlendShader::addToKey(const skgpu::graphite::KeyContext& keyContext,
+ skgpu::graphite::PaintParamsKeyBuilder* builder,
+ skgpu::graphite::PipelineDataGatherer* gatherer) const {
+ using namespace skgpu::graphite;
+
+ BlendShaderBlock::BeginBlock(keyContext, builder, gatherer);
+
+ as_SB(fSrc)->addToKey(keyContext, builder, gatherer);
+ as_SB(fDst)->addToKey(keyContext, builder, gatherer);
+
+ SkSpan<const float> porterDuffConstants = skgpu::GetPorterDuffBlendConstants(fMode);
+ if (!porterDuffConstants.empty()) {
+ CoeffBlenderBlock::BeginBlock(keyContext, builder, gatherer, porterDuffConstants);
+ builder->endBlock();
+ } else {
+ BlendModeBlenderBlock::BeginBlock(keyContext, builder, gatherer, fMode);
+ builder->endBlock();
+ }
+
+ builder->endBlock(); // BlendShaderBlock
+}
+#endif
+
+sk_sp<SkShader> SkShaders::Blend(SkBlendMode mode, sk_sp<SkShader> dst, sk_sp<SkShader> src) {
+ if (!src || !dst) {
+ return nullptr;
+ }
+ switch (mode) {
+ case SkBlendMode::kClear:
+ return Color(0);
+ case SkBlendMode::kDst:
+ return dst;
+ case SkBlendMode::kSrc:
+ return src;
+ default:
+ break;
+ }
+ return sk_sp<SkShader>(new SkBlendShader(mode, std::move(dst), std::move(src)));
+}
+
+sk_sp<SkShader> SkShaders::Blend(sk_sp<SkBlender> blender,
+ sk_sp<SkShader> dst,
+ sk_sp<SkShader> src) {
+ if (!src || !dst) {
+ return nullptr;
+ }
+ if (!blender) {
+ return SkShaders::Blend(SkBlendMode::kSrcOver, std::move(dst), std::move(src));
+ }
+ if (std::optional<SkBlendMode> mode = as_BB(blender)->asBlendMode()) {
+ return sk_make_sp<SkBlendShader>(mode.value(), std::move(dst), std::move(src));
+ }
+
+#ifdef SK_ENABLE_SKSL
+ // This isn't a built-in blend mode; we might as well use a runtime effect to evaluate it.
+ static SkRuntimeEffect* sBlendEffect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
+ "uniform shader s, d;"
+ "uniform blender b;"
+ "half4 main(float2 xy) {"
+ "return b.eval(s.eval(xy), d.eval(xy));"
+ "}"
+ );
+ SkRuntimeEffect::ChildPtr children[] = {std::move(src), std::move(dst), std::move(blender)};
+ return sBlendEffect->makeShader(/*uniforms=*/{}, children);
+#else
+ // We need SkSL to render this blend.
+ return nullptr;
+#endif
+}
+
+void SkRegisterBlendShaderFlattenable() {
+ SK_REGISTER_FLATTENABLE(SkBlendShader);
+ // Previous name
+ SkFlattenable::Register("SkShader_Blend", SkBlendShader::CreateProc);
+}
diff --git a/src/shaders/SkBlendShader.h b/src/shaders/SkBlendShader.h
new file mode 100644
index 0000000..0cea61e
--- /dev/null
+++ b/src/shaders/SkBlendShader.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkBlendShader_DEFINED
+#define SkBlendShader_DEFINED
+
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkShader.h"
+#include "src/shaders/SkShaderBase.h"
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/Blend.h"
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif
+
+#if defined(SK_ENABLE_SKVM)
+#include "src/core/SkVM.h"
+#endif
+
+#include <utility>
+
+class SkReadBuffer;
+class SkWriteBuffer;
+enum class SkBlendMode;
+struct SkStageRec;
+
+class SkBlendShader final : public SkShaderBase {
+public:
+ SkBlendShader(SkBlendMode mode, sk_sp<SkShader> dst, sk_sp<SkShader> src)
+ : fDst(std::move(dst)), fSrc(std::move(src)), fMode(mode) {}
+
+ ShaderType type() const override { return ShaderType::kBlend; }
+
+#if defined(SK_GRAPHITE)
+ void addToKey(const skgpu::graphite::KeyContext&,
+ skgpu::graphite::PaintParamsKeyBuilder*,
+ skgpu::graphite::PipelineDataGatherer*) const override;
+#endif
+
+ sk_sp<SkShader> dst() const { return fDst; }
+ sk_sp<SkShader> src() const { return fSrc; }
+ SkBlendMode mode() const { return fMode; }
+
+protected:
+ SkBlendShader(SkReadBuffer&);
+ void flatten(SkWriteBuffer&) const override;
+ bool appendStages(const SkStageRec&, const SkShaders::MatrixRec&) const override;
+
+#if defined(SK_ENABLE_SKVM)
+ skvm::Color program(skvm::Builder*,
+ skvm::Coord device,
+ skvm::Coord local,
+ skvm::Color paint,
+ const SkShaders::MatrixRec& mRec,
+ const SkColorInfo& dst,
+ skvm::Uniforms*,
+ SkArenaAlloc*) const override;
+#endif // defined(SK_ENABLE_SKVM)
+
+private:
+ friend void ::SkRegisterBlendShaderFlattenable();
+ SK_FLATTENABLE_HOOKS(SkBlendShader)
+
+ sk_sp<SkShader> fDst;
+ sk_sp<SkShader> fSrc;
+ SkBlendMode fMode;
+};
+
+#endif
diff --git a/src/shaders/SkColorFilterShader.cpp b/src/shaders/SkColorFilterShader.cpp
index a862a87..4745cea 100644
--- a/src/shaders/SkColorFilterShader.cpp
+++ b/src/shaders/SkColorFilterShader.cpp
@@ -5,27 +5,26 @@
* found in the LICENSE file.
*/
-#include "include/core/SkShader.h"
-#include "include/core/SkString.h"
-#include "src/base/SkArenaAlloc.h"
-#include "src/core/SkRasterPipeline.h"
-#include "src/core/SkReadBuffer.h"
-#include "src/core/SkVM.h"
-#include "src/core/SkWriteBuffer.h"
-#include "src/effects/colorfilters/SkColorFilterBase.h"
#include "src/shaders/SkColorFilterShader.h"
-#if defined(SK_GANESH)
-#include "src/gpu/ganesh/GrFPArgs.h"
-#include "src/gpu/ganesh/GrFragmentProcessor.h"
-#include "src/gpu/ganesh/GrFragmentProcessors.h"
-#endif
+#include "include/core/SkColorFilter.h"
+#include "include/core/SkShader.h"
+#include "include/private/base/SkAssert.h"
+#include "src/base/SkArenaAlloc.h"
+#include "src/core/SkEffectPriv.h"
+#include "src/core/SkRasterPipeline.h"
+#include "src/core/SkRasterPipelineOpList.h"
+#include "src/core/SkReadBuffer.h"
+#include "src/core/SkWriteBuffer.h"
+#include "src/effects/colorfilters/SkColorFilterBase.h"
#if defined(SK_GRAPHITE)
#include "src/gpu/graphite/KeyHelpers.h"
#include "src/gpu/graphite/PaintParamsKey.h"
#endif
+#include <utility>
+
SkColorFilterShader::SkColorFilterShader(sk_sp<SkShader> shader,
float alpha,
sk_sp<SkColorFilter> filter)
@@ -56,7 +55,8 @@
buffer.writeFlattenable(fFilter.get());
}
-bool SkColorFilterShader::appendStages(const SkStageRec& rec, const MatrixRec& mRec) const {
+bool SkColorFilterShader::appendStages(const SkStageRec& rec,
+ const SkShaders::MatrixRec& mRec) const {
if (!as_SB(fShader)->appendStages(rec, mRec)) {
return false;
}
@@ -74,7 +74,7 @@
skvm::Coord device,
skvm::Coord local,
skvm::Color paint,
- const MatrixRec& mRec,
+ const SkShaders::MatrixRec& mRec,
const SkColorInfo& dst,
skvm::Uniforms* uniforms,
SkArenaAlloc* alloc) const {
@@ -96,29 +96,6 @@
return fFilter->program(p,c, dst, uniforms,alloc);
}
#endif
-#if defined(SK_GANESH)
-/////////////////////////////////////////////////////////////////////
-
-std::unique_ptr<GrFragmentProcessor>
-SkColorFilterShader::asFragmentProcessor(const GrFPArgs& args, const MatrixRec& mRec) const {
- auto shaderFP = as_SB(fShader)->asFragmentProcessor(args, mRec);
- if (!shaderFP) {
- return nullptr;
- }
-
- // TODO I guess, but it shouldn't come up as used today.
- SkASSERT(fAlpha == 1.0f);
-
- auto [success, fp] = GrFragmentProcessors::Make(args.fContext,
- fFilter.get(),
- std::move(shaderFP),
- *args.fDstColorInfo,
- args.fSurfaceProps);
- // If the filter FP could not be created, we still want to return the shader FP, so checking
- // success can be omitted here.
- return std::move(fp);
-}
-#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -137,14 +114,4 @@
builder->endBlock();
}
-#endif // SK_ENABLE_SKSL
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-sk_sp<SkShader> SkShader::makeWithColorFilter(sk_sp<SkColorFilter> filter) const {
- SkShader* base = const_cast<SkShader*>(this);
- if (!filter) {
- return sk_ref_sp(base);
- }
- return sk_make_sp<SkColorFilterShader>(sk_ref_sp(base), 1.0f, std::move(filter));
-}
+#endif // SK_ENABLE_SKSL
diff --git a/src/shaders/SkColorFilterShader.h b/src/shaders/SkColorFilterShader.h
index 34406bf..95e3af2 100644
--- a/src/shaders/SkColorFilterShader.h
+++ b/src/shaders/SkColorFilterShader.h
@@ -8,36 +8,44 @@
#ifndef SkColorFilterShader_DEFINED
#define SkColorFilterShader_DEFINED
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkShader.h"
#include "src/effects/colorfilters/SkColorFilterBase.h"
#include "src/shaders/SkShaderBase.h"
-class SkArenaAlloc;
+class SkColorFilter;
+class SkReadBuffer;
+class SkWriteBuffer;
+struct SkStageRec;
class SkColorFilterShader : public SkShaderBase {
public:
SkColorFilterShader(sk_sp<SkShader> shader, float alpha, sk_sp<SkColorFilter> filter);
-#if defined(SK_GANESH)
- std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&,
- const MatrixRec&) const override;
-#endif
+ ShaderType type() const override { return ShaderType::kColorFilter; }
+
#if defined(SK_GRAPHITE)
void addToKey(const skgpu::graphite::KeyContext&,
skgpu::graphite::PaintParamsKeyBuilder*,
skgpu::graphite::PipelineDataGatherer*) const override;
#endif
+ sk_sp<SkShader> shader() const { return fShader; }
+ sk_sp<SkColorFilterBase> filter() const { return fFilter; }
+ float alpha() const { return fAlpha; }
+
private:
bool isOpaque() const override;
void flatten(SkWriteBuffer&) const override;
- bool appendStages(const SkStageRec&, const MatrixRec&) const override;
+ bool appendStages(const SkStageRec&, const SkShaders::MatrixRec&) const override;
#if defined(SK_ENABLE_SKVM)
skvm::Color program(skvm::Builder*,
skvm::Coord device,
skvm::Coord local,
skvm::Color paint,
- const MatrixRec&,
+ const SkShaders::MatrixRec&,
const SkColorInfo& dst,
skvm::Uniforms* uniforms,
SkArenaAlloc*) const override;
@@ -47,9 +55,7 @@
sk_sp<SkShader> fShader;
sk_sp<SkColorFilterBase> fFilter;
- float fAlpha;
-
- using INHERITED = SkShaderBase;
+ float fAlpha;
};
#endif
diff --git a/src/shaders/SkColorShader.cpp b/src/shaders/SkColorShader.cpp
index 829b291..bd611e6 100644
--- a/src/shaders/SkColorShader.cpp
+++ b/src/shaders/SkColorShader.cpp
@@ -5,15 +5,20 @@
* found in the LICENSE file.
*/
+#include "src/shaders/SkColorShader.h"
+
+#include "include/core/SkAlphaType.h"
#include "include/core/SkColorSpace.h"
+#include "include/core/SkData.h"
#include "include/core/SkFlattenable.h"
-#include "src/base/SkArenaAlloc.h"
-#include "src/base/SkUtils.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkShader.h"
+#include "include/private/base/SkTPin.h"
#include "src/core/SkColorSpacePriv.h"
#include "src/core/SkColorSpaceXformSteps.h"
+#include "src/core/SkEffectPriv.h"
#include "src/core/SkRasterPipeline.h"
#include "src/core/SkReadBuffer.h"
-#include "src/core/SkVM.h"
#include "src/core/SkWriteBuffer.h"
#include "src/shaders/SkShaderBase.h"
@@ -22,100 +27,7 @@
#include "src/gpu/graphite/PaintParamsKey.h"
#endif
-/** \class SkColorShader
- A Shader that represents a single color. In general, this effect can be
- accomplished by just using the color field on the paint, but if an
- actual shader object is needed, this provides that feature.
-*/
-class SkColorShader : public SkShaderBase {
-public:
- /** Create a ColorShader that ignores the color in the paint, and uses the
- specified color. Note: like all shaders, at draw time the paint's alpha
- will be respected, and is applied to the specified color.
- */
- explicit SkColorShader(SkColor c);
-
- bool isOpaque() const override;
- bool isConstant() const override { return true; }
-
- GradientType asGradient(GradientInfo* info, SkMatrix* localMatrix) const override;
-
-#if defined(SK_GANESH)
- std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&,
- const MatrixRec&) const override;
-#endif
-
-#if defined(SK_GRAPHITE)
- void addToKey(const skgpu::graphite::KeyContext&,
- skgpu::graphite::PaintParamsKeyBuilder*,
- skgpu::graphite::PipelineDataGatherer*) const override;
-#endif
-
-private:
- friend void ::SkRegisterColorShaderFlattenable();
- SK_FLATTENABLE_HOOKS(SkColorShader)
-
- void flatten(SkWriteBuffer&) const override;
-
- bool onAsLuminanceColor(SkColor* lum) const override {
- *lum = fColor;
- return true;
- }
-
- bool appendStages(const SkStageRec&, const MatrixRec&) const override;
-
-#if defined(SK_ENABLE_SKVM)
- skvm::Color program(skvm::Builder*,
- skvm::Coord device,
- skvm::Coord local,
- skvm::Color paint,
- const MatrixRec&,
- const SkColorInfo& dst,
- skvm::Uniforms* uniforms,
- SkArenaAlloc*) const override;
-#endif
-
- SkColor fColor;
-};
-
-class SkColor4Shader : public SkShaderBase {
-public:
- SkColor4Shader(const SkColor4f&, sk_sp<SkColorSpace>);
-
- bool isOpaque() const override { return fColor.isOpaque(); }
- bool isConstant() const override { return true; }
-
-#if defined(SK_GANESH)
- std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&,
- const MatrixRec&) const override;
-#endif
-#if defined(SK_GRAPHITE)
- void addToKey(const skgpu::graphite::KeyContext&,
- skgpu::graphite::PaintParamsKeyBuilder*,
- skgpu::graphite::PipelineDataGatherer*) const override;
-#endif
-
-private:
- friend void ::SkRegisterColor4ShaderFlattenable();
- SK_FLATTENABLE_HOOKS(SkColor4Shader)
-
- void flatten(SkWriteBuffer&) const override;
- bool appendStages(const SkStageRec&, const MatrixRec&) const override;
-
-#if defined(SK_ENABLE_SKVM)
- skvm::Color program(skvm::Builder*,
- skvm::Coord device,
- skvm::Coord local,
- skvm::Color paint,
- const MatrixRec&,
- const SkColorInfo& dst,
- skvm::Uniforms* uniforms,
- SkArenaAlloc*) const override;
-#endif
-
- sk_sp<SkColorSpace> fColorSpace;
- const SkColor4f fColor;
-};
+#include <utility>
SkColorShader::SkColorShader(SkColor c) : fColor(c) {}
@@ -131,21 +43,6 @@
buffer.writeColor(fColor);
}
-SkShaderBase::GradientType SkColorShader::asGradient(GradientInfo* info,
- SkMatrix* localMatrix) const {
- if (info) {
- if (info->fColors && info->fColorCount >= 1) {
- info->fColors[0] = fColor;
- }
- info->fColorCount = 1;
- info->fTileMode = SkTileMode::kRepeat;
- }
- if (localMatrix) {
- *localMatrix = SkMatrix::I();
- }
- return GradientType::kColor;
-}
-
SkColor4Shader::SkColor4Shader(const SkColor4f& color, sk_sp<SkColorSpace> space)
: fColorSpace(std::move(space))
, fColor({color.fR, color.fG, color.fB, SkTPin(color.fA, 0.0f, 1.0f)})
@@ -173,7 +70,7 @@
}
}
-bool SkColorShader::appendStages(const SkStageRec& rec, const MatrixRec&) const {
+bool SkColorShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec&) const {
SkColor4f color = SkColor4f::FromColor(fColor);
SkColorSpaceXformSteps(sk_srgb_singleton(), kUnpremul_SkAlphaType,
rec.fDstCS, kUnpremul_SkAlphaType).apply(color.vec());
@@ -181,7 +78,7 @@
return true;
}
-bool SkColor4Shader::appendStages(const SkStageRec& rec, const MatrixRec&) const {
+bool SkColor4Shader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec&) const {
SkColor4f color = fColor;
SkColorSpaceXformSteps(fColorSpace.get(), kUnpremul_SkAlphaType,
rec.fDstCS, kUnpremul_SkAlphaType).apply(color.vec());
@@ -194,7 +91,7 @@
skvm::Coord /*device*/,
skvm::Coord /*local*/,
skvm::Color /*paint*/,
- const MatrixRec&,
+ const SkShaders::MatrixRec&,
const SkColorInfo& dst,
skvm::Uniforms* uniforms,
SkArenaAlloc*) const {
@@ -208,7 +105,7 @@
skvm::Coord /*device*/,
skvm::Coord /*local*/,
skvm::Color /*paint*/,
- const MatrixRec&,
+ const SkShaders::MatrixRec&,
const SkColorInfo& dst,
skvm::Uniforms* uniforms,
SkArenaAlloc*) const {
@@ -219,30 +116,6 @@
}
#endif // defined(SK_ENABLE_SKVM)
-#if defined(SK_GANESH)
-
-#include "src/gpu/ganesh/GrColorInfo.h"
-#include "src/gpu/ganesh/GrColorSpaceXform.h"
-#include "src/gpu/ganesh/GrFPArgs.h"
-#include "src/gpu/ganesh/GrFragmentProcessor.h"
-#include "src/gpu/ganesh/SkGr.h"
-
-std::unique_ptr<GrFragmentProcessor> SkColorShader::asFragmentProcessor(const GrFPArgs& args,
- const MatrixRec&) const {
- return GrFragmentProcessor::MakeColor(SkColorToPMColor4f(fColor, *args.fDstColorInfo));
-}
-
-std::unique_ptr<GrFragmentProcessor> SkColor4Shader::asFragmentProcessor(const GrFPArgs& args,
- const MatrixRec&) const {
- SkColorSpaceXformSteps steps{ fColorSpace.get(), kUnpremul_SkAlphaType,
- args.fDstColorInfo->colorSpace(), kUnpremul_SkAlphaType };
- SkColor4f color = fColor;
- steps.apply(color.vec());
- return GrFragmentProcessor::MakeColor(color.premul());
-}
-
-#endif
-
#if defined(SK_GRAPHITE)
void SkColorShader::addToKey(const skgpu::graphite::KeyContext& keyContext,
skgpu::graphite::PaintParamsKeyBuilder* builder,
@@ -264,14 +137,39 @@
}
#endif
-sk_sp<SkShader> SkShaders::Color(SkColor color) { return sk_make_sp<SkColorShader>(color); }
+SkUpdatableColorShader::SkUpdatableColorShader(SkColorSpace* cs)
+ : fSteps{sk_srgb_singleton(), kUnpremul_SkAlphaType, cs, kUnpremul_SkAlphaType} {}
-sk_sp<SkShader> SkShaders::Color(const SkColor4f& color, sk_sp<SkColorSpace> space) {
- if (!SkScalarsAreFinite(color.vec(), 4)) {
- return nullptr;
- }
- return sk_make_sp<SkColor4Shader>(color, std::move(space));
+#if defined(SK_ENABLE_SKVM)
+skvm::Color SkUpdatableColorShader::program(skvm::Builder* builder,
+ skvm::Coord device,
+ skvm::Coord local,
+ skvm::Color paint,
+ const SkShaders::MatrixRec&,
+ const SkColorInfo& dst,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc* alloc) const {
+ skvm::Uniform color = uniforms->pushPtr(fValues);
+ skvm::F32 r = builder->arrayF(color, 0);
+ skvm::F32 g = builder->arrayF(color, 1);
+ skvm::F32 b = builder->arrayF(color, 2);
+ skvm::F32 a = builder->arrayF(color, 3);
+
+ return {r, g, b, a};
}
+#endif
+
+void SkUpdatableColorShader::updateColor(SkColor c) const {
+ SkColor4f c4 = SkColor4f::FromColor(c);
+ fSteps.apply(c4.vec());
+ auto cp4 = c4.premul();
+ fValues[0] = cp4.fR;
+ fValues[1] = cp4.fG;
+ fValues[2] = cp4.fB;
+ fValues[3] = cp4.fA;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
void SkRegisterColor4ShaderFlattenable() {
SK_REGISTER_FLATTENABLE(SkColor4Shader);
@@ -280,3 +178,14 @@
void SkRegisterColorShaderFlattenable() {
SK_REGISTER_FLATTENABLE(SkColorShader);
}
+
+namespace SkShaders {
+sk_sp<SkShader> Color(SkColor color) { return sk_make_sp<SkColorShader>(color); }
+
+sk_sp<SkShader> Color(const SkColor4f& color, sk_sp<SkColorSpace> space) {
+ if (!SkScalarsAreFinite(color.vec(), 4)) {
+ return nullptr;
+ }
+ return sk_make_sp<SkColor4Shader>(color, std::move(space));
+}
+} // namespace SkShaders
diff --git a/src/shaders/SkColorShader.h b/src/shaders/SkColorShader.h
new file mode 100644
index 0000000..c5fedc5
--- /dev/null
+++ b/src/shaders/SkColorShader.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkColorShader_DEFINED
+#define SkColorShader_DEFINED
+
+#include "include/core/SkColor.h"
+#include "include/core/SkColorSpace.h"
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkRefCnt.h"
+#include "src/core/SkColorSpaceXformSteps.h"
+#include "src/shaders/SkShaderBase.h"
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif
+
+class SkReadBuffer;
+class SkWriteBuffer;
+struct SkStageRec;
+
+/** \class SkColorShader
+ A Shader that represents a single color. In general, this effect can be
+ accomplished by just using the color field on the paint, but if an
+ actual shader object is needed, this provides that feature.
+*/
+class SkColorShader : public SkShaderBase {
+public:
+ /** Create a ColorShader that ignores the color in the paint, and uses the
+ specified color. Note: like all shaders, at draw time the paint's alpha
+ will be respected, and is applied to the specified color.
+ */
+ explicit SkColorShader(SkColor c);
+
+ bool isOpaque() const override;
+ bool isConstant() const override { return true; }
+
+ ShaderType type() const override { return ShaderType::kColor; }
+
+#if defined(SK_GRAPHITE)
+ void addToKey(const skgpu::graphite::KeyContext&,
+ skgpu::graphite::PaintParamsKeyBuilder*,
+ skgpu::graphite::PipelineDataGatherer*) const override;
+#endif
+
+ SkColor color() const { return fColor; }
+
+private:
+ friend void ::SkRegisterColorShaderFlattenable();
+ SK_FLATTENABLE_HOOKS(SkColorShader)
+
+ void flatten(SkWriteBuffer&) const override;
+
+ bool onAsLuminanceColor(SkColor* lum) const override {
+ *lum = fColor;
+ return true;
+ }
+
+ bool appendStages(const SkStageRec&, const SkShaders::MatrixRec&) const override;
+
+#if defined(SK_ENABLE_SKVM)
+ skvm::Color program(skvm::Builder*,
+ skvm::Coord device,
+ skvm::Coord local,
+ skvm::Color paint,
+ const SkShaders::MatrixRec&,
+ const SkColorInfo& dst,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc*) const override;
+#endif
+
+ SkColor fColor;
+};
+
+class SkColor4Shader : public SkShaderBase {
+public:
+ SkColor4Shader(const SkColor4f&, sk_sp<SkColorSpace>);
+
+ bool isOpaque() const override { return fColor.isOpaque(); }
+ bool isConstant() const override { return true; }
+
+ ShaderType type() const override { return ShaderType::kColor4; }
+
+#if defined(SK_GRAPHITE)
+ void addToKey(const skgpu::graphite::KeyContext&,
+ skgpu::graphite::PaintParamsKeyBuilder*,
+ skgpu::graphite::PipelineDataGatherer*) const override;
+#endif
+
+ sk_sp<SkColorSpace> colorSpace() const { return fColorSpace; }
+ SkColor4f color() const { return fColor; }
+
+private:
+ friend void ::SkRegisterColor4ShaderFlattenable();
+ SK_FLATTENABLE_HOOKS(SkColor4Shader)
+
+ void flatten(SkWriteBuffer&) const override;
+ bool appendStages(const SkStageRec&, const SkShaders::MatrixRec&) const override;
+
+#if defined(SK_ENABLE_SKVM)
+ skvm::Color program(skvm::Builder*,
+ skvm::Coord device,
+ skvm::Coord local,
+ skvm::Color paint,
+ const SkShaders::MatrixRec&,
+ const SkColorInfo& dst,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc*) const override;
+#endif
+
+ sk_sp<SkColorSpace> fColorSpace;
+ const SkColor4f fColor;
+};
+
+class SkUpdatableColorShader : public SkShaderBase {
+public:
+ explicit SkUpdatableColorShader(SkColorSpace* cs);
+#if defined(SK_ENABLE_SKVM)
+ skvm::Color program(skvm::Builder* builder,
+ skvm::Coord device,
+ skvm::Coord local,
+ skvm::Color paint,
+ const SkShaders::MatrixRec&,
+ const SkColorInfo& dst,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc* alloc) const override;
+#endif
+
+ ShaderType type() const override { return ShaderType::kUpdatableColor; }
+
+ void updateColor(SkColor c) const;
+
+private:
+ // For serialization. This will never be called.
+ Factory getFactory() const override { return nullptr; }
+ const char* getTypeName() const override { return nullptr; }
+
+ SkColorSpaceXformSteps fSteps;
+ mutable float fValues[4];
+};
+
+#endif
diff --git a/src/shaders/SkComposeShader.cpp b/src/shaders/SkComposeShader.cpp
deleted file mode 100644
index 767aa5e..0000000
--- a/src/shaders/SkComposeShader.cpp
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "include/core/SkColorFilter.h"
-#include "include/core/SkFlattenable.h"
-#include "include/core/SkString.h"
-#include "include/effects/SkRuntimeEffect.h"
-#include "include/private/SkColorData.h"
-#include "src/base/SkArenaAlloc.h"
-#include "src/core/SkBlendModePriv.h"
-#include "src/core/SkBlenderBase.h"
-#include "src/core/SkRasterPipeline.h"
-#include "src/core/SkReadBuffer.h"
-#include "src/core/SkRuntimeEffectPriv.h"
-#include "src/core/SkVM.h"
-#include "src/core/SkWriteBuffer.h"
-#include "src/shaders/SkShaderBase.h"
-
-#if defined(SK_GRAPHITE)
-#include "src/gpu/Blend.h"
-#include "src/gpu/graphite/KeyHelpers.h"
-#include "src/gpu/graphite/PaintParamsKey.h"
-#endif
-
-class SkShader_Blend final : public SkShaderBase {
-public:
- SkShader_Blend(SkBlendMode mode, sk_sp<SkShader> dst, sk_sp<SkShader> src)
- : fDst(std::move(dst))
- , fSrc(std::move(src))
- , fMode(mode) {}
-
-#if defined(SK_GANESH)
- std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&,
- const MatrixRec&) const override;
-#endif
-
-#if defined(SK_GRAPHITE)
- void addToKey(const skgpu::graphite::KeyContext&,
- skgpu::graphite::PaintParamsKeyBuilder*,
- skgpu::graphite::PipelineDataGatherer*) const override;
-#endif
-
-protected:
- SkShader_Blend(SkReadBuffer&);
- void flatten(SkWriteBuffer&) const override;
- bool appendStages(const SkStageRec&, const MatrixRec&) const override;
-
-#if defined(SK_ENABLE_SKVM)
- skvm::Color program(skvm::Builder*,
- skvm::Coord device,
- skvm::Coord local,
- skvm::Color paint,
- const MatrixRec& mRec,
- const SkColorInfo& dst,
- skvm::Uniforms*,
- SkArenaAlloc*) const override;
-#endif // defined(SK_ENABLE_SKVM)
-
-private:
- friend void ::SkRegisterComposeShaderFlattenable();
- SK_FLATTENABLE_HOOKS(SkShader_Blend)
-
- sk_sp<SkShader> fDst;
- sk_sp<SkShader> fSrc;
- SkBlendMode fMode;
-
- using INHERITED = SkShaderBase;
-};
-
-sk_sp<SkFlattenable> SkShader_Blend::CreateProc(SkReadBuffer& buffer) {
- sk_sp<SkShader> dst(buffer.readShader());
- sk_sp<SkShader> src(buffer.readShader());
- if (!buffer.validate(dst && src)) {
- return nullptr;
- }
-
- unsigned mode = buffer.read32();
-
- if (mode == kCustom_SkBlendMode) {
- sk_sp<SkBlender> blender = buffer.readBlender();
- if (buffer.validate(blender != nullptr)) {
- return SkShaders::Blend(std::move(blender), std::move(dst), std::move(src));
- }
- } else {
- if (buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode)) {
- return SkShaders::Blend(static_cast<SkBlendMode>(mode), std::move(dst), std::move(src));
- }
- }
- return nullptr;
-}
-
-void SkShader_Blend::flatten(SkWriteBuffer& buffer) const {
- buffer.writeFlattenable(fDst.get());
- buffer.writeFlattenable(fSrc.get());
- buffer.write32((int)fMode);
-}
-
-// Returns the output of e0, and leaves the output of e1 in r,g,b,a
-static float* append_two_shaders(const SkStageRec& rec,
- const SkShaderBase::MatrixRec& mRec,
- SkShader* s0,
- SkShader* s1) {
- struct Storage {
- float fCoords[2 * SkRasterPipeline_kMaxStride];
- float fRes0 [4 * SkRasterPipeline_kMaxStride];
- };
- auto storage = rec.fAlloc->make<Storage>();
-
- // Note we cannot simply apply mRec here and then unconditionally store the coordinates. When
- // building for Android Framework it would interrupt the backwards local matrix concatenation if
- // mRec had a pending local matrix and either of the children also had a local matrix.
- // b/256873449
- if (mRec.rasterPipelineCoordsAreSeeded()) {
- rec.fPipeline->append(SkRasterPipelineOp::store_src_rg, storage->fCoords);
- }
- if (!as_SB(s0)->appendStages(rec, mRec)) {
- return nullptr;
- }
- rec.fPipeline->append(SkRasterPipelineOp::store_src, storage->fRes0);
-
- if (mRec.rasterPipelineCoordsAreSeeded()) {
- rec.fPipeline->append(SkRasterPipelineOp::load_src_rg, storage->fCoords);
- }
- if (!as_SB(s1)->appendStages(rec, mRec)) {
- return nullptr;
- }
- return storage->fRes0;
-}
-
-bool SkShader_Blend::appendStages(const SkStageRec& rec, const MatrixRec& mRec) const {
- float* res0 = append_two_shaders(rec, mRec, fDst.get(), fSrc.get());
- if (!res0) {
- return false;
- }
-
- rec.fPipeline->append(SkRasterPipelineOp::load_dst, res0);
- SkBlendMode_AppendStages(fMode, rec.fPipeline);
- return true;
-}
-
-#if defined(SK_ENABLE_SKVM)
-skvm::Color SkShader_Blend::program(skvm::Builder* p,
- skvm::Coord device,
- skvm::Coord local,
- skvm::Color paint,
- const MatrixRec& mRec,
- const SkColorInfo& cinfo,
- skvm::Uniforms* uniforms,
- SkArenaAlloc* alloc) const {
- skvm::Color d,s;
- if ((d = as_SB(fDst)->program(p, device, local, paint, mRec, cinfo, uniforms, alloc)) &&
- (s = as_SB(fSrc)->program(p, device, local, paint, mRec, cinfo, uniforms, alloc))) {
- return p->blend(fMode, s,d);
- }
- return {};
-}
-#endif
-
-#if defined(SK_GANESH)
-
-#include "include/gpu/GrRecordingContext.h"
-#include "src/gpu/ganesh/GrFPArgs.h"
-#include "src/gpu/ganesh/GrFragmentProcessor.h"
-#include "src/gpu/ganesh/effects/GrBlendFragmentProcessor.h"
-
-std::unique_ptr<GrFragmentProcessor>
-SkShader_Blend::asFragmentProcessor(const GrFPArgs& args, const MatrixRec& mRec) const {
- auto fpA = as_SB(fDst)->asFragmentProcessor(args, mRec);
- auto fpB = as_SB(fSrc)->asFragmentProcessor(args, mRec);
- if (!fpA || !fpB) {
- // This is unexpected. Both src and dst shaders should be valid. Just fail.
- return nullptr;
- }
- return GrBlendFragmentProcessor::Make(std::move(fpB), std::move(fpA), fMode);
-}
-#endif
-
-#if defined(SK_GRAPHITE)
-void SkShader_Blend::addToKey(const skgpu::graphite::KeyContext& keyContext,
- skgpu::graphite::PaintParamsKeyBuilder* builder,
- skgpu::graphite::PipelineDataGatherer* gatherer) const {
- using namespace skgpu::graphite;
-
- BlendShaderBlock::BeginBlock(keyContext, builder, gatherer);
-
- as_SB(fSrc)->addToKey(keyContext, builder, gatherer);
- as_SB(fDst)->addToKey(keyContext, builder, gatherer);
-
- SkSpan<const float> porterDuffConstants = skgpu::GetPorterDuffBlendConstants(fMode);
- if (!porterDuffConstants.empty()) {
- CoeffBlenderBlock::BeginBlock(keyContext, builder, gatherer, porterDuffConstants);
- builder->endBlock();
- } else {
- BlendModeBlenderBlock::BeginBlock(keyContext, builder, gatherer, fMode);
- builder->endBlock();
- }
-
- builder->endBlock(); // BlendShaderBlock
-}
-#endif
-
-sk_sp<SkShader> SkShaders::Blend(SkBlendMode mode, sk_sp<SkShader> dst, sk_sp<SkShader> src) {
- if (!src || !dst) {
- return nullptr;
- }
- switch (mode) {
- case SkBlendMode::kClear: return Color(0);
- case SkBlendMode::kDst: return dst;
- case SkBlendMode::kSrc: return src;
- default: break;
- }
- return sk_sp<SkShader>(new SkShader_Blend(mode, std::move(dst), std::move(src)));
-}
-
-sk_sp<SkShader> SkShaders::Blend(sk_sp<SkBlender> blender,
- sk_sp<SkShader> dst,
- sk_sp<SkShader> src) {
- if (!src || !dst) {
- return nullptr;
- }
- if (!blender) {
- return SkShaders::Blend(SkBlendMode::kSrcOver, std::move(dst), std::move(src));
- }
- if (std::optional<SkBlendMode> mode = as_BB(blender)->asBlendMode()) {
- return sk_make_sp<SkShader_Blend>(mode.value(), std::move(dst), std::move(src));
- }
-
-#ifdef SK_ENABLE_SKSL
- // This isn't a built-in blend mode; we might as well use a runtime effect to evaluate it.
- static SkRuntimeEffect* sBlendEffect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
- "uniform shader s, d;"
- "uniform blender b;"
- "half4 main(float2 xy) {"
- "return b.eval(s.eval(xy), d.eval(xy));"
- "}"
- );
- SkRuntimeEffect::ChildPtr children[] = {std::move(src), std::move(dst), std::move(blender)};
- return sBlendEffect->makeShader(/*uniforms=*/{}, children);
-#else
- // We need SkSL to render this blend.
- return nullptr;
-#endif
-}
-
-void SkRegisterComposeShaderFlattenable() {
- SK_REGISTER_FLATTENABLE(SkShader_Blend);
-}
diff --git a/src/shaders/SkCoordClampShader.cpp b/src/shaders/SkCoordClampShader.cpp
index a9ee141..9c5336d 100644
--- a/src/shaders/SkCoordClampShader.cpp
+++ b/src/shaders/SkCoordClampShader.cpp
@@ -5,67 +5,32 @@
* found in the LICENSE file.
*/
+#include "src/shaders/SkCoordClampShader.h"
+
#include "include/core/SkFlattenable.h"
+#include "include/private/base/SkTo.h"
#include "src/base/SkArenaAlloc.h"
+#include "src/core/SkEffectPriv.h"
#include "src/core/SkRasterPipeline.h"
+#include "src/core/SkRasterPipelineOpContexts.h"
+#include "src/core/SkRasterPipelineOpList.h"
#include "src/core/SkReadBuffer.h"
-#include "src/core/SkRuntimeEffectPriv.h"
-#include "src/core/SkVM.h"
#include "src/core/SkWriteBuffer.h"
#include "src/shaders/SkShaderBase.h"
-#if defined(SK_GANESH)
-#include "include/effects/SkRuntimeEffect.h"
-#include "include/gpu/GrRecordingContext.h"
-#include "src/gpu/ganesh/GrFPArgs.h"
-#include "src/gpu/ganesh/GrFragmentProcessor.h"
-#include "src/gpu/ganesh/effects/GrSkSLFP.h"
-#endif
-
#if defined(SK_GRAPHITE)
#include "src/gpu/graphite/KeyHelpers.h"
#include "src/gpu/graphite/PaintParamsKey.h"
#endif // SK_GRAPHITE
-class SkShader_CoordClamp final : public SkShaderBase {
-public:
- SkShader_CoordClamp(sk_sp<SkShader> shader, const SkRect& subset)
- : fShader(std::move(shader)), fSubset(subset) {}
-
-#if defined(SK_GANESH)
- std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&,
- const MatrixRec&) const override;
-#endif
-#if defined(SK_GRAPHITE)
- void addToKey(const skgpu::graphite::KeyContext&,
- skgpu::graphite::PaintParamsKeyBuilder*,
- skgpu::graphite::PipelineDataGatherer*) const override;
-#endif
-
-protected:
- SkShader_CoordClamp(SkReadBuffer&);
- void flatten(SkWriteBuffer&) const override;
- bool appendStages(const SkStageRec&, const MatrixRec&) const override;
#if defined(SK_ENABLE_SKVM)
- skvm::Color program(skvm::Builder*,
- skvm::Coord device,
- skvm::Coord local,
- skvm::Color paint,
- const MatrixRec&,
- const SkColorInfo& dst,
- skvm::Uniforms*,
- SkArenaAlloc*) const override;
+#include "src/core/SkRuntimeEffectPriv.h"
+#include "src/core/SkVM.h"
#endif
-private:
- friend void ::SkRegisterCoordClampShaderFlattenable();
- SK_FLATTENABLE_HOOKS(SkShader_CoordClamp)
+#include <optional>
- sk_sp<SkShader> fShader;
- SkRect fSubset;
-};
-
-sk_sp<SkFlattenable> SkShader_CoordClamp::CreateProc(SkReadBuffer& buffer) {
+sk_sp<SkFlattenable> SkCoordClampShader::CreateProc(SkReadBuffer& buffer) {
sk_sp<SkShader> shader(buffer.readShader());
SkRect subset = buffer.readRect();
if (!buffer.validate(SkToBool(shader))) {
@@ -74,13 +39,14 @@
return SkShaders::CoordClamp(std::move(shader), subset);
}
-void SkShader_CoordClamp::flatten(SkWriteBuffer& buffer) const {
+void SkCoordClampShader::flatten(SkWriteBuffer& buffer) const {
buffer.writeFlattenable(fShader.get());
buffer.writeRect(fSubset);
}
-bool SkShader_CoordClamp::appendStages(const SkStageRec& rec, const MatrixRec& mRec) const {
- std::optional<MatrixRec> childMRec = mRec.apply(rec);
+bool SkCoordClampShader::appendStages(const SkStageRec& rec,
+ const SkShaders::MatrixRec& mRec) const {
+ std::optional<SkShaders::MatrixRec> childMRec = mRec.apply(rec);
if (!childMRec.has_value()) {
return false;
}
@@ -94,15 +60,15 @@
}
#if defined(SK_ENABLE_SKVM)
-skvm::Color SkShader_CoordClamp::program(skvm::Builder* p,
- skvm::Coord device,
- skvm::Coord local,
- skvm::Color paint,
- const MatrixRec& mRec,
- const SkColorInfo& cinfo,
- skvm::Uniforms* uniforms,
- SkArenaAlloc* alloc) const {
- std::optional<MatrixRec> childMRec = mRec.apply(p, &local, uniforms);
+skvm::Color SkCoordClampShader::program(skvm::Builder* p,
+ skvm::Coord device,
+ skvm::Coord local,
+ skvm::Color paint,
+ const SkShaders::MatrixRec& mRec,
+ const SkColorInfo& cinfo,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc* alloc) const {
+ std::optional<SkShaders::MatrixRec> childMRec = mRec.apply(p, &local, uniforms);
if (!childMRec.has_value()) {
return {};
}
@@ -120,44 +86,10 @@
}
#endif
-#if defined(SK_GANESH)
-std::unique_ptr<GrFragmentProcessor> SkShader_CoordClamp::asFragmentProcessor(
- const GrFPArgs& args, const MatrixRec& mRec) const {
- static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
- "uniform shader c;"
- "uniform float4 s;"
- "half4 main(float2 p) {"
- "return c.eval(clamp(p, s.LT, s.RB));"
- "}");
-
- auto fp = as_SB(fShader)->asFragmentProcessor(args, mRec.applied());
- if (!fp) {
- return nullptr;
- }
-
- GrSkSLFP::OptFlags flags = GrSkSLFP::OptFlags::kNone;
- if (fp->compatibleWithCoverageAsAlpha()) {
- flags |= GrSkSLFP::OptFlags::kCompatibleWithCoverageAsAlpha;
- }
- if (fp->preservesOpaqueInput()) {
- flags |= GrSkSLFP::OptFlags::kPreservesOpaqueInput;
- }
- fp = GrSkSLFP::Make(effect,
- "clamp_fp",
- /*inputFP=*/nullptr,
- flags,
- "c", std::move(fp),
- "s", fSubset);
- bool success;
- std::tie(success, fp) = mRec.apply(std::move(fp));
- return success ? std::move(fp) : nullptr;
-}
-#endif // defined(SK_GANESH)
-
#if defined(SK_GRAPHITE)
-void SkShader_CoordClamp::addToKey(const skgpu::graphite::KeyContext& keyContext,
- skgpu::graphite::PaintParamsKeyBuilder* builder,
- skgpu::graphite::PipelineDataGatherer* gatherer) const {
+void SkCoordClampShader::addToKey(const skgpu::graphite::KeyContext& keyContext,
+ skgpu::graphite::PaintParamsKeyBuilder* builder,
+ skgpu::graphite::PipelineDataGatherer* gatherer) const {
using namespace skgpu::graphite;
CoordClampShaderBlock::CoordClampData data(fSubset);
@@ -168,7 +100,12 @@
}
#endif // SK_GRAPHITE
-void SkRegisterCoordClampShaderFlattenable() { SK_REGISTER_FLATTENABLE(SkShader_CoordClamp); }
+void SkRegisterCoordClampShaderFlattenable() {
+ SK_REGISTER_FLATTENABLE(SkCoordClampShader);
+
+ // Previous name
+ SkFlattenable::Register("SkShader_CoordClamp", SkCoordClampShader::CreateProc);
+}
sk_sp<SkShader> SkShaders::CoordClamp(sk_sp<SkShader> shader, const SkRect& subset) {
if (!shader) {
@@ -177,5 +114,5 @@
if (!subset.isSorted()) {
return nullptr;
}
- return sk_make_sp<SkShader_CoordClamp>(std::move(shader), subset);
+ return sk_make_sp<SkCoordClampShader>(std::move(shader), subset);
}
diff --git a/src/shaders/SkCoordClampShader.h b/src/shaders/SkCoordClampShader.h
new file mode 100644
index 0000000..b44e7af
--- /dev/null
+++ b/src/shaders/SkCoordClampShader.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkCoordClampShader_DEFINED
+#define SkCoordClampShader_DEFINED
+
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkRect.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkShader.h"
+#include "src/shaders/SkShaderBase.h"
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif // SK_GRAPHITE
+
+#include <utility>
+
+class SkReadBuffer;
+class SkWriteBuffer;
+struct SkStageRec;
+
+class SkCoordClampShader final : public SkShaderBase {
+public:
+ SkCoordClampShader(sk_sp<SkShader> shader, const SkRect& subset)
+ : fShader(std::move(shader)), fSubset(subset) {}
+
+ ShaderType type() const override { return ShaderType::kCoordClamp; }
+
+#if defined(SK_GRAPHITE)
+ void addToKey(const skgpu::graphite::KeyContext&,
+ skgpu::graphite::PaintParamsKeyBuilder*,
+ skgpu::graphite::PipelineDataGatherer*) const override;
+#endif
+
+ sk_sp<SkShader> shader() const { return fShader; }
+ SkRect subset() const { return fSubset; }
+
+protected:
+ SkCoordClampShader(SkReadBuffer&);
+ void flatten(SkWriteBuffer&) const override;
+ bool appendStages(const SkStageRec&, const SkShaders::MatrixRec&) const override;
+#if defined(SK_ENABLE_SKVM)
+ skvm::Color program(skvm::Builder*,
+ skvm::Coord device,
+ skvm::Coord local,
+ skvm::Color paint,
+ const SkShaders::MatrixRec&,
+ const SkColorInfo& dst,
+ skvm::Uniforms*,
+ SkArenaAlloc*) const override;
+#endif
+
+private:
+ friend void ::SkRegisterCoordClampShaderFlattenable();
+ SK_FLATTENABLE_HOOKS(SkCoordClampShader)
+
+ sk_sp<SkShader> fShader;
+ SkRect fSubset;
+};
+
+#endif
diff --git a/src/shaders/SkEmptyShader.cpp b/src/shaders/SkEmptyShader.cpp
index 9c836b8..7853d3c 100644
--- a/src/shaders/SkEmptyShader.cpp
+++ b/src/shaders/SkEmptyShader.cpp
@@ -5,52 +5,20 @@
* found in the LICENSE file.
*/
-#include "src/shaders/SkShaderBase.h"
+#include "src/shaders/SkEmptyShader.h"
#include "include/core/SkFlattenable.h"
-#include "src/core/SkVM.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkShader.h"
-/**
- * \class SkEmptyShader
- * A Shader that always draws nothing. Its createContext always returns nullptr.
- */
-class SkEmptyShader : public SkShaderBase {
-public:
- SkEmptyShader() {}
-
-protected:
- void flatten(SkWriteBuffer& buffer) const override {
- // Do nothing.
- // We just don't want to fall through to SkShader::flatten(),
- // which will write data we don't care to serialize or decode.
- }
-
- bool appendStages(const SkStageRec&, const MatrixRec&) const override { return false; }
-
-#if defined(SK_ENABLE_SKVM)
- skvm::Color program(skvm::Builder*,
- skvm::Coord,
- skvm::Coord,
- skvm::Color,
- const MatrixRec&,
- const SkColorInfo&,
- skvm::Uniforms*,
- SkArenaAlloc*) const override;
-#endif
-
-private:
- friend void ::SkRegisterEmptyShaderFlattenable();
- SK_FLATTENABLE_HOOKS(SkEmptyShader)
-
- using INHERITED = SkShaderBase;
-};
+class SkReadBuffer;
#if defined(SK_ENABLE_SKVM)
skvm::Color SkEmptyShader::program(skvm::Builder*,
skvm::Coord,
skvm::Coord,
skvm::Color,
- const MatrixRec&,
+ const SkShaders::MatrixRec&,
const SkColorInfo&,
skvm::Uniforms*,
SkArenaAlloc*) const {
diff --git a/src/shaders/SkEmptyShader.h b/src/shaders/SkEmptyShader.h
new file mode 100644
index 0000000..ae36c62
--- /dev/null
+++ b/src/shaders/SkEmptyShader.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/shaders/SkShaderBase.h"
+
+#include "include/core/SkFlattenable.h"
+
+#if defined(SK_ENABLE_SKVM)
+#include "src/core/SkVM.h"
+#endif
+
+class SkReadBuffer;
+class SkWriteBuffer;
+struct SkStageRec;
+
+/**
+ * \class SkEmptyShader
+ * A Shader that always draws nothing. Its createContext always returns nullptr.
+ */
+class SkEmptyShader : public SkShaderBase {
+public:
+ SkEmptyShader() {}
+
+protected:
+ void flatten(SkWriteBuffer& buffer) const override {
+ // Do nothing.
+ // We just don't want to fall through to SkShader::flatten(),
+ // which will write data we don't care to serialize or decode.
+ }
+
+ bool appendStages(const SkStageRec&, const SkShaders::MatrixRec&) const override {
+ return false;
+ }
+
+ ShaderType type() const override { return ShaderType::kEmpty; }
+
+#if defined(SK_ENABLE_SKVM)
+ skvm::Color program(skvm::Builder*,
+ skvm::Coord,
+ skvm::Coord,
+ skvm::Color,
+ const SkShaders::MatrixRec&,
+ const SkColorInfo&,
+ skvm::Uniforms*,
+ SkArenaAlloc*) const override;
+#endif
+
+private:
+ friend void ::SkRegisterEmptyShaderFlattenable();
+ SK_FLATTENABLE_HOOKS(SkEmptyShader)
+};
diff --git a/src/shaders/SkGainmapShader.cpp b/src/shaders/SkGainmapShader.cpp
index 16ccd58..e2cf426 100644
--- a/src/shaders/SkGainmapShader.cpp
+++ b/src/shaders/SkGainmapShader.cpp
@@ -7,14 +7,22 @@
#include "include/private/SkGainmapShader.h"
+#include "include/core/SkColor.h"
+#include "include/core/SkColorFilter.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkImage.h"
+#include "include/core/SkMatrix.h"
#include "include/core/SkShader.h"
+#include "include/core/SkString.h"
#include "include/effects/SkRuntimeEffect.h"
#include "include/private/SkGainmapInfo.h"
+#include "include/private/base/SkAssert.h"
+#include "include/private/base/SkFloatingPoint.h"
#include "src/core/SkColorFilterPriv.h"
#include "src/core/SkImageInfoPriv.h"
+#include <cstdint>
+
#ifdef SK_ENABLE_SKSL
static constexpr char gGainmapSKSL[] =
"uniform shader base;"
diff --git a/src/shaders/SkImageShader.cpp b/src/shaders/SkImageShader.cpp
index bcc13d0..749e33f 100644
--- a/src/shaders/SkImageShader.cpp
+++ b/src/shaders/SkImageShader.cpp
@@ -7,25 +7,36 @@
#include "src/shaders/SkImageShader.h"
+#include "include/core/SkAlphaType.h"
+#include "include/core/SkBitmap.h"
+#include "include/core/SkBlendMode.h"
+#include "include/core/SkColorType.h"
+#include "include/core/SkMatrix.h"
+#include "include/core/SkPaint.h"
+#include "include/core/SkPixmap.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkShader.h"
+#include "include/core/SkTileMode.h"
+#include "include/private/base/SkMath.h"
+#include "modules/skcms/skcms.h"
#include "src/base/SkArenaAlloc.h"
-#include "src/core/SkColorSpacePriv.h"
#include "src/core/SkColorSpaceXformSteps.h"
+#include "src/core/SkEffectPriv.h"
#include "src/core/SkImageInfoPriv.h"
-#include "src/core/SkMatrixPriv.h"
-#include "src/core/SkMatrixProvider.h"
+#include "src/core/SkImagePriv.h"
#include "src/core/SkMipmapAccessor.h"
-#include "src/core/SkOpts.h"
+#include "src/core/SkPicturePriv.h"
#include "src/core/SkRasterPipeline.h"
+#include "src/core/SkRasterPipelineOpContexts.h"
+#include "src/core/SkRasterPipelineOpList.h"
#include "src/core/SkReadBuffer.h"
-#include "src/core/SkVM.h"
+#include "src/core/SkSamplingPriv.h"
#include "src/core/SkWriteBuffer.h"
-#include "src/core/SkYUVMath.h"
#include "src/image/SkImage_Base.h"
-#include "src/shaders/SkBitmapProcShader.h"
#include "src/shaders/SkLocalMatrixShader.h"
-#include "src/shaders/SkTransformShader.h"
#if defined(SK_GRAPHITE)
+#include "src/core/SkYUVMath.h"
#include "src/gpu/Blend.h"
#include "src/gpu/graphite/ImageUtils.h"
#include "src/gpu/graphite/Image_Graphite.h"
@@ -57,6 +68,20 @@
}
#endif
+#if defined(SK_ENABLE_SKVM)
+#include "src/core/SkVM.h"
+#endif
+
+#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
+#include "src/shaders/SkBitmapProcShader.h"
+#endif
+
+#include <optional>
+#include <tuple>
+#include <utility>
+
+class SkColorSpace;
+
SkM44 SkImageShader::CubicResamplerMatrix(float B, float C) {
#if 0
constexpr SkM44 kMitchell = SkM44( 1.f/18.f, -9.f/18.f, 15.f/18.f, -7.f/18.f,
@@ -98,12 +123,11 @@
#endif
}
-// TODO: currently this only *always* used in asFragmentProcessor(), which is excluded on no-gpu
-// builds. No-gpu builds only use needs_subset() in asserts, so release+no-gpu doesn't use it, which
-// can cause builds to fail if unused warnings are treated as errors.
-[[maybe_unused]] static bool needs_subset(SkImage* img, const SkRect& subset) {
+#if defined(SK_DEBUG)
+static bool needs_subset(SkImage* img, const SkRect& subset) {
return subset != SkRect::Make(img->dimensions());
}
+#endif
SkImageShader::SkImageShader(sk_sp<SkImage> img,
const SkRect& subset,
@@ -367,48 +391,6 @@
clampAsIfUnpremul);
}
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-#if defined(SK_GANESH)
-
-#include "src/gpu/ganesh/GrColorInfo.h"
-#include "src/gpu/ganesh/GrFPArgs.h"
-#include "src/gpu/ganesh/effects/GrBlendFragmentProcessor.h"
-#include "src/gpu/ganesh/image/GrImageUtils.h"
-
-std::unique_ptr<GrFragmentProcessor>
-SkImageShader::asFragmentProcessor(const GrFPArgs& args, const MatrixRec& mRec) const {
- SkTileMode tileModes[2] = {fTileModeX, fTileModeY};
- const SkRect* subset = needs_subset(fImage.get(), fSubset) ? &fSubset : nullptr;
- auto fp = skgpu::ganesh::AsFragmentProcessor(
- args.fContext, fImage, fSampling, tileModes, SkMatrix::I(), subset);
- if (!fp) {
- return nullptr;
- }
-
- bool success;
- std::tie(success, fp) = mRec.apply(std::move(fp));
- if (!success) {
- return nullptr;
- }
-
- if (!fRaw) {
- fp = GrColorSpaceXformEffect::Make(std::move(fp),
- fImage->colorSpace(),
- fImage->alphaType(),
- args.fDstColorInfo->colorSpace(),
- kPremul_SkAlphaType);
-
- if (fImage->isAlphaOnly()) {
- fp = GrBlendFragmentProcessor::Make<SkBlendMode::kDstIn>(std::move(fp), nullptr);
- }
- }
-
- return fp;
-}
-
-#endif
-
#if defined(SK_GRAPHITE)
void SkImageShader::addToKey(const skgpu::graphite::KeyContext& keyContext,
@@ -554,7 +536,6 @@
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
-#include "src/core/SkImagePriv.h"
sk_sp<SkShader> SkMakeBitmapShaderForPaint(const SkPaint& paint, const SkBitmap& src,
SkTileMode tmx, SkTileMode tmy,
@@ -658,7 +639,7 @@
return SkSamplingOptions(filter, sampling.mipmap);
}
-bool SkImageShader::appendStages(const SkStageRec& rec, const MatrixRec& mRec) const {
+bool SkImageShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec& mRec) const {
SkASSERT(!needs_subset(fImage.get(), fSubset)); // TODO(skbug.com/12784)
// We only support certain sampling options in stages so far
@@ -943,7 +924,7 @@
skvm::Coord device,
skvm::Coord origLocal,
skvm::Color paint,
- const MatrixRec& mRec,
+ const SkShaders::MatrixRec& mRec,
const SkColorInfo& dst,
skvm::Uniforms* uniforms,
SkArenaAlloc* alloc) const {
diff --git a/src/shaders/SkImageShader.h b/src/shaders/SkImageShader.h
index ca60198..8334192 100644
--- a/src/shaders/SkImageShader.h
+++ b/src/shaders/SkImageShader.h
@@ -8,19 +8,29 @@
#ifndef SkImageShader_DEFINED
#define SkImageShader_DEFINED
+#include "include/core/SkFlattenable.h"
#include "include/core/SkImage.h"
#include "include/core/SkM44.h"
-#include "src/shaders/SkBitmapProcShader.h"
+#include "include/core/SkRect.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkSamplingOptions.h"
+#include "include/core/SkTypes.h"
#include "src/shaders/SkShaderBase.h"
-namespace skgpu {
-class Swizzle;
-}
-
+#if defined(SK_GRAPHITE)
namespace skgpu::graphite {
class KeyContext;
enum class ReadSwizzle;
}
+#endif
+
+class SkArenaAlloc;
+class SkMatrix;
+class SkReadBuffer;
+class SkShader;
+class SkWriteBuffer;
+enum class SkTileMode;
+struct SkStageRec;
class SkImageShader : public SkShaderBase {
public:
@@ -56,10 +66,8 @@
bool isOpaque() const override;
-#if defined(SK_GANESH)
- std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&,
- const MatrixRec&) const override;
-#endif
+ ShaderType type() const override { return ShaderType::kImage; }
+
#if defined(SK_GRAPHITE)
void addToKey(const skgpu::graphite::KeyContext&,
skgpu::graphite::PaintParamsKeyBuilder*,
@@ -67,6 +75,13 @@
#endif
static SkM44 CubicResamplerMatrix(float B, float C);
+ SkTileMode tileModeX() const { return fTileModeX; }
+ SkTileMode tileModeY() const { return fTileModeY; }
+ sk_sp<SkImage> image() const { return fImage; }
+ SkSamplingOptions sampling() const { return fSampling; }
+ SkRect subset() const { return fSubset; }
+ bool isRaw() const { return fRaw; }
+
private:
SK_FLATTENABLE_HOOKS(SkImageShader)
@@ -76,14 +91,14 @@
#endif
SkImage* onIsAImage(SkMatrix*, SkTileMode*) const override;
- bool appendStages(const SkStageRec&, const MatrixRec&) const override;
+ bool appendStages(const SkStageRec&, const SkShaders::MatrixRec&) const override;
#if defined(SK_ENABLE_SKVM)
skvm::Color program(skvm::Builder*,
skvm::Coord device,
skvm::Coord local,
skvm::Color paint,
- const MatrixRec&,
+ const SkShaders::MatrixRec&,
const SkColorInfo& dst,
skvm::Uniforms* uniforms,
SkArenaAlloc*) const override;
diff --git a/src/shaders/SkLocalMatrixShader.cpp b/src/shaders/SkLocalMatrixShader.cpp
index 7744bff..f19078b 100644
--- a/src/shaders/SkLocalMatrixShader.cpp
+++ b/src/shaders/SkLocalMatrixShader.cpp
@@ -4,17 +4,11 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
-
-#include "src/base/SkTLazy.h"
-#include "src/core/SkMatrixProvider.h"
-#include "src/core/SkVM.h"
#include "src/shaders/SkLocalMatrixShader.h"
-#if defined(SK_GANESH)
-#include "src/gpu/ganesh/GrFPArgs.h"
-#include "src/gpu/ganesh/GrFragmentProcessor.h"
-#include "src/gpu/ganesh/effects/GrMatrixEffect.h"
-#endif
+#include "src/base/SkTLazy.h"
+#include "src/core/SkReadBuffer.h"
+#include "src/core/SkWriteBuffer.h"
#if defined(SK_GRAPHITE)
#include "src/gpu/graphite/KeyContext.h"
@@ -22,6 +16,10 @@
#include "src/gpu/graphite/PaintParamsKey.h"
#endif
+class SkImage;
+enum class SkTileMode;
+struct SkStageRec;
+
SkShaderBase::GradientType SkLocalMatrixShader::asGradient(GradientInfo* info,
SkMatrix* localMatrix) const {
GradientType type = as_SB(fWrappedShader)->asGradient(info, localMatrix);
@@ -31,13 +29,6 @@
return type;
}
-#if defined(SK_GANESH)
-std::unique_ptr<GrFragmentProcessor> SkLocalMatrixShader::asFragmentProcessor(
- const GrFPArgs& args, const MatrixRec& mRec) const {
- return as_SB(fWrappedShader)->asFragmentProcessor(args, mRec.concat(fLocalMatrix));
-}
-#endif
-
#if defined(SK_GRAPHITE)
void SkLocalMatrixShader::addToKey(const skgpu::graphite::KeyContext& keyContext,
skgpu::graphite::PaintParamsKeyBuilder* builder,
@@ -97,7 +88,8 @@
return image;
}
-bool SkLocalMatrixShader::appendStages(const SkStageRec& rec, const MatrixRec& mRec) const {
+bool SkLocalMatrixShader::appendStages(const SkStageRec& rec,
+ const SkShaders::MatrixRec& mRec) const {
return as_SB(fWrappedShader)->appendStages(rec, mRec.concat(fLocalMatrix));
}
@@ -106,7 +98,7 @@
skvm::Coord device,
skvm::Coord local,
skvm::Color paint,
- const MatrixRec& mRec,
+ const SkShaders::MatrixRec& mRec,
const SkColorInfo& dst,
skvm::Uniforms* uniforms,
SkArenaAlloc* alloc) const {
@@ -121,97 +113,30 @@
}
#endif
-sk_sp<SkShader> SkShader::makeWithLocalMatrix(const SkMatrix& localMatrix) const {
- if (localMatrix.isIdentity()) {
- return sk_ref_sp(const_cast<SkShader*>(this));
- }
-
- const SkMatrix* lm = &localMatrix;
-
- sk_sp<SkShader> baseShader;
- SkMatrix otherLocalMatrix;
- sk_sp<SkShader> proxy = as_SB(this)->makeAsALocalMatrixShader(&otherLocalMatrix);
- if (proxy) {
- otherLocalMatrix = SkShaderBase::ConcatLocalMatrices(localMatrix, otherLocalMatrix);
- lm = &otherLocalMatrix;
- baseShader = proxy;
- } else {
- baseShader = sk_ref_sp(const_cast<SkShader*>(this));
- }
-
- return sk_make_sp<SkLocalMatrixShader>(std::move(baseShader), *lm);
-}
-
////////////////////////////////////////////////////////////////////
-/**
- * Replaces the CTM when used. Created to support clipShaders, which have to be evaluated
- * using the CTM that was present at the time they were specified (which may be different
- * from the CTM at the time something is drawn through the clip.
- */
-class SkCTMShader final : public SkShaderBase {
-public:
- SkCTMShader(sk_sp<SkShader> proxy, const SkMatrix& ctm)
- : fProxyShader(std::move(proxy))
- , fCTM(ctm)
- {}
+SkCTMShader::SkCTMShader(sk_sp<SkShader> proxy, const SkMatrix& ctm)
+ : fProxyShader(std::move(proxy)), fCTM(ctm) {}
- GradientType asGradient(GradientInfo* info, SkMatrix* localMatrix) const override {
- return as_SB(fProxyShader)->asGradient(info, localMatrix);
- }
+SkShaderBase::GradientType SkCTMShader::asGradient(GradientInfo* info,
+ SkMatrix* localMatrix) const {
+ return as_SB(fProxyShader)->asGradient(info, localMatrix);
+}
-#if defined(SK_GANESH)
- std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&,
- const MatrixRec&) const override;
-#endif
-
-protected:
- void flatten(SkWriteBuffer&) const override { SkASSERT(false); }
-
- bool appendStages(const SkStageRec& rec, const MatrixRec&) const override {
- return as_SB(fProxyShader)->appendRootStages(rec, fCTM);
- }
+bool SkCTMShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec&) const {
+ return as_SB(fProxyShader)->appendRootStages(rec, fCTM);
+}
#if defined(SK_ENABLE_SKVM)
- skvm::Color program(skvm::Builder* p,
- skvm::Coord device,
- skvm::Coord local,
- skvm::Color paint,
- const MatrixRec& mRec,
- const SkColorInfo& dst,
- skvm::Uniforms* uniforms,
- SkArenaAlloc* alloc) const override {
- return as_SB(fProxyShader)->rootProgram(p, device, paint, fCTM, dst, uniforms, alloc);
- }
-#endif
-
-private:
- SK_FLATTENABLE_HOOKS(SkCTMShader)
-
- sk_sp<SkShader> fProxyShader;
- SkMatrix fCTM;
-
- using INHERITED = SkShaderBase;
-};
-
-
-#if defined(SK_GANESH)
-std::unique_ptr<GrFragmentProcessor> SkCTMShader::asFragmentProcessor(const GrFPArgs& args,
- const MatrixRec& mRec) const {
- SkMatrix ctmInv;
- if (!fCTM.invert(&ctmInv)) {
- return nullptr;
- }
-
- auto base = as_SB(fProxyShader)->asRootFragmentProcessor(args, fCTM);
- if (!base) {
- return nullptr;
- }
-
- // In order for the shader to be evaluated with the original CTM, we explicitly evaluate it
- // at sk_FragCoord, and pass that through the inverse of the original CTM. This avoids requiring
- // local coords for the shader and mapping from the draw's local to device and then back.
- return GrFragmentProcessor::DeviceSpace(GrMatrixEffect::Make(ctmInv, std::move(base)));
+skvm::Color SkCTMShader::program(skvm::Builder* p,
+ skvm::Coord device,
+ skvm::Coord local,
+ skvm::Color paint,
+ const SkShaders::MatrixRec& mRec,
+ const SkColorInfo& dst,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc* alloc) const {
+ return as_SB(fProxyShader)->rootProgram(p, device, paint, fCTM, dst, uniforms, alloc);
}
#endif
@@ -219,7 +144,3 @@
SkASSERT(false);
return nullptr;
}
-
-sk_sp<SkShader> SkShaderBase::makeWithCTM(const SkMatrix& postM) const {
- return sk_sp<SkShader>(new SkCTMShader(sk_ref_sp(this), postM));
-}
diff --git a/src/shaders/SkLocalMatrixShader.h b/src/shaders/SkLocalMatrixShader.h
index 12b62be..ce007c1 100644
--- a/src/shaders/SkLocalMatrixShader.h
+++ b/src/shaders/SkLocalMatrixShader.h
@@ -8,12 +8,22 @@
#ifndef SkLocalMatrixShader_DEFINED
#define SkLocalMatrixShader_DEFINED
-#include "src/core/SkReadBuffer.h"
-#include "src/core/SkWriteBuffer.h"
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkMatrix.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkShader.h"
+#include "include/core/SkTypes.h"
#include "src/shaders/SkShaderBase.h"
-class GrFragmentProcessor;
+#include <type_traits>
+#include <utility>
+
class SkArenaAlloc;
+class SkImage;
+class SkReadBuffer;
+class SkWriteBuffer;
+enum class SkTileMode;
+struct SkStageRec;
class SkLocalMatrixShader final : public SkShaderBase {
public:
@@ -31,11 +41,8 @@
: fLocalMatrix(localMatrix), fWrappedShader(std::move(wrapped)) {}
GradientType asGradient(GradientInfo* info, SkMatrix* localMatrix) const override;
+ ShaderType type() const override { return ShaderType::kLocalMatrix; }
-#if defined(SK_GANESH)
- std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&,
- const MatrixRec&) const override;
-#endif
#if defined(SK_GRAPHITE)
void addToKey(const skgpu::graphite::KeyContext&,
skgpu::graphite::PaintParamsKeyBuilder*,
@@ -49,6 +56,9 @@
return fWrappedShader;
}
+ const SkMatrix& localMatrix() const { return fLocalMatrix; }
+ sk_sp<SkShader> wrappedShader() const { return fWrappedShader; }
+
protected:
void flatten(SkWriteBuffer&) const override;
@@ -58,14 +68,14 @@
SkImage* onIsAImage(SkMatrix* matrix, SkTileMode* mode) const override;
- bool appendStages(const SkStageRec&, const MatrixRec&) const override;
+ bool appendStages(const SkStageRec&, const SkShaders::MatrixRec&) const override;
#if defined(SK_ENABLE_SKVM)
skvm::Color program(skvm::Builder*,
skvm::Coord device,
skvm::Coord local,
skvm::Color paint,
- const MatrixRec&,
+ const SkShaders::MatrixRec&,
const SkColorInfo& dst,
skvm::Uniforms* uniforms,
SkArenaAlloc*) const override;
@@ -76,8 +86,45 @@
SkMatrix fLocalMatrix;
sk_sp<SkShader> fWrappedShader;
+};
- using INHERITED = SkShaderBase;
+/**
+ * Replaces the CTM when used. Created to support clipShaders, which have to be evaluated
+ * using the CTM that was present at the time they were specified (which may be different
+ * from the CTM at the time something is drawn through the clip.
+ */
+class SkCTMShader final : public SkShaderBase {
+public:
+ SkCTMShader(sk_sp<SkShader> proxy, const SkMatrix& ctm);
+
+ GradientType asGradient(GradientInfo* info, SkMatrix* localMatrix) const override;
+
+ ShaderType type() const override { return ShaderType::kCTM; }
+
+ const SkMatrix& ctm() const { return fCTM; }
+ sk_sp<SkShader> proxyShader() const { return fProxyShader; }
+
+protected:
+ void flatten(SkWriteBuffer&) const override { SkASSERT(false); }
+
+ bool appendStages(const SkStageRec& rec, const SkShaders::MatrixRec&) const override;
+
+#if defined(SK_ENABLE_SKVM)
+ skvm::Color program(skvm::Builder* p,
+ skvm::Coord device,
+ skvm::Coord local,
+ skvm::Color paint,
+ const SkShaders::MatrixRec& mRec,
+ const SkColorInfo& dst,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc* alloc) const override;
+#endif
+
+private:
+ SK_FLATTENABLE_HOOKS(SkCTMShader)
+
+ sk_sp<SkShader> fProxyShader;
+ SkMatrix fCTM;
};
#endif
diff --git a/src/shaders/SkPerlinNoiseShader.cpp b/src/shaders/SkPerlinNoiseShader.cpp
deleted file mode 100644
index 0052c17..0000000
--- a/src/shaders/SkPerlinNoiseShader.cpp
+++ /dev/null
@@ -1,1141 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "include/effects/SkPerlinNoiseShader.h"
-
-#include "include/core/SkBitmap.h"
-#include "include/core/SkColorFilter.h"
-#include "include/core/SkShader.h"
-#include "include/core/SkString.h"
-#include "include/core/SkUnPreMultiply.h"
-#include "include/private/base/SkTPin.h"
-#include "src/base/SkArenaAlloc.h"
-#include "src/core/SkMatrixProvider.h"
-#include "src/core/SkReadBuffer.h"
-#include "src/core/SkVM.h"
-#include "src/core/SkWriteBuffer.h"
-
-#if defined(SK_GANESH)
-#include "include/gpu/GrRecordingContext.h"
-#include "src/gpu/KeyBuilder.h"
-#include "src/gpu/ganesh/GrFPArgs.h"
-#include "src/gpu/ganesh/GrFragmentProcessor.h"
-#include "src/gpu/ganesh/GrProcessorUnitTest.h"
-#include "src/gpu/ganesh/GrRecordingContextPriv.h"
-#include "src/gpu/ganesh/GrShaderCaps.h"
-#include "src/gpu/ganesh/SkGr.h"
-#include "src/gpu/ganesh/effects/GrMatrixEffect.h"
-#include "src/gpu/ganesh/effects/GrTextureEffect.h"
-#include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
-#include "src/gpu/ganesh/glsl/GrGLSLProgramDataManager.h"
-#include "src/gpu/ganesh/glsl/GrGLSLUniformHandler.h"
-#endif // SK_GANESH
-
-#if defined(SK_GRAPHITE)
-#include "src/gpu/graphite/KeyContext.h"
-#include "src/gpu/graphite/KeyHelpers.h"
-#include "src/gpu/graphite/Log.h"
-#include "src/gpu/graphite/PaintParamsKey.h"
-#include "src/gpu/graphite/RecorderPriv.h"
-#include "src/gpu/graphite/TextureProxyView.h"
-#include "src/image/SkImage_Base.h"
-#endif // SK_GRAPHITE
-
-static const int kBlockSize = 256;
-static const int kBlockMask = kBlockSize - 1;
-static const int kPerlinNoise = 4096;
-static const int kRandMaximum = SK_MaxS32; // 2**31 - 1
-
-class SkPerlinNoiseShaderImpl : public SkShaderBase {
-public:
- struct StitchData {
- StitchData()
- : fWidth(0)
- , fWrapX(0)
- , fHeight(0)
- , fWrapY(0)
- {}
-
- StitchData(SkScalar w, SkScalar h)
- : fWidth(std::min(SkScalarRoundToInt(w), SK_MaxS32 - kPerlinNoise))
- , fWrapX(kPerlinNoise + fWidth)
- , fHeight(std::min(SkScalarRoundToInt(h), SK_MaxS32 - kPerlinNoise))
- , fWrapY(kPerlinNoise + fHeight) {}
-
- bool operator==(const StitchData& other) const {
- return fWidth == other.fWidth &&
- fWrapX == other.fWrapX &&
- fHeight == other.fHeight &&
- fWrapY == other.fWrapY;
- }
-
- int fWidth; // How much to subtract to wrap for stitching.
- int fWrapX; // Minimum value to wrap.
- int fHeight;
- int fWrapY;
- };
-
- struct PaintingData {
- PaintingData(const SkISize& tileSize, SkScalar seed,
- SkScalar baseFrequencyX, SkScalar baseFrequencyY,
- const SkMatrix& matrix)
- {
- SkVector tileVec;
- matrix.mapVector(SkIntToScalar(tileSize.fWidth), SkIntToScalar(tileSize.fHeight),
- &tileVec);
-
- SkSize scale;
- if (!matrix.decomposeScale(&scale, nullptr)) {
- scale.set(SK_ScalarNearlyZero, SK_ScalarNearlyZero);
- }
- fBaseFrequency.set(baseFrequencyX * SkScalarInvert(scale.width()),
- baseFrequencyY * SkScalarInvert(scale.height()));
- fTileSize.set(SkScalarRoundToInt(tileVec.fX), SkScalarRoundToInt(tileVec.fY));
- this->init(seed);
- if (!fTileSize.isEmpty()) {
- this->stitch();
- }
-
- #if defined(SK_GANESH) || defined(SK_GRAPHITE)
- SkImageInfo info = SkImageInfo::MakeA8(kBlockSize, 1);
- fPermutationsBitmap.installPixels(info, fLatticeSelector, info.minRowBytes());
- fPermutationsBitmap.setImmutable();
-
- info = SkImageInfo::Make(kBlockSize, 4, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
- fNoiseBitmap.installPixels(info, fNoise[0][0], info.minRowBytes());
- fNoiseBitmap.setImmutable();
- #endif
- }
-
- #if defined(SK_GANESH) || defined(SK_GRAPHITE)
- PaintingData(const PaintingData& that)
- : fSeed(that.fSeed)
- , fTileSize(that.fTileSize)
- , fBaseFrequency(that.fBaseFrequency)
- , fStitchDataInit(that.fStitchDataInit)
- , fPermutationsBitmap(that.fPermutationsBitmap)
- , fNoiseBitmap(that.fNoiseBitmap) {
- memcpy(fLatticeSelector, that.fLatticeSelector, sizeof(fLatticeSelector));
- memcpy(fNoise, that.fNoise, sizeof(fNoise));
- memcpy(fGradient, that.fGradient, sizeof(fGradient));
- }
- #endif
-
- int fSeed;
- uint8_t fLatticeSelector[kBlockSize];
- uint16_t fNoise[4][kBlockSize][2];
- SkPoint fGradient[4][kBlockSize];
- SkISize fTileSize;
- SkVector fBaseFrequency;
- StitchData fStitchDataInit;
-
- private:
-
- #if defined(SK_GANESH) || defined(SK_GRAPHITE)
- SkBitmap fPermutationsBitmap;
- SkBitmap fNoiseBitmap;
- #endif
-
- inline int random() {
- // See https://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement
- // m = kRandMaximum, 2**31 - 1 (2147483647)
- static constexpr int kRandAmplitude = 16807; // 7**5; primitive root of m
- static constexpr int kRandQ = 127773; // m / a
- static constexpr int kRandR = 2836; // m % a
-
- int result = kRandAmplitude * (fSeed % kRandQ) - kRandR * (fSeed / kRandQ);
- if (result <= 0) {
- result += kRandMaximum;
- }
- fSeed = result;
- return result;
- }
-
- // Only called once. Could be part of the constructor.
- void init(SkScalar seed) {
- // According to the SVG spec, we must truncate (not round) the seed value.
- fSeed = SkScalarTruncToInt(seed);
- // The seed value clamp to the range [1, kRandMaximum - 1].
- if (fSeed <= 0) {
- fSeed = -(fSeed % (kRandMaximum - 1)) + 1;
- }
- if (fSeed > kRandMaximum - 1) {
- fSeed = kRandMaximum - 1;
- }
- for (int channel = 0; channel < 4; ++channel) {
- for (int i = 0; i < kBlockSize; ++i) {
- fLatticeSelector[i] = i;
- fNoise[channel][i][0] = (random() % (2 * kBlockSize));
- fNoise[channel][i][1] = (random() % (2 * kBlockSize));
- }
- }
- for (int i = kBlockSize - 1; i > 0; --i) {
- int k = fLatticeSelector[i];
- int j = random() % kBlockSize;
- SkASSERT(j >= 0);
- SkASSERT(j < kBlockSize);
- fLatticeSelector[i] = fLatticeSelector[j];
- fLatticeSelector[j] = k;
- }
-
- // Perform the permutations now
- {
- // Copy noise data
- uint16_t noise[4][kBlockSize][2];
- for (int i = 0; i < kBlockSize; ++i) {
- for (int channel = 0; channel < 4; ++channel) {
- for (int j = 0; j < 2; ++j) {
- noise[channel][i][j] = fNoise[channel][i][j];
- }
- }
- }
- // Do permutations on noise data
- for (int i = 0; i < kBlockSize; ++i) {
- for (int channel = 0; channel < 4; ++channel) {
- for (int j = 0; j < 2; ++j) {
- fNoise[channel][i][j] = noise[channel][fLatticeSelector[i]][j];
- }
- }
- }
- }
-
- // Half of the largest possible value for 16 bit unsigned int
- static constexpr SkScalar kHalfMax16bits = 32767.5f;
-
- // Compute gradients from permuted noise data
- static constexpr SkScalar kInvBlockSizef = 1.0 / SkIntToScalar(kBlockSize);
- for (int channel = 0; channel < 4; ++channel) {
- for (int i = 0; i < kBlockSize; ++i) {
- fGradient[channel][i] = SkPoint::Make(
- (fNoise[channel][i][0] - kBlockSize) * kInvBlockSizef,
- (fNoise[channel][i][1] - kBlockSize) * kInvBlockSizef);
- fGradient[channel][i].normalize();
- // Put the normalized gradient back into the noise data
- fNoise[channel][i][0] =
- SkScalarRoundToInt((fGradient[channel][i].fX + 1) * kHalfMax16bits);
- fNoise[channel][i][1] =
- SkScalarRoundToInt((fGradient[channel][i].fY + 1) * kHalfMax16bits);
- }
- }
- }
-
- // Only called once. Could be part of the constructor.
- void stitch() {
- SkScalar tileWidth = SkIntToScalar(fTileSize.width());
- SkScalar tileHeight = SkIntToScalar(fTileSize.height());
- SkASSERT(tileWidth > 0 && tileHeight > 0);
- // When stitching tiled turbulence, the frequencies must be adjusted
- // so that the tile borders will be continuous.
- if (fBaseFrequency.fX) {
- SkScalar lowFrequencx =
- SkScalarFloorToScalar(tileWidth * fBaseFrequency.fX) / tileWidth;
- SkScalar highFrequencx =
- SkScalarCeilToScalar(tileWidth * fBaseFrequency.fX) / tileWidth;
- // BaseFrequency should be non-negative according to the standard.
- // lowFrequencx can be 0 if fBaseFrequency.fX is very small.
- if (sk_ieee_float_divide(fBaseFrequency.fX, lowFrequencx) < highFrequencx / fBaseFrequency.fX) {
- fBaseFrequency.fX = lowFrequencx;
- } else {
- fBaseFrequency.fX = highFrequencx;
- }
- }
- if (fBaseFrequency.fY) {
- SkScalar lowFrequency =
- SkScalarFloorToScalar(tileHeight * fBaseFrequency.fY) / tileHeight;
- SkScalar highFrequency =
- SkScalarCeilToScalar(tileHeight * fBaseFrequency.fY) / tileHeight;
- // lowFrequency can be 0 if fBaseFrequency.fY is very small.
- if (sk_ieee_float_divide(fBaseFrequency.fY, lowFrequency) < highFrequency / fBaseFrequency.fY) {
- fBaseFrequency.fY = lowFrequency;
- } else {
- fBaseFrequency.fY = highFrequency;
- }
- }
- fStitchDataInit = StitchData(tileWidth * fBaseFrequency.fX,
- tileHeight * fBaseFrequency.fY);
- }
-
- public:
-
-#if defined(SK_GANESH) || defined(SK_GRAPHITE)
- const SkBitmap& getPermutationsBitmap() const { return fPermutationsBitmap; }
-
- const SkBitmap& getNoiseBitmap() const { return fNoiseBitmap; }
-#endif
- };
-
- /**
- * About the noise types : the difference between the first 2 is just minor tweaks to the
- * algorithm, they're not 2 entirely different noises. The output looks different, but once the
- * noise is generated in the [1, -1] range, the output is brought back in the [0, 1] range by
- * doing :
- * kFractalNoise_Type : noise * 0.5 + 0.5
- * kTurbulence_Type : abs(noise)
- * Very little differs between the 2 types, although you can tell the difference visually.
- */
- enum Type {
- kFractalNoise_Type,
- kTurbulence_Type,
- kLast_Type = kTurbulence_Type
- };
-
- static const int kMaxOctaves = 255; // numOctaves must be <= 0 and <= kMaxOctaves
-
- SkPerlinNoiseShaderImpl(SkPerlinNoiseShaderImpl::Type type, SkScalar baseFrequencyX,
- SkScalar baseFrequencyY, int numOctaves, SkScalar seed,
- const SkISize* tileSize);
-
- class PerlinNoiseShaderContext : public Context {
- public:
- PerlinNoiseShaderContext(const SkPerlinNoiseShaderImpl& shader, const ContextRec&);
-
- void shadeSpan(int x, int y, SkPMColor[], int count) override;
-
- private:
- SkPMColor shade(const SkPoint& point, StitchData& stitchData) const;
- SkScalar calculateTurbulenceValueForPoint(int channel,
- StitchData& stitchData,
- const SkPoint& point) const;
- SkScalar noise2D(int channel,
- const StitchData& stitchData,
- const SkPoint& noiseVector) const;
-
- SkMatrix fMatrix;
- PaintingData fPaintingData;
-
- using INHERITED = Context;
- };
-
-#if defined(SK_GANESH)
- std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&,
- const MatrixRec&) const override;
-#endif
-#if defined(SK_GRAPHITE)
- void addToKey(const skgpu::graphite::KeyContext&,
- skgpu::graphite::PaintParamsKeyBuilder*,
- skgpu::graphite::PipelineDataGatherer*) const override;
-#endif
-#if defined(SK_ENABLE_SKVM)
- skvm::Color program(skvm::Builder*,
- skvm::Coord,
- skvm::Coord,
- skvm::Color,
- const MatrixRec&,
- const SkColorInfo&,
- skvm::Uniforms*,
- SkArenaAlloc*) const override {
- // Unimplemented
- return {};
- }
-#endif
-
-protected:
- void flatten(SkWriteBuffer&) const override;
-#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
- Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
-#endif
-
-private:
- SK_FLATTENABLE_HOOKS(SkPerlinNoiseShaderImpl)
-
- const SkPerlinNoiseShaderImpl::Type fType;
- const SkScalar fBaseFrequencyX;
- const SkScalar fBaseFrequencyY;
- const int fNumOctaves;
- const SkScalar fSeed;
- const SkISize fTileSize;
- const bool fStitchTiles;
-
- friend void SkRegisterPerlinNoiseShaderFlattenable();
-
- using INHERITED = SkShaderBase;
-};
-
-namespace {
-
-// noiseValue is the color component's value (or color)
-// limitValue is the maximum perlin noise array index value allowed
-// newValue is the current noise dimension (either width or height)
-inline int checkNoise(int noiseValue, int limitValue, int newValue) {
- // If the noise value would bring us out of bounds of the current noise array while we are
- // stiching noise tiles together, wrap the noise around the current dimension of the noise to
- // stay within the array bounds in a continuous fashion (so that tiling lines are not visible)
- if (noiseValue >= limitValue) {
- noiseValue -= newValue;
- }
- return noiseValue;
-}
-
-inline SkScalar smoothCurve(SkScalar t) {
- return t * t * (3 - 2 * t);
-}
-
-} // end namespace
-
-SkPerlinNoiseShaderImpl::SkPerlinNoiseShaderImpl(SkPerlinNoiseShaderImpl::Type type,
- SkScalar baseFrequencyX,
- SkScalar baseFrequencyY,
- int numOctaves,
- SkScalar seed,
- const SkISize* tileSize)
- : fType(type)
- , fBaseFrequencyX(baseFrequencyX)
- , fBaseFrequencyY(baseFrequencyY)
- , fNumOctaves(numOctaves > kMaxOctaves ? kMaxOctaves : numOctaves) //[0,255] octaves allowed
- , fSeed(seed)
- , fTileSize(nullptr == tileSize ? SkISize::Make(0, 0) : *tileSize)
- , fStitchTiles(!fTileSize.isEmpty()) {
- SkASSERT(numOctaves >= 0 && numOctaves <= kMaxOctaves);
- SkASSERT(fBaseFrequencyX >= 0);
- SkASSERT(fBaseFrequencyY >= 0);
-}
-
-sk_sp<SkFlattenable> SkPerlinNoiseShaderImpl::CreateProc(SkReadBuffer& buffer) {
- Type type = buffer.read32LE(kLast_Type);
-
- SkScalar freqX = buffer.readScalar();
- SkScalar freqY = buffer.readScalar();
- int octaves = buffer.read32LE<int>(kMaxOctaves);
-
- SkScalar seed = buffer.readScalar();
- SkISize tileSize;
- tileSize.fWidth = buffer.readInt();
- tileSize.fHeight = buffer.readInt();
-
- switch (type) {
- case kFractalNoise_Type:
- return SkShaders::MakeFractalNoise(freqX, freqY, octaves, seed, &tileSize);
- case kTurbulence_Type:
- return SkShaders::MakeTurbulence(freqX, freqY, octaves, seed, &tileSize);
- default:
- // Really shouldn't get here b.c. of earlier check on type
- buffer.validate(false);
- return nullptr;
- }
-}
-
-void SkPerlinNoiseShaderImpl::flatten(SkWriteBuffer& buffer) const {
- buffer.writeInt((int) fType);
- buffer.writeScalar(fBaseFrequencyX);
- buffer.writeScalar(fBaseFrequencyY);
- buffer.writeInt(fNumOctaves);
- buffer.writeScalar(fSeed);
- buffer.writeInt(fTileSize.fWidth);
- buffer.writeInt(fTileSize.fHeight);
-}
-
-SkScalar SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::noise2D(
- int channel, const StitchData& stitchData, const SkPoint& noiseVector) const {
- struct Noise {
- int noisePositionIntegerValue;
- int nextNoisePositionIntegerValue;
- SkScalar noisePositionFractionValue;
- Noise(SkScalar component)
- {
- SkScalar position = component + kPerlinNoise;
- noisePositionIntegerValue = SkScalarFloorToInt(position);
- noisePositionFractionValue = position - SkIntToScalar(noisePositionIntegerValue);
- nextNoisePositionIntegerValue = noisePositionIntegerValue + 1;
- }
- };
- Noise noiseX(noiseVector.x());
- Noise noiseY(noiseVector.y());
- SkScalar u, v;
- const SkPerlinNoiseShaderImpl& perlinNoiseShader = static_cast<const SkPerlinNoiseShaderImpl&>(fShader);
- // If stitching, adjust lattice points accordingly.
- if (perlinNoiseShader.fStitchTiles) {
- noiseX.noisePositionIntegerValue =
- checkNoise(noiseX.noisePositionIntegerValue, stitchData.fWrapX, stitchData.fWidth);
- noiseY.noisePositionIntegerValue =
- checkNoise(noiseY.noisePositionIntegerValue, stitchData.fWrapY, stitchData.fHeight);
- noiseX.nextNoisePositionIntegerValue =
- checkNoise(noiseX.nextNoisePositionIntegerValue, stitchData.fWrapX, stitchData.fWidth);
- noiseY.nextNoisePositionIntegerValue =
- checkNoise(noiseY.nextNoisePositionIntegerValue, stitchData.fWrapY, stitchData.fHeight);
- }
- noiseX.noisePositionIntegerValue &= kBlockMask;
- noiseY.noisePositionIntegerValue &= kBlockMask;
- noiseX.nextNoisePositionIntegerValue &= kBlockMask;
- noiseY.nextNoisePositionIntegerValue &= kBlockMask;
- int i = fPaintingData.fLatticeSelector[noiseX.noisePositionIntegerValue];
- int j = fPaintingData.fLatticeSelector[noiseX.nextNoisePositionIntegerValue];
- int b00 = (i + noiseY.noisePositionIntegerValue) & kBlockMask;
- int b10 = (j + noiseY.noisePositionIntegerValue) & kBlockMask;
- int b01 = (i + noiseY.nextNoisePositionIntegerValue) & kBlockMask;
- int b11 = (j + noiseY.nextNoisePositionIntegerValue) & kBlockMask;
- SkScalar sx = smoothCurve(noiseX.noisePositionFractionValue);
- SkScalar sy = smoothCurve(noiseY.noisePositionFractionValue);
-
- if (sx < 0 || sy < 0 || sx > 1 || sy > 1) {
- return 0; // Check for pathological inputs.
- }
-
- // This is taken 1:1 from SVG spec: http://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement
- SkPoint fractionValue = SkPoint::Make(noiseX.noisePositionFractionValue,
- noiseY.noisePositionFractionValue); // Offset (0,0)
- u = fPaintingData.fGradient[channel][b00].dot(fractionValue);
- fractionValue.fX -= SK_Scalar1; // Offset (-1,0)
- v = fPaintingData.fGradient[channel][b10].dot(fractionValue);
- SkScalar a = SkScalarInterp(u, v, sx);
- fractionValue.fY -= SK_Scalar1; // Offset (-1,-1)
- v = fPaintingData.fGradient[channel][b11].dot(fractionValue);
- fractionValue.fX = noiseX.noisePositionFractionValue; // Offset (0,-1)
- u = fPaintingData.fGradient[channel][b01].dot(fractionValue);
- SkScalar b = SkScalarInterp(u, v, sx);
- return SkScalarInterp(a, b, sy);
-}
-
-SkScalar SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::calculateTurbulenceValueForPoint(
- int channel, StitchData& stitchData, const SkPoint& point) const {
- const SkPerlinNoiseShaderImpl& perlinNoiseShader = static_cast<const SkPerlinNoiseShaderImpl&>(fShader);
- if (perlinNoiseShader.fStitchTiles) {
- stitchData = fPaintingData.fStitchDataInit;
- }
- SkScalar turbulenceFunctionResult = 0;
- SkPoint noiseVector(SkPoint::Make(point.x() * fPaintingData.fBaseFrequency.fX,
- point.y() * fPaintingData.fBaseFrequency.fY));
- SkScalar ratio = SK_Scalar1;
- for (int octave = 0; octave < perlinNoiseShader.fNumOctaves; ++octave) {
- SkScalar noise = noise2D(channel, stitchData, noiseVector);
- SkScalar numer = (perlinNoiseShader.fType == kFractalNoise_Type) ?
- noise : SkScalarAbs(noise);
- turbulenceFunctionResult += numer / ratio;
- noiseVector.fX *= 2;
- noiseVector.fY *= 2;
- ratio *= 2;
- if (perlinNoiseShader.fStitchTiles) {
- stitchData = StitchData(SkIntToScalar(stitchData.fWidth) * 2,
- SkIntToScalar(stitchData.fHeight) * 2);
- }
- }
-
- if (perlinNoiseShader.fType == kFractalNoise_Type) {
- // For kFractalNoise the result is: noise[-1,1] * 0.5 + 0.5
- turbulenceFunctionResult = SkScalarHalf(turbulenceFunctionResult + 1);
- }
-
- if (channel == 3) { // Scale alpha by paint value
- turbulenceFunctionResult *= SkIntToScalar(getPaintAlpha()) / 255;
- }
-
- // Clamp result
- return SkTPin(turbulenceFunctionResult, 0.0f, SK_Scalar1);
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-SkPMColor SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::shade(
- const SkPoint& point, StitchData& stitchData) const {
- SkPoint newPoint;
- fMatrix.mapPoints(&newPoint, &point, 1);
- newPoint.fX = SkScalarRoundToScalar(newPoint.fX);
- newPoint.fY = SkScalarRoundToScalar(newPoint.fY);
-
- U8CPU rgba[4];
- for (int channel = 3; channel >= 0; --channel) {
- SkScalar value;
- value = calculateTurbulenceValueForPoint(channel, stitchData, newPoint);
- rgba[channel] = SkScalarFloorToInt(255 * value);
- }
- return SkPreMultiplyARGB(rgba[3], rgba[0], rgba[1], rgba[2]);
-}
-
-#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
-SkShaderBase::Context* SkPerlinNoiseShaderImpl::onMakeContext(const ContextRec& rec,
- SkArenaAlloc* alloc) const {
- // should we pay attention to rec's device-colorspace?
- return alloc->make<PerlinNoiseShaderContext>(*this, rec);
-}
-#endif
-
-static inline SkMatrix total_matrix(const SkShaderBase::ContextRec& rec,
- const SkShaderBase& shader) {
- if (rec.fLocalMatrix) {
- return SkMatrix::Concat(*rec.fMatrix, *rec.fLocalMatrix);
- }
- return *rec.fMatrix;
-}
-
-SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::PerlinNoiseShaderContext(
- const SkPerlinNoiseShaderImpl& shader, const ContextRec& rec)
- : INHERITED(shader, rec)
- , fMatrix(total_matrix(rec, shader)) // used for temp storage, adjusted below
- , fPaintingData(shader.fTileSize, shader.fSeed, shader.fBaseFrequencyX,
- shader.fBaseFrequencyY, fMatrix)
-{
- // This (1,1) translation is due to WebKit's 1 based coordinates for the noise
- // (as opposed to 0 based, usually). The same adjustment is in the setData() function.
- fMatrix.setTranslate(-fMatrix.getTranslateX() + SK_Scalar1,
- -fMatrix.getTranslateY() + SK_Scalar1);
-}
-
-void SkPerlinNoiseShaderImpl::PerlinNoiseShaderContext::shadeSpan(
- int x, int y, SkPMColor result[], int count) {
- SkPoint point = SkPoint::Make(SkIntToScalar(x), SkIntToScalar(y));
- StitchData stitchData;
- for (int i = 0; i < count; ++i) {
- result[i] = shade(point, stitchData);
- point.fX += SK_Scalar1;
- }
-}
-
-/////////////////////////////////////////////////////////////////////
-
-#if defined(SK_GANESH)
-
-class GrPerlinNoise2Effect : public GrFragmentProcessor {
-public:
- static std::unique_ptr<GrFragmentProcessor> Make(
- SkPerlinNoiseShaderImpl::Type type,
- int numOctaves,
- bool stitchTiles,
- std::unique_ptr<SkPerlinNoiseShaderImpl::PaintingData> paintingData,
- GrSurfaceProxyView permutationsView,
- GrSurfaceProxyView noiseView,
- const GrCaps& caps) {
- static constexpr GrSamplerState kRepeatXSampler = {GrSamplerState::WrapMode::kRepeat,
- GrSamplerState::WrapMode::kClamp,
- GrSamplerState::Filter::kNearest};
- auto permutationsFP =
- GrTextureEffect::Make(std::move(permutationsView), kPremul_SkAlphaType,
- SkMatrix::I(), kRepeatXSampler, caps);
- auto noiseFP = GrTextureEffect::Make(std::move(noiseView), kPremul_SkAlphaType,
- SkMatrix::I(), kRepeatXSampler, caps);
-
- return std::unique_ptr<GrFragmentProcessor>(
- new GrPerlinNoise2Effect(type,
- numOctaves,
- stitchTiles,
- std::move(paintingData),
- std::move(permutationsFP),
- std::move(noiseFP)));
- }
-
- const char* name() const override { return "PerlinNoise"; }
-
- std::unique_ptr<GrFragmentProcessor> clone() const override {
- return std::unique_ptr<GrFragmentProcessor>(new GrPerlinNoise2Effect(*this));
- }
-
- const SkPerlinNoiseShaderImpl::StitchData& stitchData() const { return fPaintingData->fStitchDataInit; }
-
- SkPerlinNoiseShaderImpl::Type type() const { return fType; }
- bool stitchTiles() const { return fStitchTiles; }
- const SkVector& baseFrequency() const { return fPaintingData->fBaseFrequency; }
- int numOctaves() const { return fNumOctaves; }
-
-private:
- class Impl : public ProgramImpl {
- public:
- SkString emitHelper(EmitArgs& args);
- void emitCode(EmitArgs&) override;
-
- private:
- void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
-
- GrGLSLProgramDataManager::UniformHandle fStitchDataUni;
- GrGLSLProgramDataManager::UniformHandle fBaseFrequencyUni;
- };
-
- std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override {
- return std::make_unique<Impl>();
- }
-
- void onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const override;
-
- bool onIsEqual(const GrFragmentProcessor& sBase) const override {
- const GrPerlinNoise2Effect& s = sBase.cast<GrPerlinNoise2Effect>();
- return fType == s.fType &&
- fPaintingData->fBaseFrequency == s.fPaintingData->fBaseFrequency &&
- fNumOctaves == s.fNumOctaves &&
- fStitchTiles == s.fStitchTiles &&
- fPaintingData->fStitchDataInit == s.fPaintingData->fStitchDataInit;
- }
-
- GrPerlinNoise2Effect(SkPerlinNoiseShaderImpl::Type type,
- int numOctaves,
- bool stitchTiles,
- std::unique_ptr<SkPerlinNoiseShaderImpl::PaintingData> paintingData,
- std::unique_ptr<GrFragmentProcessor> permutationsFP,
- std::unique_ptr<GrFragmentProcessor> noiseFP)
- : INHERITED(kGrPerlinNoise2Effect_ClassID, kNone_OptimizationFlags)
- , fType(type)
- , fNumOctaves(numOctaves)
- , fStitchTiles(stitchTiles)
- , fPaintingData(std::move(paintingData)) {
- this->registerChild(std::move(permutationsFP), SkSL::SampleUsage::Explicit());
- this->registerChild(std::move(noiseFP), SkSL::SampleUsage::Explicit());
- this->setUsesSampleCoordsDirectly();
- }
-
- GrPerlinNoise2Effect(const GrPerlinNoise2Effect& that)
- : INHERITED(that)
- , fType(that.fType)
- , fNumOctaves(that.fNumOctaves)
- , fStitchTiles(that.fStitchTiles)
- , fPaintingData(new SkPerlinNoiseShaderImpl::PaintingData(*that.fPaintingData)) {}
-
- GR_DECLARE_FRAGMENT_PROCESSOR_TEST
-
- SkPerlinNoiseShaderImpl::Type fType;
- int fNumOctaves;
- bool fStitchTiles;
-
- std::unique_ptr<SkPerlinNoiseShaderImpl::PaintingData> fPaintingData;
-
- using INHERITED = GrFragmentProcessor;
-};
-
-/////////////////////////////////////////////////////////////////////
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrPerlinNoise2Effect)
-
-#if GR_TEST_UTILS
-std::unique_ptr<GrFragmentProcessor> GrPerlinNoise2Effect::TestCreate(GrProcessorTestData* d) {
- int numOctaves = d->fRandom->nextRangeU(2, 10);
- bool stitchTiles = d->fRandom->nextBool();
- SkScalar seed = SkIntToScalar(d->fRandom->nextU());
- SkISize tileSize;
- tileSize.fWidth = d->fRandom->nextRangeU(4, 4096);
- tileSize.fHeight = d->fRandom->nextRangeU(4, 4096);
- SkScalar baseFrequencyX = d->fRandom->nextRangeScalar(0.01f, 0.99f);
- SkScalar baseFrequencyY = d->fRandom->nextRangeScalar(0.01f, 0.99f);
-
- sk_sp<SkShader> shader(d->fRandom->nextBool() ?
- SkShaders::MakeFractalNoise(baseFrequencyX, baseFrequencyY, numOctaves, seed,
- stitchTiles ? &tileSize : nullptr) :
- SkShaders::MakeTurbulence(baseFrequencyX, baseFrequencyY, numOctaves, seed,
- stitchTiles ? &tileSize : nullptr));
-
- GrTest::TestAsFPArgs asFPArgs(d);
- return as_SB(shader)->asRootFragmentProcessor(asFPArgs.args(), GrTest::TestMatrix(d->fRandom));
-}
-#endif
-
-SkString GrPerlinNoise2Effect::Impl::emitHelper(EmitArgs& args) {
- const GrPerlinNoise2Effect& pne = args.fFp.cast<GrPerlinNoise2Effect>();
-
- GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-
- // Add noise function
- const GrShaderVar gPerlinNoiseArgs[] = {{"chanCoord", SkSLType::kHalf },
- {"noiseVec ", SkSLType::kHalf2}};
-
- const GrShaderVar gPerlinNoiseStitchArgs[] = {{"chanCoord" , SkSLType::kHalf },
- {"noiseVec" , SkSLType::kHalf2},
- {"stitchData", SkSLType::kHalf2}};
-
- SkString noiseCode;
-
- noiseCode.append(
- "half4 floorVal;"
- "floorVal.xy = floor(noiseVec);"
- "floorVal.zw = floorVal.xy + half2(1);"
- "half2 fractVal = fract(noiseVec);"
-
- // smooth curve : t^2*(3 - 2*t)
- "half2 noiseSmooth = fractVal*fractVal*(half2(3) - 2*fractVal);"
- );
-
- // Adjust frequencies if we're stitching tiles
- if (pne.stitchTiles()) {
- noiseCode.append(
- "floorVal -= step(stitchData.xyxy, floorVal) * stitchData.xyxy;"
- );
- }
-
- // NOTE: We need to explicitly pass half4(1) as input color here, because the helper function
- // can't see fInputColor (which is "_input" in the FP's outer function). skbug.com/10506
- SkString sampleX = this->invokeChild(0, "half4(1)", args, "half2(floorVal.x, 0.5)");
- SkString sampleY = this->invokeChild(0, "half4(1)", args, "half2(floorVal.z, 0.5)");
- noiseCode.appendf("half2 latticeIdx = half2(%s.a, %s.a);", sampleX.c_str(), sampleY.c_str());
-
- if (args.fShaderCaps->fPerlinNoiseRoundingFix) {
- // Android rounding for Tegra devices, like, for example: Xoom (Tegra 2), Nexus 7 (Tegra 3).
- // The issue is that colors aren't accurate enough on Tegra devices. For example, if an
- // 8 bit value of 124 (or 0.486275 here) is entered, we can get a texture value of
- // 123.513725 (or 0.484368 here). The following rounding operation prevents these precision
- // issues from affecting the result of the noise by making sure that we only have multiples
- // of 1/255. (Note that 1/255 is about 0.003921569, which is the value used here).
- noiseCode.append(
- "latticeIdx = floor(latticeIdx * half2(255.0) + half2(0.5)) * half2(0.003921569);");
- }
-
- // Get (x,y) coordinates with the permuted x
- noiseCode.append("half4 bcoords = 256*latticeIdx.xyxy + floorVal.yyww;");
-
- noiseCode.append("half2 uv;");
-
- // This is the math to convert the two 16bit integer packed into rgba 8 bit input into a
- // [-1,1] vector and perform a dot product between that vector and the provided vector.
- // Save it as a string because we will repeat it 4x.
- static constexpr const char* inc8bit = "0.00390625"; // 1.0 / 256.0
- SkString dotLattice =
- SkStringPrintf("dot((lattice.ga + lattice.rb*%s)*2 - half2(1), fractVal)", inc8bit);
-
- SkString sampleA = this->invokeChild(1, "half4(1)", args, "half2(bcoords.x, chanCoord)");
- SkString sampleB = this->invokeChild(1, "half4(1)", args, "half2(bcoords.y, chanCoord)");
- SkString sampleC = this->invokeChild(1, "half4(1)", args, "half2(bcoords.w, chanCoord)");
- SkString sampleD = this->invokeChild(1, "half4(1)", args, "half2(bcoords.z, chanCoord)");
-
- // Compute u, at offset (0,0)
- noiseCode.appendf("half4 lattice = %s;", sampleA.c_str());
- noiseCode.appendf("uv.x = %s;", dotLattice.c_str());
-
- // Compute v, at offset (-1,0)
- noiseCode.append("fractVal.x -= 1.0;");
- noiseCode.appendf("lattice = %s;", sampleB.c_str());
- noiseCode.appendf("uv.y = %s;", dotLattice.c_str());
-
- // Compute 'a' as a linear interpolation of 'u' and 'v'
- noiseCode.append("half2 ab;");
- noiseCode.append("ab.x = mix(uv.x, uv.y, noiseSmooth.x);");
-
- // Compute v, at offset (-1,-1)
- noiseCode.append("fractVal.y -= 1.0;");
- noiseCode.appendf("lattice = %s;", sampleC.c_str());
- noiseCode.appendf("uv.y = %s;", dotLattice.c_str());
-
- // Compute u, at offset (0,-1)
- noiseCode.append("fractVal.x += 1.0;");
- noiseCode.appendf("lattice = %s;", sampleD.c_str());
- noiseCode.appendf("uv.x = %s;", dotLattice.c_str());
-
- // Compute 'b' as a linear interpolation of 'u' and 'v'
- noiseCode.append("ab.y = mix(uv.x, uv.y, noiseSmooth.x);");
- // Compute the noise as a linear interpolation of 'a' and 'b'
- noiseCode.append("return mix(ab.x, ab.y, noiseSmooth.y);");
-
- SkString noiseFuncName = fragBuilder->getMangledFunctionName("noiseFuncName");
- if (pne.stitchTiles()) {
- fragBuilder->emitFunction(SkSLType::kHalf, noiseFuncName.c_str(),
- {gPerlinNoiseStitchArgs, std::size(gPerlinNoiseStitchArgs)},
- noiseCode.c_str());
- } else {
- fragBuilder->emitFunction(SkSLType::kHalf, noiseFuncName.c_str(),
- {gPerlinNoiseArgs, std::size(gPerlinNoiseArgs)},
- noiseCode.c_str());
- }
-
- return noiseFuncName;
-}
-
-void GrPerlinNoise2Effect::Impl::emitCode(EmitArgs& args) {
-
- SkString noiseFuncName = this->emitHelper(args);
-
- const GrPerlinNoise2Effect& pne = args.fFp.cast<GrPerlinNoise2Effect>();
-
- GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
- GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
-
- fBaseFrequencyUni = uniformHandler->addUniform(&pne, kFragment_GrShaderFlag, SkSLType::kHalf2,
- "baseFrequency");
- const char* baseFrequencyUni = uniformHandler->getUniformCStr(fBaseFrequencyUni);
-
- const char* stitchDataUni = nullptr;
- if (pne.stitchTiles()) {
- fStitchDataUni = uniformHandler->addUniform(&pne, kFragment_GrShaderFlag, SkSLType::kHalf2,
- "stitchData");
- stitchDataUni = uniformHandler->getUniformCStr(fStitchDataUni);
- }
-
- // There are rounding errors if the floor operation is not performed here
- fragBuilder->codeAppendf("half2 noiseVec = half2(floor(%s.xy) * %s);",
- args.fSampleCoord, baseFrequencyUni);
-
- // Clear the color accumulator
- fragBuilder->codeAppendf("half4 color = half4(0);");
-
- if (pne.stitchTiles()) {
- fragBuilder->codeAppendf("half2 stitchData = %s;", stitchDataUni);
- }
-
- fragBuilder->codeAppendf("half ratio = 1.0;");
-
- // Loop over all octaves
- fragBuilder->codeAppendf("for (int octave = 0; octave < %d; ++octave) {", pne.numOctaves());
- fragBuilder->codeAppendf( "color += ");
- if (pne.type() != SkPerlinNoiseShaderImpl::kFractalNoise_Type) {
- fragBuilder->codeAppend("abs(");
- }
-
- // There are 4 lines, put y coords at center of each.
- static constexpr const char* chanCoordR = "0.5";
- static constexpr const char* chanCoordG = "1.5";
- static constexpr const char* chanCoordB = "2.5";
- static constexpr const char* chanCoordA = "3.5";
- if (pne.stitchTiles()) {
- fragBuilder->codeAppendf(
- "half4(%s(%s, noiseVec, stitchData), %s(%s, noiseVec, stitchData),"
- "%s(%s, noiseVec, stitchData), %s(%s, noiseVec, stitchData))",
- noiseFuncName.c_str(), chanCoordR,
- noiseFuncName.c_str(), chanCoordG,
- noiseFuncName.c_str(), chanCoordB,
- noiseFuncName.c_str(), chanCoordA);
- } else {
- fragBuilder->codeAppendf(
- "half4(%s(%s, noiseVec), %s(%s, noiseVec),"
- "%s(%s, noiseVec), %s(%s, noiseVec))",
- noiseFuncName.c_str(), chanCoordR,
- noiseFuncName.c_str(), chanCoordG,
- noiseFuncName.c_str(), chanCoordB,
- noiseFuncName.c_str(), chanCoordA);
- }
- if (pne.type() != SkPerlinNoiseShaderImpl::kFractalNoise_Type) {
- fragBuilder->codeAppend(")"); // end of "abs("
- }
- fragBuilder->codeAppend(" * ratio;");
-
- fragBuilder->codeAppend("noiseVec *= half2(2.0);"
- "ratio *= 0.5;");
-
- if (pne.stitchTiles()) {
- fragBuilder->codeAppend("stitchData *= half2(2.0);");
- }
- fragBuilder->codeAppend("}"); // end of the for loop on octaves
-
- if (pne.type() == SkPerlinNoiseShaderImpl::kFractalNoise_Type) {
- // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2
- // by fractalNoise and (turbulenceFunctionResult) by turbulence.
- fragBuilder->codeAppendf("color = color * half4(0.5) + half4(0.5);");
- }
-
- // Clamp values
- fragBuilder->codeAppendf("color = saturate(color);");
-
- // Pre-multiply the result
- fragBuilder->codeAppendf("return half4(color.rgb * color.aaa, color.a);");
-}
-
-void GrPerlinNoise2Effect::Impl::onSetData(const GrGLSLProgramDataManager& pdman,
- const GrFragmentProcessor& processor) {
- const GrPerlinNoise2Effect& turbulence = processor.cast<GrPerlinNoise2Effect>();
-
- const SkVector& baseFrequency = turbulence.baseFrequency();
- pdman.set2f(fBaseFrequencyUni, baseFrequency.fX, baseFrequency.fY);
-
- if (turbulence.stitchTiles()) {
- const SkPerlinNoiseShaderImpl::StitchData& stitchData = turbulence.stitchData();
- pdman.set2f(fStitchDataUni,
- SkIntToScalar(stitchData.fWidth),
- SkIntToScalar(stitchData.fHeight));
- }
-}
-
-void GrPerlinNoise2Effect::onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const {
- uint32_t key = fNumOctaves;
- key = key << 3; // Make room for next 3 bits
- switch (fType) {
- case SkPerlinNoiseShaderImpl::kFractalNoise_Type:
- key |= 0x1;
- break;
- case SkPerlinNoiseShaderImpl::kTurbulence_Type:
- key |= 0x2;
- break;
- default:
- // leave key at 0
- break;
- }
- if (fStitchTiles) {
- key |= 0x4; // Flip the 3rd bit if tile stitching is on
- }
- b->add32(key);
-}
-
-/////////////////////////////////////////////////////////////////////
-
-std::unique_ptr<GrFragmentProcessor> SkPerlinNoiseShaderImpl::asFragmentProcessor(
- const GrFPArgs& args, const MatrixRec& mRec) const {
- SkASSERT(args.fContext);
- SkASSERT(fNumOctaves);
-
- const SkMatrix& totalMatrix = mRec.totalMatrix();
-
- // Either we don't stitch tiles, or we have a valid tile size
- SkASSERT(!fStitchTiles || !fTileSize.isEmpty());
-
- auto paintingData = std::make_unique<SkPerlinNoiseShaderImpl::PaintingData>(fTileSize,
- fSeed,
- fBaseFrequencyX,
- fBaseFrequencyY,
- totalMatrix);
-
- // Like shadeSpan, we start from device space. We will account for that below with a device
- // space effect.
-
- auto context = args.fContext;
-
- const SkBitmap& permutationsBitmap = paintingData->getPermutationsBitmap();
- const SkBitmap& noiseBitmap = paintingData->getNoiseBitmap();
-
- auto permutationsView = std::get<0>(GrMakeCachedBitmapProxyView(
- context,
- permutationsBitmap,
- /*label=*/"PerlinNoiseShader_FragmentProcessor_PermutationsView"));
- auto noiseView = std::get<0>(GrMakeCachedBitmapProxyView(
- context, noiseBitmap, /*label=*/"PerlinNoiseShader_FragmentProcessor_NoiseView"));
-
- if (permutationsView && noiseView) {
- return GrFragmentProcessor::DeviceSpace(
- GrMatrixEffect::Make(SkMatrix::Translate(1 - totalMatrix.getTranslateX(),
- 1 - totalMatrix.getTranslateY()),
- GrPerlinNoise2Effect::Make(fType,
- fNumOctaves,
- fStitchTiles,
- std::move(paintingData),
- std::move(permutationsView),
- std::move(noiseView),
- *context->priv().caps())));
- }
- return nullptr;
-}
-
-#endif
-
-#if defined(SK_GRAPHITE)
-
-// If either of these change then the corresponding change must also be made in the SkSL
-// perlin_noise_shader function.
-static_assert((int)SkPerlinNoiseShaderImpl::kFractalNoise_Type ==
- (int)skgpu::graphite::PerlinNoiseShaderBlock::Type::kFractalNoise);
-static_assert((int)SkPerlinNoiseShaderImpl::kTurbulence_Type ==
- (int)skgpu::graphite::PerlinNoiseShaderBlock::Type::kTurbulence);
-
-// If kBlockSize changes here then it must also be changed in the SkSL noise_function
-// implementation.
-static_assert(kBlockSize == 256);
-
-void SkPerlinNoiseShaderImpl::addToKey(const skgpu::graphite::KeyContext& keyContext,
- skgpu::graphite::PaintParamsKeyBuilder* builder,
- skgpu::graphite::PipelineDataGatherer* gatherer) const {
- using namespace skgpu::graphite;
-
- SkASSERT(fNumOctaves);
-
- SkMatrix totalMatrix = keyContext.local2Dev().asM33();
- if (keyContext.localMatrix()) {
- totalMatrix.preConcat(*keyContext.localMatrix());
- }
-
- SkMatrix invTotal;
- bool result = totalMatrix.invert(&invTotal);
- if (!result) {
- SKGPU_LOG_W("Couldn't invert totalMatrix for PerlinNoiseShader");
-
- SolidColorShaderBlock::BeginBlock(keyContext, builder, gatherer, {1, 0, 0, 1});
- builder->endBlock();
- return;
- }
-
- auto paintingData = std::make_unique<SkPerlinNoiseShaderImpl::PaintingData>(fTileSize,
- fSeed,
- fBaseFrequencyX,
- fBaseFrequencyY,
- totalMatrix);
-
- sk_sp<TextureProxy> perm =
- RecorderPriv::CreateCachedProxy(keyContext.recorder(),
- paintingData->getPermutationsBitmap());
-
- sk_sp<TextureProxy> noise = RecorderPriv::CreateCachedProxy(keyContext.recorder(),
- paintingData->getNoiseBitmap());
-
- if (!perm || !noise) {
- SKGPU_LOG_W("Couldn't create tables for PerlinNoiseShader");
-
- SolidColorShaderBlock::BeginBlock(keyContext, builder, gatherer, {1, 0, 0, 1});
- builder->endBlock();
- return;
- }
-
- PerlinNoiseShaderBlock::PerlinNoiseData data(static_cast<PerlinNoiseShaderBlock::Type>(fType),
- paintingData->fBaseFrequency,
- fNumOctaves,
- { paintingData->fStitchDataInit.fWidth,
- paintingData->fStitchDataInit.fHeight });
-
- data.fPermutationsProxy = std::move(perm);
- data.fNoiseProxy = std::move(noise);
-
- // This (1,1) translation is due to WebKit's 1 based coordinates for the noise
- // (as opposed to 0 based, usually). Remember: this matrix (shader2World) is going to be
- // inverted before being applied.
- SkMatrix shader2Local = SkMatrix::Translate(-1 + totalMatrix.getTranslateX(),
- -1 + totalMatrix.getTranslateY());
- shader2Local.postConcat(invTotal);
-
- LocalMatrixShaderBlock::LMShaderData lmShaderData(shader2Local);
-
- KeyContextWithLocalMatrix newContext(keyContext, shader2Local);
-
- LocalMatrixShaderBlock::BeginBlock(newContext, builder, gatherer, &lmShaderData);
- PerlinNoiseShaderBlock::BeginBlock(newContext, builder, gatherer, &data);
- builder->endBlock();
- builder->endBlock();
-}
-#endif // SK_GRAPHITE
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-static bool valid_input(SkScalar baseX, SkScalar baseY, int numOctaves, const SkISize* tileSize,
- SkScalar seed) {
- if (!(baseX >= 0 && baseY >= 0)) {
- return false;
- }
- if (!(numOctaves >= 0 && numOctaves <= SkPerlinNoiseShaderImpl::kMaxOctaves)) {
- return false;
- }
- if (tileSize && !(tileSize->width() >= 0 && tileSize->height() >= 0)) {
- return false;
- }
- if (!SkScalarIsFinite(seed)) {
- return false;
- }
- return true;
-}
-
-void SkRegisterPerlinNoiseShaderFlattenable() {
- SK_REGISTER_FLATTENABLE(SkPerlinNoiseShaderImpl);
-}
-
-namespace SkShaders {
-sk_sp<SkShader> MakeFractalNoise(SkScalar baseFrequencyX, SkScalar baseFrequencyY,
- int numOctaves, SkScalar seed, const SkISize* tileSize) {
- if (!valid_input(baseFrequencyX, baseFrequencyY, numOctaves, tileSize, seed)) {
- return nullptr;
- }
-
- if (0 == numOctaves) {
- // For kFractalNoise, w/o any octaves, the entire shader collapses to:
- // [0,0,0,0] * 0.5 + 0.5
- constexpr SkColor4f kTransparentGray = {0.5f, 0.5f, 0.5f, 0.5f};
-
- return SkShaders::Color(kTransparentGray, /* colorSpace= */ nullptr);
- }
-
- return sk_sp<SkShader>(new SkPerlinNoiseShaderImpl(SkPerlinNoiseShaderImpl::kFractalNoise_Type,
- baseFrequencyX, baseFrequencyY, numOctaves,
- seed, tileSize));
-}
-
-sk_sp<SkShader> MakeTurbulence(SkScalar baseFrequencyX, SkScalar baseFrequencyY,
- int numOctaves, SkScalar seed, const SkISize* tileSize) {
- if (!valid_input(baseFrequencyX, baseFrequencyY, numOctaves, tileSize, seed)) {
- return nullptr;
- }
-
- if (0 == numOctaves) {
- // For kTurbulence, w/o any octaves, the entire shader collapses to: [0,0,0,0]
- return SkShaders::Color(SkColors::kTransparent, /* colorSpace= */ nullptr);
- }
-
- return sk_sp<SkShader>(new SkPerlinNoiseShaderImpl(SkPerlinNoiseShaderImpl::kTurbulence_Type,
- baseFrequencyX, baseFrequencyY, numOctaves,
- seed, tileSize));
-}
-
-} // namespace SkShaders
diff --git a/src/shaders/SkPerlinNoiseShaderImpl.cpp b/src/shaders/SkPerlinNoiseShaderImpl.cpp
new file mode 100644
index 0000000..374ee56
--- /dev/null
+++ b/src/shaders/SkPerlinNoiseShaderImpl.cpp
@@ -0,0 +1,413 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/shaders/SkPerlinNoiseShaderImpl.h"
+
+#include "include/core/SkColorSpace.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkShader.h"
+#include "include/effects/SkPerlinNoiseShader.h"
+#include "include/private/base/SkCPUTypes.h"
+#include "include/private/base/SkTPin.h"
+#include "src/base/SkArenaAlloc.h"
+#include "src/core/SkReadBuffer.h"
+#include "src/core/SkWriteBuffer.h"
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyContext.h"
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/Log.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#include "src/gpu/graphite/RecorderPriv.h"
+#include "src/gpu/graphite/TextureProxyView.h"
+#include "src/image/SkImage_Base.h"
+#endif // SK_GRAPHITE
+
+namespace {
+
+// noiseValue is the color component's value (or color)
+// limitValue is the maximum perlin noise array index value allowed
+// newValue is the current noise dimension (either width or height)
+inline int checkNoise(int noiseValue, int limitValue, int newValue) {
+ // If the noise value would bring us out of bounds of the current noise array while we are
+ // stiching noise tiles together, wrap the noise around the current dimension of the noise to
+ // stay within the array bounds in a continuous fashion (so that tiling lines are not visible)
+ if (noiseValue >= limitValue) {
+ noiseValue -= newValue;
+ }
+ return noiseValue;
+}
+
+inline SkScalar smoothCurve(SkScalar t) { return t * t * (3 - 2 * t); }
+
+} // end namespace
+
+SkPerlinNoiseShader::SkPerlinNoiseShader(SkPerlinNoiseShader::Type type,
+ SkScalar baseFrequencyX,
+ SkScalar baseFrequencyY,
+ int numOctaves,
+ SkScalar seed,
+ const SkISize* tileSize)
+ : fType(type)
+ , fBaseFrequencyX(baseFrequencyX)
+ , fBaseFrequencyY(baseFrequencyY)
+ , fNumOctaves(numOctaves > kMaxOctaves ? kMaxOctaves
+ : numOctaves) //[0,255] octaves allowed
+ , fSeed(seed)
+ , fTileSize(nullptr == tileSize ? SkISize::Make(0, 0) : *tileSize)
+ , fStitchTiles(!fTileSize.isEmpty()) {
+ SkASSERT(numOctaves >= 0 && numOctaves <= kMaxOctaves);
+ SkASSERT(fBaseFrequencyX >= 0);
+ SkASSERT(fBaseFrequencyY >= 0);
+}
+
+sk_sp<SkFlattenable> SkPerlinNoiseShader::CreateProc(SkReadBuffer& buffer) {
+ Type type = buffer.read32LE(kLast_Type);
+
+ SkScalar freqX = buffer.readScalar();
+ SkScalar freqY = buffer.readScalar();
+ int octaves = buffer.read32LE<int>(kMaxOctaves);
+
+ SkScalar seed = buffer.readScalar();
+ SkISize tileSize;
+ tileSize.fWidth = buffer.readInt();
+ tileSize.fHeight = buffer.readInt();
+
+ switch (type) {
+ case kFractalNoise_Type:
+ return SkShaders::MakeFractalNoise(freqX, freqY, octaves, seed, &tileSize);
+ case kTurbulence_Type:
+ return SkShaders::MakeTurbulence(freqX, freqY, octaves, seed, &tileSize);
+ default:
+ // Really shouldn't get here b.c. of earlier check on type
+ buffer.validate(false);
+ return nullptr;
+ }
+}
+
+void SkPerlinNoiseShader::flatten(SkWriteBuffer& buffer) const {
+ buffer.writeInt((int)fType);
+ buffer.writeScalar(fBaseFrequencyX);
+ buffer.writeScalar(fBaseFrequencyY);
+ buffer.writeInt(fNumOctaves);
+ buffer.writeScalar(fSeed);
+ buffer.writeInt(fTileSize.fWidth);
+ buffer.writeInt(fTileSize.fHeight);
+}
+
+SkScalar SkPerlinNoiseShader::PerlinNoiseShaderContext::noise2D(int channel,
+ const StitchData& stitchData,
+ const SkPoint& noiseVector) const {
+ struct Noise {
+ int noisePositionIntegerValue;
+ int nextNoisePositionIntegerValue;
+ SkScalar noisePositionFractionValue;
+ Noise(SkScalar component) {
+ SkScalar position = component + kPerlinNoise;
+ noisePositionIntegerValue = SkScalarFloorToInt(position);
+ noisePositionFractionValue = position - SkIntToScalar(noisePositionIntegerValue);
+ nextNoisePositionIntegerValue = noisePositionIntegerValue + 1;
+ }
+ };
+ Noise noiseX(noiseVector.x());
+ Noise noiseY(noiseVector.y());
+ SkScalar u, v;
+ const SkPerlinNoiseShader& perlinNoiseShader = static_cast<const SkPerlinNoiseShader&>(fShader);
+ // If stitching, adjust lattice points accordingly.
+ if (perlinNoiseShader.fStitchTiles) {
+ noiseX.noisePositionIntegerValue =
+ checkNoise(noiseX.noisePositionIntegerValue, stitchData.fWrapX, stitchData.fWidth);
+ noiseY.noisePositionIntegerValue =
+ checkNoise(noiseY.noisePositionIntegerValue, stitchData.fWrapY, stitchData.fHeight);
+ noiseX.nextNoisePositionIntegerValue = checkNoise(
+ noiseX.nextNoisePositionIntegerValue, stitchData.fWrapX, stitchData.fWidth);
+ noiseY.nextNoisePositionIntegerValue = checkNoise(
+ noiseY.nextNoisePositionIntegerValue, stitchData.fWrapY, stitchData.fHeight);
+ }
+ noiseX.noisePositionIntegerValue &= kBlockMask;
+ noiseY.noisePositionIntegerValue &= kBlockMask;
+ noiseX.nextNoisePositionIntegerValue &= kBlockMask;
+ noiseY.nextNoisePositionIntegerValue &= kBlockMask;
+ int i = fPaintingData.fLatticeSelector[noiseX.noisePositionIntegerValue];
+ int j = fPaintingData.fLatticeSelector[noiseX.nextNoisePositionIntegerValue];
+ int b00 = (i + noiseY.noisePositionIntegerValue) & kBlockMask;
+ int b10 = (j + noiseY.noisePositionIntegerValue) & kBlockMask;
+ int b01 = (i + noiseY.nextNoisePositionIntegerValue) & kBlockMask;
+ int b11 = (j + noiseY.nextNoisePositionIntegerValue) & kBlockMask;
+ SkScalar sx = smoothCurve(noiseX.noisePositionFractionValue);
+ SkScalar sy = smoothCurve(noiseY.noisePositionFractionValue);
+
+ if (sx < 0 || sy < 0 || sx > 1 || sy > 1) {
+ return 0; // Check for pathological inputs.
+ }
+
+ // This is taken 1:1 from SVG spec: http://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement
+ SkPoint fractionValue = SkPoint::Make(noiseX.noisePositionFractionValue,
+ noiseY.noisePositionFractionValue); // Offset (0,0)
+ u = fPaintingData.fGradient[channel][b00].dot(fractionValue);
+ fractionValue.fX -= SK_Scalar1; // Offset (-1,0)
+ v = fPaintingData.fGradient[channel][b10].dot(fractionValue);
+ SkScalar a = SkScalarInterp(u, v, sx);
+ fractionValue.fY -= SK_Scalar1; // Offset (-1,-1)
+ v = fPaintingData.fGradient[channel][b11].dot(fractionValue);
+ fractionValue.fX = noiseX.noisePositionFractionValue; // Offset (0,-1)
+ u = fPaintingData.fGradient[channel][b01].dot(fractionValue);
+ SkScalar b = SkScalarInterp(u, v, sx);
+ return SkScalarInterp(a, b, sy);
+}
+
+SkScalar SkPerlinNoiseShader::PerlinNoiseShaderContext::calculateTurbulenceValueForPoint(
+ int channel, StitchData& stitchData, const SkPoint& point) const {
+ const SkPerlinNoiseShader& perlinNoiseShader = static_cast<const SkPerlinNoiseShader&>(fShader);
+ if (perlinNoiseShader.fStitchTiles) {
+ stitchData = fPaintingData.fStitchDataInit;
+ }
+ SkScalar turbulenceFunctionResult = 0;
+ SkPoint noiseVector(SkPoint::Make(point.x() * fPaintingData.fBaseFrequency.fX,
+ point.y() * fPaintingData.fBaseFrequency.fY));
+ SkScalar ratio = SK_Scalar1;
+ for (int octave = 0; octave < perlinNoiseShader.fNumOctaves; ++octave) {
+ SkScalar noise = noise2D(channel, stitchData, noiseVector);
+ SkScalar numer =
+ (perlinNoiseShader.fType == kFractalNoise_Type) ? noise : SkScalarAbs(noise);
+ turbulenceFunctionResult += numer / ratio;
+ noiseVector.fX *= 2;
+ noiseVector.fY *= 2;
+ ratio *= 2;
+ if (perlinNoiseShader.fStitchTiles) {
+ stitchData = StitchData(SkIntToScalar(stitchData.fWidth) * 2,
+ SkIntToScalar(stitchData.fHeight) * 2);
+ }
+ }
+
+ if (perlinNoiseShader.fType == kFractalNoise_Type) {
+ // For kFractalNoise the result is: noise[-1,1] * 0.5 + 0.5
+ turbulenceFunctionResult = SkScalarHalf(turbulenceFunctionResult + 1);
+ }
+
+ if (channel == 3) { // Scale alpha by paint value
+ turbulenceFunctionResult *= SkIntToScalar(getPaintAlpha()) / 255;
+ }
+
+ // Clamp result
+ return SkTPin(turbulenceFunctionResult, 0.0f, SK_Scalar1);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkPMColor SkPerlinNoiseShader::PerlinNoiseShaderContext::shade(const SkPoint& point,
+ StitchData& stitchData) const {
+ SkPoint newPoint;
+ fMatrix.mapPoints(&newPoint, &point, 1);
+ newPoint.fX = SkScalarRoundToScalar(newPoint.fX);
+ newPoint.fY = SkScalarRoundToScalar(newPoint.fY);
+
+ U8CPU rgba[4];
+ for (int channel = 3; channel >= 0; --channel) {
+ SkScalar value;
+ value = calculateTurbulenceValueForPoint(channel, stitchData, newPoint);
+ rgba[channel] = SkScalarFloorToInt(255 * value);
+ }
+ return SkPreMultiplyARGB(rgba[3], rgba[0], rgba[1], rgba[2]);
+}
+
+#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
+SkShaderBase::Context* SkPerlinNoiseShader::onMakeContext(const ContextRec& rec,
+ SkArenaAlloc* alloc) const {
+ // should we pay attention to rec's device-colorspace?
+ return alloc->make<PerlinNoiseShaderContext>(*this, rec);
+}
+#endif
+
+static inline SkMatrix total_matrix(const SkShaderBase::ContextRec& rec,
+ const SkShaderBase& shader) {
+ if (rec.fLocalMatrix) {
+ return SkMatrix::Concat(*rec.fMatrix, *rec.fLocalMatrix);
+ }
+ return *rec.fMatrix;
+}
+
+SkPerlinNoiseShader::PerlinNoiseShaderContext::PerlinNoiseShaderContext(
+ const SkPerlinNoiseShader& shader, const ContextRec& rec)
+ : Context(shader, rec)
+ , fMatrix(total_matrix(rec, shader)) // used for temp storage, adjusted below
+ , fPaintingData(shader.fTileSize,
+ shader.fSeed,
+ shader.fBaseFrequencyX,
+ shader.fBaseFrequencyY,
+ fMatrix) {
+ // This (1,1) translation is due to WebKit's 1 based coordinates for the noise
+ // (as opposed to 0 based, usually). The same adjustment is in the setData() function.
+ fMatrix.setTranslate(-fMatrix.getTranslateX() + SK_Scalar1,
+ -fMatrix.getTranslateY() + SK_Scalar1);
+}
+
+void SkPerlinNoiseShader::PerlinNoiseShaderContext::shadeSpan(int x,
+ int y,
+ SkPMColor result[],
+ int count) {
+ SkPoint point = SkPoint::Make(SkIntToScalar(x), SkIntToScalar(y));
+ StitchData stitchData;
+ for (int i = 0; i < count; ++i) {
+ result[i] = shade(point, stitchData);
+ point.fX += SK_Scalar1;
+ }
+}
+
+#if defined(SK_GRAPHITE)
+
+// If either of these change then the corresponding change must also be made in the SkSL
+// perlin_noise_shader function.
+static_assert((int)SkPerlinNoiseShader::kFractalNoise_Type ==
+ (int)skgpu::graphite::PerlinNoiseShaderBlock::Type::kFractalNoise);
+static_assert((int)SkPerlinNoiseShader::kTurbulence_Type ==
+ (int)skgpu::graphite::PerlinNoiseShaderBlock::Type::kTurbulence);
+
+void SkPerlinNoiseShader::addToKey(const skgpu::graphite::KeyContext& keyContext,
+ skgpu::graphite::PaintParamsKeyBuilder* builder,
+ skgpu::graphite::PipelineDataGatherer* gatherer) const {
+ // If kBlockSize changes here then it must also be changed in the SkSL noise_function
+ // implementation.
+ static_assert(SkPerlinNoiseShader::kBlockSize == 256);
+
+ using namespace skgpu::graphite;
+
+ SkASSERT(fNumOctaves);
+
+ SkMatrix totalMatrix = keyContext.local2Dev().asM33();
+ if (keyContext.localMatrix()) {
+ totalMatrix.preConcat(*keyContext.localMatrix());
+ }
+
+ SkMatrix invTotal;
+ bool result = totalMatrix.invert(&invTotal);
+ if (!result) {
+ SKGPU_LOG_W("Couldn't invert totalMatrix for PerlinNoiseShader");
+
+ SolidColorShaderBlock::BeginBlock(keyContext, builder, gatherer, {1, 0, 0, 1});
+ builder->endBlock();
+ return;
+ }
+
+ auto paintingData = this->getPaintingData(totalMatrix);
+ paintingData->generateBitmaps();
+
+ sk_sp<TextureProxy> perm = RecorderPriv::CreateCachedProxy(
+ keyContext.recorder(), paintingData->getPermutationsBitmap());
+
+ sk_sp<TextureProxy> noise =
+ RecorderPriv::CreateCachedProxy(keyContext.recorder(), paintingData->getNoiseBitmap());
+
+ if (!perm || !noise) {
+ SKGPU_LOG_W("Couldn't create tables for PerlinNoiseShader");
+
+ SolidColorShaderBlock::BeginBlock(keyContext, builder, gatherer, {1, 0, 0, 1});
+ builder->endBlock();
+ return;
+ }
+
+ PerlinNoiseShaderBlock::PerlinNoiseData data(
+ static_cast<PerlinNoiseShaderBlock::Type>(fType),
+ paintingData->fBaseFrequency,
+ fNumOctaves,
+ {paintingData->fStitchDataInit.fWidth, paintingData->fStitchDataInit.fHeight});
+
+ data.fPermutationsProxy = std::move(perm);
+ data.fNoiseProxy = std::move(noise);
+
+ // This (1,1) translation is due to WebKit's 1 based coordinates for the noise
+ // (as opposed to 0 based, usually). Remember: this matrix (shader2World) is going to be
+ // inverted before being applied.
+ SkMatrix shader2Local =
+ SkMatrix::Translate(-1 + totalMatrix.getTranslateX(), -1 + totalMatrix.getTranslateY());
+ shader2Local.postConcat(invTotal);
+
+ LocalMatrixShaderBlock::LMShaderData lmShaderData(shader2Local);
+
+ KeyContextWithLocalMatrix newContext(keyContext, shader2Local);
+
+ LocalMatrixShaderBlock::BeginBlock(newContext, builder, gatherer, &lmShaderData);
+ PerlinNoiseShaderBlock::BeginBlock(newContext, builder, gatherer, &data);
+ builder->endBlock();
+ builder->endBlock();
+}
+#endif // SK_GRAPHITE
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static bool valid_input(
+ SkScalar baseX, SkScalar baseY, int numOctaves, const SkISize* tileSize, SkScalar seed) {
+ if (!(baseX >= 0 && baseY >= 0)) {
+ return false;
+ }
+ if (!(numOctaves >= 0 && numOctaves <= SkPerlinNoiseShader::kMaxOctaves)) {
+ return false;
+ }
+ if (tileSize && !(tileSize->width() >= 0 && tileSize->height() >= 0)) {
+ return false;
+ }
+ if (!SkScalarIsFinite(seed)) {
+ return false;
+ }
+ return true;
+}
+
+void SkRegisterPerlinNoiseShaderFlattenable() {
+ SK_REGISTER_FLATTENABLE(SkPerlinNoiseShader);
+ // Previous name
+ SkFlattenable::Register("SkPerlinNoiseShaderImpl", SkPerlinNoiseShader::CreateProc);
+}
+
+namespace SkShaders {
+sk_sp<SkShader> MakeFractalNoise(SkScalar baseFrequencyX,
+ SkScalar baseFrequencyY,
+ int numOctaves,
+ SkScalar seed,
+ const SkISize* tileSize) {
+ if (!valid_input(baseFrequencyX, baseFrequencyY, numOctaves, tileSize, seed)) {
+ return nullptr;
+ }
+
+ if (0 == numOctaves) {
+ // For kFractalNoise, w/o any octaves, the entire shader collapses to:
+ // [0,0,0,0] * 0.5 + 0.5
+ constexpr SkColor4f kTransparentGray = {0.5f, 0.5f, 0.5f, 0.5f};
+
+ return SkShaders::Color(kTransparentGray, /* colorSpace= */ nullptr);
+ }
+
+ return sk_sp<SkShader>(new SkPerlinNoiseShader(SkPerlinNoiseShader::kFractalNoise_Type,
+ baseFrequencyX,
+ baseFrequencyY,
+ numOctaves,
+ seed,
+ tileSize));
+}
+
+sk_sp<SkShader> MakeTurbulence(SkScalar baseFrequencyX,
+ SkScalar baseFrequencyY,
+ int numOctaves,
+ SkScalar seed,
+ const SkISize* tileSize) {
+ if (!valid_input(baseFrequencyX, baseFrequencyY, numOctaves, tileSize, seed)) {
+ return nullptr;
+ }
+
+ if (0 == numOctaves) {
+ // For kTurbulence, w/o any octaves, the entire shader collapses to: [0,0,0,0]
+ return SkShaders::Color(SkColors::kTransparent, /* colorSpace= */ nullptr);
+ }
+
+ return sk_sp<SkShader>(new SkPerlinNoiseShader(SkPerlinNoiseShader::kTurbulence_Type,
+ baseFrequencyX,
+ baseFrequencyY,
+ numOctaves,
+ seed,
+ tileSize));
+}
+
+} // namespace SkShaders
diff --git a/src/shaders/SkPerlinNoiseShaderImpl.h b/src/shaders/SkPerlinNoiseShaderImpl.h
new file mode 100644
index 0000000..29175ac
--- /dev/null
+++ b/src/shaders/SkPerlinNoiseShaderImpl.h
@@ -0,0 +1,351 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkPerlinNoiseShaderImpl_DEFINED
+#define SkPerlinNoiseShaderImpl_DEFINED
+
+#include "include/core/SkAlphaType.h"
+#include "include/core/SkBitmap.h"
+#include "include/core/SkColor.h"
+#include "include/core/SkColorType.h"
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkImageInfo.h"
+#include "include/core/SkMatrix.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkSize.h"
+#include "include/core/SkTypes.h"
+#include "include/private/base/SkFloatingPoint.h"
+#include "include/private/base/SkMath.h"
+#include "src/shaders/SkShaderBase.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+
+class SkArenaAlloc;
+class SkReadBuffer;
+class SkWriteBuffer;
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyContext.h"
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/Log.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#include "src/gpu/graphite/RecorderPriv.h"
+#include "src/gpu/graphite/TextureProxyView.h"
+#include "src/image/SkImage_Base.h"
+#endif // SK_GRAPHITE
+
+class SkPerlinNoiseShader : public SkShaderBase {
+private:
+ static constexpr int kBlockSize = 256;
+ static constexpr int kBlockMask = kBlockSize - 1;
+ static constexpr int kPerlinNoise = 4096;
+ static constexpr int kRandMaximum = SK_MaxS32; // 2**31 - 1
+
+public:
+ struct StitchData {
+ StitchData() : fWidth(0), fWrapX(0), fHeight(0), fWrapY(0) {}
+
+ StitchData(SkScalar w, SkScalar h)
+ : fWidth(std::min(SkScalarRoundToInt(w), SK_MaxS32 - kPerlinNoise))
+ , fWrapX(kPerlinNoise + fWidth)
+ , fHeight(std::min(SkScalarRoundToInt(h), SK_MaxS32 - kPerlinNoise))
+ , fWrapY(kPerlinNoise + fHeight) {}
+
+ bool operator==(const StitchData& other) const {
+ return fWidth == other.fWidth && fWrapX == other.fWrapX && fHeight == other.fHeight &&
+ fWrapY == other.fWrapY;
+ }
+
+ int fWidth; // How much to subtract to wrap for stitching.
+ int fWrapX; // Minimum value to wrap.
+ int fHeight;
+ int fWrapY;
+ };
+
+ struct PaintingData {
+ PaintingData(const SkISize& tileSize,
+ SkScalar seed,
+ SkScalar baseFrequencyX,
+ SkScalar baseFrequencyY,
+ const SkMatrix& matrix) {
+ SkVector tileVec;
+ matrix.mapVector(
+ SkIntToScalar(tileSize.fWidth), SkIntToScalar(tileSize.fHeight), &tileVec);
+
+ SkSize scale;
+ if (!matrix.decomposeScale(&scale, nullptr)) {
+ scale.set(SK_ScalarNearlyZero, SK_ScalarNearlyZero);
+ }
+ fBaseFrequency.set(baseFrequencyX * SkScalarInvert(scale.width()),
+ baseFrequencyY * SkScalarInvert(scale.height()));
+ fTileSize.set(SkScalarRoundToInt(tileVec.fX), SkScalarRoundToInt(tileVec.fY));
+ this->init(seed);
+ if (!fTileSize.isEmpty()) {
+ this->stitch();
+ }
+ }
+
+ void generateBitmaps() {
+ SkImageInfo info = SkImageInfo::MakeA8(kBlockSize, 1);
+ fPermutationsBitmap.installPixels(info, fLatticeSelector, info.minRowBytes());
+ fPermutationsBitmap.setImmutable();
+
+ info = SkImageInfo::Make(kBlockSize, 4, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+ fNoiseBitmap.installPixels(info, fNoise[0][0], info.minRowBytes());
+ fNoiseBitmap.setImmutable();
+ }
+
+ PaintingData(const PaintingData& that)
+ : fSeed(that.fSeed)
+ , fTileSize(that.fTileSize)
+ , fBaseFrequency(that.fBaseFrequency)
+ , fStitchDataInit(that.fStitchDataInit)
+ , fPermutationsBitmap(that.fPermutationsBitmap)
+ , fNoiseBitmap(that.fNoiseBitmap) {
+ memcpy(fLatticeSelector, that.fLatticeSelector, sizeof(fLatticeSelector));
+ memcpy(fNoise, that.fNoise, sizeof(fNoise));
+ memcpy(fGradient, that.fGradient, sizeof(fGradient));
+ }
+
+ int fSeed;
+ uint8_t fLatticeSelector[kBlockSize];
+ uint16_t fNoise[4][kBlockSize][2];
+ SkPoint fGradient[4][kBlockSize];
+ SkISize fTileSize;
+ SkVector fBaseFrequency;
+ StitchData fStitchDataInit;
+
+ private:
+ SkBitmap fPermutationsBitmap;
+ SkBitmap fNoiseBitmap;
+
+ inline int random() {
+ // See https://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement
+ // m = kRandMaximum, 2**31 - 1 (2147483647)
+ static constexpr int kRandAmplitude = 16807; // 7**5; primitive root of m
+ static constexpr int kRandQ = 127773; // m / a
+ static constexpr int kRandR = 2836; // m % a
+
+ int result = kRandAmplitude * (fSeed % kRandQ) - kRandR * (fSeed / kRandQ);
+ if (result <= 0) {
+ result += kRandMaximum;
+ }
+ fSeed = result;
+ return result;
+ }
+
+ // Only called once. Could be part of the constructor.
+ void init(SkScalar seed) {
+ // According to the SVG spec, we must truncate (not round) the seed value.
+ fSeed = SkScalarTruncToInt(seed);
+ // The seed value clamp to the range [1, kRandMaximum - 1].
+ if (fSeed <= 0) {
+ fSeed = -(fSeed % (kRandMaximum - 1)) + 1;
+ }
+ if (fSeed > kRandMaximum - 1) {
+ fSeed = kRandMaximum - 1;
+ }
+ for (int channel = 0; channel < 4; ++channel) {
+ for (int i = 0; i < kBlockSize; ++i) {
+ fLatticeSelector[i] = i;
+ fNoise[channel][i][0] = (random() % (2 * kBlockSize));
+ fNoise[channel][i][1] = (random() % (2 * kBlockSize));
+ }
+ }
+ for (int i = kBlockSize - 1; i > 0; --i) {
+ int k = fLatticeSelector[i];
+ int j = random() % kBlockSize;
+ SkASSERT(j >= 0);
+ SkASSERT(j < kBlockSize);
+ fLatticeSelector[i] = fLatticeSelector[j];
+ fLatticeSelector[j] = k;
+ }
+
+ // Perform the permutations now
+ {
+ // Copy noise data
+ uint16_t noise[4][kBlockSize][2];
+ for (int i = 0; i < kBlockSize; ++i) {
+ for (int channel = 0; channel < 4; ++channel) {
+ for (int j = 0; j < 2; ++j) {
+ noise[channel][i][j] = fNoise[channel][i][j];
+ }
+ }
+ }
+ // Do permutations on noise data
+ for (int i = 0; i < kBlockSize; ++i) {
+ for (int channel = 0; channel < 4; ++channel) {
+ for (int j = 0; j < 2; ++j) {
+ fNoise[channel][i][j] = noise[channel][fLatticeSelector[i]][j];
+ }
+ }
+ }
+ }
+
+ // Half of the largest possible value for 16 bit unsigned int
+ static constexpr SkScalar kHalfMax16bits = 32767.5f;
+
+ // Compute gradients from permuted noise data
+ static constexpr SkScalar kInvBlockSizef = 1.0 / SkIntToScalar(kBlockSize);
+ for (int channel = 0; channel < 4; ++channel) {
+ for (int i = 0; i < kBlockSize; ++i) {
+ fGradient[channel][i] =
+ SkPoint::Make((fNoise[channel][i][0] - kBlockSize) * kInvBlockSizef,
+ (fNoise[channel][i][1] - kBlockSize) * kInvBlockSizef);
+ fGradient[channel][i].normalize();
+ // Put the normalized gradient back into the noise data
+ fNoise[channel][i][0] =
+ SkScalarRoundToInt((fGradient[channel][i].fX + 1) * kHalfMax16bits);
+ fNoise[channel][i][1] =
+ SkScalarRoundToInt((fGradient[channel][i].fY + 1) * kHalfMax16bits);
+ }
+ }
+ }
+
+ // Only called once. Could be part of the constructor.
+ void stitch() {
+ SkScalar tileWidth = SkIntToScalar(fTileSize.width());
+ SkScalar tileHeight = SkIntToScalar(fTileSize.height());
+ SkASSERT(tileWidth > 0 && tileHeight > 0);
+ // When stitching tiled turbulence, the frequencies must be adjusted
+ // so that the tile borders will be continuous.
+ if (fBaseFrequency.fX) {
+ SkScalar lowFrequencx =
+ SkScalarFloorToScalar(tileWidth * fBaseFrequency.fX) / tileWidth;
+ SkScalar highFrequencx =
+ SkScalarCeilToScalar(tileWidth * fBaseFrequency.fX) / tileWidth;
+ // BaseFrequency should be non-negative according to the standard.
+ // lowFrequencx can be 0 if fBaseFrequency.fX is very small.
+ if (sk_ieee_float_divide(fBaseFrequency.fX, lowFrequencx) <
+ highFrequencx / fBaseFrequency.fX) {
+ fBaseFrequency.fX = lowFrequencx;
+ } else {
+ fBaseFrequency.fX = highFrequencx;
+ }
+ }
+ if (fBaseFrequency.fY) {
+ SkScalar lowFrequency =
+ SkScalarFloorToScalar(tileHeight * fBaseFrequency.fY) / tileHeight;
+ SkScalar highFrequency =
+ SkScalarCeilToScalar(tileHeight * fBaseFrequency.fY) / tileHeight;
+ // lowFrequency can be 0 if fBaseFrequency.fY is very small.
+ if (sk_ieee_float_divide(fBaseFrequency.fY, lowFrequency) <
+ highFrequency / fBaseFrequency.fY) {
+ fBaseFrequency.fY = lowFrequency;
+ } else {
+ fBaseFrequency.fY = highFrequency;
+ }
+ }
+ fStitchDataInit =
+ StitchData(tileWidth * fBaseFrequency.fX, tileHeight * fBaseFrequency.fY);
+ }
+
+ public:
+ const SkBitmap& getPermutationsBitmap() const {
+ SkASSERT(!fPermutationsBitmap.drawsNothing());
+ return fPermutationsBitmap;
+ }
+ const SkBitmap& getNoiseBitmap() const {
+ SkASSERT(!fNoiseBitmap.drawsNothing());
+ return fNoiseBitmap;
+ }
+ }; // struct PaintingData
+
+ /**
+ * About the noise types : the difference between the first 2 is just minor tweaks to the
+ * algorithm, they're not 2 entirely different noises. The output looks different, but once the
+ * noise is generated in the [1, -1] range, the output is brought back in the [0, 1] range by
+ * doing :
+ * kFractalNoise_Type : noise * 0.5 + 0.5
+ * kTurbulence_Type : abs(noise)
+ * Very little differs between the 2 types, although you can tell the difference visually.
+ */
+ enum Type { kFractalNoise_Type, kTurbulence_Type, kLast_Type = kTurbulence_Type };
+
+ static const int kMaxOctaves = 255; // numOctaves must be <= 0 and <= kMaxOctaves
+
+ SkPerlinNoiseShader(SkPerlinNoiseShader::Type type,
+ SkScalar baseFrequencyX,
+ SkScalar baseFrequencyY,
+ int numOctaves,
+ SkScalar seed,
+ const SkISize* tileSize);
+
+ ShaderType type() const override { return ShaderType::kPerlinNoise; }
+
+ class PerlinNoiseShaderContext : public Context {
+ public:
+ PerlinNoiseShaderContext(const SkPerlinNoiseShader& shader, const ContextRec&);
+
+ void shadeSpan(int x, int y, SkPMColor[], int count) override;
+
+ private:
+ SkPMColor shade(const SkPoint& point, StitchData& stitchData) const;
+ SkScalar calculateTurbulenceValueForPoint(int channel,
+ StitchData& stitchData,
+ const SkPoint& point) const;
+ SkScalar noise2D(int channel,
+ const StitchData& stitchData,
+ const SkPoint& noiseVector) const;
+
+ SkMatrix fMatrix;
+ PaintingData fPaintingData;
+ };
+
+#if defined(SK_GRAPHITE)
+ void addToKey(const skgpu::graphite::KeyContext&,
+ skgpu::graphite::PaintParamsKeyBuilder*,
+ skgpu::graphite::PipelineDataGatherer*) const override;
+#endif
+#if defined(SK_ENABLE_SKVM)
+ skvm::Color program(skvm::Builder*,
+ skvm::Coord,
+ skvm::Coord,
+ skvm::Color,
+ const SkShaders::MatrixRec&,
+ const SkColorInfo&,
+ skvm::Uniforms*,
+ SkArenaAlloc*) const override {
+ // Unimplemented
+ return {};
+ }
+#endif
+
+ SkPerlinNoiseShader::Type noiseType() const { return fType; }
+ int numOctaves() const { return fNumOctaves; }
+ bool stitchTiles() const { return fStitchTiles; }
+ SkISize tileSize() const { return fTileSize; }
+
+ std::unique_ptr<PaintingData> getPaintingData(const SkMatrix& mat) const {
+ return std::make_unique<PaintingData>(
+ fTileSize, fSeed, fBaseFrequencyX, fBaseFrequencyY, mat);
+ }
+
+protected:
+ void flatten(SkWriteBuffer&) const override;
+#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
+ Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
+#endif
+
+private:
+ SK_FLATTENABLE_HOOKS(SkPerlinNoiseShader)
+
+ const SkPerlinNoiseShader::Type fType;
+ const SkScalar fBaseFrequencyX;
+ const SkScalar fBaseFrequencyY;
+ const int fNumOctaves;
+ const SkScalar fSeed;
+ const SkISize fTileSize;
+ const bool fStitchTiles;
+
+ friend void SkRegisterPerlinNoiseShaderFlattenable();
+};
+
+#endif
diff --git a/src/shaders/SkPictureShader.cpp b/src/shaders/SkPictureShader.cpp
index e0943bb..18945c4 100644
--- a/src/shaders/SkPictureShader.cpp
+++ b/src/shaders/SkPictureShader.cpp
@@ -7,40 +7,28 @@
#include "src/shaders/SkPictureShader.h"
-#include "include/core/SkBitmap.h"
+#include "include/core/SkAlphaType.h"
#include "include/core/SkCanvas.h"
+#include "include/core/SkColorSpace.h"
+#include "include/core/SkColorType.h"
#include "include/core/SkImage.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkSamplingOptions.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkShader.h"
+#include "include/core/SkSurface.h"
+#include "include/core/SkTileMode.h"
+#include "include/private/base/SkDebug.h"
#include "src/base/SkArenaAlloc.h"
+#include "src/core/SkEffectPriv.h"
#include "src/core/SkImageInfoPriv.h"
-#include "src/core/SkImagePriv.h"
#include "src/core/SkMatrixPriv.h"
-#include "src/core/SkMatrixProvider.h"
-#include "src/core/SkMatrixUtils.h"
#include "src/core/SkPicturePriv.h"
-#include "src/core/SkRasterPipeline.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkResourceCache.h"
-#include "src/core/SkVM.h"
-#include "src/shaders/SkBitmapProcShader.h"
-#include "src/shaders/SkImageShader.h"
+#include "src/core/SkWriteBuffer.h"
#include "src/shaders/SkLocalMatrixShader.h"
-#if defined(SK_GANESH)
-#include "include/gpu/GrDirectContext.h"
-#include "include/gpu/GrRecordingContext.h"
-#include "include/gpu/ganesh/SkSurfaceGanesh.h"
-#include "src/gpu/ganesh/GrCaps.h"
-#include "src/gpu/ganesh/GrColorInfo.h"
-#include "src/gpu/ganesh/GrFPArgs.h"
-#include "src/gpu/ganesh/GrFragmentProcessor.h"
-#include "src/gpu/ganesh/GrRecordingContextPriv.h"
-#include "src/gpu/ganesh/SkGr.h"
-#include "src/gpu/ganesh/effects/GrTextureEffect.h"
-#include "src/gpu/ganesh/image/GrImageUtils.h"
-#include "src/image/SkImage_Base.h"
-#include "src/shaders/SkLocalMatrixShader.h"
-#endif
-
#if defined(SK_GRAPHITE)
#include "include/gpu/graphite/Surface.h"
#include "src/gpu/graphite/Caps.h"
@@ -50,6 +38,12 @@
#include "src/gpu/graphite/RecorderPriv.h"
#endif
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <utility>
+class SkDiscardableMemory;
+
sk_sp<SkShader> SkPicture::makeShader(SkTileMode tmx, SkTileMode tmy, SkFilterMode filter,
const SkMatrix* localMatrix, const SkRect* tile) const {
if (localMatrix && !localMatrix->invert(nullptr)) {
@@ -191,87 +185,79 @@
return cs ? sk_ref_sp(cs) : SkColorSpace::MakeSRGB();
}
-struct CachedImageInfo {
- bool success;
- SkSize tileScale; // Additional scale factors to apply when sampling image.
- SkMatrix matrixForDraw; // Matrix used to produce an image from the picture
- SkImageInfo imageInfo;
- SkSurfaceProps props;
+SkPictureShader::CachedImageInfo SkPictureShader::CachedImageInfo::Make(
+ const SkRect& bounds,
+ const SkMatrix& totalM,
+ SkColorType dstColorType,
+ SkColorSpace* dstColorSpace,
+ const int maxTextureSize,
+ const SkSurfaceProps& propsIn) {
+ SkSurfaceProps props = propsIn.cloneWithPixelGeometry(kUnknown_SkPixelGeometry);
- static CachedImageInfo Make(const SkRect& bounds,
- const SkMatrix& totalM,
- SkColorType dstColorType,
- SkColorSpace* dstColorSpace,
- const int maxTextureSize,
- const SkSurfaceProps& propsIn) {
- SkSurfaceProps props = propsIn.cloneWithPixelGeometry(kUnknown_SkPixelGeometry);
-
- const SkSize scaledSize = [&]() {
- SkSize size;
- // Use a rotation-invariant scale
- if (!totalM.decomposeScale(&size, nullptr)) {
- SkPoint center = {bounds.centerX(), bounds.centerY()};
- SkScalar area = SkMatrixPriv::DifferentialAreaScale(totalM, center);
- if (!SkScalarIsFinite(area) || SkScalarNearlyZero(area)) {
- size = {1, 1}; // ill-conditioned matrix
- } else {
- size.fWidth = size.fHeight = SkScalarSqrt(area);
- }
+ const SkSize scaledSize = [&]() {
+ SkSize size;
+ // Use a rotation-invariant scale
+ if (!totalM.decomposeScale(&size, nullptr)) {
+ SkPoint center = {bounds.centerX(), bounds.centerY()};
+ SkScalar area = SkMatrixPriv::DifferentialAreaScale(totalM, center);
+ if (!SkScalarIsFinite(area) || SkScalarNearlyZero(area)) {
+ size = {1, 1}; // ill-conditioned matrix
+ } else {
+ size.fWidth = size.fHeight = SkScalarSqrt(area);
}
- size.fWidth *= bounds.width();
- size.fHeight *= bounds.height();
+ }
+ size.fWidth *= bounds.width();
+ size.fHeight *= bounds.height();
- // Clamp the tile size to about 4M pixels
- static const SkScalar kMaxTileArea = 2048 * 2048;
- SkScalar tileArea = size.width() * size.height();
- if (tileArea > kMaxTileArea) {
- SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea);
- size.set(size.width() * clampScale, size.height() * clampScale);
- }
-
- // Scale down the tile size if larger than maxTextureSize for GPU path
- // or it should fail on create texture
- if (maxTextureSize) {
- if (size.width() > maxTextureSize || size.height() > maxTextureSize) {
- SkScalar downScale = maxTextureSize / std::max(size.width(),
- size.height());
- size.set(SkScalarFloorToScalar(size.width() * downScale),
- SkScalarFloorToScalar(size.height() * downScale));
- }
- }
- return size;
- }();
-
- const SkISize tileSize = scaledSize.toCeil();
- if (tileSize.isEmpty()) {
- return {false, {}, {}, {}, {}};
+ // Clamp the tile size to about 4M pixels
+ static const SkScalar kMaxTileArea = 2048 * 2048;
+ SkScalar tileArea = size.width() * size.height();
+ if (tileArea > kMaxTileArea) {
+ SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea);
+ size.set(size.width() * clampScale, size.height() * clampScale);
}
- const SkSize tileScale = {
- tileSize.width() / bounds.width(), tileSize.height() / bounds.height()
- };
- auto imgCS = ref_or_srgb(dstColorSpace);
- const SkColorType imgCT = SkColorTypeMaxBitsPerChannel(dstColorType) <= 8
- ? kRGBA_8888_SkColorType
- : kRGBA_F16Norm_SkColorType;
-
- return {true,
- tileScale,
- SkMatrix::RectToRect(bounds, SkRect::MakeIWH(tileSize.width(), tileSize.height())),
- SkImageInfo::Make(tileSize, imgCT, kPremul_SkAlphaType, imgCS),
- props};
- }
-
- sk_sp<SkImage> makeImage(sk_sp<SkSurface> surf, const SkPicture* pict) const {
- if (!surf) {
- return nullptr;
+ // Scale down the tile size if larger than maxTextureSize for GPU path
+ // or it should fail on create texture
+ if (maxTextureSize) {
+ if (size.width() > maxTextureSize || size.height() > maxTextureSize) {
+ SkScalar downScale = maxTextureSize / std::max(size.width(), size.height());
+ size.set(SkScalarFloorToScalar(size.width() * downScale),
+ SkScalarFloorToScalar(size.height() * downScale));
+ }
}
- auto canvas = surf->getCanvas();
- canvas->concat(matrixForDraw);
- canvas->drawPicture(pict);
- return surf->makeImageSnapshot();
+ return size;
+ }();
+
+ const SkISize tileSize = scaledSize.toCeil();
+ if (tileSize.isEmpty()) {
+ return {false, {}, {}, {}, {}};
}
-};
+
+ const SkSize tileScale = {tileSize.width() / bounds.width(),
+ tileSize.height() / bounds.height()};
+ auto imgCS = ref_or_srgb(dstColorSpace);
+ const SkColorType imgCT = SkColorTypeMaxBitsPerChannel(dstColorType) <= 8
+ ? kRGBA_8888_SkColorType
+ : kRGBA_F16Norm_SkColorType;
+
+ return {true,
+ tileScale,
+ SkMatrix::RectToRect(bounds, SkRect::MakeIWH(tileSize.width(), tileSize.height())),
+ SkImageInfo::Make(tileSize, imgCT, kPremul_SkAlphaType, imgCS),
+ props};
+}
+
+sk_sp<SkImage> SkPictureShader::CachedImageInfo::makeImage(sk_sp<SkSurface> surf,
+ const SkPicture* pict) const {
+ if (!surf) {
+ return nullptr;
+ }
+ auto canvas = surf->getCanvas();
+ canvas->concat(matrixForDraw);
+ canvas->drawPicture(pict);
+ return surf->makeImageSnapshot();
+}
// Returns a cached image shader, which wraps a single picture tile at the given
// CTM/local matrix. Also adjusts the local matrix for tile scaling.
@@ -307,7 +293,7 @@
return image->makeShader(fTmx, fTmy, SkSamplingOptions(fFilter), &lm);
}
-bool SkPictureShader::appendStages(const SkStageRec& rec, const MatrixRec& mRec) const {
+bool SkPictureShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec& mRec) const {
// Keep bitmapShader alive by using alloc instead of stack memory
auto& bitmapShader = *rec.fAlloc->make<sk_sp<SkShader>>();
// We don't check whether the total local matrix is valid here because we have to assume *some*
@@ -329,7 +315,7 @@
skvm::Coord device,
skvm::Coord local,
skvm::Color paint,
- const MatrixRec& mRec,
+ const SkShaders::MatrixRec& mRec,
const SkColorInfo& dst,
skvm::Uniforms* uniforms,
SkArenaAlloc* alloc) const {
@@ -365,92 +351,6 @@
}
#endif
-/////////////////////////////////////////////////////////////////////////////////////////
-
-#if defined(SK_GANESH)
-
-#include "src/gpu/ganesh/GrProxyProvider.h"
-
-std::unique_ptr<GrFragmentProcessor> SkPictureShader::asFragmentProcessor(
- const GrFPArgs& args, const MatrixRec& mRec) const {
- auto ctx = args.fContext;
- SkColorType dstColorType = GrColorTypeToSkColorType(args.fDstColorInfo->colorType());
- if (dstColorType == kUnknown_SkColorType) {
- dstColorType = kRGBA_8888_SkColorType;
- }
-
- auto dstCS = ref_or_srgb(args.fDstColorInfo->colorSpace());
-
- auto info = CachedImageInfo::Make(fTile,
- mRec.totalMatrix(),
- dstColorType,
- dstCS.get(),
- ctx->priv().caps()->maxTextureSize(),
- args.fSurfaceProps);
- if (!info.success) {
- return nullptr;
- }
-
- // Gotta be sure the GPU can support our requested colortype (might be FP16)
- if (!ctx->colorTypeSupportedAsSurface(info.imageInfo.colorType())) {
- info.imageInfo = info.imageInfo.makeColorType(kRGBA_8888_SkColorType);
- }
-
- static const skgpu::UniqueKey::Domain kDomain = skgpu::UniqueKey::GenerateDomain();
- skgpu::UniqueKey key;
- std::tuple keyData = {
- dstCS->toXYZD50Hash(),
- dstCS->transferFnHash(),
- static_cast<uint32_t>(dstColorType),
- fPicture->uniqueID(),
- fTile,
- info.tileScale,
- info.props
- };
- skgpu::UniqueKey::Builder builder(&key, kDomain, sizeof(keyData)/sizeof(uint32_t),
- "Picture Shader Image");
- memcpy(&builder[0], &keyData, sizeof(keyData));
- builder.finish();
-
- GrProxyProvider* provider = ctx->priv().proxyProvider();
- GrSurfaceProxyView view;
- if (auto proxy = provider->findOrCreateProxyByUniqueKey(key)) {
- view = GrSurfaceProxyView(proxy, kTopLeft_GrSurfaceOrigin, skgpu::Swizzle());
- } else {
- const int msaaSampleCount = 0;
- const bool createWithMips = false;
- auto image = info.makeImage(SkSurfaces::RenderTarget(ctx,
- skgpu::Budgeted::kYes,
- info.imageInfo,
- msaaSampleCount,
- kTopLeft_GrSurfaceOrigin,
- &info.props,
- createWithMips),
- fPicture.get());
- if (!image) {
- return nullptr;
- }
-
- auto [v, ct] = skgpu::ganesh::AsView(ctx, image, GrMipmapped::kNo);
- view = std::move(v);
- provider->assignUniqueKeyToProxy(key, view.asTextureProxy());
- }
-
- const GrSamplerState sampler(static_cast<GrSamplerState::WrapMode>(fTmx),
- static_cast<GrSamplerState::WrapMode>(fTmy),
- fFilter);
- auto fp = GrTextureEffect::Make(std::move(view),
- kPremul_SkAlphaType,
- SkMatrix::I(),
- sampler,
- *ctx->priv().caps());
- SkMatrix scale = SkMatrix::Scale(info.tileScale.width(), info.tileScale.height());
- bool success;
- std::tie(success, fp) = mRec.apply(std::move(fp), scale);
- return success ? std::move(fp) : nullptr;
-}
-#endif
-
#if defined(SK_GRAPHITE)
void SkPictureShader::addToKey(const skgpu::graphite::KeyContext& keyContext,
skgpu::graphite::PaintParamsKeyBuilder* builder,
diff --git a/src/shaders/SkPictureShader.h b/src/shaders/SkPictureShader.h
index f4c9f1a..bb810b0 100644
--- a/src/shaders/SkPictureShader.h
+++ b/src/shaders/SkPictureShader.h
@@ -8,13 +8,28 @@
#ifndef SkPictureShader_DEFINED
#define SkPictureShader_DEFINED
-#include "include/core/SkTileMode.h"
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkImageInfo.h"
+#include "include/core/SkMatrix.h"
+#include "include/core/SkPicture.h"
+#include "include/core/SkRect.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkSize.h"
+#include "include/core/SkSurfaceProps.h"
+#include "include/core/SkTypes.h"
#include "src/shaders/SkShaderBase.h"
-#include <atomic>
class SkArenaAlloc;
-class SkBitmap;
-class SkPicture;
+class SkColorSpace;
+class SkImage;
+class SkReadBuffer;
+class SkShader;
+class SkSurface;
+class SkWriteBuffer;
+enum SkColorType : int;
+enum class SkFilterMode;
+enum class SkTileMode;
+struct SkStageRec;
/*
* An SkPictureShader can be used to draw SkPicture-based patterns.
@@ -27,10 +42,6 @@
static sk_sp<SkShader> Make(sk_sp<SkPicture>, SkTileMode, SkTileMode, SkFilterMode,
const SkMatrix*, const SkRect*);
-#if defined(SK_GANESH)
- std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&,
- const MatrixRec&) const override;
-#endif
#if defined(SK_GRAPHITE)
void addToKey(const skgpu::graphite::KeyContext&,
skgpu::graphite::PaintParamsKeyBuilder*,
@@ -39,16 +50,41 @@
SkPictureShader(sk_sp<SkPicture>, SkTileMode, SkTileMode, SkFilterMode, const SkRect*);
+ ShaderType type() const override { return ShaderType::kPicture; }
+
+ sk_sp<SkPicture> picture() const { return fPicture; }
+ SkRect tile() const { return fTile; }
+ SkTileMode tileModeX() const { return fTmx; }
+ SkTileMode tileModeY() const { return fTmy; }
+ SkFilterMode filter() const { return fFilter; }
+
+ struct CachedImageInfo {
+ bool success;
+ SkSize tileScale; // Additional scale factors to apply when sampling image.
+ SkMatrix matrixForDraw; // Matrix used to produce an image from the picture
+ SkImageInfo imageInfo;
+ SkSurfaceProps props;
+
+ static CachedImageInfo Make(const SkRect& bounds,
+ const SkMatrix& totalM,
+ SkColorType dstColorType,
+ SkColorSpace* dstColorSpace,
+ const int maxTextureSize,
+ const SkSurfaceProps& propsIn);
+
+ sk_sp<SkImage> makeImage(sk_sp<SkSurface> surf, const SkPicture* pict) const;
+ };
+
protected:
SkPictureShader(SkReadBuffer&);
void flatten(SkWriteBuffer&) const override;
- bool appendStages(const SkStageRec&, const MatrixRec&) const override;
+ bool appendStages(const SkStageRec&, const SkShaders::MatrixRec&) const override;
#if defined(SK_ENABLE_SKVM)
skvm::Color program(skvm::Builder*,
skvm::Coord device,
skvm::Coord local,
skvm::Color paint,
- const MatrixRec&,
+ const SkShaders::MatrixRec&,
const SkColorInfo& dst,
skvm::Uniforms* uniforms,
SkArenaAlloc* alloc) const override;
@@ -68,9 +104,7 @@
sk_sp<SkPicture> fPicture;
SkRect fTile;
SkTileMode fTmx, fTmy;
- SkFilterMode fFilter;
-
- using INHERITED = SkShaderBase;
+ SkFilterMode fFilter;
};
#endif // SkPictureShader_DEFINED
diff --git a/src/shaders/SkRuntimeShader.cpp b/src/shaders/SkRuntimeShader.cpp
new file mode 100644
index 0000000..cbaee17
--- /dev/null
+++ b/src/shaders/SkRuntimeShader.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "src/shaders/SkRuntimeShader.h"
+
+#include "include/core/SkCapabilities.h"
+#include "include/core/SkData.h"
+#include "include/core/SkMatrix.h"
+#include "include/core/SkShader.h"
+#include "include/core/SkString.h"
+#include "include/effects/SkRuntimeEffect.h"
+#include "include/private/SkSLSampleUsage.h"
+#include "include/private/base/SkAssert.h"
+#include "include/private/base/SkDebug.h"
+#include "include/private/base/SkTArray.h"
+#include "include/sksl/SkSLDebugTrace.h"
+#include "src/base/SkTLazy.h"
+#include "src/core/SkEffectPriv.h"
+#include "src/core/SkPicturePriv.h"
+#include "src/core/SkReadBuffer.h"
+#include "src/core/SkRuntimeEffectPriv.h"
+#include "src/core/SkWriteBuffer.h"
+#include "src/shaders/SkShaderBase.h"
+#include "src/sksl/codegen/SkSLRasterPipelineBuilder.h"
+#include "src/sksl/tracing/SkSLDebugTracePriv.h"
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyContext.h"
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif
+
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <utility>
+
+class SkColorSpace;
+struct SkIPoint;
+
+SkRuntimeShader::SkRuntimeShader(sk_sp<SkRuntimeEffect> effect,
+ sk_sp<SkSL::DebugTracePriv> debugTrace,
+ sk_sp<const SkData> uniforms,
+ SkSpan<SkRuntimeEffect::ChildPtr> children)
+ : fEffect(std::move(effect))
+ , fDebugTrace(std::move(debugTrace))
+ , fUniformData(std::move(uniforms))
+ , fChildren(children.begin(), children.end()) {}
+
+SkRuntimeShader::SkRuntimeShader(sk_sp<SkRuntimeEffect> effect,
+ sk_sp<SkSL::DebugTracePriv> debugTrace,
+ UniformsCallback uniformsCallback,
+ SkSpan<SkRuntimeEffect::ChildPtr> children)
+ : fEffect(std::move(effect))
+ , fDebugTrace(std::move(debugTrace))
+ , fUniformsCallback(std::move(uniformsCallback))
+ , fChildren(children.begin(), children.end()) {}
+
+static sk_sp<SkSL::DebugTracePriv> make_debug_trace(SkRuntimeEffect* effect,
+ const SkIPoint& coord) {
+ auto debugTrace = sk_make_sp<SkSL::DebugTracePriv>();
+ debugTrace->setSource(effect->source());
+ debugTrace->setTraceCoord(coord);
+ return debugTrace;
+}
+
+SkRuntimeEffect::TracedShader SkRuntimeShader::makeTracedClone(const SkIPoint& coord) {
+ sk_sp<SkRuntimeEffect> unoptimized = fEffect->makeUnoptimizedClone();
+ sk_sp<SkSL::DebugTracePriv> debugTrace = make_debug_trace(unoptimized.get(), coord);
+ auto debugShader = sk_make_sp<SkRuntimeShader>(
+ unoptimized, debugTrace, this->uniformData(nullptr), SkSpan(fChildren));
+
+ return SkRuntimeEffect::TracedShader{std::move(debugShader), std::move(debugTrace)};
+}
+
+#if defined(SK_GRAPHITE)
+void SkRuntimeShader::addToKey(const skgpu::graphite::KeyContext& keyContext,
+ skgpu::graphite::PaintParamsKeyBuilder* builder,
+ skgpu::graphite::PipelineDataGatherer* gatherer) const {
+ using namespace skgpu::graphite;
+
+ sk_sp<const SkData> uniforms = SkRuntimeEffectPriv::TransformUniforms(
+ fEffect->uniforms(),
+ this->uniformData(keyContext.dstColorInfo().colorSpace()),
+ keyContext.dstColorInfo().colorSpace());
+ SkASSERT(uniforms);
+
+ RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, {fEffect, std::move(uniforms)});
+
+ SkRuntimeEffectPriv::AddChildrenToKey(
+ fChildren, fEffect->children(), keyContext, builder, gatherer);
+
+ builder->endBlock();
+}
+#endif
+
+bool SkRuntimeShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec& mRec) const {
+#ifdef SK_ENABLE_SKSL_IN_RASTER_PIPELINE
+ if (!SkRuntimeEffectPriv::CanDraw(SkCapabilities::RasterBackend().get(), fEffect.get())) {
+ // SkRP has support for many parts of #version 300 already, but for now, we restrict its
+ // usage in runtime effects to just #version 100.
+ return false;
+ }
+ if (const SkSL::RP::Program* program = fEffect->getRPProgram(fDebugTrace.get())) {
+ std::optional<SkShaders::MatrixRec> newMRec = mRec.apply(rec);
+ if (!newMRec.has_value()) {
+ return false;
+ }
+ SkSpan<const float> uniforms =
+ SkRuntimeEffectPriv::UniformsAsSpan(fEffect->uniforms(),
+ this->uniformData(rec.fDstCS),
+ /*alwaysCopyIntoAlloc=*/fUniformData == nullptr,
+ rec.fDstCS,
+ rec.fAlloc);
+ RuntimeEffectRPCallbacks callbacks(rec, *newMRec, fChildren, fEffect->fSampleUsages);
+ bool success = program->appendStages(rec.fPipeline, rec.fAlloc, &callbacks, uniforms);
+ return success;
+ }
+#endif
+ return false;
+}
+
+#if defined(SK_ENABLE_SKVM)
+skvm::Color SkRuntimeShader::program(skvm::Builder* p,
+ skvm::Coord device,
+ skvm::Coord local,
+ skvm::Color paint,
+ const SkShaders::MatrixRec& mRec,
+ const SkColorInfo& colorInfo,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc* alloc) const {
+ if (!SkRuntimeEffectPriv::CanDraw(SkCapabilities::RasterBackend().get(), fEffect.get())) {
+ return {};
+ }
+
+ sk_sp<const SkData> inputs = SkRuntimeEffectPriv::TransformUniforms(
+ fEffect->uniforms(), this->uniformData(colorInfo.colorSpace()), colorInfo.colorSpace());
+ SkASSERT(inputs);
+
+ // Ensure any pending transform is applied before running the runtime shader's code, which
+ // gets to use and manipulate the coordinates.
+ std::optional<SkShaders::MatrixRec> newMRec = mRec.apply(p, &local, uniforms);
+ if (!newMRec.has_value()) {
+ return {};
+ }
+ // We could omit this for children that are only sampled with passthrough coords.
+ newMRec->markTotalMatrixInvalid();
+
+ RuntimeEffectVMCallbacks callbacks(p, uniforms, alloc, fChildren, *newMRec, paint, colorInfo);
+ std::vector<skvm::Val> uniform =
+ SkRuntimeEffectPriv::MakeSkVMUniforms(p, uniforms, fEffect->uniformSize(), *inputs);
+
+ return SkSL::ProgramToSkVM(*fEffect->fBaseProgram,
+ fEffect->fMain,
+ p,
+ fDebugTrace.get(),
+ SkSpan(uniform),
+ device,
+ local,
+ paint,
+ paint,
+ &callbacks);
+}
+#endif
+
+void SkRuntimeShader::flatten(SkWriteBuffer& buffer) const {
+ buffer.writeString(fEffect->source().c_str());
+ buffer.writeDataAsByteArray(this->uniformData(nullptr).get());
+ SkRuntimeEffectPriv::WriteChildEffects(buffer, fChildren);
+}
+
+sk_sp<const SkData> SkRuntimeShader::uniformData(const SkColorSpace* dstCS) const {
+ if (fUniformData) {
+ return fUniformData;
+ }
+
+ // We want to invoke the uniforms-callback each time a paint occurs.
+ SkASSERT(fUniformsCallback);
+ sk_sp<const SkData> uniforms = fUniformsCallback({dstCS});
+ SkASSERT(uniforms && uniforms->size() == fEffect->uniformSize());
+ return uniforms;
+}
+
+sk_sp<SkFlattenable> SkRuntimeShader::CreateProc(SkReadBuffer& buffer) {
+ if (!buffer.validate(buffer.allowSkSL())) {
+ return nullptr;
+ }
+
+ SkString sksl;
+ buffer.readString(&sksl);
+ sk_sp<SkData> uniforms = buffer.readByteArrayAsData();
+
+ SkTLazy<SkMatrix> localM;
+ if (buffer.isVersionLT(SkPicturePriv::kNoShaderLocalMatrix)) {
+ uint32_t flags = buffer.read32();
+ if (flags & kHasLegacyLocalMatrix_Flag) {
+ buffer.readMatrix(localM.init());
+ }
+ }
+
+ auto effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForShader, std::move(sksl));
+#if !SK_LENIENT_SKSL_DESERIALIZATION
+ if (!buffer.validate(effect != nullptr)) {
+ return nullptr;
+ }
+#endif
+
+ skia_private::STArray<4, SkRuntimeEffect::ChildPtr> children;
+ if (!SkRuntimeEffectPriv::ReadChildEffects(buffer, effect.get(), &children)) {
+ return nullptr;
+ }
+
+#if SK_LENIENT_SKSL_DESERIALIZATION
+ if (!effect) {
+ // If any children were SkShaders, return the first one. This is a reasonable fallback.
+ for (int i = 0; i < children.size(); i++) {
+ if (children[i].shader()) {
+ SkDebugf("Serialized SkSL failed to compile. Replacing shader with child %d.\n", i);
+ return sk_ref_sp(children[i].shader());
+ }
+ }
+
+ // We don't know what to do, so just return nullptr (but *don't* poison the buffer).
+ SkDebugf("Serialized SkSL failed to compile. Ignoring/dropping SkSL shader.\n");
+ return nullptr;
+ }
+#endif
+
+ return effect->makeShader(std::move(uniforms), SkSpan(children), localM.getMaybeNull());
+}
diff --git a/src/shaders/SkRuntimeShader.h b/src/shaders/SkRuntimeShader.h
new file mode 100644
index 0000000..9a8372b
--- /dev/null
+++ b/src/shaders/SkRuntimeShader.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkRuntimeShader_DEFINED
+#define SkRuntimeShader_DEFINED
+
+#include "include/core/SkData.h"
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkSpan.h"
+#include "include/effects/SkRuntimeEffect.h"
+#include "include/private/base/SkDebug.h"
+#include "src/core/SkRuntimeEffectPriv.h"
+#include "src/shaders/SkShaderBase.h"
+#include "src/sksl/tracing/SkSLDebugTracePriv.h"
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyContext.h"
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif
+
+#include <vector>
+
+class SkColorSpace;
+class SkReadBuffer;
+class SkWriteBuffer;
+struct SkIPoint;
+struct SkStageRec;
+
+using UniformsCallback = SkRuntimeEffectPriv::UniformsCallback;
+
+class SkRuntimeShader : public SkShaderBase {
+public:
+ SkRuntimeShader(sk_sp<SkRuntimeEffect> effect,
+ sk_sp<SkSL::DebugTracePriv> debugTrace,
+ sk_sp<const SkData> uniforms,
+ SkSpan<SkRuntimeEffect::ChildPtr> children);
+
+ SkRuntimeShader(sk_sp<SkRuntimeEffect> effect,
+ sk_sp<SkSL::DebugTracePriv> debugTrace,
+ UniformsCallback uniformsCallback,
+ SkSpan<SkRuntimeEffect::ChildPtr> children);
+
+ SkRuntimeEffect::TracedShader makeTracedClone(const SkIPoint& coord);
+
+ bool isOpaque() const override { return fEffect->alwaysOpaque(); }
+
+ ShaderType type() const override { return ShaderType::kRuntime; }
+
+#if defined(SK_GRAPHITE)
+ void addToKey(const skgpu::graphite::KeyContext& keyContext,
+ skgpu::graphite::PaintParamsKeyBuilder* builder,
+ skgpu::graphite::PipelineDataGatherer* gatherer) const override;
+#endif
+
+ bool appendStages(const SkStageRec& rec, const SkShaders::MatrixRec& mRec) const override;
+
+#if defined(SK_ENABLE_SKVM)
+ skvm::Color program(skvm::Builder* p,
+ skvm::Coord device,
+ skvm::Coord local,
+ skvm::Color paint,
+ const SkShaders::MatrixRec& mRec,
+ const SkColorInfo& colorInfo,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc* alloc) const override;
+#endif
+
+ void flatten(SkWriteBuffer& buffer) const override;
+
+ SkRuntimeEffect* asRuntimeEffect() const override { return fEffect.get(); }
+
+ sk_sp<SkRuntimeEffect> effect() const { return fEffect; }
+ std::vector<SkRuntimeEffect::ChildPtr> children() const { return fChildren; }
+
+ sk_sp<const SkData> uniformData(const SkColorSpace* dstCS) const;
+
+ SK_FLATTENABLE_HOOKS(SkRuntimeShader)
+
+private:
+ enum Flags {
+ kHasLegacyLocalMatrix_Flag = 1 << 1,
+ };
+
+ sk_sp<SkRuntimeEffect> fEffect;
+ sk_sp<SkSL::DebugTracePriv> fDebugTrace;
+ sk_sp<const SkData> fUniformData;
+ UniformsCallback fUniformsCallback;
+ std::vector<SkRuntimeEffect::ChildPtr> fChildren;
+};
+
+#endif
diff --git a/src/shaders/SkShader.cpp b/src/shaders/SkShader.cpp
index f6ed560..80caa89 100644
--- a/src/shaders/SkShader.cpp
+++ b/src/shaders/SkShader.cpp
@@ -4,335 +4,49 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
+#include "include/core/SkShader.h"
-#include "include/core/SkMallocPixelRef.h"
-#include "include/core/SkPaint.h"
-#include "include/core/SkScalar.h"
-#include "src/base/SkArenaAlloc.h"
-#include "src/base/SkTLazy.h"
-#include "src/core/SkColorSpacePriv.h"
-#include "src/core/SkColorSpaceXformSteps.h"
-#include "src/core/SkMatrixProvider.h"
-#include "src/core/SkRasterPipeline.h"
-#include "src/core/SkReadBuffer.h"
-#include "src/core/SkWriteBuffer.h"
-#include "src/shaders/SkBitmapProcShader.h"
-#include "src/shaders/SkImageShader.h"
+#include "include/core/SkMatrix.h"
+#include "include/core/SkRefCnt.h"
+#include "src/shaders/SkColorFilterShader.h"
+#include "src/shaders/SkLocalMatrixShader.h"
#include "src/shaders/SkShaderBase.h"
-#include "src/shaders/SkTransformShader.h"
-#if defined(SK_GANESH)
-#include "src/gpu/ganesh/GrFragmentProcessor.h"
-#include "src/gpu/ganesh/effects/GrMatrixEffect.h"
-#endif
+#include <utility>
-#if defined(SK_GRAPHITE)
-#include "src/gpu/graphite/KeyHelpers.h"
-#include "src/gpu/graphite/PaintParamsKey.h"
-#endif
-
-SkShaderBase::SkShaderBase() = default;
-
-SkShaderBase::~SkShaderBase() = default;
-
-SkShaderBase::MatrixRec::MatrixRec(const SkMatrix& ctm) : fCTM(ctm) {}
-
-std::optional<SkShaderBase::MatrixRec>
-SkShaderBase::MatrixRec::apply(const SkStageRec& rec, const SkMatrix& postInv) const {
- SkMatrix total = fPendingLocalMatrix;
- if (!fCTMApplied) {
- total = SkMatrix::Concat(fCTM, total);
- }
- if (!total.invert(&total)) {
- return {};
- }
- total = SkMatrix::Concat(postInv, total);
- if (!fCTMApplied) {
- rec.fPipeline->append(SkRasterPipelineOp::seed_shader);
- }
- // append_matrix is a no-op if total worked out to identity.
- rec.fPipeline->append_matrix(rec.fAlloc, total);
- return MatrixRec{fCTM,
- fTotalLocalMatrix,
- /*pendingLocalMatrix=*/SkMatrix::I(),
- fTotalMatrixIsValid,
- /*ctmApplied=*/true};
-}
-
-#if defined(SK_ENABLE_SKVM)
-std::optional<SkShaderBase::MatrixRec>
-SkShaderBase::MatrixRec::apply(skvm::Builder* p,
- skvm::Coord* local,
- skvm::Uniforms* uniforms,
- const SkMatrix& postInv) const {
- SkMatrix total = fPendingLocalMatrix;
- if (!fCTMApplied) {
- total = SkMatrix::Concat(fCTM, total);
- }
- if (!total.invert(&total)) {
- return {};
- }
- total = SkMatrix::Concat(postInv, total);
- // ApplyMatrix is a no-op if total worked out to identity.
- *local = SkShaderBase::ApplyMatrix(p, total, *local, uniforms);
- return MatrixRec{fCTM,
- fTotalLocalMatrix,
- /*pendingLocalMatrix=*/SkMatrix::I(),
- fTotalMatrixIsValid,
- /*ctmApplied=*/true};
-}
-#endif
-#if defined(SK_GANESH)
-GrFPResult SkShaderBase::MatrixRec::apply(std::unique_ptr<GrFragmentProcessor> fp,
- const SkMatrix& postInv) const {
- // FP matrices work differently than SkRasterPipeline and SkVM. The starting coordinates
- // provided to the root SkShader's FP are already in local space. So we never apply the inverse
- // CTM.
- SkASSERT(!fCTMApplied);
- SkMatrix total;
- if (!fPendingLocalMatrix.invert(&total)) {
- return {false, std::move(fp)};
- }
- total = SkMatrix::Concat(postInv, total);
- // GrMatrixEffect returns 'fp' if total worked out to identity.
- return {true, GrMatrixEffect::Make(total, std::move(fp))};
-}
-
-SkShaderBase::MatrixRec SkShaderBase::MatrixRec::applied() const {
- // We mark the CTM as "not applied" because we *never* apply the CTM for FPs. Their starting
- // coords are local, not device, coords.
- return MatrixRec{fCTM,
- fTotalLocalMatrix,
- /*pendingLocalMatrix=*/SkMatrix::I(),
- fTotalMatrixIsValid,
- /*ctmApplied=*/false};
-}
-#endif
-
-SkShaderBase::MatrixRec SkShaderBase::MatrixRec::concat(const SkMatrix& m) const {
- return {fCTM,
- SkShaderBase::ConcatLocalMatrices(fTotalLocalMatrix, m),
- SkShaderBase::ConcatLocalMatrices(fPendingLocalMatrix, m),
- fTotalMatrixIsValid,
- fCTMApplied};
-}
-
-void SkShaderBase::flatten(SkWriteBuffer& buffer) const { this->INHERITED::flatten(buffer); }
-
-bool SkShaderBase::computeTotalInverse(const SkMatrix& ctm,
- const SkMatrix* localMatrix,
- SkMatrix* totalInverse) const {
- return (localMatrix ? SkMatrix::Concat(ctm, *localMatrix) : ctm).invert(totalInverse);
-}
-
-bool SkShaderBase::asLuminanceColor(SkColor* colorPtr) const {
- SkColor storage;
- if (nullptr == colorPtr) {
- colorPtr = &storage;
- }
- if (this->onAsLuminanceColor(colorPtr)) {
- *colorPtr = SkColorSetA(*colorPtr, 0xFF); // we only return opaque
- return true;
- }
- return false;
-}
-
-SkShaderBase::Context* SkShaderBase::makeContext(const ContextRec& rec, SkArenaAlloc* alloc) const {
-#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
- // We always fall back to raster pipeline when perspective is present.
- if (rec.fMatrix->hasPerspective() || (rec.fLocalMatrix && rec.fLocalMatrix->hasPerspective()) ||
- !this->computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, nullptr)) {
- return nullptr;
- }
-
- return this->onMakeContext(rec, alloc);
-#else
- return nullptr;
-#endif
-}
-
-SkShaderBase::Context::Context(const SkShaderBase& shader, const ContextRec& rec)
- : fShader(shader), fCTM(*rec.fMatrix)
-{
- // We should never use a context with perspective.
- SkASSERT(!rec.fMatrix->hasPerspective());
- SkASSERT(!rec.fLocalMatrix || !rec.fLocalMatrix->hasPerspective());
-
- // Because the context parameters must be valid at this point, we know that the matrix is
- // invertible.
- SkAssertResult(fShader.computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, &fTotalInverse));
-
- fPaintAlpha = rec.fPaintAlpha;
-}
-
-SkShaderBase::Context::~Context() {}
-
-bool SkShaderBase::ContextRec::isLegacyCompatible(SkColorSpace* shaderColorSpace) const {
- // In legacy pipelines, shaders always produce premul (or opaque) and the destination is also
- // always premul (or opaque). (And those "or opaque" caveats won't make any difference here.)
- SkAlphaType shaderAT = kPremul_SkAlphaType,
- dstAT = kPremul_SkAlphaType;
- return 0 == SkColorSpaceXformSteps{shaderColorSpace, shaderAT,
- fDstColorSpace, dstAT}.flags.mask();
-}
+class SkColorFilter;
+class SkImage;
+enum class SkTileMode;
SkImage* SkShader::isAImage(SkMatrix* localMatrix, SkTileMode xy[2]) const {
return as_SB(this)->onIsAImage(localMatrix, xy);
}
-#if defined(SK_GANESH)
-std::unique_ptr<GrFragmentProcessor>
-SkShaderBase::asRootFragmentProcessor(const GrFPArgs& args, const SkMatrix& ctm) const {
- return this->asFragmentProcessor(args, MatrixRec(ctm));
-}
-
-std::unique_ptr<GrFragmentProcessor> SkShaderBase::asFragmentProcessor(const GrFPArgs&,
- const MatrixRec&) const {
- return nullptr;
-}
-#endif
-
-sk_sp<SkShader> SkShaderBase::makeAsALocalMatrixShader(SkMatrix*) const {
- return nullptr;
-}
-
-#if defined(SK_GRAPHITE)
-// TODO: add implementations for derived classes
-void SkShaderBase::addToKey(const skgpu::graphite::KeyContext& keyContext,
- skgpu::graphite::PaintParamsKeyBuilder* builder,
- skgpu::graphite::PipelineDataGatherer* gatherer) const {
- using namespace skgpu::graphite;
-
- SolidColorShaderBlock::BeginBlock(keyContext, builder, gatherer, {1, 0, 0, 1});
- builder->endBlock();
-}
-#endif
-
-bool SkShaderBase::appendRootStages(const SkStageRec& rec, const SkMatrix& ctm) const {
- return this->appendStages(rec, MatrixRec(ctm));
-}
-
-bool SkShaderBase::appendStages(const SkStageRec& rec, const MatrixRec& mRec) const {
- // SkShader::Context::shadeSpan() handles the paint opacity internally,
- // but SkRasterPipelineBlitter applies it as a separate stage.
- // We skip the internal shadeSpan() step by forcing the paint opaque.
- SkColor4f opaquePaintColor = rec.fPaintColor.makeOpaque();
-
- // We don't have a separate ctm and local matrix at this point. Just pass the combined matrix
- // as the CTM. TODO: thread the MatrixRec through the legacy context system.
- auto tm = mRec.totalMatrix();
- ContextRec cr(opaquePaintColor,
- tm,
- nullptr,
- rec.fDstColorType,
- sk_srgb_singleton(),
- rec.fSurfaceProps);
-
- struct CallbackCtx : SkRasterPipeline_CallbackCtx {
- sk_sp<const SkShader> shader;
- Context* ctx;
- };
- auto cb = rec.fAlloc->make<CallbackCtx>();
- cb->shader = sk_ref_sp(this);
- cb->ctx = as_SB(this)->makeContext(cr, rec.fAlloc);
- cb->fn = [](SkRasterPipeline_CallbackCtx* self, int active_pixels) {
- auto c = (CallbackCtx*)self;
- int x = (int)c->rgba[0],
- y = (int)c->rgba[1];
- SkPMColor tmp[SkRasterPipeline_kMaxStride_highp];
- c->ctx->shadeSpan(x,y, tmp, active_pixels);
-
- for (int i = 0; i < active_pixels; i++) {
- auto rgba_4f = SkPMColor4f::FromPMColor(tmp[i]);
- memcpy(c->rgba + 4*i, rgba_4f.vec(), 4*sizeof(float));
- }
- };
-
- if (cb->ctx) {
- rec.fPipeline->append(SkRasterPipelineOp::seed_shader);
- rec.fPipeline->append(SkRasterPipelineOp::callback, cb);
- rec.fAlloc->make<SkColorSpaceXformSteps>(sk_srgb_singleton(), kPremul_SkAlphaType,
- rec.fDstCS, kPremul_SkAlphaType)
- ->apply(rec.fPipeline);
- return true;
+sk_sp<SkShader> SkShader::makeWithLocalMatrix(const SkMatrix& localMatrix) const {
+ if (localMatrix.isIdentity()) {
+ return sk_ref_sp(const_cast<SkShader*>(this));
}
- return false;
-}
-#if defined(SK_ENABLE_SKVM)
-skvm::Color SkShaderBase::rootProgram(skvm::Builder* p,
- skvm::Coord device,
- skvm::Color paint,
- const SkMatrix& ctm,
- const SkColorInfo& dst,
- skvm::Uniforms* uniforms,
- SkArenaAlloc* alloc) const {
- // Shader subclasses should always act as if the destination were premul or opaque.
- // SkVMBlitter handles all the coordination of unpremul itself, via premul.
- SkColorInfo tweaked = dst.alphaType() == kUnpremul_SkAlphaType
- ? dst.makeAlphaType(kPremul_SkAlphaType)
- : dst;
+ const SkMatrix* lm = &localMatrix;
- // Force opaque alpha for all opaque shaders.
- //
- // This is primarily nice in that we usually have a 1.0f constant splat
- // somewhere in the program anyway, and this will let us drop the work the
- // shader notionally does to produce alpha, p->extract(...), etc. in favor
- // of that simple hoistable splat.
- //
- // More subtly, it makes isOpaque() a parameter to all shader program
- // generation, guaranteeing that is-opaque bit is mixed into the overall
- // shader program hash and blitter Key. This makes it safe for us to use
- // that bit to make decisions when constructing an SkVMBlitter, like doing
- // SrcOver -> Src strength reduction.
- if (auto color = this->program(p,
- device,
- /*local=*/device,
- paint,
- MatrixRec(ctm),
- tweaked,
- uniforms,
- alloc)) {
- if (this->isOpaque()) {
- color.a = p->splat(1.0f);
- }
- return color;
+ sk_sp<SkShader> baseShader;
+ SkMatrix otherLocalMatrix;
+ sk_sp<SkShader> proxy = as_SB(this)->makeAsALocalMatrixShader(&otherLocalMatrix);
+ if (proxy) {
+ otherLocalMatrix = SkShaderBase::ConcatLocalMatrices(localMatrix, otherLocalMatrix);
+ lm = &otherLocalMatrix;
+ baseShader = proxy;
+ } else {
+ baseShader = sk_ref_sp(const_cast<SkShader*>(this));
}
- return {};
-}
-#endif // defined(SK_ENABLE_SKVM)
-// need a cheap way to invert the alpha channel of a shader (i.e. 1 - a)
-sk_sp<SkShader> SkShaderBase::makeInvertAlpha() const {
- return this->makeWithColorFilter(SkColorFilters::Blend(0xFFFFFFFF, SkBlendMode::kSrcOut));
+ return sk_make_sp<SkLocalMatrixShader>(std::move(baseShader), *lm);
}
-#if defined(SK_ENABLE_SKVM)
-skvm::Coord SkShaderBase::ApplyMatrix(skvm::Builder* p, const SkMatrix& m,
- skvm::Coord coord, skvm::Uniforms* uniforms) {
- skvm::F32 x = coord.x,
- y = coord.y;
- if (m.isIdentity()) {
- // That was easy.
- } else if (m.isTranslate()) {
- x = p->add(x, p->uniformF(uniforms->pushF(m[2])));
- y = p->add(y, p->uniformF(uniforms->pushF(m[5])));
- } else if (m.isScaleTranslate()) {
- x = p->mad(x, p->uniformF(uniforms->pushF(m[0])), p->uniformF(uniforms->pushF(m[2])));
- y = p->mad(y, p->uniformF(uniforms->pushF(m[4])), p->uniformF(uniforms->pushF(m[5])));
- } else { // Affine or perspective.
- auto dot = [&,x,y](int row) {
- return p->mad(x, p->uniformF(uniforms->pushF(m[3*row+0])),
- p->mad(y, p->uniformF(uniforms->pushF(m[3*row+1])),
- p->uniformF(uniforms->pushF(m[3*row+2]))));
- };
- x = dot(0);
- y = dot(1);
- if (m.hasPerspective()) {
- x = x * (1.0f / dot(2));
- y = y * (1.0f / dot(2));
- }
+sk_sp<SkShader> SkShader::makeWithColorFilter(sk_sp<SkColorFilter> filter) const {
+ SkShader* base = const_cast<SkShader*>(this);
+ if (!filter) {
+ return sk_ref_sp(base);
}
- return {x,y};
+ return sk_make_sp<SkColorFilterShader>(sk_ref_sp(base), 1.0f, std::move(filter));
}
-#endif // defined(SK_ENABLE_SKVM)
diff --git a/src/shaders/SkShaderBase.cpp b/src/shaders/SkShaderBase.cpp
new file mode 100644
index 0000000..b571223
--- /dev/null
+++ b/src/shaders/SkShaderBase.cpp
@@ -0,0 +1,318 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/shaders/SkShaderBase.h"
+
+#include "include/core/SkAlphaType.h"
+#include "include/core/SkBlendMode.h"
+#include "include/core/SkColorFilter.h"
+#include "include/private/SkColorData.h"
+#include "src/base/SkArenaAlloc.h"
+#include "src/core/SkColorSpacePriv.h"
+#include "src/core/SkColorSpaceXformSteps.h"
+#include "src/core/SkEffectPriv.h"
+#include "src/core/SkRasterPipeline.h"
+#include "src/core/SkRasterPipelineOpContexts.h"
+#include "src/core/SkRasterPipelineOpList.h"
+#include "src/shaders/SkLocalMatrixShader.h"
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif
+
+#include <cstring>
+
+class SkWriteBuffer;
+
+namespace SkShaders {
+MatrixRec::MatrixRec(const SkMatrix& ctm) : fCTM(ctm) {}
+
+std::optional<MatrixRec> MatrixRec::apply(const SkStageRec& rec, const SkMatrix& postInv) const {
+ SkMatrix total = fPendingLocalMatrix;
+ if (!fCTMApplied) {
+ total = SkMatrix::Concat(fCTM, total);
+ }
+ if (!total.invert(&total)) {
+ return {};
+ }
+ total = SkMatrix::Concat(postInv, total);
+ if (!fCTMApplied) {
+ rec.fPipeline->append(SkRasterPipelineOp::seed_shader);
+ }
+ // append_matrix is a no-op if total worked out to identity.
+ rec.fPipeline->append_matrix(rec.fAlloc, total);
+ return MatrixRec{fCTM,
+ fTotalLocalMatrix,
+ /*pendingLocalMatrix=*/SkMatrix::I(),
+ fTotalMatrixIsValid,
+ /*ctmApplied=*/true};
+}
+
+#if defined(SK_ENABLE_SKVM)
+std::optional<MatrixRec> MatrixRec::apply(skvm::Builder* p,
+ skvm::Coord* local,
+ skvm::Uniforms* uniforms,
+ const SkMatrix& postInv) const {
+ SkMatrix total = fPendingLocalMatrix;
+ if (!fCTMApplied) {
+ total = SkMatrix::Concat(fCTM, total);
+ }
+ if (!total.invert(&total)) {
+ return {};
+ }
+ total = SkMatrix::Concat(postInv, total);
+ // ApplyMatrix is a no-op if total worked out to identity.
+ *local = SkShaderBase::ApplyMatrix(p, total, *local, uniforms);
+ return MatrixRec{fCTM,
+ fTotalLocalMatrix,
+ /*pendingLocalMatrix=*/SkMatrix::I(),
+ fTotalMatrixIsValid,
+ /*ctmApplied=*/true};
+}
+#endif
+
+std::tuple<SkMatrix, bool> MatrixRec::applyForFragmentProcessor(const SkMatrix& postInv) const {
+ SkASSERT(!fCTMApplied);
+ SkMatrix total;
+ if (!fPendingLocalMatrix.invert(&total)) {
+ return {SkMatrix::I(), false};
+ }
+ return {SkMatrix::Concat(postInv, total), true};
+}
+
+MatrixRec MatrixRec::applied() const {
+ // We mark the CTM as "not applied" because we *never* apply the CTM for FPs. Their starting
+ // coords are local, not device, coords.
+ return MatrixRec{fCTM,
+ fTotalLocalMatrix,
+ /*pendingLocalMatrix=*/SkMatrix::I(),
+ fTotalMatrixIsValid,
+ /*ctmApplied=*/false};
+}
+
+MatrixRec MatrixRec::concat(const SkMatrix& m) const {
+ return {fCTM,
+ SkShaderBase::ConcatLocalMatrices(fTotalLocalMatrix, m),
+ SkShaderBase::ConcatLocalMatrices(fPendingLocalMatrix, m),
+ fTotalMatrixIsValid,
+ fCTMApplied};
+}
+
+} // namespace SkShaders
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+SkShaderBase::SkShaderBase() = default;
+
+SkShaderBase::~SkShaderBase() = default;
+
+void SkShaderBase::flatten(SkWriteBuffer& buffer) const { this->INHERITED::flatten(buffer); }
+
+bool SkShaderBase::computeTotalInverse(const SkMatrix& ctm,
+ const SkMatrix* localMatrix,
+ SkMatrix* totalInverse) const {
+ return (localMatrix ? SkMatrix::Concat(ctm, *localMatrix) : ctm).invert(totalInverse);
+}
+
+bool SkShaderBase::asLuminanceColor(SkColor* colorPtr) const {
+ SkColor storage;
+ if (nullptr == colorPtr) {
+ colorPtr = &storage;
+ }
+ if (this->onAsLuminanceColor(colorPtr)) {
+ *colorPtr = SkColorSetA(*colorPtr, 0xFF); // we only return opaque
+ return true;
+ }
+ return false;
+}
+
+SkShaderBase::Context* SkShaderBase::makeContext(const ContextRec& rec, SkArenaAlloc* alloc) const {
+#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
+ // We always fall back to raster pipeline when perspective is present.
+ if (rec.fMatrix->hasPerspective() || (rec.fLocalMatrix && rec.fLocalMatrix->hasPerspective()) ||
+ !this->computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, nullptr)) {
+ return nullptr;
+ }
+
+ return this->onMakeContext(rec, alloc);
+#else
+ return nullptr;
+#endif
+}
+
+SkShaderBase::Context::Context(const SkShaderBase& shader, const ContextRec& rec)
+ : fShader(shader), fCTM(*rec.fMatrix) {
+ // We should never use a context with perspective.
+ SkASSERT(!rec.fMatrix->hasPerspective());
+ SkASSERT(!rec.fLocalMatrix || !rec.fLocalMatrix->hasPerspective());
+
+ // Because the context parameters must be valid at this point, we know that the matrix is
+ // invertible.
+ SkAssertResult(fShader.computeTotalInverse(*rec.fMatrix, rec.fLocalMatrix, &fTotalInverse));
+
+ fPaintAlpha = rec.fPaintAlpha;
+}
+
+SkShaderBase::Context::~Context() {}
+
+bool SkShaderBase::ContextRec::isLegacyCompatible(SkColorSpace* shaderColorSpace) const {
+ // In legacy pipelines, shaders always produce premul (or opaque) and the destination is also
+ // always premul (or opaque). (And those "or opaque" caveats won't make any difference here.)
+ SkAlphaType shaderAT = kPremul_SkAlphaType, dstAT = kPremul_SkAlphaType;
+ return 0 ==
+ SkColorSpaceXformSteps{shaderColorSpace, shaderAT, fDstColorSpace, dstAT}.flags.mask();
+}
+
+sk_sp<SkShader> SkShaderBase::makeAsALocalMatrixShader(SkMatrix*) const { return nullptr; }
+
+#if defined(SK_GRAPHITE)
+// TODO: add implementations for derived classes
+void SkShaderBase::addToKey(const skgpu::graphite::KeyContext& keyContext,
+ skgpu::graphite::PaintParamsKeyBuilder* builder,
+ skgpu::graphite::PipelineDataGatherer* gatherer) const {
+ using namespace skgpu::graphite;
+
+ SolidColorShaderBlock::BeginBlock(keyContext, builder, gatherer, {1, 0, 0, 1});
+ builder->endBlock();
+}
+#endif
+
+bool SkShaderBase::appendRootStages(const SkStageRec& rec, const SkMatrix& ctm) const {
+ return this->appendStages(rec, SkShaders::MatrixRec(ctm));
+}
+
+bool SkShaderBase::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec& mRec) const {
+ // SkShader::Context::shadeSpan() handles the paint opacity internally,
+ // but SkRasterPipelineBlitter applies it as a separate stage.
+ // We skip the internal shadeSpan() step by forcing the paint opaque.
+ SkColor4f opaquePaintColor = rec.fPaintColor.makeOpaque();
+
+ // We don't have a separate ctm and local matrix at this point. Just pass the combined matrix
+ // as the CTM. TODO: thread the MatrixRec through the legacy context system.
+ auto tm = mRec.totalMatrix();
+ ContextRec cr(opaquePaintColor,
+ tm,
+ nullptr,
+ rec.fDstColorType,
+ sk_srgb_singleton(),
+ rec.fSurfaceProps);
+
+ struct CallbackCtx : SkRasterPipeline_CallbackCtx {
+ sk_sp<const SkShader> shader;
+ Context* ctx;
+ };
+ auto cb = rec.fAlloc->make<CallbackCtx>();
+ cb->shader = sk_ref_sp(this);
+ cb->ctx = as_SB(this)->makeContext(cr, rec.fAlloc);
+ cb->fn = [](SkRasterPipeline_CallbackCtx* self, int active_pixels) {
+ auto c = (CallbackCtx*)self;
+ int x = (int)c->rgba[0], y = (int)c->rgba[1];
+ SkPMColor tmp[SkRasterPipeline_kMaxStride_highp];
+ c->ctx->shadeSpan(x, y, tmp, active_pixels);
+
+ for (int i = 0; i < active_pixels; i++) {
+ auto rgba_4f = SkPMColor4f::FromPMColor(tmp[i]);
+ memcpy(c->rgba + 4 * i, rgba_4f.vec(), 4 * sizeof(float));
+ }
+ };
+
+ if (cb->ctx) {
+ rec.fPipeline->append(SkRasterPipelineOp::seed_shader);
+ rec.fPipeline->append(SkRasterPipelineOp::callback, cb);
+ rec.fAlloc
+ ->make<SkColorSpaceXformSteps>(
+ sk_srgb_singleton(), kPremul_SkAlphaType, rec.fDstCS, kPremul_SkAlphaType)
+ ->apply(rec.fPipeline);
+ return true;
+ }
+ return false;
+}
+
+sk_sp<SkShader> SkShaderBase::makeWithCTM(const SkMatrix& postM) const {
+ return sk_sp<SkShader>(new SkCTMShader(sk_ref_sp(this), postM));
+}
+
+#if defined(SK_ENABLE_SKVM)
+skvm::Color SkShaderBase::rootProgram(skvm::Builder* p,
+ skvm::Coord device,
+ skvm::Color paint,
+ const SkMatrix& ctm,
+ const SkColorInfo& dst,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc* alloc) const {
+ // Shader subclasses should always act as if the destination were premul or opaque.
+ // SkVMBlitter handles all the coordination of unpremul itself, via premul.
+ SkColorInfo tweaked =
+ dst.alphaType() == kUnpremul_SkAlphaType ? dst.makeAlphaType(kPremul_SkAlphaType) : dst;
+
+ // Force opaque alpha for all opaque shaders.
+ //
+ // This is primarily nice in that we usually have a 1.0f constant splat
+ // somewhere in the program anyway, and this will let us drop the work the
+ // shader notionally does to produce alpha, p->extract(...), etc. in favor
+ // of that simple hoistable splat.
+ //
+ // More subtly, it makes isOpaque() a parameter to all shader program
+ // generation, guaranteeing that is-opaque bit is mixed into the overall
+ // shader program hash and blitter Key. This makes it safe for us to use
+ // that bit to make decisions when constructing an SkVMBlitter, like doing
+ // SrcOver -> Src strength reduction.
+ if (auto color = this->program(p,
+ device,
+ /*local=*/device,
+ paint,
+ SkShaders::MatrixRec(ctm),
+ tweaked,
+ uniforms,
+ alloc)) {
+ if (this->isOpaque()) {
+ color.a = p->splat(1.0f);
+ }
+ return color;
+ }
+ return {};
+}
+#endif // defined(SK_ENABLE_SKVM)
+
+// need a cheap way to invert the alpha channel of a shader (i.e. 1 - a)
+sk_sp<SkShader> SkShaderBase::makeInvertAlpha() const {
+ return this->makeWithColorFilter(SkColorFilters::Blend(0xFFFFFFFF, SkBlendMode::kSrcOut));
+}
+
+#if defined(SK_ENABLE_SKVM)
+skvm::Coord SkShaderBase::ApplyMatrix(skvm::Builder* p,
+ const SkMatrix& m,
+ skvm::Coord coord,
+ skvm::Uniforms* uniforms) {
+ skvm::F32 x = coord.x, y = coord.y;
+ if (m.isIdentity()) {
+ // That was easy.
+ } else if (m.isTranslate()) {
+ x = p->add(x, p->uniformF(uniforms->pushF(m[2])));
+ y = p->add(y, p->uniformF(uniforms->pushF(m[5])));
+ } else if (m.isScaleTranslate()) {
+ x = p->mad(x, p->uniformF(uniforms->pushF(m[0])), p->uniformF(uniforms->pushF(m[2])));
+ y = p->mad(y, p->uniformF(uniforms->pushF(m[4])), p->uniformF(uniforms->pushF(m[5])));
+ } else { // Affine or perspective.
+ auto dot = [&, x, y](int row) {
+ return p->mad(x,
+ p->uniformF(uniforms->pushF(m[3 * row + 0])),
+ p->mad(y,
+ p->uniformF(uniforms->pushF(m[3 * row + 1])),
+ p->uniformF(uniforms->pushF(m[3 * row + 2]))));
+ };
+ x = dot(0);
+ y = dot(1);
+ if (m.hasPerspective()) {
+ x = x * (1.0f / dot(2));
+ y = y * (1.0f / dot(2));
+ }
+ }
+ return {x, y};
+}
+#endif // defined(SK_ENABLE_SKVM)
diff --git a/src/shaders/SkShaderBase.h b/src/shaders/SkShaderBase.h
index c0f76fd..c7ecb18 100644
--- a/src/shaders/SkShaderBase.h
+++ b/src/shaders/SkShaderBase.h
@@ -9,42 +9,204 @@
#define SkShaderBase_DEFINED
#include "include/core/SkColor.h"
-#include "include/core/SkImageInfo.h"
+#include "include/core/SkFlattenable.h"
#include "include/core/SkMatrix.h"
-#include "include/core/SkPaint.h"
-#include "include/core/SkSamplingOptions.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkScalar.h"
#include "include/core/SkShader.h"
#include "include/core/SkSurfaceProps.h"
+#include "include/core/SkTypes.h"
#include "include/private/base/SkNoncopyable.h"
-#include "src/base/SkTLazy.h"
-#include "src/core/SkEffectPriv.h"
-#include "src/core/SkMask.h"
-#include "src/core/SkVM_fwd.h"
+#include <cstddef>
+#include <cstdint>
+#include <optional>
#include <tuple>
-class GrFragmentProcessor;
-struct GrFPArgs;
class SkArenaAlloc;
class SkColorSpace;
class SkImage;
-struct SkImageInfo;
-class SkPaint;
-class SkRasterPipeline;
class SkRuntimeEffect;
-class SkStageUpdater;
-class SkUpdatableShader;
+class SkWriteBuffer;
+enum SkColorType : int;
+enum class SkTileMode;
+struct SkDeserialProcs;
+struct SkStageRec;
+#if defined(SK_GRAPHITE)
namespace skgpu::graphite {
class KeyContext;
class PaintParamsKeyBuilder;
class PipelineDataGatherer;
}
-
-#if defined(SK_GANESH)
-using GrFPResult = std::tuple<bool /*success*/, std::unique_ptr<GrFragmentProcessor>>;
#endif
+#if defined(SK_ENABLE_SKVM)
+#include "include/core/SkImageInfo.h"
+#include "src/core/SkVM.h"
+#endif
+
+namespace SkShaders {
+/**
+ * This is used to accumulate matrices, starting with the CTM, when building up
+ * SkRasterPipeline, SkVM, and GrFragmentProcessor by walking the SkShader tree. It avoids
+ * adding a matrix multiply for each individual matrix. It also handles the reverse matrix
+ * concatenation order required by Android Framework, see b/256873449.
+ *
+ * This also tracks the dubious concept of a "total matrix", which includes all the matrices
+ * encountered during traversal to the current shader, including ones that have already been
+ * applied. The total matrix represents the transformation from the current shader's coordinate
+ * space to device space. It is dubious because it doesn't account for SkShaders that manipulate
+ * the coordinates passed to their children, which may not even be representable by a matrix.
+ *
+ * The total matrix is used for mipmap level selection and a filter downgrade optimizations in
+ * SkImageShader and sizing of the SkImage created by SkPictureShader. If we can remove usages
+ * of the "total matrix" and if Android Framework could be updated to not use backwards local
+ * matrix concatenation this could just be replaced by a simple SkMatrix or SkM44 passed down
+ * during traversal.
+ */
+class MatrixRec {
+public:
+ MatrixRec() = default;
+
+ explicit MatrixRec(const SkMatrix& ctm);
+
+ /**
+ * Returns a new MatrixRec that represents the existing total and pending matrix
+ * pre-concat'ed with m.
+ */
+ MatrixRec SK_WARN_UNUSED_RESULT concat(const SkMatrix& m) const;
+
+ /**
+ * Appends a mul by the inverse of the pending local matrix to the pipeline. 'postInv' is an
+ * additional matrix to post-apply to the inverted pending matrix. If the pending matrix is
+ * not invertible the std::optional result won't have a value and the pipeline will be
+ * unmodified.
+ */
+ std::optional<MatrixRec> SK_WARN_UNUSED_RESULT apply(const SkStageRec& rec,
+ const SkMatrix& postInv = {}) const;
+
+#if defined(SK_ENABLE_SKVM)
+ /**
+ * Muls local by the inverse of the pending matrix. 'postInv' is an additional matrix to
+ * post-apply to the inverted pending matrix. If the pending matrix is not invertible the
+ * std::optional result won't have a value and the Builder will be unmodified.
+ */
+ std::optional<MatrixRec> SK_WARN_UNUSED_RESULT apply(skvm::Builder*,
+ skvm::Coord* local, // inout
+ skvm::Uniforms*,
+ const SkMatrix& postInv = {}) const;
+#endif
+
+ /**
+ * FP matrices work differently than SkRasterPipeline and SkVM. The starting coordinates
+ * provided to the root SkShader's FP are already in local space. So we never apply the inverse
+ * CTM. This returns the inverted pending local matrix with the provided postInv matrix
+ * applied after it. If the pending local matrix cannot be inverted, the boolean is false.
+ */
+ std::tuple<SkMatrix, bool> applyForFragmentProcessor(const SkMatrix& postInv) const;
+
+ /**
+ * A parent FP may need to create a FP for its child by calling
+ * SkShaderBase::asFragmentProcessor() and then pass the result to the apply() above.
+ * This comes up when the parent needs to ensure pending matrices are applied before the
+ * child because the parent is going to manipulate the coordinates *after* any pending
+ * matrix and pass the resulting coords to the child. This function gets a MatrixRec that
+ * reflects the state after this MatrixRec has bee applied but it does not apply it!
+ * Example:
+ * auto childFP = fChild->asFragmentProcessor(args, mrec.applied());
+ * childFP = MakeAWrappingFPThatModifiesChildsCoords(std::move(childFP));
+ * auto [success, parentFP] = mrec.apply(std::move(childFP));
+ */
+ MatrixRec applied() const;
+
+ /** Call to indicate that the mapping from shader to device space is not known. */
+ void markTotalMatrixInvalid() { fTotalMatrixIsValid = false; }
+
+ /** Marks the CTM as already applied; can avoid re-seeding the shader unnecessarily. */
+ void markCTMApplied() { fCTMApplied = true; }
+
+ /**
+ * Indicates whether the total matrix of a MatrixRec passed to a SkShader actually
+ * represents the full transform between that shader's coordinate space and device space.
+ */
+ bool totalMatrixIsValid() const { return fTotalMatrixIsValid; }
+
+ /**
+ * Gets the total transform from the current shader's space to device space. This may or
+ * may not be valid. Shaders should avoid making decisions based on this matrix if
+ * totalMatrixIsValid() is false.
+ */
+ SkMatrix totalMatrix() const { return SkMatrix::Concat(fCTM, fTotalLocalMatrix); }
+
+ /** Gets the inverse of totalMatrix(), if invertible. */
+ bool SK_WARN_UNUSED_RESULT totalInverse(SkMatrix* out) const {
+ return this->totalMatrix().invert(out);
+ }
+
+ /** Is there a transform that has not yet been applied by a parent shader? */
+ bool hasPendingMatrix() const {
+ return (!fCTMApplied && !fCTM.isIdentity()) || !fPendingLocalMatrix.isIdentity();
+ }
+
+ /** When generating raster pipeline, have the device coordinates been seeded? */
+ bool rasterPipelineCoordsAreSeeded() const { return fCTMApplied; }
+
+private:
+ MatrixRec(const SkMatrix& ctm,
+ const SkMatrix& totalLocalMatrix,
+ const SkMatrix& pendingLocalMatrix,
+ bool totalIsValid,
+ bool ctmApplied)
+ : fCTM(ctm)
+ , fTotalLocalMatrix(totalLocalMatrix)
+ , fPendingLocalMatrix(pendingLocalMatrix)
+ , fTotalMatrixIsValid(totalIsValid)
+ , fCTMApplied(ctmApplied) {}
+
+ const SkMatrix fCTM;
+
+ // Concatenation of all local matrices, including those already applied.
+ const SkMatrix fTotalLocalMatrix;
+
+ // The accumulated local matrices from walking down the shader hierarchy that have NOT yet
+ // been incorporated into the SkRasterPipeline.
+ const SkMatrix fPendingLocalMatrix;
+
+ bool fTotalMatrixIsValid = true;
+
+ // Tracks whether the CTM has already been applied (and in raster pipeline whether the
+ // device coords have been seeded.)
+ bool fCTMApplied = false;
+};
+
+} // namespace SkShaders
+
+#define SK_ALL_SHADERS(M) \
+ M(Blend) \
+ M(CTM) \
+ M(Color) \
+ M(Color4) \
+ M(ColorFilter) \
+ M(CoordClamp) \
+ M(Empty) \
+ M(GradientBase) \
+ M(Image) \
+ M(LocalMatrix) \
+ M(PerlinNoise) \
+ M(Picture) \
+ M(Runtime) \
+ M(Transform) \
+ M(TriColor) \
+ M(UpdatableColor)
+
+#define SK_ALL_GRADIENTS(M) \
+ M(Conical) \
+ M(Linear) \
+ M(Radial) \
+ M(Sweep)
+
class SkShaderBase : public SkShader {
public:
~SkShaderBase() override;
@@ -58,13 +220,19 @@
*/
virtual bool isConstant() const { return false; }
+ enum class ShaderType {
+#define M(type) k##type,
+ SK_ALL_SHADERS(M)
+#undef M
+ };
+
+ virtual ShaderType type() const = 0;
+
enum class GradientType {
kNone,
- kColor,
- kLinear,
- kRadial,
- kSweep,
- kConical
+#define M(type) k##type,
+ SK_ALL_GRADIENTS(M)
+#undef M
};
/**
@@ -181,165 +349,12 @@
};
/**
- * This is used to accumulate matrices, starting with the CTM, when building up
- * SkRasterPipeline, SkVM, and GrFragmentProcessor by walking the SkShader tree. It avoids
- * adding a matrix multiply for each individual matrix. It also handles the reverse matrix
- * concatenation order required by Android Framework, see b/256873449.
- *
- * This also tracks the dubious concept of a "total matrix", which includes all the matrices
- * encountered during traversal to the current shader, including ones that have already been
- * applied. The total matrix represents the transformation from the current shader's coordinate
- * space to device space. It is dubious because it doesn't account for SkShaders that manipulate
- * the coordinates passed to their children, which may not even be representable by a matrix.
- *
- * The total matrix is used for mipmap level selection and a filter downgrade optimizations in
- * SkImageShader and sizing of the SkImage created by SkPictureShader. If we can remove usages
- * of the "total matrix" and if Android Framework could be updated to not use backwards local
- * matrix concatenation this could just be replaced by a simple SkMatrix or SkM44 passed down
- * during traversal.
- */
- class MatrixRec {
- public:
- MatrixRec() = default;
-
- explicit MatrixRec(const SkMatrix& ctm);
-
- /**
- * Returns a new MatrixRec that represents the existing total and pending matrix
- * pre-concat'ed with m.
- */
- MatrixRec SK_WARN_UNUSED_RESULT concat(const SkMatrix& m) const;
-
- /**
- * Appends a mul by the inverse of the pending local matrix to the pipeline. 'postInv' is an
- * additional matrix to post-apply to the inverted pending matrix. If the pending matrix is
- * not invertible the std::optional result won't have a value and the pipeline will be
- * unmodified.
- */
- std::optional<MatrixRec> SK_WARN_UNUSED_RESULT apply(const SkStageRec& rec,
- const SkMatrix& postInv = {}) const;
-
-#if defined(SK_ENABLE_SKVM)
- /**
- * Muls local by the inverse of the pending matrix. 'postInv' is an additional matrix to
- * post-apply to the inverted pending matrix. If the pending matrix is not invertible the
- * std::optional result won't have a value and the Builder will be unmodified.
- */
- std::optional<MatrixRec> SK_WARN_UNUSED_RESULT apply(skvm::Builder*,
- skvm::Coord* local, // inout
- skvm::Uniforms*,
- const SkMatrix& postInv = {}) const;
-#endif
-#if defined(SK_GANESH)
- /**
- * Produces an FP that muls its input coords by the inverse of the pending matrix and then
- * samples the passed FP with those coordinates. 'postInv' is an additional matrix to
- * post-apply to the inverted pending matrix. If the pending matrix is not invertible the
- * GrFPResult's bool will be false and the passed FP will be returned to the caller in the
- * GrFPResult.
- */
- GrFPResult SK_WARN_UNUSED_RESULT apply(std::unique_ptr<GrFragmentProcessor>,
- const SkMatrix& postInv = {}) const;
- /**
- * A parent FP may need to create a FP for its child by calling
- * SkShaderBase::asFragmentProcessor() and then pass the result to the apply() above.
- * This comes up when the parent needs to ensure pending matrices are applied before the
- * child because the parent is going to manipulate the coordinates *after* any pending
- * matrix and pass the resulting coords to the child. This function gets a MatrixRec that
- * reflects the state after this MatrixRec has bee applied but it does not apply it!
- * Example:
- * auto childFP = fChild->asFragmentProcessor(args, mrec.applied());
- * childFP = MakeAWrappingFPThatModifiesChildsCoords(std::move(childFP));
- * auto [success, parentFP] = mrec.apply(std::move(childFP));
- */
- MatrixRec applied() const;
-#endif
-
- /** Call to indicate that the mapping from shader to device space is not known. */
- void markTotalMatrixInvalid() { fTotalMatrixIsValid = false; }
-
- /** Marks the CTM as already applied; can avoid re-seeding the shader unnecessarily. */
- void markCTMApplied() { fCTMApplied = true; }
-
- /**
- * Indicates whether the total matrix of a MatrixRec passed to a SkShader actually
- * represents the full transform between that shader's coordinate space and device space.
- */
- bool totalMatrixIsValid() const { return fTotalMatrixIsValid; }
-
- /**
- * Gets the total transform from the current shader's space to device space. This may or
- * may not be valid. Shaders should avoid making decisions based on this matrix if
- * totalMatrixIsValid() is false.
- */
- SkMatrix totalMatrix() const { return SkMatrix::Concat(fCTM, fTotalLocalMatrix); }
-
- /** Gets the inverse of totalMatrix(), if invertible. */
- bool SK_WARN_UNUSED_RESULT totalInverse(SkMatrix* out) const {
- return this->totalMatrix().invert(out);
- }
-
- /** Is there a transform that has not yet been applied by a parent shader? */
- bool hasPendingMatrix() const {
- return (!fCTMApplied && !fCTM.isIdentity()) || !fPendingLocalMatrix.isIdentity();
- }
-
- /** When generating raster pipeline, have the device coordinates been seeded? */
- bool rasterPipelineCoordsAreSeeded() const { return fCTMApplied; }
-
- private:
- MatrixRec(const SkMatrix& ctm,
- const SkMatrix& totalLocalMatrix,
- const SkMatrix& pendingLocalMatrix,
- bool totalIsValid,
- bool ctmApplied)
- : fCTM(ctm)
- , fTotalLocalMatrix(totalLocalMatrix)
- , fPendingLocalMatrix(pendingLocalMatrix)
- , fTotalMatrixIsValid(totalIsValid)
- , fCTMApplied(ctmApplied) {}
-
- const SkMatrix fCTM;
-
- // Concatenation of all local matrices, including those already applied.
- const SkMatrix fTotalLocalMatrix;
-
- // The accumulated local matrices from walking down the shader hierarchy that have NOT yet
- // been incorporated into the SkRasterPipeline.
- const SkMatrix fPendingLocalMatrix;
-
- bool fTotalMatrixIsValid = true;
-
- // Tracks whether the CTM has already been applied (and in raster pipeline whether the
- // device coords have been seeded.)
- bool fCTMApplied = false;
- };
-
- /**
* Make a context using the memory provided by the arena.
*
* @return pointer to context or nullptr if can't be created
*/
Context* makeContext(const ContextRec&, SkArenaAlloc*) const;
-#if defined(SK_GANESH)
- /**
- * Call on the root SkShader to produce a GrFragmentProcessor.
- *
- * The returned GrFragmentProcessor expects an unpremultiplied input color and produces a
- * premultiplied output.
- */
- std::unique_ptr<GrFragmentProcessor> asRootFragmentProcessor(const GrFPArgs&,
- const SkMatrix& ctm) const;
- /**
- * Virtualized implementation of above. Any pending matrix in the MatrixRec should be applied
- * to the coords if the SkShader uses its coordinates. This can be done by calling
- * MatrixRec::apply() to wrap a GrFragmentProcessor in a GrMatrixEffect.
- */
- virtual std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&,
- const MatrixRec&) const;
-#endif
-
/**
* If the shader can represent its "average" luminance in a single color, return true and
* if color is not NULL, return that color. If it cannot, return false and ignore the color
@@ -363,7 +378,7 @@
* in r,g MatrixRec::apply() must be called (unless the shader doesn't require it's input
* coords). The default impl creates shadercontext and calls that (not very efficient).
*/
- virtual bool appendStages(const SkStageRec&, const MatrixRec&) const;
+ virtual bool appendStages(const SkStageRec&, const SkShaders::MatrixRec&) const;
bool SK_WARN_UNUSED_RESULT computeTotalInverse(const SkMatrix& ctm,
const SkMatrix* localMatrix,
@@ -415,7 +430,7 @@
skvm::Coord device,
skvm::Coord local,
skvm::Color paint,
- const MatrixRec&,
+ const SkShaders::MatrixRec&,
const SkColorInfo& dst,
skvm::Uniforms*,
SkArenaAlloc*) const = 0;
@@ -466,7 +481,7 @@
static skvm::Coord ApplyMatrix(skvm::Builder*, const SkMatrix&, skvm::Coord, skvm::Uniforms*);
#endif
- using INHERITED = SkShader;
+ friend class SkShaders::MatrixRec;
};
inline SkShaderBase* as_SB(SkShader* shader) {
return static_cast<SkShaderBase*>(shader);
@@ -480,9 +495,9 @@
return static_cast<SkShaderBase*>(shader.get());
}
+void SkRegisterBlendShaderFlattenable();
void SkRegisterColor4ShaderFlattenable();
void SkRegisterColorShaderFlattenable();
-void SkRegisterComposeShaderFlattenable();
void SkRegisterCoordClampShaderFlattenable();
void SkRegisterEmptyShaderFlattenable();
void SkRegisterPerlinNoiseShaderFlattenable();
diff --git a/src/shaders/SkTransformShader.cpp b/src/shaders/SkTransformShader.cpp
index afe52cd..8ea3cd6 100644
--- a/src/shaders/SkTransformShader.cpp
+++ b/src/shaders/SkTransformShader.cpp
@@ -5,10 +5,19 @@
* found in the LICENSE file.
*/
-#include "src/core/SkMatrixProvider.h"
-#include "src/core/SkRasterPipeline.h"
#include "src/shaders/SkTransformShader.h"
+#include "include/core/SkMatrix.h"
+#include "src/core/SkEffectPriv.h"
+#include "src/core/SkRasterPipeline.h"
+#include "src/core/SkRasterPipelineOpList.h"
+
+#if defined(SK_ENABLE_SKVM)
+#include "src/core/SkVM.h"
+#endif
+
+#include <optional>
+
SkTransformShader::SkTransformShader(const SkShaderBase& shader, bool allowPerspective)
: fShader{shader}, fAllowPerspective{allowPerspective} {
SkMatrix::I().get9(fMatrixStorage);
@@ -19,7 +28,7 @@
skvm::Coord device,
skvm::Coord local,
skvm::Color color,
- const MatrixRec& mRec,
+ const SkShaders::MatrixRec& mRec,
const SkColorInfo& dst,
skvm::Uniforms* uniforms,
SkArenaAlloc* alloc) const {
@@ -31,13 +40,13 @@
// optimization opportunity, not a correctness bug.
SkASSERT(!mRec.hasPendingMatrix());
- std::optional<MatrixRec> childMRec = mRec.apply(b, &local, uniforms);
+ std::optional<SkShaders::MatrixRec> childMRec = mRec.apply(b, &local, uniforms);
if (!childMRec.has_value()) {
return {};
}
// The matrix we're about to insert gets updated between uses of the VM so our children can't
// know the total transform when they add their stages. We don't incorporate this shader's
- // matrix into the MatrixRec at all.
+ // matrix into the SkShaders::MatrixRec at all.
childMRec->markTotalMatrixInvalid();
auto matrix = uniforms->pushPtr(&fMatrixStorage);
@@ -75,7 +84,8 @@
return false;
}
-bool SkTransformShader::appendStages(const SkStageRec& rec, const MatrixRec& mRec) const {
+bool SkTransformShader::appendStages(const SkStageRec& rec,
+ const SkShaders::MatrixRec& mRec) const {
// We have to seed and apply any constant matrices before appending our matrix that may
// mutate. We could try to add one matrix stage and then incorporate the parent matrix
// with the variable matrix in each call to update(). However, in practice our callers
@@ -83,13 +93,13 @@
// shaders so the call to apply below should just seed the coordinates. If this assert fires
// it just indicates an optimization opportunity, not a correctness bug.
SkASSERT(!mRec.hasPendingMatrix());
- std::optional<MatrixRec> childMRec = mRec.apply(rec);
+ std::optional<SkShaders::MatrixRec> childMRec = mRec.apply(rec);
if (!childMRec.has_value()) {
return false;
}
// The matrix we're about to insert gets updated between uses of the pipeline so our children
// can't know the total transform when they add their stages. We don't even incorporate this
- // matrix into the MatrixRec at all.
+ // matrix into the SkShaders::MatrixRec at all.
childMRec->markTotalMatrixInvalid();
auto type = fAllowPerspective ? SkRasterPipelineOp::matrix_perspective
diff --git a/src/shaders/SkTransformShader.h b/src/shaders/SkTransformShader.h
index f536cf4..ea08bda 100644
--- a/src/shaders/SkTransformShader.h
+++ b/src/shaders/SkTransformShader.h
@@ -7,9 +7,13 @@
#ifndef SkTextCoordShader_DEFINED
#define SkTextCoordShader_DEFINED
-#include "src/core/SkVM.h"
+#include "include/core/SkScalar.h"
+#include "include/private/base/SkAssert.h"
#include "src/shaders/SkShaderBase.h"
+class SkMatrix;
+struct SkStageRec;
+
// SkTransformShader applies a matrix transform to the shader coordinates, like a local matrix
// shader. The difference with a typical local matrix shader is that this shader's matrix is
// not combined with the inverse CTM or other local matrices in order to facilitate modifying the
@@ -28,7 +32,7 @@
skvm::Coord device,
skvm::Coord local,
skvm::Color color,
- const MatrixRec& mRec,
+ const SkShaders::MatrixRec& mRec,
const SkColorInfo& dst,
skvm::Uniforms* uniforms,
SkArenaAlloc* alloc) const override;
@@ -36,11 +40,13 @@
// Adds a pipestage to multiply the incoming coords in 'r' and 'g' by the matrix. The child
// shader is called with no pending local matrix and the total transform as unknowable.
- bool appendStages(const SkStageRec& rec, const MatrixRec&) const override;
+ bool appendStages(const SkStageRec& rec, const SkShaders::MatrixRec&) const override;
// Change the matrix used by the generated SkRasterpipeline or SkVM.
bool update(const SkMatrix& matrix);
+ ShaderType type() const override { return ShaderType::kTransform; }
+
// These are never serialized/deserialized
Factory getFactory() const override {
SkDEBUGFAIL("SkTransformShader shouldn't be serialized.");
diff --git a/src/shaders/SkTriColorShader.cpp b/src/shaders/SkTriColorShader.cpp
new file mode 100644
index 0000000..9e4e5ee
--- /dev/null
+++ b/src/shaders/SkTriColorShader.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/shaders/SkTriColorShader.h"
+
+#include "include/core/SkMatrix.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkTypes.h"
+#include "include/private/SkColorData.h"
+#include "src/base/SkVx.h"
+#include "src/core/SkEffectPriv.h"
+#include "src/core/SkRasterPipeline.h"
+#include "src/core/SkRasterPipelineOpList.h"
+
+#if defined(SK_ENABLE_SKVM)
+#include "src/core/SkVM.h"
+#endif
+
+bool SkTriColorShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec&) const {
+ rec.fPipeline->append(SkRasterPipelineOp::seed_shader);
+ if (fUsePersp) {
+ rec.fPipeline->append(SkRasterPipelineOp::matrix_perspective, &fM33);
+ }
+ rec.fPipeline->append(SkRasterPipelineOp::matrix_4x3, &fM43);
+ return true;
+}
+
+#if defined(SK_ENABLE_SKVM)
+skvm::Color SkTriColorShader::program(skvm::Builder* b,
+ skvm::Coord device,
+ skvm::Coord local,
+ skvm::Color,
+ const SkShaders::MatrixRec&,
+ const SkColorInfo&,
+ skvm::Uniforms* uniforms,
+ SkArenaAlloc* alloc) const {
+ fColorMatrix = uniforms->pushPtr(&fM43);
+
+ skvm::F32 x = local.x, y = local.y;
+
+ if (fUsePersp) {
+ fCoordMatrix = uniforms->pushPtr(&fM33);
+ auto dot = [&, x, y](int row) {
+ return b->mad(x, b->arrayF(fCoordMatrix, row),
+ b->mad(y, b->arrayF(fCoordMatrix, row + 3),
+ b->arrayF(fCoordMatrix, row + 6)));
+ };
+
+ x = dot(0);
+ y = dot(1);
+ x = x * (1.0f / dot(2));
+ y = y * (1.0f / dot(2));
+ }
+
+ auto colorDot = [&, x, y](int row) {
+ return b->mad(x, b->arrayF(fColorMatrix, row),
+ b->mad(y, b->arrayF(fColorMatrix, row + 4),
+ b->arrayF(fColorMatrix, row + 8)));
+ };
+
+ skvm::Color color;
+ color.r = colorDot(0);
+ color.g = colorDot(1);
+ color.b = colorDot(2);
+ color.a = colorDot(3);
+ return color;
+}
+#endif
+
+bool SkTriColorShader::update(const SkMatrix& ctmInv,
+ const SkPoint pts[],
+ const SkPMColor4f colors[],
+ int index0,
+ int index1,
+ int index2) {
+ SkMatrix m, im;
+ m.reset();
+ m.set(0, pts[index1].fX - pts[index0].fX);
+ m.set(1, pts[index2].fX - pts[index0].fX);
+ m.set(2, pts[index0].fX);
+ m.set(3, pts[index1].fY - pts[index0].fY);
+ m.set(4, pts[index2].fY - pts[index0].fY);
+ m.set(5, pts[index0].fY);
+ if (!m.invert(&im)) {
+ return false;
+ }
+
+ fM33.setConcat(im, ctmInv);
+
+ auto c0 = skvx::float4::Load(colors[index0].vec()),
+ c1 = skvx::float4::Load(colors[index1].vec()),
+ c2 = skvx::float4::Load(colors[index2].vec());
+
+ (c1 - c0).store(&fM43.fMat[0]);
+ (c2 - c0).store(&fM43.fMat[4]);
+ c0.store(&fM43.fMat[8]);
+
+ if (!fUsePersp) {
+ fM43.setConcat(fM43, fM33);
+ }
+ return true;
+}
diff --git a/src/shaders/SkTriColorShader.h b/src/shaders/SkTriColorShader.h
new file mode 100644
index 0000000..38ad997
--- /dev/null
+++ b/src/shaders/SkTriColorShader.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkTriColorShader_DEFINED
+#define SkTriColorShader_DEFINED
+
+#include "include/core/SkMatrix.h"
+#include "include/core/SkTypes.h"
+#include "include/private/SkColorData.h"
+#include "src/shaders/SkShaderBase.h"
+
+struct SkPoint;
+struct SkStageRec;
+
+class SkTriColorShader : public SkShaderBase {
+public:
+ SkTriColorShader(bool isOpaque, bool usePersp) : fIsOpaque(isOpaque), fUsePersp(usePersp) {}
+
+ ShaderType type() const override { return ShaderType::kTriColor; }
+
+ // This gets called for each triangle, without re-calling appendStages.
+ bool update(const SkMatrix& ctmInv,
+ const SkPoint pts[],
+ const SkPMColor4f colors[],
+ int index0,
+ int index1,
+ int index2);
+
+protected:
+ bool appendStages(const SkStageRec& rec, const SkShaders::MatrixRec&) const override;
+
+#if defined(SK_ENABLE_SKVM)
+ skvm::Color program(skvm::Builder*,
+ skvm::Coord,
+ skvm::Coord,
+ skvm::Color,
+ const SkShaders::MatrixRec&,
+ const SkColorInfo&,
+ skvm::Uniforms*,
+ SkArenaAlloc*) const override;
+#endif
+
+private:
+ bool isOpaque() const override { return fIsOpaque; }
+ // For serialization. This will never be called.
+ Factory getFactory() const override { return nullptr; }
+ const char* getTypeName() const override { return nullptr; }
+
+ struct Matrix43 {
+ float fMat[12]; // column major
+
+ // Pass a by value, so we don't have to worry about aliasing with this
+ void setConcat(const Matrix43 a, const SkMatrix& b) {
+ SkASSERT(!b.hasPerspective());
+
+ fMat[0] = a.dot(0, b.getScaleX(), b.getSkewY());
+ fMat[1] = a.dot(1, b.getScaleX(), b.getSkewY());
+ fMat[2] = a.dot(2, b.getScaleX(), b.getSkewY());
+ fMat[3] = a.dot(3, b.getScaleX(), b.getSkewY());
+
+ fMat[4] = a.dot(0, b.getSkewX(), b.getScaleY());
+ fMat[5] = a.dot(1, b.getSkewX(), b.getScaleY());
+ fMat[6] = a.dot(2, b.getSkewX(), b.getScaleY());
+ fMat[7] = a.dot(3, b.getSkewX(), b.getScaleY());
+
+ fMat[8] = a.dot(0, b.getTranslateX(), b.getTranslateY()) + a.fMat[8];
+ fMat[9] = a.dot(1, b.getTranslateX(), b.getTranslateY()) + a.fMat[9];
+ fMat[10] = a.dot(2, b.getTranslateX(), b.getTranslateY()) + a.fMat[10];
+ fMat[11] = a.dot(3, b.getTranslateX(), b.getTranslateY()) + a.fMat[11];
+ }
+
+ private:
+ float dot(int index, float x, float y) const {
+ return fMat[index + 0] * x + fMat[index + 4] * y;
+ }
+ };
+
+ // If fUsePersp, we need both of these matrices,
+ // otherwise we can combine them, and only use fM43
+
+ Matrix43 fM43;
+ SkMatrix fM33;
+ const bool fIsOpaque;
+ const bool fUsePersp; // controls our stages, and what we do in update()
+#if defined(SK_ENABLE_SKVM)
+ mutable skvm::Uniform fColorMatrix;
+ mutable skvm::Uniform fCoordMatrix;
+#endif
+};
+
+#endif
diff --git a/src/shaders/gradients/BUILD.bazel b/src/shaders/gradients/BUILD.bazel
index 604026d..c03aeb6 100644
--- a/src/shaders/gradients/BUILD.bazel
+++ b/src/shaders/gradients/BUILD.bazel
@@ -5,14 +5,16 @@
exports_files_legacy()
GRADIENT_FILES = [
- "SkGradientShader.cpp",
- "SkGradientShaderBase.cpp",
- "SkGradientShaderBase.h",
+ "SkConicalGradient.cpp",
+ "SkConicalGradient.h",
+ "SkGradientBaseShader.cpp",
+ "SkGradientBaseShader.h",
"SkLinearGradient.cpp",
"SkLinearGradient.h",
"SkRadialGradient.cpp",
+ "SkRadialGradient.h",
"SkSweepGradient.cpp",
- "SkTwoPointConicalGradient.cpp",
+ "SkSweepGradient.h",
]
split_srcs_and_hdrs(
diff --git a/src/shaders/gradients/SkConicalGradient.cpp b/src/shaders/gradients/SkConicalGradient.cpp
new file mode 100644
index 0000000..0a71e8b
--- /dev/null
+++ b/src/shaders/gradients/SkConicalGradient.cpp
@@ -0,0 +1,449 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "src/shaders/gradients/SkConicalGradient.h"
+
+#include "include/core/SkColor.h"
+#include "include/core/SkColorSpace.h"
+#include "include/core/SkMatrix.h"
+#include "include/core/SkShader.h"
+#include "include/core/SkTileMode.h"
+#include "include/effects/SkGradientShader.h"
+#include "include/private/base/SkAssert.h"
+#include "include/private/base/SkFloatingPoint.h"
+#include "include/private/base/SkTArray.h"
+#include "src/base/SkArenaAlloc.h"
+#include "src/core/SkRasterPipeline.h"
+#include "src/core/SkRasterPipelineOpContexts.h"
+#include "src/core/SkRasterPipelineOpList.h"
+#include "src/core/SkReadBuffer.h"
+#include "src/core/SkWriteBuffer.h"
+#include "src/shaders/SkLocalMatrixShader.h"
+#include "src/shaders/SkShaderBase.h"
+#include "src/shaders/gradients/SkGradientBaseShader.h"
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyContext.h"
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif
+
+#include <algorithm>
+#include <cmath>
+#include <cstdint>
+#include <utility>
+
+bool SkConicalGradient::FocalData::set(SkScalar r0, SkScalar r1, SkMatrix* matrix) {
+ fIsSwapped = false;
+ fFocalX = sk_ieee_float_divide(r0, (r0 - r1));
+ if (SkScalarNearlyZero(fFocalX - 1)) {
+ // swap r0, r1
+ matrix->postTranslate(-1, 0);
+ matrix->postScale(-1, 1);
+ std::swap(r0, r1);
+ fFocalX = 0; // because r0 is now 0
+ fIsSwapped = true;
+ }
+
+ // Map {focal point, (1, 0)} to {(0, 0), (1, 0)}
+ const SkPoint from[2] = { {fFocalX, 0}, {1, 0} };
+ const SkPoint to[2] = { {0, 0}, {1, 0} };
+ SkMatrix focalMatrix;
+ if (!focalMatrix.setPolyToPoly(from, to, 2)) {
+ return false;
+ }
+ matrix->postConcat(focalMatrix);
+ fR1 = r1 / SkScalarAbs(1 - fFocalX); // focalMatrix has a scale of 1/(1-f)
+
+ // The following transformations are just to accelerate the shader computation by saving
+ // some arithmatic operations.
+ if (this->isFocalOnCircle()) {
+ matrix->postScale(0.5, 0.5);
+ } else {
+ matrix->postScale(fR1 / (fR1 * fR1 - 1), 1 / sqrt(SkScalarAbs(fR1 * fR1 - 1)));
+ }
+ matrix->postScale(SkScalarAbs(1 - fFocalX), SkScalarAbs(1 - fFocalX)); // scale |1 - f|
+ return true;
+}
+
+sk_sp<SkShader> SkConicalGradient::Create(const SkPoint& c0,
+ SkScalar r0,
+ const SkPoint& c1,
+ SkScalar r1,
+ const Descriptor& desc,
+ const SkMatrix* localMatrix) {
+ SkMatrix gradientMatrix;
+ Type gradientType;
+
+ if (SkScalarNearlyZero((c0 - c1).length())) {
+ if (SkScalarNearlyZero(std::max(r0, r1)) || SkScalarNearlyEqual(r0, r1)) {
+ // Degenerate case; avoid dividing by zero. Should have been caught by caller but
+ // just in case, recheck here.
+ return nullptr;
+ }
+ // Concentric case: we can pretend we're radial (with a tiny twist).
+ const SkScalar scale = sk_ieee_float_divide(1, std::max(r0, r1));
+ gradientMatrix = SkMatrix::Translate(-c1.x(), -c1.y());
+ gradientMatrix.postScale(scale, scale);
+
+ gradientType = Type::kRadial;
+ } else {
+ const SkPoint centers[2] = { c0 , c1 };
+ const SkPoint unitvec[2] = { {0, 0}, {1, 0} };
+
+ if (!gradientMatrix.setPolyToPoly(centers, unitvec, 2)) {
+ // Degenerate case.
+ return nullptr;
+ }
+
+ gradientType = SkScalarNearlyZero(r1 - r0) ? Type::kStrip : Type::kFocal;
+ }
+
+ FocalData focalData;
+ if (gradientType == Type::kFocal) {
+ const auto dCenter = (c0 - c1).length();
+ if (!focalData.set(r0 / dCenter, r1 / dCenter, &gradientMatrix)) {
+ return nullptr;
+ }
+ }
+ return SkLocalMatrixShader::MakeWrapped<SkConicalGradient>(
+ localMatrix, c0, r0, c1, r1, desc, gradientType, gradientMatrix, focalData);
+}
+
+SkConicalGradient::SkConicalGradient(const SkPoint& start,
+ SkScalar startRadius,
+ const SkPoint& end,
+ SkScalar endRadius,
+ const Descriptor& desc,
+ Type type,
+ const SkMatrix& gradientMatrix,
+ const FocalData& data)
+ : SkGradientBaseShader(desc, gradientMatrix)
+ , fCenter1(start)
+ , fCenter2(end)
+ , fRadius1(startRadius)
+ , fRadius2(endRadius)
+ , fType(type) {
+ // this is degenerate, and should be caught by our caller
+ SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
+ if (type == Type::kFocal) {
+ fFocalData = data;
+ }
+}
+
+bool SkConicalGradient::isOpaque() const {
+ // Because areas outside the cone are left untouched, we cannot treat the
+ // shader as opaque even if the gradient itself is opaque.
+ // TODO(junov): Compute whether the cone fills the plane crbug.com/222380
+ return false;
+}
+
+// Returns the original non-sorted version of the gradient
+SkShaderBase::GradientType SkConicalGradient::asGradient(GradientInfo* info,
+ SkMatrix* localMatrix) const {
+ if (info) {
+ commonAsAGradient(info);
+ info->fPoint[0] = fCenter1;
+ info->fPoint[1] = fCenter2;
+ info->fRadius[0] = fRadius1;
+ info->fRadius[1] = fRadius2;
+ }
+ if (localMatrix) {
+ *localMatrix = SkMatrix::I();
+ }
+ return GradientType::kConical;
+}
+
+sk_sp<SkFlattenable> SkConicalGradient::CreateProc(SkReadBuffer& buffer) {
+ DescriptorScope desc;
+ SkMatrix legacyLocalMatrix;
+ if (!desc.unflatten(buffer, &legacyLocalMatrix)) {
+ return nullptr;
+ }
+ SkPoint c1 = buffer.readPoint();
+ SkPoint c2 = buffer.readPoint();
+ SkScalar r1 = buffer.readScalar();
+ SkScalar r2 = buffer.readScalar();
+
+ if (!buffer.isValid()) {
+ return nullptr;
+ }
+ return SkGradientShader::MakeTwoPointConical(c1,
+ r1,
+ c2,
+ r2,
+ desc.fColors,
+ std::move(desc.fColorSpace),
+ desc.fPositions,
+ desc.fColorCount,
+ desc.fTileMode,
+ desc.fInterpolation,
+ &legacyLocalMatrix);
+}
+
+void SkConicalGradient::flatten(SkWriteBuffer& buffer) const {
+ this->SkGradientBaseShader::flatten(buffer);
+ buffer.writePoint(fCenter1);
+ buffer.writePoint(fCenter2);
+ buffer.writeScalar(fRadius1);
+ buffer.writeScalar(fRadius2);
+}
+
+void SkConicalGradient::appendGradientStages(SkArenaAlloc* alloc,
+ SkRasterPipeline* p,
+ SkRasterPipeline* postPipeline) const {
+ const auto dRadius = fRadius2 - fRadius1;
+
+ if (fType == Type::kRadial) {
+ p->append(SkRasterPipelineOp::xy_to_radius);
+
+ // Tiny twist: radial computes a t for [0, r2], but we want a t for [r1, r2].
+ auto scale = std::max(fRadius1, fRadius2) / dRadius;
+ auto bias = -fRadius1 / dRadius;
+
+ p->append_matrix(alloc, SkMatrix::Translate(bias, 0) * SkMatrix::Scale(scale, 1));
+ return;
+ }
+
+ if (fType == Type::kStrip) {
+ auto* ctx = alloc->make<SkRasterPipeline_2PtConicalCtx>();
+ SkScalar scaledR0 = fRadius1 / this->getCenterX1();
+ ctx->fP0 = scaledR0 * scaledR0;
+ p->append(SkRasterPipelineOp::xy_to_2pt_conical_strip, ctx);
+ p->append(SkRasterPipelineOp::mask_2pt_conical_nan, ctx);
+ postPipeline->append(SkRasterPipelineOp::apply_vector_mask, &ctx->fMask);
+ return;
+ }
+
+ auto* ctx = alloc->make<SkRasterPipeline_2PtConicalCtx>();
+ ctx->fP0 = 1 / fFocalData.fR1;
+ ctx->fP1 = fFocalData.fFocalX;
+
+ if (fFocalData.isFocalOnCircle()) {
+ p->append(SkRasterPipelineOp::xy_to_2pt_conical_focal_on_circle);
+ } else if (fFocalData.isWellBehaved()) {
+ p->append(SkRasterPipelineOp::xy_to_2pt_conical_well_behaved, ctx);
+ } else if (fFocalData.isSwapped() || 1 - fFocalData.fFocalX < 0) {
+ p->append(SkRasterPipelineOp::xy_to_2pt_conical_smaller, ctx);
+ } else {
+ p->append(SkRasterPipelineOp::xy_to_2pt_conical_greater, ctx);
+ }
+
+ if (!fFocalData.isWellBehaved()) {
+ p->append(SkRasterPipelineOp::mask_2pt_conical_degenerates, ctx);
+ }
+ if (1 - fFocalData.fFocalX < 0) {
+ p->append(SkRasterPipelineOp::negate_x);
+ }
+ if (!fFocalData.isNativelyFocal()) {
+ p->append(SkRasterPipelineOp::alter_2pt_conical_compensate_focal, ctx);
+ }
+ if (fFocalData.isSwapped()) {
+ p->append(SkRasterPipelineOp::alter_2pt_conical_unswap);
+ }
+ if (!fFocalData.isWellBehaved()) {
+ postPipeline->append(SkRasterPipelineOp::apply_vector_mask, &ctx->fMask);
+ }
+}
+
+#if defined(SK_ENABLE_SKVM)
+skvm::F32 SkConicalGradient::transformT(skvm::Builder* p,
+ skvm::Uniforms* uniforms,
+ skvm::Coord coord,
+ skvm::I32* mask) const {
+ auto mag = [](skvm::F32 x, skvm::F32 y) { return sqrt(x * x + y * y); };
+
+ // See https://skia.org/dev/design/conical, and appendStages() above.
+ // There's a lot going on here, and I'm not really sure what's independent
+ // or disjoint, what can be reordered, simplified, etc. Tweak carefully.
+
+ const skvm::F32 x = coord.x, y = coord.y;
+ if (fType == Type::kRadial) {
+ float denom = 1.0f / (fRadius2 - fRadius1);
+ float scale = std::max(fRadius1, fRadius2) * denom;
+ float bias = -fRadius1 * denom;
+ return mag(x,y) * p->uniformF(uniforms->pushF(scale))
+ + p->uniformF(uniforms->pushF(bias ));
+ }
+
+ if (fType == Type::kStrip) {
+ float r = fRadius1 / this->getCenterX1();
+ skvm::F32 t = x + sqrt(p->uniformF(uniforms->pushF(r * r)) - y * y);
+
+ *mask = (t == t); // t != NaN
+ return t;
+ }
+
+ const skvm::F32 invR1 = p->uniformF(uniforms->pushF(1 / fFocalData.fR1));
+
+ skvm::F32 t;
+ if (fFocalData.isFocalOnCircle()) {
+ t = (y / x) * y + x; // (x^2 + y^2) / x ~~> x + y^2/x ~~> y/x * y + x
+ } else if (fFocalData.isWellBehaved()) {
+ t = mag(x, y) - x * invR1;
+ } else {
+ skvm::F32 k = sqrt(x * x - y * y);
+ if (fFocalData.isSwapped() || 1 - fFocalData.fFocalX < 0) {
+ k = -k;
+ }
+ t = k - x * invR1;
+ }
+
+ if (!fFocalData.isWellBehaved()) {
+ // TODO: not sure why we consider t == 0 degenerate
+ *mask = (t > 0.0f); // and implicitly, t != NaN
+ }
+
+ const skvm::F32 focalX = p->uniformF(uniforms->pushF(fFocalData.fFocalX));
+ if (1 - fFocalData.fFocalX < 0) {
+ t = -t;
+ }
+ if (!fFocalData.isNativelyFocal()) {
+ t += focalX;
+ }
+ if (fFocalData.isSwapped()) {
+ t = 1.0f - t;
+ }
+ return t;
+}
+#endif
+
+#if defined(SK_GRAPHITE)
+void SkConicalGradient::addToKey(const skgpu::graphite::KeyContext& keyContext,
+ skgpu::graphite::PaintParamsKeyBuilder* builder,
+ skgpu::graphite::PipelineDataGatherer* gatherer) const {
+ this->addToKeyCommon(keyContext,
+ builder,
+ gatherer,
+ GradientType::kConical,
+ fCenter1,
+ fCenter2,
+ fRadius1,
+ fRadius2,
+ 0.0f,
+ 0.0f);
+}
+#endif
+
+// assumes colors is SkColor4f* and pos is SkScalar*
+#define EXPAND_1_COLOR(count) \
+ SkColor4f tmp[2]; \
+ do { \
+ if (1 == count) { \
+ tmp[0] = tmp[1] = colors[0]; \
+ colors = tmp; \
+ pos = nullptr; \
+ count = 2; \
+ } \
+ } while (0)
+
+sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
+ SkScalar startRadius,
+ const SkPoint& end,
+ SkScalar endRadius,
+ const SkColor4f colors[],
+ sk_sp<SkColorSpace> colorSpace,
+ const SkScalar pos[],
+ int colorCount,
+ SkTileMode mode,
+ const Interpolation& interpolation,
+ const SkMatrix* localMatrix) {
+ if (startRadius < 0 || endRadius < 0) {
+ return nullptr;
+ }
+ if (!SkGradientBaseShader::ValidGradient(colors, colorCount, mode, interpolation)) {
+ return nullptr;
+ }
+ if (SkScalarNearlyZero((start - end).length(), SkGradientBaseShader::kDegenerateThreshold)) {
+ // If the center positions are the same, then the gradient is the radial variant of a 2 pt
+ // conical gradient, an actual radial gradient (startRadius == 0), or it is fully degenerate
+ // (startRadius == endRadius).
+ if (SkScalarNearlyEqual(
+ startRadius, endRadius, SkGradientBaseShader::kDegenerateThreshold)) {
+ // Degenerate case, where the interpolation region area approaches zero. The proper
+ // behavior depends on the tile mode, which is consistent with the default degenerate
+ // gradient behavior, except when mode = clamp and the radii > 0.
+ if (mode == SkTileMode::kClamp &&
+ endRadius > SkGradientBaseShader::kDegenerateThreshold) {
+ // The interpolation region becomes an infinitely thin ring at the radius, so the
+ // final gradient will be the first color repeated from p=0 to 1, and then a hard
+ // stop switching to the last color at p=1.
+ static constexpr SkScalar circlePos[3] = {0, 1, 1};
+ SkColor4f reColors[3] = {colors[0], colors[0], colors[colorCount - 1]};
+ return MakeRadial(start,
+ endRadius,
+ reColors,
+ std::move(colorSpace),
+ circlePos,
+ 3,
+ mode,
+ interpolation,
+ localMatrix);
+ } else {
+ // Otherwise use the default degenerate case
+ return SkGradientBaseShader::MakeDegenerateGradient(
+ colors, pos, colorCount, std::move(colorSpace), mode);
+ }
+ } else if (SkScalarNearlyZero(startRadius, SkGradientBaseShader::kDegenerateThreshold)) {
+ // We can treat this gradient as radial, which is faster. If we got here, we know
+ // that endRadius is not equal to 0, so this produces a meaningful gradient
+ return MakeRadial(start,
+ endRadius,
+ colors,
+ std::move(colorSpace),
+ pos,
+ colorCount,
+ mode,
+ interpolation,
+ localMatrix);
+ }
+ // Else it's the 2pt conical radial variant with no degenerate radii, so fall through to the
+ // regular 2pt constructor.
+ }
+
+ if (localMatrix && !localMatrix->invert(nullptr)) {
+ return nullptr;
+ }
+ EXPAND_1_COLOR(colorCount);
+
+ SkGradientBaseShader::ColorStopOptimizer opt(colors, pos, colorCount, mode);
+
+ SkGradientBaseShader::Descriptor desc(
+ opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, interpolation);
+ return SkConicalGradient::Create(start, startRadius, end, endRadius, desc, localMatrix);
+}
+
+#undef EXPAND_1_COLOR
+
+sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
+ SkScalar startRadius,
+ const SkPoint& end,
+ SkScalar endRadius,
+ const SkColor colors[],
+ const SkScalar pos[],
+ int colorCount,
+ SkTileMode mode,
+ uint32_t flags,
+ const SkMatrix* localMatrix) {
+ SkColorConverter converter(colors, colorCount);
+ return MakeTwoPointConical(start,
+ startRadius,
+ end,
+ endRadius,
+ converter.fColors4f.begin(),
+ nullptr,
+ pos,
+ colorCount,
+ mode,
+ flags,
+ localMatrix);
+}
+
+void SkRegisterConicalGradientShaderFlattenable() {
+ SK_REGISTER_FLATTENABLE(SkConicalGradient);
+ // Previous name
+ SkFlattenable::Register("SkTwoPointConicalGradient", SkConicalGradient::CreateProc);
+}
diff --git a/src/shaders/gradients/SkConicalGradient.h b/src/shaders/gradients/SkConicalGradient.h
new file mode 100644
index 0000000..e66d617
--- /dev/null
+++ b/src/shaders/gradients/SkConicalGradient.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkConicalGradient_DEFINED
+#define SkConicalGradient_DEFINED
+
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkScalar.h"
+#include "src/shaders/gradients/SkGradientBaseShader.h"
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyContext.h"
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif
+
+class SkArenaAlloc;
+class SkMatrix;
+class SkRasterPipeline;
+class SkReadBuffer;
+class SkShader;
+class SkWriteBuffer;
+
+// Please see https://skia.org/dev/design/conical for how our shader works.
+class SkConicalGradient final : public SkGradientBaseShader {
+public:
+ // See https://skia.org/dev/design/conical for what focal data means and how our shader works.
+ // We make it public so the GPU shader can also use it.
+ struct FocalData {
+ SkScalar fR1; // r1 after mapping focal point to (0, 0)
+ SkScalar fFocalX; // f
+ bool fIsSwapped; // whether we swapped r0, r1
+
+ // The input r0, r1 are the radii when we map centers to {(0, 0), (1, 0)}.
+ // We'll post concat matrix with our transformation matrix that maps focal point to (0, 0).
+ // Returns true if the set succeeded
+ bool set(SkScalar r0, SkScalar r1, SkMatrix* matrix);
+
+ // Whether the focal point (0, 0) is on the end circle with center (1, 0) and radius r1. If
+ // this is true, it's as if an aircraft is flying at Mach 1 and all circles (soundwaves)
+ // will go through the focal point (aircraft). In our previous implementations, this was
+ // known as the edge case where the inside circle touches the outside circle (on the focal
+ // point). If we were to solve for t bruteforcely using a quadratic equation, this case
+ // implies that the quadratic equation degenerates to a linear equation.
+ bool isFocalOnCircle() const { return SkScalarNearlyZero(1 - fR1); }
+
+ bool isSwapped() const { return fIsSwapped; }
+ bool isWellBehaved() const { return !this->isFocalOnCircle() && fR1 > 1; }
+ bool isNativelyFocal() const { return SkScalarNearlyZero(fFocalX); }
+ };
+
+ enum class Type { kRadial, kStrip, kFocal };
+
+ static sk_sp<SkShader> Create(const SkPoint& start,
+ SkScalar startRadius,
+ const SkPoint& end,
+ SkScalar endRadius,
+ const Descriptor&,
+ const SkMatrix* localMatrix);
+
+ GradientType asGradient(GradientInfo* info, SkMatrix* localMatrix) const override;
+#if defined(SK_GRAPHITE)
+ void addToKey(const skgpu::graphite::KeyContext&,
+ skgpu::graphite::PaintParamsKeyBuilder*,
+ skgpu::graphite::PipelineDataGatherer*) const override;
+#endif
+ bool isOpaque() const override;
+
+ SkScalar getCenterX1() const { return SkPoint::Distance(fCenter1, fCenter2); }
+ SkScalar getStartRadius() const { return fRadius1; }
+ SkScalar getDiffRadius() const { return fRadius2 - fRadius1; }
+ const SkPoint& getStartCenter() const { return fCenter1; }
+ const SkPoint& getEndCenter() const { return fCenter2; }
+ SkScalar getEndRadius() const { return fRadius2; }
+
+ Type getType() const { return fType; }
+ const FocalData& getFocalData() const { return fFocalData; }
+
+ SkConicalGradient(const SkPoint& c0,
+ SkScalar r0,
+ const SkPoint& c1,
+ SkScalar r1,
+ const Descriptor&,
+ Type,
+ const SkMatrix&,
+ const FocalData&);
+
+protected:
+ void flatten(SkWriteBuffer& buffer) const override;
+
+ void appendGradientStages(SkArenaAlloc* alloc,
+ SkRasterPipeline* tPipeline,
+ SkRasterPipeline* postPipeline) const override;
+#if defined(SK_ENABLE_SKVM)
+ skvm::F32 transformT(skvm::Builder*,
+ skvm::Uniforms*,
+ skvm::Coord coord,
+ skvm::I32* mask) const final;
+#endif
+
+private:
+ friend void ::SkRegisterConicalGradientShaderFlattenable();
+ SK_FLATTENABLE_HOOKS(SkConicalGradient)
+
+ SkPoint fCenter1;
+ SkPoint fCenter2;
+ SkScalar fRadius1;
+ SkScalar fRadius2;
+ Type fType;
+
+ FocalData fFocalData;
+};
+
+#endif
diff --git a/src/shaders/gradients/SkGradientShaderBase.cpp b/src/shaders/gradients/SkGradientBaseShader.cpp
similarity index 79%
rename from src/shaders/gradients/SkGradientShaderBase.cpp
rename to src/shaders/gradients/SkGradientBaseShader.cpp
index 29f643d..8f6d412 100644
--- a/src/shaders/gradients/SkGradientShaderBase.cpp
+++ b/src/shaders/gradients/SkGradientBaseShader.cpp
@@ -5,21 +5,33 @@
* found in the LICENSE file.
*/
-#include "src/shaders/gradients/SkGradientShaderBase.h"
+#include "src/shaders/gradients/SkGradientBaseShader.h"
+#include "include/core/SkAlphaType.h"
#include "include/core/SkColorSpace.h"
+#include "include/core/SkColorType.h"
+#include "include/core/SkData.h"
+#include "include/core/SkImageInfo.h"
+#include "include/core/SkShader.h"
+#include "include/core/SkTileMode.h"
+#include "include/private/base/SkFloatBits.h"
+#include "include/private/base/SkFloatingPoint.h"
+#include "include/private/base/SkMalloc.h"
+#include "include/private/base/SkTPin.h"
+#include "include/private/base/SkTo.h"
+#include "src/base/SkArenaAlloc.h"
#include "src/base/SkVx.h"
#include "src/core/SkColorSpacePriv.h"
#include "src/core/SkColorSpaceXformSteps.h"
#include "src/core/SkConvertPixels.h"
-#include "src/core/SkMatrixProvider.h"
+#include "src/core/SkEffectPriv.h"
+#include "src/core/SkPicturePriv.h"
#include "src/core/SkRasterPipeline.h"
+#include "src/core/SkRasterPipelineOpContexts.h"
+#include "src/core/SkRasterPipelineOpList.h"
#include "src/core/SkReadBuffer.h"
-#include "src/core/SkVM.h"
#include "src/core/SkWriteBuffer.h"
-using namespace skia_private;
-
#if defined(SK_GRAPHITE)
#include "src/base/SkHalf.h"
#include "src/core/SkColorSpacePriv.h"
@@ -30,7 +42,12 @@
#include "src/gpu/graphite/RecorderPriv.h"
#endif
+#include <algorithm>
#include <cmath>
+#include <optional>
+#include <utility>
+
+using namespace skia_private;
enum GradientSerializationFlags {
// Bits 29:31 used for various boolean flags
@@ -56,13 +73,13 @@
kInterpolationInPremul_GSF = 0x1,
};
-SkGradientShaderBase::Descriptor::Descriptor() {
+SkGradientBaseShader::Descriptor::Descriptor() {
sk_bzero(this, sizeof(*this));
fTileMode = SkTileMode::kClamp;
}
-SkGradientShaderBase::Descriptor::~Descriptor() = default;
+SkGradientBaseShader::Descriptor::~Descriptor() = default;
-void SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const {
+void SkGradientBaseShader::flatten(SkWriteBuffer& buffer) const {
uint32_t flags = 0;
if (fPositions) {
flags |= kHasPosition_GSF;
@@ -117,7 +134,7 @@
return true;
}
-bool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer,
+bool SkGradientBaseShader::DescriptorScope::unflatten(SkReadBuffer& buffer,
SkMatrix* legacyLocalMatrix) {
// New gradient format. Includes floating point color, color space, densely packed flags
uint32_t flags = buffer.readUInt();
@@ -165,7 +182,7 @@
////////////////////////////////////////////////////////////////////////////////////////////
-SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit)
+SkGradientBaseShader::SkGradientBaseShader(const Descriptor& desc, const SkMatrix& ptsToUnit)
: fPtsToUnit(ptsToUnit)
, fColorSpace(desc.fColorSpace ? desc.fColorSpace : SkColorSpace::MakeSRGB())
, fFirstStopIsImplicit(false)
@@ -200,7 +217,7 @@
size_t storageSize =
fColorCount * (sizeof(SkColor4f) + (desc.fPositions ? sizeof(SkScalar) : 0));
- fColors = reinterpret_cast<SkColor4f*>(fStorage.reset(storageSize));
+ fColors = reinterpret_cast<SkColor4f*>(fStorage.reset(storageSize));
fPositions = desc.fPositions ? reinterpret_cast<SkScalar*>(fColors + fColorCount) : nullptr;
// Now copy over the colors, adding the duplicates at t=0 and t=1 as needed
@@ -220,7 +237,7 @@
if (desc.fPositions) {
SkScalar prev = 0;
SkScalar* positions = fPositions;
- *positions++ = prev; // force the first pos to 0
+ *positions++ = prev; // force the first pos to 0
int startIndex = fFirstStopIsImplicit ? 0 : 1;
int count = desc.fColorCount + fLastStopIsImplicit;
@@ -242,10 +259,12 @@
}
}
-SkGradientShaderBase::~SkGradientShaderBase() {}
+SkGradientBaseShader::~SkGradientBaseShader() {}
-static void add_stop_color(SkRasterPipeline_GradientCtx* ctx, size_t stop,
- SkPMColor4f Fs, SkPMColor4f Bs) {
+static void add_stop_color(SkRasterPipeline_GradientCtx* ctx,
+ size_t stop,
+ SkPMColor4f Fs,
+ SkPMColor4f Bs) {
(ctx->fs[0])[stop] = Fs.fR;
(ctx->fs[1])[stop] = Fs.fG;
(ctx->fs[2])[stop] = Fs.fB;
@@ -258,64 +277,70 @@
}
static void add_const_color(SkRasterPipeline_GradientCtx* ctx, size_t stop, SkPMColor4f color) {
- add_stop_color(ctx, stop, { 0, 0, 0, 0 }, color);
+ add_stop_color(ctx, stop, {0, 0, 0, 0}, color);
}
// Calculate a factor F and a bias B so that color = F*t + B when t is in range of
// the stop. Assume that the distance between stops is 1/gapCount.
-static void init_stop_evenly(SkRasterPipeline_GradientCtx* ctx, float gapCount, size_t stop,
- SkPMColor4f c_l, SkPMColor4f c_r) {
+static void init_stop_evenly(SkRasterPipeline_GradientCtx* ctx,
+ float gapCount,
+ size_t stop,
+ SkPMColor4f c_l,
+ SkPMColor4f c_r) {
// Clankium's GCC 4.9 targeting ARMv7 is barfing when we use Sk4f math here, so go scalar...
SkPMColor4f Fs = {
- (c_r.fR - c_l.fR) * gapCount,
- (c_r.fG - c_l.fG) * gapCount,
- (c_r.fB - c_l.fB) * gapCount,
- (c_r.fA - c_l.fA) * gapCount,
+ (c_r.fR - c_l.fR) * gapCount,
+ (c_r.fG - c_l.fG) * gapCount,
+ (c_r.fB - c_l.fB) * gapCount,
+ (c_r.fA - c_l.fA) * gapCount,
};
SkPMColor4f Bs = {
- c_l.fR - Fs.fR*(stop/gapCount),
- c_l.fG - Fs.fG*(stop/gapCount),
- c_l.fB - Fs.fB*(stop/gapCount),
- c_l.fA - Fs.fA*(stop/gapCount),
+ c_l.fR - Fs.fR * (stop / gapCount),
+ c_l.fG - Fs.fG * (stop / gapCount),
+ c_l.fB - Fs.fB * (stop / gapCount),
+ c_l.fA - Fs.fA * (stop / gapCount),
};
add_stop_color(ctx, stop, Fs, Bs);
}
// For each stop we calculate a bias B and a scale factor F, such that
// for any t between stops n and n+1, the color we want is B[n] + F[n]*t.
-static void init_stop_pos(SkRasterPipeline_GradientCtx* ctx, size_t stop, float t_l, float t_r,
- SkPMColor4f c_l, SkPMColor4f c_r) {
+static void init_stop_pos(SkRasterPipeline_GradientCtx* ctx,
+ size_t stop,
+ float t_l,
+ float t_r,
+ SkPMColor4f c_l,
+ SkPMColor4f c_r) {
// See note about Clankium's old compiler in init_stop_evenly().
SkPMColor4f Fs = {
- (c_r.fR - c_l.fR) / (t_r - t_l),
- (c_r.fG - c_l.fG) / (t_r - t_l),
- (c_r.fB - c_l.fB) / (t_r - t_l),
- (c_r.fA - c_l.fA) / (t_r - t_l),
+ (c_r.fR - c_l.fR) / (t_r - t_l),
+ (c_r.fG - c_l.fG) / (t_r - t_l),
+ (c_r.fB - c_l.fB) / (t_r - t_l),
+ (c_r.fA - c_l.fA) / (t_r - t_l),
};
SkPMColor4f Bs = {
- c_l.fR - Fs.fR*t_l,
- c_l.fG - Fs.fG*t_l,
- c_l.fB - Fs.fB*t_l,
- c_l.fA - Fs.fA*t_l,
+ c_l.fR - Fs.fR * t_l,
+ c_l.fG - Fs.fG * t_l,
+ c_l.fB - Fs.fB * t_l,
+ c_l.fA - Fs.fA * t_l,
};
ctx->ts[stop] = t_l;
add_stop_color(ctx, stop, Fs, Bs);
}
-void SkGradientShaderBase::AppendGradientFillStages(SkRasterPipeline* p,
+void SkGradientBaseShader::AppendGradientFillStages(SkRasterPipeline* p,
SkArenaAlloc* alloc,
const SkPMColor4f* pmColors,
const SkScalar* positions,
int count) {
// The two-stop case with stops at 0 and 1.
if (count == 2 && positions == nullptr) {
- const SkPMColor4f c_l = pmColors[0],
- c_r = pmColors[1];
+ const SkPMColor4f c_l = pmColors[0], c_r = pmColors[1];
// See F and B below.
auto ctx = alloc->make<SkRasterPipeline_EvenlySpaced2StopGradientCtx>();
(skvx::float4::Load(c_r.vec()) - skvx::float4::Load(c_l.vec())).store(ctx->f);
- ( skvx::float4::Load(c_l.vec())).store(ctx->b);
+ (skvx::float4::Load(c_l.vec())).store(ctx->b);
p->append(SkRasterPipelineOp::evenly_spaced_2_stop_gradient, ctx);
} else {
@@ -350,7 +375,7 @@
ctx->ts = alloc->makeArray<float>(count + 1);
- // Remove the default stops inserted by SkGradientShaderBase::SkGradientShaderBase
+ // Remove the default stops inserted by SkGradientBaseShader::SkGradientBaseShader
// because they are naturally handled by the search method.
int firstStop;
int lastStop;
@@ -363,12 +388,12 @@
}
size_t stopCount = 0;
- float t_l = positions[firstStop];
+ float t_l = positions[firstStop];
SkPMColor4f c_l = pmColors[firstStop];
add_const_color(ctx, stopCount++, c_l);
// N.B. lastStop is the index of the last stop, not one after.
for (int i = firstStop; i < lastStop; i++) {
- float t_r = positions[i + 1];
+ float t_r = positions[i + 1];
SkPMColor4f c_r = pmColors[i + 1];
SkASSERT(t_l <= t_r);
if (t_l < t_r) {
@@ -388,12 +413,13 @@
}
}
-bool SkGradientShaderBase::appendStages(const SkStageRec& rec, const MatrixRec& mRec) const {
+bool SkGradientBaseShader::appendStages(const SkStageRec& rec,
+ const SkShaders::MatrixRec& mRec) const {
SkRasterPipeline* p = rec.fPipeline;
SkArenaAlloc* alloc = rec.fAlloc;
SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr;
- std::optional<MatrixRec> newMRec = mRec.apply(rec, fPtsToUnit);
+ std::optional<SkShaders::MatrixRec> newMRec = mRec.apply(rec, fPtsToUnit);
if (!newMRec.has_value()) {
return false;
}
@@ -402,9 +428,13 @@
this->appendGradientStages(alloc, p, &postPipeline);
- switch(fTileMode) {
- case SkTileMode::kMirror: p->append(SkRasterPipelineOp::mirror_x_1); break;
- case SkTileMode::kRepeat: p->append(SkRasterPipelineOp::repeat_x_1); break;
+ switch (fTileMode) {
+ case SkTileMode::kMirror:
+ p->append(SkRasterPipelineOp::mirror_x_1);
+ break;
+ case SkTileMode::kRepeat:
+ p->append(SkRasterPipelineOp::repeat_x_1);
+ break;
case SkTileMode::kDecal:
decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>();
decal_ctx->limit_x = SkBits2Float(SkFloat2Bits(1.0f) + 1);
@@ -445,7 +475,8 @@
p->append(SkRasterPipelineOp::unpremul_polar);
colorIsPremul = false;
break;
- default: break;
+ default:
+ break;
}
}
@@ -500,59 +531,44 @@
f[0] = (lab.g * (1 / 500.0f)) + f[1];
f[2] = f[1] - (lab.b * (1 / 200.0f));
- skvm::F32 f_cubed[3] = { f[0]*f[0]*f[0], f[1]*f[1]*f[1], f[2]*f[2]*f[2] };
+ skvm::F32 f_cubed[3] = {f[0] * f[0] * f[0], f[1] * f[1] * f[1], f[2] * f[2] * f[2]};
- skvm::F32 xyz[3] = {
- skvm::select(f_cubed[0] > e, f_cubed[0], (116 * f[0] - 16) * (1 / k)),
- skvm::select(lab.r > k * e , f_cubed[1], lab.r * (1 / k)),
- skvm::select(f_cubed[2] > e, f_cubed[2], (116 * f[2] - 16) * (1 / k))
- };
+ skvm::F32 xyz[3] = {skvm::select(f_cubed[0] > e, f_cubed[0], (116 * f[0] - 16) * (1 / k)),
+ skvm::select(lab.r > k * e, f_cubed[1], lab.r * (1 / k)),
+ skvm::select(f_cubed[2] > e, f_cubed[2], (116 * f[2] - 16) * (1 / k))};
- constexpr float D50[3] = { 0.3457f / 0.3585f, 1.0f, (1.0f - 0.3457f - 0.3585f) / 0.3585f };
- return skvm::Color { xyz[0]*D50[0], xyz[1]*D50[1], xyz[2]*D50[2], lab.a };
+ constexpr float D50[3] = {0.3457f / 0.3585f, 1.0f, (1.0f - 0.3457f - 0.3585f) / 0.3585f};
+ return skvm::Color{xyz[0] * D50[0], xyz[1] * D50[1], xyz[2] * D50[2], lab.a};
}
// Skia stores all polar colors with hue in the first component, so this "LCH -> Lab" transform
// actually takes "HCL". This is also used to do the same polar transform for OkHCL to OkLAB.
static skvm::Color css_hcl_to_lab(skvm::Color hcl) {
skvm::F32 hueRadians = hcl.r * (SK_FloatPI / 180);
- return skvm::Color {
- hcl.b,
- hcl.g * approx_cos(hueRadians),
- hcl.g * approx_sin(hueRadians),
- hcl.a
- };
+ return skvm::Color{
+ hcl.b, hcl.g * approx_cos(hueRadians), hcl.g * approx_sin(hueRadians), hcl.a};
}
-static skvm::Color css_hcl_to_xyz(skvm::Color hcl) {
- return css_lab_to_xyz(css_hcl_to_lab(hcl));
-}
+static skvm::Color css_hcl_to_xyz(skvm::Color hcl) { return css_lab_to_xyz(css_hcl_to_lab(hcl)); }
static skvm::Color css_oklab_to_linear_srgb(skvm::Color oklab) {
skvm::F32 l_ = oklab.r + 0.3963377774f * oklab.g + 0.2158037573f * oklab.b,
m_ = oklab.r - 0.1055613458f * oklab.g - 0.0638541728f * oklab.b,
s_ = oklab.r - 0.0894841775f * oklab.g - 1.2914855480f * oklab.b;
- skvm::F32 l = l_*l_*l_,
- m = m_*m_*m_,
- s = s_*s_*s_;
+ skvm::F32 l = l_ * l_ * l_, m = m_ * m_ * m_, s = s_ * s_ * s_;
- return skvm::Color {
- +4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s,
- -1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s,
- -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s,
- oklab.a
- };
-
+ return skvm::Color{+4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s,
+ -1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s,
+ -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s,
+ oklab.a};
}
static skvm::Color css_okhcl_to_linear_srgb(skvm::Color okhcl) {
return css_oklab_to_linear_srgb(css_hcl_to_lab(okhcl));
}
-static skvm::F32 mod_f(skvm::F32 x, float y) {
- return x - y * skvm::floor(x * (1 / y));
-}
+static skvm::F32 mod_f(skvm::F32 x, float y) { return x - y * skvm::floor(x * (1 / y)); }
static skvm::Color css_hsl_to_srgb(skvm::Color hsl) {
hsl.r = mod_f(hsl.r, 360);
@@ -562,17 +578,15 @@
hsl.b *= 0.01f;
skvm::F32 k[3] = {
- mod_f(0 + hsl.r * (1 / 30.0f), 12),
- mod_f(8 + hsl.r * (1 / 30.0f), 12),
- mod_f(4 + hsl.r * (1 / 30.0f), 12),
+ mod_f(0 + hsl.r * (1 / 30.0f), 12),
+ mod_f(8 + hsl.r * (1 / 30.0f), 12),
+ mod_f(4 + hsl.r * (1 / 30.0f), 12),
};
skvm::F32 a = hsl.g * min(hsl.b, 1 - hsl.b);
- return skvm::Color {
- hsl.b - a * clamp(min(k[0] - 3, 9 - k[0]), -1, 1),
- hsl.b - a * clamp(min(k[1] - 3, 9 - k[1]), -1, 1),
- hsl.b - a * clamp(min(k[2] - 3, 9 - k[2]), -1, 1),
- hsl.a
- };
+ return skvm::Color{hsl.b - a * clamp(min(k[0] - 3, 9 - k[0]), -1, 1),
+ hsl.b - a * clamp(min(k[1] - 3, 9 - k[1]), -1, 1),
+ hsl.b - a * clamp(min(k[2] - 3, 9 - k[2]), -1, 1),
+ hsl.a};
}
static skvm::Color css_hwb_to_srgb(skvm::Color hwb, skvm::Builder* p) {
@@ -588,19 +602,17 @@
skvm::I32 isGray = (hwb.g + hwb.b) >= 1;
- return skvm::Color {
- select(isGray, gray, rgb.r),
- select(isGray, gray, rgb.g),
- select(isGray, gray, rgb.b),
- hwb.a
- };
+ return skvm::Color{select(isGray, gray, rgb.r),
+ select(isGray, gray, rgb.g),
+ select(isGray, gray, rgb.b),
+ hwb.a};
}
-skvm::Color SkGradientShaderBase::program(skvm::Builder* p,
+skvm::Color SkGradientBaseShader::program(skvm::Builder* p,
skvm::Coord device,
skvm::Coord local,
skvm::Color /*paint*/,
- const MatrixRec& mRec,
+ const SkShaders::MatrixRec& mRec,
const SkColorInfo& dstInfo,
skvm::Uniforms* uniforms,
SkArenaAlloc* alloc) const {
@@ -609,13 +621,13 @@
}
skvm::I32 mask = p->splat(~0);
- skvm::F32 t = this->transformT(p,uniforms, local, &mask);
+ skvm::F32 t = this->transformT(p, uniforms, local, &mask);
// Perhaps unexpectedly, clamping is handled naturally by our search, so we
// don't explicitly clamp t to [0,1]. That clamp would break hard stops
// right at 0 or 1 boundaries in kClamp mode. (kRepeat and kMirror always
// produce values in [0,1].)
- switch(fTileMode) {
+ switch (fTileMode) {
case SkTileMode::kClamp:
break;
@@ -630,8 +642,7 @@
case SkTileMode::kMirror: {
// t = | (t-1) - 2*(floor( (t-1)*0.5 )) - 1 |
// {-A-} {--------B-------}
- skvm::F32 A = t - 1.0f,
- B = floor(A * 0.5f);
+ skvm::F32 A = t - 1.0f, B = floor(A * 0.5f);
t = abs(A - (B + B) - 1.0f);
} break;
}
@@ -642,8 +653,10 @@
// Transform our colors into a scale factor f and bias b such that for
// any t between stops i and i+1, the color we want is mad(t, f[i], b[i]).
- using F4 = skvx::Vec<4,float>;
- struct FB { F4 f,b; };
+ using F4 = skvx::Vec<4, float>;
+ struct FB {
+ F4 f, b;
+ };
skvm::Color color;
auto uniformF = [&](float x) { return p->uniformF(uniforms->pushF(x)); };
@@ -653,17 +666,15 @@
SkASSERT(fPositions == nullptr);
// With 2 stops, we upload the single FB as uniforms and interpolate directly with t.
- F4 lo = F4::Load(rgba + 0),
- hi = F4::Load(rgba + 1);
- F4 F = hi - lo,
- B = lo;
+ F4 lo = F4::Load(rgba + 0), hi = F4::Load(rgba + 1);
+ F4 F = hi - lo, B = lo;
auto T = clamp01(t);
color = {
- T * uniformF(F[0]) + uniformF(B[0]),
- T * uniformF(F[1]) + uniformF(B[1]),
- T * uniformF(F[2]) + uniformF(B[2]),
- T * uniformF(F[3]) + uniformF(B[3]),
+ T * uniformF(F[0]) + uniformF(B[0]),
+ T * uniformF(F[1]) + uniformF(B[1]),
+ T * uniformF(F[2]) + uniformF(B[2]),
+ T * uniformF(F[3]) + uniformF(B[3]),
};
} else {
// To handle clamps in search we add a conceptual stop at t=-inf, so we
@@ -673,28 +684,27 @@
// stops: (-inf) t0 t1 t2 ...
//
// Both these arrays could end up shorter if any hard stops share the same t.
- FB* fb = alloc->makeArrayDefault<FB>(fColorCount+1);
+ FB* fb = alloc->makeArrayDefault<FB>(fColorCount + 1);
std::vector<float> stops; // TODO: SkSTArray?
stops.reserve(fColorCount);
// Here's our conceptual stop at t=-inf covering all t<=0, clamping to our first color.
- float t_lo = this->getPos(0);
+ float t_lo = this->getPos(0);
F4 color_lo = F4::Load(rgba);
- fb[0] = { 0.0f, color_lo };
+ fb[0] = {0.0f, color_lo};
// N.B. No stops[] entry for this implicit -inf.
// Now the non-edge cases, calculating scale and bias between adjacent normal stops.
for (int i = 1; i < fColorCount; i++) {
- float t_hi = this->getPos(i);
+ float t_hi = this->getPos(i);
F4 color_hi = F4::Load(rgba + i);
// If t_lo == t_hi, we're on a hard stop, and transition immediately to the next color.
SkASSERT(t_lo <= t_hi);
if (t_lo < t_hi) {
- F4 f = (color_hi - color_lo) / (t_hi - t_lo),
- b = color_lo - f*t_lo;
+ F4 f = (color_hi - color_lo) / (t_hi - t_lo), b = color_lo - f * t_lo;
stops.push_back(t_lo);
- fb[stops.size()] = {f,b};
+ fb[stops.size()] = {f, b};
}
t_lo = t_hi;
@@ -702,7 +712,7 @@
}
// Anything >= our final t clamps to our final color.
stops.push_back(t_lo);
- fb[stops.size()] = { 0.0f, color_lo };
+ fb[stops.size()] = {0.0f, color_lo};
// We'll gather FBs from that array we just created.
skvm::Uniform fbs = uniforms->pushPtr(fb);
@@ -742,10 +752,10 @@
// This is what we've been building towards!
color = {
- t * Fr + Br,
- t * Fg + Bg,
- t * Fb + Bb,
- t * Fa + Ba,
+ t * Fr + Br,
+ t * Fg + Bg,
+ t * Fb + Bb,
+ t * Fa + Ba,
};
}
@@ -770,19 +780,20 @@
color.r = hue;
colorIsPremul = false;
} break;
- default: break;
+ default:
+ break;
}
}
// Convert colors in exotic spaces back to their intermediate SkColorSpace
switch (fInterpolation.fColorSpace) {
- case ColorSpace::kLab: color = css_lab_to_xyz(color); break;
- case ColorSpace::kOKLab: color = css_oklab_to_linear_srgb(color); break;
- case ColorSpace::kLCH: color = css_hcl_to_xyz(color); break;
- case ColorSpace::kOKLCH: color = css_okhcl_to_linear_srgb(color); break;
- case ColorSpace::kHSL: color = css_hsl_to_srgb(color); break;
- case ColorSpace::kHWB: color = css_hwb_to_srgb(color, p); break;
- default: break;
+ case ColorSpace::kLab: color = css_lab_to_xyz(color); break;
+ case ColorSpace::kOKLab: color = css_oklab_to_linear_srgb(color); break;
+ case ColorSpace::kLCH: color = css_hcl_to_xyz(color); break;
+ case ColorSpace::kOKLCH: color = css_okhcl_to_linear_srgb(color); break;
+ case ColorSpace::kHSL: color = css_hsl_to_srgb(color); break;
+ case ColorSpace::kHWB: color = css_hwb_to_srgb(color, p); break;
+ default: break;
}
// Now transform from intermediate to destination color space.
@@ -802,15 +813,15 @@
.program(p, uniforms, color);
return {
- pun_to_F32(mask & pun_to_I32(color.r)),
- pun_to_F32(mask & pun_to_I32(color.g)),
- pun_to_F32(mask & pun_to_I32(color.b)),
- pun_to_F32(mask & pun_to_I32(color.a)),
+ pun_to_F32(mask & pun_to_I32(color.r)),
+ pun_to_F32(mask & pun_to_I32(color.g)),
+ pun_to_F32(mask & pun_to_I32(color.b)),
+ pun_to_F32(mask & pun_to_I32(color.a)),
};
}
#endif // defined(SK_ENABLE_SKVM)
-bool SkGradientShaderBase::isOpaque() const {
+bool SkGradientBaseShader::isOpaque() const {
return fColorsAreOpaque && (this->getTileMode() != SkTileMode::kDecal);
}
@@ -818,7 +829,7 @@
return (numer + (denom >> 1)) / denom;
}
-bool SkGradientShaderBase::onAsLuminanceColor(SkColor* lum) const {
+bool SkGradientBaseShader::onAsLuminanceColor(SkColor* lum) const {
// we just compute an average color.
// possibly we could weight this based on the proportional width for each color
// assuming they are not evenly distributed in the fPos array.
@@ -841,15 +852,18 @@
SkColorSpace* dst) {
using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
switch (cs) {
- case ColorSpace::kDestination: return sk_ref_sp(dst);
+ case ColorSpace::kDestination:
+ return sk_ref_sp(dst);
// css-color-4 allows XYZD50 and XYZD65. For gradients, those are redundant. Interpolating
// in any linear RGB space, (regardless of white point), gives the same answer.
- case ColorSpace::kSRGBLinear: return SkColorSpace::MakeSRGBLinear();
+ case ColorSpace::kSRGBLinear:
+ return SkColorSpace::MakeSRGBLinear();
case ColorSpace::kSRGB:
case ColorSpace::kHSL:
- case ColorSpace::kHWB: return SkColorSpace::MakeSRGB();
+ case ColorSpace::kHWB:
+ return SkColorSpace::MakeSRGB();
case ColorSpace::kLab:
case ColorSpace::kLCH:
@@ -889,18 +903,18 @@
hue *= 60;
}
- return { hue, sat * 100, light * 100, rgb.fA };
+ return {hue, sat * 100, light * 100, rgb.fA};
}
static SkPMColor4f srgb_to_hwb(SkPMColor4f rgb) {
SkPMColor4f hsl = srgb_to_hsl(rgb);
- float white = std::min({rgb.fR, rgb.fG, rgb.fB});
+ float white = std::min({rgb.fR, rgb.fG, rgb.fB});
float black = 1 - std::max({rgb.fR, rgb.fG, rgb.fB});
- return { hsl.fR, white * 100, black * 100, rgb.fA };
+ return {hsl.fR, white * 100, black * 100, rgb.fA};
}
static SkPMColor4f xyzd50_to_lab(SkPMColor4f xyz) {
- constexpr float D50[3] = { 0.3457f / 0.3585f, 1.0f, (1.0f - 0.3457f - 0.3585f) / 0.3585f };
+ constexpr float D50[3] = {0.3457f / 0.3585f, 1.0f, (1.0f - 0.3457f - 0.3585f) / 0.3585f};
constexpr float e = 216.0f / 24389;
constexpr float k = 24389.0f / 27;
@@ -911,7 +925,7 @@
f[i] = (v > e) ? std::cbrtf(v) : (k * v + 16) / 116;
}
- return { (116 * f[1]) - 16, 500 * (f[0] - f[1]), 200 * (f[1] - f[2]), xyz.fA };
+ return {(116 * f[1]) - 16, 500 * (f[0] - f[1]), 200 * (f[1] - f[2]), xyz.fA};
}
// The color space is technically LCH, but we produce HCL, so that all polar spaces have hue in the
@@ -919,10 +933,7 @@
static SkPMColor4f xyzd50_to_hcl(SkPMColor4f xyz) {
SkPMColor4f Lab = xyzd50_to_lab(xyz);
float hue = sk_float_radians_to_degrees(atan2f(Lab[2], Lab[1]));
- return {hue >= 0 ? hue : hue + 360,
- sqrtf(Lab[1] * Lab[1] + Lab[2] * Lab[2]),
- Lab[0],
- xyz.fA};
+ return {hue >= 0 ? hue : hue + 360, sqrtf(Lab[1] * Lab[1] + Lab[2] * Lab[2]), Lab[0], xyz.fA};
}
// https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklab
@@ -933,12 +944,10 @@
l = std::cbrtf(l);
m = std::cbrtf(m);
s = std::cbrtf(s);
- return {
- 0.2104542553f*l + 0.7936177850f*m - 0.0040720468f*s,
- 1.9779984951f*l - 2.4285922050f*m + 0.4505937099f*s,
- 0.0259040371f*l + 0.7827717662f*m - 0.8086757660f*s,
- rgb.fA
- };
+ return {0.2104542553f * l + 0.7936177850f * m - 0.0040720468f * s,
+ 1.9779984951f * l - 2.4285922050f * m + 0.4505937099f * s,
+ 0.0259040371f * l + 0.7827717662f * m - 0.8086757660f * s,
+ rgb.fA};
}
// The color space is technically OkLCH, but we produce HCL, so that all polar spaces have hue in
@@ -953,11 +962,11 @@
}
static SkPMColor4f premul_polar(SkPMColor4f hsl) {
- return { hsl.fR, hsl.fG * hsl.fA, hsl.fB * hsl.fA, hsl.fA };
+ return {hsl.fR, hsl.fG * hsl.fA, hsl.fB * hsl.fA, hsl.fA};
}
static SkPMColor4f premul_rgb(SkPMColor4f rgb) {
- return { rgb.fR * rgb.fA, rgb.fG * rgb.fA, rgb.fB * rgb.fA, rgb.fA };
+ return {rgb.fR * rgb.fA, rgb.fG * rgb.fA, rgb.fB * rgb.fA, rgb.fA};
}
static bool color_space_is_polar(SkGradientShader::Interpolation::ColorSpace cs) {
@@ -996,7 +1005,7 @@
// Two have hue as the first component, and two have it as the third component. To reduce
// complexity, we always store hue in the first component, swapping it with luminance for
// LCH and Oklch. The backend code (eg, shaders) needs to know about this.
-SkColor4fXformer::SkColor4fXformer(const SkGradientShaderBase* shader, SkColorSpace* dst) {
+SkColor4fXformer::SkColor4fXformer(const SkGradientBaseShader* shader, SkColorSpace* dst) {
using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
using HueMethod = SkGradientShader::Interpolation::HueMethod;
@@ -1013,8 +1022,12 @@
auto srcInfo = info.makeColorSpace(shader->fColorSpace);
fColors.reset(colorCount);
- SkAssertResult(SkConvertPixels(dstInfo, fColors.begin(), info.minRowBytes(),
- srcInfo, shader->fColors, info.minRowBytes()));
+ SkAssertResult(SkConvertPixels(dstInfo,
+ fColors.begin(),
+ info.minRowBytes(),
+ srcInfo,
+ shader->fColors,
+ info.minRowBytes()));
// 3) Transform to the interpolation color space (if it's special)
ConvertColorProc convertFn = nullptr;
@@ -1043,8 +1056,8 @@
if (color_space_is_polar(interpolation.fColorSpace)) {
float delta = 0;
for (int i = 0; i < colorCount - 1; ++i) {
- float h1 = fColors[i].fR;
- float& h2 = fColors[i+1].fR;
+ float h1 = fColors[i].fR;
+ float& h2 = fColors[i + 1].fR;
h2 += delta;
switch (interpolation.fHueMethod) {
case HueMethod::kShorter:
@@ -1092,8 +1105,12 @@
case ColorSpace::kHSL:
case ColorSpace::kHWB:
case ColorSpace::kLCH:
- case ColorSpace::kOKLCH: premulFn = premul_polar; break;
- default: premulFn = premul_rgb; break;
+ case ColorSpace::kOKLCH:
+ premulFn = premul_polar;
+ break;
+ default:
+ premulFn = premul_rgb;
+ break;
}
}
@@ -1107,14 +1124,14 @@
SkColorConverter::SkColorConverter(const SkColor* colors, int count) {
const float ONE_OVER_255 = 1.f / 255;
for (int i = 0; i < count; ++i) {
- fColors4f.push_back({ SkColorGetR(colors[i]) * ONE_OVER_255,
- SkColorGetG(colors[i]) * ONE_OVER_255,
- SkColorGetB(colors[i]) * ONE_OVER_255,
- SkColorGetA(colors[i]) * ONE_OVER_255 });
+ fColors4f.push_back({SkColorGetR(colors[i]) * ONE_OVER_255,
+ SkColorGetG(colors[i]) * ONE_OVER_255,
+ SkColorGetB(colors[i]) * ONE_OVER_255,
+ SkColorGetA(colors[i]) * ONE_OVER_255});
}
}
-void SkGradientShaderBase::commonAsAGradient(GradientInfo* info) const {
+void SkGradientBaseShader::commonAsAGradient(GradientInfo* info) const {
if (info) {
if (info->fColorCount >= fColorCount) {
if (info->fColors) {
@@ -1138,14 +1155,16 @@
// Return true if these parameters are valid/legal/safe to construct a gradient
//
-bool SkGradientShaderBase::ValidGradient(const SkColor4f colors[], int count, SkTileMode tileMode,
+bool SkGradientBaseShader::ValidGradient(const SkColor4f colors[],
+ int count,
+ SkTileMode tileMode,
const Interpolation& interpolation) {
return nullptr != colors && count >= 1 && (unsigned)tileMode < kSkTileModeCount &&
(unsigned)interpolation.fColorSpace < Interpolation::kColorSpaceCount &&
(unsigned)interpolation.fHueMethod < Interpolation::kHueMethodCount;
}
-SkGradientShaderBase::Descriptor::Descriptor(const SkColor4f colors[],
+SkGradientBaseShader::Descriptor::Descriptor(const SkColor4f colors[],
sk_sp<SkColorSpace> colorSpace,
const SkScalar positions[],
int colorCount,
@@ -1160,7 +1179,8 @@
SkASSERT(fColorCount > 1);
}
-static SkColor4f average_gradient_color(const SkColor4f colors[], const SkScalar pos[],
+static SkColor4f average_gradient_color(const SkColor4f colors[],
+ const SkScalar pos[],
int colorCount) {
// The gradient is a piecewise linear interpolation between colors. For a given interval,
// the integral between the two endpoints is 0.5 * (ci + cj) * (pj - pi), which provides that
@@ -1214,12 +1234,12 @@
// Except for special circumstances of clamped gradients, every gradient shape--when degenerate--
// can be mapped to the same fallbacks. The specific shape factories must account for special
// clamped conditions separately, this will always return the last color for clamped gradients.
-sk_sp<SkShader> SkGradientShaderBase::MakeDegenerateGradient(const SkColor4f colors[],
+sk_sp<SkShader> SkGradientBaseShader::MakeDegenerateGradient(const SkColor4f colors[],
const SkScalar pos[],
int colorCount,
sk_sp<SkColorSpace> colorSpace,
SkTileMode mode) {
- switch(mode) {
+ switch (mode) {
case SkTileMode::kDecal:
// normally this would reject the area outside of the interpolation region, so since
// inside region is empty when the radii are equal, the entire draw region is empty
@@ -1229,8 +1249,8 @@
// repeat and mirror are treated the same: the border colors are never visible,
// but approximate the final color as infinite repetitions of the colors, so
// it can be represented as the average color of the gradient.
- return SkShaders::Color(
- average_gradient_color(colors, pos, colorCount), std::move(colorSpace));
+ return SkShaders::Color(average_gradient_color(colors, pos, colorCount),
+ std::move(colorSpace));
case SkTileMode::kClamp:
// Depending on how the gradient shape degenerates, there may be a more specialized
// fallback representation for the factories to use, but this is a reasonable default.
@@ -1240,39 +1260,28 @@
return nullptr;
}
-SkGradientShaderBase::ColorStopOptimizer::ColorStopOptimizer(const SkColor4f* colors,
+SkGradientBaseShader::ColorStopOptimizer::ColorStopOptimizer(const SkColor4f* colors,
const SkScalar* pos,
int count,
SkTileMode mode)
- : fColors(colors)
- , fPos(pos)
- , fCount(count) {
-
+ : fColors(colors), fPos(pos), fCount(count) {
if (!pos || count != 3) {
return;
}
- if (SkScalarNearlyEqual(pos[0], 0.0f) &&
- SkScalarNearlyEqual(pos[1], 0.0f) &&
+ if (SkScalarNearlyEqual(pos[0], 0.0f) && SkScalarNearlyEqual(pos[1], 0.0f) &&
SkScalarNearlyEqual(pos[2], 1.0f)) {
-
- if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode ||
- colors[0] == colors[1]) {
-
+ if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode || colors[0] == colors[1]) {
// Ignore the leftmost color/pos.
fColors += 1;
- fPos += 1;
- fCount = 2;
+ fPos += 1;
+ fCount = 2;
}
- } else if (SkScalarNearlyEqual(pos[0], 0.0f) &&
- SkScalarNearlyEqual(pos[1], 1.0f) &&
+ } else if (SkScalarNearlyEqual(pos[0], 0.0f) && SkScalarNearlyEqual(pos[1], 1.0f) &&
SkScalarNearlyEqual(pos[2], 1.0f)) {
-
- if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode ||
- colors[1] == colors[2]) {
-
+ if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode || colors[1] == colors[2]) {
// Ignore the rightmost color/pos.
- fCount = 2;
+ fCount = 2;
}
}
}
@@ -1287,7 +1296,7 @@
skgpu::graphite::PaintParamsKeyBuilder* builder,
skgpu::graphite::PipelineDataGatherer* gatherer,
const skgpu::graphite::GradientShaderBlocks::GradientData& gradData,
- const SkGradientShaderBase::Interpolation& interp,
+ const SkGradientBaseShader::Interpolation& interp,
SkColorSpace* intermediateCS) {
using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
using namespace skgpu::graphite;
@@ -1309,25 +1318,24 @@
const SkColorInfo& dstColorInfo = keyContext.dstColorInfo();
- SkColorSpace* dstColorSpace = dstColorInfo.colorSpace() ? dstColorInfo.colorSpace()
- : sk_srgb_singleton();
+ SkColorSpace* dstColorSpace =
+ dstColorInfo.colorSpace() ? dstColorInfo.colorSpace() : sk_srgb_singleton();
- SkAlphaType intermediateAlphaType = inputPremul ? kPremul_SkAlphaType
- : kUnpremul_SkAlphaType;
+ SkAlphaType intermediateAlphaType = inputPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
- ColorSpaceTransformBlock::ColorSpaceTransformData data(intermediateCS, intermediateAlphaType,
- dstColorSpace, dstColorInfo.alphaType());
+ ColorSpaceTransformBlock::ColorSpaceTransformData data(
+ intermediateCS, intermediateAlphaType, dstColorSpace, dstColorInfo.alphaType());
// The gradient block and colorSpace conversion block need to be combined together
// (via the colorFilterShader block) so that the localMatrix block can treat them as
// one child.
ColorFilterShaderBlock::BeginBlock(keyContext, builder, gatherer);
- GradientShaderBlocks::BeginBlock(keyContext, builder, gatherer, gradData);
- builder->endBlock();
+ GradientShaderBlocks::BeginBlock(keyContext, builder, gatherer, gradData);
+ builder->endBlock();
- ColorSpaceTransformBlock::BeginBlock(keyContext, builder, gatherer, &data);
- builder->endBlock();
+ ColorSpaceTransformBlock::BeginBlock(keyContext, builder, gatherer, &data);
+ builder->endBlock();
builder->endBlock();
}
@@ -1337,25 +1345,23 @@
const float* offsets) {
SkBitmap colorsAndOffsetsBitmap;
- colorsAndOffsetsBitmap.allocPixels(SkImageInfo::Make(numStops, 2,
- kRGBA_F16_SkColorType,
- kPremul_SkAlphaType));
+ colorsAndOffsetsBitmap.allocPixels(
+ SkImageInfo::Make(numStops, 2, kRGBA_F16_SkColorType, kPremul_SkAlphaType));
for (int i = 0; i < numStops; i++) {
// TODO: there should be a way to directly set a premul pixel in a bitmap with
// a premul color.
SkColor4f unpremulColor = colors[i].unpremul();
- colorsAndOffsetsBitmap.erase(unpremulColor,
- SkIRect::MakeXYWH(i, 0, 1, 1));
+ colorsAndOffsetsBitmap.erase(unpremulColor, SkIRect::MakeXYWH(i, 0, 1, 1));
- float offset = offsets ? offsets[i] : SkIntToFloat(i) / (numStops-1);
+ float offset = offsets ? offsets[i] : SkIntToFloat(i) / (numStops - 1);
SkASSERT(offset >= 0.0f && offset <= 1.0f);
int exponent;
float mantissa = frexp(offset, &exponent);
SkHalf halfE = SkFloatToHalf(exponent);
- if ((int) SkHalfToFloat(halfE) != exponent) {
+ if ((int)SkHalfToFloat(halfE) != exponent) {
SKGPU_LOG_W("Encoding gradient to f16 failed");
return {};
}
@@ -1363,29 +1369,32 @@
#if defined(SK_DEBUG)
SkHalf halfM = SkFloatToHalf(mantissa);
- float restored = ldexp(SkHalfToFloat(halfM), (int) SkHalfToFloat(halfE));
+ float restored = ldexp(SkHalfToFloat(halfM), (int)SkHalfToFloat(halfE));
float error = abs(restored - offset);
SkASSERT(error < 0.001f);
#endif
// TODO: we're only using 2 of the f16s here. The encoding could be altered to better
// preserve precision. This encoding yields < 0.001f error for 2^20 evenly spaced stops.
- colorsAndOffsetsBitmap.erase(SkColor4f{mantissa, (float) exponent, 0, 1},
+ colorsAndOffsetsBitmap.erase(SkColor4f{mantissa, (float)exponent, 0, 1},
SkIRect::MakeXYWH(i, 1, 1, 1));
}
return colorsAndOffsetsBitmap;
}
-} // anonymous namespace
+} // anonymous namespace
-void SkGradientShaderBase::addToKeyCommon(const skgpu::graphite::KeyContext& keyContext,
+void SkGradientBaseShader::addToKeyCommon(const skgpu::graphite::KeyContext& keyContext,
skgpu::graphite::PaintParamsKeyBuilder* builder,
skgpu::graphite::PipelineDataGatherer* gatherer,
GradientType type,
- SkPoint point0, SkPoint point1,
- float radius0, float radius1,
- float bias, float scale) const {
+ SkPoint point0,
+ SkPoint point1,
+ float radius0,
+ float radius1,
+ float bias,
+ float scale) const {
using namespace skgpu::graphite;
SkColor4fXformer xformedColors(this, keyContext.dstColorInfo().colorSpace());
@@ -1395,9 +1404,8 @@
if (fColorCount > GradientShaderBlocks::GradientData::kNumInternalStorageStops) {
if (fColorsAndOffsetsBitmap.empty()) {
- fColorsAndOffsetsBitmap = create_color_and_offset_bitmap(fColorCount,
- colors,
- fPositions);
+ fColorsAndOffsetsBitmap =
+ create_color_and_offset_bitmap(fColorCount, colors, fPositions);
if (fColorsAndOffsetsBitmap.empty()) {
SKGPU_LOG_W("Couldn't create GradientShader's color and offset bitmap");
@@ -1418,9 +1426,12 @@
}
GradientShaderBlocks::GradientData data(type,
- point0, point1,
- radius0, radius1,
- bias, scale,
+ point0,
+ point1,
+ radius0,
+ radius1,
+ bias,
+ scale,
fTileMode,
fColorCount,
colors,
@@ -1428,9 +1439,12 @@
std::move(proxy),
fInterpolation);
- make_interpolated_to_dst(keyContext, builder, gatherer,
- data, fInterpolation,
+ make_interpolated_to_dst(keyContext,
+ builder,
+ gatherer,
+ data,
+ fInterpolation,
xformedColors.fIntermediateColorSpace.get());
}
-#endif // defined(SK_GRAPHITE)
+#endif // defined(SK_GRAPHITE)
diff --git a/src/shaders/gradients/SkGradientShaderBase.h b/src/shaders/gradients/SkGradientBaseShader.h
similarity index 63%
rename from src/shaders/gradients/SkGradientShaderBase.h
rename to src/shaders/gradients/SkGradientBaseShader.h
index b103ea4..017a126 100644
--- a/src/shaders/gradients/SkGradientShaderBase.h
+++ b/src/shaders/gradients/SkGradientBaseShader.h
@@ -8,25 +8,34 @@
#ifndef SkGradientShaderPriv_DEFINED
#define SkGradientShaderPriv_DEFINED
-#include "include/effects/SkGradientShader.h"
-
+#include "include/core/SkColor.h"
+#include "include/core/SkColorSpace.h"
#include "include/core/SkMatrix.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkScalar.h"
+#include "include/effects/SkGradientShader.h"
+#include "include/private/SkColorData.h"
+#include "include/private/base/SkAssert.h"
#include "include/private/base/SkTArray.h"
#include "include/private/base/SkTemplates.h"
-#include "src/core/SkVM.h"
#include "src/shaders/SkShaderBase.h"
#if defined(SK_GRAPHITE)
#include "src/gpu/graphite/KeyHelpers.h"
#endif
+#include <cstddef>
+#include <cstdint>
+
class SkArenaAlloc;
-class SkColorSpace;
class SkRasterPipeline;
class SkReadBuffer;
+class SkShader;
class SkWriteBuffer;
+enum class SkTileMode;
+struct SkStageRec;
-class SkGradientShaderBase : public SkShaderBase {
+class SkGradientBaseShader : public SkShaderBase {
public:
using Interpolation = SkGradientShader::Interpolation;
@@ -41,12 +50,12 @@
SkTileMode mode,
const Interpolation& interpolation);
- const SkColor4f* fColors;
+ const SkColor4f* fColors;
sk_sp<SkColorSpace> fColorSpace;
- const SkScalar* fPositions;
- int fColorCount; // length of fColors (and fPositions, if not nullptr)
- SkTileMode fTileMode;
- Interpolation fInterpolation;
+ const SkScalar* fPositions;
+ int fColorCount; // length of fColors (and fPositions, if not nullptr)
+ SkTileMode fTileMode;
+ Interpolation fInterpolation;
};
class DescriptorScope : public Descriptor {
@@ -57,11 +66,13 @@
private:
skia_private::STArray<16, SkColor4f, true> fColorStorage;
- skia_private::STArray<16, SkScalar , true> fPositionStorage;
+ skia_private::STArray<16, SkScalar, true> fPositionStorage;
};
- SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit);
- ~SkGradientShaderBase() override;
+ SkGradientBaseShader(const Descriptor& desc, const SkMatrix& ptsToUnit);
+ ~SkGradientBaseShader() override;
+
+ ShaderType type() const final { return ShaderType::kGradientBase; }
bool isOpaque() const override;
@@ -71,20 +82,26 @@
const SkMatrix& getGradientMatrix() const { return fPtsToUnit; }
- static bool ValidGradient(const SkColor4f colors[], int count, SkTileMode tileMode,
+ static bool ValidGradient(const SkColor4f colors[],
+ int count,
+ SkTileMode tileMode,
const Interpolation& interpolation);
- static sk_sp<SkShader> MakeDegenerateGradient(const SkColor4f colors[], const SkScalar pos[],
- int colorCount, sk_sp<SkColorSpace> colorSpace,
+ static sk_sp<SkShader> MakeDegenerateGradient(const SkColor4f colors[],
+ const SkScalar pos[],
+ int colorCount,
+ sk_sp<SkColorSpace> colorSpace,
SkTileMode mode);
struct ColorStopOptimizer {
- ColorStopOptimizer(const SkColor4f* colors, const SkScalar* pos, int count,
+ ColorStopOptimizer(const SkColor4f* colors,
+ const SkScalar* pos,
+ int count,
SkTileMode mode);
const SkColor4f* fColors;
- const SkScalar* fPos;
- int fCount;
+ const SkScalar* fPos;
+ int fCount;
};
// The default SkScalarNearlyZero threshold of .0024 is too big and causes regressions for svg
@@ -98,29 +115,32 @@
bool onAsLuminanceColor(SkColor*) const override;
- bool appendStages(const SkStageRec&, const MatrixRec&) const override;
+ bool appendStages(const SkStageRec&, const SkShaders::MatrixRec&) const override;
#if defined(SK_ENABLE_SKVM)
skvm::Color program(skvm::Builder*,
skvm::Coord device,
skvm::Coord local,
skvm::Color paint,
- const MatrixRec&,
+ const SkShaders::MatrixRec&,
const SkColorInfo& dstCS,
skvm::Uniforms* uniforms,
SkArenaAlloc* alloc) const override;
#endif
- virtual void appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* tPipeline,
+ virtual void appendGradientStages(SkArenaAlloc* alloc,
+ SkRasterPipeline* tPipeline,
SkRasterPipeline* postPipeline) const = 0;
#if defined(SK_ENABLE_SKVM)
// Produce t from (x,y), modifying mask if it should be anything other than ~0.
- virtual skvm::F32 transformT(skvm::Builder*, skvm::Uniforms*,
- skvm::Coord coord, skvm::I32* mask) const = 0;
+ virtual skvm::F32 transformT(skvm::Builder*,
+ skvm::Uniforms*,
+ skvm::Coord coord,
+ skvm::I32* mask) const = 0;
#endif
const SkMatrix fPtsToUnit;
- SkTileMode fTileMode;
+ SkTileMode fTileMode;
#if defined(SK_GRAPHITE)
// When the number of stops exceeds Graphite's uniform-based limit the colors and offsets
@@ -132,9 +152,12 @@
skgpu::graphite::PaintParamsKeyBuilder*,
skgpu::graphite::PipelineDataGatherer*,
GradientType,
- SkPoint point0, SkPoint point1,
- float radius0, float radius1,
- float bias, float scale) const;
+ SkPoint point0,
+ SkPoint point1,
+ float radius0,
+ float radius1,
+ float bias,
+ float scale) const;
#endif
public:
@@ -154,13 +177,13 @@
return fColors[i].toSkColor();
}
- SkColor4f* fColors; // points into fStorage
- SkScalar* fPositions; // points into fStorage, or nullptr
- int fColorCount; // length of fColors (and fPositions, if not nullptr)
- sk_sp<SkColorSpace> fColorSpace; // color space of gradient stops
- Interpolation fInterpolation;
- bool fFirstStopIsImplicit;
- bool fLastStopIsImplicit;
+ SkColor4f* fColors; // points into fStorage
+ SkScalar* fPositions; // points into fStorage, or nullptr
+ int fColorCount; // length of fColors (and fPositions, if not nullptr)
+ sk_sp<SkColorSpace> fColorSpace; // color space of gradient stops
+ Interpolation fInterpolation;
+ bool fFirstStopIsImplicit;
+ bool fLastStopIsImplicit;
bool colorsAreOpaque() const { return fColorsAreOpaque; }
@@ -168,20 +191,18 @@
private:
// Reserve inline space for up to 4 stops.
- inline static constexpr size_t kInlineStopCount = 4;
- inline static constexpr size_t kInlineStorageSize = (sizeof(SkColor4f) + sizeof(SkScalar))
- * kInlineStopCount;
+ inline static constexpr size_t kInlineStopCount = 4;
+ inline static constexpr size_t kInlineStorageSize =
+ (sizeof(SkColor4f) + sizeof(SkScalar)) * kInlineStopCount;
skia_private::AutoSTMalloc<kInlineStorageSize, uint8_t> fStorage;
- bool fColorsAreOpaque;
-
- using INHERITED = SkShaderBase;
+ bool fColorsAreOpaque;
};
///////////////////////////////////////////////////////////////////////////////
struct SkColor4fXformer {
- SkColor4fXformer(const SkGradientShaderBase* shader, SkColorSpace* dst);
+ SkColor4fXformer(const SkGradientBaseShader* shader, SkColorSpace* dst);
skia_private::STArray<4, SkPMColor4f, true> fColors;
sk_sp<SkColorSpace> fIntermediateColorSpace;
@@ -193,9 +214,9 @@
skia_private::STArray<2, SkColor4f, true> fColors4f;
};
+void SkRegisterConicalGradientShaderFlattenable();
void SkRegisterLinearGradientShaderFlattenable();
void SkRegisterRadialGradientShaderFlattenable();
void SkRegisterSweepGradientShaderFlattenable();
-void SkRegisterTwoPointConicalGradientShaderFlattenable();
#endif
diff --git a/src/shaders/gradients/SkGradientShader.cpp b/src/shaders/gradients/SkGradientShader.cpp
deleted file mode 100644
index 7040d5d..0000000
--- a/src/shaders/gradients/SkGradientShader.cpp
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
diff --git a/src/shaders/gradients/SkLinearGradient.cpp b/src/shaders/gradients/SkLinearGradient.cpp
index 3839f54..079e597 100644
--- a/src/shaders/gradients/SkLinearGradient.cpp
+++ b/src/shaders/gradients/SkLinearGradient.cpp
@@ -7,9 +7,18 @@
#include "src/shaders/gradients/SkLinearGradient.h"
+#include "include/core/SkColor.h"
+#include "include/core/SkColorSpace.h"
+#include "include/core/SkMatrix.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkShader.h"
+#include "include/effects/SkGradientShader.h"
+#include "include/private/base/SkTArray.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkWriteBuffer.h"
#include "src/shaders/SkLocalMatrixShader.h"
+#include "src/shaders/SkShaderBase.h"
#if defined(SK_GRAPHITE)
#include "src/gpu/graphite/KeyContext.h"
@@ -17,6 +26,13 @@
#include "src/gpu/graphite/PaintParamsKey.h"
#endif
+#include <cstdint>
+#include <utility>
+
+class SkArenaAlloc;
+class SkRasterPipeline;
+enum class SkTileMode;
+
static SkMatrix pts_to_unit_matrix(const SkPoint pts[2]) {
SkVector vec = pts[1] - pts[0];
SkScalar mag = vec.length();
@@ -33,10 +49,7 @@
///////////////////////////////////////////////////////////////////////////////
SkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc)
- : SkGradientShaderBase(desc, pts_to_unit_matrix(pts))
- , fStart(pts[0])
- , fEnd(pts[1]) {
-}
+ : SkGradientBaseShader(desc, pts_to_unit_matrix(pts)), fStart(pts[0]), fEnd(pts[1]) {}
sk_sp<SkFlattenable> SkLinearGradient::CreateProc(SkReadBuffer& buffer) {
DescriptorScope desc;
@@ -89,19 +102,6 @@
return GradientType::kLinear;
}
-/////////////////////////////////////////////////////////////////////
-
-#if defined(SK_GANESH)
-
-#include "src/gpu/ganesh/gradients/GrGradientShader.h"
-
-std::unique_ptr<GrFragmentProcessor> SkLinearGradient::asFragmentProcessor(
- const GrFPArgs& args, const MatrixRec& mRec) const {
- return GrGradientShader::MakeLinear(*this, args, mRec);
-}
-
-#endif
-
#if defined(SK_GRAPHITE)
void SkLinearGradient::addToKey(const skgpu::graphite::KeyContext& keyContext,
skgpu::graphite::PaintParamsKeyBuilder* builder,
@@ -125,7 +125,7 @@
if (!pts || !SkScalarIsFinite((pts[1] - pts[0]).length())) {
return nullptr;
}
- if (!SkGradientShaderBase::ValidGradient(colors, colorCount, mode, interpolation)) {
+ if (!SkGradientBaseShader::ValidGradient(colors, colorCount, mode, interpolation)) {
return nullptr;
}
if (1 == colorCount) {
@@ -136,19 +136,19 @@
}
if (SkScalarNearlyZero((pts[1] - pts[0]).length(),
- SkGradientShaderBase::kDegenerateThreshold)) {
+ SkGradientBaseShader::kDegenerateThreshold)) {
// Degenerate gradient, the only tricky complication is when in clamp mode, the limit of
// the gradient approaches two half planes of solid color (first and last). However, they
// are divided by the line perpendicular to the start and end point, which becomes undefined
// once start and end are exactly the same, so just use the end color for a stable solution.
- return SkGradientShaderBase::MakeDegenerateGradient(colors, pos, colorCount,
- std::move(colorSpace), mode);
+ return SkGradientBaseShader::MakeDegenerateGradient(
+ colors, pos, colorCount, std::move(colorSpace), mode);
}
- SkGradientShaderBase::ColorStopOptimizer opt(colors, pos, colorCount, mode);
+ SkGradientBaseShader::ColorStopOptimizer opt(colors, pos, colorCount, mode);
- SkGradientShaderBase::Descriptor desc(opt.fColors, std::move(colorSpace), opt.fPos,
- opt.fCount, mode, interpolation);
+ SkGradientBaseShader::Descriptor desc(
+ opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, interpolation);
return SkLocalMatrixShader::MakeWrapped<SkLinearGradient>(localMatrix, pts, desc);
}
diff --git a/src/shaders/gradients/SkLinearGradient.h b/src/shaders/gradients/SkLinearGradient.h
index e6efec5..572ebf1 100644
--- a/src/shaders/gradients/SkLinearGradient.h
+++ b/src/shaders/gradients/SkLinearGradient.h
@@ -8,17 +8,21 @@
#ifndef SkLinearGradient_DEFINED
#define SkLinearGradient_DEFINED
-#include "src/shaders/gradients/SkGradientShaderBase.h"
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkPoint.h"
+#include "src/shaders/gradients/SkGradientBaseShader.h"
-class SkLinearGradient final : public SkGradientShaderBase {
+class SkArenaAlloc;
+class SkMatrix;
+class SkRasterPipeline;
+class SkReadBuffer;
+class SkWriteBuffer;
+
+class SkLinearGradient final : public SkGradientBaseShader {
public:
SkLinearGradient(const SkPoint pts[2], const Descriptor&);
GradientType asGradient(GradientInfo* info, SkMatrix* localMatrix) const override;
-#if defined(SK_GANESH)
- std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&,
- const MatrixRec&) const override;
-#endif
#if defined(SK_GRAPHITE)
void addToKey(const skgpu::graphite::KeyContext&,
skgpu::graphite::PaintParamsKeyBuilder*,
@@ -43,7 +47,7 @@
class LinearGradient4fContext;
friend class SkGradientShader;
- using INHERITED = SkGradientShaderBase;
+ using INHERITED = SkGradientBaseShader;
const SkPoint fStart;
const SkPoint fEnd;
};
diff --git a/src/shaders/gradients/SkRadialGradient.cpp b/src/shaders/gradients/SkRadialGradient.cpp
index 77adb28..c72f367 100644
--- a/src/shaders/gradients/SkRadialGradient.cpp
+++ b/src/shaders/gradients/SkRadialGradient.cpp
@@ -4,11 +4,22 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
+#include "src/shaders/gradients/SkRadialGradient.h"
+#include "include/core/SkColor.h"
+#include "include/core/SkColorSpace.h"
+#include "include/core/SkMatrix.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkShader.h"
+#include "include/effects/SkGradientShader.h"
+#include "include/private/base/SkTArray.h"
#include "src/core/SkRasterPipeline.h"
+#include "src/core/SkRasterPipelineOpList.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkWriteBuffer.h"
#include "src/shaders/SkLocalMatrixShader.h"
+#include "src/shaders/SkShaderBase.h"
+#include "src/shaders/gradients/SkGradientBaseShader.h"
#if defined(SK_GRAPHITE)
#include "src/gpu/graphite/KeyContext.h"
@@ -16,12 +27,14 @@
#include "src/gpu/graphite/PaintParamsKey.h"
#endif
-#include "src/shaders/gradients/SkGradientShaderBase.h"
+#include <cstdint>
+#include <utility>
-namespace {
+class SkArenaAlloc;
+enum class SkTileMode;
-SkMatrix rad_to_unit_matrix(const SkPoint& center, SkScalar radius) {
- SkScalar inv = SkScalarInvert(radius);
+static SkMatrix rad_to_unit_matrix(const SkPoint& center, SkScalar radius) {
+ SkScalar inv = SkScalarInvert(radius);
SkMatrix matrix;
matrix.setTranslate(-center.fX, -center.fY);
@@ -29,47 +42,10 @@
return matrix;
}
-} // namespace
-
-/////////////////////////////////////////////////////////////////////
-class SkRadialGradient final : public SkGradientShaderBase {
-public:
- SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor&);
-
- GradientType asGradient(GradientInfo* info, SkMatrix* matrix) const override;
-#if defined(SK_GANESH)
- std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&,
- const MatrixRec&) const override;
-#endif
-#if defined(SK_GRAPHITE)
- void addToKey(const skgpu::graphite::KeyContext&,
- skgpu::graphite::PaintParamsKeyBuilder*,
- skgpu::graphite::PipelineDataGatherer*) const override;
-#endif
-protected:
- SkRadialGradient(SkReadBuffer& buffer);
- void flatten(SkWriteBuffer& buffer) const override;
-
- void appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* tPipeline,
- SkRasterPipeline* postPipeline) const override;
-#if defined(SK_ENABLE_SKVM)
- skvm::F32 transformT(skvm::Builder*, skvm::Uniforms*,
- skvm::Coord coord, skvm::I32* mask) const final;
-#endif
-
-private:
- friend void ::SkRegisterRadialGradientShaderFlattenable();
- SK_FLATTENABLE_HOOKS(SkRadialGradient)
-
- const SkPoint fCenter;
- const SkScalar fRadius;
-};
-
SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor& desc)
- : SkGradientShaderBase(desc, rad_to_unit_matrix(center, radius))
- , fCenter(center)
- , fRadius(radius) {
-}
+ : SkGradientBaseShader(desc, rad_to_unit_matrix(center, radius))
+ , fCenter(center)
+ , fRadius(radius) {}
SkShaderBase::GradientType SkRadialGradient::asGradient(GradientInfo* info,
SkMatrix* localMatrix) const {
@@ -104,7 +80,7 @@
}
void SkRadialGradient::flatten(SkWriteBuffer& buffer) const {
- this->SkGradientShaderBase::flatten(buffer);
+ this->SkGradientBaseShader::flatten(buffer);
buffer.writePoint(fCenter);
buffer.writeScalar(fRadius);
}
@@ -121,29 +97,6 @@
}
#endif
-/////////////////////////////////////////////////////////////////////
-
-#if defined(SK_GANESH)
-
-#include "src/core/SkRuntimeEffectPriv.h"
-#include "src/gpu/ganesh/effects/GrSkSLFP.h"
-#include "src/gpu/ganesh/gradients/GrGradientShader.h"
-
-std::unique_ptr<GrFragmentProcessor>
-SkRadialGradient::asFragmentProcessor(const GrFPArgs& args, const MatrixRec& mRec) const {
- static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
- "half4 main(float2 coord) {"
- "return half4(half(length(coord)), 1, 0, 0);" // y = 1 for always valid
- "}"
- );
- // The radial gradient never rejects a pixel so it doesn't change opacity
- auto fp = GrSkSLFP::Make(effect, "RadialLayout", /*inputFP=*/nullptr,
- GrSkSLFP::OptFlags::kPreservesOpaqueInput);
- return GrGradientShader::MakeGradientFP(*this, args, mRec, std::move(fp));
-}
-
-#endif
-
#if defined(SK_GRAPHITE)
void SkRadialGradient::addToKey(const skgpu::graphite::KeyContext& keyContext,
skgpu::graphite::PaintParamsKeyBuilder* builder,
@@ -167,7 +120,7 @@
if (radius < 0) {
return nullptr;
}
- if (!SkGradientShaderBase::ValidGradient(colors, colorCount, mode, interpolation)) {
+ if (!SkGradientBaseShader::ValidGradient(colors, colorCount, mode, interpolation)) {
return nullptr;
}
if (1 == colorCount) {
@@ -177,16 +130,16 @@
return nullptr;
}
- if (SkScalarNearlyZero(radius, SkGradientShaderBase::kDegenerateThreshold)) {
+ if (SkScalarNearlyZero(radius, SkGradientBaseShader::kDegenerateThreshold)) {
// Degenerate gradient optimization, and no special logic needed for clamped radial gradient
- return SkGradientShaderBase::MakeDegenerateGradient(colors, pos, colorCount,
- std::move(colorSpace), mode);
+ return SkGradientBaseShader::MakeDegenerateGradient(
+ colors, pos, colorCount, std::move(colorSpace), mode);
}
- SkGradientShaderBase::ColorStopOptimizer opt(colors, pos, colorCount, mode);
+ SkGradientBaseShader::ColorStopOptimizer opt(colors, pos, colorCount, mode);
- SkGradientShaderBase::Descriptor desc(opt.fColors, std::move(colorSpace), opt.fPos,
- opt.fCount, mode, interpolation);
+ SkGradientBaseShader::Descriptor desc(
+ opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, interpolation);
return SkLocalMatrixShader::MakeWrapped<SkRadialGradient>(localMatrix, center, radius, desc);
}
diff --git a/src/shaders/gradients/SkRadialGradient.h b/src/shaders/gradients/SkRadialGradient.h
new file mode 100644
index 0000000..0f882d1
--- /dev/null
+++ b/src/shaders/gradients/SkRadialGradient.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkRadialGradient_DEFINED
+#define SkRadialGradient_DEFINED
+
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkScalar.h"
+#include "src/shaders/gradients/SkGradientBaseShader.h"
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyContext.h"
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif
+
+class SkArenaAlloc;
+class SkMatrix;
+class SkRasterPipeline;
+class SkReadBuffer;
+class SkWriteBuffer;
+
+class SkRadialGradient final : public SkGradientBaseShader {
+public:
+ SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor&);
+
+ GradientType asGradient(GradientInfo* info, SkMatrix* matrix) const override;
+#if defined(SK_GRAPHITE)
+ void addToKey(const skgpu::graphite::KeyContext&,
+ skgpu::graphite::PaintParamsKeyBuilder*,
+ skgpu::graphite::PipelineDataGatherer*) const override;
+#endif
+protected:
+ SkRadialGradient(SkReadBuffer& buffer);
+ void flatten(SkWriteBuffer& buffer) const override;
+
+ void appendGradientStages(SkArenaAlloc* alloc,
+ SkRasterPipeline* tPipeline,
+ SkRasterPipeline* postPipeline) const override;
+#if defined(SK_ENABLE_SKVM)
+ skvm::F32 transformT(skvm::Builder*,
+ skvm::Uniforms*,
+ skvm::Coord coord,
+ skvm::I32* mask) const final;
+#endif
+
+private:
+ friend void ::SkRegisterRadialGradientShaderFlattenable();
+ SK_FLATTENABLE_HOOKS(SkRadialGradient)
+
+ const SkPoint fCenter;
+ const SkScalar fRadius;
+};
+
+#endif
diff --git a/src/shaders/gradients/SkSweepGradient.cpp b/src/shaders/gradients/SkSweepGradient.cpp
index dbcfa94..ce5af4a 100644
--- a/src/shaders/gradients/SkSweepGradient.cpp
+++ b/src/shaders/gradients/SkSweepGradient.cpp
@@ -5,11 +5,25 @@
* found in the LICENSE file.
*/
+#include "src/shaders/gradients/SkSweepGradient.h"
+
+#include "include/core/SkColor.h"
+#include "include/core/SkColorSpace.h"
+#include "include/core/SkMatrix.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkShader.h"
+#include "include/core/SkTileMode.h"
+#include "include/effects/SkGradientShader.h"
+#include "include/private/base/SkAssert.h"
#include "include/private/base/SkFloatingPoint.h"
+#include "include/private/base/SkTArray.h"
#include "src/core/SkRasterPipeline.h"
+#include "src/core/SkRasterPipelineOpList.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkWriteBuffer.h"
#include "src/shaders/SkLocalMatrixShader.h"
+#include "src/shaders/SkShaderBase.h"
+#include "src/shaders/gradients/SkGradientBaseShader.h"
#if defined(SK_GRAPHITE)
#include "src/gpu/graphite/KeyContext.h"
@@ -17,50 +31,20 @@
#include "src/gpu/graphite/PaintParamsKey.h"
#endif
-#include "src/shaders/gradients/SkGradientShaderBase.h"
+#include <cstdint>
+#include <tuple>
+#include <utility>
-class SkSweepGradient final : public SkGradientShaderBase {
-public:
- SkSweepGradient(const SkPoint& center, SkScalar t0, SkScalar t1, const Descriptor&);
+class SkArenaAlloc;
- GradientType asGradient(GradientInfo* info, SkMatrix* localMatrix) const override;
-
-#if defined(SK_GANESH)
- std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&,
- const MatrixRec&) const override;
-#endif
-#if defined(SK_GRAPHITE)
- void addToKey(const skgpu::graphite::KeyContext&,
- skgpu::graphite::PaintParamsKeyBuilder*,
- skgpu::graphite::PipelineDataGatherer*) const override;
-#endif
-
-protected:
- void flatten(SkWriteBuffer& buffer) const override;
-
- void appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* tPipeline,
- SkRasterPipeline* postPipeline) const override;
-#if defined(SK_ENABLE_SKVM)
- skvm::F32 transformT(skvm::Builder*, skvm::Uniforms*,
- skvm::Coord coord, skvm::I32* mask) const final;
-#endif
-
-private:
- friend void ::SkRegisterSweepGradientShaderFlattenable();
- SK_FLATTENABLE_HOOKS(SkSweepGradient)
-
- const SkPoint fCenter;
- const SkScalar fTBias;
- const SkScalar fTScale;
-};
-
-SkSweepGradient::SkSweepGradient(const SkPoint& center, SkScalar t0, SkScalar t1,
+SkSweepGradient::SkSweepGradient(const SkPoint& center,
+ SkScalar t0,
+ SkScalar t1,
const Descriptor& desc)
- : SkGradientShaderBase(desc, SkMatrix::Translate(-center.x(), -center.y()))
- , fCenter(center)
- , fTBias(-t0)
- , fTScale(1 / (t1 - t0))
-{
+ : SkGradientBaseShader(desc, SkMatrix::Translate(-center.x(), -center.y()))
+ , fCenter(center)
+ , fTBias(-t0)
+ , fTScale(1 / (t1 - t0)) {
SkASSERT(t0 < t1);
}
@@ -105,7 +89,7 @@
}
void SkSweepGradient::flatten(SkWriteBuffer& buffer) const {
- this->SkGradientShaderBase::flatten(buffer);
+ this->SkGradientBaseShader::flatten(buffer);
buffer.writePoint(fCenter);
buffer.writeScalar(fTBias);
buffer.writeScalar(fTScale);
@@ -148,52 +132,6 @@
}
#endif
-/////////////////////////////////////////////////////////////////////
-
-#if defined(SK_GANESH)
-
-#include "src/core/SkRuntimeEffectPriv.h"
-#include "src/gpu/ganesh/GrCaps.h"
-#include "src/gpu/ganesh/GrRecordingContextPriv.h"
-#include "src/gpu/ganesh/effects/GrSkSLFP.h"
-#include "src/gpu/ganesh/gradients/GrGradientShader.h"
-
-std::unique_ptr<GrFragmentProcessor> SkSweepGradient::asFragmentProcessor(
- const GrFPArgs& args, const MatrixRec& mRec) const {
- // On some devices they incorrectly implement atan2(y,x) as atan(y/x). In actuality it is
- // atan2(y,x) = 2 * atan(y / (sqrt(x^2 + y^2) + x)). So to work around this we pass in (sqrt(x^2
- // + y^2) + x) as the second parameter to atan2 in these cases. We let the device handle the
- // undefined behavior of the second paramenter being 0 instead of doing the divide ourselves and
- // using atan instead.
- int useAtanWorkaround =
- args.fContext->priv().caps()->shaderCaps()->fAtan2ImplementedAsAtanYOverX;
- static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
- "uniform half bias;"
- "uniform half scale;"
- "uniform int useAtanWorkaround;" // specialized
-
- "half4 main(float2 coord) {"
- "half angle = bool(useAtanWorkaround)"
- "? half(2 * atan(-coord.y, length(coord) - coord.x))"
- ": half(atan(-coord.y, -coord.x));"
-
- // 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi]
- "half t = (angle * 0.1591549430918 + 0.5 + bias) * scale;"
- "return half4(t, 1, 0, 0);" // y = 1 for always valid
- "}"
- );
-
- // The sweep gradient never rejects a pixel so it doesn't change opacity
- auto fp = GrSkSLFP::Make(effect, "SweepLayout", /*inputFP=*/nullptr,
- GrSkSLFP::OptFlags::kPreservesOpaqueInput,
- "bias", fTBias,
- "scale", fTScale,
- "useAtanWorkaround", GrSkSLFP::Specialize(useAtanWorkaround));
- return GrGradientShader::MakeGradientFP(*this, args, mRec, std::move(fp));
-}
-
-#endif
-
#if defined(SK_GRAPHITE)
void SkSweepGradient::addToKey(const skgpu::graphite::KeyContext& keyContext,
skgpu::graphite::PaintParamsKeyBuilder* builder,
@@ -216,7 +154,7 @@
SkScalar endAngle,
const Interpolation& interpolation,
const SkMatrix* localMatrix) {
- if (!SkGradientShaderBase::ValidGradient(colors, colorCount, mode, interpolation)) {
+ if (!SkGradientBaseShader::ValidGradient(colors, colorCount, mode, interpolation)) {
return nullptr;
}
if (1 == colorCount) {
@@ -229,10 +167,10 @@
return nullptr;
}
- if (SkScalarNearlyEqual(startAngle, endAngle, SkGradientShaderBase::kDegenerateThreshold)) {
+ if (SkScalarNearlyEqual(startAngle, endAngle, SkGradientBaseShader::kDegenerateThreshold)) {
// Degenerate gradient, which should follow default degenerate behavior unless it is
// clamped and the angle is greater than 0.
- if (mode == SkTileMode::kClamp && endAngle > SkGradientShaderBase::kDegenerateThreshold) {
+ if (mode == SkTileMode::kClamp && endAngle > SkGradientBaseShader::kDegenerateThreshold) {
// In this case, the first color is repeated from 0 to the angle, then a hardstop
// switches to the last color (all other colors are compressed to the infinitely thin
// interpolation region).
@@ -241,8 +179,8 @@
return MakeSweep(cx, cy, reColors, std::move(colorSpace), clampPos, 3, mode, 0,
endAngle, interpolation, localMatrix);
} else {
- return SkGradientShaderBase::MakeDegenerateGradient(colors, pos, colorCount,
- std::move(colorSpace), mode);
+ return SkGradientBaseShader::MakeDegenerateGradient(
+ colors, pos, colorCount, std::move(colorSpace), mode);
}
}
@@ -251,10 +189,10 @@
mode = SkTileMode::kClamp;
}
- SkGradientShaderBase::ColorStopOptimizer opt(colors, pos, colorCount, mode);
+ SkGradientBaseShader::ColorStopOptimizer opt(colors, pos, colorCount, mode);
- SkGradientShaderBase::Descriptor desc(opt.fColors, std::move(colorSpace), opt.fPos,
- opt.fCount, mode, interpolation);
+ SkGradientBaseShader::Descriptor desc(
+ opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, interpolation);
const SkScalar t0 = startAngle / 360,
t1 = endAngle / 360;
diff --git a/src/shaders/gradients/SkSweepGradient.h b/src/shaders/gradients/SkSweepGradient.h
new file mode 100644
index 0000000..997435c
--- /dev/null
+++ b/src/shaders/gradients/SkSweepGradient.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkSweepGradientShader_DEFINED
+#define SkSweepGradientShader_DEFINED
+
+#include "include/core/SkFlattenable.h"
+#include "include/core/SkPoint.h"
+#include "include/core/SkScalar.h"
+#include "src/shaders/gradients/SkGradientBaseShader.h"
+
+#if defined(SK_GRAPHITE)
+#include "src/gpu/graphite/KeyContext.h"
+#include "src/gpu/graphite/KeyHelpers.h"
+#include "src/gpu/graphite/PaintParamsKey.h"
+#endif
+
+class SkArenaAlloc;
+class SkMatrix;
+class SkRasterPipeline;
+class SkReadBuffer;
+class SkWriteBuffer;
+
+class SkSweepGradient final : public SkGradientBaseShader {
+public:
+ SkSweepGradient(const SkPoint& center, SkScalar t0, SkScalar t1, const Descriptor&);
+
+ GradientType asGradient(GradientInfo* info, SkMatrix* localMatrix) const override;
+
+#if defined(SK_GRAPHITE)
+ void addToKey(const skgpu::graphite::KeyContext&,
+ skgpu::graphite::PaintParamsKeyBuilder*,
+ skgpu::graphite::PipelineDataGatherer*) const override;
+#endif
+
+ SkScalar tBias() const { return fTBias; }
+ SkScalar tScale() const { return fTScale; }
+
+protected:
+ void flatten(SkWriteBuffer& buffer) const override;
+
+ void appendGradientStages(SkArenaAlloc* alloc,
+ SkRasterPipeline* tPipeline,
+ SkRasterPipeline* postPipeline) const override;
+#if defined(SK_ENABLE_SKVM)
+ skvm::F32 transformT(skvm::Builder*,
+ skvm::Uniforms*,
+ skvm::Coord coord,
+ skvm::I32* mask) const final;
+#endif
+
+private:
+ friend void ::SkRegisterSweepGradientShaderFlattenable();
+ SK_FLATTENABLE_HOOKS(SkSweepGradient)
+
+ const SkPoint fCenter;
+ const SkScalar fTBias;
+ const SkScalar fTScale;
+};
+
+#endif
diff --git a/src/shaders/gradients/SkTwoPointConicalGradient.cpp b/src/shaders/gradients/SkTwoPointConicalGradient.cpp
deleted file mode 100644
index 8639452..0000000
--- a/src/shaders/gradients/SkTwoPointConicalGradient.cpp
+++ /dev/null
@@ -1,647 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "include/private/base/SkFloatingPoint.h"
-#include "src/core/SkRasterPipeline.h"
-#include "src/core/SkReadBuffer.h"
-#include "src/core/SkWriteBuffer.h"
-#include "src/shaders/SkLocalMatrixShader.h"
-#include "src/shaders/gradients/SkGradientShaderBase.h"
-
-#include <utility>
-
-#if defined(SK_GRAPHITE)
-#include "src/gpu/graphite/KeyContext.h"
-#include "src/gpu/graphite/KeyHelpers.h"
-#include "src/gpu/graphite/PaintParamsKey.h"
-#endif
-
-// Please see https://skia.org/dev/design/conical for how our shader works.
-
-class SkTwoPointConicalGradient final : public SkGradientShaderBase {
-public:
- // See https://skia.org/dev/design/conical for what focal data means and how our shader works.
- // We make it public so the GPU shader can also use it.
- struct FocalData {
- SkScalar fR1; // r1 after mapping focal point to (0, 0)
- SkScalar fFocalX; // f
- bool fIsSwapped; // whether we swapped r0, r1
-
- // The input r0, r1 are the radii when we map centers to {(0, 0), (1, 0)}.
- // We'll post concat matrix with our transformation matrix that maps focal point to (0, 0).
- // Returns true if the set succeeded
- bool set(SkScalar r0, SkScalar r1, SkMatrix* matrix);
-
- // Whether the focal point (0, 0) is on the end circle with center (1, 0) and radius r1. If
- // this is true, it's as if an aircraft is flying at Mach 1 and all circles (soundwaves)
- // will go through the focal point (aircraft). In our previous implementations, this was
- // known as the edge case where the inside circle touches the outside circle (on the focal
- // point). If we were to solve for t bruteforcely using a quadratic equation, this case
- // implies that the quadratic equation degenerates to a linear equation.
- bool isFocalOnCircle() const { return SkScalarNearlyZero(1 - fR1); }
-
- bool isSwapped() const { return fIsSwapped; }
- bool isWellBehaved() const { return !this->isFocalOnCircle() && fR1 > 1; }
- bool isNativelyFocal() const { return SkScalarNearlyZero(fFocalX); }
- };
-
- enum class Type {
- kRadial,
- kStrip,
- kFocal
- };
-
- static sk_sp<SkShader> Create(const SkPoint& start, SkScalar startRadius,
- const SkPoint& end, SkScalar endRadius,
- const Descriptor&, const SkMatrix* localMatrix);
-
- GradientType asGradient(GradientInfo* info, SkMatrix* localMatrix) const override;
-#if defined(SK_GANESH)
- std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&,
- const MatrixRec&) const override;
-#endif
-#if defined(SK_GRAPHITE)
- void addToKey(const skgpu::graphite::KeyContext&,
- skgpu::graphite::PaintParamsKeyBuilder*,
- skgpu::graphite::PipelineDataGatherer*) const override;
-#endif
- bool isOpaque() const override;
-
- SkScalar getCenterX1() const { return SkPoint::Distance(fCenter1, fCenter2); }
- SkScalar getStartRadius() const { return fRadius1; }
- SkScalar getDiffRadius() const { return fRadius2 - fRadius1; }
- const SkPoint& getStartCenter() const { return fCenter1; }
- const SkPoint& getEndCenter() const { return fCenter2; }
- SkScalar getEndRadius() const { return fRadius2; }
-
- Type getType() const { return fType; }
- const FocalData& getFocalData() const { return fFocalData; }
-
- SkTwoPointConicalGradient(const SkPoint& c0, SkScalar r0,
- const SkPoint& c1, SkScalar r1,
- const Descriptor&, Type, const SkMatrix&, const FocalData&);
-
-protected:
- void flatten(SkWriteBuffer& buffer) const override;
-
- void appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* tPipeline,
- SkRasterPipeline* postPipeline) const override;
-#if defined(SK_ENABLE_SKVM)
- skvm::F32 transformT(skvm::Builder*, skvm::Uniforms*,
- skvm::Coord coord, skvm::I32* mask) const final;
-#endif
-
-private:
- friend void ::SkRegisterTwoPointConicalGradientShaderFlattenable();
- SK_FLATTENABLE_HOOKS(SkTwoPointConicalGradient)
-
- SkPoint fCenter1;
- SkPoint fCenter2;
- SkScalar fRadius1;
- SkScalar fRadius2;
- Type fType;
-
- FocalData fFocalData;
-};
-
-bool SkTwoPointConicalGradient::FocalData::set(SkScalar r0, SkScalar r1, SkMatrix* matrix) {
- fIsSwapped = false;
- fFocalX = sk_ieee_float_divide(r0, (r0 - r1));
- if (SkScalarNearlyZero(fFocalX - 1)) {
- // swap r0, r1
- matrix->postTranslate(-1, 0);
- matrix->postScale(-1, 1);
- std::swap(r0, r1);
- fFocalX = 0; // because r0 is now 0
- fIsSwapped = true;
- }
-
- // Map {focal point, (1, 0)} to {(0, 0), (1, 0)}
- const SkPoint from[2] = { {fFocalX, 0}, {1, 0} };
- const SkPoint to[2] = { {0, 0}, {1, 0} };
- SkMatrix focalMatrix;
- if (!focalMatrix.setPolyToPoly(from, to, 2)) {
- return false;
- }
- matrix->postConcat(focalMatrix);
- fR1 = r1 / SkScalarAbs(1 - fFocalX); // focalMatrix has a scale of 1/(1-f)
-
- // The following transformations are just to accelerate the shader computation by saving
- // some arithmatic operations.
- if (this->isFocalOnCircle()) {
- matrix->postScale(0.5, 0.5);
- } else {
- matrix->postScale(fR1 / (fR1 * fR1 - 1), 1 / sqrt(SkScalarAbs(fR1 * fR1 - 1)));
- }
- matrix->postScale(SkScalarAbs(1 - fFocalX), SkScalarAbs(1 - fFocalX)); // scale |1 - f|
- return true;
-}
-
-sk_sp<SkShader> SkTwoPointConicalGradient::Create(const SkPoint& c0, SkScalar r0,
- const SkPoint& c1, SkScalar r1,
- const Descriptor& desc,
- const SkMatrix* localMatrix) {
- SkMatrix gradientMatrix;
- Type gradientType;
-
- if (SkScalarNearlyZero((c0 - c1).length())) {
- if (SkScalarNearlyZero(std::max(r0, r1)) || SkScalarNearlyEqual(r0, r1)) {
- // Degenerate case; avoid dividing by zero. Should have been caught by caller but
- // just in case, recheck here.
- return nullptr;
- }
- // Concentric case: we can pretend we're radial (with a tiny twist).
- const SkScalar scale = sk_ieee_float_divide(1, std::max(r0, r1));
- gradientMatrix = SkMatrix::Translate(-c1.x(), -c1.y());
- gradientMatrix.postScale(scale, scale);
-
- gradientType = Type::kRadial;
- } else {
- const SkPoint centers[2] = { c0 , c1 };
- const SkPoint unitvec[2] = { {0, 0}, {1, 0} };
-
- if (!gradientMatrix.setPolyToPoly(centers, unitvec, 2)) {
- // Degenerate case.
- return nullptr;
- }
-
- gradientType = SkScalarNearlyZero(r1 - r0) ? Type::kStrip : Type::kFocal;
- }
-
- FocalData focalData;
- if (gradientType == Type::kFocal) {
- const auto dCenter = (c0 - c1).length();
- if (!focalData.set(r0 / dCenter, r1 / dCenter, &gradientMatrix)) {
- return nullptr;
- }
- }
- return SkLocalMatrixShader::MakeWrapped<SkTwoPointConicalGradient>(localMatrix,
- c0, r0,
- c1, r1,
- desc,
- gradientType,
- gradientMatrix,
- focalData);
-}
-
-SkTwoPointConicalGradient::SkTwoPointConicalGradient(
- const SkPoint& start, SkScalar startRadius,
- const SkPoint& end, SkScalar endRadius,
- const Descriptor& desc, Type type, const SkMatrix& gradientMatrix, const FocalData& data)
- : SkGradientShaderBase(desc, gradientMatrix)
- , fCenter1(start)
- , fCenter2(end)
- , fRadius1(startRadius)
- , fRadius2(endRadius)
- , fType(type)
-{
- // this is degenerate, and should be caught by our caller
- SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
- if (type == Type::kFocal) {
- fFocalData = data;
- }
-}
-
-bool SkTwoPointConicalGradient::isOpaque() const {
- // Because areas outside the cone are left untouched, we cannot treat the
- // shader as opaque even if the gradient itself is opaque.
- // TODO(junov): Compute whether the cone fills the plane crbug.com/222380
- return false;
-}
-
-// Returns the original non-sorted version of the gradient
-SkShaderBase::GradientType SkTwoPointConicalGradient::asGradient(GradientInfo* info,
- SkMatrix* localMatrix) const {
- if (info) {
- commonAsAGradient(info);
- info->fPoint[0] = fCenter1;
- info->fPoint[1] = fCenter2;
- info->fRadius[0] = fRadius1;
- info->fRadius[1] = fRadius2;
- }
- if (localMatrix) {
- *localMatrix = SkMatrix::I();
- }
- return GradientType::kConical;
-}
-
-sk_sp<SkFlattenable> SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer) {
- DescriptorScope desc;
- SkMatrix legacyLocalMatrix;
- if (!desc.unflatten(buffer, &legacyLocalMatrix)) {
- return nullptr;
- }
- SkPoint c1 = buffer.readPoint();
- SkPoint c2 = buffer.readPoint();
- SkScalar r1 = buffer.readScalar();
- SkScalar r2 = buffer.readScalar();
-
- if (!buffer.isValid()) {
- return nullptr;
- }
- return SkGradientShader::MakeTwoPointConical(c1, r1,
- c2, r2,
- desc.fColors,
- std::move(desc.fColorSpace),
- desc.fPositions,
- desc.fColorCount,
- desc.fTileMode,
- desc.fInterpolation,
- &legacyLocalMatrix);
-}
-
-void SkTwoPointConicalGradient::flatten(SkWriteBuffer& buffer) const {
- this->SkGradientShaderBase::flatten(buffer);
- buffer.writePoint(fCenter1);
- buffer.writePoint(fCenter2);
- buffer.writeScalar(fRadius1);
- buffer.writeScalar(fRadius2);
-}
-
-void SkTwoPointConicalGradient::appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* p,
- SkRasterPipeline* postPipeline) const {
- const auto dRadius = fRadius2 - fRadius1;
-
- if (fType == Type::kRadial) {
- p->append(SkRasterPipelineOp::xy_to_radius);
-
- // Tiny twist: radial computes a t for [0, r2], but we want a t for [r1, r2].
- auto scale = std::max(fRadius1, fRadius2) / dRadius;
- auto bias = -fRadius1 / dRadius;
-
- p->append_matrix(alloc, SkMatrix::Translate(bias, 0) * SkMatrix::Scale(scale, 1));
- return;
- }
-
- if (fType == Type::kStrip) {
- auto* ctx = alloc->make<SkRasterPipeline_2PtConicalCtx>();
- SkScalar scaledR0 = fRadius1 / this->getCenterX1();
- ctx->fP0 = scaledR0 * scaledR0;
- p->append(SkRasterPipelineOp::xy_to_2pt_conical_strip, ctx);
- p->append(SkRasterPipelineOp::mask_2pt_conical_nan, ctx);
- postPipeline->append(SkRasterPipelineOp::apply_vector_mask, &ctx->fMask);
- return;
- }
-
- auto* ctx = alloc->make<SkRasterPipeline_2PtConicalCtx>();
- ctx->fP0 = 1/fFocalData.fR1;
- ctx->fP1 = fFocalData.fFocalX;
-
- if (fFocalData.isFocalOnCircle()) {
- p->append(SkRasterPipelineOp::xy_to_2pt_conical_focal_on_circle);
- } else if (fFocalData.isWellBehaved()) {
- p->append(SkRasterPipelineOp::xy_to_2pt_conical_well_behaved, ctx);
- } else if (fFocalData.isSwapped() || 1 - fFocalData.fFocalX < 0) {
- p->append(SkRasterPipelineOp::xy_to_2pt_conical_smaller, ctx);
- } else {
- p->append(SkRasterPipelineOp::xy_to_2pt_conical_greater, ctx);
- }
-
- if (!fFocalData.isWellBehaved()) {
- p->append(SkRasterPipelineOp::mask_2pt_conical_degenerates, ctx);
- }
- if (1 - fFocalData.fFocalX < 0) {
- p->append(SkRasterPipelineOp::negate_x);
- }
- if (!fFocalData.isNativelyFocal()) {
- p->append(SkRasterPipelineOp::alter_2pt_conical_compensate_focal, ctx);
- }
- if (fFocalData.isSwapped()) {
- p->append(SkRasterPipelineOp::alter_2pt_conical_unswap);
- }
- if (!fFocalData.isWellBehaved()) {
- postPipeline->append(SkRasterPipelineOp::apply_vector_mask, &ctx->fMask);
- }
-}
-
-#if defined(SK_ENABLE_SKVM)
-skvm::F32 SkTwoPointConicalGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms,
- skvm::Coord coord, skvm::I32* mask) const {
- auto mag = [](skvm::F32 x, skvm::F32 y) { return sqrt(x*x + y*y); };
-
- // See https://skia.org/dev/design/conical, and appendStages() above.
- // There's a lot going on here, and I'm not really sure what's independent
- // or disjoint, what can be reordered, simplified, etc. Tweak carefully.
-
- const skvm::F32 x = coord.x,
- y = coord.y;
- if (fType == Type::kRadial) {
- float denom = 1.0f / (fRadius2 - fRadius1),
- scale = std::max(fRadius1, fRadius2) * denom,
- bias = -fRadius1 * denom;
- return mag(x,y) * p->uniformF(uniforms->pushF(scale))
- + p->uniformF(uniforms->pushF(bias ));
- }
-
- if (fType == Type::kStrip) {
- float r = fRadius1 / this->getCenterX1();
- skvm::F32 t = x + sqrt(p->uniformF(uniforms->pushF(r*r)) - y*y);
-
- *mask = (t == t); // t != NaN
- return t;
- }
-
- const skvm::F32 invR1 = p->uniformF(uniforms->pushF(1 / fFocalData.fR1));
-
- skvm::F32 t;
- if (fFocalData.isFocalOnCircle()) {
- t = (y/x) * y + x; // (x^2 + y^2) / x ~~> x + y^2/x ~~> y/x * y + x
- } else if (fFocalData.isWellBehaved()) {
- t = mag(x,y) - x*invR1;
- } else {
- skvm::F32 k = sqrt(x*x - y*y);
- if (fFocalData.isSwapped() || 1 - fFocalData.fFocalX < 0) {
- k = -k;
- }
- t = k - x*invR1;
- }
-
- if (!fFocalData.isWellBehaved()) {
- // TODO: not sure why we consider t == 0 degenerate
- *mask = (t > 0.0f); // and implicitly, t != NaN
- }
-
- const skvm::F32 focalX = p->uniformF(uniforms->pushF(fFocalData.fFocalX));
- if (1 - fFocalData.fFocalX < 0) { t = -t; }
- if (!fFocalData.isNativelyFocal()) { t += focalX; }
- if ( fFocalData.isSwapped()) { t = 1.0f - t; }
- return t;
-}
-#endif
-
-/////////////////////////////////////////////////////////////////////
-
-#if defined(SK_GANESH)
-
-#include "src/core/SkRuntimeEffectPriv.h"
-#include "src/gpu/ganesh/effects/GrSkSLFP.h"
-#include "src/gpu/ganesh/gradients/GrGradientShader.h"
-
-std::unique_ptr<GrFragmentProcessor>
-SkTwoPointConicalGradient::asFragmentProcessor(const GrFPArgs& args, const MatrixRec& mRec) const {
- // The 2 point conical gradient can reject a pixel so it does change opacity even if the input
- // was opaque. Thus, all of these layout FPs disable that optimization.
- std::unique_ptr<GrFragmentProcessor> fp;
- SkTLazy<SkMatrix> matrix;
- switch (this->getType()) {
- case SkTwoPointConicalGradient::Type::kStrip: {
- static const SkRuntimeEffect* kEffect =
- SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
- "uniform half r0_2;"
- "half4 main(float2 p) {"
- "half v = 1;" // validation flag,set to negative to discard fragment later
- "float t = r0_2 - p.y * p.y;"
- "if (t >= 0) {"
- "t = p.x + sqrt(t);"
- "} else {"
- "v = -1;"
- "}"
- "return half4(half(t), v, 0, 0);"
- "}"
- );
- float r0 = this->getStartRadius() / this->getCenterX1();
- fp = GrSkSLFP::Make(kEffect, "TwoPointConicalStripLayout", /*inputFP=*/nullptr,
- GrSkSLFP::OptFlags::kNone,
- "r0_2", r0 * r0);
- } break;
-
- case SkTwoPointConicalGradient::Type::kRadial: {
- static const SkRuntimeEffect* kEffect =
- SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
- "uniform half r0;"
- "uniform half lengthScale;"
- "half4 main(float2 p) {"
- "half v = 1;" // validation flag,set to negative to discard fragment later
- "float t = length(p) * lengthScale - r0;"
- "return half4(half(t), v, 0, 0);"
- "}"
- );
- float dr = this->getDiffRadius();
- float r0 = this->getStartRadius() / dr;
- bool isRadiusIncreasing = dr >= 0;
- fp = GrSkSLFP::Make(kEffect, "TwoPointConicalRadialLayout", /*inputFP=*/nullptr,
- GrSkSLFP::OptFlags::kNone,
- "r0", r0,
- "lengthScale", isRadiusIncreasing ? 1.0f : -1.0f);
-
- // GPU radial matrix is different from the original matrix, since we map the diff radius
- // to have |dr| = 1, so manually compute the final gradient matrix here.
-
- // Map center to (0, 0)
- matrix.set(SkMatrix::Translate(-this->getStartCenter().fX,
- -this->getStartCenter().fY));
- // scale |diffRadius| to 1
- matrix->postScale(1 / dr, 1 / dr);
- } break;
-
- case SkTwoPointConicalGradient::Type::kFocal: {
- static const SkRuntimeEffect* kEffect =
- SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
- // Optimization flags, all specialized:
- "uniform int isRadiusIncreasing;"
- "uniform int isFocalOnCircle;"
- "uniform int isWellBehaved;"
- "uniform int isSwapped;"
- "uniform int isNativelyFocal;"
-
- "uniform half invR1;" // 1/r1
- "uniform half fx;" // focalX = r0/(r0-r1)
-
- "half4 main(float2 p) {"
- "float t = -1;"
- "half v = 1;" // validation flag,set to negative to discard fragment later
-
- "float x_t = -1;"
- "if (bool(isFocalOnCircle)) {"
- "x_t = dot(p, p) / p.x;"
- "} else if (bool(isWellBehaved)) {"
- "x_t = length(p) - p.x * invR1;"
- "} else {"
- "float temp = p.x * p.x - p.y * p.y;"
-
- // Only do sqrt if temp >= 0; this is significantly slower than
- // checking temp >= 0 in the if statement that checks r(t) >= 0.
- // But GPU may break if we sqrt a negative float. (Although I
- // haven't observed that on any devices so far, and the old
- // approach also does sqrt negative value without a check.) If
- // the performance is really critical, maybe we should just
- // compute the area where temp and x_t are always valid and drop
- // all these ifs.
- "if (temp >= 0) {"
- "if (bool(isSwapped) || !bool(isRadiusIncreasing)) {"
- "x_t = -sqrt(temp) - p.x * invR1;"
- "} else {"
- "x_t = sqrt(temp) - p.x * invR1;"
- "}"
- "}"
- "}"
-
- // The final calculation of t from x_t has lots of static
- // optimizations but only do them when x_t is positive (which
- // can be assumed true if isWellBehaved is true)
- "if (!bool(isWellBehaved)) {"
- // This will still calculate t even though it will be ignored
- // later in the pipeline to avoid a branch
- "if (x_t <= 0.0) {"
- "v = -1;"
- "}"
- "}"
- "if (bool(isRadiusIncreasing)) {"
- "if (bool(isNativelyFocal)) {"
- "t = x_t;"
- "} else {"
- "t = x_t + fx;"
- "}"
- "} else {"
- "if (bool(isNativelyFocal)) {"
- "t = -x_t;"
- "} else {"
- "t = -x_t + fx;"
- "}"
- "}"
-
- "if (bool(isSwapped)) {"
- "t = 1 - t;"
- "}"
-
- "return half4(half(t), v, 0, 0);"
- "}"
- );
-
- const SkTwoPointConicalGradient::FocalData& focalData = this->getFocalData();
- bool isRadiusIncreasing = (1 - focalData.fFocalX) > 0,
- isFocalOnCircle = focalData.isFocalOnCircle(),
- isWellBehaved = focalData.isWellBehaved(),
- isSwapped = focalData.isSwapped(),
- isNativelyFocal = focalData.isNativelyFocal();
-
- fp = GrSkSLFP::Make(kEffect, "TwoPointConicalFocalLayout", /*inputFP=*/nullptr,
- GrSkSLFP::OptFlags::kNone,
- "isRadiusIncreasing", GrSkSLFP::Specialize<int>(isRadiusIncreasing),
- "isFocalOnCircle", GrSkSLFP::Specialize<int>(isFocalOnCircle),
- "isWellBehaved", GrSkSLFP::Specialize<int>(isWellBehaved),
- "isSwapped", GrSkSLFP::Specialize<int>(isSwapped),
- "isNativelyFocal", GrSkSLFP::Specialize<int>(isNativelyFocal),
- "invR1", 1.0f / focalData.fR1,
- "fx", focalData.fFocalX);
- } break;
- }
- return GrGradientShader::MakeGradientFP(*this,
- args,
- mRec,
- std::move(fp),
- matrix.getMaybeNull());
-}
-
-#endif
-
-#if defined(SK_GRAPHITE)
-void SkTwoPointConicalGradient::addToKey(const skgpu::graphite::KeyContext& keyContext,
- skgpu::graphite::PaintParamsKeyBuilder* builder,
- skgpu::graphite::PipelineDataGatherer* gatherer) const {
- this->addToKeyCommon(keyContext, builder, gatherer,
- GradientType::kConical,
- fCenter1, fCenter2,
- fRadius1, fRadius2,
- 0.0f, 0.0f);
-}
-#endif
-
-// assumes colors is SkColor4f* and pos is SkScalar*
-#define EXPAND_1_COLOR(count) \
- SkColor4f tmp[2]; \
- do { \
- if (1 == count) { \
- tmp[0] = tmp[1] = colors[0]; \
- colors = tmp; \
- pos = nullptr; \
- count = 2; \
- } \
- } while (0)
-
-sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
- SkScalar startRadius,
- const SkPoint& end,
- SkScalar endRadius,
- const SkColor4f colors[],
- sk_sp<SkColorSpace> colorSpace,
- const SkScalar pos[],
- int colorCount,
- SkTileMode mode,
- const Interpolation& interpolation,
- const SkMatrix* localMatrix) {
- if (startRadius < 0 || endRadius < 0) {
- return nullptr;
- }
- if (!SkGradientShaderBase::ValidGradient(colors, colorCount, mode, interpolation)) {
- return nullptr;
- }
- if (SkScalarNearlyZero((start - end).length(), SkGradientShaderBase::kDegenerateThreshold)) {
- // If the center positions are the same, then the gradient is the radial variant of a 2 pt
- // conical gradient, an actual radial gradient (startRadius == 0), or it is fully degenerate
- // (startRadius == endRadius).
- if (SkScalarNearlyEqual(startRadius, endRadius,
- SkGradientShaderBase::kDegenerateThreshold)) {
- // Degenerate case, where the interpolation region area approaches zero. The proper
- // behavior depends on the tile mode, which is consistent with the default degenerate
- // gradient behavior, except when mode = clamp and the radii > 0.
- if (mode == SkTileMode::kClamp &&
- endRadius > SkGradientShaderBase::kDegenerateThreshold) {
- // The interpolation region becomes an infinitely thin ring at the radius, so the
- // final gradient will be the first color repeated from p=0 to 1, and then a hard
- // stop switching to the last color at p=1.
- static constexpr SkScalar circlePos[3] = {0, 1, 1};
- SkColor4f reColors[3] = {colors[0], colors[0], colors[colorCount - 1]};
- return MakeRadial(start, endRadius, reColors, std::move(colorSpace),
- circlePos, 3, mode, interpolation, localMatrix);
- } else {
- // Otherwise use the default degenerate case
- return SkGradientShaderBase::MakeDegenerateGradient(colors, pos, colorCount,
- std::move(colorSpace), mode);
- }
- } else if (SkScalarNearlyZero(startRadius, SkGradientShaderBase::kDegenerateThreshold)) {
- // We can treat this gradient as radial, which is faster. If we got here, we know
- // that endRadius is not equal to 0, so this produces a meaningful gradient
- return MakeRadial(start, endRadius, colors, std::move(colorSpace), pos, colorCount,
- mode, interpolation, localMatrix);
- }
- // Else it's the 2pt conical radial variant with no degenerate radii, so fall through to the
- // regular 2pt constructor.
- }
-
- if (localMatrix && !localMatrix->invert(nullptr)) {
- return nullptr;
- }
- EXPAND_1_COLOR(colorCount);
-
- SkGradientShaderBase::ColorStopOptimizer opt(colors, pos, colorCount, mode);
-
- SkGradientShaderBase::Descriptor desc(opt.fColors, std::move(colorSpace), opt.fPos,
- opt.fCount, mode, interpolation);
- return SkTwoPointConicalGradient::Create(start, startRadius, end, endRadius, desc, localMatrix);
-}
-
-#undef EXPAND_1_COLOR
-
-sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
- SkScalar startRadius,
- const SkPoint& end,
- SkScalar endRadius,
- const SkColor colors[],
- const SkScalar pos[],
- int colorCount,
- SkTileMode mode,
- uint32_t flags,
- const SkMatrix* localMatrix) {
- SkColorConverter converter(colors, colorCount);
- return MakeTwoPointConical(start, startRadius, end, endRadius, converter.fColors4f.begin(),
- nullptr, pos, colorCount, mode, flags, localMatrix);
-}
-
-void SkRegisterTwoPointConicalGradientShaderFlattenable() {
- SK_REGISTER_FLATTENABLE(SkTwoPointConicalGradient);
-}
diff --git a/src/svg/SkSVGDevice.cpp b/src/svg/SkSVGDevice.cpp
index d3ec053..2f245fa 100644
--- a/src/svg/SkSVGDevice.cpp
+++ b/src/svg/SkSVGDevice.cpp
@@ -52,6 +52,7 @@
#include "src/core/SkFontPriv.h"
#include "src/core/SkTHash.h"
#include "src/image/SkImage_Base.h"
+#include "src/shaders/SkColorShader.h"
#include "src/shaders/SkShaderBase.h"
#include "src/text/GlyphRun.h"
#include "src/xml/SkXMLWriter.h"
@@ -437,11 +438,17 @@
void SkSVGDevice::AutoElement::addGradientShaderResources(const SkShader* shader,
const SkPaint& paint,
Resources* resources) {
+ SkASSERT(shader);
+ if (as_SB(shader)->type() == SkShaderBase::ShaderType::kColor) {
+ auto colorShader = static_cast<const SkColorShader*>(shader);
+ resources->fPaintServer = svg_color(colorShader->color());
+ return;
+ }
+
SkShaderBase::GradientInfo grInfo;
const auto gradient_type = as_SB(shader)->asGradient(&grInfo);
- if (gradient_type != SkShaderBase::GradientType::kColor &&
- gradient_type != SkShaderBase::GradientType::kLinear) {
+ if (gradient_type != SkShaderBase::GradientType::kLinear) {
// TODO: other gradient support
return;
}
@@ -458,9 +465,8 @@
SkASSERT(grInfo.fColorCount <= grOffsets.count());
SkASSERT(grColors.size() > 0);
- resources->fPaintServer = gradient_type == SkShaderBase::GradientType::kColor
- ? svg_color(grColors[0])
- : SkStringPrintf("url(#%s)", addLinearGradientDef(grInfo, shader, localMatrix).c_str());
+ resources->fPaintServer =
+ SkStringPrintf("url(#%s)", addLinearGradientDef(grInfo, shader, localMatrix).c_str());
}
void SkSVGDevice::AutoElement::addColorFilterResources(const SkColorFilter& cf,
@@ -608,7 +614,9 @@
const SkShader* shader = paint.getShader();
SkASSERT(shader);
- if (as_SB(shader)->asGradient() != SkShaderBase::GradientType::kNone) {
+ auto shaderType = as_SB(shader)->type();
+ if (shaderType == SkShaderBase::ShaderType::kColor ||
+ shaderType == SkShaderBase::ShaderType::kGradientBase) {
this->addGradientShaderResources(shader, paint, resources);
} else if (shader->isAImage()) {
this->addImageShaderResources(shader, paint, resources);
diff --git a/src/xps/SkXPSDevice.cpp b/src/xps/SkXPSDevice.cpp
index 7516d7a..a832101 100644
--- a/src/xps/SkXPSDevice.cpp
+++ b/src/xps/SkXPSDevice.cpp
@@ -49,6 +49,7 @@
#include "src/image/SkImage_Base.h"
#include "src/sfnt/SkSFNTHeader.h"
#include "src/sfnt/SkTTCFHeader.h"
+#include "src/shaders/SkColorShader.h"
#include "src/shaders/SkShaderBase.h"
#include "src/text/GlyphRun.h"
#include "src/utils/SkClipStackUtils.h"
@@ -976,22 +977,16 @@
}
//Gradient shaders.
- SkShaderBase::GradientInfo info;
- SkShaderBase::GradientType gradientType = as_SB(shader)->asGradient(&info);
+ auto shaderBase = as_SB(shader);
- if (gradientType == SkShaderBase::GradientType::kNone) {
- //Nothing to see, move along.
-
- } else if (gradientType == SkShaderBase::GradientType::kColor) {
- SkASSERT(1 == info.fColorCount);
- SkColor color;
- info.fColors = &color;
- as_SB(shader)->asGradient(&info);
+ if (shaderBase->type() == SkShaderBase::ShaderType::kColor) {
+ auto colorShader = static_cast<const SkColorShader*>(shader);
SkAlpha alpha = skPaint.getAlpha();
- HR(this->createXpsSolidColorBrush(color, alpha, brush));
+ HR(this->createXpsSolidColorBrush(colorShader->color(), alpha, brush));
return S_OK;
-
- } else {
+ } else if (shaderBase->type() == SkShaderBase::ShaderType::kGradientBase) {
+ SkShaderBase::GradientInfo info;
+ SkShaderBase::GradientType gradientType = shaderBase->asGradient(&info);
if (info.fColorCount == 0) {
const SkColor color = skPaint.getColor();
HR(this->createXpsSolidColorBrush(color, 0xFF, brush));
@@ -1003,7 +998,7 @@
AutoTArray<SkScalar> colorOffsets(info.fColorCount);
info.fColors = colors.get();
info.fColorOffsets = colorOffsets.get();
- as_SB(shader)->asGradient(&info, &localMatrix);
+ shaderBase->asGradient(&info, &localMatrix);
if (1 == info.fColorCount) {
SkColor color = info.fColors[0];
diff --git a/tests/BlurTest.cpp b/tests/BlurTest.cpp
index 42c2b4e..e89c0f7 100644
--- a/tests/BlurTest.cpp
+++ b/tests/BlurTest.cpp
@@ -505,7 +505,7 @@
{ 1, 1, 1 }, 0, 127, 127
};
p.setMaskFilter(SkEmbossMaskFilter::Make(1, light));
- p.setShader(SkPerlinNoiseShader::MakeFractalNoise(1.0f, 1.0f, 2, 0.0f));
+ p.setShader(SkShaders::MakeFractalNoise(1.0f, 1.0f, 2, 0.0f));
sk_sp<SkSurface> surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(100, 100));
surface->getCanvas()->drawPaint(p);
diff --git a/tests/GradientTest.cpp b/tests/GradientTest.cpp
index 8bd3ffd..0552e60 100644
--- a/tests/GradientTest.cpp
+++ b/tests/GradientTest.cpp
@@ -33,6 +33,7 @@
#include "src/base/SkTLazy.h"
#include "src/gpu/ganesh/GrColorInfo.h"
#include "src/gpu/ganesh/GrFPArgs.h"
+#include "src/gpu/ganesh/GrFragmentProcessors.h"
#include "src/shaders/SkShaderBase.h"
#include "tests/Test.h"
@@ -105,11 +106,7 @@
static void color_gradproc(skiatest::Reporter* reporter, const GradRec& rec, const GradRec&) {
sk_sp<SkShader> s(SkShaders::Color(rec.fColors[0]));
- REPORTER_ASSERT(reporter, SkShaderBase::GradientType::kColor == as_SB(s)->asGradient());
-
- SkShaderBase::GradientInfo info;
- as_SB(s)->asGradient(&info);
- REPORTER_ASSERT(reporter, 1 == info.fColorCount);
+ REPORTER_ASSERT(reporter, SkShaderBase::GradientType::kNone == as_SB(s)->asGradient());
}
static void linear_gradproc(skiatest::Reporter* reporter, const GradRec& buildRec,
@@ -446,7 +443,7 @@
auto context = GrDirectContext::MakeMock(&options);
GrFPArgs args(context.get(), &dstColorInfo, props);
- as_SB(gradient)->asRootFragmentProcessor(args, SkMatrix::I());
+ GrFragmentProcessors::Make(gradient.get(), args, SkMatrix::I());
}
// "Interesting" fuzzer values.
diff --git a/tests/ImageFilterTest.cpp b/tests/ImageFilterTest.cpp
index 4d2e0e1..6a5e42e 100644
--- a/tests/ImageFilterTest.cpp
+++ b/tests/ImageFilterTest.cpp
@@ -256,7 +256,7 @@
}
{
sk_sp<SkImageFilter> paintFilter(SkImageFilters::Shader(
- SkPerlinNoiseShader::MakeTurbulence(SK_Scalar1, SK_Scalar1, 1, 0)));
+ SkShaders::MakeTurbulence(SK_Scalar1, SK_Scalar1, 1, 0)));
this->addFilter("paint and blur", SkImageFilters::Blur(
kBlurSigma, kBlurSigma, std::move(paintFilter), cropRect));
diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp
index 111ce6d..c78c367 100644
--- a/tests/PDFPrimitivesTest.cpp
+++ b/tests/PDFPrimitivesTest.cpp
@@ -481,7 +481,7 @@
SkPaint paint;
paint.setBlendMode(SkBlendMode::kDarken);
- paint.setShader(SkPerlinNoiseShader::MakeFractalNoise(0, 0, 2, 0, nullptr));
+ paint.setShader(SkShaders::MakeFractalNoise(0, 0, 2, 0, nullptr));
paint.setColor4f(SkColor4f{0, 0, 0 ,0});
canvas->drawPath(SkPath(), paint);
diff --git a/tests/ShaderTest.cpp b/tests/ShaderTest.cpp
index 26b05b3..9dc97b4 100644
--- a/tests/ShaderTest.cpp
+++ b/tests/ShaderTest.cpp
@@ -65,10 +65,9 @@
srcBitmap.eraseColor(SK_ColorRED);
SkCanvas canvas(srcBitmap);
SkPaint p;
- p.setShader(
- SkShaders::Blend(SkBlendMode::kClear,
- SkShaders::Empty(),
- SkPerlinNoiseShader::MakeFractalNoise(1.0f, 1.0f, 2, 0.0f)));
+ p.setShader(SkShaders::Blend(SkBlendMode::kClear,
+ SkShaders::Empty(),
+ SkShaders::MakeFractalNoise(1.0f, 1.0f, 2, 0.0f)));
SkRRect rr;
SkVector rd[] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}};
rr.setRectRadii({0, 0, 0, 0}, rd);
diff --git a/tests/TriangulatingPathRendererTests.cpp b/tests/TriangulatingPathRendererTests.cpp
index 8d61c0c..4844123 100644
--- a/tests/TriangulatingPathRendererTests.cpp
+++ b/tests/TriangulatingPathRendererTests.cpp
@@ -35,6 +35,7 @@
#include "src/gpu/ganesh/GrColorInfo.h"
#include "src/gpu/ganesh/GrEagerVertexAllocator.h"
#include "src/gpu/ganesh/GrFragmentProcessor.h"
+#include "src/gpu/ganesh/GrFragmentProcessors.h"
#include "src/gpu/ganesh/GrPaint.h"
#include "src/gpu/ganesh/GrStyle.h"
#include "src/gpu/ganesh/GrUserStencilSettings.h"
@@ -46,7 +47,6 @@
#include "src/gpu/ganesh/geometry/GrStyledShape.h"
#include "src/gpu/ganesh/geometry/GrTriangulator.h"
#include "src/gpu/ganesh/ops/TriangulatingPathRenderer.h"
-#include "src/shaders/SkShaderBase.h"
#include "tests/CtsEnforcement.h"
#include "tests/Test.h"
#include "tools/ToolUtils.h"
@@ -828,7 +828,7 @@
pts, colors, nullptr, std::size(colors), SkTileMode::kClamp);
GrColorInfo colorInfo(GrColorType::kRGBA_8888, kPremul_SkAlphaType, nullptr);
SkSurfaceProps props; // default props for testing
- return as_SB(shader)->asRootFragmentProcessor({rContext, &colorInfo, props}, ctm);
+ return GrFragmentProcessors::Make(shader.get(), {rContext, &colorInfo, props}, ctm);
}
static void test_path(GrRecordingContext* rContext,
diff --git a/tests/graphite/ImageShaderTest.cpp b/tests/graphite/ImageShaderTest.cpp
index 66860ef..85a0ec9 100644
--- a/tests/graphite/ImageShaderTest.cpp
+++ b/tests/graphite/ImageShaderTest.cpp
@@ -8,6 +8,7 @@
#include "tests/Test.h"
#include "include/core/SkBitmap.h"
+#include "include/core/SkTileMode.h"
#include "include/gpu/graphite/Context.h"
#include "include/gpu/graphite/Surface.h"
#include "src/gpu/graphite/Surface_Graphite.h"
diff --git a/tests/graphite/PaintParamsKeyTest.cpp b/tests/graphite/PaintParamsKeyTest.cpp
index 9641c66..22b5702 100644
--- a/tests/graphite/PaintParamsKeyTest.cpp
+++ b/tests/graphite/PaintParamsKeyTest.cpp
@@ -256,8 +256,7 @@
o = PrecompileShaders::TwoPointConicalGradient();
break;
case SkShaderBase::GradientType::kNone:
- case SkShaderBase::GradientType::kColor:
- SkASSERT(0);
+ SkDEBUGFAIL("Gradient shader says its type is none");
break;
}
diff --git a/toolchain/linux_trampolines/clang_trampoline_linux.sh b/toolchain/linux_trampolines/clang_trampoline_linux.sh
index f6b5923..97304a9 100755
--- a/toolchain/linux_trampolines/clang_trampoline_linux.sh
+++ b/toolchain/linux_trampolines/clang_trampoline_linux.sh
@@ -36,6 +36,7 @@
"src/gpu/ganesh/surface/"
"src/image/"
"src/pathops/"
+ "src/shaders/"
"src/sksl/"
"src/svg/"
"src/text/"
@@ -117,6 +118,7 @@
"src/gpu/ganesh/GrSurfaceProxyView.cpp"
"src/gpu/ganesh/GrTextureProxy.cpp"
"src/gpu/ganesh/SkGr.cpp"
+ "src/gpu/ganesh/effects/GrPerlinNoise2Effect.cpp"
"src/pdf/SkJpeg"
# See //bazel/generate_cpp_files_for_headers.bzl and //include/BUILD.bazel for more.
diff --git a/tools/skpbench/skpbench.cpp b/tools/skpbench/skpbench.cpp
index e7d5555..ca09d64 100644
--- a/tools/skpbench/skpbench.cpp
+++ b/tools/skpbench/skpbench.cpp
@@ -699,7 +699,7 @@
// Use a perlin shader to warmup the GPU.
SkPaint perlin;
- perlin.setShader(SkPerlinNoiseShader::MakeTurbulence(0.1f, 0.1f, 1, 0, nullptr));
+ perlin.setShader(SkShaders::MakeTurbulence(0.1f, 0.1f, 1, 0, nullptr));
recording->drawRect(bounds, perlin);
return recorder.finishRecordingAsPicture();