首先,先要了解Exif是个什么东东,搬出百度百科html
可交换图像文件格式(英语:Exchangeable image file format,官方简称Exif),是专门为数码相机的照片设定的,能够记录数码照片的属性信息和拍摄数据。
说到底Exif就是一种格式,用来存储图片的一些信息,这些信息和咱们平常比较相关的有拍摄设备,拍摄地点,图片尺寸等,不过今天的主角是另一个——那就是图片方向(orientation)。这个图片方向不是指咱们平时使用图片编辑器旋转的方向,而是拍照时手机的方向。总共有八个方向:git
下图是JPEG ORIENTATION对应图片方向的纠正算法,这里它经过三位二进制数表明八种方向,而后再经过每一位二进制数对应不一样的操做来对图片进行纠正,以下:github
最高位二进制数表明对角线翻转的操做,第二位二进制数表明旋转180度的操做,最低位表明水平翻转的操做。
例如001,就是水平翻转,因此能够看到001的图形和原图形关于水平轴对称。经过把八个方向的图形用3个二进制数即三种操做组合,就能够很方便的对图形作转换,编码伪代码以下:算法
if (value & 100b != 0) image.flip-diagonally if (value & 010b != 0) image.rotate-180 if (value & 001b != 0) image.flip-horizontally
那有人就会困惑了,本身怎么平时没有看到这种图片呢,这是由于咱们使用的图片查看器或者是浏览器对orientation作了兼容,会对展现的图片作转换。windows
以下是windows文件夹的展现:浏览器
下面则是Android Studio的图片展现性能优化
因此能够看到,windows是默认对图片orientation作了处理,而Android的ImageView则没有处理因此看到的是图片原本的方向。
这是八个F的图片连接。编辑器
在Android里面,三星手机的拍照是个奇葩的存在,三星手机的exif是旋转90度,别家手机则是0度,因此三星手机的照片须要作处理,这里是一张三星手机照片的exif信息:post
三星手机的方向是Rotate 90CW,意思就是须要顺时针方向(ClockWise)旋转90度。
脑袋转的快的同窗能够对照上面的F图,相信很快看出是101这张图。
那咱们取出图片的orientation值进行验证:性能
try { val exifInterface = ExifInterface(resources.openRawResource(id)) val orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL) Log.e("orientation", orientation.toString()) } catch (e: IOException) { e.printStackTrace() }
打印结果是6,和上面的101对不上,其实在Android的orientation是须要作减1处理的,也就是说6其实对应的是101这种状态。另外,须要注意的是,若是打印结果是0,那么说明图片没有orientation这个信息。
那接下来咱们进行编码,这是第一张方式:
val options = BitmapFactory.Options() var bitmap = BitmapFactory.decodeResource(resources, R.mipmap.error_orientation, options) val matrix = Matrix() matrix.postRotate(getOrientation(R.mipmap.error_orientation).toFloat()) bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) imageview.setImageBitmap(bitmap) private fun getOrientation(id:Int): Int { var degree = 0 try { val exifInterface = ExifInterface(resources.openRawResource(id)) val orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL) when (orientation) { ExifInterface.ORIENTATION_ROTATE_90 -> degree = 90 ExifInterface.ORIENTATION_ROTATE_180 -> degree = 180 ExifInterface.ORIENTATION_ROTATE_270 -> degree = 270 } } catch (e: IOException) { e.printStackTrace() } return degree }
通常来讲,咱们只须要处理这三种角度,上面三个角度对应的orientation是6 3 8,也就是101,010,111这三种状态。为何通常只须要处理这三种状态呢,本身脑补一下拿相机的角度,不外乎就四种状况,除了正常的状况下,不就只须要处理三种状况吗?嘿嘿,我真是个小机灵鬼。
固然,若是要严谨一点,仍是须要按照JPEG那种操做方式来,以下:
val options = BitmapFactory.Options() var bitmap = BitmapFactory.decodeResource(resources, R.mipmap.f7t, options) val matrix = genOrientationMatrix(R.mipmap.f7t) bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) imageview.setImageBitmap(bitmap) private fun genOrientationMatrix(id:Int): Matrix { val matrix = Matrix() try { val exifInterface = ExifInterface(resources.openRawResource(id)) var orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL) if (orientation > 0) { orientation-- if (orientation and 0b100 != 0) { //对角线翻转 matrix.postScale(-1.0f, 1.0f) matrix.postRotate(-90f) } if (orientation and 0b010 != 0) { //旋转180度 matrix.postRotate(180f) } if (orientation and 0b001 != 0) { //水平翻转 matrix.postScale(-1.0f, 1.0f) } } return matrix } catch (e: IOException) { e.printStackTrace() } return matrix }
其实就是将JPEG对于orientation的转换利用代码进行实现,对矩阵进行相应的变换。
Exif是一种存储了相片一些信息的格式,日常咱们在进行Android开发的时候,通常须要考虑方向的问题,可是在平常生活,这个也是暴露咱们隐私的入口,因此手机在拍照的时候,最好将保存位置这些选项关闭,避免泄漏本身的隐私。