Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Optimized `SpeedLimitWidget` memory usage.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import android.Manifest
import androidx.test.filters.SmallTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.GrantPermissionRule
import com.mapbox.androidauto.navigation.speedlimit.SpeedLimitWidget
import com.mapbox.androidauto.navigation.speedlimit.SpeedLimitBitmapRenderer
import com.mapbox.androidauto.testing.BitmapTestUtil
import com.mapbox.navigation.base.speed.model.SpeedLimitSign
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestName
Expand All @@ -32,85 +33,133 @@ class SpeedLimitRendererTest {

@Test
fun speed_limit_120_speed_150_mutcd() {
val bitmap =
SpeedLimitWidget.drawMutcdSpeedLimitSign(speedLimit = 120, speed = 150, warn = true)
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
SpeedLimitSign.MUTCD,
speedLimit = 120,
speed = 150,
warn = true
)
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
}

@Test
fun speed_limit_120_speed_90_mutcd() {
val bitmap =
SpeedLimitWidget.drawMutcdSpeedLimitSign(speedLimit = 120, speed = 90, warn = false)
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
SpeedLimitSign.MUTCD,
speedLimit = 120,
speed = 90,
warn = false
)
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
}

@Test
fun speed_limit_65_speed_30_mutcd() {
val bitmap =
SpeedLimitWidget.drawMutcdSpeedLimitSign(speedLimit = 65, speed = 30, warn = false)
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
SpeedLimitSign.MUTCD,
speedLimit = 65,
speed = 30,
warn = false
)
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
}

@Test
fun speed_limit_5_speed_30_mutcd() {
val bitmap =
SpeedLimitWidget.drawMutcdSpeedLimitSign(speedLimit = 5, speed = 30, warn = true)
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
SpeedLimitSign.MUTCD,
speedLimit = 5,
speed = 30,
warn = true
)
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
}

@Test
fun speed_limit_5_speed_0_mutcd() {
val bitmap =
SpeedLimitWidget.drawMutcdSpeedLimitSign(speedLimit = 5, speed = 0, warn = false)
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
SpeedLimitSign.MUTCD,
speedLimit = 5,
speed = 0,
warn = false
)
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
}

@Test
fun speed_limit_unknown_speed_5_mutcd() {
val bitmap =
SpeedLimitWidget.drawMutcdSpeedLimitSign(speedLimit = null, speed = 5, warn = false)
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
SpeedLimitSign.MUTCD,
speedLimit = null,
speed = 5,
warn = false
)
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
}

@Test
fun speed_limit_120_speed_150_vienna() {
val bitmap =
SpeedLimitWidget.drawViennaSpeedLimitSign(speedLimit = 120, speed = 150, warn = true)
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
SpeedLimitSign.VIENNA,
speedLimit = 120,
speed = 150,
warn = true
)
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
}

@Test
fun speed_limit_120_speed_90_vienna() {
val bitmap =
SpeedLimitWidget.drawViennaSpeedLimitSign(speedLimit = 120, speed = 90, warn = false)
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
SpeedLimitSign.VIENNA,
speedLimit = 120,
speed = 90,
warn = false
)
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
}

@Test
fun speed_limit_65_speed_30_vienna() {
val bitmap =
SpeedLimitWidget.drawViennaSpeedLimitSign(speedLimit = 65, speed = 30, warn = false)
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
SpeedLimitSign.VIENNA,
speedLimit = 65,
speed = 30,
warn = false
)
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
}

@Test
fun speed_limit_5_speed_30_vienna() {
val bitmap =
SpeedLimitWidget.drawViennaSpeedLimitSign(speedLimit = 5, speed = 30, warn = true)
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
SpeedLimitSign.VIENNA,
speedLimit = 5,
speed = 30,
warn = true
)
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
}

@Test
fun speed_limit_5_speed_0_vienna() {
val bitmap =
SpeedLimitWidget.drawViennaSpeedLimitSign(speedLimit = 5, speed = 0, warn = false)
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
SpeedLimitSign.VIENNA,
speedLimit = 5,
speed = 0,
warn = false
)
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
}

