Michael Ludwig | 6d1c0d4 | 2019-09-04 17:39:42 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2019 Google LLC |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #include "samplecode/Sample.h" |
| 9 | |
| 10 | #include "include/core/SkCanvas.h" |
| 11 | #include "include/core/SkColor.h" |
| 12 | #include "include/core/SkFont.h" |
| 13 | #include "include/core/SkPaint.h" |
| 14 | #include "include/core/SkPath.h" |
| 15 | #include "include/core/SkPoint.h" |
| 16 | #include "include/core/SkRect.h" |
| 17 | |
| 18 | #include "tools/ToolUtils.h" |
| 19 | |
Michael Ludwig | 6d1c0d4 | 2019-09-04 17:39:42 -0400 | [diff] [blame] | 20 | static constexpr float kLineHeight = 16.f; |
| 21 | static constexpr float kLineInset = 8.f; |
| 22 | |
| 23 | static float print_size(SkCanvas* canvas, const char* prefix, const SkIRect& rect, |
Michael Ludwig | b6e8922 | 2019-09-05 13:10:21 -0400 | [diff] [blame] | 24 | float x, float y, const SkFont& font, const SkPaint& paint) { |
Michael Ludwig | 6d1c0d4 | 2019-09-04 17:39:42 -0400 | [diff] [blame] | 25 | canvas->drawString(prefix, x, y, font, paint); |
| 26 | y += kLineHeight; |
| 27 | SkString sz; |
| 28 | sz.appendf("%d x %d", rect.width(), rect.height()); |
| 29 | canvas->drawString(sz, x, y, font, paint); |
| 30 | return y + kLineHeight; |
| 31 | } |
| 32 | |
Michael Ludwig | b6e8922 | 2019-09-05 13:10:21 -0400 | [diff] [blame] | 33 | static float print_info(SkCanvas* canvas, const SkIRect& origLayerBounds, |
| 34 | const SkIRect& localLayerBounds, const SkIRect& filterInputBounds, |
| 35 | const SkIRect& devLayerBounds) { |
Michael Ludwig | 6d1c0d4 | 2019-09-04 17:39:42 -0400 | [diff] [blame] | 36 | SkFont font(nullptr, 12); |
| 37 | SkPaint text; |
| 38 | text.setAntiAlias(true); |
| 39 | |
| 40 | float y = kLineHeight; |
| 41 | |
| 42 | text.setColor(SK_ColorBLACK); |
| 43 | y = print_size(canvas, "Orig layer", origLayerBounds, kLineInset, y, font, text); |
| 44 | text.setColor(SK_ColorRED); |
| 45 | y = print_size(canvas, "Filter layer", localLayerBounds, kLineInset, y, font, text); |
| 46 | text.setColor(SK_ColorBLUE); |
| 47 | y = print_size(canvas, "Filter input", filterInputBounds, kLineInset, y, font, text); |
| 48 | text.setColor(SK_ColorMAGENTA); |
| 49 | y = print_size(canvas, "Backdrop size", devLayerBounds, kLineInset, y, font, text); |
| 50 | |
| 51 | return y; |
| 52 | } |
| 53 | |
| 54 | static SkPaint line_paint(SkScalar width, SkColor color) { |
| 55 | SkPaint paint; |
| 56 | paint.setColor(color); |
| 57 | paint.setStrokeWidth(width); |
| 58 | paint.setStyle(SkPaint::kStroke_Style); |
| 59 | paint.setAntiAlias(true); |
| 60 | return paint; |
| 61 | } |
| 62 | |
| 63 | class BackdropBoundsSample : public Sample { |
| 64 | public: |
| 65 | BackdropBoundsSample() {} |
| 66 | |
| 67 | void onDrawContent(SkCanvas* canvas) override { |
| 68 | SkMatrix ctm = canvas->getTotalMatrix(); |
| 69 | |
| 70 | // This decomposition is for the backdrop filtering, and does not represent the CTM that |
| 71 | // the layer actually uses (unless it also has a filter during restore). |
| 72 | SkMatrix toGlobal, layerMatrix; |
| 73 | SkSize scale; |
| 74 | if (ctm.isScaleTranslate()) { |
| 75 | // No decomposition needed |
| 76 | toGlobal = SkMatrix::I(); |
| 77 | layerMatrix = ctm; |
| 78 | } else if (ctm.decomposeScale(&scale, &toGlobal)) { |
Mike Reed | 1f60733 | 2020-05-21 12:11:27 -0400 | [diff] [blame] | 79 | layerMatrix = SkMatrix::Scale(scale.fWidth, scale.fHeight); |
Michael Ludwig | 6d1c0d4 | 2019-09-04 17:39:42 -0400 | [diff] [blame] | 80 | } else { |
| 81 | toGlobal = ctm; |
| 82 | layerMatrix = SkMatrix::I(); |
| 83 | } |
| 84 | |
| 85 | SkMatrix fromGlobal; |
| 86 | if (!toGlobal.invert(&fromGlobal)) { |
| 87 | SkDebugf("Unable to invert CTM\n"); |
| 88 | return; |
| 89 | } |
| 90 | |
| 91 | // The local content, e.g. what would be submitted to drawRect |
| 92 | const SkRect localContentRect = SkRect::MakeLTRB(45.5f, 23.123f, 150.f, 140.45f); |
| 93 | canvas->drawRect(localContentRect, line_paint(0.f, SK_ColorBLACK)); |
| 94 | |
| 95 | canvas->save(); |
| 96 | // The layer bounds of the content, this is the size of the actual layer and does not |
| 97 | // reflect the backdrop specific decomposition. |
| 98 | canvas->setMatrix(SkMatrix::I()); |
| 99 | SkIRect origLayerBounds = ctm.mapRect(localContentRect).roundOut(); |
| 100 | canvas->drawRect(SkRect::Make(origLayerBounds), line_paint(1.f, SK_ColorBLACK)); |
| 101 | |
| 102 | // Have to undo the full CTM transform on the layer bounds to get the layer bounds |
| 103 | // for the specific backdrop filter decomposition |
| 104 | canvas->setMatrix(toGlobal); |
| 105 | SkIRect layerBounds = fromGlobal.mapRect(SkRect::Make(origLayerBounds)).roundOut(); |
| 106 | canvas->drawRect(SkRect::Make(layerBounds), line_paint(0.5f, SK_ColorRED)); |
| 107 | |
| 108 | // Input bounds for the backdrop filter to cover the actual layer bounds (emulate some |
| 109 | // blur that must outset by 5px for reading on the edge). |
| 110 | SkIRect filterInputBounds = layerBounds; |
| 111 | filterInputBounds.outset(5, 5); |
| 112 | canvas->drawRect(SkRect::Make(filterInputBounds), line_paint(1.f, SK_ColorBLUE)); |
| 113 | |
Michael Ludwig | 6d1c0d4 | 2019-09-04 17:39:42 -0400 | [diff] [blame] | 114 | // The destination bounds that must be snapped in order to transform and fill the |
| 115 | // filterInputBounds |
| 116 | canvas->setMatrix(SkMatrix::I()); |
| 117 | SkIRect devLayerBounds = toGlobal.mapRect(SkRect::Make(filterInputBounds)).roundOut(); |
| 118 | canvas->drawRect(SkRect::Make(devLayerBounds), line_paint(2.f, SK_ColorMAGENTA)); |
| 119 | |
| 120 | // The destination bounds mapped back into the layer space, which should cover 'layerBounds' |
| 121 | SkPath backdropCoveringBounds; |
| 122 | |
| 123 | // Add axis lines, to show perspective distortion |
| 124 | SkIRect local = fromGlobal.mapRect(SkRect::Make(devLayerBounds)).roundOut(); |
| 125 | static int kAxisSpace = 10; |
| 126 | for (int y = local.fTop + kAxisSpace; y <= local.fBottom - kAxisSpace; y += kAxisSpace) { |
| 127 | backdropCoveringBounds.moveTo(local.fLeft, y); |
| 128 | backdropCoveringBounds.lineTo(local.fRight, y); |
| 129 | } |
| 130 | for (int x = local.fLeft + kAxisSpace; x <= local.fRight - kAxisSpace; x += kAxisSpace) { |
| 131 | backdropCoveringBounds.moveTo(x, local.fTop); |
| 132 | backdropCoveringBounds.lineTo(x, local.fBottom); |
| 133 | } |
| 134 | |
| 135 | canvas->setMatrix(toGlobal); |
| 136 | canvas->drawPath(backdropCoveringBounds, line_paint(0.f, SK_ColorGREEN)); |
| 137 | |
| 138 | canvas->resetMatrix(); |
| 139 | print_info(canvas, origLayerBounds, layerBounds, filterInputBounds, devLayerBounds); |
| 140 | |
| 141 | canvas->restore(); |
| 142 | } |
| 143 | |
| 144 | SkString name() override { return SkString("BackdropBounds"); } |
| 145 | |
| 146 | private: |
| 147 | |
| 148 | typedef Sample INHERITED; |
| 149 | }; |
| 150 | |
| 151 | DEF_SAMPLE(return new BackdropBoundsSample();) |