1 /*
2  * Copyright (C) 2020 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 package com.android.systemui.screenshot;
17 
18 import android.annotation.Nullable;
19 import android.graphics.Canvas;
20 import android.graphics.ColorFilter;
21 import android.graphics.PixelFormat;
22 import android.graphics.Rect;
23 import android.graphics.RenderNode;
24 import android.graphics.drawable.Drawable;
25 import android.util.Log;
26 
27 /**
28  * Draws a set of hardware image tiles from a display list. The tiles exist in virtual coordinate
29  * space that may extend into positive or negative values. The origin is the upper-left-most corner
30  * of bounding box, which is drawn at 0,0 to the drawable (output) bounds.
31  */
32 public class TiledImageDrawable extends Drawable {
33 
34     private static final String TAG = "TiledImageDrawable";
35 
36     private final ImageTileSet mTiles;
37     private RenderNode mNode;
38 
TiledImageDrawable(ImageTileSet tiles)39     public TiledImageDrawable(ImageTileSet tiles) {
40         mTiles = tiles;
41         mTiles.addOnContentChangedListener(this::onContentChanged);
42     }
43 
44 
onContentChanged()45     private void onContentChanged() {
46         if (mNode != null && mNode.hasDisplayList()) {
47             mNode.discardDisplayList();
48         }
49         invalidateSelf();
50     }
51 
rebuildDisplayListIfNeeded()52     private void rebuildDisplayListIfNeeded() {
53         if (mNode != null && mNode.hasDisplayList()) {
54             return;
55         }
56         if (mNode == null) {
57             mNode = new RenderNode("TiledImageDrawable");
58         }
59         mNode.setPosition(0, 0, mTiles.getWidth(), mTiles.getHeight());
60         Canvas canvas = mNode.beginRecording();
61         // Align content (virtual) top/left with 0,0, within the render node
62         canvas.translate(-mTiles.getLeft(), -mTiles.getTop());
63         for (int i = 0; i < mTiles.size(); i++) {
64             ImageTile tile = mTiles.get(i);
65             canvas.save();
66             canvas.translate(tile.getLeft(), tile.getTop());
67             canvas.drawRenderNode(tile.getDisplayList());
68             canvas.restore();
69         }
70         mNode.endRecording();
71     }
72 
73     /**
74      * Draws the tiled image to the canvas, with the top/left (virtual) coordinate aligned to 0,0
75      * placed at left/top of the drawable's bounds.
76      */
77     @Override
draw(Canvas canvas)78     public void draw(Canvas canvas) {
79         rebuildDisplayListIfNeeded();
80         if (canvas.isHardwareAccelerated()) {
81             Rect bounds = getBounds();
82             canvas.save();
83             canvas.clipRect(0, 0, bounds.width(), bounds.height());
84             canvas.translate(-bounds.left, -bounds.top);
85             canvas.drawRenderNode(mNode);
86             canvas.restore();
87         } else {
88             Log.d(TAG, "Canvas is not hardware accelerated. Skipping draw!");
89         }
90     }
91 
92     @Override
getIntrinsicWidth()93     public int getIntrinsicWidth() {
94         return mTiles.getWidth();
95     }
96 
97     @Override
getIntrinsicHeight()98     public int getIntrinsicHeight() {
99         return mTiles.getHeight();
100     }
101 
102     @Override
setAlpha(int alpha)103     public void setAlpha(int alpha) {
104         if (mNode.setAlpha(alpha / 255f)) {
105             invalidateSelf();
106         }
107     }
108 
109     @Override
setColorFilter(@ullable ColorFilter colorFilter)110     public void setColorFilter(@Nullable ColorFilter colorFilter) {
111         throw new IllegalArgumentException("not implemented");
112     }
113 
114     @Override
getOpacity()115     public int getOpacity() {
116         return PixelFormat.OPAQUE;
117     }
118 }
119