一、前文

之前有个朋友委托我实现一个图片拼接的组件,感觉挺有意思,于是周末花了些时间去研究了下,其实拼接这一步并不难,但是我在研究中发现了Matrix这个东西,非常好的东西。为此,我竟然拾起了多年没有动过的线性代数。

二、原理

要彻底搞懂matrix还是需要一定的线性代数上面的理解,不过对于基本使用,了解到矩阵乘法就足够了。

在android坐标系中,分为x、y和z三个轴,分别代表了长、宽、高三个维度。如下图所示

在android中,使用三维坐标(x,y,z)组成一个行列式与一个三阶行列式进行矩阵乘法。

图中显示的使用初始坐标组成的矩阵与单位矩阵进行矩阵乘法。矩阵乘法使用可以参考 矩阵乘法

Martix会把输入进来的矩阵带入到其内部的矩阵中进行计算,最终输出新的矩阵,来达到对图形形态的处理。

三、基本方法的使用

Matrix提供的基本方法有三种模式,

setXXX()方法,例如 setRotate(),setScale()

preXXX()方法,例如 preRotate(),preScale()

postXXX()方法,例如 postRotate(),postScale()

其中,setXXX()会先将矩阵重置为单位矩阵,然后再进行矩阵变幻

preXXX()和postXXX()方法会牵扯到矩阵的前乘和后乘,如果了解了矩阵乘法规则,就会明白矩阵前乘和后乘得出来的结果是不一样的,不过一般情况下都会选择使用post方法,后乘。

其中还有扩展方法比如:

mapRect(rect) / mapRect(desRect,orgRect)

第一个方法即使用原始矩阵代入运算,会将返回的矩阵直接覆盖在传入的矩阵中

第二个方法则是对于需要保存原始矩阵的情况下,会把原始矩阵的计算结果赋值到指定的矩阵中

setRectToRect(src,des,stf)

这个方法相当于将原始矩阵填充到目标矩阵中,所以也就要求两个矩阵都是有值的。其中填充模式由第三个参数决定。/**

* 独立缩放X和Y,直到和src的rect和目标rect确切的匹配。这可能会改变原始rect的宽高比

*/

FILL(0),

/**

* 在保持原有宽高比的情况下计算出一个合适的缩放比例,但也会确保原始rect合适的填入目标rect,

* 最终会把开始的一个边与目标的开始边左边对齐

*/

START(1),

/**

* 与START类似,不过最终结果会尽可能居中

*/

CENTER(2),

/**

* 与START类似,不过最终结果会尽可能靠右边

*/

END(3);

复制代码

invert(inverse)

反转矩阵,可以应用到类似倒影一类的实现中

setPolyToPoly(src,srcIndex,dst,dstIndex,pointCount)   这是一个比较神奇的方法。随着pointCount点数量,可以对原始矩阵进行平移、旋转、错切、翻页效果。功能非常强大。

此外,关于Matrix还有颜色变幻等效果,更多扩展用法后面会讲到。

四、实践到自定义view中

写一个自定义view,最重要的是要了解view的绘制过程。简单的绘制流程如下

其中不带on的方法都为调度方法,不可被重写,这些方法里面会把前期一些必要的数据准备出来,带on前缀的方法都是实际进行处理的方法。

measure方法是测量控件大小的,layout是用来布局,根据measure测量的结果,把其中每个元素在其内部进行位置的计算。最后会执行的draw方法,draw也分为draw和onDraw,可以根据自己需求来改写对应的方法。

其中,onMeasure的方法如下所示:

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

// measure child img

final int maxImgWidth = getMeasuredWidth();

final int maxImgHeight = getMeasuredHeight();

final int measureWidthSize = MeasureSpec.getSize(widthMeasureSpec);

final int measureHeightSize = MeasureSpec.getSize(heightMeasureSpec);

int totalImageHeight = 0;

// 缩放和旋转影响size的交给measure

for (int i = 0; i < imgList.size(); i++) {

ImageData imageData = imgList.get(i);

final int imgOrgWidth = imageData.getImgWidth();

final int imgOrgHeight = imageData.getImgHeight();

int imgRotateWidth;

int imgRotateHeight;

if (imageData.scale > 0) {

imageData.matrix.setScale(imageData.scale, imageData.scale);

} else {

final float sizeProportion = (float) imgOrgWidth / imgOrgHeight;

if (imgOrgHeight > imgOrgWidth) {

if (measureHeightSize == MeasureSpec.EXACTLY &&

imgOrgHeight > maxImgHeight) {

imgRotateWidth = (int) (maxImgHeight * sizeProportion);

imgRotateHeight = maxImgHeight;

} else {

imgRotateWidth = imgOrgWidth;

imgRotateHeight = imgOrgHeight;

}

} else {

if (imgOrgWidth > maxImgWidth) {

imgRotateHeight = (int) (maxImgWidth / sizeProportion);

imgRotateWidth = maxImgWidth;

} else {

imgRotateWidth = imgOrgWidth;

imgRotateHeight = imgOrgHeight;

}

}

// resize

imageData.reSize(imgRotateWidth, imgRotateHeight);

}

// rotate

imageData.matrix.mapRect(imageData.drawRect, imageData.orgRect);

imageData.matrix.postRotate(imageData.rotateAngle, imageData.drawRect.centerX(),

imageData.drawRect.top + (imageData.drawRect.height() * 0.5f));

imageData.matrix.mapRect(imageData.drawRect, imageData.orgRect);

totalImageHeight += imageData.drawRect.height();

}

