問題說明 在使用 CameraX API 時,如果有在相機初始時實作下列程式碼,原則上API 會根據目前手機的方向來自動轉正相片。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 // 控制照片轉正 val orientationEventListener = object : OrientationEventListener(this) { override fun onOrientationChanged(orientation: Int) { val rotation: Int = when (orientation) { in 45..134 -> Surface.ROTATION_270 // 頭朝右 in 135..224 -> Surface.ROTATION_180 // 頭朝下 in 225..314 -> Surface.ROTATION_90 // 頭朝左 else -> Surface.ROTATION_0 // 頭朝上 } this@MainActivity.rotation = rotation imageCapture!!.targetRotation = rotation } } orientationEventListener.enable() 但是,當我們需要在輸出的照片上繪製文字或圖案時,就會遇到讀入的Bitmap 都是預設的橫向,而當我們需要繪製的時候,就會有長寬座標錯置的問題。
解決方式 必須把照片轉兩次。
需要轉兩次的原因在於,第一次的轉正是為了讓我們能夠根據使用者的角度,去繪製我們需要繪製的圖案或文字,但當繪製完成後,在輸出之前必須再將照片轉回原始讀入時的橫向,讓電腦端能夠用EXIF來自動轉正相片。
轉正跟轉回,可以試著拿張名片或紙自己轉轉看就會懂了。
實作的程式碼如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 companion object { private const val ROTATION_HEAD_UP = 0 private const val ROTATION_HEAD_LEFT = 1 private const val ROTATION_HEAD_DOWN = 2 private const val ROTATION_HEAD_RIGHT = 3 } private fun processPictureFile(photoFile: File) { // 建立 Bitmap的設定 val bitmapOptions = BitmapFactory.Options() bitmapOptions.inPurgeable = true bitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565 bitmapOptions.inDither = true bitmapOptions.inMutable = true // 避免Canvas物件建立時picBitmap為immutable // 1. 旋轉照片 val mat = Matrix() when (rotation) { ROTATION_HEAD_UP -> mat.postRotate(90f) // 手機上端朝右時,照片會上下顛倒,需特別處理轉正。 ROTATION_HEAD_RIGHT -> mat.postRotate(180f) ROTATION_HEAD_DOWN -> mat.postRotate(270f) ROTATION_HEAD_LEFT -> mat.postRotate(0f) } try { // 讀入原始相片 val inputStream = ByteArrayInputStream(photoFile.readBytes()) // 原始相片轉成 Bitmap val bitmap = BitmapFactory.decodeStream(inputStream, null, bitmapOptions) ?: return Log.d(TAG, "processPictureFile: bitmap.width ${bitmap.width}") Log.d(TAG, "processPictureFile: bitmap.height ${bitmap.height}") // 複製一份要轉方向的 Bitmap val picBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, mat, true) // 建立畫布 val canvas = Canvas(picBitmap) // 繪製在畫布上 canvas.drawBitmap(bitmap, mat, null) // TODO: 執行你需要做的繪製行為... // 設定轉回去的矩陣 val restoreMat = Matrix() when (rotation) { ROTATION_HEAD_UP -> restoreMat.postRotate(270f) ROTATION_HEAD_RIGHT -> restoreMat.postRotate(180f) ROTATION_HEAD_DOWN -> restoreMat.postRotate(90f) ROTATION_HEAD_LEFT -> restoreMat.postRotate(0f) } // 執行轉回去 val restoreBitmap = Bitmap.createBitmap(picBitmap, 0, 0, picBitmap.width, picBitmap.height, restoreMat, true) val restoreCanvas = Canvas(restoreBitmap) restoreCanvas.drawBitmap(restoreBitmap, mat, null) // TODO: 輸出... } catch (e: java.lang.Exception) { Log.e(TAG, "processPictureFile: ", e) } } 補充: 相片轉正的邏輯 對 Android 手機而言,頭朝左的水平方向(landscape),才是正常的方向,所以預設讀取的圖片都會是橫向。 因此直向拍照等方式都需要轉向。
...