1/* 2 * Copyright (C) 2011 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.layoutlib.bridge.impl; 18 19import com.android.ide.common.rendering.api.DrawableParams; 20import com.android.ide.common.rendering.api.HardwareConfig; 21import com.android.ide.common.rendering.api.ResourceValue; 22import com.android.ide.common.rendering.api.Result; 23import com.android.ide.common.rendering.api.Result.Status; 24import com.android.layoutlib.bridge.android.BridgeContext; 25import com.android.layoutlib.bridge.android.RenderParamsFlags; 26import com.android.resources.ResourceType; 27 28import android.graphics.Bitmap; 29import android.graphics.Bitmap_Delegate; 30import android.graphics.Canvas; 31import android.graphics.drawable.Drawable; 32import android.graphics.drawable.StateListDrawable; 33import android.view.AttachInfo_Accessor; 34import android.view.View.MeasureSpec; 35import android.widget.FrameLayout; 36 37import java.awt.AlphaComposite; 38import java.awt.Color; 39import java.awt.Graphics2D; 40import java.awt.image.BufferedImage; 41import java.util.ArrayList; 42import java.util.Collections; 43import java.util.List; 44 45/** 46 * Action to render a given Drawable provided through {@link DrawableParams#getDrawable()}. 47 * 48 * The class only provides a simple {@link #render()} method, but the full life-cycle of the 49 * action must be respected. 50 * 51 * @see RenderAction 52 * 53 */ 54public class RenderDrawable extends RenderAction<DrawableParams> { 55 56 public RenderDrawable(DrawableParams params) { 57 super(new DrawableParams(params)); 58 } 59 60 public Result render() { 61 checkLock(); 62 // get the drawable resource value 63 DrawableParams params = getParams(); 64 HardwareConfig hardwareConfig = params.getHardwareConfig(); 65 ResourceValue drawableResource = params.getDrawable(); 66 67 // resolve it 68 BridgeContext context = getContext(); 69 drawableResource = context.getRenderResources().resolveResValue(drawableResource); 70 71 if (drawableResource == null) { 72 return Status.ERROR_NOT_A_DRAWABLE.createResult(); 73 } 74 75 ResourceType resourceType = drawableResource.getResourceType(); 76 if (resourceType != ResourceType.DRAWABLE && resourceType != ResourceType.MIPMAP) { 77 return Status.ERROR_NOT_A_DRAWABLE.createResult(); 78 } 79 80 Drawable d = ResourceHelper.getDrawable(drawableResource, context); 81 82 final Boolean allStates = 83 params.getFlag(RenderParamsFlags.FLAG_KEY_RENDER_ALL_DRAWABLE_STATES); 84 if (allStates == Boolean.TRUE) { 85 final List<BufferedImage> result; 86 87 if (d instanceof StateListDrawable) { 88 result = new ArrayList<BufferedImage>(); 89 final StateListDrawable stateList = (StateListDrawable) d; 90 for (int i = 0; i < stateList.getStateCount(); i++) { 91 final Drawable stateDrawable = stateList.getStateDrawable(i); 92 result.add(renderImage(hardwareConfig, stateDrawable, context)); 93 } 94 } else { 95 result = Collections.singletonList(renderImage(hardwareConfig, d, context)); 96 } 97 98 return Status.SUCCESS.createResult(result); 99 } else { 100 BufferedImage image = renderImage(hardwareConfig, d, context); 101 return Status.SUCCESS.createResult(image); 102 } 103 } 104 105 private BufferedImage renderImage(HardwareConfig hardwareConfig, Drawable d, 106 BridgeContext context) { 107 // create a simple FrameLayout 108 FrameLayout content = new FrameLayout(context); 109 110 // get the actual Drawable object to draw 111 content.setBackground(d); 112 113 // set the AttachInfo on the root view. 114 AttachInfo_Accessor.setAttachInfo(content); 115 116 117 // measure 118 int w = d.getIntrinsicWidth(); 119 int h = d.getIntrinsicHeight(); 120 121 final int screenWidth = hardwareConfig.getScreenWidth(); 122 final int screenHeight = hardwareConfig.getScreenHeight(); 123 124 if (w == -1 || h == -1) { 125 // Use screen size when either intrinsic width or height isn't available 126 w = screenWidth; 127 h = screenHeight; 128 } else if (w > screenWidth || h > screenHeight) { 129 // If image wouldn't fit to the screen, resize it to avoid cropping. 130 131 // We need to find scale such that scale * w <= screenWidth, scale * h <= screenHeight 132 double scale = Math.min((double) screenWidth / w, (double) screenHeight / h); 133 134 // scale * w / scale * h = w / h, so, proportions are preserved. 135 w = (int) Math.floor(scale * w); 136 h = (int) Math.floor(scale * h); 137 } 138 139 int w_spec = MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY); 140 int h_spec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY); 141 content.measure(w_spec, h_spec); 142 143 // now do the layout. 144 content.layout(0, 0, w, h); 145 146 // preDraw setup 147 AttachInfo_Accessor.dispatchOnPreDraw(content); 148 149 // draw into a new image 150 BufferedImage image = getImage(w, h); 151 152 // create an Android bitmap around the BufferedImage 153 Bitmap bitmap = Bitmap_Delegate.createBitmap(image, 154 true /*isMutable*/, hardwareConfig.getDensity()); 155 156 // create a Canvas around the Android bitmap 157 Canvas canvas = new Canvas(bitmap); 158 canvas.setDensity(hardwareConfig.getDensity().getDpiValue()); 159 160 // and draw 161 content.draw(canvas); 162 return image; 163 } 164 165 protected BufferedImage getImage(int w, int h) { 166 BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); 167 Graphics2D gc = image.createGraphics(); 168 gc.setComposite(AlphaComposite.Src); 169 170 gc.setColor(new Color(0x00000000, true)); 171 gc.fillRect(0, 0, w, h); 172 173 // done 174 gc.dispose(); 175 176 return image; 177 } 178 179} 180