Horis 2 лет назад
Родитель
Сommit
0177a828b2

+ 2 - 2
app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt

@@ -81,6 +81,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
         } else {
             invalidate()
         }
+        submitRenderTask()
     }
 
     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 -> {
                     if (!selectAble) return@touch
                     column.selected = true
-                    invalidate()
                     select(textPos)
                 }
             }
@@ -601,7 +601,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
                 }
             }
         }
-        invalidate()
+        submitRenderTask()
         callBack.onCancelSelect()
     }
 

+ 9 - 1
app/src/main/java/io/legado/app/utils/canvasrecorder/CanvasRecorderApi23Impl.kt

@@ -2,6 +2,8 @@ package io.legado.app.utils.canvasrecorder
 
 import android.graphics.Canvas
 import android.graphics.Picture
+import io.legado.app.utils.canvasrecorder.objectpool.synchronized
+import io.legado.app.utils.canvasrecorder.pools.PicturePool
 
 class CanvasRecorderApi23Impl : BaseCanvasRecorder() {
 
@@ -12,7 +14,7 @@ class CanvasRecorderApi23Impl : BaseCanvasRecorder() {
 
     private fun initPicture() {
         if (picture == null) {
-            picture = Picture()
+            picture = picturePool.obtain()
         }
     }
 
@@ -33,7 +35,13 @@ class CanvasRecorderApi23Impl : BaseCanvasRecorder() {
 
     override fun recycle() {
         super.recycle()
+        if (picture == null) return
+        picturePool.recycle(picture!!)
         picture = null
     }
 
+    companion object {
+        private val picturePool = PicturePool().synchronized()
+    }
+
 }

+ 13 - 3
app/src/main/java/io/legado/app/utils/canvasrecorder/CanvasRecorderApi29Impl.kt

@@ -5,6 +5,9 @@ import android.graphics.Picture
 import android.graphics.RenderNode
 import android.os.Build
 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)
 class CanvasRecorderApi29Impl : BaseCanvasRecorder() {
@@ -17,10 +20,10 @@ class CanvasRecorderApi29Impl : BaseCanvasRecorder() {
 
     private fun init() {
         if (renderNode == null) {
-            renderNode = RenderNode("CanvasRecorder")
+            renderNode = renderNodePool.obtain()
         }
         if (picture == null) {
-            picture = Picture()
+            picture = picturePool.obtain()
         }
     }
 
@@ -56,9 +59,16 @@ class CanvasRecorderApi29Impl : BaseCanvasRecorder() {
 
     override fun recycle() {
         super.recycle()
-        renderNode?.discardDisplayList()
+        if (renderNode == null || picture == null) return
+        renderNodePool.recycle(renderNode!!)
         renderNode = null
+        picturePool.recycle(picture!!)
         picture = null
     }
 
+    companion object {
+        private val picturePool = PicturePool().synchronized()
+        private val renderNodePool = RenderNodePool().synchronized()
+    }
+
 }

+ 16 - 9
app/src/main/java/io/legado/app/utils/canvasrecorder/CanvasRecorderImpl.kt

@@ -3,6 +3,8 @@ package io.legado.app.utils.canvasrecorder
 import android.graphics.Bitmap
 import android.graphics.Canvas
 import android.graphics.Color
+import io.legado.app.utils.canvasrecorder.pools.BitmapPool
+import io.legado.app.utils.canvasrecorder.pools.CanvasPool
 
 class CanvasRecorderImpl : BaseCanvasRecorder() {
 
@@ -14,19 +16,15 @@ class CanvasRecorderImpl : BaseCanvasRecorder() {
 
     private fun init(width: Int, height: Int) {
         if (bitmap == null) {
-            bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
-        }
-        if (canvas == null) {
-            canvas = Canvas(bitmap!!)
+            bitmap = bitmapPool.obtain(width, height)
         }
         if (bitmap!!.width != width || bitmap!!.height != height) {
             if (canReconfigure(width, height)) {
                 bitmap!!.reconfigure(width, height, Bitmap.Config.ARGB_8888)
             } else {
-                bitmap!!.recycle()
-                bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+                bitmapPool.recycle(bitmap!!)
+                bitmap = bitmapPool.obtain(width, height)
             }
-            canvas!!.setBitmap(bitmap!!)
         }
     }
 
@@ -37,12 +35,15 @@ class CanvasRecorderImpl : BaseCanvasRecorder() {
     override fun beginRecording(width: Int, height: Int): Canvas {
         init(width, height)
         bitmap!!.eraseColor(Color.TRANSPARENT)
+        canvas = canvasPool.obtain().apply { setBitmap(bitmap!!) }
         return canvas!!
     }
 
     override fun endRecording() {
         bitmap!!.prepareToDraw()
         super.endRecording()
+        canvasPool.recycle(canvas!!)
+        canvas = null
     }
 
     override fun draw(canvas: Canvas) {
@@ -52,8 +53,14 @@ class CanvasRecorderImpl : BaseCanvasRecorder() {
 
     override fun recycle() {
         super.recycle()
-        bitmap?.recycle()
-        bitmap = null
+        val bitmap = bitmap ?: return
+        bitmapPool.recycle(bitmap)
+        this.bitmap = null
+    }
+
+    companion object {
+        private val canvasPool = CanvasPool(2)
+        private val bitmapPool = BitmapPool()
     }
 
 }

+ 1 - 1
app/src/main/java/io/legado/app/utils/canvasrecorder/CanvasRecorderLocked.kt

@@ -31,7 +31,7 @@ class CanvasRecorderLocked(private val delegate: CanvasRecorder) :
 
     override fun draw(canvas: Canvas) {
         if (lock == null) return
-        if (!lock!!.tryLock()) return
+        lock!!.lock()
         try {
             delegate.draw(canvas)
         } finally {

+ 24 - 0
app/src/main/java/io/legado/app/utils/canvasrecorder/objectpool/BaseObjectPool.kt

@@ -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))
+    }
+
+}

+ 11 - 0
app/src/main/java/io/legado/app/utils/canvasrecorder/objectpool/ObjectPool.kt

@@ -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
app/src/main/java/io/legado/app/utils/canvasrecorder/objectpool/ObjectPoolExtensions.kt

@@ -0,0 +1,3 @@
+package io.legado.app.utils.canvasrecorder.objectpool
+
+fun <T> ObjectPool<T>.synchronized(): ObjectPool<T> = ObjectPoolLocked(this)

+ 15 - 0
app/src/main/java/io/legado/app/utils/canvasrecorder/objectpool/ObjectPoolLocked.kt

@@ -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)
+    }
+
+}

+ 63 - 0
app/src/main/java/io/legado/app/utils/canvasrecorder/pools/BitmapPool.kt

@@ -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
+        }
+    }
+
+}

+ 21 - 0
app/src/main/java/io/legado/app/utils/canvasrecorder/pools/CanvasPool.kt

@@ -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)
+    }
+
+}

+ 10 - 0
app/src/main/java/io/legado/app/utils/canvasrecorder/pools/PicturePool.kt

@@ -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()
+
+}

+ 18 - 0
app/src/main/java/io/legado/app/utils/canvasrecorder/pools/RenderNodePool.kt

@@ -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")
+
+}