zetbaitsu/Compressor resolution not working - android

So I am using zetbaitsu/Compressor library for image compression and resize the image but when I try to compress the image dimension of original dimension (3503 x 5254) the result for the following height value are as follow
800,700,600 will give the resolution of (876 x 1314) which is 1/8 of the original size and
when the value is 900+ the resolution of the image gets (1752 x2627) which is 1/2 of the original.
So is there any way to change the size exactly as per our standards as I tried other similar question i was not able to get the desired results.

It's actuallly only possible by creating your own custom constraint for this library.
The usual resolution constraint of this lib (Methodcall)(Method declaration) uses the following method (found here):
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
So it reduces the size of the image by 2 as long as it is bigger than the requested size.
The easiest way to change this is probably to create your own custom constraint and change the calculateInSampleSize() call to your own method calculating the modifier for the exact size.
Edit:
This is a sample constraint, how it should work. I couldn't test so far, but this should give you an idea how it works:
public class SizeContstraint implements Constraint
{
private int _height;
// Resulting image height
public SizeContstraint(int height)
{
_height = height;
}
// Checks, if the constraint is valid
#Override
public boolean isSatisfied(#NotNull final File file)
{
// Get the file options (height, width, e.t.c)
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
// Calculate if the current image is bigger than the necessary size
return calculateExactSizeModifier(options, _height) > 1;
}
// This method is only called, if the constraint is invald (the image is too big)
#NotNull
#Override
public File satisfy(#NotNull final File file)
{
int height = _height;
int width = 0;
// Get the file options (height, width, e.t.c)
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
// Get the size modifier to be able to resize the image
int modifier = calculateExactSizeModifier(options, height);
// Calculate the width using the modifier
width = options.outWidth / modifier;
// Resize the file into a bitmap
Bitmap bmp = id.zelory.compressor.UtilKt.decodeSampledBitmapFromFile(file, width, height);
// Write the bitmap back into the file
return id.zelory.compressor.UtilKt.overWrite(file, bmp, Bitmap.CompressFormat.JPEG, 100);
}
private int calculateExactSizeModifier(BitmapFactory.Options options, int reqHeight)
{
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int modifier = 1;
// Calculate modifier if height bigger than requested height
if (height > reqHeight)
{
modifier = height / reqHeight;
}
return modifier;
}
}

Related

java.lang.OutOfMemoryError While Displaying image list in viewFlipper

I'm trying to have live tile images in my application. I tested the functionality on some devices (API>22) and they work. But now i'm testing on API 22 and i'm getting the error in the title. I've searched through the site and I found this to be particularly helpful OutOfMemoryExceptionGridView. But I'm loading my images (straight from the res folder) to an array, then using a viewflipper to make the slideshow
How do I change my block of code (to fix the main OOME) since what I linked above uses bitmaps, and I am directly calling the res id.
This is my code:
#Override
public View onCreateView(LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {
int[] images = {R.drawable.img1, R.drawable.img2, R.drawable.img3}; //store images into array
viewFlipper = view.findViewById(R.id.firstTile); //main tile to have slideshow
for (int image : images) {
flipperImages(image); //performs the slideshow per image
}
}
public void flipperImages(int image) {
ImageView imageViewFirstTile = new ImageView(getContext());
imageViewFirstTile.setBackgroundResource(image);
viewFlipper.addView(imageViewFirstTile);
viewFlipper.setFlipInterval(12500);
viewFlipper.setAutoStart(true);
viewFlipper.setInAnimation(getContext(), android.R.anim.slide_in_left);
viewFlipper.setOutAnimation(getContext(), android.R.anim.slide_out_right);
}
How can I fix the main error with this implementation (calling the res id of the images directly)?
If the images can be scaled down to avoid loading the full sized images, I would consider that as a first step. There is no need to load the full-sized Bitmaps into memory if they window in which they're displayed is a fraction of the size. In your current approach, the images are not subsampled and as a result the entire Bitmap is loaded into memory at it's full size. So you have 3 full sized Bitmaps in memory.
For subsampling an image, see the following code. (Source: https://developer.android.com/topic/performance/graphics/load-bitmap)
/* reqWidth and reqHeight would be the dimensions of your ImageView or some other preferred dimensions. For example, if your Bitmap is 500 X 500 and your ViewFlipper/ImageView is only 100 X 100, passing 100 as reqWidth and 100 as reqHeight will subsample the image until it's dimensions are close to the 100 X 100. This will largely shrink the size loaded into memory. */
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}

Bitmap color code change after scaledown

I have one image which have color code of #AAA28B (170R,162G,139B). Now let's say current image size is 1200x1200 now I want to make it scale down at 600x600 so i use below code and get resulted in bitmap but the problem is its bitmap color code change to #ABA38C, So it RGB values increase to plus one compare to original image color and become (171R,163G,140B) how to prevent this?.
See attach a screenshot where the first image if original loaded directly from android drawable and other it loads by using following code.
private void loadImage(int width, int height){
Bitmap bitMapTest=BitmapFactory.decodeResource(getResources(),R.drawable.ic_test);
Log.i("IMAGE","Image format is"+ bitMapTest.getConfig().name()+
"Image size is"+ bitMapTest.hasAlpha() + "bitmap size is"+ bitMapTest.getHeight());
Bitmap decodeBitmap=decodeSampledBitmapFromResource(getResources(),R.drawable.ic_test,width,height);
Log.i("IMAGE","Image format is"+ decodeBitmap.getConfig().name()+
"Image size is"+ decodeBitmap.hasAlpha() + "bitmap size is"+ decodeBitmap.getHeight());
//adjust of alha deos not give me any help result remain same
//decodeBitmap=adjustOpacity(decodeBitmap);
mImgView.setImageBitmap(decodeBitmap);
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inPreferredConfig= Bitmap.Config.ARGB_8888;
options.inScaled=false;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 2;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
Here is orginal image :-
It seems like applying following code for scaledown working fine for the solid image like this.
Bitmap decodeBitmap=Bitmap.createScaledBitmap(sourceBitmap,width,height,false/*Make sure filter is false*/);

How are you supposed to "load large bitmaps efficiently" if you can't get the dimensions?

I am getting pretty frustrated with all these official Android docs that conveniently gloss over the fact that even when you apply a Bitmap to an ImageView, the ImageView's dimensions are 0, 0, and yet you can't get the dimensions prior to mapping, either!
https://developer.android.com/training/displaying-bitmaps/load-bitmap.html
All these methods assume you already know fixed dimensions for the ImageView's height and width in terms of pixels, but this is almost useless because in practice you're supposed to either use dp units or layout_weight so things scale to the viewable region so things look right on various phones.
The only way, it would seem, to get the dimensions is to go to the trouble of inefficiently mapping the bitmap and then getting the dimensions that way, but even if you do, the dimensions will show up as 0 if you do imageView.getWidth() or getHeight()! You have to do a bunch of weird async stuff to wait until the dimensions somehow "settle" and THEN you can finally get the dimensions, but at this stage, what's the point when you've already wasted time doing an inefficient mapping?
Is there some well known workaround to all this that Google isn't explaining in the docs? How are you supposed to know the ImageView dimensions when you're not working with pixels in the XML? It's mind-boggling to me that this isn't documented more.
Here is a sample of the problem:
<ImageView
android:id="#+id/my_imageview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
I have this ImageView inside a DialogFragment. The idea is that I can map a Bitmap to this ImageView and it will fit inside the DialogFragment by means of something like myImageView.setImageBitmap(BitmapFactory.decodeFile(path_to_file));, but this by itself is inefficient if the Bitmap is large.
However, in order to load the Bitmap efficiently, you need to already know the dimensions of the ImageView -- and in pixels! But you can't get the dimensions of the ImageView until you've already mapped the Bitmap inefficiently and waited for some kind of post-execute stage where the dimensions have settled in.
These are the methods I am using to do efficient mapping (assuming you already know the sizes -- it breaks if one of the dimensions is 0, so I added a fix). This code is from Google:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height == 0 || width == 0) {
return 0; //my fix
}
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromFilePath(String pathName, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(pathName, options);
}
And for example this does not work:
myImageView.post(new Runnable() {
#Override
public void run() {
int reqHeight = myImageView.getHeight(); //height = 0
int reqWidth = myImageView.getWidth(); //width = 528
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(picFullPath, options);
int realWidth = options.outWidth;
int realHeight = options.outHeight;
reqHeight = realHeight * reqWidth / realWidth; //reqHeight = 396, reqWidth = 528
Bitmap bm = decodeSampledBitmapFromFilePath(picFullPath, reqWidth, reqHeight);
myImageView.setImageBitmap(bm);
}
});
myImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int finalHeight = myImageView.getHeight(); //480!
int finalWidth = myImageView.getWidth(); //608!
}
});
You Should get height and Width of view after inflate any view from XML or Dynamically and then use bellow code to get rendered view height and width :
imageView.measure(0,0);
imageView.getMeasuredHeight();
imageView.getMeasuredWidth();
But Above method may given 0 for height and width , if you are not passing any height and width from XML or you are not used any relative property in Relative layout
I use this approach when I need to load bitmap after I know ImageView size:
myImageView.post(new Runnable() {
#Override
public void run() {
int requiredHeight = myImageView.getHeight(); //height is ready, but is 0 - not calculated.
int requiredWidth = myImageView.getWidth(); //width ready
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
int realWidth = option.outWidth;
int realHeight = option.outHeight;
//calculating required Height
requiredHeight = realHeight / realWidth * requiredWidth;
//now we have both - required height and width.
Bitmap bm = decodeSampledBitmapFromFilePath(pathName, requiredWidth, requiredHeight);
myImageView.setImageBitmap(bm);
}
});

