| /* |
| * 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::MakeScale(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();) |