158 lines
6.0 KiB
Kotlin
158 lines
6.0 KiB
Kotlin
package jp.juggler.subwaytooter
|
|
|
|
import android.content.Context
|
|
import android.graphics.drawable.PictureDrawable
|
|
import com.bumptech.glide.Glide
|
|
import com.bumptech.glide.GlideBuilder
|
|
import com.bumptech.glide.Registry
|
|
import com.bumptech.glide.annotation.GlideModule
|
|
import com.bumptech.glide.integration.webp.decoder.*
|
|
import com.bumptech.glide.load.Options
|
|
import com.bumptech.glide.load.ResourceDecoder
|
|
import com.bumptech.glide.load.engine.Resource
|
|
import com.bumptech.glide.load.resource.SimpleResource
|
|
import com.bumptech.glide.load.resource.drawable.DrawableResource
|
|
import com.bumptech.glide.load.resource.transcode.ResourceTranscoder
|
|
import com.bumptech.glide.module.AppGlideModule
|
|
import com.caverock.androidsvg.SVG
|
|
import com.caverock.androidsvg.SVGParseException
|
|
import com.github.penfeizhou.animation.apng.APNGDrawable
|
|
import com.github.penfeizhou.animation.apng.decode.APNGDecoder
|
|
import com.github.penfeizhou.animation.apng.decode.APNGParser
|
|
import com.github.penfeizhou.animation.io.ByteBufferReader
|
|
import com.github.penfeizhou.animation.loader.ByteBufferLoader
|
|
import java.io.IOException
|
|
import java.io.InputStream
|
|
import java.nio.ByteBuffer
|
|
import kotlin.math.min
|
|
|
|
@GlideModule
|
|
class MyAppGlideModule : AppGlideModule() {
|
|
|
|
companion object {
|
|
|
|
private val svgSig = "<svg".toByteArray(Charsets.UTF_8)
|
|
|
|
private fun findBytes(data: ByteArray, dataSize: Int = data.size, key: ByteArray): Int {
|
|
|
|
fun check(start: Int): Boolean {
|
|
for (j in key.indices) {
|
|
if (data[start + j] != key[j]) return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
for (i in 0..dataSize - key.size) {
|
|
if (check(i)) return i
|
|
}
|
|
return -1
|
|
}
|
|
}
|
|
|
|
// SVG: InputStream => SVG
|
|
inner class SvgDecoder : ResourceDecoder<InputStream, SVG> {
|
|
|
|
@Throws(IOException::class)
|
|
override fun handles(source: InputStream, options: Options): Boolean {
|
|
val size = min(source.available(), 1024)
|
|
if (size <= 0) return false
|
|
val buf = ByteArray(size)
|
|
val nRead = source.read(buf, 0, size)
|
|
return -1 != findBytes(buf, nRead, svgSig)
|
|
}
|
|
|
|
@Throws(IOException::class)
|
|
override fun decode(
|
|
source: InputStream,
|
|
width: Int,
|
|
height: Int,
|
|
options: Options,
|
|
): Resource<SVG> {
|
|
try {
|
|
val svg = SVG.getFromInputStream(source)
|
|
return SimpleResource(svg)
|
|
} catch (ex: SVGParseException) {
|
|
throw IOException("Cannot load SVG from stream", ex)
|
|
}
|
|
}
|
|
}
|
|
|
|
// SVG: transcode SVG => PictureDrawable
|
|
class SvgDrawableTranscoder : ResourceTranscoder<SVG, PictureDrawable> {
|
|
override fun transcode(
|
|
toTranscode: Resource<SVG>,
|
|
options: Options,
|
|
) = try {
|
|
SimpleResource(PictureDrawable(toTranscode.get().renderToPicture()))
|
|
} catch (ex: Throwable) {
|
|
throw IOException("Cannot render SVG.", ex)
|
|
}
|
|
}
|
|
|
|
// APNG: transcode ByteBuffer => APNGDecoder
|
|
class ApngByteBufferDecoder : ResourceDecoder<ByteBuffer, APNGDecoder> {
|
|
override fun handles(source: ByteBuffer, options: Options) =
|
|
source.limit() >= 8 && APNGParser.isAPNG(ByteBufferReader(source))
|
|
|
|
override fun decode(
|
|
source: ByteBuffer,
|
|
width: Int,
|
|
height: Int,
|
|
options: Options,
|
|
) = object : Resource<APNGDecoder> {
|
|
val decoder = object : ByteBufferLoader() {
|
|
override fun getByteBuffer() = source.apply { position(0) }
|
|
}.let { APNGDecoder(it, null) }
|
|
|
|
override fun getResourceClass() = APNGDecoder::class.java
|
|
override fun getSize() = source.limit()
|
|
override fun get() = decoder
|
|
override fun recycle() = decoder.stop()
|
|
}
|
|
}
|
|
|
|
// APNG: transcode APNGDecoder => APNGDrawable
|
|
class ApngTranscoder : ResourceTranscoder<APNGDecoder, APNGDrawable> {
|
|
override fun transcode(
|
|
toTranscode: Resource<APNGDecoder>,
|
|
options: Options,
|
|
) = APNGDrawable(toTranscode.get()).apply { setAutoPlay(false) }.let {
|
|
object : DrawableResource<APNGDrawable>(it) {
|
|
override fun getResourceClass() = APNGDrawable::class.java
|
|
override fun getSize() = it.memorySize
|
|
override fun recycle() = it.stop()
|
|
}
|
|
}
|
|
}
|
|
|
|
// v3との互換性のためにAndroidManifestを読むかどうか(デフォルトtrue)
|
|
override fun isManifestParsingEnabled(): Boolean {
|
|
return false
|
|
}
|
|
|
|
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
|
|
// デフォルト実装は何もしないらしい
|
|
super.registerComponents(context, glide, registry)
|
|
|
|
// App1を初期化してからOkHttp3Factoryと連動させる
|
|
App1.prepare(context.applicationContext, "MyAppGlideModule.registerComponents()")
|
|
App1.registerGlideComponents(context, glide, registry)
|
|
|
|
//SVGデコーダーの追加
|
|
registry
|
|
.prepend(ByteBuffer::class.java, APNGDecoder::class.java, ApngByteBufferDecoder())
|
|
.register(APNGDecoder::class.java, APNGDrawable::class.java, ApngTranscoder())
|
|
.register(SVG::class.java, PictureDrawable::class.java, SvgDrawableTranscoder())
|
|
.append(InputStream::class.java, SVG::class.java, SvgDecoder())
|
|
}
|
|
|
|
override fun applyOptions(context: Context, builder: GlideBuilder) {
|
|
// デフォルト実装は何もしないらしい
|
|
super.applyOptions(context, builder)
|
|
|
|
// App1を初期化してから色々する
|
|
App1.prepare(context.applicationContext, "MyAppGlideModule.applyOptions()")
|
|
App1.applyGlideOptions(context, builder)
|
|
}
|
|
}
|