• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 package android.support.v17.leanback.graphics;
17 
18 import android.support.annotation.RequiresApi;
19 import android.graphics.Bitmap;
20 import android.graphics.Canvas;
21 import android.graphics.ColorFilter;
22 import android.graphics.Paint;
23 import android.graphics.PixelFormat;
24 import android.graphics.Rect;
25 import android.graphics.drawable.Drawable;
26 import android.os.Build;
27 import android.support.annotation.NonNull;
28 import android.util.IntProperty;
29 import android.util.Property;
30 
31 /**
32  * Subclass of {@link Drawable} that can be used to draw a bitmap into a region. Bitmap
33  * will be scaled to fit the full width of the region and will be aligned to the top left corner.
34  * Any region outside the bounds will be clipped during {@link #draw(Canvas)} call. Top
35  * position of the bitmap can be controlled by {@link #setVerticalOffset(int)} call or
36  * {@link #PROPERTY_VERTICAL_OFFSET}.
37  */
38 public class FitWidthBitmapDrawable extends Drawable {
39 
40     static class BitmapState extends Drawable.ConstantState {
41         Paint mPaint;
42         Bitmap mBitmap;
43         Rect mSource;
44         final Rect mDefaultSource = new Rect();
45         int mOffset;
46 
BitmapState()47         BitmapState() {
48             mPaint = new Paint();
49         }
50 
BitmapState(BitmapState other)51         BitmapState(BitmapState other) {
52             mBitmap = other.mBitmap;
53             mPaint = new Paint(other.mPaint);
54             mSource = other.mSource != null ? new Rect(other.mSource) : null;
55             mDefaultSource.set(other.mDefaultSource);
56             mOffset = other.mOffset;
57         }
58 
59         @NonNull
60         @Override
newDrawable()61         public Drawable newDrawable() {
62             return new FitWidthBitmapDrawable(this);
63         }
64 
65         @Override
getChangingConfigurations()66         public int getChangingConfigurations() {
67             return 0;
68         }
69     }
70 
71     final Rect mDest = new Rect();
72     BitmapState mBitmapState;
73     boolean mMutated = false;
74 
FitWidthBitmapDrawable()75     public FitWidthBitmapDrawable() {
76         mBitmapState = new BitmapState();
77     }
78 
FitWidthBitmapDrawable(BitmapState state)79     FitWidthBitmapDrawable(BitmapState state) {
80         mBitmapState = state;
81     }
82 
83     @Override
getConstantState()84     public ConstantState getConstantState() {
85         return mBitmapState;
86     }
87 
88     @Override
mutate()89     public Drawable mutate() {
90         if (!mMutated && super.mutate() == this) {
91             mBitmapState = new BitmapState(mBitmapState);
92             mMutated = true;
93         }
94         return this;
95     }
96 
97     /**
98      * Sets the bitmap.
99      */
setBitmap(Bitmap bitmap)100     public void setBitmap(Bitmap bitmap) {
101         mBitmapState.mBitmap = bitmap;
102         if (bitmap != null) {
103             mBitmapState.mDefaultSource.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
104         } else {
105             mBitmapState.mDefaultSource.set(0, 0, 0, 0);
106         }
107         mBitmapState.mSource = null;
108     }
109 
110     /**
111      * Returns the bitmap.
112      */
getBitmap()113     public Bitmap getBitmap() {
114         return mBitmapState.mBitmap;
115     }
116 
117     /**
118      * Sets the {@link Rect} used for extracting the bitmap.
119      */
setSource(Rect source)120     public void setSource(Rect source) {
121         mBitmapState.mSource = source;
122     }
123 
124     /**
125      * Returns the {@link Rect} used for extracting the bitmap.
126      */
getSource()127     public Rect getSource() {
128         return mBitmapState.mSource;
129     }
130 
131     /**
132      * Sets the vertical offset which will be used for drawing the bitmap. The bitmap drawing
133      * will start the provided vertical offset.
134      * @see #PROPERTY_VERTICAL_OFFSET
135      */
setVerticalOffset(int offset)136     public void setVerticalOffset(int offset) {
137         mBitmapState.mOffset = offset;
138         invalidateSelf();
139     }
140 
141     /**
142      * Returns the current vertical offset.
143      * @see #PROPERTY_VERTICAL_OFFSET
144      */
getVerticalOffset()145     public int getVerticalOffset() {
146         return mBitmapState.mOffset;
147     }
148 
149     @Override
draw(Canvas canvas)150     public void draw(Canvas canvas) {
151         if (mBitmapState.mBitmap != null) {
152             Rect bounds = getBounds();
153             mDest.left = 0;
154             mDest.top = mBitmapState.mOffset;
155             mDest.right = bounds.width();
156 
157             Rect source = validateSource();
158             float scale = (float) bounds.width() / source.width();
159             mDest.bottom = mDest.top + (int) (source.height() * scale);
160             int i = canvas.save();
161             canvas.clipRect(bounds);
162             canvas.drawBitmap(mBitmapState.mBitmap, source, mDest, mBitmapState.mPaint);
163             canvas.restoreToCount(i);
164         }
165     }
166 
167     @Override
setAlpha(int alpha)168     public void setAlpha(int alpha) {
169         final int oldAlpha = mBitmapState.mPaint.getAlpha();
170         if (alpha != oldAlpha) {
171             mBitmapState.mPaint.setAlpha(alpha);
172             invalidateSelf();
173         }
174     }
175 
176     /**
177      * @return Alpha value between 0(inclusive) and 255(inclusive)
178      */
179     @Override
getAlpha()180     public int getAlpha() {
181         return mBitmapState.mPaint.getAlpha();
182     }
183 
184     @Override
setColorFilter(ColorFilter colorFilter)185     public void setColorFilter(ColorFilter colorFilter) {
186         mBitmapState.mPaint.setColorFilter(colorFilter);
187         invalidateSelf();
188     }
189 
190     @Override
getOpacity()191     public int getOpacity() {
192         final Bitmap bitmap = mBitmapState.mBitmap;
193         return (bitmap == null || bitmap.hasAlpha() || mBitmapState.mPaint.getAlpha() < 255)
194                 ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
195     }
196 
validateSource()197     private Rect validateSource() {
198         if (mBitmapState.mSource == null) {
199             return mBitmapState.mDefaultSource;
200         } else {
201             return mBitmapState.mSource;
202         }
203     }
204 
205     /**
206      * Property for {@link #setVerticalOffset(int)} and {@link #getVerticalOffset()}.
207      */
208     public static final Property<FitWidthBitmapDrawable, Integer> PROPERTY_VERTICAL_OFFSET;
209 
210     static {
211         if (Build.VERSION.SDK_INT >= 24) {
212             // use IntProperty
213             PROPERTY_VERTICAL_OFFSET = getVerticalOffsetIntProperty();
214         } else {
215             // use Property
216             PROPERTY_VERTICAL_OFFSET = new Property<FitWidthBitmapDrawable, Integer>(Integer.class,
217                     "verticalOffset") {
218                 @Override
219                 public void set(FitWidthBitmapDrawable object, Integer value) {
220                     object.setVerticalOffset(value);
221                 }
222 
223                 @Override
224                 public Integer get(FitWidthBitmapDrawable object) {
225                     return object.getVerticalOffset();
226                 }
227             };
228         }
229     }
230 
231     @RequiresApi(24)
getVerticalOffsetIntProperty()232     static IntProperty<FitWidthBitmapDrawable> getVerticalOffsetIntProperty() {
233         return new IntProperty<FitWidthBitmapDrawable>("verticalOffset") {
234             @Override
235             public void setValue(FitWidthBitmapDrawable fitWidthBitmapDrawable, int value) {
236                 fitWidthBitmapDrawable.setVerticalOffset(value);
237             }
238 
239             @Override
240             public Integer get(FitWidthBitmapDrawable fitWidthBitmapDrawable) {
241                 return fitWidthBitmapDrawable.getVerticalOffset();
242             }
243         };
244     }
245 }
246