Android Glide 확장

Android가 라이브러리 Glide를 이미지로 읽고 apng을 처리하려고 할 때
잘 안 풀려서 해결하는데 시간이 많이 걸리니까 해결책을 써요.

svg 처리 시도


com.caverock:androidsvg-Aar:1.4 이 프로그램 라이브러리는Glide에서 사용할 수 있습니다.
먼저 GlideModule을 생성합니다.
문서에 따라 AppGlideModule을 상속합니다.
참고로 모델이라는 이름에서 알 수 있어요.
Glide 인스턴스 설정은 여기서부터 시작됩니다.
@GlideModule(glideName = "GlideApp")
class MiGlideModule : AppGlideModule(){

    override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
    
    }


    override fun isManifestParsingEnabled(): Boolean {
        return false
    }
}

InputStream을 SVG로 변환하는 추가 처리


svg 처리 추가
Glide는 응용 프로그램과 prepend에 임의의 이미지 처리를 추가할 수 있습니다.
Glide는 응용 프로그램과 prepend의 차이로 이미지를 decorder 처리합니다
Chain of Responsibility 형태의 처리입니다.
이전 디코더 판정 처리가 실패하면 다음 디코더 처리를 할 수 있습니다.
따라서 먼저 처리하고 싶으면prepend를 처리하고, 나중에 처리하고 싶으면appeend를 사용하세요.
prepend가 어지럽게 처리되면 GLide가 기본적으로 들어오는 그림의 Decorder 처리를 방해합니다
그 처리를 덮어쓸 의도가 없으면 appeend를 사용하십시오.
SVG는 Glide의 표준 처리에서 모두 처리할 수 없기 때문에 적용하십시오.
처리하는 내용은 InputStream을 받아서 SvgDecoder를 통해 SVG 클래스로 변환하는 것입니다.
@GlideModule(glideName = "GlideApp")
class MiGlideModule : AppGlideModule(){

    override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
        registry
            .append(InputStream::class.java, SVG::class.java, SvgDecoder())

    }


    override fun isManifestParsingEnabled(): Boolean {
        return false
    }
}
다음 패키지에서 SvgDecorder 클래스를 만듭니다.
handlers는 이 그림이 이 데코더가 처리할 수 있는 그림인지 아닌지를 판정합니다.
진실로 돌아오면 판정할 수 있다.가짜로 돌아왔다면 판정할 수 없다.
아까 appeend, prepend였으면.
문답이 필요 없이 진실로 답장하면 나중에 응용해도 decorder가 처리되지 않는 현상이 발생한다.
decode는 첫 번째 매개 변수 입력(InputStream)에 따른 입력 값입니다.
출력 보조 매개변수의 출력(SVG) 처리를 기술했습니다.

private const val SVG_HEADER: Int = 0x3C737667
private const val SVG_HEADER_STARTS_WITH_XML = 0x3c3f786d
class SvgDecoder : ResourceDecoder<InputStream, SVG>{


    override fun handles(source: InputStream, options: Options): Boolean {
        val buffer = ByteArray(8)
        val cnt = source.read(buffer)
        if (cnt < 8) {
            Log.d("SvgDecoder", "svgではない")
            return false
        }

        Log.d("SvgDecoder", "svgだった")
        val header = ByteBuffer.wrap(buffer).int
        return header == SVG_HEADER || header == SVG_HEADER_STARTS_WITH_XML
    }

    override fun decode(
        source: InputStream,
        width: Int,
        height: Int,
        options: Options
    ): Resource<SVG>? {
        return try {
            val svg = SVG.getFromInputStream(source)
            SimpleResource(svg)
        }catch(e: SVGParseException){
            Log.e("SvgDecoder", "error", e)
            null
        }

    }
}

SVG를 Drawarble로 만듭니다.


SvgDecoder 적용
InputStream에서 SVG를 받을 수 있습니다.
Android는 SVG를 직접 사용할 수 없습니다.
Drawable로 변환하는 처리를 추가합니다.
공식.에서 PictureDrawarble를 사용하지만 PictureDrawarble는 사이즈 조정과 복잡한 일을 할 수 없기 때문에 한때는 BitmapDrawable로 전환되었다.
package あなたのパッケージ名
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Picture
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.PictureDrawable
import com.bumptech.glide.load.Options
import com.bumptech.glide.load.engine.Resource
import com.bumptech.glide.load.resource.SimpleResource
import com.bumptech.glide.load.resource.transcode.ResourceTranscoder
import com.caverock.androidsvg.SVG

class SvgBitmapTransCoder(val context: Context): ResourceTranscoder<SVG, BitmapDrawable>{
    override fun transcode(toTranscode: Resource<SVG>, options: Options): Resource<BitmapDrawable> {
        val svg = toTranscode.get()
        val picture: Picture = svg.renderToPicture()
        val drawable = PictureDrawable(picture)

        val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
        val canvas = Canvas(bitmap)
        canvas.drawPicture(drawable.picture)
        return SimpleResource(BitmapDrawable(context.resources, bitmap))
    }
}
GlideModule에 추가하십시오.
※ 생략했습니다.
registry.register(SVG::class.java, BitmapDrawable::class.java, SvgBitmapTransCoder(context))

능처리apng


