Skip to content

Commit

Permalink
Bitmap (and ram) usage reduced.
Browse files Browse the repository at this point in the history
Address issue #1 OutOfMemoryError with photo from camera, Galaxy S6
  • Loading branch information
inlacou committed Jan 31, 2018
1 parent a8c463a commit 009f9bc
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import kotlinx.android.synthetic.main.activity_example.*
import libraries.inlacou.com.imagegetter.ImageGetter
import picturedraweditor.inlacou.bvapps.com.picturedraweditor.PictureDrawEditorAct
import picturedraweditor.inlacou.bvapps.com.picturedraweditor.PictureDrawEditorMdl
import java.io.File

class ExampleActivity : AppCompatActivity() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,20 @@ import android.util.Log
import android.view.MotionEvent
import android.view.View
import colorpickerlayout.inlacou.bvapps.com.colorpicklayout.ColorPickLayout
import java.io.*

import java.io.File
import java.io.FileOutputStream
import java.nio.channels.FileChannel
import java.util.ArrayList
import kotlin.concurrent.thread
import java.nio.channels.FileChannel.MapMode.READ_WRITE



/**
* Created by inlacou on 20/12/17.
*/
class CanvasView(internal var context: Context, attrs: AttributeSet) : View(context, attrs) {

private var mBitmap: Bitmap? = null
private var mCanvas: Canvas? = null
private var currentPath: Path = Path()
private var currentPaint: Paint = Paint()
Expand All @@ -44,6 +46,7 @@ class CanvasView(internal var context: Context, attrs: AttributeSet) : View(cont
private val redoPaints = ArrayList<Paint>()

fun setModel(model: PictureDrawEditorMdl) {
Log.d(DEBUG_TAG, "setModel")
this.model = model
}

Expand Down Expand Up @@ -92,9 +95,10 @@ class CanvasView(internal var context: Context, attrs: AttributeSet) : View(cont

private fun updateBitmap(w: Int, h: Int) {
// your Canvas will draw onto the defined Bitmap
if(mBitmap==null) {
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
mCanvas = Canvas(mBitmap!!)
if(mCanvas==null) {
mCanvas = Canvas(model!!.layer0!!)
}else{
Log.d(DEBUG_TAG + ".updateBitmap", "ignored")
}
}

Expand All @@ -116,7 +120,10 @@ class CanvasView(internal var context: Context, attrs: AttributeSet) : View(cont
if (file.exists()) file.delete()
try {
val out = FileOutputStream(file)
overlay(model!!.layer0, mBitmap).compress(Bitmap.CompressFormat.PNG, 90, out)
//val bm = overlay(model!!.layer0!!, mBitmap!!)
model!!.layer0!!.compress(Bitmap.CompressFormat.PNG, 90, out)
//bm.recycle()
model?.layer0?.recycle()
out.flush()
out.close()
} catch (e: Exception) {
Expand Down Expand Up @@ -222,21 +229,76 @@ class CanvasView(internal var context: Context, attrs: AttributeSet) : View(cont
return false
}

fun onDestroy(){
model?.layer0?.recycle()
}

companion object {

private val DEBUG_TAG = CanvasView::class.java.simpleName
private val TOLERANCE = 5f

fun overlay(bmp1: Bitmap?, bmp2: Bitmap?): Bitmap {
Log.d(DEBUG_TAG + ".overlay1", bmp1!!.width.toString() + ", " + bmp1.height)
Log.d(DEBUG_TAG + ".overlay2", bmp2!!.width.toString() + ", " + bmp2.height)
val bmOverlay = Bitmap.createBitmap(bmp1.width, bmp1.height, bmp1.config)
val scaledBmp2 = Bitmap.createScaledBitmap(bmp2, bmp1.width, bmp1.height, false)
@Deprecated("Just here for reference")
private fun overlay(bmp1: Bitmap, bmp2: Bitmap): Bitmap {
val w1 = bmp1.width
val h1 = bmp1.width
val bmOverlay = Bitmap.createBitmap(w1, h1, bmp1.config)
val canvas = Canvas(bmOverlay)
canvas.drawBitmap(bmp1, Matrix(), null)
bmp1.recycle()
val scaledBmp2 = Bitmap.createScaledBitmap(bmp2, w1, h1, false)
canvas.drawBitmap(scaledBmp2, 0f, 0f, null)
bmp2.recycle()
scaledBmp2.recycle()
return bmOverlay
}

fun convertToMutable(imgIn: Bitmap): Bitmap {
var imgIn = imgIn
try {
//this is the file going to use temporally to save the bytes.
// This file will not be a image, it will store the raw image data.
val file = File(Environment.getExternalStorageDirectory().toString() + File.separator + "temp.tmp")

//Open an RandomAccessFile
//Make sure you have added uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
//into AndroidManifest.xml file
val randomAccessFile = RandomAccessFile(file, "rw")

// get the width and height of the source bitmap.
val width = imgIn.width
val height = imgIn.height
val type = imgIn.config

//Copy the byte to the file
//Assume source bitmap loaded using options.inPreferredConfig = Config.ARGB_8888;
val channel = randomAccessFile.channel
val map = channel.map(FileChannel.MapMode.READ_WRITE, 0, imgIn.rowBytes.toLong() * height.toLong())
imgIn.copyPixelsToBuffer(map)
//recycle the source bitmap, this will be no longer used.
imgIn.recycle()
System.gc()// try to force the bytes from the imgIn to be released

//Create a new bitmap to load the bitmap again. Probably the memory will be available.
imgIn = Bitmap.createBitmap(width, height, type)
map.position(0)
//load it back from temporary
imgIn.copyPixelsFromBuffer(map)
//close the temporary file and channel , then delete that also
channel.close()
randomAccessFile.close()

// delete the temp file
file.delete()

} catch (e: FileNotFoundException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}

return imgIn
}
}

interface FileSavedListener{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,6 @@ private Paint getFill()
return p;
}

/*@Override
protected int hGetMaximumHeight() {
return circleRadius * 2 + strokeWidth;
}
@Override
protected int hGetMaximumWidth() {
return circleRadius * 2 + strokeWidth;
}*/

public int getCircleRadius() {
return circleRadius;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.app.Dialog
import android.app.ProgressDialog
import android.content.DialogInterface
import android.content.Intent
import android.graphics.Bitmap
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.ImageView
Expand All @@ -15,13 +16,9 @@ import android.graphics.BitmapFactory
import android.graphics.Point
import android.net.Uri
import android.provider.MediaStore
import android.util.Log
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import android.view.ViewTreeObserver
import android.widget.SeekBar
import android.widget.Toast
import colorpickerlayout.inlacou.bvapps.com.colorpicklayout.ColorListener
import colorpickerlayout.inlacou.bvapps.com.colorpicklayout.ColorPickLayout
import colorpickerlayout.inlacou.bvapps.com.colorpicklayout.ColorWrapper
Expand Down Expand Up @@ -111,8 +108,6 @@ class PictureDrawEditorAct : AppCompatActivity() {
}

private fun populate() {
Log.d(DEBUG_TAG, "model: $model")

window.decorView.systemUiVisibility = (
//View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
//or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
Expand All @@ -133,15 +128,10 @@ class PictureDrawEditorAct : AppCompatActivity() {

imageView.viewTreeObserver.addOnGlobalLayoutListener(object: ViewTreeObserver.OnGlobalLayoutListener{
override fun onGlobalLayout() {
var size = Point()
val size = Point()
windowManager.defaultDisplay.getRealSize(size)
Log.d(DEBUG_TAG, "screen size: $size | proportion: ${size.x.toDouble()/size.y}")
Log.d(DEBUG_TAG, "image size: ${imageView.width}, ${imageView.height} | proportion: ${imageView.width.toDouble()/imageView.height}")
//Log.d(DEBUG_TAG, "new screen size: $size | proportion: ${size.x.toDouble()/size.y}")
//Log.d(DEBUG_TAG, "new image size: ${imageView.width}, ${imageView.width*size.y/size.x} | proportion: ${imageView.width.toDouble()/(imageView.width*size.y/size.x)}")

imageView.viewTreeObserver.removeOnGlobalLayoutListener(this)
//Utils.resizeView(imageView, -1, imageView.width*size.y/size.x)
}
})

Expand All @@ -156,13 +146,18 @@ class PictureDrawEditorAct : AppCompatActivity() {
e.printStackTrace()
}

if (model.layer0 == null) {
return
model.layer0?.let {
val display = windowManager.defaultDisplay;
val size = Point()
display.getSize(size)
val width = size.x
val height = size.y
model.layer0 = CanvasView.convertToMutable(model.layer0!!)
model.layer0 = Bitmap.createScaledBitmap(model.layer0, width, height, false)
imageView.adjustViewBounds = true
imageView.scaleType = ImageView.ScaleType.CENTER_INSIDE
imageView.setImageDrawable(BitmapDrawable(resources, model.layer0))
}
imageView.adjustViewBounds = true
imageView.scaleType = ImageView.ScaleType.CENTER_INSIDE
val drawable = BitmapDrawable(resources, model.layer0)
imageView.setImageDrawable(drawable)
}

private fun setListeners() {
Expand Down Expand Up @@ -272,7 +267,6 @@ class PictureDrawEditorAct : AppCompatActivity() {
progressDialog.setMessage(getString(R.string.Saving))
progressDialog.isIndeterminate = true
progressDialog.setOnShowListener {
Log.d(DEBUG_TAG, "onShow")
canvas.saveImage(object: CanvasView.FileSavedListener{
override fun onFileSaved(file: File) {
progressDialog.dismiss()
Expand Down Expand Up @@ -310,6 +304,7 @@ class PictureDrawEditorAct : AppCompatActivity() {

override fun onDestroy() {
controller.onDestroy()
canvas.onDestroy()
super.onDestroy()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import android.view.View
*/
object Utils {
fun resizeView(view: View, width: Int, height: Int) {
Log.d("Utils.resizeView", "size: $width, $height")
val lp = view.layoutParams
if (width > -1) {
lp.width = width
Expand Down

0 comments on commit 009f9bc

Please sign in to comment.