| /* | |
| * Copyright 2012 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "gm/gm.h" | |
| #include "include/core/SkBitmap.h" | |
| #include "include/core/SkCanvas.h" | |
| #include "include/core/SkColor.h" | |
| #include "include/core/SkFont.h" | |
| #include "include/core/SkImage.h" | |
| #include "include/core/SkImageFilter.h" | |
| #include "include/core/SkImageInfo.h" | |
| #include "include/core/SkPaint.h" | |
| #include "include/core/SkPixelRef.h" | |
| #include "include/core/SkRRect.h" | |
| #include "include/core/SkRect.h" | |
| #include "include/core/SkRefCnt.h" | |
| #include "include/core/SkScalar.h" | |
| #include "include/core/SkTypeface.h" | |
| #include "include/effects/SkImageFilters.h" | |
| #include "src/core/SkRandom.h" | |
| #include "tools/ToolUtils.h" | |
| #include "tools/fonts/FontToolUtils.h" | |
| #include "tools/timer/TimeUtils.h" | |
| #include <utility> | |
| #define WIDTH 500 | |
| #define HEIGHT 500 | |
| static void draw_content(SkCanvas* canvas, float maxTextSize, int count) { | |
| const char* str = "The quick brown fox jumped over the lazy dog."; | |
| SkRandom rand; | |
| SkFont font = ToolUtils::DefaultPortableFont(); | |
| for (int i = 0; i < count; ++i) { | |
| int x = rand.nextULessThan(WIDTH); | |
| int y = rand.nextULessThan(HEIGHT); | |
| SkPaint paint; | |
| paint.setColor(ToolUtils::color_to_565(rand.nextBits(24) | 0xFF000000)); | |
| font.setSize(rand.nextRangeScalar(0, maxTextSize)); | |
| canvas->drawString(str, SkIntToScalar(x), SkIntToScalar(y), font, paint); | |
| } | |
| } | |
| DEF_SIMPLE_GM_BG(imagemagnifier, canvas, WIDTH, HEIGHT, SK_ColorBLACK) { | |
| SkPaint filterPaint; | |
| filterPaint.setImageFilter( | |
| SkImageFilters::Magnifier(SkRect::MakeWH(WIDTH, HEIGHT), 2.f, 100.f, | |
| SkFilterMode::kLinear, nullptr)); | |
| canvas->saveLayer(nullptr, &filterPaint); | |
| draw_content(canvas, 300.f, 25); | |
| canvas->restore(); | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| #define WIDTH_HEIGHT 256 | |
| static sk_sp<SkImage> make_img() { | |
| SkBitmap bitmap; | |
| bitmap.allocN32Pixels(WIDTH_HEIGHT, WIDTH_HEIGHT); | |
| SkCanvas canvas(bitmap); | |
| canvas.clear(0x0); | |
| SkPaint paint; | |
| paint.setColor(SK_ColorBLUE); | |
| for (float pos = 0; pos < WIDTH_HEIGHT; pos += 16) { | |
| canvas.drawLine(0, pos, SkIntToScalar(WIDTH_HEIGHT), pos, paint); | |
| canvas.drawLine(pos, 0, pos, SkIntToScalar(WIDTH_HEIGHT), paint); | |
| } | |
| SkBitmap result; | |
| result.setInfo(SkImageInfo::MakeS32(WIDTH_HEIGHT, WIDTH_HEIGHT, kPremul_SkAlphaType)); | |
| result.setPixelRef(sk_ref_sp(bitmap.pixelRef()), 0, 0); | |
| return result.asImage(); | |
| } | |
| DEF_SIMPLE_GM_BG(imagemagnifier_cropped, canvas, WIDTH_HEIGHT, WIDTH_HEIGHT, SK_ColorBLACK) { | |
| sk_sp<SkImageFilter> imageSource(SkImageFilters::Image(make_img(), SkFilterMode::kNearest)); | |
| // Crop out a 16 pixel ring around the result | |
| const SkIRect cropRect = SkIRect::MakeXYWH(16, 16, WIDTH_HEIGHT-32, WIDTH_HEIGHT-32); | |
| SkPaint filterPaint; | |
| filterPaint.setImageFilter(SkImageFilters::Magnifier( | |
| SkRect::MakeWH(WIDTH_HEIGHT, WIDTH_HEIGHT), | |
| WIDTH_HEIGHT / (WIDTH_HEIGHT - 96.f), 64.f, {}, | |
| std::move(imageSource), &cropRect)); | |
| canvas->saveLayer(nullptr, &filterPaint); | |
| canvas->restore(); | |
| } | |
| class ImageMagnifierBounds : public skiagm::GM { | |
| public: | |
| ImageMagnifierBounds() : fX(0.f), fY(0.f) {} | |
| protected: | |
| SkString getName() const override { return SkString("imagemagnifier_bounds"); } | |
| SkISize getISize() override { return SkISize::Make(768, 512); } | |
| bool onAnimate(double nanos) override { | |
| fX = TimeUtils::SineWave(nanos, 10.f, 0.f, -200.f, 200.f); | |
| fY = TimeUtils::SineWave(nanos, 10.f, 3.f, -200.f, 200.f); | |
| return true; | |
| } | |
| void onDraw(SkCanvas* canvas) override { | |
| this->drawRow(canvas, 16.f); // fish eye distortion | |
| canvas->translate(0.f, 256.f); | |
| this->drawRow(canvas, 0.f); // no distortion, just zoom | |
| } | |
| private: | |
| void drawRow(SkCanvas* canvas, float inset) { | |
| // Draw the magnifier two ways: backdrop filtered and then through a saveLayer with a | |
| // regular filter. Lastly draw the un-filtered input. Relevant bounds are displayed on | |
| // top of the rendering: | |
| // - black = the lens bounding box | |
| // - red = the clipped inset lens bounds | |
| // - blue = the source of the undistorted magnified content | |
| auto drawBorder = [canvas](SkRect rect, SkColor color, | |
| float width, float borderInset = 0.f) { | |
| SkPaint paint; | |
| paint.setStyle(SkPaint::kStroke_Style); | |
| paint.setStrokeWidth(width); | |
| paint.setColor(color); | |
| paint.setAntiAlias(true); | |
| // This draws the original rect (unrounded) when borderInset = 0 | |
| rect.inset(borderInset, borderInset); | |
| canvas->drawRRect(SkRRect::MakeRectXY(rect, borderInset, borderInset), paint); | |
| }; | |
| // Logically there is a 'widgetBounds' that is the region of pixels to | |
| // be filled with magnified content. Pixels inside widgetBounds are | |
| // scaled up by a factor of 'zoomAmount', with a non linear distortion | |
| // applied to pixels up to 'inset' inside 'widgetBounds'. The specific | |
| // linearly scaled region is termed the 'srcRect' and is adjusted | |
| // dynamically if parts of 'widgetBounds' are offscreen. | |
| SkRect widgetBounds = {16.f, 24.f, 220.f, 248.f}; | |
| widgetBounds.offset(fX, fY); // animating helps highlight magnifier behavior | |
| constexpr float kZoomAmount = 2.5f; | |
| // The available content for backdrops, which clips the widgetBounds as it animates. | |
| constexpr SkRect kOutBounds = {0.f, 0.f, 256.f, 256.f}; | |
| // The filter responds to any crop (explicit or from missing backdrop content). Compute | |
| // the corresponding clipped bounds and source bounds for visualization purposes. | |
| SkPoint zoomCenter = widgetBounds.center(); | |
| SkRect clippedWidget = widgetBounds; | |
| SkAssertResult(clippedWidget.intersect(kOutBounds)); | |
| zoomCenter = {SkTPin(zoomCenter.fX, clippedWidget.fLeft, clippedWidget.fRight), | |
| SkTPin(zoomCenter.fY, clippedWidget.fTop, clippedWidget.fBottom)}; | |
| zoomCenter = zoomCenter * (1.f - 1.f / kZoomAmount); | |
| SkRect srcRect = {clippedWidget.fLeft / kZoomAmount + zoomCenter.fX, | |
| clippedWidget.fTop / kZoomAmount + zoomCenter.fY, | |
| clippedWidget.fRight / kZoomAmount + zoomCenter.fX, | |
| clippedWidget.fBottom / kZoomAmount + zoomCenter.fY}; | |
| // Internally, the magnifier filter performs equivalent calculations but responds to the | |
| // canvas matrix and available input automatically. | |
| sk_sp<SkImageFilter> magnifier = | |
| SkImageFilters::Magnifier(widgetBounds, kZoomAmount, inset, | |
| SkFilterMode::kLinear, nullptr, kOutBounds); | |
| // Draw once as a backdrop filter | |
| canvas->save(); | |
| canvas->clipRect(kOutBounds); | |
| draw_content(canvas, 32.f, 350); | |
| canvas->saveLayer({nullptr, nullptr, magnifier.get(), 0}); | |
| canvas->restore(); | |
| drawBorder(widgetBounds, SK_ColorBLACK, 2.f); | |
| if (inset > 0.f) { | |
| drawBorder(clippedWidget, SK_ColorRED, 2.f, inset); | |
| } | |
| canvas->restore(); | |
| // Draw once as a regular filter | |
| canvas->save(); | |
| canvas->translate(256.f, 0.f); | |
| canvas->clipRect(kOutBounds); | |
| SkPaint paint; | |
| paint.setImageFilter(magnifier); | |
| canvas->saveLayer(nullptr, &paint); | |
| draw_content(canvas, 32.f, 350); | |
| canvas->restore(); | |
| drawBorder(widgetBounds, SK_ColorBLACK, 2.f); | |
| if (inset > 0.f) { | |
| drawBorder(clippedWidget, SK_ColorRED, 2.f, inset); | |
| } | |
| canvas->restore(); | |
| // Draw once unfiltered | |
| canvas->save(); | |
| canvas->translate(512.f, 0.f); | |
| canvas->clipRect(kOutBounds); | |
| draw_content(canvas, 32.f, 350); | |
| drawBorder(widgetBounds, SK_ColorBLACK, 2.f); | |
| drawBorder(srcRect, SK_ColorBLUE, 2.f, inset / kZoomAmount); | |
| canvas->restore(); | |
| } | |
| private: | |
| SkScalar fX; | |
| SkScalar fY; | |
| }; | |
| DEF_GM(return new ImageMagnifierBounds(); ) |