diff --git a/apng_library/src/main/java/oupson/apng/APNGDisassembler.kt b/apng_library/src/main/java/oupson/apng/APNGDisassembler.kt index 8d54a80..770957f 100644 --- a/apng_library/src/main/java/oupson/apng/APNGDisassembler.kt +++ b/apng_library/src/main/java/oupson/apng/APNGDisassembler.kt @@ -247,7 +247,7 @@ class APNGDisassembler(val byteArray: ByteArray) { pngList.forEach { val btm = Bitmap.createBitmap(it.maxWidth, it.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) + canvas.drawBitmap(BitmapFactory.decodeByteArray(it.byteArray, 0, it.byteArray.size), it.x_offsets!!.toFloat(), it.y_offsets!!.toFloat(), null) generatedFrame.add(btm) } return generatedFrame diff --git a/apng_library/src/main/java/oupson/apng/Apng.kt b/apng_library/src/main/java/oupson/apng/Apng.kt index e1ae150..89687e9 100644 --- a/apng_library/src/main/java/oupson/apng/Apng.kt +++ b/apng_library/src/main/java/oupson/apng/Apng.kt @@ -2,6 +2,7 @@ package oupson.apng import android.graphics.Bitmap import android.graphics.BitmapFactory +import android.util.Log import oupson.apng.Utils.Companion.convertImage import oupson.apng.Utils.Companion.getBlend_op import oupson.apng.Utils.Companion.getDispose_op @@ -37,12 +38,17 @@ class Apng { frames.add(Frame(toByteArray(bitmap), delay)) } + fun addFrames(bitmap: Bitmap, delay: Float, blend_op: Utils.Companion.blend_op, dispose_op: Utils.Companion.dispose_op) { + frames.add(Frame(toByteArray(bitmap), delay, blend_op, dispose_op)) + } + 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 { + seq = 0 val res = ArrayList() // Add PNG signature res.addAll(ApngFactory.pngSignature.toList()) @@ -77,17 +83,23 @@ class Apng { 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()) + + if (frames[0].x_offsets == null) { + 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()) + } } 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()) + fcTL.addAll(to4Bytes(frames[0].x_offsets!!).toList()) + fcTL.addAll(to4Bytes(frames[0].y_offsets!!).toList()) } // Set frame delay @@ -154,17 +166,22 @@ class Apng { 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()) + if (frames[0].x_offsets == null) { + 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()) + } } 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()) + fcTL.addAll(to4Bytes(frames[0].x_offsets!!).toList()) + fcTL.addAll(to4Bytes(frames[0].y_offsets!!).toList()) } // Set frame delay @@ -202,6 +219,7 @@ class Apng { } for (i in 1 until frames.size) { + Log.e("Seq", seq.toString()) // If it's the first frame val framesByte = ArrayList() val fcTL = ArrayList() @@ -217,17 +235,22 @@ class Apng { 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()) + if (frames[i].x_offsets == null) { + 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()) + } } 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()) + fcTL.addAll(to4Bytes(frames[i].x_offsets!!).toList()) + fcTL.addAll(to4Bytes(frames[i].y_offsets!!).toList()) } // Set frame delay diff --git a/apng_library/src/main/java/oupson/apng/ApngAnimator.kt b/apng_library/src/main/java/oupson/apng/ApngAnimator.kt index 9042652..a4e318c 100644 --- a/apng_library/src/main/java/oupson/apng/ApngAnimator.kt +++ b/apng_library/src/main/java/oupson/apng/ApngAnimator.kt @@ -78,11 +78,11 @@ class ApngAnimator { // Clear current frame rect // If `blend_op` is APNG_BLEND_OP_SOURCE all color components of the frame, including alpha, overwrite the current contents of the frame's output buffer region. if (it.blend_op == Utils.Companion.blend_op.APNG_BLEND_OP_SOURCE) { - canvas.drawRect(it.x_offsets.toFloat(), it.y_offsets.toFloat(), it.x_offsets + current.width.toFloat(), it.y_offsets + current.height.toFloat(), { val paint = Paint(); paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR); paint }()) + canvas.drawRect(it.x_offsets!!.toFloat(), it.y_offsets!!.toFloat(), it.x_offsets!! + current.width.toFloat(), it.y_offsets!! + current.height.toFloat(), { val paint = Paint(); paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR); paint }()) } // Draw the bitmap - canvas.drawBitmap(current, it.x_offsets.toFloat(), it.y_offsets.toFloat(), null) + canvas.drawBitmap(current, it.x_offsets!!.toFloat(), it.y_offsets!!.toFloat(), null) generatedFrame.add(btm) // Don't add current frame to bitmap buffer @@ -95,7 +95,7 @@ class ApngAnimator { val res = Bitmap.createBitmap(Frames[0].maxWidth, Frames[0].maxHeight, Bitmap.Config.ARGB_8888) val can = Canvas(res) can.drawBitmap(btm, 0f, 0f, null) - can.drawRect(lastFrame!!.x_offsets.toFloat(), lastFrame!!.y_offsets.toFloat(), lastFrame!!.x_offsets + lastFrame!!.width.toFloat(), lastFrame!!.y_offsets + lastFrame!!.height.toFloat(), { val paint = Paint(); paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR); paint }()) + can.drawRect(lastFrame!!.x_offsets!!.toFloat(), lastFrame!!.y_offsets!!.toFloat(), lastFrame!!.x_offsets!! + lastFrame!!.width.toFloat(), lastFrame!!.y_offsets!! + lastFrame!!.height.toFloat(), { val paint = Paint(); paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR); paint }()) bitmapBuffer = res } else { @@ -148,11 +148,11 @@ class ApngAnimator { // Clear current frame rect // If `blend_op` is APNG_BLEND_OP_SOURCE all color components of the frame, including alpha, overwrite the current contents of the frame's output buffer region. if (it.blend_op == Utils.Companion.blend_op.APNG_BLEND_OP_SOURCE) { - canvas.drawRect(it.x_offsets.toFloat(), it.y_offsets.toFloat(), it.x_offsets + current.width.toFloat(), it.y_offsets + current.height.toFloat(), { val paint = Paint(); paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR); paint }()) + canvas.drawRect(it.x_offsets!!.toFloat(), it.y_offsets!!.toFloat(), it.x_offsets!! + current.width.toFloat(), it.y_offsets!! + current.height.toFloat(), { val paint = Paint(); paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR); paint }()) } // Draw the bitmap - canvas.drawBitmap(current, it.x_offsets.toFloat(), it.y_offsets.toFloat(), null) + canvas.drawBitmap(current, it.x_offsets!!.toFloat(), it.y_offsets!!.toFloat(), null) generatedFrame.add(btm) // Don't add current frame to bitmap buffer @@ -165,7 +165,7 @@ class ApngAnimator { val res = Bitmap.createBitmap(Frames[0].maxWidth, Frames[0].maxHeight, Bitmap.Config.ARGB_8888) val can = Canvas(res) can.drawBitmap(btm, 0f, 0f, null) - can.drawRect(lastFrame!!.x_offsets.toFloat(), lastFrame!!.y_offsets.toFloat(), lastFrame!!.x_offsets + lastFrame!!.width.toFloat(), lastFrame!!.y_offsets + lastFrame!!.height.toFloat(), { val paint = Paint(); paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR); paint }()) + can.drawRect(lastFrame!!.x_offsets!!.toFloat(), lastFrame!!.y_offsets!!.toFloat(), lastFrame!!.x_offsets!! + lastFrame!!.width.toFloat(), lastFrame!!.y_offsets!! + lastFrame!!.height.toFloat(), { val paint = Paint(); paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR); paint }()) bitmapBuffer = res } else { @@ -220,11 +220,11 @@ class ApngAnimator { // Clear current frame rect // If `blend_op` is APNG_BLEND_OP_SOURCE all color components of the frame, including alpha, overwrite the current contents of the frame's output buffer region. if (it.blend_op == Utils.Companion.blend_op.APNG_BLEND_OP_SOURCE) { - canvas.drawRect(it.x_offsets.toFloat(), it.y_offsets.toFloat(), it.x_offsets + current.width.toFloat(), it.y_offsets + current.height.toFloat(), { val paint = Paint(); paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR); paint }()) + canvas.drawRect(it.x_offsets!!.toFloat(), it.y_offsets!!.toFloat(), it.x_offsets!! + current.width.toFloat(), it.y_offsets!! + current.height.toFloat(), { val paint = Paint(); paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR); paint }()) } // Draw the bitmap - canvas.drawBitmap(current, it.x_offsets.toFloat(), it.y_offsets.toFloat(), null) + canvas.drawBitmap(current, it.x_offsets!!.toFloat(), it.y_offsets!!.toFloat(), null) generatedFrame.add(btm) // Don't add current frame to bitmap buffer @@ -237,7 +237,7 @@ class ApngAnimator { val res = Bitmap.createBitmap(Frames[0].maxWidth, Frames[0].maxHeight, Bitmap.Config.ARGB_8888) val can = Canvas(res) can.drawBitmap(btm, 0f, 0f, null) - can.drawRect(lastFrame!!.x_offsets.toFloat(), lastFrame!!.y_offsets.toFloat(), lastFrame!!.x_offsets + lastFrame!!.width.toFloat(), lastFrame!!.y_offsets + lastFrame!!.height.toFloat(), { val paint = Paint(); paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR); paint }()) + can.drawRect(lastFrame!!.x_offsets!!.toFloat(), lastFrame!!.y_offsets!!.toFloat(), lastFrame!!.x_offsets!! + lastFrame!!.width.toFloat(), lastFrame!!.y_offsets!! + lastFrame!!.height.toFloat(), { val paint = Paint(); paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR); paint }()) bitmapBuffer = res } else { diff --git a/apng_library/src/main/java/oupson/apng/Frame.kt b/apng_library/src/main/java/oupson/apng/Frame.kt index 492764d..b7419f6 100644 --- a/apng_library/src/main/java/oupson/apng/Frame.kt +++ b/apng_library/src/main/java/oupson/apng/Frame.kt @@ -23,8 +23,8 @@ class Frame { val delay : Float - val x_offsets : Int - val y_offsets : Int + var x_offsets : Int? = null + var y_offsets : Int? = null val maxWidth : Int val maxHeight : Int @@ -48,9 +48,6 @@ class Frame { delay = 1000f - x_offsets = 0 - y_offsets = 0 - maxHeight = -1 maxWidth = -1 blend_op = Utils.Companion.blend_op.APNG_BLEND_OP_SOURCE @@ -75,9 +72,6 @@ class Frame { this.delay = delay - x_offsets = 0 - y_offsets = 0 - maxHeight = -1 maxWidth = -1 blend_op = Utils.Companion.blend_op.APNG_BLEND_OP_SOURCE @@ -87,6 +81,32 @@ class Frame { } } + constructor(byteArray: ByteArray, delay : Float, 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 + + + 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, blend_op: Utils.Companion.blend_op, dispose_op: Utils.Companion.dispose_op) { if (isPng(byteArray)) { this.byteArray = byteArray diff --git a/app-test/src/main/java/oupson/apngcreator/MainActivity.kt b/app-test/src/main/java/oupson/apngcreator/MainActivity.kt index dcf01e0..a558c19 100644 --- a/app-test/src/main/java/oupson/apngcreator/MainActivity.kt +++ b/app-test/src/main/java/oupson/apngcreator/MainActivity.kt @@ -40,9 +40,9 @@ class MainActivity : AppCompatActivity() { 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(file2.readBytes(), 0, file2.readBytes().size), 2000f, Utils.Companion.blend_op.APNG_BLEND_OP_OVER, Utils.Companion.dispose_op.APNG_DISPOSE_OP_NONE) - 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) + apng.addFrames(BitmapFactory.decodeByteArray(file1.readBytes(), 0, file1.readBytes().size), 1000f, Utils.Companion.blend_op.APNG_BLEND_OP_OVER, Utils.Companion.dispose_op.APNG_DISPOSE_OP_NONE) animator = ApngAnimator(imageView) animator.load(apng.generateAPNGByteArray())