SubwayTooter-Android-App/app/src/main/java/jp/juggler/subwaytooter/MyAppGlideModule.kt

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