다음 프로그램 라이브러리를 사용하면 apng의 대응을 간단하게 할 수 있습니다
이 프로그램 라이브러리의 처리 시스템을 보면 Glide의 표준 처리 효율보다 현저히 낮다
왜냐하면 고장난 Gif를 읽으면 프로그램이 붕괴돼요.
나는 도서관에 대한 의존을 최대한 줄이고 싶다.
그래서 이 프로그램 라이브러리의 apng 부분을 Glide에 잘 편집하고 싶습니다.
https://github.com/penfeizhou/APNG4Android

apng의 불만 사항


svg 대응은 헤더로 할 수 있어요.
apng의 머리가 png으로 변하기 때문에 png=apng 판정이 진행되면
pg를 처리할 수 없기 때문에 주의해야 합니다.(라이브러리에 따라 다름)

build.gradle에 라이브러리 추가


버전은 라이브러리 참조.
dependencies {
	implementation 'com.github.penfeizhou.android.animation:apng:${VERSION}'
}

apng의 decorder를 추가합니다.


소스 코드의 내용은 전체 프로그램 라이브러리를 참고합니다.
판정 처리 중 머리의 PNG 여부를 먼저 판정한다.
이 판단에 가입함으로써 불필요한 데이터를 읽을 필요가 없다
비용을 줄일 수 있다.
최종적으로 APNGparser에 대한 isAPNG 판정
png 처리가 파괴되는 것을 방지합니다.
참고로 ByteBuffer를 선택한 이유는 다음과 같습니다.
glide의 원본 코드, png 등의 처리를 보았는데 우선 ByteBuffer를 통해 처리되었다(모호한 기억)
const val PNG: Long = 0x89504E47


class ByteBufferApngDecoder : ResourceDecoder<ByteBuffer, FrameSeqDecoder<*, *>> {


    override fun decode(
        source: ByteBuffer,
        width: Int,
        height: Int,
        options: Options
    ): Resource<FrameSeqDecoder<*, *>> {
        val loader: Loader = object : ByteBufferLoader() {
            override fun getByteBuffer(): ByteBuffer {
                source.position(0)
                return source
            }
        }
        val decoder = APNGDecoder(loader, null)
        return FrameSeqDecoderResource(decoder, source.limit())
    }

    override fun handles(source: ByteBuffer, options: Options): Boolean {
        Log.d("ByteBufferApngDecoder", "apng decoder on decode")
        val byteBufferArray = ByteArray(8)
        source.get(byteBufferArray, 0, 4)
        val header = ByteBuffer.wrap(byteBufferArray).long ushr 32
        if (header != PNG) {
            Log.d("ByteBufferApngDecoder", "is not png:${header.toHexString()}")
            return false
        }

        val result = APNGParser.isAPNG(ByteBufferReader(source))
        Log.d("ByteBufferApngDecoder", "handlers isApng:$result")
        return result

    }

    private class FrameSeqDecoderResource(
        private val decoder: FrameSeqDecoder<*, *>,
        private val size: Int
    ) : Resource<FrameSeqDecoder<*, *>> {
        override fun getResourceClass(): Class<FrameSeqDecoder<*, *>> {
            return FrameSeqDecoder::class.java
        }

        override fun get(): FrameSeqDecoder<*, *> {
            return decoder
        }

        override fun getSize(): Int {
            return size
        }

        override fun recycle() {
            decoder.stop()
        }
    }

}

Glide Module에 추가


최종 GlideModule입니다.
클라스에서 제안했어야 했는데 너무 많이 썼어.
SVG 의 차이점은 다음과 같습니다.
  • 소스가 더 더러워
  • transode 처리를 통해 자원 방출 처리
  • 응용 대신 prepand를 사용합니다
  • 특히 프로그램이 아닌 prepend를 사용하는 이유
    Glide의 pg의 Decorder보다 먼저 판정을 내려야 하기 때문이다.
    
    @GlideModule(glideName = "GlideApp")
    class MiGlideModule : AppGlideModule(){
    
        override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
            registry
                .prepend(ByteBuffer::class.java, FrameSeqDecoder::class.java, ByteBufferApngDecoder())
                .register(FrameSeqDecoder::class.java, Drawable::class.java, object : ResourceTranscoder<FrameSeqDecoder<*, *>, Drawable> {
                    override fun transcode(
                        toTranscode: Resource<FrameSeqDecoder<*, *>>,
                        options: Options
                    ): Resource<Drawable> {
                        val apngDrawable = APNGDrawable(toTranscode.get() as APNGDecoder)
                        apngDrawable.setAutoPlay(false)
                        return object : DrawableResource<Drawable>(apngDrawable) {
                            override fun getResourceClass(): Class<Drawable> {
                                return Drawable::class.java
                            }
    
                            override fun getSize(): Int {
                                return apngDrawable.memorySize
                            }
    
                            override fun recycle() {
                                apngDrawable.stop()
                            }
    
                        }
                    }
                })
                .register(SVG::class.java, BitmapDrawable::class.java, SvgBitmapTransCoder(context))
                .append(InputStream::class.java, SVG::class.java, SvgDecoder())
    
        }
    
    
        override fun isManifestParsingEnabled(): Boolean {
            return false
        }
    }
    
    이상.

    좋은 웹페이지 즐겨찾기