Skip to content

Commit 61c5f08

Browse files
tomaszrybakiewiczrunner
andauthored
[Android Auto] Optimized SpeedLimitWidget memory usage (#6859)
* [Android Auto] Optimized SpeedLimitWidget Bitmap usage by utilizing LruBitmapPool - Moved SpeedLimitWidget draw logic to separate Drawable classes. * Rename changelog files * [Android Auto] Addressing PR feedback. Renamed SpeedLimitBitmapProvider -> SpeedLimitBitmapRenderer. Moved MutcdSpeedLimitDrawable and ViennaSpeedLimitDrawable to separate files Co-authored-by: runner <runner@fv-az853-792>
1 parent ba16db2 commit 61c5f08

File tree

8 files changed

+483
-278
lines changed

8 files changed

+483
-278
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Optimized `SpeedLimitWidget` memory usage.

libnavui-androidauto/src/androidTest/java/com/mapbox/androidauto/car/navigation/speedlimit/SpeedLimitRendererTest.kt

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import android.Manifest
44
import androidx.test.filters.SmallTest
55
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
66
import androidx.test.rule.GrantPermissionRule
7-
import com.mapbox.androidauto.navigation.speedlimit.SpeedLimitWidget
7+
import com.mapbox.androidauto.navigation.speedlimit.SpeedLimitBitmapRenderer
88
import com.mapbox.androidauto.testing.BitmapTestUtil
9+
import com.mapbox.navigation.base.speed.model.SpeedLimitSign
910
import org.junit.Rule
1011
import org.junit.Test
1112
import org.junit.rules.TestName
@@ -32,85 +33,133 @@ class SpeedLimitRendererTest {
3233

3334
@Test
3435
fun speed_limit_120_speed_150_mutcd() {
35-
val bitmap =
36-
SpeedLimitWidget.drawMutcdSpeedLimitSign(speedLimit = 120, speed = 150, warn = true)
36+
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
37+
SpeedLimitSign.MUTCD,
38+
speedLimit = 120,
39+
speed = 150,
40+
warn = true
41+
)
3742
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
3843
}
3944

4045
@Test
4146
fun speed_limit_120_speed_90_mutcd() {
42-
val bitmap =
43-
SpeedLimitWidget.drawMutcdSpeedLimitSign(speedLimit = 120, speed = 90, warn = false)
47+
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
48+
SpeedLimitSign.MUTCD,
49+
speedLimit = 120,
50+
speed = 90,
51+
warn = false
52+
)
4453
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
4554
}
4655

4756
@Test
4857
fun speed_limit_65_speed_30_mutcd() {
49-
val bitmap =
50-
SpeedLimitWidget.drawMutcdSpeedLimitSign(speedLimit = 65, speed = 30, warn = false)
58+
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
59+
SpeedLimitSign.MUTCD,
60+
speedLimit = 65,
61+
speed = 30,
62+
warn = false
63+
)
5164
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
5265
}
5366

5467
@Test
5568
fun speed_limit_5_speed_30_mutcd() {
56-
val bitmap =
57-
SpeedLimitWidget.drawMutcdSpeedLimitSign(speedLimit = 5, speed = 30, warn = true)
69+
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
70+
SpeedLimitSign.MUTCD,
71+
speedLimit = 5,
72+
speed = 30,
73+
warn = true
74+
)
5875
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
5976
}
6077

6178
@Test
6279
fun speed_limit_5_speed_0_mutcd() {
63-
val bitmap =
64-
SpeedLimitWidget.drawMutcdSpeedLimitSign(speedLimit = 5, speed = 0, warn = false)
80+
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
81+
SpeedLimitSign.MUTCD,
82+
speedLimit = 5,
83+
speed = 0,
84+
warn = false
85+
)
6586
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
6687
}
6788

6889
@Test
6990
fun speed_limit_unknown_speed_5_mutcd() {
70-
val bitmap =
71-
SpeedLimitWidget.drawMutcdSpeedLimitSign(speedLimit = null, speed = 5, warn = false)
91+
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
92+
SpeedLimitSign.MUTCD,
93+
speedLimit = null,
94+
speed = 5,
95+
warn = false
96+
)
7297
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
7398
}
7499

75100
@Test
76101
fun speed_limit_120_speed_150_vienna() {
77-
val bitmap =
78-
SpeedLimitWidget.drawViennaSpeedLimitSign(speedLimit = 120, speed = 150, warn = true)
102+
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
103+
SpeedLimitSign.VIENNA,
104+
speedLimit = 120,
105+
speed = 150,
106+
warn = true
107+
)
79108
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
80109
}
81110

