ImageView.java revision 4df2423a947bcd3f024cc3d3a1a315a8dc428598
1/*
2 * Copyright (C) 2006 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 android.widget;
18
19import android.content.Context;
20import android.content.res.Resources;
21import android.content.res.TypedArray;
22import android.graphics.Bitmap;
23import android.graphics.Canvas;
24import android.graphics.ColorFilter;
25import android.graphics.Matrix;
26import android.graphics.PorterDuff;
27import android.graphics.PorterDuffColorFilter;
28import android.graphics.RectF;
29import android.graphics.drawable.BitmapDrawable;
30import android.graphics.drawable.Drawable;
31import android.net.Uri;
32import android.util.AttributeSet;
33import android.util.Log;
34import android.view.View;
35import android.widget.RemoteViews.RemoteView;
36
37
38/**
39 * Displays an arbitrary image, such as an icon.  The ImageView class
40 * can load images from various sources (such as resources or content
41 * providers), takes care of computing its measurement from the image so that
42 * it can be used in any layout manager, and provides various display options
43 * such as scaling and tinting.
44 *
45 * @attr ref android.R.styleable#ImageView_adjustViewBounds
46 * @attr ref android.R.styleable#ImageView_src
47 * @attr ref android.R.styleable#ImageView_maxWidth
48 * @attr ref android.R.styleable#ImageView_maxHeight
49 * @attr ref android.R.styleable#ImageView_tint
50 * @attr ref android.R.styleable#ImageView_scaleType
51 * @attr ref android.R.styleable#ImageView_cropToPadding
52 */
53@RemoteView
54public class ImageView extends View {
55    // settable by the client
56    private Uri mUri;
57    private int mResource = 0;
58    private Matrix mMatrix;
59    private ScaleType mScaleType;
60    private boolean mHaveFrame = false;
61    private boolean mAdjustViewBounds = false;
62    private int mMaxWidth = Integer.MAX_VALUE;
63    private int mMaxHeight = Integer.MAX_VALUE;
64
65    // these are applied to the drawable
66    private ColorFilter mColorFilter;
67    private int mAlpha = 255;
68    private int mViewAlphaScale = 256;
69
70    private Drawable mDrawable = null;
71    private int[] mState = null;
72    private boolean mMergeState = false;
73    private int mLevel = 0;
74    private int mDrawableWidth;
75    private int mDrawableHeight;
76    private Matrix mDrawMatrix = null;
77
78    // Avoid allocations...
79    private RectF mTempSrc = new RectF();
80    private RectF mTempDst = new RectF();
81
82    private boolean mCropToPadding;
83
84    private boolean mBaselineAligned = false;
85
86    private static final ScaleType[] sScaleTypeArray = {
87        ScaleType.MATRIX,
88        ScaleType.FIT_XY,
89        ScaleType.FIT_START,
90        ScaleType.FIT_CENTER,
91        ScaleType.FIT_END,
92        ScaleType.CENTER,
93        ScaleType.CENTER_CROP,
94        ScaleType.CENTER_INSIDE
95    };
96
97    public ImageView(Context context) {
98        super(context);
99        initImageView();
100    }
101
102    public ImageView(Context context, AttributeSet attrs) {
103        this(context, attrs, 0);
104    }
105
106    public ImageView(Context context, AttributeSet attrs, int defStyle) {
107        super(context, attrs, defStyle);
108        initImageView();
109
110        TypedArray a = context.obtainStyledAttributes(attrs,
111                com.android.internal.R.styleable.ImageView, defStyle, 0);
112
113        Drawable d = a.getDrawable(com.android.internal.R.styleable.ImageView_src);
114        if (d != null) {
115            setImageDrawable(d);
116        }
117
118        mBaselineAligned = a.getBoolean(
119                com.android.internal.R.styleable.ImageView_baselineAlignBottom, false);
120
121        setAdjustViewBounds(
122            a.getBoolean(com.android.internal.R.styleable.ImageView_adjustViewBounds,
123            false));
124
125        setMaxWidth(a.getDimensionPixelSize(
126                com.android.internal.R.styleable.ImageView_maxWidth, Integer.MAX_VALUE));
127
128        setMaxHeight(a.getDimensionPixelSize(
129                com.android.internal.R.styleable.ImageView_maxHeight, Integer.MAX_VALUE));
130
131        int index = a.getInt(com.android.internal.R.styleable.ImageView_scaleType, -1);
132        if (index >= 0) {
133            setScaleType(sScaleTypeArray[index]);
134        }
135
136        int tint = a.getInt(com.android.internal.R.styleable.ImageView_tint, 0);
137        if (tint != 0) {
138            setColorFilter(tint, PorterDuff.Mode.SRC_ATOP);
139        }
140
141        mCropToPadding = a.getBoolean(
142                com.android.internal.R.styleable.ImageView_cropToPadding, false);
143
144        a.recycle();
145
146        //need inflate syntax/reader for matrix
147    }
148
149    private void initImageView() {
150        mMatrix     = new Matrix();
151        mScaleType  = ScaleType.FIT_CENTER;
152    }
153
154    @Override
155    protected boolean verifyDrawable(Drawable dr) {
156        return mDrawable == dr || super.verifyDrawable(dr);
157    }
158
159    @Override
160    public void invalidateDrawable(Drawable dr) {
161        if (dr == mDrawable) {
162            /* we invalidate the whole view in this case because it's very
163             * hard to know where the drawable actually is. This is made
164             * complicated because of the offsets and transformations that
165             * can be applied. In theory we could get the drawable's bounds
166             * and run them through the transformation and offsets, but this
167             * is probably not worth the effort.
168             */
169            invalidate();
170        } else {
171            super.invalidateDrawable(dr);
172        }
173    }
174
175    @Override
176    protected boolean onSetAlpha(int alpha) {
177        if (getBackground() == null) {
178            int scale = alpha + (alpha >> 7);
179            if (mViewAlphaScale != scale) {
180                mViewAlphaScale = scale;
181                applyColorMod();
182            }
183            return true;
184        }
185        return false;
186    }
187
188    /**
189     * Set this to true if you want the ImageView to adjust its bounds
190     * to preserve the aspect ratio of its drawable.
191     * @param adjustViewBounds Whether to adjust the bounds of this view
192     * to presrve the original aspect ratio of the drawable
193     *
194     * @attr ref android.R.styleable#ImageView_adjustViewBounds
195     */
196    @android.view.RemotableViewMethod
197    public void setAdjustViewBounds(boolean adjustViewBounds) {
198        mAdjustViewBounds = adjustViewBounds;
199        if (adjustViewBounds) {
200            setScaleType(ScaleType.FIT_CENTER);
201        }
202    }
203
204    /**
205     * An optional argument to supply a maximum width for this view. Only valid if
206     * {@link #setAdjustViewBounds} has been set to true. To set an image to be a maximum of 100 x
207     * 100 while preserving the original aspect ratio, do the following: 1) set adjustViewBounds to
208     * true 2) set maxWidth and maxHeight to 100 3) set the height and width layout params to
209     * WRAP_CONTENT.
210     *
211     * <p>
212     * Note that this view could be still smaller than 100 x 100 using this approach if the original
213     * image is small. To set an image to a fixed size, specify that size in the layout params and
214     * then use {@link #setScaleType} to determine how to fit the image within the bounds.
215     * </p>
216     *
217     * @param maxWidth maximum width for this view
218     *
219     * @attr ref android.R.styleable#ImageView_maxWidth
220     */
221    @android.view.RemotableViewMethod
222    public void setMaxWidth(int maxWidth) {
223        mMaxWidth = maxWidth;
224    }
225
226    /**
227     * An optional argument to supply a maximum height for this view. Only valid if
228     * {@link #setAdjustViewBounds} has been set to true. To set an image to be a maximum of 100 x
229     * 100 while preserving the original aspect ratio, do the following: 1) set adjustViewBounds to
230     * true 2) set maxWidth and maxHeight to 100 3) set the height and width layout params to
231     * WRAP_CONTENT.
232     *
233     * <p>
234     * Note that this view could be still smaller than 100 x 100 using this approach if the original
235     * image is small. To set an image to a fixed size, specify that size in the layout params and
236     * then use {@link #setScaleType} to determine how to fit the image within the bounds.
237     * </p>
238     *
239     * @param maxHeight maximum height for this view
240     *
241     * @attr ref android.R.styleable#ImageView_maxHeight
242     */
243    @android.view.RemotableViewMethod
244    public void setMaxHeight(int maxHeight) {
245        mMaxHeight = maxHeight;
246    }
247
248    /** Return the view's drawable, or null if no drawable has been
249        assigned.
250    */
251    public Drawable getDrawable() {
252        return mDrawable;
253    }
254
255    /**
256     * Sets a drawable as the content of this ImageView.
257     *
258     * @param resId the resource identifier of the the drawable
259     *
260     * @attr ref android.R.styleable#ImageView_src
261     */
262    @android.view.RemotableViewMethod
263    public void setImageResource(int resId) {
264        if (mUri != null || mResource != resId) {
265            updateDrawable(null);
266            mResource = resId;
267            mUri = null;
268            resolveUri();
269            requestLayout();
270            invalidate();
271        }
272    }
273
274    /**
275     * Sets the content of this ImageView to the specified Uri.
276     *
277     * @param uri The Uri of an image
278     */
279    @android.view.RemotableViewMethod
280    public void setImageURI(Uri uri) {
281        if (mResource != 0 ||
282                (mUri != uri &&
283                 (uri == null || mUri == null || !uri.equals(mUri)))) {
284            updateDrawable(null);
285            mResource = 0;
286            mUri = uri;
287            resolveUri();
288            requestLayout();
289            invalidate();
290        }
291    }
292
293
294    /**
295     * Sets a drawable as the content of this ImageView.
296     *
297     * @param drawable The drawable to set
298     */
299    public void setImageDrawable(Drawable drawable) {
300        if (mDrawable != drawable) {
301            mResource = 0;
302            mUri = null;
303            updateDrawable(drawable);
304            requestLayout();
305            invalidate();
306        }
307    }
308
309    /**
310     * Sets a Bitmap as the content of this ImageView.
311     *
312     * @param bm The bitmap to set
313     */
314    @android.view.RemotableViewMethod
315    public void setImageBitmap(Bitmap bm) {
316        // if this is used frequently, may handle bitmaps explicitly
317        // to reduce the intermediate drawable object
318        setImageDrawable(new BitmapDrawable(bm));
319    }
320
321    public void setImageState(int[] state, boolean merge) {
322        mState = state;
323        mMergeState = merge;
324        if (mDrawable != null) {
325            refreshDrawableState();
326            resizeFromDrawable();
327        }
328    }
329
330    @Override
331    public void setSelected(boolean selected) {
332        super.setSelected(selected);
333        resizeFromDrawable();
334    }
335
336    /**
337     * Sets the image level, when it is constructed from a
338     * {@link android.graphics.drawable.LevelListDrawable}.
339     *
340     * @param level The new level for the image.
341     */
342    @android.view.RemotableViewMethod
343    public void setImageLevel(int level) {
344        mLevel = level;
345        if (mDrawable != null) {
346            mDrawable.setLevel(level);
347            resizeFromDrawable();
348        }
349    }
350
351    /**
352     * Options for scaling the bounds of an image to the bounds of this view.
353     */
354    public enum ScaleType {
355        /**
356         * Scale using the image matrix when drawing. The image matrix can be set using
357         * {@link ImageView#setImageMatrix(Matrix)}. From XML, use this syntax:
358         * <code>android:scaleType="matrix"</code>.
359         */
360        MATRIX      (0),
361        /**
362         * Scale the image using {@link Matrix.ScaleToFit#FILL}.
363         * From XML, use this syntax: <code>android:scaleType="fitXY"</code>.
364         */
365        FIT_XY      (1),
366        /**
367         * Scale the image using {@link Matrix.ScaleToFit#START}.
368         * From XML, use this syntax: <code>android:scaleType="fitStart"</code>.
369         */
370        FIT_START   (2),
371        /**
372         * Scale the image using {@link Matrix.ScaleToFit#CENTER}.
373         * From XML, use this syntax:
374         * <code>android:scaleType="fitCenter"</code>.
375         */
376        FIT_CENTER  (3),
377        /**
378         * Scale the image using {@link Matrix.ScaleToFit#END}.
379         * From XML, use this syntax: <code>android:scaleType="fitEnd"</code>.
380         */
381        FIT_END     (4),
382        /**
383         * Center the image in the view, but perform no scaling.
384         * From XML, use this syntax: <code>android:scaleType="center"</code>.
385         */
386        CENTER      (5),
387        /**
388         * Scale the image uniformly (maintain the image's aspect ratio) so
389         * that both dimensions (width and height) of the image will be equal
390         * to or larger than the corresponding dimension of the view
391         * (minus padding). The image is then centered in the view.
392         * From XML, use this syntax: <code>android:scaleType="centerCrop"</code>.
393         */
394        CENTER_CROP (6),
395        /**
396         * Scale the image uniformly (maintain the image's aspect ratio) so
397         * that both dimensions (width and height) of the image will be equal
398         * to or less than the corresponding dimension of the view
399         * (minus padding). The image is then centered in the view.
400         * From XML, use this syntax: <code>android:scaleType="centerInside"</code>.
401         */
402        CENTER_INSIDE (7);
403
404        ScaleType(int ni) {
405            nativeInt = ni;
406        }
407        final int nativeInt;
408    }
409
410    /**
411     * Controls how the image should be resized or moved to match the size
412     * of this ImageView.
413     *
414     * @param scaleType The desired scaling mode.
415     *
416     * @attr ref android.R.styleable#ImageView_scaleType
417     */
418    public void setScaleType(ScaleType scaleType) {
419        if (scaleType == null) {
420            throw new NullPointerException();
421        }
422
423        if (mScaleType != scaleType) {
424            mScaleType = scaleType;
425
426            setWillNotCacheDrawing(mScaleType == ScaleType.CENTER);
427
428            requestLayout();
429            invalidate();
430        }
431    }
432
433    /**
434     * Return the current scale type in use by this ImageView.
435     *
436     * @see ImageView.ScaleType
437     *
438     * @attr ref android.R.styleable#ImageView_scaleType
439     */
440    public ScaleType getScaleType() {
441        return mScaleType;
442    }
443
444    /** Return the view's optional matrix. This is applied to the
445        view's drawable when it is drawn. If there is not matrix,
446        this method will return null.
447        Do not change this matrix in place. If you want a different matrix
448        applied to the drawable, be sure to call setImageMatrix().
449    */
450    public Matrix getImageMatrix() {
451        return mMatrix;
452    }
453
454    public void setImageMatrix(Matrix matrix) {
455        // collaps null and identity to just null
456        if (matrix != null && matrix.isIdentity()) {
457            matrix = null;
458        }
459
460        // don't invalidate unless we're actually changing our matrix
461        if (matrix == null && !mMatrix.isIdentity() ||
462                matrix != null && !mMatrix.equals(matrix)) {
463            mMatrix.set(matrix);
464            invalidate();
465        }
466    }
467
468    private void resolveUri() {
469        if (mDrawable != null) {
470            return;
471        }
472
473        Resources rsrc = getResources();
474        if (rsrc == null) {
475            return;
476        }
477
478        Drawable d = null;
479
480        if (mResource != 0) {
481            try {
482                d = rsrc.getDrawable(mResource);
483            } catch (Exception e) {
484                Log.w("ImageView", "Unable to find resource: " + mResource, e);
485                // Don't try again.
486                mUri = null;
487            }
488        } else if (mUri != null) {
489            if ("content".equals(mUri.getScheme())) {
490                try {
491                    d = Drawable.createFromStream(
492                        mContext.getContentResolver().openInputStream(mUri),
493                        null);
494                } catch (Exception e) {
495                    Log.w("ImageView", "Unable to open content: " + mUri, e);
496                }
497            } else {
498                d = Drawable.createFromPath(mUri.toString());
499            }
500
501            if (d == null) {
502                System.out.println("resolveUri failed on bad bitmap uri: "
503                                   + mUri);
504                // Don't try again.
505                mUri = null;
506            }
507        } else {
508            return;
509        }
510
511        updateDrawable(d);
512    }
513
514    @Override
515    public int[] onCreateDrawableState(int extraSpace) {
516        if (mState == null) {
517            return super.onCreateDrawableState(extraSpace);
518        } else if (!mMergeState) {
519            return mState;
520        } else {
521            return mergeDrawableStates(
522                    super.onCreateDrawableState(extraSpace + mState.length), mState);
523        }
524    }
525
526    private void updateDrawable(Drawable d) {
527        if (mDrawable != null) {
528            mDrawable.setCallback(null);
529            unscheduleDrawable(mDrawable);
530        }
531        mDrawable = d;
532        if (d != null) {
533            d.setCallback(this);
534            if (d.isStateful()) {
535                d.setState(getDrawableState());
536            }
537            d.setLevel(mLevel);
538            mDrawableWidth = d.getIntrinsicWidth();
539            mDrawableHeight = d.getIntrinsicHeight();
540            applyColorMod();
541            configureBounds();
542        }
543    }
544
545    private void resizeFromDrawable() {
546        Drawable d = mDrawable;
547        if (d != null) {
548            int w = d.getIntrinsicWidth();
549            if (w < 0) w = mDrawableWidth;
550            int h = d.getIntrinsicHeight();
551            if (h < 0) h = mDrawableHeight;
552            if (w != mDrawableWidth || h != mDrawableHeight) {
553                mDrawableWidth = w;
554                mDrawableHeight = h;
555                requestLayout();
556            }
557        }
558    }
559
560    private static final Matrix.ScaleToFit[] sS2FArray = {
561        Matrix.ScaleToFit.FILL,
562        Matrix.ScaleToFit.START,
563        Matrix.ScaleToFit.CENTER,
564        Matrix.ScaleToFit.END
565    };
566
567    private static Matrix.ScaleToFit scaleTypeToScaleToFit(ScaleType st)  {
568        // ScaleToFit enum to their corresponding Matrix.ScaleToFit values
569        return sS2FArray[st.nativeInt - 1];
570    }
571
572    @Override
573    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
574        resolveUri();
575        int w;
576        int h;
577
578        // Desired aspect ratio of the view's contents (not including padding)
579        float desiredAspect = 0.0f;
580
581        // We are allowed to change the view's width
582        boolean resizeWidth = false;
583
584        // We are allowed to change the view's height
585        boolean resizeHeight = false;
586
587        if (mDrawable == null) {
588            // If no drawable, its intrinsic size is 0.
589            mDrawableWidth = -1;
590            mDrawableHeight = -1;
591            w = h = 0;
592        } else {
593            w = mDrawableWidth;
594            h = mDrawableHeight;
595            if (w <= 0) w = 1;
596            if (h <= 0) h = 1;
597
598            // We are supposed to adjust view bounds to match the aspect
599            // ratio of our drawable. See if that is possible.
600            if (mAdjustViewBounds) {
601
602                int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
603                int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
604
605                resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
606                resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;
607
608                desiredAspect = (float)w/(float)h;
609            }
610        }
611
612        int pleft = mPaddingLeft;
613        int pright = mPaddingRight;
614        int ptop = mPaddingTop;
615        int pbottom = mPaddingBottom;
616
617        int widthSize;
618        int heightSize;
619
620        if (resizeWidth || resizeHeight) {
621            /* If we get here, it means we want to resize to match the
622                drawables aspect ratio, and we have the freedom to change at
623                least one dimension.
624            */
625
626            // Get the max possible width given our constraints
627            widthSize = resolveAdjustedSize(w + pleft + pright,
628                                                 mMaxWidth, widthMeasureSpec);
629
630            // Get the max possible height given our constraints
631            heightSize = resolveAdjustedSize(h + ptop + pbottom,
632                                                mMaxHeight, heightMeasureSpec);
633
634            if (desiredAspect != 0.0f) {
635                // See what our actual aspect ratio is
636                float actualAspect = (float)(widthSize - pleft - pright) /
637                                        (heightSize - ptop - pbottom);
638
639                if (Math.abs(actualAspect - desiredAspect) > 0.0000001) {
640
641                    boolean done = false;
642
643                    // Try adjusting width to be proportional to height
644                    if (resizeWidth) {
645                        int newWidth = (int)(desiredAspect *
646                                            (heightSize - ptop - pbottom))
647                                            + pleft + pright;
648                        if (newWidth <= widthSize) {
649                            widthSize = newWidth;
650                            done = true;
651                        }
652                    }
653
654                    // Try adjusting height to be proportional to width
655                    if (!done && resizeHeight) {
656                        int newHeight = (int)((widthSize - pleft - pright)
657                                            / desiredAspect) + ptop + pbottom;
658                        if (newHeight <= heightSize) {
659                            heightSize = newHeight;
660                        }
661                    }
662                }
663            }
664        } else {
665            /* We are either don't want to preserve the drawables aspect ratio,
666               or we are not allowed to change view dimensions. Just measure in
667               the normal way.
668            */
669            w += pleft + pright;
670            h += ptop + pbottom;
671
672            w = Math.max(w, getSuggestedMinimumWidth());
673            h = Math.max(h, getSuggestedMinimumHeight());
674
675            widthSize = resolveSize(w, widthMeasureSpec);
676            heightSize = resolveSize(h, heightMeasureSpec);
677        }
678
679        setMeasuredDimension(widthSize, heightSize);
680    }
681
682    private int resolveAdjustedSize(int desiredSize, int maxSize,
683                                   int measureSpec) {
684        int result = desiredSize;
685        int specMode = MeasureSpec.getMode(measureSpec);
686        int specSize =  MeasureSpec.getSize(measureSpec);
687        switch (specMode) {
688            case MeasureSpec.UNSPECIFIED:
689                /* Parent says we can be as big as we want. Just don't be larger
690                   than max size imposed on ourselves.
691                */
692                result = Math.min(desiredSize, maxSize);
693                break;
694            case MeasureSpec.AT_MOST:
695                // Parent says we can be as big as we want, up to specSize.
696                // Don't be larger than specSize, and don't be larger than
697                // the max size imposed on ourselves.
698                result = Math.min(Math.min(desiredSize, specSize), maxSize);
699                break;
700            case MeasureSpec.EXACTLY:
701                // No choice. Do what we are told.
702                result = specSize;
703                break;
704        }
705        return result;
706    }
707
708    @Override
709    protected boolean setFrame(int l, int t, int r, int b) {
710        boolean changed = super.setFrame(l, t, r, b);
711        mHaveFrame = true;
712        configureBounds();
713        return changed;
714    }
715
716    private void configureBounds() {
717        if (mDrawable == null || !mHaveFrame) {
718            return;
719        }
720
721        int dwidth = mDrawableWidth;
722        int dheight = mDrawableHeight;
723
724        int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
725        int vheight = getHeight() - mPaddingTop - mPaddingBottom;
726
727        boolean fits = (dwidth < 0 || vwidth == dwidth) &&
728                       (dheight < 0 || vheight == dheight);
729
730        if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
731            /* If the drawable has no intrinsic size, or we're told to
732                scaletofit, then we just fill our entire view.
733            */
734            mDrawable.setBounds(0, 0, vwidth, vheight);
735            mDrawMatrix = null;
736        } else {
737            // We need to do the scaling ourself, so have the drawable
738            // use its native size.
739            mDrawable.setBounds(0, 0, dwidth, dheight);
740
741            if (ScaleType.MATRIX == mScaleType) {
742                // Use the specified matrix as-is.
743                if (mMatrix.isIdentity()) {
744                    mDrawMatrix = null;
745                } else {
746                    mDrawMatrix = mMatrix;
747                }
748            } else if (fits) {
749                // The bitmap fits exactly, no transform needed.
750                mDrawMatrix = null;
751            } else if (ScaleType.CENTER == mScaleType) {
752                // Center bitmap in view, no scaling.
753                mDrawMatrix = mMatrix;
754                mDrawMatrix.setTranslate((vwidth - dwidth) * 0.5f,
755                                         (vheight - dheight) * 0.5f);
756            } else if (ScaleType.CENTER_CROP == mScaleType) {
757                mDrawMatrix = mMatrix;
758
759                float scale;
760                float dx = 0, dy = 0;
761
762                if (dwidth * vheight > vwidth * dheight) {
763                    scale = (float) vheight / (float) dheight;
764                    dx = (vwidth - dwidth * scale) * 0.5f;
765                } else {
766                    scale = (float) vwidth / (float) dwidth;
767                    dy = (vheight - dheight * scale) * 0.5f;
768                }
769
770                mDrawMatrix.setScale(scale, scale);
771                mDrawMatrix.postTranslate(dx, dy);
772            } else if (ScaleType.CENTER_INSIDE == mScaleType) {
773                mDrawMatrix = mMatrix;
774                float scale;
775                float dx;
776                float dy;
777
778                if (dwidth <= vwidth && dheight <= vheight) {
779                    scale = 1.0f;
780                } else {
781                    scale = Math.min((float) vwidth / (float) dwidth,
782                            (float) vheight / (float) dheight);
783                }
784
785                dx = (vwidth - dwidth * scale) * 0.5f;
786                dy = (vheight - dheight * scale) * 0.5f;
787
788                mDrawMatrix.setScale(scale, scale);
789                mDrawMatrix.postTranslate(dx, dy);
790            } else {
791                // Generate the required transform.
792                mTempSrc.set(0, 0, dwidth, dheight);
793                mTempDst.set(0, 0, vwidth, vheight);
794
795                mDrawMatrix = mMatrix;
796                mDrawMatrix.setRectToRect(mTempSrc, mTempDst,
797                                          scaleTypeToScaleToFit(mScaleType));
798            }
799        }
800    }
801
802    @Override
803    protected void drawableStateChanged() {
804        super.drawableStateChanged();
805        Drawable d = mDrawable;
806        if (d != null && d.isStateful()) {
807            d.setState(getDrawableState());
808        }
809    }
810
811    @Override
812    protected void onDraw(Canvas canvas) {
813        super.onDraw(canvas);
814
815        if (mDrawable == null) {
816            return; // couldn't resolve the URI
817        }
818
819        if (mDrawableWidth == 0 || mDrawableHeight == 0) {
820            return;     // nothing to draw (empty bounds)
821        }
822
823        if (mDrawMatrix == null && mPaddingTop == 0 && mPaddingLeft == 0) {
824            mDrawable.draw(canvas);
825        } else {
826            int saveCount = canvas.getSaveCount();
827            canvas.save();
828
829            if (mCropToPadding) {
830                final int scrollX = mScrollX;
831                final int scrollY = mScrollY;
832                canvas.clipRect(scrollX + mPaddingLeft, scrollY + mPaddingTop,
833                        scrollX + mRight - mLeft - mPaddingRight,
834                        scrollY + mBottom - mTop - mPaddingBottom);
835            }
836
837            canvas.translate(mPaddingLeft, mPaddingTop);
838
839            if (mDrawMatrix != null) {
840                canvas.concat(mDrawMatrix);
841            }
842            mDrawable.draw(canvas);
843            canvas.restoreToCount(saveCount);
844        }
845    }
846
847    @Override
848    public int getBaseline() {
849        return mBaselineAligned ? getMeasuredHeight() : -1;
850    }
851
852    /**
853     * Set a tinting option for the image.
854     *
855     * @param color Color tint to apply.
856     * @param mode How to apply the color.  The standard mode is
857     * {@link PorterDuff.Mode#SRC_ATOP}
858     *
859     * @attr ref android.R.styleable#ImageView_tint
860     */
861    public final void setColorFilter(int color, PorterDuff.Mode mode) {
862        setColorFilter(new PorterDuffColorFilter(color, mode));
863    }
864
865    public final void clearColorFilter() {
866        setColorFilter(null);
867    }
868
869    /**
870     * Apply an arbitrary colorfilter to the image.
871     *
872     * @param cf the colorfilter to apply (may be null)
873     */
874    public void setColorFilter(ColorFilter cf) {
875        if (mColorFilter != cf) {
876            mColorFilter = cf;
877            applyColorMod();
878            invalidate();
879        }
880    }
881
882    public void setAlpha(int alpha) {
883        alpha &= 0xFF;          // keep it legal
884        if (mAlpha != alpha) {
885            mAlpha = alpha;
886            applyColorMod();
887            invalidate();
888        }
889    }
890
891    private void applyColorMod() {
892        if (mDrawable != null) {
893            mDrawable.setColorFilter(mColorFilter);
894            mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8);
895        }
896    }
897}
898