package cn.flightfeather.thirdapp.util.photo
|
|
import android.app.Activity
|
import android.content.res.Resources
|
import android.graphics.*
|
import android.os.Build
|
import android.support.annotation.ColorInt
|
import android.support.annotation.IdRes
|
import android.support.annotation.IntegerRes
|
import android.util.DisplayMetrics
|
import cn.flightfeather.thirdapp.R
|
import cn.flightfeather.thirdapp.util.DateFormatter
|
import cn.flightfeather.thirdapp.util.dp
|
import cn.flightfeather.thirdapp.util.sp
|
import java.util.*
|
import java.util.regex.Pattern
|
import kotlin.math.max
|
import kotlin.math.roundToInt
|
|
/**
|
* @author riku
|
* Date: 2020/5/9
|
* 图片、文本合并工具
|
* 规定合并的图片为1~3张, 其中纵向的图片规定宽度保持为屏幕宽度的1/3进行缩放, 横向的图则根据图片张数和横纵组合自适应,具体看代码.
|
*/
|
class ImageMergeUtil(activity: Activity) {
|
|
private val padding = 4F.dp
|
private val bgWidth:Float
|
private val bgHeight: Float
|
private val textPaint:Paint
|
private val indexPaint:Paint
|
private val titlePaint:Paint
|
|
//设定每行最大图片数
|
val colNum = 3
|
|
init {
|
val dm = DisplayMetrics()
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
activity.windowManager.defaultDisplay.getRealMetrics(dm)
|
} else {
|
activity.windowManager.defaultDisplay.getMetrics(dm)
|
}
|
bgWidth = dm.widthPixels.toFloat()
|
bgHeight = dm.heightPixels.toFloat()
|
|
textPaint = Paint().apply {
|
textAlign = Paint.Align.LEFT
|
color = Color.BLACK
|
style = Paint.Style.FILL
|
// strokeWidth = density * 2
|
isAntiAlias = true
|
textSize = 14f.sp
|
}
|
indexPaint = Paint().apply {
|
textAlign = Paint.Align.CENTER
|
color = Color.BLACK
|
style = Paint.Style.FILL
|
// strokeWidth = density * 2
|
isAntiAlias = true
|
textSize = 14f.sp
|
}
|
titlePaint = Paint().apply {
|
textAlign = Paint.Align.LEFT
|
color = Color.BLACK
|
style = Paint.Style.FILL
|
isAntiAlias = true
|
textSize = 14f.sp
|
typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
|
}
|
}
|
|
fun mergeImage(bitmaps: MutableList<Bitmap>): Bitmap? {
|
var result: Bitmap? = null
|
val lengthWays = isLengthWays(bitmaps)
|
|
when (bitmaps.size) {
|
1 -> {
|
when (lengthWays) {
|
"h" -> {
|
val b = resizeBitmap(bitmaps[0], (bgWidth - padding * 4) / 3)
|
result = createCanvas(b.width + padding.toInt() * 2, b.height + padding.toInt() * 2) {
|
it.drawBitmap(b, padding, padding, null)
|
}
|
}
|
"w" -> {
|
val b = resizeBitmap(bitmaps[0], newHeight = (bgHeight - padding * 2) / 4)
|
result = createCanvas(b.width + padding.toInt() * 2, b.height + padding.toInt() * 2) {
|
it.drawBitmap(b, padding, padding, null)
|
}
|
}
|
}
|
}
|
2 -> {
|
when (lengthWays) {
|
"hh" -> {
|
val b0 = resizeBitmap(bitmaps[0], (bgWidth - padding * 4) / 3)
|
val b1 = resizeBitmap(bitmaps[1], (bgWidth - padding * 4) / 3)
|
result = createCanvas(b0.width + b1.width + padding.toInt() * 3, b0.height + padding.toInt() * 2) {
|
it.drawBitmap(b0, padding, padding, null)
|
it.drawBitmap(b1, b0.width + padding * 2, padding, null)
|
}
|
}
|
"hw" -> {
|
val b0 = resizeBitmap(bitmaps[0], (bgWidth - padding * 4) / 3)
|
val b1 = resizeBitmap(bitmaps[1], newHeight = b0.height.toFloat())
|
result = createCanvas(b0.width + b1.width + padding.toInt() * 3, b0.height + padding.toInt() * 2) {
|
it.drawBitmap(b0, padding, padding, null)
|
it.drawBitmap(b1, b0.width + padding * 2, padding, null)
|
}
|
}
|
"ww" -> {
|
val b0 = bitmaps[1]
|
var b1 = bitmaps[1]
|
if (bitmaps[0].height != bitmaps[1].width) {
|
b1 = resizeBitmap(bitmaps[1], newHeight = bitmaps[0].height.toFloat())
|
}
|
result = createCanvas(b0.width + b1.width + padding.toInt() * 3, b0.height + padding.toInt() * 2) {
|
it.drawBitmap(b0, padding, padding, null)
|
it.drawBitmap(b1, b0.width + padding * 2, padding, null)
|
}
|
}
|
}
|
}
|
3 -> {
|
when (lengthWays) {
|
"hhh" -> {
|
val b0 = resizeBitmap(bitmaps[0], (bgWidth - padding * 4) / 3)
|
val b1 = resizeBitmap(bitmaps[1], (bgWidth - padding * 4) / 3)
|
val b2 = resizeBitmap(bitmaps[2], (bgWidth - padding * 4) / 3)
|
result = createCanvas((b0.width + padding.toInt()) * 3, b0.height + padding.toInt() * 2) {
|
it.drawBitmap(b0, padding, padding, null)
|
it.drawBitmap(b1, b0.width + padding * 2, padding, null)
|
it.drawBitmap(b2, b0.width + b1.width + padding * 3, padding, null)
|
}
|
}
|
"hww" -> {
|
val b0 = resizeBitmap(bitmaps[0], (bgWidth - padding * 4) / 3)
|
val b1 = resizeBitmap(bitmaps[1], newHeight = (b0.height - padding) / 2)
|
val b2 = resizeBitmap(bitmaps[2], newHeight = (b0.height - padding) / 2)
|
result = createCanvas(b0.width + b1.width + padding.toInt() * 3, b0.height + padding.toInt() * 2) {
|
it.drawBitmap(b0, padding, padding, null)
|
it.drawBitmap(b1, b0.width + padding * 2, padding, null)
|
it.drawBitmap(b2, b0.width + padding * 2, b1.height + padding, null)
|
}
|
}
|
"hhw" -> {
|
val b0 = resizeBitmap(bitmaps[0], (bgWidth - padding * 4) / 3)
|
val b1 = resizeBitmap(bitmaps[1], (bgWidth - padding * 4) / 3)
|
val b2 = resizeBitmap(bitmaps[2], b0.width.toFloat() * 2 + padding)
|
result = createCanvas((b0.width + padding.toInt()) * 3, b0.height + padding.toInt() * 2) {
|
it.drawBitmap(b0, padding, padding, null)
|
it.drawBitmap(b1, b0.width + padding * 2, padding, null)
|
it.drawBitmap(b2, padding, b0.height + padding, null)
|
}
|
}
|
"www" -> {
|
val b0 = bitmaps[0]
|
var b1 = bitmaps[1]
|
var b2 = bitmaps[2]
|
if (b1.width != b0.width) {
|
b1 = resizeBitmap(b1, b0.width.toFloat())
|
}
|
if (b2.width != b0.width) {
|
b2 = resizeBitmap(b2, b0.width.toFloat())
|
}
|
result = createCanvas(b0.width + padding.toInt() * 2, b0.height + padding.toInt() * 2) {
|
it.drawBitmap(b0, padding, padding, null)
|
it.drawBitmap(b1, padding, b0.height + padding * 2, null)
|
it.drawBitmap(b2, padding, b0.height * 2 + padding * 3, null)
|
}
|
}
|
}
|
}
|
|
}
|
|
return result
|
}
|
|
/**
|
* 返回图片的横向, 纵向组合信息
|
* 确保纵向图片永远排在前面
|
* @param bitmaps 图片数量
|
* @return w: 横向; h: 纵向
|
*/
|
private fun isLengthWays(bitmaps: MutableList<Bitmap>): String {
|
var result = ""
|
val tempList = mutableListOf<Bitmap>()
|
|
bitmaps.forEach {
|
if (it.width >= it.height) {
|
tempList.add(it)
|
result += "w"
|
} else {
|
tempList.add(0,it)
|
result = "h$result"
|
}
|
}
|
bitmaps.clear()
|
bitmaps.addAll(tempList)
|
return result
|
}
|
|
fun mergeVertical(bitmap1: Bitmap, bitmap2: Bitmap): Bitmap {
|
val result: Bitmap
|
val width = max(bitmap1.width, bitmap2.width)
|
if (bitmap2.width != bitmap1.width && (bitmap1.width > bgWidth || bitmap2.width > bgWidth)) {
|
val h2 = bitmap2.height * width / bitmap2.width
|
result = Bitmap.createBitmap(width, bitmap1.height + h2, Bitmap.Config.ARGB_8888)
|
val canvas = Canvas(result)
|
val newBitmap2 = resizeBitmap(bitmap2, width.toFloat(), h2.toFloat())
|
canvas.drawBitmap(bitmap1, 0F, padding * 4, null)
|
canvas.drawBitmap(newBitmap2, 0F, bitmap1.height.toFloat() + padding, null)
|
} else {
|
result = Bitmap.createBitmap(width, bitmap1.height + bitmap2.height, Bitmap.Config.ARGB_8888)
|
val canvas = Canvas(result)
|
canvas.drawBitmap(bitmap1, 0F, padding, null)
|
canvas.drawBitmap(bitmap2, 0F, bitmap1.height.toFloat() + padding * 4, null)
|
}
|
return result
|
}
|
|
fun mergeHorizontal(bitmap1: Bitmap, bitmap2: Bitmap): Bitmap {
|
val result: Bitmap
|
val height = bitmap1.height
|
if (bitmap2.height != height) {
|
val w2 = bitmap2.width * height / bitmap2.height
|
result = Bitmap.createBitmap(
|
(bitmap1.width + w2 + padding * 3).toInt(),
|
(height + padding * 2).toInt(),
|
Bitmap.Config.ARGB_8888
|
)
|
val canvas = Canvas(result)
|
val newBitmap2 = resizeBitmap(bitmap2, w2.toFloat(), height.toFloat())
|
canvas.drawBitmap(bitmap1, padding, padding, null)
|
canvas.drawBitmap(newBitmap2, bitmap1.width.toFloat() + padding * 2, padding, null)
|
} else {
|
result = Bitmap.createBitmap(
|
(bitmap1.width + bitmap2.width + padding * 3).toInt(),
|
(height + padding * 2).toInt(), Bitmap.Config.ARGB_8888
|
)
|
val canvas = Canvas(result)
|
canvas.drawBitmap(bitmap1, padding, padding, null)
|
canvas.drawBitmap(bitmap2, bitmap1.width.toFloat() + padding * 2, padding, null)
|
}
|
return result
|
}
|
|
/**
|
* 合并图片和一段文字
|
* 当图片的宽度大于屏幕宽度时,默认将文本合成在图片下方
|
* 当图片的宽度小于屏幕宽度时,默认将文本合成在图片右侧
|
*/
|
fun mergeVerticalText(bitmap: Bitmap, text: String, title: String? = null, titleIcon: Int = 0, res: Resources? = null): Bitmap {
|
|
var textMaxWidth = bgWidth - padding * 2
|
|
if (bitmap.width < bgWidth / 2) {
|
textMaxWidth = bgWidth - bitmap.width - padding * 2
|
}
|
|
val bounds = Rect()
|
var textArray = arrayOf<String>()
|
|
/* 中文字符,首行缩进 */
|
val p = Pattern.compile("[\u4e00-\u9fa5]")
|
val m = p.matcher(text)
|
val textIndent = if (m.find()) {
|
"\u3000\u3000$text"
|
// text
|
} else {
|
text
|
}
|
|
/* 文本换行 */
|
textArray = linefeed(textIndent, textMaxWidth, textPaint)
|
|
val fontMetrics = textPaint.fontMetrics
|
val textHeight = fontMetrics.bottom - fontMetrics.top
|
val totalTextHeight = textHeight * textArray.size
|
|
/* 根据屏幕大小重绘新的图片宽高,确保文本字体显示不会被缩小 */
|
val scaledBitmap = if (bitmap.width > bgWidth) {
|
resizeBitmap(bitmap, bgWidth, bitmap.height * bgWidth / bitmap.width)
|
} else {
|
bitmap
|
}
|
val newWidth = max(scaledBitmap.width, bgWidth.toInt())
|
var newHeight = scaledBitmap.height + padding.toInt() * 5 + totalTextHeight.toInt()
|
if (scaledBitmap.width < bgWidth / 2) {
|
newHeight = max(scaledBitmap.height, totalTextHeight.toInt()) + padding.toInt() * 2
|
} else if (title != null) {
|
val fm = titlePaint.fontMetrics
|
val h = fm.bottom - fm.top
|
newHeight += (h + padding).toInt()
|
}
|
val newBitmap = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888)
|
val canvas = Canvas(newBitmap)
|
canvas.drawBitmap(scaledBitmap, 0F, 0F, null)
|
|
var x = padding
|
var y = scaledBitmap.height + padding - fontMetrics.top
|
if (scaledBitmap.width < bgWidth / 2) {
|
x = scaledBitmap.width.toFloat()
|
y = -fontMetrics.top
|
}
|
if (title != null) {
|
val fm = titlePaint.fontMetrics
|
val h = fm.bottom - fm.top
|
val options = BitmapFactory.Options().apply {
|
outWidth = h.toInt()
|
outHeight = h.toInt()
|
}
|
val icon = BitmapFactory.decodeResource(res, titleIcon, options)
|
canvas.drawBitmap(icon, x, y + fm.ascent, null)
|
canvas.drawText(title, x + h + padding, y, titlePaint)
|
y += h + padding
|
}
|
textArray.forEach {
|
canvas.drawText(it, x, y, textPaint)
|
y += textHeight
|
}
|
|
return newBitmap
|
}
|
|
fun mergeText(bitmap1: Bitmap, text: String, coordinate: Pair<Float, Float>): Bitmap {
|
val result = Bitmap.createBitmap(bitmap1.width, bitmap1.height, Bitmap.Config.ARGB_8888)
|
val canvas = Canvas(result)
|
canvas.drawBitmap(bitmap1, 0F, 0F, null)
|
canvas.drawText(text, coordinate.first, coordinate.second, null)
|
return result
|
}
|
|
fun createTitle(text: String, time: Date?): Bitmap {
|
val timeStr = DateFormatter.dateFormat.format(time)
|
val tFm = textPaint.fontMetrics
|
val tH = tFm.bottom - tFm.top
|
|
val b = Bitmap.createBitmap(bgWidth.toInt(), 1, Bitmap.Config.ARGB_8888)
|
|
val textMaxWidth = bgWidth - padding * 2
|
val textArray = linefeed(text, textMaxWidth, titlePaint)
|
val fm = titlePaint.fontMetrics
|
val h = fm.bottom - fm.top
|
|
|
val result = Bitmap.createBitmap(bgWidth.toInt(), (b.height + h * textArray.size + tH + padding * 2).roundToInt(), Bitmap.Config.ARGB_8888)
|
var y = padding + b.height - fm.top
|
Canvas(result).apply {
|
textArray.forEach {
|
val x = max(bgWidth / 2 - titlePaint.measureText(it) / 2, padding)
|
drawText(it, x, y, titlePaint)
|
y += h
|
}
|
y -= h
|
val tWidth = textPaint.measureText(timeStr)
|
val x = bgWidth - padding - tWidth
|
y += tH
|
drawText(timeStr, x, y, textPaint)
|
}
|
return result
|
}
|
|
fun mergeIndex(bitmap: Bitmap, index: Int, res: Resources): Bitmap {
|
val l = "-"
|
val indexText = if (index < 10) {
|
"0$index"
|
} else {
|
"$index"
|
}
|
|
val indexWidth = indexPaint.measureText(indexText)
|
val halfWidth = (bitmap.width - indexWidth - padding * 4) / 2
|
val w = textPaint.measureText(l)
|
val count = (halfWidth / w).toInt()
|
val label = StringBuilder()
|
repeat(count) {
|
label.append(l)
|
}
|
val labelWidth = textPaint.measureText(label.toString())
|
|
val indexFm = indexPaint.fontMetrics
|
val labelFm = textPaint.fontMetrics
|
|
val imageWidth = max(indexWidth, indexFm.bottom - indexFm.top)
|
val options = BitmapFactory.Options().apply {
|
outWidth = imageWidth.toInt()
|
outHeight = imageWidth.toInt()
|
}
|
val image = BitmapFactory.decodeResource(res, R.mipmap.ic_feiyu, options)
|
val h = max((indexFm.bottom - indexFm.top) * 2, labelFm.bottom - labelFm.top) + padding
|
|
val result = Bitmap.createBitmap(bitmap.width, bitmap.height + h.toInt(), Bitmap.Config.ARGB_8888)
|
val canvas = Canvas(result)
|
canvas.drawBitmap(bitmap, 0F, 0F, null)
|
canvas.drawText(label.toString(), 0F, bitmap.height + padding * 2 - indexFm.top + indexFm.bottom, textPaint)
|
canvas.drawText(label.toString(), bitmap.width - labelWidth, bitmap.height + padding * 2 - indexFm.top + indexFm.bottom, textPaint)
|
val centreX = bitmap.width / 2
|
canvas.drawText(indexText, centreX - indexWidth / 2, bitmap.height + padding * 2 - indexFm.top, textPaint)
|
canvas.drawBitmap(image, centreX - imageWidth / 2, bitmap.height + padding * 2 - indexFm.top + indexFm.bottom, null)
|
|
return result
|
}
|
|
fun mergeTitle(bitmap: Bitmap, title: String): Bitmap {
|
titlePaint
|
return bitmap
|
}
|
|
fun drawBg(bitmap: Bitmap, @ColorInt bgColor: Int): Bitmap {
|
Canvas(bitmap).drawColor(bgColor, PorterDuff.Mode.DST_OVER)
|
return bitmap
|
}
|
|
private fun createCanvas(width: Int, height: Int, onDraw: (canvas: Canvas) -> Unit): Bitmap {
|
val b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
onDraw(Canvas(b))
|
return b
|
}
|
|
private fun resizeBitmap(bitmap: Bitmap, newWidth: Float? = null, newHeight: Float? = null): Bitmap {
|
var scaleW = newWidth?.let { it / bitmap.width }
|
var scaleH = newHeight?.let { it / bitmap.height }
|
if (scaleW == null && scaleH == null) {
|
scaleW = 1F
|
scaleH = 1F
|
}else if (scaleW == null) {
|
scaleW = scaleH
|
} else {
|
scaleH = scaleW
|
}
|
val matrix = Matrix()
|
matrix.postScale(scaleW!!, scaleH!!)
|
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
|
}
|
|
/**
|
* 文本换行
|
*/
|
private fun linefeed(textIndent: String, textMaxWidth: Float, paint: Paint): Array<String> {
|
var textArray = arrayOf<String>()
|
val str = StringBuilder()
|
for (i in textIndent.indices) {
|
str.append(textIndent[i])
|
//measureText 在计算文本宽度时,会包含空格
|
val width = paint.measureText(str.toString())
|
//getTextBounds 在计算宽度时,会忽略掉空格
|
// textPaint.getTextBounds(str.toString(), 0, str.length, bounds)
|
|
if (width > textMaxWidth) {
|
val lastChar = str.last()
|
val chars = str.deleteCharAt(str.lastIndex)
|
textArray = textArray.plus(chars.toString())
|
str.clear()
|
str.append(lastChar)
|
}
|
}
|
if (str.isNotEmpty()) {
|
textArray = textArray.plus(str.toString())
|
}
|
return textArray
|
}
|
|
}
|