4

Hi i am creating image editing app wher i am create bitmap for canvas and imageview

here i am using 385kb image file with 2000x2000 dimension in imageView

here is the imageenter image description here

my problem is that first time i create bitmap with other small image and edit image it work some time but for second time with above size and dimension it generate java.lang.OutOfMemoryError :(

this is my related methods code

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // initialize variable
        init();

        // initialize view
        initView();

        // initialize view's event listener
        initEventListener();

        setImageToImageView(ims);

    }  

private void init() {

        context = MainActivity.this;
        db_colorImage = new DB_ColorImage(context);

        AppUtils.setColorPreference(MainActivity.this, AppConstant.PREF_COLOR_PIC, "FF0000");
        AppUtils.setComboColorPreference(MainActivity.this, AppConstant.PREF_COMBO, 1);

        openCatId = getIntent().getIntExtra("open_cat_id", -1);
        openCat = getIntent().getStringExtra("image_categoryname");
        openImgName = getIntent().getStringExtra("img_name");

        db_colorImage.openDB();
        db_colorImage.startTransaction();
        assetsImgxx = db_colorImage.getXXX(openCatId);
        db_colorImage.successfullTransaction();
        db_colorImage.completeTransaction();
        db_colorImage.closeDB();


        assetsAndSdImage = new ArrayList<>(getIntent().getStringArrayListExtra("again"));           // aa list edit karelu with sdcard image

        try {

            ims = getAssets().open(openCat + "/" + openImgName);
        } catch (IOException e) {
            File getFromSd = new File(AppUtils.appFolder() + File.separator + openImgName);
            try {
                ims = new FileInputStream(getFromSd);
            } catch (FileNotFoundException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }

    }  

    public void setImageToImageView(InputStream ims) {
        Drawable d = Drawable.createFromStream(ims, null);

        BitmapDrawable drawable = (BitmapDrawable) d;

        Bitmap bmp = drawable.getBitmap();


        if (bmp != null) {
            _alteredBitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Bitmap.Config.ARGB_8888); // here i used ARGB_8888 because jnibitmap.cpp also use ARGB_8888. If we do not use this it wont color on image
        }

        Canvas canvas = new Canvas(_alteredBitmap);

        Paint paint = new Paint();

        Matrix matrix = new Matrix();

        canvas.drawBitmap(bmp, matrix, paint);

        imageView.setImageBitmap(_alteredBitmap);  // we can also use imageView.setImageResource(R.drawable.test_image);

        // ******* clear bitmap after use *******
        // check after done this step it will not produce any problem
        if (bmp != null) {
            bmp.recycle();
            bmp = null;
        }
    }  

_alteredBitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Bitmap.Config.ARGB_8888);

at this line where i get EXCEPTION, here is the log

01-27 19:12:33.590 27252-27252/? E/AndroidRuntime: FATAL EXCEPTION: main
                                                   java.lang.OutOfMemoryError
                                                       at android.graphics.Bitmap.nativeCreate(Native Method)
                                                       at android.graphics.Bitmap.createBitmap(Bitmap.java:640)
                                                       at android.graphics.Bitmap.createBitmap(Bitmap.java:620)
                                                       at com.yptech.myfloodfilldemo.ui.MainActivity.setImageToImageView(MainActivity.java:387)
                                                       at com.yptech.myfloodfilldemo.ui.MainActivity.onCreate(MainActivity.java:96)
                                                       at android.app.Activity.performCreate(Activity.java:5008)
                                                       at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
                                                       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
                                                       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
                                                       at android.app.ActivityThread.access$600(ActivityThread.java:130)
                                                       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
                                                       at android.os.Handler.dispatchMessage(Handler.java:99)
                                                       at android.os.Looper.loop(Looper.java:137)
                                                       at android.app.ActivityThread.main(ActivityThread.java:4745)
                                                       at java.lang.reflect.Method.invokeNative(Native Method)
                                                       at java.lang.reflect.Method.invoke(Method.java:511)
                                                       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
                                                       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
                                                       at dalvik.system.NativeStart.main(Native Method)  

After edit image I am saving this image so I don't want to reduce image quality so I use Bitmap.Config.ARGB_8888. I tried so many solution like android developer document on bitmap but it not work for me.

is OutOfMemoryError depends upon image size and dimension ??? and how to solve my problem for OutOfMemoryError while createBitmap ??

when I am using largHeap it work fine but is that better approach to use android:largeHeap="true" ??

thanks in advance :)

