Canvas->Bitmap绘制

Canvas.drawBitmap 常用重载方法的详解

java 复制代码
/**
 * 如果绘制的Bitmap不需要拉伸,缩放,使用这个方法,比如绘制马赛克底图
 *
 * @param bitmap 要绘制的位图
 * @param left 被绘制的位图左侧的位置
 * @param top 被绘制的位图顶部的位置
 * @param paint 用于绘制位图的 Paint(可以为 null)
 */
public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) {
    super.drawBitmap(bitmap, left, top, paint);
}

/**
 * 从Bitmap中拿出一块Rect区域,绘制到另外一块Rect区域上,比如绘制到一个指定的View区域上
 *
 * @param bitmap 要绘制的位图
 * @param src 源矩形,定义位图中要绘制的区域。可以为 null,表示绘制整个位图。
 * @param dst 目标矩形,定义位图在画布上绘制的位置和大小。
 * @param paint 用于绘制位图的 Paint(可以为 null)
 */
public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst, @Nullable Paint paint) {
    super.drawBitmap(bitmap, src, dst, paint);
}

/**
 * 通过矩阵变换Bitmap,适合需要复杂变换的场景,比如绘制一张平移、旋转、缩放的Bitmap
 *
 * @param bitmap 要绘制的位图
 * @param matrix 用于变换位图的 Matrix,包括平移、旋转、缩放等操作
 * @param paint 用于绘制位图的 Paint(可以为 null)
 */
public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) {
    super.drawBitmap(bitmap, matrix, paint);
}

如何把Bitmap从原始区域Rect绘制到目标区域Rect上?

  • 一张Bitmap的宽高往往是比手机屏幕宽高大得多,原始Bitmap自身携带一个原始区域Rect,也就是Rect(0, 0, bitmap.width, bitmap.height),如果要绘制到手机屏幕区域某个View的区域,也就是目标Rect,需要使用到Canvas和变换矩阵Matrix的组合使用

方法一

kotlin 复制代码
override fun onDraw(canvas: Canvas) {
    // 如果需要在一块指定Rect上绘制平移、旋转、缩放的Bitmap
    bitmap?.let { bitmap ->
        // 计算平移
        val translateX = targetRect.left + (targetRect.width() - bitmap.width) / 2
        val translateY = targetRect.top + (targetRect.height() - bitmap.height) / 2
        // 计算缩放
        val scaleX = targetRect.width() / bitmap.width
        val scaleY = targetRect.height() / bitmap.height
        val scale = min(scaleX, scaleY)
        // 计算旋转
        val degree = 15f
        // 图片原始区域到目标区域的变换矩阵,存储平移、缩放、旋转数值
        val targetMatrix = Matrix().apply {
            postTranslate(translateX, translateY)
            postScale(scale, scale, targetRect.centerX(), targetRect.centerY())
            postRotate(degree, targetRect.centerX(), targetRect.centerY())
            postConcat(手势矩阵)
        }
        
        canvas.concat(targetMatrix) // canvas拼接变换矩阵
        canvas.drawBitmap(bitmap, null, targetRect, Paint())
    }
}

方法二

kotlin 复制代码
override fun onDraw(canvas: Canvas) {
    // 如果需要在一块指定Rect上绘制平移、旋转、缩放的Bitmap
    bitmap?.let { bitmap ->
        // 计算平移
        val translateX = targetRect.left + (targetRect.width() - bitmap.width) / 2
        val translateY = targetRect.top + (targetRect.height() - bitmap.height) / 2
        // 计算缩放
        val scaleX = targetRect.width() / bitmap.width
        val scaleY = targetRect.height() / bitmap.height
        val scale = min(scaleX, scaleY)
        // 计算旋转
        val degree = 15f
        // 图片原始区域到目标区域的变换矩阵,存储平移、缩放、旋转数值
        val targetMatrix = Matrix().apply {
            postTranslate(translateX, translateY)
            postScale(scale, scale, targetRect.centerX(), targetRect.centerY())
            postRotate(degree, targetRect.centerX(), targetRect.centerY())
            postConcat(手势矩阵)
        }
        
        canvas.drawBitmap(bitmap, targetMatrix, Paint())
    }
}

方法三

java 复制代码
override fun onDraw(canvas: Canvas) {
    // 如果需要在一块指定Rect上绘制平移、旋转、缩放的Bitmap
    bitmap?.let { bitmap ->
        // 定义源矩形和目标矩形
        val srcRect = RectF(0f, 0f, bitmap.width.toFloat(), bitmap.height.toFloat())
        val targetRectF = RectF(targetRect)

        // 创建一个矩阵来描述从源矩形到目标矩形的变换
        val targetMatrix = Matrix().apply {
            // 设置矩形到矩形的变换,保持纵横比居中缩放
            setRectToRect(srcRect, targetRectF, Matrix.ScaleToFit.CENTER)
            // 额外的旋转变换
            postRotate(15f, targetRectF.centerX(), targetRectF.centerY())
            postConcat(手势矩阵)
        }

        // 使用变换矩阵绘制位图
        canvas.drawBitmap(bitmap, targetMatrix, Paint())
    }
}

Canvas绘制现场保护

kotlin 复制代码
override fun onDraw(canvas: Canvas) {
    canvas.save() // 隔离绘制现场,以下修改画布的操作不会记录
    canvas.concat(变换矩阵) // 画布绘制的所有东西,应用这个变换
    canvas.clipRect(目标区域) // 将画布裁剪到目标区域中
    canvas.clipOutRecet(目标区域) // 将画布裁剪到目标区域之外
    canvas.restore() // 恢复绘制现场,恢复修改画布之前的画布状态
    
    canvas.withSave{
        canvas.concat(变换矩阵) // 画布绘制的所有东西,应用这个变换
        canvas.clipRect(目标区域) // 将画布裁剪到目标区域中
        canvas.clipOutRecet(目标区域) // 将画布裁剪到目标区域之外
    }
}

public inline fun Canvas.withSave(block: Canvas.() -> Unit) {
    val checkpoint = save() // 返回一个Int值
    try {
        block() // 执行lambda表达式
    } finally {
        restoreToCount(checkpoint) // 从绘制栈中恢复指定Int值时的画布状态
    }
}
相关推荐
huohaiyu44 分钟前
Hashtable,HashMap,ConcurrentHashMap之间的区别
java·开发语言·多线程·哈希
f30517091 小时前
Python实现数据可视化用Matplotlib轻松创建专业级图表
android
信奥卷王2 小时前
[GESP202503 五级] 原根判断
java·数据结构·算法
小咕聊编程2 小时前
【含文档+源码】基于SpringBoot的过滤协同算法之网上服装商城设计与实现
java·spring boot·后端
Zz_waiting.2 小时前
Spring 原理
java·spring·spring自动管理
2501_915918415 小时前
iOS 26 App 性能测试|性能评测|iOS 26 性能对比:实战策略
android·macos·ios·小程序·uni-app·cocoa·iphone
Predestination王瀞潞5 小时前
IO操作(Num22)
开发语言·c++
瓯雅爱分享6 小时前
Java+Vue构建的采购招投标一体化管理系统,集成招标计划、投标审核、在线竞价、中标公示及合同跟踪功能,附完整源码,助力企业实现采购全流程自动化与规范化
java·mysql·vue·软件工程·源代码管理
宋恩淇要努力6 小时前
C++继承
开发语言·c++
沿着路走到底7 小时前
python 基础
开发语言·python