1 /*
2  * Copyright (C) 2017 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 "SkBitmap.h"
18 #include "SkBlendMode.h"
19 #include "SkColorFilter.h"
20 #include "SkFont.h"
21 #include "SkImageInfo.h"
22 #include "SkRefCnt.h"
23 #include "TestSceneBase.h"
24 #include "tests/common/BitmapAllocationTestUtils.h"
25 #include "hwui/Paint.h"
26 
27 class TvApp;
28 class TvAppNoRoundedCorner;
29 class TvAppColorFilter;
30 class TvAppNoRoundedCornerColorFilter;
31 
32 static bool _TvApp(BitmapAllocationTestUtils::registerBitmapAllocationScene<TvApp>(
33         "tvapp",
34         "A dense grid of cards:"
35         "with rounded corner, using overlay RenderNode for dimming."));
36 
37 static bool _TvAppNoRoundedCorner(
38         BitmapAllocationTestUtils::registerBitmapAllocationScene<TvAppNoRoundedCorner>(
39                 "tvapp_norc",
40                 "A dense grid of cards:"
41                 "no rounded corner, using overlay RenderNode for dimming"));
42 
43 static bool _TvAppColorFilter(
44         BitmapAllocationTestUtils::registerBitmapAllocationScene<TvAppColorFilter>(
45                 "tvapp_cf",
46                 "A dense grid of cards:"
47                 "with rounded corner, using ColorFilter for dimming"));
48 
49 static bool _TvAppNoRoundedCornerColorFilter(
50         BitmapAllocationTestUtils::registerBitmapAllocationScene<TvAppNoRoundedCornerColorFilter>(
51                 "tvapp_norc_cf",
52                 "A dense grid of cards:"
53                 "no rounded corner, using ColorFilter for dimming"));
54 
55 class TvApp : public TestScene {
56 public:
TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator)57     explicit TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator)
58             : TestScene(), mAllocator(allocator) {}
59 
60     sp<RenderNode> mBg;
61     std::vector<sp<RenderNode>> mCards;
62     std::vector<sp<RenderNode>> mInfoAreas;
63     std::vector<sp<RenderNode>> mImages;
64     std::vector<sp<RenderNode>> mOverlays;
65     std::vector<sk_sp<Bitmap>> mCachedBitmaps;
66     BitmapAllocationTestUtils::BitmapAllocator mAllocator;
67     sk_sp<Bitmap> mSingleBitmap;
68     int mSeed = 0;
69     int mSeed2 = 0;
70 
createContent(int width,int height,Canvas & canvas)71     void createContent(int width, int height, Canvas& canvas) override {
72         mBg = createBitmapNode(canvas, 0xFF9C27B0, 0, 0, width, height);
73         canvas.drawRenderNode(mBg.get());
74 
75         canvas.enableZ(true);
76         mSingleBitmap = mAllocator(dp(160), dp(120), kRGBA_8888_SkColorType,
77                                    [](SkBitmap& skBitmap) { skBitmap.eraseColor(0xFF0000FF); });
78 
79         for (int y = dp(18) - dp(178); y < height - dp(18); y += dp(178)) {
80             bool isFirstCard = true;
81             for (int x = dp(18); x < width - dp(18); x += dp(178)) {
82                 sp<RenderNode> card = createCard(x, y, dp(160), dp(160), isFirstCard);
83                 isFirstCard = false;
84                 canvas.drawRenderNode(card.get());
85                 mCards.push_back(card);
86             }
87         }
88         canvas.enableZ(false);
89     }
90 
doFrame(int frameNr)91     void doFrame(int frameNr) override {
92         size_t numCards = mCards.size();
93         for (size_t ci = 0; ci < numCards; ci++) {
94             updateCard(ci, frameNr);
95         }
96     }
97 
98 private:
createBitmapNode(Canvas & canvas,SkColor color,int left,int top,int width,int height)99     sp<RenderNode> createBitmapNode(Canvas& canvas, SkColor color, int left, int top, int width,
100                                     int height) {
101         return TestUtils::createNode(
102                 left, top, left + width, top + height,
103                 [this, width, height, color](RenderProperties& props, Canvas& canvas) {
104                     sk_sp<Bitmap> bitmap =
105                             mAllocator(width, height, kRGBA_8888_SkColorType,
106                                        [color](SkBitmap& skBitmap) { skBitmap.eraseColor(color); });
107                     canvas.drawBitmap(*bitmap, 0, 0, nullptr);
108                 });
109     }
110 
createSharedBitmapNode(Canvas & canvas,int left,int top,int width,int height,sk_sp<Bitmap> bitmap)111     sp<RenderNode> createSharedBitmapNode(Canvas& canvas, int left, int top, int width, int height,
112                                           sk_sp<Bitmap> bitmap) {
113         return TestUtils::createNode(left, top, left + width, top + height,
114                                      [bitmap](RenderProperties& props, Canvas& canvas) {
115                                          canvas.drawBitmap(*bitmap, 0, 0, nullptr);
116                                      });
117     }
118 
createInfoNode(Canvas & canvas,int left,int top,int width,int height,const char * text,const char * text2)119     sp<RenderNode> createInfoNode(Canvas& canvas, int left, int top, int width, int height,
120                                   const char* text, const char* text2) {
121         return TestUtils::createNode(left, top, left + width, top + height,
122                                      [text, text2](RenderProperties& props, Canvas& canvas) {
123                                          canvas.drawColor(0xFFFFEEEE, SkBlendMode::kSrcOver);
124 
125                                          Paint paint;
126                                          paint.setAntiAlias(true);
127                                          paint.getSkFont().setSize(24);
128 
129                                          paint.setColor(Color::Black);
130                                          TestUtils::drawUtf8ToCanvas(&canvas, text, paint, 10, 30);
131                                          paint.getSkFont().setSize(20);
132                                          TestUtils::drawUtf8ToCanvas(&canvas, text2, paint, 10, 54);
133 
134                                      });
135     }
136 
createColorNode(Canvas & canvas,int left,int top,int width,int height,SkColor color)137     sp<RenderNode> createColorNode(Canvas& canvas, int left, int top, int width, int height,
138                                    SkColor color) {
139         return TestUtils::createNode(left, top, left + width, top + height,
140                                      [color](RenderProperties& props, Canvas& canvas) {
141                                          canvas.drawColor(color, SkBlendMode::kSrcOver);
142                                      });
143     }
144 
useSingleBitmap()145     virtual bool useSingleBitmap() { return false; }
146 
roundedCornerRadius()147     virtual float roundedCornerRadius() { return dp(2); }
148 
149     // when true, use overlay RenderNode for dimming, otherwise apply a ColorFilter to dim image
useOverlay()150     virtual bool useOverlay() { return true; }
151 
createCard(int x,int y,int width,int height,bool selected)152     sp<RenderNode> createCard(int x, int y, int width, int height, bool selected) {
153         return TestUtils::createNode(x, y, x + width, y + height, [width, height, selected, this](
154                                                                           RenderProperties& props,
155                                                                           Canvas& canvas) {
156             if (selected) {
157                 props.setElevation(dp(16));
158                 props.setScaleX(1.2);
159                 props.setScaleY(1.2);
160             }
161             props.mutableOutline().setRoundRect(0, 0, width, height, roundedCornerRadius(), 1);
162             props.mutableOutline().setShouldClip(true);
163 
164             sk_sp<Bitmap> bitmap =
165                     useSingleBitmap() ? mSingleBitmap
166                                       : mAllocator(width, dp(120), kRGBA_8888_SkColorType,
167                                                    [this](SkBitmap& skBitmap) {
168                                                        skBitmap.eraseColor(0xFF000000 |
169                                                                            ((mSeed << 3) & 0xFF));
170                                                    });
171             sp<RenderNode> cardImage = createSharedBitmapNode(canvas, 0, 0, width, dp(120), bitmap);
172             canvas.drawRenderNode(cardImage.get());
173             mCachedBitmaps.push_back(bitmap);
174             mImages.push_back(cardImage);
175 
176             char buffer[128];
177             sprintf(buffer, "Video %d-%d", mSeed, mSeed + 1);
178             mSeed++;
179             char buffer2[128];
180             sprintf(buffer2, "Studio %d", mSeed2++);
181             sp<RenderNode> infoArea =
182                     createInfoNode(canvas, 0, dp(120), width, height, buffer, buffer2);
183             canvas.drawRenderNode(infoArea.get());
184             mInfoAreas.push_back(infoArea);
185 
186             if (useOverlay()) {
187                 sp<RenderNode> overlayColor =
188                         createColorNode(canvas, 0, 0, width, height, 0x00000000);
189                 canvas.drawRenderNode(overlayColor.get());
190                 mOverlays.push_back(overlayColor);
191             }
192         });
193     }
194 
updateCard(int ci,int curFrame)195     void updateCard(int ci, int curFrame) {
196         // updating card's translation Y
197         sp<RenderNode> card = mCards[ci];
198         card->setPropertyFieldsDirty(RenderNode::Y);
199         card->mutateStagingProperties().setTranslationY(curFrame % 150);
200 
201         // re-recording card's canvas, not necessary but to add some burden to CPU
202         std::unique_ptr<Canvas> cardcanvas(Canvas::create_recording_canvas(
203                 card->stagingProperties().getWidth(), card->stagingProperties().getHeight(),
204                 card.get()));
205         sp<RenderNode> image = mImages[ci];
206         sp<RenderNode> infoArea = mInfoAreas[ci];
207         cardcanvas->drawRenderNode(infoArea.get());
208 
209         if (useOverlay()) {
210             cardcanvas->drawRenderNode(image.get());
211             // re-recording card overlay's canvas, animating overlay color alpha
212             sp<RenderNode> overlay = mOverlays[ci];
213             std::unique_ptr<Canvas> canvas(
214                     Canvas::create_recording_canvas(overlay->stagingProperties().getWidth(),
215                                                     overlay->stagingProperties().getHeight(),
216                                                     overlay.get()));
217             canvas->drawColor((curFrame % 150) << 24, SkBlendMode::kSrcOver);
218             canvas->finishRecording(overlay.get());
219             cardcanvas->drawRenderNode(overlay.get());
220         } else {
221             // re-recording image node's canvas, animating ColorFilter
222             std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(
223                     image->stagingProperties().getWidth(), image->stagingProperties().getHeight(),
224                     image.get()));
225             Paint paint;
226             sk_sp<SkColorFilter> filter(
227                     SkColorFilters::Blend((curFrame % 150) << 24, SkBlendMode::kSrcATop));
228             paint.setColorFilter(filter);
229             sk_sp<Bitmap> bitmap = mCachedBitmaps[ci];
230             canvas->drawBitmap(*bitmap, 0, 0, &paint);
231             canvas->finishRecording(image.get());
232             cardcanvas->drawRenderNode(image.get());
233         }
234 
235         cardcanvas->finishRecording(card.get());
236     }
237 };
238 
239 class TvAppNoRoundedCorner : public TvApp {
240 public:
TvAppNoRoundedCorner(BitmapAllocationTestUtils::BitmapAllocator allocator)241     explicit TvAppNoRoundedCorner(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {}
242 
243 private:
roundedCornerRadius()244     virtual float roundedCornerRadius() override { return dp(0); }
245 };
246 
247 class TvAppColorFilter : public TvApp {
248 public:
TvAppColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator)249     explicit TvAppColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {}
250 
251 private:
useOverlay()252     virtual bool useOverlay() override { return false; }
253 };
254 
255 class TvAppNoRoundedCornerColorFilter : public TvApp {
256 public:
TvAppNoRoundedCornerColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator)257     explicit TvAppNoRoundedCornerColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator)
258             : TvApp(allocator) {}
259 
260 private:
roundedCornerRadius()261     virtual float roundedCornerRadius() override { return dp(0); }
262 
useOverlay()263     virtual bool useOverlay() override { return false; }
264 };
265