• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.systemui.util.drawable
2 
3 import android.content.res.Resources
4 import android.graphics.Bitmap
5 import android.graphics.Canvas
6 import android.graphics.drawable.Animatable
7 import android.graphics.drawable.Animatable2
8 import android.graphics.drawable.AnimatedImageDrawable
9 import android.graphics.drawable.AnimatedRotateDrawable
10 import android.graphics.drawable.AnimatedStateListDrawable
11 import android.graphics.drawable.AnimatedVectorDrawable
12 import android.graphics.drawable.BitmapDrawable
13 import android.graphics.drawable.Drawable
14 import android.util.Log
15 import androidx.annotation.Px
16 import com.android.systemui.util.traceSection
17 
18 class DrawableSize {
19 
20     companion object {
21 
22         const val TAG = "SysUiDrawableSize"
23 
24         /**
25          * Downscales passed Drawable to set maximum width and height. This will only
26          * be done for Drawables that can be downscaled non-destructively - e.g. animated
27          * and stateful drawables will no be downscaled.
28          *
29          * Downscaling will keep the aspect ratio.
30          * This method will not touch drawables that already fit into size specification.
31          *
32          * @param resources Resources on which to base the density of resized drawable.
33          * @param drawable Drawable to downscale.
34          * @param maxWidth Maximum width of the downscaled drawable.
35          * @param maxHeight Maximum height of the downscaled drawable.
36          *
37          * @return returns downscaled drawable if it's possible to downscale it or original if it's
38          *         not.
39          */
40         @JvmStatic
downscaleToSizenull41         fun downscaleToSize(
42             res: Resources,
43             drawable: Drawable,
44             @Px maxWidth: Int,
45             @Px maxHeight: Int
46         ): Drawable {
47             traceSection("DrawableSize#downscaleToSize") {
48                 // Bitmap drawables can contain big bitmaps as their content while sneaking it past
49                 // us using density scaling. Inspect inside the Bitmap drawables for actual bitmap
50                 // size for those.
51                 val originalWidth = (drawable as? BitmapDrawable)?.bitmap?.width
52                                     ?: drawable.intrinsicWidth
53                 val originalHeight = (drawable as? BitmapDrawable)?.bitmap?.height
54                                     ?: drawable.intrinsicHeight
55 
56                 // Don't touch drawable if we can't resolve sizes for whatever reason.
57                 if (originalWidth <= 0 || originalHeight <= 0) {
58                     return drawable
59                 }
60 
61                 // Do not touch drawables that are already within bounds.
62                 if (originalWidth < maxWidth && originalHeight < maxHeight) {
63                     if (Log.isLoggable(TAG, Log.DEBUG)) {
64                         Log.d(TAG, "Not resizing $originalWidth x $originalHeight" + " " +
65                                 "to $maxWidth x $maxHeight")
66                     }
67 
68                     return drawable
69                 }
70 
71                 if (!isSimpleBitmap(drawable)) {
72                     return drawable
73                 }
74 
75                 val scaleWidth = maxWidth.toFloat() / originalWidth.toFloat()
76                 val scaleHeight = maxHeight.toFloat() / originalHeight.toFloat()
77                 val scale = minOf(scaleHeight, scaleWidth)
78 
79                 val width = (originalWidth * scale).toInt()
80                 val height = (originalHeight * scale).toInt()
81 
82                 if (width <= 0 || height <= 0) {
83                     Log.w(TAG, "Attempted to resize ${drawable.javaClass.simpleName} " +
84                             "from $originalWidth x $originalHeight to invalid $width x $height.")
85                     return drawable
86                 }
87 
88                 if (Log.isLoggable(TAG, Log.DEBUG)) {
89                     Log.d(TAG, "Resizing large drawable (${drawable.javaClass.simpleName}) " +
90                             "from $originalWidth x $originalHeight to $width x $height")
91                 }
92 
93                 // We want to keep existing config if it's more efficient than 32-bit RGB.
94                 val config = (drawable as? BitmapDrawable)?.bitmap?.config
95                         ?: Bitmap.Config.ARGB_8888
96                 val scaledDrawableBitmap = Bitmap.createBitmap(width, height, config)
97                 val canvas = Canvas(scaledDrawableBitmap)
98 
99                 val originalBounds = drawable.bounds
100                 drawable.setBounds(0, 0, width, height)
101                 drawable.draw(canvas)
102                 drawable.bounds = originalBounds
103 
104                 return BitmapDrawable(res, scaledDrawableBitmap)
105             }
106         }
107 
isSimpleBitmapnull108         private fun isSimpleBitmap(drawable: Drawable): Boolean {
109             return !(drawable.isStateful || isAnimated(drawable))
110         }
111 
isAnimatednull112         private fun isAnimated(drawable: Drawable): Boolean {
113             if (drawable is Animatable || drawable is Animatable2) {
114                 return true
115             }
116 
117             return drawable is AnimatedImageDrawable ||
118                 drawable is AnimatedRotateDrawable ||
119                 drawable is AnimatedStateListDrawable ||
120                 drawable is AnimatedVectorDrawable
121         }
122     }
123 }