Skip to content

Commit 6e45631

Browse files
committed
Add bitmap pool
1 parent c1192f2 commit 6e45631

File tree

16 files changed

+371
-111
lines changed

16 files changed

+371
-111
lines changed

app/src/main/java/omega_r/com/omegatypesexample/MainActivity.kt

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,14 @@ import java.io.ByteArrayOutputStream
2323

2424
class MainActivity : BaseActivity() {
2525

26-
companion object {
27-
28-
init {
29-
GlideImagesProcessor.setAsCurrentImagesProcessor()
30-
}
31-
32-
}
33-
3426
private val exampleTextView by bind<TextView>(R.id.textview)
3527
private val imageView by bind<ImageView>(R.id.imageview)
3628

3729
override fun onCreate(savedInstanceState: Bundle?) {
3830
super.onCreate(savedInstanceState)
3931

32+
GlideImagesProcessor.setGlideBitmapPool(this)
33+
4034
setContentView(R.layout.activity_main)
4135
val text = Text.from("test ") +
4236
Text.from(
@@ -55,28 +49,31 @@ class MainActivity : BaseActivity() {
5549
title = list.join(",", postfix = ".").getCharSequence(this)
5650

5751

58-
val image = intent.getSerializableExtra("test") as? Image ?: run {
59-
60-
val image = Image.from("https://dejagerart.com/wp-content/uploads/2018/09/Test-Logo-Circle-black-transparent.png")
61-
62-
ImageProcessors.current.launch {
63-
val stream = image.getStream(this@MainActivity, Bitmap.CompressFormat.PNG)
64-
val bitmap = BitmapFactory.decodeStream(stream)
65-
66-
val bitmapImage = Image.from(BitmapDrawable(this@MainActivity.resources, bitmap))
67-
68-
withContext(Dispatchers.Main) {
69-
imageView.setImage(bitmapImage)
52+
val image = Image.from("https://dejagerart.com/wp-content/uploads/2018/09/Test-Logo-Circle-black-transparent.png")
7053

7154

72-
intent.putExtra("test", bitmapImage)
73-
finish()
74-
startActivity(intent)
75-
// imageView.setImageBitmap(bitmap)
76-
}
77-
}
78-
image
79-
}
55+
// val image = intent.getSerializableExtra("test") as? Image ?: run {
56+
//
57+
// val image = Image.from("https://dejagerart.com/wp-content/uploads/2018/09/Test-Logo-Circle-black-transparent.png")
58+
//
59+
// ImageProcessors.current.launch {
60+
// val stream = image.getStream(this@MainActivity, Bitmap.CompressFormat.PNG)
61+
// val bitmap = BitmapFactory.decodeStream(stream)
62+
//
63+
// val bitmapImage = Image.from(BitmapDrawable([email protected], bitmap))
64+
//
65+
// withContext(Dispatchers.Main) {
66+
// imageView.setImage(bitmapImage)
67+
//
68+
//
69+
//// intent.putExtra("test", bitmapImage)
70+
//// finish()
71+
//// startActivity(intent)
72+
//// imageView.setImageBitmap(bitmap)
73+
// }
74+
// }
75+
// image
76+
// }
8077

8178
imageView.setImage(image)
8279

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.omega_r.libs.omegatypes.image
2+
3+
import android.graphics.Bitmap
4+
import com.omega_r.libs.omegatypes.decoders.SimpleBitmapDecoders
5+
6+
/**
7+
* Created by Anton Knyazev on 2019-10-09.
8+
*/
9+
class GlideBitmapPool(private val bitmapPool: com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool) : SimpleBitmapDecoders.BitmapPool {
10+
11+
// companion object {
12+
//
13+
// }
14+
15+
override fun getBitmap(outWidth: Int, outHeight: Int, inPreferredConfig: Bitmap.Config?): Bitmap? {
16+
return bitmapPool.get(outWidth, outHeight, inPreferredConfig)
17+
}
18+
19+
override fun putBitmap(bitmap: Bitmap) {
20+
return bitmapPool.put(bitmap)
21+
}
22+
23+
override fun clearMemory() {
24+
bitmapPool.clearMemory()
25+
}
26+
27+
override fun trimMemory(level: Int) {
28+
bitmapPool.trimMemory(level)
29+
}
30+
31+
}

glide/src/main/java/com/omega_r/libs/omegatypes/image/GlideImagesProcessor.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import com.bumptech.glide.Glide
99
import com.bumptech.glide.RequestBuilder
1010
import com.bumptech.glide.request.target.CustomViewTarget
1111
import com.bumptech.glide.request.transition.Transition
12+
import com.omega_r.libs.omegatypes.decoders.BitmapDecoders
13+
import com.omega_r.libs.omegatypes.decoders.SimpleBitmapDecoders
1214
import com.omega_r.libs.omegatypes.image.Image.Companion.NO_PLACEHOLDER_RES
1315
import java.io.InputStream
1416
import kotlin.reflect.KClass
@@ -27,6 +29,10 @@ class GlideImagesProcessor(
2729
current = GlideImagesProcessor(current)
2830
}
2931

32+
fun setGlideBitmapPool(context: Context) {
33+
BitmapDecoders.current = SimpleBitmapDecoders(GlideBitmapPool(Glide.get(context).bitmapPool))
34+
}
35+
3036
}
3137

3238
private val excludeImageClasses = listOf(*excludeImageClasses)
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
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

Comments
 (0)