82111
@Test
83112
fun speed_limit_120_speed_90_vienna() {
84-
val bitmap =
85-
SpeedLimitWidget.drawViennaSpeedLimitSign(speedLimit = 120, speed = 90, warn = false)
113+
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
114+
SpeedLimitSign.VIENNA,
115+
speedLimit = 120,
116+
speed = 90,
117+
warn = false
118+
)
86119
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
87120
}
88121

89122
@Test
90123
fun speed_limit_65_speed_30_vienna() {
91-
val bitmap =
92-
SpeedLimitWidget.drawViennaSpeedLimitSign(speedLimit = 65, speed = 30, warn = false)
124+
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
125+
SpeedLimitSign.VIENNA,
126+
speedLimit = 65,
127+
speed = 30,
128+
warn = false
129+
)
93130
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
94131
}
95132

96133
@Test
97134
fun speed_limit_5_speed_30_vienna() {
98-
val bitmap =
99-
SpeedLimitWidget.drawViennaSpeedLimitSign(speedLimit = 5, speed = 30, warn = true)
135+
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
136+
SpeedLimitSign.VIENNA,
137+
speedLimit = 5,
138+
speed = 30,
139+
warn = true
140+
)
100141
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
101142
}
102143

103144
@Test
104145
fun speed_limit_5_speed_0_vienna() {
105-
val bitmap =
106-
SpeedLimitWidget.drawViennaSpeedLimitSign(speedLimit = 5, speed = 0, warn = false)
146+
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
147+
SpeedLimitSign.VIENNA,
148+
speedLimit = 5,
149+
speed = 0,
150+
warn = false
151+
)
107152
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
108153
}
109154

