/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "include/core/SkAlphaType.h" #include "include/core/SkBitmap.h" #include "include/core/SkBlendMode.h" #include "include/core/SkBlender.h" #include "include/core/SkCanvas.h" #include "include/core/SkColor.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/SkPaint.h" #include "include/core/SkPixmap.h" #include "include/core/SkPoint.h" #include "include/core/SkRRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkSamplingOptions.h" #include "include/core/SkShader.h" #include "include/core/SkSize.h" #include "include/core/SkString.h" #include "include/core/SkSurface.h" #include "include/core/SkTileMode.h" #include "include/effects/SkPerlinNoiseShader.h" #include "include/effects/SkRuntimeEffect.h" #include "include/private/base/SkAssert.h" #include "tests/CtsEnforcement.h" #include "tests/Test.h" #include #include #if defined(SK_GANESH) || defined(SK_GRAPHITE) #include "include/gpu/GpuTypes.h" #endif #if defined(SK_GANESH) #include "include/gpu/ganesh/GrDirectContext.h" #include "include/gpu/ganesh/SkSurfaceGanesh.h" struct GrContextOptions; #endif #if defined(SK_GRAPHITE) #include "include/gpu/graphite/Context.h" #include "include/gpu/graphite/Surface.h" #endif static void check_isaimage(skiatest::Reporter* reporter, SkShader* shader, int expectedW, int expectedH, SkTileMode expectedX, SkTileMode expectedY, const SkMatrix& expectedM) { SkTileMode tileModes[2]; SkMatrix localM; // wack these so we don't get a false positive localM.setScale(9999, -9999); tileModes[0] = tileModes[1] = (SkTileMode)99; SkImage* image = shader->isAImage(&localM, tileModes); REPORTER_ASSERT(reporter, image); REPORTER_ASSERT(reporter, image->width() == expectedW); REPORTER_ASSERT(reporter, image->height() == expectedH); REPORTER_ASSERT(reporter, localM == expectedM); REPORTER_ASSERT(reporter, tileModes[0] == expectedX); REPORTER_ASSERT(reporter, tileModes[1] == expectedY); } DEF_TEST(Shader_isAImage, reporter) { const int W = 100; const int H = 100; SkBitmap bm; bm.allocN32Pixels(W, H); auto img = bm.asImage(); const SkMatrix localM = SkMatrix::Scale(2, 3); const SkTileMode tmx = SkTileMode::kRepeat; const SkTileMode tmy = SkTileMode::kMirror; auto shader0 = bm.makeShader(tmx, tmy, SkSamplingOptions(), localM); auto shader1 = bm.asImage()->makeShader(tmx, tmy, SkSamplingOptions(), localM); check_isaimage(reporter, shader0.get(), W, H, tmx, tmy, localM); check_isaimage(reporter, shader1.get(), W, H, tmx, tmy, localM); } // Make sure things are ok with just a single leg. DEF_TEST(ComposeShaderSingle, reporter) { SkBitmap srcBitmap; srcBitmap.allocN32Pixels(10, 10); srcBitmap.eraseColor(SK_ColorRED); SkCanvas canvas(srcBitmap); SkPaint p; 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); canvas.drawRRect(rr, p); } // Tests that nested blending will render as expected. static void test_nested_blends(skiatest::Reporter* reporter, SkSurface* surface) { auto [redEffect, redError] = SkRuntimeEffect::MakeForShader(SkString(R"( half4 main(float2 coord) { return half4(1, 0, 0, 1); } )")); auto [greenEffect, greenError] = SkRuntimeEffect::MakeForShader(SkString(R"( half4 main(float2 coord) { return half4(0, 1, 0, 1); } )")); auto [blendEffect, blenderError] = SkRuntimeEffect::MakeForBlender(SkString(R"( half4 main(half4 src, half4 dst) { return (src + dst) * 0.5; } )")); auto [nestedBlendEffect, nestedBlenderError] = SkRuntimeEffect::MakeForBlender(SkString(R"( uniform blender child_blender; half4 main(half4 src, half4 dst) { return (child_blender.eval(src, dst) + dst) * 0.5; } )")); sk_sp redShader = redEffect->makeShader(nullptr, {}); sk_sp greenShader = greenEffect->makeShader(nullptr, {}); sk_sp blender = blendEffect->makeBlender(nullptr); std::vector children = {SkRuntimeEffect::ChildPtr(blender)}; sk_sp nestedBlender = nestedBlendEffect->makeBlender(nullptr, children); SkPaint paint; paint.setShader(SkShaders::Blend(nestedBlender, greenShader, redShader)); paint.setBlender(blender); // Do the drawing. SkCanvas* canvas = surface->getCanvas(); canvas->drawPaint(paint); // Read pixels. SkBitmap bitmap; SkPixmap pixmap; bitmap.allocPixels(surface->imageInfo()); SkAssertResult(bitmap.peekPixels(&pixmap)); if (!surface->readPixels(pixmap, 0, 0)) { ERRORF(reporter, "readPixels failed"); return; } // Check the resulting blended color. // First, in the paint's shader, red and green are averaged in the child blender to get // (0.5, 0.5, 0, 1), which is then averaged with green in the parent blender to get // (0.25, 0.75, 0, 1). Then, in the paint's blender this is averaged with a transparent // background to get (0.125, 0.375, 0, 0.5) and then unpremuled to get (0.25, 0.75, 0, 0.5). constexpr SkColor4f kExpected = {0.25f, 0.75f, 0.0f, 0.5f}; constexpr float kTolerance[4] = {0.01f, 0.01f, 0.0f, 0.01f}; SkColor4f color = pixmap.getColor4f(0, 0); for (int i = 0; i < 4; ++i) { if (std::abs(color[i] - kExpected[i]) > kTolerance[i]) { ERRORF(reporter, "Wrong color, expected (%.2f %.2f %.2f %.2f), actual (%.2f, %.2f, %.2f, %.2f)", kExpected.fR, kExpected.fG, kExpected.fB, kExpected.fA, color.fR, color.fG, color.fB, color.fA); break; } } } DEF_TEST(ShaderTestNestedBlendsCpu, reporter) { SkImageInfo ii = SkImageInfo::Make(SkISize::Make(1, 1), SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kPremul_SkAlphaType); sk_sp surface = SkSurfaces::Raster(ii); test_nested_blends(reporter, surface.get()); } #if defined(SK_GANESH) DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(ShaderTestNestedBlendsGanesh, reporter, contextInfo, CtsEnforcement::kApiLevel_V) { SkImageInfo ii = SkImageInfo::Make(SkISize::Make(1, 1), SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kPremul_SkAlphaType); GrDirectContext* context = contextInfo.directContext(); sk_sp surface = SkSurfaces::RenderTarget(context, skgpu::Budgeted::kYes, ii); test_nested_blends(reporter, surface.get()); } #endif #if defined(SK_GRAPHITE) DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(ShaderTestNestedBlendsGraphite, reporter, context, CtsEnforcement::kApiLevel_V) { using namespace skgpu::graphite; SkImageInfo ii = SkImageInfo::Make(SkISize::Make(1, 1), SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kPremul_SkAlphaType); std::unique_ptr recorder = context->makeRecorder(); sk_sp surface = SkSurfaces::RenderTarget(recorder.get(), ii); test_nested_blends(reporter, surface.get()); } #endif