1 /* 2 * Copyright 2015 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "gm/gm.h" 9 #include "include/core/SkCanvas.h" 10 #include "include/core/SkColor.h" 11 #include "include/core/SkColorSpace.h" 12 #include "include/core/SkImage.h" 13 #include "include/core/SkPaint.h" 14 #include "include/core/SkRect.h" 15 #include "include/core/SkScalar.h" 16 #include "include/core/SkShader.h" 17 #include "include/core/SkSize.h" 18 #include "include/core/SkString.h" 19 #include "include/core/SkSurface.h" 20 #include "include/gpu/GpuTypes.h" 21 #include "include/gpu/ganesh/SkSurfaceGanesh.h" 22 23 namespace skiagm { 24 25 // This GM exercises anisotropic image scaling. 26 class AnisotropicGM : public GM { 27 public: 28 enum class Mode { kLinear, kMip, kAniso }; 29 AnisotropicGM(Mode mode)30 AnisotropicGM(Mode mode) : fMode(mode) { 31 switch (fMode) { 32 case Mode::kLinear: 33 fSampling = SkSamplingOptions(SkFilterMode::kLinear); 34 break; 35 case Mode::kMip: 36 fSampling = SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear); 37 break; 38 case Mode::kAniso: 39 fSampling = SkSamplingOptions::Aniso(16); 40 break; 41 } 42 this->setBGColor(0xFFCCCCCC); 43 } 44 45 protected: getName() const46 SkString getName() const override { 47 SkString name("anisotropic_image_scale_"); 48 switch (fMode) { 49 case Mode::kLinear: 50 name += "linear"; 51 break; 52 case Mode::kMip: 53 name += "mip"; 54 break; 55 case Mode::kAniso: 56 name += "aniso"; 57 break; 58 } 59 return name; 60 } 61 getISize()62 SkISize getISize() override { 63 return SkISize::Make(2*kImageSize + 3*kSpacer, 64 kNumVertImages*kImageSize + (kNumVertImages+1)*kSpacer); 65 } 66 67 // Create an image consisting of lines radiating from its center onOnceBeforeDraw()68 void onOnceBeforeDraw() override { 69 constexpr int kNumLines = 100; 70 constexpr SkScalar kAngleStep = 360.0f / kNumLines; 71 constexpr int kInnerOffset = 10; 72 73 auto info = SkImageInfo::MakeN32(kImageSize, kImageSize, kOpaque_SkAlphaType); 74 auto surf = SkSurfaces::Raster(info); 75 auto canvas = surf->getCanvas(); 76 77 canvas->clear(SK_ColorWHITE); 78 79 SkPaint p; 80 p.setAntiAlias(true); 81 82 SkScalar angle = 0.0f, sin, cos; 83 84 canvas->translate(kImageSize/2.0f, kImageSize/2.0f); 85 for (int i = 0; i < kNumLines; ++i, angle += kAngleStep) { 86 sin = SkScalarSin(angle); 87 cos = SkScalarCos(angle); 88 canvas->drawLine(cos * kInnerOffset, sin * kInnerOffset, 89 cos * kImageSize/2, sin * kImageSize/2, p); 90 } 91 fImage = surf->makeImageSnapshot(); 92 } 93 draw(SkCanvas * canvas,int x,int y,int xSize,int ySize)94 void draw(SkCanvas* canvas, int x, int y, int xSize, int ySize) { 95 SkRect r = SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y), 96 SkIntToScalar(xSize), SkIntToScalar(ySize)); 97 canvas->drawImageRect(fImage, r, fSampling); 98 } 99 onDraw(SkCanvas * canvas)100 void onDraw(SkCanvas* canvas) override { 101 SkScalar gScales[] = { 0.9f, 0.8f, 0.75f, 0.6f, 0.5f, 0.4f, 0.25f, 0.2f, 0.1f }; 102 103 SkASSERT(kNumVertImages-1 == (int)std::size(gScales)/2); 104 105 // Minimize vertically 106 for (int i = 0; i < (int)std::size(gScales); ++i) { 107 int height = SkScalarFloorToInt(fImage->height() * gScales[i]); 108 109 int yOff; 110 if (i <= (int)std::size(gScales)/2) { 111 yOff = kSpacer + i * (fImage->height() + kSpacer); 112 } else { 113 // Position the more highly squashed images with their less squashed counterparts 114 yOff = (std::size(gScales) - i) * (fImage->height() + kSpacer) - height; 115 } 116 117 this->draw(canvas, kSpacer, yOff, fImage->width(), height); 118 } 119 120 // Minimize horizontally 121 for (int i = 0; i < (int)std::size(gScales); ++i) { 122 int width = SkScalarFloorToInt(fImage->width() * gScales[i]); 123 124 int xOff, yOff; 125 if (i <= (int)std::size(gScales)/2) { 126 xOff = fImage->width() + 2*kSpacer; 127 yOff = kSpacer + i * (fImage->height() + kSpacer); 128 } else { 129 // Position the more highly squashed images with their less squashed counterparts 130 xOff = fImage->width() + 2*kSpacer + fImage->width() - width; 131 yOff = kSpacer + (std::size(gScales) - i - 1) * (fImage->height() + kSpacer); 132 } 133 134 this->draw(canvas, xOff, yOff, width, fImage->height()); 135 } 136 } 137 138 private: 139 inline static constexpr int kImageSize = 256; 140 inline static constexpr int kSpacer = 10; 141 inline static constexpr int kNumVertImages = 5; 142 143 sk_sp<SkImage> fImage; 144 SkSamplingOptions fSampling; 145 Mode fMode; 146 147 using INHERITED = GM; 148 }; 149 150 ////////////////////////////////////////////////////////////////////////////// 151 152 DEF_GM(return new AnisotropicGM(AnisotropicGM::Mode::kLinear);) 153 DEF_GM(return new AnisotropicGM(AnisotropicGM::Mode::kMip);) 154 DEF_GM(return new AnisotropicGM(AnisotropicGM::Mode::kAniso);) 155 156 ////////////////////////////////////////////////////////////////////////////// 157 158 class AnisoMipsGM : public GM { 159 public: 160 AnisoMipsGM() = default; 161 162 protected: getName() const163 SkString getName() const override { return SkString("anisomips"); } 164 getISize()165 SkISize getISize() override { return SkISize::Make(520, 260); } 166 updateImage(SkSurface * surf,SkColor color)167 sk_sp<SkImage> updateImage(SkSurface* surf, SkColor color) { 168 surf->getCanvas()->clear(color); 169 SkPaint paint; 170 paint.setColor(~color | 0xFF000000); 171 surf->getCanvas()->drawRect(SkRect::MakeLTRB(surf->width() *2/5.f, 172 surf->height()*2/5.f, 173 surf->width() *3/5.f, 174 surf->height()*3/5.f), 175 paint); 176 return surf->makeImageSnapshot()->withDefaultMipmaps(); 177 } 178 onDraw(SkCanvas * canvas)179 void onDraw(SkCanvas* canvas) override { 180 auto ct = canvas->imageInfo().colorType() == kUnknown_SkColorType 181 ? kRGBA_8888_SkColorType 182 : canvas->imageInfo().colorType(); 183 auto ii = SkImageInfo::Make(kImageSize, 184 kImageSize, 185 ct, 186 kPremul_SkAlphaType, 187 canvas->imageInfo().refColorSpace()); 188 // In GPU mode we want a surface that is created with mipmaps to ensure that we exercise the 189 // case where the SkSurface and SkImage share a texture. If the surface texture isn't 190 // created with MIPs then asking for a mipmapped image will cause a copy to a mipped 191 // texture. 192 sk_sp<SkSurface> surface; 193 if (auto rc = canvas->recordingContext()) { 194 surface = SkSurfaces::RenderTarget(rc, 195 skgpu::Budgeted::kYes, 196 ii, 197 /* sampleCount= */ 1, 198 kTopLeft_GrSurfaceOrigin, 199 /*surfaceProps=*/nullptr, 200 /*shouldCreateWithMips=*/true); 201 if (!surface) { 202 // We could be in an abandoned context situation. 203 return; 204 } 205 } else { 206 surface = canvas->makeSurface(ii); 207 if (!surface) { // could be a recording canvas. 208 surface = SkSurfaces::Raster(ii); 209 } 210 } 211 212 static constexpr float kScales[] = {1.f, 0.5f, 0.25f, 0.125f}; 213 SkColor kColors[] = {0xFFF0F0F0, SK_ColorBLUE, SK_ColorGREEN, SK_ColorRED}; 214 static const SkSamplingOptions kSampling = SkSamplingOptions::Aniso(16); 215 216 for (bool shader : {false, true}) { 217 int c = 0; 218 canvas->save(); 219 for (float sy : kScales) { 220 canvas->save(); 221 for (float sx : kScales) { 222 canvas->save(); 223 canvas->scale(sx, sy); 224 auto image = this->updateImage(surface.get(), kColors[c]); 225 if (shader) { 226 SkPaint paint; 227 paint.setShader(image->makeShader(kSampling)); 228 canvas->drawRect(SkRect::Make(image->dimensions()), paint); 229 } else { 230 canvas->drawImage(image, 0, 0, kSampling); 231 } 232 canvas->restore(); 233 canvas->translate(ii.width() * sx + kPad, 0); 234 c = (c + 1) % std::size(kColors); 235 } 236 canvas->restore(); 237 canvas->translate(0, ii.width() * sy + kPad); 238 } 239 canvas->restore(); 240 for (float sx : kScales) { 241 canvas->translate(ii.width() * sx + kPad, 0); 242 } 243 } 244 } 245 246 private: 247 inline static constexpr int kImageSize = 128; 248 inline static constexpr int kPad = 5; 249 250 using INHERITED = GM; 251 }; 252 253 ////////////////////////////////////////////////////////////////////////////// 254 255 DEF_GM(return new AnisoMipsGM();) 256 257 } // namespace skiagm 258