Add raw resource decoder

This commit is contained in:
oupson 2019-05-17 21:23:40 +02:00
parent 887d679c4b
commit 5bb270132f
6 changed files with 81 additions and 29 deletions

View File

@ -20,6 +20,7 @@ import java.util.zip.CRC32
/**
* Create an APNG file
*/
@Suppress("unused")
class Apng {
@Suppress("MemberVisibilityCanBePrivate")
var maxWidth : Int? = null
@ -45,7 +46,6 @@ class Apng {
* @param disposeOp `DisposeOp` specifies how the output buffer should be changed at the end of the delay (before rendering the next frame).
* @param blendOp `BlendOp` specifies whether the frame is to be alpha blended into the current output buffer content, or whether it should completely replace its region in the output buffer.
*/
fun addFrames(bitmap : Bitmap, index : Int? = null, delay : Float = 1000f, xOffset : Int = 0, yOffset : Int = 0, disposeOp: Utils.Companion.DisposeOp = Utils.Companion.DisposeOp.APNG_DISPOSE_OP_NONE, blendOp: Utils.Companion.BlendOp = Utils.Companion.BlendOp.APNG_BLEND_OP_SOURCE) {
if (index == null)
frames.add(Frame(PngEncoder.encode(bitmap, true), delay, xOffset, yOffset, blendOp, disposeOp))
@ -53,7 +53,6 @@ class Apng {
frames.add(index, Frame(PngEncoder.encode(bitmap, true), delay, xOffset, yOffset, blendOp, disposeOp))
}
@Suppress("unused")
fun addFrames(frame : Frame, index: Int? = null) {
if (index == null)
frames.add(frame)
@ -95,6 +94,7 @@ class Apng {
// Add the frame number
fcTL.addAll(to4Bytes(seq).toList())
// foreach fcTL or fdAT we must increment seq
seq++
@ -102,6 +102,7 @@ class Apng {
fcTL.addAll(to4Bytes(frames[0].width).toList())
fcTL.addAll(to4Bytes(frames[0].height).toList())
// Add offsets
fcTL.addAll(to4Bytes(frames[0].xOffsets).toList())
fcTL.addAll(to4Bytes(frames[0].yOffsets).toList())
@ -369,7 +370,6 @@ class Apng {
* @param keepCover Keep the cover
* @param sizePercent Reduce image width/height by percents.
*/
@Suppress("unused")
fun reduceSize(maxColor : Int, keepCover : Boolean? = null, sizePercent : Int? = null) {
val apng = Apng()
if (keepCover != false) {

View File

@ -5,6 +5,7 @@ import android.content.SharedPreferences
import android.graphics.*
import android.net.Uri
import android.widget.ImageView
import androidx.annotation.RawRes
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.runOnUiThread
import org.jetbrains.anko.uiThread
@ -19,7 +20,7 @@ import java.net.URL
fun ImageView.loadApng(file: File, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) = ApngAnimator(this.context).loadInto(this).apply {
load(file, speed, apngAnimatorOptions)
}
@Suppress("unused")
fun ImageView.loadApng(uri : Uri, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) = ApngAnimator(this.context).loadInto(this).apply {
load(uri, speed, apngAnimatorOptions)
}
@ -31,9 +32,14 @@ fun ImageView.loadApng(url: URL, speed: Float? = null, apngAnimatorOptions: Apng
fun ImageView.loadApng(byteArray: ByteArray, speed: Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) = ApngAnimator(this.context).loadInto(this).apply {
load(byteArray, speed, apngAnimatorOptions)
}
@Suppress("unused")
fun ImageView.loadApng(string: String, speed : Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) = ApngAnimator(this.context).loadInto(this).apply {
load(string, speed, apngAnimatorOptions)
}
@Suppress("unused")
fun ImageView.loadApng(@RawRes res : Int, speed : Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) = ApngAnimator(this.context).loadInto(this).apply {
load(res, speed, apngAnimatorOptions)
}
/**
* Class to play APNG
@ -56,20 +62,26 @@ class ApngAnimator(private val context: Context?) {
}
}
private var imageView: ImageView? = null
var anim: CustomAnimationDrawable? = null
private var activeAnimation: CustomAnimationDrawable? = null
private var doOnLoaded : (ApngAnimator) -> Unit = {}
@Suppress("PrivatePropertyName")
@SuppressWarnings("WeakerAccess")
private var AnimationLoopListener : () -> Unit = {}
private var animationLoopListener : () -> Unit = {}
private var duration : ArrayList<Float>? = null
private var scaleType : ImageView.ScaleType? = null
@Suppress("MemberVisibilityCanBePrivate")
var isApng = false
@SuppressWarnings("WeakerAccess")
@Suppress("MemberVisibilityCanBePrivate")
var loadNotApng = true
private val sharedPreferences : SharedPreferences? = context?.getSharedPreferences("apngAnimator", Context.MODE_PRIVATE)
private val sharedPreferences : SharedPreferences? by lazy {
context?.getSharedPreferences("apngAnimator", Context.MODE_PRIVATE)
}
init {
loadNotApng = sharedPreferences?.getBoolean("loadNotApng", true) ?: true
@ -87,7 +99,7 @@ class ApngAnimator(private val context: Context?) {
}
/**
* Load into an imageview
* Load into an ImageView
* @param imageView Image view selected.
*/
fun loadInto(imageView: ImageView): ApngAnimator {
@ -111,8 +123,8 @@ class ApngAnimator(private val context: Context?) {
this@ApngAnimator.speed = speed
scaleType = apngAnimatorOptions?.scaleType
// Download PNG
APNGDisassembler.disassemble(bytes).frames.apply {
draw(this).apply {
APNGDisassembler.disassemble(bytes).frames.also {frames ->
draw(frames).apply {
setupAnimationDrawableAndStart(this)
}
}
@ -144,8 +156,8 @@ class ApngAnimator(private val context: Context?) {
this@ApngAnimator.speed = speed
scaleType = apngAnimatorOptions?.scaleType
// Download PNG
APNGDisassembler.disassemble(it).frames.apply {
draw(this).apply {
APNGDisassembler.disassemble(it).frames.also {frames ->
draw(frames).apply {
setupAnimationDrawableAndStart(this)
}
}
@ -181,8 +193,8 @@ class ApngAnimator(private val context: Context?) {
this@ApngAnimator.speed = speed
scaleType = apngAnimatorOptions?.scaleType
// Download PNG
APNGDisassembler.disassemble(this).frames.apply {
draw(this).apply {
APNGDisassembler.disassemble(this).frames.also { frames ->
draw(frames).apply {
setupAnimationDrawableAndStart(this)
}
}
@ -218,8 +230,8 @@ class ApngAnimator(private val context: Context?) {
this@ApngAnimator.speed = speed
scaleType = apngAnimatorOptions?.scaleType
// Download PNG
APNGDisassembler.disassemble(byteArray).frames.apply {
draw(this).apply {
APNGDisassembler.disassemble(byteArray).frames.also { frames ->
draw(frames).apply {
setupAnimationDrawableAndStart(this)
}
}
@ -270,6 +282,34 @@ class ApngAnimator(private val context: Context?) {
return this
}
fun load(@RawRes res : Int, speed : Float? = null, apngAnimatorOptions: ApngAnimatorOptions? = null) : ApngAnimator {
doAsync {
val byteArray = context?.resources?.openRawResource(res)?.readBytes() ?: byteArrayOf()
this@ApngAnimator.speed = speed
if (isApng(byteArray)) {
isApng = true
this@ApngAnimator.speed = speed
scaleType = apngAnimatorOptions?.scaleType
// Download PNG
APNGDisassembler.disassemble(byteArray).frames.also { frames ->
draw(frames).apply {
setupAnimationDrawableAndStart(this)
}
}
} else {
if (loadNotApng) {
context?.runOnUiThread {
imageView?.scaleType = this@ApngAnimator.scaleType ?: ImageView.ScaleType.FIT_CENTER
imageView?.setImageBitmap(BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size))
}
} else {
throw NotApngException()
}
}
}
return this
}
/**
* Sets up the animation drawable and any required listeners. The animation will automatically start.
*/
@ -360,7 +400,7 @@ class ApngAnimator(private val context: Context?) {
}
activeAnimation = animResume
imageView?.setImageDrawable(activeAnimation)
activeAnimation?.setOnAnimationLoopListener(AnimationLoopListener)
activeAnimation?.setOnAnimationLoopListener(animationLoopListener)
imageView?.invalidate()
duration = dura
break@frameLoop
@ -385,7 +425,7 @@ class ApngAnimator(private val context: Context?) {
*/
fun setOnAnimationLoopListener(animationLoopListener : () -> Unit) {
if (isApng) {
AnimationLoopListener = animationLoopListener
this.animationLoopListener = animationLoopListener
anim?.setOnAnimationLoopListener(animationLoopListener)
}
}

View File

@ -8,6 +8,7 @@ import androidx.annotation.ColorInt
import oupson.apng.utils.Utils
import java.util.*
// TODO Work on this class
class BitmapDiffCalculator(firstBitmap: Bitmap, secondBitmap : Bitmap) {
val res : Bitmap
var xOffset : Int = 0

View File

@ -38,7 +38,18 @@ class Utils {
/**
* Signature for png / APNG files
*/
val pngSignature: ByteArray = byteArrayOf(0x89.toByte(), 0x50.toByte(), 0x4E.toByte(), 0x47.toByte(), 0x0D.toByte(), 0x0A.toByte(), 0x1A.toByte(), 0x0A.toByte())
val pngSignature: ByteArray by lazy {
byteArrayOf(
0x89.toByte(),
0x50.toByte(),
0x4E.toByte(),
0x47.toByte(),
0x0D.toByte(),
0x0A.toByte(),
0x1A.toByte(),
0x0A.toByte()
)
}
enum class DisposeOp {
APNG_DISPOSE_OP_NONE,
@ -137,12 +148,12 @@ class Utils {
return lengthString.toLong(16).toInt()
}
val fcTL : String = Arrays.toString(byteArrayOf(0x66, 0x63, 0x54, 0x4c))
val IEND : String = Arrays.toString(byteArrayOf(0x49, 0x45, 0x4e, 0x44))
val IDAT : String = Arrays.toString(byteArrayOf(0x49, 0x44, 0x41, 0x54))
val fdAT : String = Arrays.toString(byteArrayOf(0x66, 0x64, 0x41, 0x54))
val plte : String = Arrays.toString(byteArrayOf(0x50, 0x4c, 0x54, 0x45))
val tnrs : String = Arrays.toString(byteArrayOf(0x74, 0x52, 0x4e, 0x53))
val IHDR : String = Arrays.toString(byteArrayOf(0x49, 0x48, 0x44, 0x52))
val fcTL : String by lazy { Arrays.toString(byteArrayOf(0x66, 0x63, 0x54, 0x4c)) }
val IEND : String by lazy { Arrays.toString(byteArrayOf(0x49, 0x45, 0x4e, 0x44)) }
val IDAT : String by lazy { Arrays.toString(byteArrayOf(0x49, 0x44, 0x41, 0x54)) }
val fdAT : String by lazy { Arrays.toString(byteArrayOf(0x66, 0x64, 0x41, 0x54)) }
val plte : String by lazy { Arrays.toString(byteArrayOf(0x50, 0x4c, 0x54, 0x45)) }
val tnrs : String by lazy { Arrays.toString(byteArrayOf(0x74, 0x52, 0x4e, 0x53)) }
val IHDR : String by lazy { Arrays.toString(byteArrayOf(0x49, 0x48, 0x44, 0x52)) }
}
}

View File

@ -123,7 +123,7 @@ class MainActivity : AppCompatActivity() {
)
val imageView = imageView {
id = View.generateViewId()
animator = this.loadApng(imageUrl).apply {
animator = this.loadApng(R.raw.bugbuckbunny).apply {
onLoaded {
setOnAnimationLoopListener {
// Log.e("app-test", "onLoop")

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB