1+ package com.omega_r.libs.omegatypes.decoders
2+
3+ import android.annotation.TargetApi
4+ import android.graphics.Bitmap
5+ import android.graphics.BitmapFactory
6+ import android.os.Build
7+ import java.io.File
8+ import java.io.InputStream
9+
10+ /* *
11+ * Created by Anton Knyazev on 2019-10-08.
12+ */
13+
14+ typealias BitmapOptions = BitmapFactory .Options
15+
16+
17+ interface BitmapDecoders {
18+
19+ companion object {
20+
21+ val default = Default ()
22+
23+ var current: BitmapDecoders = default
24+
25+ }
26+
27+ fun decodeBitmap (source : File , requiredWidth : Int? = null, requiredHeight : Int? = null): Bitmap ?
28+
29+ fun decodeBitmap (source : InputStream , requiredWidth : Int? = null, requiredHeight : Int? = null): Bitmap ?
30+
31+ fun decodeBitmap (source : ByteArray , requiredWidth : Int? = null, requiredHeight : Int? = null): Bitmap ?
32+
33+ fun recycle (bitmap : Bitmap )
34+
35+ fun clearMemory ()
36+
37+ fun trimMemory (level : Int )
38+
39+ fun calculateInSampleSize (options : BitmapFactory .Options , reqWidth : Int , reqHeight : Int ): Int {
40+ // Raw height and width of image
41+ val height = options.outHeight
42+ val width = options.outWidth
43+ var inSampleSize = 1
44+
45+ if (height > reqHeight || width > reqWidth) {
46+
47+ val halfHeight = height / 2
48+ val halfWidth = width / 2
49+
50+ // Calculate the largest inSampleSize value that is a power of 2 and keeps both
51+ // height and width larger than the requested height and width.
52+ while (halfHeight / inSampleSize > reqHeight && halfWidth / inSampleSize > reqWidth) {
53+ inSampleSize * = 2
54+ }
55+ }
56+
57+ return inSampleSize
58+ }
59+
60+
61+ @TargetApi(Build .VERSION_CODES .KITKAT )
62+ private fun getBitmapByteSize (bitmap : Bitmap ): Int {
63+ // The return value of getAllocationByteCount silently changes for recycled bitmaps from the
64+ // internal buffer size to row bytes * height. To avoid random inconsistencies in caches, we
65+ // instead assert here.
66+ check(! bitmap.isRecycled) {
67+ (" Cannot obtain size for recycled Bitmap: " + bitmap
68+ + " [" + bitmap.width + " x" + bitmap.height + " ] " + bitmap.config)
69+ }
70+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .KITKAT ) {
71+ // Workaround for KitKat initial release NPE in Bitmap, fixed in MR1. See issue #148.
72+ try {
73+ return bitmap.allocationByteCount
74+ } catch (e: NullPointerException ) {
75+ // Do nothing.
76+ }
77+
78+ }
79+ return bitmap.height * bitmap.rowBytes
80+ }
81+
82+ fun getBitmapByteSize (width : Int , height : Int , config : Bitmap .Config ): Int {
83+ return width * height * getBytesPerPixel(config)
84+ }
85+
86+ fun getBytesPerPixel (config : Bitmap .Config ? ): Int {
87+ return when (config) {
88+ Bitmap .Config .ALPHA_8 -> 1
89+ Bitmap .Config .RGB_565 , Bitmap .Config .ARGB_4444 -> 2
90+ Bitmap .Config .ARGB_8888 -> 4
91+ else -> 4
92+ }
93+ }
94+
95+ class Default : BitmapDecoders {
96+
97+ override fun decodeBitmap (source : File , requiredWidth : Int? , requiredHeight : Int? ): Bitmap ? {
98+ return decodeBitmap(requiredWidth, requiredHeight) {
99+ BitmapFactory .decodeFile(source.absolutePath, it)
100+ }
101+ }
102+
103+ override fun decodeBitmap (source : InputStream , requiredWidth : Int? , requiredHeight : Int? ): Bitmap ? {
104+ return decodeBitmap(requiredWidth, requiredHeight) {
105+ BitmapFactory .decodeStream(source, null , it)
106+ }
107+ }
108+
109+ override fun decodeBitmap (source : ByteArray , requiredWidth : Int? , requiredHeight : Int? ): Bitmap ? {
110+ return decodeBitmap(requiredWidth, requiredHeight) {
111+ BitmapFactory .decodeByteArray(source, 0 , source.size, it)
112+ }
113+ }
114+
115+ private inline fun decodeBitmap (reqWidth : Int? , reqHeight : Int? , bitmapFactory : (BitmapFactory .Options ? ) -> Bitmap ? ): Bitmap ? {
116+ val options = if (reqWidth != null && reqHeight != null ) {
117+ BitmapOptions ().apply {
118+ inJustDecodeBounds = true
119+ bitmapFactory(this )
120+ inJustDecodeBounds = false
121+ inSampleSize = calculateInSampleSize(this , reqWidth, reqHeight)
122+ }
123+ } else null
124+
125+ return bitmapFactory(options)
126+
127+ }
128+
129+ override fun recycle (bitmap : Bitmap ) {
130+ bitmap.recycle()
131+ }
132+
133+ override fun clearMemory () {
134+ // nothing
135+ }
136+
137+ override fun trimMemory (level : Int ) {
138+ // nothing
139+ }
140+
141+ }
142+
143+ }
144+
145+ @JvmOverloads
146+ fun File.toBitmap (
147+ requiredWidth : Int? = null,
148+ requiredHeight : Int? = null,
149+ decoders : BitmapDecoders = BitmapDecoders .current
150+ ): Bitmap ? {
151+ return decoders.decodeBitmap(this , requiredWidth, requiredHeight)
152+ }
153+
154+ @JvmOverloads
155+ fun InputStream.toBitmap (
156+ requiredWidth : Int? = null,
157+ requiredHeight : Int? = null,
158+ decoders : BitmapDecoders = BitmapDecoders .current
159+ ): Bitmap ? {
160+ return decoders.decodeBitmap(this , requiredWidth, requiredHeight)
161+ }
162+
163+ @JvmOverloads
164+ fun ByteArray.toBitmap (
165+ requiredWidth : Int? = null,
166+ requiredHeight : Int? = null,
167+ decoders : BitmapDecoders = BitmapDecoders .current
168+ ): Bitmap ? {
169+ return decoders.decodeBitmap(this , requiredWidth, requiredHeight)
170+ }
0 commit comments