0

I'm attempting to perform a bit of memory management and it appears that I'll need to scaled the image before I can use it - the problem is - I'm not sure how that can be accomplished while still keeping the mirror/transparency effect I've created.

The app is crashing on this line:

Bitmap bitmapWithReflection = Bitmap.createBitmap(width 
                      , (height + height/2), Config.ARGB_8888);

and I've attempted to implement:

                Bitmap reflectionImage = Bitmap.createScaledBitmap(originalImage, height/2, height/2, false);

However that is not correct and results in another fatal crash with nothing of value in my logcat:

              11-27 17:30:58.206: I/art(2111): Clamp target GC heap from 99MB to 96MB
11-27 17:31:01.354: I/Process(2111): Sending signal. PID: 2111 SIG: 9
11-27 17:31:42.609: I/Process(2235): Sending signal. PID: 2235 SIG: 9

How might this be avoided?

Logcat:

11-27 17:26:00.490: D/AndroidRuntime(1953): Shutting down VM
11-27 17:26:00.492: E/AndroidRuntime(1953): FATAL EXCEPTION: main
11-27 17:26:00.492: E/AndroidRuntime(1953): Process: com.cb, PID: 1953
11-27 17:26:00.492: E/AndroidRuntime(1953): java.lang.OutOfMemoryError: Failed to allocate a 10898412 byte allocation with 1855424 free bytes and 1811KB until OOM
11-27 17:26:00.492: E/AndroidRuntime(1953):     at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at android.graphics.Bitmap.nativeCreate(Native Method)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at android.graphics.Bitmap.createBitmap(Bitmap.java:812)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at android.graphics.Bitmap.createBitmap(Bitmap.java:789)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at android.graphics.Bitmap.createBitmap(Bitmap.java:756)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at com.cb.CoverFlowExample$ImageAdapter.createReflectedImages(CoverFlowExample.java:133)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at com.cb.CoverFlowExample.onCreate(CoverFlowExample.java:47)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at android.app.Activity.performCreate(Activity.java:5990)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at android.app.ActivityThread.access$800(ActivityThread.java:151)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at android.os.Handler.dispatchMessage(Handler.java:102)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at android.os.Looper.loop(Looper.java:135)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at android.app.ActivityThread.main(ActivityThread.java:5254)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at java.lang.reflect.Method.invoke(Native Method)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at java.lang.reflect.Method.invoke(Method.java:372)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
11-27 17:26:00.492: E/AndroidRuntime(1953):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
11-27 17:26:04.564: I/Process(1953): Sending signal. PID: 1953 SIG: 9

Source:

 int index = 0;
                for (int imageId : mImageIds) {
                    Bitmap originalImage = BitmapFactory.decodeResource(getResources(), 
                            imageId);
                    int width = originalImage.getWidth();
                    int height = originalImage.getHeight();


                    //This will not scale but will flip on the Y axis
                    Matrix matrix = new Matrix();
                    matrix.preScale(1, -1);

                    //Create a Bitmap with the flip matrix applied to it.
                    //We only want the bottom half of the image
                    Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0, height/2, width, height/2, matrix, false);


                    //Create a new bitmap with same width but taller to fit reflection
                    Bitmap bitmapWithReflection = Bitmap.createBitmap(width 
                      , (height + height/2), Config.ARGB_8888);

                   //Create a new Canvas with the bitmap that's big enough for
                   //the image plus gap plus reflection
                   Canvas canvas = new Canvas(bitmapWithReflection);
                   //Draw in the original image
                   canvas.drawBitmap(originalImage, 0, 0, null);
                   //Draw in the gap
                   Paint deafaultPaint = new Paint();
                   canvas.drawRect(0, height, width, height + reflectionGap, deafaultPaint);
                   //Draw in the reflection
                   canvas.drawBitmap(reflectionImage,0, height + reflectionGap, null);

                   //Create a shader that is a linear gradient that covers the reflection
                   Paint paint = new Paint(); 
                   LinearGradient shader = new LinearGradient(0, originalImage.getHeight(), 0, 
                     bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff, 0x00ffffff, 
                     TileMode.CLAMP); 
                   //Set the paint to use this shader (linear gradient)
                   paint.setShader(shader); 
                   //Set the Transfer mode to be porter duff and destination in
                   paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN)); 
                   //Draw a rectangle using the paint with our linear gradient
                   canvas.drawRect(0, height, width, 
                     bitmapWithReflection.getHeight() + reflectionGap, paint); 

                   ImageView imageView = new ImageView(mContext);
                   imageView.setImageBitmap(bitmapWithReflection);
                   imageView.setLayoutParams(new CoverFlow.LayoutParams(1900, 1500));
                   imageView.setScaleType(ScaleType.FIT_XY);
                   mImages[index++] = imageView;

                }
                return true;
        }
User Name
  • 3
  • 3
  • 1
    scale first, then apply effects? I.e. load less at the initial `decodeResource` step – zapl Nov 27 '15 at 22:45
  • I tried this but it's still crashing: Bitmap originalImage = BitmapFactory.decodeResource(getResources(), imageId); int width = originalImage.getWidth(); int height = originalImage.getHeight(); Bitmap.createScaledBitmap(originalImage, height, width, false); – User Name Nov 27 '15 at 22:57
  • Might you be able to show me how that can be accomplished? I also tried this: Bitmap preoriginalImage = BitmapFactory.decodeResource(getResources(), imageId); int width = preoriginalImage.getWidth(); int height = preoriginalImage.getHeight(); Bitmap originalImage = Bitmap.createScaledBitmap(preoriginalImage, width, height, false); – User Name Nov 27 '15 at 23:08
  • http://stackoverflow.com/questions/16408505/how-to-downsample-images-correctly there is an example you can tell `decodeResource` to load a downscaled image. The `inSampleSize` factor is also explained in http://developer.android.com/training/displaying-bitmaps/load-bitmap.html#load-bitmap – zapl Nov 27 '15 at 23:14
  • Thank you for your help - I feel like I'm getting closer but it's still crashing - can you take a look? http://pastebin.com/SSMgE4Fj – User Name Nov 27 '15 at 23:29
  • (btw - if you'd like credit for your help - I'll gladly accept your answer if youd like to post it as one) – User Name Nov 27 '15 at 23:31

1 Answers1

1

The most basic version of scaling images is to specify a sample size. Or in other words tell the bitmap decoder to only decode every n-th pixel and row. So 2 means it's loading only 500x500 if the original image is 1000x1000, that's a reduction of 4 in memory.

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
int index = 0;
for (int imageId : mImageIds) {
    Bitmap originalImage = BitmapFactory.decodeResource(getResources(),
            imageId, options);

This may not be enough depending on size, what images you use, the phone and several other factors. There is an article here

http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

that explains the most important parts.

zapl
  • 63,179
  • 10
  • 123
  • 154