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