| /* |
| * Copyright 2025 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "tests/graphite/precompile/PaintParamsTestUtils.h" |
| |
| #if defined(SK_GRAPHITE) |
| |
| #include "include/core/SkBitmap.h" |
| #include "include/core/SkBlurTypes.h" |
| #include "include/core/SkCanvas.h" |
| #include "include/core/SkFont.h" |
| #include "include/core/SkMaskFilter.h" |
| #include "include/core/SkPathBuilder.h" |
| #include "include/core/SkPictureRecorder.h" |
| #include "include/core/SkRRect.h" |
| #include "include/core/SkShader.h" |
| #include "include/core/SkYUVAPixmaps.h" |
| #include "include/effects/SkBlenders.h" |
| #include "include/effects/SkColorMatrix.h" |
| #include "include/effects/SkGradient.h" |
| #include "include/effects/SkHighContrastFilter.h" |
| #include "include/effects/SkImageFilters.h" |
| #include "include/effects/SkLumaColorFilter.h" |
| #include "include/effects/SkOverdrawColorFilter.h" |
| #include "include/effects/SkPerlinNoiseShader.h" |
| #include "include/effects/SkRuntimeEffect.h" |
| #include "include/gpu/graphite/Image.h" |
| #include "include/gpu/graphite/Recorder.h" |
| #include "include/gpu/graphite/precompile/Precompile.h" |
| #include "include/gpu/graphite/precompile/PrecompileBlender.h" |
| #include "include/gpu/graphite/precompile/PrecompileColorFilter.h" |
| #include "include/gpu/graphite/precompile/PrecompileImageFilter.h" |
| #include "include/gpu/graphite/precompile/PrecompileMaskFilter.h" |
| #include "include/gpu/graphite/precompile/PrecompileRuntimeEffect.h" |
| #include "include/gpu/graphite/precompile/PrecompileShader.h" |
| #include "src/core/SkBlenderBase.h" |
| #include "src/core/SkColorFilterPriv.h" |
| #include "src/gpu/graphite/PrecompileContextPriv.h" |
| #include "src/gpu/graphite/precompile/PrecompileColorFiltersPriv.h" |
| #include "src/gpu/graphite/precompile/PrecompileShadersPriv.h" |
| #include "src/shaders/SkImageShader.h" |
| #include "tools/ToolUtils.h" |
| #include "tools/fonts/FontToolUtils.h" |
| #include "tools/graphite/precompile/PrecompileEffectFactories.h" |
| |
| using namespace skgpu::graphite; |
| using PrecompileShaders::GradientShaderFlags; |
| using PrecompileShaders::ImageShaderFlags; |
| using PrecompileShaders::YUVImageShaderFlags; |
| |
| namespace skiatest::graphite { |
| |
| //-------------------------------------------------------------------------------------------------- |
| // String Converters |
| //-------------------------------------------------------------------------------------------------- |
| |
| const char* ToStr(ShaderType s) { |
| switch (s) { |
| #define M(type) \ |
| case ShaderType::k##type: \ |
| return "ShaderType::k" #type; |
| SK_ALL_TEST_SHADERS(M) |
| #undef M |
| } |
| SkUNREACHABLE; |
| } |
| |
| const char* ToStr(MaskFilterType mf) { |
| switch (mf) { |
| #define M(type) \ |
| case MaskFilterType::k##type: \ |
| return "MaskFilterType::k" #type; |
| SK_ALL_TEST_MASKFILTERS(M) |
| #undef M |
| } |
| SkUNREACHABLE; |
| } |
| |
| const char* ToStr(BlenderType b) { |
| switch (b) { |
| #define M(type) \ |
| case BlenderType::k##type: \ |
| return "BlenderType::k" #type; |
| SK_ALL_TEST_BLENDERS(M) |
| #undef M |
| } |
| SkUNREACHABLE; |
| } |
| |
| const char* ToStr(ColorFilterType cf) { |
| switch (cf) { |
| #define M(type) \ |
| case ColorFilterType::k##type: \ |
| return "ColorFilterType::k" #type; |
| SK_ALL_TEST_COLORFILTERS(M) |
| #undef M |
| } |
| SkUNREACHABLE; |
| } |
| |
| const char* ToStr(ClipType c) { |
| switch (c) { |
| #define M(type) \ |
| case ClipType::k##type: \ |
| return "ClipType::k" #type; |
| SK_ALL_TEST_CLIPS(M) |
| #undef M |
| } |
| SkUNREACHABLE; |
| } |
| |
| const char* ToStr(ImageFilterType c) { |
| switch (c) { |
| #define M(type) \ |
| case ImageFilterType::k##type: \ |
| return "ImageFilterType::k" #type; |
| SK_ALL_TEST_IMAGE_FILTERS(M) |
| #undef M |
| } |
| SkUNREACHABLE; |
| } |
| |
| const char* ToStr(DrawTypeFlags dt) { |
| // Note: This logic assumes single-bit flags for simple string conversion, |
| // but the flags can be combined. |
| // We mask out modifiers (like kAnalyticClip) for the base check. |
| DrawTypeFlags base = static_cast<DrawTypeFlags>(dt & ~DrawTypeFlags::kAnalyticClip); |
| |
| switch (base) { |
| case DrawTypeFlags::kBitmapText_Mask: |
| return "DrawTypeFlags::kBitmapText_Mask"; |
| case DrawTypeFlags::kBitmapText_LCD: |
| return "DrawTypeFlags::kBitmapText_LCD"; |
| case DrawTypeFlags::kBitmapText_Color: |
| return "DrawTypeFlags::kBitmapText_Color"; |
| case DrawTypeFlags::kSDFText: |
| return "DrawTypeFlags::kSDFText"; |
| case DrawTypeFlags::kSDFText_LCD: |
| return "DrawTypeFlags::kSDFText_LCD"; |
| case DrawTypeFlags::kDrawVertices: |
| return "DrawTypeFlags::kDrawVertices"; |
| case DrawTypeFlags::kCircularArc: |
| return "DrawTypeFlags::kCircularArc"; |
| case DrawTypeFlags::kAnalyticRRect: |
| return "DrawTypeFlags::kAnalyticRRect"; |
| case DrawTypeFlags::kPerEdgeAAQuad: |
| return "DrawTypeFlags::kPerEdgeAAQuad"; |
| case DrawTypeFlags::kNonAAFillRect: |
| return "DrawTypeFlags::kNonAAFillRect"; |
| case DrawTypeFlags::kNonSimpleShape: |
| return "DrawTypeFlags::kNonSimpleShape"; |
| default: |
| break; |
| } |
| |
| if (dt == DrawTypeFlags::kNone) return "DrawTypeFlags::kNone"; |
| |
| return "DrawTypeFlags::kMultiple/Combo"; |
| } |
| |
| //-------------------------------------------------------------------------------------------------- |
| // Internal Helpers (Static) |
| //-------------------------------------------------------------------------------------------------- |
| |
| static constexpr skcms_TransferFunction gTransferFunctions[] = { |
| SkNamedTransferFn::kSRGB, |
| SkNamedTransferFn::k2Dot2, |
| SkNamedTransferFn::kLinear, |
| SkNamedTransferFn::kRec2020, |
| SkNamedTransferFn::kPQ, |
| SkNamedTransferFn::kHLG, |
| }; |
| |
| static constexpr int kTransferFunctionCount = std::size(gTransferFunctions); |
| |
| static const skcms_TransferFunction& random_xfer_function(SkRandom* rand) { |
| return gTransferFunctions[rand->nextULessThan(kTransferFunctionCount)]; |
| } |
| |
| static constexpr skcms_Matrix3x3 gGamuts[] = { |
| SkNamedGamut::kSRGB, |
| SkNamedGamut::kAdobeRGB, |
| SkNamedGamut::kDisplayP3, |
| SkNamedGamut::kRec2020, |
| SkNamedGamut::kXYZ, |
| }; |
| |
| static constexpr int kGamutCount = std::size(gGamuts); |
| |
| static const skcms_Matrix3x3& random_gamut(SkRandom* rand) { |
| return gGamuts[rand->nextULessThan(kGamutCount)]; |
| } |
| |
| enum class ColorSpaceType { kNone, kSRGB, kSRGBLinear, kSRGBSpin, kRGB, kLast = kRGB }; |
| |
| static constexpr int kColorSpaceTypeCount = static_cast<int>(ColorSpaceType::kLast) + 1; |
| |
| static ColorSpaceType random_colorspacetype(SkRandom* rand) { |
| return static_cast<ColorSpaceType>(rand->nextULessThan(kColorSpaceTypeCount)); |
| } |
| |
| static sk_sp<SkColorSpace> random_colorspace(SkRandom* rand) { |
| ColorSpaceType cs = random_colorspacetype(rand); |
| |
| switch (cs) { |
| case ColorSpaceType::kNone: |
| return nullptr; |
| case ColorSpaceType::kSRGB: |
| return SkColorSpace::MakeSRGB(); |
| case ColorSpaceType::kSRGBLinear: |
| return SkColorSpace::MakeSRGBLinear(); |
| case ColorSpaceType::kSRGBSpin: |
| return SkColorSpace::MakeSRGB()->makeColorSpin(); |
| case ColorSpaceType::kRGB: |
| return SkColorSpace::MakeRGB(random_xfer_function(rand), random_gamut(rand)); |
| } |
| SkUNREACHABLE; |
| } |
| |
| enum class ColorConstraint { |
| kNone, |
| kOpaque, |
| kTransparent, |
| }; |
| |
| static SkColor random_color(SkRandom* rand, ColorConstraint constraint) { |
| uint32_t color = rand->nextU(); |
| switch (constraint) { |
| case ColorConstraint::kNone: |
| return color; |
| case ColorConstraint::kOpaque: |
| return 0xff000000 | color; |
| case ColorConstraint::kTransparent: |
| return 0x80000000 | color; |
| } |
| SkUNREACHABLE; |
| } |
| |
| static SkColor4f random_color4f(SkRandom* rand, ColorConstraint constraint) { |
| SkColor4f result = {rand->nextRangeF(0.0f, 1.0f), |
| rand->nextRangeF(0.0f, 1.0f), |
| rand->nextRangeF(0.0f, 1.0f), |
| rand->nextRangeF(0.0f, 1.0f)}; |
| switch (constraint) { |
| case ColorConstraint::kNone: |
| return result; |
| case ColorConstraint::kOpaque: |
| result.fA = 1.0f; |
| return result; |
| case ColorConstraint::kTransparent: |
| result.fA = 0.5f; |
| return result; |
| } |
| SkUNREACHABLE; |
| } |
| |
| static SkTileMode random_tilemode(SkRandom* rand) { |
| return static_cast<SkTileMode>(rand->nextULessThan(kSkTileModeCount)); |
| } |
| |
| static ShaderType random_shadertype(SkRandom* rand) { |
| // defined in PaintParamsTestUtils.h, assumes contiguous enum ending at kLast |
| return static_cast<ShaderType>(rand->nextULessThan(static_cast<int>(ShaderType::kLast) + 1)); |
| } |
| |
| static SkBlendMode random_porter_duff_bm(SkRandom* rand) { |
| return static_cast<SkBlendMode>( |
| rand->nextRangeU((unsigned int)SkBlendMode::kClear, (unsigned int)SkBlendMode::kPlus)); |
| } |
| |
| static SkBlendMode random_complex_bm(SkRandom* rand) { |
| return static_cast<SkBlendMode>(rand->nextRangeU((unsigned int)SkBlendMode::kPlus, |
| (unsigned int)SkBlendMode::kLastMode)); |
| } |
| |
| static SkBlendMode random_blend_mode(SkRandom* rand) { |
| return static_cast<SkBlendMode>(rand->nextULessThan(kSkBlendModeCount)); |
| } |
| |
| static BlenderType random_blendertype(SkRandom* rand) { |
| return static_cast<BlenderType>(rand->nextULessThan(static_cast<int>(BlenderType::kLast) + 1)); |
| } |
| |
| static ColorFilterType random_colorfiltertype(SkRandom* rand) { |
| return static_cast<ColorFilterType>( |
| rand->nextULessThan(static_cast<int>(ColorFilterType::kLast) + 1)); |
| } |
| |
| [[maybe_unused]] static ImageFilterType random_imagefiltertype(SkRandom* rand) { |
| return static_cast<ImageFilterType>( |
| rand->nextULessThan(static_cast<int>(ImageFilterType::kLast) + 1)); |
| } |
| |
| DrawTypeFlags RandomDrawType(SkRandom* rand) { |
| uint32_t index = rand->nextULessThan(11); |
| |
| switch (index) { |
| case 0: |
| return DrawTypeFlags::kBitmapText_Mask; |
| case 1: |
| return DrawTypeFlags::kBitmapText_LCD; |
| case 2: |
| return DrawTypeFlags::kBitmapText_Color; |
| case 3: |
| return DrawTypeFlags::kSDFText; |
| case 4: |
| return DrawTypeFlags::kSDFText_LCD; |
| case 5: |
| return DrawTypeFlags::kDrawVertices; |
| case 6: |
| return DrawTypeFlags::kCircularArc; |
| case 7: |
| return DrawTypeFlags::kAnalyticRRect; |
| case 8: |
| return DrawTypeFlags::kPerEdgeAAQuad; |
| case 9: |
| return DrawTypeFlags::kNonAAFillRect; |
| case 10: |
| return DrawTypeFlags::kNonSimpleShape; |
| } |
| |
| SkASSERT(0); |
| return DrawTypeFlags::kNone; |
| } |
| |
| enum LocalMatrixConstraint { |
| kNone, |
| kWithPerspective, |
| }; |
| |
| static SkMatrix* random_local_matrix( |
| SkRandom* rand, |
| SkMatrix* storage, |
| LocalMatrixConstraint constraint = LocalMatrixConstraint::kNone) { |
| uint32_t matrix = rand->nextULessThan(constraint == LocalMatrixConstraint::kNone ? 4 : 3); |
| switch (matrix) { |
| case 0: |
| storage->setTranslate(2.0f, 2.0f); |
| break; |
| case 1: |
| storage->setIdentity(); |
| break; |
| case 2: |
| storage->setScale(0.25f, 0.25f, 0.0f, 0.0f); |
| break; |
| case 3: |
| return nullptr; |
| } |
| if (constraint == LocalMatrixConstraint::kWithPerspective) { |
| storage->setPerspX(0.5f); |
| } |
| return storage; |
| } |
| |
| static sk_sp<SkImage> make_image(SkRandom* rand, Recorder* recorder) { |
| SkColorType ct = SkColorType::kRGBA_8888_SkColorType; |
| if (rand->nextBool()) { |
| ct = SkColorType::kAlpha_8_SkColorType; |
| } |
| SkImageInfo info = SkImageInfo::Make(32, 32, ct, kPremul_SkAlphaType, random_colorspace(rand)); |
| SkBitmap bitmap; |
| bitmap.allocPixels(info); |
| bitmap.eraseColor(SK_ColorBLACK); |
| sk_sp<SkImage> img = bitmap.asImage(); |
| return SkImages::TextureFromImage(recorder, img, {false}); |
| } |
| |
| static sk_sp<SkImage> make_yuv_image(SkRandom* rand, Recorder* recorder) { |
| SkYUVAInfo::PlaneConfig planeConfig = SkYUVAInfo::PlaneConfig::kY_UV; |
| if (rand->nextBool()) { |
| planeConfig = SkYUVAInfo::PlaneConfig::kY_U_V_A; |
| } |
| SkYUVAInfo yuvaInfo( |
| { |
| 32, |
| 32, |
| }, |
| planeConfig, |
| SkYUVAInfo::Subsampling::k420, |
| kJPEG_Full_SkYUVColorSpace); |
| SkYUVAPixmapInfo pmInfo(yuvaInfo, SkYUVAPixmapInfo::DataType::kUnorm8, nullptr); |
| SkYUVAPixmaps pixmaps = SkYUVAPixmaps::Allocate(pmInfo); |
| for (int i = 0; i < pixmaps.numPlanes(); ++i) { |
| pixmaps.plane(i).erase(SK_ColorBLACK); |
| } |
| sk_sp<SkColorSpace> cs; |
| if (rand->nextBool()) { |
| cs = SkColorSpace::MakeSRGBLinear(); |
| } |
| return SkImages::TextureFromYUVAPixmaps(recorder, |
| pixmaps, |
| {/* fMipmapped= */ false}, |
| /* limitToMaxTextureSize= */ false, |
| std::move(cs)); |
| } |
| |
| static sk_sp<SkPicture> make_picture(SkRandom* rand) { |
| constexpr SkRect kRect = SkRect::MakeWH(128, 128); |
| SkPictureRecorder recorder; |
| SkCanvas* canvas = recorder.beginRecording(kRect); |
| SkPaint paint; |
| canvas->drawRect(kRect, paint); |
| return recorder.finishRecordingAsPicture(); |
| } |
| |
| //-------------------------------------------------------------------------------------------------- |
| // Shader Generators |
| //-------------------------------------------------------------------------------------------------- |
| |
| std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_coord_clamp_shader(SkRandom* rand, |
| Recorder* recorder, |
| bool* reqSKPOption) { |
| auto [s, o] = CreateRandomShader(rand, recorder, random_shadertype(rand), reqSKPOption); |
| SkASSERT(!s == !o); |
| if (!s) { |
| return {nullptr, nullptr}; |
| } |
| constexpr SkRect kSubset{0, 0, 256, 256}; |
| sk_sp<SkShader> ccs = SkShaders::CoordClamp(std::move(s), kSubset); |
| sk_sp<PrecompileShader> cco = PrecompileShaders::CoordClamp({std::move(o)}); |
| return {ccs, cco}; |
| } |
| |
| std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_perlin_noise_shader(SkRandom* rand) { |
| sk_sp<SkShader> s; |
| sk_sp<PrecompileShader> o; |
| if (rand->nextBool()) { |
| s = SkShaders::MakeFractalNoise(0.3f, 0.3f, 2, 4); |
| o = PrecompileShaders::MakeFractalNoise(); |
| } else { |
| s = SkShaders::MakeTurbulence(0.3f, 0.3f, 2, 4); |
| o = PrecompileShaders::MakeTurbulence(); |
| } |
| return {s, o}; |
| } |
| |
| std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_picture_shader(SkRandom* rand, |
| bool* reqSKPOption) { |
| sk_sp<SkPicture> picture = make_picture(rand); |
| |
| if (reqSKPOption) { |
| *reqSKPOption = true; |
| } |
| |
| SkMatrix lmStorage; |
| SkMatrix* lmPtr = random_local_matrix(rand, &lmStorage); |
| |
| sk_sp<SkShader> s = picture->makeShader( |
| SkTileMode::kClamp, SkTileMode::kClamp, SkFilterMode::kLinear, lmPtr, nullptr); |
| sk_sp<PrecompileShader> o = PrecompileShadersPriv::Picture(SkToBool(lmPtr)); |
| return {s, o}; |
| } |
| |
| std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_solid_shader( |
| SkRandom* rand, ColorConstraint constraint = ColorConstraint::kNone) { |
| sk_sp<SkShader> s; |
| sk_sp<PrecompileShader> o; |
| if (rand->nextBool()) { |
| s = SkShaders::Color(random_color(rand, constraint)); |
| o = PrecompileShaders::Color(); |
| } else { |
| sk_sp<SkColorSpace> cs = random_colorspace(rand); |
| s = SkShaders::Color(random_color4f(rand, constraint), cs); |
| o = PrecompileShaders::Color(std::move(cs)); |
| } |
| return {s, o}; |
| } |
| |
| std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_gradient_shader( |
| SkRandom* rand, |
| SkShaderBase::GradientType type, |
| ColorConstraint constraint = ColorConstraint::kOpaque) { |
| static constexpr int kMaxNumStops = 9; |
| SkColor4f colors[kMaxNumStops]; |
| for (int i = 0; i < kMaxNumStops; ++i) colors[i] = random_color4f(rand, constraint); |
| |
| static const SkPoint kPts[kMaxNumStops] = {{-100.0f, -100.0f}, |
| {-50.0f, -50.0f}, |
| {-25.0f, -25.0f}, |
| {-12.5f, -12.5f}, |
| {0.0f, 0.0f}, |
| {12.5f, 12.5f}, |
| {25.0f, 25.0f}, |
| {50.0f, 50.0f}, |
| {100.0f, 100.0f}}; |
| static const float kOffsets[kMaxNumStops] = { |
| 0.0f, 0.125f, 0.25f, 0.375f, 0.5f, 0.625f, 0.75f, 0.875f, 1.0f}; |
| |
| size_t numStops; |
| switch (rand->nextULessThan(3)) { |
| case 0: |
| numStops = 2; |
| break; |
| case 1: |
| numStops = 7; |
| break; |
| case 2: |
| [[fallthrough]]; |
| default: |
| numStops = kMaxNumStops; |
| break; |
| } |
| |
| SkMatrix lmStorage; |
| SkMatrix* lmPtr = random_local_matrix(rand, &lmStorage); |
| const SkGradient::Interpolation::InPremul inPremul = |
| rand->nextBool() ? SkGradient::Interpolation::InPremul::kYes |
| : SkGradient::Interpolation::InPremul::kNo; |
| const SkGradient::Interpolation::ColorSpace colorSpace = |
| static_cast<SkGradient::Interpolation::ColorSpace>( |
| rand->nextULessThan(SkGradient::Interpolation::kColorSpaceCount)); |
| SkGradient::Interpolation interpolation = {inPremul, colorSpace}; |
| |
| sk_sp<SkShader> s; |
| sk_sp<PrecompileShader> o; |
| SkTileMode tm = random_tilemode(rand); |
| const SkGradient grad = {{{colors, numStops}, {kOffsets, numStops}, tm}, interpolation}; |
| |
| switch (type) { |
| case SkShaderBase::GradientType::kLinear: |
| s = SkShaders::LinearGradient(kPts, grad, lmPtr); |
| o = PrecompileShaders::LinearGradient(GradientShaderFlags::kAll, interpolation); |
| break; |
| case SkShaderBase::GradientType::kRadial: |
| s = SkShaders::RadialGradient(/* center= */ {0, 0}, /* radius= */ 100, grad, lmPtr); |
| o = PrecompileShaders::RadialGradient(GradientShaderFlags::kAll, interpolation); |
| break; |
| case SkShaderBase::GradientType::kSweep: |
| s = SkShaders::SweepGradient(/* center= */ {0, 0}, grad, lmPtr); |
| o = PrecompileShaders::SweepGradient(GradientShaderFlags::kAll, interpolation); |
| break; |
| case SkShaderBase::GradientType::kConical: |
| s = SkShaders::TwoPointConicalGradient(/* start= */ {100, 100}, |
| /* startRadius= */ 100, |
| /* end= */ {-100, -100}, |
| /* endRadius= */ 100, |
| grad, lmPtr); |
| o = PrecompileShaders::TwoPointConicalGradient(GradientShaderFlags::kAll, |
| interpolation); |
| break; |
| case SkShaderBase::GradientType::kNone: |
| SkDEBUGFAIL("Gradient shader says its type is none"); |
| break; |
| } |
| return {s, o}; |
| } |
| |
| std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_localmatrix_shader(SkRandom* rand, |
| Recorder* recorder, |
| bool* reqSKPOption) { |
| auto [s, o] = CreateRandomShader(rand, recorder, random_shadertype(rand), reqSKPOption); |
| SkASSERT(!s == !o); |
| if (!s) { |
| return {nullptr, nullptr}; |
| } |
| bool hasPerspective = rand->nextBool(); |
| SkMatrix lmStorage; |
| random_local_matrix(rand, |
| &lmStorage, |
| hasPerspective ? LocalMatrixConstraint::kWithPerspective |
| : LocalMatrixConstraint::kNone); |
| return {s->makeWithLocalMatrix(lmStorage), o->makeWithLocalMatrix(hasPerspective)}; |
| } |
| |
| std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_colorfilter_shader(SkRandom* rand, |
| Recorder* recorder, |
| bool* reqSKPOption) { |
| auto [s, o] = CreateRandomShader(rand, recorder, random_shadertype(rand), reqSKPOption); |
| SkASSERT(!s == !o); |
| if (!s) { |
| return {nullptr, nullptr}; |
| } |
| auto [cf, cfO] = CreateRandomColorFilter(rand, random_colorfiltertype(rand)); |
| return {s->makeWithColorFilter(std::move(cf)), o->makeWithColorFilter(std::move(cfO))}; |
| } |
| |
| std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_image_shader(SkRandom* rand, |
| Recorder* recorder) { |
| SkTileMode tmX = random_tilemode(rand); |
| SkTileMode tmY = random_tilemode(rand); |
| std::vector<SkTileMode> precompileTileModes = |
| (tmX == tmY) ? std::vector<SkTileMode>{tmX} |
| : std::vector<SkTileMode>{SkTileMode::kClamp, SkTileMode::kRepeat}; |
| |
| SkMatrix lmStorage; |
| SkMatrix* lmPtr = random_local_matrix(rand, &lmStorage); |
| |
| sk_sp<SkShader> s; |
| sk_sp<PrecompileShader> o; |
| sk_sp<SkImage> image = make_image(rand, recorder); |
| SkColorInfo colorInfo = image->imageInfo().colorInfo(); |
| |
| switch (rand->nextULessThan(4)) { |
| case 0: { // Non-subset image |
| s = SkShaders::Image(std::move(image), tmX, tmY, SkSamplingOptions(), lmPtr); |
| o = PrecompileShaders::Image(ImageShaderFlags::kAll, {colorInfo}, precompileTileModes); |
| } break; |
| case 1: { // Subset image |
| const SkRect subset = SkRect::MakeWH(image->width() / 2, image->height() / 2); |
| s = SkImageShader::MakeSubset( |
| std::move(image), subset, tmX, tmY, SkSamplingOptions(), lmPtr); |
| o = PrecompileShaders::Image(ImageShaderFlags::kAll, {colorInfo}, precompileTileModes); |
| } break; |
| case 2: { // Cubic-sampled image |
| s = SkShaders::Image(std::move(image), tmX, tmY, SkCubicResampler::Mitchell(), lmPtr); |
| o = PrecompileShaders::Image(ImageShaderFlags::kAll, {colorInfo}, precompileTileModes); |
| } break; |
| default: { // Raw image draw |
| s = SkShaders::RawImage(std::move(image), tmX, tmY, SkSamplingOptions(), lmPtr); |
| o = PrecompileShaders::RawImage( |
| ImageShaderFlags::kExcludeCubic, {colorInfo}, precompileTileModes); |
| } break; |
| } |
| return {s, o}; |
| } |
| |
| std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_yuv_image_shader(SkRandom* rand, |
| Recorder* recorder) { |
| SkTileMode tmX = random_tilemode(rand); |
| SkTileMode tmY = random_tilemode(rand); |
| SkMatrix lmStorage; |
| SkMatrix* lmPtr = random_local_matrix(rand, &lmStorage); |
| sk_sp<SkShader> s; |
| sk_sp<PrecompileShader> o; |
| SkSamplingOptions samplingOptions(SkFilterMode::kLinear); |
| bool useCubic = rand->nextBool(); |
| if (useCubic) { |
| samplingOptions = SkCubicResampler::Mitchell(); |
| } |
| sk_sp<SkImage> yuvImage = make_yuv_image(rand, recorder); |
| SkColorInfo colorInfo = yuvImage->imageInfo().colorInfo(); |
| |
| if (rand->nextBool()) { |
| s = SkImageShader::MakeSubset(std::move(yuvImage), |
| SkRect::MakeXYWH(8, 8, 16, 16), |
| tmX, |
| tmY, |
| samplingOptions, |
| lmPtr); |
| } else { |
| s = SkShaders::Image(std::move(yuvImage), tmX, tmY, samplingOptions, lmPtr); |
| } |
| o = PrecompileShaders::YUVImage( |
| useCubic ? YUVImageShaderFlags::kCubicSampling : YUVImageShaderFlags::kExcludeCubic, |
| {colorInfo}); |
| return {s, o}; |
| } |
| |
| std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_blend_shader(SkRandom* rand, |
| Recorder* recorder, |
| bool* reqSKPOption) { |
| auto [blender, blenderO] = CreateRandomBlender(rand, random_blendertype(rand)); |
| auto [dstS, dstO] = CreateRandomShader(rand, recorder, random_shadertype(rand), reqSKPOption); |
| if (!dstS) return {nullptr, nullptr}; |
| auto [srcS, srcO] = CreateRandomShader(rand, recorder, random_shadertype(rand), reqSKPOption); |
| if (!srcS) return {nullptr, nullptr}; |
| |
| auto s = SkShaders::Blend(std::move(blender), std::move(dstS), std::move(srcS)); |
| auto o = PrecompileShaders::Blend( |
| SkSpan<const sk_sp<PrecompileBlender>>({blenderO}), {dstO}, {srcO}); |
| return {s, o}; |
| } |
| |
| std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> create_workingCS_shader(SkRandom* rand, |
| Recorder* recorder, |
| bool* reqSKPOption) { |
| auto [wrappedS, wrappedO] = |
| CreateRandomShader(rand, recorder, random_shadertype(rand), reqSKPOption); |
| if (!wrappedS) return {nullptr, nullptr}; |
| sk_sp<SkColorSpace> cs = random_colorspace(rand); |
| sk_sp<SkShader> s = wrappedS->makeWithWorkingColorSpace(cs); |
| sk_sp<PrecompileShader> o = wrappedO->makeWithWorkingColorSpace(std::move(cs)); |
| return {s, o}; |
| } |
| |
| std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> CreateRandomShader(SkRandom* rand, |
| Recorder* recorder, |
| ShaderType shaderType, |
| bool* reqSKPOption) { |
| switch (shaderType) { |
| case ShaderType::kNone: |
| return {nullptr, nullptr}; |
| case ShaderType::kBlend: |
| return create_blend_shader(rand, recorder, reqSKPOption); |
| case ShaderType::kColorFilter: |
| return create_colorfilter_shader(rand, recorder, reqSKPOption); |
| case ShaderType::kCoordClamp: |
| return create_coord_clamp_shader(rand, recorder, reqSKPOption); |
| case ShaderType::kConicalGradient: |
| return create_gradient_shader(rand, SkShaderBase::GradientType::kConical); |
| case ShaderType::kImage: |
| return create_image_shader(rand, recorder); |
| case ShaderType::kLinearGradient: |
| return create_gradient_shader(rand, SkShaderBase::GradientType::kLinear); |
| case ShaderType::kLocalMatrix: |
| return create_localmatrix_shader(rand, recorder, reqSKPOption); |
| case ShaderType::kPerlinNoise: |
| return create_perlin_noise_shader(rand); |
| case ShaderType::kPicture: |
| return create_picture_shader(rand, reqSKPOption); |
| case ShaderType::kRadialGradient: |
| return create_gradient_shader(rand, SkShaderBase::GradientType::kRadial); |
| case ShaderType::kRuntime: |
| return PrecompileFactories::CreateAnnulusRuntimeShader(); |
| case ShaderType::kSolidColor: |
| return create_solid_shader(rand); |
| case ShaderType::kSweepGradient: |
| return create_gradient_shader(rand, SkShaderBase::GradientType::kSweep); |
| case ShaderType::kYUVImage: |
| return create_yuv_image_shader(rand, recorder); |
| case ShaderType::kWorkingColorSpace: |
| return create_workingCS_shader(rand, recorder, reqSKPOption); |
| } |
| SkUNREACHABLE; |
| } |
| |
| std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> CreateClipShader(SkRandom* rand, |
| Recorder* recorder) { |
| switch (rand->nextULessThan(5)) { |
| case 0: |
| return create_gradient_shader( |
| rand, SkShaderBase::GradientType::kConical, ColorConstraint::kTransparent); |
| case 1: |
| return create_gradient_shader( |
| rand, SkShaderBase::GradientType::kLinear, ColorConstraint::kTransparent); |
| case 2: |
| return create_gradient_shader( |
| rand, SkShaderBase::GradientType::kRadial, ColorConstraint::kTransparent); |
| case 3: |
| return create_solid_shader(rand, ColorConstraint::kTransparent); |
| case 4: |
| return create_gradient_shader( |
| rand, SkShaderBase::GradientType::kSweep, ColorConstraint::kTransparent); |
| } |
| SkUNREACHABLE; |
| } |
| |
| //-------------------------------------------------------------------------------------------------- |
| // Blender Generators |
| //-------------------------------------------------------------------------------------------------- |
| |
| std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> create_bm_blender(SkRandom* rand, |
| SkBlendMode bm) { |
| return {SkBlender::Mode(bm), PrecompileBlenders::Mode(bm)}; |
| } |
| |
| std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> create_arithmetic_blender() { |
| sk_sp<SkBlender> b = SkBlenders::Arithmetic(0.5f, 0.5f, 0.5f, 0.5f, true); |
| sk_sp<PrecompileBlender> o = PrecompileBlenders::Arithmetic(); |
| return {std::move(b), std::move(o)}; |
| } |
| |
| std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> create_rt_blender(SkRandom* rand) { |
| int option = rand->nextULessThan(3); |
| switch (option) { |
| case 0: |
| return PrecompileFactories::CreateSrcRuntimeBlender(); |
| case 1: |
| return PrecompileFactories::CreateDstRuntimeBlender(); |
| case 2: |
| return PrecompileFactories::CreateComboRuntimeBlender(); |
| } |
| return {nullptr, nullptr}; |
| } |
| |
| std::pair<sk_sp<SkBlender>, sk_sp<PrecompileBlender>> CreateRandomBlender(SkRandom* rand, |
| BlenderType type) { |
| switch (type) { |
| case BlenderType::kNone: |
| return {nullptr, nullptr}; |
| case BlenderType::kPorterDuff: |
| return create_bm_blender(rand, random_porter_duff_bm(rand)); |
| case BlenderType::kShaderBased: |
| return create_bm_blender(rand, random_complex_bm(rand)); |
| case BlenderType::kArithmetic: |
| return create_arithmetic_blender(); |
| case BlenderType::kRuntime: |
| return create_rt_blender(rand); |
| } |
| SkUNREACHABLE; |
| } |
| |
| //-------------------------------------------------------------------------------------------------- |
| // ColorFilter Generators |
| //-------------------------------------------------------------------------------------------------- |
| |
| std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_rt_colorfilter( |
| SkRandom* rand) { |
| int option = rand->nextULessThan(3); |
| switch (option) { |
| case 0: |
| return PrecompileFactories::CreateDoubleRuntimeColorFilter(); |
| case 1: |
| return PrecompileFactories::CreateHalfRuntimeColorFilter(); |
| case 2: |
| return PrecompileFactories::CreateComboRuntimeColorFilter(); |
| } |
| return {nullptr, nullptr}; |
| } |
| |
| std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_lerp_colorfilter( |
| SkRandom* rand) { |
| auto [dst, dstO] = CreateRandomColorFilter(rand, random_colorfiltertype(rand)); |
| auto [src, srcO] = CreateRandomColorFilter(rand, random_colorfiltertype(rand)); |
| while (src == dst) { // Avoid optimization where src == dst |
| std::tie(src, srcO) = CreateRandomColorFilter(rand, random_colorfiltertype(rand)); |
| } |
| sk_sp<SkColorFilter> cf = SkColorFilters::Lerp(0.5f, std::move(dst), std::move(src)); |
| sk_sp<PrecompileColorFilter> o = PrecompileColorFilters::Lerp({dstO}, {srcO}); |
| return {cf, o}; |
| } |
| |
| std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_lighting_colorfilter() { |
| return {SkColorFilters::Lighting(SK_ColorGREEN, SK_ColorRED), |
| PrecompileColorFilters::Lighting()}; |
| } |
| |
| std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_blendmode_colorfilter( |
| SkRandom* rand) { |
| sk_sp<SkColorFilter> cf; |
| SkBlendMode blend; |
| while (!cf) { |
| blend = random_blend_mode(rand); |
| cf = SkColorFilters::Blend( |
| random_color4f(rand, ColorConstraint::kNone), random_colorspace(rand), blend); |
| } |
| sk_sp<PrecompileColorFilter> o = PrecompileColorFilters::Blend({&blend, 1}); |
| return {std::move(cf), std::move(o)}; |
| } |
| |
| std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_matrix_colorfilter() { |
| sk_sp<SkColorFilter> cf = SkColorFilters::Matrix( |
| SkColorMatrix::RGBtoYUV(SkYUVColorSpace::kJPEG_Full_SkYUVColorSpace)); |
| sk_sp<PrecompileColorFilter> o = PrecompileColorFilters::Matrix(); |
| return {std::move(cf), std::move(o)}; |
| } |
| |
| std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_color_space_colorfilter( |
| SkRandom* rand) { |
| sk_sp<SkColorSpace> src = random_colorspace(rand); |
| sk_sp<SkColorSpace> dst = random_colorspace(rand); |
| return {SkColorFilterPriv::MakeColorSpaceXform(src, dst), |
| PrecompileColorFiltersPriv::ColorSpaceXform({src}, {dst})}; |
| } |
| |
| std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_compose_colorfilter( |
| SkRandom* rand) { |
| auto [outerCF, outerO] = CreateRandomColorFilter(rand, random_colorfiltertype(rand)); |
| auto [innerCF, innerO] = CreateRandomColorFilter(rand, random_colorfiltertype(rand)); |
| return {SkColorFilters::Compose(std::move(outerCF), std::move(innerCF)), |
| PrecompileColorFilters::Compose({std::move(outerO)}, {std::move(innerO)})}; |
| } |
| |
| std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_gaussian_colorfilter() { |
| return {SkColorFilterPriv::MakeGaussian(), PrecompileColorFiltersPriv::Gaussian()}; |
| } |
| |
| std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_table_colorfilter() { |
| static constexpr uint8_t kTable[256] = {0}; |
| return {SkColorFilters::Table(kTable), PrecompileColorFilters::Table()}; |
| } |
| |
| std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_workingformat_colorfilter( |
| SkRandom* rand) { |
| auto [childCF, childO] = CreateRandomColorFilter(rand, random_colorfiltertype(rand)); |
| if (!childCF) return {nullptr, nullptr}; |
| const skcms_TransferFunction* tf = rand->nextBool() ? &random_xfer_function(rand) : nullptr; |
| const skcms_Matrix3x3* gamut = rand->nextBool() ? &random_gamut(rand) : nullptr; |
| const SkAlphaType unpremul = kUnpremul_SkAlphaType; |
| sk_sp<SkColorFilter> cf = |
| SkColorFilterPriv::WithWorkingFormat(std::move(childCF), tf, gamut, &unpremul); |
| sk_sp<PrecompileColorFilter> o = PrecompileColorFiltersPriv::WithWorkingFormat( |
| {std::move(childO)}, tf, gamut, &unpremul); |
| return {std::move(cf), std::move(o)}; |
| } |
| |
| std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_overdraw_colorfilter() { |
| static const SkColor kColors[SkOverdrawColorFilter::kNumColors] = { |
| SK_ColorBLACK, SK_ColorBLUE, SK_ColorCYAN, SK_ColorGREEN, SK_ColorYELLOW, SK_ColorRED}; |
| return {SkOverdrawColorFilter::MakeWithSkColors(kColors), PrecompileColorFilters::Overdraw()}; |
| } |
| |
| std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_high_contrast_colorfilter() { |
| SkHighContrastConfig config(false, SkHighContrastConfig::InvertStyle::kInvertBrightness, 0.5f); |
| return {SkHighContrastFilter::Make(config), PrecompileColorFilters::HighContrast()}; |
| } |
| |
| std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_luma_colorfilter() { |
| return {SkLumaColorFilter::Make(), PrecompileColorFilters::Luma()}; |
| } |
| |
| std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_linear_to_srgb_colorfilter() { |
| return {SkColorFilters::LinearToSRGBGamma(), PrecompileColorFilters::LinearToSRGBGamma()}; |
| } |
| |
| std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_srgb_to_linear_colorfilter() { |
| return {SkColorFilters::SRGBToLinearGamma(), PrecompileColorFilters::SRGBToLinearGamma()}; |
| } |
| |
| std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> create_hsla_matrix_colorfilter() { |
| sk_sp<SkColorFilter> cf = SkColorFilters::HSLAMatrix( |
| SkColorMatrix::RGBtoYUV(SkYUVColorSpace::kJPEG_Full_SkYUVColorSpace)); |
| sk_sp<PrecompileColorFilter> o = PrecompileColorFilters::HSLAMatrix(); |
| return {std::move(cf), std::move(o)}; |
| } |
| |
| std::pair<sk_sp<SkColorFilter>, sk_sp<PrecompileColorFilter>> CreateRandomColorFilter( |
| SkRandom* rand, ColorFilterType type) { |
| switch (type) { |
| case ColorFilterType::kNone: |
| return {nullptr, nullptr}; |
| case ColorFilterType::kBlendMode: |
| return create_blendmode_colorfilter(rand); |
| case ColorFilterType::kColorSpaceXform: |
| return create_color_space_colorfilter(rand); |
| case ColorFilterType::kCompose: |
| return create_compose_colorfilter(rand); |
| case ColorFilterType::kGaussian: |
| return create_gaussian_colorfilter(); |
| case ColorFilterType::kHighContrast: |
| return create_high_contrast_colorfilter(); |
| case ColorFilterType::kHSLAMatrix: |
| return create_hsla_matrix_colorfilter(); |
| case ColorFilterType::kLerp: |
| return create_lerp_colorfilter(rand); |
| case ColorFilterType::kLighting: |
| return create_lighting_colorfilter(); |
| case ColorFilterType::kLinearToSRGB: |
| return create_linear_to_srgb_colorfilter(); |
| case ColorFilterType::kLuma: |
| return create_luma_colorfilter(); |
| case ColorFilterType::kMatrix: |
| return create_matrix_colorfilter(); |
| case ColorFilterType::kOverdraw: |
| return create_overdraw_colorfilter(); |
| case ColorFilterType::kRuntime: |
| return create_rt_colorfilter(rand); |
| case ColorFilterType::kSRGBToLinear: |
| return create_srgb_to_linear_colorfilter(); |
| case ColorFilterType::kTable: |
| return create_table_colorfilter(); |
| case ColorFilterType::kWorkingFormat: |
| return create_workingformat_colorfilter(rand); |
| } |
| SkUNREACHABLE; |
| } |
| |
| //-------------------------------------------------------------------------------------------------- |
| // ImageFilter Generators |
| //-------------------------------------------------------------------------------------------------- |
| |
| std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> arithmetic_imagefilter(SkRandom*) { |
| sk_sp<SkImageFilter> arithmeticIF = |
| SkImageFilters::Arithmetic(0.5f, 0.5f, 0.5f, 0.5f, false, nullptr, nullptr); |
| sk_sp<PrecompileImageFilter> option = PrecompileImageFilters::Arithmetic(nullptr, nullptr); |
| return {std::move(arithmeticIF), std::move(option)}; |
| } |
| |
| std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> blendmode_imagefilter( |
| SkRandom* rand) { |
| SkBlendMode bm = random_blend_mode(rand); |
| sk_sp<SkImageFilter> blendIF = SkImageFilters::Blend(bm, nullptr, nullptr); |
| sk_sp<PrecompileImageFilter> blendO = PrecompileImageFilters::Blend(bm, nullptr, nullptr); |
| return {std::move(blendIF), std::move(blendO)}; |
| } |
| |
| std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> runtime_blender_imagefilter( |
| SkRandom* rand) { |
| auto [blender, blenderO] = CreateRandomBlender(rand, BlenderType::kRuntime); |
| sk_sp<SkImageFilter> blenderIF = SkImageFilters::Blend(std::move(blender), nullptr, nullptr); |
| sk_sp<PrecompileImageFilter> option = |
| PrecompileImageFilters::Blend(std::move(blenderO), nullptr, nullptr); |
| return {std::move(blenderIF), std::move(option)}; |
| } |
| |
| std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> blur_imagefilter(SkRandom* rand) { |
| float sigma = 5.0f; |
| int option = rand->nextULessThan(3); |
| if (option == 0) |
| sigma = 1.0f; |
| else if (option == 1) |
| sigma = 2.0f; |
| sk_sp<SkImageFilter> blurIF = SkImageFilters::Blur(sigma, sigma, nullptr); |
| sk_sp<PrecompileImageFilter> blurO = PrecompileImageFilters::Blur(nullptr); |
| return {std::move(blurIF), std::move(blurO)}; |
| } |
| |
| std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> displacement_imagefilter( |
| Recorder* recorder, SkRandom* rand) { |
| sk_sp<SkImage> checkerboard = |
| ToolUtils::create_checkerboard_image(16, 16, SK_ColorWHITE, SK_ColorBLACK, 4); |
| checkerboard = SkImages::TextureFromImage(recorder, std::move(checkerboard), {false}); |
| sk_sp<SkImageFilter> imageIF( |
| SkImageFilters::Image(std::move(checkerboard), SkFilterMode::kLinear)); |
| sk_sp<SkImageFilter> displacementIF = SkImageFilters::DisplacementMap( |
| SkColorChannel::kR, SkColorChannel::kB, 2.0f, std::move(imageIF), nullptr); |
| sk_sp<PrecompileImageFilter> option = PrecompileImageFilters::DisplacementMap(nullptr); |
| return {std::move(displacementIF), std::move(option)}; |
| } |
| |
| std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> colorfilter_imagefilter( |
| SkRandom* rand) { |
| auto [cf, o] = CreateRandomColorFilter(rand, random_colorfiltertype(rand)); |
| sk_sp<SkImageFilter> inputIF; |
| sk_sp<PrecompileImageFilter> inputO; |
| if (rand->nextBool()) { |
| auto [cf2, o2] = CreateRandomColorFilter(rand, random_colorfiltertype(rand)); |
| inputIF = SkImageFilters::ColorFilter(std::move(cf2), nullptr); |
| inputO = PrecompileImageFilters::ColorFilter(std::move(o2), nullptr); |
| } |
| sk_sp<SkImageFilter> cfIF = SkImageFilters::ColorFilter(std::move(cf), std::move(inputIF)); |
| sk_sp<PrecompileImageFilter> cfIFO = |
| PrecompileImageFilters::ColorFilter(std::move(o), std::move(inputO)); |
| return {std::move(cfIF), std::move(cfIFO)}; |
| } |
| |
| std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> lighting_imagefilter(SkRandom* rand) { |
| static constexpr SkPoint3 kLocation{10.0f, 2.0f, 30.0f}; |
| static constexpr SkPoint3 kTarget{0, 0, 0}; |
| static constexpr SkPoint3 kDirection{0, 1, 0}; |
| sk_sp<SkImageFilter> lightingIF; |
| int option = rand->nextULessThan(6); |
| switch (option) { |
| case 0: |
| lightingIF = |
| SkImageFilters::DistantLitDiffuse(kDirection, SK_ColorRED, 1.0f, 0.5f, nullptr); |
| break; |
| case 1: |
| lightingIF = |
| SkImageFilters::PointLitDiffuse(kLocation, SK_ColorGREEN, 1.0f, 0.5f, nullptr); |
| break; |
| case 2: |
| lightingIF = SkImageFilters::SpotLitDiffuse( |
| kLocation, kTarget, 2.0f, 30.0f, SK_ColorBLUE, 1.0f, 0.5f, nullptr); |
| break; |
| case 3: |
| lightingIF = SkImageFilters::DistantLitSpecular( |
| kDirection, SK_ColorCYAN, 1.0f, 0.5f, 2.0f, nullptr); |
| break; |
| case 4: |
| lightingIF = SkImageFilters::PointLitSpecular( |
| kLocation, SK_ColorMAGENTA, 1.0f, 0.5f, 2.0f, nullptr); |
| break; |
| case 5: |
| lightingIF = SkImageFilters::SpotLitSpecular( |
| kLocation, kTarget, 2.0f, 30.0f, SK_ColorYELLOW, 1.0f, 4.0f, 0.5f, nullptr); |
| break; |
| } |
| sk_sp<PrecompileImageFilter> lightingO = PrecompileImageFilters::Lighting(nullptr); |
| return {std::move(lightingIF), std::move(lightingO)}; |
| } |
| |
| std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> matrix_convolution_imagefilter( |
| SkRandom* rand) { |
| int kernelSize = 1; |
| int option = rand->nextULessThan(3); |
| if (option == 0) |
| kernelSize = 3; |
| else if (option == 1) |
| kernelSize = 7; |
| else if (option == 2) |
| kernelSize = 11; |
| |
| int center = (kernelSize * kernelSize - 1) / 2; |
| std::vector<float> kernel(kernelSize * kernelSize, SkIntToScalar(1)); |
| kernel[center] = 2.0f - kernelSize * kernelSize; |
| |
| sk_sp<SkImageFilter> matrixConvIF = SkImageFilters::MatrixConvolution({kernelSize, kernelSize}, |
| kernel.data(), |
| 0.3f, |
| 100.0f, |
| {1, 1}, |
| SkTileMode::kMirror, |
| false, |
| nullptr); |
| sk_sp<PrecompileImageFilter> convOption = PrecompileImageFilters::MatrixConvolution(nullptr); |
| return {std::move(matrixConvIF), std::move(convOption)}; |
| } |
| |
| std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> morphology_imagefilter( |
| SkRandom* rand) { |
| static constexpr float kRadX = 2.0f, kRadY = 4.0f; |
| sk_sp<SkImageFilter> morphologyIF; |
| if (rand->nextBool()) { |
| morphologyIF = SkImageFilters::Erode(kRadX, kRadY, nullptr); |
| } else { |
| morphologyIF = SkImageFilters::Dilate(kRadX, kRadY, nullptr); |
| } |
| sk_sp<PrecompileImageFilter> option = PrecompileImageFilters::Morphology(nullptr); |
| return {std::move(morphologyIF), std::move(option)}; |
| } |
| |
| std::pair<sk_sp<SkImageFilter>, sk_sp<PrecompileImageFilter>> CreateRandomImageFilter( |
| Recorder* recorder, SkRandom* rand, ImageFilterType type) { |
| switch (type) { |
| case ImageFilterType::kNone: |
| return {}; |
| case ImageFilterType::kArithmetic: |
| return arithmetic_imagefilter(rand); |
| case ImageFilterType::kBlendMode: |
| return blendmode_imagefilter(rand); |
| case ImageFilterType::kRuntimeBlender: |
| return runtime_blender_imagefilter(rand); |
| case ImageFilterType::kBlur: |
| return blur_imagefilter(rand); |
| case ImageFilterType::kColorFilter: |
| return colorfilter_imagefilter(rand); |
| case ImageFilterType::kDisplacement: |
| return displacement_imagefilter(recorder, rand); |
| case ImageFilterType::kLighting: |
| return lighting_imagefilter(rand); |
| case ImageFilterType::kMatrixConvolution: |
| return matrix_convolution_imagefilter(rand); |
| case ImageFilterType::kMorphology: |
| return morphology_imagefilter(rand); |
| } |
| SkUNREACHABLE; |
| } |
| |
| //-------------------------------------------------------------------------------------------------- |
| // MaskFilter Generators |
| //-------------------------------------------------------------------------------------------------- |
| |
| std::pair<sk_sp<SkMaskFilter>, sk_sp<PrecompileMaskFilter>> create_blur_maskfilter(SkRandom* rand) { |
| SkBlurStyle style; |
| switch (rand->nextULessThan(4)) { |
| case 0: |
| style = kNormal_SkBlurStyle; |
| break; |
| case 1: |
| style = kSolid_SkBlurStyle; |
| break; |
| case 2: |
| style = kOuter_SkBlurStyle; |
| break; |
| case 3: |
| [[fallthrough]]; |
| default: |
| style = kInner_SkBlurStyle; |
| break; |
| } |
| float sigma = 1.0f; |
| switch (rand->nextULessThan(2)) { |
| case 0: |
| sigma = 1.0f; |
| break; |
| case 1: |
| sigma = 2.0f; |
| break; |
| case 2: |
| [[fallthrough]]; |
| default: |
| sigma = 5.0f; |
| break; |
| } |
| bool respectCTM = rand->nextBool(); |
| return {SkMaskFilter::MakeBlur(style, sigma, respectCTM), PrecompileMaskFilters::Blur()}; |
| } |
| |
| std::pair<sk_sp<SkMaskFilter>, sk_sp<PrecompileMaskFilter>> CreateRandomMaskFilter( |
| SkRandom* rand, MaskFilterType type) { |
| switch (type) { |
| case MaskFilterType::kNone: |
| return {nullptr, nullptr}; |
| case MaskFilterType::kBlur: |
| return create_blur_maskfilter(rand); |
| } |
| SkUNREACHABLE; |
| } |
| |
| //-------------------------------------------------------------------------------------------------- |
| // Main Entry Points |
| //-------------------------------------------------------------------------------------------------- |
| |
| std::pair<SkPaint, PaintOptions> CreateRandomPaint(SkRandom* rand, |
| Recorder* recorder, |
| ShaderType shaderType, |
| BlenderType blenderType, |
| ColorFilterType cfType, |
| MaskFilterType mfType, |
| ImageFilterType imageFilterType, |
| bool* reqSKPPaintOption) { |
| SkColor paintColor = random_color(rand, ColorConstraint::kNone); |
| SkPaint paint; |
| paint.setColor(paintColor); |
| PaintOptions paintOptions; |
| paintOptions.setPaintColorIsOpaque(SkColorGetA(paintColor) == 0xFF); |
| |
| { |
| std::pair<sk_sp<SkShader>, sk_sp<PrecompileShader>> sAndO; |
| if (shaderType == ShaderType::kPicture) { |
| sAndO = create_picture_shader(rand, reqSKPPaintOption); |
| } else { |
| sAndO = CreateRandomShader(rand, recorder, shaderType, reqSKPPaintOption); |
| } |
| |
| auto [s, o] = sAndO; |
| SkASSERT(!s == !o); |
| |
| if (s) { |
| paint.setShader(std::move(s)); |
| paintOptions.setShaders({o}); |
| } |
| } |
| |
| { |
| auto [cf, o] = CreateRandomColorFilter(rand, cfType); |
| SkASSERT(!cf == !o); |
| if (cf) { |
| paint.setColorFilter(std::move(cf)); |
| paintOptions.setColorFilters({o}); |
| } |
| } |
| |
| { |
| auto [mf, o] = CreateRandomMaskFilter(rand, mfType); |
| SkASSERT(!mf == !o); |
| if (mf) { |
| paint.setMaskFilter(std::move(mf)); |
| paintOptions.setMaskFilters({o}); |
| } |
| } |
| |
| { |
| auto [b, o] = CreateRandomBlender(rand, blenderType); |
| SkASSERT(!b == !o); |
| if (b) { |
| paint.setBlender(std::move(b)); |
| paintOptions.setBlenders({o}); |
| } |
| } |
| |
| { |
| auto [filter, o] = CreateRandomImageFilter(recorder, rand, imageFilterType); |
| SkASSERT(!filter == !o); |
| if (filter) { |
| paint.setImageFilter(std::move(filter)); |
| paintOptions.setImageFilters({o}); |
| } |
| } |
| |
| if (rand->nextBool()) { |
| paint.setDither(true); |
| paintOptions.setDither(true); |
| } |
| |
| return {paint, paintOptions}; |
| } |
| |
| static SkPath make_path() { |
| SkPathBuilder path; |
| path.moveTo(0, 0); |
| path.lineTo(8, 2); |
| path.lineTo(16, 0); |
| path.lineTo(14, 8); |
| path.lineTo(16, 16); |
| path.lineTo(8, 14); |
| path.lineTo(0, 16); |
| path.lineTo(2, 8); |
| path.close(); |
| return path.detach(); |
| } |
| |
| DrawData::DrawData() { |
| static constexpr int kMaskTextFontSize = 16; |
| static constexpr int kPathTextFontSize = 300; |
| |
| SkFont font(ToolUtils::DefaultPortableTypeface(), kMaskTextFontSize); |
| SkFont lcdFont(ToolUtils::DefaultPortableTypeface(), kMaskTextFontSize); |
| lcdFont.setSubpixel(true); |
| lcdFont.setEdging(SkFont::Edging::kSubpixelAntiAlias); |
| |
| ToolUtils::EmojiTestSample emojiTestSample = |
| ToolUtils::EmojiSample(ToolUtils::EmojiFontFormat::ColrV0); |
| SkFont emojiFont(emojiTestSample.typeface); |
| SkFont pathFont(ToolUtils::DefaultPortableTypeface(), kPathTextFontSize); |
| |
| const char text[] = "hambur1"; |
| constexpr int kNumVerts = 4; |
| constexpr SkPoint kPositions[kNumVerts]{{0, 0}, {0, 16}, {16, 16}, {16, 0}}; |
| constexpr SkColor kColors[kNumVerts] = { |
| SK_ColorBLUE, SK_ColorGREEN, SK_ColorCYAN, SK_ColorYELLOW}; |
| |
| fPath = make_path(); |
| fBlob = SkTextBlob::MakeFromText(text, strlen(text), font); |
| fLCDBlob = SkTextBlob::MakeFromText(text, strlen(text), lcdFont); |
| fEmojiBlob = SkTextBlob::MakeFromText( |
| emojiTestSample.sampleText, strlen(emojiTestSample.sampleText), emojiFont); |
| fPathBlob = SkTextBlob::MakeFromText(text, strlen(text), pathFont); |
| fVertsWithColors = SkVertices::MakeCopy( |
| SkVertices::kTriangleFan_VertexMode, kNumVerts, kPositions, kPositions, kColors); |
| fVertsWithOutColors = SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, |
| kNumVerts, |
| kPositions, |
| kPositions, |
| /* colors= */ nullptr); |
| } |
| |
| void non_simple_draws(SkCanvas* canvas, const SkPaint& paint, const DrawData& drawData) { |
| canvas->drawPath(drawData.fPath, paint); |
| canvas->drawTextBlob(drawData.fPathBlob, 0, 16, paint); |
| if (paint.getStyle() == SkPaint::kStroke_Style) { |
| canvas->drawArc({0, 0, 16, 16}, 0, 90, /* useCenter= */ true, paint); |
| } |
| } |
| |
| void ExecuteDraw(SkCanvas* canvas, |
| const SkPaint& paint, |
| const DrawData& drawData, |
| DrawTypeFlags dt) { |
| // Mask out the modifiers that don't affect which API is called on the canvas |
| DrawTypeFlags drawType = static_cast<DrawTypeFlags>(dt & ~DrawTypeFlags::kAnalyticClip); |
| |
| switch (drawType) { |
| case DrawTypeFlags::kBitmapText_Mask: |
| canvas->drawTextBlob(drawData.fBlob, 0, 16, paint); |
| break; |
| case DrawTypeFlags::kBitmapText_LCD: |
| canvas->drawTextBlob(drawData.fLCDBlob, 0, 16, paint); |
| break; |
| case DrawTypeFlags::kBitmapText_Color: |
| canvas->drawTextBlob(drawData.fEmojiBlob, 0, 16, paint); |
| break; |
| case DrawTypeFlags::kSDFText: { |
| SkMatrix perspective; |
| perspective.setPerspX(0.01f); |
| perspective.setPerspY(0.001f); |
| canvas->save(); |
| canvas->concat(perspective); |
| canvas->drawTextBlob(drawData.fBlob, 0, 16, paint); |
| canvas->restore(); |
| } break; |
| case DrawTypeFlags::kSDFText_LCD: { |
| SkMatrix perspective; |
| perspective.setPerspX(0.01f); |
| perspective.setPerspY(0.001f); |
| canvas->save(); |
| canvas->concat(perspective); |
| canvas->drawTextBlob(drawData.fLCDBlob, 0, 16, paint); |
| canvas->restore(); |
| } break; |
| case DrawTypeFlags::kDrawVertices: |
| canvas->drawVertices(drawData.fVertsWithColors, SkBlendMode::kDst, paint); |
| canvas->drawVertices(drawData.fVertsWithOutColors, SkBlendMode::kDst, paint); |
| break; |
| case DrawTypeFlags::kCircularArc: |
| canvas->drawArc({0, 0, 16, 16}, 0, 90, /* useCenter= */ false, paint); |
| if (paint.getStyle() == SkPaint::kFill_Style) { |
| canvas->drawArc({0, 0, 16, 16}, 0, 90, /* useCenter= */ true, paint); |
| } |
| break; |
| case DrawTypeFlags::kAnalyticRRect: |
| canvas->drawRRect(SkRRect::MakeOval({0, 0, 15, 15}), paint); |
| canvas->drawRRect(SkRRect::MakeRectXY({0, 0, 15, 15}, 4, 4), paint); |
| break; |
| case DrawTypeFlags::kPerEdgeAAQuad: |
| if (!paint.getShader() && !paint.getColorFilter() && !paint.getImageFilter() && |
| paint.asBlendMode().has_value()) { |
| canvas->experimental_DrawEdgeAAQuad(SkRect::MakeWH(15, 15), |
| nullptr, |
| SkCanvas::kAll_QuadAAFlags, |
| paint.getColor4f(), |
| paint.asBlendMode().value()); |
| } |
| break; |
| case DrawTypeFlags::kNonAAFillRect: |
| canvas->drawRect(SkRect::MakeWH(15, 15), paint); |
| break; |
| case DrawTypeFlags::kNonSimpleShape: |
| non_simple_draws(canvas, paint, drawData); |
| break; |
| default: |
| SkASSERT(false); |
| break; |
| } |
| } |
| |
| } // namespace skiatest::graphite |
| |
| #endif // SK_GRAPHITE |