• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <SkFont.h>
18 #include <cstdio>
19 #include "TestSceneBase.h"
20 #include "hwui/Paint.h"
21 #include "tests/common/TestUtils.h"
22 
23 class StretchyListViewAnimation;
24 class StretchyListViewHolePunch;
25 class StretchyUniformListView;
26 class StretchyUniformListViewHolePunch;
27 class StretchyUniformLayerListView;
28 class StretchyUniformLayerListViewHolePunch;
29 
30 static TestScene::Registrar _StretchyListViewAnimation(TestScene::Info{
31         "stretchylistview",
32         "A mock ListView of scrolling content that's stretching. Doesn't re-bind/re-record views "
33         "as they are recycled, so won't upload much content (either glyphs, or bitmaps).",
34         TestScene::simpleCreateScene<StretchyListViewAnimation>});
35 
36 static TestScene::Registrar _StretchyListViewHolePunch(TestScene::Info{
37         "stretchylistview_holepunch",
38         "A mock ListView of scrolling content that's stretching. Includes a hole punch",
39         TestScene::simpleCreateScene<StretchyListViewHolePunch>});
40 
41 static TestScene::Registrar _StretchyUniformListView(TestScene::Info{
42         "stretchylistview_uniform",
43         "A mock ListView of scrolling content that's stretching using a uniform stretch effect.",
44         TestScene::simpleCreateScene<StretchyUniformListView>});
45 
46 static TestScene::Registrar _StretchyUniformListViewHolePunch(TestScene::Info{
47         "stretchylistview_uniform_holepunch",
48         "A mock ListView of scrolling content that's stretching using a uniform stretch effect. "
49         "Includes a hole punch",
50         TestScene::simpleCreateScene<StretchyUniformListViewHolePunch>});
51 
52 static TestScene::Registrar _StretchyUniformLayerListView(TestScene::Info{
53         "stretchylistview_uniform_layer",
54         "A mock ListView of scrolling content that's stretching using a uniform stretch effect. "
55         "Uses a layer",
56         TestScene::simpleCreateScene<StretchyUniformLayerListView>});
57 
58 static TestScene::Registrar _StretchyUniformLayerListViewHolePunch(TestScene::Info{
59         "stretchylistview_uniform_layer_holepunch",
60         "A mock ListView of scrolling content that's stretching using a uniform stretch effect. "
61         "Uses a layer & includes a hole punch",
62         TestScene::simpleCreateScene<StretchyUniformLayerListViewHolePunch>});
63 
64 class StretchyListViewAnimation : public TestScene {
65 protected:
stretchBehavior()66     virtual StretchEffectBehavior stretchBehavior() { return StretchEffectBehavior::Shader; }
haveHolePunch()67     virtual bool haveHolePunch() { return false; }
forceLayer()68     virtual bool forceLayer() { return false; }
69 
70 private:
71     int mItemHeight;
72     int mItemSpacing;
73     int mItemWidth;
74     int mItemLeft;
75     sp<RenderNode> mListView;
76     std::vector<sp<RenderNode> > mListItems;
77 
createRandomCharIcon(int cardHeight)78     sk_sp<Bitmap> createRandomCharIcon(int cardHeight) {
79         SkBitmap skBitmap;
80         int size = cardHeight - (dp(10) * 2);
81         sk_sp<Bitmap> bitmap(TestUtils::createBitmap(size, size, &skBitmap));
82         SkCanvas canvas(skBitmap);
83         canvas.clear(0);
84 
85         SkPaint paint;
86         paint.setAntiAlias(true);
87         SkColor randomColor = BrightColors[rand() % BrightColorsCount];
88         paint.setColor(randomColor);
89         canvas.drawCircle(size / 2, size / 2, size / 2, paint);
90 
91         bool bgDark =
92                 SkColorGetR(randomColor) + SkColorGetG(randomColor) + SkColorGetB(randomColor) <
93                 128 * 3;
94         paint.setColor(bgDark ? Color::White : Color::Grey_700);
95 
96         SkFont font;
97         font.setSize(size / 2);
98         char charToShow = 'A' + (rand() % 26);
99         const SkPoint pos = {SkIntToScalar(size / 2),
100                              /*approximate centering*/ SkFloatToScalar(size * 0.7f)};
101         canvas.drawSimpleText(&charToShow, 1, SkTextEncoding::kUTF8, pos.fX, pos.fY, font, paint);
102         return bitmap;
103     }
104 
createBoxBitmap(bool filled)105     static sk_sp<Bitmap> createBoxBitmap(bool filled) {
106         int size = dp(20);
107         int stroke = dp(2);
108         SkBitmap skBitmap;
109         auto bitmap = TestUtils::createBitmap(size, size, &skBitmap);
110         SkCanvas canvas(skBitmap);
111         canvas.clear(Color::Transparent);
112 
113         SkPaint paint;
114         paint.setAntiAlias(true);
115         paint.setColor(filled ? Color::Yellow_500 : Color::Grey_700);
116         paint.setStyle(filled ? SkPaint::kStrokeAndFill_Style : SkPaint::kStroke_Style);
117         paint.setStrokeWidth(stroke);
118         canvas.drawRect(SkRect::MakeLTRB(stroke, stroke, size - stroke, size - stroke), paint);
119         return bitmap;
120     }
121 
createListItem(RenderProperties & props,Canvas & canvas,int cardId,int itemWidth,int itemHeight)122     void createListItem(RenderProperties& props, Canvas& canvas, int cardId, int itemWidth,
123                         int itemHeight) {
124         static sk_sp<Bitmap> filledBox(createBoxBitmap(true));
125         static sk_sp<Bitmap> strokedBox(createBoxBitmap(false));
126         const bool addHolePunch = cardId == 2 && haveHolePunch();
127         // TODO: switch to using round rect clipping, once merging correctly handles that
128         Paint roundRectPaint;
129         roundRectPaint.setAntiAlias(true);
130         roundRectPaint.setColor(Color::White);
131         if (addHolePunch) {
132             // Punch a hole but then cover it up, we don't want to actually see it
133             canvas.punchHole(SkRRect::MakeRect(SkRect::MakeWH(itemWidth, itemHeight)));
134         }
135         canvas.drawRoundRect(0, 0, itemWidth, itemHeight, dp(6), dp(6), roundRectPaint);
136 
137         Paint textPaint;
138         textPaint.setColor(rand() % 2 ? Color::Black : Color::Grey_500);
139         textPaint.getSkFont().setSize(dp(20));
140         textPaint.setAntiAlias(true);
141         char buf[256];
142         snprintf(buf, sizeof(buf), "This card is #%d", cardId);
143         TestUtils::drawUtf8ToCanvas(&canvas, buf, textPaint, itemHeight, dp(25));
144         textPaint.getSkFont().setSize(dp(15));
145         if (addHolePunch) {
146             TestUtils::drawUtf8ToCanvas(&canvas, "I have a hole punch", textPaint, itemHeight,
147                                         dp(45));
148         } else {
149             TestUtils::drawUtf8ToCanvas(&canvas, "This is some more text on the card", textPaint,
150                                         itemHeight, dp(45));
151         }
152 
153         auto randomIcon = createRandomCharIcon(itemHeight);
154         canvas.drawBitmap(*randomIcon, dp(10), dp(10), nullptr);
155 
156         auto box = rand() % 2 ? filledBox : strokedBox;
157         canvas.drawBitmap(*box, itemWidth - dp(10) - box->width(), dp(10), nullptr);
158     }
159 
createContent(int width,int height,Canvas & canvas)160     void createContent(int width, int height, Canvas& canvas) override {
161         srand(0);
162         mItemHeight = dp(60);
163         mItemSpacing = dp(16);
164         mItemWidth = std::min((height - mItemSpacing * 2), (int)dp(300));
165         mItemLeft = (width - mItemWidth) / 2;
166         int heightWithSpacing = mItemHeight + mItemSpacing;
167         for (int y = 0; y < height + (heightWithSpacing - 1); y += heightWithSpacing) {
168             int id = mListItems.size();
169             auto node = TestUtils::createNode(mItemLeft, y, mItemLeft + mItemWidth, y + mItemHeight,
170                                               [this, id](RenderProperties& props, Canvas& canvas) {
171                                                   createListItem(props, canvas, id, mItemWidth,
172                                                                  mItemHeight);
173                                               });
174             mListItems.push_back(node);
175         }
176         mListView = TestUtils::createNode(0, 0, width, height,
177                                           [this](RenderProperties& props, Canvas& canvas) {
178                                               for (size_t ci = 0; ci < mListItems.size(); ci++) {
179                                                   canvas.drawRenderNode(mListItems[ci].get());
180                                               }
181                                           });
182 
183         canvas.drawColor(Color::Grey_500, SkBlendMode::kSrcOver);
184         canvas.drawRenderNode(mListView.get());
185     }
186 
doFrame(int frameNr)187     void doFrame(int frameNr) override {
188         if (frameNr == 0) {
189             Properties::setStretchEffectBehavior(stretchBehavior());
190             if (forceLayer()) {
191                 mListView->mutateStagingProperties().mutateLayerProperties().setType(
192                         LayerType::RenderLayer);
193             }
194         }
195         auto& props = mListView->mutateStagingProperties();
196         auto& stretch = props.mutateLayerProperties().mutableStretchEffect();
197         stretch.setEmpty();
198         frameNr = frameNr % 150;
199         // Animate from 0f to .1f
200         const float sY = (frameNr > 75 ? 150 - frameNr : frameNr) / 1500.f;
201         stretch.mergeWith({{.fX = 0, .fY = sY},
202                            static_cast<float>(props.getWidth()),
203                            static_cast<float>(props.getHeight())});
204         mListView->setPropertyFieldsDirty(RenderNode::GENERIC);
205     }
206 };
207 
208 class StretchyListViewHolePunch : public StretchyListViewAnimation {
haveHolePunch()209     bool haveHolePunch() override { return true; }
210 };
211 
212 class StretchyUniformListView : public StretchyListViewAnimation {
stretchBehavior()213     StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::UniformScale; }
214 };
215 
216 class StretchyUniformListViewHolePunch : public StretchyListViewAnimation {
stretchBehavior()217     StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::UniformScale; }
haveHolePunch()218     bool haveHolePunch() override { return true; }
219 };
220 
221 class StretchyUniformLayerListView : public StretchyListViewAnimation {
stretchBehavior()222     StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::UniformScale; }
forceLayer()223     bool forceLayer() override { return true; }
224 };
225 
226 class StretchyUniformLayerListViewHolePunch : public StretchyListViewAnimation {
stretchBehavior()227     StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::UniformScale; }
haveHolePunch()228     bool haveHolePunch() override { return true; }
forceLayer()229     bool forceLayer() override { return true; }
230 };