Work on optimizer
Add verification of xOffset and yOffset : `x_offset` + `width` <= `IHDR` width `y_offset` + `height` <= `IHDR` height
This commit is contained in:
parent
ef95ec2e1f
commit
fb764591be
Binary file not shown.
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -5,3 +5,4 @@ class NotPngException : Exception()
|
|||
class NotApngException : Exception()
|
||||
class NoFcTL : Exception()
|
||||
class BadCRC : Exception()
|
||||
class BadApng(override val message: String? = null) : Exception()
|
|
@ -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 {
|
||||
|
|
|
@ -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<Bitmap> = ArrayList()
|
||||
var bitmapAdapter : frameListViewAdapter? = null
|
||||
var bitmapAdapter : AnkoAdapter<Bitmap>? = 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<CreatorActivity> {
|
||||
lateinit var listView : ListView
|
||||
lateinit var addFrameButton : FloatingActionButton
|
||||
lateinit var createButton : FloatingActionButton
|
||||
|
||||
override fun createView(ui: AnkoContext<CreatorActivity>) = 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<T>(itemFactory: () -> List<T>, viewFactory: Context.(index: Int, items: List<T>, view: View?) -> View): BaseAdapter() {
|
||||
val viewFactory = viewFactory
|
||||
val items: List<T> 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)
|
||||
}
|
||||
}
|
|
@ -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<Bitmap>) : ArrayAdapter<Bitmap>(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<ImageView>(R.id.frameAdapterImageView)
|
||||
imageView.setImageBitmap(bitmaps[position])
|
||||
return view
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".CreatorActivity">
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
android:id="@+id/fab_create"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:clickable="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:srcCompat="@drawable/ic_play_arrow_white_24dp" />
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
android:id="@+id/fab_add_frame"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:clickable="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:srcCompat="@drawable/ic_add_white_24dp" />
|
||||
|
||||
<ListView
|
||||
android:id="@+id/dragView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</android.support.constraint.ConstraintLayout>
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<ImageView android:id="@+id/frameAdapterImageView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
</RelativeLayout>
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.3.11'
|
||||
ext.anko_version = '0.10.8'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
|
|
Loading…
Reference in New Issue