Xwite 4 lat temu
rodzic
commit
a90e932b43

+ 5 - 0
app/build.gradle

@@ -220,6 +220,11 @@ dependencies {
     implementation("com.github.bumptech.glide:glide:$glideVersion")
     kapt("com.github.bumptech.glide:compiler:$glideVersion")
 
+    //Svg
+    implementation("com.caverock:androidsvg-aar:1.4")
+    //Glide svg plugin
+    implementation("com.github.qoqa:glide-svg:4.0.2")
+
     //webServer
     def nanoHttpdVersion = "2.3.1"
     implementation("org.nanohttpd:nanohttpd:$nanoHttpdVersion")

+ 10 - 14
app/src/main/java/io/legado/app/ui/book/read/page/provider/ImageProvider.kt

@@ -17,6 +17,7 @@ import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.model.ReadBook
 import io.legado.app.model.localBook.EpubFile
 import io.legado.app.utils.BitmapUtils
+import io.legado.app.utils.SvgUtils
 import io.legado.app.utils.FileUtils
 import io.legado.app.utils.toastOnUi
 import kotlinx.coroutines.Dispatchers.IO
@@ -55,8 +56,8 @@ object ImageProvider {
             if (oldBitmap != errorBitmap) {
                 oldBitmap.recycle()
                 triggerRecycled = true
-                putDebug("ImageProvider: trigger bitmap recycle. URI: $filePath")
-                putDebug("ImageProvider : cacheUsage ${size()}bytes / ${maxSize()}bytes")
+                //putDebug("ImageProvider: trigger bitmap recycle. URI: $filePath")
+                //putDebug("ImageProvider : cacheUsage ${size()}bytes / ${maxSize()}bytes")
             }
         }
     }
