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.h" 9 10#include "Resources.h" 11#include "SampleCode.h" 12#include "SkAnimTimer.h" 13#include "SkCanvas.h" 14#include "SkInterpolator.h" 15#include "SkGradientShader.h" 16#include "SkData.h" 17#include "SkPath.h" 18#include "SkSurface.h" 19#include "SkRandom.h" 20#include "SkTime.h" 21 22static sk_sp<SkSurface> make_surface(SkCanvas* canvas, const SkImageInfo& info) { 23 auto surface = canvas->makeSurface(info); 24 if (!surface) { 25 surface = SkSurface::MakeRaster(info); 26 } 27 return surface; 28} 29 30static sk_sp<SkShader> make_shader(const SkRect& bounds) { 31 sk_sp<SkImage> image(GetResourceAsImage("images/mandrill_128.png")); 32 return image ? image->makeShader() : nullptr; 33} 34 35#define N 128 36#define ANGLE_DELTA 3 37#define SCALE_DELTA (SK_Scalar1 / 32) 38 39static sk_sp<SkImage> make_image() { 40 SkImageInfo info = SkImageInfo::MakeN32(N, N, kOpaque_SkAlphaType); 41 auto surface(SkSurface::MakeRaster(info)); 42 SkCanvas* canvas = surface->getCanvas(); 43 canvas->drawColor(SK_ColorWHITE); 44 45 SkPath path; 46 path.setFillType(SkPath::kEvenOdd_FillType); 47 48 path.addRect(SkRect::MakeWH(N/2, N)); 49 path.addRect(SkRect::MakeWH(N, N/2)); 50 path.moveTo(0, 0); path.lineTo(N, 0); path.lineTo(0, N); path.close(); 51 52 SkPaint paint; 53 paint.setShader(make_shader(SkRect::MakeWH(N, N))); 54 55 canvas->drawPath(path, paint); 56 return surface->makeImageSnapshot(); 57} 58 59static sk_sp<SkImage> zoom_up(SkSurface* origSurf, SkImage* orig) { 60 const SkScalar S = 16; // amount to scale up 61 const int D = 2; // dimension scaling for the offscreen 62 // since we only view the center, don't need to produce the entire thing 63 64 SkImageInfo info = SkImageInfo::MakeN32(orig->width() * D, orig->height() * D, 65 kOpaque_SkAlphaType); 66 auto surface(origSurf->makeSurface(info)); 67 SkCanvas* canvas = surface->getCanvas(); 68 canvas->drawColor(SK_ColorWHITE); 69 canvas->scale(S, S); 70 canvas->translate(-SkScalarHalf(orig->width()) * (S - D) / S, 71 -SkScalarHalf(orig->height()) * (S - D) / S); 72 canvas->drawImage(orig, 0, 0, nullptr); 73 74 if (S > 3) { 75 SkPaint paint; 76 paint.setColor(SK_ColorWHITE); 77 for (int i = 1; i < orig->height(); ++i) { 78 SkScalar y = SkIntToScalar(i); 79 canvas->drawLine(0, y, SkIntToScalar(orig->width()), y, paint); 80 } 81 for (int i = 1; i < orig->width(); ++i) { 82 SkScalar x = SkIntToScalar(i); 83 canvas->drawLine(x, 0, x, SkIntToScalar(orig->height()), paint); 84 } 85 } 86 return surface->makeImageSnapshot(); 87} 88 89struct AnimValue { 90 SkScalar fValue; 91 SkScalar fMin; 92 SkScalar fMax; 93 SkScalar fMod; 94 95 operator SkScalar() const { return fValue; } 96 97 void set(SkScalar value, SkScalar min, SkScalar max) { 98 fValue = value; 99 fMin = min; 100 fMax = max; 101 fMod = 0; 102 } 103 104 void setMod(SkScalar value, SkScalar mod) { 105 fValue = value; 106 fMin = 0; 107 fMax = 0; 108 fMod = mod; 109 } 110 111 SkScalar inc(SkScalar delta) { 112 fValue += delta; 113 return this->fixUp(); 114 } 115 116 SkScalar fixUp() { 117 if (fMod) { 118 fValue = SkScalarMod(fValue, fMod); 119 } else { 120 if (fValue > fMax) { 121 fValue = fMax; 122 } else if (fValue < fMin) { 123 fValue = fMin; 124 } 125 } 126 return fValue; 127 } 128}; 129 130static void draw_box_frame(SkCanvas* canvas, int width, int height) { 131 SkPaint p; 132 p.setStyle(SkPaint::kStroke_Style); 133 p.setColor(SK_ColorRED); 134 SkRect r = SkRect::MakeIWH(width, height); 135 r.inset(0.5f, 0.5f); 136 canvas->drawRect(r, p); 137 canvas->drawLine(r.left(), r.top(), r.right(), r.bottom(), p); 138 canvas->drawLine(r.left(), r.bottom(), r.right(), r.top(), p); 139} 140 141class FilterQualityView : public SampleView { 142 sk_sp<SkImage> fImage; 143 AnimValue fScale, fAngle; 144 SkSize fCell; 145 SkInterpolator fTrans; 146 SkMSec fCurrTime; 147 bool fShowFatBits; 148 149public: 150 FilterQualityView() : fTrans(2, 2), fShowFatBits(true) { 151 fCell.set(256, 256); 152 153 fScale.set(1, SK_Scalar1 / 8, 1); 154 fAngle.setMod(0, 360); 155 156 SkScalar values[2]; 157 fTrans.setMirror(true); 158 fTrans.setReset(true); 159 160 fCurrTime = 0; 161 162 fTrans.setRepeatCount(999); 163 values[0] = values[1] = 0; 164 fTrans.setKeyFrame(0, fCurrTime, values); 165 values[0] = values[1] = 1; 166 fTrans.setKeyFrame(1, fCurrTime + 2000, values); 167 } 168 169protected: 170 bool onQuery(SkEvent* evt) override { 171 if (SampleCode::TitleQ(*evt)) { 172 SampleCode::TitleR(evt, "FilterQuality"); 173 return true; 174 } 175 SkUnichar uni; 176 if (SampleCode::CharQ(*evt, &uni)) { 177 switch (uni) { 178 case '1': fAngle.inc(-ANGLE_DELTA); return true; 179 case '2': fAngle.inc( ANGLE_DELTA); return true; 180 case '3': fScale.inc(-SCALE_DELTA); return true; 181 case '4': fScale.inc( SCALE_DELTA); return true; 182 case '5': fShowFatBits = !fShowFatBits; return true; 183 default: break; 184 } 185 } 186 return this->INHERITED::onQuery(evt); 187 } 188 189 void drawTheImage(SkCanvas* canvas, const SkISize& size, SkFilterQuality filter, 190 SkScalar dx, SkScalar dy) { 191 SkPaint paint; 192 paint.setAntiAlias(true); 193 paint.setFilterQuality(filter); 194 195 SkAutoCanvasRestore acr(canvas, true); 196 197 canvas->translate(dx, dy); 198 199 canvas->translate(SkScalarHalf(size.width()), SkScalarHalf(size.height())); 200 canvas->scale(fScale, fScale); 201 canvas->rotate(fAngle); 202 canvas->drawImage(fImage.get(), -SkScalarHalf(fImage->width()), -SkScalarHalf(fImage->height()), 203 &paint); 204 205 if (false) { 206 acr.restore(); 207 draw_box_frame(canvas, size.width(), size.height()); 208 } 209 } 210 211 void drawHere(SkCanvas* canvas, SkFilterQuality filter, SkScalar dx, SkScalar dy) { 212 SkCanvas* origCanvas = canvas; 213 SkAutoCanvasRestore acr(canvas, true); 214 215 SkISize size = SkISize::Make(fImage->width(), fImage->height()); 216 217 sk_sp<SkSurface> surface; 218 if (fShowFatBits) { 219 // scale up so we don't clip rotations 220 SkImageInfo info = SkImageInfo::MakeN32(fImage->width() * 2, fImage->height() * 2, 221 kOpaque_SkAlphaType); 222 surface = make_surface(canvas, info); 223 canvas = surface->getCanvas(); 224 canvas->drawColor(SK_ColorWHITE); 225 size.set(info.width(), info.height()); 226 } else { 227 canvas->translate(SkScalarHalf(fCell.width() - fImage->width()), 228 SkScalarHalf(fCell.height() - fImage->height())); 229 } 230 this->drawTheImage(canvas, size, filter, dx, dy); 231 232 if (surface) { 233 sk_sp<SkImage> orig(surface->makeImageSnapshot()); 234 sk_sp<SkImage> zoomed(zoom_up(surface.get(), orig.get())); 235 origCanvas->drawImage(zoomed.get(), 236 SkScalarHalf(fCell.width() - zoomed->width()), 237 SkScalarHalf(fCell.height() - zoomed->height())); 238 } 239 } 240 241 void drawBorders(SkCanvas* canvas) { 242 SkPaint p; 243 p.setStyle(SkPaint::kStroke_Style); 244 p.setColor(SK_ColorBLUE); 245 246 SkRect r = SkRect::MakeWH(fCell.width() * 2, fCell.height() * 2); 247 r.inset(SK_ScalarHalf, SK_ScalarHalf); 248 canvas->drawRect(r, p); 249 canvas->drawLine(r.left(), r.centerY(), r.right(), r.centerY(), p); 250 canvas->drawLine(r.centerX(), r.top(), r.centerX(), r.bottom(), p); 251 } 252 253 void onOnceBeforeDraw() override { 254 fImage = make_image(); 255 } 256 257 void onDrawContent(SkCanvas* canvas) override { 258 fCell.set(this->height() / 2, this->height() / 2); 259 260 SkScalar trans[2]; 261 fTrans.timeToValues(fCurrTime, trans); 262 263 for (int y = 0; y < 2; ++y) { 264 for (int x = 0; x < 2; ++x) { 265 int index = y * 2 + x; 266 SkAutoCanvasRestore acr(canvas, true); 267 canvas->translate(fCell.width() * x, fCell.height() * y); 268 SkRect r = SkRect::MakeWH(fCell.width(), fCell.height()); 269 r.inset(4, 4); 270 canvas->clipRect(r); 271 this->drawHere(canvas, SkFilterQuality(index), trans[0], trans[1]); 272 } 273 } 274 275 this->drawBorders(canvas); 276 277 const SkScalar textX = fCell.width() * 2 + 30; 278 279 SkPaint paint; 280 paint.setAntiAlias(true); 281 paint.setTextSize(36); 282 SkString str; 283 str.appendScalar(fScale); 284 canvas->drawString(str, textX, 100, paint); 285 str.reset(); str.appendScalar(fAngle); 286 canvas->drawString(str, textX, 150, paint); 287 288 str.reset(); str.appendScalar(trans[0]); 289 canvas->drawString(str, textX, 200, paint); 290 str.reset(); str.appendScalar(trans[1]); 291 canvas->drawString(str, textX, 250, paint); 292 } 293 294 bool onAnimate(const SkAnimTimer& timer) override { 295 fCurrTime = timer.msec(); 296 return true; 297 } 298 299private: 300 typedef SampleView INHERITED; 301}; 302 303////////////////////////////////////////////////////////////////////////////// 304 305static SkView* MyFactory() { return new FilterQualityView; } 306static SkViewRegister reg(MyFactory); 307