/* * 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; text.setAntiAlias(true); float y = kLineHeight; text.setColor(SK_ColorBLACK); y = print_size(canvas, "Orig layer", origLayerBounds, kLineInset, y, font, text); text.setColor(SK_ColorRED); y = print_size(canvas, "Filter layer", localLayerBounds, kLineInset, y, font, text); text.setColor(SK_ColorBLUE); y = print_size(canvas, "Filter input", filterInputBounds, kLineInset, y, font, text); text.setColor(SK_ColorMAGENTA); y = print_size(canvas, "Backdrop size", devLayerBounds, kLineInset, y, font, text); return y; } static SkPaint line_paint(SkScalar width, SkColor color) { SkPaint paint; paint.setColor(color); paint.setStrokeWidth(width); paint.setStyle(SkPaint::kStroke_Style); paint.setAntiAlias(true); return paint; } class BackdropBoundsSample : public Sample { public: 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"); return; } // 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)); canvas->save(); // The layer bounds of the content, this is the size of the actual layer and does not // reflect the backdrop specific decomposition. canvas->setMatrix(SkMatrix::I()); 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 canvas->setMatrix(toGlobal); 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 canvas->setMatrix(SkMatrix::I()); 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->setMatrix(toGlobal); canvas->drawPath(backdropCoveringBounds, line_paint(0.f, SK_ColorGREEN)); canvas->resetMatrix(); print_info(canvas, origLayerBounds, layerBounds, filterInputBounds, devLayerBounds); canvas->restore(); } SkString name() override { return SkString("BackdropBounds"); } private: typedef Sample INHERITED; }; DEF_SAMPLE(return new BackdropBoundsSample();)