From 46d26d66ee387a34a7c9c6e17023d9a3c41743f4 Mon Sep 17 00:00:00 2001 From: oupson Date: Sat, 29 Sep 2018 21:10:58 +0200 Subject: [PATCH] Initial commit --- .idea/caches/build_file_checksums.ser | Bin 596 -> 596 bytes apng_library/build.gradle | 1 + .../main/java/oupson/apng/APNGDisassembler.kt | 54 +++++++---------- .../src/main/java/oupson/apng/ApngAnimator.kt | 56 +++++++++++++++++- .../oupson/apng/{APNG.kt => ApngFactory.kt} | 16 ++--- .../src/main/java/oupson/apng/Frame.kt | 2 +- .../src/main/java/oupson/apng/Loader.kt | 38 ++++++++++++ .../src/main/java/oupson/apng/Utils.kt | 48 +++++++++++++++ .../oupson/apng/customView/ApngImageView.kt | 46 -------------- .../src/main/java/oupson/apng/fcTL.kt | 2 - app-test/build.gradle | 4 +- app-test/src/main/AndroidManifest.xml | 1 + .../java/oupson/apngcreator/MainActivity.kt | 18 +++++- .../src/main/res/layout/activity_main.xml | 15 ++++- 14 files changed, 204 insertions(+), 97 deletions(-) rename apng_library/src/main/java/oupson/apng/{APNG.kt => ApngFactory.kt} (95%) create mode 100644 apng_library/src/main/java/oupson/apng/Loader.kt create mode 100644 apng_library/src/main/java/oupson/apng/Utils.kt delete mode 100644 apng_library/src/main/java/oupson/apng/customView/ApngImageView.kt diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 56ffea8808b84391f742ac7bc1bf3d35f8e03b56..2b4ce546490e5b9293675b45990e56c7d8a48f26 100644 GIT binary patch delta 56 zcmV-80LTB-1k?nOm;@%gtCNwO)eyyuDc-pFspxKVa)iz~ENPR00hADp() + val pngList = ArrayList() var png : ArrayList? = null - var delayList = ArrayList() + var delay = -1f - var yOffset= ArrayList() + var yOffset= -1 - var xOffset = ArrayList() + var xOffset = -1 var maxWidth = 0 var maxHeight = 0 init { - if (isApng(byteArray)) { + if (ApngFactory.isApng(byteArray)) { val ihdr = IHDR() ihdr.parseIHDR(byteArray) maxWidth = ihdr.pngWidth @@ -32,14 +33,13 @@ class APNGDisassembler(val byteArray: ByteArray) { png = ArrayList() val fcTL = fcTL(byteArray.copyOfRange(i-4, i + 28)) - delayList.add(fcTL.delay) - - yOffset.add(fcTL.y_offset) - xOffset.add(fcTL.x_offset) + delay = fcTL.delay + yOffset = fcTL.y_offset + xOffset = fcTL.x_offset Log.e("APNG", "delay : + ${fcTL.delay}") val width = fcTL.pngWidth val height = fcTL.pngHeight - png!!.addAll(APNG.pngSignature.toList()) + png!!.addAll(pngSignature.toList()) png!!.addAll(generate_ihdr(ihdr, width, height).toList()) } else { // Add IEND body length : 0 @@ -51,20 +51,19 @@ class APNGDisassembler(val byteArray: ByteArray) { crC32.update(iend, 0, iend.size) png!!.addAll(iend.toList()) png!!.addAll(to4Bytes(crC32.value.toInt()).toList()) - - pngList.add(png!!.toByteArray()) + pngList.add(Frame(png!!.toByteArray(), delay, xOffset, yOffset, maxWidth, maxHeight)) png = ArrayList() val fcTL = fcTL(byteArray.copyOfRange(i-4, i + 28)) - delayList.add(fcTL.delay) + delay = fcTL.delay - yOffset.add(fcTL.y_offset) - xOffset.add(fcTL.x_offset) + yOffset = fcTL.y_offset + xOffset = fcTL.x_offset val width = fcTL.pngWidth val height = fcTL.pngHeight - png!!.addAll(APNG.pngSignature.toList()) + png!!.addAll(pngSignature.toList()) png!!.addAll(generate_ihdr(ihdr, width, height).toList()) } } else if (byteArray[i] == 0x49.toByte() && byteArray[i + 1] == 0x44.toByte() && byteArray[ i + 2 ] == 0x41.toByte() && byteArray[ i + 3 ] == 0x54.toByte()) { @@ -75,7 +74,7 @@ class APNGDisassembler(val byteArray: ByteArray) { } - var bodySize = lengthString.toLong(16).toInt() + val bodySize = lengthString.toLong(16).toInt() png!!.addAll(byteArray.copyOfRange(i-4, i).toList()) val body = ArrayList() body.addAll(byteArrayOf(0x49, 0x44, 0x41, 0x54).toList()) @@ -114,20 +113,13 @@ class APNGDisassembler(val byteArray: ByteArray) { } } } else { + var p = "" + p += String(byteArray.copyOfRange(0, 50)) + Log.e("TAG", "Not a apng : $p") throw NotApngException() } } - fun getbitmapList() : ArrayList - { - var res = ArrayList() - Log.e("APNG", "pngList : ${pngList.size}, delayList : ${delayList.size}") - for (i in 0 until pngList.size) { - res.add(Frame(pngList[i], delayList[i], xOffset[i], yOffset[i], maxWidth, maxHeight)) - } - return res - } - private fun generate_ihdr(ihdrOfApng: IHDR, width : Int, height : Int) : ByteArray { val ihdr = ArrayList() @@ -179,6 +171,4 @@ class APNGDisassembler(val byteArray: ByteArray) { -} -class extractedFrame(val bitmap: Bitmap, val delay : Float, val xoffset : Int, val yoffset : Int, val maxWidth : Int, val maxHeight : Int) { } \ No newline at end of file diff --git a/apng_library/src/main/java/oupson/apng/ApngAnimator.kt b/apng_library/src/main/java/oupson/apng/ApngAnimator.kt index ab26808..b8837c1 100644 --- a/apng_library/src/main/java/oupson/apng/ApngAnimator.kt +++ b/apng_library/src/main/java/oupson/apng/ApngAnimator.kt @@ -6,7 +6,10 @@ import android.graphics.Canvas import android.os.Environment import android.os.Handler import android.widget.ImageView +import org.jetbrains.anko.doAsync +import org.jetbrains.anko.uiThread import java.io.File +import java.net.URL class ApngAnimator(val imageView : ImageView) { var play = true @@ -23,7 +26,52 @@ class ApngAnimator(val imageView : ImageView) { } fun load(file: File) { - val extractedFrame = APNGDisassembler(file.readBytes()).getbitmapList() + val extractedFrame = APNGDisassembler(file.readBytes()).pngList + Frames = extractedFrame + + Frames.forEach { + val btm = Bitmap.createBitmap(Frames[0].maxWidth, Frames[0].maxHeight, Bitmap.Config.ARGB_8888) + val canvas = Canvas(btm) + canvas.drawBitmap(BitmapFactory.decodeByteArray(it.byteArray, 0, it.byteArray.size), it.x_offsets.toFloat(), it.y_offsets.toFloat(), null) + generatedFrame.add(btm) + } + + nextFrame() + } + + fun load(string: String) { + if (string.contains("http") || string.contains("https")) { + val url = URL(string) + doAsync { + val extractedFrame = APNGDisassembler(Loader().load(url)).pngList + Frames = extractedFrame + + Frames.forEach { + val btm = Bitmap.createBitmap(Frames[0].maxWidth, Frames[0].maxHeight, Bitmap.Config.ARGB_8888) + val canvas = Canvas(btm) + canvas.drawBitmap(BitmapFactory.decodeByteArray(it.byteArray, 0, it.byteArray.size), it.x_offsets.toFloat(), it.y_offsets.toFloat(), null) + generatedFrame.add(btm) + } + uiThread { + nextFrame() + } + } + } else if (File(string).exists()) { + val extractedFrame = APNGDisassembler(Loader().load(File(string))).pngList + Frames = extractedFrame + + Frames.forEach { + val btm = Bitmap.createBitmap(Frames[0].maxWidth, Frames[0].maxHeight, Bitmap.Config.ARGB_8888) + val canvas = Canvas(btm) + canvas.drawBitmap(BitmapFactory.decodeByteArray(it.byteArray, 0, it.byteArray.size), it.x_offsets.toFloat(), it.y_offsets.toFloat(), null) + generatedFrame.add(btm) + } + nextFrame() + } + } + + fun load(byteArray: ByteArray) { + val extractedFrame = APNGDisassembler(byteArray).pngList Frames = extractedFrame Frames.forEach { @@ -53,8 +101,10 @@ class ApngAnimator(val imageView : ImageView) { } fun play() { - play = true - mustPlay() + if (!play) { + play = true + mustPlay() + } } private fun mustPlay() { diff --git a/apng_library/src/main/java/oupson/apng/APNG.kt b/apng_library/src/main/java/oupson/apng/ApngFactory.kt similarity index 95% rename from apng_library/src/main/java/oupson/apng/APNG.kt rename to apng_library/src/main/java/oupson/apng/ApngFactory.kt index 70d4e91..5d40793 100644 --- a/apng_library/src/main/java/oupson/apng/APNG.kt +++ b/apng_library/src/main/java/oupson/apng/ApngFactory.kt @@ -15,7 +15,7 @@ import java.util.zip.CRC32 * @throws NoFrameException * */ -class APNG { +class ApngFactory { private var seq = 0 @@ -26,7 +26,7 @@ class APNG { * * @throws NoFrameException */ - fun create() : ByteArray { + fun create(): ByteArray { if (frames.isNotEmpty()) { val res = ArrayList() @@ -191,7 +191,7 @@ class APNG { } // Animation Control chunk - private fun generateACTL() : ArrayList { + private fun generateACTL(): ArrayList { val res = ArrayList() val actl = ArrayList() @@ -242,14 +242,14 @@ class APNG { companion object { // Return true if png - fun isPng(byteArray: ByteArray) : Boolean { + fun isPng(byteArray: ByteArray): Boolean { return byteArray.copyOfRange(0, 8).contentToString() == pngSignature.contentToString() } - fun isApng(byteArray: ByteArray) : Boolean { + fun isApng(byteArray: ByteArray): Boolean { for (i in 0 until byteArray.size) { // if byteArray contain acTL - if (byteArray[i] == 0x66.toByte() && byteArray[i + 1] == 0x63.toByte() && byteArray[ i + 2] == 0x54.toByte() && byteArray[ i + 3] == 0x4c.toByte()) { + if (byteArray[i] == 0x66.toByte() && byteArray[i + 1] == 0x63.toByte() && byteArray[i + 2] == 0x54.toByte() && byteArray[i + 3] == 0x4c.toByte()) { // It's an apng return true } @@ -259,7 +259,7 @@ class APNG { } // Signature for png - val pngSignature : ByteArray = byteArrayOf(0x89.toByte(), 0x50.toByte(), 0x4E.toByte(), 0x47.toByte(), 0x0D.toByte(), 0x0A.toByte(), 0x1A.toByte(), 0x0A.toByte()) + val pngSignature: ByteArray = byteArrayOf(0x89.toByte(), 0x50.toByte(), 0x4E.toByte(), 0x47.toByte(), 0x0D.toByte(), 0x0A.toByte(), 0x1A.toByte(), 0x0A.toByte()) } /** @@ -273,7 +273,7 @@ class APNG { } // Generate Image Header chunk - private fun generate_ihdr() : ByteArray { + private fun generate_ihdr(): ByteArray { val ihdr = ArrayList() // We need a body var to know body length and generate crc diff --git a/apng_library/src/main/java/oupson/apng/Frame.kt b/apng_library/src/main/java/oupson/apng/Frame.kt index 8730ceb..033a506 100644 --- a/apng_library/src/main/java/oupson/apng/Frame.kt +++ b/apng_library/src/main/java/oupson/apng/Frame.kt @@ -1,6 +1,6 @@ package oupson.apng -import oupson.apng.APNG.Companion.isPng +import oupson.apng.ApngFactory.Companion.isPng /** * A frame for an animated png diff --git a/apng_library/src/main/java/oupson/apng/Loader.kt b/apng_library/src/main/java/oupson/apng/Loader.kt new file mode 100644 index 0000000..038d86d --- /dev/null +++ b/apng_library/src/main/java/oupson/apng/Loader.kt @@ -0,0 +1,38 @@ +package oupson.apng + +import android.os.Bundle +import android.widget.ImageView +import java.io.* +import java.net.URI +import java.net.URL +import java.net.URLConnection + +class Loader { + fun load(url : URL) : ByteArray { + try { + val connection = url.openConnection() + connection.connect() + // this will be useful so that you can show a typical 0-100% progress bar + val fileLength = connection.contentLength + // download the file + val input = BufferedInputStream(connection.getInputStream()) + val output = ByteArrayOutputStream() + + val data = ByteArray(1024) + var count: Int = 0 + while ({count = input.read(data); count}() != -1) { + output.write(data, 0, count) + } + output.flush() + output.close() + input.close() + + return output.toByteArray() + } catch (e: IOException) { + throw e + } + } + fun load(file : File) : ByteArray { + return file.readBytes() + } +} \ No newline at end of file diff --git a/apng_library/src/main/java/oupson/apng/Utils.kt b/apng_library/src/main/java/oupson/apng/Utils.kt new file mode 100644 index 0000000..b279428 --- /dev/null +++ b/apng_library/src/main/java/oupson/apng/Utils.kt @@ -0,0 +1,48 @@ +package oupson.apng + +class Utils { + companion object { + enum class dispose_op { + APNG_DISPOSE_OP_NONE, + APNG_DISPOSE_OP_BACKGROUND, + APNG_DISPOSE_OP_PREVIOUS + } + + fun getDispose_op(dispose_op: dispose_op) : Int { + return when(dispose_op) { + Companion.dispose_op.APNG_DISPOSE_OP_NONE -> 0 + Companion.dispose_op.APNG_DISPOSE_OP_BACKGROUND -> 1 + Companion.dispose_op.APNG_DISPOSE_OP_PREVIOUS -> 2 + } + } + + fun getDispose_op(int: Int) : dispose_op { + return when(int) { + 0 -> Companion.dispose_op.APNG_DISPOSE_OP_NONE + 1 -> Companion.dispose_op.APNG_DISPOSE_OP_BACKGROUND + 2 -> Companion.dispose_op.APNG_DISPOSE_OP_PREVIOUS + else -> dispose_op.APNG_DISPOSE_OP_NONE + } + } + + enum class blend_op() { + APNG_BLEND_OP_SOURCE, + APNG_BLEND_OP_OVER + } + + fun getBlend_op(blend_op: blend_op) : Int { + return when(blend_op) { + Companion.blend_op.APNG_BLEND_OP_SOURCE -> 0 + Companion.blend_op.APNG_BLEND_OP_OVER -> 1 + } + } + + fun getBlend_op(int : Int) : blend_op{ + return when(int) { + 0 -> Companion.blend_op.APNG_BLEND_OP_SOURCE + 1 -> Companion.blend_op.APNG_BLEND_OP_OVER + else -> blend_op.APNG_BLEND_OP_SOURCE + } + } + } +} \ No newline at end of file diff --git a/apng_library/src/main/java/oupson/apng/customView/ApngImageView.kt b/apng_library/src/main/java/oupson/apng/customView/ApngImageView.kt deleted file mode 100644 index f0faccb..0000000 --- a/apng_library/src/main/java/oupson/apng/customView/ApngImageView.kt +++ /dev/null @@ -1,46 +0,0 @@ -package oupson.apng.customView - -import android.content.Context -import android.widget.ImageView -import android.graphics.Bitmap -import android.graphics.Canvas -import android.os.Handler -import android.util.AttributeSet -import oupson.apng.extractedFrame - - -class ApngImageView(context: Context, attrs: AttributeSet) : ImageView(context, attrs) { - var Frames = ArrayList() - var myHandler: Handler - var counter = 0 - - val generatedFrame = ArrayList() - - init { - myHandler = Handler() - } - fun load(frames : ArrayList) { - Frames = frames - - Frames.forEach { - val btm = Bitmap.createBitmap(Frames[0].maxWidth, Frames[0].maxHeight, Bitmap.Config.ARGB_8888) - val canvas = Canvas(btm) - canvas.drawBitmap(it.bitmap, it.xoffset.toFloat(), it.yoffset.toFloat(), null) - generatedFrame.add(btm) - } - - nextFrame() - } - - fun nextFrame() { - if (counter == Frames.size) { - counter = 0 - } - val delay = Frames[counter].delay - this.setImageBitmap(generatedFrame[counter]) - counter++ - myHandler.postDelayed({ - nextFrame() - }, delay.toLong()) - } -} \ No newline at end of file diff --git a/apng_library/src/main/java/oupson/apng/fcTL.kt b/apng_library/src/main/java/oupson/apng/fcTL.kt index b2166ad..d1eea0c 100644 --- a/apng_library/src/main/java/oupson/apng/fcTL.kt +++ b/apng_library/src/main/java/oupson/apng/fcTL.kt @@ -1,7 +1,5 @@ package oupson.apng -import android.util.Log - class fcTL(byteArray: ByteArray) { private var corpsSize = -1 diff --git a/app-test/build.gradle b/app-test/build.gradle index b6cfa01..6a629b5 100644 --- a/app-test/build.gradle +++ b/app-test/build.gradle @@ -30,7 +30,7 @@ android { productFlavors { } } - +ext.anko_version='0.10.6' dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'com.android.support:appcompat-v7:27.1.1' @@ -39,4 +39,6 @@ dependencies { androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' implementation project(':apng_library') + implementation "org.jetbrains.anko:anko:$anko_version" + implementation 'com.squareup.picasso:picasso:2.71828' } diff --git a/app-test/src/main/AndroidManifest.xml b/app-test/src/main/AndroidManifest.xml index 55661fa..9667448 100644 --- a/app-test/src/main/AndroidManifest.xml +++ b/app-test/src/main/AndroidManifest.xml @@ -2,6 +2,7 @@ + + +