110155
@Test
111156
fun speed_limit_unknown_speed_5_vienna() {
112-
val bitmap =
113-
SpeedLimitWidget.drawViennaSpeedLimitSign(speedLimit = null, speed = 5, warn = false)
157+
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
158+
SpeedLimitSign.VIENNA,
159+
speedLimit = null,
160+
speed = 5,
161+
warn = false
162+
)
114163
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
115164
}
116165
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package com.mapbox.androidauto.navigation.speedlimit
2+
3+
import android.graphics.Canvas
4+
import android.graphics.Color
5+
import android.graphics.Rect
6+
7+
internal class MutcdSpeedLimitDrawable : SpeedLimitDrawable() {
8+
companion object {
9+
const val WIDTH = 77
10+
const val HEIGHT = 115
11+
const val BITMAP_BYTE_SIZE: Long =
12+
(WIDTH * HEIGHT * BYTES_PER_ARGB_8888_PIXEL).toLong()
13+
const val HEIGHT_SIGN = 67f
14+
const val RADIUS = 9f
15+
const val STROKE_SIGN = 2f
16+
const val TITLE_1 = "SPEED"
17+
const val TITLE_2 = "LIMIT"
18+
const val STROKE_PADDING = 2f
19+
}
20+
21+
private val speedLimitTextPaint = createTextPaint(Color.BLACK, textSize = 27f)
22+
private val signBorderPaint = createBackgroundPaint(COLOR_BORDER)
23+
24+
private val titleRect1 = Rect().apply {
25+
titlePaint.getTextBounds(TITLE_1, 0, TITLE_1.length, this)
26+
}
27+
private val titleRect2 = Rect().apply {
28+
titlePaint.getTextBounds(TITLE_2, 0, TITLE_2.length, this)
29+
}
30+
private val borderRect = createFullRect(WIDTH, HEIGHT, inset = 0f)
31+
private val backgroundRect = createFullRect(WIDTH, HEIGHT, STROKE)
32+
private val signBorderRect =
33+
createRect(WIDTH, HEIGHT_SIGN, inset = STROKE + STROKE_PADDING)
34+
private val signBackgroundRect = createRect(
35+
WIDTH,
36+
HEIGHT_SIGN,
37+
inset = STROKE + STROKE_PADDING + STROKE_SIGN,
38+
)
39+
40+
override fun draw(canvas: Canvas) {
41+
drawShadows(canvas)
42+
drawBackground(canvas)
43+
drawSignBackground(canvas)
44+
drawSignSpeedLimitText(canvas)
45+
drawCurrentSpeedText(canvas)
46+
}
47+
48+
private fun drawShadows(canvas: Canvas) {
49+
borderPaint.setShadowLayer(RADIUS_SHADOW_SMALL, 0f, OFFSET_SHADOW_SMALL, COLOR_SHADOW)
50+
canvas.drawRoundRect(borderRect, RADIUS, RADIUS, borderPaint)
51+
borderPaint.setShadowLayer(RADIUS_SHADOW, 0f, OFFSET_SHADOW, COLOR_SHADOW)
52+
canvas.drawRoundRect(borderRect, RADIUS, RADIUS, borderPaint)
53+
}
54+
55+
private fun drawBackground(canvas: Canvas) {
56+
canvas.drawRoundRect(
57+
backgroundRect,
58+
RADIUS - STROKE,
59+
RADIUS - STROKE,
60+
if (warn) backgroundPaintWarning else backgroundPaintNormal,
61+
)
62+
}
63+
64+
private fun drawSignBackground(canvas: Canvas) {
65+
val radiusSignBorder = RADIUS - STROKE - STROKE_PADDING
66+
canvas.drawRoundRect(
67+
signBorderRect,
68+
radiusSignBorder,
69+
radiusSignBorder,
70+
signBorderPaint,
71+
)
72+
canvas.drawRoundRect(
73+
signBackgroundRect,
74+
radiusSignBorder - STROKE_SIGN,
75+
radiusSignBorder - STROKE_SIGN,
76+
backgroundPaintNormal,
77+
)
78+
79+
val titleY1 = signBackgroundRect.top + 7.5f - titleRect1.exactCenterY()
80+
canvas.drawText(TITLE_1, WIDTH / 2f, titleY1, titlePaint)
81+
val titleY2 = signBackgroundRect.top + 19.5f - titleRect2.exactCenterY()
82+
canvas.drawText(TITLE_2, WIDTH / 2f, titleY2, titlePaint)
83+
}
84+
85+
private fun drawSignSpeedLimitText(canvas: Canvas) {
86+
val speedLimitText = speedLimit?.toString() ?: SPEED_LIMIT_NO_DATA
87+
speedLimitTextPaint.getTextBounds(
88+
speedLimitText,
89+
0,
90+
speedLimitText.length,
91+
speedLimitRect,
92+
)
93+
val speedLimitY = signBackgroundRect.top + 41.5f - speedLimitRect.exactCenterY()
94+
canvas.drawText(speedLimitText, WIDTH / 2f, speedLimitY, speedLimitPaintVienna)
95+
}
96+
97+
private fun drawCurrentSpeedText(canvas: Canvas) {
98+
val speedText = speed.toString()
99+
val speedPaint = if (warn) speedPaintWarning else speedPaintNormal
100+
speedPaint.getTextBounds(speedText, 0, speedText.length, speedRect)
101+
val speedY = signBorderRect.bottom + 14 - speedRect.exactCenterY()
102+
canvas.drawText(speedText, WIDTH / 2f, speedY, speedPaint)
103+
}
104+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.mapbox.androidauto.navigation.speedlimit
2+
3+
import android.graphics.Bitmap
4+
import android.graphics.Canvas
5+
import com.bumptech.glide.load.engine.bitmap_recycle.LruBitmapPool
6+
import com.mapbox.navigation.base.speed.model.SpeedLimitSign
7+
8+
internal class SpeedLimitBitmapRenderer {
9+
private val mutcdDrawable: SpeedLimitDrawable = MutcdSpeedLimitDrawable()
10+
private val viennaDrawable: SpeedLimitDrawable = ViennaSpeedLimitDrawable()
11+
private val bitmapPool: LruBitmapPool = LruBitmapPool(
12+
MutcdSpeedLimitDrawable.BITMAP_BYTE_SIZE + ViennaSpeedLimitDrawable.BITMAP_BYTE_SIZE
13+
)
14+
15+
fun getBitmap(
16+
signFormat: SpeedLimitSign,
17+
speedLimit: Int? = null,
18+
speed: Int = 0,
19+
warn: Boolean = false,
20+
): Bitmap {
21+
val drawable = when (signFormat) {
22+
SpeedLimitSign.MUTCD -> mutcdDrawable
23+
SpeedLimitSign.VIENNA -> viennaDrawable
24+
}
25+
drawable.speedLimit = speedLimit
26+
drawable.speed = speed
27+
drawable.warn = warn
28+
29+
val bitmap = bitmapPool.get(signFormat)
30+
drawable.draw(Canvas(bitmap))
31+
bitmapPool.put(bitmap)
32+
return bitmap
33+
}
34+
35+
private fun LruBitmapPool.get(sign: SpeedLimitSign): Bitmap {
36+
return when (sign) {
37+
SpeedLimitSign.MUTCD -> get(
38+
MutcdSpeedLimitDrawable.WIDTH,
39+
MutcdSpeedLimitDrawable.HEIGHT,
40+
Bitmap.Config.ARGB_8888
41+
)
42+
SpeedLimitSign.VIENNA -> get(
43+
ViennaSpeedLimitDrawable.WIDTH,
44+
ViennaSpeedLimitDrawable.HEIGHT,
45+
Bitmap.Config.ARGB_8888
46+
)
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)