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