blob: b458d9b1409615f5218b56a1dc81c4b419188460 [file] [log] [blame]
Skuhneea7a7fb2015-08-28 07:10:31 -07001/*
2 * Copyright (C) 2014 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
17package com.android.test.hwui;
18
19import android.app.Activity;
20import android.graphics.Canvas;
21import android.graphics.ColorFilter;
22import android.graphics.Paint;
23import android.graphics.PixelFormat;
24import android.graphics.Rect;
25import android.graphics.drawable.Drawable;
26import android.os.Bundle;
27import android.view.DisplayListCanvas;
28import android.view.HardwareRenderer;
29import android.view.RenderNode;
30import android.view.ThreadedRenderer;
31import android.view.View;
32import android.view.View.OnClickListener;
33import android.widget.AbsoluteLayout;
34import android.widget.AbsoluteLayout.LayoutParams;
35
36public class MultiProducerActivity extends Activity implements OnClickListener {
37 private static final int DURATION = 800;
38 private View mBackgroundTarget = null;
39 private View mFrameTarget = null;
40 private View mContent = null;
41 // The width & height of our "output drawing".
42 private final int WIDTH = 900;
43 private final int HEIGHT = 600;
44 // A border width around the drawing.
45 private static final int BORDER_WIDTH = 20;
46 // The Gap between the content and the frame which should get filled on the right and bottom
47 // side by the backdrop.
48 final int CONTENT_GAP = 100;
49
50 // For debug purposes - disable drawing of frame / background.
51 private final boolean USE_FRAME = true;
52 private final boolean USE_BACK = true;
53
54 @Override
55 protected void onCreate(Bundle savedInstanceState) {
56 super.onCreate(savedInstanceState);
57 // To make things simple - we do a quick and dirty absolute layout.
58 final AbsoluteLayout layout = new AbsoluteLayout(this);
59
60 // Create the outer frame
61 if (USE_FRAME) {
62 mFrameTarget = new View(this);
63 LayoutParams frameLP = new LayoutParams(WIDTH, HEIGHT, 0, 0);
64 layout.addView(mFrameTarget, frameLP);
65 }
66
67 // Create the background which fills the gap between content and frame.
68 if (USE_BACK) {
69 mBackgroundTarget = new View(this);
70 LayoutParams backgroundLP = new LayoutParams(
71 WIDTH - 2 * BORDER_WIDTH, HEIGHT - 2 * BORDER_WIDTH,
72 BORDER_WIDTH, BORDER_WIDTH);
73 layout.addView(mBackgroundTarget, backgroundLP);
74 }
75
76 // Create the content
77 // Note: We reduce the size by CONTENT_GAP pixels on right and bottom, so that they get
78 // drawn by the backdrop.
79 mContent = new View(this);
80 mContent.setBackground(new ColorPulse(0xFFF44336, 0xFF9C27B0, null));
81 mContent.setOnClickListener(this);
82 LayoutParams contentLP = new LayoutParams(WIDTH - 2 * BORDER_WIDTH - CONTENT_GAP,
83 HEIGHT - 2 * BORDER_WIDTH - CONTENT_GAP, BORDER_WIDTH, BORDER_WIDTH);
84 layout.addView(mContent, contentLP);
85
86 setContentView(layout);
87 }
88
89 @Override
90 protected void onStart() {
91 super.onStart();
92 View view = mBackgroundTarget != null ? mBackgroundTarget : mFrameTarget;
93 if (view != null) {
94 view.post(mSetup);
95 }
96 }
97
98 @Override
99 protected void onStop() {
100 super.onStop();
101 View view = mBackgroundTarget != null ? mBackgroundTarget : mFrameTarget;
102 if (view != null) {
103 view.removeCallbacks(mSetup);
104 }
105 if (mBgRenderer != null) {
106 mBgRenderer.destroy();
107 mBgRenderer = null;
108 }
109 }
110
111 @Override
112 public void onClick(View view) {
113 sBlockThread.run();
114 }
115
116 private Runnable mSetup = new Runnable() {
117 @Override
118 public void run() {
119 View view = mBackgroundTarget != null ? mBackgroundTarget : mFrameTarget;
120 if (view == null) {
121 view.postDelayed(mSetup, 50);
122 }
123 HardwareRenderer renderer = view.getHardwareRenderer();
124 if (renderer == null || view.getWidth() == 0) {
125 view.postDelayed(mSetup, 50);
126 }
127 ThreadedRenderer threaded = (ThreadedRenderer) renderer;
128
129 mBgRenderer = new FakeFrame(threaded,mFrameTarget, mBackgroundTarget);
130 mBgRenderer.start();
131 }
132 };
133
134 private FakeFrame mBgRenderer;
135 private class FakeFrame extends Thread {
136 ThreadedRenderer mRenderer;
137 volatile boolean mRunning = true;
138 View mTargetFrame;
139 View mTargetBack;
140 Drawable mFrameContent;
141 Drawable mBackContent;
142 // The Z value where to place this.
143 int mZFrame;
144 int mZBack;
145 String mRenderNodeName;
146
147 FakeFrame(ThreadedRenderer renderer, View targetFrame, View targetBack) {
148 mRenderer = renderer;
149 mTargetFrame = targetFrame;
150
151 mTargetBack = targetBack;
152 mFrameContent = new ColorPulse(0xFF101010, 0xFF707070, new Rect(0, 0, WIDTH, HEIGHT));
153 mBackContent = new ColorPulse(0xFF909090, 0xFFe0e0e0, null);
154 }
155
156 @Override
157 public void run() {
158 Rect currentFrameBounds = new Rect();
159 Rect currentBackBounds = new Rect();
160 Rect newBounds = new Rect();
161 int[] surfaceOrigin = new int[2];
162 RenderNode nodeFrame = null;
163 RenderNode nodeBack = null;
164
165 // Since we are overriding the window painting logic we need to at least fill the
166 // surface with some window content (otherwise the world will go black).
167 try {
168 Thread.sleep(200);
169 } catch (InterruptedException e) {
170 }
171
172 if (mTargetBack != null) {
173 nodeBack = RenderNode.create("FakeBackdrop", null);
174 nodeBack.setClipToBounds(true);
175 mRenderer.addRenderNode(nodeBack, true);
176 }
177
178 if (mTargetFrame != null) {
179 nodeFrame = RenderNode.create("FakeFrame", null);
180 nodeFrame.setClipToBounds(true);
181 mRenderer.addRenderNode(nodeFrame, false);
182 }
183
184 while (mRunning) {
185 // Get the surface position to draw to within our surface.
186 surfaceOrigin[0] = 0;
187 surfaceOrigin[1] = 0;
188 // This call should be done while the rendernode's displaylist is produced.
189 // For simplicity of this test we do this before we kick off the draw.
190 mContent.getLocationInSurface(surfaceOrigin);
191 mRenderer.setContentOverdrawProtectionBounds(surfaceOrigin[0], surfaceOrigin[1],
192 surfaceOrigin[0] + mContent.getWidth(),
193 surfaceOrigin[1] + mContent.getHeight());
194 // Determine new position for frame.
195 if (nodeFrame != null) {
196 surfaceOrigin[0] = 0;
197 surfaceOrigin[1] = 0;
198 mTargetFrame.getLocationInSurface(surfaceOrigin);
199 newBounds.set(surfaceOrigin[0], surfaceOrigin[1],
200 surfaceOrigin[0] + mTargetFrame.getWidth(),
201 surfaceOrigin[1] + mTargetFrame.getHeight());
202 if (!currentFrameBounds.equals(newBounds)) {
203 currentFrameBounds.set(newBounds);
204 nodeFrame.setLeftTopRightBottom(currentFrameBounds.left,
205 currentFrameBounds.top,
206 currentFrameBounds.right, currentFrameBounds.bottom);
207 }
208
209 // Draw frame
210 DisplayListCanvas canvas = nodeFrame.start(currentFrameBounds.width(),
211 currentFrameBounds.height());
212 mFrameContent.draw(canvas);
213 nodeFrame.end(canvas);
214 }
215
216 // Determine new position for backdrop
217 if (nodeBack != null) {
218 surfaceOrigin[0] = 0;
219 surfaceOrigin[1] = 0;
220 mTargetBack.getLocationInSurface(surfaceOrigin);
221 newBounds.set(surfaceOrigin[0], surfaceOrigin[1],
222 surfaceOrigin[0] + mTargetBack.getWidth(),
223 surfaceOrigin[1] + mTargetBack.getHeight());
224 if (!currentBackBounds.equals(newBounds)) {
225 currentBackBounds.set(newBounds);
226 nodeBack.setLeftTopRightBottom(currentBackBounds.left,
227 currentBackBounds.top,
228 currentBackBounds.right, currentBackBounds.bottom);
229 }
230
231 // Draw Backdrop
232 DisplayListCanvas canvas = nodeBack.start(currentBackBounds.width(),
233 currentBackBounds.height());
234 mBackContent.draw(canvas);
235 nodeBack.end(canvas);
236 }
237
238 // we need to only render one guy - the rest will happen automatically (I think).
239 if (nodeFrame != null) {
240 mRenderer.drawRenderNode(nodeFrame);
241 }
242 if (nodeBack != null) {
243 mRenderer.drawRenderNode(nodeBack);
244 }
245 try {
246 Thread.sleep(5);
247 } catch (InterruptedException e) {}
248 }
249 if (nodeFrame != null) {
250 mRenderer.removeRenderNode(nodeFrame);
251 }
252 if (nodeBack != null) {
253 mRenderer.removeRenderNode(nodeBack);
254 }
255 }
256
257 public void destroy() {
258 mRunning = false;
259 try {
260 join();
261 } catch (InterruptedException e) {}
262 }
263 }
264
265 private final static Runnable sBlockThread = new Runnable() {
266 @Override
267 public void run() {
268 try {
269 Thread.sleep(DURATION);
270 } catch (InterruptedException e) {
271 }
272 }
273 };
274
275 static class ColorPulse extends Drawable {
276
277 private int mColorStart;
278 private int mColorEnd;
279 private int mStep;
280 private Rect mRect;
281 private Paint mPaint = new Paint();
282
283 public ColorPulse(int color1, int color2, Rect rect) {
284 mColorStart = color1;
285 mColorEnd = color2;
286 if (rect != null) {
287 mRect = new Rect(rect.left + BORDER_WIDTH / 2, rect.top + BORDER_WIDTH / 2,
288 rect.right - BORDER_WIDTH / 2, rect.bottom - BORDER_WIDTH / 2);
289 }
290 }
291
292 static int evaluate(float fraction, int startInt, int endInt) {
293 int startA = (startInt >> 24) & 0xff;
294 int startR = (startInt >> 16) & 0xff;
295 int startG = (startInt >> 8) & 0xff;
296 int startB = startInt & 0xff;
297
298 int endA = (endInt >> 24) & 0xff;
299 int endR = (endInt >> 16) & 0xff;
300 int endG = (endInt >> 8) & 0xff;
301 int endB = endInt & 0xff;
302
303 return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
304 (int)((startR + (int)(fraction * (endR - startR))) << 16) |
305 (int)((startG + (int)(fraction * (endG - startG))) << 8) |
306 (int)((startB + (int)(fraction * (endB - startB))));
307 }
308
309 @Override
310 public void draw(Canvas canvas) {
311 float frac = mStep / 50.0f;
312 int color = evaluate(frac, mColorStart, mColorEnd);
313 if (mRect != null && !mRect.isEmpty()) {
314 mPaint.setStyle(Paint.Style.STROKE);
315 mPaint.setStrokeWidth(BORDER_WIDTH);
316 mPaint.setColor(color);
317 canvas.drawRect(mRect, mPaint);
318 } else {
319 canvas.drawColor(color);
320 }
321
322 mStep++;
323 if (mStep >= 50) {
324 mStep = 0;
325 int tmp = mColorStart;
326 mColorStart = mColorEnd;
327 mColorEnd = tmp;
328 }
329 invalidateSelf();
330 }
331
332 @Override
333 public void setAlpha(int alpha) {
334 }
335
336 @Override
337 public void setColorFilter(ColorFilter colorFilter) {
338 }
339
340 @Override
341 public int getOpacity() {
342 return mRect == null || mRect.isEmpty() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;
343 }
344
345 }
346}
347