Quality problems when resizing an image at runtime
After experimenting I have finally found a way to do this with good quality results. I'll write this up for anyone that might find this answer helpful in the future.
To solve the first problem, the artifacts and weird dithering introduced into the images, you need to insure your image stays as a 32-bit ARGB_8888 image. Using the code in my question, you can simply add this line to the options before the second decode.
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
After adding that, the artifacts were gone but edges throughout the images came through jagged instead of crisp. After some more experimentation I discovered that resizing the bitmap using a Matrix instead of Bitmap.createScaledBitmap produced much crisper results.
With those two solutions, the images are now resizing perfectly. Below is the code I am using in case it benefits someone else coming across this problem.
// Get the source image's dimensions
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(STRING_PATH_TO_FILE, options);
int srcWidth = options.outWidth;
int srcHeight = options.outHeight;
// Only scale if the source is big enough. This code is just trying to fit a image into a certain width.
if(desiredWidth > srcWidth)
desiredWidth = srcWidth;
// Calculate the correct inSampleSize/scale value. This helps reduce memory use. It should be a power of 2
// from: https://stackoverflow.com/questions/477572/android-strange-out-of-memory-issue/823966#823966
int inSampleSize = 1;
while(srcWidth / 2 > desiredWidth){
srcWidth /= 2;
srcHeight /= 2;
inSampleSize *= 2;
}
float desiredScale = (float) desiredWidth / srcWidth;
// Decode with inSampleSize
options.inJustDecodeBounds = false;
options.inDither = false;
options.inSampleSize = inSampleSize;
options.inScaled = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap sampledSrcBitmap = BitmapFactory.decodeFile(STRING_PATH_TO_FILE, options);
// Resize
Matrix matrix = new Matrix();
matrix.postScale(desiredScale, desiredScale);
Bitmap scaledBitmap = Bitmap.createBitmap(sampledSrcBitmap, 0, 0, sampledSrcBitmap.getWidth(), sampledSrcBitmap.getHeight(), matrix, true);
sampledSrcBitmap = null;
// Save
FileOutputStream out = new FileOutputStream(NEW_FILE_PATH);
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
scaledBitmap = null;
EDIT: After continual work on this I have found that the images still aren't 100% perfect. I'll make an update if I can improve it.
Update: After revisting this, I found this question on SO and there was an answer that mentioned the inScaled option. This helped with the quality as well so I added updated the answer above to include it. I also now null the bitmaps after they are done being used.
Also, as a side note, if you are using these images in a WebView, make sure you take this post into consideration.
Note: you should also add a check to make sure the width and height are valid numbers (not -1). If they are, it will cause the inSampleSize loop to become infinite.
android: quality of the images resized in runtime
Please refer to Romain's answer below.
I would like to delete this answer, but I am unable to since it's marked as accepted.
Note that Android [when this answer was originally written, without explicitly requesting 32-bit colour] would only do 16-bit colour, so there may be times when you get unwanted artifacts.
You can also try using the dither options to tell Android to automatically apply dithering which, while not perfect, can help make things look a bit better.
Bad image quality after resizing/scaling bitmap
I had blury images on low screen resolutions until I disabled scaling on bitmap load from resources:
Options options = new BitmapFactory.Options();
options.inScaled = false;
Bitmap source = BitmapFactory.decodeResource(a.getResources(), path, options);
Will PNG image lose quality when resized using HTML or an APP
Try it out and see if you can tell a difference.
If the image has a lot of details, you should be able to tell "the-resized-png-image.png" would look worse than the original. Now, this is assuming my past experience with chrome, the quality may vary from browser to browser due to differences in the way they downscale images.
There is also a possibility of browsers changing the way they downscale images in updates, so it may also vary with browser versions.
Here are other questions that are consistent with my findings:
Why does image lose quality when resized on the source but does not when resized using html
Make Firefox image scaling down similar to the results in Chrome or IE
Blurry downscaled images in Chrome
Has image downscaling improved in Safari 6 and 7? (5 used to be slightly blurred)
Doing your own downscaling:
- HTML5 Canvas Resize (Downscale) Image High Quality?
So, to answer your question: Depends on the browser/app.
Resize image without loss of information
You're constantly losing information from your image after each resizing attempt. If you want solution (like in MS Word) you have to keep somewhere original image but show only resized copy.
The best solution would be creating an object to store original image and making resized copy on demand. You can improve quality of this solution adding simple cache so you don't actually generate another resized copy of your image for every request but only if your application demands image with different height or width than last time.
I hope I helped you a bit.
Related Topics
Differences Between Constraintlayout and Relativelayout
"Failed to Install the Following Android Sdk Packages as Some Licences Have Not Been Accepted" Error
Android: How to Periodically Send Location to a Server
Adt Will Not Allow Creation of Android Activity
Java.Lang.Classnotfoundexception on Working App
Java.Lang.Illegalargumentexception: Contains a Path Separator
Set Inputtype for an Edittext Programmatically
Actionbaractivity Is Deprecated
Sharing Data Between Fragments Using New Architecture Component Viewmodel
How to Create Text File and Insert Data to That File on Android
Is a Relativelayout More Expensive Than a Linearlayout
How to Fix: "Hax Is Not Working and Emulator Runs in Emulation Mode"
How to Save Geofire Coordinates Along with Other Items in Firebase Database
Move Layouts Up When Soft Keyboard Is Shown
Java.Lang.Illegalstateexception: Can Not Perform This Action After Onsaveinstancestate