How to display large size bitmaps in imageview android?

I have images more than 6000px height. Whenever I tried to display these images I got out of memory exception.
I have seen many links but none of matches my need. Because mostly people suggesting image re-sizing solution. But If I'll re-size my image than I am unable to read the text in images due to poor quality.
I want some help in creating some code that can open an image without re-sizing it also with zooming effect.
Any help would be really appreciated.
Try to use google recommendation for this problem.
For avoid OOM you need to implement this block of code:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
Try using a WebView to show the image, instead of ImageView
I would suggest you "cut" this image in 3 smaller ones and then load them as three images: the first one will be at y:0 and be height 2000px, the second one y:2000px and height 2000px and the third one at y: 4000px.
Do you think this could work?
Good luck
try this:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fullscreen_view);
imageView = (ImageView) findViewById(R.id.full_image_view);
imageView.setImageBitmap(decodeSampledBitmapFromResource(imgpath));
}
//Load a bitmap from a resource with a target size
Bitmap decodeSampledBitmapFromResource(String res) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(res, options);
//Calculate display dimention for maximum reqwidth and reqheigth
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int xDim = size.x;
int yDim = size.y;
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, xDim, yDim);
// Decode bitmap with inSampleSize se5t
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(res, options);
}
int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
int inSampleSize = 1; //Default subsampling size
// Calculate the largest inSampleSize value
while ((options.outHeight / inSampleSize) > reqHeight
|| (options.outWidth / inSampleSize) > reqWidth) {
inSampleSize += 1;
}
return inSampleSize;
}
In Application manifest file tag set largeHeap true
<application
android:largeHeap="true"
Also Use Picasso or Glide to load image in ImageView

Can not understand this scale bitmap code

In one of the tutorials Google proposes the following scaling bitmap algorithm to make a bitmap fit a given window:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
( http://developer.android.com/training/displaying-bitmaps/load-bitmap.html )
I don't understand the if(width > height) part
Suppose a bitmap has its height of 5 and its width of 2. Then suppose a window has the height of 1 and the width of 1. It looks like the bitmap won't fit the window then.
It makes sure that the size ratio is respected

Categories

Resources