I'm trying to make a custom camera app using hardware.camera
.
I've implemented a PictureCallback
which will write into a file with a certain path when the picture is taken. The data
written into the file is the ByteArray
returned by takePicture
in camera API.
So after writing into the file, I've noticed the picture taken vertically is saved horizontally. The problem wasn't because of the Exif
tag cause the byteArray had ORIENTATION_NORMAL
both before and after writing into the file.
The data
written into the file is the ByteArray
returned by takePicture
in camera API.
Here's what takePicture
looks like in Camera.Java
:
public final void takePicture(ShutterCallback shutter, PictureCallback raw,
PictureCallback jpeg) {
takePicture(shutter, raw, null, jpeg);
}
Here's part of the CameraPreview
which will capture the photo :
Code for Camera Preview
val imageProcessor = ImageProcessor()
private val fileSaver = FileSaver(context)
fun capture() {
val callback = PictureCallback { data, _ ->
imageProcessor.process(data)?.apply {
val file = fileSaver.saveBitmap(this, outputFileName ?: DEFAULT_FILE_NAME)
onCaptureTaken?.invoke(file)
}
}
camera?.takePicture(null, null, callback)
}
Code for ImageProcessor.kt
class ImageProcessor {
fun process(data: ByteArray): Bitmap? {
val options = BitmapFactory.Options().apply {
inMutable = true
}
val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size, options)
return fixImageRotation(data, bitmap)
}
private fun fixImageRotation(picture: ByteArray, bitmap: Bitmap): Bitmap? {
return when (exifPostProcessor(picture)) {
ExifInterface.ORIENTATION_ROTATE_90 ->
rotateImage(bitmap, 90F)
ExifInterface.ORIENTATION_ROTATE_180 ->
rotateImage(bitmap, 180F)
ExifInterface.ORIENTATION_ROTATE_270 ->
rotateImage(
bitmap, 270F
)
ExifInterface.ORIENTATION_NORMAL -> bitmap
else -> bitmap
}
}
private fun rotateImage(source: Bitmap, angle: Float): Bitmap? {
val matrix = Matrix()
matrix.postRotate(angle)
return Bitmap.createBitmap(
source, 0, 0, source.width, source.height,
matrix, true
)
}
private fun exifPostProcessor(picture: ByteArray?): Int {
try {
return getExifOrientation(ByteArrayInputStream(picture))
} catch (e: IOException) {
e.printStackTrace()
}
return -1
}
@Throws(IOException::class)
private fun getExifOrientation(inputStream: InputStream): Int {
val exif = ExifInterface(inputStream)
return exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL
)
}
}
Code for FileSaver.kt
internal class FileSaver(context: Context) {
private val context: Context = context.applicationContext
fun saveBitmap(bitmap: Bitmap, fileName: String): File {
val file = File(mkdirsCacheFolder(), fileName)
try {
FileOutputStream(file).use { out ->
bitmap.compress(Bitmap.CompressFormat.JPEG, ORIGINAL_QUALITY, out)
}
bitmap.recycle()
} catch (e: IOException) {
e.printStackTrace()
}
return file
}
private fun mkdirsCacheFolder(): File {
return File(context.externalCacheDir, CACHE_DIRECTORY).apply {
if (!exists()) {
mkdirs()
}
}
}
companion object {
private const val ORIGINAL_QUALITY = 100
private const val CACHE_DIRECTORY = "/Lens"
}
}
Any suggestions?
EDIT:
I printed the Exif tag and it turns out to be ORIENTATION_NORMAL
so I don't really know if it is rotated at all.
Edit 2 : Sample pictures were taken in portrait mode and opened from file manager[! Not that, these results are tested on both emulator and real android phone and they are the same. Preview: Preview
Captured image from file manager: Captured image from file manager