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 android.view; 18 19 import com.android.layoutlib.bridge.android.BridgeContext; 20 import com.android.layoutlib.bridge.android.RenderParamsFlags; 21 import com.android.resources.Density; 22 import com.android.tools.layoutlib.annotations.LayoutlibDelegate; 23 24 import android.content.Context; 25 import android.graphics.Bitmap; 26 import android.graphics.Bitmap_Delegate; 27 import android.graphics.Canvas; 28 import android.graphics.Outline; 29 import android.graphics.Path_Delegate; 30 import android.graphics.Rect; 31 import android.graphics.Region.Op; 32 import android.view.animation.Transformation; 33 import android.view.shadow.HighQualityShadowPainter; 34 35 import java.awt.Graphics2D; 36 import java.awt.image.BufferedImage; 37 38 /** 39 * Delegate used to provide new implementation of a select few methods of {@link ViewGroup} 40 * <p/> 41 * Through the layoutlib_create tool, the original methods of ViewGroup have been replaced by calls 42 * to methods of the same name in this delegate class. 43 */ 44 public class ViewGroup_Delegate { 45 46 /** 47 * Overrides the original drawChild call in ViewGroup to draw the shadow. 48 */ 49 @LayoutlibDelegate 50 /*package*/ static boolean drawChild(ViewGroup thisVG, Canvas canvas, View child, 51 long drawingTime) { 52 if (child.getZ() > thisVG.getZ()) { 53 // The background's bounds are set lazily. Make sure they are set correctly so that 54 // the outline obtained is correct. 55 child.setBackgroundBounds(); 56 ViewOutlineProvider outlineProvider = child.getOutlineProvider(); 57 if (outlineProvider != null) { 58 Outline outline = child.mAttachInfo.mTmpOutline; 59 outlineProvider.getOutline(child, outline); 60 if (outline.mPath != null || (outline.mRect != null && !outline.mRect.isEmpty())) { 61 int restoreTo = transformCanvas(thisVG, canvas, child); 62 drawShadow(thisVG, canvas, child, outline); 63 canvas.restoreToCount(restoreTo); 64 } 65 } 66 } 67 return thisVG.drawChild_Original(canvas, child, drawingTime); 68 } 69 70 private static void drawShadow(ViewGroup parent, Canvas canvas, View child, 71 Outline outline) { 72 boolean highQualityShadow = false; 73 boolean enableShadow = true; 74 float elevation = getElevation(child, parent); 75 Context bridgeContext = parent.getContext(); 76 if (bridgeContext instanceof BridgeContext) { 77 highQualityShadow = ((BridgeContext) bridgeContext).getLayoutlibCallback() 78 .getFlag(RenderParamsFlags.FLAG_RENDER_HIGH_QUALITY_SHADOW); 79 enableShadow = ((BridgeContext) bridgeContext).getLayoutlibCallback() 80 .getFlag(RenderParamsFlags.FLAG_ENABLE_SHADOW); 81 } 82 83 if (!enableShadow) { 84 return; 85 } 86 87 if(outline.mMode == Outline.MODE_ROUND_RECT && outline.mRect != null) { 88 if (highQualityShadow) { 89 float densityDpi = bridgeContext.getResources().getDisplayMetrics().densityDpi; 90 HighQualityShadowPainter.paintRectShadow( 91 parent, outline, elevation, canvas, child.getAlpha(), densityDpi); 92 } else { 93 RectShadowPainter.paintShadow(outline, elevation, canvas, child.getAlpha()); 94 } 95 return; 96 } 97 98 BufferedImage shadow = null; 99 if (outline.mPath != null) { 100 shadow = getPathShadow(outline, canvas, elevation, child.getAlpha()); 101 } 102 if (shadow == null) { 103 return; 104 } 105 Bitmap bitmap = Bitmap_Delegate.createBitmap(shadow, false, 106 Density.getEnum(canvas.getDensity())); 107 canvas.save(); 108 Rect clipBounds = canvas.getClipBounds(); 109 Rect newBounds = new Rect(clipBounds); 110 newBounds.inset((int)-elevation, (int)-elevation); 111 canvas.clipRectUnion(newBounds); 112 canvas.drawBitmap(bitmap, 0, 0, null); 113 canvas.restore(); 114 } 115 116 private static float getElevation(View child, ViewGroup parent) { 117 return child.getZ() - parent.getZ(); 118 } 119 120 private static BufferedImage getPathShadow(Outline outline, Canvas canvas, float elevation, 121 float alpha) { 122 Rect clipBounds = canvas.getClipBounds(); 123 if (clipBounds.isEmpty()) { 124 return null; 125 } 126 BufferedImage image = new BufferedImage(clipBounds.width(), clipBounds.height(), 127 BufferedImage.TYPE_INT_ARGB); 128 Graphics2D graphics = image.createGraphics(); 129 graphics.draw(Path_Delegate.getDelegate(outline.mPath.mNativePath).getJavaShape()); 130 graphics.dispose(); 131 return ShadowPainter.createDropShadow(image, (int) elevation, alpha); 132 } 133 134 // Copied from android.view.View#draw(Canvas, ViewGroup, long) and removed code paths 135 // which were never taken. Ideally, we should hook up the shadow code in the same method so 136 // that we don't have to transform the canvas twice. 137 private static int transformCanvas(ViewGroup thisVG, Canvas canvas, View child) { 138 final int restoreTo = canvas.save(); 139 final boolean childHasIdentityMatrix = child.hasIdentityMatrix(); 140 int flags = thisVG.mGroupFlags; 141 Transformation transformToApply = null; 142 boolean concatMatrix = false; 143 if ((flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) { 144 final Transformation t = thisVG.getChildTransformation(); 145 final boolean hasTransform = thisVG.getChildStaticTransformation(child, t); 146 if (hasTransform) { 147 final int transformType = t.getTransformationType(); 148 transformToApply = transformType != Transformation.TYPE_IDENTITY ? t : null; 149 concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0; 150 } 151 } 152 concatMatrix |= childHasIdentityMatrix; 153 154 child.computeScroll(); 155 int sx = child.mScrollX; 156 int sy = child.mScrollY; 157 158 canvas.translate(child.mLeft - sx, child.mTop - sy); 159 float alpha = child.getAlpha() * child.getTransitionAlpha(); 160 161 if (transformToApply != null || alpha < 1 || !childHasIdentityMatrix) { 162 if (transformToApply != null || !childHasIdentityMatrix) { 163 int transX = -sx; 164 int transY = -sy; 165 166 if (transformToApply != null) { 167 if (concatMatrix) { 168 // Undo the scroll translation, apply the transformation matrix, 169 // then redo the scroll translate to get the correct result. 170 canvas.translate(-transX, -transY); 171 canvas.concat(transformToApply.getMatrix()); 172 canvas.translate(transX, transY); 173 } 174 } 175 if (!childHasIdentityMatrix) { 176 canvas.translate(-transX, -transY); 177 canvas.concat(child.getMatrix()); 178 canvas.translate(transX, transY); 179 } 180 181 } 182 } 183 return restoreTo; 184 } 185 } 186