Home | History | Annotate | Download | only in impl
      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 
     17 package com.android.layoutlib.bridge.impl;
     18 
     19 import com.android.ide.common.rendering.api.DrawableParams;
     20 import com.android.ide.common.rendering.api.HardwareConfig;
     21 import com.android.ide.common.rendering.api.ResourceValue;
     22 import com.android.ide.common.rendering.api.Result;
     23 import com.android.ide.common.rendering.api.Result.Status;
     24 import com.android.layoutlib.bridge.android.BridgeContext;
     25 import com.android.layoutlib.bridge.android.RenderParamsFlags;
     26 import com.android.resources.ResourceType;
     27 
     28 import android.graphics.Bitmap;
     29 import android.graphics.Bitmap_Delegate;
     30 import android.graphics.Canvas;
     31 import android.graphics.drawable.Drawable;
     32 import android.graphics.drawable.StateListDrawable;
     33 import android.view.AttachInfo_Accessor;
     34 import android.view.View.MeasureSpec;
     35 import android.widget.FrameLayout;
     36 
     37 import java.awt.AlphaComposite;
     38 import java.awt.Color;
     39 import java.awt.Graphics2D;
     40 import java.awt.image.BufferedImage;
     41 import java.util.ArrayList;
     42 import java.util.Collections;
     43 import 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  */
     54 public 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