优化
This commit is contained in:
@@ -81,6 +81,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|||||||
} else {
|
} else {
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
submitRenderTask()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
||||||
@@ -216,7 +217,6 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|||||||
is TextColumn -> {
|
is TextColumn -> {
|
||||||
if (!selectAble) return@touch
|
if (!selectAble) return@touch
|
||||||
column.selected = true
|
column.selected = true
|
||||||
invalidate()
|
|
||||||
select(textPos)
|
select(textPos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -601,7 +601,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
invalidate()
|
submitRenderTask()
|
||||||
callBack.onCancelSelect()
|
callBack.onCancelSelect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package io.legado.app.utils.canvasrecorder
|
|||||||
|
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.Picture
|
import android.graphics.Picture
|
||||||
|
import io.legado.app.utils.canvasrecorder.objectpool.synchronized
|
||||||
|
import io.legado.app.utils.canvasrecorder.pools.PicturePool
|
||||||
|
|
||||||
class CanvasRecorderApi23Impl : BaseCanvasRecorder() {
|
class CanvasRecorderApi23Impl : BaseCanvasRecorder() {
|
||||||
|
|
||||||
@@ -12,7 +14,7 @@ class CanvasRecorderApi23Impl : BaseCanvasRecorder() {
|
|||||||
|
|
||||||
private fun initPicture() {
|
private fun initPicture() {
|
||||||
if (picture == null) {
|
if (picture == null) {
|
||||||
picture = Picture()
|
picture = picturePool.obtain()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +35,13 @@ class CanvasRecorderApi23Impl : BaseCanvasRecorder() {
|
|||||||
|
|
||||||
override fun recycle() {
|
override fun recycle() {
|
||||||
super.recycle()
|
super.recycle()
|
||||||
|
if (picture == null) return
|
||||||
|
picturePool.recycle(picture!!)
|
||||||
picture = null
|
picture = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val picturePool = PicturePool().synchronized()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import android.graphics.Picture
|
|||||||
import android.graphics.RenderNode
|
import android.graphics.RenderNode
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
|
import io.legado.app.utils.canvasrecorder.objectpool.synchronized
|
||||||
|
import io.legado.app.utils.canvasrecorder.pools.PicturePool
|
||||||
|
import io.legado.app.utils.canvasrecorder.pools.RenderNodePool
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.Q)
|
@RequiresApi(Build.VERSION_CODES.Q)
|
||||||
class CanvasRecorderApi29Impl : BaseCanvasRecorder() {
|
class CanvasRecorderApi29Impl : BaseCanvasRecorder() {
|
||||||
@@ -17,10 +20,10 @@ class CanvasRecorderApi29Impl : BaseCanvasRecorder() {
|
|||||||
|
|
||||||
private fun init() {
|
private fun init() {
|
||||||
if (renderNode == null) {
|
if (renderNode == null) {
|
||||||
renderNode = RenderNode("CanvasRecorder")
|
renderNode = renderNodePool.obtain()
|
||||||
}
|
}
|
||||||
if (picture == null) {
|
if (picture == null) {
|
||||||
picture = Picture()
|
picture = picturePool.obtain()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,9 +59,16 @@ class CanvasRecorderApi29Impl : BaseCanvasRecorder() {
|
|||||||
|
|
||||||
override fun recycle() {
|
override fun recycle() {
|
||||||
super.recycle()
|
super.recycle()
|
||||||
renderNode?.discardDisplayList()
|
if (renderNode == null || picture == null) return
|
||||||
|
renderNodePool.recycle(renderNode!!)
|
||||||
renderNode = null
|
renderNode = null
|
||||||
|
picturePool.recycle(picture!!)
|
||||||
picture = null
|
picture = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val picturePool = PicturePool().synchronized()
|
||||||
|
private val renderNodePool = RenderNodePool().synchronized()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package io.legado.app.utils.canvasrecorder
|
|||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
import io.legado.app.utils.canvasrecorder.pools.BitmapPool
|
||||||
|
import io.legado.app.utils.canvasrecorder.pools.CanvasPool
|
||||||
|
|
||||||
class CanvasRecorderImpl : BaseCanvasRecorder() {
|
class CanvasRecorderImpl : BaseCanvasRecorder() {
|
||||||
|
|
||||||
@@ -14,19 +16,15 @@ class CanvasRecorderImpl : BaseCanvasRecorder() {
|
|||||||
|
|
||||||
private fun init(width: Int, height: Int) {
|
private fun init(width: Int, height: Int) {
|
||||||
if (bitmap == null) {
|
if (bitmap == null) {
|
||||||
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
bitmap = bitmapPool.obtain(width, height)
|
||||||
}
|
|
||||||
if (canvas == null) {
|
|
||||||
canvas = Canvas(bitmap!!)
|
|
||||||
}
|
}
|
||||||
if (bitmap!!.width != width || bitmap!!.height != height) {
|
if (bitmap!!.width != width || bitmap!!.height != height) {
|
||||||
if (canReconfigure(width, height)) {
|
if (canReconfigure(width, height)) {
|
||||||
bitmap!!.reconfigure(width, height, Bitmap.Config.ARGB_8888)
|
bitmap!!.reconfigure(width, height, Bitmap.Config.ARGB_8888)
|
||||||
} else {
|
} else {
|
||||||
bitmap!!.recycle()
|
bitmapPool.recycle(bitmap!!)
|
||||||
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
bitmap = bitmapPool.obtain(width, height)
|
||||||
}
|
}
|
||||||
canvas!!.setBitmap(bitmap!!)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,12 +35,15 @@ class CanvasRecorderImpl : BaseCanvasRecorder() {
|
|||||||
override fun beginRecording(width: Int, height: Int): Canvas {
|
override fun beginRecording(width: Int, height: Int): Canvas {
|
||||||
init(width, height)
|
init(width, height)
|
||||||
bitmap!!.eraseColor(Color.TRANSPARENT)
|
bitmap!!.eraseColor(Color.TRANSPARENT)
|
||||||
|
canvas = canvasPool.obtain().apply { setBitmap(bitmap!!) }
|
||||||
return canvas!!
|
return canvas!!
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun endRecording() {
|
override fun endRecording() {
|
||||||
bitmap!!.prepareToDraw()
|
bitmap!!.prepareToDraw()
|
||||||
super.endRecording()
|
super.endRecording()
|
||||||
|
canvasPool.recycle(canvas!!)
|
||||||
|
canvas = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun draw(canvas: Canvas) {
|
override fun draw(canvas: Canvas) {
|
||||||
@@ -52,8 +53,14 @@ class CanvasRecorderImpl : BaseCanvasRecorder() {
|
|||||||
|
|
||||||
override fun recycle() {
|
override fun recycle() {
|
||||||
super.recycle()
|
super.recycle()
|
||||||
bitmap?.recycle()
|
val bitmap = bitmap ?: return
|
||||||
bitmap = null
|
bitmapPool.recycle(bitmap)
|
||||||
|
this.bitmap = null
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val canvasPool = CanvasPool(2)
|
||||||
|
private val bitmapPool = BitmapPool()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class CanvasRecorderLocked(private val delegate: CanvasRecorder) :
|
|||||||
|
|
||||||
override fun draw(canvas: Canvas) {
|
override fun draw(canvas: Canvas) {
|
||||||
if (lock == null) return
|
if (lock == null) return
|
||||||
if (!lock!!.tryLock()) return
|
lock!!.lock()
|
||||||
try {
|
try {
|
||||||
delegate.draw(canvas)
|
delegate.draw(canvas)
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package io.legado.app.utils.canvasrecorder.objectpool
|
||||||
|
|
||||||
|
import androidx.annotation.CallSuper
|
||||||
|
import java.lang.ref.SoftReference
|
||||||
|
import java.util.LinkedList
|
||||||
|
|
||||||
|
abstract class BaseObjectPool<T> : ObjectPool<T> {
|
||||||
|
|
||||||
|
private val pool = LinkedList<SoftReference<T>>()
|
||||||
|
|
||||||
|
override fun obtain(): T {
|
||||||
|
while (true) {
|
||||||
|
if (pool.isEmpty()) break
|
||||||
|
return pool.poll()?.get() ?: continue
|
||||||
|
}
|
||||||
|
return create()
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
override fun recycle(target: T) {
|
||||||
|
pool.add(SoftReference(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package io.legado.app.utils.canvasrecorder.objectpool
|
||||||
|
|
||||||
|
interface ObjectPool<T> {
|
||||||
|
|
||||||
|
fun obtain(): T
|
||||||
|
|
||||||
|
fun recycle(target: T)
|
||||||
|
|
||||||
|
fun create(): T
|
||||||
|
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
package io.legado.app.utils.canvasrecorder.objectpool
|
||||||
|
|
||||||
|
fun <T> ObjectPool<T>.synchronized(): ObjectPool<T> = ObjectPoolLocked(this)
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package io.legado.app.utils.canvasrecorder.objectpool
|
||||||
|
|
||||||
|
class ObjectPoolLocked<T>(private val delegate: ObjectPool<T>) : ObjectPool<T> by delegate {
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun obtain(): T {
|
||||||
|
return delegate.obtain()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun recycle(target: T) {
|
||||||
|
return delegate.recycle(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package io.legado.app.utils.canvasrecorder.pools
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import java.lang.ref.SoftReference
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
class BitmapPool {
|
||||||
|
|
||||||
|
private val reusableBitmaps: MutableSet<SoftReference<Bitmap>> = ConcurrentHashMap.newKeySet()
|
||||||
|
|
||||||
|
fun recycle(bitmap: Bitmap) {
|
||||||
|
reusableBitmaps.add(SoftReference(bitmap))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun obtain(width: Int, height: Int): Bitmap {
|
||||||
|
if (reusableBitmaps.isEmpty()) {
|
||||||
|
return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||||
|
}
|
||||||
|
val iterator = reusableBitmaps.iterator()
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
val item = iterator.next().get() ?: continue
|
||||||
|
if (item.isMutable) {
|
||||||
|
// Check to see it the item can be used for inBitmap.
|
||||||
|
if (canReconfigure(item, width, height)) {
|
||||||
|
// Remove from reusable set so it can't be used again.
|
||||||
|
iterator.remove()
|
||||||
|
item.reconfigure(width, height, Bitmap.Config.ARGB_8888)
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Remove from the set if the reference has been cleared.
|
||||||
|
iterator.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun canReconfigure(
|
||||||
|
candidate: Bitmap,
|
||||||
|
width: Int,
|
||||||
|
height: Int
|
||||||
|
): Boolean {
|
||||||
|
// From Android 4.4 (KitKat) onward we can re-use if the byte size of
|
||||||
|
// the new bitmap is smaller than the reusable bitmap candidate
|
||||||
|
// allocation byte count.
|
||||||
|
val byteCount: Int = width * height * getBytesPerPixel(candidate.config)
|
||||||
|
return byteCount <= candidate.allocationByteCount
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper function to return the byte usage per pixel of a bitmap based on its configuration.
|
||||||
|
*/
|
||||||
|
private fun getBytesPerPixel(config: Bitmap.Config): Int {
|
||||||
|
return when (config) {
|
||||||
|
Bitmap.Config.ARGB_8888 -> 4
|
||||||
|
Bitmap.Config.RGB_565, Bitmap.Config.ARGB_4444 -> 2
|
||||||
|
Bitmap.Config.ALPHA_8 -> 1
|
||||||
|
else -> 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package io.legado.app.utils.canvasrecorder.pools
|
||||||
|
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import androidx.core.util.Pools
|
||||||
|
|
||||||
|
class CanvasPool(size: Int) {
|
||||||
|
|
||||||
|
private val pool = Pools.SynchronizedPool<Canvas>(size)
|
||||||
|
|
||||||
|
fun obtain(): Canvas {
|
||||||
|
val canvas = pool.acquire() ?: Canvas()
|
||||||
|
return canvas
|
||||||
|
}
|
||||||
|
|
||||||
|
fun recycle(canvas: Canvas) {
|
||||||
|
canvas.setBitmap(null)
|
||||||
|
canvas.restoreToCount(1)
|
||||||
|
pool.release(canvas)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package io.legado.app.utils.canvasrecorder.pools
|
||||||
|
|
||||||
|
import android.graphics.Picture
|
||||||
|
import io.legado.app.utils.canvasrecorder.objectpool.BaseObjectPool
|
||||||
|
|
||||||
|
class PicturePool : BaseObjectPool<Picture>() {
|
||||||
|
|
||||||
|
override fun create(): Picture = Picture()
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package io.legado.app.utils.canvasrecorder.pools
|
||||||
|
|
||||||
|
import android.graphics.RenderNode
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import io.legado.app.utils.canvasrecorder.objectpool.BaseObjectPool
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.Q)
|
||||||
|
class RenderNodePool : BaseObjectPool<RenderNode>() {
|
||||||
|
|
||||||
|
override fun recycle(target: RenderNode) {
|
||||||
|
target.discardDisplayList()
|
||||||
|
super.recycle(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun create(): RenderNode = RenderNode("CanvasRecorder")
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user