@@ -97,15 +98,16 @@ object ImageProvider {
         bookSource: BookSource?
     ): Size {
         val file = cacheImage(book, src, bookSource)
+        //svg size
+        val size = SvgUtils.getSize(file.absolutePath)
+        if (size != null) return size
         val op = BitmapFactory.Options()
         // inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight;
         op.inJustDecodeBounds = true
         BitmapFactory.decodeFile(file.absolutePath, op)
         if (op.outWidth < 1 && op.outHeight < 1) {
-            Coroutine.async {
-                putDebug("ImageProvider: delete file due to image size ${op.outHeight}*${op.outWidth}. path: ${file.absolutePath}")
-                file.delete()
-            }
+            putDebug("ImageProvider: $src Unsupported image type")
+            //file.delete() 重复下载
             return Size(errorBitmap.width, errorBitmap.height)
         }
         return Size(op.outWidth, op.outHeight)
@@ -135,6 +137,7 @@ object ImageProvider {
         if (height != null && AppConfig.asyncLoadImage && ReadBook.pageAnim() == PageAnim.scrollPageAnim) {
             Coroutine.async {
                 val bitmap = BitmapUtils.decodeBitmap(vFile.absolutePath, width, height)
+                    ?: SvgUtils.createBitmap(vFile.absolutePath, width, height)
                     ?: throw NoStackTraceException(appCtx.getString(R.string.error_decode_bitmap))
                 withContext(Main) {
                     bitmapLruCache.put(vFile.absolutePath, bitmap)
@@ -142,10 +145,6 @@ object ImageProvider {
             }.onError {
                 //错误图片占位,防止重复获取
                 bitmapLruCache.put(vFile.absolutePath, errorBitmap)
-                putDebug(
-                    "ImageProvider: decode bitmap failed. path: ${vFile.absolutePath}\n$it",
-                    it
-                )
             }.onFinally {
                 block?.invoke()
             }
@@ -154,16 +153,13 @@ object ImageProvider {
         @Suppress("BlockingMethodInNonBlockingContext")
         return kotlin.runCatching {
             val bitmap = BitmapUtils.decodeBitmap(vFile.absolutePath, width, height)
+                ?: SvgUtils.createBitmap(vFile.absolutePath, width, height)
                 ?: throw NoStackTraceException(appCtx.getString(R.string.error_decode_bitmap))
             bitmapLruCache.put(vFile.absolutePath, bitmap)
             bitmap
         }.onFailure {
             //错误图片占位,防止重复获取
             bitmapLruCache.put(vFile.absolutePath, errorBitmap)
-            putDebug(
-                "ImageProvider: decode bitmap failed. path: ${vFile.absolutePath}\n$it",
-                it
-            )
         }.getOrDefault(errorBitmap)
     }
 

+ 77 - 0
app/src/main/java/io/legado/app/utils/SvgUtils.kt

@@ -0,0 +1,77 @@
+package io.legado.app.utils
+
+import android.graphics.Canvas
+import android.graphics.Bitmap
+import android.graphics.drawable.PictureDrawable
+import android.util.Size
+import java.io.FileInputStream
+import java.io.InputStream
+import com.caverock.androidsvg.SVG
+import com.caverock.androidsvg.PreserveAspectRatio
+import com.caverock.androidsvg.SVGParseException
+import kotlin.math.max
+
+@Suppress("WeakerAccess", "MemberVisibilityCanBePrivate")
+object SvgUtils {
+
+    /**
+     * 从Svg中解码bitmap
+     * https://github.com/qoqa/glide-svg/blob/master/library/src/main/java/ch/qoqa/glide/svg/SvgBitmapTranscoder.kt
+     */
+    
+    fun createBitmap(filePath: String, width: Int, height: Int? = null): Bitmap? {
+        val inputStream = FileInputStream(filePath)
+        return createBitmap(inputStream, width, height)
+    }
+
+    fun createBitmap(inputStream: InputStream, width: Int, height: Int? = null): Bitmap? {
+        return kotlin.runCatching {
+            val svg = SVG.getFromInputStream(inputStream)
+            createBitmap(svg, width, height)
+        }.getOrNull()
+    }
+
+    //获取svg图片大小
+    fun getSize(filePath: String): Size? {
+        val inputStream = FileInputStream(filePath)
+        return getSize(inputStream)
+    }
+
+    fun getSize(inputStream: InputStream): Size? {
+        return kotlin.runCatching {
+            val svg = SVG.getFromInputStream(inputStream)
+            getSize(svg)
+        }.getOrNull()
+    }
+
+    /////// private method
+    private fun createBitmap(svg: SVG, width: Int, height: Int? = null): Bitmap {
+        val size = getSize(svg)
+        val wRatio = width.let { size.width / it } ?: -1
+        val hRatio = height?.let { size.height / it } ?: -1
+        //如果超出指定大小,则缩小相应的比例
+        val ratio = when {
+            wRatio > 1 && hRatio > 1 -> max(wRatio, hRatio)
+            wRatio > 1 -> wRatio
+            hRatio > 1 -> hRatio
+            else -> 1
+        }
+        svg.documentPreserveAspectRatio = PreserveAspectRatio.START
+        val picture = svg.renderToPicture(size.width / ratio, size.height / ratio)
+        val drawable = PictureDrawable(picture)
+
+        val bitmap = Bitmap.createBitmap(size.width / ratio, size.height / ratio, Bitmap.Config.ARGB_8888)
+        val canvas = Canvas(bitmap)
+        canvas.drawPicture(drawable.picture)
+        return bitmap
+    }
+
+    private fun getSize(svg: SVG): Size {
+        val width = svg.documentWidth.toInt().takeIf { it > 0 }
+            ?: (svg.documentViewBox.right - svg.documentViewBox.left).toInt()
+        val height = svg.documentHeight.toInt().takeIf { it > 0 }
+            ?: (svg.documentViewBox.bottom - svg.documentViewBox.top).toInt()
+        return Size(width, height)      
+    }
+
+}