switch (measureHeightSize) {

// wrap_content

case MeasureSpec.AT_MOST:

setMeasuredDimension(MeasureSpec.makeMeasureSpec(maxImgWidth,

measureWidthSize), MeasureSpec.makeMeasureSpec(totalImageHeight,

measureHeightSize));

break;

// match_parent or accurate num

case MeasureSpec.EXACTLY:

setMeasuredDimension(MeasureSpec.makeMeasureSpec(maxImgWidth,

measureHeightSize));

break;

case MeasureSpec.UNSPECIFIED:

setMeasuredDimension(MeasureSpec.makeMeasureSpec(maxImgWidth,

measureWidthSize), MeasureSpec.makeMeasureSpec(totalImageHeight,

measureHeightSize));

break;

}

}

复制代码

所有影响尺寸计算相关的方法都会放到这个measure里面进行计算,比如scale和rotate,都会影响size大小。所以在这里计算完成后,好在layout中进行正确的布局。

layout中的代码如下:

@Override

protected void onLayout(boolean changed, int left, int top, int right, int bottom){

super.onLayout(changed, left, top, right, bottom);

// measure child layout

int cursorTop = top;

int mid = (right - left) >> 1;

for (int i = 0; i < imgList.size(); i++) {

final ImageData imageData = imgList.get(i);

// fix layout translate

imageData.matrix.mapRect(imageData.drawRect, imageData.orgRect);

int translateTop = (int) (cursorTop + (imageData.orgRect.top -

imageData.drawRect.top));

int translateLeft = (int) (mid - imageData.drawRect.centerX());

imageData.matrix.postTranslate(translateLeft, translateTop);

imageData.matrix.mapRect(imageData.drawRect, imageData.orgRect);

cursorTop = (int) imageData.drawRect.bottom;

}

}

复制代码

两个方法中,要做到Matrix多效果叠加,切记要保留一个bitmap最原始的矩阵,然后再接下来的计算中需要用到当前尺寸的时候,使用Martix计算出临时的尺寸对其进行计算。

两个方法中,Bitmap被封装到一个ImageData类里面,进行对象化,这样可以更好的管理Bitmap的处理和数据记录。

ImageData如下:

public class ImageData{

public ImageData(Bitmap bitmap){

this.bitmap = bitmap;

this.matrix = new Matrix();

orgRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());

}

// 默认置0

float scale = 0f;

// 0点在3点钟方向,达到垂直居中的效果,需要置为-90度

float rotateAngle = -90f;

RectF drawRect = new RectF();

RectF orgRect = new RectF();

Bitmap bitmap;

Matrix matrix;

private float distanceStub = 0f;

private float angleStub = 0f;

public Bitmap getBitmap(){

return bitmap;

}

public RectF getDrawRect(){

return drawRect;

}

public int getImgWidth(){

return bitmap.getWidth();

}

public int getImgHeight(){

return bitmap.getHeight();

}

public void layout(int l, int t, int r, int b){

drawRect.set(l, t, r, b);

}

void reSize(int w, int h){

int orgWidth = bitmap.getWidth();

int orgHeight = bitmap.getHeight();

// 计算缩放比例

float scaleWidth = ((float) w) / orgWidth;

float scaleHeight = ((float) h) / orgHeight;

scale = (scaleWidth + scaleHeight) * 0.5f;

matrix.postScale(scale, scale);

}

void clearMatrixCache(){

matrix.reset();

}

void setScale(float scale){

this.scale = scale;

}

float getScale(){

return this.scale;

}

void setRotateAngle(float angle){

this.rotateAngle = angle;

}

float getRotateAngle(){

return this.rotateAngle;

}

/**

* imageData的触摸处理事件

*

* @param e 触摸事件

*/

protected void onTouchEvent(MotionEvent e){

// ...

}

private float getPointDistance(MotionEvent e){

// ...

}

private float getPointAngle(MotionEvent e){

// ...

}

}

复制代码

这里面跟本文无关的方法都隐藏了,随后会讲到.

那么我们来看看效果

使用方法,跟目录gradle里面添加:

repositories {

...

maven { url 'https://jitpack.io' }

}

复制代码

app.gradle中添加:

compile 'com.github.Kongdy:ImageStitching:v1.0.0'

复制代码

本文代码:github.com/Kongdy/Imag…

个人github地址:github.com/Kongdy

个人掘金主页:juejin.im/user/595a64…

csdn主页:blog.csdn.net/u014303003

