blob: 67ac714fc7c00ed198faee3f98134058ea266cd5 [file] [log] [blame]
* Copyright 2019 Google LLC
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
#include "samplecode/Sample.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkFont.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPath.h"
#include "include/core/SkPoint.h"
#include "include/core/SkRect.h"
#include "tools/ToolUtils.h"
static constexpr float kLineHeight = 16.f;
static constexpr float kLineInset = 8.f;
static float print_size(SkCanvas* canvas, const char* prefix, const SkIRect& rect,
float x, float y, const SkFont& font, const SkPaint& paint) {
canvas->drawString(prefix, x, y, font, paint);
y += kLineHeight;
SkString sz;
sz.appendf("%d x %d", rect.width(), rect.height());
canvas->drawString(sz, x, y, font, paint);
return y + kLineHeight;
static float print_info(SkCanvas* canvas, const SkIRect& origLayerBounds,
const SkIRect& localLayerBounds, const SkIRect& filterInputBounds,
const SkIRect& devLayerBounds) {
SkFont font(nullptr, 12);
SkPaint text;
float y = kLineHeight;
y = print_size(canvas, "Orig layer", origLayerBounds, kLineInset, y, font, text);
y = print_size(canvas, "Filter layer", localLayerBounds, kLineInset, y, font, text);
y = print_size(canvas, "Filter input", filterInputBounds, kLineInset, y, font, text);
y = print_size(canvas, "Backdrop size", devLayerBounds, kLineInset, y, font, text);
return y;
static SkPaint line_paint(SkScalar width, SkColor color) {
SkPaint paint;
return paint;
class BackdropBoundsSample : public Sample {
BackdropBoundsSample() {}
void onDrawContent(SkCanvas* canvas) override {
SkMatrix ctm = canvas->getTotalMatrix();
// This decomposition is for the backdrop filtering, and does not represent the CTM that
// the layer actually uses (unless it also has a filter during restore).
SkMatrix toGlobal, layerMatrix;
SkSize scale;
if (ctm.isScaleTranslate()) {
// No decomposition needed
toGlobal = SkMatrix::I();
layerMatrix = ctm;
} else if (ctm.decomposeScale(&scale, &toGlobal)) {
layerMatrix = SkMatrix::Scale(scale.fWidth, scale.fHeight);
} else {
toGlobal = ctm;
layerMatrix = SkMatrix::I();
SkMatrix fromGlobal;
if (!toGlobal.invert(&fromGlobal)) {
SkDebugf("Unable to invert CTM\n");
// The local content, e.g. what would be submitted to drawRect
const SkRect localContentRect = SkRect::MakeLTRB(45.5f, 23.123f, 150.f, 140.45f);
canvas->drawRect(localContentRect, line_paint(0.f, SK_ColorBLACK));
// The layer bounds of the content, this is the size of the actual layer and does not
// reflect the backdrop specific decomposition.
SkIRect origLayerBounds = ctm.mapRect(localContentRect).roundOut();
canvas->drawRect(SkRect::Make(origLayerBounds), line_paint(1.f, SK_ColorBLACK));
// Have to undo the full CTM transform on the layer bounds to get the layer bounds
// for the specific backdrop filter decomposition
SkIRect layerBounds = fromGlobal.mapRect(SkRect::Make(origLayerBounds)).roundOut();
canvas->drawRect(SkRect::Make(layerBounds), line_paint(0.5f, SK_ColorRED));
// Input bounds for the backdrop filter to cover the actual layer bounds (emulate some
// blur that must outset by 5px for reading on the edge).
SkIRect filterInputBounds = layerBounds;
filterInputBounds.outset(5, 5);
canvas->drawRect(SkRect::Make(filterInputBounds), line_paint(1.f, SK_ColorBLUE));
// The destination bounds that must be snapped in order to transform and fill the
// filterInputBounds
SkIRect devLayerBounds = toGlobal.mapRect(SkRect::Make(filterInputBounds)).roundOut();
canvas->drawRect(SkRect::Make(devLayerBounds), line_paint(2.f, SK_ColorMAGENTA));
// The destination bounds mapped back into the layer space, which should cover 'layerBounds'
SkPath backdropCoveringBounds;
// Add axis lines, to show perspective distortion
SkIRect local = fromGlobal.mapRect(SkRect::Make(devLayerBounds)).roundOut();
static int kAxisSpace = 10;
for (int y = local.fTop + kAxisSpace; y <= local.fBottom - kAxisSpace; y += kAxisSpace) {
backdropCoveringBounds.moveTo(local.fLeft, y);
backdropCoveringBounds.lineTo(local.fRight, y);
for (int x = local.fLeft + kAxisSpace; x <= local.fRight - kAxisSpace; x += kAxisSpace) {
backdropCoveringBounds.moveTo(x, local.fTop);
backdropCoveringBounds.lineTo(x, local.fBottom);
canvas->drawPath(backdropCoveringBounds, line_paint(0.f, SK_ColorGREEN));
print_info(canvas, origLayerBounds, layerBounds, filterInputBounds, devLayerBounds);
SkString name() override { return SkString("BackdropBounds"); }
typedef Sample INHERITED;
DEF_SAMPLE(return new BackdropBoundsSample();)