Add create method - Work in progress
This commit is contained in:
parent
ffec8cc269
commit
681fc1ea8d
Binary file not shown.
|
@ -105,6 +105,7 @@ class APNGDisassembler(val byteArray: ByteArray) {
|
|||
// Check if is IDAT
|
||||
else if (byteArray[i] == 0x49.toByte() && byteArray[i + 1] == 0x44.toByte() && byteArray[ i + 2 ] == 0x41.toByte() && byteArray[ i + 3 ] == 0x54.toByte()) {
|
||||
if (png == null) {
|
||||
png = ArrayList()
|
||||
if (cover == null) {
|
||||
cover = ArrayList()
|
||||
png!!.addAll(pngSignature.toList())
|
||||
|
|
|
@ -0,0 +1,352 @@
|
|||
package oupson.apng
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import oupson.apng.Utils.Companion.convertImage
|
||||
import oupson.apng.Utils.Companion.getBlend_op
|
||||
import oupson.apng.Utils.Companion.getDispose_op
|
||||
import oupson.apng.Utils.Companion.to2Bytes
|
||||
import oupson.apng.Utils.Companion.to4Bytes
|
||||
import oupson.apng.Utils.Companion.toByteArray
|
||||
import java.util.zip.CRC32
|
||||
|
||||
class Apng {
|
||||
|
||||
private var seq = 0
|
||||
|
||||
var maxWidth : Int? = null
|
||||
var maxHeight : Int? = null
|
||||
|
||||
var cover : Bitmap? = null
|
||||
set(value) {
|
||||
field = convertImage(value!!)
|
||||
}
|
||||
|
||||
var frames : ArrayList<Frame> = ArrayList()
|
||||
|
||||
init {
|
||||
|
||||
}
|
||||
|
||||
// region addFrames
|
||||
fun addFrames(bitmap: Bitmap) {
|
||||
frames.add(Frame(toByteArray(bitmap)))
|
||||
}
|
||||
|
||||
fun addFrames(bitmap: Bitmap, delay : Float) {
|
||||
frames.add(Frame(toByteArray(bitmap), delay))
|
||||
}
|
||||
|
||||
fun addFrames(bitmap: Bitmap, delay: Float, xOffset : Int, yOffset : Int, blend_op: Utils.Companion.blend_op, dispose_op: Utils.Companion.dispose_op) {
|
||||
frames.add(Frame(toByteArray(bitmap), delay, xOffset, yOffset, blend_op, dispose_op))
|
||||
}
|
||||
//endregion
|
||||
|
||||
fun generateAPNGByteArray() : ByteArray {
|
||||
val res = ArrayList<Byte>()
|
||||
// Add PNG signature
|
||||
res.addAll(ApngFactory.pngSignature.toList())
|
||||
|
||||
// Add Image Header
|
||||
res.addAll(generateIhdr().toList())
|
||||
|
||||
// Add Animation Controller
|
||||
res.addAll(generateACTL())
|
||||
|
||||
// Get max height and max width
|
||||
val maxHeight = frames.sortedByDescending { it.height }[0].height
|
||||
val maxWitdh = frames.sortedByDescending { it.width }[0].width
|
||||
|
||||
if (cover == null) {
|
||||
val framesByte = ArrayList<Byte>()
|
||||
// region fcTL
|
||||
// Create the fcTL
|
||||
val fcTL = ArrayList<Byte>()
|
||||
// Add the length of the chunk body
|
||||
framesByte.addAll(byteArrayOf(0x00, 0x00, 0x00, 0x1A).toList())
|
||||
|
||||
// Add fcTL
|
||||
fcTL.addAll(byteArrayOf(0x66, 0x63, 0x54, 0x4c).toList())
|
||||
|
||||
// Add the frame number
|
||||
fcTL.addAll(to4Bytes(seq).toList())
|
||||
seq++
|
||||
|
||||
// Add width and height
|
||||
fcTL.addAll(to4Bytes(frames[0].width).toList())
|
||||
fcTL.addAll(to4Bytes(frames[0].height).toList())
|
||||
|
||||
// Calculate offset
|
||||
if (frames[0].width < maxWitdh) {
|
||||
val xOffset = (maxWitdh / 2) - (frames[0].width / 2)
|
||||
fcTL.addAll(to4Bytes(xOffset).toList())
|
||||
} else {
|
||||
fcTL.addAll(to4Bytes(0).toList())
|
||||
}
|
||||
if (frames[0].height < maxHeight) {
|
||||
val xOffset = (maxHeight / 2) - (frames[0].height / 2)
|
||||
fcTL.addAll(to4Bytes(xOffset).toList())
|
||||
} else {
|
||||
fcTL.addAll(to4Bytes(0).toList())
|
||||
}
|
||||
|
||||
// Set frame delay
|
||||
fcTL.addAll(to2Bytes(frames[0].delay.toInt()).toList())
|
||||
fcTL.addAll(to2Bytes(1000).toList())
|
||||
|
||||
fcTL.add(getDispose_op(frames[0].dispose_op).toByte())
|
||||
fcTL.add(getBlend_op(frames[0].blend_op).toByte())
|
||||
|
||||
val crc = CRC32()
|
||||
crc.update(fcTL.toByteArray(), 0, fcTL.size)
|
||||
framesByte.addAll(fcTL)
|
||||
framesByte.addAll(to4Bytes(crc.value.toInt()).toList())
|
||||
// endregion
|
||||
|
||||
// region idat
|
||||
frames[0].idat.IDATBody.forEach {
|
||||
val fdat = ArrayList<Byte>()
|
||||
// Add the chunk body length
|
||||
framesByte.addAll(to4Bytes(it.size).toList())
|
||||
// Add IDAT
|
||||
fdat.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList())
|
||||
// Add chunk body
|
||||
fdat.addAll(it.toList())
|
||||
// Generate CRC
|
||||
val crc1 = CRC32()
|
||||
crc1.update(fdat.toByteArray(), 0, fdat.size)
|
||||
framesByte.addAll(fdat)
|
||||
framesByte.addAll(to4Bytes(crc1.value.toInt()).toList())
|
||||
}
|
||||
// endregion
|
||||
res.addAll(framesByte)
|
||||
} else {
|
||||
val framesByte = ArrayList<Byte>()
|
||||
// Add cover image : Not part of annimation
|
||||
// region idat
|
||||
val idat = IDAT()
|
||||
idat.parseIDAT(toByteArray(cover!!))
|
||||
idat.IDATBody.forEach {
|
||||
val idatByteArray = ArrayList<Byte>()
|
||||
framesByte.addAll(to4Bytes(it.size).toList())
|
||||
idatByteArray.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList())
|
||||
idatByteArray.addAll(it.toList())
|
||||
val crc1 = CRC32()
|
||||
crc1.update(idatByteArray.toByteArray(), 0, idatByteArray.size)
|
||||
framesByte.addAll(idatByteArray)
|
||||
framesByte.addAll(to4Bytes(crc1.value.toInt()).toList())
|
||||
}
|
||||
// endregion
|
||||
|
||||
val fcTL = ArrayList<Byte>()
|
||||
// Add the length of the chunk body
|
||||
framesByte.addAll(byteArrayOf(0x00, 0x00, 0x00, 0x1A).toList())
|
||||
|
||||
// Add fcTL
|
||||
fcTL.addAll(byteArrayOf(0x66, 0x63, 0x54, 0x4c).toList())
|
||||
|
||||
// Add the frame number
|
||||
fcTL.addAll(to4Bytes(seq).toList())
|
||||
seq++
|
||||
|
||||
// Add width and height
|
||||
fcTL.addAll(to4Bytes(frames[0].width).toList())
|
||||
fcTL.addAll(to4Bytes(frames[0].height).toList())
|
||||
|
||||
// Calculate offset
|
||||
if (frames[0].width < maxWitdh) {
|
||||
val xOffset = (maxWitdh / 2) - (frames[0].width / 2)
|
||||
fcTL.addAll(to4Bytes(xOffset).toList())
|
||||
} else {
|
||||
fcTL.addAll(to4Bytes(0).toList())
|
||||
}
|
||||
if (frames[0].height < maxHeight) {
|
||||
val xOffset = (maxHeight / 2) - (frames[0].height / 2)
|
||||
fcTL.addAll(to4Bytes(xOffset).toList())
|
||||
} else {
|
||||
fcTL.addAll(to4Bytes(0).toList())
|
||||
}
|
||||
|
||||
// Set frame delay
|
||||
fcTL.addAll(to2Bytes(frames[0].delay.toInt()).toList())
|
||||
fcTL.addAll(to2Bytes(1000).toList())
|
||||
|
||||
fcTL.add(getDispose_op(frames[0].dispose_op).toByte())
|
||||
fcTL.add(getBlend_op(frames[0].blend_op).toByte())
|
||||
|
||||
val crc = CRC32()
|
||||
crc.update(fcTL.toByteArray(), 0, fcTL.size)
|
||||
framesByte.addAll(fcTL)
|
||||
framesByte.addAll(to4Bytes(crc.value.toInt()).toList())
|
||||
// endregion
|
||||
|
||||
// region fdat
|
||||
frames[0].idat.IDATBody.forEach {
|
||||
val fdat = ArrayList<Byte>()
|
||||
// Add the chunk body length
|
||||
framesByte.addAll(to4Bytes(it.size + 4).toList())
|
||||
// Add fdat
|
||||
fdat.addAll(byteArrayOf(0x66, 0x64, 0x41, 0x54).toList())
|
||||
fdat.addAll(to4Bytes(seq).toList())
|
||||
seq++
|
||||
// Add chunk body
|
||||
fdat.addAll(it.toList())
|
||||
// Generate CRC
|
||||
val crc1 = CRC32()
|
||||
crc1.update(fdat.toByteArray(), 0, fdat.size)
|
||||
framesByte.addAll(fdat)
|
||||
framesByte.addAll(to4Bytes(crc1.value.toInt()).toList())
|
||||
}
|
||||
// endregion
|
||||
res.addAll(framesByte)
|
||||
}
|
||||
|
||||
for (i in 1 until frames.size) {
|
||||
// If it's the first frame
|
||||
val framesByte = ArrayList<Byte>()
|
||||
val fcTL = ArrayList<Byte>()
|
||||
// region fcTL
|
||||
framesByte.addAll(byteArrayOf(0x00, 0x00, 0x00, 0x1A).toList())
|
||||
|
||||
fcTL.addAll(byteArrayOf(0x66, 0x63, 0x54, 0x4c).toList())
|
||||
|
||||
// Frame number
|
||||
fcTL.addAll(to4Bytes(seq).toList())
|
||||
seq++
|
||||
// width and height
|
||||
fcTL.addAll(to4Bytes(frames[i].width).toList())
|
||||
fcTL.addAll(to4Bytes(frames[i].height).toList())
|
||||
|
||||
if (frames[i].width < maxWitdh) {
|
||||
val xOffset = (maxWitdh / 2) - (frames[i].width / 2)
|
||||
fcTL.addAll(to4Bytes(xOffset).toList())
|
||||
} else {
|
||||
fcTL.addAll(to4Bytes(0).toList())
|
||||
}
|
||||
if (frames[i].height < maxHeight) {
|
||||
val xOffset = (maxHeight / 2) - (frames[i].height / 2)
|
||||
fcTL.addAll(to4Bytes(xOffset).toList())
|
||||
} else {
|
||||
fcTL.addAll(to4Bytes(0).toList())
|
||||
}
|
||||
|
||||
// Set frame delay
|
||||
fcTL.addAll(to2Bytes(frames[i].delay.toInt()).toList())
|
||||
fcTL.addAll(to2Bytes(1000).toList())
|
||||
|
||||
fcTL.add(getDispose_op(frames[i].dispose_op).toByte())
|
||||
fcTL.add(getBlend_op(frames[i].blend_op).toByte())
|
||||
|
||||
val crc = CRC32()
|
||||
crc.update(fcTL.toByteArray(), 0, fcTL.size)
|
||||
framesByte.addAll(fcTL)
|
||||
framesByte.addAll(to4Bytes(crc.value.toInt()).toList())
|
||||
// endregion
|
||||
|
||||
// region fdAT
|
||||
// Write fdAT
|
||||
frames[i].idat.IDATBody.forEach {
|
||||
val fdat = ArrayList<Byte>()
|
||||
// Add IDAT size of frame + 4 byte of the seq
|
||||
framesByte.addAll(to4Bytes(it.size + 4).toList())
|
||||
// Add fdAT
|
||||
fdat.addAll(byteArrayOf(0x66, 0x64, 0x41, 0x54).toList())
|
||||
// Add Sequence number
|
||||
// ! THIS IS NOT FRAME NUMBER
|
||||
fdat.addAll(to4Bytes(seq).toList())
|
||||
// Increase seq
|
||||
seq++
|
||||
fdat.addAll(it.toList())
|
||||
// Generate CRC
|
||||
val crc1 = CRC32()
|
||||
crc1.update(fdat.toByteArray(), 0, fdat.size)
|
||||
framesByte.addAll(fdat)
|
||||
framesByte.addAll(to4Bytes(crc1.value.toInt()).toList())
|
||||
}
|
||||
// endregion
|
||||
res.addAll(framesByte)
|
||||
}
|
||||
|
||||
|
||||
if (frames.isNotEmpty()) {
|
||||
|
||||
// Add IEND body length : 0
|
||||
res.addAll(to4Bytes(0).toList())
|
||||
// Add IEND
|
||||
val iend = byteArrayOf(0x49, 0x45, 0x4E, 0x44)
|
||||
// Generate crc for IEND
|
||||
val crC32 = CRC32()
|
||||
crC32.update(iend, 0, iend.size)
|
||||
res.addAll(iend.toList())
|
||||
res.addAll(to4Bytes(crC32.value.toInt()).toList())
|
||||
return res.toByteArray()
|
||||
} else {
|
||||
throw NoFrameException()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun generateCover(bitmap: Bitmap, maxWidth : Int, maxHeight : Int) : Bitmap {
|
||||
return Bitmap.createScaledBitmap(bitmap, maxWidth, maxHeight, false)
|
||||
}
|
||||
|
||||
private fun generateIhdr(): ByteArray {
|
||||
val ihdr = ArrayList<Byte>()
|
||||
|
||||
// We need a body var to know body length and generate crc
|
||||
val ihdr_body = ArrayList<Byte>()
|
||||
|
||||
// Get max height and max width of all the frames
|
||||
maxHeight = frames.sortedByDescending { it.height }[0].height
|
||||
maxWidth = frames.sortedByDescending { it.width }[0].width
|
||||
|
||||
if (((maxWidth != frames[0].width) && (maxHeight != frames[0].height)) && cover == null) {
|
||||
cover = generateCover(BitmapFactory.decodeByteArray(frames[0].byteArray, 0, frames[0].byteArray.size), maxWidth!!, maxHeight!!)
|
||||
}
|
||||
|
||||
// Add chunk body length
|
||||
ihdr.addAll(to4Bytes(frames[0].ihdr.ihdrCorps.size).toList())
|
||||
// Add IHDR
|
||||
ihdr_body.addAll(byteArrayOf(0x49.toByte(), 0x48.toByte(), 0x44.toByte(), 0x52.toByte()).toList())
|
||||
|
||||
// Add the max width and height
|
||||
ihdr_body.addAll(to4Bytes(maxWidth!!).toList())
|
||||
ihdr_body.addAll(to4Bytes(maxHeight!!).toList())
|
||||
|
||||
// Add complicated stuff like depth color ...
|
||||
// If you want correct png you need same parameters. Good solution is to create new png.
|
||||
ihdr_body.addAll(frames[0].ihdr.ihdrCorps.copyOfRange(8, 13).toList())
|
||||
|
||||
// Generate CRC
|
||||
val crC32 = CRC32()
|
||||
crC32.update(ihdr_body.toByteArray(), 0, ihdr_body.size)
|
||||
ihdr.addAll(ihdr_body)
|
||||
ihdr.addAll(to4Bytes(crC32.value.toInt()).toList())
|
||||
return ihdr.toByteArray()
|
||||
}
|
||||
|
||||
// Animation Control chunk
|
||||
private fun generateACTL(): ArrayList<Byte> {
|
||||
val res = ArrayList<Byte>()
|
||||
val actl = ArrayList<Byte>()
|
||||
|
||||
// Add length bytes
|
||||
res.addAll(to4Bytes(8).toList())
|
||||
|
||||
// Add acTL
|
||||
actl.addAll(byteArrayOf(0x61, 0x63, 0x54, 0x4c).toList())
|
||||
|
||||
// Add number of frames
|
||||
actl.addAll(to4Bytes(frames.size).toList())
|
||||
|
||||
// Number of repeat, 0 to infinite
|
||||
actl.addAll(to4Bytes(0).toList())
|
||||
res.addAll(actl)
|
||||
|
||||
// generate crc
|
||||
val crc = CRC32()
|
||||
crc.update(actl.toByteArray(), 0, actl.size)
|
||||
res.addAll(to4Bytes(crc.value.toInt()).toList())
|
||||
return res
|
||||
}
|
||||
}
|
|
@ -14,7 +14,9 @@ import java.net.URL
|
|||
|
||||
|
||||
class ApngAnimator {
|
||||
var play = true
|
||||
var isPlaying = true
|
||||
get() = field
|
||||
private set(value) {field = value}
|
||||
|
||||
var Frames = ArrayList<Frame>()
|
||||
|
||||
|
@ -282,18 +284,18 @@ class ApngAnimator {
|
|||
}
|
||||
|
||||
fun pause() {
|
||||
play = false
|
||||
isPlaying = false
|
||||
|
||||
}
|
||||
fun play() {
|
||||
if (!play) {
|
||||
play = true
|
||||
if (!isPlaying) {
|
||||
isPlaying = true
|
||||
ifmustPlay()
|
||||
}
|
||||
}
|
||||
|
||||
private fun ifmustPlay() {
|
||||
if (play) {
|
||||
if (isPlaying) {
|
||||
nextFrame()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,6 +87,34 @@ class Frame {
|
|||
}
|
||||
}
|
||||
|
||||
constructor(byteArray: ByteArray, delay : Float, xOffsets : Int, yOffsets : Int, blend_op: Utils.Companion.blend_op, dispose_op: Utils.Companion.dispose_op) {
|
||||
if (isPng(byteArray)) {
|
||||
this.byteArray = byteArray
|
||||
// Get width and height for image
|
||||
ihdr = IHDR()
|
||||
ihdr.parseIHDR(byteArray)
|
||||
|
||||
width = ihdr.pngWidth
|
||||
height = ihdr.pngHeight
|
||||
|
||||
// Get image bytes
|
||||
idat = IDAT()
|
||||
idat.parseIDAT(byteArray)
|
||||
|
||||
this.delay = delay
|
||||
|
||||
x_offsets = xOffsets
|
||||
y_offsets = yOffsets
|
||||
|
||||
this.maxWidth = -1
|
||||
this.maxHeight = -1
|
||||
this.blend_op = blend_op
|
||||
this.dispose_op = dispose_op
|
||||
} else {
|
||||
throw NotPngException()
|
||||
}
|
||||
}
|
||||
|
||||
constructor(byteArray: ByteArray, delay : Float, xOffsets : Int, yOffsets : Int, maxWidth : Int, maxHeight : Int, blend_op: Utils.Companion.blend_op, dispose_op: Utils.Companion.dispose_op) {
|
||||
if (isPng(byteArray)) {
|
||||
this.byteArray = byteArray
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package oupson.apng
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
class Utils {
|
||||
companion object {
|
||||
enum class dispose_op {
|
||||
|
@ -44,5 +48,42 @@ class Utils {
|
|||
else -> blend_op.APNG_BLEND_OP_SOURCE
|
||||
}
|
||||
}
|
||||
|
||||
fun toByteArray(bitmap: Bitmap) : ByteArray {
|
||||
val bos = ByteArrayOutputStream();
|
||||
convertImage(bitmap).compress(Bitmap.CompressFormat.PNG, 0 /*ignored for PNG*/, bos);
|
||||
return bos.toByteArray();
|
||||
}
|
||||
|
||||
fun convertImage(bitmap: Bitmap) : Bitmap{
|
||||
val btm = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
|
||||
val canvas = Canvas(btm)
|
||||
canvas.drawBitmap(bitmap, 0f, 0f, null)
|
||||
return btm
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a 4 bytes array from an Int
|
||||
* @param i The int
|
||||
*/
|
||||
fun to4Bytes(i: Int): ByteArray {
|
||||
val result = ByteArray(4)
|
||||
result[0] = (i shr 24).toByte()
|
||||
result[1] = (i shr 16).toByte()
|
||||
result[2] = (i shr 8).toByte()
|
||||
result[3] = i /*>> 0*/.toByte()
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a 2 bytes array from an Int
|
||||
* @param i The int
|
||||
*/
|
||||
fun to2Bytes(i: Int): ByteArray {
|
||||
val result = ByteArray(2)
|
||||
result[0] = (i shr 8).toByte()
|
||||
result[1] = i /*>> 0*/.toByte()
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,3 +43,8 @@ dependencies {
|
|||
implementation "org.jetbrains.anko:anko:$anko_version"
|
||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||
}
|
||||
kotlin {
|
||||
experimental {
|
||||
coroutines "enable"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.provider.MediaStore
|
|||
import android.support.v7.app.AppCompatActivity
|
||||
import android.util.Log
|
||||
import kotlinx.android.synthetic.main.activity_main2.*
|
||||
import org.jetbrains.anko.sdk27.coroutines.onClick
|
||||
import oupson.apng.ApngAnimator
|
||||
|
||||
class Main2Activity : AppCompatActivity() {
|
||||
|
@ -26,6 +27,14 @@ class Main2Activity : AppCompatActivity() {
|
|||
}
|
||||
Log.e("TAG", intent.data.toString())
|
||||
animator.load(getImageRealPath(contentResolver, uri, null))
|
||||
|
||||
imageView3.onClick {
|
||||
if (animator.isPlaying) {
|
||||
animator.pause()
|
||||
} else {
|
||||
animator.play()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getImageRealPath(contentResolver: ContentResolver, uri: Uri, whereClause: String?): String {
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package oupson.apngcreator
|
||||
|
||||
import android.graphics.BitmapFactory
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.AnimationDrawable
|
||||
import android.os.Environment
|
||||
import android.os.Environment.DIRECTORY_PICTURES
|
||||
import android.system.Os
|
||||
import oupson.apng.APNGDisassembler
|
||||
import java.io.File
|
||||
|
@ -17,7 +19,10 @@ import android.system.Os.mkdir
|
|||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import com.squareup.picasso.Picasso
|
||||
import oupson.apng.Apng
|
||||
import oupson.apng.Utils
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.lang.reflect.Field
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.HttpURLConnection.HTTP_OK
|
||||
import java.net.URL
|
||||
|
@ -31,11 +36,21 @@ class MainActivity : AppCompatActivity() {
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
val apng = Apng()
|
||||
val file1 = File(Environment.getExternalStoragePublicDirectory(DIRECTORY_PICTURES), "hopital.jpg")
|
||||
val file2 = File(Environment.getExternalStoragePublicDirectory(DIRECTORY_PICTURES), "test.jpg")
|
||||
|
||||
apng.addFrames(BitmapFactory.decodeByteArray(file1.readBytes(), 0, file1.readBytes().size))
|
||||
apng.addFrames(BitmapFactory.decodeByteArray(file2.readBytes(), 0, file2.readBytes().size), 2000f, 0, 0, Utils.Companion.blend_op.APNG_BLEND_OP_OVER, Utils.Companion.dispose_op.APNG_DISPOSE_OP_NONE)
|
||||
|
||||
animator = ApngAnimator(imageView)
|
||||
animator.load(imageUrl)
|
||||
animator.load(apng.generateAPNGByteArray())
|
||||
|
||||
Picasso.get().load(imageUrl).into(imageView2);
|
||||
|
||||
val out = File(Environment.getExternalStoragePublicDirectory(DIRECTORY_PICTURES), "out.png")
|
||||
out.createNewFile()
|
||||
out.writeBytes(apng.generateAPNGByteArray())
|
||||
|
||||
play.setOnClickListener {
|
||||
animator.play()
|
||||
|
|
|
@ -8,7 +8,7 @@ buildscript {
|
|||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.2.0'
|
||||
classpath 'com.android.tools.build:gradle:3.2.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
|
Loading…
Reference in New Issue