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 
20 static constexpr float kLineHeight = 16.f;
21 static constexpr float kLineInset = 8.f;
22 
print_size(SkCanvas * canvas,const char * prefix,const SkIRect & rect,float x,float y,const SkFont & font,const SkPaint & paint)23 static float print_size(SkCanvas* canvas, const char* prefix, const SkIRect& rect,
24                         float x, float y, const SkFont& font, const SkPaint& paint) {
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 
print_info(SkCanvas * canvas,const SkIRect & origLayerBounds,const SkIRect & localLayerBounds,const SkIRect & filterInputBounds,const SkIRect & devLayerBounds)33 static float print_info(SkCanvas* canvas, const SkIRect& origLayerBounds,
34                         const SkIRect& localLayerBounds, const SkIRect& filterInputBounds,
35                         const SkIRect& devLayerBounds) {
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 
line_paint(SkScalar width,SkColor color)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:
BackdropBoundsSample()65     BackdropBoundsSample() {}
66 
onDrawContent(SkCanvas * canvas)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)) {
79             layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight);
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 
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 
name()144     SkString name() override { return SkString("BackdropBounds"); }
145 
146 private:
147 
148     typedef Sample INHERITED;
149 };
150 
151 DEF_SAMPLE(return new BackdropBoundsSample();)
152