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

181 lines
7.2 KiB
Kotlin

package jp.juggler.subwaytooter
import android.content.Context
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
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.engine.bitmap_recycle.ArrayPool
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
import com.bumptech.glide.load.resource.SimpleResource
import com.bumptech.glide.load.resource.bitmap.BitmapDrawableDecoder
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 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
}
}
// Decodes an SVG internal representation from an [InputStream].
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)
}
}
}
// Convert the [SVG]'s internal representation to an Android-compatible one ([Picture]).
class SvgDrawableTranscoder : ResourceTranscoder<SVG, PictureDrawable> {
override fun transcode(
toTranscode: Resource<SVG>,
options: Options,
): Resource<PictureDrawable> {
val svg = toTranscode.get()
val picture = svg.renderToPicture()
val drawable = PictureDrawable(picture)
return SimpleResource(drawable)
}
}
// 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
.register(SVG::class.java, PictureDrawable::class.java, SvgDrawableTranscoder())
.append(InputStream::class.java, SVG::class.java, SvgDecoder())
///////
// Animated WebP
// // We should put our decoder before the build-in decoders,
// // because the Downsampler will consume arbitrary data and make the inputstream corrupt
// // on some devices
// val resources: Resources = context.resources
// val bitmapPool: BitmapPool = glide.bitmapPool
// val arrayPool: ArrayPool = glide.arrayPool
// /* static webp decoders */
// val webpDownsampler = WebpDownsampler(
// registry.imageHeaderParsers,
// resources.getDisplayMetrics(), bitmapPool, arrayPool
// )
// val bitmapDecoder = AnimatedWebpBitmapDecoder(arrayPool, bitmapPool)
// val byteBufferBitmapDecoder = ByteBufferBitmapWebpDecoder(webpDownsampler)
// val streamBitmapDecoder = StreamBitmapWebpDecoder(webpDownsampler, arrayPool)
// /* animate webp decoders */
// val byteBufferWebpDecoder = ByteBufferWebpDecoder(context, arrayPool, bitmapPool)
// registry /* Bitmaps for static webp images */
// .prepend(
// Registry.BUCKET_BITMAP,
// ByteBuffer::class.java,
// Bitmap::class.java, byteBufferBitmapDecoder
// )
// .prepend(
// Registry.BUCKET_BITMAP,
// InputStream::class.java,
// Bitmap::class.java, streamBitmapDecoder
// ) /* BitmapDrawables for static webp images */
// .prepend(
// Registry.BUCKET_BITMAP_DRAWABLE,
// ByteBuffer::class.java,
// BitmapDrawable::class.java,
// BitmapDrawableDecoder(resources, byteBufferBitmapDecoder)
// )
// .prepend(
// Registry.BUCKET_BITMAP_DRAWABLE,
// InputStream::class.java,
// BitmapDrawable::class.java,
// BitmapDrawableDecoder(resources, streamBitmapDecoder)
// ) /* Bitmaps for animated webp images*/
// .prepend(
// Registry.BUCKET_BITMAP,
// ByteBuffer::class.java,
// Bitmap::class.java, ByteBufferAnimatedBitmapDecoder(bitmapDecoder)
// )
// .prepend(
// Registry.BUCKET_BITMAP,
// InputStream::class.java,
// Bitmap::class.java, StreamAnimatedBitmapDecoder(bitmapDecoder)
// ) /* Animated webp images */
// .prepend(ByteBuffer::class.java, WebpDrawable::class.java, byteBufferWebpDecoder)
// .prepend(
// InputStream::class.java,
// WebpDrawable::class.java, StreamWebpDecoder(byteBufferWebpDecoder, arrayPool)
// )
// .prepend(WebpDrawable::class.java, WebpDrawableEncoder())
}
override fun applyOptions(context: Context, builder: GlideBuilder) {
// デフォルト実装は何もしないらしい
super.applyOptions(context, builder)
// App1を初期化してから色々する
App1.prepare(context.applicationContext, "MyAppGlideModule.applyOptions()")
App1.applyGlideOptions(context, builder)
}
}