blob: fe6ff2f9a021b994888a99089b64affac0801056 [file] [log] [blame]
/*
* 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/base/SkRandom.h"
#include "tools/ToolUtils.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::create_portable_typeface());
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,
SkSamplingOptions{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()));
// 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 onShortName() override { return SkString("imagemagnifier_bounds"); }
SkISize onISize() 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,
SkSamplingOptions{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(); )