1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2012 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "gm/gm.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFont.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageFilter.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageInfo.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPixelRef.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRRect.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypeface.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/effects/SkImageFilters.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkRandom.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "tools/ToolUtils.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "tools/fonts/FontToolUtils.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "tools/timer/TimeUtils.h"
28*c8dee2aaSAndroid Build Coastguard Worker
29*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
30*c8dee2aaSAndroid Build Coastguard Worker
31*c8dee2aaSAndroid Build Coastguard Worker #define WIDTH 500
32*c8dee2aaSAndroid Build Coastguard Worker #define HEIGHT 500
33*c8dee2aaSAndroid Build Coastguard Worker
draw_content(SkCanvas * canvas,float maxTextSize,int count)34*c8dee2aaSAndroid Build Coastguard Worker static void draw_content(SkCanvas* canvas, float maxTextSize, int count) {
35*c8dee2aaSAndroid Build Coastguard Worker const char* str = "The quick brown fox jumped over the lazy dog.";
36*c8dee2aaSAndroid Build Coastguard Worker SkRandom rand;
37*c8dee2aaSAndroid Build Coastguard Worker SkFont font = ToolUtils::DefaultPortableFont();
38*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < count; ++i) {
39*c8dee2aaSAndroid Build Coastguard Worker int x = rand.nextULessThan(WIDTH);
40*c8dee2aaSAndroid Build Coastguard Worker int y = rand.nextULessThan(HEIGHT);
41*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
42*c8dee2aaSAndroid Build Coastguard Worker paint.setColor(ToolUtils::color_to_565(rand.nextBits(24) | 0xFF000000));
43*c8dee2aaSAndroid Build Coastguard Worker font.setSize(rand.nextRangeScalar(0, maxTextSize));
44*c8dee2aaSAndroid Build Coastguard Worker canvas->drawString(str, SkIntToScalar(x), SkIntToScalar(y), font, paint);
45*c8dee2aaSAndroid Build Coastguard Worker }
46*c8dee2aaSAndroid Build Coastguard Worker }
47*c8dee2aaSAndroid Build Coastguard Worker
DEF_SIMPLE_GM_BG(imagemagnifier,canvas,WIDTH,HEIGHT,SK_ColorBLACK)48*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM_BG(imagemagnifier, canvas, WIDTH, HEIGHT, SK_ColorBLACK) {
49*c8dee2aaSAndroid Build Coastguard Worker SkPaint filterPaint;
50*c8dee2aaSAndroid Build Coastguard Worker filterPaint.setImageFilter(
51*c8dee2aaSAndroid Build Coastguard Worker SkImageFilters::Magnifier(SkRect::MakeWH(WIDTH, HEIGHT), 2.f, 100.f,
52*c8dee2aaSAndroid Build Coastguard Worker SkFilterMode::kLinear, nullptr));
53*c8dee2aaSAndroid Build Coastguard Worker canvas->saveLayer(nullptr, &filterPaint);
54*c8dee2aaSAndroid Build Coastguard Worker draw_content(canvas, 300.f, 25);
55*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
56*c8dee2aaSAndroid Build Coastguard Worker }
57*c8dee2aaSAndroid Build Coastguard Worker
58*c8dee2aaSAndroid Build Coastguard Worker ////////////////////////////////////////////////////////////////////////////////
59*c8dee2aaSAndroid Build Coastguard Worker #define WIDTH_HEIGHT 256
60*c8dee2aaSAndroid Build Coastguard Worker
make_img()61*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkImage> make_img() {
62*c8dee2aaSAndroid Build Coastguard Worker SkBitmap bitmap;
63*c8dee2aaSAndroid Build Coastguard Worker bitmap.allocN32Pixels(WIDTH_HEIGHT, WIDTH_HEIGHT);
64*c8dee2aaSAndroid Build Coastguard Worker SkCanvas canvas(bitmap);
65*c8dee2aaSAndroid Build Coastguard Worker
66*c8dee2aaSAndroid Build Coastguard Worker canvas.clear(0x0);
67*c8dee2aaSAndroid Build Coastguard Worker
68*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
69*c8dee2aaSAndroid Build Coastguard Worker paint.setColor(SK_ColorBLUE);
70*c8dee2aaSAndroid Build Coastguard Worker
71*c8dee2aaSAndroid Build Coastguard Worker for (float pos = 0; pos < WIDTH_HEIGHT; pos += 16) {
72*c8dee2aaSAndroid Build Coastguard Worker canvas.drawLine(0, pos, SkIntToScalar(WIDTH_HEIGHT), pos, paint);
73*c8dee2aaSAndroid Build Coastguard Worker canvas.drawLine(pos, 0, pos, SkIntToScalar(WIDTH_HEIGHT), paint);
74*c8dee2aaSAndroid Build Coastguard Worker }
75*c8dee2aaSAndroid Build Coastguard Worker
76*c8dee2aaSAndroid Build Coastguard Worker SkBitmap result;
77*c8dee2aaSAndroid Build Coastguard Worker result.setInfo(SkImageInfo::MakeS32(WIDTH_HEIGHT, WIDTH_HEIGHT, kPremul_SkAlphaType));
78*c8dee2aaSAndroid Build Coastguard Worker result.setPixelRef(sk_ref_sp(bitmap.pixelRef()), 0, 0);
79*c8dee2aaSAndroid Build Coastguard Worker
80*c8dee2aaSAndroid Build Coastguard Worker return result.asImage();
81*c8dee2aaSAndroid Build Coastguard Worker }
82*c8dee2aaSAndroid Build Coastguard Worker
DEF_SIMPLE_GM_BG(imagemagnifier_cropped,canvas,WIDTH_HEIGHT,WIDTH_HEIGHT,SK_ColorBLACK)83*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM_BG(imagemagnifier_cropped, canvas, WIDTH_HEIGHT, WIDTH_HEIGHT, SK_ColorBLACK) {
84*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImageFilter> imageSource(SkImageFilters::Image(make_img(), SkFilterMode::kNearest));
85*c8dee2aaSAndroid Build Coastguard Worker
86*c8dee2aaSAndroid Build Coastguard Worker // Crop out a 16 pixel ring around the result
87*c8dee2aaSAndroid Build Coastguard Worker const SkIRect cropRect = SkIRect::MakeXYWH(16, 16, WIDTH_HEIGHT-32, WIDTH_HEIGHT-32);
88*c8dee2aaSAndroid Build Coastguard Worker
89*c8dee2aaSAndroid Build Coastguard Worker SkPaint filterPaint;
90*c8dee2aaSAndroid Build Coastguard Worker filterPaint.setImageFilter(SkImageFilters::Magnifier(
91*c8dee2aaSAndroid Build Coastguard Worker SkRect::MakeWH(WIDTH_HEIGHT, WIDTH_HEIGHT),
92*c8dee2aaSAndroid Build Coastguard Worker WIDTH_HEIGHT / (WIDTH_HEIGHT - 96.f), 64.f, {},
93*c8dee2aaSAndroid Build Coastguard Worker std::move(imageSource), &cropRect));
94*c8dee2aaSAndroid Build Coastguard Worker
95*c8dee2aaSAndroid Build Coastguard Worker canvas->saveLayer(nullptr, &filterPaint);
96*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
97*c8dee2aaSAndroid Build Coastguard Worker }
98*c8dee2aaSAndroid Build Coastguard Worker
99*c8dee2aaSAndroid Build Coastguard Worker class ImageMagnifierBounds : public skiagm::GM {
100*c8dee2aaSAndroid Build Coastguard Worker public:
ImageMagnifierBounds()101*c8dee2aaSAndroid Build Coastguard Worker ImageMagnifierBounds() : fX(0.f), fY(0.f) {}
102*c8dee2aaSAndroid Build Coastguard Worker
103*c8dee2aaSAndroid Build Coastguard Worker protected:
getName() const104*c8dee2aaSAndroid Build Coastguard Worker SkString getName() const override { return SkString("imagemagnifier_bounds"); }
getISize()105*c8dee2aaSAndroid Build Coastguard Worker SkISize getISize() override { return SkISize::Make(768, 512); }
106*c8dee2aaSAndroid Build Coastguard Worker
onAnimate(double nanos)107*c8dee2aaSAndroid Build Coastguard Worker bool onAnimate(double nanos) override {
108*c8dee2aaSAndroid Build Coastguard Worker fX = TimeUtils::SineWave(nanos, 10.f, 0.f, -200.f, 200.f);
109*c8dee2aaSAndroid Build Coastguard Worker fY = TimeUtils::SineWave(nanos, 10.f, 3.f, -200.f, 200.f);
110*c8dee2aaSAndroid Build Coastguard Worker return true;
111*c8dee2aaSAndroid Build Coastguard Worker }
112*c8dee2aaSAndroid Build Coastguard Worker
onDraw(SkCanvas * canvas)113*c8dee2aaSAndroid Build Coastguard Worker void onDraw(SkCanvas* canvas) override {
114*c8dee2aaSAndroid Build Coastguard Worker this->drawRow(canvas, 16.f); // fish eye distortion
115*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(0.f, 256.f);
116*c8dee2aaSAndroid Build Coastguard Worker this->drawRow(canvas, 0.f); // no distortion, just zoom
117*c8dee2aaSAndroid Build Coastguard Worker }
118*c8dee2aaSAndroid Build Coastguard Worker
119*c8dee2aaSAndroid Build Coastguard Worker private:
120*c8dee2aaSAndroid Build Coastguard Worker
drawRow(SkCanvas * canvas,float inset)121*c8dee2aaSAndroid Build Coastguard Worker void drawRow(SkCanvas* canvas, float inset) {
122*c8dee2aaSAndroid Build Coastguard Worker // Draw the magnifier two ways: backdrop filtered and then through a saveLayer with a
123*c8dee2aaSAndroid Build Coastguard Worker // regular filter. Lastly draw the un-filtered input. Relevant bounds are displayed on
124*c8dee2aaSAndroid Build Coastguard Worker // top of the rendering:
125*c8dee2aaSAndroid Build Coastguard Worker // - black = the lens bounding box
126*c8dee2aaSAndroid Build Coastguard Worker // - red = the clipped inset lens bounds
127*c8dee2aaSAndroid Build Coastguard Worker // - blue = the source of the undistorted magnified content
128*c8dee2aaSAndroid Build Coastguard Worker auto drawBorder = [canvas](SkRect rect, SkColor color,
129*c8dee2aaSAndroid Build Coastguard Worker float width, float borderInset = 0.f) {
130*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
131*c8dee2aaSAndroid Build Coastguard Worker paint.setStyle(SkPaint::kStroke_Style);
132*c8dee2aaSAndroid Build Coastguard Worker paint.setStrokeWidth(width);
133*c8dee2aaSAndroid Build Coastguard Worker paint.setColor(color);
134*c8dee2aaSAndroid Build Coastguard Worker paint.setAntiAlias(true);
135*c8dee2aaSAndroid Build Coastguard Worker
136*c8dee2aaSAndroid Build Coastguard Worker // This draws the original rect (unrounded) when borderInset = 0
137*c8dee2aaSAndroid Build Coastguard Worker rect.inset(borderInset, borderInset);
138*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRRect(SkRRect::MakeRectXY(rect, borderInset, borderInset), paint);
139*c8dee2aaSAndroid Build Coastguard Worker };
140*c8dee2aaSAndroid Build Coastguard Worker
141*c8dee2aaSAndroid Build Coastguard Worker // Logically there is a 'widgetBounds' that is the region of pixels to
142*c8dee2aaSAndroid Build Coastguard Worker // be filled with magnified content. Pixels inside widgetBounds are
143*c8dee2aaSAndroid Build Coastguard Worker // scaled up by a factor of 'zoomAmount', with a non linear distortion
144*c8dee2aaSAndroid Build Coastguard Worker // applied to pixels up to 'inset' inside 'widgetBounds'. The specific
145*c8dee2aaSAndroid Build Coastguard Worker // linearly scaled region is termed the 'srcRect' and is adjusted
146*c8dee2aaSAndroid Build Coastguard Worker // dynamically if parts of 'widgetBounds' are offscreen.
147*c8dee2aaSAndroid Build Coastguard Worker SkRect widgetBounds = {16.f, 24.f, 220.f, 248.f};
148*c8dee2aaSAndroid Build Coastguard Worker widgetBounds.offset(fX, fY); // animating helps highlight magnifier behavior
149*c8dee2aaSAndroid Build Coastguard Worker
150*c8dee2aaSAndroid Build Coastguard Worker constexpr float kZoomAmount = 2.5f;
151*c8dee2aaSAndroid Build Coastguard Worker
152*c8dee2aaSAndroid Build Coastguard Worker // The available content for backdrops, which clips the widgetBounds as it animates.
153*c8dee2aaSAndroid Build Coastguard Worker constexpr SkRect kOutBounds = {0.f, 0.f, 256.f, 256.f};
154*c8dee2aaSAndroid Build Coastguard Worker
155*c8dee2aaSAndroid Build Coastguard Worker // The filter responds to any crop (explicit or from missing backdrop content). Compute
156*c8dee2aaSAndroid Build Coastguard Worker // the corresponding clipped bounds and source bounds for visualization purposes.
157*c8dee2aaSAndroid Build Coastguard Worker SkPoint zoomCenter = widgetBounds.center();
158*c8dee2aaSAndroid Build Coastguard Worker SkRect clippedWidget = widgetBounds;
159*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(clippedWidget.intersect(kOutBounds));
160*c8dee2aaSAndroid Build Coastguard Worker zoomCenter = {SkTPin(zoomCenter.fX, clippedWidget.fLeft, clippedWidget.fRight),
161*c8dee2aaSAndroid Build Coastguard Worker SkTPin(zoomCenter.fY, clippedWidget.fTop, clippedWidget.fBottom)};
162*c8dee2aaSAndroid Build Coastguard Worker zoomCenter = zoomCenter * (1.f - 1.f / kZoomAmount);
163*c8dee2aaSAndroid Build Coastguard Worker SkRect srcRect = {clippedWidget.fLeft / kZoomAmount + zoomCenter.fX,
164*c8dee2aaSAndroid Build Coastguard Worker clippedWidget.fTop / kZoomAmount + zoomCenter.fY,
165*c8dee2aaSAndroid Build Coastguard Worker clippedWidget.fRight / kZoomAmount + zoomCenter.fX,
166*c8dee2aaSAndroid Build Coastguard Worker clippedWidget.fBottom / kZoomAmount + zoomCenter.fY};
167*c8dee2aaSAndroid Build Coastguard Worker
168*c8dee2aaSAndroid Build Coastguard Worker // Internally, the magnifier filter performs equivalent calculations but responds to the
169*c8dee2aaSAndroid Build Coastguard Worker // canvas matrix and available input automatically.
170*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImageFilter> magnifier =
171*c8dee2aaSAndroid Build Coastguard Worker SkImageFilters::Magnifier(widgetBounds, kZoomAmount, inset,
172*c8dee2aaSAndroid Build Coastguard Worker SkFilterMode::kLinear, nullptr, kOutBounds);
173*c8dee2aaSAndroid Build Coastguard Worker
174*c8dee2aaSAndroid Build Coastguard Worker // Draw once as a backdrop filter
175*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
176*c8dee2aaSAndroid Build Coastguard Worker canvas->clipRect(kOutBounds);
177*c8dee2aaSAndroid Build Coastguard Worker draw_content(canvas, 32.f, 350);
178*c8dee2aaSAndroid Build Coastguard Worker canvas->saveLayer({nullptr, nullptr, magnifier.get(), 0});
179*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
180*c8dee2aaSAndroid Build Coastguard Worker
181*c8dee2aaSAndroid Build Coastguard Worker drawBorder(widgetBounds, SK_ColorBLACK, 2.f);
182*c8dee2aaSAndroid Build Coastguard Worker if (inset > 0.f) {
183*c8dee2aaSAndroid Build Coastguard Worker drawBorder(clippedWidget, SK_ColorRED, 2.f, inset);
184*c8dee2aaSAndroid Build Coastguard Worker }
185*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
186*c8dee2aaSAndroid Build Coastguard Worker
187*c8dee2aaSAndroid Build Coastguard Worker // Draw once as a regular filter
188*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
189*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(256.f, 0.f);
190*c8dee2aaSAndroid Build Coastguard Worker canvas->clipRect(kOutBounds);
191*c8dee2aaSAndroid Build Coastguard Worker
192*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
193*c8dee2aaSAndroid Build Coastguard Worker paint.setImageFilter(magnifier);
194*c8dee2aaSAndroid Build Coastguard Worker canvas->saveLayer(nullptr, &paint);
195*c8dee2aaSAndroid Build Coastguard Worker draw_content(canvas, 32.f, 350);
196*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
197*c8dee2aaSAndroid Build Coastguard Worker
198*c8dee2aaSAndroid Build Coastguard Worker drawBorder(widgetBounds, SK_ColorBLACK, 2.f);
199*c8dee2aaSAndroid Build Coastguard Worker if (inset > 0.f) {
200*c8dee2aaSAndroid Build Coastguard Worker drawBorder(clippedWidget, SK_ColorRED, 2.f, inset);
201*c8dee2aaSAndroid Build Coastguard Worker }
202*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
203*c8dee2aaSAndroid Build Coastguard Worker
204*c8dee2aaSAndroid Build Coastguard Worker // Draw once unfiltered
205*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
206*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(512.f, 0.f);
207*c8dee2aaSAndroid Build Coastguard Worker canvas->clipRect(kOutBounds);
208*c8dee2aaSAndroid Build Coastguard Worker draw_content(canvas, 32.f, 350);
209*c8dee2aaSAndroid Build Coastguard Worker
210*c8dee2aaSAndroid Build Coastguard Worker drawBorder(widgetBounds, SK_ColorBLACK, 2.f);
211*c8dee2aaSAndroid Build Coastguard Worker drawBorder(srcRect, SK_ColorBLUE, 2.f, inset / kZoomAmount);
212*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
213*c8dee2aaSAndroid Build Coastguard Worker }
214*c8dee2aaSAndroid Build Coastguard Worker
215*c8dee2aaSAndroid Build Coastguard Worker private:
216*c8dee2aaSAndroid Build Coastguard Worker SkScalar fX;
217*c8dee2aaSAndroid Build Coastguard Worker SkScalar fY;
218*c8dee2aaSAndroid Build Coastguard Worker };
219*c8dee2aaSAndroid Build Coastguard Worker
220*c8dee2aaSAndroid Build Coastguard Worker DEF_GM(return new ImageMagnifierBounds(); )
221