@Test
fun speed_limit_unknown_speed_5_vienna() {
val bitmap =
SpeedLimitWidget.drawViennaSpeedLimitSign(speedLimit = null, speed = 5, warn = false)
val bitmap = SpeedLimitBitmapRenderer().getBitmap(
SpeedLimitSign.VIENNA,
speedLimit = null,
speed = 5,
warn = false
)
bitmapUtils.assertBitmapsSimilar(testName, bitmap)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.mapbox.androidauto.navigation.speedlimit

import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Rect

internal class MutcdSpeedLimitDrawable : SpeedLimitDrawable() {
companion object {
const val WIDTH = 77
const val HEIGHT = 115
const val BITMAP_BYTE_SIZE: Long =
(WIDTH * HEIGHT * BYTES_PER_ARGB_8888_PIXEL).toLong()
const val HEIGHT_SIGN = 67f
const val RADIUS = 9f
const val STROKE_SIGN = 2f
const val TITLE_1 = "SPEED"
const val TITLE_2 = "LIMIT"
const val STROKE_PADDING = 2f
}

private val speedLimitTextPaint = createTextPaint(Color.BLACK, textSize = 27f)
private val signBorderPaint = createBackgroundPaint(COLOR_BORDER)

private val titleRect1 = Rect().apply {
titlePaint.getTextBounds(TITLE_1, 0, TITLE_1.length, this)
}
private val titleRect2 = Rect().apply {
titlePaint.getTextBounds(TITLE_2, 0, TITLE_2.length, this)
}
private val borderRect = createFullRect(WIDTH, HEIGHT, inset = 0f)
private val backgroundRect = createFullRect(WIDTH, HEIGHT, STROKE)
private val signBorderRect =
createRect(WIDTH, HEIGHT_SIGN, inset = STROKE + STROKE_PADDING)
private val signBackgroundRect = createRect(
WIDTH,
HEIGHT_SIGN,
inset = STROKE + STROKE_PADDING + STROKE_SIGN,
)

override fun draw(canvas: Canvas) {
drawShadows(canvas)
drawBackground(canvas)
drawSignBackground(canvas)
drawSignSpeedLimitText(canvas)
drawCurrentSpeedText(canvas)
}

private fun drawShadows(canvas: Canvas) {
borderPaint.setShadowLayer(RADIUS_SHADOW_SMALL, 0f, OFFSET_SHADOW_SMALL, COLOR_SHADOW)
canvas.drawRoundRect(borderRect, RADIUS, RADIUS, borderPaint)
borderPaint.setShadowLayer(RADIUS_SHADOW, 0f, OFFSET_SHADOW, COLOR_SHADOW)
canvas.drawRoundRect(borderRect, RADIUS, RADIUS, borderPaint)
}

private fun drawBackground(canvas: Canvas) {
canvas.drawRoundRect(
backgroundRect,
RADIUS - STROKE,
RADIUS - STROKE,
if (warn) backgroundPaintWarning else backgroundPaintNormal,
)
}

private fun drawSignBackground(canvas: Canvas) {
val radiusSignBorder = RADIUS - STROKE - STROKE_PADDING
canvas.drawRoundRect(
signBorderRect,
radiusSignBorder,
radiusSignBorder,
signBorderPaint,
)
canvas.drawRoundRect(
signBackgroundRect,
radiusSignBorder - STROKE_SIGN,
radiusSignBorder - STROKE_SIGN,
backgroundPaintNormal,
)

val titleY1 = signBackgroundRect.top + 7.5f - titleRect1.exactCenterY()
canvas.drawText(TITLE_1, WIDTH / 2f, titleY1, titlePaint)
val titleY2 = signBackgroundRect.top + 19.5f - titleRect2.exactCenterY()
canvas.drawText(TITLE_2, WIDTH / 2f, titleY2, titlePaint)
}

private fun drawSignSpeedLimitText(canvas: Canvas) {
val speedLimitText = speedLimit?.toString() ?: SPEED_LIMIT_NO_DATA
speedLimitTextPaint.getTextBounds(
speedLimitText,
0,
speedLimitText.length,
speedLimitRect,
)
val speedLimitY = signBackgroundRect.top + 41.5f - speedLimitRect.exactCenterY()
canvas.drawText(speedLimitText, WIDTH / 2f, speedLimitY, speedLimitPaintVienna)
}

private fun drawCurrentSpeedText(canvas: Canvas) {
val speedText = speed.toString()
val speedPaint = if (warn) speedPaintWarning else speedPaintNormal
speedPaint.getTextBounds(speedText, 0, speedText.length, speedRect)
val speedY = signBorderRect.bottom + 14 - speedRect.exactCenterY()
canvas.drawText(speedText, WIDTH / 2f, speedY, speedPaint)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.mapbox.androidauto.navigation.speedlimit

import android.graphics.Bitmap
import android.graphics.Canvas
import com.bumptech.glide.load.engine.bitmap_recycle.LruBitmapPool
import com.mapbox.navigation.base.speed.model.SpeedLimitSign

internal class SpeedLimitBitmapRenderer {
private val mutcdDrawable: SpeedLimitDrawable = MutcdSpeedLimitDrawable()
private val viennaDrawable: SpeedLimitDrawable = ViennaSpeedLimitDrawable()
private val bitmapPool: LruBitmapPool = LruBitmapPool(
MutcdSpeedLimitDrawable.BITMAP_BYTE_SIZE + ViennaSpeedLimitDrawable.BITMAP_BYTE_SIZE
)

fun getBitmap(
signFormat: SpeedLimitSign,
speedLimit: Int? = null,
speed: Int = 0,
warn: Boolean = false,
): Bitmap {
val drawable = when (signFormat) {
SpeedLimitSign.MUTCD -> mutcdDrawable
SpeedLimitSign.VIENNA -> viennaDrawable
}
drawable.speedLimit = speedLimit
drawable.speed = speed
drawable.warn = warn

val bitmap = bitmapPool.get(signFormat)
drawable.draw(Canvas(bitmap))
bitmapPool.put(bitmap)
return bitmap
}

private fun LruBitmapPool.get(sign: SpeedLimitSign): Bitmap {
return when (sign) {
SpeedLimitSign.MUTCD -> get(
MutcdSpeedLimitDrawable.WIDTH,
MutcdSpeedLimitDrawable.HEIGHT,
Bitmap.Config.ARGB_8888
)
SpeedLimitSign.VIENNA -> get(
ViennaSpeedLimitDrawable.WIDTH,
ViennaSpeedLimitDrawable.HEIGHT,
Bitmap.Config.ARGB_8888
)
}
}
}
Loading