1+ package com.tomclaw.imageloader
2+
3+ import android.graphics.Bitmap
4+ import android.graphics.BitmapFactory
5+ import android.graphics.Matrix
6+ import android.media.ExifInterface
7+ import java.io.BufferedInputStream
8+ import java.io.File
9+ import java.io.FileInputStream
10+ import java.io.IOException
11+ import java.io.InputStream
12+
13+ interface BitmapDecoder {
14+
15+ fun getBitmap (file : File , reqWidth : Int , reqHeight : Int ): Bitmap ?
16+
17+ }
18+
19+ class BitmapDecoderImpl () : BitmapDecoder {
20+
21+ override fun getBitmap (
22+ file : File ,
23+ reqWidth : Int ,
24+ reqHeight : Int
25+ ): Bitmap ? {
26+ var bitmap: Bitmap ?
27+ var inputStream: InputStream ? = null
28+ try {
29+ inputStream = FileInputStream (file)
30+ bitmap = decodeSampledBitmapFromStream(inputStream, reqWidth, reqHeight)
31+ val rotation = getRotation(file)
32+ if (bitmap != null && rotation != 0 ) {
33+ val width = bitmap.width
34+ val height = bitmap.height
35+ val m = Matrix ()
36+ m.setRotate(rotation.toFloat(), (width / 2 ).toFloat(), (height / 2 ).toFloat())
37+ bitmap = Bitmap .createBitmap(bitmap, 0 , 0 , width, height, m, false )
38+ }
39+ } catch (ignored: Throwable ) {
40+ ignored.printStackTrace()
41+ bitmap = null
42+ } finally {
43+ if (inputStream != null ) {
44+ try {
45+ inputStream.close()
46+ } catch (ignored: IOException ) {
47+ }
48+ }
49+ }
50+ return bitmap
51+ }
52+
53+ private fun decodeSampledBitmapFromStream (
54+ stream : InputStream ,
55+ reqWidth : Int ,
56+ reqHeight : Int
57+ ): Bitmap ? {
58+ var bitmap: Bitmap ?
59+ try {
60+ val inputStream: InputStream = BufferedInputStream (stream, THUMBNAIL_BUFFER_SIZE )
61+ inputStream.mark(THUMBNAIL_BUFFER_SIZE )
62+
63+ // First decode with inJustDecodeBounds=true to check dimensions
64+ val options = BitmapFactory .Options ()
65+ options.inJustDecodeBounds = true
66+ BitmapFactory .decodeStream(inputStream, null , options)
67+
68+ // Calculate inSampleSize
69+ val widthSample = (options.outWidth / reqWidth).toFloat()
70+ val heightSample = (options.outHeight / reqHeight).toFloat()
71+ var scaleFactor = Math .max(widthSample, heightSample)
72+ if (scaleFactor < 1 ) {
73+ scaleFactor = 1f
74+ }
75+ options.inJustDecodeBounds = false
76+ options.inSampleSize = scaleFactor.toInt()
77+ options.inPreferredConfig = Bitmap .Config .RGB_565
78+
79+ // Decode bitmap with inSampleSize set
80+ inputStream.reset()
81+ bitmap = BitmapFactory .decodeStream(inputStream, null , options)
82+ } catch (ignored: Throwable ) {
83+ ignored.printStackTrace()
84+ bitmap = null
85+ }
86+ return bitmap
87+ }
88+
89+ private fun getRotation (file : File ): Int {
90+ return when (obtainFileOrientation(file.absolutePath)) {
91+ ExifInterface .ORIENTATION_ROTATE_90 , ExifInterface .ORIENTATION_TRANSPOSE -> 90
92+ ExifInterface .ORIENTATION_ROTATE_180 , ExifInterface .ORIENTATION_FLIP_VERTICAL -> 180
93+ ExifInterface .ORIENTATION_ROTATE_270 , ExifInterface .ORIENTATION_TRANSVERSE -> 270
94+ else -> 0
95+ }
96+ }
97+
98+ private fun obtainFileOrientation (fileName : String ): Int {
99+ return try {
100+ val exifInterface = ExifInterface (fileName)
101+ exifInterface.getAttributeInt(
102+ ExifInterface .TAG_ORIENTATION ,
103+ ExifInterface .ORIENTATION_NORMAL
104+ )
105+ } catch (ex: IOException ) {
106+ ExifInterface .ORIENTATION_UNDEFINED
107+ }
108+ }
109+
110+ }
111+
112+ /* *
113+ * Buffer is large enough to rewind past any EXIF headers.
114+ */
115+ private const val THUMBNAIL_BUFFER_SIZE = 128 * 1024
0 commit comments