Skip to content

Instantly share code, notes, and snippets.

@rock3r
Created August 20, 2023 10:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rock3r/b3823998391b562a6b092251575144f8 to your computer and use it in GitHub Desktop.
Save rock3r/b3823998391b562a6b092251575144f8 to your computer and use it in GitHub Desktop.
Draw text on a bitmap
@OptIn(ExperimentalAnimationApi::class)
@Composable
private fun DrawText(it: PaddingValues) {
Column(Modifier.padding(it)) {
var text by remember { mutableStateOf("Vel eum voluptatem nulla. Ut enim unde sit autem laboriosam consequatur ut. Quo eius veniam occaecati minima. Porro architecto et omnis. Et dolor qui non. Tempora et consequuntur ea quia iste qui ducimus cum.\n") }
OutlinedTextField(value = text, onValueChange = { text = it.trim() })
val baseFontSizePx = with(LocalDensity.current) { 24.sp.toPx() }
val context = LocalContext.current
var bitmap by remember { mutableStateOf<ImageBitmap?>(null) }
LaunchedEffect(key1 = text) {
bitmap = null
launch {
val baseDrawable = ResourcesCompat.getDrawable(context.resources, R.drawable.myImage, context.theme)
as? BitmapDrawable ?: error("Damnit")
val bitmapWithText = baseDrawable.bitmap.drawTextOnTop(text, baseFontSizePx, context.resources)
bitmap = bitmapWithText.asImageBitmap()
}
}
Spacer(modifier = Modifier.weight(1f))
AnimatedContent(targetState = bitmap, label = "My thing") { imageBitmap ->
if (imageBitmap == null) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(100.dp), contentAlignment = Alignment.Center
) {
CircularProgressIndicator()
}
} else {
Image(painter = BitmapPainter(imageBitmap), contentDescription = text)
}
}
}
}
private fun Bitmap.drawTextOnTop(text: String, textSize: Float, resources: Resources): Bitmap =
drawTextOnBitmap(this, text, textSize, Color.White, resources.getFont(R.font.pacifico))
?: this
private fun drawTextOnBitmap(bitmap: Bitmap, text: String, textSize: Float, textColor: Color, typeface: android.graphics.Typeface): Bitmap? {
if (textSize <= 0f) {
Timber.tag("DrawText").e("Text is too big to fit")
return null
}
val paint = TextPaint().apply {
this.textSize = textSize
this.color = textColor.toArgb()
this.typeface = typeface
this.style = Paint.Style.FILL
}
val availableWidth = bitmap.width * .9f // 10% (5% + 5%) padding
val layout = StaticLayout.Builder.obtain(text, 0, text.length, paint, availableWidth.toInt())
.setAlignment(Layout.Alignment.ALIGN_CENTER)
.build()
// Get the size of the static layout.
val layoutWidth = layout.width
val layoutHeight = layout.height
// Make sure that the static layout fits in the bitmap.
val availableHeight = bitmap.height * .9f // 10% (5% + 5%) padding
if (layoutWidth > availableWidth || layoutHeight > availableHeight) {
// The static layout is too wide. Reduce the text size.
return drawTextOnBitmap(bitmap, text, textSize - 1f, textColor, typeface)
}
// Draw the static layout on the bitmap.
val newBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true)
val canvas = Canvas(newBitmap)
canvas.withSave {
// Apply padding by translating to 5% of width and height before drawing
translate(bitmap.width * .05f, bitmap.height * .05f)
layout.draw(canvas)
}
return newBitmap
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment