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