絵文字デコード先のビットマップの大きさを入力データのアスペクト比に合わせる
This commit is contained in:
parent
1ccd05e08a
commit
a0243ee8b9
|
@ -6,9 +6,11 @@ import jp.juggler.util.data.encodeUTF8
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
import kotlin.math.round
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
class ApngFrames private constructor(
|
class ApngFrames private constructor(
|
||||||
private val pixelSizeMax: Int = 0,
|
private val pixelSizeMax: Float = 0f,
|
||||||
private val debug: Boolean = false,
|
private val debug: Boolean = false,
|
||||||
) : ApngDecoderCallback, MyGifDecoderCallback {
|
) : ApngDecoderCallback, MyGifDecoderCallback {
|
||||||
|
|
||||||
|
@ -29,43 +31,64 @@ class ApngFrames private constructor(
|
||||||
color = 0
|
color = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return w,h
|
||||||
|
fun scaleEmojiSize(
|
||||||
|
srcW: Float,
|
||||||
|
srcH: Float,
|
||||||
|
maxSize: Float,
|
||||||
|
): Pair<Float, Float> = when {
|
||||||
|
// 入力サイズの情報がない
|
||||||
|
srcW <= 0f || srcH <= 0f -> Pair(maxSize, maxSize)
|
||||||
|
else -> {
|
||||||
|
val sqMax = maxSize * maxSize
|
||||||
|
val sqOriginal = srcW * srcH
|
||||||
|
val aspect = srcW / srcH
|
||||||
|
when {
|
||||||
|
// 既に十分小さい
|
||||||
|
sqOriginal <= sqMax -> Pair(srcW, srcH)
|
||||||
|
// アスペクト比に応じたスケーリング
|
||||||
|
else -> Pair(
|
||||||
|
sqrt(sqMax * aspect),
|
||||||
|
sqrt(sqMax / aspect),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun createBlankBitmap(w: Int, h: Int) =
|
private fun createBlankBitmap(w: Int, h: Int) =
|
||||||
Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
|
Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
|
||||||
|
|
||||||
private fun scale(max: Int, num: Int, den: Int) =
|
|
||||||
(max.toFloat() * num.toFloat() / den.toFloat() + 0.5f).toInt()
|
|
||||||
|
|
||||||
private fun scaleBitmap(
|
private fun scaleBitmap(
|
||||||
sizeMax: Int,
|
sizeMax: Float,
|
||||||
src: Bitmap,
|
src: Bitmap,
|
||||||
recycleSrc: Boolean = true, // true: ownership of "src" will be moved or recycled.
|
recycleSrc: Boolean = true, // true: ownership of "src" will be moved or recycled.
|
||||||
): Bitmap {
|
): Bitmap {
|
||||||
|
if (sizeMax <= 0) {
|
||||||
|
return when {
|
||||||
|
recycleSrc -> src
|
||||||
|
else -> src.copy(Bitmap.Config.ARGB_8888, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
val wSrc = src.width
|
val wSrc = src.width
|
||||||
val hSrc = src.height
|
val hSrc = src.height
|
||||||
if (sizeMax <= 0 || (wSrc <= sizeMax && hSrc <= sizeMax)) {
|
val (wDst, hDst) = scaleEmojiSize(
|
||||||
return if (recycleSrc) {
|
wSrc.toFloat(),
|
||||||
src
|
hSrc.toFloat(),
|
||||||
} else {
|
sizeMax,
|
||||||
src.copy(Bitmap.Config.ARGB_8888, false)
|
)
|
||||||
|
val wDstInt = max(1, round(wDst).toInt())
|
||||||
|
val hDstInt = max(1, round(hDst).toInt())
|
||||||
|
if (wSrc <= wDstInt && hSrc <= hDstInt ) {
|
||||||
|
return when {
|
||||||
|
recycleSrc -> src
|
||||||
|
else -> src.copy(Bitmap.Config.ARGB_8888, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val wDst: Int
|
val b2 = createBlankBitmap(wDstInt, hDstInt)
|
||||||
val hDst: Int
|
|
||||||
if (wSrc >= hSrc) {
|
|
||||||
wDst = sizeMax
|
|
||||||
hDst = max(1, scale(sizeMax, hSrc, wSrc))
|
|
||||||
} else {
|
|
||||||
hDst = sizeMax
|
|
||||||
wDst = max(1, scale(sizeMax, wSrc, hSrc))
|
|
||||||
}
|
|
||||||
//Log.v(TAG,"scaleBitmap: $wSrc,$hSrc => $wDst,$hDst")
|
|
||||||
|
|
||||||
val b2 = createBlankBitmap(wDst, hDst)
|
|
||||||
val canvas = Canvas(b2)
|
val canvas = Canvas(b2)
|
||||||
val rectSrc = Rect(0, 0, wSrc, hSrc)
|
val rectSrc = Rect(0, 0, wSrc, hSrc)
|
||||||
val rectDst = Rect(0, 0, wDst, hDst)
|
val rectDst = Rect(0, 0, wDstInt, hDstInt)
|
||||||
canvas.drawBitmap(src, rectSrc, rectDst, sPaintDontBlend)
|
canvas.drawBitmap(src, rectSrc, rectDst, sPaintDontBlend)
|
||||||
|
|
||||||
if (recycleSrc) src.recycle()
|
if (recycleSrc) src.recycle()
|
||||||
|
@ -83,12 +106,12 @@ class ApngFrames private constructor(
|
||||||
Bitmap.Config.ARGB_8888
|
Bitmap.Config.ARGB_8888
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun toAndroidBitmap(src: ApngBitmap, sizeMax: Int) =
|
private fun toAndroidBitmap(src: ApngBitmap, sizeMax: Float) =
|
||||||
scaleBitmap(sizeMax, toAndroidBitmap(src))
|
scaleBitmap(sizeMax, toAndroidBitmap(src))
|
||||||
|
|
||||||
private fun parseApng(
|
private fun parseApng(
|
||||||
inStream: InputStream,
|
inStream: InputStream,
|
||||||
pixelSizeMax: Int,
|
pixelSizeMax: Float,
|
||||||
debug: Boolean = false,
|
debug: Boolean = false,
|
||||||
): ApngFrames {
|
): ApngFrames {
|
||||||
val result = ApngFrames(pixelSizeMax, debug)
|
val result = ApngFrames(pixelSizeMax, debug)
|
||||||
|
@ -105,7 +128,7 @@ class ApngFrames private constructor(
|
||||||
|
|
||||||
private fun parseWebP(
|
private fun parseWebP(
|
||||||
inStream: InputStream,
|
inStream: InputStream,
|
||||||
pixelSizeMax: Int,
|
pixelSizeMax: Float,
|
||||||
debug: Boolean = false,
|
debug: Boolean = false,
|
||||||
): ApngFrames {
|
): ApngFrames {
|
||||||
val result = ApngFrames(pixelSizeMax, debug)
|
val result = ApngFrames(pixelSizeMax, debug)
|
||||||
|
@ -122,7 +145,7 @@ class ApngFrames private constructor(
|
||||||
|
|
||||||
private fun parseGif(
|
private fun parseGif(
|
||||||
inStream: InputStream,
|
inStream: InputStream,
|
||||||
pixelSizeMax: Int,
|
pixelSizeMax: Float,
|
||||||
debug: Boolean = false,
|
debug: Boolean = false,
|
||||||
): ApngFrames {
|
): ApngFrames {
|
||||||
val result = ApngFrames(pixelSizeMax, debug)
|
val result = ApngFrames(pixelSizeMax, debug)
|
||||||
|
@ -166,7 +189,7 @@ class ApngFrames private constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parse(
|
fun parse(
|
||||||
pixelSizeMax: Int,
|
pixelSizeMax: Float,
|
||||||
debug: Boolean = false,
|
debug: Boolean = false,
|
||||||
opener: () -> InputStream?,
|
opener: () -> InputStream?,
|
||||||
): ApngFrames? {
|
): ApngFrames? {
|
||||||
|
@ -284,7 +307,7 @@ class ApngFrames private constructor(
|
||||||
val animationControl = this.animationControl
|
val animationControl = this.animationControl
|
||||||
val frames = this.frames
|
val frames = this.frames
|
||||||
|
|
||||||
if (animationControl == null || frames == null || frames.isEmpty()) {
|
if (animationControl == null || frames.isNullOrEmpty()) {
|
||||||
// ここは通らないはず…
|
// ここは通らないはず…
|
||||||
result.bitmap = null
|
result.bitmap = null
|
||||||
result.delay = Long.MAX_VALUE
|
result.delay = Long.MAX_VALUE
|
||||||
|
@ -409,6 +432,7 @@ class ApngFrames private constructor(
|
||||||
frameControl.width,
|
frameControl.width,
|
||||||
frameControl.height
|
frameControl.height
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
package jp.juggler.subwaytooter
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import jp.juggler.util.data.*
|
||||||
|
import org.junit.Assert.fail
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class TestBitmapSample {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BitmapFactory.Options.inSampleSize の取り扱いの確認
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun test() {
|
||||||
|
val srcSize = 1024
|
||||||
|
|
||||||
|
val baSrc = run {
|
||||||
|
val bitmap = Bitmap.createBitmap(srcSize, srcSize, Bitmap.Config.ARGB_8888)
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream().use { outStream ->
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.JPEG, 70, outStream)
|
||||||
|
outStream.toByteArray()
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
bitmap.recycle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (n in 0..32) {
|
||||||
|
val options = BitmapFactory.Options().apply {
|
||||||
|
inScaled = false
|
||||||
|
outWidth = 0
|
||||||
|
outHeight = 0
|
||||||
|
inJustDecodeBounds = false
|
||||||
|
inSampleSize = n
|
||||||
|
}
|
||||||
|
val expectedSizeA: Int
|
||||||
|
val expectedSizeB: Int
|
||||||
|
when (n) {
|
||||||
|
// ドキュメントには "If set to a value > 1" とあり、1以下の値はリサイズに影響しない
|
||||||
|
0, 1 -> {
|
||||||
|
expectedSizeA = srcSize
|
||||||
|
expectedSizeB = srcSize
|
||||||
|
}
|
||||||
|
// 2以上の場合、ドキュメントには
|
||||||
|
// "Note: the decoder uses a final value based on powers of 2, any other value will be rounded down to the nearest power of 2."
|
||||||
|
// とあるが、実際に試すと端末により width = srcSize/n となることがある。
|
||||||
|
else -> {
|
||||||
|
expectedSizeA = srcSize.div(n.takeHighestOneBit())
|
||||||
|
expectedSizeB = srcSize.div(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val bitmap = BitmapFactory.decodeByteArray(baSrc, 0, baSrc.size, options)!!
|
||||||
|
try {
|
||||||
|
when (bitmap.width) {
|
||||||
|
expectedSizeA -> Unit
|
||||||
|
expectedSizeB -> Unit
|
||||||
|
else -> fail("inSampleSize=$n, srcSize=$srcSize, resultWidth=${bitmap.width}")
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
bitmap.recycle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -72,7 +72,7 @@ class ActText : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SearchHilightSpan(color: Int) : BackgroundColorSpan(color)
|
class SearchResultSpan(color: Int) : BackgroundColorSpan(color)
|
||||||
|
|
||||||
private var account: SavedAccount? = null
|
private var account: SavedAccount? = null
|
||||||
|
|
||||||
|
@ -360,7 +360,7 @@ class ActText : AppCompatActivity() {
|
||||||
|
|
||||||
private fun searchHighlight(newPos: Int?) {
|
private fun searchHighlight(newPos: Int?) {
|
||||||
views.etText.text?.let { e ->
|
views.etText.text?.let { e ->
|
||||||
for (span in e.getSpans(0, e.length, SearchHilightSpan::class.java)) {
|
for (span in e.getSpans(0, e.length, SearchResultSpan::class.java)) {
|
||||||
try {
|
try {
|
||||||
e.removeSpan(span)
|
e.removeSpan(span)
|
||||||
} catch (ignored: Throwable) {
|
} catch (ignored: Throwable) {
|
||||||
|
@ -372,7 +372,7 @@ class ActText : AppCompatActivity() {
|
||||||
else -> R.attr.colorButtonBgCw
|
else -> R.attr.colorButtonBgCw
|
||||||
}
|
}
|
||||||
e.setSpan(
|
e.setSpan(
|
||||||
SearchHilightSpan(attrColor(attrId)),
|
SearchResultSpan(attrColor(attrId)),
|
||||||
pos,
|
pos,
|
||||||
pos + searchKeywordLength,
|
pos + searchKeywordLength,
|
||||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
|
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
|
||||||
|
|
|
@ -9,21 +9,23 @@ import android.os.Handler
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import com.caverock.androidsvg.SVG
|
import com.caverock.androidsvg.SVG
|
||||||
import jp.juggler.apng.ApngFrames
|
import jp.juggler.apng.ApngFrames
|
||||||
|
import jp.juggler.apng.ApngFrames.Companion.scaleEmojiSize
|
||||||
import jp.juggler.subwaytooter.App1
|
import jp.juggler.subwaytooter.App1
|
||||||
import jp.juggler.subwaytooter.pref.PrefS
|
import jp.juggler.subwaytooter.pref.PrefS
|
||||||
import jp.juggler.subwaytooter.table.EmojiCacheDbOpenHelper
|
import jp.juggler.subwaytooter.table.EmojiCacheDbOpenHelper
|
||||||
import jp.juggler.subwaytooter.table.daoImageAspect
|
import jp.juggler.subwaytooter.table.daoImageAspect
|
||||||
import jp.juggler.util.coroutine.EmptyScope
|
import jp.juggler.util.coroutine.EmptyScope
|
||||||
import jp.juggler.util.data.*
|
import jp.juggler.util.data.clip
|
||||||
import jp.juggler.util.log.*
|
import jp.juggler.util.log.LogCategory
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.*
|
import java.util.LinkedList
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
class CustomEmojiCache(
|
class CustomEmojiCache(
|
||||||
val context: Context,
|
val context: Context,
|
||||||
|
@ -248,7 +250,7 @@ class CustomEmojiCache(
|
||||||
|
|
||||||
ts = elapsedTime
|
ts = elapsedTime
|
||||||
val frames = try {
|
val frames = try {
|
||||||
data?.let { decodeAPNG(it, request.url) }
|
data?.let { decodeImage(it, request.url) }
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
log.e(ex, "decode failed.")
|
log.e(ex, "decode failed.")
|
||||||
null
|
null
|
||||||
|
@ -327,13 +329,13 @@ class CustomEmojiCache(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decodeAPNG(data: ByteArray, url: String): ApngFrames? {
|
private fun decodeImage(data: ByteArray, url: String): ApngFrames? {
|
||||||
val errors = ArrayList<Throwable>()
|
val errors = ArrayList<Throwable>()
|
||||||
|
|
||||||
val maxSize = PrefS.spEmojiPixels.toInt().clip(16, 1024)
|
val maxSize = PrefS.spEmojiPixels.toInt().clip(16, 1024).toFloat()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// APNGをデコード AWebPも
|
// APNG,AWebP,AGIF
|
||||||
val x = ApngFrames.parse(maxSize) { ByteArrayInputStream(data) }
|
val x = ApngFrames.parse(maxSize) { ByteArrayInputStream(data) }
|
||||||
if (x != null) return x
|
if (x != null) return x
|
||||||
error("ApngFrames.parse returns null.")
|
error("ApngFrames.parse returns null.")
|
||||||
|
@ -354,7 +356,7 @@ class CustomEmojiCache(
|
||||||
|
|
||||||
// SVGのロードを試みる
|
// SVGのロードを試みる
|
||||||
try {
|
try {
|
||||||
val b = decodeSVG(url, data, maxSize.toFloat())
|
val b = decodeSVG(url, data, maxSize)
|
||||||
if (b != null) return ApngFrames(b)
|
if (b != null) return ApngFrames(b)
|
||||||
error("decodeSVG returns null.")
|
error("decodeSVG returns null.")
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
|
@ -373,22 +375,27 @@ class CustomEmojiCache(
|
||||||
|
|
||||||
private fun decodeBitmap(
|
private fun decodeBitmap(
|
||||||
data: ByteArray,
|
data: ByteArray,
|
||||||
@Suppress("SameParameterValue") pixelMax: Int,
|
maxPixels: Float,
|
||||||
): Bitmap? {
|
): Bitmap? {
|
||||||
options.inJustDecodeBounds = true
|
options.inJustDecodeBounds = true
|
||||||
options.inScaled = false
|
options.inScaled = false
|
||||||
options.outWidth = 0
|
options.outWidth = 0
|
||||||
options.outHeight = 0
|
options.outHeight = 0
|
||||||
BitmapFactory.decodeByteArray(data, 0, data.size, options)
|
BitmapFactory.decodeByteArray(data, 0, data.size, options)
|
||||||
var w = options.outWidth
|
var srcW = options.outWidth
|
||||||
var h = options.outHeight
|
var srcH = options.outHeight
|
||||||
if (w <= 0 || h <= 0) error("decodeBitmap: can't decode bounds.")
|
if (srcW <= 0 || srcH <= 0) error("decodeBitmap: can't decode bounds.")
|
||||||
|
|
||||||
|
val (preferW, preferH) = scaleEmojiSize(
|
||||||
|
srcW.toFloat(),
|
||||||
|
srcH.toFloat(),
|
||||||
|
maxPixels
|
||||||
|
)
|
||||||
var bits = 0
|
var bits = 0
|
||||||
while (w > pixelMax || h > pixelMax) {
|
while (srcW > preferW || srcW > preferH) {
|
||||||
++bits
|
++bits
|
||||||
w = w shr 1
|
srcW = srcW shr 1
|
||||||
h = h shr 1
|
srcH = srcH shr 1
|
||||||
}
|
}
|
||||||
options.inJustDecodeBounds = false
|
options.inJustDecodeBounds = false
|
||||||
options.inSampleSize = 1 shl bits
|
options.inSampleSize = 1 shl bits
|
||||||
|
@ -398,33 +405,20 @@ class CustomEmojiCache(
|
||||||
private fun decodeSVG(
|
private fun decodeSVG(
|
||||||
url: String,
|
url: String,
|
||||||
data: ByteArray,
|
data: ByteArray,
|
||||||
@Suppress("SameParameterValue") pixelMax: Float,
|
maxSize: Float,
|
||||||
): Bitmap? {
|
): Bitmap? {
|
||||||
try {
|
try {
|
||||||
val svg = SVG.getFromInputStream(ByteArrayInputStream(data))
|
val svg = SVG.getFromInputStream(ByteArrayInputStream(data))
|
||||||
|
|
||||||
// the width in pixels, or -1 if there is no width available.
|
val (wDst, hDst) = scaleEmojiSize(
|
||||||
// the height in pixels, or -1 if there is no height available.
|
// the width in pixels, or -1 if there is no width available.
|
||||||
val srcW = svg.documentWidth
|
svg.documentWidth,
|
||||||
val srcH = svg.documentHeight
|
// the height in pixels, or -1 if there is no height available.
|
||||||
val aspect = if (srcW <= 0f || srcH <= 0f) {
|
svg.documentHeight,
|
||||||
// widthやheightの情報がない
|
maxSize
|
||||||
1f
|
)
|
||||||
} else {
|
val wCeil = max(1f, ceil(wDst))
|
||||||
srcW / srcH
|
val hCeil = max(1f, ceil(hDst))
|
||||||
}
|
|
||||||
|
|
||||||
val dstW: Float
|
|
||||||
val dstH: Float
|
|
||||||
if (aspect >= 1f) {
|
|
||||||
dstW = pixelMax
|
|
||||||
dstH = pixelMax / aspect
|
|
||||||
} else {
|
|
||||||
dstH = pixelMax
|
|
||||||
dstW = pixelMax * aspect
|
|
||||||
}
|
|
||||||
val wCeil = ceil(dstW)
|
|
||||||
val hCeil = ceil(dstH)
|
|
||||||
|
|
||||||
// Create a Bitmap to render our SVG to
|
// Create a Bitmap to render our SVG to
|
||||||
val b = Bitmap.createBitmap(wCeil.toInt(), hCeil.toInt(), Bitmap.Config.ARGB_8888)
|
val b = Bitmap.createBitmap(wCeil.toInt(), hCeil.toInt(), Bitmap.Config.ARGB_8888)
|
||||||
|
@ -433,10 +427,10 @@ class CustomEmojiCache(
|
||||||
|
|
||||||
svg.renderToCanvas(
|
svg.renderToCanvas(
|
||||||
canvas,
|
canvas,
|
||||||
if (aspect >= 1f) {
|
if (wDst >= hDst) {
|
||||||
RectF(0f, hCeil - dstH, dstW, dstH) // 後半はw,hを指定する
|
RectF(0f, hCeil - hDst, wDst, hDst) // 後半はw,hを指定する
|
||||||
} else {
|
} else {
|
||||||
RectF(wCeil - dstW, 0f, dstW, dstH) // 後半はw,hを指定する
|
RectF(wCeil - wDst, 0f, wDst, hDst) // 後半はw,hを指定する
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return b
|
return b
|
||||||
|
|
|
@ -181,7 +181,7 @@ class ActList : AppCompatActivity(), CoroutineScope {
|
||||||
|
|
||||||
val job = async(AppDispatchers.IO) {
|
val job = async(AppDispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
ApngFrames.parse(128) { resources?.openRawResource(resId) }
|
ApngFrames.parse(128f) { resources?.openRawResource(resId) }
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
ex.printStackTrace()
|
ex.printStackTrace()
|
||||||
null
|
null
|
||||||
|
|
|
@ -62,7 +62,7 @@ class ActViewer : AsyncActivity() {
|
||||||
apngFrames = withContext(AppDispatchers.IO) {
|
apngFrames = withContext(AppDispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
ApngFrames.parse(
|
ApngFrames.parse(
|
||||||
1024,
|
1024f,
|
||||||
debug = true
|
debug = true
|
||||||
) { resources?.openRawResource(resId) }
|
) { resources?.openRawResource(resId) }
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
|
|
Loading…
Reference in New Issue