/* * Copyright 2018 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "gm/gm.h" #include "include/core/SkBitmap.h" #include "include/core/SkBlendMode.h" #include "include/core/SkCanvas.h" #include "include/core/SkColor.h" #include "include/core/SkImage.h" #include "include/core/SkImageInfo.h" #include "include/core/SkMatrix.h" #include "include/core/SkPaint.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" #include "include/core/SkShader.h" #include "include/core/SkSurface.h" #include "include/core/SkTypes.h" #include "include/effects/SkGradientShader.h" #include "tools/Resources.h" #include "tools/ToolUtils.h" static sk_sp make_image(SkCanvas* rootCanvas) { static constexpr SkScalar kSize = 50; SkImageInfo info = SkImageInfo::MakeN32Premul(kSize, kSize); auto surface(SkSurface::MakeRaster(info)); SkPaint p; p.setAntiAlias(true); p.setColor(SK_ColorGREEN); surface->getCanvas()->drawCircle(kSize / 2, kSize / 2, kSize / 2, p); p.setStyle(SkPaint::kStroke_Style); p.setColor(SK_ColorRED); surface->getCanvas()->drawLine(kSize * .25f, kSize * .50f, kSize * .75f, kSize * .50f, p); surface->getCanvas()->drawLine(kSize * .50f, kSize * .25f, kSize * .50f, kSize * .75f, p); sk_sp img = surface->makeImageSnapshot(); return ToolUtils::MakeTextureImage(rootCanvas, std::move(img)); } DEF_SIMPLE_GM(localmatrixshader_nested, canvas, 450, 1200) { auto image = make_image(canvas); if (!image) { return; } using FactoryT = sk_sp (*)(const sk_sp&, const SkMatrix& inner, const SkMatrix& outer); static const FactoryT gFactories[] = { // SkLocalMatrixShader(SkImageShader(inner), outer) [](const sk_sp& img, const SkMatrix& inner, const SkMatrix& outer) { return img->makeShader(SkSamplingOptions(), inner)->makeWithLocalMatrix(outer); }, // SkLocalMatrixShader(SkLocalMatrixShader(SkImageShader(I), inner), outer) [](const sk_sp& img, const SkMatrix& inner, const SkMatrix& outer) { return img->makeShader(SkSamplingOptions())->makeWithLocalMatrix(inner)->makeWithLocalMatrix(outer); }, // SkLocalMatrixShader(SkComposeShader(SkImageShader(inner)), outer) [](const sk_sp& img, const SkMatrix& inner, const SkMatrix& outer) { return SkShaders::Blend(SkBlendMode::kSrcOver, SkShaders::Color(SK_ColorTRANSPARENT), img->makeShader(SkSamplingOptions(), inner)) ->makeWithLocalMatrix(outer); }, // SkLocalMatrixShader(SkComposeShader(SkLocalMatrixShader(SkImageShader(I), inner)), outer) [](const sk_sp& img, const SkMatrix& inner, const SkMatrix& outer) { return SkShaders::Blend(SkBlendMode::kSrcOver, SkShaders::Color(SK_ColorTRANSPARENT), img->makeShader(SkSamplingOptions())->makeWithLocalMatrix(inner)) ->makeWithLocalMatrix(outer); }, }; static const auto inner = SkMatrix::Scale(2, 2), outer = SkMatrix::Translate(20, 20); SkPaint border; border.setAntiAlias(true); border.setStyle(SkPaint::kStroke_Style); auto rect = SkRect::Make(image->bounds()); SkAssertResult(SkMatrix::Concat(inner, outer).mapRect(&rect)); const auto drawColumn = [&]() { SkAutoCanvasRestore acr(canvas, true); for (const auto& f : gFactories) { SkPaint p; p.setShader(f(image, inner, outer)); canvas->drawRect(rect, p); canvas->drawRect(rect, border); canvas->translate(0, rect.height() * 1.5f); } }; drawColumn(); { SkAutoCanvasRestore acr(canvas, true); canvas->translate(0, rect.height() * SK_ARRAY_COUNT(gFactories) * 1.5f); drawColumn(); } canvas->translate(rect.width() * 1.5f, 0); canvas->scale(2, 2); drawColumn(); } DEF_SIMPLE_GM(localmatrixshader_persp, canvas, 542, 266) { auto image = GetResourceAsImage("images/yellow_rose.png"); SkBitmap downsized; downsized.allocPixels(image->imageInfo().makeWH(128, 128)); image->scalePixels(downsized.pixmap(), SkSamplingOptions(SkFilterMode::kLinear)); image = downsized.asImage(); SkRect imgRect = SkRect::MakeIWH(image->width(), image->height()); // scale matrix SkMatrix scale = SkMatrix::Scale(1.f / 5.f, 1.f / 5.f); // perspective matrix SkPoint src[4]; imgRect.toQuad(src); SkPoint dst[4] = {{0, 10.f}, {image->width() + 28.f, -100.f}, {image->width() - 28.f, image->height() + 100.f}, {0.f, image->height() - 10.f}}; SkMatrix persp; SkAssertResult(persp.setPolyToPoly(src, dst, 4)); // combined persp * scale SkMatrix perspScale = SkMatrix::Concat(persp, scale); auto draw = [&](sk_sp shader, bool applyPerspToCTM) { canvas->save(); canvas->clipRect(imgRect); if (applyPerspToCTM) { canvas->concat(persp); } SkPaint imgShaderPaint; imgShaderPaint.setShader(std::move(shader)); canvas->drawPaint(imgShaderPaint); canvas->restore(); canvas->translate(10.f + image->width(), 0.f); // advance }; // SkImageShader canvas->save(); // 4 variants that all attempt to apply sample at persp * scale w/ an image shader // 1. scale provided to SkImage::makeShader(...) but drawn with persp auto s1 = image->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, SkSamplingOptions(), &scale); draw(s1, true); // 2. persp provided to SkImage::makeShader, then wrapped in scale makeWithLocalMatrix // These pre-concat, so it ends up as persp * scale. auto s2 = image->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, SkSamplingOptions(), &persp) ->makeWithLocalMatrix(scale); draw(s2, false); // 3. Providing pre-computed persp*scale to SkImage::makeShader() auto s3 = image->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, SkSamplingOptions(), &perspScale); draw(s3, false); // 4. Providing pre-computed persp*scale to makeWithLocalMatrix auto s4 = image->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, SkSamplingOptions()) ->makeWithLocalMatrix(perspScale); draw(s4, false); canvas->restore(); canvas->translate(0.f, 10.f + image->height()); // advance to next row // SkGradientShader const SkColor kGradColors[] = { SK_ColorBLACK, SK_ColorTRANSPARENT }; canvas->save(); // 1. scale provided to Make, drawn with persp auto g1 = SkGradientShader::MakeRadial({imgRect.centerX(), imgRect.centerY()}, imgRect.width() / 2.f, kGradColors, nullptr, 2, SkTileMode::kRepeat, 0, &scale); draw(g1, true); // 2. persp provided to Make, then wrapped with makeWithLocalMatrix (pre-concat as before). auto g2 = SkGradientShader::MakeRadial({imgRect.centerX(), imgRect.centerY()}, imgRect.width() / 2.f, kGradColors, nullptr, 2, SkTileMode::kRepeat, 0, &persp) ->makeWithLocalMatrix(scale); draw(g2, false); // 3. Provide per-computed persp*scale to Make auto g3 = SkGradientShader::MakeRadial({imgRect.centerX(), imgRect.centerY()}, imgRect.width() / 2.f, kGradColors, nullptr, 2, SkTileMode::kRepeat, 0, &perspScale); draw(g3, false); // 4. Providing pre-computed persp*scale to makeWithLocalMatrix auto g4 = SkGradientShader::MakeRadial({imgRect.centerX(), imgRect.centerY()}, imgRect.width() / 2.f, kGradColors, nullptr, 2, SkTileMode::kRepeat) ->makeWithLocalMatrix(perspScale); draw(g4, false); canvas->restore(); }