Support svg

pull/2395/head
Xwite 2 years ago committed by Xwite
parent 4631e6ea01
commit 139140f631
  1. 5
      app/build.gradle
  2. 24
      app/src/main/java/io/legado/app/ui/book/read/page/provider/ImageProvider.kt
  3. 77
      app/src/main/java/io/legado/app/utils/SvgUtils.kt

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

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

@ -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)
}
}
Loading…
Cancel
Save