/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "SkBlendMode.h" #include "TestSceneBase.h" #include "tests/common/BitmapAllocationTestUtils.h" #include "hwui/Paint.h" class TvApp; class TvAppNoRoundedCorner; class TvAppColorFilter; class TvAppNoRoundedCornerColorFilter; static bool _TvApp(BitmapAllocationTestUtils::registerBitmapAllocationScene<TvApp>( "tvapp", "A dense grid of cards:" "with rounded corner, using overlay RenderNode for dimming.")); static bool _TvAppNoRoundedCorner( BitmapAllocationTestUtils::registerBitmapAllocationScene<TvAppNoRoundedCorner>( "tvapp_norc", "A dense grid of cards:" "no rounded corner, using overlay RenderNode for dimming")); static bool _TvAppColorFilter( BitmapAllocationTestUtils::registerBitmapAllocationScene<TvAppColorFilter>( "tvapp_cf", "A dense grid of cards:" "with rounded corner, using ColorFilter for dimming")); static bool _TvAppNoRoundedCornerColorFilter( BitmapAllocationTestUtils::registerBitmapAllocationScene<TvAppNoRoundedCornerColorFilter>( "tvapp_norc_cf", "A dense grid of cards:" "no rounded corner, using ColorFilter for dimming")); class TvApp : public TestScene { public: explicit TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator) : TestScene(), mAllocator(allocator) {} sp<RenderNode> mBg; std::vector<sp<RenderNode>> mCards; std::vector<sp<RenderNode>> mInfoAreas; std::vector<sp<RenderNode>> mImages; std::vector<sp<RenderNode>> mOverlays; std::vector<sk_sp<Bitmap>> mCachedBitmaps; BitmapAllocationTestUtils::BitmapAllocator mAllocator; sk_sp<Bitmap> mSingleBitmap; int mSeed = 0; int mSeed2 = 0; void createContent(int width, int height, Canvas& canvas) override { mBg = createBitmapNode(canvas, 0xFF9C27B0, 0, 0, width, height); canvas.drawRenderNode(mBg.get()); canvas.insertReorderBarrier(true); mSingleBitmap = mAllocator(dp(160), dp(120), kRGBA_8888_SkColorType, [](SkBitmap& skBitmap) { skBitmap.eraseColor(0xFF0000FF); }); for (int y = dp(18) - dp(178); y < height - dp(18); y += dp(178)) { bool isFirstCard = true; for (int x = dp(18); x < width - dp(18); x += dp(178)) { sp<RenderNode> card = createCard(x, y, dp(160), dp(160), isFirstCard); isFirstCard = false; canvas.drawRenderNode(card.get()); mCards.push_back(card); } } canvas.insertReorderBarrier(false); } void doFrame(int frameNr) override { size_t numCards = mCards.size(); for (size_t ci = 0; ci < numCards; ci++) { updateCard(ci, frameNr); } } private: sp<RenderNode> createBitmapNode(Canvas& canvas, SkColor color, int left, int top, int width, int height) { return TestUtils::createNode( left, top, left + width, top + height, [this, width, height, color](RenderProperties& props, Canvas& canvas) { sk_sp<Bitmap> bitmap = mAllocator(width, height, kRGBA_8888_SkColorType, [color](SkBitmap& skBitmap) { skBitmap.eraseColor(color); }); canvas.drawBitmap(*bitmap, 0, 0, nullptr); }); } sp<RenderNode> createSharedBitmapNode(Canvas& canvas, int left, int top, int width, int height, sk_sp<Bitmap> bitmap) { return TestUtils::createNode(left, top, left + width, top + height, [bitmap](RenderProperties& props, Canvas& canvas) { canvas.drawBitmap(*bitmap, 0, 0, nullptr); }); } sp<RenderNode> createInfoNode(Canvas& canvas, int left, int top, int width, int height, const char* text, const char* text2) { return TestUtils::createNode(left, top, left + width, top + height, [text, text2](RenderProperties& props, Canvas& canvas) { canvas.drawColor(0xFFFFEEEE, SkBlendMode::kSrcOver); Paint paint; paint.setAntiAlias(true); paint.getSkFont().setSize(24); paint.setColor(Color::Black); TestUtils::drawUtf8ToCanvas(&canvas, text, paint, 10, 30); paint.getSkFont().setSize(20); TestUtils::drawUtf8ToCanvas(&canvas, text2, paint, 10, 54); }); } sp<RenderNode> createColorNode(Canvas& canvas, int left, int top, int width, int height, SkColor color) { return TestUtils::createNode(left, top, left + width, top + height, [color](RenderProperties& props, Canvas& canvas) { canvas.drawColor(color, SkBlendMode::kSrcOver); }); } virtual bool useSingleBitmap() { return false; } virtual float roundedCornerRadius() { return dp(2); } // when true, use overlay RenderNode for dimming, otherwise apply a ColorFilter to dim image virtual bool useOverlay() { return true; } sp<RenderNode> createCard(int x, int y, int width, int height, bool selected) { return TestUtils::createNode(x, y, x + width, y + height, [width, height, selected, this]( RenderProperties& props, Canvas& canvas) { if (selected) { props.setElevation(dp(16)); props.setScaleX(1.2); props.setScaleY(1.2); } props.mutableOutline().setRoundRect(0, 0, width, height, roundedCornerRadius(), 1); props.mutableOutline().setShouldClip(true); sk_sp<Bitmap> bitmap = useSingleBitmap() ? mSingleBitmap : mAllocator(width, dp(120), kRGBA_8888_SkColorType, [this](SkBitmap& skBitmap) { skBitmap.eraseColor(0xFF000000 | ((mSeed << 3) & 0xFF)); }); sp<RenderNode> cardImage = createSharedBitmapNode(canvas, 0, 0, width, dp(120), bitmap); canvas.drawRenderNode(cardImage.get()); mCachedBitmaps.push_back(bitmap); mImages.push_back(cardImage); char buffer[128]; sprintf(buffer, "Video %d-%d", mSeed, mSeed + 1); mSeed++; char buffer2[128]; sprintf(buffer2, "Studio %d", mSeed2++); sp<RenderNode> infoArea = createInfoNode(canvas, 0, dp(120), width, height, buffer, buffer2); canvas.drawRenderNode(infoArea.get()); mInfoAreas.push_back(infoArea); if (useOverlay()) { sp<RenderNode> overlayColor = createColorNode(canvas, 0, 0, width, height, 0x00000000); canvas.drawRenderNode(overlayColor.get()); mOverlays.push_back(overlayColor); } }); } void updateCard(int ci, int curFrame) { // updating card's translation Y sp<RenderNode> card = mCards[ci]; card->setPropertyFieldsDirty(RenderNode::Y); card->mutateStagingProperties().setTranslationY(curFrame % 150); // re-recording card's canvas, not necessary but to add some burden to CPU std::unique_ptr<Canvas> cardcanvas(Canvas::create_recording_canvas( card->stagingProperties().getWidth(), card->stagingProperties().getHeight(), card.get())); sp<RenderNode> image = mImages[ci]; sp<RenderNode> infoArea = mInfoAreas[ci]; cardcanvas->drawRenderNode(infoArea.get()); if (useOverlay()) { cardcanvas->drawRenderNode(image.get()); // re-recording card overlay's canvas, animating overlay color alpha sp<RenderNode> overlay = mOverlays[ci]; std::unique_ptr<Canvas> canvas( Canvas::create_recording_canvas(overlay->stagingProperties().getWidth(), overlay->stagingProperties().getHeight(), overlay.get())); canvas->drawColor((curFrame % 150) << 24, SkBlendMode::kSrcOver); overlay->setStagingDisplayList(canvas->finishRecording()); cardcanvas->drawRenderNode(overlay.get()); } else { // re-recording image node's canvas, animating ColorFilter std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas( image->stagingProperties().getWidth(), image->stagingProperties().getHeight(), image.get())); SkPaint paint; sk_sp<SkColorFilter> filter( SkColorFilter::MakeModeFilter((curFrame % 150) << 24, SkBlendMode::kSrcATop)); paint.setColorFilter(filter); sk_sp<Bitmap> bitmap = mCachedBitmaps[ci]; canvas->drawBitmap(*bitmap, 0, 0, &paint); image->setStagingDisplayList(canvas->finishRecording()); cardcanvas->drawRenderNode(image.get()); } card->setStagingDisplayList(cardcanvas->finishRecording()); } }; class TvAppNoRoundedCorner : public TvApp { public: explicit TvAppNoRoundedCorner(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {} private: virtual float roundedCornerRadius() override { return dp(0); } }; class TvAppColorFilter : public TvApp { public: explicit TvAppColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {} private: virtual bool useOverlay() override { return false; } }; class TvAppNoRoundedCornerColorFilter : public TvApp { public: explicit TvAppNoRoundedCornerColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {} private: virtual float roundedCornerRadius() override { return dp(0); } virtual bool useOverlay() override { return false; } };