diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index b431ad3..8d68dff 100644 Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ diff --git a/apng_library/build.gradle b/apng_library/build.gradle index 03c3bbc..8060cfd 100644 --- a/apng_library/build.gradle +++ b/apng_library/build.gradle @@ -32,7 +32,8 @@ dependencies { androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation "org.jetbrains.anko:anko:0.10.8" + implementation "org.jetbrains.anko:anko:$anko_version" + } repositories { mavenCentral() diff --git a/apng_library/src/main/java/oupson/apng/APNGDisassembler.kt b/apng_library/src/main/java/oupson/apng/APNGDisassembler.kt index 848bb2d..bfffd30 100644 --- a/apng_library/src/main/java/oupson/apng/APNGDisassembler.kt +++ b/apng_library/src/main/java/oupson/apng/APNGDisassembler.kt @@ -3,6 +3,7 @@ package oupson.apng import android.graphics.BitmapFactory import oupson.apng.chunks.IHDR import oupson.apng.chunks.fcTL +import oupson.apng.exceptions.BadApng import oupson.apng.exceptions.BadCRC import oupson.apng.exceptions.NotApngException import oupson.apng.utils.Utils @@ -103,6 +104,13 @@ class APNGDisassembler { dispose_op = fcTL.dispose_op val width = fcTL.pngWidth val height = fcTL.pngHeight + + if (xOffset + width > maxWidth) { + throw BadApng("`y_offset` + `height` must be <= `IHDR` height") + } else if (yOffset + height > maxHeight) { + throw BadApng("`y_offset` + `height` must be <= `IHDR` height") + } + png!!.addAll(pngSignature.toList()) png!!.addAll(generateIhdr(ihdr, width, height).toList()) plte?.let { diff --git a/apng_library/src/main/java/oupson/apng/Apng.kt b/apng_library/src/main/java/oupson/apng/Apng.kt index fbcb1af..179d86e 100644 --- a/apng_library/src/main/java/oupson/apng/Apng.kt +++ b/apng_library/src/main/java/oupson/apng/Apng.kt @@ -186,8 +186,8 @@ class Apng { fcTL.addAll(to4Bytes(0).toList()) } } else { - fcTL.addAll(to4Bytes(frames[0].x_offsets!!).toList()) - fcTL.addAll(to4Bytes(frames[0].y_offsets!!).toList()) + fcTL.addAll(to4Bytes(frames[0].x_offsets).toList()) + fcTL.addAll(to4Bytes(frames[0].y_offsets).toList()) } // Set frame delay @@ -519,15 +519,14 @@ class Apng { it.maxWidth = maxWidth it.maxHeight = maxHeight } - for (i in 0 until frames.size){ - File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "frameCreated$i.png").writeBytes(frames[i].byteArray) - } val drawedFrame = ApngAnimator(null).draw(frames) for (i in 1 until frames.size) { val diffCalculator = BitmapDiffCalculator(drawedFrame[i - 1], drawedFrame[i]) - frames[i].byteArray = PngEncoder.encode(diffCalculator.res) + File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "frame$i.png").writeBytes(PngEncoder.encode(diffCalculator.res, true)) + frames[i].byteArray = PngEncoder.encode(diffCalculator.res, true) frames[i].x_offsets = diffCalculator.xOffset frames[i].y_offsets = diffCalculator.yOffset + frames[i].blend_op = Utils.Companion.blend_op.APNG_BLEND_OP_OVER } } } \ No newline at end of file diff --git a/apng_library/src/main/java/oupson/apng/Frame.kt b/apng_library/src/main/java/oupson/apng/Frame.kt index 25c1af6..84c1e66 100644 --- a/apng_library/src/main/java/oupson/apng/Frame.kt +++ b/apng_library/src/main/java/oupson/apng/Frame.kt @@ -31,8 +31,8 @@ class Frame { val delay : Float - var x_offsets : Int? = null - var y_offsets : Int? = null + var x_offsets : Int = 0 + var y_offsets : Int = 0 var maxWidth : Int? = null var maxHeight : Int? = null diff --git a/apng_library/src/main/java/oupson/apng/ImageUtils/BitmapDiffCalculator.kt b/apng_library/src/main/java/oupson/apng/ImageUtils/BitmapDiffCalculator.kt index 78cfedc..ed98464 100644 --- a/apng_library/src/main/java/oupson/apng/ImageUtils/BitmapDiffCalculator.kt +++ b/apng_library/src/main/java/oupson/apng/ImageUtils/BitmapDiffCalculator.kt @@ -4,7 +4,9 @@ import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint +import android.support.annotation.ColorInt import oupson.apng.utils.Utils +import java.util.* class BitmapDiffCalculator(firstBitmap: Bitmap, secondBitmap : Bitmap) { val res : Bitmap @@ -37,7 +39,7 @@ class BitmapDiffCalculator(firstBitmap: Bitmap, secondBitmap : Bitmap) { } bottomLoop@ while (true) { for (x in 0 until difBitmap.width) { - if (height - 1 < 0) { + if (height < 0) { break@bottomLoop } else if (difBitmap.getPixel(x, height - 1) != Color.TRANSPARENT) { break@bottomLoop @@ -61,9 +63,55 @@ class BitmapDiffCalculator(firstBitmap: Bitmap, secondBitmap : Bitmap) { } width -= 1 } - val btm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) - val canvas2 = Canvas(btm) - canvas2.drawBitmap(difBitmap, -xOffset.toFloat(), -yOffset.toFloat(), Paint()) + val btm = Bitmap.createBitmap(difBitmap, xOffset, yOffset, width - xOffset, height - yOffset) res = btm } + + fun Bitmap.trim(@ColorInt color: Int = Color.TRANSPARENT): Bitmap { + + var top = height + var bottom = 0 + var right = width + var left = 0 + + var colored = IntArray(width, { color }) + var buffer = IntArray(width) + + for (y in bottom until top) { + getPixels(buffer, 0, width, 0, y, width, 1) + if (!Arrays.equals(colored, buffer)) { + bottom = y + break + } + } + + for (y in top - 1 downTo bottom) { + getPixels(buffer, 0, width, 0, y, width, 1) + if (!Arrays.equals(colored, buffer)) { + top = y + break + } + } + + val heightRemaining = top - bottom + colored = IntArray(heightRemaining, { color }) + buffer = IntArray(heightRemaining) + + for (x in left until right) { + getPixels(buffer, 0, 1, x, bottom, 1, heightRemaining) + if (!Arrays.equals(colored, buffer)) { + left = x + break + } + } + + for (x in right - 1 downTo left) { + getPixels(buffer, 0, 1, x, bottom, 1, heightRemaining) + if (!Arrays.equals(colored, buffer)) { + right = x + break + } + } + return Bitmap.createBitmap(this, left, bottom, right - left, top - bottom) + } } \ No newline at end of file diff --git a/apng_library/src/main/java/oupson/apng/exceptions/customException.kt b/apng_library/src/main/java/oupson/apng/exceptions/customException.kt index 19fae10..e35972f 100644 --- a/apng_library/src/main/java/oupson/apng/exceptions/customException.kt +++ b/apng_library/src/main/java/oupson/apng/exceptions/customException.kt @@ -4,4 +4,5 @@ class NoFrameException : Exception() class NotPngException : Exception() class NotApngException : Exception() class NoFcTL : Exception() -class BadCRC : Exception() \ No newline at end of file +class BadCRC : Exception() +class BadApng(override val message: String? = null) : Exception() \ No newline at end of file diff --git a/app-test/build.gradle b/app-test/build.gradle index 97a3977..6f75aa5 100644 --- a/app-test/build.gradle +++ b/app-test/build.gradle @@ -30,7 +30,6 @@ android { productFlavors { } } -ext.anko_version = '0.10.8' dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" @@ -47,7 +46,8 @@ dependencies { // implementation fileTree(include: ['*.aar'], dir: 'libs') //implementation 'com.github.oupson:Kapng-Android:1.0.0' implementation 'com.android.support:design:27.1.1' // where X.X.X version - implementation 'asia.ivity.android:drag-sort-listview:1.0' + implementation "org.jetbrains.anko:anko-appcompat-v7:$anko_version" + implementation "org.jetbrains.anko:anko-design:$anko_version" } kotlin { experimental { diff --git a/app-test/src/main/java/oupson/apngcreator/CreatorActivity.kt b/app-test/src/main/java/oupson/apngcreator/CreatorActivity.kt index 5f8d868..a601be4 100644 --- a/app-test/src/main/java/oupson/apngcreator/CreatorActivity.kt +++ b/app-test/src/main/java/oupson/apngcreator/CreatorActivity.kt @@ -6,28 +6,26 @@ import android.graphics.Bitmap import android.graphics.BitmapFactory import android.os.Bundle import android.os.Environment +import android.support.design.widget.FloatingActionButton import android.support.v7.app.AppCompatActivity -import kotlinx.android.synthetic.main.activity_creator.* -import org.jetbrains.anko.alert -import org.jetbrains.anko.customView -import org.jetbrains.anko.imageView +import android.widget.ListView +import org.jetbrains.anko.* +import org.jetbrains.anko.design.floatingActionButton import org.jetbrains.anko.sdk27.coroutines.onClick -import oupson.apng.APNGDisassembler import oupson.apng.Apng import oupson.apng.ApngAnimator -import oupson.apngcreator.adapter.frameListViewAdapter +import oupson.apngcreator.adapter.AnkoAdapter import java.io.File - class CreatorActivity : AppCompatActivity() { var items : ArrayList = ArrayList() - var bitmapAdapter : frameListViewAdapter? = null + var bitmapAdapter : AnkoAdapter? = null val PICK_IMAGE = 999 - + var view = CreatorActivityLayout() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_creator) - fab_add_frame.onClick { + view.setContentView(this) + view.addFrameButton.onClick { val getIntent = Intent(Intent.ACTION_GET_CONTENT) getIntent.type = "image/*" @@ -39,14 +37,16 @@ class CreatorActivity : AppCompatActivity() { startActivityForResult(chooserIntent, PICK_IMAGE) } - fab_create.onClick { + view.createButton.onClick { var apngCreated = Apng() items.forEach { bitmap -> apngCreated.addFrames(bitmap) } - apngCreated = APNGDisassembler.disassemble(apngCreated.toByteArray()).apply { + File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "apn0.png").writeBytes(apngCreated.toByteArray()) + + apngCreated.apply { optimiseFrame() } val a = ApngAnimator(applicationContext) @@ -64,15 +64,31 @@ class CreatorActivity : AppCompatActivity() { File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "frameCreated$i.png").writeBytes(PngEncoder.encode(vt)) } } - this.setImageDrawable(anim.anim) */ + this.setImageDrawable(anim.anim) } } }.show() } } - bitmapAdapter = frameListViewAdapter(this, items) - dragView.adapter = bitmapAdapter + bitmapAdapter = AnkoAdapter({items}) {index, items, view -> + with(items[index]) { + verticalLayout { + lparams { + width = matchParent + height = matchParent + } + imageView { + setImageBitmap(this@with) + }.lparams { + width = matchParent + height = matchParent + } + } + } + } + /* frameListViewAdapter(this, items) */ + view.listView.adapter = bitmapAdapter } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { @@ -88,3 +104,39 @@ class CreatorActivity : AppCompatActivity() { } } } + +class CreatorActivityLayout : AnkoComponent { + lateinit var listView : ListView + lateinit var addFrameButton : FloatingActionButton + lateinit var createButton : FloatingActionButton + + override fun createView(ui: AnkoContext) = with(ui) { + relativeLayout { + listView = listView { + }.lparams { + width = matchParent + height = matchParent + } + addFrameButton = floatingActionButton { + imageResource = R.drawable.ic_add_white_24dp + isClickable = true + }.lparams { + width = wrapContent + height = wrapContent + margin = dip(5) + alignParentBottom() + alignParentEnd() + } + createButton = floatingActionButton { + imageResource = R.drawable.ic_play_arrow_white_24dp + isClickable = true + }.lparams { + width = wrapContent + height = wrapContent + margin = dip(5) + alignParentBottom() + alignParentStart() + } + } + } +} diff --git a/app-test/src/main/java/oupson/apngcreator/adapter/AnkoAdapter.kt b/app-test/src/main/java/oupson/apngcreator/adapter/AnkoAdapter.kt new file mode 100644 index 0000000..aa645b7 --- /dev/null +++ b/app-test/src/main/java/oupson/apngcreator/adapter/AnkoAdapter.kt @@ -0,0 +1,28 @@ +package oupson.apngcreator.adapter + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter + +class AnkoAdapter(itemFactory: () -> List, viewFactory: Context.(index: Int, items: List, view: View?) -> View): BaseAdapter() { + val viewFactory = viewFactory + val items: List by lazy { itemFactory() } + + override fun getView(index: Int, view: View?, viewGroup: ViewGroup?): View { + return viewGroup!!.context.viewFactory(index, items, view) + } + + override fun getCount(): Int { + return items.size + } + + override fun getItem(index: Int): T { + return items.get(index) + } + + override fun getItemId(index: Int): Long { + + return (items.get(index) as Any).hashCode().toLong() + (index.toLong() * Int.MAX_VALUE) + } +} \ No newline at end of file diff --git a/app-test/src/main/java/oupson/apngcreator/adapter/frameListViewAdapter.kt b/app-test/src/main/java/oupson/apngcreator/adapter/frameListViewAdapter.kt deleted file mode 100644 index db15a02..0000000 --- a/app-test/src/main/java/oupson/apngcreator/adapter/frameListViewAdapter.kt +++ /dev/null @@ -1,21 +0,0 @@ -package oupson.apngcreator.adapter - -import android.content.Context -import android.graphics.Bitmap -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ArrayAdapter -import android.widget.ImageView -import oupson.apngcreator.R - -class frameListViewAdapter (context: Context, val bitmaps: List) : ArrayAdapter(context, 0, bitmaps) { - - override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - val view = LayoutInflater.from(context).inflate(R.layout.framelistviewadapterlayout, parent, false) - - val imageView = view.findViewById(R.id.frameAdapterImageView) - imageView.setImageBitmap(bitmaps[position]) - return view - } -} \ No newline at end of file diff --git a/app-test/src/main/res/layout/activity_creator.xml b/app-test/src/main/res/layout/activity_creator.xml deleted file mode 100644 index bcd986f..0000000 --- a/app-test/src/main/res/layout/activity_creator.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app-test/src/main/res/layout/framelistviewadapterlayout.xml b/app-test/src/main/res/layout/framelistviewadapterlayout.xml deleted file mode 100644 index 1d9e116..0000000 --- a/app-test/src/main/res/layout/framelistviewadapterlayout.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/build.gradle b/build.gradle index 9f9021f..6a54904 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ buildscript { ext.kotlin_version = '1.3.11' + ext.anko_version = '0.10.8' repositories { google() jcenter()