Harin Kaklotar
  • 305
  • 7
  • 22
  • 1
    http://stackoverflow.com/questions/5697760/android-out-of-memory-exception-when-creating-bitmap – Danieboy Jan 27 '17 at 14:09
  • `using 385kb image`. Do you mean an image file? A jpg file? – greenapps Jan 27 '17 at 14:18
  • @greenapps yes 358kb jpg file – Harin Kaklotar Jan 27 '17 at 14:20
  • Well than edit your post i would say. – greenapps Jan 27 '17 at 14:21
  • @greenapps I add sample image. for this image when I am creating second time bitmap (first time create bitmap fill color and save the image and go back and when I open this activty code create the bitmap and then) I got exception :( I think I have to manage memory for create bitmap second time because before start this activity second time i have to clear all caches of this app. what u think ?? – Harin Kaklotar Jan 27 '17 at 20:46
  • I don't know why someone down vote this question?? because of Question title are common as others? but in my case I have different scenario – Harin Kaklotar Jan 27 '17 at 20:50

4 Answers4

2

Optimize the bitmap before using it.

Bitmap bitmap = MediaOptimize.reduceResolution(mediaPath, width, height);

Functions:

public Bitmap reduceResolution(String filePath, int viewWidth, int viewHeight) {
    int reqHeight = viewHeight;
    int reqWidth = viewWidth;

    BitmapFactory.Options options = new BitmapFactory.Options();

    // First decode with inJustDecodeBounds=true to check dimensions
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(filePath, options);

    double viewAspectRatio = 1.0 * viewWidth/viewHeight;
    double bitmapAspectRatio = 1.0 * options.outWidth/options.outHeight;

    if (bitmapAspectRatio > viewAspectRatio)
        reqHeight = (int) (viewWidth/bitmapAspectRatio);
    else
        reqWidth = (int) (viewHeight * bitmapAspectRatio);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    System.gc();                                        // TODO - remove explicit gc calls
    return BitmapFactory.decodeFile(filePath, options);
}


private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {

    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {
        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
    }
    return inSampleSize;
}
Jaydev
  • 1,794
  • 20
  • 37
  • hi but after reduceResolution I also lost image quality – Harin Kaklotar Jan 27 '17 at 14:25
  • hi I am using BitmapFactory.decodeStream with InputStream instead of BitmapFactory.decodeFile in reduceResolution method then after end it give null bitmap – Harin Kaklotar Jan 28 '17 at 05:34
  • Every device has a resolution limit of width and height. Use it till that limit, you won't see any perceived reduce in image quality. – Jaydev Jan 30 '17 at 08:09
1

I'm using Glide (https://bumptech.github.io/glide/) to loading large bitmaps efficiently. In case of loading photos from your device Glide also handle the correct rotation for you.

val myUri = ...
val neededHeight = 600
val neededWidth = 800

val bitmap = GlideApp
                .with(context)
                .asBitmap()
                .load(myUri)
                .override(neededWidth, neededHeight)
                .submit()
                .get()
D.Roters
  • 221
  • 2
  • 6
-1

Try this out, just add it to AndroidManifest.xml

<application
    ...
    android:largeHeap="true">
    <activity
        android:name=".MainActivity"
        ...

Simon Schnell
  • 1,160
  • 14
  • 24
  • 2
    that is really bad practice to have largeHeap true in badly optimized app. Optimize your app firtst and if you do it right you probably don't need largeHeap property. 385kb image should be loaded smothly on every phone (the way @Jaydev showed) http://stackoverflow.com/questions/30480007/is-using-largeheap-in-android-manifest-a-good-practice – Karol Żygłowicz Jan 27 '17 at 14:57
  • @KarolŻygłowicz you are right but I make simple app and load this image(which i post in question) in ImageView it not load and app close – Harin Kaklotar Jan 27 '17 at 15:29
  • 1
    @Hari Kaklotar. I don't exactly know what you are trying to achieve with the quality because you are trying to show 2000x2000 image with Bitmap.Config.ARGB_8888. so based on documentation with ARGB_8888 each pixel is stored on 4 bytes. So 2000*2000*4 bytes = 15.25 Mb of data. That is wy you are getting OutOfMemoryError because its a lot for a single image that should weight 385kb as you mentioned. – Karol Żygłowicz Jan 27 '17 at 15:48
  • @KarolŻygłowicz i am try to create color fill app so I am using [library] (https://github.com/mar3kk/threekkapps_library) for fill color in image so as per this lib i have to set Bitmap.Config.ARGB_8888 to fill color my image to get high quality. that's why I am creating Bitmap with this value. and get Exception :( – Harin Kaklotar Jan 27 '17 at 20:24
  • Simon thank you for your replay it works, Thank u very much for help :) but as per @KarolŻygłowicz it is not good idea to use android:largeHeap="true". – Harin Kaklotar Jan 27 '17 at 20:28
-1

The image is way to big. I recommend shrinking the image or using picasso library to make better use of memory issues and image caching.

Bram Jongebloet
  • 306
  • 1
  • 2
  • 10
  • image loading is not my problem, as per your suggestion I can load image using those lib but I want to creatBitmap. Thanks for your help – Harin Kaklotar Jan 27 '17 at 20:29