android视频拼接图片大小,android图片裁剪拼接实现(一):Matrix基本使用相关推荐

  1. python自带的PIL库扩展图片大小给图片加上文字描述

    利用python自带的PIL库扩展图片大小给图片加上文字描述.大多都是库函数调用,只是给定图片宽度后计算文字所需行数的代码需要写. 代码比较丑,but it works. #!/usr/bin/env ...

  2. python add picture显示过大_利用Python自带PIL库扩展图片大小给图片加文字描述的方法示例...

    前言 最近的一个项目中需要在图片上添加文字,使用了OpenCV,结果发现利用opencv给图像添加文字有局限.可利用的字体类型比较少,需要安装Freetype扩展,比较复杂.而且不能用putText函 ...

  3. vue H5app plus调取手机相册,限制图片大小,图片转base64

    vue H5app plus调取手机相册,限制图片大小,图片转base64 直接上代码 filmImg(){ //移动端发送图片let _this = this plus.gallery.pick(f ...

  4. 怎么压缩图片大小,图片压缩方法

    怎么压缩图片大小?工作中如果遇到一些体积较大的图片,尤其一些清晰度很高的图片,可能一张小尺寸的图片就有2-3M的大小,这个我们的使用打开造成很多的麻烦,例如因为超过限制大小而不能上传,打开的时候很大, ...

  5. 格式工厂为保持输出视频质量与大小,自动裁剪视频长度

    格式工厂为保持输出视频质量与大小,自动裁剪视频长度 记录一下,当天老大叫我顺便输出一个嵌入式产品培训视频,原pr输出为500M左右的MP4,老大要求能在微信上发给客户,100M以内的要求,于是用格式工 ...

  6. android相册和拍照并裁剪图片大小,Android 拍照并对照片进行裁剪和压缩实例详解...

    Android 拍照并对照片进行裁剪和压缩实例详解 本文主要介绍 Android 调用摄像头拍照并对照片进行裁剪和压缩,文中给出了主要步骤和关键代码. 调用摄像头拍照,对拍摄照片进行裁剪,代码如下. ...

  7. android 裁剪图片大小 控制图片尺寸

    用BitmapFactory获取适合屏幕大小的图片 和自带的图片裁剪工具 package com.lin.image;import android.app.Activity; import andro ...

  8. android 图片自动裁剪图片大小,android调用原生图片裁剪后图片尺寸缩放的解决方法...

    在安卓开发中,如果对拍照后的图片进行图片裁剪,如果是调用系统的裁剪,如下: /* * 裁剪图片 */ private void cropPhoto() { Intent intent = new In ...

  9. android+怎么分享图片大小,Android微信分享图片大于32k进行压缩

    微信分享视频的时候,需要传一个图片数组,大小不能大于32k. 解决方案:使用Bitmap自带的compress方法解决了这个问题. 源码如下: package com.example.test; im ...

  10. android页面设置背景图片大小,android页面设置background为图片后,页面滑动掉帧问题...

    最近接手的一个android项目里面,有个viewpager+3个fragment的页面,就是很常见的可以左右滑动切换页面的那种布局.接手的时候告诉我,这个页面有卡顿现象,性能需要优化.一开始觉得是f ...

最新文章

  1. Blender从头开始装配和动画制作低多边形风格的FPS手臂
  2. nginx之upsream实现负载均衡
  3. rtmp 时间戳问题
  4. C++中的抽象类及纯虚函数的实现与否
  5. c语言如何获取串口列表,如何通过串口来读写数据,请教达人
  6. github搭建个人博客 hexo d无效
  7. 工作378-封装axios方法
  8. 机器学习问题总结(05)
  9. 字符串匹配——BMH算法
  10. 关于jquery基本过滤器中:eq()无法传变量的问题
  11. oracle 天转换成月函数_oracle中to_date详细用法示例(oracle日期格式转换)
  12. 赢者通吃自编码器(WTA-AE)
  13. android手机无法开机自动启动,安卓手机无法开机的6种解决方法
  14. msfconsole 控制台简介
  15. 抖音短剧本应该怎么写
  16. 字符串练习:手机号码屏蔽,身份证号码信息查看,游戏骂人敏感词替换
  17. 14宽的键槽深度多少_平键和键槽的标准尺寸规格表
  18. 江西省2021年工业互联网安全技术技能大赛
  19. asp.net英语四六级考试报名系统
  20. 均值-方差模型实现及应用_python_数据分析_9

热门文章

  1. 游戏开发经验之没有美工程序员是如何制作游戏的
  2. CF923B Producing Snow 堆/优先队列
  3. Magic Data入选亿欧智库2022中国语音交互车端应用产业图谱
  4. 为什么我们要努力的赚钱?
  5. Tomcat彻底卸载干净方法
  6. 【QtOpenCV 直方图计算 split/calcHist/normalize】
  7. 高精度IMU精对准原理和代码实现
  8. 数据挖掘之Apriori频繁项集挖掘
  9. 「图灵奇点」将应收账款证券化,区块链协作缩短尽调时间
  10. 大战Mysql2003