/* * Copyright 2015 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/SkCanvas.h" #include "include/core/SkColor.h" #include "include/core/SkColorSpace.h" #include "include/core/SkImage.h" #include "include/core/SkPaint.h" #include "include/core/SkRect.h" #include "include/core/SkScalar.h" #include "include/core/SkSize.h" #include "include/core/SkString.h" #include "include/core/SkSurface.h" namespace skiagm { // This GM exercises anisotropic image scaling. class AnisotropicGM : public GM { public: enum class Mode { kLinear, kMip, kAniso }; AnisotropicGM(Mode mode) : fMode(mode) { switch (fMode) { case Mode::kLinear: fSampling = SkSamplingOptions(SkFilterMode::kLinear); break; case Mode::kMip: fSampling = SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear); break; case Mode::kAniso: fSampling = SkSamplingOptions::Aniso(16); break; } this->setBGColor(0xFFCCCCCC); } protected: SkString onShortName() override { SkString name("anisotropic_image_scale_"); switch (fMode) { case Mode::kLinear: name += "linear"; break; case Mode::kMip: name += "mip"; break; case Mode::kAniso: name += "aniso"; break; } return name; } SkISize onISize() override { return SkISize::Make(2*kImageSize + 3*kSpacer, kNumVertImages*kImageSize + (kNumVertImages+1)*kSpacer); } // Create an image consisting of lines radiating from its center void onOnceBeforeDraw() override { constexpr int kNumLines = 100; constexpr SkScalar kAngleStep = 360.0f / kNumLines; constexpr int kInnerOffset = 10; auto info = SkImageInfo::MakeN32(kImageSize, kImageSize, kOpaque_SkAlphaType); auto surf = SkSurface::MakeRaster(info); auto canvas = surf->getCanvas(); canvas->clear(SK_ColorWHITE); SkPaint p; p.setAntiAlias(true); SkScalar angle = 0.0f, sin, cos; canvas->translate(kImageSize/2.0f, kImageSize/2.0f); for (int i = 0; i < kNumLines; ++i, angle += kAngleStep) { sin = SkScalarSin(angle); cos = SkScalarCos(angle); canvas->drawLine(cos * kInnerOffset, sin * kInnerOffset, cos * kImageSize/2, sin * kImageSize/2, p); } fImage = surf->makeImageSnapshot(); } void draw(SkCanvas* canvas, int x, int y, int xSize, int ySize) { SkRect r = SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(xSize), SkIntToScalar(ySize)); canvas->drawImageRect(fImage, r, fSampling); } void onDraw(SkCanvas* canvas) override { SkScalar gScales[] = { 0.9f, 0.8f, 0.75f, 0.6f, 0.5f, 0.4f, 0.25f, 0.2f, 0.1f }; SkASSERT(kNumVertImages-1 == (int)SK_ARRAY_COUNT(gScales)/2); // Minimize vertically for (int i = 0; i < (int)SK_ARRAY_COUNT(gScales); ++i) { int height = SkScalarFloorToInt(fImage->height() * gScales[i]); int yOff; if (i <= (int)SK_ARRAY_COUNT(gScales)/2) { yOff = kSpacer + i * (fImage->height() + kSpacer); } else { // Position the more highly squashed images with their less squashed counterparts yOff = (SK_ARRAY_COUNT(gScales) - i) * (fImage->height() + kSpacer) - height; } this->draw(canvas, kSpacer, yOff, fImage->width(), height); } // Minimize horizontally for (int i = 0; i < (int)SK_ARRAY_COUNT(gScales); ++i) { int width = SkScalarFloorToInt(fImage->width() * gScales[i]); int xOff, yOff; if (i <= (int)SK_ARRAY_COUNT(gScales)/2) { xOff = fImage->width() + 2*kSpacer; yOff = kSpacer + i * (fImage->height() + kSpacer); } else { // Position the more highly squashed images with their less squashed counterparts xOff = fImage->width() + 2*kSpacer + fImage->width() - width; yOff = kSpacer + (SK_ARRAY_COUNT(gScales) - i - 1) * (fImage->height() + kSpacer); } this->draw(canvas, xOff, yOff, width, fImage->height()); } } private: inline static constexpr int kImageSize = 256; inline static constexpr int kSpacer = 10; inline static constexpr int kNumVertImages = 5; sk_sp fImage; SkSamplingOptions fSampling; Mode fMode; using INHERITED = GM; }; ////////////////////////////////////////////////////////////////////////////// DEF_GM(return new AnisotropicGM(AnisotropicGM::Mode::kLinear);) DEF_GM(return new AnisotropicGM(AnisotropicGM::Mode::kMip);) DEF_GM(return new AnisotropicGM(AnisotropicGM::Mode::kAniso);) ////////////////////////////////////////////////////////////////////////////// class AnisoMipsGM : public GM { public: AnisoMipsGM() = default; protected: SkString onShortName() override { return SkString("anisomips"); } SkISize onISize() override { return SkISize::Make(520, 260); } sk_sp updateImage(SkSurface* surf, SkColor color) { surf->getCanvas()->clear(color); SkPaint paint; paint.setColor(~color | 0xFF000000); surf->getCanvas()->drawRect(SkRect::MakeLTRB(surf->width() *2/5.f, surf->height()*2/5.f, surf->width() *3/5.f, surf->height()*3/5.f), paint); return surf->makeImageSnapshot()->withDefaultMipmaps(); } void onDraw(SkCanvas* canvas) override { auto ct = canvas->imageInfo().colorType() == kUnknown_SkColorType ? kRGBA_8888_SkColorType : canvas->imageInfo().colorType(); auto ii = SkImageInfo::Make(kImageSize, kImageSize, ct, kPremul_SkAlphaType, canvas->imageInfo().refColorSpace()); // In GPU mode we want a surface that is created with mipmaps to ensure that we exercise the // case where the SkSurface and SkImage share a texture. If the surface texture isn't // created with MIPs then asking for a mipmapped image will cause a copy to a mipped // texture. sk_sp surface; if (auto rc = canvas->recordingContext()) { surface = SkSurface::MakeRenderTarget(rc, SkBudgeted::kYes, ii, 1, kTopLeft_GrSurfaceOrigin, /*surfaceProps=*/nullptr, /*shouldCreateWithMips=*/true); if (!surface) { // We could be in an abandoned context situation. return; } } else { surface = canvas->makeSurface(ii); if (!surface) { // could be a recording canvas. surface = SkSurface::MakeRaster(ii); } } static constexpr float kScales[] = {1.f, 0.5f, 0.25f, 0.125f}; SkColor kColors[] = {0xFFF0F0F0, SK_ColorBLUE, SK_ColorGREEN, SK_ColorRED}; static const SkSamplingOptions kSampling = SkSamplingOptions::Aniso(16); for (bool shader : {false, true}) { int c = 0; canvas->save(); for (float sy : kScales) { canvas->save(); for (float sx : kScales) { canvas->save(); canvas->scale(sx, sy); auto image = this->updateImage(surface.get(), kColors[c]); if (shader) { SkPaint paint; paint.setShader(image->makeShader(kSampling)); canvas->drawRect(SkRect::Make(image->dimensions()), paint); } else { canvas->drawImage(image, 0, 0, kSampling); } canvas->restore(); canvas->translate(ii.width() * sx + kPad, 0); c = (c + 1) % SK_ARRAY_COUNT(kColors); } canvas->restore(); canvas->translate(0, ii.width() * sy + kPad); } canvas->restore(); for (float sx : kScales) { canvas->translate(ii.width() * sx + kPad, 0); } } } private: inline static constexpr int kImageSize = 128; inline static constexpr int kPad = 5; using INHERITED = GM; }; ////////////////////////////////////////////////////////////////////////////// DEF_GM(return new AnisoMipsGM();) } // namespace skiagm