diff --git a/app/src/main/java/com/inspection/camera/util/ImageProcessor.kt b/app/src/main/java/com/inspection/camera/util/ImageProcessor.kt index 469e3e4..9cd4190 100644 --- a/app/src/main/java/com/inspection/camera/util/ImageProcessor.kt +++ b/app/src/main/java/com/inspection/camera/util/ImageProcessor.kt @@ -30,7 +30,7 @@ import java.util.Locale */ object ImageProcessor { - private val dateFormat = SimpleDateFormat("yyyy年-MM月-dd日 HH:mm:ss", Locale.getDefault()) + private val dateFormat = SimpleDateFormat("yyyy年M月d日 HH:mm:ss", Locale.getDefault()) private val fileNameFormat = SimpleDateFormat("yyyyMMddHHmm", Locale.getDefault()) /** @@ -92,6 +92,7 @@ object ImageProcessor { /** * 添加水印到图片 + * 右下角半透明白色文字,带黑色描边/阴影,高质感摄影风格 */ fun addWatermark( sourceBitmap: Bitmap, @@ -99,51 +100,54 @@ object ImageProcessor { locationText: String, style: WatermarkStyle ): Bitmap { - android.util.Log.d("ImageProcessor", "addWatermark called, timeText=$timeText, locationText=$locationText") - val result = sourceBitmap.copy(Bitmap.Config.ARGB_8888, true) val canvas = Canvas(result) - // 使用固定的字体大小,基于图片宽度比例 - val baseFontSize = result.width / 40f // 字体大小为图片宽度的1/40 - val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { - textSize = baseFontSize - color = style.textColor.toArgb() - typeface = Typeface.DEFAULT_BOLD - textAlign = Paint.Align.CENTER - } - - val watermarkText = "$timeText $locationText" - val textWidth = paint.measureText(watermarkText) - val textHeight = paint.fontMetrics.let { it.descent - it.ascent } + // 字体大小:图片宽度的1/40,现代简洁字体 + val baseFontSize = result.width / 40f + val lineHeight = baseFontSize * 1.3f - android.util.Log.d("ImageProcessor", "Watermark: width=$textWidth, height=$textHeight, text=$watermarkText") - - // 计算位置(底部中央) - val padding = result.width / 30f // 边距为图片宽度的1/30 - val x = result.width / 2f + // 位置:右下角 + val padding = result.width / 30f + val x = result.width - padding val y = result.height - padding - // 绘制背景 - val bgColorInt = style.backgroundColor.toArgb() - android.util.Log.d("ImageProcessor", "Background color int: $bgColorInt") - if (bgColorInt != 0) { - val bgPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { - color = bgColorInt + // 绘制水印文字 - 先绘制阴影/描边,再绘制主文字 + fun drawTextWithShadow(text: String, textY: Float) { + // 绘制阴影(向右下偏移) + val shadowPaint = android.graphics.Paint().apply { + isAntiAlias = true + textSize = baseFontSize + color = android.graphics.Color.argb(120, 0, 0, 0) + typeface = Typeface.create("sans-serif-medium", Typeface.NORMAL) + textAlign = android.graphics.Paint.Align.RIGHT } - val bgRect = RectF( - x - textWidth / 2 - padding / 2, - y - textHeight - padding / 2, - x + textWidth / 2 + padding / 2, - y + padding / 2 - ) - canvas.drawRoundRect(bgRect, 8f, 8f, bgPaint) - android.util.Log.d("ImageProcessor", "Background drawn at x=$x, y=$y") + canvas.drawText(text, x + 1.5f, textY + 1.5f, shadowPaint) + + // 绘制主文字 - 半透明白色 + val textPaint = android.graphics.Paint().apply { + isAntiAlias = true + textSize = baseFontSize + color = android.graphics.Color.argb(230, 255, 255, 255) + typeface = Typeface.create("sans-serif-medium", Typeface.NORMAL) + textAlign = android.graphics.Paint.Align.RIGHT + } + canvas.drawText(text, x, textY, textPaint) } - // 绘制文字 - canvas.drawText(watermarkText, x, y, paint) - android.util.Log.d("ImageProcessor", "Text drawn at x=$x, y=$y") + // 绘制水印 + if (timeText.isNotBlank()) { + // 时间行 + drawTextWithShadow(timeText, y) + + // 位置行(在时间上方) + if (locationText.isNotBlank()) { + drawTextWithShadow(locationText, y - lineHeight - 8) + } + } else if (locationText.isNotBlank()) { + // 只有位置信息 + drawTextWithShadow(locationText, y) + } return result } @@ -169,8 +173,9 @@ object ImageProcessor { val rows = layoutType.rows val outputWidth = 1920 - val cellWidth = outputWidth / cols - val cellHeight = outputWidth / cols // 保持正方形格子 + val cellSpacing = 16 // 单元格间距 16px (约 4dp * 4) + val cellWidth = (outputWidth - cellSpacing * (cols + 1)) / cols + val cellHeight = cellWidth // 保持正方形格子 // 底部文字区域高度 val textAreaHeight = if (title.isNotBlank() || content.isNotBlank()) { @@ -180,7 +185,7 @@ object ImageProcessor { } // 图片区域高度 - val imageAreaHeight = cellHeight * rows + val imageAreaHeight = cellHeight * rows + cellSpacing * (rows + 1) val outputHeight = imageAreaHeight + textAreaHeight @@ -197,8 +202,8 @@ object ImageProcessor { val col = index % cols val row = index / cols - val left = col * cellWidth - val top = row * cellHeight + val left = cellSpacing + col * (cellWidth + cellSpacing) + val top = cellSpacing + row * (cellHeight + cellSpacing) try { val sourceBitmap = tryLoadBitmap(context, imageItem) @@ -228,85 +233,53 @@ object ImageProcessor { if (textAreaHeight > 0) { val textTop = imageAreaHeight - // 绘制白色背景 + // 绘制统一的深灰色背景 + val bgColor = android.graphics.Color.argb(255, 60, 60, 60) // 深灰色 canvas.drawRect( 0f, textTop.toFloat(), outputWidth.toFloat(), outputHeight.toFloat(), - Paint().apply { color = Color.WHITE } + Paint().apply { color = bgColor } ) - // 绘制分割线 - canvas.drawLine( - 0f, textTop.toFloat(), - outputWidth.toFloat(), textTop.toFloat(), - Paint().apply { - color = Color.LTGRAY - strokeWidth = 2f - } - ) + val padding = 30f + val titleHeight: Float - // 绘制标题 + // 绘制标题 - 粗体白色居中 if (title.isNotBlank()) { val titlePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { - textSize = titleStyle.fontSize * 2f // 放大标题 - color = titleStyle.textColor.toArgb() - typeface = Typeface.DEFAULT_BOLD - textAlign = Paint.Align.CENTER + textSize = outputWidth / 25f + color = android.graphics.Color.WHITE + typeface = Typeface.create("sans-serif", Typeface.BOLD) // 粗体无衬线 + textAlign = android.graphics.Paint.Align.CENTER } - // 标题背景 - val titleBgPaint = Paint().apply { - color = titleStyle.backgroundColor.toArgb() - } - val titleWidth = titlePaint.measureText(title) - val titleHeight = titlePaint.fontMetrics.let { it.descent - it.ascent } - val titleBgRect = RectF( - (outputWidth - titleWidth) / 2 - 20, - textTop + 20f, - (outputWidth + titleWidth) / 2 + 20, - textTop + 20f + titleHeight + 20 - ) - canvas.drawRoundRect(titleBgRect, 8f, 8f, titleBgPaint) - - // 标题文字 - canvas.drawText(title, outputWidth / 2f, textTop + 20f + titleHeight, titlePaint) + titleHeight = titlePaint.fontMetrics.let { it.descent - it.ascent } + canvas.drawText(title, outputWidth / 2f, textTop + padding + titleHeight, titlePaint) + } else { + titleHeight = 0f } - // 绘制内容 + // 绘制内容 - 细体白色靠左 if (content.isNotBlank()) { val contentPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { - textSize = contentStyle.fontSize * 1.5f // 放大内容 - color = contentStyle.textColor.toArgb() - typeface = Typeface.DEFAULT + textSize = outputWidth / 35f + color = android.graphics.Color.WHITE + typeface = Typeface.create("sans-serif", Typeface.NORMAL) // 细体 + textAlign = android.graphics.Paint.Align.LEFT } - val padding = 40f val contentMaxWidth = outputWidth - padding * 2 val contentLines = wrapText(content, contentPaint, contentMaxWidth) val lineHeight = contentPaint.fontMetrics.let { it.descent - it.ascent } - // 内容背景 - val contentBgPaint = Paint().apply { - color = contentStyle.backgroundColor.toArgb() - } - - // 从标题下方开始绘制内容 + // 内容起始Y坐标 - 标题下方再往下移一行 val contentStartY = if (title.isNotBlank()) { - textTop + 80f + lineHeight + textTop + padding + titleHeight + lineHeight + 20 } else { - textTop + 20f + lineHeight + textTop + padding + lineHeight * 2 } - val totalContentHeight = contentLines.size * lineHeight + 20 - val contentBgRect = RectF( - padding - 10, - contentStartY - lineHeight - 10, - outputWidth - padding + 10, - contentStartY + totalContentHeight - ) - canvas.drawRoundRect(contentBgRect, 8f, 8f, contentBgPaint) - - // 内容文字 + // 内容文字(靠左) var y = contentStartY contentLines.forEach { line -> canvas.drawText(line, padding, y, contentPaint)