Home | History | Annotate | Download | only in hwui
      1 /*
      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 
     17 package com.android.test.hwui;
     18 
     19 import android.app.Activity;
     20 import android.graphics.Canvas;
     21 import android.graphics.ColorFilter;
     22 import android.graphics.Paint;
     23 import android.graphics.PixelFormat;
     24 import android.graphics.Rect;
     25 import android.graphics.drawable.Drawable;
     26 import android.os.Bundle;
     27 import android.view.DisplayListCanvas;
     28 import android.view.ThreadedRenderer;
     29 import android.view.RenderNode;
     30 import android.view.ThreadedRenderer;
     31 import android.view.View;
     32 import android.view.View.OnClickListener;
     33 import android.widget.AbsoluteLayout;
     34 import android.widget.AbsoluteLayout.LayoutParams;
     35 
     36 public 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             ThreadedRenderer 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.setContentDrawBounds(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 
    348