设有如下情况:
canvas是一个刚好可以覆盖屏幕大小的画布,其中有3条path因为translate或scale被部分或全部移动到屏幕外面。如何把超出屏幕的部分(Path上有负坐标或超过屏幕宽高的x,y值)和屏幕中的一起合成一个位图?并且已知所有的path子类均有dx,dy值记录了偏移的位置,这些path在刚写入屏幕canvas中时x,y坐标数组均为正值
方法如下:
首先,要算出一个刚好可以覆盖所有Path的面积的Range,一种方法是直接遍历出左上角的点和最右下角的点,但android本身就有range之间union成一个大range的方法,并求出总面积的宽高所以只需这样即可(使用kotlin编写):
var totalRectF = SerRectF()
Log.i("layerSize", "sum:" + getLayerUtil().sum)
Log.i("curLayer.allList.size", getLayerUtil().curLayer.allList.size.toString())
//计算所有path合成的总面积
getLayerUtil().curLayer.allList?.forEach {
if (it != null) {
if (it.mPath != null && it.mPath.pathPoints != null) {
var itRange: SerRectF = SerRectF()
it.mPath.computeBounds(itRange, false)
Log.i("itRange", itRange.toString())
totalRectF.union(itRange)
}
}
}
width = totalRectF.width()
height = totalRectF.height()
然后,我们需要在新的画布上,算出负值最大的x值,取绝对值后使其成为一个虚假的x的0点(虚拟x轴)。
加入现在原画布有两个点:
点1的x点原本值为10,偏移了-30(dx=-30),现在值变为-20。
点2的x点原本值为40,偏移了-30(dx=-30),现在值为10的点。
那么画布x最负值就是-20。而我们设-20的绝对值20为模拟的x的0点。
那么写入的时候基于新画布的点1值就是 现在值 + 虚拟0点 = 虚拟现在值,也就是-20 + 20 = 0,
点2基于新画布的值就是 现在值 + 虚拟0点 = 虚拟现在值,也就是10 + 20 = 30,保持了相对距离的同时,他们的值也都变为了正。
等效的算法就是 dx = dx + 20 = -10,原本值 + 新的dx =新的现在值,例如10 +(-10) = 0, 40 + (-10)= 30 。
写入path的时候,先把画布移到虚假的新dx=-10的反向位置x=-(-10)=10,然后此时写入点10,它再被新dx偏移-10,就刚好写在了画布的x=0的应属位置上(因为计算机总面积时是以这个最小的点确定画布左边界的,所以它是最靠左边的点,理应是0)。
同样,先把画布移到虚假的新dx=-10的反向位置x=-(-10)=10,写入点40,被新dx偏移-10,写在了画布x=30的该点该在的x轴的位置上。
同样的方法,也可以用于构建虚拟的y轴,使得一切负坐标都可以通过对虚拟的x,y轴进行加减运算成为正坐标,从而可以写入只可以看见正数的画布中。
// 虚拟画布的0点
var canvasVirtualZeroXPoint: Float = 0f
var canvasVirtualZeroYPoint: Float = 0f
if (totalRectF.left >= 0f) canvasVirtualZeroXPoint = 0f
else canvasVirtualZeroXPoint = Math.abs(totalRectF.left)
if (totalRectF.top >= 0f) canvasVirtualZeroYPoint = 0f
else canvasVirtualZeroYPoint = Math.abs(totalRectF.top)
示意图:
具体完整实现:
/** 漫游拿全景 */
fun getBitmap(): Bitmap {
var totalRectF = SerRectF()
//计算所有path合成的总面积
getLayerUtil().curLayer.allList?.forEach {
if (it != null) {
if (it.mPath != null && it.mPath.pathPoints != null) {
var itRange: SerRectF = SerRectF()
it.mPath.computeBounds(itRange, false)
Log.i("itRange", itRange.toString())
totalRectF.union(itRange)
}
}
}
var width: Float = 1f
var height: Float = 1f
/****用范围值Rectf来得到宽度高度Start***/
width = totalRectF.width()
height = totalRectF.height()
// 虚拟画布的0点
var canvasVirtualZeroXPoint: Float = 0f
var canvasVirtualZeroYPoint: Float = 0f
if (totalRectF.left >= 0f) canvasVirtualZeroXPoint = 0f
else canvasVirtualZeroXPoint = Math.abs(totalRectF.left)
if (totalRectF.top >= 0f) canvasVirtualZeroYPoint = 0f
else canvasVirtualZeroYPoint = Math.abs(totalRectF.top)
Log.i("虚拟0线", String.format("虚拟0线x:%f, 虚拟0线y:%f", canvasVirtualZeroXPoint, canvasVirtualZeroYPoint))
var bitmap = Bitmap.createBitmap(width.toInt(), height.toInt(), Bitmap.Config.ARGB_4444) //统计该用多大的位图
var can: Canvas = Canvas(bitmap)
getLayerUtil().curLayer?.allList?.forEach {
///
var fixPath: SerPath = SerPath()
var dx: Float = 0f
var dy: Float = 0f
dx = it.dx
dy = it.dy
if (it.mPath != null) {
Log.i("qrcodeMaking", String.format("dx:%f, dy:%f", dx, dy))
fixPath.addPath(it.mPath, canvasVirtualZeroXPoint + dx, canvasVirtualZeroYPoint + dy) //取消原来的偏移值,改写偏移值
can.translate(-dx, -dy) //画布对偏移值反向偏移使得相对0点真正起到0点的效果
can.drawPath(fixPath, it.cPaint)
can.translate(dx, dy) //取消画布偏移
}
//
// Log.i("画图记录", String.format("start_x:%f, start_y:%f, current_x:%f, current_y:%f, dx:%f, dy:%f",
// it.start.x, it.start.y, it.current.x, it.current.y, dx, dy))
if (it != null) {
if (it.mPath != null && it.mPath.pathPoints != null) {
if (it.mPath.pathPoints.isNotEmpty()) {
Log.i("画图记录", String.format("x:%f, y:%f, dx:%f, dy:%f", it.mPath.pathPoints.get(0)[0] + dx, it.mPath.pathPoints.get(0)[1] + dy, dx, dy))
}
}
}
}
try {
var bgBitmap: Bitmap
if (getBgColor() == 0) { //如果背景色为空
if (getBgBitmap() == null) { //而且背景图为空则使用默认背景图填充后再绘制
bgBitmap = BitmapFactory.decodeResource(resources, R.drawable.background) //背景位图
} else { //否则使用自定义背景图填充后再绘制内容
bgBitmap = getBgBitmap()
}
var resultBitmap: Bitmap = Tool.getPPBitmap(bitmap, bgBitmap)
Log.i("resultBitmapSize", String.format("width:%d,height:%d", bitmap.width, bitmap.height))
return resultBitmap
} else { //否则使用背景色先填充再绘制图形
var resultBitmap: Bitmap = Tool.getPPBitmap(bitmap, getBgColor())
Log.i("resultBitmapSize", String.format("width:%d,height:%d", bitmap.width, bitmap.height))
return resultBitmap
}
} catch (exception: Exception) {
exception.printStackTrace()
return Tool.getPPBitmap(mLayerUtil.getCurLayer().toBitmap(), bgBitmap)
}
}
实际效果
原图是这样:
①被拖上了左上角,加了一个③,被拖到右下角
然后开启截屏功能
顺利得到大幅的全景图