Work on optimizer

Add verification of xOffset and yOffset : `x_offset` + `width`  <= `IHDR` width
                                          `y_offset` + `height` <= `IHDR` height
This commit is contained in:
oupson 2019-01-12 17:01:41 +01:00
parent ef95ec2e1f
commit fb764591be
14 changed files with 170 additions and 100 deletions

View File

@ -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()

View File

@ -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 {

View File

@ -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
}
}
}

View File

@ -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

View File

@ -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)
}
}

View File

@ -4,4 +4,5 @@ class NoFrameException : Exception()
class NotPngException : Exception()
class NotApngException : Exception()
class NoFcTL : Exception()
class BadCRC : Exception()
class BadCRC : Exception()
class BadApng(override val message: String? = null) : Exception()

View File

@ -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 {

View File

@ -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()
}
}
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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>

View File

@ -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>

View File

@ -2,6 +2,7 @@
buildscript {
ext.kotlin_version = '1.3.11'
ext.anko_version = '0.10.8'
repositories {
google()
jcenter()