blob: 35e05c704bd0025248de462b76c80537411a5b68 [file] [log] [blame]
bsalomon@google.com82aa7482012-08-13 14:22:17 +00001/*
2 * Copyright 2012 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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "gm/gm.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -04009#include "include/core/SkBitmap.h"
10#include "include/core/SkCanvas.h"
11#include "include/core/SkColor.h"
12#include "include/core/SkFont.h"
13#include "include/core/SkImage.h"
14#include "include/core/SkImageFilter.h"
15#include "include/core/SkImageInfo.h"
16#include "include/core/SkPaint.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "include/core/SkPixelRef.h"
Michael Ludwig872787e2023-05-12 10:31:07 -040018#include "include/core/SkRRect.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040019#include "include/core/SkRect.h"
20#include "include/core/SkRefCnt.h"
21#include "include/core/SkScalar.h"
22#include "include/core/SkTypeface.h"
Michael Ludwig898bbfa2019-08-02 15:21:23 -040023#include "include/effects/SkImageFilters.h"
Kevin Lubick0d4d1142023-02-13 09:13:10 -050024#include "src/base/SkRandom.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "tools/ToolUtils.h"
Kevin Lubicke836c3a2023-10-20 06:55:35 -040026#include "tools/fonts/FontToolUtils.h"
Michael Ludwig8d1f9212023-05-03 11:44:20 -040027#include "tools/timer/TimeUtils.h"
bsalomon@google.com82aa7482012-08-13 14:22:17 +000028
Ben Wagner7fde8e12019-05-01 17:28:53 -040029#include <utility>
30
bsalomon@google.com82aa7482012-08-13 14:22:17 +000031#define WIDTH 500
32#define HEIGHT 500
33
Michael Ludwig8d1f9212023-05-03 11:44:20 -040034static void draw_content(SkCanvas* canvas, float maxTextSize, int count) {
35 const char* str = "The quick brown fox jumped over the lazy dog.";
36 SkRandom rand;
Kevin Lubicke836c3a2023-10-20 06:55:35 -040037 SkFont font = ToolUtils::DefaultPortableFont();
Michael Ludwig8d1f9212023-05-03 11:44:20 -040038 for (int i = 0; i < count; ++i) {
39 int x = rand.nextULessThan(WIDTH);
40 int y = rand.nextULessThan(HEIGHT);
41 SkPaint paint;
42 paint.setColor(ToolUtils::color_to_565(rand.nextBits(24) | 0xFF000000));
43 font.setSize(rand.nextRangeScalar(0, maxTextSize));
44 canvas->drawString(str, SkIntToScalar(x), SkIntToScalar(y), font, paint);
45 }
46}
47
halcanary2a243382015-09-09 08:16:41 -070048DEF_SIMPLE_GM_BG(imagemagnifier, canvas, WIDTH, HEIGHT, SK_ColorBLACK) {
senorblancocbf6b6e2014-10-23 15:00:10 -070049 SkPaint filterPaint;
50 filterPaint.setImageFilter(
Michael Ludwig0dc9e8d2023-05-02 16:23:56 -040051 SkImageFilters::Magnifier(SkRect::MakeWH(WIDTH, HEIGHT), 2.f, 100.f,
Michael Ludwig2c49e602023-05-30 13:12:19 -040052 SkFilterMode::kLinear, nullptr));
halcanary96fcdcc2015-08-27 07:41:13 -070053 canvas->saveLayer(nullptr, &filterPaint);
Michael Ludwig8d1f9212023-05-03 11:44:20 -040054 draw_content(canvas, 300.f, 25);
bsalomon@google.com82aa7482012-08-13 14:22:17 +000055 canvas->restore();
bsalomon@google.com82aa7482012-08-13 14:22:17 +000056}
robertphillipsb2a4dc62016-04-14 07:54:04 -070057
58////////////////////////////////////////////////////////////////////////////////
59#define WIDTH_HEIGHT 256
60
61static sk_sp<SkImage> make_img() {
Brian Osmanb44bb312016-12-20 15:54:11 -050062 SkBitmap bitmap;
63 bitmap.allocN32Pixels(WIDTH_HEIGHT, WIDTH_HEIGHT);
64 SkCanvas canvas(bitmap);
robertphillipsb2a4dc62016-04-14 07:54:04 -070065
Brian Osmanb44bb312016-12-20 15:54:11 -050066 canvas.clear(0x0);
robertphillipsb2a4dc62016-04-14 07:54:04 -070067
68 SkPaint paint;
69 paint.setColor(SK_ColorBLUE);
70
71 for (float pos = 0; pos < WIDTH_HEIGHT; pos += 16) {
Brian Osmanb44bb312016-12-20 15:54:11 -050072 canvas.drawLine(0, pos, SkIntToScalar(WIDTH_HEIGHT), pos, paint);
73 canvas.drawLine(pos, 0, pos, SkIntToScalar(WIDTH_HEIGHT), paint);
robertphillipsb2a4dc62016-04-14 07:54:04 -070074 }
75
Brian Osmanb44bb312016-12-20 15:54:11 -050076 SkBitmap result;
77 result.setInfo(SkImageInfo::MakeS32(WIDTH_HEIGHT, WIDTH_HEIGHT, kPremul_SkAlphaType));
78 result.setPixelRef(sk_ref_sp(bitmap.pixelRef()), 0, 0);
79
Mike Reedac9f0c92020-12-23 10:11:33 -050080 return result.asImage();
robertphillipsb2a4dc62016-04-14 07:54:04 -070081}
82
83DEF_SIMPLE_GM_BG(imagemagnifier_cropped, canvas, WIDTH_HEIGHT, WIDTH_HEIGHT, SK_ColorBLACK) {
Michael Ludwig642f2182023-05-30 20:57:39 -040084 sk_sp<SkImageFilter> imageSource(SkImageFilters::Image(make_img(), SkFilterMode::kNearest));
robertphillipsb2a4dc62016-04-14 07:54:04 -070085
86 // Crop out a 16 pixel ring around the result
Michael Ludwig898bbfa2019-08-02 15:21:23 -040087 const SkIRect cropRect = SkIRect::MakeXYWH(16, 16, WIDTH_HEIGHT-32, WIDTH_HEIGHT-32);
robertphillipsb2a4dc62016-04-14 07:54:04 -070088
89 SkPaint filterPaint;
Michael Ludwig898bbfa2019-08-02 15:21:23 -040090 filterPaint.setImageFilter(SkImageFilters::Magnifier(
Michael Ludwig0dc9e8d2023-05-02 16:23:56 -040091 SkRect::MakeWH(WIDTH_HEIGHT, WIDTH_HEIGHT),
92 WIDTH_HEIGHT / (WIDTH_HEIGHT - 96.f), 64.f, {},
93 std::move(imageSource), &cropRect));
robertphillipsb2a4dc62016-04-14 07:54:04 -070094
95 canvas->saveLayer(nullptr, &filterPaint);
96 canvas->restore();
97}
Michael Ludwig8d1f9212023-05-03 11:44:20 -040098
99class ImageMagnifierBounds : public skiagm::GM {
100public:
101 ImageMagnifierBounds() : fX(0.f), fY(0.f) {}
102
103protected:
Leandro Lovisolo24fa2112023-08-15 19:05:17 +0000104 SkString getName() const override { return SkString("imagemagnifier_bounds"); }
Leandro Lovisolo8f023882023-08-15 21:13:52 +0000105 SkISize getISize() override { return SkISize::Make(768, 512); }
Michael Ludwig8d1f9212023-05-03 11:44:20 -0400106
107 bool onAnimate(double nanos) override {
Michael Ludwig872787e2023-05-12 10:31:07 -0400108 fX = TimeUtils::SineWave(nanos, 10.f, 0.f, -200.f, 200.f);
109 fY = TimeUtils::SineWave(nanos, 10.f, 3.f, -200.f, 200.f);
Michael Ludwig8d1f9212023-05-03 11:44:20 -0400110 return true;
111 }
112
113 void onDraw(SkCanvas* canvas) override {
Michael Ludwig872787e2023-05-12 10:31:07 -0400114 this->drawRow(canvas, 16.f); // fish eye distortion
115 canvas->translate(0.f, 256.f);
116 this->drawRow(canvas, 0.f); // no distortion, just zoom
117 }
118
119private:
120
121 void drawRow(SkCanvas* canvas, float inset) {
Michael Ludwig8d1f9212023-05-03 11:44:20 -0400122 // Draw the magnifier two ways: backdrop filtered and then through a saveLayer with a
123 // regular filter. Lastly draw the un-filtered input. Relevant bounds are displayed on
124 // top of the rendering:
125 // - black = the lens bounding box
126 // - red = the clipped inset lens bounds
127 // - blue = the source of the undistorted magnified content
Michael Ludwig872787e2023-05-12 10:31:07 -0400128 auto drawBorder = [canvas](SkRect rect, SkColor color,
129 float width, float borderInset = 0.f) {
Michael Ludwig8d1f9212023-05-03 11:44:20 -0400130 SkPaint paint;
131 paint.setStyle(SkPaint::kStroke_Style);
132 paint.setStrokeWidth(width);
133 paint.setColor(color);
Michael Ludwig872787e2023-05-12 10:31:07 -0400134 paint.setAntiAlias(true);
135
136 // This draws the original rect (unrounded) when borderInset = 0
137 rect.inset(borderInset, borderInset);
138 canvas->drawRRect(SkRRect::MakeRectXY(rect, borderInset, borderInset), paint);
Michael Ludwig8d1f9212023-05-03 11:44:20 -0400139 };
140
141 // Logically there is a 'widgetBounds' that is the region of pixels to
142 // be filled with magnified content. Pixels inside widgetBounds are
143 // scaled up by a factor of 'zoomAmount', with a non linear distortion
144 // applied to pixels up to 'inset' inside 'widgetBounds'. The specific
145 // linearly scaled region is termed the 'srcRect' and is adjusted
146 // dynamically if parts of 'widgetBounds' are offscreen.
147 SkRect widgetBounds = {16.f, 24.f, 220.f, 248.f};
148 widgetBounds.offset(fX, fY); // animating helps highlight magnifier behavior
149
Michael Ludwig8d1f9212023-05-03 11:44:20 -0400150 constexpr float kZoomAmount = 2.5f;
151
152 // The available content for backdrops, which clips the widgetBounds as it animates.
153 constexpr SkRect kOutBounds = {0.f, 0.f, 256.f, 256.f};
154
Michael Ludwig094e3482023-05-03 13:58:13 -0400155 // The filter responds to any crop (explicit or from missing backdrop content). Compute
156 // the corresponding clipped bounds and source bounds for visualization purposes.
157 SkPoint zoomCenter = widgetBounds.center();
Michael Ludwig8d1f9212023-05-03 11:44:20 -0400158 SkRect clippedWidget = widgetBounds;
159 SkAssertResult(clippedWidget.intersect(kOutBounds));
Michael Ludwig094e3482023-05-03 13:58:13 -0400160 zoomCenter = {SkTPin(zoomCenter.fX, clippedWidget.fLeft, clippedWidget.fRight),
161 SkTPin(zoomCenter.fY, clippedWidget.fTop, clippedWidget.fBottom)};
162 zoomCenter = zoomCenter * (1.f - 1.f / kZoomAmount);
163 SkRect srcRect = {clippedWidget.fLeft / kZoomAmount + zoomCenter.fX,
164 clippedWidget.fTop / kZoomAmount + zoomCenter.fY,
165 clippedWidget.fRight / kZoomAmount + zoomCenter.fX,
166 clippedWidget.fBottom / kZoomAmount + zoomCenter.fY};
Michael Ludwig8d1f9212023-05-03 11:44:20 -0400167
Michael Ludwig094e3482023-05-03 13:58:13 -0400168 // Internally, the magnifier filter performs equivalent calculations but responds to the
169 // canvas matrix and available input automatically.
Michael Ludwig8d1f9212023-05-03 11:44:20 -0400170 sk_sp<SkImageFilter> magnifier =
Michael Ludwig872787e2023-05-12 10:31:07 -0400171 SkImageFilters::Magnifier(widgetBounds, kZoomAmount, inset,
Michael Ludwig2c49e602023-05-30 13:12:19 -0400172 SkFilterMode::kLinear, nullptr, kOutBounds);
Michael Ludwig8d1f9212023-05-03 11:44:20 -0400173
174 // Draw once as a backdrop filter
175 canvas->save();
176 canvas->clipRect(kOutBounds);
177 draw_content(canvas, 32.f, 350);
Michael Ludwig094e3482023-05-03 13:58:13 -0400178 canvas->saveLayer({nullptr, nullptr, magnifier.get(), 0});
Michael Ludwig8d1f9212023-05-03 11:44:20 -0400179 canvas->restore();
180
Michael Ludwig872787e2023-05-12 10:31:07 -0400181 drawBorder(widgetBounds, SK_ColorBLACK, 2.f);
182 if (inset > 0.f) {
183 drawBorder(clippedWidget, SK_ColorRED, 2.f, inset);
184 }
Michael Ludwig8d1f9212023-05-03 11:44:20 -0400185 canvas->restore();
186
187 // Draw once as a regular filter
188 canvas->save();
189 canvas->translate(256.f, 0.f);
190 canvas->clipRect(kOutBounds);
191
Michael Ludwig094e3482023-05-03 13:58:13 -0400192 SkPaint paint;
193 paint.setImageFilter(magnifier);
194 canvas->saveLayer(nullptr, &paint);
195 draw_content(canvas, 32.f, 350);
Michael Ludwig8d1f9212023-05-03 11:44:20 -0400196 canvas->restore();
197
Michael Ludwig872787e2023-05-12 10:31:07 -0400198 drawBorder(widgetBounds, SK_ColorBLACK, 2.f);
199 if (inset > 0.f) {
200 drawBorder(clippedWidget, SK_ColorRED, 2.f, inset);
201 }
Michael Ludwig8d1f9212023-05-03 11:44:20 -0400202 canvas->restore();
203
204 // Draw once unfiltered
205 canvas->save();
206 canvas->translate(512.f, 0.f);
207 canvas->clipRect(kOutBounds);
208 draw_content(canvas, 32.f, 350);
209
Michael Ludwig872787e2023-05-12 10:31:07 -0400210 drawBorder(widgetBounds, SK_ColorBLACK, 2.f);
211 drawBorder(srcRect, SK_ColorBLUE, 2.f, inset / kZoomAmount);
Michael Ludwig8d1f9212023-05-03 11:44:20 -0400212 canvas->restore();
213 }
214
215private:
216 SkScalar fX;
217 SkScalar fY;
218};
219
220DEF_GM(return new ImageMagnifierBounds(); )