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 inner class SvgDecoder : ResourceDecoder { @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 { 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 { override fun transcode( toTranscode: Resource, options: Options, ) = try { SimpleResource(PictureDrawable(toTranscode.get().renderToPicture())) } catch (ex: Throwable) { throw IOException("Cannot render SVG.", ex) } } // APNG: transcode ByteBuffer => APNGDecoder class ApngByteBufferDecoder : ResourceDecoder { 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 { 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 { override fun transcode( toTranscode: Resource, options: Options, ) = APNGDrawable(toTranscode.get()).apply { setAutoPlay(false) }.let { object : DrawableResource(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) } }